AbstractIntegratedPropagator.java

  1. /* Copyright 2002-2023 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.integration;

  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.Collection;
  21. import java.util.Collections;
  22. import java.util.HashMap;
  23. import java.util.LinkedList;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.Queue;

  27. import org.hipparchus.analysis.UnivariateFunction;
  28. import org.hipparchus.analysis.solvers.BracketedUnivariateSolver;
  29. import org.hipparchus.analysis.solvers.BracketingNthOrderBrentSolver;
  30. import org.hipparchus.exception.MathRuntimeException;
  31. import org.hipparchus.ode.DenseOutputModel;
  32. import org.hipparchus.ode.ExpandableODE;
  33. import org.hipparchus.ode.ODEIntegrator;
  34. import org.hipparchus.ode.ODEState;
  35. import org.hipparchus.ode.ODEStateAndDerivative;
  36. import org.hipparchus.ode.OrdinaryDifferentialEquation;
  37. import org.hipparchus.ode.SecondaryODE;
  38. import org.hipparchus.ode.events.Action;
  39. import org.hipparchus.ode.events.AdaptableInterval;
  40. import org.hipparchus.ode.events.ODEEventDetector;
  41. import org.hipparchus.ode.events.ODEEventHandler;
  42. import org.hipparchus.ode.sampling.AbstractODEStateInterpolator;
  43. import org.hipparchus.ode.sampling.ODEStateInterpolator;
  44. import org.hipparchus.ode.sampling.ODEStepHandler;
  45. import org.hipparchus.util.Precision;
  46. import org.orekit.attitudes.AttitudeProvider;
  47. import org.orekit.errors.OrekitException;
  48. import org.orekit.errors.OrekitInternalError;
  49. import org.orekit.errors.OrekitMessages;
  50. import org.orekit.frames.Frame;
  51. import org.orekit.orbits.OrbitType;
  52. import org.orekit.orbits.PositionAngleType;
  53. import org.orekit.propagation.AbstractPropagator;
  54. import org.orekit.propagation.BoundedPropagator;
  55. import org.orekit.propagation.EphemerisGenerator;
  56. import org.orekit.propagation.PropagationType;
  57. import org.orekit.propagation.SpacecraftState;
  58. import org.orekit.propagation.events.EventDetector;
  59. import org.orekit.propagation.events.handlers.EventHandler;
  60. import org.orekit.propagation.sampling.OrekitStepHandler;
  61. import org.orekit.propagation.sampling.OrekitStepInterpolator;
  62. import org.orekit.time.AbsoluteDate;
  63. import org.orekit.utils.DoubleArrayDictionary;


  64. /** Common handling of {@link org.orekit.propagation.Propagator Propagator}
  65.  *  methods for both numerical and semi-analytical propagators.
  66.  *  @author Luc Maisonobe
  67.  */
  68. public abstract class AbstractIntegratedPropagator extends AbstractPropagator {

  69.     /** Internal name used for complete secondary state dimension.
  70.      * @since 11.1
  71.      */
  72.     private static final String SECONDARY_DIMENSION = "Orekit-secondary-dimension";

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

  75.     /** Step handlers dedicated to ephemeris generation. */
  76.     private final List<StoringStepHandler> ephemerisGenerators;

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

  79.     /** Offsets of secondary states managed by {@link AdditionalEquations}.
  80.      * @since 11.1
  81.      */
  82.     private final Map<String, Integer> secondaryOffsets;

  83.     /** Additional derivatives providers.
  84.      * @since 11.1
  85.      */
  86.     private List<AdditionalDerivativesProvider> additionalDerivativesProviders;

  87.     /** Map of secondary equation offset in main
  88.     /** Counter for differential equations calls. */
  89.     private int calls;

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

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

  94.     /** Type of orbit to output (mean or osculating) <br/>
  95.      * <p>
  96.      * This is used only in the case of semi-analytical propagators where there is a clear separation between
  97.      * mean and short periodic elements. It is ignored by the Numerical propagator.
  98.      * </p>
  99.      */
  100.     private PropagationType propagationType;

  101.     /** Build a new instance.
  102.      * @param integrator numerical integrator to use for propagation.
  103.      * @param propagationType type of orbit to output (mean or osculating).
  104.      */
  105.     protected AbstractIntegratedPropagator(final ODEIntegrator integrator, final PropagationType propagationType) {
  106.         detectors                      = new ArrayList<>();
  107.         ephemerisGenerators            = new ArrayList<>();
  108.         additionalDerivativesProviders = new ArrayList<>();
  109.         this.secondaryOffsets          = new HashMap<>();
  110.         this.integrator                = integrator;
  111.         this.propagationType           = propagationType;
  112.         this.resetAtEnd                = true;
  113.     }

  114.     /** Allow/disallow resetting the initial state at end of propagation.
  115.      * <p>
  116.      * By default, at the end of the propagation, the propagator resets the initial state
  117.      * to the final state, thus allowing a new propagation to be started from there without
  118.      * recomputing the part already performed. Calling this method with {@code resetAtEnd} set
  119.      * to false changes prevents such reset.
  120.      * </p>
  121.      * @param resetAtEnd if true, at end of each propagation, the {@link
  122.      * #getInitialState() initial state} will be reset to the final state of
  123.      * the propagation, otherwise the initial state will be preserved
  124.      * @since 9.0
  125.      */
  126.     public void setResetAtEnd(final boolean resetAtEnd) {
  127.         this.resetAtEnd = resetAtEnd;
  128.     }

  129.     /** Getter for the resetting flag regarding initial state.
  130.      * @return resetting flag
  131.      * @since 12.0
  132.      */
  133.     public boolean getResetAtEnd() {
  134.         return this.resetAtEnd;
  135.     }

  136.     /** Initialize the mapper. */
  137.     protected void initMapper() {
  138.         stateMapper = createMapper(null, Double.NaN, null, null, null, null);
  139.     }

  140.     /** Get the integrator's name.
  141.      * @return name of underlying integrator
  142.      * @since 12.0
  143.      */
  144.     public String getIntegratorName() {
  145.         return integrator.getName();
  146.     }

  147.     /**  {@inheritDoc} */
  148.     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
  149.         super.setAttitudeProvider(attitudeProvider);
  150.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  151.                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  152.                                    attitudeProvider, stateMapper.getFrame());
  153.     }

  154.     /** Set propagation orbit type.
  155.      * @param orbitType orbit type to use for propagation, null for
  156.      * propagating using {@link org.orekit.utils.AbsolutePVCoordinates AbsolutePVCoordinates}
  157.      * rather than {@link org.orekit.orbits Orbit}
  158.      */
  159.     protected void setOrbitType(final OrbitType orbitType) {
  160.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  161.                                    orbitType, stateMapper.getPositionAngleType(),
  162.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  163.     }

  164.     /** Get propagation parameter type.
  165.      * @return orbit type used for propagation, null for
  166.      * propagating using {@link org.orekit.utils.AbsolutePVCoordinates AbsolutePVCoordinates}
  167.      * rather than {@link org.orekit.orbits Orbit}
  168.      */
  169.     protected OrbitType getOrbitType() {
  170.         return stateMapper.getOrbitType();
  171.     }

  172.     /** Get the propagation type.
  173.      * @return propagation type.
  174.      * @since 11.1
  175.      */
  176.     public PropagationType getPropagationType() {
  177.         return propagationType;
  178.     }

  179.     /** Set position angle type.
  180.      * <p>
  181.      * The position parameter type is meaningful only if {@link
  182.      * #getOrbitType() propagation orbit type}
  183.      * support it. As an example, it is not meaningful for propagation
  184.      * in {@link OrbitType#CARTESIAN Cartesian} parameters.
  185.      * </p>
  186.      * @param positionAngleType angle type to use for propagation
  187.      */
  188.     protected void setPositionAngleType(final PositionAngleType positionAngleType) {
  189.         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
  190.                                    stateMapper.getOrbitType(), positionAngleType,
  191.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  192.     }

  193.     /** Get propagation parameter type.
  194.      * @return angle type to use for propagation
  195.      */
  196.     protected PositionAngleType getPositionAngleType() {
  197.         return stateMapper.getPositionAngleType();
  198.     }

  199.     /** Set the central attraction coefficient μ.
  200.      * @param mu central attraction coefficient (m³/s²)
  201.      */
  202.     public void setMu(final double mu) {
  203.         stateMapper = createMapper(stateMapper.getReferenceDate(), mu,
  204.                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  205.                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
  206.     }

  207.     /** Get the central attraction coefficient μ.
  208.      * @return mu central attraction coefficient (m³/s²)
  209.      * @see #setMu(double)
  210.      */
  211.     public double getMu() {
  212.         return stateMapper.getMu();
  213.     }

  214.     /** Get the number of calls to the differential equations computation method.
  215.      * <p>The number of calls is reset each time the {@link #propagate(AbsoluteDate)}
  216.      * method is called.</p>
  217.      * @return number of calls to the differential equations computation method
  218.      */
  219.     public int getCalls() {
  220.         return calls;
  221.     }

  222.     /** {@inheritDoc} */
  223.     @Override
  224.     public boolean isAdditionalStateManaged(final String name) {

  225.         // first look at already integrated states
  226.         if (super.isAdditionalStateManaged(name)) {
  227.             return true;
  228.         }

  229.         // then look at states we integrate ourselves
  230.         for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  231.             if (provider.getName().equals(name)) {
  232.                 return true;
  233.             }
  234.         }

  235.         return false;
  236.     }

  237.     /** {@inheritDoc} */
  238.     @Override
  239.     public String[] getManagedAdditionalStates() {
  240.         final String[] alreadyIntegrated = super.getManagedAdditionalStates();
  241.         final String[] managed = new String[alreadyIntegrated.length + additionalDerivativesProviders.size()];
  242.         System.arraycopy(alreadyIntegrated, 0, managed, 0, alreadyIntegrated.length);
  243.         for (int i = 0; i < additionalDerivativesProviders.size(); ++i) {
  244.             managed[i + alreadyIntegrated.length] = additionalDerivativesProviders.get(i).getName();
  245.         }
  246.         return managed;
  247.     }

  248.     /** Add a provider for user-specified state derivatives to be integrated along with the orbit propagation.
  249.      * @param provider provider for additional derivatives
  250.      * @see #addAdditionalStateProvider(org.orekit.propagation.AdditionalStateProvider)
  251.      * @since 11.1
  252.      */
  253.     public void addAdditionalDerivativesProvider(final AdditionalDerivativesProvider provider) {

  254.         // check if the name is already used
  255.         if (isAdditionalStateManaged(provider.getName())) {
  256.             // these derivatives are already registered, complain
  257.             throw new OrekitException(OrekitMessages.ADDITIONAL_STATE_NAME_ALREADY_IN_USE,
  258.                                       provider.getName());
  259.         }

  260.         // this is really a new set of derivatives, add it
  261.         additionalDerivativesProviders.add(provider);

  262.         secondaryOffsets.clear();

  263.     }

  264.     /** Get an unmodifiable list of providers for additional derivatives.
  265.      * @return providers for the additional derivatives
  266.      * @since 11.1
  267.      */
  268.     public List<AdditionalDerivativesProvider> getAdditionalDerivativesProviders() {
  269.         return Collections.unmodifiableList(additionalDerivativesProviders);
  270.     }

  271.     /** {@inheritDoc} */
  272.     public void addEventDetector(final EventDetector detector) {
  273.         detectors.add(detector);
  274.     }

  275.     /** {@inheritDoc} */
  276.     public Collection<EventDetector> getEventsDetectors() {
  277.         return Collections.unmodifiableCollection(detectors);
  278.     }

  279.     /** {@inheritDoc} */
  280.     public void clearEventsDetectors() {
  281.         detectors.clear();
  282.     }

  283.     /** Set up all user defined event detectors.
  284.      */
  285.     protected void setUpUserEventDetectors() {
  286.         for (final EventDetector detector : detectors) {
  287.             setUpEventDetector(integrator, detector);
  288.         }
  289.     }

  290.     /** Wrap an Orekit event detector and register it to the integrator.
  291.      * @param integ integrator into which event detector should be registered
  292.      * @param detector event detector to wrap
  293.      */
  294.     protected void setUpEventDetector(final ODEIntegrator integ, final EventDetector detector) {
  295.         integ.addEventDetector(new AdaptedEventDetector(detector));
  296.     }

  297.     /** {@inheritDoc} */
  298.     @Override
  299.     public EphemerisGenerator getEphemerisGenerator() {
  300.         final StoringStepHandler storingHandler = new StoringStepHandler();
  301.         ephemerisGenerators.add(storingHandler);
  302.         return storingHandler;
  303.     }

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

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

  328.     /** {@inheritDoc} */
  329.     public SpacecraftState propagate(final AbsoluteDate target) {
  330.         if (getStartDate() == null) {
  331.             if (getInitialState() == null) {
  332.                 throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  333.             }
  334.             setStartDate(getInitialState().getDate());
  335.         }
  336.         return propagate(getStartDate(), target);
  337.     }

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

  340.         if (getInitialState() == null) {
  341.             throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
  342.         }

  343.         // make sure the integrator will be reset properly even if we change its events handlers and step handlers
  344.         try (IntegratorResetter resetter = new IntegratorResetter(integrator)) {

  345.             // prepare handling of STM and Jacobian matrices
  346.             setUpStmAndJacobianGenerators();

  347.             // Initialize additional states
  348.             initializeAdditionalStates(tEnd);

  349.             if (!tStart.equals(getInitialState().getDate())) {
  350.                 // if propagation start date is not initial date,
  351.                 // propagate from initial to start date without event detection
  352.                 try (IntegratorResetter startResetter = new IntegratorResetter(integrator)) {
  353.                     integrateDynamics(tStart);
  354.                 }
  355.             }

  356.             // set up events added by user
  357.             setUpUserEventDetectors();

  358.             // set up step handlers
  359.             for (final OrekitStepHandler handler : getMultiplexer().getHandlers()) {
  360.                 integrator.addStepHandler(new AdaptedStepHandler(handler));
  361.             }
  362.             for (final StoringStepHandler generator : ephemerisGenerators) {
  363.                 generator.setEndDate(tEnd);
  364.                 integrator.addStepHandler(generator);
  365.             }

  366.             // propagate from start date to end date with event detection
  367.             final SpacecraftState finalState = integrateDynamics(tEnd);

  368.             return finalState;

  369.         }

  370.     }

  371.     /** Set up State Transition Matrix and Jacobian matrix handling.
  372.      * @since 11.1
  373.      */
  374.     protected void setUpStmAndJacobianGenerators() {
  375.         // nothing to do by default
  376.     }

  377.     /** Propagation with or without event detection.
  378.      * @param tEnd target date to which orbit should be propagated
  379.      * @return state at end of propagation
  380.      */
  381.     private SpacecraftState integrateDynamics(final AbsoluteDate tEnd) {
  382.         try {

  383.             initializePropagation();

  384.             if (getInitialState().getDate().equals(tEnd)) {
  385.                 // don't extrapolate
  386.                 return getInitialState();
  387.             }

  388.             // space dynamics view
  389.             stateMapper = createMapper(getInitialState().getDate(), stateMapper.getMu(),
  390.                                        stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
  391.                                        stateMapper.getAttitudeProvider(), getInitialState().getFrame());


  392.             if (Double.isNaN(getMu())) {
  393.                 setMu(getInitialState().getMu());
  394.             }

  395.             if (getInitialState().getMass() <= 0.0) {
  396.                 throw new OrekitException(OrekitMessages.NOT_POSITIVE_SPACECRAFT_MASS,
  397.                                           getInitialState().getMass());
  398.             }

  399.             // convert space flight dynamics API to math API
  400.             final SpacecraftState initialIntegrationState = getInitialIntegrationState();
  401.             final ODEState mathInitialState = createInitialState(initialIntegrationState);
  402.             final ExpandableODE mathODE = createODE(integrator);

  403.             // mathematical integration
  404.             final ODEStateAndDerivative mathFinalState;
  405.             beforeIntegration(initialIntegrationState, tEnd);
  406.             mathFinalState = integrator.integrate(mathODE, mathInitialState,
  407.                                                   tEnd.durationFrom(getInitialState().getDate()));
  408.             afterIntegration();

  409.             // get final state
  410.             SpacecraftState finalState =
  411.                             stateMapper.mapArrayToState(stateMapper.mapDoubleToDate(mathFinalState.getTime(),
  412.                                                                                     tEnd),
  413.                                                         mathFinalState.getPrimaryState(),
  414.                                                         mathFinalState.getPrimaryDerivative(),
  415.                                                         propagationType);

  416.             if (!additionalDerivativesProviders.isEmpty()) {
  417.                 final double[] secondary            = mathFinalState.getSecondaryState(1);
  418.                 final double[] secondaryDerivatives = mathFinalState.getSecondaryDerivative(1);
  419.                 for (AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  420.                     final String   name        = provider.getName();
  421.                     final int      offset      = secondaryOffsets.get(name);
  422.                     final int      dimension   = provider.getDimension();
  423.                     finalState = finalState.
  424.                                  addAdditionalState(name, Arrays.copyOfRange(secondary, offset, offset + dimension)).
  425.                                  addAdditionalStateDerivative(name, Arrays.copyOfRange(secondaryDerivatives, offset, offset + dimension));
  426.                 }
  427.             }
  428.             finalState = updateAdditionalStates(finalState);

  429.             if (resetAtEnd) {
  430.                 resetInitialState(finalState);
  431.                 setStartDate(finalState.getDate());
  432.             }

  433.             return finalState;

  434.         } catch (MathRuntimeException mre) {
  435.             throw OrekitException.unwrap(mre);
  436.         }
  437.     }

  438.     /** Get the initial state for integration.
  439.      * @return initial state for integration
  440.      */
  441.     protected SpacecraftState getInitialIntegrationState() {
  442.         return getInitialState();
  443.     }

  444.     /** Create an initial state.
  445.      * @param initialState initial state in flight dynamics world
  446.      * @return initial state in mathematics world
  447.      */
  448.     private ODEState createInitialState(final SpacecraftState initialState) {

  449.         // retrieve initial state
  450.         final double[] primary = new double[getBasicDimension()];
  451.         stateMapper.mapStateToArray(initialState, primary, null);

  452.         if (secondaryOffsets.isEmpty()) {
  453.             // compute dimension of the secondary state
  454.             int offset = 0;
  455.             for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  456.                 secondaryOffsets.put(provider.getName(), offset);
  457.                 offset += provider.getDimension();
  458.             }
  459.             secondaryOffsets.put(SECONDARY_DIMENSION, offset);
  460.         }

  461.         return new ODEState(0.0, primary, secondary(initialState));

  462.     }

  463.     /** Create secondary state.
  464.      * @param state spacecraft state
  465.      * @return secondary state
  466.      * @since 11.1
  467.      */
  468.     private double[][] secondary(final SpacecraftState state) {

  469.         if (secondaryOffsets.isEmpty()) {
  470.             return null;
  471.         }

  472.         final double[][] secondary = new double[1][secondaryOffsets.get(SECONDARY_DIMENSION)];
  473.         for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  474.             final String   name       = provider.getName();
  475.             final int      offset     = secondaryOffsets.get(name);
  476.             final double[] additional = state.getAdditionalState(name);
  477.             System.arraycopy(additional, 0, secondary[0], offset, additional.length);
  478.         }

  479.         return secondary;

  480.     }

  481.     /** Create secondary state derivative.
  482.      * @param state spacecraft state
  483.      * @return secondary state derivative
  484.      * @since 11.1
  485.      */
  486.     private double[][] secondaryDerivative(final SpacecraftState state) {

  487.         if (secondaryOffsets.isEmpty()) {
  488.             return null;
  489.         }

  490.         final double[][] secondaryDerivative = new double[1][secondaryOffsets.get(SECONDARY_DIMENSION)];
  491.         for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  492.             final String   name       = provider.getName();
  493.             final int      offset     = secondaryOffsets.get(name);
  494.             final double[] additionalDerivative = state.getAdditionalStateDerivative(name);
  495.             System.arraycopy(additionalDerivative, 0, secondaryDerivative[0], offset, additionalDerivative.length);
  496.         }

  497.         return secondaryDerivative;

  498.     }

  499.     /** Create an ODE with all equations.
  500.      * @param integ numerical integrator to use for propagation.
  501.      * @return a new ode
  502.      */
  503.     private ExpandableODE createODE(final ODEIntegrator integ) {

  504.         final ExpandableODE ode =
  505.                 new ExpandableODE(new ConvertedMainStateEquations(getMainStateEquations(integ)));

  506.         // secondary part of the ODE
  507.         if (!additionalDerivativesProviders.isEmpty()) {
  508.             ode.addSecondaryEquations(new ConvertedSecondaryStateEquations());
  509.         }

  510.         return ode;

  511.     }

  512.     /** Method called just before integration.
  513.      * <p>
  514.      * The default implementation does nothing, it may be specialized in subclasses.
  515.      * </p>
  516.      * @param initialState initial state
  517.      * @param tEnd target date at which state should be propagated
  518.      */
  519.     protected void beforeIntegration(final SpacecraftState initialState,
  520.                                      final AbsoluteDate tEnd) {
  521.         // do nothing by default
  522.     }

  523.     /** Method called just after integration.
  524.      * <p>
  525.      * The default implementation does nothing, it may be specialized in subclasses.
  526.      * </p>
  527.      */
  528.     protected void afterIntegration() {
  529.         // do nothing by default
  530.     }

  531.     /** Get state vector dimension without additional parameters.
  532.      * @return state vector dimension without additional parameters.
  533.      */
  534.     public int getBasicDimension() {
  535.         return 7;
  536.     }

  537.     /** Get the integrator used by the propagator.
  538.      * @return the integrator.
  539.      */
  540.     protected ODEIntegrator getIntegrator() {
  541.         return integrator;
  542.     }

  543.     /** Convert a state from mathematical world to space flight dynamics world.
  544.      * @param os mathematical state
  545.      * @return space flight dynamics state
  546.      */
  547.     private SpacecraftState convert(final ODEStateAndDerivative os) {

  548.         SpacecraftState s = stateMapper.mapArrayToState(os.getTime(),
  549.                                                         os.getPrimaryState(),
  550.                                                         os.getPrimaryDerivative(),
  551.                                                         propagationType);
  552.         if (os.getNumberOfSecondaryStates() > 0) {
  553.             final double[] secondary           = os.getSecondaryState(1);
  554.             final double[] secondaryDerivative = os.getSecondaryDerivative(1);
  555.             for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  556.                 final String name      = provider.getName();
  557.                 final int    offset    = secondaryOffsets.get(name);
  558.                 final int    dimension = provider.getDimension();
  559.                 s = s.addAdditionalState(name, Arrays.copyOfRange(secondary, offset, offset + dimension));
  560.                 s = s.addAdditionalStateDerivative(name, Arrays.copyOfRange(secondaryDerivative, offset, offset + dimension));
  561.             }
  562.         }
  563.         s = updateAdditionalStates(s);

  564.         return s;

  565.     }

  566.     /** Convert a state from space flight dynamics world to mathematical world.
  567.      * @param state space flight dynamics state
  568.      * @return mathematical state
  569.      */
  570.     private ODEStateAndDerivative convert(final SpacecraftState state) {

  571.         // retrieve initial state
  572.         final double[] primary    = new double[getBasicDimension()];
  573.         final double[] primaryDot = new double[getBasicDimension()];
  574.         stateMapper.mapStateToArray(state, primary, primaryDot);

  575.         // secondary part of the ODE
  576.         final double[][] secondary           = secondary(state);
  577.         final double[][] secondaryDerivative = secondaryDerivative(state);

  578.         return new ODEStateAndDerivative(stateMapper.mapDateToDouble(state.getDate()),
  579.                                          primary, primaryDot,
  580.                                          secondary, secondaryDerivative);

  581.     }

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

  584.         /**
  585.          * Initialize the equations at the start of propagation. This method will be
  586.          * called before any calls to {@link #computeDerivatives(SpacecraftState)}.
  587.          *
  588.          * <p> The default implementation of this method does nothing.
  589.          *
  590.          * @param initialState initial state information at the start of propagation.
  591.          * @param target       date of propagation. Not equal to {@code
  592.          *                     initialState.getDate()}.
  593.          */
  594.         default void init(final SpacecraftState initialState, final AbsoluteDate target) {
  595.         }

  596.         /** Compute differential equations for main state.
  597.          * @param state current state
  598.          * @return derivatives of main state
  599.          */
  600.         double[] computeDerivatives(SpacecraftState state);

  601.     }

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

  604.         /** Main state equations. */
  605.         private final MainStateEquations main;

  606.         /** Simple constructor.
  607.          * @param main main state equations
  608.          */
  609.         ConvertedMainStateEquations(final MainStateEquations main) {
  610.             this.main = main;
  611.             calls = 0;
  612.         }

  613.         /** {@inheritDoc} */
  614.         public int getDimension() {
  615.             return getBasicDimension();
  616.         }

  617.         @Override
  618.         public void init(final double t0, final double[] y0, final double finalTime) {
  619.             // update space dynamics view
  620.             SpacecraftState initialState = stateMapper.mapArrayToState(t0, y0, null, PropagationType.MEAN);
  621.             initialState = updateAdditionalStates(initialState);
  622.             final AbsoluteDate target = stateMapper.mapDoubleToDate(finalTime);
  623.             main.init(initialState, target);
  624.         }

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

  627.             // increment calls counter
  628.             ++calls;

  629.             // update space dynamics view
  630.             SpacecraftState currentState = stateMapper.mapArrayToState(t, y, null, PropagationType.MEAN);

  631.             currentState = updateAdditionalStates(currentState);
  632.             // compute main state differentials
  633.             return main.computeDerivatives(currentState);

  634.         }

  635.     }

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

  638.         /** Dimension of the combined additional states. */
  639.         private final int combinedDimension;

  640.         /** Simple constructor.
  641.           */
  642.         ConvertedSecondaryStateEquations() {
  643.             this.combinedDimension = secondaryOffsets.get(SECONDARY_DIMENSION);
  644.         }

  645.         /** {@inheritDoc} */
  646.         @Override
  647.         public int getDimension() {
  648.             return combinedDimension;
  649.         }

  650.         /** {@inheritDoc} */
  651.         @Override
  652.         public void init(final double t0, final double[] primary0,
  653.                          final double[] secondary0, final double finalTime) {
  654.             // update space dynamics view
  655.             final SpacecraftState initialState = convert(t0, primary0, null, secondary0);

  656.             final AbsoluteDate target = stateMapper.mapDoubleToDate(finalTime);
  657.             for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  658.                 provider.init(initialState, target);
  659.             }

  660.         }

  661.         /** {@inheritDoc} */
  662.         @Override
  663.         public double[] computeDerivatives(final double t, final double[] primary,
  664.                                            final double[] primaryDot, final double[] secondary) {

  665.             // update space dynamics view
  666.             // the integrable generators generate method will be called here,
  667.             // according to the generators yield order
  668.             SpacecraftState updated = convert(t, primary, primaryDot, secondary);

  669.             // set up queue for equations
  670.             final Queue<AdditionalDerivativesProvider> pending = new LinkedList<>(additionalDerivativesProviders);

  671.             // gather the derivatives from all additional equations, taking care of dependencies
  672.             final double[] secondaryDot = new double[combinedDimension];
  673.             int yieldCount = 0;
  674.             while (!pending.isEmpty()) {
  675.                 final AdditionalDerivativesProvider provider = pending.remove();
  676.                 if (provider.yields(updated)) {
  677.                     // this provider has to wait for another one,
  678.                     // we put it again in the pending queue
  679.                     pending.add(provider);
  680.                     if (++yieldCount >= pending.size()) {
  681.                         // all pending providers yielded!, they probably need data not yet initialized
  682.                         // we let the propagation proceed, if these data are really needed right now
  683.                         // an appropriate exception will be triggered when caller tries to access them
  684.                         break;
  685.                     }
  686.                 } else {
  687.                     // we can use these equations right now
  688.                     final String              name           = provider.getName();
  689.                     final int                 offset         = secondaryOffsets.get(name);
  690.                     final int                 dimension      = provider.getDimension();
  691.                     final CombinedDerivatives derivatives    = provider.combinedDerivatives(updated);
  692.                     final double[]            additionalPart = derivatives.getAdditionalDerivatives();
  693.                     final double[]            mainPart       = derivatives.getMainStateDerivativesIncrements();
  694.                     System.arraycopy(additionalPart, 0, secondaryDot, offset, dimension);
  695.                     updated = updated.addAdditionalStateDerivative(name, additionalPart);
  696.                     if (mainPart != null) {
  697.                         // this equation does change the main state derivatives
  698.                         for (int i = 0; i < mainPart.length; ++i) {
  699.                             primaryDot[i] += mainPart[i];
  700.                         }
  701.                     }
  702.                     yieldCount = 0;
  703.                 }
  704.             }

  705.             return secondaryDot;

  706.         }

  707.         /** Convert mathematical view to space view.
  708.          * @param t current value of the independent <I>time</I> variable
  709.          * @param primary array containing the current value of the primary state vector
  710.          * @param primaryDot array containing the derivative of the primary state vector
  711.          * @param secondary array containing the current value of the secondary state vector
  712.          * @return space view of the state
  713.          */
  714.         private SpacecraftState convert(final double t, final double[] primary,
  715.                                         final double[] primaryDot, final double[] secondary) {

  716.             SpacecraftState initialState = stateMapper.mapArrayToState(t, primary, primaryDot, PropagationType.MEAN);

  717.             for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  718.                 final String name      = provider.getName();
  719.                 final int    offset    = secondaryOffsets.get(name);
  720.                 final int    dimension = provider.getDimension();
  721.                 initialState = initialState.addAdditionalState(name, Arrays.copyOfRange(secondary, offset, offset + dimension));
  722.             }

  723.             return updateAdditionalStates(initialState);

  724.         }

  725.     }

  726.     /** Adapt an {@link org.orekit.propagation.events.EventDetector}
  727.      * to Hipparchus {@link org.hipparchus.ode.events.ODEEventDetector} interface.
  728.      * @author Fabien Maussion
  729.      */
  730.     private class AdaptedEventDetector implements ODEEventDetector {

  731.         /** Underlying event detector. */
  732.         private final EventDetector detector;

  733.         /** Underlying event handler.
  734.          * @since 12.0
  735.          */
  736.         private final EventHandler handler;

  737.         /** Time of the previous call to g. */
  738.         private double lastT;

  739.         /** Value from the previous call to g. */
  740.         private double lastG;

  741.         /** Build a wrapped event detector.
  742.          * @param detector event detector to wrap
  743.         */
  744.         AdaptedEventDetector(final EventDetector detector) {
  745.             this.detector = detector;
  746.             this.handler  = detector.getHandler();
  747.             this.lastT    = Double.NaN;
  748.             this.lastG    = Double.NaN;
  749.         }

  750.         /** {@inheritDoc} */
  751.         @Override
  752.         public AdaptableInterval getMaxCheckInterval() {
  753.             return s -> detector.getMaxCheckInterval().currentInterval(convert(s));
  754.         }

  755.         /** {@inheritDoc} */
  756.         @Override
  757.         public int getMaxIterationCount() {
  758.             return detector.getMaxIterationCount();
  759.         }

  760.         /** {@inheritDoc} */
  761.         @Override
  762.         public BracketedUnivariateSolver<UnivariateFunction> getSolver() {
  763.             return new BracketingNthOrderBrentSolver(0, detector.getThreshold(), 0, 5);
  764.         }

  765.         /** {@inheritDoc} */
  766.         public void init(final ODEStateAndDerivative s0, final double t) {
  767.             detector.init(convert(s0), stateMapper.mapDoubleToDate(t));
  768.             this.lastT = Double.NaN;
  769.             this.lastG = Double.NaN;
  770.         }

  771.         /** {@inheritDoc} */
  772.         public double g(final ODEStateAndDerivative s) {
  773.             if (!Precision.equals(lastT, s.getTime(), 0)) {
  774.                 lastT = s.getTime();
  775.                 lastG = detector.g(convert(s));
  776.             }
  777.             return lastG;
  778.         }

  779.         /** {@inheritDoc} */
  780.         public ODEEventHandler getHandler() {

  781.             return new ODEEventHandler() {

  782.                 /** {@inheritDoc} */
  783.                 public Action eventOccurred(final ODEStateAndDerivative s, final ODEEventDetector d, final boolean increasing) {
  784.                     return handler.eventOccurred(convert(s), detector, increasing);
  785.                 }

  786.                 /** {@inheritDoc} */
  787.                 public ODEState resetState(final ODEEventDetector d, final ODEStateAndDerivative s) {

  788.                     final SpacecraftState oldState = convert(s);
  789.                     final SpacecraftState newState = handler.resetState(detector, oldState);
  790.                     stateChanged(newState);

  791.                     // main part
  792.                     final double[] primary    = new double[s.getPrimaryStateDimension()];
  793.                     stateMapper.mapStateToArray(newState, primary, null);

  794.                     // secondary part
  795.                     final double[][] secondary = new double[1][secondaryOffsets.get(SECONDARY_DIMENSION)];
  796.                     for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
  797.                         final String name      = provider.getName();
  798.                         final int    offset    = secondaryOffsets.get(name);
  799.                         final int    dimension = provider.getDimension();
  800.                         System.arraycopy(newState.getAdditionalState(name), 0, secondary[0], offset, dimension);
  801.                     }

  802.                     return new ODEState(newState.getDate().durationFrom(getStartDate()),
  803.                                         primary, secondary);

  804.                 }

  805.             };
  806.         }

  807.     }

  808.     /** Adapt an {@link org.orekit.propagation.sampling.OrekitStepHandler}
  809.      * to Hipparchus {@link ODEStepHandler} interface.
  810.      * @author Luc Maisonobe
  811.      */
  812.     private class AdaptedStepHandler implements ODEStepHandler {

  813.         /** Underlying handler. */
  814.         private final OrekitStepHandler handler;

  815.         /** Build an instance.
  816.          * @param handler underlying handler to wrap
  817.          */
  818.         AdaptedStepHandler(final OrekitStepHandler handler) {
  819.             this.handler = handler;
  820.         }

  821.         /** {@inheritDoc} */
  822.         public void init(final ODEStateAndDerivative s0, final double t) {
  823.             handler.init(convert(s0), stateMapper.mapDoubleToDate(t));
  824.         }

  825.         /** {@inheritDoc} */
  826.         @Override
  827.         public void handleStep(final ODEStateInterpolator interpolator) {
  828.             handler.handleStep(new AdaptedStepInterpolator(interpolator));
  829.         }

  830.         /** {@inheritDoc} */
  831.         @Override
  832.         public void finish(final ODEStateAndDerivative finalState) {
  833.             handler.finish(convert(finalState));
  834.         }

  835.     }

  836.     /** Adapt an Hipparchus {@link ODEStateInterpolator}
  837.      * to an orekit {@link OrekitStepInterpolator} interface.
  838.      * @author Luc Maisonobe
  839.      */
  840.     private class AdaptedStepInterpolator implements OrekitStepInterpolator {

  841.         /** Underlying raw rawInterpolator. */
  842.         private final ODEStateInterpolator mathInterpolator;

  843.         /** Simple constructor.
  844.          * @param mathInterpolator underlying raw interpolator
  845.          */
  846.         AdaptedStepInterpolator(final ODEStateInterpolator mathInterpolator) {
  847.             this.mathInterpolator = mathInterpolator;
  848.         }

  849.         /** {@inheritDoc}} */
  850.         @Override
  851.         public SpacecraftState getPreviousState() {
  852.             return convert(mathInterpolator.getPreviousState());
  853.         }

  854.         /** {@inheritDoc}} */
  855.         @Override
  856.         public boolean isPreviousStateInterpolated() {
  857.             return mathInterpolator.isPreviousStateInterpolated();
  858.         }

  859.         /** {@inheritDoc}} */
  860.         @Override
  861.         public SpacecraftState getCurrentState() {
  862.             return convert(mathInterpolator.getCurrentState());
  863.         }

  864.         /** {@inheritDoc}} */
  865.         @Override
  866.         public boolean isCurrentStateInterpolated() {
  867.             return mathInterpolator.isCurrentStateInterpolated();
  868.         }

  869.         /** {@inheritDoc}} */
  870.         @Override
  871.         public SpacecraftState getInterpolatedState(final AbsoluteDate date) {
  872.             return convert(mathInterpolator.getInterpolatedState(date.durationFrom(stateMapper.getReferenceDate())));
  873.         }

  874.         /** {@inheritDoc}} */
  875.         @Override
  876.         public boolean isForward() {
  877.             return mathInterpolator.isForward();
  878.         }

  879.         /** {@inheritDoc}} */
  880.         @Override
  881.         public AdaptedStepInterpolator restrictStep(final SpacecraftState newPreviousState,
  882.                                                     final SpacecraftState newCurrentState) {
  883.             try {
  884.                 final AbstractODEStateInterpolator aosi = (AbstractODEStateInterpolator) mathInterpolator;
  885.                 return new AdaptedStepInterpolator(aosi.restrictStep(convert(newPreviousState),
  886.                                                                      convert(newCurrentState)));
  887.             } catch (ClassCastException cce) {
  888.                 // this should never happen
  889.                 throw new OrekitInternalError(cce);
  890.             }
  891.         }

  892.     }

  893.     /** Specialized step handler storing interpolators for ephemeris generation.
  894.      * @since 11.0
  895.      */
  896.     private class StoringStepHandler implements ODEStepHandler, EphemerisGenerator {

  897.         /** Underlying raw mathematical model. */
  898.         private DenseOutputModel model;

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

  901.         /** Generated ephemeris. */
  902.         private BoundedPropagator ephemeris;

  903.         /** Last interpolator handled by the object.*/
  904.         private  ODEStateInterpolator lastInterpolator;

  905.         /** Set the end date.
  906.          * @param endDate end date
  907.          */
  908.         public void setEndDate(final AbsoluteDate endDate) {
  909.             this.endDate = endDate;
  910.         }

  911.         /** {@inheritDoc} */
  912.         @Override
  913.         public void init(final ODEStateAndDerivative s0, final double t) {

  914.             this.model = new DenseOutputModel();
  915.             model.init(s0, t);

  916.             // ephemeris will be generated when last step is processed
  917.             this.ephemeris = null;

  918.             this.lastInterpolator = null;

  919.         }

  920.         /** {@inheritDoc} */
  921.         @Override
  922.         public BoundedPropagator getGeneratedEphemeris() {
  923.             // Each time we try to get the ephemeris, rebuild it using the last data.
  924.             buildEphemeris();
  925.             return ephemeris;
  926.         }

  927.         /** {@inheritDoc} */
  928.         @Override
  929.         public void handleStep(final ODEStateInterpolator interpolator) {
  930.             model.handleStep(interpolator);
  931.             lastInterpolator = interpolator;
  932.         }

  933.         /** {@inheritDoc} */
  934.         @Override
  935.         public void finish(final ODEStateAndDerivative finalState) {
  936.             buildEphemeris();
  937.         }

  938.         /** Method used to produce ephemeris at a given time.
  939.          * Can be used at multiple times, updating the ephemeris to
  940.          * its last state.
  941.          */
  942.         private void buildEphemeris() {
  943.             // buildEphemeris was built in order to allow access to what was previously the finish method.
  944.             // This now allows to call it through getGeneratedEphemeris, therefore through an external call,
  945.             // which was not previously the case.

  946.             // Update the model's finalTime with the last interpolator.
  947.             model.finish(lastInterpolator.getCurrentState());

  948.             // set up the boundary dates
  949.             final double tI = model.getInitialTime();
  950.             final double tF = model.getFinalTime();
  951.             // tI is almost? always zero
  952.             final AbsoluteDate startDate =
  953.                             stateMapper.mapDoubleToDate(tI);
  954.             final AbsoluteDate finalDate =
  955.                             stateMapper.mapDoubleToDate(tF, this.endDate);
  956.             final AbsoluteDate minDate;
  957.             final AbsoluteDate maxDate;
  958.             if (tF < tI) {
  959.                 minDate = finalDate;
  960.                 maxDate = startDate;
  961.             } else {
  962.                 minDate = startDate;
  963.                 maxDate = finalDate;
  964.             }

  965.             // get the initial additional states that are not managed
  966.             final DoubleArrayDictionary unmanaged = new DoubleArrayDictionary();
  967.             for (final DoubleArrayDictionary.Entry initial : getInitialState().getAdditionalStatesValues().getData()) {
  968.                 if (!isAdditionalStateManaged(initial.getKey())) {
  969.                     // this additional state was in the initial state, but is unknown to the propagator
  970.                     // we simply copy its initial value as is
  971.                     unmanaged.put(initial.getKey(), initial.getValue());
  972.                 }
  973.             }

  974.             // get the names of additional states managed by differential equations
  975.             final String[] names      = new String[additionalDerivativesProviders.size()];
  976.             final int[]    dimensions = new int[additionalDerivativesProviders.size()];
  977.             for (int i = 0; i < names.length; ++i) {
  978.                 names[i] = additionalDerivativesProviders.get(i).getName();
  979.                 dimensions[i] = additionalDerivativesProviders.get(i).getDimension();
  980.             }

  981.             // create the ephemeris
  982.             ephemeris = new IntegratedEphemeris(startDate, minDate, maxDate,
  983.                                                 stateMapper, propagationType, model,
  984.                                                 unmanaged, getAdditionalStateProviders(),
  985.                                                 names, dimensions);

  986.         }

  987.     }

  988.     /** Wrapper for resetting an integrator handlers.
  989.      * <p>
  990.      * This class is intended to be used in a try-with-resource statement.
  991.      * If propagator-specific event handlers and step handlers are added to
  992.      * the integrator in the try block, they will be removed automatically
  993.      * when leaving the block, so the integrator only keeps its own handlers
  994.      * between calls to {@link AbstractIntegratedPropagator#propagate(AbsoluteDate, AbsoluteDate).
  995.      * </p>
  996.      * @since 11.0
  997.      */
  998.     private static class IntegratorResetter implements AutoCloseable {

  999.         /** Wrapped integrator. */
  1000.         private final ODEIntegrator integrator;

  1001.         /** Initial event detectors list. */
  1002.         private final List<ODEEventDetector> detectors;

  1003.         /** Initial step handlers list. */
  1004.         private final List<ODEStepHandler> stepHandlers;

  1005.         /** Simple constructor.
  1006.          * @param integrator wrapped integrator
  1007.          */
  1008.         IntegratorResetter(final ODEIntegrator integrator) {
  1009.             this.integrator   = integrator;
  1010.             this.detectors    = new ArrayList<>(integrator.getEventDetectors());
  1011.             this.stepHandlers = new ArrayList<>(integrator.getStepHandlers());
  1012.         }

  1013.         /** {@inheritDoc}
  1014.          * <p>
  1015.          * Reset event handlers and step handlers back to the initial list
  1016.          * </p>
  1017.          */
  1018.         @Override
  1019.         public void close() {

  1020.             // reset event handlers
  1021.             integrator.clearEventDetectors();
  1022.             detectors.forEach(integrator::addEventDetector);

  1023.             // reset step handlers
  1024.             integrator.clearStepHandlers();
  1025.             stepHandlers.forEach(integrator::addStepHandler);

  1026.         }

  1027.     }

  1028. }