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