Skip to content

Commit

Permalink
Add short document describing PIDHandler and its usage
Browse files Browse the repository at this point in the history
  • Loading branch information
tmadlener committed Apr 19, 2024
1 parent 978b0bb commit ddb4d7f
Show file tree
Hide file tree
Showing 2 changed files with 230 additions and 0 deletions.
229 changes: 229 additions & 0 deletions doc/PIDHandler.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
# PIDHandler introduction and usage

This page contains some bigger picture introduction for the utilities that are
availbale to work with `ParticleID`s and its related metadata. It also contains
examples for the most common usage patterns.

## PIDHandler basics
The `PIDHandler` can be use to work with `ParticleIDCollection`s. The main
features are
- the retrieval meta information for a `ParticleIDCollection`, making it
possible to e.g. retrieve the indices of parameters from parameter names
- the possibility to invert the relation to `ReconstructedParticle`s, which
allows to, e.g. get all `ParticleID`s that point to one
`ReconstructedParticle`.

## ParticleIDMeta basics
`ParticleIDMeta` is a simple struct that bundles all ParticleID meta information
for one collection together. Whenever metadata is involved for ParticleIDs this
will be the thing to use.

## General considerations

There is no strictly enforced coupling between the meta information that is set
for a collection and the contents of that collection. In order for everything to
work as expected the following assumptions have to be met
- The collection name that is passed to the `setAlgoInfo` methods to set meta
information has to match the collection name used for putting collections into
events.
- All elements of a `ParticleIDCollection` have the same value set for
`algorithmType`. Additionally, it is usually assumed that this is a unique
value.
- The `ParticleIDMeta::paramNames` is assumed to match the parameters as they
are set in the `ParticleID` elements.

Additionally there are a few usage considerations to be kept in mind
- The `PIDHandler` can be used without any meta data about the contained
`ParticleID`s. However, in this case it will only be useful for inverting
relations and for getting `ParticleID`s that point to a given
`ReconstructedParticle`.
- There is no guarantee that the meta information that is set in a `PIDHandler`
is consistent with the elements it knows about. Some constructors attempt to
construct a consistent handler, but it is always possible to set meta
information via `setMetaInfo`, which might be completely unrelated to the
actual contents of a handler.

