Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-15 08:13:38

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