Back to home page

EIC code displayed by LXR

 
 

    


Warning, /acts/docs/core/eventdata/tracks.md is written in an unsupported language. File is not indexed.

0001 (edm_tracks)=
0002 # High-level Track Event Data Model
0003 
0004 Track information in ACTS can be divided into two parts: track-level
0005 information and track state-level information.
0006 
0007 Track-level information are properties that relate to the full track. This
0008 includes the fitted track parameters with respect to some reference point,
0009 often the origin of the detector or the beamspot. It can also include summary
0010 information from the track finding stage, like the overall number of clusters
0011 that were used in the creation of the track, or the fit quality from the track
0012 fit.
0013 
0014 Tracks are built-up from track states, where each track state corresponds to a
0015 discrete state determining the track properties. This mainly includes
0016 measurements states, expected intersections with sensors where no measurement
0017 was found ({term}`holes<Hole>`), and intersections with known passive material. The
0018 {term}`EDM` allows building up a track from these track states iteratively. For
0019 example, the Kalman Filter will append track states to the sequence whenever it
0020 encounters a sensitive detector layer. The content of the track states is
0021 defined such that the fitter can store all relevant information, with as little
0022 need for extra information as possible. It is also designed to be flexible
0023 enough to support different fitters, which might require different information
0024 to be stored, as well as the Combinatorial Kalman Filter, which produces a tree
0025 of track states, instead of a fully linear sequence.
0026 
0027 Ultimately, each output track is associated with a well-defined sequence of
0028 track states, allowing downstream consumers of the {term}`EDM` to access the fully
0029 detailed information produced during track reconstruction.
0030 
0031 ---
0032 
0033 This section will first discuss the architecture and general implementation of
0034 the Track {term}`EDM` in [](#edm_track_conceptual), and then report on the API
0035 of the involved classes in [](#edm_track_api).
0036 
0037 (edm_track_conceptual)=
0038 ## Conceptual
0039 
0040 ### Architecture
0041 
0042 The Track {term}`EDM` is structured such that the memory-layout can be
0043 {term}`SoA`, while presenting an object-oriented interface for convenient
0044 usage.
0045 
0046 {numref}`track_proxy` shows this object-oriented access model for
0047 the example of the track container and track proxy object. The track container
0048 holds vectors of the various pieces of information, and has methods to add a
0049 track, and to allow iteration over all tracks. This iteration, or index based
0050 access, yields a track proxy object, which exposes the properties as methods
0051 returning references, while internally only holding a pointer to and an index
0052 into the track container. The types are built in a way that preserves
0053 const-correctness, i.e. even though a track proxy is a value type which can be
0054 copied, it will not allow modification of the underlying track container if it
0055 is immutable:
0056 
0057 ```cpp
0058 auto mutableTrackContainer = /*...*/;
0059 auto trackProxy = mutableTrackContainer.getTrack(5); // is mutable
0060 const auto& constTrackProxy = trackProxy; // is const
0061 // ...
0062 auto constTrackContainer = /*...*/;
0063 auto trackProxy = trackContainer.getTrack(5); // is const, even as an lvalue
0064 ```
0065 
0066 (track_proxy)=
0067 :::{figure} figures/proxy.svg
0068 :align: center
0069 Illustration of the proxy pattern used in the track {term}`EDM`. The track
0070 proxy logically represents a single track, and points to the data stored in
0071 the track container.
0072 :::
0073 
0074 The track {term}`EDM` is fully agnostic to the concrete persistency framework
0075 of an experiment. This avoids having to convert the data between different
0076 representations, if implemented correctly.
0077 
0078 ### Implementation
0079 
0080 To make the {term}`EDM` implementation independent of an experiment persistency
0081 framework, it is separated into a *frontend layer* and a *backend layer*. The
0082 frontend layer contains user-facing getters and setters, as well as convenience
0083 methods that can be helpful. These methods are located either in the proxy
0084 objects or in the containers, depending on whether they operate on a single
0085 element or the entire container.
0086 
0087 Overall, there are four main classes that make up the frontend layer:
0088 {class}`Acts::TrackProxy`, {class}`Acts::TrackContainer`,
0089 {class}`Acts::TrackStateProxy` and {class}`Acts::MultiTrajectory`. The latter
0090 serves as the track state container, where the name indicates that it is able
0091 to handle a branching tree structure of track states. `TrackProxy` and
0092 `TrackStateProxy` expose methods to get the local track parameters and
0093 covariance, corresponding reference surface, and also includes global
0094 statistics like the total number of measurements, {term}`outliers<Outlier>` or
0095 {term}`holes<Hole>` in case of `TrackProxy`. `TrackProxy` also has a method to
0096 conveniently iterate over the associated track states from the last track state
0097 to the first one yielding `TrackStateProxy` objects from the track state
0098 container. In the common case of a track from the center of a cylindrical
0099 detector going outward, the default iteration order is from the outside
0100 inwards.
0101 
0102 In case of `TrackStateProxy`, functionality is exposed in the frontend
0103 layer to allocate optional components, with the goal of reduced memory
0104 footprint. There are two main use cases of this: track parameters and measurements.
0105 The track-state {term}`EDM` supports storing up to three sets of local track parameters
0106 and covariance matrices, modeled after the information the Kalman Filter
0107 formalism needs to store:
0108 
0109 1. predicted parameter vector and covariance matrix
0110 2. filtered parameter vector and covariance matrix
0111 3. smoothed parameter vector and covariance matrix
0112 
0113 In case of combinatorial track finding (see [](#track_finding), specifically
0114 {numref}`tracking_ckf`), track hypothesis can start out with a common sequence
0115 of track states, and then branch out when multiple compatible measurements are
0116 encountered, as seen in {numref}`ckf_tree`.
0117 
0118 The track state {term}`EDM` allows allocating only the track parameters that
0119 are needed, and also allows sharing the same track parameters between multiple
0120 track states, so that branching track states can share for example the same
0121 predicted parameters. How this is achieved exactly is left to the backend
0122 layer. Measurements are handled in a similar way, where the track finding
0123 decides how much storage is needed based on the number of dimensions of an
0124 incoming measurement. It then instructs the {term}`EDM` through the frontend
0125 layer to ensure enough memory is available, where the specifics are again left
0126 up to the backend layer.
0127 
0128 The backend layer exposes an interface that is used by the frontend layer to
0129 store and retrieve information. It uses dedicated methods where needed, such as
0130 for storing reference surfaces or source-link objects, which are lightweight
0131 container objects for experiment-specific measurements. For the majority of
0132 components, the frontend communicates with the backend through a single method
0133 to obtain references to the underlying data. Components are accessed via hashes
0134 of the component name, where the hashes are calculated at compile-time wherever
0135 possible. The backend can then use the hashed component name to retrieve the
0136 relevant memory. To allow directly manipulating the backing memory, the frontend
0137 expects the backend to return references into the backing storage.
0138 
0139 `TrackProxy` provides a method to copy a track between different track
0140 containers, and only uses the frontend layer to accomplish this. This means that
0141 copying tracks between different backend implementations is trivial.
0142 
0143 (track_architecture)=
0144 :::{figure} figures/edm_diagram.svg
0145 :align: center
0146 Diagram of the {term}`EDM` architecture. The frontend layer is used by other
0147 ACTS components, and downstream clients. It is separated from the backend
0148 layer by an interface. Conversion to and from EDM4hep is possible. Examples
0149 of direct backend implementations are shown.
0150 :::
0151 
0152 {numref}`track_architecture` shows a diagram of the {term}`EDM` architecture. At the center
0153 are the `TrackProxy` and `TrackContainer`. These classes are
0154 produced by the track finding and track fitting components, and are the main
0155 interface point with the clients of tracking. In ACTS itself, all of the
0156 performance monitoring and downstream reconstruction is either directly built on
0157 top of these objects, or converts them into an internal {term}`EDM` on the use
0158 case. Behind the backend interface, the track container coordinates with both a
0159 track state and a track backend, where a few examples are shown, and will be
0160 discussed below.
0161 
0162 (edm_track_api)=
0163 ## Track API
0164 
0165 :::{doxygenclass} Acts::TrackProxy
0166 :members:
0167 :::
0168 
0169 :::{doxygenclass} Acts::TrackContainer
0170 :members:
0171 :::
0172 
0173 (edm_track_iteration)=
0174 ## Track state iteration and forward linking
0175 
0176 By default, track states are connected as a one-directional linked list, where
0177 each track state knows its *previous* track state. {numref}`ckf_tree` shows an
0178 example of a track state tree, like it is constructed by the combinatorial
0179 track finding.
0180 
0181 In {numref}`ckf_tree` states $S_7$ and $S_6$ are the two {term}`tip states<tip
0182 state>` of the track state tree, while $S_1$ is the single {term}`stem state`.
0183 In the case of combinatorial track finding starting from e.g. a
0184 {term}`seed<Seed>`, it could be the location of the innermost {term}`space
0185 point<Space point>`.
0186 
0187 (ckf_tree)=
0188 :::{figure} figures/ckf_tree.svg
0189 :width: 400px
0190 :align: center
0191 Illustration of a branching multi-trajectory that is created during
0192 combinatorial track finding.
0193 :::
0194 
0195 Each track object points at a single {term}`tip state` to define its track state sequence.
0196 The {class}`Acts::TrackProxy` class has various methods to access the track state sequence:
0197 
0198 ```cpp
0199 auto track = getTrackFromSomewhere();
0200 for(const auto trackState : track.trackStatesReversed()) {
0201   // do something with track state
0202 }
0203 ```
0204 
0205 Note that {func}`Acts::TrackProxy::trackStatesReversed` iterates from the {term}`tip state` to
0206 the {term}`stem state`, i.e. from the outside in.
0207 
0208 :::{attention}
0209 By-default, it is not possible to iterate *forward* through the track states on a track!
0210 The track's track states need to be *forward-linked* for this to be possible.
0211 :::
0212 
0213 The reason for this is:
0214 As the trajectory branches at the second sensor into $S_2$/$S_3$, it is not
0215 possible to connect the states forward, i.e. store in $S_1$ what the *next*
0216 state is going to be: it is ambiguous!
0217 
0218 However, when track finding has concluded, and the trajectories identified by
0219 {term}`tip states<tip state>` $S_7$ and $S_8$ have been discarded or are being
0220 copied into an output container, it is possible to *forward link* the track
0221 state sequences. This is possible **if** the trajectory does not branch
0222 anymore! {func}`Acts::TrackProxy::copyFrom` will implicitly forward link the
0223 track states, as it is guaranteed to not branch after copying.
0224 
0225 You can manually add forward-linking to a track by calling
0226 {func}`Acts::TrackProxy::linkForward` or
0227 {func}`Acts::TrackProxy::reverseTrackStates`.
0228 
0229 :::{warning}
0230 Calling either {func}`Acts::TrackProxy::linkForward` or
0231 {func}`Acts::TrackProxy::reverseTrackStates` on a track state sequence which
0232 has branching will break the branching! If you have other tracks pointing at a
0233 {term}`tip state` that branches from the sequence you're trying to
0234 forward-link, it will be corrupted!
0235 :::
0236 
0237 In this example, before any forward linking, the sequence looks like this:
0238 
0239 :::{graphviz}
0240 
0241 digraph {
0242 rankdir="LR";
0243 S2 -> S1;
0244 S3 -> S1;
0245 S7 -> S5 -> S4 -> S2;
0246 S6 -> S3;
0247 }
0248 
0249 :::
0250 
0251 After a copy operation of $S_6$ and $S_7$ the resultig track state sequences will look like this:
0252 
0253 
0254 :::{graphviz}
0255 
0256 digraph {
0257 rankdir="LR";
0258 S11[label="S1 (copy)"];
0259 S12[label="S1 (copy)"];
0260 S7 -> S5 -> S4 -> S11;
0261 S11 -> S4 -> S5 -> S7;
0262 S6 -> S3 -> S12;
0263 S12 -> S3 -> S6;
0264 }
0265 
0266 :::
0267 
0268 This now includes both forward and backward links, which allows iteration from
0269 $S_1$/$S_2$ to $S_6$/$S_7$ and the other way around.
0270 
0271 Forward iteration can then be achieved like this:
0272 
0273 ```cpp
0274 auto track = getTrackFromSomewhere();
0275 for(const auto trackState : track.trackStates()) {
0276   // iterate forward
0277   // do something with track state
0278 }
0279 ```
0280 
0281 and the innermost track state becomes directly accessible via
0282 {func}`Acts::TrackProxy::innermostTrackState`.
0283 
0284 :::{Attention}
0285 If the track container has branching track state sequences, running a smoothing
0286 step in-place on branching tracks is problematic: if tracks are smoothed one by
0287 one, the last track of each shared track state (i.e. the track state where
0288 branching occurs) will overwrite the smoothing result of all previous tracks.
0289 
0290 Consider again the track states in {numref}`ckf_tree`. $S_1$ is the common
0291 ancestor for $S_2$ and $S_3$, and has a single slot to store smoothed
0292 parameters. If smoothing happens for the track ending in $S_6$, then smoothing
0293 the track ending in $S_7$ only the values written by the final smoothing of
0294 $S_37 will survive in $S_1$'s storage. This can be unexpected!
0295 :::
0296 
0297 ## Track State API
0298 
0299 :::{doxygenclass} Acts::TrackStateProxy
0300 :members:
0301 :::
0302 
0303 :::{doxygenclass} Acts::MultiTrajectory
0304 :members:
0305 :::
0306 
0307 (track_edm_component_sharing)=
0308 ## Component sharing
0309 
0310 {class}`Acts::MultiTrajectory` is designed so that components can be shared
0311 between track states. This can be achieved using the
0312 {func}`Acts::TrackStateProxy::shareFrom` can be used to set this up.
0313 
0314 Shareable components are
0315 - predicted parameters and covariance
0316 - filtered parameters and covariance
0317 - smoothed parameters and covariance
0318 - jacobian
0319 
0320 To illustrate why this can be useful, consider again {numref}`ckf_tree`, where
0321 $S_2$ and $S_3$ branch out from a shared $S_1$. In this case, the predicted
0322 parameter vector and covariance, as well as the jacobian from $S_1\to S_2$ and
0323 $S_1 \to S_3$ will be identical. In this case, the combinatorial track finding
0324 will use the sharing functionality to share these components.
0325 
0326 :::{attention}
0327 Sharing these components introduces *cross-talk* between track states, and this
0328 is intentional. If e.g. the predicted covariance is modified through either of
0329 the track states, the changes will be visible when accessed from the other
0330 track state as well.
0331 :::
0332 
0333 (track_edm_dynamic_columns)=
0334 ## Dynamic columns
0335 
0336 Aside from the static properties that both the track states and the track have,
0337 the {term}`EDM` supports adding almost arbitrary additional information as
0338 dynamic columns.  The implementation of the dynamic column mechanism is given
0339 by the backend, where the interface layer classes
0340 {class}`Acts::MultiTrajectory` and {class}`Acts::TrackContainer` and associated
0341 proxies only coordinate the creation, access and copying of dynamic columns.
0342 
0343 The following illustrates the
0344 usage of dynamic columns for {class}`Acts::TrackContainer`, but usage on
0345 {class}`Acts::MultiTrajectory` is identical.
0346 
0347 Assume you create a track container using some combination of backends (see
0348 [](#edm_track_backends) for information on the backends shipped with ACTS).
0349 
0350 ```cpp
0351 Acts::TrackContainer tc{/*...*/};
0352 
0353 // add dynamic columns programmatically
0354 
0355 tc.addColumn<float>("col_a");
0356 tc.addColumn<uint8_t>("col_b");
0357 
0358 ```
0359 
0360 Adding columns is only supported on *mutable* track containers, const track
0361 containers should contain the original dynamic columns from when they were
0362 created. It is up to the backend to implement recovering dynamic columns from
0363 e.g. input files.
0364 
0365 :::{note}
0366 Which types are supported depends on the backend being used. See
0367 [](#edm_track_backends) for information on the backends shipped with ACTS, and
0368 which types they support.
0369 :::
0370 
0371 With these dynamic columns registered, it is now possible to set and get values for these columns on tracks.
0372 
0373 ```cpp
0374 using namespace Acts::HashedStringLiterals;
0375 auto track = tc.makeTrack();
0376 
0377 // these two are equivalent
0378 track.component<float, "col_a"_hash>() = 42.42;
0379 track.component<float>("col_a"_hash) = 52.52;
0380 std::cout << track.component<float, "col_a"_hash>() << std::endl; // prints: 52.52
0381 ```
0382 
0383 :::{tip}
0384 The expression `"col_a"_hash` is a user-defined literal that internally calls
0385 ```cpp
0386 Acts::hashedString("col_a");
0387 ```
0388 This literal is only available after
0389 ```cpp
0390 using namespace Acts::HashedStringLiterals;
0391 ```
0392 :::
0393 
0394 The components are accessed by a hash of the name of the component. This hash
0395 can be calculated from a string at compile-time, if the string is known at
0396 compile time. The difference between the two component access signatures is
0397 that in the first case, the hash of the component is guaranteed to be evaluated
0398 at compile-time, since it is given to the `component` function as a template
0399 argument. A third option is available to access components: see
0400 [](#edm_track_accessors).
0401 
0402 
0403 (edm_track_accessors)=
0404 ## Accessors
0405 
0406 It can be inconvenient to have to write the full component access signature,
0407 especially if you want to access the same components repeatedly. An alternative
0408 are **accessors**. They encapsulate the type of the component, and the
0409 component name hash into an object:
0410 
0411 ```cpp
0412 // definition of the accessor with a type and the name of the component
0413 Acts::ProxyAccessor<float> extra("extra");
0414 // component access by calling it on a proxy
0415 extra(track) = 42.2;
0416 std::cout << extra(track) << std::endl; // prints 42.2
0417 ```
0418 
0419 :::{tip}
0420 The same accessor also works for {class}`Acts::TrackStateProxy` objects, as it shares
0421 the same component access mechanism with {class}`Acts::TrackProxy`.
0422 :::
0423 
0424 The above accessor is a **mutable** accessor, meaning it can only be used with
0425 mutable proxy objects!
0426 
0427 ```cpp
0428 ConstTrackProxy<...> constTrack = /*...*/;
0429 extra(constTrack); // this will give a compile error!
0430 ```
0431 
0432 To access properties on const proxy objects, you need to use a dedicated
0433 accessor type:
0434 
0435 ```cpp
0436 Acts::ConstProxyAccessor<float> extraConst("extra");
0437 std::cout << extraConst(constTrack) << std::endl; // prints 42.2
0438 // using the const accessor on a mutable proxy also works
0439 std::cout << extraConst(track) << std::endl; // prints 42.2
0440 ```
0441 
0442 For both const and mutable proxy accessors you do not actually need a mutable reference, as the internal accessor state is not mutated after construction. You can safely use a static instance of these accessors to avoid constructing them over and over again:
0443 
0444 ```cpp
0445 template<TrackProxyConcept track_proxy_t>
0446 void doSomething(track_proxy_t track, float value) {
0447     // only created once, never changed
0448     static const Acts::ProxyAccessor<float> extra("extra");
0449     extra(track) = value;
0450 }
0451 ```
0452 
0453 ## Holders
0454 
0455 The {class}`Acts::TrackContainer` implements a mechanism to optionally own the
0456 backends that it is constructed with. This is implemented using a *holder*
0457 type, which is passed either as a template parameter, or deduced automatically.
0458 
0459 Available default holders are:
0460 
0461 - `Acts::detail::RefHolder` which does not own the backends
0462 - `Acts::detail::ConstRefHolder` which does not own the backends and does not permit mutations.
0463 - `Acts::detail::ValueHolder` which owns the backends by value. This is
0464   auto-deduced if the backends are given as values or rvalue-references.
0465 
0466 Other user-specified holders can also be used, for example, it is possible to use `std::shared_ptr` as a holder directly, like:
0467 
0468 ```cpp
0469 std::shared_ptr<Acts::VectorTrackContainer> vtc{
0470     std::make_shared<Acts::VectorTrackContainer>()};
0471 std::shared_ptr<Acts::VectorMultiTrajectory> mtj{
0472     std::make_shared<Acts::VectorMultiTrajectory>()};
0473 
0474 Acts::TrackContainer<Acts::VectorTrackContainer, Acts::VectorMultiTrajectory, std::shared_ptr>
0475 tc{vtc, mtj};
0476 ```
0477 
0478 ### How to create a track from scratch
0479 
0480 Tracks can be created directly from the {term}`EDM` interface. You start by creating or obtaining a mutable track container:
0481 
0482 ```cpp
0483 Acts::TrackContainer tc{Acts::VectorTrackContainer{}, Acts::VectorMutiTrajectory{}};
0484 ```
0485 
0486 A single track can be added like this:
0487 
0488 ```cpp
0489 auto track = tc.makeTrack();
0490 // set some properties
0491 track.parameters() << 0.1_mm, 3_mm, 1/20_GeV, 0.2, 0.4, 25_mm;
0492 track.setReferenceSurface(
0493     Acts::Surface::makeSurface<Acts::PerigeeSurface>(Acts::Vector3::Zero()));
0494 ```
0495 
0496 The track is still lacking track states. You can *append* track states to the
0497 track, which means that a track state is attached behind the outermost track
0498 state currently assigned.
0499 
0500 ```cpp
0501 auto ts1 = track.appendTrackState();
0502 ts1.smoothed() << 0.4_um, 1_mm, 1/19_GeV, 0.21, 0.37, 40_ns;
0503 //...
0504 auto ts2 = track.appendTrackState();
0505 ts2.smoothed() << 0.4_um, 1_mm, 1/19_GeV, 0.21, 0.37, 40_ns;
0506 
0507 ```
0508 
0509 Note that this means that you have to create track state from the inside out!
0510 If you have to add track states from the outside in, you can still append them
0511 and reverse the track at the very end.
0512 
0513 ```cpp
0514 track.reverseTrackStates();
0515 ```
0516 
0517 
0518 ## Track EDM backends
0519 
0520 (edm_track_backends)=
0521 ### Backends shipped with ACTS
0522 
0523 #### Transient vector backend
0524 
0525 The transient vector backend implements the reference backend for the track
0526 {term}`EDM`. It does not implement any persistency directly. The implementation of
0527 this backend for both track and track state containers uses separate classes
0528 for the mutable and const versions, in order to fully comply with const
0529 correctness. It also uses a common base class internally, which is however an
0530 implementation detail.
0531 
0532 To build a track container with this backend, you can write
0533 
0534 ```cpp
0535 Acts::VectorMultiTrajectory mtj{};
0536 Acts::VectorTrackContainer vtc{};
0537 Acts::TrackContainer tc{vtc, mtj};
0538 ```
0539 
0540 or
0541 
0542 ```cpp
0543 Acts::ConstVectorTrackContainer vtc{/* ... */};
0544 Acts::ConstVectorMultiTrajectory mtj{/* ... */};
0545 Acts::TrackContainer ctc{vtc, mtj};
0546 ```
0547 :::{note}
0548 There are currently no restrictions on types that can be used as dynamic
0549 columns. Any type can be stored and retrieved back from the backend.
0550 
0551 Keep in mind that the transient vector backend does not support persistency,
0552 meaning that there is no mechanism to serialize dynamic columns (or static
0553 columns for that matter) to disk and read them back in.
0554 :::
0555 
0556 #### PODIO backend
0557 
0558 The PODIO track {term}`EDM` backend shipped with the library uses a custom PODIO-{term}`EDM`
0559 defined in `edm.yml` in the ACTS core repository.
0560 
0561 The working model is this:
0562 
0563 1. Mutable PODIO track and track state backends are created with a
0564    [helper](#podio_helper)
0565    ```cpp
0566    Acts::MutablePodioTrackStateContainer tsc{helper};
0567    Acts::MutablePodioTrackContainer ptc{helper};
0568    ```
0569 2. A track container is built using these PODIO backend instances
0570    ```cpp
0571    Acts::TrackContainer tc{ptc, tsc};
0572    ```
0573 3. The track container is used by some algorithm
0574    ```cpp
0575    tc.makeTrack();
0576    // ...
0577    ```
0578 4. The mutable backends *released into* a `podio::Frame` for writing.
0579    ```cpp
0580    ptc.releaseInto(frame);
0581    tsc.releaseInto(frame);
0582    // write frame
0583    ```
0584 5. When reading, const track state and track PODIO backends are created from a
0585    `podio::Frame`
0586    ```cpp
0587    auto frame = /* read frame */;
0588    Acts::ConstPodioTrackStateContainer tsc{helper, frame};
0589    Acts::ConstPodioTrackContainer ptc{helper, frame};
0590    ```
0591 
0592 :::{note}
0593 The PODIO backend currently supports all types that can be written to a
0594 `podio::UserDataCollection` for dynamic columns. At the time of writing these
0595 are: `float`, `double`, `int8_t`, `int16_t`, `int32_t`, `int64_t`, `uint8_t`,
0596 `uint16_t`, `uint32_t`, `uint64_t`.
0597 
0598 In particular, it is not possible to write `bool` values directly. A workaround
0599 is using `uint8_t` and making boolean expressions explicit.
0600 :::
0601 
0602 (podio_helper)=
0603 ##### Helper for `Surface`s and `SourceLink`s
0604 
0605 PODIO cannot directly store `Surface`s and `SourceLink`s. The PODIO backends rely on a helper class
0606 that implements the following interface:
0607 
0608 :::{doxygenclass} Acts::PodioUtil::ConversionHelper
0609 :::
0610 
0611 Specifically, the PODIO backends will, before persisting and after reading,
0612 consult the helper object to convert between an in-memory `Surface` and an
0613 optional identifier. The identifier a 64-bit integer whose interpretation can
0614 depend on the experiment, it could be a sensor index (hash in ATLAS) for
0615 example.
0616 
0617 If no identifier is returned, that means the surface is not expressible as an
0618 identifier, and it needs to be persisted directly, which is implemented
0619 centrally. In that case, the defining parameters of the surface are saved, and
0620 the object is rebuilt when reading. An example of this is in the case of a
0621 reference surface like a perigee surface that represents the beam axis, which
0622 is not commonly identified in the experiment geometry.
0623 
0624 A similar mechanism is used for source links. Remember that
0625 {class}`Acts::SourceLink` is type-erased proxy object that stands for an
0626 experiment-specific *uncalibrated* measurement. As such, the PODIO backend
0627 cannot directly store these. For use with the PODIO backends, source links need
0628 to be convertible to and from 64-bit integers, with the help of the helper
0629 object, that can communicate with the experiment infrastructure.
0630 
0631 ### How to build a backend
0632 
0633 Both track and track state backends need to conform to respective concepts.
0634 Effectively, the backend has to allow for collection functionality, like
0635 getting the current size etc.
0636 
0637 Further, the backend needs to respond to queries for properties of the
0638 elements, where the element is identified by an index.
0639 
0640 The backend needs to flag itself as mutable or const, by specializing
0641 
0642 ```cpp
0643 template <typename T>
0644 struct IsReadOnlyMultiTrajectory;
0645 // or
0646 template <typename T>
0647 struct IsReadOnlyTrackContainer;
0648 ```
0649 
0650 This informs the interface layer to permit or prevent mutable access.
0651 
0652 Common between both track and track state backends is the component access.
0653 Here, the component is identified by a compile-time string hash of the
0654 component name, which the backend responds to by a type-erased mutable or const
0655 pointer. There is a hard requirement for the backend to return stable pointers
0656 here, as the interface layer expects this and exposes this in the API.
0657 
0658 This can be accomplished like in the transient backend with a `switch`-statements like:
0659 
0660 ```cpp
0661 switch (key) {
0662   case "previous"_hash:
0663     return &m_previous[istate];
0664   case "next"_hash:
0665     return &m_next[istate];
0666   case "predicted"_hash:
0667     return &m_index[istate].ipredicted;
0668   case "filtered"_hash:
0669     return &m_index[istate].ifiltered;
0670   case "smoothed"_hash:
0671     return &m_index[istate].ismoothed;
0672   case "projector"_hash:
0673     return &m_projectors[instance.m_index[istate].iprojector];
0674   case "measdim"_hash:
0675     return &m_index[istate].measdim;
0676   case "chi2"_hash:
0677     return &m_index[istate].chi2;
0678   case "pathLength"_hash:
0679     return &m_index[istate].pathLength;
0680   case "typeFlags"_hash:
0681     return &m_index[istate].typeFlags;
0682   default:
0683     // handle dynamic columns
0684 }
0685 ```
0686 
0687 The `default` statement deals with [](#track_edm_dynamic_columns). For support
0688 of dynamic columns, the backend needs to implement `hasColumn_impl` to return
0689 if a column exists, mutable backends need to implement `addColumn_impl` which
0690 adds a column with a type and a string name. `copyDynamicFrom_impl` and
0691 `ensureDynamicColumn_impl` on mutable backends allows copying dynamic content
0692 between container backends. `dynamicKeys_impl` returns an iterator pair that
0693 informs the caller of which dynamic keys are registered. This is also used in
0694 dynamic column copies.
0695 
0696 Both backend types return parameter vectors and covariance matrices. This is
0697 always done as `Eigen` maps, which have reference semantics into the backing
0698 storage.
0699 
0700 #### TrackContainer backend
0701 
0702 - Mutable & const
0703     - Get size
0704     - Reference parameter vector and covariance matrix access
0705     - Get if column exists
0706     - Get reference surface (returns `const Surface*`)
0707     - Get particle hypothesis (returns by-value, so allows on-the-fly creation)
0708     - Get dynamic keys
0709     - Type-erased component access
0710 - Mutable
0711     - Add and remove a track
0712     - Add column
0713     - Ensure dynamic columns, copy dynamic columns
0714     - Reserve number of entries
0715     - Set reference surface
0716     - Set particle hypothesis
0717     - Clear the container
0718 
0719 #### MultiTrajectory (track state) backend
0720 
0721 The track state container can [share components](#track_edm_component_sharing).
0722 This is implemented for the shareable components by the interface layer looking
0723 up a *component* that stores an index into a separate
0724 shareable-component-container. Then, functions exists which return `Eigen` maps
0725 into the relevant backend storage based on the index looked up as a component
0726 before.
0727 
0728 The track state container also handles *calibrated* measurement in a packed
0729 way. This is to say, that for $N$ dimensional measurement, the backend can
0730 store only the dimension $N$ and then $N$ numbers representing the measurement
0731 vector and $N\times N$ numbers for the associated covariance matrix. To enable
0732 this, a mutable backend exposes `allocateCalibrated_impl` which accepts a track
0733 state index and the measurement size, and ensures storage is available to hand
0734 out a reference into associated backing storage. An example is the transient
0735 vector backend, which stores the offset into measurement and covariance matrix
0736 vector, and ensures its size is consistent.
0737 
0738 In both cases above, the *absence* of the value can be indicated by setting the
0739 indices to the sentinel value `kInvalid`. The backend also has query methods
0740 that test if the index is invalid and return a boolean.
0741 
0742 The track state container also stores `SourceLink`s, which are assigned by
0743 value and returned by value, which allows the backend to unpack assigned source
0744 links and repackage uncalibrated measurement references back into a source link
0745 when returning.
0746 
0747 - Mutable & const
0748     - Get the calibrated measurement dimension (*size*)
0749     - Get uncalibrated source link
0750     - Get reference surface
0751     - Get parameter from *parameter* index
0752     - Get covariance from *covariance* index
0753     - Get jacobian from *jacobian* index
0754     - Get measurement vector given compile-time measurement dimension
0755     - Get measurement covariance matrix given compile-time measurement dimension
0756     - Check if an optional component is set
0757     - Get the container size
0758     - Type-erased component access
0759     - Get if column exists
0760     - Get dynamic keys
0761 
0762 - Mutable
0763     - Add a track state
0764     - Share track state components from another track state of the same container (otherwise no sharing is supported)
0765     - Unset an optional component
0766     - Clear the container
0767     - Add column
0768     - Ensure dynamic columns, copy dynamic columns
0769     - Set an uncalibrated source link that is passed by value
0770     - Set the reference surface
0771 
0772 ## Glossary
0773 
0774 :::{glossary}
0775 tip state
0776   The state at the *tip* of a trajectory, usually the *outermost* track state, on the opposite end of the {term}`stem state`.
0777 
0778 stem state
0779   The state at the *stem* of a trajectory, meaning the *innermost* track state. The opposite end is the {term}`tip state`.
0780 :::