File indexing completed on 2025-01-18 09:12:48
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/GenericBoundTrackParameters.hpp"
0016 #include "Acts/EventData/GenericCurvilinearTrackParameters.hpp"
0017 #include "Acts/EventData/TrackParameters.hpp"
0018 #include "Acts/Geometry/GeometryContext.hpp"
0019 #include "Acts/Geometry/GeometryIdentifier.hpp"
0020 #include "Acts/MagneticField/ConstantBField.hpp"
0021 #include "Acts/MagneticField/MagneticFieldContext.hpp"
0022 #include "Acts/Propagator/ActorList.hpp"
0023 #include "Acts/Propagator/ConstrainedStep.hpp"
0024 #include "Acts/Propagator/EigenStepper.hpp"
0025 #include "Acts/Propagator/EigenStepperDenseExtension.hpp"
0026 #include "Acts/Propagator/Propagator.hpp"
0027 #include "Acts/Propagator/StraightLineStepper.hpp"
0028 #include "Acts/Propagator/VoidNavigator.hpp"
0029 #include "Acts/Surfaces/CurvilinearSurface.hpp"
0030 #include "Acts/Surfaces/CylinderBounds.hpp"
0031 #include "Acts/Surfaces/CylinderSurface.hpp"
0032 #include "Acts/Surfaces/PlaneSurface.hpp"
0033 #include "Acts/Surfaces/Surface.hpp"
0034 #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp"
0035 #include "Acts/Utilities/Logger.hpp"
0036 #include "Acts/Utilities/Result.hpp"
0037
0038 #include <cmath>
0039 #include <cstddef>
0040 #include <limits>
0041 #include <memory>
0042 #include <numbers>
0043 #include <optional>
0044 #include <random>
0045 #include <type_traits>
0046 #include <utility>
0047
0048 namespace bdata = boost::unit_test::data;
0049 using namespace Acts::UnitLiterals;
0050 using Acts::VectorHelpers::makeVector4;
0051 using Acts::VectorHelpers::perp;
0052
0053 namespace Acts::Test {
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
0149 BOOST_AUTO_TEST_CASE(PropagatorOptions_) {
0150 using NullOptionsType = EigenPropagatorType::Options<>;
0151 NullOptionsType null_options(tgContext, mfContext);
0152
0153 using ActorList = ActorList<PerpendicularMeasure>;
0154 using OptionsType = EigenPropagatorType::Options<ActorList>;
0155 OptionsType options(tgContext, mfContext);
0156 }
0157
0158 BOOST_DATA_TEST_CASE(
0159 cylinder_passage_observer_,
0160 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 0,
0161 bdata::distribution = std::uniform_real_distribution<double>(
0162 0.4_GeV, 10_GeV))) ^
0163 bdata::random(
0164 (bdata::engine = std::mt19937(), bdata::seed = 1,
0165 bdata::distribution = std::uniform_real_distribution<double>(
0166 -std::numbers::pi, std::numbers::pi))) ^
0167 bdata::random(
0168 (bdata::engine = std::mt19937(), bdata::seed = 2,
0169 bdata::distribution = std::uniform_real_distribution<double>(
0170 1., std::numbers::pi - 1.))) ^
0171 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 3,
0172 bdata::distribution =
0173 std::uniform_int_distribution<std::uint8_t>(0, 1))) ^
0174 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 4,
0175 bdata::distribution =
0176 std::uniform_real_distribution<double>(-1_ns,
0177 1_ns))) ^
0178 bdata::xrange(ntests),
0179 pT, phi, theta, charge, time, index) {
0180 double dcharge = -1 + 2 * charge;
0181 (void)index;
0182
0183 using CylinderObserver = SurfaceObserver<CylinderSurface>;
0184 using ActorList = ActorList<CylinderObserver>;
0185
0186
0187 EigenPropagatorType::Options<ActorList> options(tgContext, mfContext);
0188
0189 options.pathLimit = 20_m;
0190 options.stepping.maxStepSize = 1_cm;
0191
0192
0193 options.actorList.get<CylinderObserver>().surface = mSurface.get();
0194
0195 using so_result = typename CylinderObserver::result_type;
0196
0197
0198 double x = 0;
0199 double y = 0;
0200 double z = 0;
0201 double px = pT * cos(phi);
0202 double py = pT * sin(phi);
0203 double pz = pT / tan(theta);
0204 double q = dcharge;
0205 Vector3 pos(x, y, z);
0206 Vector3 mom(px, py, pz);
0207 CurvilinearTrackParameters start(makeVector4(pos, time), mom.normalized(),
0208 q / mom.norm(), std::nullopt,
0209 ParticleHypothesis::pion());
0210
0211 const auto& result = epropagator.propagate(start, *cSurface, options).value();
0212 auto& sor = result.get<so_result>();
0213
0214 BOOST_CHECK_EQUAL(sor.surfaces_passed, 1u);
0215 CHECK_CLOSE_ABS(sor.surface_passed_r, 10., 1e-5);
0216 }
0217
0218 BOOST_DATA_TEST_CASE(
0219 curvilinear_additive_,
0220 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 0,
0221 bdata::distribution = std::uniform_real_distribution<double>(
0222 0.4_GeV, 10_GeV))) ^
0223 bdata::random(
0224 (bdata::engine = std::mt19937(), bdata::seed = 1,
0225 bdata::distribution = std::uniform_real_distribution<double>(
0226 -std::numbers::pi, std::numbers::pi))) ^
0227 bdata::random(
0228 (bdata::engine = std::mt19937(), bdata::seed = 2,
0229 bdata::distribution = std::uniform_real_distribution<double>(
0230 1., std::numbers::pi - 1.))) ^
0231 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 3,
0232 bdata::distribution =
0233 std::uniform_int_distribution<std::uint8_t>(0, 1))) ^
0234 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 4,
0235 bdata::distribution =
0236 std::uniform_real_distribution<double>(-1_ns,
0237 1_ns))) ^
0238 bdata::xrange(ntests),
0239 pT, phi, theta, charge, time, index) {
0240 double dcharge = -1 + 2 * charge;
0241 (void)index;
0242
0243
0244 EigenPropagatorType::Options<> options_2s(tgContext, mfContext);
0245 options_2s.pathLimit = 50_cm;
0246 options_2s.stepping.maxStepSize = 1_cm;
0247
0248
0249 double x = 0;
0250 double y = 0;
0251 double z = 0;
0252 double px = pT * cos(phi);
0253 double py = pT * sin(phi);
0254 double pz = pT / tan(theta);
0255 double q = dcharge;
0256 Vector3 pos(x, y, z);
0257 Vector3 mom(px, py, pz);
0258
0259 Covariance cov;
0260
0261 cov << 10_mm, 0, 0.123, 0, 0.5, 0, 0, 10_mm, 0, 0.162, 0, 0, 0.123, 0, 0.1, 0,
0262 0, 0, 0, 0.162, 0, 0.1, 0, 0, 0.5, 0, 0, 0, 1. / (10_GeV), 0, 0, 0, 0, 0,
0263 0, 0;
0264 CurvilinearTrackParameters start(makeVector4(pos, time), mom.normalized(),
0265 q / mom.norm(), cov,
0266 ParticleHypothesis::pion());
0267
0268 const auto& mid_parameters =
0269 epropagator.propagate(start, options_2s).value().endParameters;
0270 const auto& end_parameters_2s =
0271 epropagator.propagate(*mid_parameters, options_2s).value().endParameters;
0272
0273
0274 EigenPropagatorType::Options<> options_1s(tgContext, mfContext);
0275 options_1s.pathLimit = 100_cm;
0276 options_1s.stepping.maxStepSize = 1_cm;
0277
0278 const auto& end_parameters_1s =
0279 epropagator.propagate(start, options_1s).value().endParameters;
0280
0281
0282 CHECK_CLOSE_REL(end_parameters_1s->position(tgContext),
0283 end_parameters_2s->position(tgContext), 0.001);
0284
0285 BOOST_CHECK(end_parameters_1s->covariance().has_value());
0286 const auto& cov_1s = *(end_parameters_1s->covariance());
0287 BOOST_CHECK(end_parameters_2s->covariance().has_value());
0288 const auto& cov_2s = *(end_parameters_2s->covariance());
0289
0290
0291 for (unsigned int i = 0; i < cov_1s.rows(); i++) {
0292 for (unsigned int j = 0; j < cov_1s.cols(); j++) {
0293 CHECK_CLOSE_OR_SMALL(cov_1s(i, j), cov_2s(i, j), 0.001, 1e-6);
0294 }
0295 }
0296 }
0297
0298 BOOST_DATA_TEST_CASE(
0299 cylinder_additive_,
0300 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 0,
0301 bdata::distribution = std::uniform_real_distribution<double>(
0302 0.4_GeV, 10_GeV))) ^
0303 bdata::random(
0304 (bdata::engine = std::mt19937(), bdata::seed = 1,
0305 bdata::distribution = std::uniform_real_distribution<double>(
0306 -std::numbers::pi, std::numbers::pi))) ^
0307 bdata::random(
0308 (bdata::engine = std::mt19937(), bdata::seed = 2,
0309 bdata::distribution = std::uniform_real_distribution<double>(
0310 1., std::numbers::pi - 1.))) ^
0311 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 3,
0312 bdata::distribution =
0313 std::uniform_int_distribution<std::uint8_t>(0, 1))) ^
0314 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 4,
0315 bdata::distribution =
0316 std::uniform_real_distribution<double>(-1_ns,
0317 1_ns))) ^
0318 bdata::xrange(ntests),
0319 pT, phi, theta, charge, time, index) {
0320 double dcharge = -1 + 2 * charge;
0321 (void)index;
0322
0323
0324 EigenPropagatorType::Options<> options_2s(tgContext, mfContext);
0325 options_2s.pathLimit = 10_m;
0326 options_2s.stepping.maxStepSize = 1_cm;
0327
0328
0329 double x = 0;
0330 double y = 0;
0331 double z = 0;
0332 double px = pT * cos(phi);
0333 double py = pT * sin(phi);
0334 double pz = pT / tan(theta);
0335 double q = dcharge;
0336 Vector3 pos(x, y, z);
0337 Vector3 mom(px, py, pz);
0338
0339 Covariance cov;
0340
0341 cov << 10_mm, 0, 0.123, 0, 0.5, 0, 0, 10_mm, 0, 0.162, 0, 0, 0.123, 0, 0.1, 0,
0342 0, 0, 0, 0.162, 0, 0.1, 0, 0, 0.5, 0, 0, 0, 1. / (10_GeV), 0, 0, 0, 0, 0,
0343 0, 0;
0344 CurvilinearTrackParameters start(makeVector4(pos, time), mom.normalized(),
0345 q / mom.norm(), cov,
0346 ParticleHypothesis::pion());
0347
0348 const auto& mid_parameters =
0349 epropagator.propagate(start, *mSurface, options_2s).value().endParameters;
0350
0351 const auto& end_parameters_2s =
0352 epropagator.propagate(*mid_parameters, *cSurface, options_2s)
0353 .value()
0354 .endParameters;
0355
0356
0357 EigenPropagatorType::Options<> options_1s(tgContext, mfContext);
0358 options_1s.pathLimit = 10_m;
0359 options_1s.stepping.maxStepSize = 1_cm;
0360
0361 const auto& end_parameters_1s =
0362 epropagator.propagate(start, *cSurface, options_1s).value().endParameters;
0363
0364
0365 CHECK_CLOSE_REL(end_parameters_1s->position(tgContext),
0366 end_parameters_2s->position(tgContext), 0.001);
0367
0368 BOOST_CHECK(end_parameters_1s->covariance().has_value());
0369 const auto& cov_1s = (*(end_parameters_1s->covariance()));
0370 BOOST_CHECK(end_parameters_2s->covariance().has_value());
0371 const auto& cov_2s = (*(end_parameters_2s->covariance()));
0372
0373
0374 for (unsigned int i = 0; i < cov_1s.rows(); i++) {
0375 for (unsigned int j = 0; j < cov_1s.cols(); j++) {
0376 CHECK_CLOSE_OR_SMALL(cov_1s(i, j), cov_2s(i, j), 0.001, 1e-6);
0377 }
0378 }
0379 }
0380
0381 BOOST_AUTO_TEST_CASE(BasicPropagatorInterface) {
0382 auto field = std::make_shared<ConstantBField>(Vector3{0, 0, 2_T});
0383 EigenStepper<> eigenStepper{field};
0384 VoidNavigator navigator{};
0385
0386 auto startSurface =
0387 CurvilinearSurface(Vector3::Zero(), Vector3::UnitX()).planeSurface();
0388 auto targetSurface =
0389 CurvilinearSurface(Vector3::UnitX() * 20_mm, Vector3::UnitX())
0390 .planeSurface();
0391
0392 BoundVector startPars;
0393 startPars << 0, 0, 0, std::numbers::pi / 2., 1 / 1_GeV, 0;
0394
0395 BoundTrackParameters startParameters{startSurface, startPars, std::nullopt,
0396 ParticleHypothesis::pion()};
0397
0398 CurvilinearTrackParameters startCurv{Vector4::Zero(), Vector3::UnitX(),
0399 1. / 1_GeV, std::nullopt,
0400 ParticleHypothesis::pion()};
0401
0402 GeometryContext gctx;
0403 MagneticFieldContext mctx;
0404
0405 {
0406 EigenPropagatorType::Options<> options{gctx, mctx};
0407
0408 Propagator propagator{eigenStepper, navigator};
0409 static_assert(std::is_base_of_v<BasePropagator, decltype(propagator)>,
0410 "Propagator does not inherit from BasePropagator");
0411 const BasePropagator* base =
0412 static_cast<const BasePropagator*>(&propagator);
0413
0414
0415 auto result =
0416 propagator.propagate(startParameters, *targetSurface, options);
0417 BOOST_REQUIRE(result.ok());
0418 BOOST_CHECK_EQUAL(&result.value().endParameters.value().referenceSurface(),
0419 targetSurface.get());
0420
0421 auto resultBase =
0422 base->propagateToSurface(startParameters, *targetSurface, options);
0423
0424 BOOST_REQUIRE(resultBase.ok());
0425 BOOST_CHECK_EQUAL(&resultBase.value().referenceSurface(),
0426 targetSurface.get());
0427
0428 BOOST_CHECK_EQUAL(result.value().endParameters.value().parameters(),
0429 resultBase.value().parameters());
0430
0431
0432 auto resultCurv =
0433 base->propagateToSurface(startCurv, *targetSurface, 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, options);
0456
0457 BOOST_REQUIRE(resultBase.ok());
0458 BOOST_CHECK_EQUAL(&resultBase.value().referenceSurface(),
0459 targetSurface.get());
0460
0461 BOOST_CHECK_EQUAL(result.value().endParameters.value().parameters(),
0462 resultBase.value().parameters());
0463
0464
0465 auto resultCurv =
0466 base->propagateToSurface(startCurv, *targetSurface, options);
0467 BOOST_CHECK(resultCurv.ok());
0468 }
0469
0470 EigenStepper<EigenStepperDenseExtension> denseEigenStepper{field};
0471
0472 {
0473 Propagator propagator{denseEigenStepper, navigator};
0474 static_assert(!std::is_base_of_v<BasePropagator, decltype(propagator)>,
0475 "Propagator unexpectedly inherits from BasePropagator");
0476 }
0477 }
0478 }