AbstractIntegratedPropagator.java

  1. /* Copyright 2002-2016 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.apache.commons.math3.exception.MathIllegalArgumentException;
  25. import org.apache.commons.math3.exception.MathIllegalStateException;
  26. import org.apache.commons.math3.ode.AbstractIntegrator;
  27. import org.apache.commons.math3.ode.ContinuousOutputModel;
  28. import org.apache.commons.math3.ode.EquationsMapper;
  29. import org.apache.commons.math3.ode.ExpandableStatefulODE;
  30. import org.apache.commons.math3.ode.FirstOrderDifferentialEquations;
  31. import org.apache.commons.math3.ode.SecondaryEquations;
  32. import org.apache.commons.math3.ode.sampling.StepHandler;
  33. import org.apache.commons.math3.ode.sampling.StepInterpolator;
  34. import org.apache.commons.math3.util.Precision;
  35. import org.orekit.attitudes.AttitudeProvider;
  36. import org.orekit.errors.OrekitException;
  37. import org.orekit.errors.OrekitExceptionWrapper;
  38. import org.orekit.errors.OrekitIllegalStateException;
  39. import org.orekit.errors.OrekitMessages;
  40. import org.orekit.errors.PropagationException;
  41. import org.orekit.frames.Frame;
  42. import org.orekit.orbits.Orbit;
  43. import org.orekit.orbits.OrbitType;
  44. import org.orekit.orbits.PositionAngle;
  45. import org.orekit.propagation.AbstractPropagator;
  46. import org.orekit.propagation.BoundedPropagator;
  47. import org.orekit.propagation.SpacecraftState;
  48. import org.orekit.propagation.events.EventDetector;
  49. import org.orekit.propagation.events.handlers.EventHandler;
  50. import org.orekit.propagation.sampling.OrekitStepHandler;
  51. import org.orekit.propagation.sampling.OrekitStepInterpolator;
  52. import org.orekit.time.AbsoluteDate;


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

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

  60.     /** Integrator selected by the user for the orbital extrapolation process. */
  61.     private final AbstractIntegrator integrator;

  62.     /** Mode handler. */
  63.     private ModeHandler modeHandler;

  64.     /** Additional equations. */
  65.     private List<AdditionalEquations> additionalEquations;

  66.     /** Counter for differential equations calls. */
  67.     private int calls;

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

  70.     /** Complete equation to be integrated. */
  71.     private ExpandableStatefulODE mathODE;

  72.     /** Underlying raw rawInterpolator. */
  73.     private StepInterpolator mathInterpolator;

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

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

  91.     /** Initialize the mapper. */
  92.     protected void initMapper() {
  93.         stateMapper = createMapper(null, Double.NaN, null, null, null, null);
  94.     }

  95.     /**  {@inheritDoc} */
  96.     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
  97.         super.setAttitudeProvider(attitudeProvider);
  98.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  99.                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  100.                                    attitudeProvider, stateMapper.getFrame());
  101.     }

  102.     /** Set propagation orbit type.
  103.      * @param orbitType orbit type to use for propagation
  104.      */
  105.     protected void setOrbitType(final OrbitType orbitType) {
  106.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  107.                                    orbitType, stateMapper.getPositionAngleType(),
  108.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  109.     }

  110.     /** Get propagation parameter type.
  111.      * @return orbit type used for propagation
  112.      */
  113.     protected OrbitType getOrbitType() {
  114.         return stateMapper.getOrbitType();
  115.     }

  116.     /** Check if only the mean elements should be used in a semianalitical propagation.
  117.      * @return true if only mean elements have to be used
  118.      */
  119.     protected boolean isMeanOrbit() {
  120.         return meanOrbit;
  121.     }

  122.     /** Set position angle type.
  123.      * <p>
  124.      * The position parameter type is meaningful only if {@link
  125.      * #getOrbitType() propagation orbit type}
  126.      * support it. As an example, it is not meaningful for propagation
  127.      * in {@link OrbitType#CARTESIAN Cartesian} parameters.
  128.      * </p>
  129.      * @param positionAngleType angle type to use for propagation
  130.      */
  131.     protected void setPositionAngleType(final PositionAngle positionAngleType) {
  132.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  133.                                    stateMapper.getOrbitType(), positionAngleType,
  134.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  135.     }

  136.     /** Get propagation parameter type.
  137.      * @return angle type to use for propagation
  138.      */
  139.     protected PositionAngle getPositionAngleType() {
  140.         return stateMapper.getPositionAngleType();
  141.     }

  142.     /** Set the central attraction coefficient μ.
  143.      * @param mu central attraction coefficient (m³/s²)
  144.      */
  145.     public void setMu(final double mu) {
  146.         stateMapper = createMapper(stateMapper.getReferenceDate(), mu,
  147.                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  148.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  149.     }

  150.     /** Get the central attraction coefficient μ.
  151.      * @return mu central attraction coefficient (m³/s²)
  152.      * @see #setMu(double)
  153.      */
  154.     public double getMu() {
  155.         return stateMapper.getMu();
  156.     }

  157.     /** Get the number of calls to the differential equations computation method.
  158.      * <p>The number of calls is reset each time the {@link #propagate(AbsoluteDate)}
  159.      * method is called.</p>
  160.      * @return number of calls to the differential equations computation method
  161.      */
  162.     public int getCalls() {
  163.         return calls;
  164.     }

  165.     /** {@inheritDoc} */
  166.     @Override
  167.     public boolean isAdditionalStateManaged(final String name) {

  168.         // first look at already integrated states
  169.         if (super.isAdditionalStateManaged(name)) {
  170.             return true;
  171.         }

  172.         // then look at states we integrate ourselves
  173.         for (final AdditionalEquations equation : additionalEquations) {
  174.             if (equation.getName().equals(name)) {
  175.                 return true;
  176.             }
  177.         }

  178.         return false;
  179.     }

  180.     /** {@inheritDoc} */
  181.     @Override
  182.     public String[] getManagedAdditionalStates() {
  183.         final String[] alreadyIntegrated = super.getManagedAdditionalStates();
  184.         final String[] managed = new String[alreadyIntegrated.length + additionalEquations.size()];
  185.         System.arraycopy(alreadyIntegrated, 0, managed, 0, alreadyIntegrated.length);
  186.         for (int i = 0; i < additionalEquations.size(); ++i) {
  187.             managed[i + alreadyIntegrated.length] = additionalEquations.get(i).getName();
  188.         }
  189.         return managed;
  190.     }

  191.     /** Add a set of user-specified equations to be integrated along with the orbit propagation.
  192.      * @param additional additional equations
  193.      * @exception OrekitException if a set of equations with the same name is already present
  194.      */
  195.     public void addAdditionalEquations(final AdditionalEquations additional)
  196.         throws OrekitException {

  197.         // check if the name is already used
  198.         if (isAdditionalStateManaged(additional.getName())) {
  199.             // this set of equations is already registered, complain
  200.             throw new OrekitException(OrekitMessages.ADDITIONAL_STATE_NAME_ALREADY_IN_USE,
  201.                                       additional.getName());
  202.         }

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

  205.     }

  206.     /** {@inheritDoc} */
  207.     public void addEventDetector(final EventDetector detector) {
  208.         detectors.add(detector);
  209.     }

  210.     /** {@inheritDoc} */
  211.     public Collection<EventDetector> getEventsDetectors() {
  212.         return Collections.unmodifiableCollection(detectors);
  213.     }

  214.     /** {@inheritDoc} */
  215.     public void clearEventsDetectors() {
  216.         detectors.clear();
  217.     }

  218.     /** Set up all user defined event detectors.
  219.      */
  220.     protected void setUpUserEventDetectors() {
  221.         for (final EventDetector detector : detectors) {
  222.             setUpEventDetector(integrator, detector);
  223.         }
  224.     }

  225.     /** Wrap an Orekit event detector and register it to the integrator.
  226.      * @param integ integrator into which event detector should be registered
  227.      * @param detector event detector to wrap
  228.      */
  229.     protected void setUpEventDetector(final AbstractIntegrator integ, final EventDetector detector) {
  230.         integ.addEventHandler(new AdaptedEventDetector(detector),
  231.                               detector.getMaxCheckInterval(),
  232.                               detector.getThreshold(),
  233.                               detector.getMaxIterationCount());
  234.     }

  235.     /** {@inheritDoc}
  236.      * <p>Note that this method has the side effect of replacing the step handlers
  237.      * of the underlying integrator set up in the {@link
  238.      * #AbstractIntegratedPropagator(AbstractIntegrator, boolean) constructor}. So if a specific
  239.      * step handler is needed, it should be added after this method has been callled.</p>
  240.      */
  241.     public void setSlaveMode() {
  242.         super.setSlaveMode();
  243.         if (integrator != null) {
  244.             integrator.clearStepHandlers();
  245.         }
  246.         modeHandler = null;
  247.     }

  248.     /** {@inheritDoc}
  249.      * <p>Note that this method has the side effect of replacing the step handlers
  250.      * of the underlying integrator set up in the {@link
  251.      * #AbstractIntegratedPropagator(AbstractIntegrator, boolean) constructor}. So if a specific
  252.      * step handler is needed, it should be added after this method has been callled.</p>
  253.      */
  254.     public void setMasterMode(final OrekitStepHandler handler) {
  255.         super.setMasterMode(handler);
  256.         integrator.clearStepHandlers();
  257.         final AdaptedStepHandler wrapped = new AdaptedStepHandler(handler);
  258.         integrator.addStepHandler(wrapped);
  259.         modeHandler = wrapped;
  260.     }

  261.     /** {@inheritDoc}
  262.      * <p>Note that this method has the side effect of replacing the step handlers
  263.      * of the underlying integrator set up in the {@link
  264.      * #AbstractIntegratedPropagator(AbstractIntegrator, boolean) constructor}. So if a specific
  265.      * step handler is needed, it should be added after this method has been called.</p>
  266.      */
  267.     public void setEphemerisMode() {
  268.         super.setEphemerisMode();
  269.         integrator.clearStepHandlers();
  270.         final EphemerisModeHandler ephemeris = new EphemerisModeHandler();
  271.         modeHandler = ephemeris;
  272.         integrator.addStepHandler(ephemeris);
  273.     }

  274.     /** {@inheritDoc} */
  275.     public BoundedPropagator getGeneratedEphemeris()
  276.         throws IllegalStateException {
  277.         if (getMode() != EPHEMERIS_GENERATION_MODE) {
  278.             throw new OrekitIllegalStateException(OrekitMessages.PROPAGATOR_NOT_IN_EPHEMERIS_GENERATION_MODE);
  279.         }
  280.         return ((EphemerisModeHandler) modeHandler).getEphemeris();
  281.     }

  282.     /** Create a mapper between raw double components and spacecraft state.
  283.     /** Simple constructor.
  284.      * <p>
  285.      * The position parameter type is meaningful only if {@link
  286.      * #getOrbitType() propagation orbit type}
  287.      * support it. As an example, it is not meaningful for propagation
  288.      * in {@link OrbitType#CARTESIAN Cartesian} parameters.
  289.      * </p>
  290.      * @param referenceDate reference date
  291.      * @param mu central attraction coefficient (m³/s²)
  292.      * @param orbitType orbit type to use for mapping
  293.      * @param positionAngleType angle type to use for propagation
  294.      * @param attitudeProvider attitude provider
  295.      * @param frame inertial frame
  296.      * @return new mapper
  297.      */
  298.     protected abstract StateMapper createMapper(final AbsoluteDate referenceDate, final double mu,
  299.                                                 final OrbitType orbitType, final PositionAngle positionAngleType,
  300.                                                 final AttitudeProvider attitudeProvider, final Frame frame);

  301.     /** Get the differential equations to integrate (for main state only).
  302.      * @param integ numerical integrator to use for propagation.
  303.      * @return differential equations for main state
  304.      */
  305.     protected abstract MainStateEquations getMainStateEquations(final AbstractIntegrator integ);

  306.     /** {@inheritDoc} */
  307.     public SpacecraftState propagate(final AbsoluteDate target) throws PropagationException {
  308.         try {
  309.             if (getStartDate() == null) {
  310.                 if (getInitialState() == null) {
  311.                     throw new PropagationException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  312.                 }
  313.                 setStartDate(getInitialState().getDate());
  314.             }
  315.             return propagate(getStartDate(), target);
  316.         } catch (OrekitException oe) {

  317.             // recover a possible embedded PropagationException
  318.             for (Throwable t = oe; t != null; t = t.getCause()) {
  319.                 if (t instanceof PropagationException) {
  320.                     throw (PropagationException) t;
  321.                 }
  322.             }
  323.             throw new PropagationException(oe);

  324.         }
  325.     }

  326.     /** {@inheritDoc} */
  327.     public SpacecraftState propagate(final AbsoluteDate tStart, final AbsoluteDate tEnd)
  328.         throws PropagationException {
  329.         try {

  330.             if (getInitialState() == null) {
  331.                 throw new PropagationException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  332.             }

  333.             if (!tStart.equals(getInitialState().getDate())) {
  334.                 // if propagation start date is not initial date,
  335.                 // propagate from initial to start date without event detection
  336.                 propagate(tStart, false);
  337.             }

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

  340.         } catch (OrekitException oe) {

  341.             // recover a possible embedded PropagationException
  342.             for (Throwable t = oe; t != null; t = t.getCause()) {
  343.                 if (t instanceof PropagationException) {
  344.                     throw (PropagationException) t;
  345.                 }
  346.             }
  347.             throw new PropagationException(oe);

  348.         }
  349.     }

  350.     /** Propagation with or without event detection.
  351.      * @param tEnd target date to which orbit should be propagated
  352.      * @param activateHandlers if true, step and event handlers should be activated
  353.      * @return state at end of propagation
  354.      * @exception PropagationException if orbit cannot be propagated
  355.      */
  356.     protected SpacecraftState propagate(final AbsoluteDate tEnd, final boolean activateHandlers)
  357.         throws PropagationException {
  358.         try {

  359.             if (getInitialState().getDate().equals(tEnd)) {
  360.                 // don't extrapolate
  361.                 return getInitialState();
  362.             }

  363.             // space dynamics view
  364.             stateMapper = createMapper(getInitialState().getDate(), stateMapper.getMu(),
  365.                                        stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  366.                                        stateMapper.getAttitudeProvider(), getInitialState().getFrame());


  367.             // set propagation orbit type
  368.             final Orbit initialOrbit = stateMapper.getOrbitType().convertType(getInitialState().getOrbit());
  369.             if (Double.isNaN(getMu())) {
  370.                 setMu(initialOrbit.getMu());
  371.             }

  372.             if (getInitialState().getMass() <= 0.0) {
  373.                 throw new PropagationException(OrekitMessages.SPACECRAFT_MASS_BECOMES_NEGATIVE,
  374.                                                getInitialState().getMass());
  375.             }

  376.             integrator.clearEventHandlers();

  377.             // set up events added by user
  378.             setUpUserEventDetectors();

  379.             // convert space flight dynamics API to math API
  380.             mathODE = createODE(integrator);
  381.             mathInterpolator = null;

  382.             // initialize mode handler
  383.             if (modeHandler != null) {
  384.                 modeHandler.initialize(activateHandlers, tEnd);
  385.             }

  386.             // mathematical integration
  387.             try {
  388.                 beforeIntegration(getInitialState(), tEnd);
  389.                 integrator.integrate(mathODE, tEnd.durationFrom(getInitialState().getDate()));
  390.                 afterIntegration();
  391.             } catch (OrekitExceptionWrapper oew) {
  392.                 throw oew.getException();
  393.             }

  394.             // get final state
  395.             SpacecraftState finalState = stateMapper.mapArrayToState(
  396.                     stateMapper.mapDoubleToDate(mathODE.getTime(), tEnd),
  397.                     mathODE.getPrimaryState(),
  398.                     meanOrbit);
  399.             finalState = updateAdditionalStates(finalState);
  400.             for (int i = 0; i < additionalEquations.size(); ++i) {
  401.                 final double[] secondary = mathODE.getSecondaryState(i);
  402.                 finalState = finalState.addAdditionalState(additionalEquations.get(i).getName(),
  403.                                                            secondary);
  404.             }
  405.             resetInitialState(finalState);
  406.             setStartDate(finalState.getDate());

  407.             return finalState;

  408.         } catch (PropagationException pe) {
  409.             throw pe;
  410.         } catch (OrekitException oe) {
  411.             throw new PropagationException(oe);
  412.         } catch (MathIllegalArgumentException miae) {
  413.             throw PropagationException.unwrap(miae);
  414.         } catch (MathIllegalStateException mise) {
  415.             throw PropagationException.unwrap(mise);
  416.         }
  417.     }

  418.     /** Get the initial state for integration.
  419.      * @return initial state for integration
  420.      * @exception PropagationException if initial state cannot be retrieved
  421.      */
  422.     protected SpacecraftState getInitialIntegrationState() throws PropagationException {
  423.         return getInitialState();
  424.     }

  425.     /** Create an ODE with all equations.
  426.      * @param integ numerical integrator to use for propagation.
  427.      * @return a new ode
  428.      * @exception OrekitException if initial state cannot be mapped
  429.      */
  430.     private ExpandableStatefulODE createODE(final AbstractIntegrator integ)
  431.         throws OrekitException {

  432.         // retrieve initial state
  433.         final double[] initialStateVector  = new double[getBasicDimension()];
  434.         stateMapper.mapStateToArray(getInitialIntegrationState(), initialStateVector);

  435.         // main part of the ODE
  436.         final ExpandableStatefulODE ode =
  437.                 new ExpandableStatefulODE(new ConvertedMainStateEquations(getMainStateEquations(integ)));
  438.         ode.setTime(0.0);
  439.         ode.setPrimaryState(initialStateVector);

  440.         // secondary part of the ODE
  441.         for (int i = 0; i < additionalEquations.size(); ++i) {
  442.             final AdditionalEquations additional = additionalEquations.get(i);
  443.             final double[] data = getInitialState().getAdditionalState(additional.getName());
  444.             final SecondaryEquations secondary =
  445.                     new ConvertedSecondaryStateEquations(additional, data.length);
  446.             ode.addSecondaryEquations(secondary);
  447.             ode.setSecondaryState(i, data);
  448.         }

  449.         return ode;

  450.     }

  451.     /** Method called just before integration.
  452.      * <p>
  453.      * The default implementation does nothing, it may be specialized in subclasses.
  454.      * </p>
  455.      * @param initialState initial state
  456.      * @param tEnd target date at which state should be propagated
  457.      * @exception OrekitException if hook cannot be run
  458.      */
  459.     protected void beforeIntegration(final SpacecraftState initialState,
  460.                                      final AbsoluteDate tEnd)
  461.         throws OrekitException {
  462.         // do nothing by default
  463.     }

  464.     /** Method called just after integration.
  465.      * <p>
  466.      * The default implementation does nothing, it may be specialized in subclasses.
  467.      * </p>
  468.      * @exception OrekitException if hook cannot be run
  469.      */
  470.     protected void afterIntegration()
  471.         throws OrekitException {
  472.         // do nothing by default
  473.     }

  474.     /** Get state vector dimension without additional parameters.
  475.      * @return state vector dimension without additional parameters.
  476.      */
  477.     public int getBasicDimension() {
  478.         return 7;

  479.     }

  480.     /** Get the integrator used by the propagator.
  481.      * @return the integrator.
  482.      */
  483.     protected AbstractIntegrator getIntegrator() {
  484.         return integrator;
  485.     }

  486.     /** Get a complete state with all additional equations.
  487.      * @param t current value of the independent <I>time</I> variable
  488.      * @param y array containing the current value of the state vector
  489.      * @return complete state
  490.      * @exception OrekitException if state cannot be mapped
  491.      */
  492.     private SpacecraftState getCompleteState(final double t, final double[] y)
  493.         throws OrekitException {

  494.         // main state
  495.         SpacecraftState state = stateMapper.mapArrayToState(t, y, true);  //not sure of the mean orbit, should be true

  496.         // pre-integrated additional states
  497.         state = updateAdditionalStates(state);

  498.         // additional states integrated here
  499.         if (!additionalEquations.isEmpty()) {

  500.             final EquationsMapper[] em = mathODE.getSecondaryMappers();
  501.             for (int i = 0; i < additionalEquations.size(); ++i) {
  502.                 final double[] secondary = new double[em[i].getDimension()];
  503.                 System.arraycopy(y, em[i].getFirstIndex(), secondary, 0, secondary.length);
  504.                 state = state.addAdditionalState(additionalEquations.get(i).getName(), secondary);
  505.             }

  506.         }

  507.         return state;

  508.     }

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

  511.         /** Compute differential equations for main state.
  512.          * @param state current state
  513.          * @return derivatives of main state
  514.          * @throws OrekitException if differentials cannot be computed
  515.          */
  516.         double[] computeDerivatives(final SpacecraftState state) throws OrekitException;

  517.     }

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

  520.         /** Main state equations. */
  521.         private final MainStateEquations main;

  522.         /** Simple constructor.
  523.          * @param main main state equations
  524.          */
  525.         ConvertedMainStateEquations(final MainStateEquations main) {
  526.             this.main = main;
  527.             calls = 0;
  528.         }

  529.         /** {@inheritDoc} */
  530.         public int getDimension() {
  531.             return getBasicDimension();
  532.         }

  533.         /** {@inheritDoc} */
  534.         public void computeDerivatives(final double t, final double[] y, final double[] yDot)
  535.             throws OrekitExceptionWrapper {

  536.             try {

  537.                 // update space dynamics view
  538.                 // use only ODE elements
  539.                 SpacecraftState currentState = stateMapper.mapArrayToState(t, y, true);
  540.                 currentState = updateAdditionalStates(currentState);

  541.                 // compute main state differentials
  542.                 final double[] mainDot = main.computeDerivatives(currentState);
  543.                 System.arraycopy(mainDot, 0, yDot, 0, mainDot.length);

  544.             } catch (OrekitException oe) {
  545.                 throw new OrekitExceptionWrapper(oe);
  546.             }


  547.             // increment calls counter
  548.             ++calls;

  549.         }

  550.     }

  551.     /** Differential equations for the secondary state (Jacobians, user variables ...), with converted API. */
  552.     private class ConvertedSecondaryStateEquations implements SecondaryEquations {

  553.         /** Additional equations. */
  554.         private final AdditionalEquations equations;

  555.         /** Dimension of the additional state. */
  556.         private final int dimension;

  557.         /** Simple constructor.
  558.          * @param equations additional equations
  559.          * @param dimension dimension of the additional state
  560.          */
  561.         ConvertedSecondaryStateEquations(final AdditionalEquations equations,
  562.                                                 final int dimension) {
  563.             this.equations = equations;
  564.             this.dimension = dimension;
  565.         }

  566.         /** {@inheritDoc} */
  567.         public int getDimension() {
  568.             return dimension;
  569.         }

  570.         /** {@inheritDoc} */
  571.         public void computeDerivatives(final double t, final double[] primary,
  572.                                        final double[] primaryDot, final double[] secondary,
  573.                                        final double[] secondaryDot)
  574.             throws OrekitExceptionWrapper {

  575.             try {

  576.                 // update space dynamics view
  577.                 // the state contains only the ODE elements
  578.                 SpacecraftState currentState = stateMapper.mapArrayToState(t, primary, true);
  579.                 currentState = updateAdditionalStates(currentState);
  580.                 currentState = currentState.addAdditionalState(equations.getName(), secondary);

  581.                 // compute additional derivatives
  582.                 final double[] additionalMainDot =
  583.                         equations.computeDerivatives(currentState, secondaryDot);
  584.                 if (additionalMainDot != null) {
  585.                     // the additional equations have an effect on main equations
  586.                     for (int i = 0; i < additionalMainDot.length; ++i) {
  587.                         primaryDot[i] += additionalMainDot[i];
  588.                     }
  589.                 }

  590.             } catch (OrekitException oe) {
  591.                 throw new OrekitExceptionWrapper(oe);
  592.             }

  593.         }

  594.     }

  595.     /** Adapt an {@link org.orekit.propagation.events.EventDetector}
  596.      * to commons-math {@link org.apache.commons.math3.ode.events.EventHandler} interface.
  597.      * @param <T> class type for the generic version
  598.      * @author Fabien Maussion
  599.      */
  600.     private class AdaptedEventDetector
  601.         implements org.apache.commons.math3.ode.events.EventHandler {

  602.         /** Underlying event detector. */
  603.         private final EventDetector detector;

  604.         /** Time of the previous call to g. */
  605.         private double lastT;

  606.         /** Value from the previous call to g. */
  607.         private double lastG;

  608.         /** Build a wrapped event detector.
  609.          * @param detector event detector to wrap
  610.         */
  611.         AdaptedEventDetector(final EventDetector detector) {
  612.             this.detector = detector;
  613.             this.lastT    = Double.NaN;
  614.             this.lastG    = Double.NaN;
  615.         }

  616.         /** {@inheritDoc} */
  617.         public void init(final double t0, final double[] y0, final double t) {
  618.             try {

  619.                 detector.init(getCompleteState(t0, y0), stateMapper.mapDoubleToDate(t));
  620.                 this.lastT = Double.NaN;
  621.                 this.lastG = Double.NaN;

  622.             } catch (OrekitException oe) {
  623.                 throw new OrekitExceptionWrapper(oe);
  624.             }
  625.         }

  626.         /** {@inheritDoc} */
  627.         public double g(final double t, final double[] y) {
  628.             try {
  629.                 if (!Precision.equals(lastT, t, 1)) {
  630.                     lastT = t;
  631.                     lastG = detector.g(getCompleteState(t, y));
  632.                 }
  633.                 return lastG;
  634.             } catch (OrekitException oe) {
  635.                 throw new OrekitExceptionWrapper(oe);
  636.             }
  637.         }

  638.         /** {@inheritDoc} */
  639.         public Action eventOccurred(final double t, final double[] y, final boolean increasing) {
  640.             try {

  641.                 final EventHandler.Action whatNext = detector.eventOccurred(getCompleteState(t, y), increasing);

  642.                 switch (whatNext) {
  643.                     case STOP :
  644.                         return Action.STOP;
  645.                     case RESET_STATE :
  646.                         return Action.RESET_STATE;
  647.                     case RESET_DERIVATIVES :
  648.                         return Action.RESET_DERIVATIVES;
  649.                     default :
  650.                         return Action.CONTINUE;
  651.                 }
  652.             } catch (OrekitException oe) {
  653.                 throw new OrekitExceptionWrapper(oe);
  654.             }
  655.         }

  656.         /** {@inheritDoc} */
  657.         public void resetState(final double t, final double[] y) {
  658.             try {
  659.                 final SpacecraftState newState = detector.resetState(getCompleteState(t, y));

  660.                 // main part
  661.                 stateMapper.mapStateToArray(newState, y);

  662.                 // secondary part
  663.                 final EquationsMapper[] em = mathODE.getSecondaryMappers();
  664.                 for (int i = 0; i < additionalEquations.size(); ++i) {
  665.                     final double[] secondary =
  666.                             newState.getAdditionalState(additionalEquations.get(i).getName());
  667.                     System.arraycopy(secondary, 0, y, em[i].getFirstIndex(), secondary.length);
  668.                 }

  669.             } catch (OrekitException oe) {
  670.                 throw new OrekitExceptionWrapper(oe);
  671.             }
  672.         }

  673.     }

  674.     /** Adapt an {@link org.orekit.propagation.sampling.OrekitStepHandler}
  675.      * to commons-math {@link StepHandler} interface.
  676.      * @author Luc Maisonobe
  677.      */
  678.     private class AdaptedStepHandler
  679.         implements OrekitStepInterpolator, StepHandler, ModeHandler {

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

  682.         /** Flag for handler . */
  683.         private boolean activate;

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

  690.         /** {@inheritDoc} */
  691.         public void initialize(final boolean activateHandlers,
  692.                                final AbsoluteDate targetDate) {
  693.             this.activate = activateHandlers;
  694.         }

  695.         /** {@inheritDoc} */
  696.         public void init(final double t0, final double[] y0, final double t) {
  697.             try {
  698.                 handler.init(getCompleteState(t0, y0), stateMapper.mapDoubleToDate(t));
  699.             } catch (OrekitException oe) {
  700.                 throw new OrekitExceptionWrapper(oe);
  701.             }
  702.         }

  703.         /** {@inheritDoc} */
  704.         public void handleStep(final StepInterpolator interpolator, final boolean isLast) {
  705.             try {
  706.                 mathInterpolator = interpolator;
  707.                 if (activate) {
  708.                     handler.handleStep(this, isLast);
  709.                 }
  710.             } catch (PropagationException pe) {
  711.                 throw new OrekitExceptionWrapper(pe);
  712.             }
  713.         }

  714.         /** Get the current grid date.
  715.          * @return current grid date
  716.          */
  717.         public AbsoluteDate getCurrentDate() {
  718.             return stateMapper.mapDoubleToDate(mathInterpolator.getCurrentTime());
  719.         }

  720.         /** Get the previous grid date.
  721.          * @return previous grid date
  722.          */
  723.         public AbsoluteDate getPreviousDate() {
  724.             return stateMapper.mapDoubleToDate(mathInterpolator.getPreviousTime());
  725.         }

  726.         /** Get the interpolated date.
  727.          * <p>If {@link #setInterpolatedDate(AbsoluteDate) setInterpolatedDate}
  728.          * has not been called, the date returned is the same as  {@link
  729.          * #getCurrentDate() getCurrentDate}.</p>
  730.          * @return interpolated date
  731.          * @see #setInterpolatedDate(AbsoluteDate)
  732.          * @see #getInterpolatedState()
  733.          */
  734.         public AbsoluteDate getInterpolatedDate() {
  735.             return stateMapper.mapDoubleToDate(mathInterpolator.getInterpolatedTime());
  736.         }

  737.         /** Set the interpolated date.
  738.          * <p>It is possible to set the interpolation date outside of the current
  739.          * step range, but accuracy will decrease as date is farther.</p>
  740.          * @param date interpolated date to set
  741.          * @see #getInterpolatedDate()
  742.          * @see #getInterpolatedState()
  743.          */
  744.         public void setInterpolatedDate(final AbsoluteDate date) {
  745.             mathInterpolator.setInterpolatedTime(stateMapper.mapDateToDouble(date));
  746.         }

  747.         /** Get the interpolated state.
  748.          * @return interpolated state at the current interpolation date
  749.          * @exception OrekitException if state cannot be interpolated or converted
  750.          * @see #getInterpolatedDate()
  751.          * @see #setInterpolatedDate(AbsoluteDate)
  752.          */
  753.         public SpacecraftState getInterpolatedState() throws OrekitException {
  754.             try {

  755.                 SpacecraftState s =
  756.                         stateMapper.mapArrayToState(mathInterpolator.getInterpolatedTime(),
  757.                                                     mathInterpolator.getInterpolatedState(),
  758.                                                     meanOrbit);
  759.                 s = updateAdditionalStates(s);
  760.                 for (int i = 0; i < additionalEquations.size(); ++i) {
  761.                     final double[] secondary = mathInterpolator.getInterpolatedSecondaryState(i);
  762.                     s = s.addAdditionalState(additionalEquations.get(i).getName(), secondary);
  763.                 }

  764.                 return s;

  765.             } catch (OrekitExceptionWrapper oew) {
  766.                 throw oew.getException();
  767.             }
  768.         }

  769.         /** Check is integration direction is forward in date.
  770.          * @return true if integration is forward in date
  771.          */
  772.         public boolean isForward() {
  773.             return mathInterpolator.isForward();
  774.         }

  775.     }

  776.     private class EphemerisModeHandler implements ModeHandler, StepHandler {

  777.         /** Underlying raw mathematical model. */
  778.         private ContinuousOutputModel model;

  779.         /** Generated ephemeris. */
  780.         private BoundedPropagator ephemeris;

  781.         /** Flag for handler . */
  782.         private boolean activate;

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

  785.         /** Creates a new instance of EphemerisModeHandler which must be
  786.          *  filled by the propagator.
  787.          */
  788.         EphemerisModeHandler() {
  789.         }

  790.         /** {@inheritDoc} */
  791.         public void initialize(final boolean activateHandlers,
  792.                                final AbsoluteDate targetDate) {
  793.             this.activate = activateHandlers;
  794.             this.model    = new ContinuousOutputModel();
  795.             this.endDate  = targetDate;

  796.             // ephemeris will be generated when last step is processed
  797.             this.ephemeris = null;

  798.         }

  799.         /** Get the generated ephemeris.
  800.          * @return a new instance of the generated ephemeris
  801.          */
  802.         public BoundedPropagator getEphemeris() {
  803.             return ephemeris;
  804.         }

  805.         /** {@inheritDoc} */
  806.         public void handleStep(final StepInterpolator interpolator, final boolean isLast)
  807.             throws OrekitExceptionWrapper {
  808.             try {
  809.                 if (activate) {
  810.                     model.handleStep(interpolator, isLast);
  811.                     if (isLast) {

  812.                         // set up the boundary dates
  813.                         final double tI = model.getInitialTime();
  814.                         final double tF = model.getFinalTime();
  815.                         // tI is almost? always zero
  816.                         final AbsoluteDate startDate =
  817.                                 stateMapper.mapDoubleToDate(tI);
  818.                         final AbsoluteDate finalDate =
  819.                                 stateMapper.mapDoubleToDate(tF, this.endDate);
  820.                         final AbsoluteDate minDate;
  821.                         final AbsoluteDate maxDate;
  822.                         if (tF < tI) {
  823.                             minDate = finalDate;
  824.                             maxDate = startDate;
  825.                         } else {
  826.                             minDate = startDate;
  827.                             maxDate = finalDate;
  828.                         }

  829.                         // get the initial additional states that are not managed
  830.                         final Map<String, double[]> unmanaged = new HashMap<String, double[]>();
  831.                         for (final Map.Entry<String, double[]> initial : getInitialState().getAdditionalStates().entrySet()) {
  832.                             if (!isAdditionalStateManaged(initial.getKey())) {
  833.                                 // this additional state was in the initial state, but is unknown to the propagator
  834.                                 // we simply copy its initial value as is
  835.                                 unmanaged.put(initial.getKey(), initial.getValue());
  836.                             }
  837.                         }

  838.                         // get the names of additional states managed by differential equations
  839.                         final String[] names = new String[additionalEquations.size()];
  840.                         for (int i = 0; i < names.length; ++i) {
  841.                             names[i] = additionalEquations.get(i).getName();
  842.                         }

  843.                         // create the ephemeris
  844.                         ephemeris = new IntegratedEphemeris(startDate, minDate, maxDate,
  845.                                                             stateMapper, meanOrbit, model, unmanaged,
  846.                                                             getAdditionalStateProviders(), names);

  847.                     }
  848.                 }
  849.             } catch (OrekitException oe) {
  850.                 throw new OrekitExceptionWrapper(oe);
  851.             }
  852.         }

  853.         /** {@inheritDoc} */
  854.         public void init(final double t0, final double[] y0, final double t) {
  855.             model.init(t0, y0, t);
  856.         }

  857.     }

  858. }