FieldDSSTPropagator.java

  1. /* Copyright 2002-2024 CS GROUP
  2.  * Licensed to CS GROUP (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.semianalytical.dsst;

  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.Collection;
  21. import java.util.Collections;
  22. import java.util.HashSet;
  23. import java.util.List;
  24. import java.util.Set;

  25. import org.hipparchus.CalculusFieldElement;
  26. import org.hipparchus.Field;
  27. import org.hipparchus.ode.FieldODEIntegrator;
  28. import org.hipparchus.ode.FieldODEStateAndDerivative;
  29. import org.hipparchus.ode.sampling.FieldODEStateInterpolator;
  30. import org.hipparchus.ode.sampling.FieldODEStepHandler;
  31. import org.hipparchus.util.FastMath;
  32. import org.hipparchus.util.MathArrays;
  33. import org.hipparchus.util.MathUtils;
  34. import org.orekit.annotation.DefaultDataContext;
  35. import org.orekit.attitudes.AttitudeProvider;
  36. import org.orekit.attitudes.FieldAttitude;
  37. import org.orekit.data.DataContext;
  38. import org.orekit.errors.OrekitException;
  39. import org.orekit.errors.OrekitInternalError;
  40. import org.orekit.errors.OrekitMessages;
  41. import org.orekit.frames.Frame;
  42. import org.orekit.orbits.FieldEquinoctialOrbit;
  43. import org.orekit.orbits.FieldOrbit;
  44. import org.orekit.orbits.OrbitType;
  45. import org.orekit.orbits.PositionAngleType;
  46. import org.orekit.propagation.FieldSpacecraftState;
  47. import org.orekit.propagation.PropagationType;
  48. import org.orekit.propagation.Propagator;
  49. import org.orekit.propagation.SpacecraftState;
  50. import org.orekit.propagation.integration.FieldAbstractIntegratedPropagator;
  51. import org.orekit.propagation.integration.FieldStateMapper;
  52. import org.orekit.propagation.numerical.FieldNumericalPropagator;
  53. import org.orekit.propagation.semianalytical.dsst.forces.DSSTForceModel;
  54. import org.orekit.propagation.semianalytical.dsst.forces.DSSTNewtonianAttraction;
  55. import org.orekit.propagation.semianalytical.dsst.forces.FieldShortPeriodTerms;
  56. import org.orekit.propagation.semianalytical.dsst.utilities.FieldAuxiliaryElements;
  57. import org.orekit.propagation.semianalytical.dsst.utilities.FieldFixedNumberInterpolationGrid;
  58. import org.orekit.propagation.semianalytical.dsst.utilities.FieldInterpolationGrid;
  59. import org.orekit.propagation.semianalytical.dsst.utilities.FieldMaxGapInterpolationGrid;
  60. import org.orekit.time.AbsoluteDate;
  61. import org.orekit.time.FieldAbsoluteDate;
  62. import org.orekit.utils.FieldArrayDictionary;
  63. import org.orekit.utils.ParameterDriver;
  64. import org.orekit.utils.ParameterObserver;
  65. import org.orekit.utils.TimeSpanMap;

  66. /**
  67.  * This class propagates {@link org.orekit.orbits.FieldOrbit orbits} using the DSST theory.
  68.  * <p>
  69.  * Whereas analytical propagators are configured only thanks to their various
  70.  * constructors and can be used immediately after construction, such a semianalytical
  71.  * propagator configuration involves setting several parameters between construction
  72.  * time and propagation time, just as numerical propagators.
  73.  * </p>
  74.  * <p>
  75.  * The configuration parameters that can be set are:
  76.  * </p>
  77.  * <ul>
  78.  * <li>the initial spacecraft state ({@link #setInitialState(FieldSpacecraftState)})</li>
  79.  * <li>the various force models ({@link #addForceModel(DSSTForceModel)},
  80.  * {@link #removeForceModels()})</li>
  81.  * <li>the discrete events that should be triggered during propagation (
  82.  * {@link #addEventDetector(org.orekit.propagation.events.FieldEventDetector)},
  83.  * {@link #clearEventsDetectors()})</li>
  84.  * <li>the binding logic with the rest of the application ({@link #getMultiplexer()})</li>
  85.  * </ul>
  86.  * <p>
  87.  * From these configuration parameters, only the initial state is mandatory.
  88.  * The default propagation settings are in {@link OrbitType#EQUINOCTIAL equinoctial}
  89.  * parameters with {@link PositionAngleType#TRUE true} longitude argument.
  90.  * The central attraction coefficient used to define the initial orbit will be used.
  91.  * However, specifying only the initial state would mean the propagator would use
  92.  * only Keplerian forces. In this case, the simpler
  93.  * {@link org.orekit.propagation.analytical.KeplerianPropagator KeplerianPropagator}
  94.  * class would be more effective.
  95.  * </p>
  96.  * <p>
  97.  * The underlying numerical integrator set up in the constructor may also have
  98.  * its own configuration parameters. Typical configuration parameters for adaptive
  99.  * stepsize integrators are the min, max and perhaps start step size as well as
  100.  * the absolute and/or relative errors thresholds.
  101.  * </p>
  102.  * <p>
  103.  * The state that is seen by the integrator is a simple six elements double array.
  104.  * These six elements are:
  105.  * <ul>
  106.  * <li>the {@link org.orekit.orbits.FieldEquinoctialOrbit equinoctial orbit parameters}
  107.  * (a, e<sub>x</sub>, e<sub>y</sub>, h<sub>x</sub>, h<sub>y</sub>, λ<sub>m</sub>)
  108.  * in meters and radians,</li>
  109.  * </ul>
  110.  *
  111.  * <p>By default, at the end of the propagation, the propagator resets the initial state to the final state,
  112.  * thus allowing a new propagation to be started from there without recomputing the part already performed.
  113.  * This behaviour can be chenged by calling {@link #setResetAtEnd(boolean)}.
  114.  * </p>
  115.  * <p>Beware the same instance cannot be used simultaneously by different threads, the class is <em>not</em>
  116.  * thread-safe.</p>
  117.  *
  118.  * @see FieldSpacecraftState
  119.  * @see DSSTForceModel
  120.  * @author Romain Di Costanzo
  121.  * @author Pascal Parraud
  122.  * @since 10.0
  123.  * @param <T> type of the field elements
  124.  */
  125. public class FieldDSSTPropagator<T extends CalculusFieldElement<T>> extends FieldAbstractIntegratedPropagator<T>  {

  126.     /** Retrograde factor I.
  127.      *  <p>
  128.      *  DSST model needs equinoctial orbit as internal representation.
  129.      *  Classical equinoctial elements have discontinuities when inclination
  130.      *  is close to zero. In this representation, I = +1. <br>
  131.      *  To avoid this discontinuity, another representation exists and equinoctial
  132.      *  elements can be expressed in a different way, called "retrograde" orbit.
  133.      *  This implies I = -1. <br>
  134.      *  As Orekit doesn't implement the retrograde orbit, I is always set to +1.
  135.      *  But for the sake of consistency with the theory, the retrograde factor
  136.      *  has been kept in the formulas.
  137.      *  </p>
  138.      */
  139.     private static final int I = 1;

  140.     /** Number of grid points per integration step to be used in interpolation of short periodics coefficients.*/
  141.     private static final int INTERPOLATION_POINTS_PER_STEP = 3;

  142.     /** Default value for epsilon. */
  143.     private static final double EPSILON_DEFAULT = 1.0e-13;

  144.     /** Default value for maxIterations. */
  145.     private static final int MAX_ITERATIONS_DEFAULT = 200;

  146.     /** Flag specifying whether the initial orbital state is given with osculating elements. */
  147.     private boolean initialIsOsculating;

  148.     /** Field used by this class.*/
  149.     private final Field<T> field;

  150.     /** Force models used to compute short periodic terms. */
  151.     private final transient List<DSSTForceModel> forceModels;

  152.     /** State mapper holding the force models. */
  153.     private FieldMeanPlusShortPeriodicMapper mapper;

  154.     /** Generator for the interpolation grid. */
  155.     private FieldInterpolationGrid<T> interpolationgrid;

  156.     /** Create a new instance of DSSTPropagator.
  157.      *  <p>
  158.      *  After creation, there are no perturbing forces at all.
  159.      *  This means that if {@link #addForceModel addForceModel}
  160.      *  is not called after creation, the integrated orbit will
  161.      *  follow a Keplerian evolution only.
  162.      *  </p>
  163.      *
  164.      * <p>This constructor uses the {@link DataContext#getDefault() default data context}.
  165.      *
  166.      *  @param field field used by default
  167.      *  @param integrator numerical integrator to use for propagation.
  168.      *  @param propagationType type of orbit to output (mean or osculating).
  169.      * @see #FieldDSSTPropagator(Field, FieldODEIntegrator, PropagationType,
  170.      * AttitudeProvider)
  171.      */
  172.     @DefaultDataContext
  173.     public FieldDSSTPropagator(final Field<T> field, final FieldODEIntegrator<T> integrator, final PropagationType propagationType) {
  174.         this(field, integrator, propagationType,
  175.                 Propagator.getDefaultLaw(DataContext.getDefault().getFrames()));
  176.     }

  177.     /** Create a new instance of DSSTPropagator.
  178.      *  <p>
  179.      *  After creation, there are no perturbing forces at all.
  180.      *  This means that if {@link #addForceModel addForceModel}
  181.      *  is not called after creation, the integrated orbit will
  182.      *  follow a Keplerian evolution only.
  183.      *  </p>
  184.      * @param field field used by default
  185.      *  @param integrator numerical integrator to use for propagation.
  186.      * @param propagationType type of orbit to output (mean or osculating).
  187.      * @param attitudeProvider attitude law to use.
  188.      * @since 10.1
  189.      */
  190.     public FieldDSSTPropagator(final Field<T> field,
  191.                                final FieldODEIntegrator<T> integrator,
  192.                                final PropagationType propagationType,
  193.                                final AttitudeProvider attitudeProvider) {
  194.         super(field, integrator, propagationType);
  195.         this.field  = field;
  196.         forceModels = new ArrayList<>();
  197.         initMapper(field);
  198.         // DSST uses only equinoctial orbits and mean longitude argument
  199.         setOrbitType(OrbitType.EQUINOCTIAL);
  200.         setPositionAngleType(PositionAngleType.MEAN);
  201.         setAttitudeProvider(attitudeProvider);
  202.         setInterpolationGridToFixedNumberOfPoints(INTERPOLATION_POINTS_PER_STEP);
  203.     }

  204.     /** Create a new instance of DSSTPropagator.
  205.      *  <p>
  206.      *  After creation, there are no perturbing forces at all.
  207.      *  This means that if {@link #addForceModel addForceModel}
  208.      *  is not called after creation, the integrated orbit will
  209.      *  follow a Keplerian evolution only. Only the mean orbits
  210.      *  will be generated.
  211.      *  </p>
  212.      *
  213.      * <p>This constructor uses the {@link DataContext#getDefault() default data context}.
  214.      *
  215.      *  @param field fied used by default
  216.      *  @param integrator numerical integrator to use for propagation.
  217.      * @see #FieldDSSTPropagator(Field, FieldODEIntegrator, AttitudeProvider)
  218.      */
  219.     @DefaultDataContext
  220.     public FieldDSSTPropagator(final Field<T> field, final FieldODEIntegrator<T> integrator) {
  221.         this(field, integrator,
  222.                 Propagator.getDefaultLaw(DataContext.getDefault().getFrames()));
  223.     }

  224.     /** Create a new instance of DSSTPropagator.
  225.      *  <p>
  226.      *  After creation, there are no perturbing forces at all.
  227.      *  This means that if {@link #addForceModel addForceModel}
  228.      *  is not called after creation, the integrated orbit will
  229.      *  follow a Keplerian evolution only. Only the mean orbits
  230.      *  will be generated.
  231.      *  </p>
  232.      * @param field fied used by default
  233.      *  @param integrator numerical integrator to use for propagation.
  234.      * @param attitudeProvider attitude law to use.
  235.      * @since 10.1
  236.      */
  237.     public FieldDSSTPropagator(final Field<T> field,
  238.                                final FieldODEIntegrator<T> integrator,
  239.                                final AttitudeProvider attitudeProvider) {
  240.         super(field, integrator, PropagationType.MEAN);
  241.         this.field  = field;
  242.         forceModels = new ArrayList<>();
  243.         initMapper(field);
  244.         // DSST uses only equinoctial orbits and mean longitude argument
  245.         setOrbitType(OrbitType.EQUINOCTIAL);
  246.         setPositionAngleType(PositionAngleType.MEAN);
  247.         setAttitudeProvider(attitudeProvider);
  248.         setInterpolationGridToFixedNumberOfPoints(INTERPOLATION_POINTS_PER_STEP);
  249.     }

  250.     /** Set the central attraction coefficient μ.
  251.      * <p>
  252.      * Setting the central attraction coefficient is
  253.      * equivalent to {@link #addForceModel(DSSTForceModel) add}
  254.      * a {@link DSSTNewtonianAttraction} force model.
  255.      * </p>
  256.     * @param mu central attraction coefficient (m³/s²)
  257.     * @see #addForceModel(DSSTForceModel)
  258.     * @see #getAllForceModels()
  259.     */
  260.     public void setMu(final T mu) {
  261.         addForceModel(new DSSTNewtonianAttraction(mu.getReal()));
  262.     }

  263.     /** Set the central attraction coefficient μ only in upper class.
  264.      * @param mu central attraction coefficient (m³/s²)
  265.      */
  266.     private void superSetMu(final T mu) {
  267.         super.setMu(mu);
  268.     }

  269.     /** Check if Newtonian attraction force model is available.
  270.      * <p>
  271.      * Newtonian attraction is always the last force model in the list.
  272.      * </p>
  273.      * @return true if Newtonian attraction force model is available
  274.      */
  275.     private boolean hasNewtonianAttraction() {
  276.         final int last = forceModels.size() - 1;
  277.         return last >= 0 && forceModels.get(last) instanceof DSSTNewtonianAttraction;
  278.     }

  279.     /** Set the initial state with osculating orbital elements.
  280.      *  @param initialState initial state (defined with osculating elements)
  281.      */
  282.     public void setInitialState(final FieldSpacecraftState<T> initialState) {
  283.         setInitialState(initialState, PropagationType.OSCULATING);
  284.     }

  285.     /** Set the initial state.
  286.      *  @param initialState initial state
  287.      *  @param stateType defined if the orbital state is defined with osculating or mean elements
  288.      */
  289.     public void setInitialState(final FieldSpacecraftState<T> initialState,
  290.                                 final PropagationType stateType) {
  291.         resetInitialState(initialState, stateType);
  292.     }

  293.     /** Reset the initial state.
  294.      *
  295.      *  @param state new initial state
  296.      */
  297.     @Override
  298.     public void resetInitialState(final FieldSpacecraftState<T> state) {
  299.         super.resetInitialState(state);
  300.         if (!hasNewtonianAttraction()) {
  301.             // use the state to define central attraction
  302.             setMu(state.getMu());
  303.         }
  304.         super.setStartDate(state.getDate());
  305.     }

  306.     /** {@inheritDoc}.
  307.      *
  308.      * <p>Change parameter {@link #initialIsOsculating()} accordingly
  309.      * @since 12.1.3
  310.      */
  311.     @Override
  312.     public void resetInitialState(final FieldSpacecraftState<T> state, final PropagationType stateType) {
  313.         // Reset initial state
  314.         resetInitialState(state);

  315.         // Change state of initial osculating, if needed
  316.         initialIsOsculating = stateType == PropagationType.OSCULATING;
  317.     }

  318.     /** Set the selected short periodic coefficients that must be stored as additional states.
  319.      * @param selectedCoefficients short periodic coefficients that must be stored as additional states
  320.      * (null means no coefficients are selected, empty set means all coefficients are selected)
  321.      */
  322.     public void setSelectedCoefficients(final Set<String> selectedCoefficients) {
  323.         mapper.setSelectedCoefficients(selectedCoefficients == null ?
  324.                                        null : new HashSet<String>(selectedCoefficients));
  325.     }

  326.     /** Get the selected short periodic coefficients that must be stored as additional states.
  327.      * @return short periodic coefficients that must be stored as additional states
  328.      * (null means no coefficients are selected, empty set means all coefficients are selected)
  329.      */
  330.     public Set<String> getSelectedCoefficients() {
  331.         final Set<String> set = mapper.getSelectedCoefficients();
  332.         return set == null ? null : Collections.unmodifiableSet(set);
  333.     }

  334.     /** Check if the initial state is provided in osculating elements.
  335.      * @return true if initial state is provided in osculating elements
  336.      */
  337.     public boolean initialIsOsculating() {
  338.         return initialIsOsculating;
  339.     }

  340.     /** Set the interpolation grid generator.
  341.      * <p>
  342.      * The generator will create an interpolation grid with a fixed
  343.      * number of points for each mean element integration step.
  344.      * </p>
  345.      * <p>
  346.      * If neither {@link #setInterpolationGridToFixedNumberOfPoints(int)}
  347.      * nor {@link #setInterpolationGridToMaxTimeGap(CalculusFieldElement)} has been called,
  348.      * by default the propagator is set as to 3 interpolations points per step.
  349.      * </p>
  350.      * @param interpolationPoints number of interpolation points at
  351.      * each integration step
  352.      * @see #setInterpolationGridToMaxTimeGap(CalculusFieldElement)
  353.      * @since 7.1
  354.      */
  355.     public void setInterpolationGridToFixedNumberOfPoints(final int interpolationPoints) {
  356.         interpolationgrid = new FieldFixedNumberInterpolationGrid<>(field, interpolationPoints);
  357.     }

  358.     /** Set the interpolation grid generator.
  359.      * <p>
  360.      * The generator will create an interpolation grid with a maximum
  361.      * time gap between interpolation points.
  362.      * </p>
  363.      * <p>
  364.      * If neither {@link #setInterpolationGridToFixedNumberOfPoints(int)}
  365.      * nor {@link #setInterpolationGridToMaxTimeGap(CalculusFieldElement)} has been called,
  366.      * by default the propagator is set as to 3 interpolations points per step.
  367.      * </p>
  368.      * @param maxGap maximum time gap between interpolation points (seconds)
  369.      * @see #setInterpolationGridToFixedNumberOfPoints(int)
  370.      * @since 7.1
  371.      */
  372.     public void setInterpolationGridToMaxTimeGap(final T maxGap) {
  373.         interpolationgrid = new FieldMaxGapInterpolationGrid<>(field, maxGap);
  374.     }

  375.     /** Add a force model to the global perturbation model.
  376.      *  <p>
  377.      *  If this method is not called at all,
  378.      *  the integrated orbit will follow a Keplerian evolution only.
  379.      *  </p>
  380.      *  @param force perturbing {@link DSSTForceModel force} to add
  381.      *  @see #removeForceModels()
  382.      *  @see #setMu(CalculusFieldElement)
  383.      */
  384.     public void addForceModel(final DSSTForceModel force) {

  385.         if (force instanceof DSSTNewtonianAttraction) {
  386.             // we want to add the central attraction force model

  387.             try {
  388.                 // ensure we are notified of any mu change
  389.                 force.getParametersDrivers().get(0).addObserver(new ParameterObserver() {
  390.                     /** {@inheritDoc} */
  391.                     @Override
  392.                     public void valueChanged(final double previousValue, final ParameterDriver driver, final AbsoluteDate date) {
  393.                         // mu PDriver should have only 1 span
  394.                         superSetMu(field.getZero().newInstance(driver.getValue()));
  395.                     }
  396.                     /** {@inheritDoc} */
  397.                     @Override
  398.                     public void valueSpanMapChanged(final TimeSpanMap<Double> previousValue, final ParameterDriver driver) {
  399.                         // mu PDriver should have only 1 span
  400.                         superSetMu(field.getZero().newInstance(driver.getValue()));
  401.                     }
  402.                 });
  403.             } catch (OrekitException oe) {
  404.                 // this should never happen
  405.                 throw new OrekitInternalError(oe);
  406.             }

  407.             if (hasNewtonianAttraction()) {
  408.                 // there is already a central attraction model, replace it
  409.                 forceModels.set(forceModels.size() - 1, force);
  410.             } else {
  411.                 // there are no central attraction model yet, add it at the end of the list
  412.                 forceModels.add(force);
  413.             }
  414.         } else {
  415.             // we want to add a perturbing force model
  416.             if (hasNewtonianAttraction()) {
  417.                 // insert the new force model before Newtonian attraction,
  418.                 // which should always be the last one in the list
  419.                 forceModels.add(forceModels.size() - 1, force);
  420.             } else {
  421.                 // we only have perturbing force models up to now, just append at the end of the list
  422.                 forceModels.add(force);
  423.             }
  424.         }

  425.         force.registerAttitudeProvider(getAttitudeProvider());

  426.     }

  427.     /** Remove all perturbing force models from the global perturbation model
  428.      *  (except central attraction).
  429.      *  <p>
  430.      *  Once all perturbing forces have been removed (and as long as no new force model is added),
  431.      *  the integrated orbit will follow a Keplerian evolution only.
  432.      *  </p>
  433.      *  @see #addForceModel(DSSTForceModel)
  434.      */
  435.     public void removeForceModels() {
  436.         final int last = forceModels.size() - 1;
  437.         if (hasNewtonianAttraction()) {
  438.             // preserve the Newtonian attraction model at the end
  439.             final DSSTForceModel newton = forceModels.get(last);
  440.             forceModels.clear();
  441.             forceModels.add(newton);
  442.         } else {
  443.             forceModels.clear();
  444.         }
  445.     }

  446.     /** Get all the force models, perturbing forces and Newtonian attraction included.
  447.      * @return list of perturbing force models, with Newtonian attraction being the
  448.      * last one
  449.      * @see #addForceModel(DSSTForceModel)
  450.      * @see #setMu(CalculusFieldElement)
  451.      */
  452.     public List<DSSTForceModel> getAllForceModels() {
  453.         return Collections.unmodifiableList(forceModels);
  454.     }

  455.     /** Get propagation parameter type.
  456.      * @return orbit type used for propagation
  457.      */
  458.     public OrbitType getOrbitType() {
  459.         return super.getOrbitType();
  460.     }

  461.     /** Get propagation parameter type.
  462.      * @return angle type to use for propagation
  463.      */
  464.     public PositionAngleType getPositionAngleType() {
  465.         return super.getPositionAngleType();
  466.     }

  467.     /** Conversion from mean to osculating orbit.
  468.      * <p>
  469.      * Compute osculating state <b>in a DSST sense</b>, corresponding to the
  470.      * mean SpacecraftState in input, and according to the Force models taken
  471.      * into account.
  472.      * </p><p>
  473.      * Since the osculating state is obtained by adding short-periodic variation
  474.      * of each force model, the resulting output will depend on the
  475.      * force models parameterized in input.
  476.      * </p>
  477.      * @param mean Mean state to convert
  478.      * @param forces Forces to take into account
  479.      * @param attitudeProvider attitude provider (may be null if there are no Gaussian force models
  480.      * like atmospheric drag, radiation pressure or specific user-defined models)
  481.      * @param <T> type of the elements
  482.      * @return osculating state in a DSST sense
  483.      */
  484.     @SuppressWarnings("unchecked")
  485.     public static <T extends CalculusFieldElement<T>> FieldSpacecraftState<T> computeOsculatingState(final FieldSpacecraftState<T> mean,
  486.                                                                                                  final AttitudeProvider attitudeProvider,
  487.                                                                                                  final Collection<DSSTForceModel> forces) {

  488.         //Create the auxiliary object
  489.         final FieldAuxiliaryElements<T> aux = new FieldAuxiliaryElements<>(mean.getOrbit(), I);

  490.         // Set the force models
  491.         final List<FieldShortPeriodTerms<T>> shortPeriodTerms = new ArrayList<>();
  492.         for (final DSSTForceModel force : forces) {
  493.             force.registerAttitudeProvider(attitudeProvider);
  494.             shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, PropagationType.OSCULATING, force.getParameters(mean.getDate().getField(), mean.getDate())));
  495.             force.updateShortPeriodTerms(force.getParametersAllValues(mean.getDate().getField()), mean);
  496.         }

  497.         final FieldEquinoctialOrbit<T> osculatingOrbit = computeOsculatingOrbit(mean, shortPeriodTerms);

  498.         return new FieldSpacecraftState<>(osculatingOrbit, mean.getAttitude(), mean.getMass(),
  499.                                           mean.getAdditionalStatesValues());

  500.     }

  501.     /** Conversion from osculating to mean orbit.
  502.      * <p>
  503.      * Compute mean state <b>in a DSST sense</b>, corresponding to the
  504.      * osculating SpacecraftState in input, and according to the Force models
  505.      * taken into account.
  506.      * </p><p>
  507.      * Since the osculating state is obtained with the computation of
  508.      * short-periodic variation of each force model, the resulting output will
  509.      * depend on the force models parameterized in input.
  510.      * </p><p>
  511.      * The computation is done through a fixed-point iteration process.
  512.      * </p>
  513.      * @param osculating Osculating state to convert
  514.      * @param attitudeProvider attitude provider (may be null if there are no Gaussian force models
  515.      * like atmospheric drag, radiation pressure or specific user-defined models)
  516.      * @param forceModel Forces to take into account
  517.      * @param <T> type of the elements
  518.      * @return mean state in a DSST sense
  519.      */
  520.     public static <T extends CalculusFieldElement<T>> FieldSpacecraftState<T> computeMeanState(final FieldSpacecraftState<T> osculating,
  521.                                                                                            final AttitudeProvider attitudeProvider,
  522.                                                                                            final Collection<DSSTForceModel> forceModel) {
  523.         return computeMeanState(osculating, attitudeProvider, forceModel, EPSILON_DEFAULT, MAX_ITERATIONS_DEFAULT);
  524.     }

  525.     /** Conversion from osculating to mean orbit.
  526.      * <p>
  527.      * Compute mean state <b>in a DSST sense</b>, corresponding to the
  528.      * osculating SpacecraftState in input, and according to the Force models
  529.      * taken into account.
  530.      * </p><p>
  531.      * Since the osculating state is obtained with the computation of
  532.      * short-periodic variation of each force model, the resulting output will
  533.      * depend on the force models parameterized in input.
  534.      * </p><p>
  535.      * The computation is done through a fixed-point iteration process.
  536.      * </p>
  537.      * @param osculating Osculating state to convert
  538.      * @param attitudeProvider attitude provider (may be null if there are no Gaussian force models
  539.      * like atmospheric drag, radiation pressure or specific user-defined models)
  540.      * @param forceModel Forces to take into account
  541.      * @param epsilon convergence threshold for mean parameters conversion
  542.      * @param maxIterations maximum iterations for mean parameters conversion
  543.      * @return mean state in a DSST sense
  544.      * @param <T> type of the elements
  545.      * @since 10.1
  546.      */
  547.     public static <T extends CalculusFieldElement<T>> FieldSpacecraftState<T> computeMeanState(final FieldSpacecraftState<T> osculating,
  548.                                                                                            final AttitudeProvider attitudeProvider,
  549.                                                                                            final Collection<DSSTForceModel> forceModel,
  550.                                                                                            final double epsilon,
  551.                                                                                            final int maxIterations) {
  552.         final FieldOrbit<T> meanOrbit = computeMeanOrbit(osculating, attitudeProvider, forceModel, epsilon, maxIterations);
  553.         return new FieldSpacecraftState<>(meanOrbit, osculating.getAttitude(), osculating.getMass(),
  554.                                           osculating.getAdditionalStatesValues());
  555.     }

  556.     /** Override the default value of the parameter.
  557.     *  <p>
  558.     *  By default, if the initial orbit is defined as osculating,
  559.     *  it will be averaged over 2 satellite revolutions.
  560.     *  This can be changed by using this method.
  561.     *  </p>
  562.     *  @param satelliteRevolution number of satellite revolutions to use for converting osculating to mean
  563.     *                             elements
  564.     */
  565.     public void setSatelliteRevolution(final int satelliteRevolution) {
  566.         mapper.setSatelliteRevolution(satelliteRevolution);
  567.     }

  568.    /** Get the number of satellite revolutions to use for converting osculating to mean elements.
  569.     *  @return number of satellite revolutions to use for converting osculating to mean elements
  570.     */
  571.     public int getSatelliteRevolution() {
  572.         return mapper.getSatelliteRevolution();
  573.     }

  574.    /** {@inheritDoc} */
  575.     @Override
  576.     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
  577.         super.setAttitudeProvider(attitudeProvider);

  578.         //Register the attitude provider for each force model
  579.         for (final DSSTForceModel force : forceModels) {
  580.             force.registerAttitudeProvider(attitudeProvider);
  581.         }
  582.     }

  583.     /** Method called just before integration.
  584.      * <p>
  585.      * The default implementation does nothing, it may be specialized in subclasses.
  586.      * </p>
  587.      * @param initialState initial state
  588.      * @param tEnd target date at which state should be propagated
  589.      */
  590.     @SuppressWarnings("unchecked")
  591.     @Override
  592.     protected void beforeIntegration(final FieldSpacecraftState<T> initialState,
  593.                                      final FieldAbsoluteDate<T> tEnd) {

  594.         // check if only mean elements must be used
  595.         final PropagationType type = isMeanOrbit();

  596.         // compute common auxiliary elements
  597.         final FieldAuxiliaryElements<T> aux = new FieldAuxiliaryElements<>(initialState.getOrbit(), I);

  598.         // initialize all perturbing forces
  599.         final List<FieldShortPeriodTerms<T>> shortPeriodTerms = new ArrayList<>();
  600.         for (final DSSTForceModel force : forceModels) {
  601.             shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, type, force.getParameters(field, initialState.getDate())));
  602.         }
  603.         mapper.setShortPeriodTerms(shortPeriodTerms);

  604.         // if required, insert the special short periodics step handler
  605.         if (type == PropagationType.OSCULATING) {
  606.             final FieldShortPeriodicsHandler spHandler = new FieldShortPeriodicsHandler(forceModels);
  607.             // Compute short periodic coefficients for this point
  608.             for (DSSTForceModel forceModel : forceModels) {
  609.                 forceModel.updateShortPeriodTerms(forceModel.getParametersAllValues(field), initialState);

  610.             }
  611.             final Collection<FieldODEStepHandler<T>> stepHandlers = new ArrayList<>();
  612.             stepHandlers.add(spHandler);
  613.             final FieldODEIntegrator<T> integrator = getIntegrator();
  614.             final Collection<FieldODEStepHandler<T>> existing = integrator.getStepHandlers();
  615.             stepHandlers.addAll(existing);

  616.             integrator.clearStepHandlers();

  617.             // add back the existing handlers after the short periodics one
  618.             for (final FieldODEStepHandler<T> sp : stepHandlers) {
  619.                 integrator.addStepHandler(sp);
  620.             }
  621.         }
  622.     }

  623.     /** {@inheritDoc} */
  624.     @Override
  625.     protected void afterIntegration() {
  626.         // remove the special short periodics step handler if added before
  627.         if (isMeanOrbit() ==  PropagationType.OSCULATING) {
  628.             final List<FieldODEStepHandler<T>> preserved = new ArrayList<>();
  629.             final FieldODEIntegrator<T> integrator = getIntegrator();

  630.             // clear the list
  631.             integrator.clearStepHandlers();

  632.             // add back the step handlers that were important for the user
  633.             for (final FieldODEStepHandler<T> sp : preserved) {
  634.                 integrator.addStepHandler(sp);
  635.             }
  636.         }
  637.     }

  638.     /** Compute mean state from osculating state.
  639.      * <p>
  640.      * Compute in a DSST sense the mean state corresponding to the input osculating state.
  641.      * </p><p>
  642.      * The computing is done through a fixed-point iteration process.
  643.      * </p>
  644.      * @param osculating initial osculating state
  645.      * @param attitudeProvider attitude provider (may be null if there are no Gaussian force models
  646.      * like atmospheric drag, radiation pressure or specific user-defined models)
  647.      * @param forceModel force models
  648.      * @param epsilon convergence threshold for mean parameters conversion
  649.      * @param maxIterations maximum iterations for mean parameters conversion
  650.      * @param <T> type of the elements
  651.      * @return mean state
  652.      * @since 10.1
  653.      */
  654.     @SuppressWarnings("unchecked")
  655.     private static <T extends CalculusFieldElement<T>> FieldOrbit<T> computeMeanOrbit(final FieldSpacecraftState<T> osculating, final AttitudeProvider attitudeProvider, final Collection<DSSTForceModel> forceModel,
  656.                                                                                   final double epsilon, final int maxIterations) {

  657.         // zero
  658.         final T zero = osculating.getDate().getField().getZero();

  659.         // rough initialization of the mean parameters
  660.         FieldEquinoctialOrbit<T> meanOrbit = (FieldEquinoctialOrbit<T>) OrbitType.EQUINOCTIAL.convertType(osculating.getOrbit());

  661.         // threshold for each parameter
  662.         final T epsilonT   = zero.newInstance(epsilon);
  663.         final T thresholdA = epsilonT.multiply(FastMath.abs(meanOrbit.getA()).add(1.));
  664.         final T thresholdE = epsilonT.multiply(meanOrbit.getE().add(1.));
  665.         final T thresholdI = epsilonT.multiply(meanOrbit.getI().add(1.));
  666.         final T thresholdL = epsilonT.multiply(zero.getPi());

  667.         // ensure all Gaussian force models can rely on attitude
  668.         for (final DSSTForceModel force : forceModel) {
  669.             force.registerAttitudeProvider(attitudeProvider);
  670.         }

  671.         int i = 0;
  672.         while (i++ < maxIterations) {

  673.             final FieldSpacecraftState<T> meanState = new FieldSpacecraftState<>(meanOrbit, osculating.getAttitude(), osculating.getMass());

  674.             //Create the auxiliary object
  675.             final FieldAuxiliaryElements<T> aux = new FieldAuxiliaryElements<>(meanOrbit, I);

  676.             // Set the force models
  677.             final List<FieldShortPeriodTerms<T>> shortPeriodTerms = new ArrayList<>();
  678.             for (final DSSTForceModel force : forceModel) {
  679.                 shortPeriodTerms.addAll(force.initializeShortPeriodTerms(aux, PropagationType.OSCULATING,
  680.                                  force.getParameters(osculating.getDate().getField(), osculating.getDate())));
  681.                 force.updateShortPeriodTerms(force.getParametersAllValues(osculating.getDate().getField()), meanState);
  682.             }

  683.             // recompute the osculating parameters from the current mean parameters
  684.             final FieldEquinoctialOrbit<T> rebuilt = computeOsculatingOrbit(meanState, shortPeriodTerms);

  685.             // adapted parameters residuals
  686.             final T deltaA  = osculating.getA().subtract(rebuilt.getA());
  687.             final T deltaEx = osculating.getEquinoctialEx().subtract(rebuilt.getEquinoctialEx());
  688.             final T deltaEy = osculating.getEquinoctialEy().subtract(rebuilt.getEquinoctialEy());
  689.             final T deltaHx = osculating.getHx().subtract(rebuilt.getHx());
  690.             final T deltaHy = osculating.getHy().subtract(rebuilt.getHy());
  691.             final T deltaLv = MathUtils.normalizeAngle(osculating.getLv().subtract(rebuilt.getLv()), zero);

  692.             // check convergence
  693.             if (FastMath.abs(deltaA).getReal()  < thresholdA.getReal() &&
  694.                 FastMath.abs(deltaEx).getReal() < thresholdE.getReal() &&
  695.                 FastMath.abs(deltaEy).getReal() < thresholdE.getReal() &&
  696.                 FastMath.abs(deltaHx).getReal() < thresholdI.getReal() &&
  697.                 FastMath.abs(deltaHy).getReal() < thresholdI.getReal() &&
  698.                 FastMath.abs(deltaLv).getReal() < thresholdL.getReal()) {
  699.                 return meanOrbit;
  700.             }

  701.             // update mean parameters
  702.             meanOrbit = new FieldEquinoctialOrbit<>(meanOrbit.getA().add(deltaA),
  703.                                                     meanOrbit.getEquinoctialEx().add(deltaEx),
  704.                                                     meanOrbit.getEquinoctialEy().add(deltaEy),
  705.                                                     meanOrbit.getHx().add(deltaHx),
  706.                                                     meanOrbit.getHy().add(deltaHy),
  707.                                                     meanOrbit.getLv().add(deltaLv),
  708.                                                     PositionAngleType.TRUE, meanOrbit.getFrame(),
  709.                                                     meanOrbit.getDate(), meanOrbit.getMu());
  710.         }

  711.         throw new OrekitException(OrekitMessages.UNABLE_TO_COMPUTE_DSST_MEAN_PARAMETERS, i);
  712.     }

  713.     /** Compute osculating state from mean state.
  714.      * <p>
  715.      * Compute and add the short periodic variation to the mean {@link SpacecraftState}.
  716.      * </p>
  717.      * @param meanState initial mean state
  718.      * @param shortPeriodTerms short period terms
  719.      * @param <T> type of the elements
  720.      * @return osculating state
  721.      */
  722.     private static <T extends CalculusFieldElement<T>> FieldEquinoctialOrbit<T> computeOsculatingOrbit(final FieldSpacecraftState<T> meanState,
  723.                                                                                                    final List<FieldShortPeriodTerms<T>> shortPeriodTerms) {

  724.         final T[] mean = MathArrays.buildArray(meanState.getDate().getField(), 6);
  725.         final T[] meanDot = MathArrays.buildArray(meanState.getDate().getField(), 6);
  726.         OrbitType.EQUINOCTIAL.mapOrbitToArray(meanState.getOrbit(), PositionAngleType.MEAN, mean, meanDot);
  727.         final T[] y = mean.clone();
  728.         for (final FieldShortPeriodTerms<T> spt : shortPeriodTerms) {
  729.             final T[] shortPeriodic = spt.value(meanState.getOrbit());
  730.             for (int i = 0; i < shortPeriodic.length; i++) {
  731.                 y[i] = y[i].add(shortPeriodic[i]);
  732.             }
  733.         }
  734.         return (FieldEquinoctialOrbit<T>) OrbitType.EQUINOCTIAL.mapArrayToOrbit(y, meanDot,
  735.                                                                                 PositionAngleType.MEAN, meanState.getDate(),
  736.                                                                                 meanState.getMu(), meanState.getFrame());
  737.     }

  738.     /** {@inheritDoc} */
  739.     @Override
  740.     protected FieldSpacecraftState<T> getInitialIntegrationState() {
  741.         if (initialIsOsculating) {
  742.             // the initial state is an osculating state,
  743.             // it must be converted to mean state
  744.             return computeMeanState(getInitialState(), getAttitudeProvider(), forceModels);
  745.         } else {
  746.             // the initial state is already a mean state
  747.             return getInitialState();
  748.         }
  749.     }

  750.     /** {@inheritDoc}
  751.      * <p>
  752.      * Note that for DSST, orbit type is hardcoded to {@link OrbitType#EQUINOCTIAL}
  753.      * and position angle type is hardcoded to {@link PositionAngleType#MEAN}, so
  754.      * the corresponding parameters are ignored.
  755.      * </p>
  756.      */
  757.     @Override
  758.     protected FieldStateMapper<T> createMapper(final FieldAbsoluteDate<T> referenceDate, final T mu,
  759.                                                final OrbitType ignoredOrbitType, final PositionAngleType ignoredPositionAngleType,
  760.                                                final AttitudeProvider attitudeProvider, final Frame frame) {

  761.         // create a mapper with the common settings provided as arguments
  762.         final FieldMeanPlusShortPeriodicMapper newMapper =
  763.                 new FieldMeanPlusShortPeriodicMapper(referenceDate, mu, attitudeProvider, frame);

  764.         // copy the specific settings from the existing mapper
  765.         if (mapper != null) {
  766.             newMapper.setSatelliteRevolution(mapper.getSatelliteRevolution());
  767.             newMapper.setSelectedCoefficients(mapper.getSelectedCoefficients());
  768.             newMapper.setShortPeriodTerms(mapper.getShortPeriodTerms());
  769.         }

  770.         mapper = newMapper;
  771.         return mapper;

  772.     }

  773.     /** Internal mapper using mean parameters plus short periodic terms. */
  774.     private class FieldMeanPlusShortPeriodicMapper extends FieldStateMapper<T> {

  775.         /** Short periodic coefficients that must be stored as additional states. */
  776.         private Set<String>                selectedCoefficients;

  777.         /** Number of satellite revolutions in the averaging interval. */
  778.         private int                        satelliteRevolution;

  779.         /** Short period terms. */
  780.         private List<FieldShortPeriodTerms<T>>     shortPeriodTerms;

  781.         /** Simple constructor.
  782.          * @param referenceDate reference date
  783.          * @param mu central attraction coefficient (m³/s²)
  784.          * @param attitudeProvider attitude provider
  785.          * @param frame inertial frame
  786.          */
  787.         FieldMeanPlusShortPeriodicMapper(final FieldAbsoluteDate<T> referenceDate, final T mu,
  788.                                          final AttitudeProvider attitudeProvider, final Frame frame) {

  789.             super(referenceDate, mu, OrbitType.EQUINOCTIAL, PositionAngleType.MEAN, attitudeProvider, frame);

  790.             this.selectedCoefficients = null;

  791.             // Default averaging period for conversion from osculating to mean elements
  792.             this.satelliteRevolution = 2;

  793.             this.shortPeriodTerms    = Collections.emptyList();

  794.         }

  795.         /** {@inheritDoc} */
  796.         @Override
  797.         public FieldSpacecraftState<T> mapArrayToState(final FieldAbsoluteDate<T> date,
  798.                                                        final T[] y, final T[] yDot,
  799.                                                        final PropagationType type) {

  800.             // add short periodic variations to mean elements to get osculating elements
  801.             // (the loop may not be performed if there are no force models and in the
  802.             //  case we want to remain in mean parameters only)
  803.             final T[] elements = y.clone();
  804.             final FieldArrayDictionary<T> coefficients;
  805.             switch (type) {
  806.                 case MEAN:
  807.                     coefficients = null;
  808.                     break;
  809.                 case OSCULATING:
  810.                     final FieldOrbit<T> meanOrbit = OrbitType.EQUINOCTIAL.mapArrayToOrbit(elements, yDot, PositionAngleType.MEAN, date, getMu(), getFrame());
  811.                     coefficients = selectedCoefficients == null ? null : new FieldArrayDictionary<>(date.getField());
  812.                     for (final FieldShortPeriodTerms<T> spt : shortPeriodTerms) {
  813.                         final T[] shortPeriodic = spt.value(meanOrbit);
  814.                         for (int i = 0; i < shortPeriodic.length; i++) {
  815.                             elements[i] = elements[i].add(shortPeriodic[i]);
  816.                         }
  817.                         if (selectedCoefficients != null) {
  818.                             coefficients.putAll(spt.getCoefficients(date, selectedCoefficients));
  819.                         }
  820.                     }
  821.                     break;
  822.                 default:
  823.                     throw new OrekitInternalError(null);
  824.             }

  825.             final T mass = elements[6];
  826.             if (mass.getReal() <= 0.0) {
  827.                 throw new OrekitException(OrekitMessages.NOT_POSITIVE_SPACECRAFT_MASS, mass);
  828.             }

  829.             final FieldOrbit<T> orbit       = OrbitType.EQUINOCTIAL.mapArrayToOrbit(elements, yDot, PositionAngleType.MEAN, date, getMu(), getFrame());
  830.             final FieldAttitude<T> attitude = getAttitudeProvider().getAttitude(orbit, date, getFrame());

  831.             if (coefficients == null) {
  832.                 return new FieldSpacecraftState<>(orbit, attitude, mass);
  833.             } else {
  834.                 return new FieldSpacecraftState<>(orbit, attitude, mass, coefficients);
  835.             }

  836.         }

  837.         /** {@inheritDoc} */
  838.         @Override
  839.         public void mapStateToArray(final FieldSpacecraftState<T> state, final T[] y, final T[] yDot) {

  840.             OrbitType.EQUINOCTIAL.mapOrbitToArray(state.getOrbit(), PositionAngleType.MEAN, y, yDot);
  841.             y[6] = state.getMass();

  842.         }

  843.         /** Set the number of satellite revolutions to use for converting osculating to mean elements.
  844.          *  <p>
  845.          *  By default, if the initial orbit is defined as osculating,
  846.          *  it will be averaged over 2 satellite revolutions.
  847.          *  This can be changed by using this method.
  848.          *  </p>
  849.          *  @param satelliteRevolution number of satellite revolutions to use for converting osculating to mean
  850.          *                             elements
  851.          */
  852.         public void setSatelliteRevolution(final int satelliteRevolution) {
  853.             this.satelliteRevolution = satelliteRevolution;
  854.         }

  855.         /** Get the number of satellite revolutions to use for converting osculating to mean elements.
  856.          *  @return number of satellite revolutions to use for converting osculating to mean elements
  857.          */
  858.         public int getSatelliteRevolution() {
  859.             return satelliteRevolution;
  860.         }

  861.         /** Set the selected short periodic coefficients that must be stored as additional states.
  862.          * @param selectedCoefficients short periodic coefficients that must be stored as additional states
  863.          * (null means no coefficients are selected, empty set means all coefficients are selected)
  864.          */
  865.         public void setSelectedCoefficients(final Set<String> selectedCoefficients) {
  866.             this.selectedCoefficients = selectedCoefficients;
  867.         }

  868.         /** Get the selected short periodic coefficients that must be stored as additional states.
  869.          * @return short periodic coefficients that must be stored as additional states
  870.          * (null means no coefficients are selected, empty set means all coefficients are selected)
  871.          */
  872.         public Set<String> getSelectedCoefficients() {
  873.             return selectedCoefficients;
  874.         }

  875.         /** Set the short period terms.
  876.          * @param shortPeriodTerms short period terms
  877.          * @since 7.1
  878.          */
  879.         public void setShortPeriodTerms(final List<FieldShortPeriodTerms<T>> shortPeriodTerms) {
  880.             this.shortPeriodTerms = shortPeriodTerms;
  881.         }

  882.         /** Get the short period terms.
  883.          * @return shortPeriodTerms short period terms
  884.          * @since 7.1
  885.          */
  886.         public List<FieldShortPeriodTerms<T>> getShortPeriodTerms() {
  887.             return shortPeriodTerms;
  888.         }

  889.     }

  890.     /** {@inheritDoc} */
  891.     @Override
  892.     protected MainStateEquations<T> getMainStateEquations(final FieldODEIntegrator<T> integrator) {
  893.         return new Main(integrator);
  894.     }

  895.     /** Internal class for mean parameters integration. */
  896.     private class Main implements MainStateEquations<T> {

  897.         /** Derivatives array. */
  898.         private final T[] yDot;

  899.         /** Simple constructor.
  900.          * @param integrator numerical integrator to use for propagation.
  901.          */
  902.         Main(final FieldODEIntegrator<T> integrator) {
  903.             yDot = MathArrays.buildArray(field, 7);

  904.             // Setup event detectors for each force model
  905.             forceModels.forEach(dsstForceModel -> dsstForceModel.getFieldEventDetectors(field).
  906.                                 forEach(eventDetector -> setUpEventDetector(integrator, eventDetector)));
  907.         }

  908.         /** {@inheritDoc} */
  909.         @Override
  910.         public void init(final FieldSpacecraftState<T> initialState, final FieldAbsoluteDate<T> target) {
  911.             forceModels.forEach(fm -> fm.init(initialState, target));
  912.         }

  913.         /** {@inheritDoc} */
  914.         @Override
  915.         public T[] computeDerivatives(final FieldSpacecraftState<T> state) {

  916.             final T zero = state.getDate().getField().getZero();
  917.             Arrays.fill(yDot, zero);

  918.             // compute common auxiliary elements
  919.             final FieldAuxiliaryElements<T> auxiliaryElements = new FieldAuxiliaryElements<>(state.getOrbit(), I);

  920.             // compute the contributions of all perturbing forces
  921.             for (final DSSTForceModel forceModel : forceModels) {
  922.                 final T[] daidt = elementRates(forceModel, state, auxiliaryElements, forceModel.getParametersAllValues(field));
  923.                 for (int i = 0; i < daidt.length; i++) {
  924.                     yDot[i] = yDot[i].add(daidt[i]);
  925.                 }
  926.             }

  927.             return yDot.clone();
  928.         }

  929.         /** This method allows to compute the mean equinoctial elements rates da<sub>i</sub> / dt
  930.          *  for a specific force model.
  931.          *  @param forceModel force to take into account
  932.          *  @param state current state
  933.          *  @param auxiliaryElements auxiliary elements related to the current orbit
  934.          *  @param parameters force model parameters (all span values for each parameters)
  935.          *  the extract parameter method {@link #extractParameters(double[], AbsoluteDate)} is called in
  936.          *  the method to select the right parameter.
  937.          *  @return the mean equinoctial elements rates da<sub>i</sub> / dt
  938.          */
  939.         private T[] elementRates(final DSSTForceModel forceModel,
  940.                                  final FieldSpacecraftState<T> state,
  941.                                  final FieldAuxiliaryElements<T> auxiliaryElements,
  942.                                  final T[] parameters) {
  943.             return forceModel.getMeanElementRate(state, auxiliaryElements, parameters);
  944.         }

  945.     }

  946.     /** Estimate tolerance vectors for an AdaptativeStepsizeIntegrator.
  947.      *  <p>
  948.      *  The errors are estimated from partial derivatives properties of orbits,
  949.      *  starting from a scalar position error specified by the user.
  950.      *  Considering the energy conservation equation V = sqrt(mu (2/r - 1/a)),
  951.      *  we get at constant energy (i.e. on a Keplerian trajectory):
  952.      *
  953.      *  <pre>
  954.      *  V r² |dV| = mu |dr|
  955.      *  </pre>
  956.      *
  957.      *  <p> So we deduce a scalar velocity error consistent with the position error. From here, we apply
  958.      *  orbits Jacobians matrices to get consistent errors on orbital parameters.
  959.      *
  960.      *  <p>
  961.      *  The tolerances are only <em>orders of magnitude</em>, and integrator tolerances are only
  962.      *  local estimates, not global ones. So some care must be taken when using these tolerances.
  963.      *  Setting 1mm as a position error does NOT mean the tolerances will guarantee a 1mm error
  964.      *  position after several orbits integration.
  965.      *  </p>
  966.      * @param <T> elements type
  967.      * @param dP user specified position error (m)
  968.      * @param orbit reference orbit
  969.      * @return a two rows array, row 0 being the absolute tolerance error
  970.      *                       and row 1 being the relative tolerance error
  971.      */
  972.     public static <T extends CalculusFieldElement<T>> double[][] tolerances(final T dP, final FieldOrbit<T> orbit) {
  973.         return FieldNumericalPropagator.tolerances(dP, orbit, OrbitType.EQUINOCTIAL);
  974.     }

  975.     /** Estimate tolerance vectors for an AdaptativeStepsizeIntegrator.
  976.      *  <p>
  977.      *  The errors are estimated from partial derivatives properties of orbits,
  978.      *  starting from scalar position and velocity errors specified by the user.
  979.      *  <p>
  980.      *  The tolerances are only <em>orders of magnitude</em>, and integrator tolerances are only
  981.      *  local estimates, not global ones. So some care must be taken when using these tolerances.
  982.      *  Setting 1mm as a position error does NOT mean the tolerances will guarantee a 1mm error
  983.      *  position after several orbits integration.
  984.      *  </p>
  985.      *
  986.      * @param <T> elements type
  987.      * @param dP user specified position error (m)
  988.      * @param dV user specified velocity error (m/s)
  989.      * @param orbit reference orbit
  990.      * @return a two rows array, row 0 being the absolute tolerance error
  991.      *                       and row 1 being the relative tolerance error
  992.      * @since 10.3
  993.      */
  994.     public static <T extends CalculusFieldElement<T>> double[][] tolerances(final T dP, final T dV,
  995.                                                                         final FieldOrbit<T> orbit) {
  996.         return FieldNumericalPropagator.tolerances(dP, dV, orbit, OrbitType.EQUINOCTIAL);
  997.     }

  998.     /** Step handler used to compute the parameters for the short periodic contributions.
  999.      * @author Lucian Barbulescu
  1000.      */
  1001.     private class FieldShortPeriodicsHandler implements FieldODEStepHandler<T> {

  1002.         /** Force models used to compute short periodic terms. */
  1003.         private final List<DSSTForceModel> forceModels;

  1004.         /** Constructor.
  1005.          * @param forceModels force models
  1006.          */
  1007.         FieldShortPeriodicsHandler(final List<DSSTForceModel> forceModels) {
  1008.             this.forceModels = forceModels;
  1009.         }

  1010.         /** {@inheritDoc} */
  1011.         @SuppressWarnings("unchecked")
  1012.         @Override
  1013.         public void handleStep(final FieldODEStateInterpolator<T> interpolator) {

  1014.             // Get the grid points to compute
  1015.             final T[] interpolationPoints =
  1016.                             interpolationgrid.getGridPoints(interpolator.getPreviousState().getTime(),
  1017.                                                             interpolator.getCurrentState().getTime());

  1018.             final FieldSpacecraftState<T>[] meanStates = new FieldSpacecraftState[interpolationPoints.length];
  1019.             for (int i = 0; i < interpolationPoints.length; ++i) {

  1020.                 // Build the mean state interpolated at grid point
  1021.                 final T time = interpolationPoints[i];
  1022.                 final FieldODEStateAndDerivative<T> sd = interpolator.getInterpolatedState(time);
  1023.                 meanStates[i] = mapper.mapArrayToState(time,
  1024.                                                        sd.getPrimaryState(),
  1025.                                                        sd.getPrimaryDerivative(),
  1026.                                                        PropagationType.MEAN);

  1027.             }

  1028.             // Compute short periodic coefficients for this step
  1029.             for (DSSTForceModel forceModel : forceModels) {
  1030.                 forceModel.updateShortPeriodTerms(forceModel.getParametersAllValues(field), meanStates);
  1031.             }

  1032.         }
  1033.     }

  1034. }