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 
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