File indexing completed on 2025-07-01 07:52:54
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "Acts/Propagator/SympyStepper.hpp"
0010
0011 #include "Acts/Propagator/EigenStepperError.hpp"
0012 #include "Acts/Propagator/detail/SympyCovarianceEngine.hpp"
0013 #include "Acts/Propagator/detail/SympyJacobianEngine.hpp"
0014
0015 #include <cmath>
0016
0017 #include "codegen/sympy_stepper_math.hpp"
0018
0019 namespace Acts {
0020
0021 SympyStepper::SympyStepper(std::shared_ptr<const MagneticFieldProvider> bField)
0022 : m_bField(std::move(bField)) {}
0023
0024 SympyStepper::SympyStepper(const Config& config) : m_bField(config.bField) {}
0025
0026 SympyStepper::State SympyStepper::makeState(const Options& options) const {
0027 State state{options, m_bField->makeCache(options.magFieldContext)};
0028 return state;
0029 }
0030
0031 void SympyStepper::initialize(State& state,
0032 const BoundTrackParameters& par) const {
0033 return initialize(state, par.parameters(), par.covariance(),
0034 par.particleHypothesis(), par.referenceSurface());
0035 }
0036
0037 void SympyStepper::initialize(State& state, const BoundVector& boundParams,
0038 const std::optional<BoundMatrix>& cov,
0039 ParticleHypothesis particleHypothesis,
0040 const Surface& surface) const {
0041 FreeVector freeParams = transformBoundToFreeParameters(
0042 surface, state.options.geoContext, boundParams);
0043
0044 state.particleHypothesis = particleHypothesis;
0045
0046 state.pathAccumulated = 0;
0047 state.nSteps = 0;
0048 state.nStepTrials = 0;
0049 state.stepSize = ConstrainedStep();
0050 state.stepSize.setAccuracy(state.options.initialStepSize);
0051 state.stepSize.setUser(state.options.maxStepSize);
0052 state.previousStepSize = 0;
0053 state.statistics = StepperStatistics();
0054
0055 state.pars = freeParams;
0056
0057
0058 state.covTransport = cov.has_value();
0059 if (state.covTransport) {
0060
0061 state.cov = *cov;
0062 state.jacToGlobal = surface.boundToFreeJacobian(
0063 state.options.geoContext, freeParams.segment<3>(eFreePos0),
0064 freeParams.segment<3>(eFreeDir0));
0065 state.jacobian = BoundMatrix::Identity();
0066 state.jacTransport = FreeMatrix::Identity();
0067 state.derivative = FreeVector::Zero();
0068 }
0069 }
0070
0071 Result<std::tuple<BoundTrackParameters, BoundMatrix, double>>
0072 SympyStepper::boundState(
0073 State& state, const Surface& surface, bool transportCov,
0074 const FreeToBoundCorrection& freeToBoundCorrection) const {
0075 return detail::sympy::boundState(
0076 state.options.geoContext, surface, state.cov, state.jacobian,
0077 state.jacTransport, state.derivative, state.jacToGlobal, state.pars,
0078 state.particleHypothesis, state.covTransport && transportCov,
0079 state.pathAccumulated, freeToBoundCorrection);
0080 }
0081
0082 bool SympyStepper::prepareCurvilinearState(State& state) const {
0083
0084 (void)state;
0085 return true;
0086 }
0087
0088 std::tuple<BoundTrackParameters, BoundMatrix, double>
0089 SympyStepper::curvilinearState(State& state, bool transportCov) const {
0090 return detail::sympy::curvilinearState(
0091 state.cov, state.jacobian, state.jacTransport, state.derivative,
0092 state.jacToGlobal, state.pars, state.particleHypothesis,
0093 state.covTransport && transportCov, state.pathAccumulated);
0094 }
0095
0096 void SympyStepper::update(State& state, const FreeVector& freeParams,
0097 const BoundVector& ,
0098 const Covariance& covariance,
0099 const Surface& surface) const {
0100 state.pars = freeParams;
0101 state.cov = covariance;
0102 state.jacToGlobal = surface.boundToFreeJacobian(
0103 state.options.geoContext, freeParams.template segment<3>(eFreePos0),
0104 freeParams.template segment<3>(eFreeDir0));
0105 }
0106
0107 void SympyStepper::update(State& state, const Vector3& uposition,
0108 const Vector3& udirection, double qOverP,
0109 double time) const {
0110 state.pars.template segment<3>(eFreePos0) = uposition;
0111 state.pars.template segment<3>(eFreeDir0) = udirection;
0112 state.pars[eFreeTime] = time;
0113 state.pars[eFreeQOverP] = qOverP;
0114 }
0115
0116 void SympyStepper::transportCovarianceToCurvilinear(State& state) const {
0117 detail::sympy::transportCovarianceToCurvilinear(
0118 state.cov, state.jacobian, state.jacTransport, state.derivative,
0119 state.jacToGlobal, state.pars.template segment<3>(eFreeDir0));
0120 }
0121
0122 void SympyStepper::transportCovarianceToBound(
0123 State& state, const Surface& surface,
0124 const FreeToBoundCorrection& freeToBoundCorrection) const {
0125 detail::sympy::transportCovarianceToBound(
0126 state.options.geoContext, surface, state.cov, state.jacobian,
0127 state.jacTransport, state.derivative, state.jacToGlobal, state.pars,
0128 freeToBoundCorrection);
0129 }
0130
0131 Result<double> SympyStepper::step(State& state, Direction propDir,
0132 const IVolumeMaterial* material) const {
0133 (void)material;
0134
0135 auto pos = position(state);
0136 auto dir = direction(state);
0137 double t = time(state);
0138 double qop = qOverP(state);
0139 double m = particleHypothesis(state).mass();
0140 double p_abs = absoluteMomentum(state);
0141
0142 auto getB = [&](const double* p) -> Result<Vector3> {
0143 return getField(state, {p[0], p[1], p[2]});
0144 };
0145
0146 const auto calcStepSizeScaling = [&](const double errorEstimate_) -> double {
0147
0148 constexpr double lower = 0.25;
0149 constexpr double upper = 4.0;
0150
0151 constexpr double exponent = 0.25;
0152
0153 double x = state.options.stepTolerance / errorEstimate_;
0154
0155 if constexpr (exponent == 0.25) {
0156
0157 x = std::sqrt(std::sqrt(x));
0158 } else {
0159 x = std::pow(x, exponent);
0160 }
0161
0162 return std::clamp(x, lower, upper);
0163 };
0164
0165 double h = state.stepSize.value() * propDir;
0166 double initialH = h;
0167 std::size_t nStepTrials = 0;
0168 double errorEstimate = 0.;
0169
0170 while (true) {
0171 ++nStepTrials;
0172 ++state.statistics.nAttemptedSteps;
0173
0174
0175 Result<bool> res =
0176 rk4(pos.data(), dir.data(), t, h, qop, m, p_abs, getB, &errorEstimate,
0177 4 * state.options.stepTolerance,
0178 state.pars.template segment<3>(eFreePos0).data(),
0179 state.pars.template segment<3>(eFreeDir0).data(),
0180 state.pars.template segment<1>(eFreeTime).data(),
0181 state.derivative.data(),
0182 state.covTransport ? state.jacTransport.data() : nullptr);
0183 if (!res.ok()) {
0184 return res.error();
0185 }
0186
0187 errorEstimate = std::max(1e-20, errorEstimate);
0188
0189 if (*res) {
0190 break;
0191 }
0192
0193 ++state.statistics.nRejectedSteps;
0194
0195 const double stepSizeScaling = calcStepSizeScaling(errorEstimate);
0196 h *= stepSizeScaling;
0197
0198
0199
0200 if (std::abs(h) < std::abs(state.options.stepSizeCutOff)) {
0201
0202 return EigenStepperError::StepSizeStalled;
0203 }
0204
0205
0206
0207 if (nStepTrials > state.options.maxRungeKuttaStepTrials) {
0208
0209 return EigenStepperError::StepSizeAdjustmentFailed;
0210 }
0211 }
0212
0213 state.pathAccumulated += h;
0214 ++state.nSteps;
0215 state.nStepTrials += nStepTrials;
0216
0217 ++state.statistics.nSuccessfulSteps;
0218 if (propDir != Direction::fromScalarZeroAsPositive(initialH)) {
0219 ++state.statistics.nReverseSteps;
0220 }
0221 state.statistics.pathLength += h;
0222 state.statistics.absolutePathLength += std::abs(h);
0223
0224 const double stepSizeScaling = calcStepSizeScaling(errorEstimate);
0225 const double nextAccuracy = std::abs(h * stepSizeScaling);
0226 const double previousAccuracy = std::abs(state.stepSize.accuracy());
0227 const double initialStepLength = std::abs(initialH);
0228 if (nextAccuracy < initialStepLength || nextAccuracy > previousAccuracy) {
0229 state.stepSize.setAccuracy(nextAccuracy);
0230 }
0231
0232 return h;
0233 }
0234
0235 void SympyStepper::setIdentityJacobian(State& state) const {
0236 state.jacobian = BoundMatrix::Identity();
0237 }
0238
0239 }