Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
MultiStepperTests.cpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file MultiStepperTests.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 
35 
36 #include <algorithm>
37 #include <array>
38 #include <cmath>
39 #include <cstddef>
40 #include <memory>
41 #include <optional>
42 #include <stdexcept>
43 #include <tuple>
44 #include <type_traits>
45 #include <utility>
46 #include <vector>
47 
48 namespace Acts {
50 } // namespace Acts
51 
52 using namespace Acts;
53 using namespace Acts::VectorHelpers;
54 
56 // Some useful global objects, typedefs and structs
60 
61 using MultiStepperLoop =
64 
65 const double defaultStepSize = 123.;
67 
68 const auto defaultBField =
69  std::make_shared<ConstantBField>(Vector3(1., 2.5, 33.33));
70 const auto defaultNullBField = std::make_shared<NullBField>();
71 
73 
74 struct Options {
75  double tolerance = 1e-4;
76  double stepSizeCutOff = 0.0;
77  std::size_t maxRungeKuttaStepTrials = 10;
78  Direction direction = defaultNDir;
80 };
81 
82 struct MockNavigator {};
83 
84 static constexpr MockNavigator mockNavigator;
85 
86 struct Navigation {};
87 
88 template <typename stepper_state_t>
90  stepper_state_t &stepping;
94 
95  DummyPropState(Direction direction, stepper_state_t &ss)
96  : stepping(ss),
97  options(Options{}),
100  options.direction = direction;
101  }
102 };
103 
104 template <typename T>
105 using components_t = typename T::components;
106 
107 // Makes random bound parameters and covariance and a plane surface at {0,0,0}
108 // with normal {1,0,0}. Optionally some external fixed bound parameters can be
109 // supplied
110 auto makeDefaultBoundPars(bool cov = true, std::size_t n = 4,
111  std::optional<BoundVector> ext_pars = std::nullopt) {
112  std::vector<std::tuple<double, BoundVector, std::optional<BoundSquareMatrix>>>
113  cmps;
114  using Opt = std::optional<BoundSquareMatrix>;
115 
116  auto make_random_sym_matrix = []() {
117  auto c = BoundSquareMatrix::Random().eval();
118  c *= c.transpose();
119  return c;
120  };
121 
122  for (auto i = 0ul; i < n; ++i) {
123  cmps.push_back({1. / n, ext_pars ? *ext_pars : BoundVector::Random(),
124  cov ? Opt{make_random_sym_matrix()} : Opt{}});
125  }
126 
127  auto surface = Acts::Surface::makeShared<Acts::PlaneSurface>(
128  Vector3::Zero(), Vector3{1., 0., 0.});
129 
131 }
132 
136 template <typename multi_stepper_t, bool Cov>
138  using MultiState = typename multi_stepper_t::State;
139  using MultiStepper = multi_stepper_t;
140 
141  constexpr std::size_t N = 4;
142  const auto multi_pars = makeDefaultBoundPars(Cov, N, BoundVector::Ones());
143 
144  MultiState state(geoCtx, magCtx, defaultBField, multi_pars, defaultStepSize);
145 
146  MultiStepper ms(defaultBField);
147 
148  BOOST_CHECK_EQUAL(N, ms.numberComponents(state));
149 
150  // Test the result & compare with the input/test for reasonable members
151  auto const_iterable = ms.constComponentIterable(state);
152  for (const auto cmp : const_iterable) {
153  BOOST_CHECK_EQUAL(cmp.jacTransport(), FreeMatrix::Identity());
154  BOOST_CHECK_EQUAL(cmp.derivative(), FreeVector::Zero());
155  if constexpr (not Cov) {
156  BOOST_CHECK_EQUAL(cmp.jacToGlobal(), BoundToFreeMatrix::Zero());
157  BOOST_CHECK_EQUAL(cmp.cov(), BoundSquareMatrix::Zero());
158  }
159  }
160 
161  BOOST_CHECK_EQUAL(state.pathAccumulated, 0.);
162  for (const auto cmp : const_iterable) {
163  BOOST_CHECK_EQUAL(cmp.pathAccumulated(), 0.);
164  }
165 
166  // covTransport in the MultiEigenStepperLoop is redundant and
167  // thus not part of the interface. However, we want to check them for
168  // consistency.
169  if constexpr (Acts::Concepts::exists<components_t, MultiState>) {
170  BOOST_CHECK(not state.covTransport);
171  for (const auto &cmp : state.components) {
172  BOOST_CHECK(cmp.state.covTransport == Cov);
173  }
174  }
175 }
176 
177 BOOST_AUTO_TEST_CASE(multi_stepper_state_no_cov) {
178  test_multi_stepper_state<MultiStepperLoop, false>();
179 }
180 
181 template <typename multi_stepper_t>
183  using MultiState = typename multi_stepper_t::State;
184 
185  // Empty component vector
186  const auto multi_pars = makeDefaultBoundPars(false, 0);
187 
188  BOOST_CHECK_THROW(
189  MultiState(geoCtx, magCtx, defaultBField, multi_pars, defaultStepSize),
190  std::invalid_argument);
191 }
192 
193 BOOST_AUTO_TEST_CASE(multi_eigen_stepper_state_invalid) {
194  test_multi_stepper_state_invalid<MultiStepperLoop>();
195 }
196 
198 // Compare the Multi-Stepper against the Eigen-Stepper for consistency
200 template <typename multi_stepper_t>
202  using MultiState = typename multi_stepper_t::State;
203  using MultiStepper = multi_stepper_t;
204 
205  const BoundVector pars = BoundVector::Ones();
206  const BoundSquareMatrix cov = BoundSquareMatrix::Identity();
207 
208  std::vector<std::tuple<double, BoundVector, std::optional<BoundSquareMatrix>>>
209  cmps(4, {0.25, pars, cov});
210 
211  auto surface = Acts::Surface::makeShared<Acts::PlaneSurface>(
212  Vector3::Zero(), Vector3::Ones().normalized());
213 
214  MultiComponentBoundTrackParameters multi_pars(surface, cmps,
216  BoundTrackParameters single_pars(surface, pars, cov, particleHypothesis);
217 
218  MultiState multi_state(geoCtx, magCtx, defaultBField, multi_pars,
220  SingleStepper::State single_state(geoCtx, defaultBField->makeCache(magCtx),
221  single_pars, defaultStepSize);
222 
223  MultiStepper multi_stepper(defaultBField);
224  SingleStepper single_stepper(defaultBField);
225 
226  for (auto cmp : multi_stepper.componentIterable(multi_state)) {
227  cmp.status() = Acts::Intersection3D::Status::reachable;
228  }
229 
230  // Do some steps and check that the results match
231  for (int i = 0; i < 10; ++i) {
232  // Single stepper
233  auto single_prop_state = DummyPropState(defaultNDir, single_state);
234  auto single_result = single_stepper.step(single_prop_state, mockNavigator);
235  single_stepper.transportCovarianceToCurvilinear(single_state);
236 
237  // Multi stepper;
238  auto multi_prop_state = DummyPropState(defaultNDir, multi_state);
239  auto multi_result = multi_stepper.step(multi_prop_state, mockNavigator);
240  multi_stepper.transportCovarianceToCurvilinear(multi_state);
241 
242  // Check equality
243  BOOST_REQUIRE(multi_result.ok() == true);
244  BOOST_REQUIRE(multi_result.ok() == single_result.ok());
245 
246  BOOST_CHECK_EQUAL(*single_result, *multi_result);
247 
248  for (const auto cmp : multi_stepper.constComponentIterable(multi_state)) {
249  BOOST_CHECK_EQUAL(cmp.pars(), single_state.pars);
250  BOOST_CHECK_EQUAL(cmp.cov(), single_state.cov);
251  BOOST_CHECK_EQUAL(cmp.jacTransport(), single_state.jacTransport);
252  BOOST_CHECK_EQUAL(cmp.jacToGlobal(), single_state.jacToGlobal);
253  BOOST_CHECK_EQUAL(cmp.derivative(), single_state.derivative);
254  BOOST_CHECK_EQUAL(cmp.pathAccumulated(), single_state.pathAccumulated);
255  }
256  }
257 }
258 
259 BOOST_AUTO_TEST_CASE(multi_eigen_vs_single_eigen) {
260  test_multi_stepper_vs_eigen_stepper<MultiStepperLoop>();
261 }
262 
264 // Test stepsize accessors
266 
267 // TODO do this later, when we introduce the MultiEigenStepperSIMD, which there
268 // needs new interfaces...
269 
271 // Test the modifying accessors to the components
273 template <typename multi_stepper_t>
275  using MultiState = typename multi_stepper_t::State;
276  using MultiStepper = multi_stepper_t;
277 
278  const auto multi_pars = makeDefaultBoundPars();
279 
280  MultiState mutable_multi_state(geoCtx, magCtx, defaultBField, multi_pars,
282  const MultiState const_multi_state(geoCtx, magCtx, defaultBField, multi_pars,
284 
285  MultiStepper multi_stepper(defaultBField);
286 
287  auto modify = [&](const auto &projector) {
288  // Here test the mutable overloads of the mutable iterable
289  for (auto cmp : multi_stepper.componentIterable(mutable_multi_state)) {
290  using type = std::decay_t<decltype(projector(cmp))>;
291  if constexpr (std::is_enum_v<type>) {
292  projector(cmp) =
293  static_cast<type>(static_cast<int>(projector(cmp)) + 1);
294  } else {
295  projector(cmp) *= 2.0;
296  }
297  }
298  };
299 
300  auto check = [&](const auto &projector) {
301  // Here test the const-member functions of the mutable iterable
302  auto mutable_state_iterable =
303  multi_stepper.componentIterable(mutable_multi_state);
304  // Here test the const iterable
305  auto const_state_iterable =
306  multi_stepper.constComponentIterable(const_multi_state);
307 
308  auto mstate_it = mutable_state_iterable.begin();
309  auto cstate_it = const_state_iterable.begin();
310  for (; cstate_it != const_state_iterable.end(); ++mstate_it, ++cstate_it) {
311  const auto mstate_cmp = *mstate_it;
312  auto cstate_cmp = *cstate_it;
313 
314  using type = std::decay_t<decltype(projector(mstate_cmp))>;
315 
316  if constexpr (std::is_arithmetic_v<type>) {
317  BOOST_CHECK_CLOSE(projector(mstate_cmp), 2.0 * projector(cstate_cmp),
318  1.e-8);
319  } else if constexpr (std::is_enum_v<type>) {
320  BOOST_CHECK_EQUAL(static_cast<int>(projector(mstate_cmp)),
321  1 + static_cast<int>(projector(cstate_cmp)));
322  } else {
323  BOOST_CHECK(
324  projector(mstate_cmp).isApprox(2.0 * projector(cstate_cmp), 1.e-8));
325  }
326  }
327  };
328 
329  const auto projectors = std::make_tuple(
330  [](auto &cmp) -> decltype(auto) { return cmp.status(); },
331  [](auto &cmp) -> decltype(auto) { return cmp.pathAccumulated(); },
332  [](auto &cmp) -> decltype(auto) { return cmp.weight(); },
333  [](auto &cmp) -> decltype(auto) { return cmp.pars(); },
334  [](auto &cmp) -> decltype(auto) { return cmp.cov(); },
335  [](auto &cmp) -> decltype(auto) { return cmp.jacTransport(); },
336  [](auto &cmp) -> decltype(auto) { return cmp.derivative(); },
337  [](auto &cmp) -> decltype(auto) { return cmp.jacobian(); },
338  [](auto &cmp) -> decltype(auto) { return cmp.jacToGlobal(); });
339 
340  std::apply(
341  [&](const auto &...projs) {
342  // clang-format off
343  ( [&]() { modify(projs); check(projs); }(), ...);
344  // clang-format on
345  },
346  projectors);
347 }
348 
349 BOOST_AUTO_TEST_CASE(multi_eigen_component_iterable_with_modification) {
350  test_components_modifying_accessors<MultiStepperLoop>();
351 }
352 
354 // Test if the surface status update works
356 template <typename multi_stepper_t>
358  using MultiState = typename multi_stepper_t::State;
359  using MultiStepper = multi_stepper_t;
360 
361  auto start_surface = Acts::Surface::makeShared<Acts::PlaneSurface>(
362  Vector3::Zero(), Vector3{1.0, 0.0, 0.0});
363 
364  auto right_surface = Acts::Surface::makeShared<Acts::PlaneSurface>(
365  Vector3{1.0, 0.0, 0.0}, Vector3{1.0, 0.0, 0.0});
366 
367  std::vector<std::tuple<double, BoundVector, std::optional<BoundSquareMatrix>>>
368  cmps(2, {0.5, BoundVector::Zero(), std::nullopt});
369  std::get<BoundVector>(cmps[0])[eBoundTheta] = M_PI_2;
370  std::get<BoundVector>(cmps[1])[eBoundTheta] = -M_PI_2;
371  std::get<BoundVector>(cmps[0])[eBoundQOverP] = 1.0;
372  std::get<BoundVector>(cmps[1])[eBoundQOverP] = 1.0;
373 
374  MultiComponentBoundTrackParameters multi_pars(start_surface, cmps,
376 
377  BOOST_REQUIRE(std::get<1>(multi_pars[0])
378  .direction()
379  .isApprox(Vector3{1.0, 0.0, 0.0}, 1.e-10));
380  BOOST_REQUIRE(std::get<1>(multi_pars[1])
381  .direction()
382  .isApprox(Vector3{-1.0, 0.0, 0.0}, 1.e-10));
383 
384  MultiState multi_state(geoCtx, magCtx, defaultNullBField, multi_pars,
386  SingleStepper::State single_state(
387  geoCtx, defaultNullBField->makeCache(magCtx), std::get<1>(multi_pars[0]),
389 
390  MultiStepper multi_stepper(defaultNullBField);
391  SingleStepper single_stepper(defaultNullBField);
392 
393  // Update surface status and check
394  {
395  auto status = multi_stepper.updateSurfaceStatus(multi_state, *right_surface,
396  Direction::Forward, false);
397 
398  BOOST_CHECK(status == Intersection3D::Status::reachable);
399 
400  auto cmp_iterable = multi_stepper.constComponentIterable(multi_state);
401 
402  BOOST_CHECK((*cmp_iterable.begin()).status() ==
403  Intersection3D::Status::reachable);
404  BOOST_CHECK((*(++cmp_iterable.begin())).status() ==
405  Intersection3D::Status::missed);
406  }
407 
408  // Step forward now
409  {
410  auto multi_prop_state = DummyPropState(Direction::Forward, multi_state);
411  multi_stepper.step(multi_prop_state, mockNavigator);
412 
413  // Single stepper
414  auto single_prop_state = DummyPropState(Direction::Forward, single_state);
415  single_stepper.step(single_prop_state, mockNavigator);
416  }
417 
418  // Update surface status and check again
419  {
420  auto status = multi_stepper.updateSurfaceStatus(multi_state, *right_surface,
421  Direction::Forward, false);
422 
423  BOOST_CHECK(status == Intersection3D::Status::onSurface);
424 
425  auto cmp_iterable = multi_stepper.constComponentIterable(multi_state);
426 
427  BOOST_CHECK((*cmp_iterable.begin()).status() ==
428  Intersection3D::Status::onSurface);
429  BOOST_CHECK((*(++cmp_iterable.begin())).status() ==
430  Intersection3D::Status::missed);
431  }
432 
433  // Start surface should be unreachable
434  {
435  auto status = multi_stepper.updateSurfaceStatus(multi_state, *start_surface,
436  Direction::Forward, false);
437 
438  BOOST_CHECK(status == Intersection3D::Status::unreachable);
439 
440  auto cmp_iterable = multi_stepper.constComponentIterable(multi_state);
441 
442  BOOST_CHECK((*cmp_iterable.begin()).status() ==
443  Intersection3D::Status::unreachable);
444  BOOST_CHECK((*(++cmp_iterable.begin())).status() ==
445  Intersection3D::Status::unreachable);
446  }
447 }
448 
449 BOOST_AUTO_TEST_CASE(test_surface_status_and_cmpwise_bound_state) {
450  test_multi_stepper_surface_status_update<MultiStepperLoop>();
451 }
452 
454 // Test Bound state computations
456 template <typename multi_stepper_t>
458  using MultiState = typename multi_stepper_t::State;
459  using MultiStepper = multi_stepper_t;
460 
461  auto start_surface = Acts::Surface::makeShared<Acts::PlaneSurface>(
462  Vector3::Zero(), Vector3{1.0, 0.0, 0.0});
463 
464  auto right_surface = Acts::Surface::makeShared<Acts::PlaneSurface>(
465  Vector3{1.0, 0.0, 0.0}, Vector3{1.0, 0.0, 0.0});
466 
467  std::vector<std::tuple<double, BoundVector, std::optional<BoundSquareMatrix>>>
468  cmps(2, {0.5, BoundVector::Zero(), std::nullopt});
469  std::get<BoundVector>(cmps[0])[eBoundTheta] = M_PI_2;
470  std::get<BoundVector>(cmps[1])[eBoundTheta] = -M_PI_2;
471  std::get<BoundVector>(cmps[0])[eBoundQOverP] = 1.0;
472  std::get<BoundVector>(cmps[1])[eBoundQOverP] = 1.0;
473 
474  MultiComponentBoundTrackParameters multi_pars(start_surface, cmps,
476 
477  BOOST_REQUIRE(std::get<1>(multi_pars[0])
478  .direction()
479  .isApprox(Vector3{1.0, 0.0, 0.0}, 1.e-10));
480  BOOST_REQUIRE(std::get<1>(multi_pars[1])
481  .direction()
482  .isApprox(Vector3{-1.0, 0.0, 0.0}, 1.e-10));
483 
484  MultiState multi_state(geoCtx, magCtx, defaultNullBField, multi_pars,
486  SingleStepper::State single_state(
487  geoCtx, defaultNullBField->makeCache(magCtx), std::get<1>(multi_pars[0]),
489 
490  MultiStepper multi_stepper(defaultNullBField);
491  SingleStepper single_stepper(defaultNullBField);
492 
493  // Step forward now
494  {
495  multi_stepper.updateSurfaceStatus(multi_state, *right_surface,
496  Direction::Forward, false);
497  auto multi_prop_state = DummyPropState(Direction::Forward, multi_state);
498  multi_stepper.step(multi_prop_state, mockNavigator);
499 
500  // Single stepper
501  single_stepper.updateSurfaceStatus(single_state, *right_surface,
502  Direction::Forward, false);
503  auto single_prop_state = DummyPropState(Direction::Forward, single_state);
504  single_stepper.step(single_prop_state, mockNavigator);
505  }
506 
507  // Check component-wise bound-state
508  {
509  auto single_bound_state = single_stepper.boundState(
510  single_state, *right_surface, true, FreeToBoundCorrection(false));
511  BOOST_REQUIRE(single_bound_state.ok());
512 
513  auto cmp_iterable = multi_stepper.componentIterable(multi_state);
514 
515  auto ok_bound_state =
516  (*cmp_iterable.begin())
517  .boundState(*right_surface, true, FreeToBoundCorrection(false));
518  BOOST_REQUIRE(ok_bound_state.ok());
519  BOOST_CHECK(*single_bound_state == *ok_bound_state);
520 
521  auto failed_bound_state =
522  (*(++cmp_iterable.begin()))
523  .boundState(*right_surface, true, FreeToBoundCorrection(false));
524  BOOST_CHECK(not failed_bound_state.ok());
525  }
526 }
527 
528 BOOST_AUTO_TEST_CASE(test_component_wise_bound_state) {
529  test_component_bound_state<MultiStepperLoop>();
530 }
531 
532 template <typename multi_stepper_t>
534  using MultiState = typename multi_stepper_t::State;
535  using MultiStepper = multi_stepper_t;
536 
537  auto surface = Acts::Surface::makeShared<Acts::PlaneSurface>(
538  Vector3::Zero(), Vector3{1.0, 0.0, 0.0});
539 
540  // Use Ones() here, so that the angles are in correct range
541  const auto pars = BoundVector::Ones().eval();
542  const auto cov = []() {
543  auto c = BoundSquareMatrix::Random().eval();
544  c *= c.transpose();
545  return c;
546  }();
547 
548  std::vector<std::tuple<double, BoundVector, std::optional<BoundSquareMatrix>>>
549  cmps(4, {0.25, pars, cov});
550 
553  MultiState multi_state(geoCtx, magCtx, defaultBField, multi_pars,
555  MultiStepper multi_stepper(defaultBField);
556 
557  auto res = multi_stepper.boundState(multi_state, *surface, true,
558  FreeToBoundCorrection(false));
559 
560  BOOST_REQUIRE(res.ok());
561 
562  const auto [bound_pars, jacobian, pathLength] = *res;
563 
564  BOOST_CHECK(jacobian == decltype(jacobian)::Zero());
565  BOOST_CHECK(pathLength == 0.0);
566  BOOST_CHECK(bound_pars.parameters().isApprox(pars, 1.e-8));
567  BOOST_CHECK(bound_pars.covariance()->isApprox(cov, 1.e-8));
568 }
569 
570 BOOST_AUTO_TEST_CASE(test_combined_bound_state) {
571  test_combined_bound_state_function<MultiStepperLoop>();
572 }
573 
575 // Test the combined curvilinear state function
577 template <typename multi_stepper_t>
579  using MultiState = typename multi_stepper_t::State;
580  using MultiStepper = multi_stepper_t;
581 
582  auto surface = Acts::Surface::makeShared<Acts::PlaneSurface>(
583  Vector3::Zero(), Vector3{1.0, 0.0, 0.0});
584 
585  // Use Ones() here, so that the angles are in correct range
586  const auto pars = BoundVector::Ones().eval();
587  const auto cov = []() {
588  auto c = BoundSquareMatrix::Random().eval();
589  c *= c.transpose();
590  return c;
591  }();
592 
593  std::vector<std::tuple<double, BoundVector, std::optional<BoundSquareMatrix>>>
594  cmps(4, {0.25, pars, cov});
596 
599  MultiState multi_state(geoCtx, magCtx, defaultBField, multi_pars,
601  MultiStepper multi_stepper(defaultBField);
602 
603  const auto [curv_pars, jac, pathLength] =
604  multi_stepper.curvilinearState(multi_state);
605 
606  BOOST_CHECK(
607  curv_pars.fourPosition(multi_state.geoContext)
608  .isApprox(check_pars.fourPosition(multi_state.geoContext), 1.e-8));
609  BOOST_CHECK(curv_pars.direction().isApprox(check_pars.direction(), 1.e-8));
610  BOOST_CHECK_CLOSE(curv_pars.absoluteMomentum(), check_pars.absoluteMomentum(),
611  1.e-8);
612  BOOST_CHECK_CLOSE(curv_pars.charge(), check_pars.charge(), 1.e-8);
613 }
614 
615 BOOST_AUTO_TEST_CASE(test_curvilinear_state) {
616  test_combined_curvilinear_state_function<MultiStepperLoop>();
617 }
618 
620 // Test single component interface
622 
623 template <typename multi_stepper_t>
625  using MultiState = typename multi_stepper_t::State;
626  using MultiStepper = multi_stepper_t;
627 
628  std::vector<std::tuple<double, BoundVector, std::optional<BoundSquareMatrix>>>
629  cmps;
630  for (int i = 0; i < 4; ++i) {
631  cmps.push_back({0.25, BoundVector::Random(), BoundSquareMatrix::Random()});
632  }
633 
634  auto surface = Acts::Surface::makeShared<Acts::PlaneSurface>(
635  Vector3::Zero(), Vector3::Ones().normalized());
636 
639 
640  MultiState multi_state(geoCtx, magCtx, defaultBField, multi_pars,
642 
643  MultiStepper multi_stepper(defaultBField);
644 
645  DummyPropState multi_prop_state(defaultNDir, multi_state);
646 
647  // Check at least some properties at the moment
648  auto check = [&](auto cmp) {
649  auto sstepper = cmp.singleStepper(multi_stepper);
650  auto &sstepping = cmp.singleState(multi_prop_state).stepping;
651 
652  BOOST_CHECK(sstepper.position(sstepping) ==
653  cmp.pars().template segment<3>(eFreePos0));
654  BOOST_CHECK(sstepper.direction(sstepping) ==
655  cmp.pars().template segment<3>(eFreeDir0));
656  BOOST_CHECK(sstepper.time(sstepping) == cmp.pars()[eFreeTime]);
657  BOOST_CHECK_CLOSE(sstepper.qOverP(sstepping), cmp.pars()[eFreeQOverP],
658  1.e-8);
659  };
660 
661  for (const auto cmp : multi_stepper.constComponentIterable(multi_state)) {
662  check(cmp);
663  }
664 
665  for (auto cmp : multi_stepper.componentIterable(multi_state)) {
666  check(cmp);
667  }
668 }
669 
670 BOOST_AUTO_TEST_CASE(test_single_component_interface) {
671  test_single_component_interface_function<MultiStepperLoop>();
672 }
673 
675 // Remove and add components
677 
678 template <typename multi_stepper_t>
680  using MultiState = typename multi_stepper_t::State;
681  using MultiStepper = multi_stepper_t;
682 
683  const auto multi_pars = makeDefaultBoundPars(4);
684 
685  MultiState multi_state(geoCtx, magCtx, defaultBField, multi_pars,
687 
688  MultiStepper multi_stepper(defaultBField);
689 
690  {
691  BoundTrackParameters pars(multi_pars.referenceSurface().getSharedPtr(),
692  BoundVector::Ones(), std::nullopt,
694  multi_stepper.addComponent(multi_state, pars, 0.0);
695  }
696 
697  BOOST_CHECK_EQUAL(multi_stepper.numberComponents(multi_state),
698  multi_pars.components().size() + 1);
699 
700  multi_stepper.clearComponents(multi_state);
701 
702  BOOST_CHECK_EQUAL(multi_stepper.numberComponents(multi_state), 0);
703 }
704 
705 BOOST_AUTO_TEST_CASE(remove_add_components_test) {
706  remove_add_components_function<MultiStepperLoop>();
707 }
708 
710 // Instantiate a Propagator with the MultiStepper
712 
713 template <typename multi_stepper_t>
715  auto bField = std::make_shared<NullBField>();
716  multi_stepper_t multi_stepper(bField);
718  std::move(multi_stepper), Navigator{Navigator::Config{}});
719 
720  auto surface = Acts::Surface::makeShared<Acts::PlaneSurface>(
721  Vector3::Zero(), Vector3{1.0, 0.0, 0.0});
723 
724  std::vector<std::tuple<double, BoundVector, std::optional<BoundSquareMatrix>>>
725  cmps(4, {0.25, BoundVector::Ones().eval(),
726  BoundSquareMatrix::Identity().eval()});
728 
729  // This only checks that this compiles, not that it runs without errors
730  // @TODO: Add test that checks the target aborter works correctly
731 
732  // Instantiate with target
733  using type_a =
734  decltype(propagator.template propagate<decltype(pars), decltype(options),
736  pars, *surface, options));
737 
738  // Instantiate without target
739  using tybe_b = decltype(propagator.propagate(pars, options));
740 }
741 
742 BOOST_AUTO_TEST_CASE(propagator_instatiation_test) {
743  propagator_instatiation_test_function<MultiStepperLoop>();
744 }