Given that there is no enforced consistency the interface and the utility
functionality makes heavy use of
[`std::optional`](https://en.cppreference.com/w/cpp/utility/optional) in its
return values / types. This makes it possible to check whether the desired
information was actually present, without having to reserve some special values
to indicate that fact.

## Usage examples

**The following examples assume the following includes** (which will not be visible in the examples)

```cpp
#include <edm4hep/utils/ParticleIDUtils.h>
#include <edm4hep/ParticleIDCollection.h>

#include <podio/Frame.h>
```

**The following examples additionally assume, that there is a `metadata` and an
`event` Frame present.** The `metadata` Frame can be obtained like this from
standard EDM4hep files.

```cpp
#include <podio/ROOTReader.h>
#include <podio/Frame.h>

podio::Frame getMetadataFrame(const std::string& filename) {
podio::ROOTReader reader{};
reader.openFile(filename);
return podio::Frame(reader.readNextEntry(podio::Category::Metadata));
}
```
Alternatively it can be created as an empty Frame and then just written using
the `podio::Category::Metadata` category name.
**Finally most of the examples assume that the desired values were found and
simply get the `value` from the returned `std::optional` directly.** This means
that most of the times you will see lines like this
```cpp
const auto value = pidHandler.getAlgoType("someAlgo").value();
```

This will throw an exception if some information is not available. If you want /
need to check first whether information is present you can do

```cpp
const auto maybeValue = pidHandler.getAlgoType("someAlgo");
if (maybeValue.has_value()) {
const auto value = maybeValue.value();
}
```

### Creating a PIDHandler for a complete event

If you want to work with a `PIDHandler` that has a somewhat global view of all
`ParticleID`s that are present you can simply construct one from an event Frame

```cpp
const auto pidHandler = edm4hep::utils::PIDHandler::from(event);
```

You can also construct a `pidHandler` that populates some meta information
internally by also passing in the `metadata` Frame to this constructor
```cpp
const auto pidHandler = edm4hep::utils::PIDHandler::from(event, metadata);
```

### Creating a PIDHandler from an arbitrary number of collections

If you simply want to use a `PIDHandler` to invert the relations for a given set
of `ParticleIDCollection`s you can do that via

```cpp
const auto& tofPID = event.get<edm4hep::ParticleIDCollection>("ToFPID");
const auto& dNdXPID = event.get<edm4hep::ParticleIDCollection>("dNdXPID");
const auto& mvaPID = event.get<edm4hep::ParticleIDCollection>("mvaPID");

const auto pidHandler = edm4hep::utils::PIDHandler::from(tofPID, dNdXPID, mvaPID);
```

**This handler will not have any access to metadata!** If you need metadata as
well, you can add that after the fact using the `addMetaInfo` method.

### Using a PIDHandler to get the ParticleIDs for a reco particle

If you have constructed a `PIDHandler` you can use it to get all or specific
related `ParticleID` objects of a `ReconstructedParticle`

```cpp
const auto pidHandler = /* constructed as above somehow */;

const auto recos = event.get<edm4hep::ReconstructedParticle>("recos");
const auto reco = recos[0];

// Get all related ParticleIDs
const auto pids = pidHandler.getPIDs(reco);

// Only get a specific ParticleID
const auto tofAlgoType = pidHandler.getAlgoType("ToFPID").value();
const auto tofPID = pidHandler.getPID(reco, tofAlgoType).value();
```

### Retrieving meta information for a collection

If you simply want to get the meta information for a collection (called "ToFPID"
in this example) do

```cpp
const auto algoInfo = edm4hep::utils::PIDHandler::getAlgoInfo(metadata, "ToFPID").value();
```


### Using meta information of a collection

If you have retrieved the meta information you can directly use that to learn
something about the `ParticleID`s it describes

```cpp
const auto& tofPIDs = event.get<edm4hep::ParticleIDCollection>("ToFPID");
const auto algoInfo = edm4hep::utils::PIDHandler::getAlgoInfo(metadata, "ToFPID").value();
const auto timeIndex = edm4hep::utils::getParamIndex(algoInfo, "time").value();

for (const auto tof : tofPIDs) {
const auto time = tof.getParameters(timeIndex);
// ...
}
```

If you have a `PIDHandler` with enough meta information set it is also possible
to retrieve the `timeIndex` from that

```cpp
const auto pidHandler = /* construct somehow see above */
const auto tofAlgoType = pidHandler.getAlgoType("ToFPID").value();
const auto timeIndex = pidHandler.getParamIndex("time").value();

// ... as above
```


### Setting meta information for a collection

If you have a `ParticleIDCollection` and want to persist some meta information
for it the following snippet shows you how to do that

```cpp
// Create ParticleID meta information for an algorihtm
const auto tofMeta = {"TimeOfFlight", 42, {"time", "time_error"}};

auto tofPIDs = edm4hep::ParticleIDCollection{};
// ... fill collection

edm4hep::utils::PIDHandler::setAlgoInfo(metadata, tofPIDs, "ToFPID", tofMeta);
event.put(std::move(tofPIDs), "ToFPID");
```
This will put the necessary metadata into the `metadata` Frame and also take
care of setting the `algorithmType` field of all elements of the `tofPIDs`
collection to `42` (the value it is set to in the `tofMeta` object).
**Things to note:**
- It is important to use the same names for putting the collection into the
`event` Frame and the one that is passed to `PIDHandler::setAlgoInfo`!
### Setting meta information using a collection name
If you want to set the meta information for a `ParticleIDCollection` that you no
longer have mutable access to, this can be done via
```cpp
// Create ParticleID meta information for an algorihtm
const auto tofMeta = {"TimeOfFlight", 42, {"time", "time_error"}};
edm4hep::utils::PIDHandler::setAlgoInfo(metadata, "ToFPID", tofMeta);
```

**Things to note:**
- In this case it is user responsibility to set the `algoType` of the
`ParticleIDMeta` to the same value as the one that is used for the elements of
the collection.
- It is important to use the same names for putting the collection into the
`event` Frame and the one that is passed to `PIDHandler::setAlgoInfo`!
1 change: 1 addition & 0 deletions utils/include/edm4hep/utils/ParticleIDUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ struct ParticleIDMeta {
std::optional<int> getParamIndex(const ParticleIDMeta& pidMetaInfo, const std::string& param);

/// Utility class to invert the ParticleID to ReconstructedParticle relation
/// See [this page](@ref md_doc_2_p_i_d_handler) for example usage and more information.
class PIDHandler {

using RecoPidMapT = std::multimap<edm4hep::ReconstructedParticle, edm4hep::ParticleID>;
Expand Down

0 comments on commit ddb4d7f

Please sign in to comment.