FieldAbstractIntegratedPropagator.java

  1. /* Copyright 2002-2022 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 java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.Collection;
  21. import java.util.Collections;
  22. import java.util.HashMap;
  23. import java.util.LinkedList;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.Queue;

  27. import org.hipparchus.CalculusFieldElement;
  28. import org.hipparchus.Field;
  29. import org.hipparchus.exception.MathIllegalArgumentException;
  30. import org.hipparchus.exception.MathIllegalStateException;
  31. import org.hipparchus.ode.FieldDenseOutputModel;
  32. import org.hipparchus.ode.FieldExpandableODE;
  33. import org.hipparchus.ode.FieldODEIntegrator;
  34. import org.hipparchus.ode.FieldODEState;
  35. import org.hipparchus.ode.FieldODEStateAndDerivative;
  36. import org.hipparchus.ode.FieldOrdinaryDifferentialEquation;
  37. import org.hipparchus.ode.FieldSecondaryODE;
  38. import org.hipparchus.ode.events.Action;
  39. import org.hipparchus.ode.events.FieldEventHandlerConfiguration;
  40. import org.hipparchus.ode.events.FieldODEEventHandler;
  41. import org.hipparchus.ode.sampling.AbstractFieldODEStateInterpolator;
  42. import org.hipparchus.ode.sampling.FieldODEStateInterpolator;
  43. import org.hipparchus.ode.sampling.FieldODEStepHandler;
  44. import org.hipparchus.util.MathArrays;
  45. import org.hipparchus.util.Precision;
  46. import org.orekit.attitudes.AttitudeProvider;
  47. import org.orekit.errors.OrekitException;
  48. import org.orekit.errors.OrekitInternalError;
  49. import org.orekit.errors.OrekitMessages;
  50. import org.orekit.frames.Frame;
  51. import org.orekit.orbits.OrbitType;
  52. import org.orekit.orbits.PositionAngle;
  53. import org.orekit.propagation.FieldAbstractPropagator;
  54. import org.orekit.propagation.FieldBoundedPropagator;
  55. import org.orekit.propagation.FieldEphemerisGenerator;
  56. import org.orekit.propagation.FieldSpacecraftState;
  57. import org.orekit.propagation.PropagationType;
  58. import org.orekit.propagation.events.FieldEventDetector;
  59. import org.orekit.propagation.sampling.FieldOrekitStepHandler;
  60. import org.orekit.propagation.sampling.FieldOrekitStepInterpolator;
  61. import org.orekit.time.FieldAbsoluteDate;
  62. import org.orekit.utils.FieldArrayDictionary;


  63. /** Common handling of {@link org.orekit.propagation.FieldPropagator FieldPropagator}
  64.  *  methods for both numerical and semi-analytical propagators.
  65.  *  @param <T> the type of the field elements
  66.  *  @author Luc Maisonobe
  67.  */
  68. public abstract class FieldAbstractIntegratedPropagator<T extends CalculusFieldElement<T>> extends FieldAbstractPropagator<T> {

  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<FieldEventDetector<T>> detectors;

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

  77.     /** Integrator selected by the user for the orbital extrapolation process. */
  78.     private final FieldODEIntegrator<T> integrator;

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

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

  87.     /** Counter for differential equations calls. */
  88.     private int calls;

  89.     /** Mapper between raw double components and space flight dynamics objects. */
  90.     private FieldStateMapper<T> stateMapper;

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

  93.     /** Type of orbit to output (mean or osculating) <br/>
  94.      * <p>
  95.      * This is used only in the case of semianalitical propagators where there is a clear separation between
  96.      * mean and short periodic elements. It is ignored by the Numerical propagator.
  97.      * </p>
  98.      */
  99.     private PropagationType propagationType;

  100.     /** Build a new instance.
  101.      * @param integrator numerical integrator to use for propagation.
  102.      * @param propagationType type of orbit to output (mean or osculating).
  103.      * @param field Field used by default
  104.      */
  105.     protected FieldAbstractIntegratedPropagator(final Field<T> field, final FieldODEIntegrator<T> integrator, final PropagationType propagationType) {
  106.         super(field);
  107.         detectors                      = new ArrayList<>();
  108.         ephemerisGenerators            = new ArrayList<>();
  109.         additionalDerivativesProviders = new ArrayList<>();
  110.         this.secondaryOffsets          = new HashMap<>();
  111.         this.integrator                = integrator;
  112.         this.propagationType           = propagationType;
  113.         this.resetAtEnd                = true;
  114.     }

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

  130.     /** Initialize the mapper.
  131.      * @param field Field used by default
  132.      */
  133.     protected void initMapper(final Field<T> field) {
  134.         final T zero = field.getZero();
  135.         stateMapper = createMapper(null, zero.add(Double.NaN), null, null, null, null);
  136.     }

  137.     /**  {@inheritDoc} */
  138.     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
  139.         super.setAttitudeProvider(attitudeProvider);
  140.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  141.                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  142.                                    attitudeProvider, stateMapper.getFrame());
  143.     }

  144.     /** Set propagation orbit type.
  145.      * @param orbitType orbit type to use for propagation
  146.      */
  147.     protected void setOrbitType(final OrbitType orbitType) {
  148.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  149.                                    orbitType, stateMapper.getPositionAngleType(),
  150.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  151.     }

  152.     /** Get propagation parameter type.
  153.      * @return orbit type used for propagation
  154.      */
  155.     protected OrbitType getOrbitType() {
  156.         return stateMapper.getOrbitType();
  157.     }

  158.     /** Check if only the mean elements should be used in a semianalitical propagation.
  159.      * @return {@link PropagationType MEAN} if only mean elements have to be used or
  160.      *         {@link PropagationType OSCULATING} if osculating elements have to be also used.
  161.      */
  162.     protected PropagationType isMeanOrbit() {
  163.         return propagationType;
  164.     }

  165.     /** Set position angle type.
  166.      * <p>
  167.      * The position parameter type is meaningful only if {@link
  168.      * #getOrbitType() propagation orbit type}
  169.      * support it. As an example, it is not meaningful for propagation
  170.      * in {@link OrbitType#CARTESIAN Cartesian} parameters.
  171.      * </p>
  172.      * @param positionAngleType angle type to use for propagation
  173.      */
  174.     protected void setPositionAngleType(final PositionAngle positionAngleType) {
  175.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  176.                                    stateMapper.getOrbitType(), positionAngleType,
  177.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  178.     }

  179.     /** Get propagation parameter type.
  180.      * @return angle type to use for propagation
  181.      */
  182.     protected PositionAngle getPositionAngleType() {
  183.         return stateMapper.getPositionAngleType();
  184.     }

  185.     /** Set the central attraction coefficient μ.
  186.      * @param mu central attraction coefficient (m³/s²)
  187.      */
  188.     public void setMu(final T mu) {
  189.         stateMapper = createMapper(stateMapper.getReferenceDate(), mu,
  190.                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  191.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  192.     }

  193.     /** Get the central attraction coefficient μ.
  194.      * @return mu central attraction coefficient (m³/s²)
  195.      * @see #setMu(CalculusFieldElement)
  196.      */
  197.     public T getMu() {
  198.         return stateMapper.getMu();
  199.     }

  200.     /** Get the number of calls to the differential equations computation method.
  201.      * <p>The number of calls is reset each time the {@link #propagate(FieldAbsoluteDate)}
  202.      * method is called.</p>
  203.      * @return number of calls to the differential equations computation method
  204.      */
  205.     public int getCalls() {
  206.         return calls;
  207.     }

  208.     /** {@inheritDoc} */
  209.     @Override
  210.     public boolean isAdditionalStateManaged(final String name) {

  211.         // first look at already integrated states
  212.         if (super.isAdditionalStateManaged(name)) {
  213.             return true;
  214.         }

  215.         // then look at states we integrate ourselves
  216.         for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  217.             if (provider.getName().equals(name)) {
  218.                 return true;
  219.             }
  220.         }

  221.         return false;
  222.     }

  223.     /** {@inheritDoc} */
  224.     @Override
  225.     public String[] getManagedAdditionalStates() {
  226.         final String[] alreadyIntegrated = super.getManagedAdditionalStates();
  227.         final String[] managed = new String[alreadyIntegrated.length + additionalDerivativesProviders.size()];
  228.         System.arraycopy(alreadyIntegrated, 0, managed, 0, alreadyIntegrated.length);
  229.         for (int i = 0; i < additionalDerivativesProviders.size(); ++i) {
  230.             managed[i + alreadyIntegrated.length] = additionalDerivativesProviders.get(i).getName();
  231.         }
  232.         return managed;
  233.     }

  234.     /** Add a set of user-specified equations to be integrated along with the orbit propagation.
  235.      * @param additional additional equations
  236.      * @deprecated as of 11.1, replaced by {@link #addAdditionalDerivativesProvider(FieldAdditionalDerivativesProvider)}
  237.      */
  238.     @Deprecated
  239.     public void addAdditionalEquations(final FieldAdditionalEquations<T> additional) {
  240.         addAdditionalDerivativesProvider(new FieldAdditionalEquationsAdapter<>(additional, this::getInitialState));
  241.     }

  242.     /** Add a provider for user-specified state derivatives to be integrated along with the orbit propagation.
  243.      * @param provider provider for additional derivatives
  244.      * @see #addAdditionalStateProvider(org.orekit.propagation.FieldAdditionalStateProvider)
  245.      * @since 11.1
  246.      */
  247.     public void addAdditionalDerivativesProvider(final FieldAdditionalDerivativesProvider<T> provider) {
  248.         // check if the name is already used
  249.         if (isAdditionalStateManaged(provider.getName())) {
  250.             // these derivatives are already registered, complain
  251.             throw new OrekitException(OrekitMessages.ADDITIONAL_STATE_NAME_ALREADY_IN_USE,
  252.                                       provider.getName());
  253.         }

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

  256.         secondaryOffsets.clear();

  257.     }

  258.     /** Get an unmodifiable list of providers for additional derivatives.
  259.      * @return providers for additional derivatives
  260.      * @since 11.1
  261.      */
  262.     public List<FieldAdditionalDerivativesProvider<T>> getAdditionalDerivativesProviders() {
  263.         return Collections.unmodifiableList(additionalDerivativesProviders);
  264.     }

  265.     /** {@inheritDoc} */
  266.     public <D extends FieldEventDetector<T>> void addEventDetector(final D detector) {
  267.         detectors.add(detector);
  268.     }

  269.     /** {@inheritDoc} */
  270.     public Collection<FieldEventDetector<T>> getEventsDetectors() {
  271.         return Collections.unmodifiableCollection(detectors);
  272.     }

  273.     /** {@inheritDoc} */
  274.     public void clearEventsDetectors() {
  275.         detectors.clear();
  276.     }

  277.     /** Set up all user defined event detectors.
  278.      */
  279.     protected void setUpUserEventDetectors() {
  280.         for (final FieldEventDetector<T> detector : detectors) {
  281.             setUpEventDetector(integrator, detector);
  282.         }
  283.     }

  284.     /** Wrap an Orekit event detector and register it to the integrator.
  285.      * @param integ integrator into which event detector should be registered
  286.      * @param detector event detector to wrap
  287.      */
  288.     protected void setUpEventDetector(final FieldODEIntegrator<T> integ, final FieldEventDetector<T> detector) {
  289.         integ.addEventHandler(new FieldAdaptedEventDetector(detector),
  290.                               detector.getMaxCheckInterval().getReal(),
  291.                               detector.getThreshold().getReal(),
  292.                               detector.getMaxIterationCount());
  293.     }

  294.     /** {@inheritDoc} */
  295.     @Override
  296.     public FieldEphemerisGenerator<T> getEphemerisGenerator() {
  297.         final FieldStoringStepHandler storingHandler = new FieldStoringStepHandler();
  298.         ephemerisGenerators.add(storingHandler);
  299.         return storingHandler;
  300.     }

  301.     /** Create a mapper between raw double components and spacecraft state.
  302.     /** Simple constructor.
  303.      * <p>
  304.      * The position parameter type is meaningful only if {@link
  305.      * #getOrbitType() propagation orbit type}
  306.      * support it. As an example, it is not meaningful for propagation
  307.      * in {@link OrbitType#CARTESIAN Cartesian} parameters.
  308.      * </p>
  309.      * @param referenceDate reference date
  310.      * @param mu central attraction coefficient (m³/s²)
  311.      * @param orbitType orbit type to use for mapping
  312.      * @param positionAngleType angle type to use for propagation
  313.      * @param attitudeProvider attitude provider
  314.      * @param frame inertial frame
  315.      * @return new mapper
  316.      */
  317.     protected abstract FieldStateMapper<T> createMapper(FieldAbsoluteDate<T> referenceDate, T mu,
  318.                                                         OrbitType orbitType, PositionAngle positionAngleType,
  319.                                                         AttitudeProvider attitudeProvider, Frame frame);

  320.     /** Get the differential equations to integrate (for main state only).
  321.      * @param integ numerical integrator to use for propagation.
  322.      * @return differential equations for main state
  323.      */
  324.     protected abstract MainStateEquations<T> getMainStateEquations(FieldODEIntegrator<T> integ);

  325.     /** {@inheritDoc} */
  326.     public FieldSpacecraftState<T> propagate(final FieldAbsoluteDate<T> target) {
  327.         if (getStartDate() == null) {
  328.             if (getInitialState() == null) {
  329.                 throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  330.             }
  331.             setStartDate(getInitialState().getDate());
  332.         }
  333.         return propagate(getStartDate(), target);
  334.     }

  335.     /** {@inheritDoc} */
  336.     public FieldSpacecraftState<T> propagate(final FieldAbsoluteDate<T> tStart, final FieldAbsoluteDate<T> tEnd) {

  337.         if (getInitialState() == null) {
  338.             throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  339.         }

  340.         // make sure the integrator will be reset properly even if we change its events handlers and step handlers
  341.         try (IntegratorResetter<T> resetter = new IntegratorResetter<>(integrator)) {

  342.             // Initialize additional states
  343.             initializeAdditionalStates(tEnd);

  344.             if (!tStart.equals(getInitialState().getDate())) {
  345.                 // if propagation start date is not initial date,
  346.                 // propagate from initial to start date without event detection
  347.                 try (IntegratorResetter<T> startResetter = new IntegratorResetter<>(integrator)) {
  348.                     integrateDynamics(tStart);
  349.                 }
  350.             }

  351.             // set up events added by user
  352.             setUpUserEventDetectors();

  353.             // set up step handlers
  354.             for (final FieldOrekitStepHandler<T> handler : getMultiplexer().getHandlers()) {
  355.                 integrator.addStepHandler(new FieldAdaptedStepHandler(handler));
  356.             }
  357.             for (final FieldStoringStepHandler generator : ephemerisGenerators) {
  358.                 generator.setEndDate(tEnd);
  359.                 integrator.addStepHandler(generator);
  360.             }

  361.             // propagate from start date to end date with event detection
  362.             return integrateDynamics(tEnd);

  363.         }

  364.     }

  365.     /** Propagation with or without event detection.
  366.      * @param tEnd target date to which orbit should be propagated
  367.      * @return state at end of propagation
  368.      */
  369.     private FieldSpacecraftState<T> integrateDynamics(final FieldAbsoluteDate<T> tEnd) {
  370.         try {

  371.             initializePropagation();

  372.             if (getInitialState().getDate().equals(tEnd)) {
  373.                 // don't extrapolate
  374.                 return getInitialState();
  375.             }
  376.             // space dynamics view
  377.             stateMapper = createMapper(getInitialState().getDate(), stateMapper.getMu(),
  378.                                        stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  379.                                        stateMapper.getAttitudeProvider(), getInitialState().getFrame());


  380.             // set propagation orbit type
  381.             //final FieldOrbit<T> initialOrbit = stateMapper.getOrbitType().convertType(getInitialState().getOrbit());
  382.             if (Double.isNaN(getMu().getReal())) {
  383.                 setMu(getInitialState().getMu());
  384.             }
  385.             if (getInitialState().getMass().getReal() <= 0.0) {
  386.                 throw new OrekitException(OrekitMessages.SPACECRAFT_MASS_BECOMES_NEGATIVE,
  387.                                                getInitialState().getMass());
  388.             }

  389.             // convert space flight dynamics API to math API
  390.             final FieldSpacecraftState<T> initialIntegrationState = getInitialIntegrationState();
  391.             final FieldODEState<T> mathInitialState = createInitialState(initialIntegrationState);
  392.             final FieldExpandableODE<T> mathODE = createODE(integrator, mathInitialState);

  393.             // mathematical integration
  394.             final FieldODEStateAndDerivative<T> mathFinalState;
  395.             beforeIntegration(initialIntegrationState, tEnd);
  396.             mathFinalState = integrator.integrate(mathODE, mathInitialState,
  397.                                                   tEnd.durationFrom(getInitialState().getDate()));

  398.             afterIntegration();

  399.             // get final state
  400.             FieldSpacecraftState<T> finalState =
  401.                             stateMapper.mapArrayToState(stateMapper.mapDoubleToDate(mathFinalState.getTime(), tEnd),
  402.                                                         mathFinalState.getPrimaryState(),
  403.                                                         mathFinalState.getPrimaryDerivative(),
  404.                                                         propagationType);
  405.             if (!additionalDerivativesProviders.isEmpty()) {
  406.                 final T[] secondary            = mathFinalState.getSecondaryState(1);
  407.                 final T[] secondaryDerivatives = mathFinalState.getSecondaryDerivative(1);
  408.                 for (FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  409.                     final String   name        = provider.getName();
  410.                     final int      offset      = secondaryOffsets.get(name);
  411.                     final int      dimension   = provider.getDimension();
  412.                     finalState = finalState.
  413.                                  addAdditionalState(name, Arrays.copyOfRange(secondary, offset, offset + dimension)).
  414.                                  addAdditionalStateDerivative(name, Arrays.copyOfRange(secondaryDerivatives, offset, offset + dimension));
  415.                 }
  416.             }
  417.             finalState = updateAdditionalStates(finalState);

  418.             if (resetAtEnd) {
  419.                 resetInitialState(finalState);
  420.                 setStartDate(finalState.getDate());
  421.             }

  422.             return finalState;

  423.         } catch (OrekitException pe) {
  424.             throw pe;
  425.         } catch (MathIllegalArgumentException | MathIllegalStateException me) {
  426.             throw OrekitException.unwrap(me);
  427.         }
  428.     }

  429.     /** Get the initial state for integration.
  430.      * @return initial state for integration
  431.      */
  432.     protected FieldSpacecraftState<T> getInitialIntegrationState() {
  433.         return getInitialState();
  434.     }

  435.     /** Create an initial state.
  436.      * @param initialState initial state in flight dynamics world
  437.      * @return initial state in mathematics world
  438.      */
  439.     private FieldODEState<T> createInitialState(final FieldSpacecraftState<T> initialState) {

  440.         // retrieve initial state
  441.         final T[] primary  = MathArrays.buildArray(initialState.getA().getField(), getBasicDimension());
  442.         stateMapper.mapStateToArray(initialState, primary, null);

  443.         if (secondaryOffsets.isEmpty()) {
  444.             // compute dimension of the secondary state
  445.             int offset = 0;
  446.             for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  447.                 secondaryOffsets.put(provider.getName(), offset);
  448.                 offset += provider.getDimension();
  449.             }
  450.             secondaryOffsets.put(SECONDARY_DIMENSION, offset);
  451.         }

  452.         return new FieldODEState<>(initialState.getA().getField().getZero(), primary, secondary(initialState));

  453.     }

  454.     /** Create secondary state.
  455.      * @param state spacecraft state
  456.      * @return secondary state
  457.      * @since 11.1
  458.      */
  459.     private T[][] secondary(final FieldSpacecraftState<T> state) {

  460.         if (secondaryOffsets.isEmpty()) {
  461.             return null;
  462.         }

  463.         final T[][] secondary = MathArrays.buildArray(state.getDate().getField(), 1, secondaryOffsets.get(SECONDARY_DIMENSION));
  464.         for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  465.             final String name       = provider.getName();
  466.             final int    offset     = secondaryOffsets.get(name);
  467.             final T[]    additional = state.getAdditionalState(name);
  468.             System.arraycopy(additional, 0, secondary[0], offset, additional.length);
  469.         }

  470.         return secondary;

  471.     }

  472.     /** Create secondary state derivative.
  473.      * @param state spacecraft state
  474.      * @return secondary state derivative
  475.      * @since 11.1
  476.      */
  477.     private T[][] secondaryDerivative(final FieldSpacecraftState<T> state) {

  478.         if (secondaryOffsets.isEmpty()) {
  479.             return null;
  480.         }

  481.         final T[][] secondaryDerivative = MathArrays.buildArray(state.getDate().getField(), 1, secondaryOffsets.get(SECONDARY_DIMENSION));
  482.         for (final FieldAdditionalDerivativesProvider<T> providcer : additionalDerivativesProviders) {
  483.             final String name       = providcer.getName();
  484.             final int    offset     = secondaryOffsets.get(name);
  485.             final T[]    additionalDerivative = state.getAdditionalStateDerivative(name);
  486.             System.arraycopy(additionalDerivative, 0, secondaryDerivative[0], offset, additionalDerivative.length);
  487.         }

  488.         return secondaryDerivative;

  489.     }

  490.     /** Create an ODE with all equations.
  491.      * @param integ numerical integrator to use for propagation.
  492.      * @param mathInitialState initial state
  493.      * @return a new ode
  494.      */
  495.     private FieldExpandableODE<T> createODE(final FieldODEIntegrator<T> integ,
  496.                                     final FieldODEState<T> mathInitialState) {

  497.         final FieldExpandableODE<T> ode =
  498.                 new FieldExpandableODE<>(new ConvertedMainStateEquations(getMainStateEquations(integ)));

  499.         // secondary part of the ODE
  500.         if (!additionalDerivativesProviders.isEmpty()) {
  501.             ode.addSecondaryEquations(new ConvertedSecondaryStateEquations());
  502.         }

  503.         return ode;

  504.     }

  505.     /** Method called just before integration.
  506.      * <p>
  507.      * The default implementation does nothing, it may be specialized in subclasses.
  508.      * </p>
  509.      * @param initialState initial state
  510.      * @param tEnd target date at which state should be propagated
  511.      */
  512.     protected void beforeIntegration(final FieldSpacecraftState<T> initialState,
  513.                                      final FieldAbsoluteDate<T> tEnd) {
  514.         // do nothing by default
  515.     }

  516.     /** Method called just after integration.
  517.      * <p>
  518.      * The default implementation does nothing, it may be specialized in subclasses.
  519.      * </p>
  520.      */
  521.     protected void afterIntegration() {
  522.         // do nothing by default
  523.     }

  524.     /** Get state vector dimension without additional parameters.
  525.      * @return state vector dimension without additional parameters.
  526.      */
  527.     public int getBasicDimension() {
  528.         return 7;

  529.     }

  530.     /** Get the integrator used by the propagator.
  531.      * @return the integrator.
  532.      */
  533.     protected FieldODEIntegrator<T> getIntegrator() {
  534.         return integrator;
  535.     }

  536.     /** Convert a state from mathematical world to space flight dynamics world.
  537.      * @param os mathematical state
  538.      * @return space flight dynamics state
  539.      */
  540.     private FieldSpacecraftState<T> convert(final FieldODEStateAndDerivative<T> os) {

  541.         FieldSpacecraftState<T> s =
  542.                         stateMapper.mapArrayToState(os.getTime(),
  543.                                                     os.getPrimaryState(),
  544.                                                     os.getPrimaryDerivative(),
  545.                                                     propagationType);
  546.         if (os.getNumberOfSecondaryStates() > 0) {
  547.             final T[] secondary           = os.getSecondaryState(1);
  548.             final T[] secondaryDerivative = os.getSecondaryDerivative(1);
  549.             for (final FieldAdditionalDerivativesProvider<T> equations : additionalDerivativesProviders) {
  550.                 final String name      = equations.getName();
  551.                 final int    offset    = secondaryOffsets.get(name);
  552.                 final int    dimension = equations.getDimension();
  553.                 s = s.addAdditionalState(name, Arrays.copyOfRange(secondary, offset, offset + dimension));
  554.                 s = s.addAdditionalStateDerivative(name, Arrays.copyOfRange(secondaryDerivative, offset, offset + dimension));
  555.             }
  556.         }
  557.         s = updateAdditionalStates(s);

  558.         return s;

  559.     }

  560.     /** Convert a state from space flight dynamics world to mathematical world.
  561.      * @param state space flight dynamics state
  562.      * @return mathematical state
  563.      */
  564.     private FieldODEStateAndDerivative<T> convert(final FieldSpacecraftState<T> state) {

  565.         // retrieve initial state
  566.         final T[] primary    = MathArrays.buildArray(getField(), getBasicDimension());
  567.         final T[] primaryDot = MathArrays.buildArray(getField(), getBasicDimension());
  568.         stateMapper.mapStateToArray(state, primary, primaryDot);

  569.         // secondary part of the ODE
  570.         final T[][] secondary           = secondary(state);
  571.         final T[][] secondaryDerivative = secondaryDerivative(state);

  572.         return new FieldODEStateAndDerivative<>(stateMapper.mapDateToDouble(state.getDate()),
  573.                                                 primary, primaryDot,
  574.                                                 secondary, secondaryDerivative);

  575.     }

  576.     /** Differential equations for the main state (orbit, attitude and mass). */
  577.     public interface MainStateEquations<T extends CalculusFieldElement<T>> {

  578.         /**
  579.          * Initialize the equations at the start of propagation. This method will be
  580.          * called before any calls to {@link #computeDerivatives(FieldSpacecraftState)}.
  581.          *
  582.          * <p> The default implementation of this method does nothing.
  583.          *
  584.          * @param initialState initial state information at the start of propagation.
  585.          * @param target       date of propagation. Not equal to {@code
  586.          *                     initialState.getDate()}.
  587.          */
  588.         void init(FieldSpacecraftState<T> initialState, FieldAbsoluteDate<T> target);

  589.         /** Compute differential equations for main state.
  590.          * @param state current state
  591.          * @return derivatives of main state
  592.          */
  593.         T[] computeDerivatives(FieldSpacecraftState<T> state);

  594.     }

  595.     /** Differential equations for the main state (orbit, attitude and mass), with converted API. */
  596.     private class ConvertedMainStateEquations implements FieldOrdinaryDifferentialEquation<T> {

  597.         /** Main state equations. */
  598.         private final MainStateEquations<T> main;

  599.         /** Simple constructor.
  600.          * @param main main state equations
  601.          */
  602.         ConvertedMainStateEquations(final MainStateEquations<T> main) {
  603.             this.main = main;
  604.             calls = 0;
  605.         }

  606.         /** {@inheritDoc} */
  607.         public int getDimension() {
  608.             return getBasicDimension();
  609.         }

  610.         @Override
  611.         public void init(final T t0, final T[] y0, final T finalTime) {
  612.             // update space dynamics view
  613.             FieldSpacecraftState<T> initialState = stateMapper.mapArrayToState(t0, y0, null, PropagationType.MEAN);
  614.             initialState = updateAdditionalStates(initialState);
  615.             final FieldAbsoluteDate<T> target = stateMapper.mapDoubleToDate(finalTime);
  616.             main.init(initialState, target);
  617.         }
  618.         /** {@inheritDoc} */
  619.         public T[] computeDerivatives(final T t, final T[] y) {

  620.             // increment calls counter
  621.             ++calls;

  622.             // update space dynamics view
  623.             FieldSpacecraftState<T> currentState = stateMapper.mapArrayToState(t, y, null, PropagationType.MEAN);
  624.             currentState = updateAdditionalStates(currentState);

  625.             // compute main state differentials
  626.             return main.computeDerivatives(currentState);

  627.         }

  628.     }

  629.     /** Differential equations for the secondary state (Jacobians, user variables ...), with converted API. */
  630.     private class ConvertedSecondaryStateEquations implements FieldSecondaryODE<T> {

  631.         /** Dimension of the combined additional states. */
  632.         private final int combinedDimension;

  633.         /** Simple constructor.
  634.          */
  635.         ConvertedSecondaryStateEquations() {
  636.             this.combinedDimension = secondaryOffsets.get(SECONDARY_DIMENSION);
  637.         }

  638.         /** {@inheritDoc} */
  639.         @Override
  640.         public int getDimension() {
  641.             return combinedDimension;
  642.         }

  643.         /** {@inheritDoc} */
  644.         @Override
  645.         public void init(final T t0, final T[] primary0,
  646.                          final T[] secondary0, final T finalTime) {
  647.             // update space dynamics view
  648.             final FieldSpacecraftState<T> initialState = convert(t0, primary0, null, secondary0);

  649.             final FieldAbsoluteDate<T> target = stateMapper.mapDoubleToDate(finalTime);
  650.             for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  651.                 provider.init(initialState, target);
  652.             }

  653.         }

  654.         /** {@inheritDoc} */
  655.         @Override
  656.         public T[] computeDerivatives(final T t, final T[] primary,
  657.                                       final T[] primaryDot, final T[] secondary) {

  658.             // update space dynamics view
  659.             // the integrable generators generate method will be called here,
  660.             // according to the generators yield order
  661.             FieldSpacecraftState<T> updated = convert(t, primary, primaryDot, secondary);

  662.             // set up queue for equations
  663.             final Queue<FieldAdditionalDerivativesProvider<T>> pending = new LinkedList<>(additionalDerivativesProviders);

  664.             // gather the derivatives from all additional equations, taking care of dependencies
  665.             final T[] secondaryDot = MathArrays.buildArray(t.getField(), combinedDimension);
  666.             int yieldCount = 0;
  667.             while (!pending.isEmpty()) {
  668.                 final FieldAdditionalDerivativesProvider<T> equations = pending.remove();
  669.                 if (equations.yield(updated)) {
  670.                     // these equations have to wait for another set,
  671.                     // we put them again in the pending queue
  672.                     pending.add(equations);
  673.                     if (++yieldCount >= pending.size()) {
  674.                         // all pending equations yielded!, they probably need data not yet initialized
  675.                         // we let the propagation proceed, if these data are really needed right now
  676.                         // an appropriate exception will be triggered when caller tries to access them
  677.                         break;
  678.                     }
  679.                 } else {
  680.                     // we can use these equations right now
  681.                     final String                      name           = equations.getName();
  682.                     final int                         offset         = secondaryOffsets.get(name);
  683.                     final int                         dimension      = equations.getDimension();
  684.                     final FieldCombinedDerivatives<T> derivatives    = equations.combinedDerivatives(updated);
  685.                     final T[]                         additionalPart = derivatives.getAdditionalDerivatives();
  686.                     final T[]                         mainPart       = derivatives.getMainStateDerivativesIncrements();
  687.                     System.arraycopy(additionalPart, 0, secondaryDot, offset, dimension);
  688.                     updated = updated.addAdditionalStateDerivative(name, additionalPart);
  689.                     if (mainPart != null) {
  690.                         // this equation does change the main state derivatives
  691.                         for (int i = 0; i < mainPart.length; ++i) {
  692.                             primaryDot[i] = primaryDot[i].add(mainPart[i]);
  693.                         }
  694.                     }
  695.                     yieldCount = 0;
  696.                 }
  697.             }

  698.             return secondaryDot;

  699.         }

  700.         /** Convert mathematical view to space view.
  701.          * @param t current value of the independent <I>time</I> variable
  702.          * @param primary array containing the current value of the primary state vector
  703.          * @param primaryDot array containing the derivative of the primary state vector
  704.          * @param secondary array containing the current value of the secondary state vector
  705.          * @return space view of the state
  706.          */
  707.         private FieldSpacecraftState<T> convert(final T t, final T[] primary,
  708.                                                 final T[] primaryDot, final T[] secondary) {

  709.             FieldSpacecraftState<T> initialState = stateMapper.mapArrayToState(t, primary, primaryDot, PropagationType.MEAN);

  710.             for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  711.                 final String name      = provider.getName();
  712.                 final int    offset    = secondaryOffsets.get(name);
  713.                 final int    dimension = provider.getDimension();
  714.                 initialState = initialState.addAdditionalState(name, Arrays.copyOfRange(secondary, offset, offset + dimension));
  715.             }

  716.             return updateAdditionalStates(initialState);

  717.         }

  718.     }

  719.     /** Adapt an {@link org.orekit.propagation.events.FieldEventDetector<T>}
  720.      * to Hipparchus {@link org.hipparchus.ode.events.FieldODEEventHandler<T>} interface.
  721.      * @param <T> class type for the generic version
  722.      * @author Fabien Maussion
  723.      */
  724.     private class FieldAdaptedEventDetector implements FieldODEEventHandler<T> {

  725.         /** Underlying event detector. */
  726.         private final FieldEventDetector<T> detector;

  727.         /** Time of the previous call to g. */
  728.         private T lastT;

  729.         /** Value from the previous call to g. */
  730.         private T lastG;

  731.         /** Build a wrapped event detector.
  732.          * @param detector event detector to wrap
  733.         */
  734.         FieldAdaptedEventDetector(final FieldEventDetector<T> detector) {
  735.             this.detector = detector;
  736.             this.lastT    = getField().getZero().add(Double.NaN);
  737.             this.lastG    = getField().getZero().add(Double.NaN);
  738.         }

  739.         /** {@inheritDoc} */
  740.         public void init(final FieldODEStateAndDerivative<T> s0, final T t) {
  741.             detector.init(convert(s0), stateMapper.mapDoubleToDate(t));
  742.             this.lastT = getField().getZero().add(Double.NaN);
  743.             this.lastG = getField().getZero().add(Double.NaN);
  744.         }

  745.         /** {@inheritDoc} */
  746.         public T g(final FieldODEStateAndDerivative<T> s) {
  747.             if (!Precision.equals(lastT.getReal(), s.getTime().getReal(), 0)) {
  748.                 lastT = s.getTime();
  749.                 lastG = detector.g(convert(s));
  750.             }
  751.             return lastG;
  752.         }

  753.         /** {@inheritDoc} */
  754.         public Action eventOccurred(final FieldODEStateAndDerivative<T> s, final boolean increasing) {
  755.             return detector.eventOccurred(convert(s), increasing);
  756.         }

  757.         /** {@inheritDoc} */
  758.         public FieldODEState<T> resetState(final FieldODEStateAndDerivative<T> s) {

  759.             final FieldSpacecraftState<T> oldState = convert(s);
  760.             final FieldSpacecraftState<T> newState = detector.resetState(oldState);
  761.             stateChanged(newState);

  762.             // main part
  763.             final T[] primary    = MathArrays.buildArray(getField(), s.getPrimaryStateDimension());
  764.             stateMapper.mapStateToArray(newState, primary, null);

  765.             // secondary part
  766.             final T[][] secondary = MathArrays.buildArray(getField(), 1, additionalDerivativesProviders.size());
  767.             for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  768.                 final String name      = provider.getName();
  769.                 final int    offset    = secondaryOffsets.get(name);
  770.                 final int    dimension = provider.getDimension();
  771.                 System.arraycopy(newState.getAdditionalState(name), 0, secondary[0], offset, dimension);
  772.             }

  773.             return new FieldODEState<>(newState.getDate().durationFrom(getStartDate()),
  774.                                        primary, secondary);
  775.         }

  776.     }

  777.     /** Adapt an {@link org.orekit.propagation.sampling.FieldOrekitStepHandler<T>}
  778.      * to Hipparchus {@link FieldODEStepHandler<T>} interface.
  779.      * @author Luc Maisonobe
  780.      */
  781.     private class FieldAdaptedStepHandler implements FieldODEStepHandler<T> {

  782.         /** Underlying handler. */
  783.         private final FieldOrekitStepHandler<T> handler;

  784.         /** Build an instance.
  785.          * @param handler underlying handler to wrap
  786.          */
  787.         FieldAdaptedStepHandler(final FieldOrekitStepHandler<T> handler) {
  788.             this.handler = handler;
  789.         }

  790.         /** {@inheritDoc} */
  791.         public void init(final FieldODEStateAndDerivative<T> s0, final T t) {
  792.             handler.init(convert(s0), stateMapper.mapDoubleToDate(t));
  793.         }

  794.         /** {@inheritDoc} */
  795.         public void handleStep(final FieldODEStateInterpolator<T> interpolator) {
  796.             handler.handleStep(new FieldAdaptedStepInterpolator(interpolator));
  797.         }

  798.         /** {@inheritDoc} */
  799.         @Override
  800.         public void finish(final FieldODEStateAndDerivative<T> finalState) {
  801.             handler.finish(convert(finalState));
  802.         }

  803.     }

  804.     /** Adapt an {@link org.orekit.propagation.sampling.FieldOrekitStepInterpolator<T>}
  805.      * to Hipparchus {@link FieldODEStepInterpolator<T>} interface.
  806.      * @author Luc Maisonobe
  807.      */
  808.     private class FieldAdaptedStepInterpolator implements FieldOrekitStepInterpolator<T> {

  809.         /** Underlying raw rawInterpolator. */
  810.         private final FieldODEStateInterpolator<T> mathInterpolator;

  811.         /** Build an instance.
  812.          * @param mathInterpolator underlying raw interpolator
  813.          */
  814.         FieldAdaptedStepInterpolator(final FieldODEStateInterpolator<T> mathInterpolator) {
  815.             this.mathInterpolator = mathInterpolator;
  816.         }

  817.         /** {@inheritDoc}} */
  818.         @Override
  819.         public FieldSpacecraftState<T> getPreviousState() {
  820.             return convert(mathInterpolator.getPreviousState());
  821.         }

  822.         /** {@inheritDoc}} */
  823.         @Override
  824.         public FieldSpacecraftState<T> getCurrentState() {
  825.             return convert(mathInterpolator.getCurrentState());
  826.         }

  827.         /** {@inheritDoc}} */
  828.         @Override
  829.         public FieldSpacecraftState<T> getInterpolatedState(final FieldAbsoluteDate<T> date) {
  830.             return convert(mathInterpolator.getInterpolatedState(date.durationFrom(getStartDate())));
  831.         }

  832.         /** Check is integration direction is forward in date.
  833.          * @return true if integration is forward in date
  834.          */
  835.         public boolean isForward() {
  836.             return mathInterpolator.isForward();
  837.         }

  838.         /** {@inheritDoc}} */
  839.         @Override
  840.         public FieldAdaptedStepInterpolator restrictStep(final FieldSpacecraftState<T> newPreviousState,
  841.                                                          final FieldSpacecraftState<T> newCurrentState) {
  842.             try {
  843.                 final AbstractFieldODEStateInterpolator<T> aosi = (AbstractFieldODEStateInterpolator<T>) mathInterpolator;
  844.                 return new FieldAdaptedStepInterpolator(aosi.restrictStep(convert(newPreviousState),
  845.                                                                           convert(newCurrentState)));
  846.             } catch (ClassCastException cce) {
  847.                 // this should never happen
  848.                 throw new OrekitInternalError(cce);
  849.             }
  850.         }

  851.     }

  852.     /** Specialized step handler storing interpolators for ephemeris generation.
  853.      * @since 11.0
  854.      */
  855.     private class FieldStoringStepHandler implements FieldODEStepHandler<T>, FieldEphemerisGenerator<T> {

  856.         /** Underlying raw mathematical model. */
  857.         private FieldDenseOutputModel<T> model;

  858.         /** the user supplied end date. Propagation may not end on this date. */
  859.         private FieldAbsoluteDate<T> endDate;

  860.         /** Generated ephemeris. */
  861.         private FieldBoundedPropagator<T> ephemeris;

  862.         /** Last interpolator handled by the object.*/
  863.         private  FieldODEStateInterpolator<T> lastInterpolator;

  864.         /** Set the end date.
  865.          * @param endDate end date
  866.          */
  867.         public void setEndDate(final FieldAbsoluteDate<T> endDate) {
  868.             this.endDate = endDate;
  869.         }

  870.         /** {@inheritDoc} */
  871.         @Override
  872.         public void init(final FieldODEStateAndDerivative<T> s0, final T t) {
  873.             this.model = new FieldDenseOutputModel<>();
  874.             model.init(s0, t);

  875.             // ephemeris will be generated when last step is processed
  876.             this.ephemeris = null;

  877.             this.lastInterpolator = null;

  878.         }

  879.         /** {@inheritDoc} */
  880.         @Override
  881.         public FieldBoundedPropagator<T> getGeneratedEphemeris() {
  882.             // Each time we try to get the ephemeris, rebuild it using the last data.
  883.             buildEphemeris();
  884.             return ephemeris;
  885.         }

  886.         /** {@inheritDoc} */
  887.         @Override
  888.         public void handleStep(final FieldODEStateInterpolator<T> interpolator) {
  889.             model.handleStep(interpolator);
  890.             lastInterpolator = interpolator;
  891.         }

  892.         /** {@inheritDoc} */
  893.         @Override
  894.         public void finish(final FieldODEStateAndDerivative<T> finalState) {
  895.             buildEphemeris();
  896.         }

  897.         /** Method used to produce ephemeris at a given time.
  898.          * Can be used at multiple times, updating the ephemeris to
  899.          * its last state.
  900.          */
  901.         private void buildEphemeris() {
  902.             // buildEphemeris was built in order to allow access to what was previously the finish method.
  903.             // This now allows to call it through getGeneratedEphemeris, therefore through an external call,
  904.             // which was not previously the case.

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

  907.             // set up the boundary dates
  908.             final T tI = model.getInitialTime();
  909.             final T tF = model.getFinalTime();
  910.             // tI is almost? always zero
  911.             final FieldAbsoluteDate<T> startDate =
  912.                             stateMapper.mapDoubleToDate(tI);
  913.             final FieldAbsoluteDate<T> finalDate =
  914.                             stateMapper.mapDoubleToDate(tF, this.endDate);
  915.             final FieldAbsoluteDate<T> minDate;
  916.             final FieldAbsoluteDate<T> maxDate;
  917.             if (tF.getReal() < tI.getReal()) {
  918.                 minDate = finalDate;
  919.                 maxDate = startDate;
  920.             } else {
  921.                 minDate = startDate;
  922.                 maxDate = finalDate;
  923.             }

  924.             // get the initial additional states that are not managed
  925.             final FieldArrayDictionary<T> unmanaged = new FieldArrayDictionary<>(startDate.getField());
  926.             for (final FieldArrayDictionary<T>.Entry initial : getInitialState().getAdditionalStatesValues().getData()) {
  927.                 if (!isAdditionalStateManaged(initial.getKey())) {
  928.                     // this additional state was in the initial state, but is unknown to the propagator
  929.                     // we simply copy its initial value as is
  930.                     unmanaged.put(initial.getKey(), initial.getValue());
  931.                 }
  932.             }

  933.             // get the names of additional states managed by differential equations
  934.             final String[] names      = new String[additionalDerivativesProviders.size()];
  935.             final int[]    dimensions = new int[additionalDerivativesProviders.size()];
  936.             for (int i = 0; i < names.length; ++i) {
  937.                 names[i] = additionalDerivativesProviders.get(i).getName();
  938.                 dimensions[i] = additionalDerivativesProviders.get(i).getDimension();
  939.             }

  940.             // create the ephemeris
  941.             ephemeris = new FieldIntegratedEphemeris<>(startDate, minDate, maxDate,
  942.                                                        stateMapper, propagationType, model,
  943.                                                        unmanaged, getAdditionalStateProviders(),
  944.                                                        names, dimensions);

  945.         }

  946.     }

  947.     /** Wrapper for resetting an integrator handlers.
  948.      * <p>
  949.      * This class is intended to be used in a try-with-resource statement.
  950.      * If propagator-specific event handlers and step handlers are added to
  951.      * the integrator in the try block, they will be removed automatically
  952.      * when leaving the block, so the integrator only keep its own handlers
  953.      * between calls to {@link AbstractIntegratedPropagator#propagate(AbsoluteDate, AbsoluteDate).
  954.      * </p>
  955.      * @param <T> the type of the field elements
  956.      * @since 11.0
  957.      */
  958.     private static class IntegratorResetter<T extends CalculusFieldElement<T>> implements AutoCloseable {

  959.         /** Wrapped integrator. */
  960.         private final FieldODEIntegrator<T> integrator;

  961.         /** Initial event handlers list. */
  962.         private final List<FieldEventHandlerConfiguration<T>> eventHandlersConfigurations;

  963.         /** Initial step handlers list. */
  964.         private final List<FieldODEStepHandler<T>> stepHandlers;

  965.         /** Simple constructor.
  966.          * @param integrator wrapped integrator
  967.          */
  968.         IntegratorResetter(final FieldODEIntegrator<T> integrator) {
  969.             this.integrator                  = integrator;
  970.             this.eventHandlersConfigurations = new ArrayList<>(integrator.getEventHandlersConfigurations());
  971.             this.stepHandlers                = new ArrayList<>(integrator.getStepHandlers());
  972.         }

  973.         /** {@inheritDoc}
  974.          * <p>
  975.          * Reset event handlers and step handlers back to the initial list
  976.          * </p>
  977.          */
  978.         @Override
  979.         public void close() {

  980.             // reset event handlers
  981.             integrator.clearEventHandlers();
  982.             eventHandlersConfigurations.forEach(c -> integrator.addEventHandler(c.getEventHandler(),
  983.                                                                                 c.getMaxCheckInterval(),
  984.                                                                                 c.getConvergence().getReal(),
  985.                                                                                 c.getMaxIterationCount(),
  986.                                                                                 c.getSolver()));

  987.             // reset step handlers
  988.             integrator.clearStepHandlers();
  989.             stepHandlers.forEach(stepHandler -> integrator.addStepHandler(stepHandler));

  990.         }

  991.     }

  992. }