Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
EigenStepperTests.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file EigenStepperTests.cpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2018-2020 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 
54 
55 #include <algorithm>
56 #include <array>
57 #include <cmath>
58 #include <functional>
59 #include <limits>
60 #include <map>
61 #include <memory>
62 #include <optional>
63 #include <string>
64 #include <tuple>
65 #include <type_traits>
66 #include <utility>
67 #include <vector>
68 
69 namespace Acts {
70 class ISurfaceMaterial;
71 class Logger;
72 } // namespace Acts
73 
74 namespace tt = boost::test_tools;
75 using namespace Acts::UnitLiterals;
77 
78 namespace Acts {
79 namespace Test {
80 
82 
83 static constexpr auto eps = 3 * std::numeric_limits<double>::epsilon();
84 
85 // Create a test context
88 
90 template <typename stepper_state_t>
91 struct PropState {
93  PropState(Direction direction, stepper_state_t sState)
94  : stepping(std::move(sState)) {
95  options.direction = direction;
96  }
98  stepper_state_t stepping;
100  struct {
101  double tolerance = 1e-4;
102  double stepSizeCutOff = 0.;
103  unsigned int maxRungeKuttaStepTrials = 10000;
105  } options;
106 };
107 
108 struct MockNavigator {};
109 
110 static constexpr MockNavigator mockNavigator;
111 
115 struct EndOfWorld {
117  double maxX = 1_m;
118 
120  EndOfWorld() = default;
121 
133  template <typename propagator_state_t, typename stepper_t,
134  typename navigator_t>
135  bool operator()(propagator_state_t& state, const stepper_t& stepper,
136  const navigator_t& /*navigator*/,
137  const Logger& /*logger*/) const {
138  const double tolerance = state.options.targetTolerance;
139  if (maxX - std::abs(stepper.position(state.stepping).x()) <= tolerance ||
140  std::abs(stepper.position(state.stepping).y()) >= 0.5_m ||
141  std::abs(stepper.position(state.stepping).z()) >= 0.5_m) {
142  return true;
143  }
144  return false;
145  }
146 };
147 
155  struct this_result {
156  // Position of the propagator after each step
157  std::vector<Vector3> position;
158  // Momentum of the propagator after each step
159  std::vector<Vector3> momentum;
160  };
161 
163 
175  template <typename propagator_state_t, typename stepper_t,
176  typename navigator_t>
177  void operator()(propagator_state_t& state, const stepper_t& stepper,
178  const navigator_t& /*navigator*/, result_type& result,
179  const Logger& /*logger*/) const {
180  result.position.push_back(stepper.position(state.stepping));
181  result.momentum.push_back(stepper.momentum(state.stepping));
182  }
183 };
184 
186 BOOST_AUTO_TEST_CASE(eigen_stepper_state_test) {
187  // Set up some variables
188  double stepSize = 123.;
189  auto bField = std::make_shared<ConstantBField>(Vector3(1., 2.5, 33.33));
190 
191  Vector3 pos(1., 2., 3.);
192  Vector3 dir(4., 5., 6.);
193  double time = 7.;
194  double absMom = 8.;
195  double charge = -1.;
196 
197  // Test charged parameters without covariance matrix
198  CurvilinearTrackParameters cp(makeVector4(pos, time), dir, charge / absMom,
199  std::nullopt, ParticleHypothesis::pion());
200  EigenStepper<>::State esState(tgContext, bField->makeCache(mfContext), cp,
201  stepSize);
202 
204 
205  // Test the result & compare with the input/test for reasonable members
206  BOOST_CHECK_EQUAL(esState.jacToGlobal, BoundToFreeMatrix::Zero());
207  BOOST_CHECK_EQUAL(esState.jacTransport, FreeMatrix::Identity());
208  BOOST_CHECK_EQUAL(esState.derivative, FreeVector::Zero());
209  BOOST_CHECK(!esState.covTransport);
210  BOOST_CHECK_EQUAL(esState.cov, Covariance::Zero());
211  BOOST_CHECK_EQUAL(esState.pathAccumulated, 0.);
212  BOOST_CHECK_EQUAL(esState.stepSize.value(), stepSize);
213  BOOST_CHECK_EQUAL(esState.previousStepSize, 0.);
214 
215  // Test without charge and covariance matrix
216  CurvilinearTrackParameters ncp(makeVector4(pos, time), dir, 1 / absMom,
217  std::nullopt, ParticleHypothesis::pion0());
218  esState = EigenStepper<>::State(tgContext, bField->makeCache(mfContext), ncp,
219  stepSize);
220  BOOST_CHECK_EQUAL(es.charge(esState), 0.);
221 
222  // Test with covariance matrix
223  Covariance cov = 8. * Covariance::Identity();
224  ncp = CurvilinearTrackParameters(makeVector4(pos, time), dir, 1 / absMom, cov,
226  esState = EigenStepper<>::State(tgContext, bField->makeCache(mfContext), ncp,
227  stepSize);
228  BOOST_CHECK_NE(esState.jacToGlobal, BoundToFreeMatrix::Zero());
229  BOOST_CHECK(esState.covTransport);
230  BOOST_CHECK_EQUAL(esState.cov, cov);
231 }
232 
235 BOOST_AUTO_TEST_CASE(eigen_stepper_test) {
236  // Set up some variables for the state
238  double stepSize = 123.;
239  auto bField = std::make_shared<ConstantBField>(Vector3(1., 2.5, 33.33));
240  auto bCache = bField->makeCache(mfContext);
241 
242  // Construct the parameters
243  Vector3 pos(1., 2., 3.);
244  Vector3 dir = Vector3(4., 5., 6.).normalized();
245  double time = 7.;
246  double absMom = 8.;
247  double charge = -1.;
248  Covariance cov = 8. * Covariance::Identity();
249  CurvilinearTrackParameters cp(makeVector4(pos, time), dir, charge / absMom,
250  cov, ParticleHypothesis::pion());
251 
252  // Build the state and the stepper
253  EigenStepper<>::State esState(tgContext, bField->makeCache(mfContext), cp,
254  stepSize);
256 
257  // Test the getters
258  CHECK_CLOSE_ABS(es.position(esState), pos, eps);
259  CHECK_CLOSE_ABS(es.direction(esState), dir, eps);
261  CHECK_CLOSE_ABS(es.charge(esState), charge, eps);
262  CHECK_CLOSE_ABS(es.time(esState), time, eps);
263  BOOST_CHECK_EQUAL(es.getField(esState, pos).value(),
264  bField->getField(pos, bCache).value());
265 
266  // Step size modifies
267  const std::string originalStepSize = esState.stepSize.toString();
268 
269  es.setStepSize(esState, -1337.);
270  BOOST_CHECK_EQUAL(esState.previousStepSize, stepSize);
271  BOOST_CHECK_EQUAL(esState.stepSize.value(), -1337.);
272 
273  es.releaseStepSize(esState);
274  BOOST_CHECK_EQUAL(esState.stepSize.value(), stepSize);
275  BOOST_CHECK_EQUAL(es.outputStepSize(esState), originalStepSize);
276 
277  // Test the curvilinear state construction
278  auto curvState = es.curvilinearState(esState);
279  auto curvPars = std::get<0>(curvState);
280  CHECK_CLOSE_ABS(curvPars.position(tgContext), cp.position(tgContext), eps);
281  CHECK_CLOSE_ABS(curvPars.momentum(), cp.momentum(), 10e-6);
282  CHECK_CLOSE_ABS(curvPars.charge(), cp.charge(), eps);
283  CHECK_CLOSE_ABS(curvPars.time(), cp.time(), eps);
284  BOOST_CHECK(curvPars.covariance().has_value());
285  BOOST_CHECK_NE(*curvPars.covariance(), cov);
286  CHECK_CLOSE_COVARIANCE(std::get<1>(curvState),
287  BoundMatrix(BoundMatrix::Identity()), eps);
288  CHECK_CLOSE_ABS(std::get<2>(curvState), 0., eps);
289 
290  // Test the update method
291  Vector3 newPos(2., 4., 8.);
292  Vector3 newMom(3., 9., 27.);
293  double newTime(321.);
294  es.update(esState, newPos, newMom.normalized(), charge / newMom.norm(),
295  newTime);
296  BOOST_CHECK_EQUAL(es.position(esState), newPos);
297  BOOST_CHECK_EQUAL(es.direction(esState), newMom.normalized());
298  BOOST_CHECK_EQUAL(es.absoluteMomentum(esState), newMom.norm());
299  BOOST_CHECK_EQUAL(es.charge(esState), charge);
300  BOOST_CHECK_EQUAL(es.time(esState), newTime);
301 
302  // The covariance transport
303  esState.cov = cov;
305  BOOST_CHECK_NE(esState.cov, cov);
306  BOOST_CHECK_NE(esState.jacToGlobal, BoundToFreeMatrix::Zero());
307  BOOST_CHECK_EQUAL(esState.jacTransport, FreeMatrix::Identity());
308  BOOST_CHECK_EQUAL(esState.derivative, FreeVector::Zero());
309 
310  // Perform a step without and with covariance transport
311  esState.cov = cov;
312  PropState ps(navDir, std::move(esState));
313 
314  ps.stepping.covTransport = false;
315  es.step(ps, mockNavigator).value();
316  CHECK_CLOSE_COVARIANCE(ps.stepping.cov, cov, eps);
317  BOOST_CHECK_NE(es.position(ps.stepping).norm(), newPos.norm());
318  BOOST_CHECK_NE(es.direction(ps.stepping), newMom.normalized());
319  BOOST_CHECK_EQUAL(es.charge(ps.stepping), charge);
320  BOOST_CHECK_LT(es.time(ps.stepping), newTime);
321  BOOST_CHECK_EQUAL(ps.stepping.derivative, FreeVector::Zero());
322  BOOST_CHECK_EQUAL(ps.stepping.jacTransport, FreeMatrix::Identity());
323 
324  ps.stepping.covTransport = true;
325  es.step(ps, mockNavigator).value();
326  CHECK_CLOSE_COVARIANCE(ps.stepping.cov, cov, eps);
327  BOOST_CHECK_NE(es.position(ps.stepping).norm(), newPos.norm());
328  BOOST_CHECK_NE(es.direction(ps.stepping), newMom.normalized());
329  BOOST_CHECK_EQUAL(es.charge(ps.stepping), charge);
330  BOOST_CHECK_LT(es.time(ps.stepping), newTime);
331  BOOST_CHECK_NE(ps.stepping.derivative, FreeVector::Zero());
332  BOOST_CHECK_NE(ps.stepping.jacTransport, FreeMatrix::Identity());
333 
335  // Construct the parameters
336  Vector3 pos2(1.5, -2.5, 3.5);
337  Vector3 dir2 = Vector3(4.5, -5.5, 6.5).normalized();
338  double time2 = 7.5;
339  double absMom2 = 8.5;
340  double charge2 = 1.;
341  BoundSquareMatrix cov2 = 8.5 * Covariance::Identity();
342  CurvilinearTrackParameters cp2(makeVector4(pos2, time2), dir2,
343  charge2 / absMom2, cov2,
346  cp2.referenceSurface(), tgContext, cp2.parameters());
347  navDir = Direction::Forward;
348  double stepSize2 = -2. * stepSize;
349 
350  auto copyState = [&](auto& field, const auto& state) {
351  using field_t = std::decay_t<decltype(field)>;
352  std::decay_t<decltype(state)> copy(tgContext, field.makeCache(mfContext),
353  cp, stepSize);
354  copy.pars = state.pars;
355  copy.covTransport = state.covTransport;
356  copy.cov = state.cov;
357  copy.jacobian = state.jacobian;
358  copy.jacToGlobal = state.jacToGlobal;
359  copy.jacTransport = state.jacTransport;
360  copy.derivative = state.derivative;
361  copy.pathAccumulated = state.pathAccumulated;
362  copy.stepSize = state.stepSize;
363  copy.previousStepSize = state.previousStepSize;
364 
365  copy.fieldCache =
366  MagneticFieldProvider::Cache::make<typename field_t::Cache>(
367  state.fieldCache.template get<typename field_t::Cache>());
368 
369  copy.geoContext = state.geoContext;
370  copy.extension = state.extension;
371  copy.auctioneer = state.auctioneer;
372  copy.stepData = state.stepData;
373 
374  return copy;
375  };
376 
377  // Reset all possible parameters
378  EigenStepper<>::State esStateCopy(copyState(*bField, ps.stepping));
379  BOOST_CHECK(cp2.covariance().has_value());
380  es.resetState(esStateCopy, cp2.parameters(), *cp2.covariance(),
381  cp2.referenceSurface(), stepSize2);
382  // Test all components
383  BOOST_CHECK_NE(esStateCopy.jacToGlobal, BoundToFreeMatrix::Zero());
384  BOOST_CHECK_NE(esStateCopy.jacToGlobal, ps.stepping.jacToGlobal);
385  BOOST_CHECK_EQUAL(esStateCopy.jacTransport, FreeMatrix::Identity());
386  BOOST_CHECK_EQUAL(esStateCopy.derivative, FreeVector::Zero());
387  BOOST_CHECK(esStateCopy.covTransport);
388  BOOST_CHECK_EQUAL(esStateCopy.cov, cov2);
389  BOOST_CHECK_EQUAL(es.position(esStateCopy),
390  freeParams.template segment<3>(eFreePos0));
391  BOOST_CHECK_EQUAL(es.direction(esStateCopy),
392  freeParams.template segment<3>(eFreeDir0).normalized());
393  BOOST_CHECK_EQUAL(es.absoluteMomentum(esStateCopy),
394  std::abs(1. / freeParams[eFreeQOverP]));
395  BOOST_CHECK_EQUAL(es.charge(esStateCopy), -es.charge(ps.stepping));
396  BOOST_CHECK_EQUAL(es.time(esStateCopy), freeParams[eFreeTime]);
397  BOOST_CHECK_EQUAL(esStateCopy.pathAccumulated, 0.);
398  BOOST_CHECK_EQUAL(esStateCopy.stepSize.value(), navDir * stepSize2);
399  BOOST_CHECK_EQUAL(esStateCopy.previousStepSize, ps.stepping.previousStepSize);
400 
401  // Reset all possible parameters except the step size
402  esStateCopy = copyState(*bField, ps.stepping);
403  es.resetState(esStateCopy, cp2.parameters(), *cp2.covariance(),
404  cp2.referenceSurface());
405  // Test all components
406  BOOST_CHECK_NE(esStateCopy.jacToGlobal, BoundToFreeMatrix::Zero());
407  BOOST_CHECK_NE(esStateCopy.jacToGlobal, ps.stepping.jacToGlobal);
408  BOOST_CHECK_EQUAL(esStateCopy.jacTransport, FreeMatrix::Identity());
409  BOOST_CHECK_EQUAL(esStateCopy.derivative, FreeVector::Zero());
410  BOOST_CHECK(esStateCopy.covTransport);
411  BOOST_CHECK_EQUAL(esStateCopy.cov, cov2);
412  BOOST_CHECK_EQUAL(es.position(esStateCopy),
413  freeParams.template segment<3>(eFreePos0));
414  BOOST_CHECK_EQUAL(es.direction(esStateCopy),
415  freeParams.template segment<3>(eFreeDir0).normalized());
416  BOOST_CHECK_EQUAL(es.absoluteMomentum(esStateCopy),
417  std::abs(1. / freeParams[eFreeQOverP]));
418  BOOST_CHECK_EQUAL(es.charge(esStateCopy), -es.charge(ps.stepping));
419  BOOST_CHECK_EQUAL(es.time(esStateCopy), freeParams[eFreeTime]);
420  BOOST_CHECK_EQUAL(esStateCopy.pathAccumulated, 0.);
421  BOOST_CHECK_EQUAL(esStateCopy.stepSize.value(),
422  std::numeric_limits<double>::max());
423  BOOST_CHECK_EQUAL(esStateCopy.previousStepSize, ps.stepping.previousStepSize);
424 
425  // Reset the least amount of parameters
426  esStateCopy = copyState(*bField, ps.stepping);
427  es.resetState(esStateCopy, cp2.parameters(), *cp2.covariance(),
428  cp2.referenceSurface());
429  // Test all components
430  BOOST_CHECK_NE(esStateCopy.jacToGlobal, BoundToFreeMatrix::Zero());
431  BOOST_CHECK_NE(esStateCopy.jacToGlobal, ps.stepping.jacToGlobal);
432  BOOST_CHECK_EQUAL(esStateCopy.jacTransport, FreeMatrix::Identity());
433  BOOST_CHECK_EQUAL(esStateCopy.derivative, FreeVector::Zero());
434  BOOST_CHECK(esStateCopy.covTransport);
435  BOOST_CHECK_EQUAL(esStateCopy.cov, cov2);
436  BOOST_CHECK_EQUAL(es.position(esStateCopy),
437  freeParams.template segment<3>(eFreePos0));
438  BOOST_CHECK_EQUAL(es.direction(esStateCopy),
439  freeParams.template segment<3>(eFreeDir0).normalized());
440  BOOST_CHECK_EQUAL(es.absoluteMomentum(esStateCopy),
441  std::abs(1. / freeParams[eFreeQOverP]));
442  BOOST_CHECK_EQUAL(es.charge(esStateCopy), -es.charge(ps.stepping));
443  BOOST_CHECK_EQUAL(es.time(esStateCopy), freeParams[eFreeTime]);
444  BOOST_CHECK_EQUAL(esStateCopy.pathAccumulated, 0.);
445  BOOST_CHECK_EQUAL(esStateCopy.stepSize.value(),
446  std::numeric_limits<double>::max());
447  BOOST_CHECK_EQUAL(esStateCopy.previousStepSize, ps.stepping.previousStepSize);
448 
450  auto plane = Surface::makeShared<PlaneSurface>(pos, dir.normalized());
452  plane, tgContext, makeVector4(pos, time), dir, charge / absMom,
454  .value();
455  esState = EigenStepper<>::State(tgContext, bField->makeCache(mfContext), cp,
456  stepSize);
457 
458  // Test the intersection in the context of a surface
459  auto targetSurface =
460  Surface::makeShared<PlaneSurface>(pos + navDir * 2. * dir, dir);
461  es.updateSurfaceStatus(esState, *targetSurface, navDir, BoundaryCheck(false));
462  CHECK_CLOSE_ABS(esState.stepSize.value(ConstrainedStep::actor), navDir * 2.,
463  eps);
464 
465  // Test the step size modification in the context of a surface
466  es.updateStepSize(esState,
467  targetSurface
468  ->intersect(esState.geoContext, es.position(esState),
469  navDir * es.direction(esState), false)
470  .closest(),
471  navDir, false);
472  CHECK_CLOSE_ABS(esState.stepSize.value(), 2., eps);
473  esState.stepSize.setUser(navDir * stepSize);
474  es.updateStepSize(esState,
475  targetSurface
476  ->intersect(esState.geoContext, es.position(esState),
477  navDir * es.direction(esState), false)
478  .closest(),
479  navDir, true);
480  CHECK_CLOSE_ABS(esState.stepSize.value(), 2., eps);
481 
482  // Test the bound state construction
483  auto boundState = es.boundState(esState, *plane).value();
484  auto boundPars = std::get<0>(boundState);
485  CHECK_CLOSE_ABS(boundPars.position(tgContext), bp.position(tgContext), eps);
486  CHECK_CLOSE_ABS(boundPars.momentum(), bp.momentum(), 1e-7);
487  CHECK_CLOSE_ABS(boundPars.charge(), bp.charge(), eps);
488  CHECK_CLOSE_ABS(boundPars.time(), bp.time(), eps);
489  BOOST_CHECK(boundPars.covariance().has_value());
490  BOOST_CHECK_NE(*boundPars.covariance(), cov);
491  CHECK_CLOSE_COVARIANCE(std::get<1>(boundState),
492  BoundMatrix(BoundMatrix::Identity()), eps);
493  CHECK_CLOSE_ABS(std::get<2>(boundState), 0., eps);
494 
495  // Transport the covariance in the context of a surface
496  es.transportCovarianceToBound(esState, *plane);
497  BOOST_CHECK_NE(esState.cov, cov);
498  BOOST_CHECK_NE(esState.jacToGlobal, BoundToFreeMatrix::Zero());
499  BOOST_CHECK_EQUAL(esState.jacTransport, FreeMatrix::Identity());
500  BOOST_CHECK_EQUAL(esState.derivative, FreeVector::Zero());
501 
502  // Update in context of a surface
504  bp.referenceSurface(), tgContext, bp.parameters());
505  freeParams.segment<3>(eFreePos0) *= 2;
506  freeParams[eFreeTime] *= 2;
507  freeParams[eFreeQOverP] *= -0.5;
508 
509  es.update(esState, freeParams, bp.parameters(), 2 * (*bp.covariance()),
510  *plane);
511  CHECK_CLOSE_OR_SMALL(es.position(esState), 2. * pos, eps, eps);
512  CHECK_CLOSE_OR_SMALL(es.direction(esState), dir, eps, eps);
513  CHECK_CLOSE_REL(es.absoluteMomentum(esState), 2 * absMom, eps);
514  BOOST_CHECK_EQUAL(es.charge(esState), -1. * charge);
515  CHECK_CLOSE_OR_SMALL(es.time(esState), 2. * time, eps, eps);
516  CHECK_CLOSE_COVARIANCE(esState.cov, Covariance(2. * cov), eps);
517 
518  // Test a case where no step size adjustment is required
519  ps.options.tolerance = 2. * 4.4258e+09;
520  double h0 = esState.stepSize.value();
521  es.step(ps, mockNavigator);
522  CHECK_CLOSE_ABS(h0, esState.stepSize.value(), eps);
523 
524  // Produce some errors
525  auto nBfield = std::make_shared<NullBField>();
526  EigenStepper<> nes(nBfield);
527  EigenStepper<>::State nesState(tgContext, nBfield->makeCache(mfContext), cp,
528  stepSize);
529  PropState nps(navDir, copyState(*nBfield, nesState));
530  // Test that we can reach the minimum step size
531  nps.options.tolerance = 1e-21;
532  nps.options.stepSizeCutOff = 1e20;
533  auto res = nes.step(nps, mockNavigator);
534  BOOST_CHECK(!res.ok());
535  BOOST_CHECK_EQUAL(res.error(), EigenStepperError::StepSizeStalled);
536 
537  // Test that the number of trials exceeds
538  nps.options.stepSizeCutOff = 0.;
539  nps.options.maxRungeKuttaStepTrials = 0.;
540  res = nes.step(nps, mockNavigator);
541  BOOST_CHECK(!res.ok());
542  BOOST_CHECK_EQUAL(res.error(), EigenStepperError::StepSizeAdjustmentFailed);
543 }
544 
553 
554 // Test case a). The DenseEnvironmentExtension should state that it is not
555 // valid in this case.
556 BOOST_AUTO_TEST_CASE(step_extension_vacuum_test) {
559  vConf.position = {0.5_m, 0., 0.};
560  vConf.length = {1_m, 1_m, 1_m};
562  conf.volumeCfg.push_back(vConf);
563  conf.position = {0.5_m, 0., 0.};
564  conf.length = {1_m, 1_m, 1_m};
565 
566  // Build detector
567  cvb.setConfig(conf);
569  tgbCfg.trackingVolumeBuilders.push_back(
570  [=](const auto& context, const auto& inner, const auto& vb) {
571  return cvb.trackingVolume(context, inner, vb);
572  });
573  TrackingGeometryBuilder tgb(tgbCfg);
574  std::shared_ptr<const TrackingGeometry> vacuum =
576 
577  // Build navigator
578  Navigator naviVac({vacuum, true, true, true});
579 
580  // Set initial parameters for the particle track
581  Covariance cov = Covariance::Identity();
582  const Vector3 startDir = makeDirectionFromPhiTheta(0_degree, 90_degree);
583  const Vector3 startMom = 1_GeV * startDir;
584  const CurvilinearTrackParameters sbtp(Vector4::Zero(), startDir, 1_e / 1_GeV,
585  cov, ParticleHypothesis::pion());
586 
587  // Create action list for surface collection
588  ActionList<StepCollector> aList;
589  AbortList<EndOfWorld> abortList;
590 
591  // Set options for propagator
593  AbortList<EndOfWorld>>
594  propOpts(tgContext, mfContext);
595  propOpts.actionList = aList;
596  propOpts.abortList = abortList;
597  propOpts.maxSteps = 100;
598  propOpts.maxStepSize = 1.5_m;
599 
600  // Build stepper and propagator
601  auto bField = std::make_shared<ConstantBField>(Vector3(0., 0., 0.));
602  EigenStepper<
605  es(bField);
609  Navigator>
610  prop(es, naviVac);
611 
612  // Launch and collect results
613  const auto& result = prop.propagate(sbtp, propOpts).value();
614  const StepCollector::this_result& stepResult =
615  result.get<typename StepCollector::result_type>();
616 
617  // Check that the propagation happened without interactions
618  for (const auto& pos : stepResult.position) {
619  CHECK_SMALL(pos.y(), 1_um);
620  CHECK_SMALL(pos.z(), 1_um);
621  if (pos == stepResult.position.back()) {
622  CHECK_CLOSE_ABS(pos.x(), 1_m, 1_um);
623  }
624  }
625  for (const auto& mom : stepResult.momentum) {
626  CHECK_CLOSE_ABS(mom, startMom, 1_keV);
627  }
628 
629  // Rebuild and check the choice of extension
630  ActionList<StepCollector> aListDef;
631 
632  // Set options for propagator
633  PropagatorOptions<ActionList<StepCollector>, AbortList<EndOfWorld>>
634  propOptsDef(tgContext, mfContext);
635  propOptsDef.actionList = aListDef;
636  propOptsDef.abortList = abortList;
637  propOptsDef.maxSteps = 100;
638  propOptsDef.maxStepSize = 1.5_m;
639 
642  propDef(esDef, naviVac);
643 
644  // Launch and collect results
645  const auto& resultDef = propDef.propagate(sbtp, propOptsDef).value();
646  const StepCollector::this_result& stepResultDef =
647  resultDef.get<typename StepCollector::result_type>();
648 
649  // Check that the right extension was chosen
650  // If chosen correctly, the number of elements should be identical
651  BOOST_CHECK_EQUAL(stepResult.position.size(), stepResultDef.position.size());
652  for (unsigned int i = 0; i < stepResult.position.size(); i++) {
653  CHECK_CLOSE_ABS(stepResult.position[i], stepResultDef.position[i], 1_um);
654  }
655  BOOST_CHECK_EQUAL(stepResult.momentum.size(), stepResultDef.momentum.size());
656  for (unsigned int i = 0; i < stepResult.momentum.size(); i++) {
657  CHECK_CLOSE_ABS(stepResult.momentum[i], stepResultDef.momentum[i], 1_keV);
658  }
659 }
660 // Test case b). The DefaultExtension should state that it is invalid here.
661 BOOST_AUTO_TEST_CASE(step_extension_material_test) {
664  vConf.position = {0.5_m, 0., 0.};
665  vConf.length = {1_m, 1_m, 1_m};
666  vConf.volumeMaterial =
667  std::make_shared<HomogeneousVolumeMaterial>(makeBeryllium());
669  conf.volumeCfg.push_back(vConf);
670  conf.position = {0.5_m, 0., 0.};
671  conf.length = {1_m, 1_m, 1_m};
672 
673  // Build detector
674  cvb.setConfig(conf);
676  tgbCfg.trackingVolumeBuilders.push_back(
677  [=](const auto& context, const auto& inner, const auto& vb) {
678  return cvb.trackingVolume(context, inner, vb);
679  });
680  TrackingGeometryBuilder tgb(tgbCfg);
681  std::shared_ptr<const TrackingGeometry> material =
683 
684  // Build navigator
685  Navigator naviMat({material, true, true, true});
686 
687  // Set initial parameters for the particle track
688  Covariance cov = Covariance::Identity();
689  const Vector3 startDir = makeDirectionFromPhiTheta(0_degree, 90_degree);
690  const Vector3 startMom = 5_GeV * startDir;
691  const CurvilinearTrackParameters sbtp(Vector4::Zero(), startDir, 1_e / 5_GeV,
692  cov, ParticleHypothesis::pion());
693 
694  // Create action list for surface collection
695  ActionList<StepCollector> aList;
696  AbortList<EndOfWorld> abortList;
697 
698  // Set options for propagator
700  AbortList<EndOfWorld>>
701  propOpts(tgContext, mfContext);
702  propOpts.actionList = aList;
703  propOpts.abortList = abortList;
704  propOpts.maxSteps = 10000;
705  propOpts.maxStepSize = 1.5_m;
706 
707  // Build stepper and propagator
708  auto bField = std::make_shared<ConstantBField>(Vector3(0., 0., 0.));
709  EigenStepper<
712  es(bField);
716  Navigator>
717  prop(es, naviMat);
718 
719  // Launch and collect results
720  const auto& result = prop.propagate(sbtp, propOpts).value();
721  const StepCollector::this_result& stepResult =
722  result.get<typename StepCollector::result_type>();
723 
724  // Check that there occurred interaction
725  for (const auto& pos : stepResult.position) {
726  CHECK_SMALL(pos.y(), 1_um);
727  CHECK_SMALL(pos.z(), 1_um);
728  if (pos == stepResult.position.front()) {
729  CHECK_SMALL(pos.x(), 1_um);
730  } else {
731  BOOST_CHECK_GT(std::abs(pos.x()), 1_um);
732  }
733  }
734  for (const auto& mom : stepResult.momentum) {
735  CHECK_SMALL(mom.y(), 1_keV);
736  CHECK_SMALL(mom.z(), 1_keV);
737  if (mom == stepResult.momentum.front()) {
738  CHECK_CLOSE_ABS(mom.x(), 5_GeV, 1_keV);
739  } else {
740  BOOST_CHECK_LT(mom.x(), 5_GeV);
741  }
742  }
743 
744  // Rebuild and check the choice of extension
745  // Set options for propagator
747  AbortList<EndOfWorld>>
748  propOptsDense(tgContext, mfContext);
749  propOptsDense.actionList = aList;
750  propOptsDense.abortList = abortList;
751  propOptsDense.maxSteps = 1000;
752  propOptsDense.maxStepSize = 1.5_m;
753 
754  // Build stepper and propagator
757  Navigator>
758  propDense(esDense, naviMat);
759 
760  // Launch and collect results
761  const auto& resultDense = propDense.propagate(sbtp, propOptsDense).value();
762  const StepCollector::this_result& stepResultDense =
763  resultDense.get<typename StepCollector::result_type>();
764 
765  // Check that the right extension was chosen
766  // If chosen correctly, the number of elements should be identical
767  BOOST_CHECK_EQUAL(stepResult.position.size(),
768  stepResultDense.position.size());
769  for (unsigned int i = 0; i < stepResult.position.size(); i++) {
770  CHECK_CLOSE_ABS(stepResult.position[i], stepResultDense.position[i], 1_um);
771  }
772  BOOST_CHECK_EQUAL(stepResult.momentum.size(),
773  stepResultDense.momentum.size());
774  for (unsigned int i = 0; i < stepResult.momentum.size(); i++) {
775  CHECK_CLOSE_ABS(stepResult.momentum[i], stepResultDense.momentum[i], 1_keV);
776  }
777 
779 
780  // Re-launch the configuration with magnetic field
781  bField->setField(Vector3{0., 1_T, 0.});
782  EigenStepper<
783  StepperExtensionList<DefaultExtension, DenseEnvironmentExtension>,
784  detail::HighestValidAuctioneer>
785  esB(bField);
787  DenseEnvironmentExtension>,
788  detail::HighestValidAuctioneer>,
789  Navigator>
790  propB(esB, naviMat);
791 
792  const auto& resultB = propB.propagate(sbtp, propOptsDense).value();
793  const StepCollector::this_result& stepResultB =
794  resultB.get<typename StepCollector::result_type>();
795 
796  // Check that there occurred interaction
797  for (const auto& pos : stepResultB.position) {
798  if (pos == stepResultB.position.front()) {
799  CHECK_SMALL(pos, 1_um);
800  } else {
801  BOOST_CHECK_GT(std::abs(pos.x()), 1_um);
802  CHECK_SMALL(pos.y(), 1_um);
803  BOOST_CHECK_GT(std::abs(pos.z()), 0.125_um);
804  }
805  }
806  for (const auto& mom : stepResultB.momentum) {
807  if (mom == stepResultB.momentum.front()) {
808  CHECK_CLOSE_ABS(mom, startMom, 1_keV);
809  } else {
810  BOOST_CHECK_NE(mom.x(), 5_GeV);
811  CHECK_SMALL(mom.y(), 1_keV);
812  BOOST_CHECK_NE(mom.z(), 0.);
813  }
814  }
815 }
816 // Test case c). Both should be involved in their part of the detector
817 BOOST_AUTO_TEST_CASE(step_extension_vacmatvac_test) {
820  vConfVac1.position = {0.5_m, 0., 0.};
821  vConfVac1.length = {1_m, 1_m, 1_m};
822  vConfVac1.name = "First vacuum volume";
824  vConfMat.position = {1.5_m, 0., 0.};
825  vConfMat.length = {1_m, 1_m, 1_m};
826  vConfMat.volumeMaterial =
827  std::make_shared<const HomogeneousVolumeMaterial>(makeBeryllium());
828  vConfMat.name = "Material volume";
830  vConfVac2.position = {2.5_m, 0., 0.};
831  vConfVac2.length = {1_m, 1_m, 1_m};
832  vConfVac2.name = "Second vacuum volume";
834  conf.volumeCfg = {vConfVac1, vConfMat, vConfVac2};
835  conf.position = {1.5_m, 0., 0.};
836  conf.length = {3_m, 1_m, 1_m};
837 
838  // Build detector
839  cvb.setConfig(conf);
841  tgbCfg.trackingVolumeBuilders.push_back(
842  [=](const auto& context, const auto& inner, const auto& vb) {
843  return cvb.trackingVolume(context, inner, vb);
844  });
845  TrackingGeometryBuilder tgb(tgbCfg);
846  std::shared_ptr<const TrackingGeometry> det = tgb.trackingGeometry(tgContext);
847 
848  // Build navigator
849  Navigator naviDet({det, true, true, true});
850 
851  // Set initial parameters for the particle track
852  CurvilinearTrackParameters sbtp(Vector4::Zero(), 0_degree, 90_degree,
853  1_e / 5_GeV, Covariance::Identity(),
855 
856  // Create action list for surface collection
857  AbortList<EndOfWorld> abortList;
858  abortList.get<EndOfWorld>().maxX = 3_m;
859 
860  // Set options for propagator
862  AbortList<EndOfWorld>>
863  propOpts(tgContext, mfContext);
864  propOpts.abortList = abortList;
865  propOpts.maxSteps = 1000;
866  propOpts.maxStepSize = 1.5_m;
867 
868  // Build stepper and propagator
869  auto bField = std::make_shared<ConstantBField>(Vector3(0., 1_T, 0.));
870  EigenStepper<
873  es(bField);
877  Navigator>
878  prop(es, naviDet);
879 
880  // Launch and collect results
881  const auto& result = prop.propagate(sbtp, propOpts).value();
882  const StepCollector::this_result& stepResult =
883  result.get<typename StepCollector::result_type>();
884 
885  // Manually set the extensions for each step and propagate through each
886  // volume by propagation to the boundaries
887  // Collect boundaries
888  std::vector<Surface const*> surs;
889  std::vector<std::shared_ptr<const BoundarySurfaceT<TrackingVolume>>>
890  boundaries = det->lowestTrackingVolume(tgContext, {0.5_m, 0., 0.})
891  ->boundarySurfaces();
892  for (auto& b : boundaries) {
893  if (b->surfaceRepresentation().center(tgContext).x() == 1_m) {
894  surs.push_back(&(b->surfaceRepresentation()));
895  break;
896  }
897  }
898  boundaries =
899  det->lowestTrackingVolume(tgContext, {1.5_m, 0., 0.})->boundarySurfaces();
900  for (auto& b : boundaries) {
901  if (b->surfaceRepresentation().center(tgContext).x() == 2_m) {
902  surs.push_back(&(b->surfaceRepresentation()));
903  break;
904  }
905  }
906  boundaries =
907  det->lowestTrackingVolume(tgContext, {2.5_m, 0., 0.})->boundarySurfaces();
908  for (auto& b : boundaries) {
909  if (b->surfaceRepresentation().center(tgContext).x() == 3_m) {
910  surs.push_back(&(b->surfaceRepresentation()));
911  break;
912  }
913  }
914 
915  // Build launcher through vacuum
916  // Set options for propagator
917 
918  PropagatorOptions<ActionList<StepCollector>, AbortList<EndOfWorld>>
919  propOptsDef(tgContext, mfContext);
920  abortList.get<EndOfWorld>().maxX = 1_m;
921  propOptsDef.abortList = abortList;
922  propOptsDef.maxSteps = 1000;
923  propOptsDef.maxStepSize = 1.5_m;
924 
925  // Build stepper and propagator
928  propDef(esDef, naviDet);
929 
930  // Launch and collect results
931  const auto& resultDef =
932  propDef.propagate(sbtp, *(surs[0]), propOptsDef).value();
933  const StepCollector::this_result& stepResultDef =
934  resultDef.get<typename StepCollector::result_type>();
935 
936  // Check the exit situation of the first volume
937  std::pair<Vector3, Vector3> endParams, endParamsControl;
938  for (unsigned int i = 0; i < stepResultDef.position.size(); i++) {
939  if (1_m - stepResultDef.position[i].x() < 1e-4) {
940  endParams =
941  std::make_pair(stepResultDef.position[i], stepResultDef.momentum[i]);
942  break;
943  }
944  }
945  for (unsigned int i = 0; i < stepResult.position.size(); i++) {
946  if (1_m - stepResult.position[i].x() < 1e-4) {
947  endParamsControl =
948  std::make_pair(stepResult.position[i], stepResult.momentum[i]);
949  break;
950  }
951  }
952 
953  CHECK_CLOSE_ABS(endParams.first, endParamsControl.first, 1_um);
954  CHECK_CLOSE_ABS(endParams.second, endParamsControl.second, 1_um);
955 
956  CHECK_CLOSE_ABS(endParams.first.x(), endParamsControl.first.x(), 1e-5);
957  CHECK_CLOSE_ABS(endParams.first.y(), endParamsControl.first.y(), 1e-5);
958  CHECK_CLOSE_ABS(endParams.first.z(), endParamsControl.first.z(), 1e-5);
959  CHECK_CLOSE_ABS(endParams.second.x(), endParamsControl.second.x(), 1e-5);
960  CHECK_CLOSE_ABS(endParams.second.y(), endParamsControl.second.y(), 1e-5);
961  CHECK_CLOSE_ABS(endParams.second.z(), endParamsControl.second.z(), 1e-5);
962 
963  // Build launcher through material
964  // Set initial parameters for the particle track by using the result of the
965  // first volume
966  CurvilinearTrackParameters sbtpPiecewise(
967  makeVector4(endParams.first, 0), endParams.second,
968  1_e / endParams.second.norm(), std::nullopt, ParticleHypothesis::pion());
969 
970  // Set options for propagator
971  DenseStepperPropagatorOptions<ActionList<StepCollector>,
972  AbortList<EndOfWorld>>
973  propOptsDense(tgContext, mfContext);
974  abortList.get<EndOfWorld>().maxX = 2_m;
975  propOptsDense.abortList = abortList;
976  propOptsDense.maxSteps = 1000;
977  propOptsDense.maxStepSize = 1.5_m;
978 
979  // Build stepper and propagator
982  Navigator>
983  propDense(esDense, naviDet);
984 
985  // Launch and collect results
986  const auto& resultDense =
987  propDense.propagate(sbtpPiecewise, *(surs[1]), propOptsDense).value();
988  const StepCollector::this_result& stepResultDense =
989  resultDense.get<typename StepCollector::result_type>();
990 
991  // Check the exit situation of the second volume
992  for (unsigned int i = 0; i < stepResultDense.position.size(); i++) {
993  if (2_m - stepResultDense.position[i].x() < 1e-4) {
994  endParams = std::make_pair(stepResultDense.position[i],
995  stepResultDense.momentum[i]);
996  break;
997  }
998  }
999  for (unsigned int i = 0; i < stepResult.position.size(); i++) {
1000  if (2_m - stepResult.position[i].x() < 1e-4) {
1001  endParamsControl =
1002  std::make_pair(stepResult.position[i], stepResult.momentum[i]);
1003  break;
1004  }
1005  }
1006 
1007  CHECK_CLOSE_ABS(endParams.first, endParamsControl.first, 1_um);
1008  CHECK_CLOSE_ABS(endParams.second, endParamsControl.second, 1_um);
1009 }
1010 
1011 // Test case a). The DenseEnvironmentExtension should state that it is not
1012 // valid in this case.
1013 BOOST_AUTO_TEST_CASE(step_extension_trackercalomdt_test) {
1014  double rotationAngle = M_PI * 0.5;
1015  Vector3 xPos(cos(rotationAngle), 0., sin(rotationAngle));
1016  Vector3 yPos(0., 1., 0.);
1017  Vector3 zPos(-sin(rotationAngle), 0., cos(rotationAngle));
1018  MaterialSlab matProp(makeBeryllium(), 0.5_mm);
1019 
1020  CuboidVolumeBuilder cvb;
1022  sConf1.position = Vector3(0.3_m, 0., 0.);
1023  sConf1.rotation.col(0) = xPos;
1024  sConf1.rotation.col(1) = yPos;
1025  sConf1.rotation.col(2) = zPos;
1026  sConf1.rBounds =
1027  std::make_shared<const RectangleBounds>(RectangleBounds(0.5_m, 0.5_m));
1028  sConf1.surMat = std::shared_ptr<const ISurfaceMaterial>(
1029  new HomogeneousSurfaceMaterial(matProp));
1030  sConf1.thickness = 1._mm;
1032  lConf1.surfaceCfg = {sConf1};
1033 
1035  sConf2.position = Vector3(0.6_m, 0., 0.);
1036  sConf2.rotation.col(0) = xPos;
1037  sConf2.rotation.col(1) = yPos;
1038  sConf2.rotation.col(2) = zPos;
1039  sConf2.rBounds =
1040  std::make_shared<const RectangleBounds>(RectangleBounds(0.5_m, 0.5_m));
1041  sConf2.surMat = std::shared_ptr<const ISurfaceMaterial>(
1042  new HomogeneousSurfaceMaterial(matProp));
1043  sConf2.thickness = 1._mm;
1045  lConf2.surfaceCfg = {sConf2};
1046 
1048  muConf1.position = {2.3_m, 0., 0.};
1049  muConf1.length = {20._cm, 20._cm, 20._cm};
1050  muConf1.volumeMaterial =
1051  std::make_shared<HomogeneousVolumeMaterial>(makeBeryllium());
1052  muConf1.name = "MDT1";
1054  muConf2.position = {2.7_m, 0., 0.};
1055  muConf2.length = {20._cm, 20._cm, 20._cm};
1056  muConf2.volumeMaterial =
1057  std::make_shared<HomogeneousVolumeMaterial>(makeBeryllium());
1058  muConf2.name = "MDT2";
1059 
1061  vConf1.position = {0.5_m, 0., 0.};
1062  vConf1.length = {1._m, 1._m, 1._m};
1063  vConf1.layerCfg = {lConf1, lConf2};
1064  vConf1.name = "Tracker";
1066  vConf2.position = {1.5_m, 0., 0.};
1067  vConf2.length = {1._m, 1._m, 1._m};
1068  vConf2.volumeMaterial =
1069  std::make_shared<HomogeneousVolumeMaterial>(makeBeryllium());
1070  vConf2.name = "Calorimeter";
1072  vConf3.position = {2.5_m, 0., 0.};
1073  vConf3.length = {1._m, 1._m, 1._m};
1074  vConf3.volumeCfg = {muConf1, muConf2};
1075  vConf3.name = "Muon system";
1077  conf.volumeCfg = {vConf1, vConf2, vConf3};
1078  conf.position = {1.5_m, 0., 0.};
1079  conf.length = {3._m, 1._m, 1._m};
1080 
1081  // Build detector
1082  cvb.setConfig(conf);
1084  tgbCfg.trackingVolumeBuilders.push_back(
1085  [=](const auto& context, const auto& inner, const auto& vb) {
1086  return cvb.trackingVolume(context, inner, vb);
1087  });
1088  TrackingGeometryBuilder tgb(tgbCfg);
1089  std::shared_ptr<const TrackingGeometry> detector =
1091 
1092  // Build navigator
1093  Navigator naviVac({detector, true, true, true});
1094 
1095  // Set initial parameters for the particle track
1096  CurvilinearTrackParameters sbtp(Vector4::Zero(), 0_degree, 90_degree,
1097  1_e / 1_GeV, Covariance::Identity(),
1099 
1100  // Set options for propagator
1102  AbortList<EndOfWorld>>
1103  propOpts(tgContext, mfContext);
1104  propOpts.abortList.get<EndOfWorld>().maxX = 3._m;
1105  propOpts.maxSteps = 10000;
1106 
1107  // Build stepper and propagator
1108  auto bField = std::make_shared<ConstantBField>(Vector3(0., 0., 0.));
1109  EigenStepper<
1112  es(bField);
1116  Navigator>
1117  prop(es, naviVac);
1118 
1119  // Launch and collect results
1120  const auto& result = prop.propagate(sbtp, propOpts).value();
1121  const StepCollector::this_result& stepResult =
1122  result.get<typename StepCollector::result_type>();
1123 
1124  // Test that momentum changes only occurred at the right detector parts
1125  double lastMomentum = stepResult.momentum[0].x();
1126  for (unsigned int i = 0; i < stepResult.position.size(); i++) {
1127  // Test for changes
1128  if ((stepResult.position[i].x() > 0.3_m &&
1129  stepResult.position[i].x() < 0.6_m) ||
1130  (stepResult.position[i].x() > 0.6_m &&
1131  stepResult.position[i].x() <= 1._m) ||
1132  (stepResult.position[i].x() > 1._m &&
1133  stepResult.position[i].x() <= 2._m) ||
1134  (stepResult.position[i].x() > 2.2_m &&
1135  stepResult.position[i].x() <= 2.4_m) ||
1136  (stepResult.position[i].x() > 2.6_m &&
1137  stepResult.position[i].x() <= 2.8_m)) {
1138  BOOST_CHECK_LE(stepResult.momentum[i].x(), lastMomentum);
1139  lastMomentum = stepResult.momentum[i].x();
1140  } else
1141  // Test the absence of momentum loss
1142  {
1143  if (stepResult.position[i].x() < 0.3_m ||
1144  (stepResult.position[i].x() > 2._m &&
1145  stepResult.position[i].x() <= 2.2_m) ||
1146  (stepResult.position[i].x() > 2.4_m &&
1147  stepResult.position[i].x() <= 2.6_m) ||
1148  (stepResult.position[i].x() > 2.8_m &&
1149  stepResult.position[i].x() <= 3._m)) {
1150  BOOST_CHECK_EQUAL(stepResult.momentum[i].x(), lastMomentum);
1151  }
1152  }
1153  }
1154 }
1155 } // namespace Test
1156 } // namespace Acts