AbstractIntegratedPropagator.java

  1. /* Copyright 2002-2025 CS GROUP
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.propagation.integration;

  18. import org.hipparchus.analysis.UnivariateFunction;
  19. import org.hipparchus.analysis.solvers.BracketedUnivariateSolver;
  20. import org.hipparchus.analysis.solvers.BracketingNthOrderBrentSolver;
  21. import org.hipparchus.exception.MathRuntimeException;
  22. import org.hipparchus.ode.DenseOutputModel;
  23. import org.hipparchus.ode.ExpandableODE;
  24. import org.hipparchus.ode.ODEIntegrator;
  25. import org.hipparchus.ode.ODEState;
  26. import org.hipparchus.ode.ODEStateAndDerivative;
  27. import org.hipparchus.ode.OrdinaryDifferentialEquation;
  28. import org.hipparchus.ode.SecondaryODE;
  29. import org.hipparchus.ode.events.Action;
  30. import org.hipparchus.ode.events.AdaptableInterval;
  31. import org.hipparchus.ode.events.ODEEventDetector;
  32. import org.hipparchus.ode.events.ODEEventHandler;
  33. import org.hipparchus.ode.sampling.AbstractODEStateInterpolator;
  34. import org.hipparchus.ode.sampling.ODEStateInterpolator;
  35. import org.hipparchus.ode.sampling.ODEStepHandler;
  36. import org.hipparchus.util.Precision;
  37. import org.orekit.attitudes.AttitudeProvider;
  38. import org.orekit.errors.OrekitException;
  39. import org.orekit.errors.OrekitInternalError;
  40. import org.orekit.errors.OrekitMessages;
  41. import org.orekit.frames.Frame;
  42. import org.orekit.orbits.OrbitType;
  43. import org.orekit.orbits.PositionAngleType;
  44. import org.orekit.propagation.AbstractPropagator;
  45. import org.orekit.propagation.BoundedPropagator;
  46. import org.orekit.propagation.EphemerisGenerator;
  47. import org.orekit.propagation.PropagationType;
  48. import org.orekit.propagation.SpacecraftState;
  49. import org.orekit.propagation.events.EventDetector;
  50. import org.orekit.propagation.events.handlers.EventHandler;
  51. import org.orekit.propagation.sampling.OrekitStepHandler;
  52. import org.orekit.propagation.sampling.OrekitStepInterpolator;
  53. import org.orekit.time.AbsoluteDate;
  54. import org.orekit.utils.DataDictionary;

  55. import java.util.ArrayList;
  56. import java.util.Arrays;
  57. import java.util.Collection;
  58. import java.util.Collections;
  59. import java.util.HashMap;
  60. import java.util.LinkedList;
  61. import java.util.List;
  62. import java.util.Map;
  63. import java.util.Queue;


  64. /** Common handling of {@link org.orekit.propagation.Propagator Propagator}
  65.  *  methods for both numerical and semi-analytical propagators.
  66.  *  @author Luc Maisonobe
  67.  */
  68. public abstract class AbstractIntegratedPropagator extends AbstractPropagator {

  69.     /** Internal name used for complete secondary state dimension.
  70.      * @since 11.1
  71.      */
  72.     private static final String SECONDARY_DIMENSION = "Orekit-secondary-dimension";

  73.     /** Event detectors not related to force models. */
  74.     private final List<EventDetector> detectors;

  75.     /** Step handlers dedicated to ephemeris generation. */
  76.     private final List<StoringStepHandler> ephemerisGenerators;

  77.     /** Integrator selected by the user for the orbital extrapolation process. */
  78.     private final ODEIntegrator integrator;

  79.     /** Offsets of secondary states managed by {@link AdditionalDerivativesProvider}.
  80.      * @since 11.1
  81.      */
  82.     private final Map<String, Integer> secondaryOffsets;

  83.     /** Additional derivatives providers.
  84.      * @since 11.1
  85.      */
  86.     private final List<AdditionalDerivativesProvider> additionalDerivativesProviders;

  87.     /** Map of secondary equation offset in main
  88.     /** Counter for differential equations calls. */
  89.     private int calls;

  90.     /** Mapper between raw double components and space flight dynamics objects. */
  91.     private StateMapper stateMapper;

  92.     /**
  93.      * Attitude provider when evaluating derivatives. Can be a frozen one for performance.
  94.      * @since 12.1
  95.      */
  96.     private AttitudeProvider attitudeProviderForDerivatives;

  97.     /** Flag for resetting the state at end of propagation. */
  98.     private boolean resetAtEnd;

  99.     /** Type of orbit to output (mean or osculating) <br/>
  100.      * <p>
  101.      * This is used only in the case of semi-analytical propagators where there is a clear separation between
  102.      * mean and short periodic elements. It is ignored by the Numerical propagator.
  103.      * </p>
  104.      */
  105.     private final PropagationType propagationType;

  106.     /** Build a new instance.
  107.      * @param integrator numerical integrator to use for propagation.
  108.      * @param propagationType type of orbit to output (mean or osculating).
  109.      */
  110.     protected AbstractIntegratedPropagator(final ODEIntegrator integrator, final PropagationType propagationType) {
  111.         detectors                      = new ArrayList<>();
  112.         ephemerisGenerators            = new ArrayList<>();
  113.         additionalDerivativesProviders = new ArrayList<>();
  114.         this.secondaryOffsets          = new HashMap<>();
  115.         this.integrator                = integrator;
  116.         this.propagationType           = propagationType;
  117.         this.resetAtEnd                = true;
  118.     }

  119.     /** Allow/disallow resetting the initial state at end of propagation.
  120.      * <p>
  121.      * By default, at the end of the propagation, the propagator resets the initial state
  122.      * to the final state, thus allowing a new propagation to be started from there without
  123.      * recomputing the part already performed. Calling this method with {@code resetAtEnd} set
  124.      * to false changes prevents such reset.
  125.      * </p>
  126.      * @param resetAtEnd if true, at end of each propagation, the {@link
  127.      * #getInitialState() initial state} will be reset to the final state of
  128.      * the propagation, otherwise the initial state will be preserved
  129.      * @since 9.0
  130.      */
  131.     public void setResetAtEnd(final boolean resetAtEnd) {
  132.         this.resetAtEnd = resetAtEnd;
  133.     }

  134.     /** Getter for the resetting flag regarding initial state.
  135.      * @return resetting flag
  136.      * @since 12.0
  137.      */
  138.     public boolean getResetAtEnd() {
  139.         return this.resetAtEnd;
  140.     }

  141.     /**
  142.      * Method called when initializing the attitude provider used when evaluating derivatives.
  143.      * @return attitude provider for derivatives
  144.      */
  145.     protected AttitudeProvider initializeAttitudeProviderForDerivatives() {
  146.         return getAttitudeProvider();
  147.     }

  148.     /** Initialize the mapper. */
  149.     protected void initMapper() {
  150.         stateMapper = createMapper(null, Double.NaN, null, null, null, null);
  151.     }

  152.     /** Get the integrator's name.
  153.      * @return name of underlying integrator
  154.      * @since 12.0
  155.      */
  156.     public String getIntegratorName() {
  157.         return integrator.getName();
  158.     }

  159.     /**  {@inheritDoc} */
  160.     @Override
  161.     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
  162.         super.setAttitudeProvider(attitudeProvider);
  163.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  164.                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  165.                                    attitudeProvider, stateMapper.getFrame());
  166.     }

  167.     /** Set propagation orbit type.
  168.      * @param orbitType orbit type to use for propagation, null for
  169.      * propagating using {@link org.orekit.utils.AbsolutePVCoordinates AbsolutePVCoordinates}
  170.      * rather than {@link org.orekit.orbits Orbit}
  171.      */
  172.     protected void setOrbitType(final OrbitType orbitType) {
  173.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  174.                                    orbitType, stateMapper.getPositionAngleType(),
  175.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  176.     }

  177.     /** Get propagation parameter type.
  178.      * @return orbit type used for propagation, null for
  179.      * propagating using {@link org.orekit.utils.AbsolutePVCoordinates AbsolutePVCoordinates}
  180.      * rather than {@link org.orekit.orbits Orbit}
  181.      */
  182.     protected OrbitType getOrbitType() {
  183.         return stateMapper.getOrbitType();
  184.     }

  185.     /** Get the propagation type.
  186.      * @return propagation type.
  187.      * @since 11.1
  188.      */
  189.     public PropagationType getPropagationType() {
  190.         return propagationType;
  191.     }

  192.     /** Set position angle type.
  193.      * <p>
  194.      * The position parameter type is meaningful only if {@link
  195.      * #getOrbitType() propagation orbit type}
  196.      * support it. As an example, it is not meaningful for propagation
  197.      * in {@link OrbitType#CARTESIAN Cartesian} parameters.
  198.      * </p>
  199.      * @param positionAngleType angle type to use for propagation
  200.      */
  201.     protected void setPositionAngleType(final PositionAngleType positionAngleType) {
  202.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  203.                                    stateMapper.getOrbitType(), positionAngleType,
  204.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  205.     }

  206.     /** Get propagation parameter type.
  207.      * @return angle type to use for propagation
  208.      */
  209.     protected PositionAngleType getPositionAngleType() {
  210.         return stateMapper.getPositionAngleType();
  211.     }

  212.     /** Set the central attraction coefficient μ.
  213.      * @param mu central attraction coefficient (m³/s²)
  214.      */
  215.     public void setMu(final double mu) {
  216.         stateMapper = createMapper(stateMapper.getReferenceDate(), mu,
  217.                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  218.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  219.     }

  220.     /** Get the central attraction coefficient μ.
  221.      * @return mu central attraction coefficient (m³/s²)
  222.      * @see #setMu(double)
  223.      */
  224.     public double getMu() {
  225.         return stateMapper.getMu();
  226.     }

  227.     /** Get the number of calls to the differential equations computation method.
  228.      * <p>The number of calls is reset each time the {@link #propagate(AbsoluteDate)}
  229.      * method is called.</p>
  230.      * @return number of calls to the differential equations computation method
  231.      */
  232.     public int getCalls() {
  233.         return calls;
  234.     }

  235.     /** {@inheritDoc} */
  236.     @Override
  237.     public boolean isAdditionalDataManaged(final String name) {

  238.         // first look at already integrated states
  239.         if (super.isAdditionalDataManaged(name)) {
  240.             return true;
  241.         }

  242.         // then look at states we integrate ourselves
  243.         for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  244.             if (provider.getName().equals(name)) {
  245.                 return true;
  246.             }
  247.         }

  248.         return false;
  249.     }

  250.     /** {@inheritDoc} */
  251.     @Override
  252.     public String[] getManagedAdditionalData() {
  253.         final String[] alreadyIntegrated = super.getManagedAdditionalData();
  254.         final String[] managed = new String[alreadyIntegrated.length + additionalDerivativesProviders.size()];
  255.         System.arraycopy(alreadyIntegrated, 0, managed, 0, alreadyIntegrated.length);
  256.         for (int i = 0; i < additionalDerivativesProviders.size(); ++i) {
  257.             managed[i + alreadyIntegrated.length] = additionalDerivativesProviders.get(i).getName();
  258.         }
  259.         return managed;
  260.     }

  261.     /** Add a provider for user-specified state derivatives to be integrated along with the orbit propagation.
  262.      * @param provider provider for additional derivatives
  263.      * @see #addAdditionalDataProvider(org.orekit.propagation.AdditionalDataProvider)
  264.      * @since 11.1
  265.      */
  266.     public void addAdditionalDerivativesProvider(final AdditionalDerivativesProvider provider) {

  267.         // check if the name is already used
  268.         if (this.isAdditionalDataManaged(provider.getName())) {
  269.             // these derivatives are already registered, complain
  270.             throw new OrekitException(OrekitMessages.ADDITIONAL_STATE_NAME_ALREADY_IN_USE,
  271.                                       provider.getName());
  272.         }

  273.         // this is really a new set of derivatives, add it
  274.         additionalDerivativesProviders.add(provider);

  275.         secondaryOffsets.clear();

  276.     }

  277.     /** Get an unmodifiable list of providers for additional derivatives.
  278.      * @return providers for the additional derivatives
  279.      * @since 11.1
  280.      */
  281.     public List<AdditionalDerivativesProvider> getAdditionalDerivativesProviders() {
  282.         return Collections.unmodifiableList(additionalDerivativesProviders);
  283.     }

  284.     /** {@inheritDoc} */
  285.     public void addEventDetector(final EventDetector detector) {
  286.         detectors.add(detector);
  287.     }

  288.     /** {@inheritDoc} */
  289.     public Collection<EventDetector> getEventDetectors() {
  290.         return Collections.unmodifiableCollection(detectors);
  291.     }

  292.     /** {@inheritDoc} */
  293.     public void clearEventsDetectors() {
  294.         detectors.clear();
  295.     }

  296.     /** Set up all user defined event detectors.
  297.      */
  298.     protected void setUpUserEventDetectors() {
  299.         for (final EventDetector detector : detectors) {
  300.             setUpEventDetector(integrator, detector);
  301.         }
  302.     }

  303.     /** Wrap an Orekit event detector and register it to the integrator.
  304.      * @param integ integrator into which event detector should be registered
  305.      * @param detector event detector to wrap
  306.      */
  307.     protected void setUpEventDetector(final ODEIntegrator integ, final EventDetector detector) {
  308.         integ.addEventDetector(new AdaptedEventDetector(detector));
  309.     }

  310.     /**
  311.      * Clear the ephemeris generators.
  312.      * @since 13.0
  313.      */
  314.     public void clearEphemerisGenerators() {
  315.         ephemerisGenerators.clear();
  316.     }

  317.     /** {@inheritDoc} */
  318.     @Override
  319.     public EphemerisGenerator getEphemerisGenerator() {
  320.         final StoringStepHandler storingHandler = new StoringStepHandler();
  321.         ephemerisGenerators.add(storingHandler);
  322.         return storingHandler;
  323.     }

  324.     /** Create a mapper between raw double components and spacecraft state.
  325.     /** Simple constructor.
  326.      * <p>
  327.      * The position parameter type is meaningful only if {@link
  328.      * #getOrbitType() propagation orbit type}
  329.      * support it. As an example, it is not meaningful for propagation
  330.      * in {@link OrbitType#CARTESIAN Cartesian} parameters.
  331.      * </p>
  332.      * @param referenceDate reference date
  333.      * @param mu central attraction coefficient (m³/s²)
  334.      * @param orbitType orbit type to use for mapping
  335.      * @param positionAngleType angle type to use for propagation
  336.      * @param attitudeProvider attitude provider
  337.      * @param frame inertial frame
  338.      * @return new mapper
  339.      */
  340.     protected abstract StateMapper createMapper(AbsoluteDate referenceDate, double mu,
  341.                                                 OrbitType orbitType, PositionAngleType positionAngleType,
  342.                                                 AttitudeProvider attitudeProvider, Frame frame);

  343.     /** Get the differential equations to integrate (for main state only).
  344.      * @param integ numerical integrator to use for propagation.
  345.      * @return differential equations for main state
  346.      */
  347.     protected abstract MainStateEquations getMainStateEquations(ODEIntegrator integ);

  348.     /** {@inheritDoc} */
  349.     @Override
  350.     public SpacecraftState propagate(final AbsoluteDate target) {
  351.         if (getStartDate() == null) {
  352.             if (getInitialState() == null) {
  353.                 throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  354.             }
  355.             setStartDate(getInitialState().getDate());
  356.         }
  357.         return propagate(getStartDate(), target);
  358.     }

  359.     /** {@inheritDoc} */
  360.     public SpacecraftState propagate(final AbsoluteDate tStart, final AbsoluteDate tEnd) {

  361.         if (getInitialState() == null) {
  362.             throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  363.         }

  364.         // make sure the integrator will be reset properly even if we change its events handlers and step handlers
  365.         try (IntegratorResetter resetter = new IntegratorResetter(integrator)) {

  366.             // prepare handling of STM and Jacobian matrices
  367.             setUpStmAndJacobianGenerators();

  368.             // Initialize additional data
  369.             initializeAdditionalData(tEnd);

  370.             if (!tStart.equals(getInitialState().getDate())) {
  371.                 // if propagation start date is not initial date,
  372.                 // propagate from initial to start date without event detection
  373.                 try (IntegratorResetter startResetter = new IntegratorResetter(integrator)) {
  374.                     integrateDynamics(tStart, true);
  375.                 }
  376.             }

  377.             // set up events added by user
  378.             setUpUserEventDetectors();

  379.             // set up step handlers
  380.             for (final OrekitStepHandler handler : getMultiplexer().getHandlers()) {
  381.                 integrator.addStepHandler(new AdaptedStepHandler(handler));
  382.             }
  383.             for (final StoringStepHandler generator : ephemerisGenerators) {
  384.                 generator.setEndDate(tEnd);
  385.                 integrator.addStepHandler(generator);
  386.             }

  387.             // propagate from start date to end date with event detection
  388.             final SpacecraftState finalState = integrateDynamics(tEnd, false);

  389.             // Finalize event detectors
  390.             getEventDetectors().forEach(detector -> detector.finish(finalState));

  391.             return finalState;
  392.         }

  393.     }

  394.     /** Reset initial state with a given propagation type.
  395.      *
  396.      * <p> By default this method returns the same as {@link #resetInitialState(SpacecraftState)}
  397.      * <p> Its purpose is mostly to be derived in DSSTPropagator
  398.      *
  399.      * @param state new initial state to consider
  400.      * @param stateType type of the new state (mean or osculating)
  401.      * @since 12.1.3
  402.      */
  403.     public void resetInitialState(final SpacecraftState state, final PropagationType stateType) {
  404.         // Default behavior, do not take propagation type into account
  405.         resetInitialState(state);
  406.     }

  407.     /** Set up State Transition Matrix and Jacobian matrix handling.
  408.      * @since 11.1
  409.      */
  410.     protected void setUpStmAndJacobianGenerators() {
  411.         // nothing to do by default
  412.     }

  413.     /** Propagation with or without event detection.
  414.      * @param tEnd target date to which orbit should be propagated
  415.      * @param forceResetAtEnd flag to force resetting state and date after integration
  416.      * @return state at end of propagation
  417.      */
  418.     private SpacecraftState integrateDynamics(final AbsoluteDate tEnd, final boolean forceResetAtEnd) {
  419.         try {

  420.             initializePropagation();

  421.             if (getInitialState().getDate().equals(tEnd)) {
  422.                 // don't extrapolate
  423.                 return getInitialState();
  424.             }

  425.             // space dynamics view
  426.             stateMapper = createMapper(getInitialState().getDate(), stateMapper.getMu(),
  427.                                        stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  428.                                        stateMapper.getAttitudeProvider(), getInitialState().getFrame());
  429.             attitudeProviderForDerivatives = initializeAttitudeProviderForDerivatives();

  430.             if (Double.isNaN(getMu())) {
  431.                 setMu(getInitialState().isOrbitDefined() ? getInitialState().getOrbit().getMu() : Double.NaN);
  432.             }

  433.             if (getInitialState().getMass() <= 0.0) {
  434.                 throw new OrekitException(OrekitMessages.NOT_POSITIVE_SPACECRAFT_MASS,
  435.                                           getInitialState().getMass());
  436.             }

  437.             // convert space flight dynamics API to math API
  438.             final SpacecraftState initialIntegrationState = getInitialIntegrationState();
  439.             final ODEState mathInitialState = createInitialState(initialIntegrationState);
  440.             final ExpandableODE mathODE = createODE(integrator);

  441.             // mathematical integration
  442.             final ODEStateAndDerivative mathFinalState;
  443.             beforeIntegration(initialIntegrationState, tEnd);
  444.             mathFinalState = integrator.integrate(mathODE, mathInitialState,
  445.                                                   tEnd.durationFrom(getInitialState().getDate()));
  446.             afterIntegration();

  447.             // get final state
  448.             SpacecraftState finalState =
  449.                             stateMapper.mapArrayToState(stateMapper.mapDoubleToDate(mathFinalState.getTime(),
  450.                                                                                     tEnd),
  451.                                                         mathFinalState.getPrimaryState(),
  452.                                                         mathFinalState.getPrimaryDerivative(),
  453.                                                         propagationType);

  454.             finalState = updateAdditionalStatesAndDerivatives(finalState, mathFinalState);

  455.             if (resetAtEnd || forceResetAtEnd) {
  456.                 resetInitialState(finalState, propagationType);
  457.                 setStartDate(finalState.getDate());
  458.             }

  459.             return finalState;

  460.         } catch (MathRuntimeException mre) {
  461.             throw OrekitException.unwrap(mre);
  462.         }
  463.     }

  464.     /**
  465.      * Returns an updated version of the inputted state with additional states, including
  466.      * from derivatives providers.
  467.      * @param originalState input state
  468.      * @param os ODE state and derivative
  469.      * @return new state
  470.      * @since 12.1
  471.      */
  472.     private SpacecraftState updateAdditionalStatesAndDerivatives(final SpacecraftState originalState,
  473.                                                                  final ODEStateAndDerivative os) {
  474.         SpacecraftState updatedState = originalState;
  475.         if (os.getNumberOfSecondaryStates() > 0) {
  476.             final double[] secondary           = os.getSecondaryState(1);
  477.             final double[] secondaryDerivative = os.getSecondaryDerivative(1);
  478.             for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  479.                 final String name      = provider.getName();
  480.                 final int    offset    = secondaryOffsets.get(name);
  481.                 final int    dimension = provider.getDimension();
  482.                 updatedState = updatedState.addAdditionalData(name, Arrays.copyOfRange(secondary, offset, offset + dimension));
  483.                 updatedState = updatedState.addAdditionalStateDerivative(name, Arrays.copyOfRange(secondaryDerivative, offset, offset + dimension));
  484.             }
  485.         }
  486.         return updateAdditionalData(updatedState);
  487.     }

  488.     /** Get the initial state for integration.
  489.      * @return initial state for integration
  490.      */
  491.     protected SpacecraftState getInitialIntegrationState() {
  492.         return getInitialState();
  493.     }

  494.     /** Create an initial state.
  495.      * @param initialState initial state in flight dynamics world
  496.      * @return initial state in mathematics world
  497.      */
  498.     private ODEState createInitialState(final SpacecraftState initialState) {

  499.         // retrieve initial state
  500.         final double[] primary = new double[getBasicDimension()];
  501.         stateMapper.mapStateToArray(initialState, primary, null);

  502.         if (secondaryOffsets.isEmpty()) {
  503.             // compute dimension of the secondary state
  504.             int offset = 0;
  505.             for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  506.                 secondaryOffsets.put(provider.getName(), offset);
  507.                 offset += provider.getDimension();
  508.             }
  509.             secondaryOffsets.put(SECONDARY_DIMENSION, offset);
  510.         }

  511.         return new ODEState(0.0, primary, secondary(initialState));

  512.     }

  513.     /** Create secondary state.
  514.      * @param state spacecraft state
  515.      * @return secondary state
  516.      * @since 11.1
  517.      */
  518.     private double[][] secondary(final SpacecraftState state) {

  519.         if (secondaryOffsets.isEmpty()) {
  520.             return null;
  521.         }

  522.         final double[][] secondary = new double[1][secondaryOffsets.get(SECONDARY_DIMENSION)];
  523.         for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  524.             final String   name       = provider.getName();
  525.             final int      offset     = secondaryOffsets.get(name);
  526.             final double[] additional = state.getAdditionalState(name);
  527.             System.arraycopy(additional, 0, secondary[0], offset, additional.length);
  528.         }

  529.         return secondary;

  530.     }

  531.     /** Create secondary state derivative.
  532.      * @param state spacecraft state
  533.      * @return secondary state derivative
  534.      * @since 11.1
  535.      */
  536.     private double[][] secondaryDerivative(final SpacecraftState state) {

  537.         if (secondaryOffsets.isEmpty()) {
  538.             return null;
  539.         }

  540.         final double[][] secondaryDerivative = new double[1][secondaryOffsets.get(SECONDARY_DIMENSION)];
  541.         for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  542.             final String   name       = provider.getName();
  543.             final int      offset     = secondaryOffsets.get(name);
  544.             final double[] additionalDerivative = state.getAdditionalStateDerivative(name);
  545.             System.arraycopy(additionalDerivative, 0, secondaryDerivative[0], offset, additionalDerivative.length);
  546.         }

  547.         return secondaryDerivative;

  548.     }

  549.     /** Create an ODE with all equations.
  550.      * @param integ numerical integrator to use for propagation.
  551.      * @return a new ode
  552.      */
  553.     private ExpandableODE createODE(final ODEIntegrator integ) {

  554.         final ExpandableODE ode =
  555.                 new ExpandableODE(new ConvertedMainStateEquations(getMainStateEquations(integ)));

  556.         // secondary part of the ODE
  557.         if (!additionalDerivativesProviders.isEmpty()) {
  558.             ode.addSecondaryEquations(new ConvertedSecondaryStateEquations());
  559.         }

  560.         return ode;

  561.     }

  562.     /** Method called just before integration.
  563.      * <p>
  564.      * The default implementation does nothing, it may be specialized in subclasses.
  565.      * </p>
  566.      * @param initialState initial state
  567.      * @param tEnd target date at which state should be propagated
  568.      */
  569.     protected void beforeIntegration(final SpacecraftState initialState,
  570.                                      final AbsoluteDate tEnd) {
  571.         // do nothing by default
  572.     }

  573.     /** Method called just after integration.
  574.      * <p>
  575.      * The default implementation does nothing, it may be specialized in subclasses.
  576.      * </p>
  577.      */
  578.     protected void afterIntegration() {
  579.         // do nothing by default
  580.     }

  581.     /** Get state vector dimension without additional parameters.
  582.      * @return state vector dimension without additional parameters.
  583.      */
  584.     public int getBasicDimension() {
  585.         return 7;
  586.     }

  587.     /** Get the integrator used by the propagator.
  588.      * @return the integrator.
  589.      */
  590.     protected ODEIntegrator getIntegrator() {
  591.         return integrator;
  592.     }

  593.     /** Convert a state from mathematical world to space flight dynamics world.
  594.      * @param os mathematical state
  595.      * @return space flight dynamics state
  596.      */
  597.     private SpacecraftState convert(final ODEStateAndDerivative os) {

  598.         final SpacecraftState s = stateMapper.mapArrayToState(os.getTime(), os.getPrimaryState(),
  599.             os.getPrimaryDerivative(), propagationType);
  600.         return updateAdditionalStatesAndDerivatives(s, os);
  601.     }

  602.     /** Convert a state from space flight dynamics world to mathematical world.
  603.      * @param state space flight dynamics state
  604.      * @return mathematical state
  605.      */
  606.     private ODEStateAndDerivative convert(final SpacecraftState state) {

  607.         // retrieve initial state
  608.         final double[] primary    = new double[getBasicDimension()];
  609.         final double[] primaryDot = new double[getBasicDimension()];
  610.         stateMapper.mapStateToArray(state, primary, primaryDot);

  611.         // secondary part of the ODE
  612.         final double[][] secondary           = secondary(state);
  613.         final double[][] secondaryDerivative = secondaryDerivative(state);

  614.         return new ODEStateAndDerivative(stateMapper.mapDateToDouble(state.getDate()),
  615.                                          primary, primaryDot,
  616.                                          secondary, secondaryDerivative);

  617.     }

  618.     /** Differential equations for the main state (orbit, attitude and mass). */
  619.     public interface MainStateEquations {

  620.         /**
  621.          * Initialize the equations at the start of propagation. This method will be
  622.          * called before any calls to {@link #computeDerivatives(SpacecraftState)}.
  623.          *
  624.          * <p> The default implementation of this method does nothing.
  625.          *
  626.          * @param initialState initial state information at the start of propagation.
  627.          * @param target       date of propagation. Not equal to {@code
  628.          *                     initialState.getDate()}.
  629.          */
  630.         default void init(final SpacecraftState initialState, final AbsoluteDate target) {
  631.         }

  632.         /** Compute differential equations for main state.
  633.          * @param state current state
  634.          * @return derivatives of main state
  635.          */
  636.         double[] computeDerivatives(SpacecraftState state);

  637.     }

  638.     /** Differential equations for the main state (orbit, attitude and mass), with converted API. */
  639.     private class ConvertedMainStateEquations implements OrdinaryDifferentialEquation {

  640.         /** Main state equations. */
  641.         private final MainStateEquations main;

  642.         /** Simple constructor.
  643.          * @param main main state equations
  644.          */
  645.         ConvertedMainStateEquations(final MainStateEquations main) {
  646.             this.main = main;
  647.             calls = 0;
  648.         }

  649.         /** {@inheritDoc} */
  650.         public int getDimension() {
  651.             return getBasicDimension();
  652.         }

  653.         @Override
  654.         public void init(final double t0, final double[] y0, final double finalTime) {
  655.             // update space dynamics view
  656.             SpacecraftState initialState = stateMapper.mapArrayToState(t0, y0, null, PropagationType.MEAN);
  657.             initialState = updateAdditionalData(initialState);
  658.             initialState = updateStatesFromAdditionalDerivativesIfKnown(initialState);
  659.             final AbsoluteDate target = stateMapper.mapDoubleToDate(finalTime);
  660.             main.init(initialState, target);
  661.             attitudeProviderForDerivatives = initializeAttitudeProviderForDerivatives();
  662.         }

  663.         /**
  664.          * Returns an updated version of the inputted state, with additional states from
  665.          * derivatives providers as given in the stored initial state.
  666.          * @param originalState input state
  667.          * @return new state
  668.          * @since 12.1
  669.          */
  670.         private SpacecraftState updateStatesFromAdditionalDerivativesIfKnown(final SpacecraftState originalState) {
  671.             SpacecraftState updatedState = originalState;
  672.             final SpacecraftState storedInitialState = getInitialState();
  673.             final double originalTime = stateMapper.mapDateToDouble(originalState.getDate());
  674.             if (storedInitialState != null && stateMapper.mapDateToDouble(storedInitialState.getDate()) == originalTime) {
  675.                 for (final AdditionalDerivativesProvider provider: additionalDerivativesProviders) {
  676.                     final String name = provider.getName();
  677.                     final double[] value = storedInitialState.getAdditionalState(name);
  678.                     updatedState = updatedState.addAdditionalData(name, value);
  679.                 }
  680.             }
  681.             return updatedState;
  682.         }

  683.         /** {@inheritDoc} */
  684.         public double[] computeDerivatives(final double t, final double[] y) {

  685.             // increment calls counter
  686.             ++calls;

  687.             // update space dynamics view
  688.             stateMapper.setAttitudeProvider(attitudeProviderForDerivatives);
  689.             SpacecraftState currentState = stateMapper.mapArrayToState(t, y, null, PropagationType.MEAN);
  690.             stateMapper.setAttitudeProvider(getAttitudeProvider());

  691.             currentState = updateAdditionalData(currentState);
  692.             // compute main state differentials
  693.             return main.computeDerivatives(currentState);

  694.         }

  695.     }

  696.     /** Differential equations for the secondary state (Jacobians, user variables ...), with converted API. */
  697.     private class ConvertedSecondaryStateEquations implements SecondaryODE {

  698.         /** Dimension of the combined additional states. */
  699.         private final int combinedDimension;

  700.         /** Simple constructor.
  701.           */
  702.         ConvertedSecondaryStateEquations() {
  703.             this.combinedDimension = secondaryOffsets.get(SECONDARY_DIMENSION);
  704.         }

  705.         /** {@inheritDoc} */
  706.         @Override
  707.         public int getDimension() {
  708.             return combinedDimension;
  709.         }

  710.         /** {@inheritDoc} */
  711.         @Override
  712.         public void init(final double t0, final double[] primary0,
  713.                          final double[] secondary0, final double finalTime) {
  714.             // update space dynamics view
  715.             final SpacecraftState initialState = convert(t0, primary0, null, secondary0);

  716.             final AbsoluteDate target = stateMapper.mapDoubleToDate(finalTime);
  717.             for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  718.                 provider.init(initialState, target);
  719.             }

  720.         }

  721.         /** {@inheritDoc} */
  722.         @Override
  723.         public double[] computeDerivatives(final double t, final double[] primary,
  724.                                            final double[] primaryDot, final double[] secondary) {

  725.             // update space dynamics view
  726.             // the integrable generators generate method will be called here,
  727.             // according to the generators yield order
  728.             SpacecraftState updated = convert(t, primary, primaryDot, secondary);

  729.             // set up queue for equations
  730.             final Queue<AdditionalDerivativesProvider> pending = new LinkedList<>(additionalDerivativesProviders);

  731.             // gather the derivatives from all additional equations, taking care of dependencies
  732.             final double[] secondaryDot = new double[combinedDimension];
  733.             int yieldCount = 0;
  734.             while (!pending.isEmpty()) {
  735.                 final AdditionalDerivativesProvider provider = pending.remove();
  736.                 if (provider.yields(updated)) {
  737.                     // this provider has to wait for another one,
  738.                     // we put it again in the pending queue
  739.                     pending.add(provider);
  740.                     if (++yieldCount >= pending.size()) {
  741.                         // all pending providers yielded!, they probably need data not yet initialized
  742.                         // we let the propagation proceed, if these data are really needed right now
  743.                         // an appropriate exception will be triggered when caller tries to access them
  744.                         break;
  745.                     }
  746.                 } else {
  747.                     // we can use these equations right now
  748.                     final String              name           = provider.getName();
  749.                     final int                 offset         = secondaryOffsets.get(name);
  750.                     final int                 dimension      = provider.getDimension();
  751.                     final CombinedDerivatives derivatives    = provider.combinedDerivatives(updated);
  752.                     final double[]            additionalPart = derivatives.getAdditionalDerivatives();
  753.                     final double[]            mainPart       = derivatives.getMainStateDerivativesIncrements();
  754.                     System.arraycopy(additionalPart, 0, secondaryDot, offset, dimension);
  755.                     updated = updated.addAdditionalStateDerivative(name, additionalPart);
  756.                     if (mainPart != null) {
  757.                         // this equation does change the main state derivatives
  758.                         for (int i = 0; i < mainPart.length; ++i) {
  759.                             primaryDot[i] += mainPart[i];
  760.                         }
  761.                     }
  762.                     yieldCount = 0;
  763.                 }
  764.             }

  765.             return secondaryDot;

  766.         }

  767.         /** Convert mathematical view to space view.
  768.          * @param t current value of the independent <I>time</I> variable
  769.          * @param primary array containing the current value of the primary state vector
  770.          * @param primaryDot array containing the derivative of the primary state vector
  771.          * @param secondary array containing the current value of the secondary state vector
  772.          * @return space view of the state
  773.          */
  774.         private SpacecraftState convert(final double t, final double[] primary,
  775.                                         final double[] primaryDot, final double[] secondary) {

  776.             SpacecraftState initialState = stateMapper.mapArrayToState(t, primary, primaryDot, PropagationType.MEAN);

  777.             for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  778.                 final String name      = provider.getName();
  779.                 final int    offset    = secondaryOffsets.get(name);
  780.                 final int    dimension = provider.getDimension();
  781.                 initialState = initialState.addAdditionalData(name, Arrays.copyOfRange(secondary, offset, offset + dimension));
  782.             }

  783.             return updateAdditionalData(initialState);

  784.         }

  785.     }

  786.     /** Adapt an {@link org.orekit.propagation.events.EventDetector}
  787.      * to Hipparchus {@link org.hipparchus.ode.events.ODEEventDetector} interface.
  788.      * @author Fabien Maussion
  789.      */
  790.     private class AdaptedEventDetector implements ODEEventDetector {

  791.         /** Underlying event detector. */
  792.         private final EventDetector detector;

  793.         /** Underlying event handler.
  794.          * @since 12.0
  795.          */
  796.         private final EventHandler handler;

  797.         /** Time of the previous call to g. */
  798.         private double lastT;

  799.         /** Value from the previous call to g. */
  800.         private double lastG;

  801.         /** Build a wrapped event detector.
  802.          * @param detector event detector to wrap
  803.         */
  804.         AdaptedEventDetector(final EventDetector detector) {
  805.             this.detector = detector;
  806.             this.handler  = detector.getHandler();
  807.             this.lastT    = Double.NaN;
  808.             this.lastG    = Double.NaN;
  809.         }

  810.         /** {@inheritDoc} */
  811.         @Override
  812.         public AdaptableInterval getMaxCheckInterval() {
  813.             return (state, isForward) -> detector.getMaxCheckInterval().currentInterval(convert(state), isForward);
  814.         }

  815.         /** {@inheritDoc} */
  816.         @Override
  817.         public int getMaxIterationCount() {
  818.             return detector.getMaxIterationCount();
  819.         }

  820.         /** {@inheritDoc} */
  821.         @Override
  822.         public BracketedUnivariateSolver<UnivariateFunction> getSolver() {
  823.             return new BracketingNthOrderBrentSolver(0, detector.getThreshold(), 0, 5);
  824.         }

  825.         /** {@inheritDoc} */
  826.         @Override
  827.         public void init(final ODEStateAndDerivative s0, final double t) {
  828.             detector.init(convert(s0), stateMapper.mapDoubleToDate(t));
  829.             this.lastT = Double.NaN;
  830.             this.lastG = Double.NaN;
  831.         }

  832.         /** {@inheritDoc} */
  833.         @Override
  834.         public void reset(final ODEStateAndDerivative intermediateState, final double finalTime) {
  835.             detector.reset(convert(intermediateState), stateMapper.mapDoubleToDate(finalTime));
  836.             this.lastT = Double.NaN;
  837.             this.lastG = Double.NaN;
  838.         }

  839.         /** {@inheritDoc} */
  840.         public double g(final ODEStateAndDerivative s) {
  841.             if (!Precision.equals(lastT, s.getTime(), 0)) {
  842.                 lastT = s.getTime();
  843.                 lastG = detector.g(convert(s));
  844.             }
  845.             return lastG;
  846.         }

  847.         /** {@inheritDoc} */
  848.         public ODEEventHandler getHandler() {

  849.             return new ODEEventHandler() {

  850.                 /** {@inheritDoc} */
  851.                 public Action eventOccurred(final ODEStateAndDerivative s, final ODEEventDetector d, final boolean increasing) {
  852.                     return handler.eventOccurred(convert(s), detector, increasing);
  853.                 }

  854.                 /** {@inheritDoc} */
  855.                 @Override
  856.                 public ODEState resetState(final ODEEventDetector d, final ODEStateAndDerivative s) {

  857.                     final SpacecraftState oldState = convert(s);
  858.                     final SpacecraftState newState = handler.resetState(detector, oldState);
  859.                     stateChanged(newState);

  860.                     // main part
  861.                     final double[] primary    = new double[s.getPrimaryStateDimension()];
  862.                     stateMapper.mapStateToArray(newState, primary, null);

  863.                     // secondary part
  864.                     final double[][] secondary = new double[1][secondaryOffsets.get(SECONDARY_DIMENSION)];
  865.                     for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  866.                         final String name      = provider.getName();
  867.                         final int    offset    = secondaryOffsets.get(name);
  868.                         final int    dimension = provider.getDimension();
  869.                         System.arraycopy(newState.getAdditionalState(name), 0, secondary[0], offset, dimension);
  870.                     }

  871.                     return new ODEState(newState.getDate().durationFrom(getStartDate()),
  872.                                         primary, secondary);

  873.                 }

  874.             };
  875.         }

  876.     }

  877.     /** Adapt an {@link org.orekit.propagation.sampling.OrekitStepHandler}
  878.      * to Hipparchus {@link ODEStepHandler} interface.
  879.      * @author Luc Maisonobe
  880.      */
  881.     private class AdaptedStepHandler implements ODEStepHandler {

  882.         /** Underlying handler. */
  883.         private final OrekitStepHandler handler;

  884.         /** Build an instance.
  885.          * @param handler underlying handler to wrap
  886.          */
  887.         AdaptedStepHandler(final OrekitStepHandler handler) {
  888.             this.handler = handler;
  889.         }

  890.         /** {@inheritDoc} */
  891.         @Override
  892.         public void init(final ODEStateAndDerivative s0, final double t) {
  893.             handler.init(convert(s0), stateMapper.mapDoubleToDate(t));
  894.         }

  895.         /** {@inheritDoc} */
  896.         @Override
  897.         public void handleStep(final ODEStateInterpolator interpolator) {
  898.             handler.handleStep(new AdaptedStepInterpolator(interpolator));
  899.         }

  900.         /** {@inheritDoc} */
  901.         @Override
  902.         public void finish(final ODEStateAndDerivative finalState) {
  903.             handler.finish(convert(finalState));
  904.         }

  905.     }

  906.     /** Adapt an Hipparchus {@link ODEStateInterpolator}
  907.      * to an orekit {@link OrekitStepInterpolator} interface.
  908.      * @author Luc Maisonobe
  909.      */
  910.     private class AdaptedStepInterpolator implements OrekitStepInterpolator {

  911.         /** Underlying raw rawInterpolator. */
  912.         private final ODEStateInterpolator mathInterpolator;

  913.         /** Simple constructor.
  914.          * @param mathInterpolator underlying raw interpolator
  915.          */
  916.         AdaptedStepInterpolator(final ODEStateInterpolator mathInterpolator) {
  917.             this.mathInterpolator = mathInterpolator;
  918.         }

  919.         /** {@inheritDoc}} */
  920.         @Override
  921.         public SpacecraftState getPreviousState() {
  922.             return convert(mathInterpolator.getPreviousState());
  923.         }

  924.         /** {@inheritDoc}} */
  925.         @Override
  926.         public boolean isPreviousStateInterpolated() {
  927.             return mathInterpolator.isPreviousStateInterpolated();
  928.         }

  929.         /** {@inheritDoc}} */
  930.         @Override
  931.         public SpacecraftState getCurrentState() {
  932.             return convert(mathInterpolator.getCurrentState());
  933.         }

  934.         /** {@inheritDoc}} */
  935.         @Override
  936.         public boolean isCurrentStateInterpolated() {
  937.             return mathInterpolator.isCurrentStateInterpolated();
  938.         }

  939.         /** {@inheritDoc}} */
  940.         @Override
  941.         public SpacecraftState getInterpolatedState(final AbsoluteDate date) {
  942.             return convert(mathInterpolator.getInterpolatedState(date.durationFrom(stateMapper.getReferenceDate())));
  943.         }

  944.         /** {@inheritDoc}} */
  945.         @Override
  946.         public boolean isForward() {
  947.             return mathInterpolator.isForward();
  948.         }

  949.         /** {@inheritDoc}} */
  950.         @Override
  951.         public AdaptedStepInterpolator restrictStep(final SpacecraftState newPreviousState,
  952.                                                     final SpacecraftState newCurrentState) {
  953.             try {
  954.                 final AbstractODEStateInterpolator aosi = (AbstractODEStateInterpolator) mathInterpolator;
  955.                 return new AdaptedStepInterpolator(aosi.restrictStep(convert(newPreviousState),
  956.                                                                      convert(newCurrentState)));
  957.             } catch (ClassCastException cce) {
  958.                 // this should never happen
  959.                 throw new OrekitInternalError(cce);
  960.             }
  961.         }

  962.     }

  963.     /** Specialized step handler storing interpolators for ephemeris generation.
  964.      * @since 11.0
  965.      */
  966.     private class StoringStepHandler implements ODEStepHandler, EphemerisGenerator {

  967.         /** Underlying raw mathematical model. */
  968.         private DenseOutputModel model;

  969.         /** the user supplied end date. Propagation may not end on this date. */
  970.         private AbsoluteDate endDate;

  971.         /** Generated ephemeris. */
  972.         private BoundedPropagator ephemeris;

  973.         /** Last interpolator handled by the object.*/
  974.         private  ODEStateInterpolator lastInterpolator;

  975.         /** Set the end date.
  976.          * @param endDate end date
  977.          */
  978.         public void setEndDate(final AbsoluteDate endDate) {
  979.             this.endDate = endDate;
  980.         }

  981.         /** {@inheritDoc} */
  982.         @Override
  983.         public void init(final ODEStateAndDerivative s0, final double t) {

  984.             this.model = new DenseOutputModel();
  985.             model.init(s0, t);

  986.             // ephemeris will be generated when last step is processed
  987.             this.ephemeris = null;

  988.             this.lastInterpolator = null;

  989.         }

  990.         /** {@inheritDoc} */
  991.         @Override
  992.         public BoundedPropagator getGeneratedEphemeris() {
  993.             // Each time we try to get the ephemeris, rebuild it using the last data.
  994.             buildEphemeris();
  995.             return ephemeris;
  996.         }

  997.         /** {@inheritDoc} */
  998.         @Override
  999.         public void handleStep(final ODEStateInterpolator interpolator) {
  1000.             model.handleStep(interpolator);
  1001.             lastInterpolator = interpolator;
  1002.         }

  1003.         /** {@inheritDoc} */
  1004.         @Override
  1005.         public void finish(final ODEStateAndDerivative finalState) {
  1006.             buildEphemeris();
  1007.         }

  1008.         /** Method used to produce ephemeris at a given time.
  1009.          * Can be used at multiple times, updating the ephemeris to
  1010.          * its last state.
  1011.          */
  1012.         private void buildEphemeris() {
  1013.             // buildEphemeris was built in order to allow access to what was previously the finish method.
  1014.             // This now allows to call it through getGeneratedEphemeris, therefore through an external call,
  1015.             // which was not previously the case.

  1016.             // Update the model's finalTime with the last interpolator.
  1017.             model.finish(lastInterpolator.getCurrentState());

  1018.             // set up the boundary dates
  1019.             final double tI = model.getInitialTime();
  1020.             final double tF = model.getFinalTime();
  1021.             // tI is almost? always zero
  1022.             final AbsoluteDate startDate =
  1023.                             stateMapper.mapDoubleToDate(tI);
  1024.             final AbsoluteDate finalDate =
  1025.                             stateMapper.mapDoubleToDate(tF, this.endDate);
  1026.             final AbsoluteDate minDate;
  1027.             final AbsoluteDate maxDate;
  1028.             if (tF < tI) {
  1029.                 minDate = finalDate;
  1030.                 maxDate = startDate;
  1031.             } else {
  1032.                 minDate = startDate;
  1033.                 maxDate = finalDate;
  1034.             }

  1035.             // get the initial additional states that are not managed
  1036.             final DataDictionary unmanaged = new DataDictionary();
  1037.             for (final DataDictionary.Entry initial : getInitialState().getAdditionalDataValues().getData()) {
  1038.                 if (!AbstractIntegratedPropagator.this.isAdditionalDataManaged(initial.getKey())) {
  1039.                     // this additional state was in the initial state, but is unknown to the propagator
  1040.                     // we simply copy its initial value as is
  1041.                     unmanaged.put(initial.getKey(), initial.getValue());
  1042.                 }
  1043.             }

  1044.             // get the names of additional states managed by differential equations
  1045.             final String[] names      = new String[additionalDerivativesProviders.size()];
  1046.             final int[]    dimensions = new int[additionalDerivativesProviders.size()];
  1047.             for (int i = 0; i < names.length; ++i) {
  1048.                 names[i] = additionalDerivativesProviders.get(i).getName();
  1049.                 dimensions[i] = additionalDerivativesProviders.get(i).getDimension();
  1050.             }

  1051.             // create the ephemeris
  1052.             ephemeris = new IntegratedEphemeris(startDate, minDate, maxDate,
  1053.                                                 stateMapper, getAttitudeProvider(), propagationType, model,
  1054.                                                 unmanaged, getAdditionalDataProviders(),
  1055.                                                 names, dimensions);

  1056.         }

  1057.     }

  1058.     /** Wrapper for resetting an integrator handlers.
  1059.      * <p>
  1060.      * This class is intended to be used in a try-with-resource statement.
  1061.      * If propagator-specific event handlers and step handlers are added to
  1062.      * the integrator in the try block, they will be removed automatically
  1063.      * when leaving the block, so the integrator only keeps its own handlers
  1064.      * between calls to {@link AbstractIntegratedPropagator#propagate(AbsoluteDate, AbsoluteDate).
  1065.      * </p>
  1066.      * @since 11.0
  1067.      */
  1068.     private static class IntegratorResetter implements AutoCloseable {

  1069.         /** Wrapped integrator. */
  1070.         private final ODEIntegrator integrator;

  1071.         /** Initial event detectors list. */
  1072.         private final List<ODEEventDetector> detectors;

  1073.         /** Initial step handlers list. */
  1074.         private final List<ODEStepHandler> stepHandlers;

  1075.         /** Simple constructor.
  1076.          * @param integrator wrapped integrator
  1077.          */
  1078.         IntegratorResetter(final ODEIntegrator integrator) {
  1079.             this.integrator   = integrator;
  1080.             this.detectors    = new ArrayList<>(integrator.getEventDetectors());
  1081.             this.stepHandlers = new ArrayList<>(integrator.getStepHandlers());
  1082.         }

  1083.         /** {@inheritDoc}
  1084.          * <p>
  1085.          * Reset event handlers and step handlers back to the initial list
  1086.          * </p>
  1087.          */
  1088.         @Override
  1089.         public void close() {

  1090.             // reset event handlers
  1091.             integrator.clearEventDetectors();
  1092.             detectors.forEach(integrator::addEventDetector);

  1093.             // reset step handlers
  1094.             integrator.clearStepHandlers();
  1095.             stepHandlers.forEach(integrator::addStepHandler);

  1096.         }

  1097.     }

  1098. }