File indexing completed on 2026-09-16 08:19:49
0001
0002
0003
0004
0005
0006
0007
0008
0009 #pragma once
0010
0011
0012
0013 #include "Acts/Material/IVolumeMaterial.hpp"
0014 #include "Acts/Propagator/EigenStepperError.hpp"
0015 #include "Acts/Propagator/SympyStepper.hpp"
0016 #include "Acts/Propagator/detail/SympyStepperDenseStep.hpp"
0017 #include "Acts/Propagator/detail/SympyStepperStatus.hpp"
0018 #include "Acts/Utilities/Result.hpp"
0019
0020 #include <cmath>
0021 #include <span>
0022
0023 #include "SympyStepperStep.hpp"
0024 #include "codegen/sympy_stepper_math.hpp"
0025
0026 namespace Acts {
0027
0028 template <detail::SympyStepMode Mode, bool WithJac>
0029 Result<double> detail::sympyStep(const SympyStepper& stepper,
0030 SympyStepper::State& state, Direction propDir,
0031 const IVolumeMaterial* material) {
0032 constexpr bool isDense = Mode == SympyStepMode::Dense;
0033
0034 double h = state.stepSize.value() * propDir;
0035
0036 const double initialH = h;
0037
0038 const Vector3 pos = stepper.position(state);
0039 const Vector3 dir = stepper.direction(state);
0040 const double t = stepper.time(state);
0041 const double qop = stepper.qOverP(state);
0042 const double pabs = stepper.absoluteMomentum(state);
0043 const double m = stepper.particleHypothesis(state).mass();
0044
0045 if constexpr (isDense) {
0046 if (material != nullptr && pabs < state.options.dense.momentumCutOff) {
0047 return EigenStepperError::StepInvalid;
0048 }
0049 }
0050
0051 const auto getB = [&](std::span<const double, 3> p) -> Result<Vector3> {
0052 return stepper.getField(state, {p[0], p[1], p[2]});
0053 };
0054
0055 if (!state.field.has_value()) {
0056 auto fieldRes = stepper.getField(state, pos);
0057 if (!fieldRes.ok()) {
0058 return fieldRes.error();
0059 }
0060 state.field = *fieldRes;
0061 }
0062
0063
0064 Vector3& lastField = *state.field;
0065
0066
0067
0068 const double stepTolerance = state.options.stepTolerance;
0069
0070 const auto calcStepSizeScaling = [&](const double errorEstimate_) -> double {
0071
0072 constexpr double lower = 0.25;
0073 constexpr double upper = 4.0;
0074
0075 constexpr double exponent = 0.25;
0076
0077 double x = stepTolerance / errorEstimate_;
0078
0079 if constexpr (exponent == 0.25) {
0080
0081 x = std::sqrt(std::sqrt(x));
0082 } else {
0083 x = std::pow(x, exponent);
0084 }
0085
0086 return std::clamp(x, lower, upper);
0087 };
0088
0089 std::size_t nStepTrials = 0;
0090 double errorEstimate = 0.;
0091
0092 while (true) {
0093 ++nStepTrials;
0094 ++state.statistics.nAttemptedSteps;
0095
0096
0097 std::error_code fieldError;
0098 const std::span<const double, 3> startPos(pos.data(), 3);
0099 const std::span<const double, 3> startDir(dir.data(), 3);
0100 const std::span<double, 3> endPos(
0101 state.pars.template segment<3>(eFreePos0).data(), 3);
0102 const std::span<double, 3> endDir(
0103 state.pars.template segment<3>(eFreeDir0).data(), 3);
0104 Rk4Status status{};
0105 if constexpr (isDense) {
0106 const std::span<double, 8> derivative(state.derivative.data(), 8);
0107 const std::span<double> jac =
0108 WithJac ? std::span<double>(state.jacToGlobal.data(),
0109 state.jacToGlobal.size())
0110 : std::span<double>();
0111 if (material == nullptr) {
0112
0113 status = rk4_vacuum(startPos, startDir, t, h, qop, m, pabs,
0114 std::span<const double, 3>(state.field->data(), 3),
0115 getB, errorEstimate, 4 * stepTolerance, fieldError,
0116 endPos, state.pars[eFreeTime], endDir,
0117 std::span<double, 3>(lastField.data(), 3),
0118 derivative, jac);
0119 } else {
0120 status = sympyDenseStep<WithJac>(stepper, state, *material, h,
0121 4 * stepTolerance, errorEstimate,
0122 lastField, fieldError, jac);
0123 }
0124 } else if constexpr (WithJac) {
0125 status =
0126 rk4_vacuum_jac(startPos, startDir, t, h, qop, m, pabs,
0127 std::span<const double, 3>(state.field->data(), 3),
0128 getB, errorEstimate, 4 * stepTolerance, fieldError,
0129 endPos, state.pars[eFreeTime], endDir,
0130 std::span<double, 3>(lastField.data(), 3),
0131 std::span<double, 8>(state.derivative.data(), 8),
0132 std::span<double>(state.jacToGlobal.data(),
0133 state.jacToGlobal.size()));
0134 } else {
0135
0136 status =
0137 rk4_vacuum_nojac(startPos, startDir, t, h, qop, m, pabs,
0138 std::span<const double, 3>(state.field->data(), 3),
0139 getB, errorEstimate, 4 * stepTolerance, fieldError,
0140 endPos, state.pars[eFreeTime], endDir,
0141 std::span<double, 3>(lastField.data(), 3));
0142 }
0143 if (status == Rk4Status::FieldError) {
0144 return fieldError;
0145 }
0146
0147 errorEstimate = std::max(1e-20, errorEstimate);
0148
0149 if (status == Rk4Status::Accepted) {
0150 break;
0151 }
0152
0153 ++state.statistics.nRejectedSteps;
0154
0155 const double stepSizeScaling = calcStepSizeScaling(errorEstimate);
0156 h *= stepSizeScaling;
0157
0158
0159
0160 if (std::abs(h) < std::abs(state.options.stepSizeCutOff)) {
0161
0162 return EigenStepperError::StepSizeStalled;
0163 }
0164
0165
0166
0167 if (nStepTrials > state.options.maxRungeKuttaStepTrials) {
0168
0169 return EigenStepperError::StepSizeAdjustmentFailed;
0170 }
0171 }
0172
0173 state.pathAccumulated += h;
0174 ++state.nSteps;
0175 state.nStepTrials += nStepTrials;
0176
0177 ++state.statistics.nSuccessfulSteps;
0178 if (propDir != Direction::fromScalarZeroAsPositive(initialH)) {
0179 ++state.statistics.nReverseSteps;
0180 }
0181 state.statistics.pathLength += h;
0182 state.statistics.absolutePathLength += std::abs(h);
0183
0184 const double stepSizeScaling = calcStepSizeScaling(errorEstimate);
0185 const double nextAccuracy = std::abs(h * stepSizeScaling);
0186 const double previousAccuracy = std::abs(state.stepSize.accuracy());
0187 const double initialStepLength = std::abs(initialH);
0188 if (nextAccuracy < initialStepLength || nextAccuracy > previousAccuracy) {
0189 state.stepSize.setAccuracy(nextAccuracy);
0190 }
0191
0192 if constexpr (isDense) {
0193 if (material != nullptr || !state.materialEffectsAccumulator.isVacuum()) {
0194 if (state.materialEffectsAccumulator.isVacuum()) {
0195 state.materialEffectsAccumulator.initialize(
0196 state.options.maxXOverX0Step, stepper.particleHypothesis(state),
0197 pabs);
0198 }
0199
0200 Material mat =
0201 material != nullptr ? material->material(pos) : Material::Vacuum();
0202
0203 state.materialEffectsAccumulator.accumulate(mat, propDir * h, qop,
0204 stepper.qOverP(state));
0205 }
0206 }
0207
0208 return h;
0209 }
0210
0211 }