FieldAbstractIntegratedPropagator.java

  1. /* Copyright 2002-2021 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.Collection;
  20. import java.util.Collections;
  21. import java.util.HashMap;
  22. import java.util.List;
  23. import java.util.Map;

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


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

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

  68.     /** Step handlers dedicated to ephemeris generation. */
  69.     private final List<FieldStoringStepHandler> generators;

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

  72.     /** Additional equations. */
  73.     private List<FieldAdditionalEquations<T>> additionalEquations;

  74.     /** Counter for differential equations calls. */
  75.     private int calls;

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

  78.     /** Equations mapper. */
  79.     private FieldEquationsMapper<T> equationsMapper;

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

  82.     /** Type of orbit to output (mean or osculating) <br/>
  83.      * <p>
  84.      * This is used only in the case of semianalitical propagators where there is a clear separation between
  85.      * mean and short periodic elements. It is ignored by the Numerical propagator.
  86.      * </p>
  87.      */
  88.     private PropagationType propagationType;

  89.     /** Build a new instance.
  90.      * @param integrator numerical integrator to use for propagation.
  91.      * @param propagationType type of orbit to output (mean or osculating).
  92.      * @param field Field used by default
  93.      */
  94.     protected FieldAbstractIntegratedPropagator(final Field<T> field, final FieldODEIntegrator<T> integrator, final PropagationType propagationType) {
  95.         super(field);
  96.         detectors            = new ArrayList<>();
  97.         generators           = new ArrayList<>();
  98.         additionalEquations  = new ArrayList<>();
  99.         this.integrator      = integrator;
  100.         this.propagationType = propagationType;
  101.         this.resetAtEnd      = true;
  102.     }

  103.     /** Allow/disallow resetting the initial state at end of propagation.
  104.      * <p>
  105.      * By default, at the end of the propagation, the propagator resets the initial state
  106.      * to the final state, thus allowing a new propagation to be started from there without
  107.      * recomputing the part already performed. Calling this method with {@code resetAtEnd} set
  108.      * to false changes prevents such reset.
  109.      * </p>
  110.      * @param resetAtEnd if true, at end of each propagation, the {@link
  111.      * #getInitialState() initial state} will be reset to the final state of
  112.      * the propagation, otherwise the initial state will be preserved
  113.      * @since 9.0
  114.      */
  115.     public void setResetAtEnd(final boolean resetAtEnd) {
  116.         this.resetAtEnd = resetAtEnd;
  117.     }

  118.     /** Initialize the mapper.
  119.      * @param field Field used by default
  120.      */
  121.     protected void initMapper(final Field<T> field) {
  122.         final T zero = field.getZero();
  123.         stateMapper = createMapper(null, zero.add(Double.NaN), null, null, null, null);
  124.     }

  125.     /**  {@inheritDoc} */
  126.     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
  127.         super.setAttitudeProvider(attitudeProvider);
  128.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  129.                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  130.                                    attitudeProvider, stateMapper.getFrame());
  131.     }

  132.     /** Set propagation orbit type.
  133.      * @param orbitType orbit type to use for propagation
  134.      */
  135.     protected void setOrbitType(final OrbitType orbitType) {
  136.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  137.                                    orbitType, stateMapper.getPositionAngleType(),
  138.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  139.     }

  140.     /** Get propagation parameter type.
  141.      * @return orbit type used for propagation
  142.      */
  143.     protected OrbitType getOrbitType() {
  144.         return stateMapper.getOrbitType();
  145.     }

  146.     /** Check if only the mean elements should be used in a semianalitical propagation.
  147.      * @return {@link PropagationType MEAN} if only mean elements have to be used or
  148.      *         {@link PropagationType OSCULATING} if osculating elements have to be also used.
  149.      */
  150.     protected PropagationType isMeanOrbit() {
  151.         return propagationType;
  152.     }

  153.     /** Set position angle type.
  154.      * <p>
  155.      * The position parameter type is meaningful only if {@link
  156.      * #getOrbitType() propagation orbit type}
  157.      * support it. As an example, it is not meaningful for propagation
  158.      * in {@link OrbitType#CARTESIAN Cartesian} parameters.
  159.      * </p>
  160.      * @param positionAngleType angle type to use for propagation
  161.      */
  162.     protected void setPositionAngleType(final PositionAngle positionAngleType) {
  163.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  164.                                    stateMapper.getOrbitType(), positionAngleType,
  165.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  166.     }

  167.     /** Get propagation parameter type.
  168.      * @return angle type to use for propagation
  169.      */
  170.     protected PositionAngle getPositionAngleType() {
  171.         return stateMapper.getPositionAngleType();
  172.     }

  173.     /** Set the central attraction coefficient μ.
  174.      * @param mu central attraction coefficient (m³/s²)
  175.      */
  176.     public void setMu(final T mu) {
  177.         stateMapper = createMapper(stateMapper.getReferenceDate(), mu,
  178.                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  179.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  180.     }

  181.     /** Get the central attraction coefficient μ.
  182.      * @return mu central attraction coefficient (m³/s²)
  183.      * @see #setMu(CalculusFieldElement)
  184.      */
  185.     public T getMu() {
  186.         return stateMapper.getMu();
  187.     }

  188.     /** Get the number of calls to the differential equations computation method.
  189.      * <p>The number of calls is reset each time the {@link #propagate(FieldAbsoluteDate)}
  190.      * method is called.</p>
  191.      * @return number of calls to the differential equations computation method
  192.      */
  193.     public int getCalls() {
  194.         return calls;
  195.     }

  196.     /** {@inheritDoc} */
  197.     @Override
  198.     public boolean isAdditionalStateManaged(final String name) {

  199.         // first look at already integrated states
  200.         if (super.isAdditionalStateManaged(name)) {
  201.             return true;
  202.         }

  203.         // then look at states we integrate ourselves
  204.         for (final FieldAdditionalEquations<T> equation : additionalEquations) {
  205.             if (equation.getName().equals(name)) {
  206.                 return true;
  207.             }
  208.         }

  209.         return false;
  210.     }

  211.     /** {@inheritDoc} */
  212.     @Override
  213.     public String[] getManagedAdditionalStates() {
  214.         final String[] alreadyIntegrated = super.getManagedAdditionalStates();
  215.         final String[] managed = new String[alreadyIntegrated.length + additionalEquations.size()];
  216.         System.arraycopy(alreadyIntegrated, 0, managed, 0, alreadyIntegrated.length);
  217.         for (int i = 0; i < additionalEquations.size(); ++i) {
  218.             managed[i + alreadyIntegrated.length] = additionalEquations.get(i).getName();
  219.         }
  220.         return managed;
  221.     }

  222.     /** Add a set of user-specified equations to be integrated along with the orbit propagation.
  223.      * @param additional additional equations
  224.      */
  225.     public void addAdditionalEquations(final FieldAdditionalEquations<T> additional) {

  226.         // check if the name is already used
  227.         if (isAdditionalStateManaged(additional.getName())) {
  228.             // this set of equations is already registered, complain
  229.             throw new OrekitException(OrekitMessages.ADDITIONAL_STATE_NAME_ALREADY_IN_USE,
  230.                                       additional.getName());
  231.         }

  232.         // this is really a new set of equations, add it
  233.         additionalEquations.add(additional);

  234.     }

  235.     /** {@inheritDoc} */
  236.     public <D extends FieldEventDetector<T>> void addEventDetector(final D detector) {
  237.         detectors.add(detector);
  238.     }

  239.     /** {@inheritDoc} */
  240.     public Collection<FieldEventDetector<T>> getEventsDetectors() {
  241.         return Collections.unmodifiableCollection(detectors);
  242.     }

  243.     /** {@inheritDoc} */
  244.     public void clearEventsDetectors() {
  245.         detectors.clear();
  246.     }

  247.     /** Set up all user defined event detectors.
  248.      */
  249.     protected void setUpUserEventDetectors() {
  250.         for (final FieldEventDetector<T> detector : detectors) {
  251.             setUpEventDetector(integrator, detector);
  252.         }
  253.     }

  254.     /** Wrap an Orekit event detector and register it to the integrator.
  255.      * @param integ integrator into which event detector should be registered
  256.      * @param detector event detector to wrap
  257.      */
  258.     protected void setUpEventDetector(final FieldODEIntegrator<T> integ, final FieldEventDetector<T> detector) {
  259.         integ.addEventHandler(new FieldAdaptedEventDetector(detector),
  260.                               detector.getMaxCheckInterval().getReal(),
  261.                               detector.getThreshold().getReal(),
  262.                               detector.getMaxIterationCount());
  263.     }

  264.     /** {@inheritDoc} */
  265.     @Override
  266.     public FieldEphemerisGenerator<T> getEphemerisGenerator() {
  267.         final FieldStoringStepHandler storingHandler = new FieldStoringStepHandler();
  268.         generators.add(storingHandler);
  269.         return storingHandler;
  270.     }

  271.     /** Create a mapper between raw double components and spacecraft state.
  272.     /** Simple constructor.
  273.      * <p>
  274.      * The position parameter type is meaningful only if {@link
  275.      * #getOrbitType() propagation orbit type}
  276.      * support it. As an example, it is not meaningful for propagation
  277.      * in {@link OrbitType#CARTESIAN Cartesian} parameters.
  278.      * </p>
  279.      * @param referenceDate reference date
  280.      * @param mu central attraction coefficient (m³/s²)
  281.      * @param orbitType orbit type to use for mapping
  282.      * @param positionAngleType angle type to use for propagation
  283.      * @param attitudeProvider attitude provider
  284.      * @param frame inertial frame
  285.      * @return new mapper
  286.      */
  287.     protected abstract FieldStateMapper<T> createMapper(FieldAbsoluteDate<T> referenceDate, T mu,
  288.                                                         OrbitType orbitType, PositionAngle positionAngleType,
  289.                                                         AttitudeProvider attitudeProvider, Frame frame);

  290.     /** Get the differential equations to integrate (for main state only).
  291.      * @param integ numerical integrator to use for propagation.
  292.      * @return differential equations for main state
  293.      */
  294.     protected abstract MainStateEquations<T> getMainStateEquations(FieldODEIntegrator<T> integ);

  295.     /** {@inheritDoc} */
  296.     public FieldSpacecraftState<T> propagate(final FieldAbsoluteDate<T> target) {
  297.         if (getStartDate() == null) {
  298.             if (getInitialState() == null) {
  299.                 throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  300.             }
  301.             setStartDate(getInitialState().getDate());
  302.         }
  303.         return propagate(getStartDate(), target);
  304.     }

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

  307.         if (getInitialState() == null) {
  308.             throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  309.         }

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

  312.             if (!tStart.equals(getInitialState().getDate())) {
  313.                 // if propagation start date is not initial date,
  314.                 // propagate from initial to start date without event detection
  315.                 integrateDynamics(tStart);
  316.             }

  317.             // set up events added by user
  318.             setUpUserEventDetectors();

  319.             // set up step handlers
  320.             for (final FieldOrekitStepHandler<T> handler : getMultiplexer().getHandlers()) {
  321.                 integrator.addStepHandler(new FieldAdaptedStepHandler(handler));
  322.             }
  323.             for (final FieldStoringStepHandler generator : generators) {
  324.                 generator.setEndDate(tEnd);
  325.                 integrator.addStepHandler(generator);
  326.             }

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

  329.         }

  330.     }

  331.     /** Propagation with or without event detection.
  332.      * @param tEnd target date to which orbit should be propagated
  333.      * @return state at end of propagation
  334.      */
  335.     private FieldSpacecraftState<T> integrateDynamics(final FieldAbsoluteDate<T> tEnd) {
  336.         try {

  337.             initializePropagation();

  338.             if (getInitialState().getDate().equals(tEnd)) {
  339.                 // don't extrapolate
  340.                 return getInitialState();
  341.             }
  342.             // space dynamics view
  343.             stateMapper = createMapper(getInitialState().getDate(), stateMapper.getMu(),
  344.                                        stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  345.                                        stateMapper.getAttitudeProvider(), getInitialState().getFrame());


  346.             // set propagation orbit type
  347.             //final FieldOrbit<T> initialOrbit = stateMapper.getOrbitType().convertType(getInitialState().getOrbit());
  348.             if (Double.isNaN(getMu().getReal())) {
  349.                 setMu(getInitialState().getMu());
  350.             }
  351.             if (getInitialState().getMass().getReal() <= 0.0) {
  352.                 throw new OrekitException(OrekitMessages.SPACECRAFT_MASS_BECOMES_NEGATIVE,
  353.                                                getInitialState().getMass());
  354.             }

  355.             // convert space flight dynamics API to math API
  356.             final FieldSpacecraftState<T> initialIntegrationState = getInitialIntegrationState();
  357.             final FieldODEState<T> mathInitialState = createInitialState(initialIntegrationState);
  358.             final FieldExpandableODE<T> mathODE = createODE(integrator, mathInitialState);
  359.             equationsMapper = mathODE.getMapper();

  360.             // mathematical integration
  361.             final FieldODEStateAndDerivative<T> mathFinalState;
  362.             beforeIntegration(initialIntegrationState, tEnd);
  363.             mathFinalState = integrator.integrate(mathODE, mathInitialState,
  364.                                                   tEnd.durationFrom(getInitialState().getDate()));

  365.             afterIntegration();

  366.             // get final state
  367.             FieldSpacecraftState<T> finalState =
  368.                             stateMapper.mapArrayToState(stateMapper.mapDoubleToDate(mathFinalState.getTime(), tEnd),
  369.                                                         mathFinalState.getPrimaryState(),
  370.                                                         mathFinalState.getPrimaryDerivative(),
  371.                                                         propagationType);
  372.             finalState = updateAdditionalStates(finalState);
  373.             for (int i = 0; i < additionalEquations.size(); ++i) {
  374.                 final T[] secondary = mathFinalState.getSecondaryState(i + 1);
  375.                 finalState = finalState.addAdditionalState(additionalEquations.get(i).getName(),
  376.                                                            secondary);
  377.             }
  378.             if (resetAtEnd) {
  379.                 resetInitialState(finalState);
  380.                 setStartDate(finalState.getDate());
  381.             }

  382.             return finalState;

  383.         } catch (OrekitException pe) {
  384.             throw pe;
  385.         } catch (MathIllegalArgumentException miae) {
  386.             throw OrekitException.unwrap(miae);
  387.         } catch (MathIllegalStateException mise) {
  388.             throw OrekitException.unwrap(mise);
  389.         }
  390.     }

  391.     /** Get the initial state for integration.
  392.      * @return initial state for integration
  393.      */
  394.     protected FieldSpacecraftState<T> getInitialIntegrationState() {
  395.         return getInitialState();
  396.     }

  397.     /** Create an initial state.
  398.      * @param initialState initial state in flight dynamics world
  399.      * @return initial state in mathematics world
  400.      */
  401.     private FieldODEState<T> createInitialState(final FieldSpacecraftState<T> initialState) {

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

  405.         // secondary part of the ODE
  406.         final T[][] secondary = MathArrays.buildArray(initialState.getA().getField(), additionalEquations.size(), -1);
  407.         for (int i = 0; i < additionalEquations.size(); ++i) {
  408.             final FieldAdditionalEquations<T> additional = additionalEquations.get(i);
  409.             final T[] addState = getInitialState().getAdditionalState(additional.getName());
  410.             secondary[i] = MathArrays.buildArray(initialState.getA().getField(), addState.length);
  411.             for (int j = 0; j < addState.length; j++) {
  412.                 secondary[i][j] = addState[j];
  413.             }
  414.         }

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

  416.     }

  417.     /** Create an ODE with all equations.
  418.      * @param integ numerical integrator to use for propagation.
  419.      * @param mathInitialState initial state
  420.      * @return a new ode
  421.      */
  422.     private FieldExpandableODE<T> createODE(final FieldODEIntegrator<T> integ,
  423.                                     final FieldODEState<T> mathInitialState) {

  424.         final FieldExpandableODE<T> ode =
  425.                 new FieldExpandableODE<>(new ConvertedMainStateEquations(getMainStateEquations(integ)));

  426.         // secondary part of the ODE
  427.         for (int i = 0; i < additionalEquations.size(); ++i) {
  428.             final FieldAdditionalEquations<T> additional = additionalEquations.get(i);
  429.             final FieldSecondaryODE<T> secondary =
  430.                     new ConvertedSecondaryStateEquations(additional,
  431.                                                          mathInitialState.getSecondaryStateDimension(i + 1));
  432.             ode.addSecondaryEquations(secondary);
  433.         }

  434.         return ode;

  435.     }

  436.     /** Method called just before integration.
  437.      * <p>
  438.      * The default implementation does nothing, it may be specialized in subclasses.
  439.      * </p>
  440.      * @param initialState initial state
  441.      * @param tEnd target date at which state should be propagated
  442.      */
  443.     protected void beforeIntegration(final FieldSpacecraftState<T> initialState,
  444.                                      final FieldAbsoluteDate<T> tEnd) {
  445.         // do nothing by default
  446.     }

  447.     /** Method called just after integration.
  448.      * <p>
  449.      * The default implementation does nothing, it may be specialized in subclasses.
  450.      * </p>
  451.      */
  452.     protected void afterIntegration() {
  453.         // do nothing by default
  454.     }

  455.     /** Get state vector dimension without additional parameters.
  456.      * @return state vector dimension without additional parameters.
  457.      */
  458.     public int getBasicDimension() {
  459.         return 7;

  460.     }

  461.     /** Get the integrator used by the propagator.
  462.      * @return the integrator.
  463.      */
  464.     protected FieldODEIntegrator<T> getIntegrator() {
  465.         return integrator;
  466.     }

  467.     /** Get a complete state with all additional equations.
  468.      * @param t current value of the independent <I>time</I> variable
  469.      * @param ts array containing the current value of the state vector
  470.      * @param tsDot array containing the current value of the state vector derivative
  471.      * @return complete state
  472.      */
  473.     private FieldSpacecraftState<T> getCompleteState(final T t, final T[] ts, final T[] tsDot) {

  474.         // main state
  475.         FieldSpacecraftState<T> state = stateMapper.mapArrayToState(t, ts, tsDot, PropagationType.MEAN);  //not sure of the mean orbit, should be true
  476.         // pre-integrated additional states
  477.         state = updateAdditionalStates(state);

  478.         // additional states integrated here
  479.         if (!additionalEquations.isEmpty()) {

  480.             for (int i = 0; i < additionalEquations.size(); ++i) {
  481.                 state = state.addAdditionalState(additionalEquations.get(i).getName(),
  482.                                                  equationsMapper.extractEquationData(i + 1, ts));
  483.             }

  484.         }

  485.         return state;

  486.     }

  487.     /** Get the interpolated state.
  488.      * @param os mathematical state
  489.      * @return interpolated state at the current interpolation date
  490.                        * the date
  491.      * @see #getInterpolatedDate()
  492.      * @see #setInterpolatedDate(FieldAbsoluteDate<T>)
  493.      */
  494.     private FieldSpacecraftState<T> convert(final FieldODEStateAndDerivative<T> os) {
  495.         try {

  496.             FieldSpacecraftState<T> s =
  497.                     stateMapper.mapArrayToState(os.getTime(),
  498.                                                 os.getPrimaryState(),
  499.                                                 os.getPrimaryDerivative(),
  500.                                                 propagationType);
  501.             s = updateAdditionalStates(s);
  502.             for (int i = 0; i < additionalEquations.size(); ++i) {
  503.                 final T[] secondary = os.getSecondaryState(i + 1);
  504.                 s = s.addAdditionalState(additionalEquations.get(i).getName(), secondary);
  505.             }

  506.             return s;

  507.         } catch (OrekitException oe) {
  508.             throw new OrekitException(oe);
  509.         }
  510.     }

  511.     /** Convert a state from space flight dynamics world to mathematical world.
  512.      * @param state space flight dynamics state
  513.      * @return mathematical state
  514.      */
  515.     private FieldODEStateAndDerivative<T> convert(final FieldSpacecraftState<T> state) {

  516.         // retrieve initial state
  517.         final T[] primary    = MathArrays.buildArray(getField(), getBasicDimension());
  518.         final T[] primaryDot = MathArrays.buildArray(getField(), getBasicDimension());
  519.         stateMapper.mapStateToArray(state, primary, primaryDot);

  520.         // secondary part of the ODE
  521.         final T[][] secondary    = MathArrays.buildArray(getField(), additionalEquations.size(), -1);
  522.         for (int i = 0; i < additionalEquations.size(); ++i) {
  523.             final FieldAdditionalEquations<T> additional = additionalEquations.get(i);
  524.             secondary[i] = state.getAdditionalState(additional.getName());
  525.         }

  526.         return new FieldODEStateAndDerivative<>(stateMapper.mapDateToDouble(state.getDate()),
  527.                                                 primary, primaryDot,
  528.                                                 secondary, null);

  529.     }

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

  532.         /**
  533.          * Initialize the equations at the start of propagation. This method will be
  534.          * called before any calls to {@link #computeDerivatives(FieldSpacecraftState)}.
  535.          *
  536.          * <p> The default implementation of this method does nothing.
  537.          *
  538.          * @param initialState initial state information at the start of propagation.
  539.          * @param target       date of propagation. Not equal to {@code
  540.          *                     initialState.getDate()}.
  541.          */
  542.         void init(FieldSpacecraftState<T> initialState, FieldAbsoluteDate<T> target);

  543.         /** Compute differential equations for main state.
  544.          * @param state current state
  545.          * @return derivatives of main state
  546.          */
  547.         T[] computeDerivatives(FieldSpacecraftState<T> state);

  548.     }

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

  551.         /** Main state equations. */
  552.         private final MainStateEquations<T> main;

  553.         /** Simple constructor.
  554.          * @param main main state equations
  555.          */
  556.         ConvertedMainStateEquations(final MainStateEquations<T> main) {
  557.             this.main = main;
  558.             calls = 0;
  559.         }

  560.         /** {@inheritDoc} */
  561.         public int getDimension() {
  562.             return getBasicDimension();
  563.         }

  564.         @Override
  565.         public void init(final T t0, final T[] y0, final T finalTime) {
  566.             // update space dynamics view
  567.             FieldSpacecraftState<T> initialState = stateMapper.mapArrayToState(t0, y0, null, PropagationType.MEAN);
  568.             initialState = updateAdditionalStates(initialState);
  569.             final FieldAbsoluteDate<T> target = stateMapper.mapDoubleToDate(finalTime);
  570.             main.init(initialState, target);
  571.         }
  572.         /** {@inheritDoc} */
  573.         public T[] computeDerivatives(final T t, final T[] y) {

  574.             // increment calls counter
  575.             ++calls;

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

  579.             // compute main state differentials
  580.             return main.computeDerivatives(currentState);

  581.         }

  582.     }

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

  585.         /** Additional equations. */
  586.         private final FieldAdditionalEquations<T> equations;

  587.         /** Dimension of the additional state. */
  588.         private final int dimension;

  589.         /** Simple constructor.
  590.          * @param equations additional equations
  591.          * @param dimension dimension of the additional state
  592.          */
  593.         ConvertedSecondaryStateEquations(final FieldAdditionalEquations<T> equations,
  594.                                          final int dimension) {
  595.             this.equations = equations;
  596.             this.dimension = dimension;
  597.         }

  598.         /** {@inheritDoc} */
  599.         @Override
  600.         public int getDimension() {
  601.             return dimension;
  602.         }

  603.         /** {@inheritDoc} */
  604.         @Override
  605.         public void init(final T t0, final T[] primary0,
  606.                          final T[] secondary0, final T finalTime) {
  607.             // update space dynamics view
  608.             FieldSpacecraftState<T> initialState = stateMapper.mapArrayToState(t0, primary0, null, PropagationType.MEAN);
  609.             initialState = updateAdditionalStates(initialState);
  610.             initialState = initialState.addAdditionalState(equations.getName(), secondary0);
  611.             final FieldAbsoluteDate<T> target = stateMapper.mapDoubleToDate(finalTime);
  612.             equations.init(initialState, target);
  613.         }

  614.         /** {@inheritDoc} */
  615.         @Override
  616.         public T[] computeDerivatives(final T t, final T[] primary,
  617.                                       final T[] primaryDot, final T[] secondary) {

  618.             // update space dynamics view
  619.             FieldSpacecraftState<T> currentState = stateMapper.mapArrayToState(t, primary, primaryDot, PropagationType.MEAN);
  620.             currentState = updateAdditionalStates(currentState);
  621.             currentState = currentState.addAdditionalState(equations.getName(), secondary);

  622.             // compute additional derivatives
  623.             final T[] secondaryDot = MathArrays.buildArray(getField(), secondary.length);
  624.             final T[] additionalMainDot =
  625.                             equations.computeDerivatives(currentState, secondaryDot);
  626.             if (additionalMainDot != null) {
  627.                 // the additional equations have an effect on main equations
  628.                 for (int i = 0; i < additionalMainDot.length; ++i) {
  629.                     primaryDot[i] = primaryDot[i].add(additionalMainDot[i]);
  630.                 }
  631.             }

  632.             return secondaryDot;

  633.         }

  634.     }

  635.     /** Adapt an {@link org.orekit.propagation.events.FieldEventDetector<T>}
  636.      * to Hipparchus {@link org.hipparchus.ode.events.FieldODEEventHandler<T>} interface.
  637.      * @param <T> class type for the generic version
  638.      * @author Fabien Maussion
  639.      */
  640.     private class FieldAdaptedEventDetector implements FieldODEEventHandler<T> {

  641.         /** Underlying event detector. */
  642.         private final FieldEventDetector<T> detector;

  643.         /** Time of the previous call to g. */
  644.         private T lastT;

  645.         /** Value from the previous call to g. */
  646.         private T lastG;

  647.         /** Build a wrapped event detector.
  648.          * @param detector event detector to wrap
  649.         */
  650.         FieldAdaptedEventDetector(final FieldEventDetector<T> detector) {
  651.             this.detector = detector;
  652.             this.lastT    = getField().getZero().add(Double.NaN);
  653.             this.lastG    = getField().getZero().add(Double.NaN);
  654.         }

  655.         /** {@inheritDoc} */
  656.         public void init(final FieldODEStateAndDerivative<T> s0, final T t) {
  657.             detector.init(getCompleteState(s0.getTime(), s0.getCompleteState(), s0.getCompleteDerivative()),
  658.                           stateMapper.mapDoubleToDate(t));
  659.             this.lastT = getField().getZero().add(Double.NaN);
  660.             this.lastG = getField().getZero().add(Double.NaN);
  661.         }

  662.         /** {@inheritDoc} */
  663.         public T g(final FieldODEStateAndDerivative<T> s) {
  664.             if (!Precision.equals(lastT.getReal(), s.getTime().getReal(), 0)) {
  665.                 lastT = s.getTime();
  666.                 lastG = detector.g(getCompleteState(s.getTime(), s.getCompleteState(), s.getCompleteDerivative()));
  667.             }
  668.             return lastG;
  669.         }

  670.         /** {@inheritDoc} */
  671.         public Action eventOccurred(final FieldODEStateAndDerivative<T> s, final boolean increasing) {
  672.             return detector.eventOccurred(
  673.                     getCompleteState(
  674.                             s.getTime(),
  675.                             s.getCompleteState(),
  676.                             s.getCompleteDerivative()),
  677.                     increasing);
  678.         }

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

  681.             final FieldSpacecraftState<T> oldState = getCompleteState(s.getTime(), s.getCompleteState(), s.getCompleteDerivative());
  682.             final FieldSpacecraftState<T> newState = detector.resetState(oldState);
  683.             stateChanged(newState);

  684.             // main part
  685.             final T[] primary    = MathArrays.buildArray(getField(), s.getPrimaryStateDimension());
  686.             stateMapper.mapStateToArray(newState, primary, null);

  687.             // secondary part
  688.             final T[][] secondary    = MathArrays.buildArray(getField(), additionalEquations.size(), -1);

  689.             for (int i = 0; i < additionalEquations.size(); ++i) {
  690.                 final FieldAdditionalEquations<T> additional = additionalEquations.get(i);
  691.                 final T[] NState = newState.getAdditionalState(additional.getName());
  692.                 secondary[i] = MathArrays.buildArray(getField(), NState.length);
  693.                 for (int j = 0; j < NState.length; j++) {
  694.                     secondary[i][j] = NState[j];
  695.                 }
  696.             }

  697.             return new FieldODEState<>(newState.getDate().durationFrom(getStartDate()),
  698.                                        primary, secondary);
  699.         }

  700.     }

  701.     /** Adapt an {@link org.orekit.propagation.sampling.FieldOrekitStepHandler<T>}
  702.      * to Hipparchus {@link FieldODEStepHandler<T>} interface.
  703.      * @author Luc Maisonobe
  704.      */
  705.     private class FieldAdaptedStepHandler implements FieldODEStepHandler<T> {

  706.         /** Underlying handler. */
  707.         private final FieldOrekitStepHandler<T> handler;

  708.         /** Build an instance.
  709.          * @param handler underlying handler to wrap
  710.          */
  711.         FieldAdaptedStepHandler(final FieldOrekitStepHandler<T> handler) {
  712.             this.handler = handler;
  713.         }

  714.         /** {@inheritDoc} */
  715.         public void init(final FieldODEStateAndDerivative<T> s0, final T t) {
  716.             handler.init(getCompleteState(s0.getTime(), s0.getCompleteState(), s0.getCompleteDerivative()),
  717.                          stateMapper.mapDoubleToDate(t));
  718.         }

  719.         /** {@inheritDoc} */
  720.         public void handleStep(final FieldODEStateInterpolator<T> interpolator) {
  721.             handler.handleStep(new FieldAdaptedStepInterpolator(interpolator));
  722.         }

  723.         /** {@inheritDoc} */
  724.         @Override
  725.         public void finish(final FieldODEStateAndDerivative<T> finalState) {
  726.             handler.finish(convert(finalState));
  727.         }

  728.     }

  729.     /** Adapt an {@link org.orekit.propagation.sampling.FieldOrekitStepInterpolator<T>}
  730.      * to Hipparchus {@link FieldODEStepInterpolator<T>} interface.
  731.      * @author Luc Maisonobe
  732.      */
  733.     private class FieldAdaptedStepInterpolator implements FieldOrekitStepInterpolator<T> {

  734.         /** Underlying raw rawInterpolator. */
  735.         private final FieldODEStateInterpolator<T> mathInterpolator;

  736.         /** Build an instance.
  737.          * @param mathInterpolator underlying raw interpolator
  738.          */
  739.         FieldAdaptedStepInterpolator(final FieldODEStateInterpolator<T> mathInterpolator) {
  740.             this.mathInterpolator = mathInterpolator;
  741.         }

  742.         /** {@inheritDoc}} */
  743.         @Override
  744.         public FieldSpacecraftState<T> getPreviousState() {
  745.             return convert(mathInterpolator.getPreviousState());
  746.         }

  747.         /** {@inheritDoc}} */
  748.         @Override
  749.         public FieldSpacecraftState<T> getCurrentState() {
  750.             return convert(mathInterpolator.getCurrentState());
  751.         }

  752.         /** {@inheritDoc}} */
  753.         @Override
  754.         public FieldSpacecraftState<T> getInterpolatedState(final FieldAbsoluteDate<T> date) {
  755.             return convert(mathInterpolator.getInterpolatedState(date.durationFrom(getStartDate())));
  756.         }

  757.         /** Check is integration direction is forward in date.
  758.          * @return true if integration is forward in date
  759.          */
  760.         public boolean isForward() {
  761.             return mathInterpolator.isForward();
  762.         }

  763.         /** {@inheritDoc}} */
  764.         @Override
  765.         public FieldAdaptedStepInterpolator restrictStep(final FieldSpacecraftState<T> newPreviousState,
  766.                                                          final FieldSpacecraftState<T> newCurrentState) {
  767.             try {
  768.                 final AbstractFieldODEStateInterpolator<T> aosi = (AbstractFieldODEStateInterpolator<T>) mathInterpolator;
  769.                 return new FieldAdaptedStepInterpolator(aosi.restrictStep(convert(newPreviousState),
  770.                                                                           convert(newCurrentState)));
  771.             } catch (ClassCastException cce) {
  772.                 // this should never happen
  773.                 throw new OrekitInternalError(cce);
  774.             }
  775.         }

  776.     }

  777.     /** Specialized step handler storing interpolators for ephemeris generation.
  778.      * @since 11.0
  779.      */
  780.     private class FieldStoringStepHandler implements FieldODEStepHandler<T>, FieldEphemerisGenerator<T> {

  781.         /** Underlying raw mathematical model. */
  782.         private FieldDenseOutputModel<T> model;

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

  785.         /** Generated ephemeris. */
  786.         private FieldBoundedPropagator<T> ephemeris;

  787.         /** Set the end date.
  788.          * @param endDate end date
  789.          */
  790.         public void setEndDate(final FieldAbsoluteDate<T> endDate) {
  791.             this.endDate = endDate;
  792.         }

  793.         /** {@inheritDoc} */
  794.         @Override
  795.         public void init(final FieldODEStateAndDerivative<T> s0, final T t) {
  796.             this.model = new FieldDenseOutputModel<>();
  797.             model.init(s0, t);

  798.             // ephemeris will be generated when last step is processed
  799.             this.ephemeris = null;

  800.         }

  801.         /** {@inheritDoc} */
  802.         @Override
  803.         public FieldBoundedPropagator<T> getGeneratedEphemeris() {
  804.             return ephemeris;
  805.         }

  806.         /** {@inheritDoc} */
  807.         @Override
  808.         public void handleStep(final FieldODEStateInterpolator<T> interpolator) {
  809.             model.handleStep(interpolator);
  810.         }

  811.         /** {@inheritDoc} */
  812.         @Override
  813.         public void finish(final FieldODEStateAndDerivative<T> finalState) {

  814.             // set up the boundary dates
  815.             final T tI = model.getInitialTime();
  816.             final T tF = model.getFinalTime();
  817.             // tI is almost? always zero
  818.             final FieldAbsoluteDate<T> startDate =
  819.                             stateMapper.mapDoubleToDate(tI);
  820.             final FieldAbsoluteDate<T> finalDate =
  821.                             stateMapper.mapDoubleToDate(tF, this.endDate);
  822.             final FieldAbsoluteDate<T> minDate;
  823.             final FieldAbsoluteDate<T> maxDate;
  824.             if (tF.getReal() < tI.getReal()) {
  825.                 minDate = finalDate;
  826.                 maxDate = startDate;
  827.             } else {
  828.                 minDate = startDate;
  829.                 maxDate = finalDate;
  830.             }

  831.             // get the initial additional states that are not managed
  832.             final Map<String, T[]> unmanaged = new HashMap<String, T[]>();
  833.             for (final Map.Entry<String, T[]> initial : getInitialState().getAdditionalStates().entrySet()) {
  834.                 if (!isAdditionalStateManaged(initial.getKey())) {
  835.                     // this additional state was in the initial state, but is unknown to the propagator
  836.                     // we simply copy its initial value as is
  837.                     unmanaged.put(initial.getKey(), initial.getValue());
  838.                 }
  839.             }

  840.             // get the names of additional states managed by differential equations
  841.             final String[] names = new String[additionalEquations.size()];
  842.             for (int i = 0; i < names.length; ++i) {
  843.                 names[i] = additionalEquations.get(i).getName();
  844.             }

  845.             // create the ephemeris
  846.             ephemeris = new FieldIntegratedEphemeris<>(startDate, minDate, maxDate,
  847.                                                        stateMapper, propagationType, model, unmanaged,
  848.                                                        getAdditionalStateProviders(), names);

  849.         }

  850.     }

  851.     /** Wrapper for resetting an integrator handlers.
  852.      * <p>
  853.      * This class is intended to be used in a try-with-resource statement.
  854.      * If propagator-specific event handlers and step handlers are added to
  855.      * the integrator in the try block, they will be removed automatically
  856.      * when leaving the block, so the integrator only keep its own handlers
  857.      * between calls to {@link AbstractIntegratedPropagator#propagate(AbsoluteDate, AbsoluteDate).
  858.      * </p>
  859.  * @param <T> the type of the field elements
  860.      * @since 11.0
  861.      */
  862.     private static class IntegratorResetter<T extends CalculusFieldElement<T>> implements AutoCloseable {

  863.         /** Wrapped integrator. */
  864.         private final FieldODEIntegrator<T> integrator;

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

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

  869.         /** Simple constructor.
  870.          * @param integrator wrapped integrator
  871.          */
  872.         IntegratorResetter(final FieldODEIntegrator<T> integrator) {
  873.             this.integrator                  = integrator;
  874.             this.eventHandlersConfigurations = new ArrayList<>(integrator.getEventHandlersConfigurations());
  875.             this.stepHandlers                = new ArrayList<>(integrator.getStepHandlers());
  876.         }

  877.         /** {@inheritDoc}
  878.          * <p>
  879.          * Reset event handlers and step handlers back to the initial list
  880.          * </p>
  881.          */
  882.         @Override
  883.         public void close() {

  884.             // reset event handlers
  885.             integrator.clearEventHandlers();
  886.             eventHandlersConfigurations.forEach(c -> integrator.addEventHandler(c.getEventHandler(),
  887.                                                                                 c.getMaxCheckInterval(),
  888.                                                                                 c.getConvergence().getReal(),
  889.                                                                                 c.getMaxIterationCount(),
  890.                                                                                 c.getSolver()));

  891.             // reset step handlers
  892.             integrator.clearStepHandlers();
  893.             stepHandlers.forEach(stepHandler -> integrator.addStepHandler(stepHandler));

  894.         }

  895.     }

  896. }