Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:24:58

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include <boost/test/test_tools.hpp>
0010 #include <boost/test/unit_test.hpp>
0011 
0012 #include "Acts/Definitions/Algebra.hpp"
0013 #include "Acts/Definitions/Common.hpp"
0014 #include "Acts/Definitions/Direction.hpp"
0015 #include "Acts/Definitions/Tolerance.hpp"
0016 #include "Acts/Definitions/TrackParametrization.hpp"
0017 #include "Acts/Definitions/Units.hpp"
0018 #include "Acts/EventData/GenericBoundTrackParameters.hpp"
0019 #include "Acts/EventData/ParticleHypothesis.hpp"
0020 #include "Acts/EventData/TrackParameters.hpp"
0021 #include "Acts/EventData/TransformationHelpers.hpp"
0022 #include "Acts/Geometry/GeometryContext.hpp"
0023 #include "Acts/MagneticField/ConstantBField.hpp"
0024 #include "Acts/MagneticField/MagneticFieldContext.hpp"
0025 #include "Acts/MagneticField/MagneticFieldProvider.hpp"
0026 #include "Acts/Propagator/AtlasStepper.hpp"
0027 #include "Acts/Propagator/ConstrainedStep.hpp"
0028 #include "Acts/Surfaces/BoundaryTolerance.hpp"
0029 #include "Acts/Surfaces/CurvilinearSurface.hpp"
0030 #include "Acts/Surfaces/DiscSurface.hpp"
0031 #include "Acts/Surfaces/PerigeeSurface.hpp"
0032 #include "Acts/Surfaces/PlaneSurface.hpp"
0033 #include "Acts/Surfaces/StrawSurface.hpp"
0034 #include "Acts/Surfaces/Surface.hpp"
0035 #include "Acts/Utilities/Result.hpp"
0036 #include "ActsTests/CommonHelpers/Assertions.hpp"
0037 #include "ActsTests/CommonHelpers/FloatComparisons.hpp"
0038 
0039 #include <algorithm>
0040 #include <array>
0041 #include <iterator>
0042 #include <limits>
0043 #include <memory>
0044 #include <optional>
0045 #include <type_traits>
0046 #include <utility>
0047 
0048 using namespace Acts;
0049 using namespace Acts::UnitLiterals;
0050 
0051 namespace ActsTests {
0052 
0053 using VectorHelpers::makeVector4;
0054 using Covariance = BoundSquareMatrix;
0055 using Jacobian = BoundMatrix;
0056 using Stepper = AtlasStepper;
0057 
0058 // epsilon for floating point comparisons
0059 static constexpr auto eps = 1024 * std::numeric_limits<double>::epsilon();
0060 
0061 // propagation settings
0062 static constexpr auto stepSize = 10_mm;
0063 static constexpr Direction navDir = Direction::Backward();
0064 static auto magneticField =
0065     std::make_shared<ConstantBField>(Vector3(0.1_T, -0.2_T, 2_T));
0066 
0067 // initial parameter state
0068 static const Vector4 pos4(1_mm, -1_mm, 2_mm, 2_ns);
0069 static const Vector3 pos = pos4.segment<3>(ePos0);
0070 static const auto time = pos4[eTime];
0071 static const Vector3 unitDir = Vector3(-2, 2, 1).normalized();
0072 static constexpr auto absMom = 1_GeV;
0073 static constexpr auto charge = -1_e;
0074 static const auto particleHypothesis = ParticleHypothesis::pion();
0075 static const Covariance cov = Covariance::Identity();
0076 
0077 // context objects
0078 static const GeometryContext geoCtx;
0079 static const MagneticFieldContext magCtx;
0080 
0081 BOOST_AUTO_TEST_SUITE(PropagatorSuite)
0082 
0083 // test state construction from parameters w/o covariance
0084 BOOST_AUTO_TEST_CASE(ConstructState) {
0085   BoundTrackParameters cp = BoundTrackParameters::createCurvilinear(
0086       pos4, unitDir, charge / absMom, std::nullopt, particleHypothesis);
0087 
0088   Stepper stepper(magneticField);
0089 
0090   Stepper::Options options(geoCtx, magCtx);
0091   options.maxStepSize = stepSize;
0092 
0093   Stepper::State state = stepper.makeState(options);
0094   stepper.initialize(state, cp);
0095 
0096   BOOST_CHECK(!state.covTransport);
0097   BOOST_CHECK_EQUAL(state.covariance, nullptr);
0098   BOOST_CHECK_EQUAL(state.pVector[0], pos.x());
0099   BOOST_CHECK_EQUAL(state.pVector[1], pos.y());
0100   BOOST_CHECK_EQUAL(state.pVector[2], pos.z());
0101   BOOST_CHECK_EQUAL(state.pVector[3], time);
0102   CHECK_CLOSE_ABS(state.pVector[4], unitDir.x(), eps);
0103   CHECK_CLOSE_ABS(state.pVector[5], unitDir.y(), eps);
0104   CHECK_CLOSE_ABS(state.pVector[6], unitDir.z(), eps);
0105   BOOST_CHECK_EQUAL(state.pVector[7], charge / absMom);
0106   BOOST_CHECK_EQUAL(state.pathAccumulated, 0.);
0107   BOOST_CHECK_EQUAL(state.stepSize.value(), stepSize);
0108   BOOST_CHECK_EQUAL(state.previousStepSize, 0.);
0109 }
0110 
0111 // test state construction from parameters w/ covariance
0112 BOOST_AUTO_TEST_CASE(ConstructStateWithCovariance) {
0113   BoundTrackParameters cp = BoundTrackParameters::createCurvilinear(
0114       pos4, unitDir, charge / absMom, cov, particleHypothesis);
0115 
0116   Stepper stepper(magneticField);
0117 
0118   Stepper::Options options(geoCtx, magCtx);
0119   options.maxStepSize = stepSize;
0120 
0121   Stepper::State state = stepper.makeState(options);
0122   stepper.initialize(state, cp);
0123 
0124   BOOST_CHECK(state.covTransport);
0125   BOOST_CHECK_EQUAL(*state.covariance, cov);
0126   BOOST_CHECK_EQUAL(state.pVector[0], pos.x());
0127   BOOST_CHECK_EQUAL(state.pVector[1], pos.y());
0128   BOOST_CHECK_EQUAL(state.pVector[2], pos.z());
0129   BOOST_CHECK_EQUAL(state.pVector[3], time);
0130   CHECK_CLOSE_ABS(state.pVector[4], unitDir.x(), eps);
0131   CHECK_CLOSE_ABS(state.pVector[5], unitDir.y(), eps);
0132   CHECK_CLOSE_ABS(state.pVector[6], unitDir.z(), eps);
0133   BOOST_CHECK_EQUAL(state.pVector[7], charge / absMom);
0134   BOOST_CHECK_EQUAL(state.pathAccumulated, 0.);
0135   BOOST_CHECK_EQUAL(state.stepSize.value(), stepSize);
0136   BOOST_CHECK_EQUAL(state.previousStepSize, 0.);
0137 }
0138 
0139 // test stepper getters for particle state
0140 BOOST_AUTO_TEST_CASE(Getters) {
0141   BoundTrackParameters cp = BoundTrackParameters::createCurvilinear(
0142       pos4, unitDir, charge / absMom, cov, particleHypothesis);
0143 
0144   Stepper stepper(magneticField);
0145 
0146   Stepper::Options options(geoCtx, magCtx);
0147   options.maxStepSize = stepSize;
0148 
0149   Stepper::State state = stepper.makeState(options);
0150   stepper.initialize(state, cp);
0151 
0152   CHECK_CLOSE_ABS(stepper.position(state), pos, eps);
0153   CHECK_CLOSE_ABS(stepper.time(state), time, eps);
0154   CHECK_CLOSE_ABS(stepper.direction(state), unitDir, eps);
0155   CHECK_CLOSE_ABS(stepper.absoluteMomentum(state), absMom, eps);
0156   BOOST_CHECK_EQUAL(stepper.charge(state), charge);
0157 }
0158 
0159 // test stepper update methods with bound state as input
0160 BOOST_AUTO_TEST_CASE(UpdateFromBound) {
0161   BoundTrackParameters cp = BoundTrackParameters::createCurvilinear(
0162       pos4, unitDir, charge / absMom, cov, particleHypothesis);
0163 
0164   Stepper stepper(magneticField);
0165 
0166   Stepper::Options options(geoCtx, magCtx);
0167   options.maxStepSize = stepSize;
0168 
0169   Stepper::State state = stepper.makeState(options);
0170   stepper.initialize(state, cp);
0171 
0172   auto newPos4 = (pos4 + Vector4(1_mm, 2_mm, 3_mm, 20_ns)).eval();
0173   auto newPos = newPos4.segment<3>(ePos0);
0174   auto newTime = newPos4[eTime];
0175   auto newUnitDir = (unitDir + Vector3(1, -1, -1)).normalized();
0176   auto newAbsMom = 0.9 * absMom;
0177 
0178   // example surface and bound parameters at the updated position
0179   std::shared_ptr<PlaneSurface> plane =
0180       CurvilinearSurface(newPos, newUnitDir).planeSurface();
0181   auto params =
0182       BoundTrackParameters::create(geoCtx, plane, newPos4, newUnitDir,
0183                                    charge / absMom, cov, particleHypothesis)
0184           .value();
0185   FreeVector freeParams;
0186   freeParams[eFreePos0] = newPos4[ePos0];
0187   freeParams[eFreePos1] = newPos4[ePos1];
0188   freeParams[eFreePos2] = newPos4[ePos2];
0189   freeParams[eFreeTime] = newPos4[eTime];
0190   freeParams[eFreeDir0] = newUnitDir[eMom0];
0191   freeParams[eFreeDir1] = newUnitDir[eMom1];
0192   freeParams[eFreeDir2] = newUnitDir[eMom2];
0193   freeParams[eFreeQOverP] = charge / newAbsMom;
0194 
0195   // WARNING for some reason there seems to be an additional flag that makes
0196   //         the update method not do anything when it is set. Why?
0197   state.state_ready = false;
0198   BOOST_CHECK(params.covariance().has_value());
0199   stepper.update(state, freeParams, params.parameters(), *params.covariance(),
0200                  *plane);
0201   CHECK_CLOSE_ABS(stepper.position(state), newPos, eps);
0202   CHECK_CLOSE_ABS(stepper.time(state), newTime, eps);
0203   CHECK_CLOSE_ABS(stepper.direction(state), newUnitDir, eps);
0204   CHECK_CLOSE_ABS(stepper.absoluteMomentum(state), newAbsMom, eps);
0205   BOOST_CHECK_EQUAL(stepper.charge(state), charge);
0206 }
0207 
0208 // test stepper update methods with individual components as input
0209 BOOST_AUTO_TEST_CASE(UpdateFromComponents) {
0210   BoundTrackParameters cp = BoundTrackParameters::createCurvilinear(
0211       pos4, unitDir, charge / absMom, cov, particleHypothesis);
0212 
0213   Stepper stepper(magneticField);
0214 
0215   Stepper::Options options(geoCtx, magCtx);
0216   options.maxStepSize = stepSize;
0217 
0218   Stepper::State state = stepper.makeState(options);
0219   stepper.initialize(state, cp);
0220 
0221   auto newPos = (pos + Vector3(1_mm, 2_mm, 3_mm)).eval();
0222   auto newTime = time + 20_ns;
0223   auto newUnitDir = (unitDir + Vector3(1, -1, -1)).normalized();
0224   auto newAbsMom = 0.9 * absMom;
0225 
0226   stepper.update(state, newPos, newUnitDir, charge / newAbsMom, newTime);
0227   CHECK_CLOSE_ABS(stepper.position(state), newPos, eps);
0228   CHECK_CLOSE_ABS(stepper.time(state), newTime, eps);
0229   CHECK_CLOSE_ABS(stepper.direction(state), newUnitDir, eps);
0230   CHECK_CLOSE_ABS(stepper.absoluteMomentum(state), newAbsMom, eps);
0231   BOOST_CHECK_EQUAL(stepper.charge(state), charge);
0232 }
0233 
0234 // test building a bound state object from the stepper state
0235 BOOST_AUTO_TEST_CASE(BuildBound) {
0236   BoundTrackParameters cp = BoundTrackParameters::createCurvilinear(
0237       pos4, unitDir, charge / absMom, cov, particleHypothesis);
0238 
0239   Stepper stepper(magneticField);
0240 
0241   Stepper::Options options(geoCtx, magCtx);
0242   options.maxStepSize = stepSize;
0243 
0244   Stepper::State state = stepper.makeState(options);
0245   stepper.initialize(state, cp);
0246 
0247   // example surface at the current state position
0248   std::shared_ptr<PlaneSurface> plane =
0249       CurvilinearSurface(pos, unitDir).planeSurface();
0250 
0251   auto&& [pars, jac, pathLength] = stepper.boundState(state, *plane).value();
0252   // check parameters
0253   CHECK_CLOSE_ABS(pars.position(geoCtx), pos, eps);
0254   CHECK_CLOSE_ABS(pars.time(), time, eps);
0255   CHECK_CLOSE_ABS(pars.momentum(), absMom * unitDir, eps);
0256   BOOST_CHECK_EQUAL(pars.charge(), charge);
0257   BOOST_CHECK(pars.covariance().has_value());
0258   BOOST_CHECK_NE(*pars.covariance(), cov);
0259   // check Jacobian. should be identity since there was no propagation yet
0260   CHECK_CLOSE_ABS(jac, Jacobian(Jacobian::Identity()), eps);
0261   // check propagation length
0262   CHECK_CLOSE_ABS(pathLength, 0., eps);
0263 }
0264 
0265 // test building a curvilinear state object from the stepper state
0266 BOOST_AUTO_TEST_CASE(BuildCurvilinear) {
0267   BoundTrackParameters cp = BoundTrackParameters::createCurvilinear(
0268       pos4, unitDir, charge / absMom, cov, particleHypothesis);
0269 
0270   Stepper stepper(magneticField);
0271 
0272   Stepper::Options options(geoCtx, magCtx);
0273   options.maxStepSize = stepSize;
0274 
0275   Stepper::State state = stepper.makeState(options);
0276   stepper.initialize(state, cp);
0277 
0278   auto&& [pars, jac, pathLength] = stepper.curvilinearState(state);
0279   // check parameters
0280   CHECK_CLOSE_ABS(pars.position(geoCtx), pos, eps);
0281   CHECK_CLOSE_ABS(pars.time(), time, eps);
0282   CHECK_CLOSE_ABS(pars.momentum(), absMom * unitDir, eps);
0283   BOOST_CHECK_EQUAL(pars.charge(), charge);
0284   BOOST_CHECK(pars.covariance().has_value());
0285   BOOST_CHECK_NE(*pars.covariance(), cov);
0286   // check Jacobian. should be identity since there was no propagation yet
0287   CHECK_CLOSE_ABS(jac, Jacobian(Jacobian::Identity()), eps);
0288   // check propagation length
0289   CHECK_CLOSE_ABS(pathLength, 0., eps);
0290 }
0291 
0292 // test step method without covariance transport
0293 BOOST_AUTO_TEST_CASE(Step) {
0294   BoundTrackParameters cp = BoundTrackParameters::createCurvilinear(
0295       pos4, unitDir, charge / absMom, cov, particleHypothesis);
0296 
0297   Stepper stepper(magneticField);
0298 
0299   Stepper::Options options(geoCtx, magCtx);
0300   options.maxStepSize = stepSize;
0301 
0302   auto state = stepper.makeState(options);
0303   stepper.initialize(state, cp);
0304   state.covTransport = false;
0305 
0306   // ensure step does not result in an error
0307   auto res = stepper.step(state, Direction::Backward(), nullptr);
0308   BOOST_CHECK(res.ok());
0309 
0310   // extract the actual step size
0311   auto h = res.value();
0312   BOOST_CHECK_EQUAL(state.stepSize.value(), stepSize);
0313   BOOST_CHECK_EQUAL(state.stepSize.value(), h * navDir);
0314 
0315   // check that the position has moved
0316   auto deltaPos = (stepper.position(state) - pos).eval();
0317   BOOST_CHECK_LT(0, deltaPos.norm());
0318   // check that time has changed
0319   auto deltaTime = stepper.time(state) - time;
0320   BOOST_CHECK_LT(0, std::abs(deltaTime));
0321   // check that the direction was rotated
0322   auto projDir = stepper.direction(state).dot(unitDir);
0323   BOOST_CHECK_LT(projDir, 1);
0324 
0325   // momentum and charge should not change
0326   CHECK_CLOSE_ABS(stepper.absoluteMomentum(state), absMom, eps);
0327   BOOST_CHECK_EQUAL(stepper.charge(state), charge);
0328 }
0329 
0330 // test step method with covariance transport
0331 BOOST_AUTO_TEST_CASE(StepWithCovariance) {
0332   BoundTrackParameters cp = BoundTrackParameters::createCurvilinear(
0333       pos4, unitDir, charge / absMom, cov, particleHypothesis);
0334 
0335   Stepper stepper(magneticField);
0336 
0337   Stepper::Options options(geoCtx, magCtx);
0338   options.maxStepSize = stepSize;
0339 
0340   auto state = stepper.makeState(options);
0341   stepper.initialize(state, cp);
0342   state.covTransport = true;
0343 
0344   // ensure step does not result in an error
0345   auto res = stepper.step(state, Direction::Backward(), nullptr);
0346   BOOST_CHECK(res.ok());
0347 
0348   // extract the actual step size
0349   auto h = res.value();
0350   BOOST_CHECK_EQUAL(state.stepSize.value(), stepSize);
0351   BOOST_CHECK_EQUAL(state.stepSize.value(), h * navDir);
0352 
0353   // check that the position has moved
0354   auto deltaPos = (stepper.position(state) - pos).eval();
0355   BOOST_CHECK_LT(0, deltaPos.norm());
0356   // check that time has changed
0357   auto deltaTime = stepper.time(state) - time;
0358   BOOST_CHECK_LT(0, std::abs(deltaTime));
0359   // check that the direction was rotated
0360   auto projDir = stepper.direction(state).dot(unitDir);
0361   BOOST_CHECK_LT(projDir, 1);
0362 
0363   // momentum and charge should not change
0364   CHECK_CLOSE_ABS(stepper.absoluteMomentum(state), absMom, eps);
0365   BOOST_CHECK_EQUAL(stepper.charge(state), charge);
0366 
0367   stepper.transportCovarianceToCurvilinear(state);
0368   BOOST_CHECK_NE(state.cov, cov);
0369 }
0370 
0371 // test state reset method
0372 BOOST_AUTO_TEST_CASE(Reset) {
0373   BoundTrackParameters cp = BoundTrackParameters::createCurvilinear(
0374       pos4, unitDir, charge / absMom, cov, particleHypothesis);
0375 
0376   Stepper stepper(magneticField);
0377 
0378   Stepper::Options options(geoCtx, magCtx);
0379   options.maxStepSize = stepSize;
0380 
0381   auto state = stepper.makeState(options);
0382   stepper.initialize(state, cp);
0383   state.covTransport = true;
0384 
0385   // ensure step does not result in an error
0386   stepper.step(state, Direction::Backward(), nullptr);
0387 
0388   // Construct the parameters
0389   Vector3 newPos(1.5, -2.5, 3.5);
0390   auto newAbsMom = 4.2 * absMom;
0391   double newTime = 7.5;
0392   double newCharge = 1.;
0393   BoundSquareMatrix newCov = 8.5 * Covariance::Identity();
0394   cp = BoundTrackParameters::createCurvilinear(makeVector4(newPos, newTime),
0395                                                unitDir, newCharge / newAbsMom,
0396                                                newCov, particleHypothesis);
0397   FreeVector freeParams = transformBoundToFreeParameters(
0398       cp.referenceSurface(), geoCtx, cp.parameters());
0399 
0400   auto copyState = [&](auto& field, const auto& other) {
0401     using field_t = std::decay_t<decltype(field)>;
0402     std::decay_t<decltype(other)> copy = stepper.makeState(options);
0403     stepper.initialize(state, cp);
0404 
0405     copy.state_ready = other.state_ready;
0406     copy.useJacobian = other.useJacobian;
0407     copy.step = other.step;
0408     copy.maxPathLength = other.maxPathLength;
0409     copy.mcondition = other.mcondition;
0410     copy.needgradient = other.needgradient;
0411     copy.newfield = other.newfield;
0412     copy.field = other.field;
0413     copy.pVector = other.pVector;
0414     std::copy(std::begin(other.parameters), std::end(other.parameters),
0415               std::begin(copy.parameters));
0416     copy.covariance = other.covariance;
0417     copy.covTransport = other.covTransport;
0418     std::copy(std::begin(other.jacobian), std::end(other.jacobian),
0419               std::begin(copy.jacobian));
0420     copy.pathAccumulated = other.pathAccumulated;
0421     copy.stepSize = other.stepSize;
0422     copy.previousStepSize = other.previousStepSize;
0423 
0424     copy.fieldCache = MagneticFieldProvider::Cache(
0425         std::in_place_type<typename field_t::Cache>,
0426         other.fieldCache.template as<typename field_t::Cache>());
0427 
0428     copy.debug = other.debug;
0429     copy.debugString = other.debugString;
0430     copy.debugPfxWidth = other.debugPfxWidth;
0431     copy.debugMsgWidth = other.debugMsgWidth;
0432 
0433     return copy;
0434   };
0435 
0436   // Reset all possible parameters
0437   Stepper::State stateCopy = copyState(*magneticField, state);
0438   BOOST_CHECK(cp.covariance().has_value());
0439   stepper.initialize(stateCopy, cp.parameters(), *cp.covariance(),
0440                      cp.particleHypothesis(), cp.referenceSurface());
0441   // Test all components
0442   BOOST_CHECK(stateCopy.covTransport);
0443   BOOST_CHECK_EQUAL(*stateCopy.covariance, newCov);
0444   BOOST_CHECK_EQUAL(stepper.position(stateCopy),
0445                     freeParams.template segment<3>(eFreePos0));
0446   BOOST_CHECK_EQUAL(stepper.direction(stateCopy),
0447                     freeParams.template segment<3>(eFreeDir0).normalized());
0448   BOOST_CHECK_EQUAL(stepper.absoluteMomentum(stateCopy),
0449                     std::abs(1. / freeParams[eFreeQOverP]));
0450   BOOST_CHECK_EQUAL(stepper.charge(stateCopy), stepper.charge(state));
0451   BOOST_CHECK_EQUAL(stepper.time(stateCopy), freeParams[eFreeTime]);
0452   BOOST_CHECK_EQUAL(stateCopy.pathAccumulated, 0.);
0453   BOOST_CHECK_EQUAL(stateCopy.stepSize.value(), stepSize);
0454   BOOST_CHECK_EQUAL(stateCopy.previousStepSize, state.previousStepSize);
0455 
0456   // Reset using different surface shapes
0457   // 1) Disc surface
0458   // Setting some parameters
0459   newPos << 0.5, -1.5, 0.;
0460   newAbsMom *= 1.23;
0461   newTime = 8.4;
0462   newCharge = -1.;
0463   newCov = 10.9 * Covariance::Identity();
0464   Transform3 trafo = Transform3::Identity();
0465   auto disc = Surface::makeShared<DiscSurface>(trafo);
0466   auto boundDisc = BoundTrackParameters::create(
0467                        geoCtx, disc, makeVector4(newPos, newTime), unitDir,
0468                        newCharge / newAbsMom, newCov, particleHypothesis)
0469                        .value();
0470 
0471   // Reset the state and test
0472   Stepper::State stateDisc = copyState(*magneticField, state);
0473   BOOST_CHECK(boundDisc.covariance().has_value());
0474   stepper.initialize(stateDisc, boundDisc.parameters(), *boundDisc.covariance(),
0475                      cp.particleHypothesis(), boundDisc.referenceSurface());
0476 
0477   CHECK_NE_COLLECTIONS(stateDisc.pVector, stateCopy.pVector);
0478   CHECK_NE_COLLECTIONS(stateDisc.pVector, state.pVector);
0479 
0480   // 2) Perigee surface
0481   // Setting some parameters
0482   newPos << -2.06155, -2.06155, 3.5;
0483   newAbsMom *= 0.45;
0484   newTime = 2.3;
0485   newCharge = 1.;
0486   newCov = 8.7 * Covariance::Identity();
0487   auto perigee = Surface::makeShared<PerigeeSurface>(trafo);
0488   auto boundPerigee =
0489       BoundTrackParameters::create(
0490           geoCtx, perigee, makeVector4(newPos, newTime), unitDir,
0491           newCharge / newAbsMom, newCov, particleHypothesis)
0492           .value();
0493 
0494   // Reset the state and test
0495   Stepper::State statePerigee = copyState(*magneticField, state);
0496   BOOST_CHECK(boundPerigee.covariance().has_value());
0497   stepper.initialize(statePerigee, boundPerigee.parameters(),
0498                      *boundPerigee.covariance(), cp.particleHypothesis(),
0499                      boundPerigee.referenceSurface());
0500   CHECK_NE_COLLECTIONS(statePerigee.pVector, stateCopy.pVector);
0501   CHECK_NE_COLLECTIONS(statePerigee.pVector, state.pVector);
0502   CHECK_NE_COLLECTIONS(statePerigee.pVector, stateDisc.pVector);
0503 
0504   // 3) Straw surface
0505   // Use the same parameters as for previous Perigee surface
0506   auto straw = Surface::makeShared<StrawSurface>(trafo);
0507   auto boundStraw = BoundTrackParameters::create(
0508                         geoCtx, straw, makeVector4(newPos, newTime), unitDir,
0509                         newCharge / newAbsMom, newCov, particleHypothesis)
0510                         .value();
0511 
0512   // Reset the state and test
0513   Stepper::State stateStraw = copyState(*magneticField, state);
0514   BOOST_CHECK(boundStraw.covariance().has_value());
0515   stepper.initialize(stateStraw, boundStraw.parameters(),
0516                      *boundStraw.covariance(), cp.particleHypothesis(),
0517                      boundStraw.referenceSurface());
0518   CHECK_NE_COLLECTIONS(stateStraw.pVector, stateCopy.pVector);
0519   CHECK_NE_COLLECTIONS(stateStraw.pVector, state.pVector);
0520   CHECK_NE_COLLECTIONS(stateStraw.pVector, stateDisc.pVector);
0521   BOOST_CHECK_EQUAL_COLLECTIONS(
0522       stateStraw.pVector.begin(), stateStraw.pVector.end(),
0523       statePerigee.pVector.begin(), statePerigee.pVector.end());
0524 }
0525 
0526 BOOST_AUTO_TEST_CASE(StepSize) {
0527   BoundTrackParameters cp = BoundTrackParameters::createCurvilinear(
0528       pos4, unitDir, charge / absMom, cov, particleHypothesis);
0529 
0530   Stepper stepper(magneticField);
0531 
0532   Stepper::Options options(geoCtx, magCtx);
0533   options.maxStepSize = stepSize;
0534 
0535   Stepper::State state = stepper.makeState(options);
0536   stepper.initialize(state, cp);
0537 
0538   stepper.updateStepSize(state, -5_cm, ConstrainedStep::Type::Navigator);
0539   BOOST_CHECK_EQUAL(state.previousStepSize, stepSize);
0540   BOOST_CHECK_EQUAL(state.stepSize.value(), -5_cm);
0541 
0542   stepper.releaseStepSize(state, ConstrainedStep::Type::Navigator);
0543   BOOST_CHECK_EQUAL(state.stepSize.value(), stepSize);
0544 }
0545 
0546 // test step size modification with target surfaces
0547 BOOST_AUTO_TEST_CASE(StepSizeSurface) {
0548   BoundTrackParameters cp = BoundTrackParameters::createCurvilinear(
0549       pos4, unitDir, charge / absMom, cov, particleHypothesis);
0550 
0551   Stepper stepper(magneticField);
0552 
0553   Stepper::Options options(geoCtx, magCtx);
0554   options.maxStepSize = stepSize;
0555 
0556   Stepper::State state = stepper.makeState(options);
0557   stepper.initialize(state, cp);
0558 
0559   auto distance = 10_mm;
0560   std::shared_ptr<PlaneSurface> target =
0561       CurvilinearSurface(pos + navDir * distance * unitDir, unitDir)
0562           .planeSurface();
0563 
0564   stepper.updateSurfaceStatus(
0565       state, *target, 0, navDir, BoundaryTolerance::Infinite(),
0566       s_onSurfaceTolerance, ConstrainedStep::Type::Navigator);
0567   BOOST_CHECK_EQUAL(state.stepSize.value(ConstrainedStep::Type::Navigator),
0568                     distance);
0569 
0570   const auto getNavigationTarget = [&](const Surface& s,
0571                                        const BoundaryTolerance& bt) {
0572     auto [intersection, intersectionIndex] =
0573         s.intersect(geoCtx, stepper.position(state),
0574                     navDir * stepper.direction(state), bt)
0575             .closestWithIndex();
0576     return NavigationTarget(intersection, intersectionIndex, s, bt);
0577   };
0578 
0579   // test the step size modification in the context of a surface
0580   stepper.updateStepSize(
0581       state, getNavigationTarget(*target, BoundaryTolerance::Infinite()),
0582       navDir, ConstrainedStep::Type::Navigator);
0583   BOOST_CHECK_EQUAL(state.stepSize.value(), distance);
0584 
0585   // start with a different step size
0586   state.stepSize.setUser(navDir * stepSize);
0587   stepper.releaseStepSize(state, ConstrainedStep::Type::Navigator);
0588   stepper.updateStepSize(
0589       state, getNavigationTarget(*target, BoundaryTolerance::Infinite()),
0590       navDir, ConstrainedStep::Type::Navigator);
0591   BOOST_CHECK_EQUAL(state.stepSize.value(), navDir * stepSize);
0592 }
0593 
0594 BOOST_AUTO_TEST_SUITE_END()
0595 
0596 }  // namespace ActsTests