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