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