AbstractIntegratedPropagator.java

  1. /* Copyright 2002-2019 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.OrekitIllegalStateException;
  42. import org.orekit.errors.OrekitInternalError;
  43. import org.orekit.errors.OrekitMessages;
  44. import org.orekit.frames.Frame;
  45. import org.orekit.orbits.Orbit;
  46. import org.orekit.orbits.OrbitType;
  47. import org.orekit.orbits.PositionAngle;
  48. import org.orekit.propagation.AbstractPropagator;
  49. import org.orekit.propagation.BoundedPropagator;
  50. import org.orekit.propagation.SpacecraftState;
  51. import org.orekit.propagation.events.EventDetector;
  52. import org.orekit.propagation.events.handlers.EventHandler;
  53. import org.orekit.propagation.sampling.OrekitStepHandler;
  54. import org.orekit.propagation.sampling.OrekitStepInterpolator;
  55. import org.orekit.time.AbsoluteDate;


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

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

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

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

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

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

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

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

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

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

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

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

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

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

  121.     /** Set propagation orbit type.
  122.      * @param orbitType orbit type to use for propagation
  123.      */
  124.     protected void setOrbitType(final OrbitType orbitType) {
  125.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  126.                                    orbitType, stateMapper.getPositionAngleType(),
  127.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  128.     }

  129.     /** Get propagation parameter type.
  130.      * @return orbit type used for propagation
  131.      */
  132.     protected OrbitType getOrbitType() {
  133.         return stateMapper.getOrbitType();
  134.     }

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

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

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

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

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

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

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

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

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

  197.         return false;
  198.     }

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

  210.     /** Add a set of user-specified equations to be integrated along with the orbit propagation.
  211.      * @param additional additional equations
  212.      */
  213.     public void addAdditionalEquations(final AdditionalEquations additional) {

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

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

  222.     }

  223.     /** {@inheritDoc} */
  224.     public void addEventDetector(final EventDetector detector) {
  225.         detectors.add(detector);
  226.     }

  227.     /** {@inheritDoc} */
  228.     public Collection<EventDetector> getEventsDetectors() {
  229.         return Collections.unmodifiableCollection(detectors);
  230.     }

  231.     /** {@inheritDoc} */
  232.     public void clearEventsDetectors() {
  233.         detectors.clear();
  234.     }

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

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

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

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

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

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

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

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

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

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

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

  350.         if (getInitialState() == null) {
  351.             throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  352.         }

  353.         if (!tStart.equals(getInitialState().getDate())) {
  354.             // if propagation start date is not initial date,
  355.             // propagate from initial to start date without event detection
  356.             propagate(tStart, false);
  357.         }

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

  360.     }

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

  368.             if (getInitialState().getDate().equals(tEnd)) {
  369.                 // don't extrapolate
  370.                 return getInitialState();
  371.             }

  372.             // space dynamics view
  373.             stateMapper = createMapper(getInitialState().getDate(), stateMapper.getMu(),
  374.                                        stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  375.                                        stateMapper.getAttitudeProvider(), getInitialState().getFrame());


  376.             // set propagation orbit type
  377.             final Orbit initialOrbit = stateMapper.getOrbitType().convertType(getInitialState().getOrbit());
  378.             if (Double.isNaN(getMu())) {
  379.                 setMu(initialOrbit.getMu());
  380.             }

  381.             if (getInitialState().getMass() <= 0.0) {
  382.                 throw new OrekitException(OrekitMessages.SPACECRAFT_MASS_BECOMES_NEGATIVE,
  383.                                           getInitialState().getMass());
  384.             }

  385.             integrator.clearEventHandlers();

  386.             // set up events added by user, only if handlers are activated
  387.             if (activateHandlers) {
  388.                 setUpUserEventDetectors();
  389.             }

  390.             // convert space flight dynamics API to math API
  391.             final ODEState mathInitialState = createInitialState(getInitialIntegrationState());
  392.             final ExpandableODE mathODE = createODE(integrator, mathInitialState);
  393.             equationsMapper = mathODE.getMapper();

  394.             // initialize mode handler
  395.             if (modeHandler != null) {
  396.                 modeHandler.initialize(activateHandlers, tEnd);
  397.             }

  398.             // mathematical integration
  399.             final ODEStateAndDerivative mathFinalState;
  400.             beforeIntegration(getInitialState(), tEnd);
  401.             mathFinalState = integrator.integrate(mathODE, mathInitialState,
  402.                                                   tEnd.durationFrom(getInitialState().getDate()));
  403.             afterIntegration();

  404.             // get final state
  405.             SpacecraftState finalState =
  406.                             stateMapper.mapArrayToState(stateMapper.mapDoubleToDate(mathFinalState.getTime(),
  407.                                                                                     tEnd),
  408.                                                         mathFinalState.getPrimaryState(),
  409.                                                         mathFinalState.getPrimaryDerivative(),
  410.                                                         meanOrbit);
  411.             finalState = updateAdditionalStates(finalState);
  412.             for (int i = 0; i < additionalEquations.size(); ++i) {
  413.                 final double[] secondary = mathFinalState.getSecondaryState(i + 1);
  414.                 finalState = finalState.addAdditionalState(additionalEquations.get(i).getName(),
  415.                                                            secondary);
  416.             }
  417.             if (resetAtEnd) {
  418.                 resetInitialState(finalState);
  419.                 setStartDate(finalState.getDate());
  420.             }

  421.             return finalState;

  422.         } catch (MathRuntimeException mre) {
  423.             throw OrekitException.unwrap(mre);
  424.         }
  425.     }

  426.     /** Get the initial state for integration.
  427.      * @return initial state for integration
  428.      */
  429.     protected SpacecraftState getInitialIntegrationState() {
  430.         return getInitialState();
  431.     }

  432.     /** Create an initial state.
  433.      * @param initialState initial state in flight dynamics world
  434.      * @return initial state in mathematics world
  435.      */
  436.     private ODEState createInitialState(final SpacecraftState initialState) {

  437.         // retrieve initial state
  438.         final double[] primary  = new double[getBasicDimension()];
  439.         stateMapper.mapStateToArray(initialState, primary, null);

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

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

  447.     }

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

  455.         final ExpandableODE ode =
  456.                 new ExpandableODE(new ConvertedMainStateEquations(getMainStateEquations(integ)));

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

  465.         return ode;

  466.     }

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

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

  486.     /** Get state vector dimension without additional parameters.
  487.      * @return state vector dimension without additional parameters.
  488.      */
  489.     public int getBasicDimension() {
  490.         return 7;

  491.     }

  492.     /** Get the integrator used by the propagator.
  493.      * @return the integrator.
  494.      */
  495.     protected ODEIntegrator getIntegrator() {
  496.         return integrator;
  497.     }

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

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

  507.         // pre-integrated additional states
  508.         state = updateAdditionalStates(state);

  509.         // additional states integrated here
  510.         if (!additionalEquations.isEmpty()) {

  511.             for (int i = 0; i < additionalEquations.size(); ++i) {
  512.                 state = state.addAdditionalState(additionalEquations.get(i).getName(),
  513.                                                  equationsMapper.extractEquationData(i + 1, y));
  514.             }

  515.         }

  516.         return state;

  517.     }

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

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

  532.         /** Compute differential equations for main state.
  533.          * @param state current state
  534.          * @return derivatives of main state
  535.          */
  536.         double[] computeDerivatives(SpacecraftState state);

  537.     }

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

  540.         /** Main state equations. */
  541.         private final MainStateEquations main;

  542.         /** Simple constructor.
  543.          * @param main main state equations
  544.          */
  545.         ConvertedMainStateEquations(final MainStateEquations main) {
  546.             this.main = main;
  547.             calls = 0;
  548.         }

  549.         /** {@inheritDoc} */
  550.         public int getDimension() {
  551.             return getBasicDimension();
  552.         }

  553.         @Override
  554.         public void init(final double t0, final double[] y0, final double finalTime) {
  555.             // update space dynamics view
  556.             SpacecraftState initialState = stateMapper.mapArrayToState(t0, y0, null, true);
  557.             initialState = updateAdditionalStates(initialState);
  558.             final AbsoluteDate target = stateMapper.mapDoubleToDate(finalTime);
  559.             main.init(initialState, target);
  560.         }

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

  563.             // increment calls counter
  564.             ++calls;

  565.             // update space dynamics view
  566.             SpacecraftState currentState = stateMapper.mapArrayToState(t, y, null, true);
  567.             currentState = updateAdditionalStates(currentState);

  568.             // compute main state differentials
  569.             return main.computeDerivatives(currentState);

  570.         }

  571.     }

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

  574.         /** Additional equations. */
  575.         private final AdditionalEquations equations;

  576.         /** Dimension of the additional state. */
  577.         private final int dimension;

  578.         /** Simple constructor.
  579.          * @param equations additional equations
  580.          * @param dimension dimension of the additional state
  581.          */
  582.         ConvertedSecondaryStateEquations(final AdditionalEquations equations,
  583.                                          final int dimension) {
  584.             this.equations = equations;
  585.             this.dimension = dimension;
  586.         }

  587.         /** {@inheritDoc} */
  588.         @Override
  589.         public int getDimension() {
  590.             return dimension;
  591.         }

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

  602.         }

  603.         /** {@inheritDoc} */
  604.         @Override
  605.         public double[] computeDerivatives(final double t, final double[] primary,
  606.                                            final double[] primaryDot, final double[] secondary) {

  607.             // update space dynamics view
  608.             SpacecraftState currentState = stateMapper.mapArrayToState(t, primary, primaryDot, true);
  609.             currentState = updateAdditionalStates(currentState);
  610.             currentState = currentState.addAdditionalState(equations.getName(), secondary);

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

  622.         }

  623.     }

  624.     /** Adapt an {@link org.orekit.propagation.events.EventDetector}
  625.      * to Hipparchus {@link org.hipparchus.ode.events.ODEEventHandler} interface.
  626.      * @param <T> class type for the generic version
  627.      * @author Fabien Maussion
  628.      */
  629.     private class AdaptedEventDetector implements ODEEventHandler {

  630.         /** Underlying event detector. */
  631.         private final EventDetector detector;

  632.         /** Time of the previous call to g. */
  633.         private double lastT;

  634.         /** Value from the previous call to g. */
  635.         private double lastG;

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

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

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

  659.         /** {@inheritDoc} */
  660.         public Action eventOccurred(final ODEStateAndDerivative s, final boolean increasing) {
  661.             final EventHandler.Action whatNext = detector.eventOccurred(getCompleteState(s.getTime(),
  662.                                                                                          s.getCompleteState(),
  663.                                                                                          s.getCompleteDerivative()),
  664.                                                                         increasing);

  665.             switch (whatNext) {
  666.                 case STOP :
  667.                     return Action.STOP;
  668.                 case RESET_STATE :
  669.                     return Action.RESET_STATE;
  670.                 case RESET_DERIVATIVES :
  671.                     return Action.RESET_DERIVATIVES;
  672.                 default :
  673.                     return Action.CONTINUE;
  674.             }
  675.         }

  676.         /** {@inheritDoc} */
  677.         public ODEState resetState(final ODEStateAndDerivative s) {

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

  680.             // main part
  681.             final double[] primary    = new double[s.getPrimaryStateDimension()];
  682.             stateMapper.mapStateToArray(newState, primary, null);

  683.             // secondary part
  684.             final double[][] secondary    = new double[additionalEquations.size()][];
  685.             for (int i = 0; i < additionalEquations.size(); ++i) {
  686.                 secondary[i] = newState.getAdditionalState(additionalEquations.get(i).getName());
  687.             }

  688.             return new ODEState(newState.getDate().durationFrom(getStartDate()),
  689.                                 primary, secondary);

  690.         }

  691.     }

  692.     /** Adapt an {@link org.orekit.propagation.sampling.OrekitStepHandler}
  693.      * to Hipparchus {@link ODEStepHandler} interface.
  694.      * @author Luc Maisonobe
  695.      */
  696.     private class AdaptedStepHandler implements ODEStepHandler, ModeHandler {

  697.         /** Underlying handler. */
  698.         private final OrekitStepHandler handler;

  699.         /** Flag for handler . */
  700.         private boolean activate;

  701.         /** Build an instance.
  702.          * @param handler underlying handler to wrap
  703.          */
  704.         AdaptedStepHandler(final OrekitStepHandler handler) {
  705.             this.handler = handler;
  706.         }

  707.         /** {@inheritDoc} */
  708.         public void initialize(final boolean activateHandlers,
  709.                                final AbsoluteDate targetDate) {
  710.             this.activate = activateHandlers;
  711.         }

  712.         /** {@inheritDoc} */
  713.         public void init(final ODEStateAndDerivative s0, final double t) {
  714.             if (activate) {
  715.                 handler.init(getCompleteState(s0.getTime(), s0.getCompleteState(), s0.getCompleteDerivative()),
  716.                              stateMapper.mapDoubleToDate(t));
  717.             }
  718.         }

  719.         /** {@inheritDoc} */
  720.         public void handleStep(final ODEStateInterpolator interpolator, final boolean isLast) {
  721.             if (activate) {
  722.                 handler.handleStep(new AdaptedStepInterpolator(interpolator), isLast);
  723.             }
  724.         }

  725.     }

  726.     /** Adapt an Hipparchus {@link ODEStateInterpolator}
  727.      * to an orekit {@link OrekitStepInterpolator} interface.
  728.      * @author Luc Maisonobe
  729.      */
  730.     private class AdaptedStepInterpolator implements OrekitStepInterpolator {

  731.         /** Underlying raw rawInterpolator. */
  732.         private final ODEStateInterpolator mathInterpolator;

  733.         /** Simple constructor.
  734.          * @param mathInterpolator underlying raw interpolator
  735.          */
  736.         AdaptedStepInterpolator(final ODEStateInterpolator mathInterpolator) {
  737.             this.mathInterpolator = mathInterpolator;
  738.         }

  739.         /** {@inheritDoc}} */
  740.         @Override
  741.         public SpacecraftState getPreviousState() {
  742.             return convert(mathInterpolator.getPreviousState());
  743.         }

  744.         /** {@inheritDoc}} */
  745.         @Override
  746.         public boolean isPreviousStateInterpolated() {
  747.             return mathInterpolator.isPreviousStateInterpolated();
  748.         }

  749.         /** {@inheritDoc}} */
  750.         @Override
  751.         public SpacecraftState getCurrentState() {
  752.             return convert(mathInterpolator.getCurrentState());
  753.         }

  754.         /** {@inheritDoc}} */
  755.         @Override
  756.         public boolean isCurrentStateInterpolated() {
  757.             return mathInterpolator.isCurrentStateInterpolated();
  758.         }

  759.         /** {@inheritDoc}} */
  760.         @Override
  761.         public SpacecraftState getInterpolatedState(final AbsoluteDate date) {
  762.             return convert(mathInterpolator.getInterpolatedState(date.durationFrom(stateMapper.getReferenceDate())));
  763.         }

  764.         /** Convert a state from mathematical world to space flight dynamics world.
  765.          * @param os mathematical state
  766.          * @return space flight dynamics state
  767.          */
  768.         private SpacecraftState convert(final ODEStateAndDerivative os) {

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

  779.             return s;

  780.         }

  781.         /** Convert a state from space flight dynamics world to mathematical world.
  782.          * @param state space flight dynamics state
  783.          * @return mathematical state
  784.          */
  785.         private ODEStateAndDerivative convert(final SpacecraftState state) {

  786.             // retrieve initial state
  787.             final double[] primary    = new double[getBasicDimension()];
  788.             final double[] primaryDot = new double[getBasicDimension()];
  789.             stateMapper.mapStateToArray(state, primary, primaryDot);

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

  796.             return new ODEStateAndDerivative(stateMapper.mapDateToDouble(state.getDate()),
  797.                                              primary, primaryDot,
  798.                                              secondary, null);

  799.         }

  800.         /** {@inheritDoc}} */
  801.         @Override
  802.         public boolean isForward() {
  803.             return mathInterpolator.isForward();
  804.         }

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

  818.     }

  819.     private class EphemerisModeHandler implements ModeHandler, ODEStepHandler {

  820.         /** Underlying raw mathematical model. */
  821.         private DenseOutputModel model;

  822.         /** Generated ephemeris. */
  823.         private BoundedPropagator ephemeris;

  824.         /** Flag for handler . */
  825.         private boolean activate;

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

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

  830.         /** Creates a new instance of EphemerisModeHandler which must be
  831.          *  filled by the propagator.
  832.          */
  833.         EphemerisModeHandler() {
  834.             this.handler = null;
  835.         }

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

  843.         /** {@inheritDoc} */
  844.         public void initialize(final boolean activateHandlers,
  845.                                final AbsoluteDate targetDate) {
  846.             this.activate = activateHandlers;
  847.             this.model    = new DenseOutputModel();
  848.             this.endDate  = targetDate;

  849.             // ephemeris will be generated when last step is processed
  850.             this.ephemeris = null;
  851.             if (this.handler != null) {
  852.                 this.handler.initialize(activateHandlers, targetDate);
  853.             }
  854.         }

  855.         /** Get the generated ephemeris.
  856.          * @return a new instance of the generated ephemeris
  857.          */
  858.         public BoundedPropagator getEphemeris() {
  859.             return ephemeris;
  860.         }

  861.         /** {@inheritDoc} */
  862.         public void handleStep(final ODEStateInterpolator interpolator, final boolean isLast) {
  863.             if (activate) {
  864.                 if (this.handler != null) {
  865.                     this.handler.handleStep(interpolator, isLast);
  866.                 }

  867.                 model.handleStep(interpolator, isLast);
  868.                 if (isLast) {

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

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

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

  900.                     // create the ephemeris
  901.                     ephemeris = new IntegratedEphemeris(startDate, minDate, maxDate,
  902.                                                         stateMapper, meanOrbit, model, unmanaged,
  903.                                                         getAdditionalStateProviders(), names);

  904.                 }
  905.             }
  906.         }

  907.         /** {@inheritDoc} */
  908.         public void init(final ODEStateAndDerivative s0, final double t) {
  909.             model.init(s0, t);
  910.             if (this.handler != null) {
  911.                 this.handler.init(s0, t);
  912.             }
  913.         }

  914.     }

  915. }