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.attitudes.AttitudeProviderModifier;
  39. import org.orekit.errors.OrekitException;
  40. import org.orekit.errors.OrekitInternalError;
  41. import org.orekit.errors.OrekitMessages;
  42. import org.orekit.frames.Frame;
  43. import org.orekit.orbits.OrbitType;
  44. import org.orekit.orbits.PositionAngleType;
  45. import org.orekit.propagation.AbstractPropagator;
  46. import org.orekit.propagation.BoundedPropagator;
  47. import org.orekit.propagation.EphemerisGenerator;
  48. import org.orekit.propagation.PropagationType;
  49. import org.orekit.propagation.SpacecraftState;
  50. import org.orekit.propagation.events.EventDetector;
  51. import org.orekit.propagation.events.handlers.EventHandler;
  52. import org.orekit.propagation.sampling.OrekitStepHandler;
  53. import org.orekit.propagation.sampling.OrekitStepInterpolator;
  54. import org.orekit.time.AbsoluteDate;
  55. import org.orekit.utils.DataDictionary;

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


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

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

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

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

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

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

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

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

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

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

  98.     /**
  99.      * Attitude provider with frozen rates, used when possible for performance.
  100.      * @since 13.1
  101.      */
  102.     private AttitudeProvider frozenAttitudeProvider;

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

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

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

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

  140.     /** Getter for the resetting flag regarding initial state.
  141.      * @return resetting flag
  142.      * @since 12.0
  143.      */
  144.     public boolean getResetAtEnd() {
  145.         return this.resetAtEnd;
  146.     }

  147.     /**
  148.      * Getter for the frozen attitude provider, used for performance when possible.
  149.      * @return frozen attitude provider
  150.      * @since 13.1
  151.      */
  152.     protected AttitudeProvider getFrozenAttitudeProvider() {
  153.         return frozenAttitudeProvider;
  154.     }

  155.     /**
  156.      * Method called when initializing the attitude provider used when evaluating derivatives.
  157.      * @return attitude provider for derivatives
  158.      */
  159.     protected AttitudeProvider initializeAttitudeProviderForDerivatives() {
  160.         return getAttitudeProvider();
  161.     }

  162.     /** Initialize the mapper. */
  163.     protected void initMapper() {
  164.         stateMapper = createMapper(null, Double.NaN, null, null, null, null);
  165.     }

  166.     /** Get the integrator's name.
  167.      * @return name of underlying integrator
  168.      * @since 12.0
  169.      */
  170.     public String getIntegratorName() {
  171.         return integrator.getName();
  172.     }

  173.     /**  {@inheritDoc} */
  174.     @Override
  175.     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
  176.         super.setAttitudeProvider(attitudeProvider);
  177.         frozenAttitudeProvider = AttitudeProviderModifier.getFrozenAttitudeProvider(attitudeProvider);
  178.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  179.                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  180.                                    attitudeProvider, stateMapper.getFrame());
  181.     }

  182.     /** Set propagation orbit type.
  183.      * @param orbitType orbit type to use for propagation, null for
  184.      * propagating using {@link org.orekit.utils.AbsolutePVCoordinates AbsolutePVCoordinates}
  185.      * rather than {@link org.orekit.orbits Orbit}
  186.      */
  187.     protected void setOrbitType(final OrbitType orbitType) {
  188.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  189.                                    orbitType, stateMapper.getPositionAngleType(),
  190.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  191.     }

  192.     /** Get propagation parameter type.
  193.      * @return orbit type used for propagation, null for
  194.      * propagating using {@link org.orekit.utils.AbsolutePVCoordinates AbsolutePVCoordinates}
  195.      * rather than {@link org.orekit.orbits Orbit}
  196.      */
  197.     protected OrbitType getOrbitType() {
  198.         return stateMapper.getOrbitType();
  199.     }

  200.     /** Get the propagation type.
  201.      * @return propagation type.
  202.      * @since 11.1
  203.      */
  204.     public PropagationType getPropagationType() {
  205.         return propagationType;
  206.     }

  207.     /** Set position angle type.
  208.      * <p>
  209.      * The position parameter type is meaningful only if {@link
  210.      * #getOrbitType() propagation orbit type}
  211.      * support it. As an example, it is not meaningful for propagation
  212.      * in {@link OrbitType#CARTESIAN Cartesian} parameters.
  213.      * </p>
  214.      * @param positionAngleType angle type to use for propagation
  215.      */
  216.     protected void setPositionAngleType(final PositionAngleType positionAngleType) {
  217.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  218.                                    stateMapper.getOrbitType(), positionAngleType,
  219.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  220.     }

  221.     /** Get propagation parameter type.
  222.      * @return angle type to use for propagation
  223.      */
  224.     protected PositionAngleType getPositionAngleType() {
  225.         return stateMapper.getPositionAngleType();
  226.     }

  227.     /** Set the central attraction coefficient μ.
  228.      * @param mu central attraction coefficient (m³/s²)
  229.      */
  230.     public void setMu(final double mu) {
  231.         stateMapper = createMapper(stateMapper.getReferenceDate(), mu,
  232.                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  233.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  234.     }

  235.     /** Get the central attraction coefficient μ.
  236.      * @return mu central attraction coefficient (m³/s²)
  237.      * @see #setMu(double)
  238.      */
  239.     public double getMu() {
  240.         return stateMapper.getMu();
  241.     }

  242.     /** Get the number of calls to the differential equations computation method.
  243.      * <p>The number of calls is reset each time the {@link #propagate(AbsoluteDate)}
  244.      * method is called.</p>
  245.      * @return number of calls to the differential equations computation method
  246.      */
  247.     public int getCalls() {
  248.         return calls;
  249.     }

  250.     /** {@inheritDoc} */
  251.     @Override
  252.     public boolean isAdditionalDataManaged(final String name) {

  253.         // first look at already integrated states
  254.         if (super.isAdditionalDataManaged(name)) {
  255.             return true;
  256.         }

  257.         // then look at states we integrate ourselves
  258.         for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  259.             if (provider.getName().equals(name)) {
  260.                 return true;
  261.             }
  262.         }

  263.         return false;
  264.     }

  265.     /** {@inheritDoc} */
  266.     @Override
  267.     public String[] getManagedAdditionalData() {
  268.         final String[] alreadyIntegrated = super.getManagedAdditionalData();
  269.         final String[] managed = new String[alreadyIntegrated.length + additionalDerivativesProviders.size()];
  270.         System.arraycopy(alreadyIntegrated, 0, managed, 0, alreadyIntegrated.length);
  271.         for (int i = 0; i < additionalDerivativesProviders.size(); ++i) {
  272.             managed[i + alreadyIntegrated.length] = additionalDerivativesProviders.get(i).getName();
  273.         }
  274.         return managed;
  275.     }

  276.     /** Add a provider for user-specified state derivatives to be integrated along with the orbit propagation.
  277.      * @param provider provider for additional derivatives
  278.      * @see #addAdditionalDataProvider(org.orekit.propagation.AdditionalDataProvider)
  279.      * @since 11.1
  280.      */
  281.     public void addAdditionalDerivativesProvider(final AdditionalDerivativesProvider provider) {

  282.         // check if the name is already used
  283.         if (this.isAdditionalDataManaged(provider.getName())) {
  284.             // these derivatives are already registered, complain
  285.             throw new OrekitException(OrekitMessages.ADDITIONAL_STATE_NAME_ALREADY_IN_USE,
  286.                                       provider.getName());
  287.         }

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

  290.         secondaryOffsets.clear();

  291.     }

  292.     /** Get an unmodifiable list of providers for additional derivatives.
  293.      * @return providers for the additional derivatives
  294.      * @since 11.1
  295.      */
  296.     public List<AdditionalDerivativesProvider> getAdditionalDerivativesProviders() {
  297.         return Collections.unmodifiableList(additionalDerivativesProviders);
  298.     }

  299.     /**
  300.      * Remove additional derivatives provider.
  301.      * @param name provider's name.
  302.      * @since 13.1
  303.      */
  304.     protected void removeAdditionalDerivativesProvider(final String name) {
  305.         additionalDerivativesProviders.removeIf(provider -> provider.getName().equals(name));
  306.     }

  307.     /** {@inheritDoc} */
  308.     public void addEventDetector(final EventDetector detector) {
  309.         detectors.add(detector);
  310.     }

  311.     /** {@inheritDoc} */
  312.     public Collection<EventDetector> getEventDetectors() {
  313.         return Collections.unmodifiableCollection(detectors);
  314.     }

  315.     /** {@inheritDoc} */
  316.     public void clearEventsDetectors() {
  317.         detectors.clear();
  318.     }

  319.     /** Set up all user defined event detectors.
  320.      */
  321.     protected void setUpUserEventDetectors() {
  322.         for (final EventDetector detector : detectors) {
  323.             setUpEventDetector(integrator, detector);
  324.         }
  325.     }

  326.     /** Wrap an Orekit event detector and register it to the integrator.
  327.      * @param integ integrator into which event detector should be registered
  328.      * @param detector event detector to wrap
  329.      */
  330.     protected void setUpEventDetector(final ODEIntegrator integ, final EventDetector detector) {
  331.         integ.addEventDetector(new AdaptedEventDetector(detector));
  332.     }

  333.     /**
  334.      * Clear the ephemeris generators.
  335.      * @since 13.0
  336.      */
  337.     public void clearEphemerisGenerators() {
  338.         ephemerisGenerators.clear();
  339.     }

  340.     /** {@inheritDoc} */
  341.     @Override
  342.     public EphemerisGenerator getEphemerisGenerator() {
  343.         final StoringStepHandler storingHandler = new StoringStepHandler();
  344.         ephemerisGenerators.add(storingHandler);
  345.         return storingHandler;
  346.     }

  347.     /** Create a mapper between raw double components and spacecraft state.
  348.     /** Simple constructor.
  349.      * <p>
  350.      * The position parameter type is meaningful only if {@link
  351.      * #getOrbitType() propagation orbit type}
  352.      * support it. As an example, it is not meaningful for propagation
  353.      * in {@link OrbitType#CARTESIAN Cartesian} parameters.
  354.      * </p>
  355.      * @param referenceDate reference date
  356.      * @param mu central attraction coefficient (m³/s²)
  357.      * @param orbitType orbit type to use for mapping
  358.      * @param positionAngleType angle type to use for propagation
  359.      * @param attitudeProvider attitude provider
  360.      * @param frame inertial frame
  361.      * @return new mapper
  362.      */
  363.     protected abstract StateMapper createMapper(AbsoluteDate referenceDate, double mu,
  364.                                                 OrbitType orbitType, PositionAngleType positionAngleType,
  365.                                                 AttitudeProvider attitudeProvider, Frame frame);

  366.     /** Get the differential equations to integrate (for main state only).
  367.      * @param integ numerical integrator to use for propagation.
  368.      * @return differential equations for main state
  369.      */
  370.     protected abstract MainStateEquations getMainStateEquations(ODEIntegrator integ);

  371.     /** {@inheritDoc} */
  372.     @Override
  373.     public SpacecraftState propagate(final AbsoluteDate target) {
  374.         if (getStartDate() == null) {
  375.             if (getInitialState() == null) {
  376.                 throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  377.             }
  378.             setStartDate(getInitialState().getDate());
  379.         }
  380.         return propagate(getStartDate(), target);
  381.     }

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

  384.         if (getInitialState() == null) {
  385.             throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  386.         }

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

  389.             // prepare handling of STM and Jacobian matrices
  390.             setUpStmAndJacobianGenerators();

  391.             // Initialize additional data
  392.             initializeAdditionalData(tEnd);

  393.             if (!tStart.equals(getInitialState().getDate())) {
  394.                 // if propagation start date is not initial date,
  395.                 // propagate from initial to start date without event detection
  396.                 try (IntegratorResetter startResetter = new IntegratorResetter(integrator)) {
  397.                     integrateDynamics(tStart, true);
  398.                 }
  399.             }

  400.             // set up events added by user
  401.             setUpUserEventDetectors();

  402.             // set up step handlers
  403.             for (final OrekitStepHandler handler : getMultiplexer().getHandlers()) {
  404.                 integrator.addStepHandler(new AdaptedStepHandler(handler));
  405.             }
  406.             for (final StoringStepHandler generator : ephemerisGenerators) {
  407.                 generator.setEndDate(tEnd);
  408.                 integrator.addStepHandler(generator);
  409.             }

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

  412.             // Finalize event detectors
  413.             getEventDetectors().forEach(detector -> detector.finish(finalState));

  414.             return finalState;
  415.         }

  416.     }

  417.     /** Reset initial state with a given propagation type.
  418.      *
  419.      * <p> By default this method returns the same as {@link #resetInitialState(SpacecraftState)}
  420.      * <p> Its purpose is mostly to be derived in DSSTPropagator
  421.      *
  422.      * @param state new initial state to consider
  423.      * @param stateType type of the new state (mean or osculating)
  424.      * @since 12.1.3
  425.      */
  426.     public void resetInitialState(final SpacecraftState state, final PropagationType stateType) {
  427.         // Default behavior, do not take propagation type into account
  428.         resetInitialState(state);
  429.     }

  430.     /** Set up State Transition Matrix and Jacobian matrix handling.
  431.      * @since 11.1
  432.      */
  433.     protected void setUpStmAndJacobianGenerators() {
  434.         // nothing to do by default
  435.     }

  436.     /** Propagation with or without event detection.
  437.      * @param tEnd target date to which orbit should be propagated
  438.      * @param forceResetAtEnd flag to force resetting state and date after integration
  439.      * @return state at end of propagation
  440.      */
  441.     private SpacecraftState integrateDynamics(final AbsoluteDate tEnd, final boolean forceResetAtEnd) {
  442.         try {

  443.             initializePropagation();

  444.             if (getInitialState().getDate().equals(tEnd)) {
  445.                 // don't extrapolate
  446.                 return getInitialState();
  447.             }

  448.             // space dynamics view
  449.             stateMapper = createMapper(getInitialState().getDate(), stateMapper.getMu(),
  450.                                        stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  451.                                        stateMapper.getAttitudeProvider(), getInitialState().getFrame());
  452.             attitudeProviderForDerivatives = initializeAttitudeProviderForDerivatives();

  453.             if (Double.isNaN(getMu())) {
  454.                 setMu(getInitialState().isOrbitDefined() ? getInitialState().getOrbit().getMu() : Double.NaN);
  455.             }

  456.             if (getInitialState().getMass() <= 0.0) {
  457.                 throw new OrekitException(OrekitMessages.NOT_POSITIVE_SPACECRAFT_MASS,
  458.                                           getInitialState().getMass());
  459.             }

  460.             // convertFromOrekit space flight dynamics API to math API
  461.             final SpacecraftState initialIntegrationState = getInitialIntegrationState();
  462.             final ODEState mathInitialState = createInitialState(initialIntegrationState);
  463.             final ExpandableODE mathODE = createODE(integrator);

  464.             // mathematical integration
  465.             final ODEStateAndDerivative mathFinalState;
  466.             beforeIntegration(initialIntegrationState, tEnd);
  467.             mathFinalState = integrator.integrate(mathODE, mathInitialState,
  468.                                                   tEnd.durationFrom(getInitialState().getDate()));
  469.             afterIntegration();

  470.             // get final state
  471.             SpacecraftState finalState =
  472.                             stateMapper.mapArrayToState(stateMapper.mapDoubleToDate(mathFinalState.getTime(),
  473.                                                                                     tEnd),
  474.                                                         mathFinalState.getPrimaryState(),
  475.                                                         mathFinalState.getPrimaryDerivative(),
  476.                                                         propagationType);

  477.             finalState = updateAdditionalStatesAndDerivatives(finalState, mathFinalState);

  478.             if (resetAtEnd || forceResetAtEnd) {
  479.                 resetInitialState(finalState, propagationType);
  480.                 setStartDate(finalState.getDate());
  481.             }

  482.             return finalState;

  483.         } catch (MathRuntimeException mre) {
  484.             throw OrekitException.unwrap(mre);
  485.         }
  486.     }

  487.     /**
  488.      * Returns an updated version of the inputted state with additional states, including
  489.      * from derivatives providers.
  490.      * @param originalState input state
  491.      * @param os ODE state and derivative
  492.      * @return new state
  493.      * @since 12.1
  494.      */
  495.     private SpacecraftState updateAdditionalStatesAndDerivatives(final SpacecraftState originalState,
  496.                                                                  final ODEStateAndDerivative os) {
  497.         SpacecraftState updatedState = originalState;
  498.         if (os.getNumberOfSecondaryStates() > 0) {
  499.             final double[] secondary           = os.getSecondaryState(1);
  500.             final double[] secondaryDerivative = os.getSecondaryDerivative(1);
  501.             for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  502.                 final String name      = provider.getName();
  503.                 final int    offset    = secondaryOffsets.get(name);
  504.                 final int    dimension = provider.getDimension();
  505.                 updatedState = updatedState.addAdditionalData(name, Arrays.copyOfRange(secondary, offset, offset + dimension));
  506.                 updatedState = updatedState.addAdditionalStateDerivative(name, Arrays.copyOfRange(secondaryDerivative, offset, offset + dimension));
  507.             }
  508.         }
  509.         return updateAdditionalData(updatedState);
  510.     }

  511.     /** Get the initial state for integration.
  512.      * @return initial state for integration
  513.      */
  514.     protected SpacecraftState getInitialIntegrationState() {
  515.         return getInitialState();
  516.     }

  517.     /** Create an initial state.
  518.      * @param initialState initial state in flight dynamics world
  519.      * @return initial state in mathematics world
  520.      */
  521.     private ODEState createInitialState(final SpacecraftState initialState) {

  522.         // retrieve initial state
  523.         final double[] primary = new double[getBasicDimension()];
  524.         stateMapper.mapStateToArray(initialState, primary, null);

  525.         if (secondaryOffsets.isEmpty()) {
  526.             // compute dimension of the secondary state
  527.             int offset = 0;
  528.             for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  529.                 secondaryOffsets.put(provider.getName(), offset);
  530.                 offset += provider.getDimension();
  531.             }
  532.             secondaryOffsets.put(SECONDARY_DIMENSION, offset);
  533.         }

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

  535.     }

  536.     /** Create secondary state.
  537.      * @param state spacecraft state
  538.      * @return secondary state
  539.      * @since 11.1
  540.      */
  541.     private double[][] secondary(final SpacecraftState state) {

  542.         if (secondaryOffsets.isEmpty()) {
  543.             return null;
  544.         }

  545.         final double[][] secondary = new double[1][secondaryOffsets.get(SECONDARY_DIMENSION)];
  546.         for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  547.             final String   name       = provider.getName();
  548.             final int      offset     = secondaryOffsets.get(name);
  549.             final double[] additional = state.getAdditionalState(name);
  550.             System.arraycopy(additional, 0, secondary[0], offset, additional.length);
  551.         }

  552.         return secondary;

  553.     }

  554.     /** Create an ODE with all equations.
  555.      * @param integ numerical integrator to use for propagation.
  556.      * @return a new ode
  557.      */
  558.     private ExpandableODE createODE(final ODEIntegrator integ) {

  559.         final ExpandableODE ode =
  560.                 new ExpandableODE(new ConvertedMainStateEquations(getMainStateEquations(integ)));

  561.         // secondary part of the ODE
  562.         if (!additionalDerivativesProviders.isEmpty()) {
  563.             ode.addSecondaryEquations(new ConvertedSecondaryStateEquations());
  564.         }

  565.         return ode;

  566.     }

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

  578.     /** Method called just after integration.
  579.      * <p>
  580.      * The default implementation does nothing, it may be specialized in subclasses.
  581.      * </p>
  582.      */
  583.     protected void afterIntegration() {
  584.         // do nothing by default
  585.     }

  586.     /** Get state vector dimension without additional parameters.
  587.      * @return state vector dimension without additional parameters.
  588.      */
  589.     public int getBasicDimension() {
  590.         return 7;
  591.     }

  592.     /** Get the integrator used by the propagator.
  593.      * @return the integrator.
  594.      */
  595.     protected ODEIntegrator getIntegrator() {
  596.         return integrator;
  597.     }

  598.     /** Convert a state from mathematical world to space flight dynamics world.
  599.      * @param os mathematical state
  600.      * @return space flight dynamics state
  601.      */
  602.     private SpacecraftState convertToOrekitWithAdditional(final ODEStateAndDerivative os) {
  603.         final SpacecraftState s = convertToOrekitWithoutAdditional(os);
  604.         return updateAdditionalStatesAndDerivatives(s, os);
  605.     }

  606.     /** Convert a state from mathematical world to space flight dynamics world without additional data and derivatives.
  607.      * @param os mathematical state
  608.      * @return space flight dynamics state
  609.      * @since 13.1
  610.      */
  611.     private SpacecraftState convertToOrekitWithoutAdditional(final ODEStateAndDerivative os) {
  612.         return stateMapper.mapArrayToState(os.getTime(), os.getPrimaryState(),
  613.             os.getPrimaryDerivative(), propagationType);
  614.     }

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

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

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

  634.     }

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

  637.         /** Main state equations. */
  638.         private final MainStateEquations main;

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

  646.         /** {@inheritDoc} */
  647.         public int getDimension() {
  648.             return getBasicDimension();
  649.         }

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

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

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

  682.             // increment calls counter
  683.             ++calls;

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

  688.             currentState = updateAdditionalData(currentState);
  689.             // compute main state differentials
  690.             return main.computeDerivatives(currentState);

  691.         }

  692.     }

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

  695.         /** Dimension of the combined additional states. */
  696.         private final int combinedDimension;

  697.         /** Simple constructor.
  698.           */
  699.         ConvertedSecondaryStateEquations() {
  700.             this.combinedDimension = secondaryOffsets.get(SECONDARY_DIMENSION);
  701.         }

  702.         /** {@inheritDoc} */
  703.         @Override
  704.         public int getDimension() {
  705.             return combinedDimension;
  706.         }

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

  713.             final AbsoluteDate target = stateMapper.mapDoubleToDate(finalTime);
  714.             for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  715.                 provider.init(initialState, target);
  716.             }

  717.         }

  718.         /** {@inheritDoc} */
  719.         @Override
  720.         public double[] computeDerivatives(final double t, final double[] primary,
  721.                                            final double[] primaryDot, final double[] secondary) {

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

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

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

  762.             return secondaryDot;

  763.         }

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

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

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

  780.             return updateAdditionalData(initialState);

  781.         }

  782.     }

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

  788.         /** Underlying event detector. */
  789.         private final EventDetector detector;

  790.         /** Underlying event handler.
  791.          * @since 12.0
  792.          */
  793.         private final EventHandler handler;

  794.         /** Time of the previous call to g. */
  795.         private double lastT;

  796.         /** Value from the previous call to g. */
  797.         private double lastG;

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

  807.         /** {@inheritDoc} */
  808.         @Override
  809.         public AdaptableInterval getMaxCheckInterval() {
  810.             return (state, isForward) -> detector.getMaxCheckInterval().currentInterval(convertToOrekitForEventFunction(state), isForward);
  811.         }

  812.         /** {@inheritDoc} */
  813.         @Override
  814.         public int getMaxIterationCount() {
  815.             return detector.getMaxIterationCount();
  816.         }

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

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

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

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

  844.         /**
  845.          * Convert state from Hipparchus to Orekit format.
  846.          * @param s state vector
  847.          * @return Orekit state
  848.          */
  849.         private SpacecraftState convertToOrekitForEventFunction(final ODEStateAndDerivative s) {
  850.             if (!this.detector.dependsOnTimeOnly()) {
  851.                 return convertToOrekitWithAdditional(s);
  852.             } else {
  853.                 // event function only needs time
  854.                 stateMapper.setAttitudeProvider(getFrozenAttitudeProvider());
  855.                 final SpacecraftState converted = convertToOrekitWithoutAdditional(s);
  856.                 stateMapper.setAttitudeProvider(getAttitudeProvider());
  857.                 return converted;
  858.             }
  859.         }

  860.         /** {@inheritDoc} */
  861.         public ODEEventHandler getHandler() {

  862.             return new ODEEventHandler() {

  863.                 /** {@inheritDoc} */
  864.                 public Action eventOccurred(final ODEStateAndDerivative s, final ODEEventDetector d, final boolean increasing) {
  865.                     return handler.eventOccurred(convertToOrekitWithAdditional(s), detector, increasing);
  866.                 }

  867.                 /** {@inheritDoc} */
  868.                 @Override
  869.                 public ODEState resetState(final ODEEventDetector d, final ODEStateAndDerivative s) {

  870.                     final SpacecraftState oldState = convertToOrekitWithAdditional(s);
  871.                     final SpacecraftState newState = handler.resetState(detector, oldState);
  872.                     stateChanged(newState);

  873.                     // main part
  874.                     final double[] primary    = new double[s.getPrimaryStateDimension()];
  875.                     stateMapper.mapStateToArray(newState, primary, null);

  876.                     // secondary part
  877.                     final double[][] secondary = new double[1][secondaryOffsets.get(SECONDARY_DIMENSION)];
  878.                     for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  879.                         final String name      = provider.getName();
  880.                         final int    offset    = secondaryOffsets.get(name);
  881.                         final int    dimension = provider.getDimension();
  882.                         System.arraycopy(newState.getAdditionalState(name), 0, secondary[0], offset, dimension);
  883.                     }

  884.                     return new ODEState(newState.getDate().durationFrom(getStartDate()),
  885.                                         primary, secondary);

  886.                 }

  887.             };
  888.         }

  889.     }

  890.     /** Adapt an {@link org.orekit.propagation.sampling.OrekitStepHandler}
  891.      * to Hipparchus {@link ODEStepHandler} interface.
  892.      * @author Luc Maisonobe
  893.      */
  894.     private class AdaptedStepHandler implements ODEStepHandler {

  895.         /** Underlying handler. */
  896.         private final OrekitStepHandler handler;

  897.         /** Build an instance.
  898.          * @param handler underlying handler to wrap
  899.          */
  900.         AdaptedStepHandler(final OrekitStepHandler handler) {
  901.             this.handler = handler;
  902.         }

  903.         /** {@inheritDoc} */
  904.         @Override
  905.         public void init(final ODEStateAndDerivative s0, final double t) {
  906.             handler.init(convertToOrekitWithAdditional(s0), stateMapper.mapDoubleToDate(t));
  907.         }

  908.         /** {@inheritDoc} */
  909.         @Override
  910.         public void handleStep(final ODEStateInterpolator interpolator) {
  911.             handler.handleStep(new AdaptedStepInterpolator(interpolator));
  912.         }

  913.         /** {@inheritDoc} */
  914.         @Override
  915.         public void finish(final ODEStateAndDerivative finalState) {
  916.             handler.finish(convertToOrekitWithAdditional(finalState));
  917.         }

  918.     }

  919.     /** Adapt an Hipparchus {@link ODEStateInterpolator}
  920.      * to an orekit {@link OrekitStepInterpolator} interface.
  921.      * @author Luc Maisonobe
  922.      */
  923.     private class AdaptedStepInterpolator implements OrekitStepInterpolator {

  924.         /** Underlying raw rawInterpolator. */
  925.         private final ODEStateInterpolator mathInterpolator;

  926.         /** Simple constructor.
  927.          * @param mathInterpolator underlying raw interpolator
  928.          */
  929.         AdaptedStepInterpolator(final ODEStateInterpolator mathInterpolator) {
  930.             this.mathInterpolator = mathInterpolator;
  931.         }

  932.         /** {@inheritDoc}} */
  933.         @Override
  934.         public SpacecraftState getPreviousState() {
  935.             return convertToOrekitWithAdditional(mathInterpolator.getPreviousState());
  936.         }

  937.         /** {@inheritDoc}} */
  938.         @Override
  939.         public boolean isPreviousStateInterpolated() {
  940.             return mathInterpolator.isPreviousStateInterpolated();
  941.         }

  942.         /** {@inheritDoc}} */
  943.         @Override
  944.         public SpacecraftState getCurrentState() {
  945.             return convertToOrekitWithAdditional(mathInterpolator.getCurrentState());
  946.         }

  947.         /** {@inheritDoc}} */
  948.         @Override
  949.         public boolean isCurrentStateInterpolated() {
  950.             return mathInterpolator.isCurrentStateInterpolated();
  951.         }

  952.         /** {@inheritDoc}} */
  953.         @Override
  954.         public SpacecraftState getInterpolatedState(final AbsoluteDate date) {
  955.             return convertToOrekitWithAdditional(mathInterpolator.getInterpolatedState(date.durationFrom(stateMapper.getReferenceDate())));
  956.         }

  957.         /** {@inheritDoc}} */
  958.         @Override
  959.         public boolean isForward() {
  960.             return mathInterpolator.isForward();
  961.         }

  962.         /** {@inheritDoc}} */
  963.         @Override
  964.         public AdaptedStepInterpolator restrictStep(final SpacecraftState newPreviousState,
  965.                                                     final SpacecraftState newCurrentState) {
  966.             try {
  967.                 final AbstractODEStateInterpolator aosi = (AbstractODEStateInterpolator) mathInterpolator;
  968.                 return new AdaptedStepInterpolator(aosi.restrictStep(convertFromOrekit(newPreviousState),
  969.                                                                      convertFromOrekit(newCurrentState)));
  970.             } catch (ClassCastException cce) {
  971.                 // this should never happen
  972.                 throw new OrekitInternalError(cce);
  973.             }
  974.         }


  975.         /** Convert a state from space flight dynamics world to mathematical world.
  976.          * @param state space flight dynamics state
  977.          * @return mathematical state
  978.          */
  979.         private ODEStateAndDerivative convertFromOrekit(final SpacecraftState state) {

  980.             // retrieve initial state
  981.             final double[] primary    = new double[getBasicDimension()];
  982.             final double[] primaryDot = new double[getBasicDimension()];
  983.             stateMapper.mapStateToArray(state, primary, primaryDot);

  984.             // secondary part of the ODE
  985.             final double[][] secondary           = secondary(state);
  986.             final double[][] secondaryDerivative = secondaryDerivative(state);

  987.             return new ODEStateAndDerivative(stateMapper.mapDateToDouble(state.getDate()),
  988.                     primary, primaryDot,
  989.                     secondary, secondaryDerivative);

  990.         }

  991.         /** Create secondary state derivative.
  992.          * @param state spacecraft state
  993.          * @return secondary state derivative
  994.          * @since 11.1
  995.          */
  996.         private double[][] secondaryDerivative(final SpacecraftState state) {

  997.             if (secondaryOffsets.isEmpty()) {
  998.                 return null;
  999.             }

  1000.             final double[][] secondaryDerivative = new double[1][secondaryOffsets.get(SECONDARY_DIMENSION)];
  1001.             for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  1002.                 final String   name       = provider.getName();
  1003.                 final int      offset     = secondaryOffsets.get(name);
  1004.                 final double[] additionalDerivative = state.getAdditionalStateDerivative(name);
  1005.                 System.arraycopy(additionalDerivative, 0, secondaryDerivative[0], offset, additionalDerivative.length);
  1006.             }

  1007.             return secondaryDerivative;

  1008.         }
  1009.     }

  1010.     /** Specialized step handler storing interpolators for ephemeris generation.
  1011.      * @since 11.0
  1012.      */
  1013.     private class StoringStepHandler implements ODEStepHandler, EphemerisGenerator {

  1014.         /** Underlying raw mathematical model. */
  1015.         private DenseOutputModel model;

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

  1018.         /** Generated ephemeris. */
  1019.         private BoundedPropagator ephemeris;

  1020.         /** Last interpolator handled by the object.*/
  1021.         private  ODEStateInterpolator lastInterpolator;

  1022.         /** Set the end date.
  1023.          * @param endDate end date
  1024.          */
  1025.         public void setEndDate(final AbsoluteDate endDate) {
  1026.             this.endDate = endDate;
  1027.         }

  1028.         /** {@inheritDoc} */
  1029.         @Override
  1030.         public void init(final ODEStateAndDerivative s0, final double t) {

  1031.             this.model = new DenseOutputModel();
  1032.             model.init(s0, t);

  1033.             // ephemeris will be generated when last step is processed
  1034.             this.ephemeris = null;

  1035.             this.lastInterpolator = null;

  1036.         }

  1037.         /** {@inheritDoc} */
  1038.         @Override
  1039.         public BoundedPropagator getGeneratedEphemeris() {
  1040.             // Each time we try to get the ephemeris, rebuild it using the last data.
  1041.             buildEphemeris();
  1042.             return ephemeris;
  1043.         }

  1044.         /** {@inheritDoc} */
  1045.         @Override
  1046.         public void handleStep(final ODEStateInterpolator interpolator) {
  1047.             model.handleStep(interpolator);
  1048.             lastInterpolator = interpolator;
  1049.         }

  1050.         /** {@inheritDoc} */
  1051.         @Override
  1052.         public void finish(final ODEStateAndDerivative finalState) {
  1053.             buildEphemeris();
  1054.         }

  1055.         /** Method used to produce ephemeris at a given time.
  1056.          * Can be used at multiple times, updating the ephemeris to
  1057.          * its last state.
  1058.          */
  1059.         private void buildEphemeris() {
  1060.             // buildEphemeris was built in order to allow access to what was previously the finish method.
  1061.             // This now allows to call it through getGeneratedEphemeris, therefore through an external call,
  1062.             // which was not previously the case.

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

  1065.             // set up the boundary dates
  1066.             final double tI = model.getInitialTime();
  1067.             final double tF = model.getFinalTime();
  1068.             // tI is almost? always zero
  1069.             final AbsoluteDate startDate =
  1070.                             stateMapper.mapDoubleToDate(tI);
  1071.             final AbsoluteDate finalDate =
  1072.                             stateMapper.mapDoubleToDate(tF, this.endDate);
  1073.             final AbsoluteDate minDate;
  1074.             final AbsoluteDate maxDate;
  1075.             if (tF < tI) {
  1076.                 minDate = finalDate;
  1077.                 maxDate = startDate;
  1078.             } else {
  1079.                 minDate = startDate;
  1080.                 maxDate = finalDate;
  1081.             }

  1082.             // get the initial additional states that are not managed
  1083.             final DataDictionary unmanaged = new DataDictionary();
  1084.             for (final DataDictionary.Entry initial : getInitialState().getAdditionalDataValues().getData()) {
  1085.                 if (!AbstractIntegratedPropagator.this.isAdditionalDataManaged(initial.getKey())) {
  1086.                     // this additional state was in the initial state, but is unknown to the propagator
  1087.                     // we simply copy its initial value as is
  1088.                     unmanaged.put(initial.getKey(), initial.getValue());
  1089.                 }
  1090.             }

  1091.             // get the names of additional states managed by differential equations
  1092.             final String[] names      = new String[additionalDerivativesProviders.size()];
  1093.             final int[]    dimensions = new int[additionalDerivativesProviders.size()];
  1094.             for (int i = 0; i < names.length; ++i) {
  1095.                 names[i] = additionalDerivativesProviders.get(i).getName();
  1096.                 dimensions[i] = additionalDerivativesProviders.get(i).getDimension();
  1097.             }

  1098.             // create the ephemeris
  1099.             ephemeris = new IntegratedEphemeris(startDate, minDate, maxDate,
  1100.                                                 stateMapper, getAttitudeProvider(), propagationType, model,
  1101.                                                 unmanaged, getAdditionalDataProviders(),
  1102.                                                 names, dimensions);

  1103.         }

  1104.     }

  1105.     /** Wrapper for resetting an integrator handlers.
  1106.      * <p>
  1107.      * This class is intended to be used in a try-with-resource statement.
  1108.      * If propagator-specific event handlers and step handlers are added to
  1109.      * the integrator in the try block, they will be removed automatically
  1110.      * when leaving the block, so the integrator only keeps its own handlers
  1111.      * between calls to {@link AbstractIntegratedPropagator#propagate(AbsoluteDate, AbsoluteDate).
  1112.      * </p>
  1113.      * @since 11.0
  1114.      */
  1115.     private static class IntegratorResetter implements AutoCloseable {

  1116.         /** Wrapped integrator. */
  1117.         private final ODEIntegrator integrator;

  1118.         /** Initial event detectors list. */
  1119.         private final List<ODEEventDetector> detectors;

  1120.         /** Initial step handlers list. */
  1121.         private final List<ODEStepHandler> stepHandlers;

  1122.         /** Simple constructor.
  1123.          * @param integrator wrapped integrator
  1124.          */
  1125.         IntegratorResetter(final ODEIntegrator integrator) {
  1126.             this.integrator   = integrator;
  1127.             this.detectors    = new ArrayList<>(integrator.getEventDetectors());
  1128.             this.stepHandlers = new ArrayList<>(integrator.getStepHandlers());
  1129.         }

  1130.         /** {@inheritDoc}
  1131.          * <p>
  1132.          * Reset event handlers and step handlers back to the initial list
  1133.          * </p>
  1134.          */
  1135.         @Override
  1136.         public void close() {

  1137.             // reset event handlers
  1138.             integrator.clearEventDetectors();
  1139.             detectors.forEach(integrator::addEventDetector);

  1140.             // reset step handlers
  1141.             integrator.clearStepHandlers();
  1142.             stepHandlers.forEach(integrator::addStepHandler);

  1143.         }

  1144.     }

  1145. }