Inter-product associations.

Inter-product association, in this context, really means a bi-directional association between an element of type A in one event-data product with an element of a possibly different type B in a possibly different event-data product.

An association that cannot be represented by a Ptr contained by the referrer is one where we wish to have a "forward" association i.e. be able to associate a new product with one already in the event.

In some cases, it may be useful to have additional data associated with such a relation.

Use cases.

Use of associations.

Use of one-to-one associations.

Iterate through a collection of clusters to find the track (if any) associated with each cluster.

Use of one-to-many associations.

Iterate through a collection of tracks, obtaining and working with the hits associated with each track.

Production of associations.

Note that for production of associations, the items to be associated may or may not be in the event.

Production of one-to-one associations.

Given clusters and tracks, associate those that are close; some clusters may have no track (and vice versa).

Production of one-to-many associations.

Given hits and tracks, associate the correct hits with each track.

Use case questions.

  1. Do we need to support collections of associations between items from multiple collections of one or both types? Yes.
  2. Is it more important for the creator to control the order of the associations in the collection (linear time for retrieval of one-to-many associations) or to have logarithmic time for such retrieval? Creator to control order.

Design discussion and proposal.

One wishes to create (and use later) a collection of associations between two types, Cluster and Track, collections of both of which are already in the event.

An example of how one might want to retrieve and use such associations is below, with respect to clusters and tracks:

Handle<Clusters> h;
e.getByLabel(h, "myClusters");
Clusters const &cl = *h;
FindOne<Track> fa(e, h, "TrackMatcher");
for (size_t i = 0; i != cl.size(); ++i) {
  // Use cl[i];
  Track const *t = fa[i]; // Returns nullptr if not found.

And the equivalent code for one-to-many associations, with respect to tracks and hits:

Handle<Tracks> h;
e.getByLabel(h, "myTracks");
Tracks const &tk = *h;
FindMany<Hit> fb(e, h, "TrackFinder");
for (size_t i = 0; i != tk.size(); ++i) {
  // Use tk[i];
  std::vector<Hit const*> v;
  fb.get(i, v); // Appends to v.

Note that the FindOne and FindMany objects are not the persistent representation of the associations -- they are "smart query objects" which encapsulate the associations requested based on the provided arguments. The persistent entity is the Assns, which is envisaged to be used only during filling or for the rare case when the associations are more relevant than the associations themselves.

For an Assns declared as:

template <typename L, typename R, typename D = void>
class Assns;

We constrain the persistent representation of Assns<A, B, D> to be identical to that of Assns<B, A, D>. As a consequence of this, it becomes irrelevant whether any given creator or user of AssociationCollection specifies A followed by B or the converse when declaring the collection. Moreover, both FindOne<A, D> and FindOne<B, D> (and FindMany<A, D>, FindMany<B, D>) shall behave correctly regardless of whether the original underlying Assns specified A or B first.

More proposed interface details.


An Assns may be filled as follows:

myAssns.addSingle(ptrA, ptrB, extraData); // Associated data object.
myAssns.addSingle(ptrA, ptrB); // No associated data object.

An Assns may be interrogated directly using iterator methods begin() and end() which when de-referenced yield std::pair<art::Ptr<A>, art::Ptr<B> >; by operator[] to similar effect; or by a method data(size_type i) which is available if applicable to retrieve the data object attached to a particular association.

The interface of an Assns is intentionally rather limited: most use cases will involve the creation of a FindOne or FindMany smart query object.

FindOne and FindMany

Common qualities.

A FindXXX object may be created using as a reference either:

  1. An art::Handle to one of the collections referred to by the associations upon which it is based.
  2. An art::View into one of said collections.
  3. An arbitrary collection of @art::Ptr@s into one or more of said collections.

In addition, an event and input tag must be provided in order to retrieve the Assns upon which the FindXXX collection will be based.