Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-16 08:19:49

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #pragma once
0010 
0011 // The step body, instantiated once per mode.
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   // The kernel reads the start field before writing the last sample, so the
0063   // two can share storage; a copy would sit on the loop-carried chain.
0064   Vector3& lastField = *state.field;
0065 
0066   // Read once: the kernel writes through spans that point into `state`, so a
0067   // later read would be ordered behind all of its stores.
0068   const double stepTolerance = state.options.stepTolerance;
0069 
0070   const auto calcStepSizeScaling = [&](const double errorEstimate_) -> double {
0071     // For details about these values see ATL-SOFT-PUB-2009-001
0072     constexpr double lower = 0.25;
0073     constexpr double upper = 4.0;
0074     // This is given by the order of the Runge-Kutta method
0075     constexpr double exponent = 0.25;
0076 
0077     double x = stepTolerance / errorEstimate_;
0078 
0079     if constexpr (exponent == 0.25) {
0080       // This is 3x faster than std::pow
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     // For details about the factor 4 see ATL-SOFT-PUB-2009-001
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         // the combined kernel: this cold branch wants no more inlined into it
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       // No jacobian, so no path derivatives either.
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     // Protect against division by zero
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     // If step size becomes too small the particle remains at the initial
0159     // place
0160     if (std::abs(h) < std::abs(state.options.stepSizeCutOff)) {
0161       // Not moving due to too low momentum needs an aborter
0162       return EigenStepperError::StepSizeStalled;
0163     }
0164 
0165     // If the parameter is off track too much or given stepSize is not
0166     // appropriate
0167     if (nStepTrials > state.options.maxRungeKuttaStepTrials) {
0168       // Too many trials, have to abort
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 }  // namespace Acts