Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-07-13 08:04:55

0001 // This file is part of the Acts project.
0002 //
0003 // Copyright (C) 2019-2022 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 http://mozilla.org/MPL/2.0/.
0008 
0009 #include "Acts/EventData/TransformationHelpers.hpp"
0010 #include "Acts/Propagator/ConstrainedStep.hpp"
0011 #include "Acts/Propagator/detail/CovarianceEngine.hpp"
0012 #include "Acts/Utilities/QuickMath.hpp"
0013 
0014 #include <limits>
0015 
0016 template <typename E, typename A>
0017 Acts::EigenStepper<E, A>::EigenStepper(
0018     std::shared_ptr<const MagneticFieldProvider> bField)
0019     : m_bField(std::move(bField)) {}
0020 
0021 template <typename E, typename A>
0022 auto Acts::EigenStepper<E, A>::makeState(
0023     std::reference_wrapper<const GeometryContext> gctx,
0024     std::reference_wrapper<const MagneticFieldContext> mctx,
0025     const BoundTrackParameters& par, double ssize) const -> State {
0026   return State{gctx, m_bField->makeCache(mctx), par, ssize};
0027 }
0028 
0029 template <typename E, typename A>
0030 void Acts::EigenStepper<E, A>::resetState(State& state,
0031                                           const BoundVector& boundParams,
0032                                           const BoundSquareMatrix& cov,
0033                                           const Surface& surface,
0034                                           const double stepSize) const {
0035   FreeVector freeParams =
0036       transformBoundToFreeParameters(surface, state.geoContext, boundParams);
0037 
0038   // Update the stepping state
0039   state.pars = freeParams;
0040   state.cov = cov;
0041   state.stepSize = ConstrainedStep(stepSize);
0042   state.pathAccumulated = 0.;
0043 
0044   // Reinitialize the stepping jacobian
0045   state.jacToGlobal = surface.boundToFreeJacobian(
0046       state.geoContext, freeParams.template segment<3>(eFreePos0),
0047       freeParams.template segment<3>(eFreeDir0));
0048   state.jacobian = BoundMatrix::Identity();
0049   state.jacTransport = FreeMatrix::Identity();
0050   state.derivative = FreeVector::Zero();
0051 }
0052 
0053 template <typename E, typename A>
0054 auto Acts::EigenStepper<E, A>::boundState(
0055     State& state, const Surface& surface, bool transportCov,
0056     const FreeToBoundCorrection& freeToBoundCorrection) const
0057     -> Result<BoundState> {
0058   return detail::boundState(
0059       state.geoContext, surface, state.cov, state.jacobian, state.jacTransport,
0060       state.derivative, state.jacToGlobal, state.pars, state.particleHypothesis,
0061       state.covTransport && transportCov, state.pathAccumulated,
0062       freeToBoundCorrection);
0063 }
0064 
0065 template <typename E, typename A>
0066 template <typename propagator_state_t, typename navigator_t>
0067 bool Acts::EigenStepper<E, A>::prepareCurvilinearState(
0068     propagator_state_t& prop_state, const navigator_t& navigator) const {
0069   // test whether the accumulated path has still its initial value.
0070   if (prop_state.stepping.pathAccumulated == 0.) {
0071     // if no step was executed the path length derivates have not been
0072     // computed but are needed to compute the curvilinear covariance. The
0073     // derivates are given by k1 for a zero step width.
0074     if (prop_state.stepping.extension.validExtensionForStep(prop_state, *this,
0075                                                             navigator)) {
0076       // First Runge-Kutta point (at current position)
0077       auto& sd = prop_state.stepping.stepData;
0078       auto pos = position(prop_state.stepping);
0079       auto fieldRes = getField(prop_state.stepping, pos);
0080       if (fieldRes.ok()) {
0081         sd.B_first = *fieldRes;
0082         if (prop_state.stepping.extension.k1(prop_state, *this, navigator,
0083                                              sd.k1, sd.B_first, sd.kQoP)) {
0084           // dr/ds :
0085           prop_state.stepping.derivative.template head<3>() =
0086               prop_state.stepping.pars.template segment<3>(eFreeDir0);
0087           // d (dr/ds) / ds :
0088           prop_state.stepping.derivative.template segment<3>(4) = sd.k1;
0089           // to set dt/ds :
0090           prop_state.stepping.extension.finalize(
0091               prop_state, *this, navigator,
0092               prop_state.stepping.pathAccumulated);
0093           return true;
0094         }
0095       }
0096     }
0097     return false;
0098   }
0099   return true;
0100 }
0101 
0102 template <typename E, typename A>
0103 auto Acts::EigenStepper<E, A>::curvilinearState(
0104     State& state, bool transportCov) const -> CurvilinearState {
0105   return detail::curvilinearState(
0106       state.cov, state.jacobian, state.jacTransport, state.derivative,
0107       state.jacToGlobal, state.pars, state.particleHypothesis,
0108       state.covTransport && transportCov, state.pathAccumulated);
0109 }
0110 
0111 template <typename E, typename A>
0112 void Acts::EigenStepper<E, A>::update(State& state,
0113                                       const FreeVector& freeParams,
0114                                       const BoundVector& /*boundParams*/,
0115                                       const Covariance& covariance,
0116                                       const Surface& surface) const {
0117   state.pars = freeParams;
0118   state.cov = covariance;
0119   state.jacToGlobal = surface.boundToFreeJacobian(
0120       state.geoContext, freeParams.template segment<3>(eFreePos0),
0121       freeParams.template segment<3>(eFreeDir0));
0122 }
0123 
0124 template <typename E, typename A>
0125 void Acts::EigenStepper<E, A>::update(State& state, const Vector3& uposition,
0126                                       const Vector3& udirection, double qOverP,
0127                                       double time) const {
0128   state.pars.template segment<3>(eFreePos0) = uposition;
0129   state.pars.template segment<3>(eFreeDir0) = udirection;
0130   state.pars[eFreeTime] = time;
0131   state.pars[eFreeQOverP] = qOverP;
0132 }
0133 
0134 template <typename E, typename A>
0135 void Acts::EigenStepper<E, A>::transportCovarianceToCurvilinear(
0136     State& state) const {
0137   detail::transportCovarianceToCurvilinear(state.cov, state.jacobian,
0138                                            state.jacTransport, state.derivative,
0139                                            state.jacToGlobal, direction(state));
0140 }
0141 
0142 template <typename E, typename A>
0143 void Acts::EigenStepper<E, A>::transportCovarianceToBound(
0144     State& state, const Surface& surface,
0145     const FreeToBoundCorrection& freeToBoundCorrection) const {
0146   detail::transportCovarianceToBound(state.geoContext.get(), surface, state.cov,
0147                                      state.jacobian, state.jacTransport,
0148                                      state.derivative, state.jacToGlobal,
0149                                      state.pars, freeToBoundCorrection);
0150 }
0151 
0152 template <typename E, typename A>
0153 template <typename propagator_state_t, typename navigator_t>
0154 Acts::Result<double> Acts::EigenStepper<E, A>::step(
0155     propagator_state_t& state, const navigator_t& navigator) const {
0156   // Runge-Kutta integrator state
0157   auto& sd = state.stepping.stepData;
0158 
0159   double errorEstimate = 0;
0160   double h2 = 0;
0161   double half_h = 0;
0162 
0163   auto pos = position(state.stepping);
0164   auto dir = direction(state.stepping);
0165 
0166   // First Runge-Kutta point (at current position)
0167   auto fieldRes = getField(state.stepping, pos);
0168   if (!fieldRes.ok()) {
0169     return fieldRes.error();
0170   }
0171   sd.B_first = *fieldRes;
0172   if (!state.stepping.extension.validExtensionForStep(state, *this,
0173                                                       navigator) ||
0174       !state.stepping.extension.k1(state, *this, navigator, sd.k1, sd.B_first,
0175                                    sd.kQoP)) {
0176     return 0.;
0177   }
0178 
0179   const auto calcStepSizeScaling = [&](const double errorEstimate_) -> double {
0180     // For details about these values see ATL-SOFT-PUB-2009-001
0181     constexpr double lower = 0.25;
0182     constexpr double upper = 4.0;
0183     // This is given by the order of the Runge-Kutta method
0184     constexpr double exponent = 0.25;
0185 
0186     // Whether to use fast power function if available
0187     constexpr bool tryUseFastPow{false};
0188 
0189     double x = state.options.stepping.stepTolerance / errorEstimate_;
0190 
0191     if constexpr (exponent == 0.25 && !tryUseFastPow) {
0192       // This is 3x faster than std::pow
0193       x = std::sqrt(std::sqrt(x));
0194     } else if constexpr (std::numeric_limits<double>::is_iec559 &&
0195                          tryUseFastPow) {
0196       x = fastPow(x, exponent);
0197     } else {
0198       x = std::pow(x, exponent);
0199     }
0200 
0201     return std::clamp(x, lower, upper);
0202   };
0203 
0204   const auto isErrorTolerable = [&](const double errorEstimate_) {
0205     // For details about these values see ATL-SOFT-PUB-2009-001
0206     constexpr double marginFactor = 4.0;
0207 
0208     return errorEstimate_ <=
0209            marginFactor * state.options.stepping.stepTolerance;
0210   };
0211 
0212   // The following functor starts to perform a Runge-Kutta step of a certain
0213   // size, going up to the point where it can return an estimate of the local
0214   // integration error. The results are stated in the local variables above,
0215   // allowing integration to continue once the error is deemed satisfactory
0216   const auto tryRungeKuttaStep = [&](const double h) -> Result<bool> {
0217     // helpers because bool and std::error_code are ambiguous
0218     constexpr auto success = &Result<bool>::success;
0219     constexpr auto failure = &Result<bool>::failure;
0220 
0221     // State the square and half of the step size
0222     h2 = h * h;
0223     half_h = h * 0.5;
0224 
0225     // Second Runge-Kutta point
0226     const Vector3 pos1 = pos + half_h * dir + h2 * 0.125 * sd.k1;
0227     auto field = getField(state.stepping, pos1);
0228     if (!field.ok()) {
0229       return failure(field.error());
0230     }
0231     sd.B_middle = *field;
0232 
0233     if (!state.stepping.extension.k2(state, *this, navigator, sd.k2,
0234                                      sd.B_middle, sd.kQoP, half_h, sd.k1)) {
0235       return success(false);
0236     }
0237 
0238     // Third Runge-Kutta point
0239     if (!state.stepping.extension.k3(state, *this, navigator, sd.k3,
0240                                      sd.B_middle, sd.kQoP, half_h, sd.k2)) {
0241       return success(false);
0242     }
0243 
0244     // Last Runge-Kutta point
0245     const Vector3 pos2 = pos + h * dir + h2 * 0.5 * sd.k3;
0246     field = getField(state.stepping, pos2);
0247     if (!field.ok()) {
0248       return failure(field.error());
0249     }
0250     sd.B_last = *field;
0251     if (!state.stepping.extension.k4(state, *this, navigator, sd.k4, sd.B_last,
0252                                      sd.kQoP, h, sd.k3)) {
0253       return success(false);
0254     }
0255 
0256     // Compute and check the local integration error estimate
0257     errorEstimate =
0258         h2 * ((sd.k1 - sd.k2 - sd.k3 + sd.k4).template lpNorm<1>() +
0259               std::abs(sd.kQoP[0] - sd.kQoP[1] - sd.kQoP[2] + sd.kQoP[3]));
0260     // Protect against division by zero
0261     errorEstimate = std::max(1e-20, errorEstimate);
0262 
0263     return success(isErrorTolerable(errorEstimate));
0264   };
0265 
0266   const double initialH =
0267       state.stepping.stepSize.value() * state.options.direction;
0268   double h = initialH;
0269   std::size_t nStepTrials = 0;
0270   // Select and adjust the appropriate Runge-Kutta step size as given
0271   // ATL-SOFT-PUB-2009-001
0272   while (true) {
0273     nStepTrials++;
0274     auto res = tryRungeKuttaStep(h);
0275     if (!res.ok()) {
0276       return res.error();
0277     }
0278     if (!!res.value()) {
0279       break;
0280     }
0281 
0282     const double stepSizeScaling = calcStepSizeScaling(errorEstimate);
0283     h *= stepSizeScaling;
0284 
0285     // If step size becomes too small the particle remains at the initial
0286     // place
0287     if (std::abs(h) < std::abs(state.options.stepping.stepSizeCutOff)) {
0288       // Not moving due to too low momentum needs an aborter
0289       return EigenStepperError::StepSizeStalled;
0290     }
0291 
0292     // If the parameter is off track too much or given stepSize is not
0293     // appropriate
0294     if (nStepTrials > state.options.stepping.maxRungeKuttaStepTrials) {
0295       // Too many trials, have to abort
0296       return EigenStepperError::StepSizeAdjustmentFailed;
0297     }
0298   }
0299 
0300   // When doing error propagation, update the associated Jacobian matrix
0301   if (state.stepping.covTransport) {
0302     // using the direction before updated below
0303 
0304     // The step transport matrix in global coordinates
0305     FreeMatrix D;
0306     if (!state.stepping.extension.finalize(state, *this, navigator, h, D)) {
0307       return EigenStepperError::StepInvalid;
0308     }
0309 
0310     // See the documentation of Acts::blockedMult for a description of blocked
0311     // matrix multiplication. However, we can go one further. Let's assume that
0312     // some of these sub-matrices are zero matrices 0₈ and identity matrices
0313     // I₈, namely:
0314     //
0315     // D₁₁ = I₈, J₁₁ = I₈, D₂₁ = 0₈, J₂₁ = 0₈
0316     //
0317     // Which gives:
0318     //
0319     // K₁₁ = I₈  * I₈  + D₁₂ * 0₈  = I₈
0320     // K₁₂ = I₈  * J₁₂ + D₁₂ * J₂₂ = J₁₂ + D₁₂ * J₂₂
0321     // K₂₁ = 0₈  * I₈  + D₂₂ * 0₈  = 0₈
0322     // K₂₂ = 0₈  * J₁₂ + D₂₂ * J₂₂ = D₂₂ * J₂₂
0323     //
0324     // Furthermore, we're constructing K in place of J, and since
0325     // K₁₁ = I₈ = J₁₁ and K₂₁ = 0₈ = D₂₁, we don't actually need to touch those
0326     // sub-matrices at all!
0327     assert((D.topLeftCorner<4, 4>().isIdentity()));
0328     assert((D.bottomLeftCorner<4, 4>().isZero()));
0329     assert((state.stepping.jacTransport.template topLeftCorner<4, 4>()
0330                 .isIdentity()));
0331     assert((state.stepping.jacTransport.template bottomLeftCorner<4, 4>()
0332                 .isZero()));
0333 
0334     state.stepping.jacTransport.template topRightCorner<4, 4>() +=
0335         D.topRightCorner<4, 4>() *
0336         state.stepping.jacTransport.template bottomRightCorner<4, 4>();
0337     state.stepping.jacTransport.template bottomRightCorner<4, 4>() =
0338         (D.bottomRightCorner<4, 4>() *
0339          state.stepping.jacTransport.template bottomRightCorner<4, 4>())
0340             .eval();
0341   } else {
0342     if (!state.stepping.extension.finalize(state, *this, navigator, h)) {
0343       return EigenStepperError::StepInvalid;
0344     }
0345   }
0346 
0347   // Update the track parameters according to the equations of motion
0348   state.stepping.pars.template segment<3>(eFreePos0) +=
0349       h * dir + h2 / 6. * (sd.k1 + sd.k2 + sd.k3);
0350   state.stepping.pars.template segment<3>(eFreeDir0) +=
0351       h / 6. * (sd.k1 + 2. * (sd.k2 + sd.k3) + sd.k4);
0352   (state.stepping.pars.template segment<3>(eFreeDir0)).normalize();
0353 
0354   if (state.stepping.covTransport) {
0355     // using the updated direction
0356     state.stepping.derivative.template head<3>() =
0357         state.stepping.pars.template segment<3>(eFreeDir0);
0358     state.stepping.derivative.template segment<3>(4) = sd.k4;
0359   }
0360 
0361   state.stepping.pathAccumulated += h;
0362   ++state.stepping.nSteps;
0363   state.stepping.nStepTrials += nStepTrials;
0364 
0365   const double stepSizeScaling = calcStepSizeScaling(errorEstimate);
0366   const double nextAccuracy = std::abs(h * stepSizeScaling);
0367   const double previousAccuracy = std::abs(state.stepping.stepSize.accuracy());
0368   const double initialStepLength = std::abs(initialH);
0369   if (nextAccuracy < initialStepLength || nextAccuracy > previousAccuracy) {
0370     state.stepping.stepSize.setAccuracy(nextAccuracy);
0371   }
0372 
0373   return h;
0374 }
0375 
0376 template <typename E, typename A>
0377 void Acts::EigenStepper<E, A>::setIdentityJacobian(State& state) const {
0378   state.jacobian = BoundMatrix::Identity();
0379 }