1   /* Copyright 2002-2026 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  
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.Collection;
22  import java.util.Collections;
23  import java.util.HashMap;
24  import java.util.LinkedList;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.Queue;
28  
29  import org.hipparchus.analysis.UnivariateFunction;
30  import org.hipparchus.analysis.solvers.BracketedUnivariateSolver;
31  import org.hipparchus.analysis.solvers.BracketingNthOrderBrentSolver;
32  import org.hipparchus.exception.MathRuntimeException;
33  import org.hipparchus.ode.DenseOutputModel;
34  import org.hipparchus.ode.ExpandableODE;
35  import org.hipparchus.ode.ODEIntegrator;
36  import org.hipparchus.ode.ODEState;
37  import org.hipparchus.ode.ODEStateAndDerivative;
38  import org.hipparchus.ode.OrdinaryDifferentialEquation;
39  import org.hipparchus.ode.SecondaryODE;
40  import org.hipparchus.ode.events.Action;
41  import org.hipparchus.ode.events.AdaptableInterval;
42  import org.hipparchus.ode.events.ODEEventDetector;
43  import org.hipparchus.ode.events.ODEEventHandler;
44  import org.hipparchus.ode.sampling.AbstractODEStateInterpolator;
45  import org.hipparchus.ode.sampling.ODEStateInterpolator;
46  import org.hipparchus.ode.sampling.ODEStepHandler;
47  import org.hipparchus.util.Precision;
48  import org.orekit.attitudes.AttitudeProvider;
49  import org.orekit.attitudes.AttitudeProviderModifier;
50  import org.orekit.errors.OrekitException;
51  import org.orekit.errors.OrekitInternalError;
52  import org.orekit.errors.OrekitMessages;
53  import org.orekit.frames.Frame;
54  import org.orekit.orbits.OrbitType;
55  import org.orekit.orbits.PositionAngleType;
56  import org.orekit.propagation.AbstractPropagator;
57  import org.orekit.propagation.BoundedPropagator;
58  import org.orekit.propagation.EphemerisGenerator;
59  import org.orekit.propagation.PropagationType;
60  import org.orekit.propagation.SpacecraftState;
61  import org.orekit.propagation.events.EventDetector;
62  import org.orekit.propagation.events.handlers.EventHandler;
63  import org.orekit.propagation.sampling.OrekitStepHandler;
64  import org.orekit.propagation.sampling.OrekitStepInterpolator;
65  import org.orekit.time.AbsoluteDate;
66  import org.orekit.utils.DataDictionary;
67  import org.orekit.utils.DoubleArrayDictionary;
68  
69  
70  /** Common handling of {@link org.orekit.propagation.Propagator Propagator}
71   *  methods for both numerical and semi-analytical propagators.
72   *  @author Luc Maisonobe
73   */
74  public abstract class AbstractIntegratedPropagator extends AbstractPropagator {
75  
76      /** Internal name used for complete secondary state dimension.
77       * @since 11.1
78       */
79      private static final String SECONDARY_DIMENSION = "Orekit-secondary-dimension";
80  
81      /** Event detectors not related to force models. */
82      private final List<EventDetector> detectors;
83  
84      /** Step handlers dedicated to ephemeris generation. */
85      private final List<StoringStepHandler> ephemerisGenerators;
86  
87      /** Integrator selected by the user for the orbital extrapolation process. */
88      private final ODEIntegrator integrator;
89  
90      /** Offsets of secondary states managed by {@link AdditionalDerivativesProvider}.
91       * @since 11.1
92       */
93      private final Map<String, Integer> secondaryOffsets;
94  
95      /** Additional derivatives providers.
96       * @since 11.1
97       */
98      private final List<AdditionalDerivativesProvider> additionalDerivativesProviders;
99  
100     /** Map of secondary equation offset in main
101     /** Counter for differential equations calls. */
102     private int calls;
103 
104     /** Mapper between raw double components and space flight dynamics objects. */
105     private StateMapper stateMapper;
106 
107     /**
108      * Attitude provider when evaluating derivatives. Can be a frozen one for performance.
109      * @since 12.1
110      */
111     private AttitudeProvider attitudeProviderForDerivatives;
112 
113     /**
114      * Attitude provider with frozen rates, used when possible for performance.
115      * @since 13.1
116      */
117     private AttitudeProvider frozenAttitudeProvider;
118 
119     /** Flag for resetting the state at end of propagation. */
120     private boolean resetAtEnd;
121 
122     /** Type of orbit to output (mean or osculating) <br/>
123      * <p>
124      * This is used only in the case of semi-analytical propagators where there is a clear separation between
125      * mean and short periodic elements. It is ignored by the Numerical propagator.
126      * </p>
127      */
128     private final PropagationType propagationType;
129 
130     /** Build a new instance.
131      * @param integrator numerical integrator to use for propagation.
132      * @param propagationType type of orbit to output (mean or osculating).
133      */
134     protected AbstractIntegratedPropagator(final ODEIntegrator integrator, final PropagationType propagationType) {
135         detectors                      = new ArrayList<>();
136         ephemerisGenerators            = new ArrayList<>();
137         additionalDerivativesProviders = new ArrayList<>();
138         this.secondaryOffsets          = new HashMap<>();
139         this.integrator                = integrator;
140         this.propagationType           = propagationType;
141         this.resetAtEnd                = true;
142     }
143 
144     /** Allow/disallow resetting the initial state at end of propagation.
145      * <p>
146      * By default, at the end of the propagation, the propagator resets the initial state
147      * to the final state, thus allowing a new propagation to be started from there without
148      * recomputing the part already performed. Calling this method with {@code resetAtEnd} set
149      * to false changes prevents such reset.
150      * </p>
151      * @param resetAtEnd if true, at end of each propagation, the {@link
152      * #getInitialState() initial state} will be reset to the final state of
153      * the propagation, otherwise the initial state will be preserved
154      * @since 9.0
155      */
156     public void setResetAtEnd(final boolean resetAtEnd) {
157         this.resetAtEnd = resetAtEnd;
158     }
159 
160     /** Getter for the resetting flag regarding initial state.
161      * @return resetting flag
162      * @since 12.0
163      */
164     public boolean getResetAtEnd() {
165         return this.resetAtEnd;
166     }
167 
168     /**
169      * Getter for the frozen attitude provider, used for performance when possible.
170      * @return frozen attitude provider
171      * @since 13.1
172      */
173     protected AttitudeProvider getFrozenAttitudeProvider() {
174         return frozenAttitudeProvider;
175     }
176 
177     /**
178      * Method called when initializing the attitude provider used when evaluating derivatives.
179      * @return attitude provider for derivatives
180      */
181     protected AttitudeProvider initializeAttitudeProviderForDerivatives() {
182         return getAttitudeProvider();
183     }
184 
185     /** Initialize the mapper. */
186     protected void initMapper() {
187         stateMapper = createMapper(null, Double.NaN, null, null, null, null);
188     }
189 
190     /** Get the integrator's name.
191      * @return name of underlying integrator
192      * @since 12.0
193      */
194     public String getIntegratorName() {
195         return integrator.getName();
196     }
197 
198     /**  {@inheritDoc} */
199     @Override
200     public void setAttitudeProvider(final AttitudeProvider attitudeProvider) {
201         super.setAttitudeProvider(attitudeProvider);
202         frozenAttitudeProvider = AttitudeProviderModifier.getFrozenAttitudeProvider(attitudeProvider);
203         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
204                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
205                                    attitudeProvider, stateMapper.getFrame());
206     }
207 
208     /** Set propagation orbit type.
209      * @param orbitType orbit type to use for propagation, null for
210      * propagating using {@link org.orekit.utils.AbsolutePVCoordinates AbsolutePVCoordinates}
211      * rather than {@link org.orekit.orbits Orbit}
212      */
213     protected void setOrbitType(final OrbitType orbitType) {
214         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
215                                    orbitType, stateMapper.getPositionAngleType(),
216                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
217     }
218 
219     /** Get propagation parameter type.
220      * @return orbit type used for propagation, null for
221      * propagating using {@link org.orekit.utils.AbsolutePVCoordinates AbsolutePVCoordinates}
222      * rather than {@link org.orekit.orbits Orbit}
223      */
224     protected OrbitType getOrbitType() {
225         return stateMapper.getOrbitType();
226     }
227 
228     /** Get the propagation type.
229      * @return propagation type.
230      * @since 11.1
231      */
232     public PropagationType getPropagationType() {
233         return propagationType;
234     }
235 
236     /** Set position angle type.
237      * <p>
238      * The position parameter type is meaningful only if {@link
239      * #getOrbitType() propagation orbit type}
240      * support it. As an example, it is not meaningful for propagation
241      * in {@link OrbitType#CARTESIAN Cartesian} parameters.
242      * </p>
243      * @param positionAngleType angle type to use for propagation
244      */
245     protected void setPositionAngleType(final PositionAngleType positionAngleType) {
246         stateMapper = createMapper(stateMapper.getReferenceDate(), stateMapper.getMu(),
247                                    stateMapper.getOrbitType(), positionAngleType,
248                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
249     }
250 
251     /** Get propagation parameter type.
252      * @return angle type to use for propagation
253      */
254     protected PositionAngleType getPositionAngleType() {
255         return stateMapper.getPositionAngleType();
256     }
257 
258     /** Set the central attraction coefficient μ.
259      * @param mu central attraction coefficient (m³/s²)
260      */
261     public void setMu(final double mu) {
262         stateMapper = createMapper(stateMapper.getReferenceDate(), mu,
263                                    stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
264                                    stateMapper.getAttitudeProvider(), stateMapper.getFrame());
265     }
266 
267     /** Get the central attraction coefficient μ.
268      * @return mu central attraction coefficient (m³/s²)
269      * @see #setMu(double)
270      */
271     public double getMu() {
272         return stateMapper.getMu();
273     }
274 
275     /** Get the number of calls to the differential equations computation method.
276      * <p>The number of calls is reset each time the {@link #propagate(AbsoluteDate)}
277      * method is called.</p>
278      * @return number of calls to the differential equations computation method
279      */
280     public int getCalls() {
281         return calls;
282     }
283 
284     /** {@inheritDoc} */
285     @Override
286     public boolean isAdditionalDataManaged(final String name) {
287 
288         // first look at already integrated states
289         if (super.isAdditionalDataManaged(name)) {
290             return true;
291         }
292 
293         // then look at states we integrate ourselves
294         for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
295             if (provider.getName().equals(name)) {
296                 return true;
297             }
298         }
299 
300         return false;
301     }
302 
303     /** {@inheritDoc} */
304     @Override
305     public String[] getManagedAdditionalData() {
306         final String[] alreadyIntegrated = super.getManagedAdditionalData();
307         final String[] managed = new String[alreadyIntegrated.length + additionalDerivativesProviders.size()];
308         System.arraycopy(alreadyIntegrated, 0, managed, 0, alreadyIntegrated.length);
309         for (int i = 0; i < additionalDerivativesProviders.size(); ++i) {
310             managed[i + alreadyIntegrated.length] = additionalDerivativesProviders.get(i).getName();
311         }
312         return managed;
313     }
314 
315     /** Add a provider for user-specified state derivatives to be integrated along with the orbit propagation.
316      * @param provider provider for additional derivatives
317      * @see #addAdditionalDataProvider(org.orekit.propagation.AdditionalDataProvider)
318      * @since 11.1
319      */
320     public void addAdditionalDerivativesProvider(final AdditionalDerivativesProvider provider) {
321 
322         // check if the name is already used
323         if (this.isAdditionalDataManaged(provider.getName())) {
324             // these derivatives are already registered, complain
325             throw new OrekitException(OrekitMessages.ADDITIONAL_STATE_NAME_ALREADY_IN_USE,
326                                       provider.getName());
327         }
328 
329         // this is really a new set of derivatives, add it
330         additionalDerivativesProviders.add(provider);
331 
332         secondaryOffsets.clear();
333 
334     }
335 
336     /** Get an unmodifiable list of providers for additional derivatives.
337      * @return providers for the additional derivatives
338      * @since 11.1
339      */
340     public List<AdditionalDerivativesProvider> getAdditionalDerivativesProviders() {
341         return Collections.unmodifiableList(additionalDerivativesProviders);
342     }
343 
344     /**
345      * Remove additional derivatives provider.
346      * @param name provider's name.
347      * @since 13.1
348      */
349     protected void removeAdditionalDerivativesProvider(final String name) {
350         additionalDerivativesProviders.removeIf(provider -> provider.getName().equals(name));
351     }
352 
353     /** {@inheritDoc} */
354     public void addEventDetector(final EventDetector detector) {
355         detectors.add(detector);
356     }
357 
358     /** {@inheritDoc} */
359     public Collection<EventDetector> getEventDetectors() {
360         return Collections.unmodifiableCollection(detectors);
361     }
362 
363     /** {@inheritDoc} */
364     public void clearEventsDetectors() {
365         detectors.clear();
366     }
367 
368     /** Set up all user defined event detectors.
369      */
370     protected void setUpUserEventDetectors() {
371         for (final EventDetector detector : detectors) {
372             setUpEventDetector(integrator, detector);
373         }
374     }
375 
376     /** Wrap an Orekit event detector and register it to the integrator.
377      * @param integ integrator into which event detector should be registered
378      * @param detector event detector to wrap
379      */
380     protected void setUpEventDetector(final ODEIntegrator integ, final EventDetector detector) {
381         integ.addEventDetector(new AdaptedEventDetector(detector));
382     }
383 
384     /**
385      * Clear the ephemeris generators.
386      * @since 13.0
387      */
388     public void clearEphemerisGenerators() {
389         ephemerisGenerators.clear();
390     }
391 
392     /** {@inheritDoc} */
393     @Override
394     public EphemerisGenerator getEphemerisGenerator() {
395         final StoringStepHandler storingHandler = new StoringStepHandler();
396         ephemerisGenerators.add(storingHandler);
397         return storingHandler;
398     }
399 
400     /** Create a mapper between raw double components and spacecraft state.
401     /** Simple constructor.
402      * <p>
403      * The position parameter type is meaningful only if {@link
404      * #getOrbitType() propagation orbit type}
405      * support it. As an example, it is not meaningful for propagation
406      * in {@link OrbitType#CARTESIAN Cartesian} parameters.
407      * </p>
408      * @param referenceDate reference date
409      * @param mu central attraction coefficient (m³/s²)
410      * @param orbitType orbit type to use for mapping
411      * @param positionAngleType angle type to use for propagation
412      * @param attitudeProvider attitude provider
413      * @param frame inertial frame
414      * @return new mapper
415      */
416     protected abstract StateMapper createMapper(AbsoluteDate referenceDate, double mu,
417                                                 OrbitType orbitType, PositionAngleType positionAngleType,
418                                                 AttitudeProvider attitudeProvider, Frame frame);
419 
420     /** Get the differential equations to integrate (for main state only).
421      * @param integ numerical integrator to use for propagation.
422      * @return differential equations for main state
423      */
424     protected abstract MainStateEquations getMainStateEquations(ODEIntegrator integ);
425 
426     /** {@inheritDoc} */
427     @Override
428     public SpacecraftState propagate(final AbsoluteDate target) {
429         if (getStartDate() == null) {
430             if (getInitialState() == null) {
431                 throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
432             }
433             setStartDate(getInitialState().getDate());
434         }
435         return propagate(getStartDate(), target);
436     }
437 
438     /** {@inheritDoc} */
439     public SpacecraftState propagate(final AbsoluteDate tStart, final AbsoluteDate tEnd) {
440 
441         if (getInitialState() == null) {
442             throw new OrekitException(OrekitMessages.INITIAL_STATE_NOT_SPECIFIED_FOR_ORBIT_PROPAGATION);
443         }
444 
445         // make sure the integrator will be reset properly even if we change its events handlers and step handlers
446         try (IntegratorResetter resetter = new IntegratorResetter(integrator)) {
447 
448             // prepare handling of STM and Jacobian matrices
449             setUpStmAndJacobianGenerators();
450 
451             // Initialize additional data
452             initializeAdditionalData(tEnd);
453 
454             if (!tStart.equals(getInitialState().getDate())) {
455                 // if propagation start date is not initial date,
456                 // propagate from initial to start date without event detection
457                 try (IntegratorResetter startResetter = new IntegratorResetter(integrator)) {
458                     integrateDynamics(tStart, true);
459                 }
460             }
461 
462             // set up events added by user
463             setUpUserEventDetectors();
464 
465             // set up step handlers
466             for (final OrekitStepHandler handler : getMultiplexer().getHandlers()) {
467                 integrator.addStepHandler(new AdaptedStepHandler(handler));
468             }
469             for (final StoringStepHandler generator : ephemerisGenerators) {
470                 generator.setEndDate(tEnd);
471                 integrator.addStepHandler(generator);
472             }
473 
474             // propagate from start date to end date with event detection
475             final SpacecraftState finalState = integrateDynamics(tEnd, false);
476 
477             // Finalize event detectors
478             getEventDetectors().forEach(detector -> detector.finish(finalState));
479 
480             return finalState;
481         }
482 
483     }
484 
485     /** Reset initial state with a given propagation type.
486      *
487      * <p> By default this method returns the same as {@link #resetInitialState(SpacecraftState)}
488      * <p> Its purpose is mostly to be derived in DSSTPropagator
489      *
490      * @param state new initial state to consider
491      * @param stateType type of the new state (mean or osculating)
492      * @since 12.1.3
493      */
494     public void resetInitialState(final SpacecraftState state, final PropagationType stateType) {
495         // Default behavior, do not take propagation type into account
496         resetInitialState(state);
497     }
498 
499     /** Set up State Transition Matrix and Jacobian matrix handling.
500      * @since 11.1
501      */
502     protected void setUpStmAndJacobianGenerators() {
503         // nothing to do by default
504     }
505 
506     @Override
507     public void clearMatricesComputation() {
508         secondaryOffsets.clear();
509         super.clearMatricesComputation();
510     }
511 
512     /** Propagation with or without event detection.
513      * @param tEnd target date to which orbit should be propagated
514      * @param forceResetAtEnd flag to force resetting state and date after integration
515      * @return state at end of propagation
516      */
517     private SpacecraftState integrateDynamics(final AbsoluteDate tEnd, final boolean forceResetAtEnd) {
518         try {
519 
520             initializePropagation();
521 
522             if (getInitialState().getDate().equals(tEnd)) {
523                 // don't extrapolate
524                 return getInitialState();
525             }
526 
527             // space dynamics view
528             stateMapper = createMapper(getInitialState().getDate(), stateMapper.getMu(),
529                                        stateMapper.getOrbitType(), stateMapper.getPositionAngleType(),
530                                        stateMapper.getAttitudeProvider(), getInitialState().getFrame());
531             attitudeProviderForDerivatives = initializeAttitudeProviderForDerivatives();
532 
533             if (Double.isNaN(getMu())) {
534                 setMu(getInitialState().isOrbitDefined() ? getInitialState().getOrbit().getMu() : Double.NaN);
535             }
536 
537             if (getInitialState().getMass() <= 0.0) {
538                 throw new OrekitException(OrekitMessages.NOT_POSITIVE_SPACECRAFT_MASS,
539                                           getInitialState().getMass());
540             }
541 
542             // convertFromOrekit space flight dynamics API to math API
543             final SpacecraftState initialIntegrationState = getInitialIntegrationState();
544             final ODEState mathInitialState = createInitialState(initialIntegrationState);
545             final ExpandableODE mathODE = createODE(integrator);
546 
547             // mathematical integration
548             final ODEStateAndDerivative mathFinalState;
549             beforeIntegration(initialIntegrationState, tEnd);
550             mathFinalState = integrator.integrate(mathODE, mathInitialState,
551                                                   tEnd.durationFrom(getInitialState().getDate()));
552             afterIntegration();
553 
554             // get final state
555             SpacecraftState finalState =
556                             stateMapper.mapArrayToState(stateMapper.mapDoubleToDate(mathFinalState.getTime(),
557                                                                                     tEnd),
558                                                         mathFinalState.getPrimaryState(),
559                                                         mathFinalState.getPrimaryDerivative(),
560                                                         propagationType);
561 
562             finalState = updateAdditionalStatesAndDerivatives(finalState, mathFinalState);
563 
564             if (resetAtEnd || forceResetAtEnd) {
565                 resetInitialState(finalState, propagationType);
566                 setStartDate(finalState.getDate());
567             }
568 
569             return finalState;
570 
571         } catch (MathRuntimeException mre) {
572             throw OrekitException.unwrap(mre);
573         }
574     }
575 
576     /**
577      * Returns an updated version of the inputted state with additional states, including
578      * from derivatives providers.
579      * @param originalState input state
580      * @param os ODE state and derivative
581      * @return new state
582      * @since 12.1
583      */
584     private SpacecraftState updateAdditionalStatesAndDerivatives(final SpacecraftState originalState,
585                                                                  final ODEStateAndDerivative os) {
586         SpacecraftState updatedState = originalState;
587         if (os.getNumberOfSecondaryStates() > 0) {
588             final double[] secondary           = os.getSecondaryState(1);
589             final double[] secondaryDerivative = os.getSecondaryDerivative(1);
590             for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
591                 final String name      = provider.getName();
592                 final int    offset    = secondaryOffsets.get(name);
593                 final int    dimension = provider.getDimension();
594                 updatedState = updatedState.addAdditionalData(name, Arrays.copyOfRange(secondary, offset, offset + dimension));
595                 updatedState = updatedState.addAdditionalStateDerivative(name, Arrays.copyOfRange(secondaryDerivative, offset, offset + dimension));
596             }
597         }
598         return updateAdditionalData(updatedState);
599     }
600 
601     /** Get the initial state for integration.
602      * @return initial state for integration
603      */
604     protected SpacecraftState getInitialIntegrationState() {
605         return getInitialState();
606     }
607 
608     /** Create an initial state.
609      * @param initialState initial state in flight dynamics world
610      * @return initial state in mathematics world
611      */
612     private ODEState createInitialState(final SpacecraftState initialState) {
613 
614         // retrieve initial state
615         final double[] primary = new double[getBasicDimension()];
616         stateMapper.mapStateToArray(initialState, primary, null);
617 
618         if (secondaryOffsets.isEmpty()) {
619             // compute dimension of the secondary state
620             int offset = 0;
621             for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
622                 secondaryOffsets.put(provider.getName(), offset);
623                 offset += provider.getDimension();
624             }
625             secondaryOffsets.put(SECONDARY_DIMENSION, offset);
626         }
627 
628         return new ODEState(0.0, primary, secondary(initialState));
629 
630     }
631 
632     /** Create secondary state.
633      * @param state spacecraft state
634      * @return secondary state
635      * @since 11.1
636      */
637     private double[][] secondary(final SpacecraftState state) {
638 
639         if (secondaryOffsets.isEmpty()) {
640             return null;
641         }
642 
643         final double[][] secondary = new double[1][secondaryOffsets.get(SECONDARY_DIMENSION)];
644         copyAdditionalStateOrDerivative(state, false, secondary);
645 
646         return secondary;
647 
648     }
649 
650     private void copyAdditionalStateOrDerivative(final SpacecraftState state, final boolean copyDerivative,
651                                                  final double[][] secondary) {
652         for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
653             final String   name       = provider.getName();
654             final int      offset     = secondaryOffsets.get(name);
655             final int    dimension = provider.getDimension();
656             final double[] secondaryArray = copyDerivative ? state.getAdditionalStateDerivative(name) :
657                     state.getAdditionalState(name);
658             System.arraycopy(secondaryArray, 0, secondary[0], offset, dimension);
659         }
660     }
661 
662     /** Create an ODE with all equations.
663      * @param integ numerical integrator to use for propagation.
664      * @return a new ode
665      */
666     private ExpandableODE createODE(final ODEIntegrator integ) {
667 
668         final ExpandableODE ode =
669                 new ExpandableODE(new ConvertedMainStateEquations(getMainStateEquations(integ)));
670 
671         // secondary part of the ODE
672         if (!additionalDerivativesProviders.isEmpty()) {
673             ode.addSecondaryEquations(new ConvertedSecondaryStateEquations());
674         }
675 
676         return ode;
677 
678     }
679 
680     /** Method called just before integration.
681      * <p>
682      * The default implementation does nothing, it may be specialized in subclasses.
683      * </p>
684      * @param initialState initial state
685      * @param tEnd target date at which state should be propagated
686      */
687     protected void beforeIntegration(final SpacecraftState initialState,
688                                      final AbsoluteDate tEnd) {
689         // do nothing by default
690     }
691 
692     /** Method called just after integration.
693      * <p>
694      * The default implementation does nothing, it may be specialized in subclasses.
695      * </p>
696      */
697     protected void afterIntegration() {
698         // do nothing by default
699     }
700 
701     /** Get state vector dimension without additional parameters.
702      * @return state vector dimension without additional parameters.
703      */
704     public int getBasicDimension() {
705         return 7;
706     }
707 
708     /** Get the integrator used by the propagator.
709      * @return the integrator.
710      */
711     protected ODEIntegrator getIntegrator() {
712         return integrator;
713     }
714 
715     /** Convert a state from mathematical world to space flight dynamics world.
716      * @param os mathematical state
717      * @return space flight dynamics state
718      */
719     private SpacecraftState convertToOrekitWithAdditional(final ODEStateAndDerivative os) {
720         final SpacecraftState s = convertToOrekitWithoutAdditional(os);
721         return updateAdditionalStatesAndDerivatives(s, os);
722     }
723 
724     /** Convert a state from mathematical world to space flight dynamics world without additional data and derivatives.
725      * @param os mathematical state
726      * @return space flight dynamics state
727      * @since 13.1
728      */
729     private SpacecraftState convertToOrekitWithoutAdditional(final ODEStateAndDerivative os) {
730         return stateMapper.mapArrayToState(os.getTime(), os.getPrimaryState(),
731             os.getPrimaryDerivative(), propagationType);
732     }
733 
734     /**
735      * Reset the state prior to continue propagation.
736      * <p>
737      * As the new state will be used for the next integration step,
738      * it shall be consistent with propagator needs. A typical example
739      * is that DSST only integrates mean elements, not osculating ones.
740      * </p>
741      * @param handler event handler
742      * @param detector event detector
743      * @param oldState old state (previous one)
744      * @return new state
745      * @since 13.1.6
746      */
747     protected SpacecraftState resetIntegrationStateAtEvent(final EventHandler handler, final EventDetector detector, final SpacecraftState oldState) {
748         return handler.resetState(detector, oldState);
749     }
750 
751     /** Differential equations for the main state (orbit, attitude and mass). */
752     public interface MainStateEquations {
753 
754         /**
755          * Initialize the equations at the start of propagation. This method will be
756          * called before any calls to {@link #computeDerivatives(SpacecraftState)}.
757          *
758          * <p> The default implementation of this method does nothing.
759          *
760          * @param initialState initial state information at the start of propagation.
761          * @param target       date of propagation. Not equal to {@code
762          *                     initialState.getDate()}.
763          */
764         default void init(final SpacecraftState initialState, final AbsoluteDate target) {
765         }
766 
767         /** Compute differential equations for main state.
768          * @param state current state
769          * @return derivatives of main state
770          */
771         double[] computeDerivatives(SpacecraftState state);
772 
773     }
774 
775     /** Differential equations for the main state (orbit, attitude and mass), with converted API. */
776     private class ConvertedMainStateEquations implements OrdinaryDifferentialEquation {
777 
778         /** Main state equations. */
779         private final MainStateEquations main;
780 
781         /** Simple constructor.
782          * @param main main state equations
783          */
784         ConvertedMainStateEquations(final MainStateEquations main) {
785             this.main = main;
786             calls = 0;
787         }
788 
789         /** {@inheritDoc} */
790         public int getDimension() {
791             return getBasicDimension();
792         }
793 
794         @Override
795         public void init(final double t0, final double[] y0, final double finalTime) {
796             // update space dynamics view
797             SpacecraftState initialState = stateMapper.mapArrayToState(t0, y0, null, PropagationType.MEAN);
798             initialState = updateAdditionalData(initialState);
799             initialState = updateStatesFromAdditionalDerivativesIfKnown(initialState);
800             final AbsoluteDate target = stateMapper.mapDoubleToDate(finalTime);
801             main.init(initialState, target);
802             attitudeProviderForDerivatives = initializeAttitudeProviderForDerivatives();
803         }
804 
805         /**
806          * Returns an updated version of the inputted state, with additional states from
807          * derivatives providers as given in the stored initial state.
808          * @param originalState input state
809          * @return new state
810          * @since 12.1
811          */
812         private SpacecraftState updateStatesFromAdditionalDerivativesIfKnown(final SpacecraftState originalState) {
813             SpacecraftState updatedState = originalState;
814             final SpacecraftState storedInitialState = getInitialState();
815             final double originalTime = stateMapper.mapDateToDouble(originalState.getDate());
816             if (storedInitialState != null && stateMapper.mapDateToDouble(storedInitialState.getDate()) == originalTime) {
817                 for (final AdditionalDerivativesProvider provider: additionalDerivativesProviders) {
818                     final String name = provider.getName();
819                     final double[] value = storedInitialState.getAdditionalState(name);
820                     updatedState = updatedState.addAdditionalData(name, value);
821                 }
822             }
823             return updatedState;
824         }
825 
826         /** {@inheritDoc} */
827         public double[] computeDerivatives(final double t, final double[] y) {
828 
829             // increment calls counter
830             ++calls;
831 
832             // update space dynamics view
833             stateMapper.setAttitudeProvider(attitudeProviderForDerivatives);
834             SpacecraftState currentState = stateMapper.mapArrayToState(t, y, null, PropagationType.MEAN);
835             stateMapper.setAttitudeProvider(getAttitudeProvider());
836 
837             currentState = updateAdditionalData(currentState);
838             // compute main state differentials
839             return main.computeDerivatives(currentState);
840 
841         }
842 
843     }
844 
845     /** Differential equations for the secondary state (Jacobians, user variables ...), with converted API. */
846     private class ConvertedSecondaryStateEquations implements SecondaryODE {
847 
848         /** Dimension of the combined additional states. */
849         private final int combinedDimension;
850 
851         /** Simple constructor.
852           */
853         ConvertedSecondaryStateEquations() {
854             this.combinedDimension = secondaryOffsets.get(SECONDARY_DIMENSION);
855         }
856 
857         /** {@inheritDoc} */
858         @Override
859         public int getDimension() {
860             return combinedDimension;
861         }
862 
863         /** {@inheritDoc} */
864         @Override
865         public void init(final double t0, final double[] primary0,
866                          final double[] secondary0, final double finalTime) {
867             // update space dynamics view
868             final SpacecraftState initialState = convert(t0, primary0, null, secondary0);
869 
870             final AbsoluteDate target = stateMapper.mapDoubleToDate(finalTime);
871             for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
872                 provider.init(initialState, target);
873             }
874 
875         }
876 
877         /** {@inheritDoc} */
878         @Override
879         public double[] computeDerivatives(final double t, final double[] primary,
880                                            final double[] primaryDot, final double[] secondary) {
881 
882             // update space dynamics view
883             // the integrable generators generate method will be called here,
884             // according to the generators yield order
885             SpacecraftState updated = convert(t, primary, primaryDot, secondary);
886 
887             // set up queue for equations
888             final Queue<AdditionalDerivativesProvider> pending = new LinkedList<>(additionalDerivativesProviders);
889 
890             // gather the derivatives from all additional equations, taking care of dependencies
891             final double[] secondaryDot = new double[combinedDimension];
892             final DoubleArrayDictionary derivativesDictionary = new DoubleArrayDictionary();
893             int yieldCount = 0;
894             while (!pending.isEmpty()) {
895                 final AdditionalDerivativesProvider provider = pending.remove();
896                 if (provider.yields(updated)) {
897                     // this provider has to wait for another one,
898                     // we put it again in the pending queue
899                     pending.add(provider);
900                     if (++yieldCount >= pending.size()) {
901                         // all pending providers yielded!, they probably need data not yet initialized
902                         // we let the propagation proceed, if these data are really needed right now
903                         // an appropriate exception will be triggered when caller tries to access them
904                         break;
905                     }
906                 } else {
907                     // we can use these equations right now
908                     final String              name           = provider.getName();
909                     final int                 offset         = secondaryOffsets.get(name);
910                     final int                 dimension      = provider.getDimension();
911                     final CombinedDerivatives derivatives    = provider.combinedDerivatives(updated);
912                     final double[]            additionalPart = derivatives.getAdditionalDerivatives();
913                     final double[]            mainPart       = derivatives.getMainStateDerivativesIncrements();
914                     System.arraycopy(additionalPart, 0, secondaryDot, offset, dimension);
915                     derivativesDictionary.put(name, additionalPart);
916                     updated = updated.withAdditionalStatesDerivatives(derivativesDictionary);
917                     if (mainPart != null) {
918                         // this equation does change the main state derivatives
919                         for (int i = 0; i < mainPart.length; ++i) {
920                             primaryDot[i] += mainPart[i];
921                         }
922                     }
923                     yieldCount = 0;
924                 }
925             }
926 
927             return secondaryDot;
928 
929         }
930 
931         /** Convert mathematical view to space view.
932          * @param t current value of the independent <I>time</I> variable
933          * @param primary array containing the current value of the primary state vector
934          * @param primaryDot array containing the derivative of the primary state vector
935          * @param secondary array containing the current value of the secondary state vector
936          * @return space view of the state
937          */
938         private SpacecraftState convert(final double t, final double[] primary,
939                                         final double[] primaryDot, final double[] secondary) {
940 
941             final SpacecraftState initialState = stateMapper.mapArrayToState(t, primary, primaryDot, PropagationType.MEAN);
942 
943             final DataDictionary additionalStateDictionary = new DataDictionary();
944             for (final AdditionalDerivativesProvider provider : additionalDerivativesProviders) {
945                 final String name      = provider.getName();
946                 final int    offset    = secondaryOffsets.get(name);
947                 final int    dimension = provider.getDimension();
948                 additionalStateDictionary.put(name, Arrays.copyOfRange(secondary, offset, offset + dimension));
949             }
950 
951             return updateAdditionalData(initialState.withAdditionalData(additionalStateDictionary));
952 
953         }
954 
955     }
956 
957     /** Adapt an {@link org.orekit.propagation.events.EventDetector}
958      * to Hipparchus {@link org.hipparchus.ode.events.ODEEventDetector} interface.
959      * @author Fabien Maussion
960      */
961     private class AdaptedEventDetector implements ODEEventDetector {
962 
963         /** Underlying event detector. */
964         private final EventDetector detector;
965 
966         /** Underlying event handler.
967          * @since 12.0
968          */
969         private final EventHandler handler;
970 
971         /** Time of the previous call to g. */
972         private double lastT;
973 
974         /** Value from the previous call to g. */
975         private double lastG;
976 
977         /** Build a wrapped event detector.
978          * @param detector event detector to wrap
979         */
980         AdaptedEventDetector(final EventDetector detector) {
981             this.detector = detector;
982             this.handler  = detector.getHandler();
983             this.lastT    = Double.NaN;
984             this.lastG    = Double.NaN;
985         }
986 
987         /** {@inheritDoc} */
988         @Override
989         public AdaptableInterval getMaxCheckInterval() {
990             return (state, isForward) -> detector.getMaxCheckInterval().currentInterval(convertToOrekitForEventFunction(state), isForward);
991         }
992 
993         /** {@inheritDoc} */
994         @Override
995         public int getMaxIterationCount() {
996             return detector.getMaxIterationCount();
997         }
998 
999         /** {@inheritDoc} */
1000         @Override
1001         public BracketedUnivariateSolver<UnivariateFunction> getSolver() {
1002             return new BracketingNthOrderBrentSolver(0, detector.getThreshold(), 0, 5);
1003         }
1004 
1005         /** {@inheritDoc} */
1006         @Override
1007         public void init(final ODEStateAndDerivative s0, final double t) {
1008             detector.init(convertToOrekitWithAdditional(s0), stateMapper.mapDoubleToDate(t));
1009             this.lastT = Double.NaN;
1010             this.lastG = Double.NaN;
1011         }
1012 
1013         /** {@inheritDoc} */
1014         @Override
1015         public void reset(final ODEStateAndDerivative intermediateState, final double finalTime) {
1016             detector.reset(convertToOrekitForEventFunction(intermediateState), stateMapper.mapDoubleToDate(finalTime));
1017             this.lastT = Double.NaN;
1018             this.lastG = Double.NaN;
1019         }
1020 
1021         /** {@inheritDoc} */
1022         public double g(final ODEStateAndDerivative s) {
1023             if (!Precision.equals(lastT, s.getTime(), 0)) {
1024                 lastT = s.getTime();
1025                 lastG = detector.g(convertToOrekitForEventFunction(s));
1026             }
1027             return lastG;
1028         }
1029 
1030         /**
1031          * Convert state from Hipparchus to Orekit format.
1032          * @param s state vector
1033          * @return Orekit state
1034          */
1035         private SpacecraftState convertToOrekitForEventFunction(final ODEStateAndDerivative s) {
1036             if (!this.detector.getEventFunction().dependsOnMainVariablesOnly()) {
1037                 return convertToOrekitWithAdditional(s);
1038             } else {
1039                 // event function does not require secondary states or attitude rates
1040                 stateMapper.setAttitudeProvider(getFrozenAttitudeProvider());
1041                 final SpacecraftState converted = convertToOrekitWithoutAdditional(s);
1042                 stateMapper.setAttitudeProvider(getAttitudeProvider());
1043                 return converted;
1044             }
1045         }
1046 
1047         /** {@inheritDoc} */
1048         public ODEEventHandler getHandler() {
1049 
1050             return new ODEEventHandler() {
1051 
1052                 /** {@inheritDoc} */
1053                 public Action eventOccurred(final ODEStateAndDerivative s, final ODEEventDetector d, final boolean increasing) {
1054                     return handler.eventOccurred(convertToOrekitWithAdditional(s), detector, increasing);
1055                 }
1056 
1057                 /** {@inheritDoc} */
1058                 @Override
1059                 public ODEState resetState(final ODEEventDetector d, final ODEStateAndDerivative s) {
1060 
1061                     final SpacecraftState oldState = convertToOrekitWithAdditional(s);
1062                     final SpacecraftState newState = resetIntegrationStateAtEvent(handler, detector, oldState);
1063                     stateChanged(newState);
1064 
1065                     // main part
1066                     final double[] primary    = new double[s.getPrimaryStateDimension()];
1067                     stateMapper.mapStateToArray(newState, primary, null);
1068 
1069                     // secondary part
1070                     final double[][] secondary = new double[1][secondaryOffsets.get(SECONDARY_DIMENSION)];
1071                     copyAdditionalStateOrDerivative(newState, false, secondary);
1072 
1073                     return new ODEState(newState.getDate().durationFrom(getStartDate()),
1074                                         primary, secondary);
1075 
1076                 }
1077 
1078             };
1079         }
1080 
1081     }
1082 
1083     /** Adapt an {@link org.orekit.propagation.sampling.OrekitStepHandler}
1084      * to Hipparchus {@link ODEStepHandler} interface.
1085      * @author Luc Maisonobe
1086      */
1087     private class AdaptedStepHandler implements ODEStepHandler {
1088 
1089         /** Underlying handler. */
1090         private final OrekitStepHandler handler;
1091 
1092         /** Build an instance.
1093          * @param handler underlying handler to wrap
1094          */
1095         AdaptedStepHandler(final OrekitStepHandler handler) {
1096             this.handler = handler;
1097         }
1098 
1099         /** {@inheritDoc} */
1100         @Override
1101         public void init(final ODEStateAndDerivative s0, final double t) {
1102             handler.init(convertToOrekitWithAdditional(s0), stateMapper.mapDoubleToDate(t));
1103         }
1104 
1105         /** {@inheritDoc} */
1106         @Override
1107         public void handleStep(final ODEStateInterpolator interpolator) {
1108             handler.handleStep(new AdaptedStepInterpolator(interpolator));
1109         }
1110 
1111         /** {@inheritDoc} */
1112         @Override
1113         public void finish(final ODEStateAndDerivative finalState) {
1114             handler.finish(convertToOrekitWithAdditional(finalState));
1115         }
1116 
1117     }
1118 
1119     /** Adapt an Hipparchus {@link ODEStateInterpolator}
1120      * to an orekit {@link OrekitStepInterpolator} interface.
1121      * @author Luc Maisonobe
1122      */
1123     private class AdaptedStepInterpolator implements OrekitStepInterpolator {
1124 
1125         /** Underlying raw rawInterpolator. */
1126         private final ODEStateInterpolator mathInterpolator;
1127 
1128         /** Simple constructor.
1129          * @param mathInterpolator underlying raw interpolator
1130          */
1131         AdaptedStepInterpolator(final ODEStateInterpolator mathInterpolator) {
1132             this.mathInterpolator = mathInterpolator;
1133         }
1134 
1135         /** {@inheritDoc}} */
1136         @Override
1137         public SpacecraftState getPreviousState() {
1138             return convertToOrekitWithAdditional(mathInterpolator.getPreviousState());
1139         }
1140 
1141         /** {@inheritDoc}} */
1142         @Override
1143         public boolean isPreviousStateInterpolated() {
1144             return mathInterpolator.isPreviousStateInterpolated();
1145         }
1146 
1147         /** {@inheritDoc}} */
1148         @Override
1149         public SpacecraftState getCurrentState() {
1150             return convertToOrekitWithAdditional(mathInterpolator.getCurrentState());
1151         }
1152 
1153         /** {@inheritDoc}} */
1154         @Override
1155         public boolean isCurrentStateInterpolated() {
1156             return mathInterpolator.isCurrentStateInterpolated();
1157         }
1158 
1159         /** {@inheritDoc}} */
1160         @Override
1161         public SpacecraftState getInterpolatedState(final AbsoluteDate date) {
1162             return convertToOrekitWithAdditional(mathInterpolator.getInterpolatedState(date.durationFrom(stateMapper.getReferenceDate())));
1163         }
1164 
1165         /** {@inheritDoc}} */
1166         @Override
1167         public boolean isForward() {
1168             return mathInterpolator.isForward();
1169         }
1170 
1171         /** {@inheritDoc}} */
1172         @Override
1173         public AdaptedStepInterpolator restrictStep(final SpacecraftState newPreviousState,
1174                                                     final SpacecraftState newCurrentState) {
1175             try {
1176                 final AbstractODEStateInterpolator aosi = (AbstractODEStateInterpolator) mathInterpolator;
1177                 return new AdaptedStepInterpolator(aosi.restrictStep(convertFromOrekit(newPreviousState),
1178                                                                      convertFromOrekit(newCurrentState)));
1179             } catch (ClassCastException cce) {
1180                 // this should never happen
1181                 throw new OrekitInternalError(cce);
1182             }
1183         }
1184 
1185 
1186         /** Convert a state from space flight dynamics world to mathematical world.
1187          * @param state space flight dynamics state
1188          * @return mathematical state
1189          */
1190         private ODEStateAndDerivative convertFromOrekit(final SpacecraftState state) {
1191 
1192             // retrieve initial state
1193             final double[] primary    = new double[getBasicDimension()];
1194             final double[] primaryDot = new double[getBasicDimension()];
1195             stateMapper.mapStateToArray(state, primary, primaryDot);
1196 
1197             // secondary part of the ODE
1198             final double[][] secondary           = secondary(state);
1199             final double[][] secondaryDerivative = secondaryDerivative(state);
1200 
1201             return new ODEStateAndDerivative(stateMapper.mapDateToDouble(state.getDate()),
1202                     primary, primaryDot,
1203                     secondary, secondaryDerivative);
1204 
1205         }
1206 
1207         /** Create secondary state derivative.
1208          * @param state spacecraft state
1209          * @return secondary state derivative
1210          * @since 11.1
1211          */
1212         private double[][] secondaryDerivative(final SpacecraftState state) {
1213 
1214             if (secondaryOffsets.isEmpty()) {
1215                 return null;
1216             }
1217 
1218             final double[][] secondaryDerivative = new double[1][secondaryOffsets.get(SECONDARY_DIMENSION)];
1219             copyAdditionalStateOrDerivative(state, true, secondaryDerivative);
1220 
1221             return secondaryDerivative;
1222 
1223         }
1224     }
1225 
1226     /** Specialized step handler storing interpolators for ephemeris generation.
1227      * @since 11.0
1228      */
1229     private class StoringStepHandler implements ODEStepHandler, EphemerisGenerator {
1230 
1231         /** Underlying raw mathematical model. */
1232         private DenseOutputModel model;
1233 
1234         /** the user supplied end date. Propagation may not end on this date. */
1235         private AbsoluteDate endDate;
1236 
1237         /** Generated ephemeris. */
1238         private BoundedPropagator ephemeris;
1239 
1240         /** Last interpolator handled by the object.*/
1241         private  ODEStateInterpolator lastInterpolator;
1242 
1243         /** Set the end date.
1244          * @param endDate end date
1245          */
1246         public void setEndDate(final AbsoluteDate endDate) {
1247             this.endDate = endDate;
1248         }
1249 
1250         /** {@inheritDoc} */
1251         @Override
1252         public void init(final ODEStateAndDerivative s0, final double t) {
1253 
1254             this.model = new DenseOutputModel();
1255             model.init(s0, t);
1256 
1257             // ephemeris will be generated when last step is processed
1258             this.ephemeris = null;
1259 
1260             this.lastInterpolator = null;
1261 
1262         }
1263 
1264         /** {@inheritDoc} */
1265         @Override
1266         public BoundedPropagator getGeneratedEphemeris() {
1267             // Each time we try to get the ephemeris, rebuild it using the last data.
1268             buildEphemeris();
1269             return ephemeris;
1270         }
1271 
1272         /** {@inheritDoc} */
1273         @Override
1274         public void handleStep(final ODEStateInterpolator interpolator) {
1275             model.handleStep(interpolator);
1276             lastInterpolator = interpolator;
1277         }
1278 
1279         /** {@inheritDoc} */
1280         @Override
1281         public void finish(final ODEStateAndDerivative finalState) {
1282             buildEphemeris();
1283         }
1284 
1285         /** Method used to produce ephemeris at a given time.
1286          * Can be used at multiple times, updating the ephemeris to
1287          * its last state.
1288          */
1289         private void buildEphemeris() {
1290             // buildEphemeris was built in order to allow access to what was previously the finish method.
1291             // This now allows to call it through getGeneratedEphemeris, therefore through an external call,
1292             // which was not previously the case.
1293 
1294             // Update the model's finalTime with the last interpolator.
1295             model.finish(lastInterpolator.getCurrentState());
1296 
1297             // set up the boundary dates
1298             final double tI = model.getInitialTime();
1299             final double tF = model.getFinalTime();
1300             // tI is almost? always zero
1301             final AbsoluteDate startDate =
1302                             stateMapper.mapDoubleToDate(tI);
1303             final AbsoluteDate finalDate =
1304                             stateMapper.mapDoubleToDate(tF, this.endDate);
1305             final AbsoluteDate minDate;
1306             final AbsoluteDate maxDate;
1307             if (tF < tI) {
1308                 minDate = finalDate;
1309                 maxDate = startDate;
1310             } else {
1311                 minDate = startDate;
1312                 maxDate = finalDate;
1313             }
1314 
1315             // get the initial additional states that are not managed
1316             final DataDictionary unmanaged = new DataDictionary();
1317             for (final DataDictionary.Entry initial : getInitialState().getAdditionalDataValues().getData()) {
1318                 if (!AbstractIntegratedPropagator.this.isAdditionalDataManaged(initial.getKey())) {
1319                     // this additional state was in the initial state, but is unknown to the propagator
1320                     // we simply copy its initial value as is
1321                     unmanaged.put(initial.getKey(), initial.getValue());
1322                 }
1323             }
1324 
1325             // get the names of additional states managed by differential equations
1326             final String[] names      = new String[additionalDerivativesProviders.size()];
1327             final int[]    dimensions = new int[additionalDerivativesProviders.size()];
1328             for (int i = 0; i < names.length; ++i) {
1329                 names[i] = additionalDerivativesProviders.get(i).getName();
1330                 dimensions[i] = additionalDerivativesProviders.get(i).getDimension();
1331             }
1332 
1333             // create the ephemeris
1334             ephemeris = new IntegratedEphemeris(startDate, minDate, maxDate,
1335                                                 stateMapper, getAttitudeProvider(), propagationType, model,
1336                                                 unmanaged, getAdditionalDataProviders(),
1337                                                 names, dimensions);
1338 
1339         }
1340 
1341     }
1342 
1343     /** Wrapper for resetting an integrator handlers.
1344      * <p>
1345      * This class is intended to be used in a try-with-resource statement.
1346      * If propagator-specific event handlers and step handlers are added to
1347      * the integrator in the try block, they will be removed automatically
1348      * when leaving the block, so the integrator only keeps its own handlers
1349      * between calls to {@link AbstractIntegratedPropagator#propagate(AbsoluteDate, AbsoluteDate)}.
1350      * </p>
1351      * @since 11.0
1352      */
1353     private static class IntegratorResetter implements AutoCloseable {
1354 
1355         /** Wrapped integrator. */
1356         private final ODEIntegrator integrator;
1357 
1358         /** Initial event detectors list. */
1359         private final List<ODEEventDetector> detectors;
1360 
1361         /** Initial step handlers list. */
1362         private final List<ODEStepHandler> stepHandlers;
1363 
1364         /** Simple constructor.
1365          * @param integrator wrapped integrator
1366          */
1367         IntegratorResetter(final ODEIntegrator integrator) {
1368             this.integrator   = integrator;
1369             this.detectors    = new ArrayList<>(integrator.getEventDetectors());
1370             this.stepHandlers = new ArrayList<>(integrator.getStepHandlers());
1371         }
1372 
1373         /** {@inheritDoc}
1374          * <p>
1375          * Reset event handlers and step handlers back to the initial list
1376          * </p>
1377          */
1378         @Override
1379         public void close() {
1380 
1381             // reset event handlers
1382             integrator.clearEventDetectors();
1383             detectors.forEach(integrator::addEventDetector);
1384 
1385             // reset step handlers
1386             integrator.clearStepHandlers();
1387             stepHandlers.forEach(integrator::addStepHandler);
1388 
1389         }
1390 
1391     }
1392 
1393 }