FieldAbstractIntegratedPropagator.java

  1. /* Copyright 2002-2022 CS GROUP
  2.  * Licensed to CS GROUP (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.propagation.integration;

  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.Collection;
  21. import java.util.Collections;
  22. import java.util.HashMap;
  23. import java.util.LinkedList;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.Queue;

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  221.         return false;
  222.     }

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

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

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

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

  256.         secondaryOffsets.clear();

  257.     }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  349.             // set up events added by user
  350.             setUpUserEventDetectors();

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

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

  361.         }

  362.     }

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

  369.             initializePropagation();

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


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

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

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

  396.             afterIntegration();

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

  416.             if (resetAtEnd) {
  417.                 resetInitialState(finalState);
  418.                 setStartDate(finalState.getDate());
  419.             }

  420.             return finalState;

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

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

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

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

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

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

  451.     }

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

  458.         if (secondaryOffsets.isEmpty()) {
  459.             return null;
  460.         }

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

  468.         return secondary;

  469.     }

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

  476.         if (secondaryOffsets.isEmpty()) {
  477.             return null;
  478.         }

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

  486.         return secondaryDerivative;

  487.     }

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

  495.         final FieldExpandableODE<T> ode =
  496.                 new FieldExpandableODE<>(new ConvertedMainStateEquations(getMainStateEquations(integ)));

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

  501.         return ode;

  502.     }

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

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

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

  527.     }

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

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

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

  556.         return s;

  557.     }

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

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

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

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

  573.     }

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

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

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

  592.     }

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

  595.         /** Main state equations. */
  596.         private final MainStateEquations<T> main;

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

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

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

  618.             // increment calls counter
  619.             ++calls;

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

  623.             // compute main state differentials
  624.             return main.computeDerivatives(currentState);

  625.         }

  626.     }

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

  629.         /** Dimension of the combined additional states. */
  630.         private final int combinedDimension;

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

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

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

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

  651.         }

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

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

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

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

  688.             return secondaryDot;

  689.         }

  690.         /** Convert mathematical view to space view.
  691.          * @param t current value of the independent <I>time</I> variable
  692.          * @param primary array containing the current value of the primary state vector
  693.          * @param primaryDot array containing the derivative of the primary state vector
  694.          * @param secondary array containing the current value of the secondary state vector
  695.          * @return space view of the state
  696.          */
  697.         private FieldSpacecraftState<T> convert(final T t, final T[] primary,
  698.                                                 final T[] primaryDot, final T[] secondary) {

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

  700.             for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  701.                 final String name      = provider.getName();
  702.                 final int    offset    = secondaryOffsets.get(name);
  703.                 final int    dimension = provider.getDimension();
  704.                 initialState = initialState.addAdditionalState(name, Arrays.copyOfRange(secondary, offset, offset + dimension));
  705.             }

  706.             return updateAdditionalStates(initialState);

  707.         }

  708.     }

  709.     /** Adapt an {@link org.orekit.propagation.events.FieldEventDetector<T>}
  710.      * to Hipparchus {@link org.hipparchus.ode.events.FieldODEEventHandler<T>} interface.
  711.      * @param <T> class type for the generic version
  712.      * @author Fabien Maussion
  713.      */
  714.     private class FieldAdaptedEventDetector implements FieldODEEventHandler<T> {

  715.         /** Underlying event detector. */
  716.         private final FieldEventDetector<T> detector;

  717.         /** Time of the previous call to g. */
  718.         private T lastT;

  719.         /** Value from the previous call to g. */
  720.         private T lastG;

  721.         /** Build a wrapped event detector.
  722.          * @param detector event detector to wrap
  723.         */
  724.         FieldAdaptedEventDetector(final FieldEventDetector<T> detector) {
  725.             this.detector = detector;
  726.             this.lastT    = getField().getZero().add(Double.NaN);
  727.             this.lastG    = getField().getZero().add(Double.NaN);
  728.         }

  729.         /** {@inheritDoc} */
  730.         public void init(final FieldODEStateAndDerivative<T> s0, final T t) {
  731.             detector.init(convert(s0), stateMapper.mapDoubleToDate(t));
  732.             this.lastT = getField().getZero().add(Double.NaN);
  733.             this.lastG = getField().getZero().add(Double.NaN);
  734.         }

  735.         /** {@inheritDoc} */
  736.         public T g(final FieldODEStateAndDerivative<T> s) {
  737.             if (!Precision.equals(lastT.getReal(), s.getTime().getReal(), 0)) {
  738.                 lastT = s.getTime();
  739.                 lastG = detector.g(convert(s));
  740.             }
  741.             return lastG;
  742.         }

  743.         /** {@inheritDoc} */
  744.         public Action eventOccurred(final FieldODEStateAndDerivative<T> s, final boolean increasing) {
  745.             return detector.eventOccurred(convert(s), increasing);
  746.         }

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

  749.             final FieldSpacecraftState<T> oldState = convert(s);
  750.             final FieldSpacecraftState<T> newState = detector.resetState(oldState);
  751.             stateChanged(newState);

  752.             // main part
  753.             final T[] primary    = MathArrays.buildArray(getField(), s.getPrimaryStateDimension());
  754.             stateMapper.mapStateToArray(newState, primary, null);

  755.             // secondary part
  756.             final T[][] secondary = MathArrays.buildArray(getField(), 1, additionalDerivativesProviders.size());
  757.             for (final FieldAdditionalDerivativesProvider<T> provider : additionalDerivativesProviders) {
  758.                 final String name      = provider.getName();
  759.                 final int    offset    = secondaryOffsets.get(name);
  760.                 final int    dimension = provider.getDimension();
  761.                 System.arraycopy(newState.getAdditionalState(name), 0, secondary[0], offset, dimension);
  762.             }

  763.             return new FieldODEState<>(newState.getDate().durationFrom(getStartDate()),
  764.                                        primary, secondary);
  765.         }

  766.     }

  767.     /** Adapt an {@link org.orekit.propagation.sampling.FieldOrekitStepHandler<T>}
  768.      * to Hipparchus {@link FieldODEStepHandler<T>} interface.
  769.      * @author Luc Maisonobe
  770.      */
  771.     private class FieldAdaptedStepHandler implements FieldODEStepHandler<T> {

  772.         /** Underlying handler. */
  773.         private final FieldOrekitStepHandler<T> handler;

  774.         /** Build an instance.
  775.          * @param handler underlying handler to wrap
  776.          */
  777.         FieldAdaptedStepHandler(final FieldOrekitStepHandler<T> handler) {
  778.             this.handler = handler;
  779.         }

  780.         /** {@inheritDoc} */
  781.         public void init(final FieldODEStateAndDerivative<T> s0, final T t) {
  782.             handler.init(convert(s0), stateMapper.mapDoubleToDate(t));
  783.         }

  784.         /** {@inheritDoc} */
  785.         public void handleStep(final FieldODEStateInterpolator<T> interpolator) {
  786.             handler.handleStep(new FieldAdaptedStepInterpolator(interpolator));
  787.         }

  788.         /** {@inheritDoc} */
  789.         @Override
  790.         public void finish(final FieldODEStateAndDerivative<T> finalState) {
  791.             handler.finish(convert(finalState));
  792.         }

  793.     }

  794.     /** Adapt an {@link org.orekit.propagation.sampling.FieldOrekitStepInterpolator<T>}
  795.      * to Hipparchus {@link FieldODEStepInterpolator<T>} interface.
  796.      * @author Luc Maisonobe
  797.      */
  798.     private class FieldAdaptedStepInterpolator implements FieldOrekitStepInterpolator<T> {

  799.         /** Underlying raw rawInterpolator. */
  800.         private final FieldODEStateInterpolator<T> mathInterpolator;

  801.         /** Build an instance.
  802.          * @param mathInterpolator underlying raw interpolator
  803.          */
  804.         FieldAdaptedStepInterpolator(final FieldODEStateInterpolator<T> mathInterpolator) {
  805.             this.mathInterpolator = mathInterpolator;
  806.         }

  807.         /** {@inheritDoc}} */
  808.         @Override
  809.         public FieldSpacecraftState<T> getPreviousState() {
  810.             return convert(mathInterpolator.getPreviousState());
  811.         }

  812.         /** {@inheritDoc}} */
  813.         @Override
  814.         public FieldSpacecraftState<T> getCurrentState() {
  815.             return convert(mathInterpolator.getCurrentState());
  816.         }

  817.         /** {@inheritDoc}} */
  818.         @Override
  819.         public FieldSpacecraftState<T> getInterpolatedState(final FieldAbsoluteDate<T> date) {
  820.             return convert(mathInterpolator.getInterpolatedState(date.durationFrom(getStartDate())));
  821.         }

  822.         /** Check is integration direction is forward in date.
  823.          * @return true if integration is forward in date
  824.          */
  825.         public boolean isForward() {
  826.             return mathInterpolator.isForward();
  827.         }

  828.         /** {@inheritDoc}} */
  829.         @Override
  830.         public FieldAdaptedStepInterpolator restrictStep(final FieldSpacecraftState<T> newPreviousState,
  831.                                                          final FieldSpacecraftState<T> newCurrentState) {
  832.             try {
  833.                 final AbstractFieldODEStateInterpolator<T> aosi = (AbstractFieldODEStateInterpolator<T>) mathInterpolator;
  834.                 return new FieldAdaptedStepInterpolator(aosi.restrictStep(convert(newPreviousState),
  835.                                                                           convert(newCurrentState)));
  836.             } catch (ClassCastException cce) {
  837.                 // this should never happen
  838.                 throw new OrekitInternalError(cce);
  839.             }
  840.         }

  841.     }

  842.     /** Specialized step handler storing interpolators for ephemeris generation.
  843.      * @since 11.0
  844.      */
  845.     private class FieldStoringStepHandler implements FieldODEStepHandler<T>, FieldEphemerisGenerator<T> {

  846.         /** Underlying raw mathematical model. */
  847.         private FieldDenseOutputModel<T> model;

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

  850.         /** Generated ephemeris. */
  851.         private FieldBoundedPropagator<T> ephemeris;

  852.         /** Set the end date.
  853.          * @param endDate end date
  854.          */
  855.         public void setEndDate(final FieldAbsoluteDate<T> endDate) {
  856.             this.endDate = endDate;
  857.         }

  858.         /** {@inheritDoc} */
  859.         @Override
  860.         public void init(final FieldODEStateAndDerivative<T> s0, final T t) {
  861.             this.model = new FieldDenseOutputModel<>();
  862.             model.init(s0, t);

  863.             // ephemeris will be generated when last step is processed
  864.             this.ephemeris = null;

  865.         }

  866.         /** {@inheritDoc} */
  867.         @Override
  868.         public FieldBoundedPropagator<T> getGeneratedEphemeris() {
  869.             return ephemeris;
  870.         }

  871.         /** {@inheritDoc} */
  872.         @Override
  873.         public void handleStep(final FieldODEStateInterpolator<T> interpolator) {
  874.             model.handleStep(interpolator);
  875.         }

  876.         /** {@inheritDoc} */
  877.         @Override
  878.         public void finish(final FieldODEStateAndDerivative<T> finalState) {

  879.             // set up the boundary dates
  880.             final T tI = model.getInitialTime();
  881.             final T tF = model.getFinalTime();
  882.             // tI is almost? always zero
  883.             final FieldAbsoluteDate<T> startDate =
  884.                             stateMapper.mapDoubleToDate(tI);
  885.             final FieldAbsoluteDate<T> finalDate =
  886.                             stateMapper.mapDoubleToDate(tF, this.endDate);
  887.             final FieldAbsoluteDate<T> minDate;
  888.             final FieldAbsoluteDate<T> maxDate;
  889.             if (tF.getReal() < tI.getReal()) {
  890.                 minDate = finalDate;
  891.                 maxDate = startDate;
  892.             } else {
  893.                 minDate = startDate;
  894.                 maxDate = finalDate;
  895.             }

  896.             // get the initial additional states that are not managed
  897.             final FieldArrayDictionary<T> unmanaged = new FieldArrayDictionary<>(startDate.getField());
  898.             for (final FieldArrayDictionary<T>.Entry initial : getInitialState().getAdditionalStatesValues().getData()) {
  899.                 if (!isAdditionalStateManaged(initial.getKey())) {
  900.                     // this additional state was in the initial state, but is unknown to the propagator
  901.                     // we simply copy its initial value as is
  902.                     unmanaged.put(initial.getKey(), initial.getValue());
  903.                 }
  904.             }

  905.             // get the names of additional states managed by differential equations
  906.             final String[] names = new String[additionalDerivativesProviders.size()];
  907.             for (int i = 0; i < names.length; ++i) {
  908.                 names[i] = additionalDerivativesProviders.get(i).getName();
  909.             }

  910.             // create the ephemeris
  911.             ephemeris = new FieldIntegratedEphemeris<>(startDate, minDate, maxDate,
  912.                                                        stateMapper, propagationType, model,
  913.                                                        unmanaged, getAdditionalStateProviders(), names);

  914.         }

  915.     }

  916.     /** Wrapper for resetting an integrator handlers.
  917.      * <p>
  918.      * This class is intended to be used in a try-with-resource statement.
  919.      * If propagator-specific event handlers and step handlers are added to
  920.      * the integrator in the try block, they will be removed automatically
  921.      * when leaving the block, so the integrator only keep its own handlers
  922.      * between calls to {@link AbstractIntegratedPropagator#propagate(AbsoluteDate, AbsoluteDate).
  923.      * </p>
  924.      * @param <T> the type of the field elements
  925.      * @since 11.0
  926.      */
  927.     private static class IntegratorResetter<T extends CalculusFieldElement<T>> implements AutoCloseable {

  928.         /** Wrapped integrator. */
  929.         private final FieldODEIntegrator<T> integrator;

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

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

  934.         /** Simple constructor.
  935.          * @param integrator wrapped integrator
  936.          */
  937.         IntegratorResetter(final FieldODEIntegrator<T> integrator) {
  938.             this.integrator                  = integrator;
  939.             this.eventHandlersConfigurations = new ArrayList<>(integrator.getEventHandlersConfigurations());
  940.             this.stepHandlers                = new ArrayList<>(integrator.getStepHandlers());
  941.         }

  942.         /** {@inheritDoc}
  943.          * <p>
  944.          * Reset event handlers and step handlers back to the initial list
  945.          * </p>
  946.          */
  947.         @Override
  948.         public void close() {

  949.             // reset event handlers
  950.             integrator.clearEventHandlers();
  951.             eventHandlersConfigurations.forEach(c -> integrator.addEventHandler(c.getEventHandler(),
  952.                                                                                 c.getMaxCheckInterval(),
  953.                                                                                 c.getConvergence().getReal(),
  954.                                                                                 c.getMaxIterationCount(),
  955.                                                                                 c.getSolver()));

  956.             // reset step handlers
  957.             integrator.clearStepHandlers();
  958.             stepHandlers.forEach(stepHandler -> integrator.addStepHandler(stepHandler));

  959.         }

  960.     }

  961. }