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