File indexing completed on 2025-07-13 08:04:55
0001
0002
0003
0004
0005
0006
0007
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
0039 state.pars = freeParams;
0040 state.cov = cov;
0041 state.stepSize = ConstrainedStep(stepSize);
0042 state.pathAccumulated = 0.;
0043
0044
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
0070 if (prop_state.stepping.pathAccumulated == 0.) {
0071
0072
0073
0074 if (prop_state.stepping.extension.validExtensionForStep(prop_state, *this,
0075 navigator)) {
0076
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
0085 prop_state.stepping.derivative.template head<3>() =
0086 prop_state.stepping.pars.template segment<3>(eFreeDir0);
0087
0088 prop_state.stepping.derivative.template segment<3>(4) = sd.k1;
0089
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& ,
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
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
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
0181 constexpr double lower = 0.25;
0182 constexpr double upper = 4.0;
0183
0184 constexpr double exponent = 0.25;
0185
0186
0187 constexpr bool tryUseFastPow{false};
0188
0189 double x = state.options.stepping.stepTolerance / errorEstimate_;
0190
0191 if constexpr (exponent == 0.25 && !tryUseFastPow) {
0192
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
0206 constexpr double marginFactor = 4.0;
0207
0208 return errorEstimate_ <=
0209 marginFactor * state.options.stepping.stepTolerance;
0210 };
0211
0212
0213
0214
0215
0216 const auto tryRungeKuttaStep = [&](const double h) -> Result<bool> {
0217
0218 constexpr auto success = &Result<bool>::success;
0219 constexpr auto failure = &Result<bool>::failure;
0220
0221
0222 h2 = h * h;
0223 half_h = h * 0.5;
0224
0225
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
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
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
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
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
0271
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
0286
0287 if (std::abs(h) < std::abs(state.options.stepping.stepSizeCutOff)) {
0288
0289 return EigenStepperError::StepSizeStalled;
0290 }
0291
0292
0293
0294 if (nStepTrials > state.options.stepping.maxRungeKuttaStepTrials) {
0295
0296 return EigenStepperError::StepSizeAdjustmentFailed;
0297 }
0298 }
0299
0300
0301 if (state.stepping.covTransport) {
0302
0303
0304
0305 FreeMatrix D;
0306 if (!state.stepping.extension.finalize(state, *this, navigator, h, D)) {
0307 return EigenStepperError::StepInvalid;
0308 }
0309
0310
0311
0312
0313
0314
0315
0316
0317
0318
0319
0320
0321
0322
0323
0324
0325
0326
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
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
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 }