Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MeasurementSelector.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file MeasurementSelector.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2019-2021 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #pragma once
10 
21 
22 #include <cassert>
23 #include <cstddef>
24 #include <iterator>
25 #include <limits>
26 #include <utility>
27 #include <vector>
28 
29 namespace Acts {
30 
38  std::vector<double> etaBins;
40  std::vector<double> chi2CutOff{std::numeric_limits<double>::max()};
42  std::vector<size_t> numMeasurementsCutOff{1};
43 };
44 
55  public:
62 
64  MeasurementSelector() = default;
69 
79 
80  template <typename traj_t>
81  Result<std::pair<
82  typename std::vector<typename traj_t::TrackStateProxy>::iterator,
83  typename std::vector<typename traj_t::TrackStateProxy>::iterator>>
84  select(std::vector<typename traj_t::TrackStateProxy>& candidates,
85  bool& isOutlier, const Logger& logger) const {
86  using Result = Result<std::pair<
87  typename std::vector<typename traj_t::TrackStateProxy>::iterator,
88  typename std::vector<typename traj_t::TrackStateProxy>::iterator>>;
89 
90  ACTS_VERBOSE("Invoked MeasurementSelector");
91 
92  // Return error if no measurement
93  if (candidates.empty()) {
94  return CombinatorialKalmanFilterError::MeasurementSelectionFailed;
95  }
96 
97  // Get geoID of this surface
98  auto surface = &candidates.front().referenceSurface();
99  auto geoID = surface->geometryId();
100 
101  // Find the appropriate cuts
102  auto cuts = m_config.find(geoID);
103  if (cuts == m_config.end()) {
104  // for now we consider missing cuts an unrecoverable error
105  // TODO consider other options e.g. do not add measurements at all (not
106  // even as outliers)
107  return CombinatorialKalmanFilterError::MeasurementSelectionFailed;
108  }
109 
110  assert(!cuts->chi2CutOff.empty());
111  const auto& chi2CutOff = cuts->chi2CutOff;
112  auto maxChi2Cut = *std::max_element(chi2CutOff.begin(), chi2CutOff.end());
113  double minChi2 = std::numeric_limits<double>::max();
114  size_t minIndex = 0;
115  auto trackStateIterEnd = candidates.end();
116  {
117  auto trackStateIter = candidates.begin();
118  // Loop over all measurements to select the compatible measurements
119  // Sort track states which do not satisfy the chi2 cut to the end.
120  // When done trackStateIterEnd will point to the first element that
121  // does not satisfy the chi2 cut.
122  assert(trackStateIter != trackStateIterEnd);
123  for (;;) {
124  double chi2 = calculateChi2(
125  // This abuses an incorrectly sized vector / matrix to access the
126  // data pointer! This works (don't use the matrix as is!), but be
127  // careful!
128  trackStateIter
129  ->template calibrated<
131  .data(),
132  trackStateIter
133  ->template calibratedCovariance<
135  .data(),
136  trackStateIter->predicted(), trackStateIter->predictedCovariance(),
137  trackStateIter->projector(), trackStateIter->calibratedSize());
138 
139  trackStateIter->chi2() = chi2;
140 
141  // only consider track states which pass the chi2 cut
142  if (chi2 >= maxChi2Cut ||
143  chi2 >= VariableCut<traj_t>(*trackStateIter, cuts, chi2CutOff,
144  logger)) {
145  --trackStateIterEnd;
146  // still check whether the element has the smallest chi2 in case an
147  // outlier is returned.
148  if (chi2 < minChi2) {
149  minChi2 = chi2;
150  // the current element will be swapped with the last unchecked
151  // element if they are different
152  minIndex = std::distance(candidates.begin(), trackStateIterEnd);
153  }
154 
155  if (trackStateIter == trackStateIterEnd) {
156  break;
157  } else {
158  // swap rejected element with last element in list
159  std::swap(*trackStateIter, *trackStateIterEnd);
160  }
161  } else {
162  // Search for the measurement with the min chi2
163  // @if there is a track state which passes the cut-off there is
164  // no need to remember the track state with the smallest chi2.
165  ++trackStateIter;
166  if (trackStateIter == trackStateIterEnd) {
167  break;
168  }
169  }
170  }
171  }
172 
173  // If there are no measurements below the chi2 cut off, return the
174  // measurement with the best chi2 and tag it as an outlier
175  if (candidates.begin() == trackStateIterEnd) {
176  const auto bestIt = std::next(candidates.begin(), minIndex);
177  ACTS_VERBOSE(
178  "No measurement candidate. Return an outlier measurement chi2="
179  << bestIt->chi2());
180  isOutlier = true;
181  // return single item range, no sorting necessary
182  return Result::success(std::pair{bestIt, std::next(bestIt, 1)});
183  }
184 
185  std::sort(candidates.begin(), trackStateIterEnd,
186  [](const auto& tsa, const auto& tsb) {
187  return tsa.chi2() < tsb.chi2();
188  });
189 
190  // use |eta| of best measurement to select numMeasurementsCut
191  const auto numMeasurementsCut = VariableCut<traj_t>(
192  *candidates.begin(), cuts, cuts->numMeasurementsCutOff, logger);
193 
194  if (static_cast<std::size_t>(std::distance(
195  candidates.begin(), trackStateIterEnd)) > numMeasurementsCut &&
196  numMeasurementsCut > 0) {
197  trackStateIterEnd = std::next(candidates.begin(), numMeasurementsCut);
198  }
199 
200  ACTS_VERBOSE("Number of selected measurements: "
201  << std::distance(candidates.begin(), trackStateIterEnd)
202  << ", max: " << numMeasurementsCut);
203 
204  isOutlier = false;
205  return std::pair{candidates.begin(), trackStateIterEnd};
206  }
207 
208  private:
209  template <typename traj_t, typename cut_value_t>
210  static cut_value_t VariableCut(
211  const typename traj_t::TrackStateProxy& trackState,
213  const std::vector<cut_value_t>& cuts, const Logger& logger) {
214  const auto& etaBins = selector->etaBins;
215  if (etaBins.empty()) {
216  return cuts[0]; // shortcut if no etaBins
217  }
218  const auto eta = std::atanh(std::cos(trackState.predicted()[eBoundTheta]));
219  const auto abseta = std::abs(eta);
220  size_t bin = 0;
221  for (auto etaBin : etaBins) {
222  if (etaBin >= abseta) {
223  break;
224  }
225  bin++;
226  }
227  if (bin >= cuts.size()) {
228  bin = cuts.size() - 1;
229  }
230  ACTS_VERBOSE("Variable cut for eta=" << eta << ": " << cuts[bin]);
231  return cuts[bin];
232  }
233 
234  double calculateChi2(
235  double* fullCalibrated, double* fullCalibratedCovariance,
237  false>::Parameters predicted,
239  false>::Covariance predictedCovariance,
241  false>::Projector projector,
242  unsigned int calibratedSize) const;
243 
245 };
246 
247 } // namespace Acts