Back to home page

EIC code displayed by LXR

 
 

    


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

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