File indexing completed on 2025-08-06 08:11:27
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <boost/test/data/test_case.hpp>
0010 #include <boost/test/tools/output_test_stream.hpp>
0011 #include <boost/test/unit_test.hpp>
0012
0013 #include "Acts/Definitions/Algebra.hpp"
0014 #include "Acts/Definitions/Direction.hpp"
0015 #include "Acts/Definitions/TrackParametrization.hpp"
0016 #include "Acts/Definitions/Units.hpp"
0017 #include "Acts/EventData/GenericBoundTrackParameters.hpp"
0018 #include "Acts/EventData/GenericCurvilinearTrackParameters.hpp"
0019 #include "Acts/EventData/TrackParameters.hpp"
0020 #include "Acts/Geometry/GeometryContext.hpp"
0021 #include "Acts/Geometry/GeometryIdentifier.hpp"
0022 #include "Acts/MagneticField/ConstantBField.hpp"
0023 #include "Acts/MagneticField/MagneticFieldContext.hpp"
0024 #include "Acts/Propagator/AbortList.hpp"
0025 #include "Acts/Propagator/ActionList.hpp"
0026 #include "Acts/Propagator/ConstrainedStep.hpp"
0027 #include "Acts/Propagator/DenseEnvironmentExtension.hpp"
0028 #include "Acts/Propagator/EigenStepper.hpp"
0029 #include "Acts/Propagator/Navigator.hpp"
0030 #include "Acts/Propagator/Propagator.hpp"
0031 #include "Acts/Propagator/StandardAborters.hpp"
0032 #include "Acts/Propagator/StepperExtensionList.hpp"
0033 #include "Acts/Propagator/StraightLineStepper.hpp"
0034 #include "Acts/Surfaces/CylinderBounds.hpp"
0035 #include "Acts/Surfaces/CylinderSurface.hpp"
0036 #include "Acts/Surfaces/PlaneSurface.hpp"
0037 #include "Acts/Surfaces/Surface.hpp"
0038 #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp"
0039 #include "Acts/Utilities/Helpers.hpp"
0040 #include "Acts/Utilities/Result.hpp"
0041
0042 #include <algorithm>
0043 #include <array>
0044 #include <cmath>
0045 #include <cstddef>
0046 #include <limits>
0047 #include <memory>
0048 #include <optional>
0049 #include <random>
0050 #include <tuple>
0051 #include <type_traits>
0052 #include <utility>
0053
0054 namespace Acts {
0055 class Logger;
0056 }
0057
0058 namespace bdata = boost::unit_test::data;
0059 using namespace Acts::UnitLiterals;
0060 using Acts::VectorHelpers::makeVector4;
0061 using Acts::VectorHelpers::perp;
0062
0063 namespace Acts::Test {
0064
0065
0066 GeometryContext tgContext = GeometryContext();
0067 MagneticFieldContext mfContext = MagneticFieldContext();
0068
0069 using Covariance = BoundSquareMatrix;
0070
0071
0072 struct PerpendicularMeasure {
0073
0074 struct this_result {
0075 double distance = std::numeric_limits<double>::max();
0076 };
0077
0078 using result_type = this_result;
0079
0080 PerpendicularMeasure() = default;
0081
0082 template <typename propagator_state_t, typename stepper_t,
0083 typename navigator_t>
0084 void operator()(propagator_state_t& state, const stepper_t& stepper,
0085 const navigator_t& , result_type& result) const {
0086 result.distance = perp(stepper.position(state.stepping));
0087 }
0088 };
0089
0090
0091 template <typename Surface>
0092 struct SurfaceObserver {
0093
0094 const Surface* surface = nullptr;
0095
0096 double tolerance = 1.e-5;
0097
0098
0099 struct this_result {
0100 std::size_t surfaces_passed = 0;
0101 double surface_passed_r = std::numeric_limits<double>::max();
0102 };
0103
0104 using result_type = this_result;
0105
0106 SurfaceObserver() = default;
0107
0108 template <typename propagator_state_t, typename stepper_t,
0109 typename navigator_t>
0110 void operator()(propagator_state_t& state, const stepper_t& stepper,
0111 const navigator_t& , result_type& result,
0112 const Logger& ) const {
0113 if (surface && !result.surfaces_passed) {
0114
0115 const double distance =
0116 surface
0117 ->intersect(state.geoContext, stepper.position(state.stepping),
0118 stepper.direction(state.stepping),
0119 BoundaryCheck(true))
0120 .closest()
0121 .pathLength();
0122
0123 state.stepping.stepSize.update(distance * state.options.direction,
0124 ConstrainedStep::actor);
0125
0126 if (std::abs(distance) <= tolerance) {
0127 ++result.surfaces_passed;
0128 result.surface_passed_r = perp(stepper.position(state.stepping));
0129
0130 state.stepping.stepSize.release(ConstrainedStep::actor);
0131 }
0132 }
0133 }
0134 };
0135
0136
0137 using BFieldType = ConstantBField;
0138 using EigenStepperType = EigenStepper<>;
0139 using EigenPropagatorType = Propagator<EigenStepperType>;
0140
0141 const double Bz = 2_T;
0142 auto bField = std::make_shared<BFieldType>(Vector3{0, 0, Bz});
0143 EigenStepperType estepper(bField);
0144 EigenPropagatorType epropagator(std::move(estepper));
0145
0146 auto mCylinder = std::make_shared<CylinderBounds>(10_mm, 1000_mm);
0147 auto mSurface =
0148 Surface::makeShared<CylinderSurface>(Transform3::Identity(), mCylinder);
0149 auto cCylinder = std::make_shared<CylinderBounds>(150_mm, 1000_mm);
0150 auto cSurface =
0151 Surface::makeShared<CylinderSurface>(Transform3::Identity(), cCylinder);
0152
0153 const int ntests = 5;
0154
0155
0156 BOOST_AUTO_TEST_CASE(PropagatorOptions_) {
0157 using null_optionsType = PropagatorOptions<>;
0158 null_optionsType null_options(tgContext, mfContext);
0159
0160
0161 using ActionListType = ActionList<PerpendicularMeasure>;
0162 using AbortConditionsType = AbortList<>;
0163
0164 using optionsType = PropagatorOptions<ActionListType, AbortConditionsType>;
0165
0166 optionsType options(tgContext, mfContext);
0167 }
0168
0169 BOOST_DATA_TEST_CASE(
0170 cylinder_passage_observer_,
0171 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 0,
0172 bdata::distribution = std::uniform_real_distribution<double>(
0173 0.4_GeV, 10_GeV))) ^
0174 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 1,
0175 bdata::distribution =
0176 std::uniform_real_distribution<double>(-M_PI,
0177 M_PI))) ^
0178 bdata::random(
0179 (bdata::engine = std::mt19937(), bdata::seed = 2,
0180 bdata::distribution =
0181 std::uniform_real_distribution<double>(1.0, M_PI - 1.0))) ^
0182 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 3,
0183 bdata::distribution =
0184 std::uniform_int_distribution<std::uint8_t>(0, 1))) ^
0185 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 4,
0186 bdata::distribution =
0187 std::uniform_real_distribution<double>(-1_ns,
0188 1_ns))) ^
0189 bdata::xrange(ntests),
0190 pT, phi, theta, charge, time, index) {
0191 double dcharge = -1 + 2 * charge;
0192 (void)index;
0193
0194 using CylinderObserver = SurfaceObserver<CylinderSurface>;
0195 using ActionListType = ActionList<CylinderObserver>;
0196 using AbortConditionsType = AbortList<>;
0197
0198
0199 PropagatorOptions<ActionListType, AbortConditionsType> options(tgContext,
0200 mfContext);
0201
0202 options.pathLimit = 20_m;
0203 options.maxStepSize = 1_cm;
0204
0205
0206 options.actionList.get<CylinderObserver>().surface = mSurface.get();
0207
0208 using so_result = typename CylinderObserver::result_type;
0209
0210
0211 double x = 0;
0212 double y = 0;
0213 double z = 0;
0214 double px = pT * cos(phi);
0215 double py = pT * sin(phi);
0216 double pz = pT / tan(theta);
0217 double q = dcharge;
0218 Vector3 pos(x, y, z);
0219 Vector3 mom(px, py, pz);
0220 CurvilinearTrackParameters start(makeVector4(pos, time), mom.normalized(),
0221 q / mom.norm(), std::nullopt,
0222 ParticleHypothesis::pion());
0223
0224 const auto& result = epropagator.propagate(start, *cSurface, options).value();
0225 auto& sor = result.get<so_result>();
0226
0227 BOOST_CHECK_EQUAL(sor.surfaces_passed, 1u);
0228 CHECK_CLOSE_ABS(sor.surface_passed_r, 10., 1e-5);
0229 }
0230
0231 BOOST_DATA_TEST_CASE(
0232 curvilinear_additive_,
0233 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 0,
0234 bdata::distribution = std::uniform_real_distribution<double>(
0235 0.4_GeV, 10_GeV))) ^
0236 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 1,
0237 bdata::distribution =
0238 std::uniform_real_distribution<double>(-M_PI,
0239 M_PI))) ^
0240 bdata::random(
0241 (bdata::engine = std::mt19937(), bdata::seed = 2,
0242 bdata::distribution =
0243 std::uniform_real_distribution<double>(1.0, M_PI - 1.0))) ^
0244 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 3,
0245 bdata::distribution =
0246 std::uniform_int_distribution<std::uint8_t>(0, 1))) ^
0247 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 4,
0248 bdata::distribution =
0249 std::uniform_real_distribution<double>(-1_ns,
0250 1_ns))) ^
0251 bdata::xrange(ntests),
0252 pT, phi, theta, charge, time, index) {
0253 double dcharge = -1 + 2 * charge;
0254 (void)index;
0255
0256
0257 PropagatorOptions<> options_2s(tgContext, mfContext);
0258 options_2s.pathLimit = 50_cm;
0259 options_2s.maxStepSize = 1_cm;
0260
0261
0262 double x = 0;
0263 double y = 0;
0264 double z = 0;
0265 double px = pT * cos(phi);
0266 double py = pT * sin(phi);
0267 double pz = pT / tan(theta);
0268 double q = dcharge;
0269 Vector3 pos(x, y, z);
0270 Vector3 mom(px, py, pz);
0271
0272 Covariance cov;
0273
0274 cov << 10_mm, 0, 0.123, 0, 0.5, 0, 0, 10_mm, 0, 0.162, 0, 0, 0.123, 0, 0.1, 0,
0275 0, 0, 0, 0.162, 0, 0.1, 0, 0, 0.5, 0, 0, 0, 1. / (10_GeV), 0, 0, 0, 0, 0,
0276 0, 0;
0277 CurvilinearTrackParameters start(makeVector4(pos, time), mom.normalized(),
0278 q / mom.norm(), cov,
0279 ParticleHypothesis::pion());
0280
0281 const auto& mid_parameters =
0282 epropagator.propagate(start, options_2s).value().endParameters;
0283 const auto& end_parameters_2s =
0284 epropagator.propagate(*mid_parameters, options_2s).value().endParameters;
0285
0286
0287 PropagatorOptions<> options_1s(tgContext, mfContext);
0288 options_1s.pathLimit = 100_cm;
0289 options_1s.maxStepSize = 1_cm;
0290
0291 const auto& end_parameters_1s =
0292 epropagator.propagate(start, options_1s).value().endParameters;
0293
0294
0295 CHECK_CLOSE_REL(end_parameters_1s->position(tgContext),
0296 end_parameters_2s->position(tgContext), 0.001);
0297
0298 BOOST_CHECK(end_parameters_1s->covariance().has_value());
0299 const auto& cov_1s = *(end_parameters_1s->covariance());
0300 BOOST_CHECK(end_parameters_2s->covariance().has_value());
0301 const auto& cov_2s = *(end_parameters_2s->covariance());
0302
0303
0304 for (unsigned int i = 0; i < cov_1s.rows(); i++) {
0305 for (unsigned int j = 0; j < cov_1s.cols(); j++) {
0306 CHECK_CLOSE_OR_SMALL(cov_1s(i, j), cov_2s(i, j), 0.001, 1e-6);
0307 }
0308 }
0309 }
0310
0311 BOOST_DATA_TEST_CASE(
0312 cylinder_additive_,
0313 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 0,
0314 bdata::distribution = std::uniform_real_distribution<double>(
0315 0.4_GeV, 10_GeV))) ^
0316 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 1,
0317 bdata::distribution =
0318 std::uniform_real_distribution<double>(-M_PI,
0319 M_PI))) ^
0320 bdata::random(
0321 (bdata::engine = std::mt19937(), bdata::seed = 2,
0322 bdata::distribution =
0323 std::uniform_real_distribution<double>(1.0, M_PI - 1.0))) ^
0324 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 3,
0325 bdata::distribution =
0326 std::uniform_int_distribution<std::uint8_t>(0, 1))) ^
0327 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 4,
0328 bdata::distribution =
0329 std::uniform_real_distribution<double>(-1_ns,
0330 1_ns))) ^
0331 bdata::xrange(ntests),
0332 pT, phi, theta, charge, time, index) {
0333 double dcharge = -1 + 2 * charge;
0334 (void)index;
0335
0336
0337 PropagatorOptions<> options_2s(tgContext, mfContext);
0338 options_2s.pathLimit = 10_m;
0339 options_2s.maxStepSize = 1_cm;
0340
0341
0342 double x = 0;
0343 double y = 0;
0344 double z = 0;
0345 double px = pT * cos(phi);
0346 double py = pT * sin(phi);
0347 double pz = pT / tan(theta);
0348 double q = dcharge;
0349 Vector3 pos(x, y, z);
0350 Vector3 mom(px, py, pz);
0351
0352 Covariance cov;
0353
0354 cov << 10_mm, 0, 0.123, 0, 0.5, 0, 0, 10_mm, 0, 0.162, 0, 0, 0.123, 0, 0.1, 0,
0355 0, 0, 0, 0.162, 0, 0.1, 0, 0, 0.5, 0, 0, 0, 1. / (10_GeV), 0, 0, 0, 0, 0,
0356 0, 0;
0357 CurvilinearTrackParameters start(makeVector4(pos, time), mom.normalized(),
0358 q / mom.norm(), cov,
0359 ParticleHypothesis::pion());
0360
0361 const auto& mid_parameters =
0362 epropagator.propagate(start, *mSurface, options_2s).value().endParameters;
0363
0364 const auto& end_parameters_2s =
0365 epropagator.propagate(*mid_parameters, *cSurface, options_2s)
0366 .value()
0367 .endParameters;
0368
0369
0370 PropagatorOptions<> options_1s(tgContext, mfContext);
0371 options_1s.pathLimit = 10_m;
0372 options_1s.maxStepSize = 1_cm;
0373
0374 const auto& end_parameters_1s =
0375 epropagator.propagate(start, *cSurface, options_1s).value().endParameters;
0376
0377
0378 CHECK_CLOSE_REL(end_parameters_1s->position(tgContext),
0379 end_parameters_2s->position(tgContext), 0.001);
0380
0381 BOOST_CHECK(end_parameters_1s->covariance().has_value());
0382 const auto& cov_1s = (*(end_parameters_1s->covariance()));
0383 BOOST_CHECK(end_parameters_2s->covariance().has_value());
0384 const auto& cov_2s = (*(end_parameters_2s->covariance()));
0385
0386
0387 for (unsigned int i = 0; i < cov_1s.rows(); i++) {
0388 for (unsigned int j = 0; j < cov_1s.cols(); j++) {
0389 CHECK_CLOSE_OR_SMALL(cov_1s(i, j), cov_2s(i, j), 0.001, 1e-6);
0390 }
0391 }
0392 }
0393
0394 BOOST_AUTO_TEST_CASE(BasicPropagatorInterface) {
0395 auto field = std::make_shared<ConstantBField>(Vector3{0, 0, 2_T});
0396 EigenStepper<> eigenStepper{field};
0397 VoidNavigator navigator{};
0398
0399 auto startSurface =
0400 Surface::makeShared<PlaneSurface>(Vector3::Zero(), Vector3::UnitX());
0401 auto targetSurface = Surface::makeShared<PlaneSurface>(
0402 Vector3::UnitX() * 20_mm, Vector3::UnitX());
0403
0404 BoundVector startPars;
0405 startPars << 0, 0, 0, M_PI / 2, 1 / 1_GeV, 0;
0406
0407 BoundTrackParameters startParameters{startSurface, startPars, std::nullopt,
0408 ParticleHypothesis::pion()};
0409
0410 CurvilinearTrackParameters startCurv{Vector4::Zero(), Vector3::UnitX(),
0411 1. / 1_GeV, std::nullopt,
0412 ParticleHypothesis::pion()};
0413
0414 GeometryContext gctx;
0415 MagneticFieldContext mctx;
0416 PropagatorOptions<> options{gctx, mctx};
0417
0418 {
0419 Propagator propagator{eigenStepper, navigator};
0420 static_assert(std::is_base_of_v<BasePropagator, decltype(propagator)>,
0421 "Propagator does not inherit from BasePropagator");
0422 const BasePropagator* base =
0423 static_cast<const BasePropagator*>(&propagator);
0424
0425
0426 auto result =
0427 propagator.propagate(startParameters, *targetSurface, options);
0428 BOOST_REQUIRE(result.ok());
0429 BOOST_CHECK_EQUAL(&result.value().endParameters.value().referenceSurface(),
0430 targetSurface.get());
0431
0432 auto resultBase =
0433 base->propagateToSurface(startParameters, *targetSurface, options);
0434
0435 BOOST_REQUIRE(resultBase.ok());
0436 BOOST_CHECK_EQUAL(&resultBase.value().referenceSurface(),
0437 targetSurface.get());
0438
0439 BOOST_CHECK_EQUAL(result.value().endParameters.value().parameters(),
0440 resultBase.value().parameters());
0441
0442
0443 auto resultCurv =
0444 base->propagateToSurface(startCurv, *targetSurface, options);
0445 BOOST_CHECK(resultCurv.ok());
0446 }
0447
0448 StraightLineStepper slStepper{};
0449 {
0450 Propagator propagator{slStepper, navigator};
0451 static_assert(std::is_base_of_v<BasePropagator, decltype(propagator)>,
0452 "Propagator does not inherit from BasePropagator");
0453 const BasePropagator* base =
0454 static_cast<const BasePropagator*>(&propagator);
0455
0456
0457 auto result =
0458 propagator.propagate(startParameters, *targetSurface, options);
0459 BOOST_REQUIRE(result.ok());
0460 BOOST_CHECK_EQUAL(&result.value().endParameters.value().referenceSurface(),
0461 targetSurface.get());
0462
0463 auto resultBase =
0464 base->propagateToSurface(startParameters, *targetSurface, options);
0465
0466 BOOST_REQUIRE(resultBase.ok());
0467 BOOST_CHECK_EQUAL(&resultBase.value().referenceSurface(),
0468 targetSurface.get());
0469
0470 BOOST_CHECK_EQUAL(result.value().endParameters.value().parameters(),
0471 resultBase.value().parameters());
0472
0473
0474 auto resultCurv =
0475 base->propagateToSurface(startCurv, *targetSurface, options);
0476 BOOST_CHECK(resultCurv.ok());
0477 }
0478
0479 EigenStepper<StepperExtensionList<DenseEnvironmentExtension>>
0480 denseEigenStepper{field};
0481
0482 {
0483 Propagator propagator{denseEigenStepper, navigator};
0484 static_assert(!std::is_base_of_v<BasePropagator, decltype(propagator)>,
0485 "Propagator unexpectedly inherits from BasePropagator");
0486 }
0487 }
0488 }