Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-07 07:58: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 #include "Acts/Definitions/Direction.hpp"
0012 #include "Acts/Definitions/TrackParametrization.hpp"
0013 #include "Acts/EventData/BoundTrackParameters.hpp"
0014 #include "Acts/EventData/ParticleHypothesis.hpp"
0015 #include "Acts/Propagator/ConstrainedStep.hpp"
0016 #include "Acts/Propagator/NavigationTarget.hpp"
0017 #include "Acts/Propagator/StepperConcept.hpp"
0018 #include "Acts/Propagator/StepperStatistics.hpp"
0019 #include "Acts/Surfaces/CurvilinearSurface.hpp"
0020 #include "Acts/Utilities/Intersection.hpp"
0021 #include "Acts/Utilities/detail/periodic.hpp"
0022 
0023 #include <sstream>
0024 #include <string>
0025 #include <tuple>
0026 #include <vector>
0027 
0028 namespace Acts {
0029 class IVolumeMaterial;
0030 class MagneticFieldProvider;
0031 }  // namespace Acts
0032 
0033 namespace Acts::Experimental {
0034 
0035 /// Collection of bound parameter variations, each entry is a pair of the index
0036 /// of the bound parameter
0037 using BoundParameterVariation = std::vector<std::pair<std::size_t, double>>;
0038 
0039 /// Base class for generating bound parameter variations
0040 struct BoundParameterVariationGenerator {
0041   virtual ~BoundParameterVariationGenerator() = default;
0042   /// Generate a variation map based on the input bound parameters and
0043   /// covariance
0044   /// @param input the input bound parameters
0045   /// @param covariance the covariance of the input bound parameters
0046   /// @return the corresponding parameter variation
0047   virtual BoundParameterVariation variationMap(
0048       const BoundVector& input, const BoundMatrix& covariance) const = 0;
0049 };
0050 
0051 /// Generator for bound parameter variations based on fixed deltas
0052 struct DeltaBoundParameterVariationGenerator
0053     : public BoundParameterVariationGenerator {
0054   /// The deltas
0055   std::vector<BoundVector> deltas;
0056 
0057   /// Construct a generator with a single delta applied to all parameters
0058   /// @param delta the delta
0059   explicit DeltaBoundParameterVariationGenerator(const double delta)
0060       : deltas(1, BoundVector::Constant(delta)) {}
0061 
0062   /// Construct a generator with multiple deltas applied to all parameters
0063   /// @param deltas_ the delta
0064   explicit DeltaBoundParameterVariationGenerator(
0065       const std::vector<double>& deltas_) {
0066     this->deltas.reserve(deltas_.size());
0067     for (const auto& delta : deltas_) {
0068       this->deltas.emplace_back(BoundVector::Constant(delta));
0069     }
0070   }
0071 
0072   /// Construct a generator with a single delta vector
0073   /// @param delta the delta vector
0074   explicit DeltaBoundParameterVariationGenerator(const BoundVector& delta)
0075       : deltas(1, delta) {}
0076 
0077   /// Construct a generator with multiple delta vectors
0078   /// @param deltas_ the delta vectors
0079   explicit DeltaBoundParameterVariationGenerator(
0080       std::vector<BoundVector> deltas_)
0081       : deltas(std::move(deltas_)) {}
0082 
0083   /// Generate a variation map based on the fixed deltas
0084   /// @return the corresponding parameter variation
0085   BoundParameterVariation variationMap(
0086       const BoundVector& /*input*/,
0087       const BoundMatrix& /*covariance*/) const override {
0088     BoundParameterVariation variationMap;
0089     variationMap.reserve(deltas.size() * eBoundSize);
0090     for (std::size_t i = 0; i < deltas.size(); ++i) {
0091       for (std::size_t j = 0; j < eBoundSize; ++j) {
0092         variationMap.emplace_back(j, deltas[i][j]);
0093       }
0094     }
0095     return variationMap;
0096   }
0097 };
0098 
0099 /// Generator for bound parameter variations based on the covariance
0100 struct CovarianceBoundParameterVariationGenerator
0101     : public BoundParameterVariationGenerator {
0102   /// The sigma factors
0103   std::vector<BoundVector> sigmaFactors;
0104 
0105   /// Construct a generator with a single sigma factor applied to all parameters
0106   /// @param sigmaFactor the sigma factor
0107   explicit CovarianceBoundParameterVariationGenerator(const double sigmaFactor)
0108       : sigmaFactors(1, BoundVector::Constant(sigmaFactor)) {}
0109 
0110   /// Construct a generator with multiple sigma factors applied to all
0111   /// parameters
0112   /// @param sigmaFactors_ the sigma factors
0113   explicit CovarianceBoundParameterVariationGenerator(
0114       const std::vector<double>& sigmaFactors_) {
0115     sigmaFactors.reserve(sigmaFactors_.size());
0116     for (const auto& factor : sigmaFactors_) {
0117       sigmaFactors.emplace_back(BoundVector::Constant(factor));
0118     }
0119   }
0120 
0121   /// Construct a generator with a single sigma factor vector
0122   /// @param sigmaFactor the sigma factor vector
0123   explicit CovarianceBoundParameterVariationGenerator(
0124       const BoundVector& sigmaFactor)
0125       : sigmaFactors(1, sigmaFactor) {}
0126 
0127   /// Construct a generator with multiple sigma factor vectors
0128   /// @param sigmaFactors_ the sigma factor vectors
0129   explicit CovarianceBoundParameterVariationGenerator(
0130       std::vector<BoundVector> sigmaFactors_)
0131       : sigmaFactors(std::move(sigmaFactors_)) {}
0132 
0133   /// Generate a variation map based on the covariance and sigma factors
0134   /// @param covariance the covariance of the input bound parameters
0135   /// @return the corresponding parameter variation
0136   BoundParameterVariation variationMap(
0137       const BoundVector& /*input*/,
0138       const BoundMatrix& covariance) const override {
0139     BoundParameterVariation variationMap;
0140     variationMap.reserve(sigmaFactors.size() * eBoundSize);
0141     for (std::size_t i = 0; i < sigmaFactors.size(); ++i) {
0142       for (std::size_t j = 0; j < eBoundSize; ++j) {
0143         variationMap.emplace_back(
0144             j, sigmaFactors[i][j] * std::sqrt(covariance(j, j)));
0145       }
0146     }
0147     return variationMap;
0148   }
0149 };
0150 
0151 /// RiddersStepper implements the Ridders method for numerical differentiation
0152 /// to compute the Jacobian of the bound to bound transformation. It uses a
0153 /// primary stepper to perform the nominal step and multiple secondary steppers
0154 /// with varied initial conditions to compute the Jacobian columns. The
0155 /// variations are generated based on the input covariance and a configurable
0156 /// variation generator.
0157 /// @tparam stepper_impl_t the type of the underlying stepper implementation
0158 template <Concepts::SingleStepper stepper_impl_t>
0159 class RiddersStepper final {
0160  public:
0161   /// The type of the underlying stepper implementation
0162   using StepperImpl = stepper_impl_t;
0163 
0164   /// The bound parameters type used by this stepper
0165   using BoundParameters = BoundTrackParameters;
0166   /// The Jacobian type for the bound to bound transformation
0167   using Jacobian = BoundMatrix;
0168   /// The covariance type for the bound parameters
0169   using Covariance = BoundMatrix;
0170   /// The type of the bound state returned by the `boundState` method
0171   using BoundState = std::tuple<BoundParameters, Jacobian, double>;
0172 
0173   /// Configuration struct for the RiddersStepper
0174   struct Config {
0175     /// Configuration for the underlying stepper implementation
0176     StepperImpl::Config stepperConfig;
0177 
0178     /// The generator for the bound parameter variations
0179     std::shared_ptr<const BoundParameterVariationGenerator> parameterVariation{
0180         std::make_shared<CovarianceBoundParameterVariationGenerator>(
0181             std::vector<double>{-1e-3, 1e-3})};
0182 
0183     /// The maximum number of steps to attempt when stepping towards the
0184     /// curvilinear surface for the bound state transformation
0185     std::size_t maxStepsToCurvilinearSurface = 10;
0186   };
0187 
0188   /// The options type for the RiddersStepper
0189   using Options = typename StepperImpl::Options;
0190 
0191   /// The state struct for the RiddersStepper
0192   struct State {
0193     /// @param primaryStepperStateIn the state of the primary stepper
0194     explicit State(const StepperImpl::State& primaryStepperStateIn)
0195         : primaryStepperState(primaryStepperStateIn) {}
0196 
0197     /// The state of the primary stepper, which performs the nominal step
0198     StepperImpl::State primaryStepperState;
0199     /// The states of the secondary steppers, which perform steps with varied
0200     /// initial conditions to compute the Jacobian columns
0201     std::vector<typename StepperImpl::State> secondaryStepperStates;
0202 
0203     /// The variation map, which contains the indices of the varied parameters
0204     /// and the corresponding deltas applied to them
0205     BoundParameterVariation variationMap;
0206 
0207     /// Flag indicating whether covariance transport is enabled
0208     bool covTransport = false;
0209     /// The covariance of the last known bound parameters
0210     Covariance cov = Covariance::Zero();
0211     /// The Jacobian of the last bound to bound transformation
0212     Jacobian jacobian = Jacobian::Identity();
0213 
0214     /// The accumulated path length during the stepping process
0215     double pathAccumulated = 0;
0216 
0217     /// Statistics for the stepper, such as the number of steps taken and the
0218     /// number of successful steps
0219     StepperStatistics statistics;
0220 
0221     // Parameters to reuse for curvilinear state
0222     /// The last propagation direction
0223     Direction lastStepPropagationDirection = Direction::Forward();
0224     /// The last material
0225     const IVolumeMaterial* lastStepMaterial = nullptr;
0226     /// The last surface tolerance
0227     double lastSurfaceTolerance = 0.;
0228     /// The last constrained step type
0229     ConstrainedStep::Type lastStepConstraintType =
0230         ConstrainedStep::Type::Navigator;
0231   };
0232 
0233   /// Construct a RiddersStepper with the given stepper implementation
0234   /// @param stepperImpl the underlying stepper implementation
0235   explicit RiddersStepper(StepperImpl stepperImpl)
0236       : m_stepperImpl(std::move(stepperImpl)) {}
0237 
0238   /// Construct a RiddersStepper with the given magnetic field provider
0239   /// @param bField the magnetic field provider
0240   explicit RiddersStepper(std::shared_ptr<const MagneticFieldProvider> bField)
0241       : m_stepperImpl(std::move(bField)) {}
0242 
0243   /// Construct a RiddersStepper with the given configuration
0244   /// @param config the configuration for the RiddersStepper
0245   explicit RiddersStepper(const Config& config)
0246       : m_config(config), m_stepperImpl(config.stepperConfig) {}
0247 
0248   /// Create a new state for the RiddersStepper based on the given options
0249   /// @param options the options
0250   /// @return a new state for the RiddersStepper
0251   State makeState(const Options& options) const {
0252     State state(m_stepperImpl.makeState(options));
0253     return state;
0254   }
0255 
0256   /// Initialize the state of the RiddersStepper based on the given bound
0257   /// parameters
0258   /// @param state the state
0259   /// @param boundParameters the bound parameters to initialize the state with
0260   void initialize(State& state, const BoundParameters& boundParameters) const {
0261     initialize(state, boundParameters.parameters(),
0262                boundParameters.covariance(),
0263                boundParameters.particleHypothesis(),
0264                boundParameters.referenceSurface());
0265   }
0266 
0267   /// Initialize the state of the RiddersStepper based on the given bound vector
0268   /// and covariance
0269   /// @param state the state
0270   /// @param boundVector the bound vector to initialize the state with
0271   /// @param covariance the covariance of the bound parameters, used to generate the variations for the secondary steppers
0272   /// @param particleHypothesis the particle hypothesis
0273   /// @param surface the surface on which the bound parameters are defined
0274   void initialize(State& state, const BoundVector& boundVector,
0275                   const std::optional<BoundMatrix>& covariance,
0276                   ParticleHypothesis particleHypothesis,
0277                   const Surface& surface) const {
0278     state.variationMap.clear();
0279     state.secondaryStepperStates.clear();
0280 
0281     m_stepperImpl.initialize(state.primaryStepperState, boundVector,
0282                              std::nullopt, particleHypothesis, surface);
0283 
0284     if (!covariance.has_value()) {
0285       state.covTransport = false;
0286       state.cov = BoundMatrix::Zero();
0287       // intentionally not resetting `jacobian` here as otherwise `update`
0288       // break. this should be revisitted at some point - might be best to
0289       // rework the stepper interface and how the jacobian is accessed
0290 
0291       return;
0292     }
0293 
0294     state.variationMap =
0295         m_config.parameterVariation->variationMap(boundVector, *covariance);
0296 
0297     for (const auto& [index, delta] : state.variationMap) {
0298       BoundVector nudgedParams = boundVector;
0299       nudgedParams[index] += delta;
0300 
0301       state.secondaryStepperStates.push_back(
0302           m_stepperImpl.makeState(state.primaryStepperState.options));
0303       m_stepperImpl.initialize(state.secondaryStepperStates.back(),
0304                                nudgedParams, std::nullopt, particleHypothesis,
0305                                surface);
0306     }
0307 
0308     state.covTransport = true;
0309     state.cov = *covariance;
0310     // same as above:
0311     // intentionally not resetting `jacobian` here as otherwise `update`
0312     // break. this should be revisitted at some point - might be best to
0313     // rework the stepper interface and how the jacobian is accessed
0314   }
0315 
0316   /// Get the magnetic field at the given position for the primary stepper state
0317   /// @param state the state of the RiddersStepper
0318   /// @param position the position at which to get the magnetic field
0319   /// @return the magnetic field at the given position for the primary stepper state
0320   Result<Vector3> getField(State& state, const Vector3& position) const {
0321     return m_stepperImpl.getField(state.primaryStepperState, position);
0322   }
0323 
0324   /// Get the position of the primary stepper state
0325   /// @param state the state of the RiddersStepper
0326   /// @return the position of the primary stepper state
0327   Vector3 position(const State& state) const {
0328     return m_stepperImpl.position(state.primaryStepperState);
0329   }
0330 
0331   /// Get the direction of the primary stepper state
0332   /// @param state the state of the RiddersStepper
0333   /// @return the direction of the primary stepper state
0334   Vector3 direction(const State& state) const {
0335     return m_stepperImpl.direction(state.primaryStepperState);
0336   }
0337 
0338   /// Get the charge over momentum of the primary stepper state
0339   /// @param state the state of the RiddersStepper
0340   /// @return the charge over momentum of the primary stepper state
0341   double qOverP(const State& state) const {
0342     return m_stepperImpl.qOverP(state.primaryStepperState);
0343   }
0344 
0345   /// Get the absolute momentum of the primary stepper state
0346   /// @param state the state of the RiddersStepper
0347   /// @return the absolute momentum of the primary stepper state
0348   double absoluteMomentum(const State& state) const {
0349     return m_stepperImpl.absoluteMomentum(state.primaryStepperState);
0350   }
0351 
0352   /// Get the momentum vector of the primary stepper state
0353   /// @param state the state of the RiddersStepper
0354   /// @return the momentum vector of the primary stepper state
0355   Vector3 momentum(const State& state) const {
0356     return m_stepperImpl.momentum(state.primaryStepperState);
0357   }
0358 
0359   /// Get the charge of the primary stepper state
0360   /// @param state the state of the RiddersStepper
0361   /// @return the charge of the primary stepper state
0362   double charge(const State& state) const {
0363     return m_stepperImpl.charge(state.primaryStepperState);
0364   }
0365 
0366   /// Get the particle hypothesis of the primary stepper state
0367   /// @param state the state of the RiddersStepper
0368   /// @return the particle hypothesis of the primary stepper state
0369   const ParticleHypothesis& particleHypothesis(const State& state) const {
0370     return m_stepperImpl.particleHypothesis(state.primaryStepperState);
0371   }
0372 
0373   /// Get the time of the primary stepper state
0374   /// @param state the state of the RiddersStepper
0375   /// @return the time of the primary stepper state
0376   double time(const State& state) const {
0377     return m_stepperImpl.time(state.primaryStepperState);
0378   }
0379 
0380   /// Update the surface status of the primary and secondary stepper states
0381   /// based on the given surface and navigation direction
0382   /// @param state the state of the RiddersStepper
0383   /// @param surface the surface
0384   /// @param index the index of the surface
0385   /// @param navDir the navigation direction
0386   /// @param boundaryTolerance the boundary tolerance
0387   /// @param surfaceTolerance the surface tolerance
0388   /// @param stype the type of the constrained step
0389   /// @param logger the logger
0390   /// @return the overall intersection status after updating the surface
0391   ///    status for the primary and secondary stepper states
0392   IntersectionStatus updateSurfaceStatus(
0393       State& state, const Surface& surface, std::uint8_t index,
0394       Direction navDir, const BoundaryTolerance& boundaryTolerance,
0395       double surfaceTolerance, ConstrainedStep::Type stype,
0396       const Logger& logger = getDummyLogger()) const {
0397     state.lastSurfaceTolerance = surfaceTolerance;
0398     state.lastStepConstraintType = stype;
0399 
0400     IntersectionStatus overallStatus = m_stepperImpl.updateSurfaceStatus(
0401         state.primaryStepperState, surface, index, navDir, boundaryTolerance,
0402         surfaceTolerance, stype, logger);
0403     if (overallStatus == IntersectionStatus::unreachable) {
0404       return overallStatus;
0405     }
0406 
0407     const auto combine = [](const IntersectionStatus a,
0408                             const IntersectionStatus b) {
0409       using enum IntersectionStatus;
0410       if ((a == unreachable) || (b == unreachable)) {
0411         return unreachable;
0412       }
0413       if ((a == reachable) || (b == reachable)) {
0414         return reachable;
0415       }
0416       return onSurface;
0417     };
0418 
0419     for (auto& secondaryState : state.secondaryStepperStates) {
0420       const IntersectionStatus status = m_stepperImpl.updateSurfaceStatus(
0421           secondaryState, surface, index, navDir, BoundaryTolerance::Infinite(),
0422           surfaceTolerance, stype, logger);
0423       overallStatus = combine(overallStatus, status);
0424     }
0425 
0426     return overallStatus;
0427   }
0428 
0429   /// Update the step size based on the given navigation target, direction, and
0430   /// constrained step type
0431   /// @param state the state of the RiddersStepper
0432   /// @param target the navigation target
0433   /// @param direction the direction
0434   /// @param stype the type of the constrained step
0435   void updateStepSize(State& state, const NavigationTarget& target,
0436                       Direction direction, ConstrainedStep::Type stype) const {
0437     m_stepperImpl.updateStepSize(state.primaryStepperState, target, direction,
0438                                  stype);
0439 
0440     for (auto& secondaryState : state.secondaryStepperStates) {
0441       m_stepperImpl.updateStepSize(secondaryState, target, direction, stype);
0442     }
0443   }
0444 
0445   /// Update the step size based on the given step size and constrained step
0446   /// type
0447   /// @param state the state of the RiddersStepper
0448   /// @param stepSize the step size
0449   /// @param stype the type of the constrained step
0450   void updateStepSize(State& state, double stepSize,
0451                       ConstrainedStep::Type stype) const {
0452     m_stepperImpl.updateStepSize(state.primaryStepperState, stepSize, stype);
0453 
0454     for (auto& secondaryState : state.secondaryStepperStates) {
0455       m_stepperImpl.updateStepSize(secondaryState, stepSize, stype);
0456     }
0457   }
0458 
0459   /// Get the step size for the given constrained step type
0460   /// @param state the state of the RiddersStepper
0461   /// @param stype the type of the constrained step for which to get the step size
0462   /// @return the step size of the primary stepper state for the given constrained step type
0463   double getStepSize(const State& state, ConstrainedStep::Type stype) const {
0464     return m_stepperImpl.getStepSize(state.primaryStepperState, stype);
0465   }
0466 
0467   /// Release the step size for the given constrained step type
0468   /// @param state the state of the RiddersStepper
0469   /// @param stype the type of the constrained step for which to release the step size
0470   void releaseStepSize(State& state, ConstrainedStep::Type stype) const {
0471     m_stepperImpl.releaseStepSize(state.primaryStepperState, stype);
0472 
0473     for (auto& secondaryState : state.secondaryStepperStates) {
0474       m_stepperImpl.releaseStepSize(secondaryState, stype);
0475     }
0476   }
0477 
0478   /// Get a string representation of the step size constraints
0479   /// @param state the state of the RiddersStepper
0480   /// @return a string representation of the step size constraints
0481   std::string outputStepSize(const State& state) const {
0482     std::stringstream ss;
0483     ss << state.primaryStepperState.stepSize.toString();
0484     ss << " || ";
0485     for (const auto& secondaryState : state.secondaryStepperStates) {
0486       ss << secondaryState.stepSize.toString() << " | ";
0487     }
0488     return ss.str();
0489   }
0490 
0491   /// Compute the bound state
0492   /// @param state the state of the RiddersStepper
0493   /// @param surface the surface
0494   /// @param transportCovariance flag indicating whether to transport the covariance to the bound parameters
0495   /// @param freeToBoundCorrection the correction
0496   /// @return the bound state
0497   Result<BoundState> boundState(
0498       State& state, const Surface& surface, bool transportCovariance = true,
0499       const FreeToBoundCorrection& freeToBoundCorrection =
0500           FreeToBoundCorrection(false)) const {
0501     Result<BoundState> primaryResult = m_stepperImpl.boundState(
0502         state.primaryStepperState, surface, false, freeToBoundCorrection);
0503 
0504     if (!transportCovariance) {
0505       std::get<1>(*primaryResult) = state.jacobian;
0506       return primaryResult;
0507     }
0508 
0509     std::vector<BoundVector> boundVectors;
0510     std::vector<double> pathLenghts;
0511     boundVectors.reserve(state.secondaryStepperStates.size());
0512     pathLenghts.reserve(state.secondaryStepperStates.size());
0513     for (auto& secondaryState : state.secondaryStepperStates) {
0514       const Result<BoundState> result = m_stepperImpl.boundState(
0515           secondaryState, surface, false, freeToBoundCorrection);
0516       if (!result.ok()) {
0517         return result.error();
0518       }
0519       boundVectors.push_back(std::get<0>(*result).parameters());
0520       pathLenghts.push_back(std::get<2>(*result));
0521     }
0522     const auto& referenceVector = std::get<0>(*primaryResult).parameters();
0523 
0524     state.jacobian = BoundMatrix::Zero();
0525     std::array<std::size_t, eBoundSize> numberOfVariationsPerParameter{};
0526 
0527     for (std::size_t i = 0; i < state.variationMap.size(); ++i) {
0528       const auto& [index, delta] = state.variationMap[i];
0529       const auto& nudgedVector = boundVectors[i];
0530 
0531       const auto diff = [&]() {
0532         BoundVector d = nudgedVector - referenceVector;
0533         d[eBoundPhi] = detail::difference_periodic(nudgedVector[eBoundPhi],
0534                                                    referenceVector[eBoundPhi],
0535                                                    2 * std::numbers::pi);
0536         return d;
0537       };
0538 
0539       state.jacobian.col(index) += diff() / delta;
0540       ++numberOfVariationsPerParameter[index];
0541     }
0542     for (std::size_t i = 0; i < eBoundSize; ++i) {
0543       state.jacobian.col(i) /= numberOfVariationsPerParameter[i];
0544     }
0545 
0546     state.cov = state.jacobian * state.cov * state.jacobian.transpose();
0547 
0548     BoundTrackParameters newParams(surface.getSharedPtr(), referenceVector,
0549                                    state.cov, particleHypothesis(state));
0550 
0551     state.variationMap =
0552         m_config.parameterVariation->variationMap(referenceVector, state.cov);
0553 
0554     return Result<BoundState>::success(
0555         BoundState{std::move(newParams), state.jacobian, pathLenghts.front()});
0556   }
0557 
0558   /// Prepare the stepper for the curvilinear transformation
0559   /// @param state the state of the RiddersStepper
0560   /// @return true if the preparation was successful for all stepper states, false otherwise
0561   bool prepareCurvilinearState(State& state) const {
0562     bool result =
0563         m_stepperImpl.prepareCurvilinearState(state.primaryStepperState);
0564     for (auto& secondaryState : state.secondaryStepperStates) {
0565       result = result && m_stepperImpl.prepareCurvilinearState(secondaryState);
0566     }
0567     return result;
0568   }
0569 
0570   /// Compute the curvilinear state
0571   /// @param state the state of the RiddersStepper
0572   /// @param transportCovariance flag indicating whether to transport the covariance to the curvilinear parameters
0573   /// @return the curvilinear state
0574   BoundState curvilinearState(State& state,
0575                               bool transportCovariance = true) const {
0576     if (!transportCovariance) {
0577       return m_stepperImpl.curvilinearState(state.primaryStepperState, false);
0578     }
0579 
0580     const auto curvilinearSurface =
0581         CurvilinearSurface(position(state), direction(state)).surface();
0582 
0583     for (std::size_t i = 0; i < m_config.maxStepsToCurvilinearSurface; ++i) {
0584       const auto surfaceStatus = updateSurfaceStatus(
0585           state, *curvilinearSurface, 0, state.lastStepPropagationDirection,
0586           BoundaryTolerance::Infinite(), state.lastSurfaceTolerance,
0587           state.lastStepConstraintType);
0588       if (surfaceStatus == IntersectionStatus::onSurface) {
0589         break;
0590       }
0591       if (surfaceStatus == IntersectionStatus::unreachable) {
0592         throw std::runtime_error(
0593             "RiddersStepper: Curvilinear surface is unreachable");
0594       }
0595       const auto stepResult = step(state, state.lastStepPropagationDirection,
0596                                    state.lastStepMaterial);
0597       if (!stepResult.ok()) {
0598         throw std::runtime_error(
0599             "RiddersStepper: Failed to step towards curvilinear surface: " +
0600             stepResult.error().message());
0601       }
0602     }
0603 
0604     return boundState(state, *curvilinearSurface, true).value();
0605   }
0606 
0607   /// Update the state of the RiddersStepper
0608   /// @param state the state of the RiddersStepper
0609   /// @param boundParams the bound vector
0610   /// @param covariance the covariance of the bound parameters
0611   /// @param surface the surface on which the bound parameters are defined
0612   void update(State& state, const FreeVector& /*freeParams*/,
0613               const BoundVector& boundParams, const BoundMatrix& covariance,
0614               const Surface& surface) const {
0615     initialize(state, boundParams, covariance, particleHypothesis(state),
0616                surface);
0617   }
0618 
0619   /// Update the state of the RiddersStepper
0620   /// @param state the state of the RiddersStepper
0621   /// @param position the position
0622   /// @param direction the direction
0623   /// @param qOverP the charge over momentum
0624   /// @param time the time
0625   void update(State& state, const Vector3& position, const Vector3& direction,
0626               double qOverP, double time) const {
0627     const auto curvilinearSurface =
0628         CurvilinearSurface(position, direction).surface();
0629     const std::optional<BoundMatrix> cov =
0630         state.covTransport ? std::optional<BoundMatrix>(state.cov)
0631                            : std::nullopt;
0632     initialize(state,
0633                transformFreeToBoundParameters(
0634                    position, time, direction, qOverP, *curvilinearSurface,
0635                    state.primaryStepperState.options.geoContext)
0636                    .value(),
0637                cov, particleHypothesis(state), *curvilinearSurface);
0638   }
0639 
0640   /// Transport the covariance to curvilinear parameters
0641   /// @param state the state of the RiddersStepper
0642   void transportCovarianceToCurvilinear(State& state) const {
0643     curvilinearState(state, true);
0644   }
0645 
0646   /// Transport the covariance to bound parameters
0647   /// @param state the state of the RiddersStepper
0648   /// @param surface the surface
0649   /// @param freeToBoundCorrection the correction
0650   void transportCovarianceToBound(
0651       State& state, const Surface& surface,
0652       const FreeToBoundCorrection& freeToBoundCorrection =
0653           FreeToBoundCorrection(false)) const {
0654     boundState(state, surface, true, freeToBoundCorrection);
0655   }
0656 
0657   /// Perform a step
0658   /// @param state the state of the RiddersStepper
0659   /// @param propagationDirection the direction
0660   /// @param material the material
0661   /// @return a result containing the step length or an error if the step failed
0662   Result<double> step(State& state, Direction propagationDirection,
0663                       const IVolumeMaterial* material) const {
0664     state.lastStepPropagationDirection = propagationDirection;
0665     state.lastStepMaterial = material;
0666 
0667     const Result<double> stepResult = m_stepperImpl.step(
0668         state.primaryStepperState, propagationDirection, material);
0669     if (!stepResult.ok()) {
0670       return stepResult.error();
0671     }
0672     for (auto& secondaryState : state.secondaryStepperStates) {
0673       const Result<double> secondaryStepResult =
0674           m_stepperImpl.step(secondaryState, propagationDirection, material);
0675       if (!secondaryStepResult.ok()) {
0676         return secondaryStepResult.error();
0677       }
0678     }
0679     state.pathAccumulated += *stepResult;
0680     return *stepResult;
0681   }
0682 
0683  private:
0684   /// The stepper configuration
0685   Config m_config;
0686 
0687   /// The underlying stepper implementation
0688   StepperImpl m_stepperImpl;
0689 };
0690 
0691 }  // namespace Acts::Experimental