FieldAbstractIntegratedPropagator.java

  1. /* Copyright 2002-2019 CS Systèmes d'Information
  2.  * Licensed to CS Systèmes d'Information (CS) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * CS licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *   http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.orekit.propagation.integration;

  18. import java.util.ArrayList;
  19. import java.util.Collection;
  20. import java.util.Collections;
  21. import java.util.HashMap;
  22. import java.util.List;
  23. import java.util.Map;

  24. import org.hipparchus.Field;
  25. import org.hipparchus.RealFieldElement;
  26. import org.hipparchus.exception.MathIllegalArgumentException;
  27. import org.hipparchus.exception.MathIllegalStateException;
  28. import org.hipparchus.ode.FieldDenseOutputModel;
  29. import org.hipparchus.ode.FieldEquationsMapper;
  30. import org.hipparchus.ode.FieldExpandableODE;
  31. import org.hipparchus.ode.FieldODEIntegrator;
  32. import org.hipparchus.ode.FieldODEState;
  33. import org.hipparchus.ode.FieldODEStateAndDerivative;
  34. import org.hipparchus.ode.FieldOrdinaryDifferentialEquation;
  35. import org.hipparchus.ode.FieldSecondaryODE;
  36. import org.hipparchus.ode.events.Action;
  37. import org.hipparchus.ode.events.FieldODEEventHandler;
  38. import org.hipparchus.ode.sampling.FieldODEStateInterpolator;
  39. import org.hipparchus.ode.sampling.FieldODEStepHandler;
  40. import org.hipparchus.util.MathArrays;
  41. import org.hipparchus.util.Precision;
  42. import org.orekit.attitudes.AttitudeProvider;
  43. import org.orekit.errors.OrekitException;
  44. import org.orekit.errors.OrekitIllegalStateException;
  45. import org.orekit.errors.OrekitMessages;
  46. import org.orekit.frames.Frame;
  47. import org.orekit.orbits.FieldOrbit;
  48. import org.orekit.orbits.OrbitType;
  49. import org.orekit.orbits.PositionAngle;
  50. import org.orekit.propagation.FieldAbstractPropagator;
  51. import org.orekit.propagation.FieldBoundedPropagator;
  52. import org.orekit.propagation.FieldSpacecraftState;
  53. import org.orekit.propagation.events.FieldEventDetector;
  54. import org.orekit.propagation.events.handlers.FieldEventHandler;
  55. import org.orekit.propagation.sampling.FieldOrekitStepHandler;
  56. import org.orekit.propagation.sampling.FieldOrekitStepInterpolator;
  57. import org.orekit.time.FieldAbsoluteDate;


  58. /** Common handling of {@link org.orekit.propagation.FieldPropagator FieldPropagator}
  59.  *  methods for both numerical and semi-analytical propagators.
  60.  *  @author Luc Maisonobe
  61.  */
  62. public abstract class FieldAbstractIntegratedPropagator<T extends RealFieldElement<T>> extends FieldAbstractPropagator<T> {

  63.     /** Event detectors not related to force models. */
  64.     private final List<FieldEventDetector<T>> detectors;

  65.     /** Integrator selected by the user for the orbital extrapolation process. */
  66.     private final FieldODEIntegrator<T> integrator;

  67.     /** Mode handler. */
  68.     private FieldModeHandler<T> modeHandler;

  69.     /** Additional equations. */
  70.     private List<FieldAdditionalEquations<T>> additionalEquations;

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

  73.     /** Mapper between raw double components and space flight dynamics objects. */
  74.     private FieldStateMapper<T> stateMapper;

  75.     /** Equations mapper. */
  76.     private FieldEquationsMapper<T> equationsMapper;

  77.     /** Underlying raw rawInterpolator. */
  78.     private FieldODEStateInterpolator<T> mathInterpolator;

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

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

  88.     /** Build a new instance.
  89.      * @param integrator numerical integrator to use for propagation.
  90.      * @param meanOrbit output only the mean orbit.
  91.      * @param field Field used by default
  92.      */
  93.     protected FieldAbstractIntegratedPropagator(final Field<T> field, final FieldODEIntegrator<T> integrator, final boolean meanOrbit) {
  94.         super(field);
  95.         detectors           = new ArrayList<FieldEventDetector<T>>();
  96.         additionalEquations = new ArrayList<FieldAdditionalEquations<T>>();
  97.         this.integrator     = integrator;
  98.         this.meanOrbit      = meanOrbit;
  99.         this.resetAtEnd     = true;
  100.     }

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

  116.     /** Initialize the mapper. */
  117.     protected void initMapper() {
  118.         stateMapper = createMapper(null, Double.NaN, null, null, null, null);
  119.     }

  120.     /**  {@inheritDoc} */
  121.     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
  122.         super.setAttitudeProvider(attitudeProvider);
  123.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  124.                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  125.                                    attitudeProvider, stateMapper.getFrame());
  126.     }

  127.     /** Set propagation orbit type.
  128.      * @param orbitType orbit type to use for propagation
  129.      */
  130.     protected void setOrbitType(final OrbitType orbitType) {
  131.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  132.                                    orbitType, stateMapper.getPositionAngleType(),
  133.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  134.     }

  135.     /** Get propagation parameter type.
  136.      * @return orbit type used for propagation
  137.      */
  138.     protected OrbitType getOrbitType() {
  139.         return stateMapper.getOrbitType();
  140.     }

  141.     /** Check if only the mean elements should be used in a semianalitical propagation.
  142.      * @return true if only mean elements have to be used
  143.      */
  144.     protected boolean isMeanOrbit() {
  145.         return meanOrbit;
  146.     }

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

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

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

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

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

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

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

  197.         // then look at states we integrate ourselves
  198.         for (final FieldAdditionalEquations<T> equation : additionalEquations) {
  199.             if (equation.getName().equals(name)) {
  200.                 return true;
  201.             }
  202.         }

  203.         return false;
  204.     }

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

  216.     /** Add a set of user-specified equations to be integrated along with the orbit propagation.
  217.      * @param additional additional equations
  218.      */
  219.     public void addAdditionalEquations(final FieldAdditionalEquations<T> additional) {

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

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

  228.     }

  229.     /** {@inheritDoc} */
  230.     public <D extends FieldEventDetector<T>> void addEventDetector(final D detector) {
  231.         detectors.add(detector);
  232.     }

  233.     /** {@inheritDoc} */
  234.     public Collection<FieldEventDetector<T>> getEventsDetectors() {
  235.         return Collections.unmodifiableCollection(detectors);
  236.     }

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

  241.     /** Set up all user defined event detectors.
  242.      */
  243.     protected void setUpUserEventDetectors() {
  244.         for (final FieldEventDetector<T> detector : detectors) {
  245.             setUpEventDetector(integrator, detector);
  246.         }
  247.     }

  248.     /** Wrap an Orekit event detector and register it to the integrator.
  249.      * @param integ integrator into which event detector should be registered
  250.      * @param detector event detector to wrap
  251.      */
  252.     protected void setUpEventDetector(final FieldODEIntegrator<T> integ, final FieldEventDetector<T> detector) {
  253.         integ.addEventHandler(new FieldAdaptedEventDetector(detector),
  254.                               detector.getMaxCheckInterval().getReal(),
  255.                               detector.getThreshold().getReal(),
  256.                               detector.getMaxIterationCount());
  257.     }

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

  271.     /** {@inheritDoc}
  272.      * <p>Note that this method has the side effect of replacing the step handlers
  273.      * of the underlying integrator set up in the {@link
  274.      * #FieldAbstractIntegratedPropagator(Field, FieldODEIntegrator, boolean) constructor}. So if a specific
  275.      * step handler is needed, it should be added after this method has been called.</p>
  276.      */
  277.     public void setMasterMode(final FieldOrekitStepHandler<T> handler) {
  278.         super.setMasterMode(handler);
  279.         integrator.clearStepHandlers();
  280.         final FieldAdaptedStepHandler wrapped = new FieldAdaptedStepHandler(handler);
  281.         integrator.addStepHandler(wrapped);
  282.         modeHandler = wrapped;
  283.     }

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

  297.     /** {@inheritDoc} */
  298.     public FieldBoundedPropagator<T> getGeneratedEphemeris()
  299.         throws IllegalStateException {
  300.         if (getMode() != EPHEMERIS_GENERATION_MODE) {
  301.             throw new OrekitIllegalStateException(OrekitMessages.PROPAGATOR_NOT_IN_EPHEMERIS_GENERATION_MODE);
  302.         }
  303.         return ((FieldEphemerisModeHandler) modeHandler).getEphemeris();
  304.     }

  305.     /** Create a mapper between raw double components and spacecraft state.
  306.     /** Simple constructor.
  307.      * <p>
  308.      * The position parameter type is meaningful only if {@link
  309.      * #getOrbitType() propagation orbit type}
  310.      * support it. As an example, it is not meaningful for propagation
  311.      * in {@link OrbitType#CARTESIAN Cartesian} parameters.
  312.      * </p>
  313.      * @param referenceDate reference date
  314.      * @param mu central attraction coefficient (m³/s²)
  315.      * @param orbitType orbit type to use for mapping
  316.      * @param positionAngleType angle type to use for propagation
  317.      * @param attitudeProvider attitude provider
  318.      * @param frame inertial frame
  319.      * @return new mapper
  320.      */
  321.     protected abstract FieldStateMapper<T> createMapper(FieldAbsoluteDate<T> referenceDate, double mu,
  322.                                                         OrbitType orbitType, PositionAngle positionAngleType,
  323.                                                         AttitudeProvider attitudeProvider, Frame frame);

  324.     /** Get the differential equations to integrate (for main state only).
  325.      * @param integ numerical integrator to use for propagation.
  326.      * @return differential equations for main state
  327.      */
  328.     protected abstract MainStateEquations<T> getMainStateEquations(FieldODEIntegrator<T> integ);

  329.     /** {@inheritDoc} */
  330.     public FieldSpacecraftState<T> propagate(final FieldAbsoluteDate<T> target) {
  331.         try {
  332.             if (getStartDate() == null) {
  333.                 if (getInitialState() == null) {
  334.                     throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  335.                 }
  336.                 setStartDate(getInitialState().getDate());
  337.             }
  338.             return propagate(getStartDate(), target);
  339.         } catch (OrekitException oe) {

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

  347.         }
  348.     }

  349.     /** {@inheritDoc} */
  350.     public FieldSpacecraftState<T> propagate(final FieldAbsoluteDate<T> tStart, final FieldAbsoluteDate<T> tEnd) {
  351.         try {

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

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

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

  362.         } catch (OrekitException oe) {

  363.             // recover a possible embedded OrekitException
  364.             for (Throwable t = oe; t != null; t = t.getCause()) {
  365.                 if (t instanceof OrekitException) {
  366.                     throw (OrekitException) t;
  367.                 }
  368.             }
  369.             throw new OrekitException(oe);

  370.         }
  371.     }

  372.     /** Propagation with or without event detection.
  373.      * @param tEnd target date to which orbit should be propagated
  374.      * @param activateHandlers if true, step and event handlers should be activated
  375.      * @return state at end of propagation
  376.      */
  377.     protected FieldSpacecraftState<T> propagate(final FieldAbsoluteDate<T> tEnd, final boolean activateHandlers) {
  378.         try {

  379.             if (getInitialState().getDate().equals(tEnd)) {
  380.                 // don't extrapolate
  381.                 return getInitialState();
  382.             }
  383.             // space dynamics view
  384.             stateMapper = createMapper(getInitialState().getDate(), stateMapper.getMu(),
  385.                                        stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  386.                                        stateMapper.getAttitudeProvider(), getInitialState().getFrame());


  387.             // set propagation orbit type
  388.             final FieldOrbit<T> initialOrbit = stateMapper.getOrbitType().convertType(getInitialState().getOrbit());
  389.             if (Double.isNaN(getMu())) {
  390.                 setMu(initialOrbit.getMu());
  391.             }
  392.             if (getInitialState().getMass().getReal() <= 0.0) {
  393.                 throw new OrekitException(OrekitMessages.SPACECRAFT_MASS_BECOMES_NEGATIVE,
  394.                                                getInitialState().getMass());
  395.             }

  396.             integrator.clearEventHandlers();
  397.             // set up events added by user
  398.             setUpUserEventDetectors();

  399.             // convert space flight dynamics API to math API
  400.             final FieldODEState<T> mathInitialState = createInitialState(getInitialIntegrationState());
  401.             final FieldExpandableODE<T> mathODE = createODE(integrator, mathInitialState);
  402.             equationsMapper = mathODE.getMapper();
  403.             mathInterpolator = null;
  404.             // initialize mode handler
  405.             if (modeHandler != null) {
  406.                 modeHandler.initialize(activateHandlers, tEnd);
  407.             }
  408.             // mathematical integration
  409.             final FieldODEStateAndDerivative<T> mathFinalState;

  410.             beforeIntegration(getInitialState(), tEnd);
  411.             mathFinalState = integrator.integrate(mathODE, mathInitialState,
  412.                                                   tEnd.durationFrom(getInitialState().getDate()));

  413.             afterIntegration();

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

  431.             return finalState;

  432.         } catch (OrekitException pe) {
  433.             throw pe;
  434.         } catch (MathIllegalArgumentException miae) {
  435.             throw OrekitException.unwrap(miae);
  436.         } catch (MathIllegalStateException mise) {
  437.             throw OrekitException.unwrap(mise);
  438.         }
  439.     }

  440.     /** Get the initial state for integration.
  441.      * @return initial state for integration
  442.      */
  443.     protected FieldSpacecraftState<T> getInitialIntegrationState() {
  444.         return getInitialState();
  445.     }

  446.     /** Create an initial state.
  447.      * @param initialState initial state in flight dynamics world
  448.      * @return initial state in mathematics world
  449.      */
  450.     private FieldODEState<T> createInitialState(final FieldSpacecraftState<T> initialState) {

  451.         // retrieve initial state
  452.         final T[] primary  = MathArrays.buildArray(initialState.getA().getField(), getBasicDimension());
  453.         stateMapper.mapStateToArray(initialState, primary, null);

  454.         // secondary part of the ODE
  455.         final T[][] secondary = MathArrays.buildArray(initialState.getA().getField(), additionalEquations.size(), -1);
  456.         for (int i = 0; i < additionalEquations.size(); ++i) {
  457.             final FieldAdditionalEquations<T> additional = additionalEquations.get(i);
  458.             final T[] addState = getInitialState().getAdditionalState(additional.getName());
  459.             secondary[i] = MathArrays.buildArray(initialState.getA().getField(), addState.length);
  460.             for (int j = 0; j < addState.length; j++) {
  461.                 secondary[i][j] = addState[j];
  462.             }
  463.         }

  464.         return new FieldODEState<>(initialState.getA().getField().getZero(), primary, secondary);

  465.     }

  466.     /** Create an ODE with all equations.
  467.      * @param integ numerical integrator to use for propagation.
  468.      * @param mathInitialState initial state
  469.      * @return a new ode
  470.      */
  471.     private FieldExpandableODE<T> createODE(final FieldODEIntegrator<T> integ,
  472.                                     final FieldODEState<T> mathInitialState) {

  473.         final FieldExpandableODE<T> ode =
  474.                 new FieldExpandableODE<>(new ConvertedMainStateEquations(getMainStateEquations(integ)));

  475.         // secondary part of the ODE
  476.         for (int i = 0; i < additionalEquations.size(); ++i) {
  477.             final FieldAdditionalEquations<T> additional = additionalEquations.get(i);
  478.             final FieldSecondaryODE<T> secondary =
  479.                     new ConvertedSecondaryStateEquations(additional,
  480.                                                          mathInitialState.getSecondaryStateDimension(i + 1));
  481.             ode.addSecondaryEquations(secondary);
  482.         }

  483.         return ode;

  484.     }

  485.     /** Method called just before integration.
  486.      * <p>
  487.      * The default implementation does nothing, it may be specialized in subclasses.
  488.      * </p>
  489.      * @param initialState initial state
  490.      * @param tEnd target date at which state should be propagated
  491.      */
  492.     protected void beforeIntegration(final FieldSpacecraftState<T> initialState,
  493.                                      final FieldAbsoluteDate<T> tEnd) {
  494.         // do nothing by default
  495.     }

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

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

  509.     }

  510.     /** Get the integrator used by the propagator.
  511.      * @return the integrator.
  512.      */
  513.     protected FieldODEIntegrator<T> getIntegrator() {
  514.         return integrator;
  515.     }

  516.     /** Get a complete state with all additional equations.
  517.      * @param t current value of the independent <I>time</I> variable
  518.      * @param ts array containing the current value of the state vector
  519.      * @param tsDot array containing the current value of the state vector derivative
  520.      * @return complete state
  521.      */
  522.     private FieldSpacecraftState<T> getCompleteState(final T t, final T[] ts, final T[] tsDot) {

  523.         // main state
  524.         FieldSpacecraftState<T> state = stateMapper.mapArrayToState(t, ts, tsDot, true);  //not sure of the mean orbit, should be true
  525.         // pre-integrated additional states
  526.         state = updateAdditionalStates(state);

  527.         // additional states integrated here
  528.         if (!additionalEquations.isEmpty()) {

  529.             for (int i = 0; i < additionalEquations.size(); ++i) {
  530.                 state = state.addAdditionalState(additionalEquations.get(i).getName(),
  531.                                                  equationsMapper.extractEquationData(i + 1, ts));
  532.             }

  533.         }

  534.         return state;

  535.     }

  536.     /** Differential equations for the main state (orbit, attitude and mass). */
  537.     public interface MainStateEquations<T extends RealFieldElement<T>> {

  538.         /**
  539.          * Initialize the equations at the start of propagation. This method will be
  540.          * called before any calls to {@link #computeDerivatives(FieldSpacecraftState)}.
  541.          *
  542.          * <p> The default implementation of this method does nothing.
  543.          *
  544.          * @param initialState initial state information at the start of propagation.
  545.          * @param target       date of propagation. Not equal to {@code
  546.          *                     initialState.getDate()}.
  547.          */
  548.         void init(FieldSpacecraftState<T> initialState, FieldAbsoluteDate<T> target);

  549.         /** Compute differential equations for main state.
  550.          * @param state current state
  551.          * @return derivatives of main state
  552.          */
  553.         T[] computeDerivatives(FieldSpacecraftState<T> state);

  554.     }

  555.     /** Differential equations for the main state (orbit, attitude and mass), with converted API. */
  556.     private class ConvertedMainStateEquations implements FieldOrdinaryDifferentialEquation<T> {

  557.         /** Main state equations. */
  558.         private final MainStateEquations<T> main;

  559.         /** Simple constructor.
  560.          * @param main main state equations
  561.          */
  562.         ConvertedMainStateEquations(final MainStateEquations<T> main) {
  563.             this.main = main;
  564.             calls = 0;
  565.         }

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

  570.         @Override
  571.         public void init(final T t0, final T[] y0, final T finalTime) {
  572.             // update space dynamics view
  573.             FieldSpacecraftState<T> initialState = stateMapper.mapArrayToState(t0, y0, null, true);
  574.             initialState = updateAdditionalStates(initialState);
  575.             final FieldAbsoluteDate<T> target = stateMapper.mapDoubleToDate(finalTime);
  576.             main.init(initialState, target);
  577.         }
  578.         /** {@inheritDoc} */
  579.         public T[] computeDerivatives(final T t, final T[] y) {

  580.             // increment calls counter
  581.             ++calls;

  582.             // update space dynamics view
  583.             FieldSpacecraftState<T> currentState = stateMapper.mapArrayToState(t, y, null, true);
  584.             currentState = updateAdditionalStates(currentState);

  585.             // compute main state differentials
  586.             return main.computeDerivatives(currentState);

  587.         }

  588.     }

  589.     /** Differential equations for the secondary state (Jacobians, user variables ...), with converted API. */
  590.     private class ConvertedSecondaryStateEquations implements FieldSecondaryODE<T> {

  591.         /** Additional equations. */
  592.         private final FieldAdditionalEquations<T> equations;

  593.         /** Dimension of the additional state. */
  594.         private final int dimension;

  595.         /** Simple constructor.
  596.          * @param equations additional equations
  597.          * @param dimension dimension of the additional state
  598.          */
  599.         ConvertedSecondaryStateEquations(final FieldAdditionalEquations<T> equations,
  600.                                          final int dimension) {
  601.             this.equations = equations;
  602.             this.dimension = dimension;
  603.         }

  604.         /** {@inheritDoc} */
  605.         @Override
  606.         public int getDimension() {
  607.             return dimension;
  608.         }

  609.         /** {@inheritDoc} */
  610.         @Override
  611.         public void init(final T t0, final T[] primary0,
  612.                          final T[] secondary0, final T finalTime) {
  613.             // update space dynamics view
  614.             FieldSpacecraftState<T> initialState = stateMapper.mapArrayToState(t0, primary0, null, true);
  615.             initialState = updateAdditionalStates(initialState);
  616.             initialState = initialState.addAdditionalState(equations.getName(), secondary0);
  617.             final FieldAbsoluteDate<T> target = stateMapper.mapDoubleToDate(finalTime);
  618.             equations.init(initialState, target);
  619.         }

  620.         /** {@inheritDoc} */
  621.         @Override
  622.         public T[] computeDerivatives(final T t, final T[] primary,
  623.                                       final T[] primaryDot, final T[] secondary) {

  624.             // update space dynamics view
  625.             FieldSpacecraftState<T> currentState = stateMapper.mapArrayToState(t, primary, primaryDot, true);
  626.             currentState = updateAdditionalStates(currentState);
  627.             currentState = currentState.addAdditionalState(equations.getName(), secondary);

  628.             // compute additional derivatives
  629.             final T[] secondaryDot = MathArrays.buildArray(getField(), secondary.length);
  630.             final T[] additionalMainDot =
  631.                     equations.computeDerivatives(currentState, secondaryDot);
  632.             if (additionalMainDot != null) {
  633.                 // the additional equations have an effect on main equations
  634.                 for (int i = 0; i < additionalMainDot.length; ++i) {
  635.                     primaryDot[i] = primaryDot[i].add(additionalMainDot[i]);
  636.                 }
  637.             }
  638.             return secondaryDot;

  639.         }

  640.     }

  641.     /** Adapt an {@link org.orekit.propagation.events.FieldEventDetector<T>}
  642.      * to Hipparchus {@link org.hipparchus.ode.events.FieldODEEventHandler<T>} interface.
  643.      * @param <T> class type for the generic version
  644.      * @author Fabien Maussion
  645.      */
  646.     private class FieldAdaptedEventDetector implements FieldODEEventHandler<T> {

  647.         /** Underlying event detector. */
  648.         private final FieldEventDetector<T> detector;

  649.         /** Time of the previous call to g. */
  650.         private T lastT;

  651.         /** Value from the previous call to g. */
  652.         private T lastG;

  653.         /** Build a wrapped event detector.
  654.          * @param detector event detector to wrap
  655.         */
  656.         FieldAdaptedEventDetector(final FieldEventDetector<T> detector) {
  657.             this.detector = detector;
  658.             this.lastT    = getField().getZero().add(Double.NaN);
  659.             this.lastG    = getField().getZero().add(Double.NaN);
  660.         }

  661.         /** {@inheritDoc} */
  662.         public void init(final FieldODEStateAndDerivative<T> s0, final T t) {
  663.             detector.init(getCompleteState(s0.getTime(), s0.getCompleteState(), s0.getCompleteDerivative()),
  664.                           stateMapper.mapDoubleToDate(t));
  665.             this.lastT = getField().getZero().add(Double.NaN);
  666.             this.lastG = getField().getZero().add(Double.NaN);
  667.         }

  668.         /** {@inheritDoc} */
  669.         public T g(final FieldODEStateAndDerivative<T> s) {
  670.             if (!Precision.equals(lastT.getReal(), s.getTime().getReal(), 0)) {
  671.                 lastT = s.getTime();
  672.                 lastG = detector.g(getCompleteState(s.getTime(), s.getCompleteState(), s.getCompleteDerivative()));
  673.             }
  674.             return lastG;
  675.         }

  676.         /** {@inheritDoc} */
  677.         public Action eventOccurred(final FieldODEStateAndDerivative<T> s, final boolean increasing) {
  678.             final FieldEventHandler.Action whatNext = detector.eventOccurred(getCompleteState(s.getTime(),
  679.                                                                                               s.getCompleteState(),
  680.                                                                                               s.getCompleteDerivative()),
  681.                                                                         increasing);

  682.             switch (whatNext) {
  683.                 case STOP :
  684.                     return Action.STOP;
  685.                 case RESET_STATE :
  686.                     return Action.RESET_STATE;
  687.                 case RESET_DERIVATIVES :
  688.                     return Action.RESET_DERIVATIVES;
  689.                 default :
  690.                     return Action.CONTINUE;
  691.             }
  692.         }

  693.         /** {@inheritDoc} */
  694.         public FieldODEState<T> resetState(final FieldODEStateAndDerivative<T> s) {

  695.             final FieldSpacecraftState<T> oldState = getCompleteState(s.getTime(), s.getCompleteState(), s.getCompleteDerivative());
  696.             final FieldSpacecraftState<T> newState = detector.resetState(oldState);

  697.             // main part
  698.             final T[] primary    = MathArrays.buildArray(getField(), s.getPrimaryStateDimension());
  699.             stateMapper.mapStateToArray(newState, primary, null);

  700.             // secondary part
  701.             final T[][] secondary    = MathArrays.buildArray(getField(), additionalEquations.size(), -1);

  702.             for (int i = 0; i < additionalEquations.size(); ++i) {
  703.                 final FieldAdditionalEquations<T> additional = additionalEquations.get(i);
  704.                 final T[] NState = newState.getAdditionalState(additional.getName());
  705.                 secondary[i] = MathArrays.buildArray(getField(), NState.length);
  706.                 for (int j = 0; j < NState.length; j++)
  707.                     secondary[i][j] = NState[j];
  708.             }

  709.             return new FieldODEState<>(newState.getDate().durationFrom(getStartDate()),
  710.                                        primary, secondary);
  711.         }

  712.     }

  713.     /** Adapt an {@link org.orekit.propagation.sampling.FieldOrekitStepHandler<T>}
  714.      * to Hipparchus {@link FieldODEStepHandler<T>} interface.
  715.      * @author Luc Maisonobe
  716.      */
  717.     private class FieldAdaptedStepHandler
  718.         implements FieldOrekitStepInterpolator<T>, FieldODEStepHandler<T>, FieldModeHandler<T> {

  719.         /** Underlying handler. */
  720.         private final FieldOrekitStepHandler<T> handler;

  721.         /** Flag for handler . */
  722.         private boolean activate;

  723.         /** Build an instance.
  724.          * @param handler underlying handler to wrap
  725.          */
  726.         FieldAdaptedStepHandler(final FieldOrekitStepHandler<T> handler) {
  727.             this.handler = handler;
  728.         }

  729.         /** {@inheritDoc} */
  730.         public void initialize(final boolean activateHandlers,
  731.                                final FieldAbsoluteDate<T> targetDate) {
  732.             this.activate = activateHandlers;
  733.         }
  734.         /** {@inheritDoc} */
  735.         public void init(final FieldODEStateAndDerivative<T> s0, final T t) {
  736.             handler.init(getCompleteState(s0.getTime(), s0.getCompleteState(), s0.getCompleteDerivative()),
  737.                          stateMapper.mapDoubleToDate(t));
  738.         }

  739.         /** {@inheritDoc} */
  740.         public void handleStep(final FieldODEStateInterpolator<T> interpolator, final boolean isLast) {
  741.             mathInterpolator = interpolator;
  742.             if (activate) {
  743.                 handler.handleStep(this, isLast);
  744.             }
  745.         }

  746.         /** {@inheritDoc}} */
  747.         @Override
  748.         public FieldSpacecraftState<T> getPreviousState() {
  749.             return convert(mathInterpolator.getPreviousState());
  750.         }

  751.         /** {@inheritDoc}} */
  752.         @Override
  753.         public FieldSpacecraftState<T> getCurrentState() {
  754.             return convert(mathInterpolator.getCurrentState());
  755.         }

  756.         /** {@inheritDoc}} */
  757.         @Override
  758.         public FieldSpacecraftState<T> getInterpolatedState(final FieldAbsoluteDate<T> date) {
  759.             return convert(mathInterpolator.getInterpolatedState(date.durationFrom(getStartDate())));
  760.         }

  761.         /** Get the interpolated state.
  762.          * @param os mathematical state
  763.          * @return interpolated state at the current interpolation date
  764.                            * the date
  765.          * @see #getInterpolatedDate()
  766.          * @see #setInterpolatedDate(FieldAbsoluteDate<T>)
  767.          */
  768.         private FieldSpacecraftState<T> convert(final FieldODEStateAndDerivative<T> os) {
  769.             try {

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

  780.                 return s;

  781.             } catch (OrekitException oe) {
  782.                 throw new OrekitException(oe);
  783.             }
  784.         }

  785.         /** Check is integration direction is forward in date.
  786.          * @return true if integration is forward in date
  787.          */
  788.         public boolean isForward() {
  789.             return mathInterpolator.isForward();
  790.         }

  791.     }

  792.     private class FieldEphemerisModeHandler implements FieldModeHandler<T>, FieldODEStepHandler<T> {

  793.         /** Underlying raw mathematical model. */
  794.         private FieldDenseOutputModel<T> model;

  795.         /** Generated ephemeris. */
  796.         private FieldBoundedPropagator<T> ephemeris;

  797.         /** Flag for handler . */
  798.         private boolean activate;

  799.         /** the user supplied end date. Propagation may not end on this date. */
  800.         private FieldAbsoluteDate<T> endDate;

  801.         /** Creates a new instance of FieldEphemerisModeHandler which must be
  802.          *  filled by the propagator.
  803.          */
  804.         FieldEphemerisModeHandler() {
  805.         }

  806.         /** {@inheritDoc} */
  807.         public void initialize(final boolean activateHandlers,
  808.                                final FieldAbsoluteDate<T> targetDate) {
  809.             this.activate = activateHandlers;
  810.             this.model    = new FieldDenseOutputModel<>();
  811.             this.endDate  = targetDate;

  812.             // ephemeris will be generated when last step is processed
  813.             this.ephemeris = null;

  814.         }

  815.         /** Get the generated ephemeris.
  816.          * @return a new instance of the generated ephemeris
  817.          */
  818.         public FieldBoundedPropagator<T> getEphemeris() {
  819.             return ephemeris;
  820.         }

  821.         /** {@inheritDoc} */
  822.         public void handleStep(final FieldODEStateInterpolator<T> interpolator, final boolean isLast) {
  823.             if (activate) {
  824.                 model.handleStep(interpolator, isLast);
  825.                 if (isLast) {

  826.                     // set up the boundary dates
  827.                     final T tI = model.getInitialTime();
  828.                     final T tF = model.getFinalTime();
  829.                     // tI is almost? always zero
  830.                     final FieldAbsoluteDate<T> startDate =
  831.                             stateMapper.mapDoubleToDate(tI);
  832.                     final FieldAbsoluteDate<T> finalDate =
  833.                             stateMapper.mapDoubleToDate(tF, this.endDate);
  834.                     final FieldAbsoluteDate<T> minDate;
  835.                     final FieldAbsoluteDate<T> maxDate;
  836.                     if (tF.getReal() < tI.getReal()) {
  837.                         minDate = finalDate;
  838.                         maxDate = startDate;
  839.                     } else {
  840.                         minDate = startDate;
  841.                         maxDate = finalDate;
  842.                     }

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

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

  857.                     // create the ephemeris
  858.                     ephemeris = new FieldIntegratedEphemeris<>(startDate, minDate, maxDate,
  859.                                                                stateMapper, meanOrbit, model, unmanaged,
  860.                                                                getAdditionalStateProviders(), names);

  861.                 }
  862.             }
  863.         }

  864.         /** {@inheritDoc} */
  865.         public void init(final FieldODEStateAndDerivative<T> s0, final T t) {
  866.             model.init(s0, t);
  867.         }

  868.     }

  869. }