AbstractIntegratedPropagator.java

  1. /* Copyright 2002-2018 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (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.OrekitExceptionWrapper;
  42. import org.orekit.errors.OrekitIllegalStateException;
  43. import org.orekit.errors.OrekitInternalError;
  44. import org.orekit.errors.OrekitMessages;
  45. import org.orekit.frames.Frame;
  46. import org.orekit.orbits.Orbit;
  47. import org.orekit.orbits.OrbitType;
  48. import org.orekit.orbits.PositionAngle;
  49. import org.orekit.propagation.AbstractPropagator;
  50. import org.orekit.propagation.BoundedPropagator;
  51. import org.orekit.propagation.SpacecraftState;
  52. import org.orekit.propagation.events.EventDetector;
  53. import org.orekit.propagation.events.handlers.EventHandler;
  54. import org.orekit.propagation.sampling.OrekitStepHandler;
  55. import org.orekit.propagation.sampling.OrekitStepInterpolator;
  56. import org.orekit.time.AbsoluteDate;


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

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

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

  66.     /** Mode handler. */
  67.     private ModeHandler modeHandler;

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

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

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

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

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

  78.     /** Output only the mean orbit. <br/>
  79.      * <p>
  80.      * This is used only in the case of semianalitical propagators where there is a clear separation between
  81.      * mean and short periodic elements. It is ignored by the Numerical propagator.
  82.      * </p>
  83.      */
  84.     private boolean meanOrbit;

  85.     /** Build a new instance.
  86.      * @param integrator numerical integrator to use for propagation.
  87.      * @param meanOrbit output only the mean orbit.
  88.      */
  89.     protected AbstractIntegratedPropagator(final ODEIntegrator integrator, final boolean meanOrbit) {
  90.         detectors           = new ArrayList<EventDetector>();
  91.         additionalEquations = new ArrayList<AdditionalEquations>();
  92.         this.integrator     = integrator;
  93.         this.meanOrbit      = meanOrbit;
  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
  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
  132.      */
  133.     protected OrbitType getOrbitType() {
  134.         return stateMapper.getOrbitType();
  135.     }

  136.     /** Check if only the mean elements should be used in a semianalitical propagation.
  137.      * @return true if only mean elements have to be used
  138.      */
  139.     protected boolean isMeanOrbit() {
  140.         return meanOrbit;
  141.     }

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

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

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

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

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

  185.     /** {@inheritDoc} */
  186.     @Override
  187.     public boolean isAdditionalStateManaged(final String name) {

  188.         // first look at already integrated states
  189.         if (super.isAdditionalStateManaged(name)) {
  190.             return true;
  191.         }

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

  198.         return false;
  199.     }

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

  211.     /** Add a set of user-specified equations to be integrated along with the orbit propagation.
  212.      * @param additional additional equations
  213.      * @exception OrekitException if a set of equations with the same name is already present
  214.      */
  215.     public void addAdditionalEquations(final AdditionalEquations additional)
  216.         throws OrekitException {

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

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

  225.     }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  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.      * @exception OrekitException if orbit cannot be propagated
  370.      */
  371.     protected SpacecraftState propagate(final AbsoluteDate tEnd, final boolean activateHandlers)
  372.         throws OrekitException {
  373.         try {

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

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


  382.             // set propagation orbit type
  383.             final Orbit initialOrbit = stateMapper.getOrbitType().convertType(getInitialState().getOrbit());
  384.             if (Double.isNaN(getMu())) {
  385.                 setMu(initialOrbit.getMu());
  386.             }

  387.             if (getInitialState().getMass() <= 0.0) {
  388.                 throw new OrekitException(OrekitMessages.SPACECRAFT_MASS_BECOMES_NEGATIVE,
  389.                                           getInitialState().getMass());
  390.             }

  391.             integrator.clearEventHandlers();

  392.             // set up events added by user, only if handlers are activated
  393.             if (activateHandlers) {
  394.                 setUpUserEventDetectors();
  395.             }

  396.             // convert space flight dynamics API to math API
  397.             final ODEState mathInitialState = createInitialState(getInitialIntegrationState());
  398.             final ExpandableODE mathODE = createODE(integrator, mathInitialState);
  399.             equationsMapper = mathODE.getMapper();

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

  404.             // mathematical integration
  405.             final ODEStateAndDerivative mathFinalState;
  406.             try {
  407.                 beforeIntegration(getInitialState(), tEnd);
  408.                 mathFinalState = integrator.integrate(mathODE, mathInitialState,
  409.                                                       tEnd.durationFrom(getInitialState().getDate()));
  410.                 afterIntegration();
  411.             } catch (OrekitExceptionWrapper oew) {
  412.                 throw oew.getException();
  413.             }

  414.             // get final state
  415.             SpacecraftState finalState =
  416.                             stateMapper.mapArrayToState(stateMapper.mapDoubleToDate(mathFinalState.getTime(),
  417.                                                                                     tEnd),
  418.                                                         mathFinalState.getPrimaryState(),
  419.                                                         mathFinalState.getPrimaryDerivative(),
  420.                                                         meanOrbit);
  421.             finalState = updateAdditionalStates(finalState);
  422.             for (int i = 0; i < additionalEquations.size(); ++i) {
  423.                 final double[] secondary = mathFinalState.getSecondaryState(i + 1);
  424.                 finalState = finalState.addAdditionalState(additionalEquations.get(i).getName(),
  425.                                                            secondary);
  426.             }
  427.             if (resetAtEnd) {
  428.                 resetInitialState(finalState);
  429.                 setStartDate(finalState.getDate());
  430.             }

  431.             return finalState;

  432.         } catch (MathRuntimeException mre) {
  433.             throw OrekitException.unwrap(mre);
  434.         }
  435.     }

  436.     /** Get the initial state for integration.
  437.      * @return initial state for integration
  438.      * @exception OrekitException if initial state cannot be retrieved
  439.      */
  440.     protected SpacecraftState getInitialIntegrationState() throws OrekitException {
  441.         return getInitialState();
  442.     }

  443.     /** Create an initial state.
  444.      * @param initialState initial state in flight dynamics world
  445.      * @return initial state in mathematics world
  446.      * @exception OrekitException if initial state cannot be mapped
  447.      */
  448.     private ODEState createInitialState(final SpacecraftState initialState)
  449.         throws OrekitException {

  450.         // retrieve initial state
  451.         final double[] primary  = new double[getBasicDimension()];
  452.         stateMapper.mapStateToArray(initialState, primary, null);

  453.         // secondary part of the ODE
  454.         final double[][] secondary = new double[additionalEquations.size()][];
  455.         for (int i = 0; i < additionalEquations.size(); ++i) {
  456.             final AdditionalEquations additional = additionalEquations.get(i);
  457.             secondary[i] = initialState.getAdditionalState(additional.getName());
  458.         }

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

  460.     }

  461.     /** Create an ODE with all equations.
  462.      * @param integ numerical integrator to use for propagation.
  463.      * @param mathInitialState initial state
  464.      * @return a new ode
  465.      * @exception OrekitException if initial state cannot be mapped
  466.      */
  467.     private ExpandableODE createODE(final ODEIntegrator integ,
  468.                                     final ODEState mathInitialState)
  469.         throws OrekitException {

  470.         final ExpandableODE ode =
  471.                 new ExpandableODE(new ConvertedMainStateEquations(getMainStateEquations(integ)));

  472.         // secondary part of the ODE
  473.         for (int i = 0; i < additionalEquations.size(); ++i) {
  474.             final AdditionalEquations additional = additionalEquations.get(i);
  475.             final SecondaryODE secondary =
  476.                     new ConvertedSecondaryStateEquations(additional,
  477.                                                          mathInitialState.getSecondaryStateDimension(i + 1));
  478.             ode.addSecondaryEquations(secondary);
  479.         }

  480.         return ode;

  481.     }

  482.     /** Method called just before integration.
  483.      * <p>
  484.      * The default implementation does nothing, it may be specialized in subclasses.
  485.      * </p>
  486.      * @param initialState initial state
  487.      * @param tEnd target date at which state should be propagated
  488.      * @exception OrekitException if hook cannot be run
  489.      */
  490.     protected void beforeIntegration(final SpacecraftState initialState,
  491.                                      final AbsoluteDate tEnd)
  492.         throws OrekitException {
  493.         // do nothing by default
  494.     }

  495.     /** Method called just after integration.
  496.      * <p>
  497.      * The default implementation does nothing, it may be specialized in subclasses.
  498.      * </p>
  499.      * @exception OrekitException if hook cannot be run
  500.      */
  501.     protected void afterIntegration()
  502.         throws OrekitException {
  503.         // do nothing by default
  504.     }

  505.     /** Get state vector dimension without additional parameters.
  506.      * @return state vector dimension without additional parameters.
  507.      */
  508.     public int getBasicDimension() {
  509.         return 7;

  510.     }

  511.     /** Get the integrator used by the propagator.
  512.      * @return the integrator.
  513.      */
  514.     protected ODEIntegrator getIntegrator() {
  515.         return integrator;
  516.     }

  517.     /** Get a complete state with all additional equations.
  518.      * @param t current value of the independent <I>time</I> variable
  519.      * @param y array containing the current value of the state vector
  520.      * @param yDot array containing the current value of the state vector derivative
  521.      * @return complete state
  522.      * @exception OrekitException if state cannot be mapped
  523.      */
  524.     private SpacecraftState getCompleteState(final double t, final double[] y, final double[] yDot)
  525.         throws OrekitException {

  526.         // main state
  527.         SpacecraftState state = stateMapper.mapArrayToState(t, y, yDot, meanOrbit);

  528.         // pre-integrated additional states
  529.         state = updateAdditionalStates(state);

  530.         // additional states integrated here
  531.         if (!additionalEquations.isEmpty()) {

  532.             for (int i = 0; i < additionalEquations.size(); ++i) {
  533.                 state = state.addAdditionalState(additionalEquations.get(i).getName(),
  534.                                                  equationsMapper.extractEquationData(i + 1, y));
  535.             }

  536.         }

  537.         return state;

  538.     }

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

  541.         /**
  542.          * Initialize the equations at the start of propagation. This method will be
  543.          * called before any calls to {@link #computeDerivatives(SpacecraftState)}.
  544.          *
  545.          * <p> The default implementation of this method does nothing.
  546.          *
  547.          * @param initialState initial state information at the start of propagation.
  548.          * @param target       date of propagation. Not equal to {@code
  549.          *                     initialState.getDate()}.
  550.          * @throws OrekitException if there is an Orekit related error during
  551.          *                         initialization.
  552.          */
  553.         default void init(final SpacecraftState initialState, final AbsoluteDate target)
  554.             throws OrekitException {
  555.         }

  556.         /** Compute differential equations for main state.
  557.          * @param state current state
  558.          * @return derivatives of main state
  559.          * @throws OrekitException if differentials cannot be computed
  560.          */
  561.         double[] computeDerivatives(SpacecraftState state) throws OrekitException;

  562.     }

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

  565.         /** Main state equations. */
  566.         private final MainStateEquations main;

  567.         /** Simple constructor.
  568.          * @param main main state equations
  569.          */
  570.         ConvertedMainStateEquations(final MainStateEquations main) {
  571.             this.main = main;
  572.             calls = 0;
  573.         }

  574.         /** {@inheritDoc} */
  575.         public int getDimension() {
  576.             return getBasicDimension();
  577.         }

  578.         @Override
  579.         public void init(final double t0, final double[] y0, final double finalTime) {
  580.             try {
  581.                 // update space dynamics view
  582.                 SpacecraftState initialState = stateMapper.mapArrayToState(t0, y0, null, true);
  583.                 initialState = updateAdditionalStates(initialState);
  584.                 final AbsoluteDate target = stateMapper.mapDoubleToDate(finalTime);
  585.                 main.init(initialState, target);
  586.             } catch (OrekitException oe) {
  587.                 throw new OrekitExceptionWrapper(oe);
  588.             }
  589.         }

  590.         /** {@inheritDoc} */
  591.         public double[] computeDerivatives(final double t, final double[] y)
  592.             throws OrekitExceptionWrapper {
  593.             try {

  594.                 // increment calls counter
  595.                 ++calls;

  596.                 // update space dynamics view
  597.                 SpacecraftState currentState = stateMapper.mapArrayToState(t, y, null, true);
  598.                 currentState = updateAdditionalStates(currentState);

  599.                 // compute main state differentials
  600.                 return main.computeDerivatives(currentState);

  601.             } catch (OrekitException oe) {
  602.                 throw new OrekitExceptionWrapper(oe);
  603.             }
  604.         }

  605.     }

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

  608.         /** Additional equations. */
  609.         private final AdditionalEquations equations;

  610.         /** Dimension of the additional state. */
  611.         private final int dimension;

  612.         /** Simple constructor.
  613.          * @param equations additional equations
  614.          * @param dimension dimension of the additional state
  615.          */
  616.         ConvertedSecondaryStateEquations(final AdditionalEquations equations,
  617.                                          final int dimension) {
  618.             this.equations = equations;
  619.             this.dimension = dimension;
  620.         }

  621.         /** {@inheritDoc} */
  622.         @Override
  623.         public int getDimension() {
  624.             return dimension;
  625.         }

  626.         /** {@inheritDoc} */
  627.         @Override
  628.         public void init(final double t0, final double[] primary0,
  629.                          final double[] secondary0, final double finalTime) {
  630.             try {
  631.                 // update space dynamics view
  632.                 SpacecraftState initialState = stateMapper.mapArrayToState(t0, primary0, null, true);
  633.                 initialState = updateAdditionalStates(initialState);
  634.                 initialState = initialState.addAdditionalState(equations.getName(), secondary0);
  635.                 final AbsoluteDate target = stateMapper.mapDoubleToDate(finalTime);
  636.                 equations.init(initialState, target);
  637.             } catch (OrekitException oe) {
  638.                 throw new OrekitExceptionWrapper(oe);
  639.             }

  640.         }

  641.         /** {@inheritDoc} */
  642.         @Override
  643.         public double[] computeDerivatives(final double t, final double[] primary,
  644.                                            final double[] primaryDot, final double[] secondary)
  645.             throws OrekitExceptionWrapper {

  646.             try {

  647.                 // update space dynamics view
  648.                 SpacecraftState currentState = stateMapper.mapArrayToState(t, primary, primaryDot, true);
  649.                 currentState = updateAdditionalStates(currentState);
  650.                 currentState = currentState.addAdditionalState(equations.getName(), secondary);

  651.                 // compute additional derivatives
  652.                 final double[] secondaryDot = new double[secondary.length];
  653.                 final double[] additionalMainDot =
  654.                         equations.computeDerivatives(currentState, secondaryDot);
  655.                 if (additionalMainDot != null) {
  656.                     // the additional equations have an effect on main equations
  657.                     for (int i = 0; i < additionalMainDot.length; ++i) {
  658.                         primaryDot[i] += additionalMainDot[i];
  659.                     }
  660.                 }
  661.                 return secondaryDot;

  662.             } catch (OrekitException oe) {
  663.                 throw new OrekitExceptionWrapper(oe);
  664.             }

  665.         }

  666.     }

  667.     /** Adapt an {@link org.orekit.propagation.events.EventDetector}
  668.      * to Hipparchus {@link org.hipparchus.ode.events.ODEEventHandler} interface.
  669.      * @param <T> class type for the generic version
  670.      * @author Fabien Maussion
  671.      */
  672.     private class AdaptedEventDetector implements ODEEventHandler {

  673.         /** Underlying event detector. */
  674.         private final EventDetector detector;

  675.         /** Time of the previous call to g. */
  676.         private double lastT;

  677.         /** Value from the previous call to g. */
  678.         private double lastG;

  679.         /** Build a wrapped event detector.
  680.          * @param detector event detector to wrap
  681.         */
  682.         AdaptedEventDetector(final EventDetector detector) {
  683.             this.detector = detector;
  684.             this.lastT    = Double.NaN;
  685.             this.lastG    = Double.NaN;
  686.         }

  687.         /** {@inheritDoc} */
  688.         public void init(final ODEStateAndDerivative s0, final double t) {
  689.             try {

  690.                 detector.init(getCompleteState(s0.getTime(), s0.getCompleteState(), s0.getCompleteDerivative()),
  691.                               stateMapper.mapDoubleToDate(t));
  692.                 this.lastT = Double.NaN;
  693.                 this.lastG = Double.NaN;

  694.             } catch (OrekitException oe) {
  695.                 throw new OrekitExceptionWrapper(oe);
  696.             }
  697.         }

  698.         /** {@inheritDoc} */
  699.         public double g(final ODEStateAndDerivative s) {
  700.             try {
  701.                 if (!Precision.equals(lastT, s.getTime(), 0)) {
  702.                     lastT = s.getTime();
  703.                     lastG = detector.g(getCompleteState(s.getTime(), s.getCompleteState(), s.getCompleteDerivative()));
  704.                 }
  705.                 return lastG;
  706.             } catch (OrekitException oe) {
  707.                 throw new OrekitExceptionWrapper(oe);
  708.             }
  709.         }

  710.         /** {@inheritDoc} */
  711.         public Action eventOccurred(final ODEStateAndDerivative s, final boolean increasing) {
  712.             try {

  713.                 final EventHandler.Action whatNext = detector.eventOccurred(getCompleteState(s.getTime(),
  714.                                                                                              s.getCompleteState(),
  715.                                                                                              s.getCompleteDerivative()),
  716.                                                                             increasing);

  717.                 switch (whatNext) {
  718.                     case STOP :
  719.                         return Action.STOP;
  720.                     case RESET_STATE :
  721.                         return Action.RESET_STATE;
  722.                     case RESET_DERIVATIVES :
  723.                         return Action.RESET_DERIVATIVES;
  724.                     default :
  725.                         return Action.CONTINUE;
  726.                 }
  727.             } catch (OrekitException oe) {
  728.                 throw new OrekitExceptionWrapper(oe);
  729.             }
  730.         }

  731.         /** {@inheritDoc} */
  732.         public ODEState resetState(final ODEStateAndDerivative s) {
  733.             try {

  734.                 final SpacecraftState oldState = getCompleteState(s.getTime(), s.getCompleteState(), s.getCompleteDerivative());
  735.                 final SpacecraftState newState = detector.resetState(oldState);

  736.                 // main part
  737.                 final double[] primary    = new double[s.getPrimaryStateDimension()];
  738.                 stateMapper.mapStateToArray(newState, primary, null);

  739.                 // secondary part
  740.                 final double[][] secondary    = new double[additionalEquations.size()][];
  741.                 for (int i = 0; i < additionalEquations.size(); ++i) {
  742.                     secondary[i] = newState.getAdditionalState(additionalEquations.get(i).getName());
  743.                 }

  744.                 return new ODEState(newState.getDate().durationFrom(getStartDate()),
  745.                                     primary, secondary);

  746.             } catch (OrekitException oe) {
  747.                 throw new OrekitExceptionWrapper(oe);
  748.             }
  749.         }

  750.     }

  751.     /** Adapt an {@link org.orekit.propagation.sampling.OrekitStepHandler}
  752.      * to Hipparchus {@link ODEStepHandler} interface.
  753.      * @author Luc Maisonobe
  754.      */
  755.     private class AdaptedStepHandler implements ODEStepHandler, ModeHandler {

  756.         /** Underlying handler. */
  757.         private final OrekitStepHandler handler;

  758.         /** Flag for handler . */
  759.         private boolean activate;

  760.         /** Build an instance.
  761.          * @param handler underlying handler to wrap
  762.          */
  763.         AdaptedStepHandler(final OrekitStepHandler handler) {
  764.             this.handler = handler;
  765.         }

  766.         /** {@inheritDoc} */
  767.         public void initialize(final boolean activateHandlers,
  768.                                final AbsoluteDate targetDate) {
  769.             this.activate = activateHandlers;
  770.         }

  771.         /** {@inheritDoc} */
  772.         public void init(final ODEStateAndDerivative s0, final double t) {
  773.             try {
  774.                 if (activate) {
  775.                     handler.init(getCompleteState(s0.getTime(), s0.getCompleteState(), s0.getCompleteDerivative()),
  776.                                  stateMapper.mapDoubleToDate(t));
  777.                 }
  778.             } catch (OrekitException oe) {
  779.                 throw new OrekitExceptionWrapper(oe);
  780.             }
  781.         }

  782.         /** {@inheritDoc} */
  783.         public void handleStep(final ODEStateInterpolator interpolator, final boolean isLast) {
  784.             try {
  785.                 if (activate) {
  786.                     handler.handleStep(new AdaptedStepInterpolator(interpolator), isLast);
  787.                 }
  788.             } catch (OrekitException pe) {
  789.                 throw new OrekitExceptionWrapper(pe);
  790.             }
  791.         }

  792.     }

  793.     /** Adapt an Hipparchus {@link ODEStateInterpolator}
  794.      * to an orekit {@link OrekitStepInterpolator} interface.
  795.      * @author Luc Maisonobe
  796.      */
  797.     private class AdaptedStepInterpolator implements OrekitStepInterpolator {

  798.         /** Underlying raw rawInterpolator. */
  799.         private final ODEStateInterpolator mathInterpolator;

  800.         /** Simple constructor.
  801.          * @param mathInterpolator underlying raw interpolator
  802.          */
  803.         AdaptedStepInterpolator(final ODEStateInterpolator mathInterpolator) {
  804.             this.mathInterpolator = mathInterpolator;
  805.         }

  806.         /** {@inheritDoc}} */
  807.         @Override
  808.         public SpacecraftState getPreviousState()
  809.             throws OrekitException {
  810.             return convert(mathInterpolator.getPreviousState());
  811.         }

  812.         /** {@inheritDoc}} */
  813.         @Override
  814.         public boolean isPreviousStateInterpolated() {
  815.             return mathInterpolator.isPreviousStateInterpolated();
  816.         }

  817.         /** {@inheritDoc}} */
  818.         @Override
  819.         public SpacecraftState getCurrentState()
  820.             throws OrekitException {
  821.             return convert(mathInterpolator.getCurrentState());
  822.         }

  823.         /** {@inheritDoc}} */
  824.         @Override
  825.         public boolean isCurrentStateInterpolated() {
  826.             return mathInterpolator.isCurrentStateInterpolated();
  827.         }

  828.         /** {@inheritDoc}} */
  829.         @Override
  830.         public SpacecraftState getInterpolatedState(final AbsoluteDate date)
  831.             throws OrekitException {
  832.             return convert(mathInterpolator.getInterpolatedState(date.durationFrom(stateMapper.getReferenceDate())));
  833.         }

  834.         /** Convert a state from mathematical world to space flight dynamics world.
  835.          * @param os mathematical state
  836.          * @return space flight dynamics state
  837.          * @exception OrekitException if arrays are inconsistent
  838.          */
  839.         private SpacecraftState convert(final ODEStateAndDerivative os)
  840.             throws OrekitException {

  841.             SpacecraftState s =
  842.                             stateMapper.mapArrayToState(os.getTime(),
  843.                                                         os.getPrimaryState(),
  844.                                                         os.getPrimaryDerivative(),
  845.                                                         meanOrbit);
  846.             s = updateAdditionalStates(s);
  847.             for (int i = 0; i < additionalEquations.size(); ++i) {
  848.                 final double[] secondary = os.getSecondaryState(i + 1);
  849.                 s = s.addAdditionalState(additionalEquations.get(i).getName(), secondary);
  850.             }

  851.             return s;

  852.         }

  853.         /** Convert a state from space flight dynamics world to mathematical world.
  854.          * @param state space flight dynamics state
  855.          * @return mathematical state
  856.          * @exception OrekitException if arrays are inconsistent
  857.          */
  858.         private ODEStateAndDerivative convert(final SpacecraftState state)
  859.             throws OrekitException {

  860.             // retrieve initial state
  861.             final double[] primary    = new double[getBasicDimension()];
  862.             final double[] primaryDot = new double[getBasicDimension()];
  863.             stateMapper.mapStateToArray(state, primary, primaryDot);

  864.             // secondary part of the ODE
  865.             final double[][] secondary    = new double[additionalEquations.size()][];
  866.             for (int i = 0; i < additionalEquations.size(); ++i) {
  867.                 final AdditionalEquations additional = additionalEquations.get(i);
  868.                 secondary[i] = state.getAdditionalState(additional.getName());
  869.             }

  870.             return new ODEStateAndDerivative(stateMapper.mapDateToDouble(state.getDate()),
  871.                                              primary, primaryDot,
  872.                                              secondary, null);

  873.         }

  874.         /** {@inheritDoc}} */
  875.         @Override
  876.         public boolean isForward() {
  877.             return mathInterpolator.isForward();
  878.         }

  879.         /** {@inheritDoc}} */
  880.         @Override
  881.         public AdaptedStepInterpolator restrictStep(final SpacecraftState newPreviousState,
  882.                                                     final SpacecraftState newCurrentState)
  883.             throws OrekitException {
  884.             try {
  885.                 final AbstractODEStateInterpolator aosi = (AbstractODEStateInterpolator) mathInterpolator;
  886.                 return new AdaptedStepInterpolator(aosi.restrictStep(convert(newPreviousState),
  887.                                                                      convert(newCurrentState)));
  888.             } catch (ClassCastException cce) {
  889.                 // this should never happen
  890.                 throw new OrekitInternalError(cce);
  891.             }
  892.         }

  893.     }

  894.     private class EphemerisModeHandler implements ModeHandler, ODEStepHandler {

  895.         /** Underlying raw mathematical model. */
  896.         private DenseOutputModel model;

  897.         /** Generated ephemeris. */
  898.         private BoundedPropagator ephemeris;

  899.         /** Flag for handler . */
  900.         private boolean activate;

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

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

  905.         /** Creates a new instance of EphemerisModeHandler which must be
  906.          *  filled by the propagator.
  907.          */
  908.         EphemerisModeHandler() {
  909.             this.handler = null;
  910.         }

  911.         /** Creates a new instance of EphemerisModeHandler which must be
  912.          *  filled by the propagator.
  913.          *  @param handler the handler to notify of every integrator step.
  914.          */
  915.         EphemerisModeHandler(final OrekitStepHandler handler) {
  916.             this.handler = new AdaptedStepHandler(handler);
  917.         }

  918.         /** {@inheritDoc} */
  919.         public void initialize(final boolean activateHandlers,
  920.                                final AbsoluteDate targetDate) {
  921.             this.activate = activateHandlers;
  922.             this.model    = new DenseOutputModel();
  923.             this.endDate  = targetDate;

  924.             // ephemeris will be generated when last step is processed
  925.             this.ephemeris = null;
  926.             if (this.handler != null) {
  927.                 this.handler.initialize(activateHandlers, targetDate);
  928.             }
  929.         }

  930.         /** Get the generated ephemeris.
  931.          * @return a new instance of the generated ephemeris
  932.          */
  933.         public BoundedPropagator getEphemeris() {
  934.             return ephemeris;
  935.         }

  936.         /** {@inheritDoc} */
  937.         public void handleStep(final ODEStateInterpolator interpolator, final boolean isLast)
  938.             throws OrekitExceptionWrapper {
  939.             try {
  940.                 if (activate) {
  941.                     if (this.handler != null) {
  942.                         this.handler.handleStep(interpolator, isLast);
  943.                     }

  944.                     model.handleStep(interpolator, isLast);
  945.                     if (isLast) {

  946.                         // set up the boundary dates
  947.                         final double tI = model.getInitialTime();
  948.                         final double tF = model.getFinalTime();
  949.                         // tI is almost? always zero
  950.                         final AbsoluteDate startDate =
  951.                                 stateMapper.mapDoubleToDate(tI);
  952.                         final AbsoluteDate finalDate =
  953.                                 stateMapper.mapDoubleToDate(tF, this.endDate);
  954.                         final AbsoluteDate minDate;
  955.                         final AbsoluteDate maxDate;
  956.                         if (tF < tI) {
  957.                             minDate = finalDate;
  958.                             maxDate = startDate;
  959.                         } else {
  960.                             minDate = startDate;
  961.                             maxDate = finalDate;
  962.                         }

  963.                         // get the initial additional states that are not managed
  964.                         final Map<String, double[]> unmanaged = new HashMap<String, double[]>();
  965.                         for (final Map.Entry<String, double[]> initial : getInitialState().getAdditionalStates().entrySet()) {
  966.                             if (!isAdditionalStateManaged(initial.getKey())) {
  967.                                 // this additional state was in the initial state, but is unknown to the propagator
  968.                                 // we simply copy its initial value as is
  969.                                 unmanaged.put(initial.getKey(), initial.getValue());
  970.                             }
  971.                         }

  972.                         // get the names of additional states managed by differential equations
  973.                         final String[] names = new String[additionalEquations.size()];
  974.                         for (int i = 0; i < names.length; ++i) {
  975.                             names[i] = additionalEquations.get(i).getName();
  976.                         }

  977.                         // create the ephemeris
  978.                         ephemeris = new IntegratedEphemeris(startDate, minDate, maxDate,
  979.                                                             stateMapper, meanOrbit, model, unmanaged,
  980.                                                             getAdditionalStateProviders(), names);

  981.                     }
  982.                 }
  983.             } catch (OrekitException oe) {
  984.                 throw new OrekitExceptionWrapper(oe);
  985.             }
  986.         }

  987.         /** {@inheritDoc} */
  988.         public void init(final ODEStateAndDerivative s0, final double t) {
  989.             model.init(s0, t);
  990.             if (this.handler != null) {
  991.                 this.handler.init(s0, t);
  992.             }
  993.         }

  994.     }

  995. }