AbstractIntegratedPropagator.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.exception.MathRuntimeException;
  25. import org.hipparchus.ode.DenseOutputModel;
  26. import org.hipparchus.ode.EquationsMapper;
  27. import org.hipparchus.ode.ExpandableODE;
  28. import org.hipparchus.ode.ODEIntegrator;
  29. import org.hipparchus.ode.ODEState;
  30. import org.hipparchus.ode.ODEStateAndDerivative;
  31. import org.hipparchus.ode.OrdinaryDifferentialEquation;
  32. import org.hipparchus.ode.SecondaryODE;
  33. import org.hipparchus.ode.events.Action;
  34. import org.hipparchus.ode.events.EventHandlerConfiguration;
  35. import org.hipparchus.ode.events.ODEEventHandler;
  36. import org.hipparchus.ode.sampling.AbstractODEStateInterpolator;
  37. import org.hipparchus.ode.sampling.ODEStateInterpolator;
  38. import org.hipparchus.ode.sampling.ODEStepHandler;
  39. import org.hipparchus.util.Precision;
  40. import org.orekit.attitudes.AttitudeProvider;
  41. import org.orekit.errors.OrekitException;
  42. import org.orekit.errors.OrekitInternalError;
  43. import org.orekit.errors.OrekitMessages;
  44. import org.orekit.frames.Frame;
  45. import org.orekit.orbits.OrbitType;
  46. import org.orekit.orbits.PositionAngle;
  47. import org.orekit.propagation.AbstractPropagator;
  48. import org.orekit.propagation.BoundedPropagator;
  49. import org.orekit.propagation.EphemerisGenerator;
  50. import org.orekit.propagation.PropagationType;
  51. import org.orekit.propagation.SpacecraftState;
  52. import org.orekit.propagation.events.EventDetector;
  53. import org.orekit.propagation.sampling.OrekitStepHandler;
  54. import org.orekit.propagation.sampling.OrekitStepInterpolator;
  55. import org.orekit.time.AbsoluteDate;


  56. /** Common handling of {@link org.orekit.propagation.Propagator Propagator}
  57.  *  methods for both numerical and semi-analytical propagators.
  58.  *  @author Luc Maisonobe
  59.  */
  60. public abstract class AbstractIntegratedPropagator extends AbstractPropagator {

  61.     /** Event detectors not related to force models. */
  62.     private final List<EventDetector> detectors;

  63.     /** Step handlers dedicated to ephemeris generation. */
  64.     private final List<StoringStepHandler> generators;

  65.     /** Integrator selected by the user for the orbital extrapolation process. */
  66.     private final ODEIntegrator integrator;

  67.     /** Additional equations. */
  68.     private List<AdditionalEquations> additionalEquations;

  69.     /** Counter for differential equations calls. */
  70.     private int calls;

  71.     /** Mapper between raw double components and space flight dynamics objects. */
  72.     private StateMapper stateMapper;

  73.     /** Equations mapper. */
  74.     private EquationsMapper equationsMapper;

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

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

  84.     /** Build a new instance.
  85.      * @param integrator numerical integrator to use for propagation.
  86.      * @param propagationType type of orbit to output (mean or osculating).
  87.      */
  88.     protected AbstractIntegratedPropagator(final ODEIntegrator integrator, final PropagationType propagationType) {
  89.         detectors            = new ArrayList<>();
  90.         generators           = new ArrayList<>();
  91.         additionalEquations  = new ArrayList<>();
  92.         this.integrator      = integrator;
  93.         this.propagationType = propagationType;
  94.         this.resetAtEnd      = true;
  95.     }

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

  111.     /** Initialize the mapper. */
  112.     protected void initMapper() {
  113.         stateMapper = createMapper(null, Double.NaN, null, null, null, null);
  114.     }

  115.     /**  {@inheritDoc} */
  116.     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
  117.         super.setAttitudeProvider(attitudeProvider);
  118.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  119.                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  120.                                    attitudeProvider, stateMapper.getFrame());
  121.     }

  122.     /** Set propagation orbit type.
  123.      * @param orbitType orbit type to use for propagation, null for
  124.      * propagating using {@link org.orekit.utils.AbsolutePVCoordinates AbsolutePVCoordinates}
  125.      * rather than {@link org.orekit.orbits Orbit}
  126.      */
  127.     protected void setOrbitType(final OrbitType orbitType) {
  128.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  129.                                    orbitType, stateMapper.getPositionAngleType(),
  130.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  131.     }

  132.     /** Get propagation parameter type.
  133.      * @return orbit type used for propagation, null for
  134.      * propagating using {@link org.orekit.utils.AbsolutePVCoordinates AbsolutePVCoordinates}
  135.      * rather than {@link org.orekit.orbits Orbit}
  136.      */
  137.     protected OrbitType getOrbitType() {
  138.         return stateMapper.getOrbitType();
  139.     }

  140.     /** Check if only the mean elements should be used in a semianalitical propagation.
  141.      * @return {@link PropagationType MEAN} if only mean elements have to be used or
  142.      *         {@link PropagationType OSCULATING} if osculating elements have to be also used.
  143.      */
  144.     protected PropagationType isMeanOrbit() {
  145.         return propagationType;
  146.     }

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

  161.     /** Get propagation parameter type.
  162.      * @return angle type to use for propagation
  163.      */
  164.     protected PositionAngle getPositionAngleType() {
  165.         return stateMapper.getPositionAngleType();
  166.     }

  167.     /** Set the central attraction coefficient μ.
  168.      * @param mu central attraction coefficient (m³/s²)
  169.      */
  170.     public void setMu(final double mu) {
  171.         stateMapper = createMapper(stateMapper.getReferenceDate(), mu,
  172.                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  173.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  174.     }

  175.     /** Get the central attraction coefficient μ.
  176.      * @return mu central attraction coefficient (m³/s²)
  177.      * @see #setMu(double)
  178.      */
  179.     public double getMu() {
  180.         return stateMapper.getMu();
  181.     }

  182.     /** Get the number of calls to the differential equations computation method.
  183.      * <p>The number of calls is reset each time the {@link #propagate(AbsoluteDate)}
  184.      * method is called.</p>
  185.      * @return number of calls to the differential equations computation method
  186.      */
  187.     public int getCalls() {
  188.         return calls;
  189.     }

  190.     /** {@inheritDoc} */
  191.     @Override
  192.     public boolean isAdditionalStateManaged(final String name) {

  193.         // first look at already integrated states
  194.         if (super.isAdditionalStateManaged(name)) {
  195.             return true;
  196.         }

  197.         // then look at states we integrate ourselves
  198.         for (final AdditionalEquations equation : additionalEquations) {
  199.             if (equation.getName().equals(name)) {
  200.                 return true;
  201.             }
  202.         }

  203.         return false;
  204.     }

  205.     /** {@inheritDoc} */
  206.     @Override
  207.     public String[] getManagedAdditionalStates() {
  208.         final String[] alreadyIntegrated = super.getManagedAdditionalStates();
  209.         final String[] managed = new String[alreadyIntegrated.length + additionalEquations.size()];
  210.         System.arraycopy(alreadyIntegrated, 0, managed, 0, alreadyIntegrated.length);
  211.         for (int i = 0; i < additionalEquations.size(); ++i) {
  212.             managed[i + alreadyIntegrated.length] = additionalEquations.get(i).getName();
  213.         }
  214.         return managed;
  215.     }

  216.     /** Add a set of user-specified equations to be integrated along with the orbit propagation.
  217.      * @param additional additional equations
  218.      */
  219.     public void addAdditionalEquations(final AdditionalEquations additional) {

  220.         // check if the name is already used
  221.         if (isAdditionalStateManaged(additional.getName())) {
  222.             // this set of equations is already registered, complain
  223.             throw new OrekitException(OrekitMessages.ADDITIONAL_STATE_NAME_ALREADY_IN_USE,
  224.                                       additional.getName());
  225.         }

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

  228.     }

  229.     /** {@inheritDoc} */
  230.     public void addEventDetector(final EventDetector detector) {
  231.         detectors.add(detector);
  232.     }

  233.     /** {@inheritDoc} */
  234.     public Collection<EventDetector> getEventsDetectors() {
  235.         return Collections.unmodifiableCollection(detectors);
  236.     }

  237.     /** {@inheritDoc} */
  238.     public void clearEventsDetectors() {
  239.         detectors.clear();
  240.     }

  241.     /** Set up all user defined event detectors.
  242.      */
  243.     protected void setUpUserEventDetectors() {
  244.         for (final EventDetector detector : detectors) {
  245.             setUpEventDetector(integrator, detector);
  246.         }
  247.     }

  248.     /** Wrap an Orekit event detector and register it to the integrator.
  249.      * @param integ integrator into which event detector should be registered
  250.      * @param detector event detector to wrap
  251.      */
  252.     protected void setUpEventDetector(final ODEIntegrator integ, final EventDetector detector) {
  253.         integ.addEventHandler(new AdaptedEventDetector(detector),
  254.                               detector.getMaxCheckInterval(),
  255.                               detector.getThreshold(),
  256.                               detector.getMaxIterationCount());
  257.     }

  258.     /** {@inheritDoc} */
  259.     @Override
  260.     public EphemerisGenerator getEphemerisGenerator() {
  261.         final StoringStepHandler storingHandler = new StoringStepHandler();
  262.         generators.add(storingHandler);
  263.         return storingHandler;
  264.     }

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

  284.     /** Get the differential equations to integrate (for main state only).
  285.      * @param integ numerical integrator to use for propagation.
  286.      * @return differential equations for main state
  287.      */
  288.     protected abstract MainStateEquations getMainStateEquations(ODEIntegrator integ);

  289.     /** {@inheritDoc} */
  290.     public SpacecraftState propagate(final AbsoluteDate target) {
  291.         if (getStartDate() == null) {
  292.             if (getInitialState() == null) {
  293.                 throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  294.             }
  295.             setStartDate(getInitialState().getDate());
  296.         }
  297.         return propagate(getStartDate(), target);
  298.     }

  299.     /** {@inheritDoc} */
  300.     public SpacecraftState propagate(final AbsoluteDate tStart, final AbsoluteDate tEnd) {

  301.         if (getInitialState() == null) {
  302.             throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  303.         }

  304.         // make sure the integrator will be reset properly even if we change its events handlers and step handlers
  305.         try (IntegratorResetter resetter = new IntegratorResetter(integrator)) {

  306.             if (!tStart.equals(getInitialState().getDate())) {
  307.                 // if propagation start date is not initial date,
  308.                 // propagate from initial to start date without event detection
  309.                 integrateDynamics(tStart);
  310.             }

  311.             // set up events added by user
  312.             setUpUserEventDetectors();

  313.             // set up step handlers
  314.             for (final OrekitStepHandler handler : getMultiplexer().getHandlers()) {
  315.                 integrator.addStepHandler(new AdaptedStepHandler(handler));
  316.             }
  317.             for (final StoringStepHandler generator : generators) {
  318.                 generator.setEndDate(tEnd);
  319.                 integrator.addStepHandler(generator);
  320.             }

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

  323.         }

  324.     }

  325.     /** Propagation with or without event detection.
  326.      * @param tEnd target date to which orbit should be propagated
  327.      * @return state at end of propagation
  328.      */
  329.     private SpacecraftState integrateDynamics(final AbsoluteDate tEnd) {
  330.         try {

  331.             initializePropagation();

  332.             if (getInitialState().getDate().equals(tEnd)) {
  333.                 // don't extrapolate
  334.                 return getInitialState();
  335.             }

  336.             // space dynamics view
  337.             stateMapper = createMapper(getInitialState().getDate(), stateMapper.getMu(),
  338.                                        stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  339.                                        stateMapper.getAttitudeProvider(), getInitialState().getFrame());


  340.             if (Double.isNaN(getMu())) {
  341.                 setMu(getInitialState().getMu());
  342.             }

  343.             if (getInitialState().getMass() <= 0.0) {
  344.                 throw new OrekitException(OrekitMessages.SPACECRAFT_MASS_BECOMES_NEGATIVE,
  345.                                           getInitialState().getMass());
  346.             }

  347.             // convert space flight dynamics API to math API
  348.             final SpacecraftState initialIntegrationState = getInitialIntegrationState();
  349.             final ODEState mathInitialState = createInitialState(initialIntegrationState);
  350.             final ExpandableODE mathODE = createODE(integrator, mathInitialState);
  351.             equationsMapper = mathODE.getMapper();

  352.             // mathematical integration
  353.             final ODEStateAndDerivative mathFinalState;
  354.             beforeIntegration(initialIntegrationState, tEnd);
  355.             mathFinalState = integrator.integrate(mathODE, mathInitialState,
  356.                                                   tEnd.durationFrom(getInitialState().getDate()));
  357.             afterIntegration();

  358.             // get final state
  359.             SpacecraftState finalState =
  360.                             stateMapper.mapArrayToState(stateMapper.mapDoubleToDate(mathFinalState.getTime(),
  361.                                                                                     tEnd),
  362.                                                         mathFinalState.getPrimaryState(),
  363.                                                         mathFinalState.getPrimaryDerivative(),
  364.                                                         propagationType);

  365.             finalState = updateAdditionalStates(finalState);
  366.             for (int i = 0; i < additionalEquations.size(); ++i) {
  367.                 final double[] secondary = mathFinalState.getSecondaryState(i + 1);
  368.                 finalState = finalState.addAdditionalState(additionalEquations.get(i).getName(),
  369.                                                            secondary);
  370.             }
  371.             if (resetAtEnd) {
  372.                 resetInitialState(finalState);
  373.                 setStartDate(finalState.getDate());
  374.             }

  375.             return finalState;

  376.         } catch (MathRuntimeException mre) {
  377.             throw OrekitException.unwrap(mre);
  378.         }
  379.     }

  380.     /** Get the initial state for integration.
  381.      * @return initial state for integration
  382.      */
  383.     protected SpacecraftState getInitialIntegrationState() {
  384.         return getInitialState();
  385.     }

  386.     /** Create an initial state.
  387.      * @param initialState initial state in flight dynamics world
  388.      * @return initial state in mathematics world
  389.      */
  390.     private ODEState createInitialState(final SpacecraftState initialState) {

  391.         // retrieve initial state
  392.         final double[] primary  = new double[getBasicDimension()];
  393.         stateMapper.mapStateToArray(initialState, primary, null);

  394.         // secondary part of the ODE
  395.         final double[][] secondary = new double[additionalEquations.size()][];
  396.         for (int i = 0; i < additionalEquations.size(); ++i) {
  397.             final AdditionalEquations additional = additionalEquations.get(i);
  398.             secondary[i] = initialState.getAdditionalState(additional.getName());
  399.         }

  400.         return new ODEState(0.0, primary, secondary);

  401.     }

  402.     /** Create an ODE with all equations.
  403.      * @param integ numerical integrator to use for propagation.
  404.      * @param mathInitialState initial state
  405.      * @return a new ode
  406.      */
  407.     private ExpandableODE createODE(final ODEIntegrator integ,
  408.                                     final ODEState mathInitialState) {

  409.         final ExpandableODE ode =
  410.                 new ExpandableODE(new ConvertedMainStateEquations(getMainStateEquations(integ)));

  411.         // secondary part of the ODE
  412.         for (int i = 0; i < additionalEquations.size(); ++i) {
  413.             final AdditionalEquations additional = additionalEquations.get(i);
  414.             final SecondaryODE secondary =
  415.                     new ConvertedSecondaryStateEquations(additional,
  416.                                                          mathInitialState.getSecondaryStateDimension(i + 1));
  417.             ode.addSecondaryEquations(secondary);
  418.         }

  419.         return ode;

  420.     }

  421.     /** Method called just before integration.
  422.      * <p>
  423.      * The default implementation does nothing, it may be specialized in subclasses.
  424.      * </p>
  425.      * @param initialState initial state
  426.      * @param tEnd target date at which state should be propagated
  427.      */
  428.     protected void beforeIntegration(final SpacecraftState initialState,
  429.                                      final AbsoluteDate tEnd) {
  430.         // do nothing by default
  431.     }

  432.     /** Method called just after integration.
  433.      * <p>
  434.      * The default implementation does nothing, it may be specialized in subclasses.
  435.      * </p>
  436.      */
  437.     protected void afterIntegration() {
  438.         // do nothing by default
  439.     }

  440.     /** Get state vector dimension without additional parameters.
  441.      * @return state vector dimension without additional parameters.
  442.      */
  443.     public int getBasicDimension() {
  444.         return 7;

  445.     }

  446.     /** Get the integrator used by the propagator.
  447.      * @return the integrator.
  448.      */
  449.     protected ODEIntegrator getIntegrator() {
  450.         return integrator;
  451.     }

  452.     /** Get a complete state with all additional equations.
  453.      * @param t current value of the independent <I>time</I> variable
  454.      * @param y array containing the current value of the state vector
  455.      * @param yDot array containing the current value of the state vector derivative
  456.      * @return complete state
  457.      */
  458.     private SpacecraftState getCompleteState(final double t, final double[] y, final double[] yDot) {

  459.         // main state
  460.         SpacecraftState state = stateMapper.mapArrayToState(t, y, yDot, propagationType);

  461.         // pre-integrated additional states
  462.         state = updateAdditionalStates(state);

  463.         // additional states integrated here
  464.         if (!additionalEquations.isEmpty()) {

  465.             for (int i = 0; i < additionalEquations.size(); ++i) {
  466.                 state = state.addAdditionalState(additionalEquations.get(i).getName(),
  467.                                                  equationsMapper.extractEquationData(i + 1, y));
  468.             }

  469.         }

  470.         return state;

  471.     }

  472.     /** Convert a state from mathematical world to space flight dynamics world.
  473.      * @param os mathematical state
  474.      * @return space flight dynamics state
  475.      */
  476.     private SpacecraftState convert(final ODEStateAndDerivative os) {

  477.         SpacecraftState s =
  478.                         stateMapper.mapArrayToState(os.getTime(),
  479.                                                     os.getPrimaryState(),
  480.                                                     os.getPrimaryDerivative(),
  481.                                                     propagationType);
  482.         s = updateAdditionalStates(s);
  483.         for (int i = 0; i < additionalEquations.size(); ++i) {
  484.             final double[] secondary = os.getSecondaryState(i + 1);
  485.             s = s.addAdditionalState(additionalEquations.get(i).getName(), secondary);
  486.         }

  487.         return s;

  488.     }

  489.     /** Convert a state from space flight dynamics world to mathematical world.
  490.      * @param state space flight dynamics state
  491.      * @return mathematical state
  492.      */
  493.     private ODEStateAndDerivative convert(final SpacecraftState state) {

  494.         // retrieve initial state
  495.         final double[] primary    = new double[getBasicDimension()];
  496.         final double[] primaryDot = new double[getBasicDimension()];
  497.         stateMapper.mapStateToArray(state, primary, primaryDot);

  498.         // secondary part of the ODE
  499.         final double[][] secondary    = new double[additionalEquations.size()][];
  500.         for (int i = 0; i < additionalEquations.size(); ++i) {
  501.             final AdditionalEquations additional = additionalEquations.get(i);
  502.             secondary[i] = state.getAdditionalState(additional.getName());
  503.         }

  504.         return new ODEStateAndDerivative(stateMapper.mapDateToDouble(state.getDate()),
  505.                                          primary, primaryDot,
  506.                                          secondary, null);

  507.     }

  508.     /** Differential equations for the main state (orbit, attitude and mass). */
  509.     public interface MainStateEquations {

  510.         /**
  511.          * Initialize the equations at the start of propagation. This method will be
  512.          * called before any calls to {@link #computeDerivatives(SpacecraftState)}.
  513.          *
  514.          * <p> The default implementation of this method does nothing.
  515.          *
  516.          * @param initialState initial state information at the start of propagation.
  517.          * @param target       date of propagation. Not equal to {@code
  518.          *                     initialState.getDate()}.
  519.          */
  520.         default void init(final SpacecraftState initialState, final AbsoluteDate target) {
  521.         }

  522.         /** Compute differential equations for main state.
  523.          * @param state current state
  524.          * @return derivatives of main state
  525.          */
  526.         double[] computeDerivatives(SpacecraftState state);

  527.     }

  528.     /** Differential equations for the main state (orbit, attitude and mass), with converted API. */
  529.     private class ConvertedMainStateEquations implements OrdinaryDifferentialEquation {

  530.         /** Main state equations. */
  531.         private final MainStateEquations main;

  532.         /** Simple constructor.
  533.          * @param main main state equations
  534.          */
  535.         ConvertedMainStateEquations(final MainStateEquations main) {
  536.             this.main = main;
  537.             calls = 0;
  538.         }

  539.         /** {@inheritDoc} */
  540.         public int getDimension() {
  541.             return getBasicDimension();
  542.         }

  543.         @Override
  544.         public void init(final double t0, final double[] y0, final double finalTime) {
  545.             // update space dynamics view
  546.             SpacecraftState initialState = stateMapper.mapArrayToState(t0, y0, null, PropagationType.MEAN);
  547.             initialState = updateAdditionalStates(initialState);
  548.             final AbsoluteDate target = stateMapper.mapDoubleToDate(finalTime);
  549.             main.init(initialState, target);
  550.         }

  551.         /** {@inheritDoc} */
  552.         public double[] computeDerivatives(final double t, final double[] y) {

  553.             // increment calls counter
  554.             ++calls;

  555.             // update space dynamics view
  556.             SpacecraftState currentState = stateMapper.mapArrayToState(t, y, null, PropagationType.MEAN);
  557.             currentState = updateAdditionalStates(currentState);

  558.             // compute main state differentials
  559.             return main.computeDerivatives(currentState);

  560.         }

  561.     }

  562.     /** Differential equations for the secondary state (Jacobians, user variables ...), with converted API. */
  563.     private class ConvertedSecondaryStateEquations implements SecondaryODE {

  564.         /** Additional equations. */
  565.         private final AdditionalEquations equations;

  566.         /** Dimension of the additional state. */
  567.         private final int dimension;

  568.         /** Simple constructor.
  569.          * @param equations additional equations
  570.          * @param dimension dimension of the additional state
  571.          */
  572.         ConvertedSecondaryStateEquations(final AdditionalEquations equations,
  573.                                          final int dimension) {
  574.             this.equations = equations;
  575.             this.dimension = dimension;
  576.         }

  577.         /** {@inheritDoc} */
  578.         @Override
  579.         public int getDimension() {
  580.             return dimension;
  581.         }

  582.         /** {@inheritDoc} */
  583.         @Override
  584.         public void init(final double t0, final double[] primary0,
  585.                          final double[] secondary0, final double finalTime) {
  586.             // update space dynamics view
  587.             SpacecraftState initialState = stateMapper.mapArrayToState(t0, primary0, null, PropagationType.MEAN);
  588.             initialState = updateAdditionalStates(initialState);
  589.             initialState = initialState.addAdditionalState(equations.getName(), secondary0);
  590.             final AbsoluteDate target = stateMapper.mapDoubleToDate(finalTime);
  591.             equations.init(initialState, target);

  592.         }

  593.         /** {@inheritDoc} */
  594.         @Override
  595.         public double[] computeDerivatives(final double t, final double[] primary,
  596.                                            final double[] primaryDot, final double[] secondary) {

  597.             // update space dynamics view
  598.             SpacecraftState currentState = stateMapper.mapArrayToState(t, primary, primaryDot, PropagationType.MEAN);
  599.             currentState = updateAdditionalStates(currentState);
  600.             currentState = currentState.addAdditionalState(equations.getName(), secondary);

  601.             // compute additional derivatives
  602.             final double[] secondaryDot = new double[secondary.length];
  603.             final double[] additionalMainDot =
  604.                             equations.computeDerivatives(currentState, secondaryDot);
  605.             if (additionalMainDot != null) {
  606.                 // the additional equations have an effect on main equations
  607.                 for (int i = 0; i < additionalMainDot.length; ++i) {
  608.                     primaryDot[i] += additionalMainDot[i];
  609.                 }
  610.             }

  611.             return secondaryDot;

  612.         }

  613.     }

  614.     /** Adapt an {@link org.orekit.propagation.events.EventDetector}
  615.      * to Hipparchus {@link org.hipparchus.ode.events.ODEEventHandler} interface.
  616.      * @author Fabien Maussion
  617.      */
  618.     private class AdaptedEventDetector implements ODEEventHandler {

  619.         /** Underlying event detector. */
  620.         private final EventDetector detector;

  621.         /** Time of the previous call to g. */
  622.         private double lastT;

  623.         /** Value from the previous call to g. */
  624.         private double lastG;

  625.         /** Build a wrapped event detector.
  626.          * @param detector event detector to wrap
  627.         */
  628.         AdaptedEventDetector(final EventDetector detector) {
  629.             this.detector = detector;
  630.             this.lastT    = Double.NaN;
  631.             this.lastG    = Double.NaN;
  632.         }

  633.         /** {@inheritDoc} */
  634.         public void init(final ODEStateAndDerivative s0, final double t) {
  635.             detector.init(getCompleteState(s0.getTime(), s0.getCompleteState(), s0.getCompleteDerivative()),
  636.                           stateMapper.mapDoubleToDate(t));
  637.             this.lastT = Double.NaN;
  638.             this.lastG = Double.NaN;
  639.         }

  640.         /** {@inheritDoc} */
  641.         public double g(final ODEStateAndDerivative s) {
  642.             if (!Precision.equals(lastT, s.getTime(), 0)) {
  643.                 lastT = s.getTime();
  644.                 lastG = detector.g(getCompleteState(s.getTime(), s.getCompleteState(), s.getCompleteDerivative()));
  645.             }
  646.             return lastG;
  647.         }

  648.         /** {@inheritDoc} */
  649.         public Action eventOccurred(final ODEStateAndDerivative s, final boolean increasing) {
  650.             return detector.eventOccurred(
  651.                     getCompleteState(
  652.                             s.getTime(),
  653.                             s.getCompleteState(),
  654.                             s.getCompleteDerivative()),
  655.                     increasing);
  656.         }

  657.         /** {@inheritDoc} */
  658.         public ODEState resetState(final ODEStateAndDerivative s) {

  659.             final SpacecraftState oldState = getCompleteState(s.getTime(), s.getCompleteState(), s.getCompleteDerivative());
  660.             final SpacecraftState newState = detector.resetState(oldState);
  661.             stateChanged(newState);

  662.             // main part
  663.             final double[] primary    = new double[s.getPrimaryStateDimension()];
  664.             stateMapper.mapStateToArray(newState, primary, null);

  665.             // secondary part
  666.             final double[][] secondary    = new double[additionalEquations.size()][];
  667.             for (int i = 0; i < additionalEquations.size(); ++i) {
  668.                 secondary[i] = newState.getAdditionalState(additionalEquations.get(i).getName());
  669.             }

  670.             return new ODEState(newState.getDate().durationFrom(getStartDate()),
  671.                                 primary, secondary);

  672.         }

  673.     }

  674.     /** Adapt an {@link org.orekit.propagation.sampling.OrekitStepHandler}
  675.      * to Hipparchus {@link ODEStepHandler} interface.
  676.      * @author Luc Maisonobe
  677.      */
  678.     private class AdaptedStepHandler implements ODEStepHandler {

  679.         /** Underlying handler. */
  680.         private final OrekitStepHandler handler;

  681.         /** Build an instance.
  682.          * @param handler underlying handler to wrap
  683.          */
  684.         AdaptedStepHandler(final OrekitStepHandler handler) {
  685.             this.handler = handler;
  686.         }

  687.         /** {@inheritDoc} */
  688.         public void init(final ODEStateAndDerivative s0, final double t) {
  689.             handler.init(getCompleteState(s0.getTime(), s0.getCompleteState(), s0.getCompleteDerivative()),
  690.                          stateMapper.mapDoubleToDate(t));
  691.         }

  692.         /** {@inheritDoc} */
  693.         @Override
  694.         public void handleStep(final ODEStateInterpolator interpolator) {
  695.             handler.handleStep(new AdaptedStepInterpolator(interpolator));
  696.         }

  697.         /** {@inheritDoc} */
  698.         @Override
  699.         public void finish(final ODEStateAndDerivative finalState) {
  700.             handler.finish(convert(finalState));
  701.         }

  702.     }

  703.     /** Adapt an Hipparchus {@link ODEStateInterpolator}
  704.      * to an orekit {@link OrekitStepInterpolator} interface.
  705.      * @author Luc Maisonobe
  706.      */
  707.     private class AdaptedStepInterpolator implements OrekitStepInterpolator {

  708.         /** Underlying raw rawInterpolator. */
  709.         private final ODEStateInterpolator mathInterpolator;

  710.         /** Simple constructor.
  711.          * @param mathInterpolator underlying raw interpolator
  712.          */
  713.         AdaptedStepInterpolator(final ODEStateInterpolator mathInterpolator) {
  714.             this.mathInterpolator = mathInterpolator;
  715.         }

  716.         /** {@inheritDoc}} */
  717.         @Override
  718.         public SpacecraftState getPreviousState() {
  719.             return convert(mathInterpolator.getPreviousState());
  720.         }

  721.         /** {@inheritDoc}} */
  722.         @Override
  723.         public boolean isPreviousStateInterpolated() {
  724.             return mathInterpolator.isPreviousStateInterpolated();
  725.         }

  726.         /** {@inheritDoc}} */
  727.         @Override
  728.         public SpacecraftState getCurrentState() {
  729.             return convert(mathInterpolator.getCurrentState());
  730.         }

  731.         /** {@inheritDoc}} */
  732.         @Override
  733.         public boolean isCurrentStateInterpolated() {
  734.             return mathInterpolator.isCurrentStateInterpolated();
  735.         }

  736.         /** {@inheritDoc}} */
  737.         @Override
  738.         public SpacecraftState getInterpolatedState(final AbsoluteDate date) {
  739.             return convert(mathInterpolator.getInterpolatedState(date.durationFrom(stateMapper.getReferenceDate())));
  740.         }

  741.         /** {@inheritDoc}} */
  742.         @Override
  743.         public boolean isForward() {
  744.             return mathInterpolator.isForward();
  745.         }

  746.         /** {@inheritDoc}} */
  747.         @Override
  748.         public AdaptedStepInterpolator restrictStep(final SpacecraftState newPreviousState,
  749.                                                     final SpacecraftState newCurrentState) {
  750.             try {
  751.                 final AbstractODEStateInterpolator aosi = (AbstractODEStateInterpolator) mathInterpolator;
  752.                 return new AdaptedStepInterpolator(aosi.restrictStep(convert(newPreviousState),
  753.                                                                      convert(newCurrentState)));
  754.             } catch (ClassCastException cce) {
  755.                 // this should never happen
  756.                 throw new OrekitInternalError(cce);
  757.             }
  758.         }

  759.     }

  760.     /** Specialized step handler storing interpolators for ephemeris generation.
  761.      * @since 11.0
  762.      */
  763.     private class StoringStepHandler implements ODEStepHandler, EphemerisGenerator {

  764.         /** Underlying raw mathematical model. */
  765.         private DenseOutputModel model;

  766.         /** the user supplied end date. Propagation may not end on this date. */
  767.         private AbsoluteDate endDate;

  768.         /** Generated ephemeris. */
  769.         private BoundedPropagator ephemeris;

  770.         /** Set the end date.
  771.          * @param endDate end date
  772.          */
  773.         public void setEndDate(final AbsoluteDate endDate) {
  774.             this.endDate = endDate;
  775.         }

  776.         /** {@inheritDoc} */
  777.         @Override
  778.         public void init(final ODEStateAndDerivative s0, final double t) {

  779.             this.model = new DenseOutputModel();
  780.             model.init(s0, t);

  781.             // ephemeris will be generated when last step is processed
  782.             this.ephemeris = null;

  783.         }

  784.         /** {@inheritDoc} */
  785.         @Override
  786.         public BoundedPropagator getGeneratedEphemeris() {
  787.             return ephemeris;
  788.         }

  789.         /** {@inheritDoc} */
  790.         @Override
  791.         public void handleStep(final ODEStateInterpolator interpolator) {
  792.             model.handleStep(interpolator);
  793.         }

  794.         /** {@inheritDoc} */
  795.         @Override
  796.         public void finish(final ODEStateAndDerivative finalState) {

  797.             // set up the boundary dates
  798.             final double tI = model.getInitialTime();
  799.             final double tF = model.getFinalTime();
  800.             // tI is almost? always zero
  801.             final AbsoluteDate startDate =
  802.                             stateMapper.mapDoubleToDate(tI);
  803.             final AbsoluteDate finalDate =
  804.                             stateMapper.mapDoubleToDate(tF, this.endDate);
  805.             final AbsoluteDate minDate;
  806.             final AbsoluteDate maxDate;
  807.             if (tF < tI) {
  808.                 minDate = finalDate;
  809.                 maxDate = startDate;
  810.             } else {
  811.                 minDate = startDate;
  812.                 maxDate = finalDate;
  813.             }

  814.             // get the initial additional states that are not managed
  815.             final Map<String, double[]> unmanaged = new HashMap<String, double[]>();
  816.             for (final Map.Entry<String, double[]> initial : getInitialState().getAdditionalStates().entrySet()) {
  817.                 if (!isAdditionalStateManaged(initial.getKey())) {
  818.                     // this additional state was in the initial state, but is unknown to the propagator
  819.                     // we simply copy its initial value as is
  820.                     unmanaged.put(initial.getKey(), initial.getValue());
  821.                 }
  822.             }

  823.             // get the names of additional states managed by differential equations
  824.             final String[] names = new String[additionalEquations.size()];
  825.             for (int i = 0; i < names.length; ++i) {
  826.                 names[i] = additionalEquations.get(i).getName();
  827.             }

  828.             // create the ephemeris
  829.             ephemeris = new IntegratedEphemeris(startDate, minDate, maxDate,
  830.                                                 stateMapper, propagationType, model, unmanaged,
  831.                                                 getAdditionalStateProviders(), names);

  832.         }

  833.     }

  834.     /** Wrapper for resetting an integrator handlers.
  835.      * <p>
  836.      * This class is intended to be used in a try-with-resource statement.
  837.      * If propagator-specific event handlers and step handlers are added to
  838.      * the integrator in the try block, they will be removed automatically
  839.      * when leaving the block, so the integrator only keep its own handlers
  840.      * between calls to {@link AbstractIntegratedPropagator#propagate(AbsoluteDate, AbsoluteDate).
  841.      * </p>
  842.      * @since 11.0
  843.      */
  844.     private static class IntegratorResetter implements AutoCloseable {

  845.         /** Wrapped integrator. */
  846.         private final ODEIntegrator integrator;

  847.         /** Initial event handlers list. */
  848.         private final List<EventHandlerConfiguration> eventHandlersConfigurations;

  849.         /** Initial step handlers list. */
  850.         private final List<ODEStepHandler> stepHandlers;

  851.         /** Simple constructor.
  852.          * @param integrator wrapped integrator
  853.          */
  854.         IntegratorResetter(final ODEIntegrator integrator) {
  855.             this.integrator                  = integrator;
  856.             this.eventHandlersConfigurations = new ArrayList<>(integrator.getEventHandlersConfigurations());
  857.             this.stepHandlers                = new ArrayList<>(integrator.getStepHandlers());
  858.         }

  859.         /** {@inheritDoc}
  860.          * <p>
  861.          * Reset event handlers and step handlers back to the initial list
  862.          * </p>
  863.          */
  864.         @Override
  865.         public void close() {

  866.             // reset event handlers
  867.             integrator.clearEventHandlers();
  868.             eventHandlersConfigurations.forEach(c -> integrator.addEventHandler(c.getEventHandler(),
  869.                                                                                 c.getMaxCheckInterval(),
  870.                                                                                 c.getConvergence(),
  871.                                                                                 c.getMaxIterationCount(),
  872.                                                                                 c.getSolver()));

  873.             // reset step handlers
  874.             integrator.clearStepHandlers();
  875.             stepHandlers.forEach(stepHandler -> integrator.addStepHandler(stepHandler));

  876.         }

  877.     }

  878. }