File indexing completed on 2026-04-07 07:47:07
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/Direction.hpp"
0014 #include "Acts/Definitions/Units.hpp"
0015 #include "Acts/Geometry/GeometryContext.hpp"
0016 #include "Acts/MagneticField/ConstantBField.hpp"
0017 #include "Acts/MagneticField/MagneticFieldContext.hpp"
0018 #include "Acts/Propagator/ActorList.hpp"
0019 #include "Acts/Propagator/EigenStepper.hpp"
0020 #include "Acts/Propagator/Propagator.hpp"
0021 #include "Acts/Propagator/StandardAborters.hpp"
0022 #include "Acts/Propagator/detail/LoopProtection.hpp"
0023 #include "Acts/Utilities/Logger.hpp"
0024 #include "Acts/Utilities/Result.hpp"
0025 #include "ActsTests/CommonHelpers/FloatComparisons.hpp"
0026
0027 #include <cmath>
0028 #include <limits>
0029 #include <memory>
0030 #include <numbers>
0031 #include <random>
0032 #include <string>
0033 #include <utility>
0034
0035 namespace bdata = boost::unit_test::data;
0036
0037 using namespace Acts;
0038 using namespace Acts::UnitLiterals;
0039 using namespace Acts::detail;
0040
0041 namespace ActsTests {
0042
0043
0044 GeometryContext tgContext = GeometryContext::dangerouslyDefaultConstruct();
0045 MagneticFieldContext mfContext = MagneticFieldContext();
0046
0047
0048 struct SteppingState {
0049
0050 Vector3 pos = Vector3(0., 0., 0.);
0051 Vector3 dir = Vector3(0., 0., 1);
0052 double p = 100_MeV;
0053 };
0054
0055
0056 struct Stepper {
0057 Vector3 field = Vector3(0., 0., 2_T);
0058
0059
0060
0061
0062
0063
0064
0065
0066 Result<Vector3> getField(SteppingState& ,
0067 const Vector3& ) const {
0068
0069 return Result<Vector3>::success(field);
0070 }
0071
0072
0073 Vector3 position(const SteppingState& state) const { return state.pos; }
0074
0075
0076 Vector3 direction(const SteppingState& state) const { return state.dir; }
0077
0078
0079 double absoluteMomentum(const SteppingState& state) const { return state.p; }
0080 };
0081
0082
0083 struct NavigationState {
0084 bool navigationBreak = false;
0085 };
0086
0087
0088 struct Options {
0089
0090 double pathLimit = std::numeric_limits<double>::max();
0091 bool loopProtection = true;
0092 double loopFraction = 0.5;
0093 Direction direction = Direction::Forward();
0094
0095 bool debug = false;
0096 std::string debugString;
0097 int debugMsgWidth = 60;
0098 int debugPfxWidth = 30;
0099
0100
0101 ActorList<PathLimitReached> abortList;
0102
0103 const Logger& logger = getDummyLogger();
0104 };
0105
0106
0107 struct PropagatorState {
0108
0109 SteppingState stepping;
0110
0111 NavigationState navigation;
0112
0113 Options options;
0114 };
0115
0116 BOOST_AUTO_TEST_SUITE(PropagatorSuite)
0117
0118
0119
0120 BOOST_DATA_TEST_CASE(
0121 loop_aborter_test,
0122 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 21,
0123 bdata::distribution = std::uniform_real_distribution<double>(
0124 -std::numbers::pi, std::numbers::pi))) ^
0125 bdata::random(
0126 (bdata::engine = std::mt19937(), bdata::seed = 22,
0127 bdata::distribution = std::uniform_real_distribution<double>(
0128 -std::numbers::pi, std::numbers::pi))) ^
0129 bdata::xrange(1),
0130 phi, deltaPhi, index) {
0131 static_cast<void>(index);
0132 static_cast<void>(deltaPhi);
0133
0134 PropagatorState pState;
0135 pState.stepping.dir = Vector3(std::cos(phi), std::sin(phi), 0.);
0136 pState.stepping.p = 100_MeV;
0137
0138 Stepper pStepper;
0139
0140 auto& pathLimit = pState.options.abortList.get<PathLimitReached>();
0141 auto initialLimit = pathLimit.internalLimit;
0142
0143 detail::setupLoopProtection(pState, pStepper, pathLimit, false,
0144 *getDefaultLogger("LoopProt", Logging::INFO));
0145
0146 auto updatedLimit =
0147 pState.options.abortList.get<PathLimitReached>().internalLimit;
0148 BOOST_CHECK_LT(updatedLimit, initialLimit);
0149 }
0150
0151 using BField = ConstantBField;
0152 using EigenStepper = EigenStepper<>;
0153 using EigenPropagator = Propagator<EigenStepper>;
0154
0155 const int ntests = 100;
0156 const int skip = 0;
0157
0158
0159
0160 BOOST_DATA_TEST_CASE(
0161 propagator_loop_protection_test,
0162 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 20,
0163 bdata::distribution = std::uniform_real_distribution<double>(
0164 0.5_GeV, 10_GeV))) ^
0165 bdata::random(
0166 (bdata::engine = std::mt19937(), bdata::seed = 21,
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 = 22,
0171 bdata::distribution = std::uniform_real_distribution<double>(
0172 1., std::numbers::pi - 1.))) ^
0173 bdata::random((bdata::engine = std::mt19937(), bdata::seed = 23,
0174 bdata::distribution =
0175 std::uniform_int_distribution<std::uint8_t>(0, 1))) ^
0176 bdata::xrange(ntests),
0177 pT, phi, theta, charge, index) {
0178 if (index < skip) {
0179 return;
0180 }
0181
0182 double px = pT * std::cos(phi);
0183 double py = pT * std::sin(phi);
0184 double pz = pT / std::tan(theta);
0185 double p = pT / std::sin(theta);
0186 double q = -1 + 2 * charge;
0187
0188 const double Bz = 2_T;
0189 auto bField = std::make_shared<BField>(Vector3{0, 0, Bz});
0190 EigenStepper estepper(bField);
0191 EigenPropagator epropagator(std::move(estepper));
0192
0193
0194 BoundTrackParameters start = BoundTrackParameters::createCurvilinear(
0195 Vector4(0, 0, 0, 42), phi, theta, q / p, std::nullopt,
0196 ParticleHypothesis::pion());
0197
0198 using PropagatorOptions = EigenPropagator::Options<ActorList<>>;
0199 PropagatorOptions options(tgContext, mfContext);
0200 options.maxSteps = 1e6;
0201 const auto& result = epropagator.propagate(start, options).value();
0202
0203
0204 CHECK_CLOSE_REL(px, -result.endParameters->momentum().x(), 1e-2);
0205 CHECK_CLOSE_REL(py, -result.endParameters->momentum().y(), 1e-2);
0206 CHECK_CLOSE_REL(pz, result.endParameters->momentum().z(), 1e-2);
0207 }
0208
0209 BOOST_AUTO_TEST_SUITE_END()
0210
0211 }