EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CombinatorialKalmanFilterTests.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file CombinatorialKalmanFilterTests.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2016-2019 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 #include <boost/test/unit_test.hpp>
10 
37 
38 #include <algorithm>
39 #include <cmath>
40 #include <random>
41 #include <vector>
42 
43 using namespace Acts::UnitLiterals;
45 
46 namespace Acts {
47 namespace Test {
48 
50  size_t sourceID = 0;
51 
53 
54  bool operator==(const ExtendedMinimalSourceLink& rhs) const {
55  return meas == rhs.meas;
56  }
57 
58  const Surface& referenceSurface() const {
59  return *MeasurementHelpers::getSurface(*meas);
60  }
61 
63  return *meas;
64  }
65 };
66 
67 // helper function to create geometry ids
68 GeometryIdentifier makeId(int volume = 0, int layer = 0, int sensitive = 0) {
70  sensitive);
71 }
72 
73 // A few initialisations and definitionas
74 using SourceLink = ExtendedMinimalSourceLink;
75 using Jacobian = BoundMatrix;
77 using Resolution = std::pair<BoundIndices, double>;
78 using ElementResolution = std::vector<Resolution>;
79 using VolumeResolution = std::map<GeometryIdentifier::Value, ElementResolution>;
80 using DetectorResolution =
81  std::map<GeometryIdentifier::Value, VolumeResolution>;
82 
83 std::normal_distribution<double> gauss(0., 1.);
84 std::default_random_engine generator(42);
85 
88 
89 // Create a test context
93 
94 template <BoundIndices... params>
96 
102  MeasurementCreator() = default;
103 
106 
107  using result_type = std::vector<FittableMeasurement<SourceLink>>;
108 
116  template <typename propagator_state_t, typename stepper_t>
117  void operator()(propagator_state_t& state, const stepper_t& stepper,
118  result_type& result) const {
119  // monitor the current surface
120  auto surface = state.navigation.currentSurface;
121  if (surface and surface->associatedDetectorElement()) {
122  auto geoID = surface->geometryId();
123  auto volumeID = geoID.volume();
124  auto layerID = geoID.layer();
125  // find volume and layer information for this
126  auto vResolution = detectorResolution.find(volumeID);
127  if (vResolution != detectorResolution.end()) {
128  // find layer resolutions
129  auto lResolution = vResolution->second.find(layerID);
130  if (lResolution != vResolution->second.end()) {
131  // Apply global to local
132  auto lpResult = surface->globalToLocal(
133  state.geoContext, stepper.position(state.stepping),
134  stepper.direction(state.stepping));
135  Acts::Vector2D lPos = lpResult.value();
136  if (lResolution->second.size() == 1) {
137  double sp = lResolution->second[0].second;
138  cov1D << sp * sp;
139  double dp = sp * gauss(generator);
140  if (lResolution->second[0].first == eBoundLoc0) {
141  // push back & move a LOC_0 measurement
142  MeasurementType<eBoundLoc0> m0(surface->getSharedPtr(), {}, cov1D,
143  lPos[eBoundLoc0] + dp);
144  result.push_back(std::move(m0));
145  } else {
146  // push back & move a LOC_1 measurement
147  MeasurementType<eBoundLoc1> m1(surface->getSharedPtr(), {}, cov1D,
148  lPos[eBoundLoc1] + dp);
149  result.push_back(std::move(m1));
150  }
151  } else if (lResolution->second.size() == 2) {
152  // Create the measurment and move it
153  double sx = lResolution->second[eBoundLoc0].second;
154  double sy = lResolution->second[eBoundLoc1].second;
155  cov2D << sx * sx, 0., 0., sy * sy;
156  double dx = sx * gauss(generator);
157  double dy = sy * gauss(generator);
158  // push back & move a LOC_0, LOC_1 measurement
160  surface->getSharedPtr(), {}, cov2D, lPos[eBoundLoc0] + dx,
161  lPos[eBoundLoc1] + dy);
162  result.push_back(std::move(m01));
163  }
164  }
165  }
166  }
167  }
168 };
169 
174 BOOST_AUTO_TEST_CASE(comb_kalman_filter_zero_field) {
175  // Build detector
177  auto detector = cGeometry();
178 
179  // Build navigator for the measurement creatoin
180  Navigator mNavigator(detector);
181  mNavigator.resolvePassive = false;
182  mNavigator.resolveMaterial = true;
183  mNavigator.resolveSensitive = true;
184 
185  // Use straingt line stepper to create the measurements
186  StraightLineStepper mStepper;
187 
188  // Define the measurement propagator
189  using MeasurementPropagator = Propagator<StraightLineStepper, Navigator>;
190 
191  // Build propagator for the measurement creation
192  MeasurementPropagator mPropagator(mStepper, mNavigator);
193 
194  // Create action list for the measurement creation
195  using MeasurementActions = ActionList<MeasurementCreator>;
196  using MeasurementAborters = AbortList<EndOfWorldReached>;
197 
198  auto pixelResX = Resolution(eBoundLoc0, 25_um);
199  auto pixelResY = Resolution(eBoundLoc1, 50_um);
200  auto stripResX = Resolution(eBoundLoc0, 100_um);
201  auto stripResY = Resolution(eBoundLoc1, 150_um);
202 
203  ElementResolution pixelElementRes = {pixelResX, pixelResY};
204  ElementResolution stripElementResI = {stripResX};
205  ElementResolution stripElementResO = {stripResY};
206 
207  VolumeResolution pixelVolumeRes;
208  pixelVolumeRes[2] = pixelElementRes;
209  pixelVolumeRes[4] = pixelElementRes;
210 
211  VolumeResolution stripVolumeRes;
212  stripVolumeRes[2] = stripElementResI;
213  stripVolumeRes[4] = stripElementResO;
214  stripVolumeRes[6] = stripElementResI;
215  stripVolumeRes[8] = stripElementResO;
216 
217  DetectorResolution detRes;
218  detRes[2] = pixelVolumeRes;
219  detRes[3] = stripVolumeRes;
220 
221  // Set options for propagator
224  auto& mCreator = mOptions.actionList.get<MeasurementCreator>();
225  mCreator.detectorResolution = detRes;
226 
227  // This vector owns the measurements
228  std::multimap<size_t, FittableMeasurement<SourceLink>> measurements;
229 
230  // Make a vector of source links and further processed for KF inputs
231  std::vector<SourceLink> sourcelinks;
232 
233  // Set the starting positions for propagation
234  double eps = 15_mm;
235  std::map<size_t, Vector3D> startingPos;
236  startingPos.emplace(0, Vector3D{-3_m, 0., 0.});
237  startingPos.emplace(1, Vector3D{-3_m, -1.0 * eps, -1.0 * eps});
238  startingPos.emplace(2, Vector3D{-3_m, eps, eps});
239 
240  // Run the propagation for a few times such that multiple measurements exist
241  // on one surface
242  // Set the starting momentum for propagation
243  for (const auto& [trackID, mPos] : startingPos) {
244  Vector4D pos4 = makeVector4(mPos, 42_ns);
245  NeutralCurvilinearTrackParameters mStart(pos4, 0_degree, 90_degree,
246  1 / 1_GeV);
247  // Launch and collect - the measurements
248  auto result = mPropagator.propagate(mStart, mOptions);
249  BOOST_CHECK(result.ok());
250 
251  // Extract measurements from result of propagation.
252  auto value = std::move(result.value());
253  auto measurementsCreated = value.get<MeasurementCreator::result_type>();
254  for (auto& meas : measurementsCreated) {
255  measurements.emplace(trackID, std::move(meas));
256  }
257  }
258 
259  // Transform the measurments to sourcelinks
260  std::transform(measurements.begin(), measurements.end(),
261  std::back_inserter(sourcelinks), [](const auto& m) {
262  return SourceLink{m.first, &m.second};
263  });
264 
265  // There should be 18 source links in total
266  BOOST_CHECK_EQUAL(sourcelinks.size(), 18);
267 
268  // The CombinatorialKalmanFilter - we use the eigen stepper for covariance
269  // transport Build navigator for the measurement creatoin
270  Navigator rNavigator(detector);
271  rNavigator.resolvePassive = false;
272  rNavigator.resolveMaterial = true;
273  rNavigator.resolveSensitive = true;
274 
275  // Configure propagation with deactivated B-field
276  ConstantBField bField(Vector3D(0., 0., 0.));
277  using RecoStepper = EigenStepper<ConstantBField>;
278  RecoStepper rStepper(bField);
279  using RecoPropagator = Propagator<RecoStepper, Navigator>;
280  RecoPropagator rPropagator(rStepper, rNavigator);
281 
282  using Updater = GainMatrixUpdater;
283  using Smoother = GainMatrixSmoother;
284  using SourceLinkSelector = CKFSourceLinkSelector;
286  CombinatorialKalmanFilter<RecoPropagator, Updater, Smoother,
287  SourceLinkSelector>;
288 
289  // Implement different chi2/nSourceLinks cutoff at different detector level
290  // NB: pixel volumeID = 2, strip volumeID= 3
291  SourceLinkSelector::Config sourcelinkSelectorConfig = {
292  // global default valies
293  {makeId(), {8.0, 10}},
294  // pixel layer 2 chi2/nSourceLinks cutoff: 8.0/5
295  {makeId(2, 2), {8.0, 5}},
296  // pixel layer 4 chi2/nSourceLinks cutoff: 7.0/5
297  {makeId(2, 4), {7.0, 5}},
298  // pixel volume chi2/nSourceLinks cutoff: 7.0/5
299  {makeId(2), {7.0, 5}},
300  // strip volume chi2/nSourceLinks cutoff: 8.0/5
301  {makeId(3), {8.0, 5}},
302  };
303  CombinatorialKalmanFilter cKF(rPropagator);
304 
305  // Run the CombinaltorialKamanFitter for track finding from different starting
306  // parameter
307  for (const auto& [trackID, pos] : startingPos) {
308  // Set initial parameters for the particle track
309  Covariance cov;
310  cov << pow(10_um, 2), 0., 0., 0., 0., 0., 0., pow(10_um, 2), 0., 0., 0., 0.,
311  0., 0., pow(0.0002, 2), 0., 0., 0., 0., 0., 0., pow(0.0002, 2), 0., 0.,
312  0., 0., 0., 0., 0.0001, 0., 0., 0., 0., 0., 0., 1.;
313  Vector3D rPos =
314  pos + Vector3D{0, 10_um * gauss(generator), 10_um * gauss(generator)};
315  double rPhi = 0_degree + 0.0002 * gauss(generator);
316  double rTheta = 90_degree + 0.0002 * gauss(generator);
317  CurvilinearTrackParameters rStart(makeVector4(rPos, 42_ns), rPhi, rTheta,
318  1_GeV, 1_e, cov);
319 
320  const Surface* rSurface = &rStart.referenceSurface();
321 
322  auto logger =
323  getDefaultLogger("CombinatorialKalmanFilter", Logging::VERBOSE);
325  tgContext, mfContext, calContext, sourcelinkSelectorConfig,
326  LoggerWrapper{*logger}, PropagatorPlainOptions(), rSurface);
327 
328  // Found the track(s)
329  auto combKalmanFilterRes = cKF.findTracks(sourcelinks, rStart, ckfOptions);
330  BOOST_CHECK(combKalmanFilterRes.ok());
331 
332  auto foundTrack = *combKalmanFilterRes;
333  auto& fittedStates = foundTrack.fittedStates;
334  auto& trackTips = foundTrack.trackTips;
335 
336  for (const auto& tip : trackTips) {
337  std::vector<size_t> sourceIds;
338  fittedStates.visitBackwards(tip, [&](const auto& trackState) {
339  sourceIds.push_back(trackState.uncalibrated().sourceID);
340  });
341 
342  BOOST_CHECK_EQUAL(sourceIds.size(), 6);
343 
344  size_t numFakeHit = 0;
345  for (const auto& id : sourceIds) {
346  numFakeHit = numFakeHit + (id != trackID ? 1 : 0);
347  }
348 
349  // Check if there are fake hits from other tracks
350  BOOST_CHECK_EQUAL(numFakeHit, 0);
351  }
352  }
353 }
354 
355 } // namespace Test
356 } // namespace Acts