AbstractIntegratedPropagator.java

  1. /* Copyright 2002-2013 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.nonstiff.ClassicalRungeKuttaIntegrator;
  33. import org.apache.commons.math3.ode.sampling.StepHandler;
  34. import org.apache.commons.math3.ode.sampling.StepInterpolator;
  35. import org.apache.commons.math3.util.FastMath;
  36. import org.apache.commons.math3.util.Precision;
  37. import org.orekit.attitudes.AttitudeProvider;
  38. import org.orekit.errors.OrekitException;
  39. import org.orekit.errors.OrekitExceptionWrapper;
  40. import org.orekit.errors.OrekitMessages;
  41. import org.orekit.errors.PropagationException;
  42. import org.orekit.frames.Frame;
  43. import org.orekit.orbits.Orbit;
  44. import org.orekit.orbits.OrbitType;
  45. import org.orekit.orbits.PositionAngle;
  46. import org.orekit.propagation.AbstractPropagator;
  47. import org.orekit.propagation.BoundedPropagator;
  48. import org.orekit.propagation.SpacecraftState;
  49. import org.orekit.propagation.events.AbstractReconfigurableDetector;
  50. import org.orekit.propagation.events.EventDetector;
  51. import org.orekit.propagation.events.handlers.EventHandler;
  52. import org.orekit.propagation.sampling.OrekitStepHandler;
  53. import org.orekit.propagation.sampling.OrekitStepInterpolator;
  54. import org.orekit.time.AbsoluteDate;


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

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

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

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

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

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

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

  72.     /** Complete equation to be integrated. */
  73.     private ExpandableStatefulODE mathODE;

  74.     /** Underlying raw rawInterpolator. */
  75.     private StepInterpolator mathInterpolator;

  76.     /** Build a new instance.
  77.      * @param integrator numerical integrator to use for propagation.
  78.      */
  79.     protected AbstractIntegratedPropagator(final AbstractIntegrator integrator) {
  80.         detectors           = new ArrayList<EventDetector>();
  81.         additionalEquations = new ArrayList<AdditionalEquations>();
  82.         this.integrator     = integrator;
  83.     }

  84.     /** Initialize the mapper. */
  85.     protected void initMapper() {
  86.         stateMapper = createMapper(null, Double.NaN, null, null, null, null);
  87.     }

  88.     /**  {@inheritDoc} */
  89.     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
  90.         super.setAttitudeProvider(attitudeProvider);
  91.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  92.                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  93.                                    attitudeProvider, stateMapper.getFrame());
  94.     }

  95.     /** Set propagation orbit type.
  96.      * @param orbitType orbit type to use for propagation
  97.      */
  98.     protected void setOrbitType(final OrbitType orbitType) {
  99.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  100.                                    orbitType, stateMapper.getPositionAngleType(),
  101.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  102.     }

  103.     /** Get propagation parameter type.
  104.      * @return orbit type used for propagation
  105.      */
  106.     protected OrbitType getOrbitType() {
  107.         return stateMapper.getOrbitType();
  108.     }

  109.     /** Set position angle type.
  110.      * <p>
  111.      * The position parameter type is meaningful only if {@link
  112.      * #getOrbitType() propagation orbit type}
  113.      * support it. As an example, it is not meaningful for propagation
  114.      * in {@link OrbitType#CARTESIAN Cartesian} parameters.
  115.      * </p>
  116.      * @param positionAngleType angle type to use for propagation
  117.      */
  118.     protected void setPositionAngleType(final PositionAngle positionAngleType) {
  119.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  120.                                    stateMapper.getOrbitType(), positionAngleType,
  121.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  122.     }

  123.     /** Get propagation parameter type.
  124.      * @return angle type to use for propagation
  125.      */
  126.     protected PositionAngle getPositionAngleType() {
  127.         return stateMapper.getPositionAngleType();
  128.     }

  129.     /** Set the central attraction coefficient &mu;.
  130.      * @param mu central attraction coefficient (m<sup>3</sup>/s<sup>2</sup>)
  131.      */
  132.     public void setMu(final double mu) {
  133.         stateMapper = createMapper(stateMapper.getReferenceDate(), mu,
  134.                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  135.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  136.     }

  137.     /** Get the central attraction coefficient &mu;.
  138.      * @return mu central attraction coefficient (m<sup>3</sup>/s<sup>2</sup>)
  139.      * @see #setMu(double)
  140.      */
  141.     public double getMu() {
  142.         return stateMapper.getMu();
  143.     }

  144.     /** Get the number of calls to the differential equations computation method.
  145.      * <p>The number of calls is reset each time the {@link #propagate(AbsoluteDate)}
  146.      * method is called.</p>
  147.      * @return number of calls to the differential equations computation method
  148.      */
  149.     public int getCalls() {
  150.         return calls;
  151.     }

  152.     /** {@inheritDoc} */
  153.     @Override
  154.     public boolean isAdditionalStateManaged(final String name) {

  155.         // first look at already integrated states
  156.         if (super.isAdditionalStateManaged(name)) {
  157.             return true;
  158.         }

  159.         // then look at states we integrate ourselves
  160.         for (final AdditionalEquations equation : additionalEquations) {
  161.             if (equation.getName().equals(name)) {
  162.                 return true;
  163.             }
  164.         }

  165.         return false;
  166.     }

  167.     /** {@inheritDoc} */
  168.     @Override
  169.     public String[] getManagedAdditionalStates() {
  170.         final String[] alreadyIntegrated = super.getManagedAdditionalStates();
  171.         final String[] managed = new String[alreadyIntegrated.length + additionalEquations.size()];
  172.         System.arraycopy(alreadyIntegrated, 0, managed, 0, alreadyIntegrated.length);
  173.         for (int i = 0; i < additionalEquations.size(); ++i) {
  174.             managed[i + alreadyIntegrated.length] = additionalEquations.get(i).getName();
  175.         }
  176.         return managed;
  177.     }

  178.     /** Add a set of user-specified equations to be integrated along with the orbit propagation.
  179.      * @param additional additional equations
  180.      * @exception OrekitException if a set of equations with the same name is already present
  181.      */
  182.     public void addAdditionalEquations(final AdditionalEquations additional)
  183.         throws OrekitException {

  184.         // check if the name is already used
  185.         if (isAdditionalStateManaged(additional.getName())) {
  186.             // this set of equations is already registered, complain
  187.             throw new OrekitException(OrekitMessages.ADDITIONAL_STATE_NAME_ALREADY_IN_USE,
  188.                                       additional.getName());
  189.         }

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

  192.     }

  193.     /** {@inheritDoc} */
  194.     public <T extends EventDetector> void addEventDetector(final T detector) {
  195.         detectors.add(detector);
  196.     }

  197.     /** {@inheritDoc} */
  198.     public Collection<EventDetector> getEventsDetectors() {
  199.         return Collections.unmodifiableCollection(detectors);
  200.     }

  201.     /** {@inheritDoc} */
  202.     public void clearEventsDetectors() {
  203.         detectors.clear();
  204.     }

  205.     /** Set up all user defined event detectors.
  206.      */
  207.     protected void setUpUserEventDetectors() {
  208.         for (final EventDetector detector : detectors) {
  209.             setUpEventDetector(integrator, detector);
  210.         }
  211.     }

  212.     /** Wrap an Orekit event detector and register it to the integrator.
  213.      * @param integ integrator into which event detector should be registered
  214.      * @param detector event detector to wrap
  215.      * @param <T> class type for the generic version
  216.      */
  217.     protected <T extends EventDetector> void setUpEventDetector(final AbstractIntegrator integ,
  218.                                                                 final T detector) {
  219.         integ.addEventHandler(new AdaptedEventDetector<T>(detector),
  220.                               detector.getMaxCheckInterval(),
  221.                               detector.getThreshold(),
  222.                               detector.getMaxIterationCount());
  223.     }

  224.     /** {@inheritDoc}
  225.      * <p>Note that this method has the side effect of replacing the step handlers
  226.      * of the underlying integrator set up in the {@link
  227.      * #AbstractIntegratedPropagator(AbstractIntegrator) constructor}. So if a specific
  228.      * step handler is needed, it should be added after this method has been callled.</p>
  229.      */
  230.     public void setSlaveMode() {
  231.         super.setSlaveMode();
  232.         if (integrator != null) {
  233.             integrator.clearStepHandlers();
  234.         }
  235.         modeHandler = null;
  236.     }

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

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

  263.     /** {@inheritDoc} */
  264.     public BoundedPropagator getGeneratedEphemeris()
  265.         throws IllegalStateException {
  266.         if (getMode() != EPHEMERIS_GENERATION_MODE) {
  267.             throw OrekitException.createIllegalStateException(OrekitMessages.PROPAGATOR_NOT_IN_EPHEMERIS_GENERATION_MODE);
  268.         }
  269.         return ((EphemerisModeHandler) modeHandler).getEphemeris();
  270.     }

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

  290.     /** Get the differential equations to integrate (for main state only).
  291.      * @param integ numerical integrator to use for propagation.
  292.      * @return differential equations for main state
  293.      */
  294.     protected abstract MainStateEquations getMainStateEquations(final AbstractIntegrator integ);

  295.     /** {@inheritDoc} */
  296.     public SpacecraftState propagate(final AbsoluteDate target) throws PropagationException {
  297.         try {
  298.             if (getStartDate() == null) {
  299.                 if (getInitialState() == null) {
  300.                     throw new PropagationException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  301.                 }
  302.                 setStartDate(getInitialState().getDate());
  303.             }
  304.             return propagate(getStartDate(), target);
  305.         } catch (OrekitException oe) {

  306.             // recover a possible embedded PropagationException
  307.             for (Throwable t = oe; t != null; t = t.getCause()) {
  308.                 if (t instanceof PropagationException) {
  309.                     throw (PropagationException) t;
  310.                 }
  311.             }
  312.             throw new PropagationException(oe);

  313.         }
  314.     }

  315.     /** {@inheritDoc} */
  316.     public SpacecraftState propagate(final AbsoluteDate tStart, final AbsoluteDate tEnd)
  317.         throws PropagationException {
  318.         try {

  319.             if (getInitialState() == null) {
  320.                 throw new PropagationException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  321.             }

  322.             if (!tStart.equals(getInitialState().getDate())) {
  323.                 // if propagation start date is not initial date,
  324.                 // propagate from initial to start date without event detection
  325.                 propagate(tStart, false);
  326.             }

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

  329.         } catch (OrekitException oe) {

  330.             // recover a possible embedded PropagationException
  331.             for (Throwable t = oe; t != null; t = t.getCause()) {
  332.                 if (t instanceof PropagationException) {
  333.                     throw (PropagationException) t;
  334.                 }
  335.             }
  336.             throw new PropagationException(oe);

  337.         }
  338.     }

  339.     /** Propagation with or without event detection.
  340.      * @param tEnd target date to which orbit should be propagated
  341.      * @param activateHandlers if true, step and event handlers should be activated
  342.      * @return state at end of propagation
  343.      * @exception PropagationException if orbit cannot be propagated
  344.      */
  345.     protected SpacecraftState propagate(final AbsoluteDate tEnd, final boolean activateHandlers)
  346.         throws PropagationException {
  347.         try {

  348.             if (getInitialState().getDate().equals(tEnd)) {
  349.                 // don't extrapolate
  350.                 return getInitialState();
  351.             }

  352.             // space dynamics view
  353.             stateMapper = createMapper(getInitialState().getDate(), stateMapper.getMu(),
  354.                                        stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  355.                                        stateMapper.getAttitudeProvider(), getInitialState().getFrame());


  356.             // set propagation orbit type
  357.             final Orbit initialOrbit = stateMapper.getOrbitType().convertType(getInitialState().getOrbit());
  358.             if (Double.isNaN(getMu())) {
  359.                 setMu(initialOrbit.getMu());
  360.             }

  361.             if (getInitialState().getMass() <= 0.0) {
  362.                 throw new PropagationException(OrekitMessages.SPACECRAFT_MASS_BECOMES_NEGATIVE,
  363.                                                getInitialState().getMass());
  364.             }

  365.             integrator.clearEventHandlers();

  366.             // set up events added by user
  367.             setUpUserEventDetectors();

  368.             // convert space flight dynamics API to math API
  369.             mathODE = createODE(integrator);
  370.             mathInterpolator = null;

  371.             // initialize mode handler
  372.             if (modeHandler != null) {
  373.                 modeHandler.initialize(activateHandlers);
  374.             }

  375.             // mathematical integration
  376.             try {
  377.                 beforeIntegration(getInitialState(), tEnd);
  378.                 integrator.integrate(mathODE, tEnd.durationFrom(getInitialState().getDate()));
  379.                 afterIntegration();
  380.             } catch (OrekitExceptionWrapper oew) {
  381.                 throw oew.getException();
  382.             }

  383.             // get final state
  384.             SpacecraftState finalState =
  385.                     stateMapper.mapArrayToState(mathODE.getTime(), mathODE.getPrimaryState());
  386.             finalState = updateAdditionalStates(finalState);
  387.             for (int i = 0; i < additionalEquations.size(); ++i) {
  388.                 final double[] secondary = mathODE.getSecondaryState(i);
  389.                 finalState = finalState.addAdditionalState(additionalEquations.get(i).getName(),
  390.                                                            secondary);
  391.             }
  392.             resetInitialState(finalState);
  393.             setStartDate(finalState.getDate());

  394.             return finalState;

  395.         } catch (PropagationException pe) {
  396.             throw pe;
  397.         } catch (OrekitException oe) {
  398.             throw new PropagationException(oe);
  399.         } catch (MathIllegalArgumentException miae) {
  400.             throw PropagationException.unwrap(miae);
  401.         } catch (MathIllegalStateException mise) {
  402.             throw PropagationException.unwrap(mise);
  403.         }
  404.     }

  405.     /** Create an ODE with all equations.
  406.      * @param integ numerical integrator to use for propagation.
  407.      * @return a new ode
  408.      * @exception OrekitException if initial state cannot be mapped
  409.      */
  410.     private ExpandableStatefulODE createODE(final AbstractIntegrator integ)
  411.         throws OrekitException {

  412.         // retrieve initial state
  413.         final double[] initialStateVector  = new double[getBasicDimension()];
  414.         stateMapper.mapStateToArray(getInitialState(), initialStateVector);

  415.         // main part of the ODE
  416.         final ExpandableStatefulODE ode =
  417.                 new ExpandableStatefulODE(new ConvertedMainStateEquations(getMainStateEquations(integ)));
  418.         ode.setTime(0.0);
  419.         ode.setPrimaryState(initialStateVector);

  420.         // secondary part of the ODE
  421.         for (int i = 0; i < additionalEquations.size(); ++i) {
  422.             final AdditionalEquations additional = additionalEquations.get(i);
  423.             final double[] data = getInitialState().getAdditionalState(additional.getName());
  424.             final SecondaryEquations secondary =
  425.                     new ConvertedSecondaryStateEquations(additional, data.length);
  426.             ode.addSecondaryEquations(secondary);
  427.             ode.setSecondaryState(i, data);
  428.         }

  429.         return ode;

  430.     }

  431.     /** Method called just before integration.
  432.      * <p>
  433.      * The default implementation does nothing, it may be specialized in subclasses.
  434.      * </p>
  435.      * @param initialState initial state
  436.      * @param tEnd target date at which state should be propagated
  437.      * @exception OrekitException if hook cannot be run
  438.      */
  439.     protected void beforeIntegration(final SpacecraftState initialState,
  440.                                      final AbsoluteDate tEnd)
  441.         throws OrekitException {
  442.         // do nothing by default
  443.     }

  444.     /** Method called just after integration.
  445.      * <p>
  446.      * The default implementation does nothing, it may be specialized in subclasses.
  447.      * </p>
  448.      * @exception OrekitException if hook cannot be run
  449.      */
  450.     protected void afterIntegration()
  451.         throws OrekitException {
  452.         // do nothing by default
  453.     }

  454.     /** Get state vector dimension without additional parameters.
  455.      * @return state vector dimension without additional parameters.
  456.      */
  457.     public int getBasicDimension() {
  458.         return 7;

  459.     }

  460.     /** Get a complete state with all additional equations.
  461.      * @param t current value of the independent <I>time</I> variable
  462.      * @param y array containing the current value of the state vector
  463.      * @return complete state
  464.      * @exception OrekitException if state cannot be mapped
  465.      */
  466.     private SpacecraftState getCompleteState(final double t, final double[] y)
  467.         throws OrekitException {

  468.         // main state
  469.         SpacecraftState state = stateMapper.mapArrayToState(t, y);

  470.         // pre-integrated additional states
  471.         state = updateAdditionalStates(state);

  472.         // additional states integrated here
  473.         if (!additionalEquations.isEmpty()) {

  474.             if (mathODE.getTotalDimension() <= y.length) {
  475.                 // the provided y vector already contains everything needed
  476.                 final EquationsMapper[] em = mathODE.getSecondaryMappers();
  477.                 for (int i = 0; i < additionalEquations.size(); ++i) {
  478.                     final double[] secondary = new double[em[i].getDimension()];
  479.                     System.arraycopy(y, em[i].getFirstIndex(), secondary, 0, secondary.length);
  480.                     state = state.addAdditionalState(additionalEquations.get(i).getName(),
  481.                                                      secondary);
  482.                 }
  483.             } else {
  484.                 // the y array doesn't contain the additional equations data

  485.                 // TODO: remove this case when MATH-965 fix is officially published
  486.                 // (i.e for the next Apache Commons Math version after 3.2)
  487.                 // The fix for MATH-965 ensures that y always contains all
  488.                 // needed data, including additional states, so the workaround
  489.                 // below will not be needed anymore

  490.                 if (mathInterpolator == null) {
  491.                     // we are still in the first step, before the step handler call
  492.                     // we build a temporary interpolator just for this step
  493.                     final double step = FastMath.abs(integrator.getCurrentSignedStepsize());
  494.                     final ClassicalRungeKuttaIntegrator firstStepIntegrator =
  495.                             new ClassicalRungeKuttaIntegrator(step);
  496.                     firstStepIntegrator.addStepHandler(new StepHandler() {

  497.                         /** {@inheritDoc} */
  498.                         public void init(final double t0, final double[] y0, final double t) {
  499.                         }

  500.                         /** {@inheritDoc} */
  501.                         public void handleStep(final StepInterpolator interpolator, final boolean isLast) {
  502.                             mathInterpolator = interpolator;
  503.                         }

  504.                     });
  505.                     final ExpandableStatefulODE localODE = createODE(firstStepIntegrator);
  506.                     firstStepIntegrator.clearEventHandlers();
  507.                     firstStepIntegrator.integrate(localODE, step);
  508.                 }

  509.                 // extract the additional data from the spied interpolator
  510.                 mathInterpolator.setInterpolatedTime(t);
  511.                 for (int i = 0; i < additionalEquations.size(); ++i) {
  512.                     final double[] secondary = mathInterpolator.getInterpolatedSecondaryState(i);
  513.                     state = state.addAdditionalState(additionalEquations.get(i).getName(),
  514.                                                      secondary);
  515.                 }

  516.             }

  517.         }

  518.         return state;

  519.     }

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

  522.         /** Compute differential equations for main state.
  523.          * @param state current state
  524.          * @return derivatives of main state
  525.          * @throws OrekitException if differentials cannot be computed
  526.          */
  527.         double[] computeDerivatives(final SpacecraftState state) throws OrekitException;

  528.     }

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

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

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

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

  544.         /** {@inheritDoc} */
  545.         public void computeDerivatives(final double t, final double[] y, final double[] yDot)
  546.             throws OrekitExceptionWrapper {

  547.             try {

  548.                 // update space dynamics view
  549.                 SpacecraftState currentState = stateMapper.mapArrayToState(t, y);
  550.                 currentState = updateAdditionalStates(currentState);

  551.                 // compute main state differentials
  552.                 final double[] mainDot = main.computeDerivatives(currentState);
  553.                 System.arraycopy(mainDot, 0, yDot, 0, mainDot.length);

  554.             } catch (OrekitException oe) {
  555.                 throw new OrekitExceptionWrapper(oe);
  556.             }


  557.             // increment calls counter
  558.             ++calls;

  559.         }

  560.     }

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

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

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

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

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

  580.         /** {@inheritDoc} */
  581.         public void computeDerivatives(final double t, final double[] primary,
  582.                                        final double[] primaryDot, final double[] secondary,
  583.                                        final double[] secondaryDot)
  584.             throws OrekitExceptionWrapper {

  585.             try {

  586.                 // update space dynamics view
  587.                 SpacecraftState currentState = stateMapper.mapArrayToState(t, primary);
  588.                 currentState = updateAdditionalStates(currentState);
  589.                 currentState = currentState.addAdditionalState(equations.getName(), secondary);

  590.                 // compute additional derivatives
  591.                 final double[] additionalMainDot =
  592.                         equations.computeDerivatives(currentState, secondaryDot);
  593.                 if (additionalMainDot != null) {
  594.                     // the additional equations have an effect on main equations
  595.                     for (int i = 0; i < additionalMainDot.length; ++i) {
  596.                         primaryDot[i] += additionalMainDot[i];
  597.                     }
  598.                 }

  599.             } catch (OrekitException oe) {
  600.                 throw new OrekitExceptionWrapper(oe);
  601.             }

  602.         }

  603.     }

  604.     /** Adapt an {@link org.orekit.propagation.events.EventDetector}
  605.      * to commons-math {@link org.apache.commons.math3.ode.events.EventHandler} interface.
  606.      * @param <T> class type for the generic version
  607.      * @author Fabien Maussion
  608.      */
  609.     private class AdaptedEventDetector<T extends EventDetector> implements org.apache.commons.math3.ode.events.EventHandler {

  610.         /** Underlying event detector. */
  611.         private final T detector;

  612.         /** Time of the previous call to g. */
  613.         private double lastT;

  614.         /** Value from the previous call to g. */
  615.         private double lastG;

  616.         /** Build a wrapped event detector.
  617.          * @param detector event detector to wrap
  618.         */
  619.         public AdaptedEventDetector(final T detector) {
  620.             this.detector = detector;
  621.             this.lastT    = Double.NaN;
  622.             this.lastG    = Double.NaN;
  623.         }

  624.         /** {@inheritDoc} */
  625.         public void init(final double t0, final double[] y0, final double t) {
  626.             try {

  627.                 detector.init(getCompleteState(t0, y0), stateMapper.mapDoubleToDate(t));
  628.                 this.lastT = Double.NaN;
  629.                 this.lastG = Double.NaN;

  630.             } catch (OrekitException oe) {
  631.                 throw new OrekitExceptionWrapper(oe);
  632.             }
  633.         }

  634.         /** {@inheritDoc} */
  635.         public double g(final double t, final double[] y) {
  636.             try {
  637.                 if (!Precision.equals(lastT, t, 1)) {
  638.                     lastT = t;
  639.                     lastG = detector.g(getCompleteState(t, y));
  640.                 }
  641.                 return lastG;
  642.             } catch (OrekitException oe) {
  643.                 throw new OrekitExceptionWrapper(oe);
  644.             }
  645.         }

  646.         /** {@inheritDoc} */
  647.         public Action eventOccurred(final double t, final double[] y, final boolean increasing) {
  648.             try {

  649.                 final SpacecraftState state = getCompleteState(t, y);
  650.                 final EventHandler.Action whatNext;
  651.                 if (detector instanceof AbstractReconfigurableDetector) {
  652.                     @SuppressWarnings("unchecked")
  653.                     final EventHandler<T> handler = ((AbstractReconfigurableDetector<T>) detector).getHandler();
  654.                     whatNext = handler.eventOccurred(state, detector, increasing);
  655.                 } else {
  656.                     @SuppressWarnings("deprecation")
  657.                     final EventDetector.Action a = detector.eventOccurred(state, increasing);
  658.                     whatNext = AbstractReconfigurableDetector.convert(a);
  659.                 }

  660.                 switch (whatNext) {
  661.                 case STOP :
  662.                     return Action.STOP;
  663.                 case RESET_STATE :
  664.                     return Action.RESET_STATE;
  665.                 case RESET_DERIVATIVES :
  666.                     return Action.RESET_DERIVATIVES;
  667.                 default :
  668.                     return Action.CONTINUE;
  669.                 }
  670.             } catch (OrekitException oe) {
  671.                 throw new OrekitExceptionWrapper(oe);
  672.             }
  673.         }

  674.         /** {@inheritDoc} */
  675.         public void resetState(final double t, final double[] y) {
  676.             try {
  677.                 final SpacecraftState oldState = getCompleteState(t, y);
  678.                 final SpacecraftState newState;
  679.                 if (detector instanceof AbstractReconfigurableDetector) {
  680.                     @SuppressWarnings("unchecked")
  681.                     final EventHandler<T> handler = ((AbstractReconfigurableDetector<T>) detector).getHandler();
  682.                     newState = handler.resetState(detector, oldState);
  683.                 } else {
  684.                     @SuppressWarnings("deprecation")
  685.                     final SpacecraftState s = detector.resetState(oldState);
  686.                     newState = s;
  687.                 }

  688.                 // main part
  689.                 stateMapper.mapStateToArray(newState, y);

  690.                 // secondary part
  691.                 final EquationsMapper[] em = mathODE.getSecondaryMappers();
  692.                 for (int i = 0; i < additionalEquations.size(); ++i) {
  693.                     final double[] secondary =
  694.                             newState.getAdditionalState(additionalEquations.get(i).getName());
  695.                     System.arraycopy(secondary, 0, y, em[i].getFirstIndex(), secondary.length);
  696.                 }

  697.             } catch (OrekitException oe) {
  698.                 throw new OrekitExceptionWrapper(oe);
  699.             }
  700.         }

  701.     }

  702.     /** Adapt an {@link org.orekit.propagation.sampling.OrekitStepHandler}
  703.      * to commons-math {@link StepHandler} interface.
  704.      * @author Luc Maisonobe
  705.      */
  706.     private class AdaptedStepHandler
  707.         implements OrekitStepInterpolator, StepHandler, ModeHandler {

  708.         /** Underlying handler. */
  709.         private final OrekitStepHandler handler;

  710.         /** Flag for handler . */
  711.         private boolean activate;

  712.         /** Build an instance.
  713.          * @param handler underlying handler to wrap
  714.          */
  715.         public AdaptedStepHandler(final OrekitStepHandler handler) {
  716.             this.handler = handler;
  717.         }

  718.         /** {@inheritDoc} */
  719.         public void initialize(final boolean activateHandlers) {
  720.             this.activate = activateHandlers;
  721.         }

  722.         /** {@inheritDoc} */
  723.         public void init(final double t0, final double[] y0, final double t) {
  724.             try {
  725.                 handler.init(getCompleteState(t0, y0), stateMapper.mapDoubleToDate(t));
  726.             } catch (OrekitException oe) {
  727.                 throw new OrekitExceptionWrapper(oe);
  728.             }
  729.         }

  730.         /** {@inheritDoc} */
  731.         public void handleStep(final StepInterpolator interpolator, final boolean isLast) {
  732.             try {
  733.                 mathInterpolator = interpolator;
  734.                 if (activate) {
  735.                     handler.handleStep(this, isLast);
  736.                 }
  737.             } catch (PropagationException pe) {
  738.                 throw new OrekitExceptionWrapper(pe);
  739.             }
  740.         }

  741.         /** Get the current grid date.
  742.          * @return current grid date
  743.          */
  744.         public AbsoluteDate getCurrentDate() {
  745.             return stateMapper.mapDoubleToDate(mathInterpolator.getCurrentTime());
  746.         }

  747.         /** Get the previous grid date.
  748.          * @return previous grid date
  749.          */
  750.         public AbsoluteDate getPreviousDate() {
  751.             return stateMapper.mapDoubleToDate(mathInterpolator.getPreviousTime());
  752.         }

  753.         /** Get the interpolated date.
  754.          * <p>If {@link #setInterpolatedDate(AbsoluteDate) setInterpolatedDate}
  755.          * has not been called, the date returned is the same as  {@link
  756.          * #getCurrentDate() getCurrentDate}.</p>
  757.          * @return interpolated date
  758.          * @see #setInterpolatedDate(AbsoluteDate)
  759.          * @see #getInterpolatedState()
  760.          */
  761.         public AbsoluteDate getInterpolatedDate() {
  762.             return stateMapper.mapDoubleToDate(mathInterpolator.getInterpolatedTime());
  763.         }

  764.         /** Set the interpolated date.
  765.          * <p>It is possible to set the interpolation date outside of the current
  766.          * step range, but accuracy will decrease as date is farther.</p>
  767.          * @param date interpolated date to set
  768.          * @see #getInterpolatedDate()
  769.          * @see #getInterpolatedState()
  770.          */
  771.         public void setInterpolatedDate(final AbsoluteDate date) {
  772.             mathInterpolator.setInterpolatedTime(stateMapper.mapDateToDouble(date));
  773.         }

  774.         /** Get the interpolated state.
  775.          * @return interpolated state at the current interpolation date
  776.          * @exception OrekitException if state cannot be interpolated or converted
  777.          * @see #getInterpolatedDate()
  778.          * @see #setInterpolatedDate(AbsoluteDate)
  779.          */
  780.         public SpacecraftState getInterpolatedState() throws OrekitException {
  781.             try {

  782.                 SpacecraftState s =
  783.                         stateMapper.mapArrayToState(mathInterpolator.getInterpolatedTime(),
  784.                                                     mathInterpolator.getInterpolatedState());
  785.                 s = updateAdditionalStates(s);
  786.                 for (int i = 0; i < additionalEquations.size(); ++i) {
  787.                     final double[] secondary = mathInterpolator.getInterpolatedSecondaryState(i);
  788.                     s = s.addAdditionalState(additionalEquations.get(i).getName(), secondary);
  789.                 }

  790.                 return s;

  791.             } catch (OrekitExceptionWrapper oew) {
  792.                 throw oew.getException();
  793.             }
  794.         }

  795.         /** Check is integration direction is forward in date.
  796.          * @return true if integration is forward in date
  797.          */
  798.         public boolean isForward() {
  799.             return mathInterpolator.isForward();
  800.         }

  801.     }

  802.     private class EphemerisModeHandler implements ModeHandler, StepHandler {

  803.         /** Underlying raw mathematical model. */
  804.         private ContinuousOutputModel model;

  805.         /** Generated ephemeris. */
  806.         private BoundedPropagator ephemeris;

  807.         /** Flag for handler . */
  808.         private boolean activate;

  809.         /** Creates a new instance of EphemerisModeHandler which must be
  810.          *  filled by the propagator.
  811.          */
  812.         public EphemerisModeHandler() {
  813.         }

  814.         /** {@inheritDoc} */
  815.         public void initialize(final boolean activateHandlers) {
  816.             this.activate = activateHandlers;
  817.             this.model    = new ContinuousOutputModel();

  818.             // ephemeris will be generated when last step is processed
  819.             this.ephemeris = null;

  820.         }

  821.         /** Get the generated ephemeris.
  822.          * @return a new instance of the generated ephemeris
  823.          */
  824.         public BoundedPropagator getEphemeris() {
  825.             return ephemeris;
  826.         }

  827.         /** {@inheritDoc} */
  828.         public void handleStep(final StepInterpolator interpolator, final boolean isLast)
  829.             throws OrekitExceptionWrapper {
  830.             try {
  831.                 if (activate) {
  832.                     model.handleStep(interpolator, isLast);
  833.                     if (isLast) {

  834.                         // set up the boundary dates
  835.                         final double tI = model.getInitialTime();
  836.                         final double tF = model.getFinalTime();
  837.                         final AbsoluteDate startDate = stateMapper.mapDoubleToDate(tI);
  838.                         final AbsoluteDate minDate;
  839.                         final AbsoluteDate maxDate;
  840.                         if (tF < tI) {
  841.                             minDate = stateMapper.mapDoubleToDate(tF);
  842.                             maxDate = startDate;
  843.                         } else {
  844.                             minDate = startDate;
  845.                             maxDate = stateMapper.mapDoubleToDate(tF);
  846.                         }

  847.                         // get the initial additional states that are not managed
  848.                         final Map<String, double[]> unmanaged = new HashMap<String, double[]>();
  849.                         for (final Map.Entry<String, double[]> initial : getInitialState().getAdditionalStates().entrySet()) {
  850.                             if (!isAdditionalStateManaged(initial.getKey())) {
  851.                                 // this additional state was in the initial state, but is unknown to the propagator
  852.                                 // we simply copy its initial value as is
  853.                                 unmanaged.put(initial.getKey(), initial.getValue());
  854.                             }
  855.                         }

  856.                         // get the names of additional states managed by differential equations
  857.                         final String[] names = new String[additionalEquations.size()];
  858.                         for (int i = 0; i < names.length; ++i) {
  859.                             names[i] = additionalEquations.get(i).getName();
  860.                         }

  861.                         // create the ephemeris
  862.                         ephemeris = new IntegratedEphemeris(startDate, minDate, maxDate,
  863.                                                             stateMapper, model, unmanaged,
  864.                                                             getAdditionalStateProviders(), names);

  865.                     }
  866.                 }
  867.             } catch (OrekitException oe) {
  868.                 throw new OrekitExceptionWrapper(oe);
  869.             }
  870.         }

  871.         /** {@inheritDoc} */
  872.         public void init(final double t0, final double[] y0, final double t) {
  873.             model.init(t0, y0, t);
  874.         }

  875.     }

  876. }