AbstractIntegratedPropagator.java

  1. /* Copyright 2002-2020 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.ODEEventHandler;
  35. import org.hipparchus.ode.sampling.AbstractODEStateInterpolator;
  36. import org.hipparchus.ode.sampling.ODEStateInterpolator;
  37. import org.hipparchus.ode.sampling.ODEStepHandler;
  38. import org.hipparchus.util.Precision;
  39. import org.orekit.attitudes.AttitudeProvider;
  40. import org.orekit.errors.OrekitException;
  41. import org.orekit.errors.OrekitIllegalStateException;
  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.PropagationType;
  50. import org.orekit.propagation.SpacecraftState;
  51. import org.orekit.propagation.events.EventDetector;
  52. import org.orekit.propagation.sampling.OrekitStepHandler;
  53. import org.orekit.propagation.sampling.OrekitStepInterpolator;
  54. import org.orekit.time.AbsoluteDate;


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

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

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

  64.     /** Mode handler. */
  65.     private ModeHandler modeHandler;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  201.         return false;
  202.     }

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

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

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

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

  226.     }

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

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

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

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

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

  256.     /** {@inheritDoc}
  257.      * <p>Note that this method has the side effect of replacing the step handlers
  258.      * of the underlying integrator set up in the {@link
  259.      * #AbstractIntegratedPropagator(ODEIntegrator, PropagationType) constructor}. So if a specific
  260.      * step handler is needed, it should be added after this method has been callled.</p>
  261.      */
  262.     public void setSlaveMode() {
  263.         super.setSlaveMode();
  264.         if (integrator != null) {
  265.             integrator.clearStepHandlers();
  266.         }
  267.         modeHandler = null;
  268.     }

  269.     /** {@inheritDoc}
  270.      * <p>Note that this method has the side effect of replacing the step handlers
  271.      * of the underlying integrator set up in the {@link
  272.      * #AbstractIntegratedPropagator(ODEIntegrator, PropagationType) constructor}. So if a specific
  273.      * step handler is needed, it should be added after this method has been callled.</p>
  274.      */
  275.     public void setMasterMode(final OrekitStepHandler handler) {
  276.         super.setMasterMode(handler);
  277.         integrator.clearStepHandlers();
  278.         final AdaptedStepHandler wrapped = new AdaptedStepHandler(handler);
  279.         integrator.addStepHandler(wrapped);
  280.         modeHandler = wrapped;
  281.     }

  282.     /** {@inheritDoc}
  283.      * <p>Note that this method has the side effect of replacing the step handlers
  284.      * of the underlying integrator set up in the {@link
  285.      * #AbstractIntegratedPropagator(ODEIntegrator, PropagationType) constructor}. So if a specific
  286.      * step handler is needed, it should be added after this method has been called.</p>
  287.      */
  288.     public void setEphemerisMode() {
  289.         super.setEphemerisMode();
  290.         integrator.clearStepHandlers();
  291.         final EphemerisModeHandler ephemeris = new EphemerisModeHandler();
  292.         modeHandler = ephemeris;
  293.         integrator.addStepHandler(ephemeris);
  294.     }

  295.     /**
  296.      * {@inheritDoc}
  297.      *
  298.      * <p>Note that this method has the side effect of replacing the step handlers of the
  299.      * underlying integrator set up in the {@link #AbstractIntegratedPropagator(ODEIntegrator,
  300.      * PropagationType) constructor}.</p>
  301.      */
  302.     @Override
  303.     public void setEphemerisMode(final OrekitStepHandler handler) {
  304.         super.setEphemerisMode();
  305.         integrator.clearStepHandlers();
  306.         final EphemerisModeHandler ephemeris = new EphemerisModeHandler(handler);
  307.         modeHandler = ephemeris;
  308.         integrator.addStepHandler(ephemeris);
  309.     }

  310.     /** {@inheritDoc} */
  311.     public BoundedPropagator getGeneratedEphemeris()
  312.         throws IllegalStateException {
  313.         if (getMode() != EPHEMERIS_GENERATION_MODE) {
  314.             throw new OrekitIllegalStateException(OrekitMessages.PROPAGATOR_NOT_IN_EPHEMERIS_GENERATION_MODE);
  315.         }
  316.         return ((EphemerisModeHandler) modeHandler).getEphemeris();
  317.     }

  318.     /** Create a mapper between raw double components and spacecraft state.
  319.     /** Simple constructor.
  320.      * <p>
  321.      * The position parameter type is meaningful only if {@link
  322.      * #getOrbitType() propagation orbit type}
  323.      * support it. As an example, it is not meaningful for propagation
  324.      * in {@link OrbitType#CARTESIAN Cartesian} parameters.
  325.      * </p>
  326.      * @param referenceDate reference date
  327.      * @param mu central attraction coefficient (m³/s²)
  328.      * @param orbitType orbit type to use for mapping
  329.      * @param positionAngleType angle type to use for propagation
  330.      * @param attitudeProvider attitude provider
  331.      * @param frame inertial frame
  332.      * @return new mapper
  333.      */
  334.     protected abstract StateMapper createMapper(AbsoluteDate referenceDate, double mu,
  335.                                                 OrbitType orbitType, PositionAngle positionAngleType,
  336.                                                 AttitudeProvider attitudeProvider, Frame frame);

  337.     /** Get the differential equations to integrate (for main state only).
  338.      * @param integ numerical integrator to use for propagation.
  339.      * @return differential equations for main state
  340.      */
  341.     protected abstract MainStateEquations getMainStateEquations(ODEIntegrator integ);

  342.     /** {@inheritDoc} */
  343.     public SpacecraftState propagate(final AbsoluteDate target) {
  344.         if (getStartDate() == null) {
  345.             if (getInitialState() == null) {
  346.                 throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  347.             }
  348.             setStartDate(getInitialState().getDate());
  349.         }
  350.         return propagate(getStartDate(), target);
  351.     }

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

  354.         if (getInitialState() == null) {
  355.             throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  356.         }

  357.         if (!tStart.equals(getInitialState().getDate())) {
  358.             // if propagation start date is not initial date,
  359.             // propagate from initial to start date without event detection
  360.             propagate(tStart, false);
  361.         }

  362.         // propagate from start date to end date with event detection
  363.         return propagate(tEnd, true);

  364.     }

  365.     /** Propagation with or without event detection.
  366.      * @param tEnd target date to which orbit should be propagated
  367.      * @param activateHandlers if true, step and event handlers should be activated
  368.      * @return state at end of propagation
  369.      */
  370.     protected SpacecraftState propagate(final AbsoluteDate tEnd, final boolean activateHandlers) {
  371.         try {

  372.             initializePropagation();

  373.             if (getInitialState().getDate().equals(tEnd)) {
  374.                 // don't extrapolate
  375.                 return getInitialState();
  376.             }

  377.             // space dynamics view
  378.             stateMapper = createMapper(getInitialState().getDate(), stateMapper.getMu(),
  379.                                        stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  380.                                        stateMapper.getAttitudeProvider(), getInitialState().getFrame());


  381.             if (Double.isNaN(getMu())) {
  382.                 setMu(getInitialState().getMu());
  383.             }

  384.             if (getInitialState().getMass() <= 0.0) {
  385.                 throw new OrekitException(OrekitMessages.SPACECRAFT_MASS_BECOMES_NEGATIVE,
  386.                                           getInitialState().getMass());
  387.             }

  388.             integrator.clearEventHandlers();

  389.             // set up events added by user, only if handlers are activated
  390.             if (activateHandlers) {
  391.                 setUpUserEventDetectors();
  392.             }

  393.             // convert space flight dynamics API to math API
  394.             final SpacecraftState initialIntegrationState = getInitialIntegrationState();
  395.             final ODEState mathInitialState = createInitialState(initialIntegrationState);
  396.             final ExpandableODE mathODE = createODE(integrator, mathInitialState);
  397.             equationsMapper = mathODE.getMapper();

  398.             // initialize mode handler
  399.             if (modeHandler != null) {
  400.                 modeHandler.initialize(activateHandlers, tEnd);
  401.             }

  402.             // mathematical integration
  403.             final ODEStateAndDerivative mathFinalState;
  404.             beforeIntegration(initialIntegrationState, tEnd);
  405.             mathFinalState = integrator.integrate(mathODE, mathInitialState,
  406.                                                   tEnd.durationFrom(getInitialState().getDate()));
  407.             afterIntegration();

  408.             // get final state
  409.             SpacecraftState finalState =
  410.                             stateMapper.mapArrayToState(stateMapper.mapDoubleToDate(mathFinalState.getTime(),
  411.                                                                                     tEnd),
  412.                                                         mathFinalState.getPrimaryState(),
  413.                                                         mathFinalState.getPrimaryDerivative(),
  414.                                                         propagationType);

  415.             finalState = updateAdditionalStates(finalState);
  416.             for (int i = 0; i < additionalEquations.size(); ++i) {
  417.                 final double[] secondary = mathFinalState.getSecondaryState(i + 1);
  418.                 finalState = finalState.addAdditionalState(additionalEquations.get(i).getName(),
  419.                                                            secondary);
  420.             }
  421.             if (resetAtEnd) {
  422.                 resetInitialState(finalState);
  423.                 setStartDate(finalState.getDate());
  424.             }

  425.             return finalState;

  426.         } catch (MathRuntimeException mre) {
  427.             throw OrekitException.unwrap(mre);
  428.         }
  429.     }

  430.     /** Get the initial state for integration.
  431.      * @return initial state for integration
  432.      */
  433.     protected SpacecraftState getInitialIntegrationState() {
  434.         return getInitialState();
  435.     }

  436.     /** Create an initial state.
  437.      * @param initialState initial state in flight dynamics world
  438.      * @return initial state in mathematics world
  439.      */
  440.     private ODEState createInitialState(final SpacecraftState initialState) {

  441.         // retrieve initial state
  442.         final double[] primary  = new double[getBasicDimension()];
  443.         stateMapper.mapStateToArray(initialState, primary, null);

  444.         // secondary part of the ODE
  445.         final double[][] secondary = new double[additionalEquations.size()][];
  446.         for (int i = 0; i < additionalEquations.size(); ++i) {
  447.             final AdditionalEquations additional = additionalEquations.get(i);
  448.             secondary[i] = initialState.getAdditionalState(additional.getName());
  449.         }

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

  451.     }

  452.     /** Create an ODE with all equations.
  453.      * @param integ numerical integrator to use for propagation.
  454.      * @param mathInitialState initial state
  455.      * @return a new ode
  456.      */
  457.     private ExpandableODE createODE(final ODEIntegrator integ,
  458.                                     final ODEState mathInitialState) {

  459.         final ExpandableODE ode =
  460.                 new ExpandableODE(new ConvertedMainStateEquations(getMainStateEquations(integ)));

  461.         // secondary part of the ODE
  462.         for (int i = 0; i < additionalEquations.size(); ++i) {
  463.             final AdditionalEquations additional = additionalEquations.get(i);
  464.             final SecondaryODE secondary =
  465.                     new ConvertedSecondaryStateEquations(additional,
  466.                                                          mathInitialState.getSecondaryStateDimension(i + 1));
  467.             ode.addSecondaryEquations(secondary);
  468.         }

  469.         return ode;

  470.     }

  471.     /** Method called just before integration.
  472.      * <p>
  473.      * The default implementation does nothing, it may be specialized in subclasses.
  474.      * </p>
  475.      * @param initialState initial state
  476.      * @param tEnd target date at which state should be propagated
  477.      */
  478.     protected void beforeIntegration(final SpacecraftState initialState,
  479.                                      final AbsoluteDate tEnd) {
  480.         // do nothing by default
  481.     }

  482.     /** Method called just after integration.
  483.      * <p>
  484.      * The default implementation does nothing, it may be specialized in subclasses.
  485.      * </p>
  486.      */
  487.     protected void afterIntegration() {
  488.         // do nothing by default
  489.     }

  490.     /** Get state vector dimension without additional parameters.
  491.      * @return state vector dimension without additional parameters.
  492.      */
  493.     public int getBasicDimension() {
  494.         return 7;

  495.     }

  496.     /** Get the integrator used by the propagator.
  497.      * @return the integrator.
  498.      */
  499.     protected ODEIntegrator getIntegrator() {
  500.         return integrator;
  501.     }

  502.     /** Get a complete state with all additional equations.
  503.      * @param t current value of the independent <I>time</I> variable
  504.      * @param y array containing the current value of the state vector
  505.      * @param yDot array containing the current value of the state vector derivative
  506.      * @return complete state
  507.      */
  508.     private SpacecraftState getCompleteState(final double t, final double[] y, final double[] yDot) {

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

  511.         // pre-integrated additional states
  512.         state = updateAdditionalStates(state);

  513.         // additional states integrated here
  514.         if (!additionalEquations.isEmpty()) {

  515.             for (int i = 0; i < additionalEquations.size(); ++i) {
  516.                 state = state.addAdditionalState(additionalEquations.get(i).getName(),
  517.                                                  equationsMapper.extractEquationData(i + 1, y));
  518.             }

  519.         }

  520.         return state;

  521.     }

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

  524.         /**
  525.          * Initialize the equations at the start of propagation. This method will be
  526.          * called before any calls to {@link #computeDerivatives(SpacecraftState)}.
  527.          *
  528.          * <p> The default implementation of this method does nothing.
  529.          *
  530.          * @param initialState initial state information at the start of propagation.
  531.          * @param target       date of propagation. Not equal to {@code
  532.          *                     initialState.getDate()}.
  533.          */
  534.         default void init(final SpacecraftState initialState, final AbsoluteDate target) {
  535.         }

  536.         /** Compute differential equations for main state.
  537.          * @param state current state
  538.          * @return derivatives of main state
  539.          */
  540.         double[] computeDerivatives(SpacecraftState state);

  541.     }

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

  544.         /** Main state equations. */
  545.         private final MainStateEquations main;

  546.         /** Simple constructor.
  547.          * @param main main state equations
  548.          */
  549.         ConvertedMainStateEquations(final MainStateEquations main) {
  550.             this.main = main;
  551.             calls = 0;
  552.         }

  553.         /** {@inheritDoc} */
  554.         public int getDimension() {
  555.             return getBasicDimension();
  556.         }

  557.         @Override
  558.         public void init(final double t0, final double[] y0, final double finalTime) {
  559.             // update space dynamics view
  560.             SpacecraftState initialState = stateMapper.mapArrayToState(t0, y0, null, PropagationType.MEAN);
  561.             initialState = updateAdditionalStates(initialState);
  562.             final AbsoluteDate target = stateMapper.mapDoubleToDate(finalTime);
  563.             main.init(initialState, target);
  564.         }

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

  567.             // increment calls counter
  568.             ++calls;

  569.             // update space dynamics view
  570.             SpacecraftState currentState = stateMapper.mapArrayToState(t, y, null, PropagationType.MEAN);
  571.             currentState = updateAdditionalStates(currentState);

  572.             // compute main state differentials
  573.             return main.computeDerivatives(currentState);

  574.         }

  575.     }

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

  578.         /** Additional equations. */
  579.         private final AdditionalEquations equations;

  580.         /** Dimension of the additional state. */
  581.         private final int dimension;

  582.         /** Simple constructor.
  583.          * @param equations additional equations
  584.          * @param dimension dimension of the additional state
  585.          */
  586.         ConvertedSecondaryStateEquations(final AdditionalEquations equations,
  587.                                          final int dimension) {
  588.             this.equations = equations;
  589.             this.dimension = dimension;
  590.         }

  591.         /** {@inheritDoc} */
  592.         @Override
  593.         public int getDimension() {
  594.             return dimension;
  595.         }

  596.         /** {@inheritDoc} */
  597.         @Override
  598.         public void init(final double t0, final double[] primary0,
  599.                          final double[] secondary0, final double finalTime) {
  600.             // update space dynamics view
  601.             SpacecraftState initialState = stateMapper.mapArrayToState(t0, primary0, null, PropagationType.MEAN);
  602.             initialState = updateAdditionalStates(initialState);
  603.             initialState = initialState.addAdditionalState(equations.getName(), secondary0);
  604.             final AbsoluteDate target = stateMapper.mapDoubleToDate(finalTime);
  605.             equations.init(initialState, target);

  606.         }

  607.         /** {@inheritDoc} */
  608.         @Override
  609.         public double[] computeDerivatives(final double t, final double[] primary,
  610.                                            final double[] primaryDot, final double[] secondary) {

  611.             // update space dynamics view
  612.             SpacecraftState currentState = stateMapper.mapArrayToState(t, primary, primaryDot, PropagationType.MEAN);
  613.             currentState = updateAdditionalStates(currentState);
  614.             currentState = currentState.addAdditionalState(equations.getName(), secondary);

  615.             // compute additional derivatives
  616.             final double[] secondaryDot = new double[secondary.length];
  617.             final double[] additionalMainDot =
  618.                             equations.computeDerivatives(currentState, secondaryDot);
  619.             if (additionalMainDot != null) {
  620.                 // the additional equations have an effect on main equations
  621.                 for (int i = 0; i < additionalMainDot.length; ++i) {
  622.                     primaryDot[i] += additionalMainDot[i];
  623.                 }
  624.             }

  625.             return secondaryDot;

  626.         }

  627.     }

  628.     /** Adapt an {@link org.orekit.propagation.events.EventDetector}
  629.      * to Hipparchus {@link org.hipparchus.ode.events.ODEEventHandler} interface.
  630.      * @author Fabien Maussion
  631.      */
  632.     private class AdaptedEventDetector implements ODEEventHandler {

  633.         /** Underlying event detector. */
  634.         private final EventDetector detector;

  635.         /** Time of the previous call to g. */
  636.         private double lastT;

  637.         /** Value from the previous call to g. */
  638.         private double lastG;

  639.         /** Build a wrapped event detector.
  640.          * @param detector event detector to wrap
  641.         */
  642.         AdaptedEventDetector(final EventDetector detector) {
  643.             this.detector = detector;
  644.             this.lastT    = Double.NaN;
  645.             this.lastG    = Double.NaN;
  646.         }

  647.         /** {@inheritDoc} */
  648.         public void init(final ODEStateAndDerivative s0, final double t) {
  649.             detector.init(getCompleteState(s0.getTime(), s0.getCompleteState(), s0.getCompleteDerivative()),
  650.                           stateMapper.mapDoubleToDate(t));
  651.             this.lastT = Double.NaN;
  652.             this.lastG = Double.NaN;
  653.         }

  654.         /** {@inheritDoc} */
  655.         public double g(final ODEStateAndDerivative s) {
  656.             if (!Precision.equals(lastT, s.getTime(), 0)) {
  657.                 lastT = s.getTime();
  658.                 lastG = detector.g(getCompleteState(s.getTime(), s.getCompleteState(), s.getCompleteDerivative()));
  659.             }
  660.             return lastG;
  661.         }

  662.         /** {@inheritDoc} */
  663.         public Action eventOccurred(final ODEStateAndDerivative s, final boolean increasing) {
  664.             return detector.eventOccurred(
  665.                     getCompleteState(
  666.                             s.getTime(),
  667.                             s.getCompleteState(),
  668.                             s.getCompleteDerivative()),
  669.                     increasing);
  670.         }

  671.         /** {@inheritDoc} */
  672.         public ODEState resetState(final ODEStateAndDerivative s) {

  673.             final SpacecraftState oldState = getCompleteState(s.getTime(), s.getCompleteState(), s.getCompleteDerivative());
  674.             final SpacecraftState newState = detector.resetState(oldState);
  675.             stateChanged(newState);

  676.             // main part
  677.             final double[] primary    = new double[s.getPrimaryStateDimension()];
  678.             stateMapper.mapStateToArray(newState, primary, null);

  679.             // secondary part
  680.             final double[][] secondary    = new double[additionalEquations.size()][];
  681.             for (int i = 0; i < additionalEquations.size(); ++i) {
  682.                 secondary[i] = newState.getAdditionalState(additionalEquations.get(i).getName());
  683.             }

  684.             return new ODEState(newState.getDate().durationFrom(getStartDate()),
  685.                                 primary, secondary);

  686.         }

  687.     }

  688.     /** Adapt an {@link org.orekit.propagation.sampling.OrekitStepHandler}
  689.      * to Hipparchus {@link ODEStepHandler} interface.
  690.      * @author Luc Maisonobe
  691.      */
  692.     private class AdaptedStepHandler implements ODEStepHandler, ModeHandler {

  693.         /** Underlying handler. */
  694.         private final OrekitStepHandler handler;

  695.         /** Flag for handler . */
  696.         private boolean activate;

  697.         /** Build an instance.
  698.          * @param handler underlying handler to wrap
  699.          */
  700.         AdaptedStepHandler(final OrekitStepHandler handler) {
  701.             this.handler = handler;
  702.         }

  703.         /** {@inheritDoc} */
  704.         public void initialize(final boolean activateHandlers,
  705.                                final AbsoluteDate targetDate) {
  706.             this.activate = activateHandlers;
  707.         }

  708.         /** {@inheritDoc} */
  709.         public void init(final ODEStateAndDerivative s0, final double t) {
  710.             if (activate) {
  711.                 handler.init(getCompleteState(s0.getTime(), s0.getCompleteState(), s0.getCompleteDerivative()),
  712.                              stateMapper.mapDoubleToDate(t));
  713.             }
  714.         }

  715.         /** {@inheritDoc} */
  716.         public void handleStep(final ODEStateInterpolator interpolator, final boolean isLast) {
  717.             if (activate) {
  718.                 handler.handleStep(new AdaptedStepInterpolator(interpolator), isLast);
  719.             }
  720.         }

  721.     }

  722.     /** Adapt an Hipparchus {@link ODEStateInterpolator}
  723.      * to an orekit {@link OrekitStepInterpolator} interface.
  724.      * @author Luc Maisonobe
  725.      */
  726.     private class AdaptedStepInterpolator implements OrekitStepInterpolator {

  727.         /** Underlying raw rawInterpolator. */
  728.         private final ODEStateInterpolator mathInterpolator;

  729.         /** Simple constructor.
  730.          * @param mathInterpolator underlying raw interpolator
  731.          */
  732.         AdaptedStepInterpolator(final ODEStateInterpolator mathInterpolator) {
  733.             this.mathInterpolator = mathInterpolator;
  734.         }

  735.         /** {@inheritDoc}} */
  736.         @Override
  737.         public SpacecraftState getPreviousState() {
  738.             return convert(mathInterpolator.getPreviousState());
  739.         }

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

  745.         /** {@inheritDoc}} */
  746.         @Override
  747.         public SpacecraftState getCurrentState() {
  748.             return convert(mathInterpolator.getCurrentState());
  749.         }

  750.         /** {@inheritDoc}} */
  751.         @Override
  752.         public boolean isCurrentStateInterpolated() {
  753.             return mathInterpolator.isCurrentStateInterpolated();
  754.         }

  755.         /** {@inheritDoc}} */
  756.         @Override
  757.         public SpacecraftState getInterpolatedState(final AbsoluteDate date) {
  758.             return convert(mathInterpolator.getInterpolatedState(date.durationFrom(stateMapper.getReferenceDate())));
  759.         }

  760.         /** Convert a state from mathematical world to space flight dynamics world.
  761.          * @param os mathematical state
  762.          * @return space flight dynamics state
  763.          */
  764.         private SpacecraftState convert(final ODEStateAndDerivative os) {

  765.             SpacecraftState s =
  766.                             stateMapper.mapArrayToState(os.getTime(),
  767.                                                         os.getPrimaryState(),
  768.                                                         os.getPrimaryDerivative(),
  769.                                                         propagationType);
  770.             s = updateAdditionalStates(s);
  771.             for (int i = 0; i < additionalEquations.size(); ++i) {
  772.                 final double[] secondary = os.getSecondaryState(i + 1);
  773.                 s = s.addAdditionalState(additionalEquations.get(i).getName(), secondary);
  774.             }

  775.             return s;

  776.         }

  777.         /** Convert a state from space flight dynamics world to mathematical world.
  778.          * @param state space flight dynamics state
  779.          * @return mathematical state
  780.          */
  781.         private ODEStateAndDerivative convert(final SpacecraftState state) {

  782.             // retrieve initial state
  783.             final double[] primary    = new double[getBasicDimension()];
  784.             final double[] primaryDot = new double[getBasicDimension()];
  785.             stateMapper.mapStateToArray(state, primary, primaryDot);

  786.             // secondary part of the ODE
  787.             final double[][] secondary    = new double[additionalEquations.size()][];
  788.             for (int i = 0; i < additionalEquations.size(); ++i) {
  789.                 final AdditionalEquations additional = additionalEquations.get(i);
  790.                 secondary[i] = state.getAdditionalState(additional.getName());
  791.             }

  792.             return new ODEStateAndDerivative(stateMapper.mapDateToDouble(state.getDate()),
  793.                                              primary, primaryDot,
  794.                                              secondary, null);

  795.         }

  796.         /** {@inheritDoc}} */
  797.         @Override
  798.         public boolean isForward() {
  799.             return mathInterpolator.isForward();
  800.         }

  801.         /** {@inheritDoc}} */
  802.         @Override
  803.         public AdaptedStepInterpolator restrictStep(final SpacecraftState newPreviousState,
  804.                                                     final SpacecraftState newCurrentState) {
  805.             try {
  806.                 final AbstractODEStateInterpolator aosi = (AbstractODEStateInterpolator) mathInterpolator;
  807.                 return new AdaptedStepInterpolator(aosi.restrictStep(convert(newPreviousState),
  808.                                                                      convert(newCurrentState)));
  809.             } catch (ClassCastException cce) {
  810.                 // this should never happen
  811.                 throw new OrekitInternalError(cce);
  812.             }
  813.         }

  814.     }

  815.     private class EphemerisModeHandler implements ModeHandler, ODEStepHandler {

  816.         /** Underlying raw mathematical model. */
  817.         private DenseOutputModel model;

  818.         /** Generated ephemeris. */
  819.         private BoundedPropagator ephemeris;

  820.         /** Flag for handler . */
  821.         private boolean activate;

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

  824.         /** User's integration step handler. May be null. */
  825.         private final AdaptedStepHandler handler;

  826.         /** Creates a new instance of EphemerisModeHandler which must be
  827.          *  filled by the propagator.
  828.          */
  829.         EphemerisModeHandler() {
  830.             this.handler = null;
  831.         }

  832.         /** Creates a new instance of EphemerisModeHandler which must be
  833.          *  filled by the propagator.
  834.          *  @param handler the handler to notify of every integrator step.
  835.          */
  836.         EphemerisModeHandler(final OrekitStepHandler handler) {
  837.             this.handler = new AdaptedStepHandler(handler);
  838.         }

  839.         /** {@inheritDoc} */
  840.         public void initialize(final boolean activateHandlers,
  841.                                final AbsoluteDate targetDate) {
  842.             this.activate = activateHandlers;
  843.             this.model    = new DenseOutputModel();
  844.             this.endDate  = targetDate;

  845.             // ephemeris will be generated when last step is processed
  846.             this.ephemeris = null;
  847.             if (this.handler != null) {
  848.                 this.handler.initialize(activateHandlers, targetDate);
  849.             }
  850.         }

  851.         /** Get the generated ephemeris.
  852.          * @return a new instance of the generated ephemeris
  853.          */
  854.         public BoundedPropagator getEphemeris() {
  855.             return ephemeris;
  856.         }

  857.         /** {@inheritDoc} */
  858.         public void handleStep(final ODEStateInterpolator interpolator, final boolean isLast) {
  859.             if (activate) {
  860.                 if (this.handler != null) {
  861.                     this.handler.handleStep(interpolator, isLast);
  862.                 }

  863.                 model.handleStep(interpolator, isLast);
  864.                 if (isLast) {

  865.                     // set up the boundary dates
  866.                     final double tI = model.getInitialTime();
  867.                     final double tF = model.getFinalTime();
  868.                     // tI is almost? always zero
  869.                     final AbsoluteDate startDate =
  870.                                     stateMapper.mapDoubleToDate(tI);
  871.                     final AbsoluteDate finalDate =
  872.                                     stateMapper.mapDoubleToDate(tF, this.endDate);
  873.                     final AbsoluteDate minDate;
  874.                     final AbsoluteDate maxDate;
  875.                     if (tF < tI) {
  876.                         minDate = finalDate;
  877.                         maxDate = startDate;
  878.                     } else {
  879.                         minDate = startDate;
  880.                         maxDate = finalDate;
  881.                     }

  882.                     // get the initial additional states that are not managed
  883.                     final Map<String, double[]> unmanaged = new HashMap<String, double[]>();
  884.                     for (final Map.Entry<String, double[]> initial : getInitialState().getAdditionalStates().entrySet()) {
  885.                         if (!isAdditionalStateManaged(initial.getKey())) {
  886.                             // this additional state was in the initial state, but is unknown to the propagator
  887.                             // we simply copy its initial value as is
  888.                             unmanaged.put(initial.getKey(), initial.getValue());
  889.                         }
  890.                     }

  891.                     // get the names of additional states managed by differential equations
  892.                     final String[] names = new String[additionalEquations.size()];
  893.                     for (int i = 0; i < names.length; ++i) {
  894.                         names[i] = additionalEquations.get(i).getName();
  895.                     }

  896.                     // create the ephemeris
  897.                     ephemeris = new IntegratedEphemeris(startDate, minDate, maxDate,
  898.                                                         stateMapper, propagationType, model, unmanaged,
  899.                                                         getAdditionalStateProviders(), names);

  900.                 }
  901.             }

  902.         }

  903.         /** {@inheritDoc} */
  904.         public void init(final ODEStateAndDerivative s0, final double t) {
  905.             model.init(s0, t);
  906.             if (this.handler != null) {
  907.                 this.handler.init(s0, t);
  908.             }
  909.         }

  910.     }

  911. }