File indexing completed on 2025-08-28 08:13:21
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011 #include <boost/test/unit_test.hpp>
0012
0013 #include "Acts/Definitions/Algebra.hpp"
0014 #include "Acts/Definitions/Direction.hpp"
0015 #include "Acts/Definitions/Tolerance.hpp"
0016 #include "Acts/Definitions/TrackParametrization.hpp"
0017 #include "Acts/EventData/GenericBoundTrackParameters.hpp"
0018 #include "Acts/EventData/MultiComponentTrackParameters.hpp"
0019 #include "Acts/EventData/TrackParameters.hpp"
0020 #include "Acts/EventData/detail/CorrectedTransformationFreeToBound.hpp"
0021 #include "Acts/Geometry/GeometryContext.hpp"
0022 #include "Acts/MagneticField/ConstantBField.hpp"
0023 #include "Acts/MagneticField/MagneticFieldContext.hpp"
0024 #include "Acts/MagneticField/NullBField.hpp"
0025 #include "Acts/Propagator/ConstrainedStep.hpp"
0026 #include "Acts/Propagator/MultiStepperLoop.hpp"
0027 #include "Acts/Propagator/Navigator.hpp"
0028 #include "Acts/Propagator/Propagator.hpp"
0029 #include "Acts/Surfaces/CurvilinearSurface.hpp"
0030 #include "Acts/Surfaces/PlaneSurface.hpp"
0031 #include "Acts/Utilities/Intersection.hpp"
0032
0033 #include <algorithm>
0034 #include <array>
0035 #include <cstddef>
0036 #include <memory>
0037 #include <numbers>
0038 #include <optional>
0039 #include <random>
0040 #include <stdexcept>
0041 #include <tuple>
0042 #include <type_traits>
0043 #include <utility>
0044 #include <vector>
0045
0046 namespace Acts {
0047 namespace Concepts {
0048 template <typename T>
0049 concept has_components = requires { typename T::components; };
0050 }
0051
0052 struct MultiStepperSurfaceReached;
0053 }
0054
0055 using namespace Acts;
0056 using namespace VectorHelpers;
0057
0058 template <Acts::Concepts::SingleStepper single_stepper_t,
0059 Acts::Concepts::MultiStepper multi_stepper_t>
0060 struct MultiStepperTester {
0061 using SingleStepper = single_stepper_t;
0062 using MultiStepper = multi_stepper_t;
0063
0064 using MultiOptions = typename multi_stepper_t::Options;
0065 using MultiState = typename multi_stepper_t::State;
0066
0067 using SingleState = typename SingleStepper::State;
0068
0069 const MagneticFieldContext magCtx;
0070 const GeometryContext geoCtx;
0071
0072 const double defaultStepSize = 123.;
0073 const Direction defaultNDir = Direction::Backward();
0074
0075 const std::shared_ptr<MagneticFieldProvider> defaultBField =
0076 std::make_shared<ConstantBField>(Vector3(1., 2.5, 33.33));
0077 const std::shared_ptr<MagneticFieldProvider> defaultNullBField =
0078 std::make_shared<NullBField>();
0079
0080 const ParticleHypothesis particleHypothesis = ParticleHypothesis::pion();
0081
0082
0083
0084
0085 auto makeDefaultBoundPars(
0086 bool cov = true, std::size_t n = 4,
0087 std::optional<BoundVector> ext_pars = std::nullopt) const {
0088 std::vector<
0089 std::tuple<double, BoundVector, std::optional<BoundSquareMatrix>>>
0090 cmps;
0091 using Opt = std::optional<BoundSquareMatrix>;
0092
0093 auto make_random_sym_matrix = []() {
0094 auto c = BoundSquareMatrix::Random().eval();
0095 c *= c.transpose();
0096 return c;
0097 };
0098
0099
0100 std::mt19937 gen;
0101 std::uniform_real_distribution<> locDis(-10., 10.);
0102 std::uniform_real_distribution<> phiDis(-std::numbers::pi,
0103 std::numbers::pi);
0104 std::uniform_real_distribution<> thetaDis(0., std::numbers::pi);
0105 std::uniform_real_distribution<> qOverPDis(-10., 10.);
0106 std::uniform_real_distribution<> timeDis(0., 100.);
0107
0108 for (auto i = 0ul; i < n; ++i) {
0109 BoundVector params = BoundVector::Zero();
0110
0111 if (ext_pars) {
0112 params = *ext_pars;
0113 } else {
0114 params[eBoundLoc0] = locDis(gen);
0115 params[eBoundLoc1] = locDis(gen);
0116 params[eBoundPhi] = phiDis(gen);
0117 params[eBoundTheta] = thetaDis(gen);
0118 params[eBoundQOverP] = qOverPDis(gen);
0119 params[eBoundTime] = timeDis(gen);
0120 }
0121
0122 cmps.push_back(
0123 {1. / n, params, cov ? Opt{make_random_sym_matrix()} : Opt{}});
0124 }
0125
0126 std::shared_ptr<PlaneSurface> surface =
0127 CurvilinearSurface(Vector3::Zero(), Vector3{1., 0., 0.}).planeSurface();
0128
0129 return MultiComponentBoundTrackParameters(surface, cmps,
0130 particleHypothesis);
0131 }
0132
0133 void test_config_constructor() const {
0134 typename SingleStepper::Config singleCfg;
0135 singleCfg.bField = defaultBField;
0136
0137 typename MultiStepper::Config multiCfg;
0138 static_cast<typename SingleStepper::Config &>(multiCfg) = singleCfg;
0139
0140 MultiStepper ms(multiCfg);
0141 }
0142
0143
0144
0145
0146 void test_max_weight_reducer() const {
0147
0148
0149 MultiOptions options(geoCtx, magCtx);
0150 options.maxStepSize = defaultStepSize;
0151
0152 SingleStepper singleStepper(defaultBField);
0153 MultiStepper multiStepper(defaultBField);
0154
0155 constexpr std::size_t N = 4;
0156 const auto multi_pars = makeDefaultBoundPars(false, N);
0157 MultiState state = multiStepper.makeState(options);
0158 multiStepper.initialize(state, multi_pars);
0159
0160 double w = 0.1;
0161 double wSum = 0.0;
0162 for (auto &[sstate, weight, _] : state.components) {
0163 weight = w;
0164 wSum += w;
0165 w += 0.1;
0166 }
0167 BOOST_CHECK_EQUAL(wSum, 1.0);
0168 BOOST_CHECK_EQUAL(state.components.back().weight, 0.4);
0169
0170 MaxWeightReducerLoop reducer{};
0171 BOOST_CHECK_EQUAL(reducer.position(state),
0172 singleStepper.position(state.components.back().state));
0173 BOOST_CHECK_EQUAL(reducer.direction(state),
0174 singleStepper.direction(state.components.back().state));
0175 }
0176
0177 void test_max_momentum_reducer() const {
0178
0179
0180 MultiOptions options(geoCtx, magCtx);
0181 options.maxStepSize = defaultStepSize;
0182
0183 SingleStepper singleStepper(defaultBField);
0184 MultiStepper multiStepper(defaultBField);
0185
0186 constexpr std::size_t N = 4;
0187 const auto multi_pars = makeDefaultBoundPars(false, N);
0188 MultiState state = multiStepper.makeState(options);
0189 multiStepper.initialize(state, multi_pars);
0190
0191 double p = 1.0;
0192 double q = 1.0;
0193 for (auto &[sstate, weight, _] : state.components) {
0194 sstate.pars[eFreeQOverP] = q / p;
0195 p *= 2.0;
0196 }
0197 BOOST_CHECK_EQUAL(state.components.back().state.pars[eFreeQOverP], q / 8.0);
0198
0199 MaxMomentumReducerLoop reducer{};
0200 BOOST_CHECK_EQUAL(reducer.position(state),
0201 singleStepper.position(state.components.back().state));
0202 BOOST_CHECK_EQUAL(reducer.direction(state),
0203 singleStepper.direction(state.components.back().state));
0204 }
0205
0206
0207
0208
0209 template <bool Cov>
0210 void test_multi_stepper_state() const {
0211 MultiOptions options(geoCtx, magCtx);
0212 options.maxStepSize = defaultStepSize;
0213
0214 SingleStepper singleStepper(defaultBField);
0215 MultiStepper multiStepper(defaultBField);
0216
0217 constexpr std::size_t N = 4;
0218 const auto multi_pars = makeDefaultBoundPars(Cov, N, BoundVector::Ones());
0219
0220 MultiState state = multiStepper.makeState(options);
0221 multiStepper.initialize(state, multi_pars);
0222
0223 BOOST_CHECK_EQUAL(N, multiStepper.numberComponents(state));
0224
0225
0226 auto const_iterable = multiStepper.constComponentIterable(state);
0227 for (const auto cmp : const_iterable) {
0228 BOOST_CHECK_EQUAL(cmp.jacTransport(), FreeMatrix::Identity());
0229 BOOST_CHECK_EQUAL(cmp.derivative(), FreeVector::Zero());
0230 if constexpr (!Cov) {
0231 BOOST_CHECK_EQUAL(cmp.jacToGlobal(), BoundToFreeMatrix::Zero());
0232 BOOST_CHECK_EQUAL(cmp.cov(), BoundSquareMatrix::Zero());
0233 }
0234 }
0235
0236 BOOST_CHECK_EQUAL(state.pathAccumulated, 0.);
0237 for (const auto cmp : const_iterable) {
0238 BOOST_CHECK_EQUAL(cmp.pathAccumulated(), 0.);
0239 }
0240
0241
0242
0243
0244 if constexpr (Concepts::has_components<MultiState>) {
0245 BOOST_CHECK(!state.covTransport);
0246 for (const auto &cmp : state.components) {
0247 BOOST_CHECK_EQUAL(cmp.state.covTransport, Cov);
0248 }
0249 }
0250 }
0251
0252 void test_multi_stepper_state_invalid() const {
0253 MultiOptions options(geoCtx, magCtx);
0254 options.maxStepSize = defaultStepSize;
0255
0256 MultiStepper multiStepper(defaultBField);
0257
0258
0259 const auto multi_pars = makeDefaultBoundPars(false, 0);
0260 MultiState state = multiStepper.makeState(options);
0261
0262 BOOST_CHECK_THROW(multiStepper.initialize(state, multi_pars),
0263 std::invalid_argument);
0264 }
0265
0266
0267
0268
0269 void test_multi_stepper_vs_eigen_stepper() const {
0270 MultiOptions options(geoCtx, magCtx);
0271 options.maxStepSize = defaultStepSize;
0272
0273 MultiStepper multi_stepper(defaultBField);
0274 SingleStepper single_stepper(defaultBField);
0275
0276 const BoundVector pars = BoundVector::Ones();
0277 const BoundSquareMatrix cov = BoundSquareMatrix::Identity();
0278
0279 std::vector<
0280 std::tuple<double, BoundVector, std::optional<BoundSquareMatrix>>>
0281 cmps(4, {0.25, pars, cov});
0282
0283 std::shared_ptr<PlaneSurface> surface =
0284 CurvilinearSurface(Vector3::Zero(), Vector3::Ones().normalized())
0285 .planeSurface();
0286
0287 MultiComponentBoundTrackParameters multi_pars(surface, cmps,
0288 particleHypothesis);
0289 BoundTrackParameters single_pars(surface, pars, cov, particleHypothesis);
0290
0291 MultiState multi_state = multi_stepper.makeState(options);
0292 SingleState single_state = single_stepper.makeState(options);
0293
0294 multi_stepper.initialize(multi_state, multi_pars);
0295 single_stepper.initialize(single_state, single_pars);
0296
0297 for (auto cmp : multi_stepper.componentIterable(multi_state)) {
0298 cmp.status() = IntersectionStatus::reachable;
0299 }
0300
0301
0302 for (int i = 0; i < 10; ++i) {
0303
0304 auto single_result =
0305 single_stepper.step(single_state, defaultNDir, nullptr);
0306 single_stepper.transportCovarianceToCurvilinear(single_state);
0307
0308
0309 auto multi_result = multi_stepper.step(multi_state, defaultNDir, nullptr);
0310 multi_stepper.transportCovarianceToCurvilinear(multi_state);
0311
0312
0313 BOOST_REQUIRE(multi_result.ok());
0314 BOOST_REQUIRE_EQUAL(multi_result.ok(), single_result.ok());
0315
0316 BOOST_CHECK_EQUAL(*single_result, *multi_result);
0317
0318 for (const auto cmp : multi_stepper.constComponentIterable(multi_state)) {
0319 BOOST_CHECK_EQUAL(cmp.pars(), single_state.pars);
0320 BOOST_CHECK_EQUAL(cmp.cov(), single_state.cov);
0321 BOOST_CHECK_EQUAL(cmp.jacTransport(), single_state.jacTransport);
0322 BOOST_CHECK_EQUAL(cmp.jacToGlobal(), single_state.jacToGlobal);
0323 BOOST_CHECK_EQUAL(cmp.derivative(), single_state.derivative);
0324 BOOST_CHECK_EQUAL(cmp.pathAccumulated(), single_state.pathAccumulated);
0325 }
0326 }
0327 }
0328
0329
0330
0331
0332
0333
0334
0335
0336
0337
0338
0339 void test_components_modifying_accessors() const {
0340 MultiOptions options(geoCtx, magCtx);
0341 options.maxStepSize = defaultStepSize;
0342
0343 const auto multi_pars = makeDefaultBoundPars();
0344
0345 MultiStepper multi_stepper(defaultBField);
0346
0347 MultiState mutable_multi_state = multi_stepper.makeState(options);
0348 MultiState const_multi_state_backend = multi_stepper.makeState(options);
0349 const MultiState &const_multi_state = const_multi_state_backend;
0350
0351 multi_stepper.initialize(mutable_multi_state, multi_pars);
0352 multi_stepper.initialize(const_multi_state_backend, multi_pars);
0353
0354 auto modify = [&](const auto &projector) {
0355
0356 for (auto cmp : multi_stepper.componentIterable(mutable_multi_state)) {
0357 using type = std::decay_t<decltype(projector(cmp))>;
0358 if constexpr (std::is_enum_v<type>) {
0359 projector(cmp) =
0360 static_cast<type>(static_cast<int>(projector(cmp)) + 1);
0361 } else {
0362 projector(cmp) *= 2.0;
0363 }
0364 }
0365 };
0366
0367 auto check = [&](const auto &projector) {
0368
0369 auto mutable_state_iterable =
0370 multi_stepper.componentIterable(mutable_multi_state);
0371
0372 auto const_state_iterable =
0373 multi_stepper.constComponentIterable(const_multi_state);
0374
0375 auto mstate_it = mutable_state_iterable.begin();
0376 auto cstate_it = const_state_iterable.begin();
0377 for (; cstate_it != const_state_iterable.end();
0378 ++mstate_it, ++cstate_it) {
0379 const auto mstate_cmp = *mstate_it;
0380 auto cstate_cmp = *cstate_it;
0381
0382 using type = std::decay_t<decltype(projector(mstate_cmp))>;
0383
0384 if constexpr (std::is_arithmetic_v<type>) {
0385 BOOST_CHECK_CLOSE(projector(mstate_cmp), 2.0 * projector(cstate_cmp),
0386 1.e-8);
0387 } else if constexpr (std::is_enum_v<type>) {
0388 BOOST_CHECK_EQUAL(static_cast<int>(projector(mstate_cmp)),
0389 1 + static_cast<int>(projector(cstate_cmp)));
0390 } else {
0391 BOOST_CHECK(projector(mstate_cmp)
0392 .isApprox(2.0 * projector(cstate_cmp), 1.e-8));
0393 }
0394 }
0395 };
0396
0397 const auto projectors = std::make_tuple(
0398 [](auto &cmp) -> decltype(auto) { return cmp.status(); },
0399 [](auto &cmp) -> decltype(auto) { return cmp.pathAccumulated(); },
0400 [](auto &cmp) -> decltype(auto) { return cmp.weight(); },
0401 [](auto &cmp) -> decltype(auto) { return cmp.pars(); },
0402 [](auto &cmp) -> decltype(auto) { return cmp.cov(); },
0403 [](auto &cmp) -> decltype(auto) { return cmp.jacTransport(); },
0404 [](auto &cmp) -> decltype(auto) { return cmp.derivative(); },
0405 [](auto &cmp) -> decltype(auto) { return cmp.jacobian(); },
0406 [](auto &cmp) -> decltype(auto) { return cmp.jacToGlobal(); });
0407
0408 std::apply(
0409 [&](const auto &...projs) {
0410
0411 ( [&]() { modify(projs); check(projs); }(), ...);
0412
0413 },
0414 projectors);
0415 }
0416
0417
0418
0419
0420 void test_multi_stepper_surface_status_update() const {
0421 MultiOptions options(geoCtx, magCtx);
0422 options.maxStepSize = defaultStepSize;
0423
0424 MultiStepper multi_stepper(defaultNullBField);
0425 SingleStepper single_stepper(defaultNullBField);
0426
0427 std::shared_ptr<PlaneSurface> start_surface =
0428 CurvilinearSurface(Vector3::Zero(), Vector3{1.0, 0.0, 0.0})
0429 .planeSurface();
0430
0431 std::shared_ptr<PlaneSurface> right_surface =
0432 CurvilinearSurface(Vector3{1.0, 0.0, 0.0}, Vector3{1.0, 0.0, 0.0})
0433 .planeSurface();
0434
0435 std::vector<
0436 std::tuple<double, BoundVector, std::optional<BoundSquareMatrix>>>
0437 cmps(2, {0.5, BoundVector::Zero(), std::nullopt});
0438 std::get<BoundVector>(cmps[0])[eBoundTheta] = std::numbers::pi / 2.;
0439 std::get<BoundVector>(cmps[1])[eBoundPhi] = std::numbers::pi;
0440 std::get<BoundVector>(cmps[1])[eBoundTheta] = std::numbers::pi / 2.;
0441 std::get<BoundVector>(cmps[0])[eBoundQOverP] = 1.0;
0442 std::get<BoundVector>(cmps[1])[eBoundQOverP] = 1.0;
0443
0444 MultiComponentBoundTrackParameters multi_pars(start_surface, cmps,
0445 particleHypothesis);
0446
0447 BOOST_REQUIRE(std::get<1>(multi_pars[0])
0448 .direction()
0449 .isApprox(Vector3{1.0, 0.0, 0.0}, 1.e-10));
0450 BOOST_REQUIRE(std::get<1>(multi_pars[1])
0451 .direction()
0452 .isApprox(Vector3{-1.0, 0.0, 0.0}, 1.e-10));
0453
0454 MultiState multi_state = multi_stepper.makeState(options);
0455 SingleState single_state = single_stepper.makeState(options);
0456
0457 multi_stepper.initialize(multi_state, multi_pars);
0458 single_stepper.initialize(single_state, std::get<1>(multi_pars[0]));
0459
0460
0461 {
0462 auto status = multi_stepper.updateSurfaceStatus(
0463 multi_state, *right_surface, 0, Direction::Forward(),
0464 BoundaryTolerance::Infinite(), s_onSurfaceTolerance,
0465 ConstrainedStep::Type::Navigator);
0466
0467 BOOST_CHECK_EQUAL(status, IntersectionStatus::reachable);
0468
0469 auto cmp_iterable = multi_stepper.constComponentIterable(multi_state);
0470 auto cmp_1 = *cmp_iterable.begin();
0471 auto cmp_2 = *(++cmp_iterable.begin());
0472
0473 BOOST_CHECK_EQUAL(cmp_1.status(), IntersectionStatus::reachable);
0474 BOOST_CHECK_EQUAL(cmp_2.status(), IntersectionStatus::reachable);
0475
0476 BOOST_CHECK_EQUAL(cmp_1.cmp.state.stepSize.value(), 1.0);
0477 BOOST_CHECK_EQUAL(cmp_2.cmp.state.stepSize.value(), -1.0);
0478 }
0479
0480
0481 {
0482 multi_stepper.step(multi_state, Direction::Forward(), nullptr);
0483
0484
0485 single_stepper.step(single_state, Direction::Forward(), nullptr);
0486 }
0487
0488
0489 {
0490 auto status = multi_stepper.updateSurfaceStatus(
0491 multi_state, *right_surface, 0, Direction::Forward(),
0492 BoundaryTolerance::Infinite(), s_onSurfaceTolerance,
0493 ConstrainedStep::Type::Navigator);
0494
0495 BOOST_CHECK_EQUAL(status, IntersectionStatus::onSurface);
0496
0497 auto cmp_iterable = multi_stepper.constComponentIterable(multi_state);
0498 auto cmp_1 = *cmp_iterable.begin();
0499 auto cmp_2 = *(++cmp_iterable.begin());
0500
0501 BOOST_CHECK_EQUAL(cmp_1.status(), IntersectionStatus::onSurface);
0502 BOOST_CHECK_EQUAL(cmp_2.status(), IntersectionStatus::onSurface);
0503 }
0504
0505
0506 {
0507 auto status = multi_stepper.updateSurfaceStatus(
0508 multi_state, *start_surface, 0, Direction::Forward(),
0509 BoundaryTolerance::Infinite(), s_onSurfaceTolerance,
0510 ConstrainedStep::Type::Navigator);
0511
0512 BOOST_CHECK_EQUAL(status, IntersectionStatus::reachable);
0513
0514 auto cmp_iterable = multi_stepper.constComponentIterable(multi_state);
0515 auto cmp_1 = *cmp_iterable.begin();
0516 auto cmp_2 = *(++cmp_iterable.begin());
0517
0518 BOOST_CHECK_EQUAL(cmp_1.status(), IntersectionStatus::reachable);
0519 BOOST_CHECK_EQUAL(cmp_2.status(), IntersectionStatus::reachable);
0520
0521 BOOST_CHECK_EQUAL(cmp_1.cmp.state.stepSize.value(), -1.0);
0522 BOOST_CHECK_EQUAL(cmp_2.cmp.state.stepSize.value(), 1.0);
0523 }
0524 }
0525
0526
0527
0528
0529 void test_component_bound_state() const {
0530 MultiOptions options(geoCtx, magCtx);
0531 options.maxStepSize = defaultStepSize;
0532
0533 MultiStepper multi_stepper(defaultNullBField);
0534 SingleStepper single_stepper(defaultNullBField);
0535
0536 std::shared_ptr<PlaneSurface> start_surface =
0537 CurvilinearSurface(Vector3::Zero(), Vector3{1.0, 0.0, 0.0})
0538 .planeSurface();
0539
0540 std::shared_ptr<PlaneSurface> right_surface =
0541 CurvilinearSurface(Vector3{1.0, 0.0, 0.0}, Vector3{1.0, 0.0, 0.0})
0542 .planeSurface();
0543
0544 std::vector<
0545 std::tuple<double, BoundVector, std::optional<BoundSquareMatrix>>>
0546 cmps(2, {0.5, BoundVector::Zero(), std::nullopt});
0547 std::get<BoundVector>(cmps[0])[eBoundTheta] = std::numbers::pi / 2.;
0548 std::get<BoundVector>(cmps[1])[eBoundPhi] = std::numbers::pi;
0549 std::get<BoundVector>(cmps[1])[eBoundTheta] = std::numbers::pi / 2.;
0550 std::get<BoundVector>(cmps[0])[eBoundQOverP] = 1.0;
0551 std::get<BoundVector>(cmps[1])[eBoundQOverP] = 1.0;
0552
0553 MultiComponentBoundTrackParameters multi_pars(start_surface, cmps,
0554 particleHypothesis);
0555
0556 BOOST_REQUIRE(std::get<1>(multi_pars[0])
0557 .direction()
0558 .isApprox(Vector3{1.0, 0.0, 0.0}, 1.e-10));
0559 BOOST_REQUIRE(std::get<1>(multi_pars[1])
0560 .direction()
0561 .isApprox(Vector3{-1.0, 0.0, 0.0}, 1.e-10));
0562
0563 MultiState multi_state = multi_stepper.makeState(options);
0564 SingleState single_state = single_stepper.makeState(options);
0565
0566 multi_stepper.initialize(multi_state, multi_pars);
0567 single_stepper.initialize(single_state, std::get<1>(multi_pars[0]));
0568
0569
0570 {
0571 multi_stepper.updateSurfaceStatus(
0572 multi_state, *right_surface, 0, Direction::Forward(),
0573 BoundaryTolerance::Infinite(), s_onSurfaceTolerance,
0574 ConstrainedStep::Type::Navigator);
0575 multi_stepper.step(multi_state, Direction::Forward(), nullptr);
0576
0577
0578 single_stepper.updateSurfaceStatus(
0579 single_state, *right_surface, 0, Direction::Forward(),
0580 BoundaryTolerance::Infinite(), s_onSurfaceTolerance,
0581 ConstrainedStep::Type::Navigator);
0582 single_stepper.step(single_state, Direction::Forward(), nullptr);
0583 }
0584
0585
0586 {
0587 auto single_bound_state = single_stepper.boundState(
0588 single_state, *right_surface, true, FreeToBoundCorrection(false));
0589 BOOST_REQUIRE(single_bound_state.ok());
0590
0591 auto cmp_iterable = multi_stepper.componentIterable(multi_state);
0592 auto cmp_1 = *cmp_iterable.begin();
0593 auto cmp_2 = *(++cmp_iterable.begin());
0594
0595 auto bound_state_1 =
0596 cmp_1.boundState(*right_surface, true, FreeToBoundCorrection(false));
0597 BOOST_REQUIRE(bound_state_1.ok());
0598 BOOST_CHECK(*single_bound_state == *bound_state_1);
0599
0600 auto bound_state_2 =
0601 cmp_2.boundState(*right_surface, true, FreeToBoundCorrection(false));
0602 BOOST_CHECK(bound_state_2.ok());
0603 }
0604 }
0605
0606 void test_combined_bound_state_function() const {
0607 MultiOptions options(geoCtx, magCtx);
0608 options.maxStepSize = defaultStepSize;
0609
0610 MultiStepper multi_stepper(defaultBField);
0611
0612 std::shared_ptr<PlaneSurface> surface =
0613 CurvilinearSurface(Vector3::Zero(), Vector3{1.0, 0.0, 0.0})
0614 .planeSurface();
0615
0616
0617 const auto pars = BoundVector::Ones().eval();
0618 const auto cov = []() {
0619 auto c = BoundSquareMatrix::Random().eval();
0620 c *= c.transpose();
0621 return c;
0622 }();
0623
0624 std::vector<
0625 std::tuple<double, BoundVector, std::optional<BoundSquareMatrix>>>
0626 cmps(4, {0.25, pars, cov});
0627
0628 MultiComponentBoundTrackParameters multi_pars(surface, cmps,
0629 particleHypothesis);
0630 MultiState multi_state = multi_stepper.makeState(options);
0631 multi_stepper.initialize(multi_state, multi_pars);
0632
0633 auto res = multi_stepper.boundState(multi_state, *surface, true,
0634 FreeToBoundCorrection(false));
0635
0636 BOOST_REQUIRE(res.ok());
0637
0638 const auto [bound_pars, jacobian, pathLength] = *res;
0639
0640 BOOST_CHECK_EQUAL(jacobian, decltype(jacobian)::Zero());
0641 BOOST_CHECK_EQUAL(pathLength, 0.0);
0642 BOOST_CHECK(bound_pars.parameters().isApprox(pars, 1.e-8));
0643 BOOST_CHECK(bound_pars.covariance()->isApprox(cov, 1.e-8));
0644 }
0645
0646
0647
0648
0649 void test_combined_curvilinear_state_function() const {
0650 MultiOptions options(geoCtx, magCtx);
0651 options.maxStepSize = defaultStepSize;
0652
0653 MultiStepper multi_stepper(defaultBField);
0654
0655 std::shared_ptr<PlaneSurface> surface =
0656 CurvilinearSurface(Vector3::Zero(), Vector3{1.0, 0.0, 0.0})
0657 .planeSurface();
0658
0659
0660 const auto pars = BoundVector::Ones().eval();
0661 const auto cov = []() {
0662 auto c = BoundSquareMatrix::Random().eval();
0663 c *= c.transpose();
0664 return c;
0665 }();
0666
0667 std::vector<
0668 std::tuple<double, BoundVector, std::optional<BoundSquareMatrix>>>
0669 cmps(4, {0.25, pars, cov});
0670 BoundTrackParameters check_pars(surface, pars, cov, particleHypothesis);
0671
0672 MultiComponentBoundTrackParameters multi_pars(surface, cmps,
0673 particleHypothesis);
0674 MultiState multi_state = multi_stepper.makeState(options);
0675 multi_stepper.initialize(multi_state, multi_pars);
0676
0677 const auto [curv_pars, jac, pathLength] =
0678 multi_stepper.curvilinearState(multi_state);
0679
0680 BOOST_CHECK(curv_pars.fourPosition(geoCtx).isApprox(
0681 check_pars.fourPosition(geoCtx), 1.e-8));
0682 BOOST_CHECK(curv_pars.direction().isApprox(check_pars.direction(), 1.e-8));
0683 BOOST_CHECK_CLOSE(curv_pars.absoluteMomentum(),
0684 check_pars.absoluteMomentum(), 1.e-8);
0685 BOOST_CHECK_CLOSE(curv_pars.charge(), check_pars.charge(), 1.e-8);
0686 }
0687
0688
0689
0690
0691
0692 void test_single_component_interface_function() const {
0693 MultiOptions options(geoCtx, magCtx);
0694 options.maxStepSize = defaultStepSize;
0695
0696 MultiStepper multi_stepper(defaultBField);
0697
0698 MultiComponentBoundTrackParameters multi_pars =
0699 makeDefaultBoundPars(true, 4);
0700
0701 MultiState multi_state = multi_stepper.makeState(options);
0702
0703 multi_stepper.initialize(multi_state, multi_pars);
0704
0705
0706 auto check = [&](auto cmp) {
0707 auto sstepper = cmp.singleStepper(multi_stepper);
0708 auto &sstepping = cmp.state();
0709
0710 BOOST_CHECK_EQUAL(sstepper.position(sstepping),
0711 cmp.pars().template segment<3>(eFreePos0));
0712 BOOST_CHECK_EQUAL(sstepper.direction(sstepping),
0713 cmp.pars().template segment<3>(eFreeDir0));
0714 BOOST_CHECK_EQUAL(sstepper.time(sstepping), cmp.pars()[eFreeTime]);
0715 BOOST_CHECK_CLOSE(sstepper.qOverP(sstepping), cmp.pars()[eFreeQOverP],
0716 1.e-8);
0717 };
0718
0719 for (const auto cmp : multi_stepper.constComponentIterable(multi_state)) {
0720 check(cmp);
0721 }
0722
0723 for (auto cmp : multi_stepper.componentIterable(multi_state)) {
0724 check(cmp);
0725 }
0726 }
0727
0728
0729
0730
0731
0732 void remove_add_components_function() const {
0733 MultiOptions options(geoCtx, magCtx);
0734
0735 MultiStepper multi_stepper(defaultBField);
0736
0737 const auto multi_pars = makeDefaultBoundPars(4);
0738
0739 MultiState multi_state = multi_stepper.makeState(options);
0740
0741 multi_stepper.initialize(multi_state, multi_pars);
0742
0743 {
0744 BoundTrackParameters pars(multi_pars.referenceSurface().getSharedPtr(),
0745 BoundVector::Ones(), std::nullopt,
0746 particleHypothesis);
0747 multi_stepper.addComponent(multi_state, pars, 0.0);
0748 }
0749
0750 BOOST_CHECK_EQUAL(multi_stepper.numberComponents(multi_state),
0751 multi_pars.components().size() + 1);
0752
0753 multi_stepper.clearComponents(multi_state);
0754
0755 BOOST_CHECK_EQUAL(multi_stepper.numberComponents(multi_state), 0);
0756 }
0757
0758
0759
0760
0761
0762 void propagator_instatiation_test_function() const {
0763 auto bField = std::make_shared<NullBField>();
0764 multi_stepper_t multi_stepper(bField);
0765
0766 Propagator propagator(std::move(multi_stepper), VoidNavigator{});
0767
0768 std::shared_ptr<PlaneSurface> surface =
0769 CurvilinearSurface(Vector3::Zero(), Vector3{1.0, 0.0, 0.0})
0770 .planeSurface();
0771 using PropagatorOptions =
0772 typename Propagator<multi_stepper_t, Navigator>::template Options<>;
0773 PropagatorOptions options(geoCtx, magCtx);
0774
0775 std::vector<
0776 std::tuple<double, BoundVector, std::optional<BoundSquareMatrix>>>
0777 cmps(4, {0.25, BoundVector::Ones().eval(),
0778 BoundSquareMatrix::Identity().eval()});
0779 MultiComponentBoundTrackParameters pars(surface, cmps, particleHypothesis);
0780
0781
0782
0783
0784
0785 using type_a =
0786 decltype(propagator.template propagate<
0787 decltype(pars), decltype(options), MultiStepperSurfaceReached>(
0788 pars, *surface, options));
0789 static_assert(!std::is_same_v<type_a, void>);
0790
0791
0792 using type_b = decltype(propagator.propagate(pars, options));
0793 static_assert(!std::is_same_v<type_b, void>);
0794 }
0795 };