FieldAbstractIntegratedPropagator.java

  1. /* Copyright 2002-2024 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.analysis.solvers.FieldBracketingNthOrderBrentSolver;
  30. import org.hipparchus.exception.MathIllegalArgumentException;
  31. import org.hipparchus.exception.MathIllegalStateException;
  32. import org.hipparchus.ode.FieldDenseOutputModel;
  33. import org.hipparchus.ode.FieldExpandableODE;
  34. import org.hipparchus.ode.FieldODEIntegrator;
  35. import org.hipparchus.ode.FieldODEState;
  36. import org.hipparchus.ode.FieldODEStateAndDerivative;
  37. import org.hipparchus.ode.FieldOrdinaryDifferentialEquation;
  38. import org.hipparchus.ode.FieldSecondaryODE;
  39. import org.hipparchus.ode.events.Action;
  40. import org.hipparchus.ode.events.FieldAdaptableInterval;
  41. import org.hipparchus.ode.events.FieldODEEventDetector;
  42. import org.hipparchus.ode.events.FieldODEEventHandler;
  43. import org.hipparchus.ode.sampling.AbstractFieldODEStateInterpolator;
  44. import org.hipparchus.ode.sampling.FieldODEStateInterpolator;
  45. import org.hipparchus.ode.sampling.FieldODEStepHandler;
  46. import org.hipparchus.util.MathArrays;
  47. import org.hipparchus.util.Precision;
  48. import org.orekit.attitudes.AttitudeProvider;
  49. import org.orekit.errors.OrekitException;
  50. import org.orekit.errors.OrekitInternalError;
  51. import org.orekit.errors.OrekitMessages;
  52. import org.orekit.frames.Frame;
  53. import org.orekit.orbits.OrbitType;
  54. import org.orekit.orbits.PositionAngleType;
  55. import org.orekit.propagation.FieldAbstractPropagator;
  56. import org.orekit.propagation.FieldBoundedPropagator;
  57. import org.orekit.propagation.FieldEphemerisGenerator;
  58. import org.orekit.propagation.FieldSpacecraftState;
  59. import org.orekit.propagation.PropagationType;
  60. import org.orekit.propagation.events.FieldEventDetector;
  61. import org.orekit.propagation.events.handlers.FieldEventHandler;
  62. import org.orekit.propagation.sampling.FieldOrekitStepHandler;
  63. import org.orekit.propagation.sampling.FieldOrekitStepInterpolator;
  64. import org.orekit.time.FieldAbsoluteDate;
  65. import org.orekit.utils.FieldArrayDictionary;


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

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

  76.     /** Event detectors not related to force models. */
  77.     private final List<FieldEventDetector<T>> detectors;

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

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

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

  86.     /** Additional derivatives providers.
  87.      * @since 11.1
  88.      */
  89.     private final List<FieldAdditionalDerivativesProvider<T>> additionalDerivativesProviders;

  90.     /** Counter for differential equations calls. */
  91.     private int calls;

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

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

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

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

  108.     /** Build a new instance.
  109.      * @param integrator numerical integrator to use for propagation.
  110.      * @param propagationType type of orbit to output (mean or osculating).
  111.      * @param field Field used by default
  112.      */
  113.     protected FieldAbstractIntegratedPropagator(final Field<T> field, final FieldODEIntegrator<T> integrator, final PropagationType propagationType) {
  114.         super(field);
  115.         detectors                      = new ArrayList<>();
  116.         ephemerisGenerators            = new ArrayList<>();
  117.         additionalDerivativesProviders = new ArrayList<>();
  118.         this.secondaryOffsets          = new HashMap<>();
  119.         this.integrator                = integrator;
  120.         this.propagationType           = propagationType;
  121.         this.resetAtEnd                = true;
  122.     }

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

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

  145.     /**
  146.      * Method called when initializing the attitude provider used when evaluating derivatives.
  147.      * @return attitude provider for derivatives
  148.      */
  149.     protected AttitudeProvider initializeAttitudeProviderForDerivatives() {
  150.         return getAttitudeProvider();
  151.     }

  152.     /** Initialize the mapper.
  153.      * @param field Field used by default
  154.      */
  155.     protected void initMapper(final Field<T> field) {
  156.         final T zero = field.getZero();
  157.         stateMapper = createMapper(null, zero.add(Double.NaN), null, null, null, null);
  158.     }

  159.     /** Get the integrator's name.
  160.      * @return name of underlying integrator
  161.      * @since 12.0
  162.      */
  163.     public String getIntegratorName() {
  164.         return integrator.getName();
  165.     }

  166.     /**  {@inheritDoc} */
  167.     @Override
  168.     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
  169.         super.setAttitudeProvider(attitudeProvider);
  170.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  171.                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  172.                                    attitudeProvider, stateMapper.getFrame());
  173.     }

  174.     /** Set propagation orbit type.
  175.      * @param orbitType orbit type to use for propagation
  176.      */
  177.     protected void setOrbitType(final OrbitType orbitType) {
  178.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  179.                                    orbitType, stateMapper.getPositionAngleType(),
  180.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  181.     }

  182.     /** Get propagation parameter type.
  183.      * @return orbit type used for propagation
  184.      */
  185.     protected OrbitType getOrbitType() {
  186.         return stateMapper.getOrbitType();
  187.     }

  188.     /** Check if only the mean elements should be used in a semi-analytical propagation.
  189.      * @return {@link PropagationType MEAN} if only mean elements have to be used or
  190.      *         {@link PropagationType OSCULATING} if osculating elements have to be also used.
  191.      */
  192.     protected PropagationType isMeanOrbit() {
  193.         return propagationType;
  194.     }

  195.     /** Get the propagation type.
  196.      * @return propagation type.
  197.      * @since 11.3.2
  198.      */
  199.     public PropagationType getPropagationType() {
  200.         return propagationType;
  201.     }

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

  216.     /** Get propagation parameter type.
  217.      * @return angle type to use for propagation
  218.      */
  219.     protected PositionAngleType getPositionAngleType() {
  220.         return stateMapper.getPositionAngleType();
  221.     }

  222.     /** Set the central attraction coefficient μ.
  223.      * @param mu central attraction coefficient (m³/s²)
  224.      */
  225.     public void setMu(final T mu) {
  226.         stateMapper = createMapper(stateMapper.getReferenceDate(), mu,
  227.                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  228.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  229.     }

  230.     /** Get the central attraction coefficient μ.
  231.      * @return mu central attraction coefficient (m³/s²)
  232.      * @see #setMu(CalculusFieldElement)
  233.      */
  234.     public T getMu() {
  235.         return stateMapper.getMu();
  236.     }

  237.     /** Get the number of calls to the differential equations computation method.
  238.      * <p>The number of calls is reset each time the {@link #propagate(FieldAbsoluteDate)}
  239.      * method is called.</p>
  240.      * @return number of calls to the differential equations computation method
  241.      */
  242.     public int getCalls() {
  243.         return calls;
  244.     }

  245.     /** {@inheritDoc} */
  246.     @Override
  247.     public boolean isAdditionalStateManaged(final String name) {

  248.         // first look at already integrated states
  249.         if (super.isAdditionalStateManaged(name)) {
  250.             return true;
  251.         }

  252.         // then look at states we integrate ourselves
  253.         for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  254.             if (provider.getName().equals(name)) {
  255.                 return true;
  256.             }
  257.         }

  258.         return false;
  259.     }

  260.     /** {@inheritDoc} */
  261.     @Override
  262.     public String[] getManagedAdditionalStates() {
  263.         final String[] alreadyIntegrated = super.getManagedAdditionalStates();
  264.         final String[] managed = new String[alreadyIntegrated.length + additionalDerivativesProviders.size()];
  265.         System.arraycopy(alreadyIntegrated, 0, managed, 0, alreadyIntegrated.length);
  266.         for (int i = 0; i < additionalDerivativesProviders.size(); ++i) {
  267.             managed[i + alreadyIntegrated.length] = additionalDerivativesProviders.get(i).getName();
  268.         }
  269.         return managed;
  270.     }

  271.     /** Add a provider for user-specified state derivatives to be integrated along with the orbit propagation.
  272.      * @param provider provider for additional derivatives
  273.      * @see #addAdditionalStateProvider(org.orekit.propagation.FieldAdditionalStateProvider)
  274.      * @since 11.1
  275.      */
  276.     public void addAdditionalDerivativesProvider(final FieldAdditionalDerivativesProvider<T> provider) {
  277.         // check if the name is already used
  278.         if (isAdditionalStateManaged(provider.getName())) {
  279.             // these derivatives are already registered, complain
  280.             throw new OrekitException(OrekitMessages.ADDITIONAL_STATE_NAME_ALREADY_IN_USE,
  281.                                       provider.getName());
  282.         }

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

  285.         secondaryOffsets.clear();

  286.     }

  287.     /** Get an unmodifiable list of providers for additional derivatives.
  288.      * @return providers for additional derivatives
  289.      * @since 11.1
  290.      */
  291.     public List<FieldAdditionalDerivativesProvider<T>> getAdditionalDerivativesProviders() {
  292.         return Collections.unmodifiableList(additionalDerivativesProviders);
  293.     }

  294.     /** {@inheritDoc} */
  295.     public <D extends FieldEventDetector<T>> void addEventDetector(final D detector) {
  296.         detectors.add(detector);
  297.     }

  298.     /** {@inheritDoc} */
  299.     public Collection<FieldEventDetector<T>> getEventsDetectors() {
  300.         return Collections.unmodifiableCollection(detectors);
  301.     }

  302.     /** {@inheritDoc} */
  303.     public void clearEventsDetectors() {
  304.         detectors.clear();
  305.     }

  306.     /** Set up all user defined event detectors.
  307.      */
  308.     protected void setUpUserEventDetectors() {
  309.         for (final FieldEventDetector<T> detector : detectors) {
  310.             setUpEventDetector(integrator, detector);
  311.         }
  312.     }

  313.     /** Wrap an Orekit event detector and register it to the integrator.
  314.      * @param integ integrator into which event detector should be registered
  315.      * @param detector event detector to wrap
  316.      */
  317.     protected void setUpEventDetector(final FieldODEIntegrator<T> integ, final FieldEventDetector<T> detector) {
  318.         integ.addEventDetector(new FieldAdaptedEventDetector(detector));
  319.     }

  320.     /** {@inheritDoc} */
  321.     @Override
  322.     public FieldEphemerisGenerator<T> getEphemerisGenerator() {
  323.         final FieldStoringStepHandler storingHandler = new FieldStoringStepHandler();
  324.         ephemerisGenerators.add(storingHandler);
  325.         return storingHandler;
  326.     }

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

  346.     /** Get the differential equations to integrate (for main state only).
  347.      * @param integ numerical integrator to use for propagation.
  348.      * @return differential equations for main state
  349.      */
  350.     protected abstract MainStateEquations<T> getMainStateEquations(FieldODEIntegrator<T> integ);

  351.     /** {@inheritDoc} */
  352.     @Override
  353.     public FieldSpacecraftState<T> propagate(final FieldAbsoluteDate<T> target) {
  354.         if (getStartDate() == null) {
  355.             if (getInitialState() == null) {
  356.                 throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  357.             }
  358.             setStartDate(getInitialState().getDate());
  359.         }
  360.         return propagate(getStartDate(), target);
  361.     }

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

  364.         if (getInitialState() == null) {
  365.             throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  366.         }

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

  369.             // Initialize additional states
  370.             initializeAdditionalStates(tEnd);

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

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

  380.             // set up step handlers
  381.             for (final FieldOrekitStepHandler<T> handler : getMultiplexer().getHandlers()) {
  382.                 integrator.addStepHandler(new FieldAdaptedStepHandler(handler));
  383.             }
  384.             for (final FieldStoringStepHandler generator : ephemerisGenerators) {
  385.                 generator.setEndDate(tEnd);
  386.                 integrator.addStepHandler(generator);
  387.             }

  388.             // propagate from start date to end date with event detection
  389.             final FieldSpacecraftState<T> state = integrateDynamics(tEnd);

  390.             // Finalize event detectors
  391.             getEventsDetectors().forEach(detector -> detector.finish(state));

  392.             return state;
  393.         }

  394.     }

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

  408.     /** Propagation with or without event detection.
  409.      * @param tEnd target date to which orbit should be propagated
  410.      * @return state at end of propagation
  411.      */
  412.     private FieldSpacecraftState<T> integrateDynamics(final FieldAbsoluteDate<T> tEnd) {
  413.         try {

  414.             initializePropagation();

  415.             if (getInitialState().getDate().equals(tEnd)) {
  416.                 // don't extrapolate
  417.                 return getInitialState();
  418.             }
  419.             // space dynamics view
  420.             stateMapper = createMapper(getInitialState().getDate(), stateMapper.getMu(),
  421.                                        stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  422.                                        stateMapper.getAttitudeProvider(), getInitialState().getFrame());

  423.             // set propagation orbit type
  424.             if (Double.isNaN(getMu().getReal())) {
  425.                 setMu(getInitialState().getMu());
  426.             }
  427.             if (getInitialState().getMass().getReal() <= 0.0) {
  428.                 throw new OrekitException(OrekitMessages.NOT_POSITIVE_SPACECRAFT_MASS,
  429.                                                getInitialState().getMass());
  430.             }

  431.             // convert space flight dynamics API to math API
  432.             final FieldSpacecraftState<T> initialIntegrationState = getInitialIntegrationState();
  433.             final FieldODEState<T> mathInitialState = createInitialState(initialIntegrationState);
  434.             final FieldExpandableODE<T> mathODE = createODE(integrator);

  435.             // mathematical integration
  436.             final FieldODEStateAndDerivative<T> mathFinalState;
  437.             beforeIntegration(initialIntegrationState, tEnd);
  438.             mathFinalState = integrator.integrate(mathODE, mathInitialState,
  439.                                                   tEnd.durationFrom(getInitialState().getDate()));

  440.             afterIntegration();

  441.             // get final state
  442.             FieldSpacecraftState<T> finalState =
  443.                             stateMapper.mapArrayToState(stateMapper.mapDoubleToDate(mathFinalState.getTime(), tEnd),
  444.                                                         mathFinalState.getPrimaryState(),
  445.                                                         mathFinalState.getPrimaryDerivative(),
  446.                                                         propagationType);

  447.             finalState = updateAdditionalStatesAndDerivatives(finalState, mathFinalState);

  448.             if (resetAtEnd) {
  449.                 resetInitialState(finalState, propagationType);
  450.                 setStartDate(finalState.getDate());
  451.             }

  452.             return finalState;

  453.         } catch (OrekitException pe) {
  454.             throw pe;
  455.         } catch (MathIllegalArgumentException | MathIllegalStateException me) {
  456.             throw OrekitException.unwrap(me);
  457.         }
  458.     }

  459.     /**
  460.      * Returns an updated version of the inputted state with additional states, including
  461.      * from derivatives providers.
  462.      * @param originalState input state
  463.      * @param os ODE state and derivative
  464.      * @return new state
  465.      * @since 12.1
  466.      */
  467.     private FieldSpacecraftState<T> updateAdditionalStatesAndDerivatives(final FieldSpacecraftState<T> originalState,
  468.                                                                          final FieldODEStateAndDerivative<T> os) {
  469.         FieldSpacecraftState<T> updatedState = originalState;
  470.         if (os.getNumberOfSecondaryStates() > 0) {
  471.             final T[] secondary           = os.getSecondaryState(1);
  472.             final T[] secondaryDerivative = os.getSecondaryDerivative(1);
  473.             for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  474.                 final String name      = provider.getName();
  475.                 final int    offset    = secondaryOffsets.get(name);
  476.                 final int    dimension = provider.getDimension();
  477.                 updatedState = updatedState.addAdditionalState(name, Arrays.copyOfRange(secondary, offset, offset + dimension));
  478.                 updatedState = updatedState.addAdditionalStateDerivative(name, Arrays.copyOfRange(secondaryDerivative, offset, offset + dimension));
  479.             }
  480.         }
  481.         return updateAdditionalStates(updatedState);
  482.     }

  483.     /** Get the initial state for integration.
  484.      * @return initial state for integration
  485.      */
  486.     protected FieldSpacecraftState<T> getInitialIntegrationState() {
  487.         return getInitialState();
  488.     }

  489.     /** Create an initial state.
  490.      * @param initialState initial state in flight dynamics world
  491.      * @return initial state in mathematics world
  492.      */
  493.     private FieldODEState<T> createInitialState(final FieldSpacecraftState<T> initialState) {

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

  497.         if (secondaryOffsets.isEmpty()) {
  498.             // compute dimension of the secondary state
  499.             int offset = 0;
  500.             for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  501.                 secondaryOffsets.put(provider.getName(), offset);
  502.                 offset += provider.getDimension();
  503.             }
  504.             secondaryOffsets.put(SECONDARY_DIMENSION, offset);
  505.         }

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

  507.     }

  508.     /** Create secondary state.
  509.      * @param state spacecraft state
  510.      * @return secondary state
  511.      * @since 11.1
  512.      */
  513.     private T[][] secondary(final FieldSpacecraftState<T> state) {

  514.         if (secondaryOffsets.isEmpty()) {
  515.             return null;
  516.         }

  517.         final T[][] secondary = MathArrays.buildArray(state.getDate().getField(), 1, secondaryOffsets.get(SECONDARY_DIMENSION));
  518.         for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  519.             final String name       = provider.getName();
  520.             final int    offset     = secondaryOffsets.get(name);
  521.             final T[]    additional = state.getAdditionalState(name);
  522.             System.arraycopy(additional, 0, secondary[0], offset, additional.length);
  523.         }

  524.         return secondary;

  525.     }

  526.     /** Create secondary state derivative.
  527.      * @param state spacecraft state
  528.      * @return secondary state derivative
  529.      * @since 11.1
  530.      */
  531.     private T[][] secondaryDerivative(final FieldSpacecraftState<T> state) {

  532.         if (secondaryOffsets.isEmpty()) {
  533.             return null;
  534.         }

  535.         final T[][] secondaryDerivative = MathArrays.buildArray(state.getDate().getField(), 1, secondaryOffsets.get(SECONDARY_DIMENSION));
  536.         for (final FieldAdditionalDerivativesProvider<T> providcer : additionalDerivativesProviders) {
  537.             final String name       = providcer.getName();
  538.             final int    offset     = secondaryOffsets.get(name);
  539.             final T[]    additionalDerivative = state.getAdditionalStateDerivative(name);
  540.             System.arraycopy(additionalDerivative, 0, secondaryDerivative[0], offset, additionalDerivative.length);
  541.         }

  542.         return secondaryDerivative;

  543.     }

  544.     /** Create an ODE with all equations.
  545.      * @param integ numerical integrator to use for propagation.
  546.      * @return a new ode
  547.      */
  548.     private FieldExpandableODE<T> createODE(final FieldODEIntegrator<T> integ) {

  549.         final FieldExpandableODE<T> ode =
  550.                 new FieldExpandableODE<>(new ConvertedMainStateEquations(getMainStateEquations(integ)));

  551.         // secondary part of the ODE
  552.         if (!additionalDerivativesProviders.isEmpty()) {
  553.             ode.addSecondaryEquations(new ConvertedSecondaryStateEquations());
  554.         }

  555.         return ode;

  556.     }

  557.     /** Method called just before integration.
  558.      * <p>
  559.      * The default implementation does nothing, it may be specialized in subclasses.
  560.      * </p>
  561.      * @param initialState initial state
  562.      * @param tEnd target date at which state should be propagated
  563.      */
  564.     protected void beforeIntegration(final FieldSpacecraftState<T> initialState,
  565.                                      final FieldAbsoluteDate<T> tEnd) {
  566.         // do nothing by default
  567.     }

  568.     /** Method called just after integration.
  569.      * <p>
  570.      * The default implementation does nothing, it may be specialized in subclasses.
  571.      * </p>
  572.      */
  573.     protected void afterIntegration() {
  574.         // do nothing by default
  575.     }

  576.     /** Get state vector dimension without additional parameters.
  577.      * @return state vector dimension without additional parameters.
  578.      */
  579.     public int getBasicDimension() {
  580.         return 7;

  581.     }

  582.     /** Get the integrator used by the propagator.
  583.      * @return the integrator.
  584.      */
  585.     protected FieldODEIntegrator<T> getIntegrator() {
  586.         return integrator;
  587.     }

  588.     /** Convert a state from mathematical world to space flight dynamics world.
  589.      * @param os mathematical state
  590.      * @return space flight dynamics state
  591.      */
  592.     private FieldSpacecraftState<T> convert(final FieldODEStateAndDerivative<T> os) {

  593.         FieldSpacecraftState<T> s =
  594.                         stateMapper.mapArrayToState(os.getTime(),
  595.                                                     os.getPrimaryState(),
  596.                                                     os.getPrimaryDerivative(),
  597.                                                     propagationType);
  598.         if (os.getNumberOfSecondaryStates() > 0) {
  599.             final T[] secondary           = os.getSecondaryState(1);
  600.             final T[] secondaryDerivative = os.getSecondaryDerivative(1);
  601.             for (final FieldAdditionalDerivativesProvider<T> equations : additionalDerivativesProviders) {
  602.                 final String name      = equations.getName();
  603.                 final int    offset    = secondaryOffsets.get(name);
  604.                 final int    dimension = equations.getDimension();
  605.                 s = s.addAdditionalState(name, Arrays.copyOfRange(secondary, offset, offset + dimension));
  606.                 s = s.addAdditionalStateDerivative(name, Arrays.copyOfRange(secondaryDerivative, offset, offset + dimension));
  607.             }
  608.         }
  609.         s = updateAdditionalStates(s);

  610.         return s;

  611.     }

  612.     /** Convert a state from space flight dynamics world to mathematical world.
  613.      * @param state space flight dynamics state
  614.      * @return mathematical state
  615.      */
  616.     private FieldODEStateAndDerivative<T> convert(final FieldSpacecraftState<T> state) {

  617.         // retrieve initial state
  618.         final T[] primary    = MathArrays.buildArray(getField(), getBasicDimension());
  619.         final T[] primaryDot = MathArrays.buildArray(getField(), getBasicDimension());
  620.         stateMapper.mapStateToArray(state, primary, primaryDot);

  621.         // secondary part of the ODE
  622.         final T[][] secondary           = secondary(state);
  623.         final T[][] secondaryDerivative = secondaryDerivative(state);

  624.         return new FieldODEStateAndDerivative<>(stateMapper.mapDateToDouble(state.getDate()),
  625.                                                 primary, primaryDot,
  626.                                                 secondary, secondaryDerivative);

  627.     }

  628.     /** Differential equations for the main state (orbit, attitude and mass).
  629.      * @param <T> type of the field element
  630.      */
  631.     public interface MainStateEquations<T extends CalculusFieldElement<T>> {

  632.         /**
  633.          * Initialize the equations at the start of propagation. This method will be
  634.          * called before any calls to {@link #computeDerivatives(FieldSpacecraftState)}.
  635.          *
  636.          * <p> The default implementation of this method does nothing.
  637.          *
  638.          * @param initialState initial state information at the start of propagation.
  639.          * @param target       date of propagation. Not equal to {@code
  640.          *                     initialState.getDate()}.
  641.          */
  642.         void init(FieldSpacecraftState<T> initialState, FieldAbsoluteDate<T> target);

  643.         /** Compute differential equations for main state.
  644.          * @param state current state
  645.          * @return derivatives of main state
  646.          */
  647.         T[] computeDerivatives(FieldSpacecraftState<T> state);

  648.     }

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

  651.         /** Main state equations. */
  652.         private final MainStateEquations<T> main;

  653.         /** Simple constructor.
  654.          * @param main main state equations
  655.          */
  656.         ConvertedMainStateEquations(final MainStateEquations<T> main) {
  657.             this.main = main;
  658.             calls = 0;
  659.         }

  660.         /** {@inheritDoc} */
  661.         public int getDimension() {
  662.             return getBasicDimension();
  663.         }

  664.         @Override
  665.         public void init(final T t0, final T[] y0, final T finalTime) {
  666.             // update space dynamics view
  667.             FieldSpacecraftState<T> initialState = stateMapper.mapArrayToState(t0, y0, null, PropagationType.MEAN);
  668.             initialState = updateAdditionalStates(initialState);
  669.             initialState = updateStatesFromAdditionalDerivativesIfKnown(initialState);
  670.             final FieldAbsoluteDate<T> target = stateMapper.mapDoubleToDate(finalTime);
  671.             main.init(initialState, target);
  672.             attitudeProviderForDerivatives = initializeAttitudeProviderForDerivatives();
  673.         }

  674.         /**
  675.          * Returns an updated version of the inputted state, with additional states from
  676.          * derivatives providers as given in the stored initial state.
  677.          * @param originalState input state
  678.          * @return new state
  679.          * @since 12.1
  680.          */
  681.         private FieldSpacecraftState<T> updateStatesFromAdditionalDerivativesIfKnown(final FieldSpacecraftState<T> originalState) {
  682.             FieldSpacecraftState<T> updatedState = originalState;
  683.             final FieldSpacecraftState<T> storedInitialState = getInitialState();
  684.             final T originalTime = stateMapper.mapDateToDouble(originalState.getDate());
  685.             if (storedInitialState != null && stateMapper.mapDateToDouble(storedInitialState.getDate()).subtract(originalTime).isZero()) {
  686.                 for (final FieldAdditionalDerivativesProvider<T> provider: additionalDerivativesProviders) {
  687.                     final String name = provider.getName();
  688.                     final T[] value = storedInitialState.getAdditionalState(name);
  689.                     updatedState = updatedState.addAdditionalState(name, value);
  690.                 }
  691.             }
  692.             return updatedState;
  693.         }

  694.         /** {@inheritDoc} */
  695.         public T[] computeDerivatives(final T t, final T[] y) {

  696.             // increment calls counter
  697.             ++calls;

  698.             // update space dynamics view
  699.             stateMapper.setAttitudeProvider(attitudeProviderForDerivatives);
  700.             FieldSpacecraftState<T> currentState = stateMapper.mapArrayToState(t, y, null, PropagationType.MEAN);
  701.             stateMapper.setAttitudeProvider(getAttitudeProvider());
  702.             currentState = updateAdditionalStates(currentState);

  703.             // compute main state differentials
  704.             return main.computeDerivatives(currentState);

  705.         }

  706.     }

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

  709.         /** Dimension of the combined additional states. */
  710.         private final int combinedDimension;

  711.         /** Simple constructor.
  712.          */
  713.         ConvertedSecondaryStateEquations() {
  714.             this.combinedDimension = secondaryOffsets.get(SECONDARY_DIMENSION);
  715.         }

  716.         /** {@inheritDoc} */
  717.         @Override
  718.         public int getDimension() {
  719.             return combinedDimension;
  720.         }

  721.         /** {@inheritDoc} */
  722.         @Override
  723.         public void init(final T t0, final T[] primary0,
  724.                          final T[] secondary0, final T finalTime) {
  725.             // update space dynamics view
  726.             final FieldSpacecraftState<T> initialState = convert(t0, primary0, null, secondary0);

  727.             final FieldAbsoluteDate<T> target = stateMapper.mapDoubleToDate(finalTime);
  728.             for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  729.                 provider.init(initialState, target);
  730.             }

  731.         }

  732.         /** {@inheritDoc} */
  733.         @Override
  734.         public T[] computeDerivatives(final T t, final T[] primary,
  735.                                       final T[] primaryDot, final T[] secondary) {

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

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

  742.             // gather the derivatives from all additional equations, taking care of dependencies
  743.             final T[] secondaryDot = MathArrays.buildArray(t.getField(), combinedDimension);
  744.             int yieldCount = 0;
  745.             while (!pending.isEmpty()) {
  746.                 final FieldAdditionalDerivativesProvider<T> equations = pending.remove();
  747.                 if (equations.yields(updated)) {
  748.                     // these equations have to wait for another set,
  749.                     // we put them again in the pending queue
  750.                     pending.add(equations);
  751.                     if (++yieldCount >= pending.size()) {
  752.                         // all pending equations yielded!, they probably need data not yet initialized
  753.                         // we let the propagation proceed, if these data are really needed right now
  754.                         // an appropriate exception will be triggered when caller tries to access them
  755.                         break;
  756.                     }
  757.                 } else {
  758.                     // we can use these equations right now
  759.                     final String                      name           = equations.getName();
  760.                     final int                         offset         = secondaryOffsets.get(name);
  761.                     final int                         dimension      = equations.getDimension();
  762.                     final FieldCombinedDerivatives<T> derivatives    = equations.combinedDerivatives(updated);
  763.                     final T[]                         additionalPart = derivatives.getAdditionalDerivatives();
  764.                     final T[]                         mainPart       = derivatives.getMainStateDerivativesIncrements();
  765.                     System.arraycopy(additionalPart, 0, secondaryDot, offset, dimension);
  766.                     updated = updated.addAdditionalStateDerivative(name, additionalPart);
  767.                     if (mainPart != null) {
  768.                         // this equation does change the main state derivatives
  769.                         for (int i = 0; i < mainPart.length; ++i) {
  770.                             primaryDot[i] = primaryDot[i].add(mainPart[i]);
  771.                         }
  772.                     }
  773.                     yieldCount = 0;
  774.                 }
  775.             }

  776.             return secondaryDot;

  777.         }

  778.         /** Convert mathematical view to space view.
  779.          * @param t current value of the independent <I>time</I> variable
  780.          * @param primary array containing the current value of the primary state vector
  781.          * @param primaryDot array containing the derivative of the primary state vector
  782.          * @param secondary array containing the current value of the secondary state vector
  783.          * @return space view of the state
  784.          */
  785.         private FieldSpacecraftState<T> convert(final T t, final T[] primary,
  786.                                                 final T[] primaryDot, final T[] secondary) {

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

  788.             for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  789.                 final String name      = provider.getName();
  790.                 final int    offset    = secondaryOffsets.get(name);
  791.                 final int    dimension = provider.getDimension();
  792.                 initialState = initialState.addAdditionalState(name, Arrays.copyOfRange(secondary, offset, offset + dimension));
  793.             }

  794.             return updateAdditionalStates(initialState);

  795.         }

  796.     }

  797.     /** Adapt an {@link org.orekit.propagation.events.FieldEventDetector<T>}
  798.      * to Hipparchus {@link org.hipparchus.ode.events.FieldODEEventDetector<T>} interface.
  799.      * @author Fabien Maussion
  800.      */
  801.     private class FieldAdaptedEventDetector implements FieldODEEventDetector<T> {

  802.         /** Underlying event detector. */
  803.         private final FieldEventDetector<T> detector;

  804.         /** Underlying event handler.
  805.          * @since 12.0
  806.          */
  807.         private final FieldEventHandler<T> handler;

  808.         /** Time of the previous call to g. */
  809.         private T lastT;

  810.         /** Value from the previous call to g. */
  811.         private T lastG;

  812.         /** Build a wrapped event detector.
  813.          * @param detector event detector to wrap
  814.         */
  815.         FieldAdaptedEventDetector(final FieldEventDetector<T> detector) {
  816.             this.detector = detector;
  817.             this.handler  = detector.getHandler();
  818.             this.lastT    = getField().getZero().add(Double.NaN);
  819.             this.lastG    = getField().getZero().add(Double.NaN);
  820.         }

  821.         /** {@inheritDoc} */
  822.         @Override
  823.         public FieldAdaptableInterval<T> getMaxCheckInterval() {
  824.             return s -> detector.getMaxCheckInterval().currentInterval(convert(s));
  825.         }

  826.         /** {@inheritDoc} */
  827.         @Override
  828.         public int getMaxIterationCount() {
  829.             return detector.getMaxIterationCount();
  830.         }

  831.         /** {@inheritDoc} */
  832.         @Override
  833.         public FieldBracketingNthOrderBrentSolver<T> getSolver() {
  834.             final T zero = detector.getThreshold().getField().getZero();
  835.             return new FieldBracketingNthOrderBrentSolver<>(zero, detector.getThreshold(), zero, 5);
  836.         }

  837.         /** {@inheritDoc} */
  838.         @Override
  839.         public void init(final FieldODEStateAndDerivative<T> s0, final T t) {
  840.             detector.init(convert(s0), stateMapper.mapDoubleToDate(t));
  841.             this.lastT = getField().getZero().add(Double.NaN);
  842.             this.lastG = getField().getZero().add(Double.NaN);
  843.         }

  844.         /** {@inheritDoc} */
  845.         public T g(final FieldODEStateAndDerivative<T> s) {
  846.             if (!Precision.equals(lastT.getReal(), s.getTime().getReal(), 0)) {
  847.                 lastT = s.getTime();
  848.                 lastG = detector.g(convert(s));
  849.             }
  850.             return lastG;
  851.         }

  852.         /** {@inheritDoc} */
  853.         public FieldODEEventHandler<T> getHandler() {

  854.             return new FieldODEEventHandler<T>() {

  855.                 /** {@inheritDoc} */
  856.                 public Action eventOccurred(final FieldODEStateAndDerivative<T> s,
  857.                                             final FieldODEEventDetector<T> d,
  858.                                             final boolean increasing) {
  859.                     return handler.eventOccurred(convert(s), detector, increasing);
  860.                 }

  861.                 /** {@inheritDoc} */
  862.                 @Override
  863.                 public FieldODEState<T> resetState(final FieldODEEventDetector<T> d,
  864.                                                    final FieldODEStateAndDerivative<T> s) {

  865.                     final FieldSpacecraftState<T> oldState = convert(s);
  866.                     final FieldSpacecraftState<T> newState = handler.resetState(detector, oldState);
  867.                     stateChanged(newState);

  868.                     // main part
  869.                     final T[] primary    = MathArrays.buildArray(getField(), s.getPrimaryStateDimension());
  870.                     stateMapper.mapStateToArray(newState, primary, null);

  871.                     // secondary part
  872.                     final T[][] secondary = MathArrays.buildArray(getField(), 1, additionalDerivativesProviders.size());
  873.                     for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  874.                         final String name      = provider.getName();
  875.                         final int    offset    = secondaryOffsets.get(name);
  876.                         final int    dimension = provider.getDimension();
  877.                         System.arraycopy(newState.getAdditionalState(name), 0, secondary[0], offset, dimension);
  878.                     }

  879.                     return new FieldODEState<>(newState.getDate().durationFrom(getStartDate()),
  880.                                                primary, secondary);
  881.                 }
  882.             };

  883.         }

  884.     }

  885.     /** Adapt an {@link org.orekit.propagation.sampling.FieldOrekitStepHandler<T>}
  886.      * to Hipparchus {@link FieldODEStepHandler<T>} interface.
  887.      * @author Luc Maisonobe
  888.      */
  889.     private class FieldAdaptedStepHandler implements FieldODEStepHandler<T> {

  890.         /** Underlying handler. */
  891.         private final FieldOrekitStepHandler<T> handler;

  892.         /** Build an instance.
  893.          * @param handler underlying handler to wrap
  894.          */
  895.         FieldAdaptedStepHandler(final FieldOrekitStepHandler<T> handler) {
  896.             this.handler = handler;
  897.         }

  898.         /** {@inheritDoc} */
  899.         @Override
  900.         public void init(final FieldODEStateAndDerivative<T> s0, final T t) {
  901.             handler.init(convert(s0), stateMapper.mapDoubleToDate(t));
  902.         }

  903.         /** {@inheritDoc} */
  904.         public void handleStep(final FieldODEStateInterpolator<T> interpolator) {
  905.             handler.handleStep(new FieldAdaptedStepInterpolator(interpolator));
  906.         }

  907.         /** {@inheritDoc} */
  908.         @Override
  909.         public void finish(final FieldODEStateAndDerivative<T> finalState) {
  910.             handler.finish(convert(finalState));
  911.         }

  912.     }

  913.     /** Adapt an {@link org.orekit.propagation.sampling.FieldOrekitStepInterpolator<T>}
  914.      * to Hipparchus {@link FieldODEStepInterpolator<T>} interface.
  915.      * @author Luc Maisonobe
  916.      */
  917.     private class FieldAdaptedStepInterpolator implements FieldOrekitStepInterpolator<T> {

  918.         /** Underlying raw rawInterpolator. */
  919.         private final FieldODEStateInterpolator<T> mathInterpolator;

  920.         /** Build an instance.
  921.          * @param mathInterpolator underlying raw interpolator
  922.          */
  923.         FieldAdaptedStepInterpolator(final FieldODEStateInterpolator<T> mathInterpolator) {
  924.             this.mathInterpolator = mathInterpolator;
  925.         }

  926.         /** {@inheritDoc}} */
  927.         @Override
  928.         public FieldSpacecraftState<T> getPreviousState() {
  929.             return convert(mathInterpolator.getPreviousState());
  930.         }

  931.         /** {@inheritDoc}} */
  932.         @Override
  933.         public FieldSpacecraftState<T> getCurrentState() {
  934.             return convert(mathInterpolator.getCurrentState());
  935.         }

  936.         /** {@inheritDoc}} */
  937.         @Override
  938.         public FieldSpacecraftState<T> getInterpolatedState(final FieldAbsoluteDate<T> date) {
  939.             return convert(mathInterpolator.getInterpolatedState(date.durationFrom(getStartDate())));
  940.         }

  941.         /** Check is integration direction is forward in date.
  942.          * @return true if integration is forward in date
  943.          */
  944.         public boolean isForward() {
  945.             return mathInterpolator.isForward();
  946.         }

  947.         /** {@inheritDoc}} */
  948.         @Override
  949.         public FieldAdaptedStepInterpolator restrictStep(final FieldSpacecraftState<T> newPreviousState,
  950.                                                          final FieldSpacecraftState<T> newCurrentState) {
  951.             try {
  952.                 final AbstractFieldODEStateInterpolator<T> aosi = (AbstractFieldODEStateInterpolator<T>) mathInterpolator;
  953.                 return new FieldAdaptedStepInterpolator(aosi.restrictStep(convert(newPreviousState),
  954.                                                                           convert(newCurrentState)));
  955.             } catch (ClassCastException cce) {
  956.                 // this should never happen
  957.                 throw new OrekitInternalError(cce);
  958.             }
  959.         }

  960.     }

  961.     /** Specialized step handler storing interpolators for ephemeris generation.
  962.      * @since 11.0
  963.      */
  964.     private class FieldStoringStepHandler implements FieldODEStepHandler<T>, FieldEphemerisGenerator<T> {

  965.         /** Underlying raw mathematical model. */
  966.         private FieldDenseOutputModel<T> model;

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

  969.         /** Generated ephemeris. */
  970.         private FieldBoundedPropagator<T> ephemeris;

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

  973.         /** Set the end date.
  974.          * @param endDate end date
  975.          */
  976.         public void setEndDate(final FieldAbsoluteDate<T> endDate) {
  977.             this.endDate = endDate;
  978.         }

  979.         /** {@inheritDoc} */
  980.         @Override
  981.         public void init(final FieldODEStateAndDerivative<T> s0, final T t) {
  982.             this.model = new FieldDenseOutputModel<>();
  983.             model.init(s0, t);

  984.             // ephemeris will be generated when last step is processed
  985.             this.ephemeris = null;

  986.             this.lastInterpolator = null;

  987.         }

  988.         /** {@inheritDoc} */
  989.         @Override
  990.         public FieldBoundedPropagator<T> getGeneratedEphemeris() {
  991.             // Each time we try to get the ephemeris, rebuild it using the last data.
  992.             buildEphemeris();
  993.             return ephemeris;
  994.         }

  995.         /** {@inheritDoc} */
  996.         @Override
  997.         public void handleStep(final FieldODEStateInterpolator<T> interpolator) {
  998.             model.handleStep(interpolator);
  999.             lastInterpolator = interpolator;
  1000.         }

  1001.         /** {@inheritDoc} */
  1002.         @Override
  1003.         public void finish(final FieldODEStateAndDerivative<T> finalState) {
  1004.             buildEphemeris();
  1005.         }

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

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

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

  1033.             // get the initial additional states that are not managed
  1034.             final FieldArrayDictionary<T> unmanaged = new FieldArrayDictionary<>(startDate.getField());
  1035.             for (final FieldArrayDictionary<T>.Entry initial : getInitialState().getAdditionalStatesValues().getData()) {
  1036.                 if (!isAdditionalStateManaged(initial.getKey())) {
  1037.                     // this additional state was in the initial state, but is unknown to the propagator
  1038.                     // we simply copy its initial value as is
  1039.                     unmanaged.put(initial.getKey(), initial.getValue());
  1040.                 }
  1041.             }

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

  1049.             // create the ephemeris
  1050.             ephemeris = new FieldIntegratedEphemeris<>(startDate, minDate, maxDate,
  1051.                                                        stateMapper, propagationType, model,
  1052.                                                        unmanaged, getAdditionalStateProviders(),
  1053.                                                        names, dimensions);

  1054.         }

  1055.     }

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

  1068.         /** Wrapped integrator. */
  1069.         private final FieldODEIntegrator<T> integrator;

  1070.         /** Initial event detectors list. */
  1071.         private final List<FieldODEEventDetector<T>> detectors;

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

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

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

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

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

  1095.         }

  1096.     }

  1097. }