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.     /** Get the propagation type.
  166.      * @return propagation type.
  167.      * @since 11.3.2
  168.      */
  169.     public PropagationType getPropagationType() {
  170.         return propagationType;
  171.     }

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

  186.     /** Get propagation parameter type.
  187.      * @return angle type to use for propagation
  188.      */
  189.     protected PositionAngle getPositionAngleType() {
  190.         return stateMapper.getPositionAngleType();
  191.     }

  192.     /** Set the central attraction coefficient μ.
  193.      * @param mu central attraction coefficient (m³/s²)
  194.      */
  195.     public void setMu(final T mu) {
  196.         stateMapper = createMapper(stateMapper.getReferenceDate(), mu,
  197.                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  198.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  199.     }

  200.     /** Get the central attraction coefficient μ.
  201.      * @return mu central attraction coefficient (m³/s²)
  202.      * @see #setMu(CalculusFieldElement)
  203.      */
  204.     public T getMu() {
  205.         return stateMapper.getMu();
  206.     }

  207.     /** Get the number of calls to the differential equations computation method.
  208.      * <p>The number of calls is reset each time the {@link #propagate(FieldAbsoluteDate)}
  209.      * method is called.</p>
  210.      * @return number of calls to the differential equations computation method
  211.      */
  212.     public int getCalls() {
  213.         return calls;
  214.     }

  215.     /** {@inheritDoc} */
  216.     @Override
  217.     public boolean isAdditionalStateManaged(final String name) {

  218.         // first look at already integrated states
  219.         if (super.isAdditionalStateManaged(name)) {
  220.             return true;
  221.         }

  222.         // then look at states we integrate ourselves
  223.         for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  224.             if (provider.getName().equals(name)) {
  225.                 return true;
  226.             }
  227.         }

  228.         return false;
  229.     }

  230.     /** {@inheritDoc} */
  231.     @Override
  232.     public String[] getManagedAdditionalStates() {
  233.         final String[] alreadyIntegrated = super.getManagedAdditionalStates();
  234.         final String[] managed = new String[alreadyIntegrated.length + additionalDerivativesProviders.size()];
  235.         System.arraycopy(alreadyIntegrated, 0, managed, 0, alreadyIntegrated.length);
  236.         for (int i = 0; i < additionalDerivativesProviders.size(); ++i) {
  237.             managed[i + alreadyIntegrated.length] = additionalDerivativesProviders.get(i).getName();
  238.         }
  239.         return managed;
  240.     }

  241.     /** Add a set of user-specified equations to be integrated along with the orbit propagation.
  242.      * @param additional additional equations
  243.      * @deprecated as of 11.1, replaced by {@link #addAdditionalDerivativesProvider(FieldAdditionalDerivativesProvider)}
  244.      */
  245.     @Deprecated
  246.     public void addAdditionalEquations(final FieldAdditionalEquations<T> additional) {
  247.         addAdditionalDerivativesProvider(new FieldAdditionalEquationsAdapter<>(additional, this::getInitialState));
  248.     }

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

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

  263.         secondaryOffsets.clear();

  264.     }

  265.     /** Get an unmodifiable list of providers for additional derivatives.
  266.      * @return providers for additional derivatives
  267.      * @since 11.1
  268.      */
  269.     public List<FieldAdditionalDerivativesProvider<T>> getAdditionalDerivativesProviders() {
  270.         return Collections.unmodifiableList(additionalDerivativesProviders);
  271.     }

  272.     /** {@inheritDoc} */
  273.     public <D extends FieldEventDetector<T>> void addEventDetector(final D detector) {
  274.         detectors.add(detector);
  275.     }

  276.     /** {@inheritDoc} */
  277.     public Collection<FieldEventDetector<T>> getEventsDetectors() {
  278.         return Collections.unmodifiableCollection(detectors);
  279.     }

  280.     /** {@inheritDoc} */
  281.     public void clearEventsDetectors() {
  282.         detectors.clear();
  283.     }

  284.     /** Set up all user defined event detectors.
  285.      */
  286.     protected void setUpUserEventDetectors() {
  287.         for (final FieldEventDetector<T> detector : detectors) {
  288.             setUpEventDetector(integrator, detector);
  289.         }
  290.     }

  291.     /** Wrap an Orekit event detector and register it to the integrator.
  292.      * @param integ integrator into which event detector should be registered
  293.      * @param detector event detector to wrap
  294.      */
  295.     protected void setUpEventDetector(final FieldODEIntegrator<T> integ, final FieldEventDetector<T> detector) {
  296.         integ.addEventHandler(new FieldAdaptedEventDetector(detector),
  297.                               detector.getMaxCheckInterval().getReal(),
  298.                               detector.getThreshold().getReal(),
  299.                               detector.getMaxIterationCount());
  300.     }

  301.     /** {@inheritDoc} */
  302.     @Override
  303.     public FieldEphemerisGenerator<T> getEphemerisGenerator() {
  304.         final FieldStoringStepHandler storingHandler = new FieldStoringStepHandler();
  305.         ephemerisGenerators.add(storingHandler);
  306.         return storingHandler;
  307.     }

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

  327.     /** Get the differential equations to integrate (for main state only).
  328.      * @param integ numerical integrator to use for propagation.
  329.      * @return differential equations for main state
  330.      */
  331.     protected abstract MainStateEquations<T> getMainStateEquations(FieldODEIntegrator<T> integ);

  332.     /** {@inheritDoc} */
  333.     public FieldSpacecraftState<T> propagate(final FieldAbsoluteDate<T> target) {
  334.         if (getStartDate() == null) {
  335.             if (getInitialState() == null) {
  336.                 throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  337.             }
  338.             setStartDate(getInitialState().getDate());
  339.         }
  340.         return propagate(getStartDate(), target);
  341.     }

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

  344.         if (getInitialState() == null) {
  345.             throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  346.         }

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

  349.             // Initialize additional states
  350.             initializeAdditionalStates(tEnd);

  351.             if (!tStart.equals(getInitialState().getDate())) {
  352.                 // if propagation start date is not initial date,
  353.                 // propagate from initial to start date without event detection
  354.                 try (IntegratorResetter<T> startResetter = new IntegratorResetter<>(integrator)) {
  355.                     integrateDynamics(tStart);
  356.                 }
  357.             }

  358.             // set up events added by user
  359.             setUpUserEventDetectors();

  360.             // set up step handlers
  361.             for (final FieldOrekitStepHandler<T> handler : getMultiplexer().getHandlers()) {
  362.                 integrator.addStepHandler(new FieldAdaptedStepHandler(handler));
  363.             }
  364.             for (final FieldStoringStepHandler generator : ephemerisGenerators) {
  365.                 generator.setEndDate(tEnd);
  366.                 integrator.addStepHandler(generator);
  367.             }

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

  370.         }

  371.     }

  372.     /** Propagation with or without event detection.
  373.      * @param tEnd target date to which orbit should be propagated
  374.      * @return state at end of propagation
  375.      */
  376.     private FieldSpacecraftState<T> integrateDynamics(final FieldAbsoluteDate<T> tEnd) {
  377.         try {

  378.             initializePropagation();

  379.             if (getInitialState().getDate().equals(tEnd)) {
  380.                 // don't extrapolate
  381.                 return getInitialState();
  382.             }
  383.             // space dynamics view
  384.             stateMapper = createMapper(getInitialState().getDate(), stateMapper.getMu(),
  385.                                        stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  386.                                        stateMapper.getAttitudeProvider(), getInitialState().getFrame());


  387.             // set propagation orbit type
  388.             //final FieldOrbit<T> initialOrbit = stateMapper.getOrbitType().convertType(getInitialState().getOrbit());
  389.             if (Double.isNaN(getMu().getReal())) {
  390.                 setMu(getInitialState().getMu());
  391.             }
  392.             if (getInitialState().getMass().getReal() <= 0.0) {
  393.                 throw new OrekitException(OrekitMessages.SPACECRAFT_MASS_BECOMES_NEGATIVE,
  394.                                                getInitialState().getMass());
  395.             }

  396.             // convert space flight dynamics API to math API
  397.             final FieldSpacecraftState<T> initialIntegrationState = getInitialIntegrationState();
  398.             final FieldODEState<T> mathInitialState = createInitialState(initialIntegrationState);
  399.             final FieldExpandableODE<T> mathODE = createODE(integrator, mathInitialState);

  400.             // mathematical integration
  401.             final FieldODEStateAndDerivative<T> mathFinalState;
  402.             beforeIntegration(initialIntegrationState, tEnd);
  403.             mathFinalState = integrator.integrate(mathODE, mathInitialState,
  404.                                                   tEnd.durationFrom(getInitialState().getDate()));

  405.             afterIntegration();

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

  425.             if (resetAtEnd) {
  426.                 resetInitialState(finalState);
  427.                 setStartDate(finalState.getDate());
  428.             }

  429.             return finalState;

  430.         } catch (OrekitException pe) {
  431.             throw pe;
  432.         } catch (MathIllegalArgumentException | MathIllegalStateException me) {
  433.             throw OrekitException.unwrap(me);
  434.         }
  435.     }

  436.     /** Get the initial state for integration.
  437.      * @return initial state for integration
  438.      */
  439.     protected FieldSpacecraftState<T> getInitialIntegrationState() {
  440.         return getInitialState();
  441.     }

  442.     /** Create an initial state.
  443.      * @param initialState initial state in flight dynamics world
  444.      * @return initial state in mathematics world
  445.      */
  446.     private FieldODEState<T> createInitialState(final FieldSpacecraftState<T> initialState) {

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

  450.         if (secondaryOffsets.isEmpty()) {
  451.             // compute dimension of the secondary state
  452.             int offset = 0;
  453.             for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  454.                 secondaryOffsets.put(provider.getName(), offset);
  455.                 offset += provider.getDimension();
  456.             }
  457.             secondaryOffsets.put(SECONDARY_DIMENSION, offset);
  458.         }

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

  460.     }

  461.     /** Create secondary state.
  462.      * @param state spacecraft state
  463.      * @return secondary state
  464.      * @since 11.1
  465.      */
  466.     private T[][] secondary(final FieldSpacecraftState<T> state) {

  467.         if (secondaryOffsets.isEmpty()) {
  468.             return null;
  469.         }

  470.         final T[][] secondary = MathArrays.buildArray(state.getDate().getField(), 1, secondaryOffsets.get(SECONDARY_DIMENSION));
  471.         for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  472.             final String name       = provider.getName();
  473.             final int    offset     = secondaryOffsets.get(name);
  474.             final T[]    additional = state.getAdditionalState(name);
  475.             System.arraycopy(additional, 0, secondary[0], offset, additional.length);
  476.         }

  477.         return secondary;

  478.     }

  479.     /** Create secondary state derivative.
  480.      * @param state spacecraft state
  481.      * @return secondary state derivative
  482.      * @since 11.1
  483.      */
  484.     private T[][] secondaryDerivative(final FieldSpacecraftState<T> state) {

  485.         if (secondaryOffsets.isEmpty()) {
  486.             return null;
  487.         }

  488.         final T[][] secondaryDerivative = MathArrays.buildArray(state.getDate().getField(), 1, secondaryOffsets.get(SECONDARY_DIMENSION));
  489.         for (final FieldAdditionalDerivativesProvider<T> providcer : additionalDerivativesProviders) {
  490.             final String name       = providcer.getName();
  491.             final int    offset     = secondaryOffsets.get(name);
  492.             final T[]    additionalDerivative = state.getAdditionalStateDerivative(name);
  493.             System.arraycopy(additionalDerivative, 0, secondaryDerivative[0], offset, additionalDerivative.length);
  494.         }

  495.         return secondaryDerivative;

  496.     }

  497.     /** Create an ODE with all equations.
  498.      * @param integ numerical integrator to use for propagation.
  499.      * @param mathInitialState initial state
  500.      * @return a new ode
  501.      */
  502.     private FieldExpandableODE<T> createODE(final FieldODEIntegrator<T> integ,
  503.                                     final FieldODEState<T> mathInitialState) {

  504.         final FieldExpandableODE<T> ode =
  505.                 new FieldExpandableODE<>(new ConvertedMainStateEquations(getMainStateEquations(integ)));

  506.         // secondary part of the ODE
  507.         if (!additionalDerivativesProviders.isEmpty()) {
  508.             ode.addSecondaryEquations(new ConvertedSecondaryStateEquations());
  509.         }

  510.         return ode;

  511.     }

  512.     /** Method called just before integration.
  513.      * <p>
  514.      * The default implementation does nothing, it may be specialized in subclasses.
  515.      * </p>
  516.      * @param initialState initial state
  517.      * @param tEnd target date at which state should be propagated
  518.      */
  519.     protected void beforeIntegration(final FieldSpacecraftState<T> initialState,
  520.                                      final FieldAbsoluteDate<T> tEnd) {
  521.         // do nothing by default
  522.     }

  523.     /** Method called just after integration.
  524.      * <p>
  525.      * The default implementation does nothing, it may be specialized in subclasses.
  526.      * </p>
  527.      */
  528.     protected void afterIntegration() {
  529.         // do nothing by default
  530.     }

  531.     /** Get state vector dimension without additional parameters.
  532.      * @return state vector dimension without additional parameters.
  533.      */
  534.     public int getBasicDimension() {
  535.         return 7;

  536.     }

  537.     /** Get the integrator used by the propagator.
  538.      * @return the integrator.
  539.      */
  540.     protected FieldODEIntegrator<T> getIntegrator() {
  541.         return integrator;
  542.     }

  543.     /** Convert a state from mathematical world to space flight dynamics world.
  544.      * @param os mathematical state
  545.      * @return space flight dynamics state
  546.      */
  547.     private FieldSpacecraftState<T> convert(final FieldODEStateAndDerivative<T> os) {

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

  565.         return s;

  566.     }

  567.     /** Convert a state from space flight dynamics world to mathematical world.
  568.      * @param state space flight dynamics state
  569.      * @return mathematical state
  570.      */
  571.     private FieldODEStateAndDerivative<T> convert(final FieldSpacecraftState<T> state) {

  572.         // retrieve initial state
  573.         final T[] primary    = MathArrays.buildArray(getField(), getBasicDimension());
  574.         final T[] primaryDot = MathArrays.buildArray(getField(), getBasicDimension());
  575.         stateMapper.mapStateToArray(state, primary, primaryDot);

  576.         // secondary part of the ODE
  577.         final T[][] secondary           = secondary(state);
  578.         final T[][] secondaryDerivative = secondaryDerivative(state);

  579.         return new FieldODEStateAndDerivative<>(stateMapper.mapDateToDouble(state.getDate()),
  580.                                                 primary, primaryDot,
  581.                                                 secondary, secondaryDerivative);

  582.     }

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

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

  596.         /** Compute differential equations for main state.
  597.          * @param state current state
  598.          * @return derivatives of main state
  599.          */
  600.         T[] computeDerivatives(FieldSpacecraftState<T> state);

  601.     }

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

  604.         /** Main state equations. */
  605.         private final MainStateEquations<T> main;

  606.         /** Simple constructor.
  607.          * @param main main state equations
  608.          */
  609.         ConvertedMainStateEquations(final MainStateEquations<T> main) {
  610.             this.main = main;
  611.             calls = 0;
  612.         }

  613.         /** {@inheritDoc} */
  614.         public int getDimension() {
  615.             return getBasicDimension();
  616.         }

  617.         @Override
  618.         public void init(final T t0, final T[] y0, final T finalTime) {
  619.             // update space dynamics view
  620.             FieldSpacecraftState<T> initialState = stateMapper.mapArrayToState(t0, y0, null, PropagationType.MEAN);
  621.             initialState = updateAdditionalStates(initialState);
  622.             final FieldAbsoluteDate<T> target = stateMapper.mapDoubleToDate(finalTime);
  623.             main.init(initialState, target);
  624.         }
  625.         /** {@inheritDoc} */
  626.         public T[] computeDerivatives(final T t, final T[] y) {

  627.             // increment calls counter
  628.             ++calls;

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

  632.             // compute main state differentials
  633.             return main.computeDerivatives(currentState);

  634.         }

  635.     }

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

  638.         /** Dimension of the combined additional states. */
  639.         private final int combinedDimension;

  640.         /** Simple constructor.
  641.          */
  642.         ConvertedSecondaryStateEquations() {
  643.             this.combinedDimension = secondaryOffsets.get(SECONDARY_DIMENSION);
  644.         }

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

  650.         /** {@inheritDoc} */
  651.         @Override
  652.         public void init(final T t0, final T[] primary0,
  653.                          final T[] secondary0, final T finalTime) {
  654.             // update space dynamics view
  655.             final FieldSpacecraftState<T> initialState = convert(t0, primary0, null, secondary0);

  656.             final FieldAbsoluteDate<T> target = stateMapper.mapDoubleToDate(finalTime);
  657.             for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  658.                 provider.init(initialState, target);
  659.             }

  660.         }

  661.         /** {@inheritDoc} */
  662.         @Override
  663.         public T[] computeDerivatives(final T t, final T[] primary,
  664.                                       final T[] primaryDot, final T[] secondary) {

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

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

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

  705.             return secondaryDot;

  706.         }

  707.         /** Convert mathematical view to space view.
  708.          * @param t current value of the independent <I>time</I> variable
  709.          * @param primary array containing the current value of the primary state vector
  710.          * @param primaryDot array containing the derivative of the primary state vector
  711.          * @param secondary array containing the current value of the secondary state vector
  712.          * @return space view of the state
  713.          */
  714.         private FieldSpacecraftState<T> convert(final T t, final T[] primary,
  715.                                                 final T[] primaryDot, final T[] secondary) {

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

  717.             for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  718.                 final String name      = provider.getName();
  719.                 final int    offset    = secondaryOffsets.get(name);
  720.                 final int    dimension = provider.getDimension();
  721.                 initialState = initialState.addAdditionalState(name, Arrays.copyOfRange(secondary, offset, offset + dimension));
  722.             }

  723.             return updateAdditionalStates(initialState);

  724.         }

  725.     }

  726.     /** Adapt an {@link org.orekit.propagation.events.FieldEventDetector<T>}
  727.      * to Hipparchus {@link org.hipparchus.ode.events.FieldODEEventHandler<T>} interface.
  728.      * @param <T> class type for the generic version
  729.      * @author Fabien Maussion
  730.      */
  731.     private class FieldAdaptedEventDetector implements FieldODEEventHandler<T> {

  732.         /** Underlying event detector. */
  733.         private final FieldEventDetector<T> detector;

  734.         /** Time of the previous call to g. */
  735.         private T lastT;

  736.         /** Value from the previous call to g. */
  737.         private T lastG;

  738.         /** Build a wrapped event detector.
  739.          * @param detector event detector to wrap
  740.         */
  741.         FieldAdaptedEventDetector(final FieldEventDetector<T> detector) {
  742.             this.detector = detector;
  743.             this.lastT    = getField().getZero().add(Double.NaN);
  744.             this.lastG    = getField().getZero().add(Double.NaN);
  745.         }

  746.         /** {@inheritDoc} */
  747.         public void init(final FieldODEStateAndDerivative<T> s0, final T t) {
  748.             detector.init(convert(s0), stateMapper.mapDoubleToDate(t));
  749.             this.lastT = getField().getZero().add(Double.NaN);
  750.             this.lastG = getField().getZero().add(Double.NaN);
  751.         }

  752.         /** {@inheritDoc} */
  753.         public T g(final FieldODEStateAndDerivative<T> s) {
  754.             if (!Precision.equals(lastT.getReal(), s.getTime().getReal(), 0)) {
  755.                 lastT = s.getTime();
  756.                 lastG = detector.g(convert(s));
  757.             }
  758.             return lastG;
  759.         }

  760.         /** {@inheritDoc} */
  761.         public Action eventOccurred(final FieldODEStateAndDerivative<T> s, final boolean increasing) {
  762.             return detector.eventOccurred(convert(s), increasing);
  763.         }

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

  766.             final FieldSpacecraftState<T> oldState = convert(s);
  767.             final FieldSpacecraftState<T> newState = detector.resetState(oldState);
  768.             stateChanged(newState);

  769.             // main part
  770.             final T[] primary    = MathArrays.buildArray(getField(), s.getPrimaryStateDimension());
  771.             stateMapper.mapStateToArray(newState, primary, null);

  772.             // secondary part
  773.             final T[][] secondary = MathArrays.buildArray(getField(), 1, additionalDerivativesProviders.size());
  774.             for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  775.                 final String name      = provider.getName();
  776.                 final int    offset    = secondaryOffsets.get(name);
  777.                 final int    dimension = provider.getDimension();
  778.                 System.arraycopy(newState.getAdditionalState(name), 0, secondary[0], offset, dimension);
  779.             }

  780.             return new FieldODEState<>(newState.getDate().durationFrom(getStartDate()),
  781.                                        primary, secondary);
  782.         }

  783.     }

  784.     /** Adapt an {@link org.orekit.propagation.sampling.FieldOrekitStepHandler<T>}
  785.      * to Hipparchus {@link FieldODEStepHandler<T>} interface.
  786.      * @author Luc Maisonobe
  787.      */
  788.     private class FieldAdaptedStepHandler implements FieldODEStepHandler<T> {

  789.         /** Underlying handler. */
  790.         private final FieldOrekitStepHandler<T> handler;

  791.         /** Build an instance.
  792.          * @param handler underlying handler to wrap
  793.          */
  794.         FieldAdaptedStepHandler(final FieldOrekitStepHandler<T> handler) {
  795.             this.handler = handler;
  796.         }

  797.         /** {@inheritDoc} */
  798.         public void init(final FieldODEStateAndDerivative<T> s0, final T t) {
  799.             handler.init(convert(s0), stateMapper.mapDoubleToDate(t));
  800.         }

  801.         /** {@inheritDoc} */
  802.         public void handleStep(final FieldODEStateInterpolator<T> interpolator) {
  803.             handler.handleStep(new FieldAdaptedStepInterpolator(interpolator));
  804.         }

  805.         /** {@inheritDoc} */
  806.         @Override
  807.         public void finish(final FieldODEStateAndDerivative<T> finalState) {
  808.             handler.finish(convert(finalState));
  809.         }

  810.     }

  811.     /** Adapt an {@link org.orekit.propagation.sampling.FieldOrekitStepInterpolator<T>}
  812.      * to Hipparchus {@link FieldODEStepInterpolator<T>} interface.
  813.      * @author Luc Maisonobe
  814.      */
  815.     private class FieldAdaptedStepInterpolator implements FieldOrekitStepInterpolator<T> {

  816.         /** Underlying raw rawInterpolator. */
  817.         private final FieldODEStateInterpolator<T> mathInterpolator;

  818.         /** Build an instance.
  819.          * @param mathInterpolator underlying raw interpolator
  820.          */
  821.         FieldAdaptedStepInterpolator(final FieldODEStateInterpolator<T> mathInterpolator) {
  822.             this.mathInterpolator = mathInterpolator;
  823.         }

  824.         /** {@inheritDoc}} */
  825.         @Override
  826.         public FieldSpacecraftState<T> getPreviousState() {
  827.             return convert(mathInterpolator.getPreviousState());
  828.         }

  829.         /** {@inheritDoc}} */
  830.         @Override
  831.         public FieldSpacecraftState<T> getCurrentState() {
  832.             return convert(mathInterpolator.getCurrentState());
  833.         }

  834.         /** {@inheritDoc}} */
  835.         @Override
  836.         public FieldSpacecraftState<T> getInterpolatedState(final FieldAbsoluteDate<T> date) {
  837.             return convert(mathInterpolator.getInterpolatedState(date.durationFrom(getStartDate())));
  838.         }

  839.         /** Check is integration direction is forward in date.
  840.          * @return true if integration is forward in date
  841.          */
  842.         public boolean isForward() {
  843.             return mathInterpolator.isForward();
  844.         }

  845.         /** {@inheritDoc}} */
  846.         @Override
  847.         public FieldAdaptedStepInterpolator restrictStep(final FieldSpacecraftState<T> newPreviousState,
  848.                                                          final FieldSpacecraftState<T> newCurrentState) {
  849.             try {
  850.                 final AbstractFieldODEStateInterpolator<T> aosi = (AbstractFieldODEStateInterpolator<T>) mathInterpolator;
  851.                 return new FieldAdaptedStepInterpolator(aosi.restrictStep(convert(newPreviousState),
  852.                                                                           convert(newCurrentState)));
  853.             } catch (ClassCastException cce) {
  854.                 // this should never happen
  855.                 throw new OrekitInternalError(cce);
  856.             }
  857.         }

  858.     }

  859.     /** Specialized step handler storing interpolators for ephemeris generation.
  860.      * @since 11.0
  861.      */
  862.     private class FieldStoringStepHandler implements FieldODEStepHandler<T>, FieldEphemerisGenerator<T> {

  863.         /** Underlying raw mathematical model. */
  864.         private FieldDenseOutputModel<T> model;

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

  867.         /** Generated ephemeris. */
  868.         private FieldBoundedPropagator<T> ephemeris;

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

  871.         /** Set the end date.
  872.          * @param endDate end date
  873.          */
  874.         public void setEndDate(final FieldAbsoluteDate<T> endDate) {
  875.             this.endDate = endDate;
  876.         }

  877.         /** {@inheritDoc} */
  878.         @Override
  879.         public void init(final FieldODEStateAndDerivative<T> s0, final T t) {
  880.             this.model = new FieldDenseOutputModel<>();
  881.             model.init(s0, t);

  882.             // ephemeris will be generated when last step is processed
  883.             this.ephemeris = null;

  884.             this.lastInterpolator = null;

  885.         }

  886.         /** {@inheritDoc} */
  887.         @Override
  888.         public FieldBoundedPropagator<T> getGeneratedEphemeris() {
  889.             // Each time we try to get the ephemeris, rebuild it using the last data.
  890.             buildEphemeris();
  891.             return ephemeris;
  892.         }

  893.         /** {@inheritDoc} */
  894.         @Override
  895.         public void handleStep(final FieldODEStateInterpolator<T> interpolator) {
  896.             model.handleStep(interpolator);
  897.             lastInterpolator = interpolator;
  898.         }

  899.         /** {@inheritDoc} */
  900.         @Override
  901.         public void finish(final FieldODEStateAndDerivative<T> finalState) {
  902.             buildEphemeris();
  903.         }

  904.         /** Method used to produce ephemeris at a given time.
  905.          * Can be used at multiple times, updating the ephemeris to
  906.          * its last state.
  907.          */
  908.         private void buildEphemeris() {
  909.             // buildEphemeris was built in order to allow access to what was previously the finish method.
  910.             // This now allows to call it through getGeneratedEphemeris, therefore through an external call,
  911.             // which was not previously the case.

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

  914.             // set up the boundary dates
  915.             final T tI = model.getInitialTime();
  916.             final T tF = model.getFinalTime();
  917.             // tI is almost? always zero
  918.             final FieldAbsoluteDate<T> startDate =
  919.                             stateMapper.mapDoubleToDate(tI);
  920.             final FieldAbsoluteDate<T> finalDate =
  921.                             stateMapper.mapDoubleToDate(tF, this.endDate);
  922.             final FieldAbsoluteDate<T> minDate;
  923.             final FieldAbsoluteDate<T> maxDate;
  924.             if (tF.getReal() < tI.getReal()) {
  925.                 minDate = finalDate;
  926.                 maxDate = startDate;
  927.             } else {
  928.                 minDate = startDate;
  929.                 maxDate = finalDate;
  930.             }

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

  940.             // get the names of additional states managed by differential equations
  941.             final String[] names      = new String[additionalDerivativesProviders.size()];
  942.             final int[]    dimensions = new int[additionalDerivativesProviders.size()];
  943.             for (int i = 0; i < names.length; ++i) {
  944.                 names[i] = additionalDerivativesProviders.get(i).getName();
  945.                 dimensions[i] = additionalDerivativesProviders.get(i).getDimension();
  946.             }

  947.             // create the ephemeris
  948.             ephemeris = new FieldIntegratedEphemeris<>(startDate, minDate, maxDate,
  949.                                                        stateMapper, propagationType, model,
  950.                                                        unmanaged, getAdditionalStateProviders(),
  951.                                                        names, dimensions);

  952.         }

  953.     }

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

  966.         /** Wrapped integrator. */
  967.         private final FieldODEIntegrator<T> integrator;

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

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

  972.         /** Simple constructor.
  973.          * @param integrator wrapped integrator
  974.          */
  975.         IntegratorResetter(final FieldODEIntegrator<T> integrator) {
  976.             this.integrator                  = integrator;
  977.             this.eventHandlersConfigurations = new ArrayList<>(integrator.getEventHandlersConfigurations());
  978.             this.stepHandlers                = new ArrayList<>(integrator.getStepHandlers());
  979.         }

  980.         /** {@inheritDoc}
  981.          * <p>
  982.          * Reset event handlers and step handlers back to the initial list
  983.          * </p>
  984.          */
  985.         @Override
  986.         public void close() {

  987.             // reset event handlers
  988.             integrator.clearEventHandlers();
  989.             eventHandlersConfigurations.forEach(c -> integrator.addEventHandler(c.getEventHandler(),
  990.                                                                                 c.getMaxCheckInterval(),
  991.                                                                                 c.getConvergence().getReal(),
  992.                                                                                 c.getMaxIterationCount(),
  993.                                                                                 c.getSolver()));

  994.             // reset step handlers
  995.             integrator.clearStepHandlers();
  996.             stepHandlers.forEach(stepHandler -> integrator.addStepHandler(stepHandler));

  997.         }

  998.     }

  999. }