ATLAS Offline Software
Loading...
Searching...
No Matches
MeasurementSelector.h
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3 */
4#pragma once
5// Alternative measurement selector
6//
7// This measurement selector is assuming the following
8// - the number of selected measurements is small (~<10)
9// - the number of measurement candidates is typically >> the
10// number of selected measuremebts
11// - the total number of candidate measurements can be large
12// - there is a simple one-to-one relation between bound state
13// parameters and measurement coordinates.
14
15#include "Acts/Utilities/Result.hpp"
16#include "Acts/Utilities/Delegate.hpp"
17#include "Acts/EventData/SourceLink.hpp"
18#include "Acts/TrackFinding/CombinatorialKalmanFilterError.hpp"
19#include "Acts/Surfaces/Surface.hpp"
20#include "Acts/Geometry/GeometryHierarchyMap.hpp"
21#include "Acts/EventData/Types.hpp"
22#include "Acts/EventData/TrackStatePropMask.hpp"
23#include "boost/container/small_vector.hpp"
24
25// for BaseTypes
26#include "Acts/EventData/TrackStateProxy.hpp"
27#include "Acts/Utilities/CalibrationContext.hpp"
28#include "Acts/EventData/BoundTrackParameters.hpp"
29
30// for MeasurementSizeMax
31#include "Acts/EventData/MultiTrajectory.hpp"
32
35
36#include <utility>
37#include <type_traits>
38#include <optional>
39
40// Types to be used during measurement selection for the prediction and the
41// measurement for calibrated measurements after selection if the actual calibration is
42// executed after the calibration, and the trajectory, track state, bound parameter
43// types.
44template <typename derived_t>
46{
47 // the measurement type after the selection e.g. a Matrix<N,1>
48 template <std::size_t N>
49 using CalibratedMeasurement = typename Acts::detail_tsp::FixedSizeTypes<N>::Coefficients;
50
51 // the measurement covariance type after the selection e.g. a Matrix<N,N>
52 template <std::size_t N>
53 using CalibratedMeasurementCovariance = typename Acts::detail_tsp::FixedSizeTypes<N>::Covariance;
54
55 // the measurement type before the selection e.g. an Eigen::Map< Matrix<N,1> > if
56 // the calibration is performed after the selection
57 template <std::size_t N>
58 using PreSelectionMeasurement = typename Acts::detail_tsp::FixedSizeTypes<N>::Coefficients;
59
60 // the measurement covariance type before the selection e.g. an Eigen::Map<Matrix<N,N> > if
61 // the calibration is performed after the selection
62 template <std::size_t N>
63 using PreSelectionMeasurementCovariance = typename Acts::detail_tsp::FixedSizeTypes<N>::Covariance;
64
65 // e.g. the same as CalibratedMeasurement
66 template <std::size_t N>
67 using Predicted = typename Acts::detail_tsp::FixedSizeTypes<N>::Coefficients;
68
69 // e.g. the same as CalibratedMeasurementCovariance
70 template <std::size_t N>
71 using PredictedCovariance = typename Acts::detail_tsp::FixedSizeTypes<N>::Covariance;
72
73 // e.g. helper template to get the value_type from the container type
74 // e.g. helper template to get the value_type from the measurement range iterator type
75
76 template <typename T_MeasurementRangeIterator>
78 using value_type = typename T_MeasurementRangeIterator::value_type;
79 };
80
81 // abstract measurement range
82 // implements empty()
83 // and can be converted by the derived_t into an iterable range
84 // over measurements of concrete measurement container_type.
85 using abstract_measurement_range_t = std::ranges::iota_view<unsigned int, unsigned int>;
86
87 // the trajectory type to which states for selected measurements are to be added
88 using trajectory_t = typename derived_t::traj_t;
89 // the track state type for new track states
90 using TrackStateProxy = trajectory_t::TrackStateProxy;
91
92 // the value type usd for matrices
93 using MatrixFloatType = double;
94 using BoundTrackParameters = Acts::BoundTrackParameters;
95 using BoundMatrix = Acts::BoundMatrix;
96
97 using BoundState = std::tuple<BoundTrackParameters, BoundMatrix, double>;
98
99 // maximum dimension of measurements
100 static const std::size_t s_dimMax = 3;
101
102 // must be the same as what is used for the CKF
103 // @TODO how to get rid of this ?
104 static constexpr std::size_t s_maxBranchesPerSurface = 10;
105};
106
107//
111 template <class T_Matrix>
112 static constexpr std::size_t matrixColumns() { return T_Matrix::ColsAtCompileTime;}
113 template <class T_Matrix>
114 static constexpr std::size_t matrixRows() { return T_Matrix::RowsAtCompileTime;}
115
116 template <typename T_Float, class T_Matrix>
117 static auto matrixTypeCast(const T_Matrix &matrix) { return matrix.template cast<T_Float>(); }
118
119 template <class T_Matrix>
120 static auto transpose(const T_Matrix &matrix) { return matrix.transpose(); }
121
122 template <class T_Matrix>
123 static auto invert(const T_Matrix &matrix) { return matrix.inverse(); }
124};
125
126
127// Map from the measurement to bound state domain
128// it is assumed that there is a simple unambiguous association
129// between coordinates of the measurement domain and the bound state domain
130// e.g. measurement coordinate 1 maps to loc0 of the bound state.
131// @TODO use FixedSizeSubspace ? currently does not provide methods to directly
132// create the a sub-space covariance matrix from a full covariance matrix,
133// and to create the projector bitset, which is stored in the TrackState.
135
136 template <std::size_t N>
137 using type = std::array<unsigned char, N>;
138
139 template <std::size_t N>
140 static constexpr type<N> identity() {
141 type<N> ret;
142 for(int i=0; i<N; ++i) {
143 ret[i]=i;
144 }
145 return ret;
146 }
147};
148
149// utility to "project" a bound state parameter vector or covariance matrix onto the 1,2, ... N dimensional measurement domain
150// @TODO allow to influence resulting matrix type ?
151template <std::size_t N,class T_ResultType,class T_Matrix>
152T_ResultType project(ParameterMapping::type<N> parameter_map, const T_Matrix &matrix)
153{
154 using MatrixIndexMapType = unsigned char; // "char" to reduce the size of the map, and if not wide enough this entire
155 // concept is likely inefficient.
156 using MatrixIndexType = unsigned int; // @TODO or std::size_t ? does not matter
157
158 // ensure that index types are wide enough
159 static_assert( MeasurementSelectorMatrixTraits::matrixRows<T_Matrix>() < std::numeric_limits<MatrixIndexMapType>::max());
160 static_assert( N*MeasurementSelectorMatrixTraits::matrixRows<T_Matrix>() < std::numeric_limits<MatrixIndexType>::max());
161
162 T_ResultType ret;
164 // handle projection of paramteter vector
165 for (MatrixIndexType meas_i=0; meas_i<N; ++meas_i) {
166 assert( meas_i < parameter_map.size() );
167 ret(meas_i,0) = matrix( parameter_map[meas_i], 0);
168 }
169 }
170 else {
171 // handle projection of covariance matrix
172 // "project" matrix
173 for (MatrixIndexType meas_i=0; meas_i<N; ++meas_i) {
174 assert( meas_i < parameter_map.size());
175 MatrixIndexType param_i = parameter_map[meas_i];
176 for (MatrixIndexType meas_j=0; meas_j<N; ++meas_j) {
177 assert( meas_j < parameter_map.size());
178 ret(meas_i,meas_j) = matrix(param_i, parameter_map[meas_j]);
179 }
180 }
181 }
182 return ret;
183}
184
185
186
187
188// helper method to compute a chi2 for the difference of two "measurement" and covariance pairs
189template <typename measurement_vector_t, typename measurement_cov_matrix_t,
190 typename predicted_vector_t, typename predicted_cov_matrix_t>
191double computeChi2(const measurement_vector_t &a,
192 const measurement_cov_matrix_t &a_cov,
193 const predicted_vector_t &b,
194 const predicted_cov_matrix_t &b_cov) {
195
196 // just sum sanity checks that a and b have the correct dimensions and that
197 // the chi2 can actually be computed.
199 static_assert( MeasurementSelectorMatrixTraits::matrixColumns<predicted_vector_t>() == 1); // b is vector
210
211 // @TODO remove abstraction i.e. assume matrix has the interface of an eigen matrix
212 auto inv_ab_cov( MeasurementSelectorMatrixTraits::invert(a_cov+b_cov) );
213 auto diff( a-b);
214 return (MeasurementSelectorMatrixTraits::transpose(diff) * inv_ab_cov * diff)(0,0);
215}
216
217// Collection to hold the n-"best" candidates
218// The objects of type PayloadType must support assignment operation, and must
219// be default constructible. Moreover it must be possible to provide a
220// "comparison" operator to order the payload objects.
221template <std::size_t N, class PayloadType >
223 using IndexType = unsigned short; // @TODO or char ? If N>>10 this concept is likely
224 // inefficient
225 // using PayloadType = Payload<DIM>;
226 TopCollection(std::size_t max_n) {
227 init(max_n);
228 }
229
230 // @param max_n the maximum number of top-candidates is fixed by the template parameter
231 // N but can be reduced further to this number
232 void init(std::size_t max_n) {
233 assert( max_n < N);
234 m_nextSlot=0;
235 m_maxSlots=max_n;
236 m_order[0]=0;
237 }
238 // @param get a slot to hold a new candidate which is not necessarily accepted in the list
239 // of the n-top candidates
240 PayloadType &slot() {
241 return m_slots[m_order[m_nextSlot] ];
242 }
243 // @param idx get the specified filled slot (read only) indicated by the index, where the index does not
244 // indicate the order in the top-candidate list
245 const PayloadType &getSlot(IndexType idx) const {
246 return m_slots[idx];
247 }
248 // @param idx get the specified filled slot indicated by the index, where the index does not
249 // indicate the order in the top-candidate list
250 PayloadType &getSlot(IndexType idx) {
251 return m_slots[idx];
252 }
253 // @param test whether the given index points to one of the accepted top candidates
254 bool isValid(IndexType idx) const {
255 return idx < m_nextSlot;
256 }
257 // Accept the element of the latest slot provided there is still a free slot or it is better than
258 // the worst element.
259 // @param comparison operator to compute e.g. a<b where "smaller" means "better"
260 void acceptAndSort(std::function<bool(const PayloadType &a, const PayloadType &b)> comparison) {
261 // bubble best element to top
262 for (unsigned int slot_i = m_nextSlot;
263 slot_i-- > 0
264 && !comparison(m_slots[ m_order[slot_i] ],m_slots[ m_order[slot_i+1] ]);) {
265 std::swap(m_order[slot_i],m_order[slot_i+1]);
266 }
267 // if there are still free slot increase the number of used slots
268 if (m_nextSlot < m_maxSlots) {
269 ++m_nextSlot;
271 }
272 }
273 // Accept the element of the latest slot provided there is still a free slot.
275 // if there are still free slot increase the number of used slots
276 if (m_nextSlot < m_maxSlots) {
277 ++m_nextSlot;
279 }
280 return (m_nextSlot < m_maxSlots);
281 }
282
283 bool empty() const {
284 return m_nextSlot==0;
285 }
286
287 IndexType size() const { return m_nextSlot; }
288
289 // helper to iterate over the slot indices in sorting order from best to worst
290 typename std::array<IndexType, N+1>::const_iterator begin() { return m_order.begin(); }
291 typename std::array<IndexType, N+1>::const_iterator end() { return m_order.begin()+m_nextSlot; }
292
293 std::array<PayloadType, N+1> m_slots; // storage for the slots
294 std::array<IndexType, N+1> m_order; // order of the filled slots
295 IndexType m_nextSlot = 0; // the index of the next free slot
296 IndexType m_maxSlots = 0; // maximum number of top-slots
297};
298
299
300
304 using Chi2CutsPerDim = std::array<std::pair<float, float>, 4>;
305
307 std::vector<float> etaBins{};
309 std::vector<Chi2CutsPerDim> chi2CutOff{ Chi2CutsPerDim{ { {15,25}, {15,25}, {15,25}, {15,25} } } };
311 std::vector<std::size_t> numMeasurementsCutOff{1};
313 std::optional<Acts::BoundaryTolerance> edgeTolerance{};
314};
315
316// Measurement type specific measirement selector
317// Assumptions:
318// - begin and end source_link_iterator point to a contiguous range of measurements in a single container
319// - all measurements in this range have the same dimensionality.
320// - the mapping of the bound parameters to the measurement domain for each measurement in this range
321// is identical
322template <std::size_t NMeasMax,
323 std::size_t DIMMAX,
324 typename derived_t >
326
327 using Config = Acts::GeometryHierarchyMap<AtlasMeasurementSelectorCuts>;
329
331
332protected:
337
338 const derived_t &derived() const { return *static_cast<const derived_t *>(this); }
339
340 // helper to create a projector bitset from a map from bound parameters to coordinates
342 template <std::size_t N>
343 static
344 Acts::ProjectorBitset create(const ParameterMapping::type<N> &parameter_map) {
345 constexpr std::size_t nrows = Acts::kMeasurementSizeMax;
346 constexpr std::size_t ncols = Acts::eBoundSize;
347
348 std::bitset<nrows * ncols> proj_bitset {};
349
350 for (unsigned int col_i=0; col_i<N; ++col_i) {
351 unsigned int row_i = parameter_map[col_i];
352 unsigned int idx = col_i *nrows + row_i; // @TODO handle row major and column major correctly
353 proj_bitset[ (nrows * ncols - 1) - idx ] = 1;
354 }
355 return proj_bitset.to_ullong();
356 }
357 };
358
359 // helper to create states without the calibrated measurement, covariance, and sourcelink
360 // if more than one state is created the states after the first will share
361 // the jacobi and the prediction, if outlier_states is true no storage is created
362 // for filtered.
363 // @TODO should there be the possibility to have multiple states which mix outlier and non-outlier states ?
364 static void createStates(std::size_t n_new_states,
365 const T_BoundState& boundState,
366 std::size_t prevTip,
367 trajectory_t& trajectory,
368 const Acts::BoundSubspaceIndices& subspaceIndices,
369 boost::container::small_vector< typename TrackStateProxy::IndexType, s_maxBranchesPerSurface> &track_states,
370 const Acts::Logger& logger,
371 bool outlier_states) {
372 // create track states for all compatible measurement candidates
373 const auto &boundParams = derived_t::boundParams(boundState);
374 const auto &pathLength = derived_t::pathLength(boundState);
375
376 using PM = Acts::TrackStatePropMask;
377
378 track_states.reserve( n_new_states );
379 std::optional<TrackStateProxy> firstTrackState{
380 std::nullopt};
381 for (unsigned int state_i=0; state_i<n_new_states; ++state_i) {
382 PM mask = PM::Predicted | PM::Filtered | PM::Jacobian | PM::Calibrated;
383
384 if (firstTrackState.has_value()) {
385 // subsequent track states don't need storage for these as they will
386 // be shared
387 mask &= ~PM::Predicted & ~PM::Jacobian;
388 }
389
390 if (outlier_states) {
391 // outlier won't have separate filtered parameters
392 mask &= ~PM::Filtered;
393 }
394
395 TrackStateProxy trackState = trajectory.makeTrackState(mask, prevTip);
396 ACTS_VERBOSE("Create SourceLink output track state #"
397 << trackState.index() << " with mask: " << mask);
398
399 if (firstTrackState.has_value()) {
400 trackState.shareFrom(*firstTrackState, PM::Predicted);
401 trackState.shareFrom(*firstTrackState, PM::Jacobian);
402 }
403 else {
404 // only set these for first
405 trackState.predicted() = boundParams.parameters();
406 if (boundParams.covariance()) {
407 trackState.predictedCovariance() = *boundParams.covariance();
408 }
409 trackState.jacobian() = derived_t::boundJacobiMatrix(boundState);
410 firstTrackState = trackState;
411 }
412 trackState.pathLength() = pathLength;
413
414 trackState.setReferenceSurface(boundParams.referenceSurface().getSharedPtr());
415
416 trackState.setProjectorSubspaceIndices(subspaceIndices);
417
418 auto typeFlags = trackState.typeFlags();
419 if (trackState.referenceSurface().surfaceMaterial() != nullptr) {
420 typeFlags.setHasMaterial();
421 }
422 typeFlags.setHasParameters();
423
424 // @TODO these track states still need some additional processing. Should there be a special
425 // flag for this ?
426 track_states.push_back( trackState.index());
427 }
428 }
429
430
431 struct Flags {
432 bool isSplitHit() const { return ActsTrk::detail::testTrackStateFlag(Acts::TrackStateFlag::IsSplitHit, flags);}
433 bool isOutlier() const { return ActsTrk::detail::testTrackStateFlag(Acts::TrackStateFlag::IsOutlier, flags);}
434 operator unsigned int() { return flags; }
435 unsigned int flags;
436 };
437
438 template <typename T1, typename T2>
439 struct PairWithFlags : std::tuple<T1,T2,unsigned int> {
440 PairWithFlags<T1,T2> &operator=(std::tuple<T1,T2,unsigned int> &&a) {
441 std::tuple<T1,T2,unsigned int>::operator=(std::move(a));
442 return *this;
443 }
444
445 const T1 &position() const { return std::get<0>(*this); }
446 const T2 &covariance() const { return std::get<1>(*this); }
447 Flags flags() const { return Flags{ std::get<2>(*this) }; }
448 };
449
450 unsigned int makeOutlierFlag(bool value) { return static_cast<unsigned int>(value)<<2u; }
451 // utility struct to temporarily store data about the best measurements
452 template <std::size_t DIM, typename T_SourceLink>
454 using MeasCovPair = PairWithFlags < typename MeasurementSelectorTraits<derived_t>::template PreSelectionMeasurement<DIM>,
455 typename MeasurementSelectorTraits<derived_t>::template PreSelectionMeasurementCovariance<DIM> >;
457 float m_chi2{};
458 std::optional<T_SourceLink> m_sourceLink;
459 };
460
461 // type and dimension specific function to select measurements from the range defined by the source link iterators.
462 // will iterate over the contiguous measurement range defined by the source link iterators where the measurements
463 // are contained in the given container. The selection lopp will get the measurement and covariance with the
464 // help of a preCalibrator. select measurements based on smallest chi2 wrt. the prediction, then optionally
465 // apply a full calibrator after the selection and finally create track states.
466 template <std::size_t DIM, typename T_MeasurementRange>
467 Acts::Result<boost::container::small_vector< typename TrackStateProxy::IndexType, s_maxBranchesPerSurface> >
468 selectMeasurementsCreateTrackStates(const Acts::GeometryContext& geometryContext,
469 const Acts::CalibrationContext& calibrationContext,
470 const Acts::Surface& surface,
471 const T_BoundState& boundState,
472 T_MeasurementRange &&measurement_range,
473 std::size_t prevTip,
474 trajectory_t& trajectory,
475 const Acts::Logger& logger,
476 const std::size_t numMeasurementsCut,
477 const std::pair<float,float>& maxChi2Cut,
478 bool forced) const {
479 Acts::Result<boost::container::small_vector< typename TrackStateProxy::IndexType, s_maxBranchesPerSurface> >
480 result = boost::container::small_vector< typename TrackStateProxy::IndexType, s_maxBranchesPerSurface>{};
481
482 using iterator_t = decltype(measurement_range.begin());
483 using container_value_t = typename MeasurementSelectorTraits<derived_t>::template MeasurementContainerTraits<iterator_t>::value_type;
484 using BaseElementType = std::remove_cv_t<std::remove_pointer_t< container_value_t > >;
485 // get calibrator
486 using TheMatchingMeasurement = MatchingMeasurement<DIM, container_value_t >;
487 using MeasCovPair = TheMatchingMeasurement::MeasCovPair;
488
489 using Predicted = typename MeasurementSelectorTraits<derived_t>::template Predicted<DIM>;
490 using PredictedCovariance = typename MeasurementSelectorTraits<derived_t>::template PredictedCovariance<DIM>;
491
492
493
494 // get prediction in the measurement domain
495 Acts::SubspaceIndices<DIM> parameter_map = derived().template parameterMap<DIM>(geometryContext,
496 calibrationContext,
497 surface);
498 auto predicted
499 = std::make_pair( project<DIM, Predicted>(parameter_map,
500 derived().boundParams(boundState).parameters()),
502 derived().boundParams(boundState).covariance().value()));
503
504 // select n measurents with the smallest chi2.
505 auto preCalibrator = derived().template preCalibrator<DIM, BaseElementType>();
506 auto postCalibrator = derived().template postCalibrator<DIM, BaseElementType>();
507 TopCollection<NMeasMax, TheMatchingMeasurement > selected_measurements(numMeasurementsCut);
508 for ( const auto &measurement : measurement_range ) {
509 TheMatchingMeasurement &matching_measurement=selected_measurements.slot();
510 if (forced && postCalibrator) {
511 // skip preCalibrator and computeChi2, by doing everything else. We start with what's done if no preCalibrator.
512 const auto &m = derived().forwardToCalibrator(measurement);
513 matching_measurement.m_measurement = std::make_tuple( m.template localPosition<DIM>(), m.template localCovariance<DIM>(), 0u );
514 matching_measurement.m_chi2 = 0.0f;
515 matching_measurement.m_sourceLink=measurement;
516 if (!selected_measurements.acceptNoSort()) break;
517 } else {
518 matching_measurement.m_measurement = preCalibrator(geometryContext,
519 calibrationContext,
520 surface,
521 derived().forwardToCalibrator(measurement),
522 derived().boundParams(boundState));
523 matching_measurement.m_chi2 = computeChi2(matching_measurement.m_measurement.position(),
524 matching_measurement.m_measurement.covariance(),
525 predicted.first,
526 predicted.second);
527 // only consider measurements which pass the outlier chi2 cut
528 if (matching_measurement.m_chi2<maxChi2Cut.second) {
529 matching_measurement.m_sourceLink=measurement;
530 selected_measurements.acceptAndSort([](const TheMatchingMeasurement &a,
531 const TheMatchingMeasurement &b) {
532 return a.m_chi2 < b.m_chi2;
533 });
534 }
535 }
536 }
537
538 if (selected_measurements.empty()) {
539 auto config_for_surface = m_config.find(surface.geometryId());
540 if (config_for_surface != m_config.end()) {
541 if (config_for_surface->edgeTolerance.has_value()) {
542 // if the prediction failes the bound test, where the edgeTolerance is expected to be negative
543 // then the "hole" is considered to be an edge hole which is not treated as a hole.
544 auto local_coords = derived().boundParams(boundState).parameters().template block<2,1>(0,0);
545 if (!surface.insideBounds(local_coords,config_for_surface->edgeTolerance.value())) {
546 result = result.failure(Acts::CombinatorialKalmanFilterError::NoMeasurementExpected);
547 }
548 }
549 }
550 }
551 else {
552
553 // apply final calibration to n-best measurements
554 // First Create states without setting information about the calibrated measurement for the selected measurements
555 // @TODO first create state then copy measurements, or crete state by state and set measurements ?
556 // the lastter has the "advantage" that the outlier flag can be set individually
557 // the former has the advantage that part of the state creation code is independent of the
558 // the measurement.
559
560 Acts::BoundSubspaceIndices boundSubspaceIndices{};
561 std::copy(parameter_map.begin(), parameter_map.end(), boundSubspaceIndices.begin());
562 createStates( selected_measurements.size(),
563 boundState,
564 prevTip,
565 trajectory,
566 boundSubspaceIndices,
567 *result,
568 logger,
569 (!selected_measurements.empty()
570 ? selected_measurements.getSlot( *(selected_measurements.begin())).m_measurement.flags().isOutlier() || postCalibrator
571 : false) );
572 assert( result->size() == selected_measurements.size() );
573
574 if (postCalibrator) {
575 // either run post selection calibrator which will set the calibrated position and covariance
576 // directly to the state.
577 auto predicted_pos_dbl = predicted.first.template cast<double>();
578 auto predicted_cov_dbl = predicted.second.template cast<double>();
579 unsigned int state_i=0u;
581 idx: selected_measurements) {
582 assert( state_i < result->size());
583 TrackStateProxy trackState( trajectory.getTrackState( (*result)[state_i] ) );
584 TheMatchingMeasurement &a_selected_measurement = selected_measurements.getSlot(idx);
585 trackState.setUncalibratedSourceLink(ActsTrk::detail::MeasurementCalibratorBase::pack(std::move(a_selected_measurement.m_sourceLink.value())));
586
587 // apply the calibration
588 postCalibrator(geometryContext,
589 calibrationContext,
590 derived().forwardToCalibrator(a_selected_measurement.m_sourceLink.value()),
591 trackState
592 );
593 // update chi2 using calibrated measurement
594 trackState.chi2() = computeChi2(trackState.template calibrated<DIM>(),
595 trackState.template calibratedCovariance<DIM>(),
596 predicted_pos_dbl,
597 predicted_cov_dbl);
598 // ... and set outlier flag
599 // and share or create storage for filtered.
600 bool is_outlier = !forced && trackState.chi2() >= maxChi2Cut.first;
601 if (is_outlier) {
602 trackState.typeFlags().setIsOutlier();
603 trackState.shareFrom(trackState, Acts::TrackStatePropMask::Predicted,
604 Acts::TrackStatePropMask::Filtered);
605 } else {
606 trackState.typeFlags().setIsMeasurement();
607 trackState.addComponents(Acts::TrackStatePropMask::Filtered);
608 }
609 ++state_i;
610 }
611 }
612 else {
613 // or copy the pre-selection calibrated position and covariance to the
614 // track states and adjust the flags.
615 unsigned int state_i=0u;
617 idx: selected_measurements) {
618 assert( state_i < result->size());
619 TrackStateProxy trackState( trajectory.getTrackState( (*result)[state_i] ) );
620 TheMatchingMeasurement &a_selected_measurement = selected_measurements.getSlot(idx);
621
622 bool is_outlier = !forced && a_selected_measurement.m_chi2 >= maxChi2Cut.first;
623 auto type_flags = trackState.typeFlags();
624 if (is_outlier) {
625 // the filtered parameters will not be computed for outliers, so has to be shared from the prediction.
626 type_flags.setIsOutlier();
627 trackState.shareFrom(trackState, Acts::TrackStatePropMask::Predicted,
628 Acts::TrackStatePropMask::Filtered);
629 } else {
630 // for measurements the filtered parameters will be computed, so space needs to be allocated.
631 type_flags.setIsMeasurement();
632 trackState.addComponents(Acts::TrackStatePropMask::Filtered);
633 }
634 if (a_selected_measurement.m_measurement.flags().isSplitHit()) {
635 type_flags.setIsSplitHit();
636 }
637
638 trackState.setUncalibratedSourceLink(ActsTrk::detail::MeasurementCalibratorBase
639 ::pack(std::move(a_selected_measurement.m_sourceLink.value())));
640
641 trackState.allocateCalibrated(DIM);
642 trackState.template calibrated<DIM>()
644 ::MatrixFloatType>(a_selected_measurement.m_measurement.position());
645 trackState.template calibratedCovariance<DIM>()
647 ::MatrixFloatType>(a_selected_measurement.m_measurement.covariance());
648 trackState.chi2() = a_selected_measurement.m_chi2;
649 ++state_i;
650 }
651 }
652 }
653
654 return result;
655 }
656
657 template <typename parameters_t>
658 static std::size_t getEtaBin(const parameters_t& boundParameters,
659 const std::vector<float> &etaBins) {
660 if (etaBins.empty()) {
661 return 0u; // shortcut if no etaBins
662 }
663 const float eta = std::abs(std::atanh(std::cos(boundParameters.parameters()[Acts::eBoundTheta])));
664 std::size_t bin = 0;
665 for (auto etaBin : etaBins) {
666 if (etaBin >= eta) {
667 break;
668 }
669 bin++;
670 }
671 return bin;
672 }
673
674public:
675 // get numMeasurement and maxChi2 cuts for the given surface
676 // @TODO should the cuts just depend on the surface or really on the bound parameters (i.e. bound eta)
677 std::tuple< std::size_t, AtlasMeasurementSelectorCuts::Chi2CutsPerDim >
678 getCuts(const Acts::Surface& surface,
679 const T_BoundState& boundState,
680 const Acts::Logger& logger) const {
681 std::tuple< std::size_t, AtlasMeasurementSelectorCuts::Chi2CutsPerDim > result;
682 std::size_t &numMeasurementsCut = std::get<0>(result);
683 AtlasMeasurementSelectorCuts::Chi2CutsPerDim &maxChi2Cut = std::get<1>(result);
684 // Get geoID of this surface
685 auto geoID = surface.geometryId();
686 // Find the appropriate cuts
687 auto cuts = m_config.find(geoID);
688 if (cuts == m_config.end()) {
689 // indicates failure
690 numMeasurementsCut = 0;
691 }
692 else {
693 // num measurement Cut
694 // getchi2 cut
695 std::size_t eta_bin = getEtaBin(derived().boundParams(boundState), cuts->etaBins);
696 numMeasurementsCut = (!cuts->numMeasurementsCutOff.empty()
697 ? cuts->numMeasurementsCutOff[ std::min(cuts->numMeasurementsCutOff.size()-1, eta_bin) ]
698 : NMeasMax);
699 if (!cuts->chi2CutOff.empty()) {
700 maxChi2Cut = cuts->chi2CutOff[ std::min(cuts->chi2CutOff.size()-1, eta_bin) ];
701 }
702 else {
703 maxChi2Cut.fill(std::make_pair(std::numeric_limits<float>::max(),
704 std::numeric_limits<float>::max()));
705 }
706 ACTS_VERBOSE("Get cut for eta-bin="
707 << (eta_bin < cuts->etaBins.size() ? cuts->etaBins[eta_bin] : std::numeric_limits<float>::max())
708 << ": chi2 (max,max-outlier) for 1D " << maxChi2Cut[1].first << ", " << maxChi2Cut[1].second
709 << ", 2D " << maxChi2Cut[2].first << ", " << maxChi2Cut[2].second
710 << " max.meas." << numMeasurementsCut);
711 }
712 return result;
713 }
714};
715
716// Measurement selector which calls a type specific selection method
717// the measurement selector expects a list of measurement containers to be provided
718// by the source link iterators as well as the index of the container to be used for the
719// given measurement range, which is defined by the source link iterators.
720// the measurement range must refer to a contiguous range of measurements contained
721// within a single measurement container.
722// The "container" itself is a variant of particular measurement containers with associated
723// dimension i.e. number of coordinates per measurement. A member funcion specific to one of
724// the alternatives of the variant will be called to perform the selection and track state
725// creation.
726template <std::size_t NMeasMax,
727 typename derived_t,
728 typename measurement_container_variant_t >
729struct MeasurementSelectorWithDispatch : public MeasurementSelectorBase< NMeasMax, MeasurementSelectorTraits<derived_t>::s_dimMax, derived_t> {
730
732 using T_BoundState = typename Base::T_BoundState;
733 using trajectory_t = typename Base::trajectory_t;
734 using TrackStateProxy = typename Base::TrackStateProxy;
735 static constexpr std::size_t s_maxBranchesPerSurface = Base::s_maxBranchesPerSurface;
736
737 // helper to get the maximum number of measurement diemsions.
738 static constexpr std::size_t dimMax() {
740 }
741
742 Acts::Result<boost::container::small_vector< typename TrackStateProxy::IndexType, s_maxBranchesPerSurface> >
743 createTrackStates(const Acts::GeometryContext& geometryContext,
744 const Acts::CalibrationContext& calibrationContext,
745 const Acts::Surface& surface,
746 const T_BoundState& boundState,
747 typename TrackStateProxy::IndexType prevTip,
748 [[maybe_unused]] std::vector<TrackStateProxy>& trackStateCandidates,
749 trajectory_t& trajectory,
750 const Acts::Logger& logger) const {
751
752 Acts::Result<boost::container::small_vector< typename TrackStateProxy::IndexType, s_maxBranchesPerSurface> >
753 result = Acts::CombinatorialKalmanFilterError::MeasurementSelectionFailed;
754 // get associated measurement container and the relevant measurement range for the given surface.
755 auto [a_measurement_container_variant_ptr, range, forced] = this->derived().containerAndRange(surface);
756 if (!forced && !this->derived().expectMeasurements( surface, a_measurement_container_variant_ptr, range)) {
757 result = result.failure(Acts::CombinatorialKalmanFilterError::NoMeasurementExpected);
758 return result;
759 }
760 if (!range.empty()) {
761 auto [numMeasurementsCut, maxChi2Cut] = this->getCuts(surface,boundState, logger);
762 // numMeasurementsCut is == 0 in case getCuts failed
763 // anyway cannot select anything if numMeasurementsCut==0;
764 if (numMeasurementsCut>0) {
765
766 result = std::visit( [this,
767 &geometryContext,
768 &calibrationContext,
769 &surface,
770 &boundState,
771 &range,
772 prevTip,
773 &trajectory,
774 &logger,
775 numMeasurementsCut,
776 &maxChi2Cut,
777 forced] (const auto &measurement_container_with_dimension) {
778 using ArgType = std::remove_cv_t<std::remove_reference_t< decltype(measurement_container_with_dimension) > >;
779 constexpr std::size_t DIM = ArgType::dimension();
780 static_assert(DIM < std::tuple_size_v<AtlasMeasurementSelectorCuts::Chi2CutsPerDim>);
781 auto measurement_range = this->derived().rangeForContainer(measurement_container_with_dimension,range);
782 return
783 this->template
785 calibrationContext,
786 surface,
787 boundState,
788 std::move(measurement_range),
789 prevTip,
790 trajectory,
791 logger,
792 numMeasurementsCut,
793 maxChi2Cut[DIM],
794 forced);
795 },
796 *a_measurement_container_variant_ptr);
797 }
798 }
799 else {
800 result = Acts::Result<boost::container::small_vector< typename TrackStateProxy::IndexType, s_maxBranchesPerSurface> >::success({});
801 }
802 return result;
803 }
804};
805
806template <std::size_t NMeasMax,
807 typename derived_t,
808 typename measurement_container_variant_t>
809struct MeasurementSelectorBaseImpl : public MeasurementSelectorWithDispatch<NMeasMax, derived_t, measurement_container_variant_t> {
813
814 // get the bound parameters and covariance of the bound state
816 return std::get<0>(boundState);
817 }
818
819 // get the jacobi matrix of the bound state
821 return std::get<1>(boundState);
822 }
823 // get the accumulated path length of the bound state
824 static double pathLength(const T_BoundState &boundState) {
825 return std::get<2>(boundState);
826 }
827
828 // perform simple transformation to cerate the type
829 // that is passed to the calibrator.
830 // by default pass the measurement by reference not pointer
831 template <typename T>
832 static const auto &forwardToCalibrator(const T &a) {
833 if constexpr( std::is_same<T, std::remove_pointer_t<T> >::value ) {
834 return a;
835 }
836 else {
837 return *a;
838 }
839 }
840
841
842 // get mapping between bound state parameters and coordinates in the measurement domain
843 // in most cases this is just the identity operation e.g. loc0 -> coord0 and loc1 -> coord1
844 // i.e. map[0]=0; map[1]=1;
845 // @TODO should this be measurement type specific, or is dimension and the surface good enough ?
846 template <std::size_t DIM>
848 parameterMap(const Acts::GeometryContext&,
849 const Acts::CalibrationContext&,
850 const Acts::Surface&) {
852 }
853
855 // the types used for the calibrated measurement and covariance
856 template <std::size_t DIM>
857 using Measurement = typename MeasurementSelectorTraits<derived_t>::template CalibratedMeasurement<DIM>;
858 template <std::size_t DIM>
859 using MeasurementCovariance = typename MeasurementSelectorTraits<derived_t>::template CalibratedMeasurementCovariance<DIM>;
860 };
861
862 // By default the methods postCalibrator and preCalibrator return delegates:
863 template <std::size_t DIM, typename measurement_t>
864 using PreCalibrator = Acts::Delegate<
865 std::tuple < typename MeasurementSelectorTraits<derived_t>::template PreSelectionMeasurement<DIM>,
866 typename MeasurementSelectorTraits<derived_t>::template PreSelectionMeasurementCovariance<DIM>,
867 unsigned int>
868 (const Acts::GeometryContext&,
869 const Acts::CalibrationContext&,
870 const Acts::Surface&,
871 const measurement_t &,
873
874 // Since the measurement types used during measurement selection and after measurement selection
875 // might be different so are the types of the calibrator delegates
876 template <std::size_t DIM, typename measurement_t>
877 using PostCalibrator = Acts::Delegate<
878 void
879 (const Acts::GeometryContext&,
880 const Acts::CalibrationContext&,
881 const measurement_t &,
882 TrackStateProxy &)>;
883
887 template <std::size_t DIM, typename measurement_t>
889 postCalibrator() const; // not implemented
890
891 // the "calibrator" which is used during the measuremnt selection
893 // this delegate must be connected or be a valid functor. If not this likely will lead to an
894 // exception or seg fault.
895 template <std::size_t DIM, typename measurement_t>
897 preCalibrator() const; // not implemented
898
899
904 std::tuple<const measurement_container_variant_t &, abstract_measurement_range_t>
905 containerAndRange(const Acts::Surface &surface) const; // not implemented
906
911 bool expectMeasurements([[maybe_unused]] const Acts::Surface &surface,
912 [[maybe_unused]] const measurement_container_variant_t *container_variant_ptr,
913 const abstract_measurement_range_t &abstract_range) const;
914
919 template <typename measurement_container_t>
920 static auto
921 rangeForContainer(const measurement_container_t &concrete_container, const abstract_measurement_range_t &abstract_range);
922};
Scalar eta() const
pseudorapidity method
static Double_t a
double computeChi2(const measurement_vector_t &a, const measurement_cov_matrix_t &a_cov, const predicted_vector_t &b, const predicted_cov_matrix_t &b_cov)
T_ResultType project(ParameterMapping::type< N > parameter_map, const T_Matrix &matrix)
size_t size() const
Number of registered mappings.
void diff(const TrigCaloCluster &a, const TrigCaloCluster &b, std::map< std::string, double > &varChange)
Base class providing the boiler code to fill the Acts multi trajectory track states.
static Acts::SourceLink pack(const Ptr_t &measurement)
Pack the measurement type pointer to an Acts::SourceLink including the intermediate conversion into a...
static Root::TMsgLogger logger("iLumiCalc")
constexpr bool testTrackStateFlag(Acts::TrackStateFlag flag, unsigned int flags)
void swap(ElementLinkVector< DOBJ > &lhs, ElementLinkVector< DOBJ > &rhs)
std::vector< std::size_t > numMeasurementsCutOff
Maximum number of associated measurements on a single surface.
std::vector< Chi2CutsPerDim > chi2CutOff
Maximum local chi2 contribution for each eta bin.
std::optional< Acts::BoundaryTolerance > edgeTolerance
Optional (expected negative) boundary tolerance to label edge holes as no measurement expected.
std::array< std::pair< float, float >, 4 > Chi2CutsPerDim
Maximum local chi2 contribution (measurement, outlier) for each measurement dimension.
std::vector< float > etaBins
bins in |eta| to specify variable selections
typename MeasurementSelectorTraits< derived_t >::template CalibratedMeasurementCovariance< DIM > MeasurementCovariance
typename MeasurementSelectorTraits< derived_t >::template CalibratedMeasurement< DIM > Measurement
MeasurementSelectorTraits< AtlasMeasurementSelector< NMeasMax, traj_t, measurement_container_variant_t > >::abstract_measurement_range_t abstract_measurement_range_t
static const MeasurementSelectorTraits< derived_t >::BoundMatrix & boundJacobiMatrix(const T_BoundState &boundState)
MeasurementSelectorTraits< AtlasMeasurementSelector< NMeasMax, traj_t, measurement_container_variant_t > >::BoundState T_BoundState
bool expectMeasurements(const Acts::Surface &surface, const measurement_container_variant_t *container_variant_ptr, const abstract_measurement_range_t &abstract_range) const
const PreCalibrator< DIM, measurement_t > & preCalibrator() const
const PostCalibrator< DIM, measurement_t > & postCalibrator() const
the calibrator used after the measurement selection which does not have to be "connected"
static double pathLength(const T_BoundState &boundState)
std::tuple< const measurement_container_variant_t &, abstract_measurement_range_t > containerAndRange(const Acts::Surface &surface) const
Get container and index range for measurements on the given surface.
static const auto & forwardToCalibrator(const T &a)
MeasurementSelectorTraits< AtlasMeasurementSelector< NMeasMax, traj_t, measurement_container_variant_t > >::TrackStateProxy TrackStateProxy
Acts::Delegate< void(const Acts::GeometryContext &, const Acts::CalibrationContext &, const measurement_t &, TrackStateProxy &)> PostCalibrator
Acts::Delegate< std::tuple< typename MeasurementSelectorTraits< AtlasMeasurementSelector< NMeasMax, traj_t, measurement_container_variant_t > >::template PreSelectionMeasurement< DIM >, typename MeasurementSelectorTraits< AtlasMeasurementSelector< NMeasMax, traj_t, measurement_container_variant_t > >::template PreSelectionMeasurementCovariance< DIM >, unsigned int >(const Acts::GeometryContext &, const Acts::CalibrationContext &, const Acts::Surface &, const measurement_t &, const typename MeasurementSelectorTraits< AtlasMeasurementSelector< NMeasMax, traj_t, measurement_container_variant_t > >::BoundTrackParameters &)> PreCalibrator
static const MeasurementSelectorTraits< derived_t >::BoundTrackParameters & boundParams(const T_BoundState &boundState)
static auto rangeForContainer(const measurement_container_t &concrete_container, const abstract_measurement_range_t &abstract_range)
Create a range over elements of the given container from an abstract range.
ParameterMapping::type< DIM > parameterMap(const Acts::GeometryContext &, const Acts::CalibrationContext &, const Acts::Surface &)
PairWithFlags< typename MeasurementSelectorTraits< derived_t >::template PreSelectionMeasurement< DIM >, typename MeasurementSelectorTraits< derived_t >::template PreSelectionMeasurementCovariance< DIM > > MeasCovPair
PairWithFlags< T1, T2 > & operator=(std::tuple< T1, T2, unsigned int > &&a)
static Acts::ProjectorBitset create(const ParameterMapping::type< N > &parameter_map)
const derived_t & derived() const
MeasurementSelectorTraits< derived_t > traits
unsigned int makeOutlierFlag(bool value)
static void createStates(std::size_t n_new_states, const T_BoundState &boundState, std::size_t prevTip, trajectory_t &trajectory, const Acts::BoundSubspaceIndices &subspaceIndices, boost::container::small_vector< typename TrackStateProxy::IndexType, s_maxBranchesPerSurface > &track_states, const Acts::Logger &logger, bool outlier_states)
std::tuple< std::size_t, AtlasMeasurementSelectorCuts::Chi2CutsPerDim > getCuts(const Acts::Surface &surface, const T_BoundState &boundState, const Acts::Logger &logger) const
static std::size_t getEtaBin(const parameters_t &boundParameters, const std::vector< float > &etaBins)
typename MeasurementSelectorTraits< derived_t >::trajectory_t trajectory_t
Acts::Result< boost::container::small_vector< typename TrackStateProxy::IndexType, s_maxBranchesPerSurface > > selectMeasurementsCreateTrackStates(const Acts::GeometryContext &geometryContext, const Acts::CalibrationContext &calibrationContext, const Acts::Surface &surface, const T_BoundState &boundState, T_MeasurementRange &&measurement_range, std::size_t prevTip, trajectory_t &trajectory, const Acts::Logger &logger, const std::size_t numMeasurementsCut, const std::pair< float, float > &maxChi2Cut, bool forced) const
typename MeasurementSelectorTraits< derived_t >::BoundState T_BoundState
typename MeasurementSelectorTraits< derived_t >::TrackStateProxy TrackStateProxy
Acts::GeometryHierarchyMap< AtlasMeasurementSelectorCuts > Config
static constexpr std::size_t matrixRows()
static constexpr std::size_t matrixColumns()
matrix adapter for Eigen additionally need +,- and *
static auto transpose(const T_Matrix &matrix)
static auto matrixTypeCast(const T_Matrix &matrix)
static auto invert(const T_Matrix &matrix)
typename T_MeasurementRangeIterator::value_type value_type
typename Acts::detail_tsp::FixedSizeTypes< N >::Covariance PredictedCovariance
Acts::BoundTrackParameters BoundTrackParameters
typename Acts::detail_tsp::FixedSizeTypes< N >::Coefficients CalibratedMeasurement
typename Acts::detail_tsp::FixedSizeTypes< N >::Coefficients PreSelectionMeasurement
trajectory_t::TrackStateProxy TrackStateProxy
typename Acts::detail_tsp::FixedSizeTypes< N >::Coefficients Predicted
typename Acts::detail_tsp::FixedSizeTypes< N >::Covariance PreSelectionMeasurementCovariance
std::ranges::iota_view< unsigned int, unsigned int > abstract_measurement_range_t
typename Acts::detail_tsp::FixedSizeTypes< N >::Covariance CalibratedMeasurementCovariance
static constexpr std::size_t s_maxBranchesPerSurface
typename derived_t::traj_t trajectory_t
static const std::size_t s_dimMax
std::tuple< BoundTrackParameters, BoundMatrix, double > BoundState
Acts::Result< boost::container::small_vector< typename TrackStateProxy::IndexType, s_maxBranchesPerSurface > > createTrackStates(const Acts::GeometryContext &geometryContext, const Acts::CalibrationContext &calibrationContext, const Acts::Surface &surface, const T_BoundState &boundState, typename TrackStateProxy::IndexType prevTip, std::vector< TrackStateProxy > &trackStateCandidates, trajectory_t &trajectory, const Acts::Logger &logger) const
typename Base::trajectory_t trajectory_t
MeasurementSelectorBase< NMeasMax, MeasurementSelectorTraits< derived_t >::s_dimMax, derived_t > Base
static constexpr std::size_t dimMax()
typename Base::T_BoundState T_BoundState
static constexpr std::size_t s_maxBranchesPerSurface
typename Base::TrackStateProxy TrackStateProxy
static constexpr type< N > identity()
std::array< unsigned char, N > type
std::array< PayloadType, N+1 > m_slots
std::array< IndexType, N+1 >::const_iterator begin()
std::array< IndexType, N+1 > m_order
const PayloadType & getSlot(IndexType idx) const
unsigned short IndexType
IndexType size() const
PayloadType & slot()
TopCollection(std::size_t max_n)
bool isValid(IndexType idx) const
void acceptAndSort(std::function< bool(const PayloadType &a, const PayloadType &b)> comparison)
std::array< IndexType, N+1 >::const_iterator end()
PayloadType & getSlot(IndexType idx)
void init(std::size_t max_n)