Linking unrelated objects with each other

Sometimes it is necessary to build links between objects whose datatypes are not related via a OneToOneRelation or a OneToManyRelation. These external relations are called Links in podio, and they are implemented as a templated version of the code that would be generated by the following yaml snippet (in this case between generic FromT and ToT datatypes):

Link:
  Description: "A weighted link between a FromT and a ToT"
  Author: "P. O. Dio"
  Members:
    - float weight // the weight of the link
  OneToOneRelations:
    - FromT from // reference to the FromT
    - ToT to     // reference to the ToT

The LinkNavigator utility

podio::LinkCollections store each link separately even if a given object is present in several links. Additionally, they don’t offer any really easy way to look up objects that are linked (apart from manually looping and comparing elements). To alleviate these issues, we provide the podio::LinkNavigator utility class that facilitates navigating links and lookups. It can be constructed from any podio::LinkCollection and can then be used to retrieve linked objects. E.g.

const auto& recoMcLinks = event.get<edm4hep::RecoMCParticleLinkCollection>("RecoMCLinks");
const auto linkNavigator = podio::LinkNavigator(recoMcLinks);

// For podio::LinkCollections with disparate types just use getLinked
const auto linkedRecs = linkNavigator.getLinked(mcParticle);

If you want to be explicit about the lookup direction, e.g. in case you have a link that has the same From and To type, you can use the overloads that take a second tag argument:

const auto linkedMCs = linkNavigator.getLinked(recoParticle, podio::ReturnTo);

The return type of all methods is a std::vector<WeightedObject>, where the WeightedObject is a simple template class that wraps the object and its weight. It supports structured bindings, so you can e.g. do the following

for (const auto& [reco, weight] : linkedRecs) {
  // do something with the reco particle and its weight
}

Alternatively, you can access the object via the o member and the weight via the weight member.

Implementation details

In order to give a slightly easier entry to the details of the implementation and also to make it easier to find where things in the generated documentation, we give a brief description of the main ideas and design choices here. With those it should be possible to dive deeper if necessary or to understand the template structure that is visible in the documentation, but should be fairly invisible in usage. We will focus mainly on the user facing classes, as those deal with the most complexity, the underlying layers are more or less what could be obtained by generating them via the yaml snippet above and sprinkling some <FromT, ToT> templates where necessary.

File structure

The user facing "podio/LinkCollection.h" header essentially just defines the PODIO_DECLARE_LINK macro (depending on whether SIO support is desired and possible or not). All the actual implementation is done in the following files:

As is visible from this structure, we did not introduce an LinkData class, since that would effectively just be a float wrapped inside a struct.