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.analytical;
18  
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.Collections;
22  import java.util.Comparator;
23  import java.util.List;
24  import java.util.PriorityQueue;
25  import java.util.Queue;
26  import java.util.stream.Collectors;
27  
28  import org.hipparchus.CalculusFieldElement;
29  import org.hipparchus.Field;
30  import org.hipparchus.exception.MathRuntimeException;
31  import org.hipparchus.ode.events.Action;
32  import org.orekit.attitudes.AttitudeProvider;
33  import org.orekit.attitudes.FieldAttitude;
34  import org.orekit.errors.OrekitException;
35  import org.orekit.errors.OrekitInternalError;
36  import org.orekit.frames.Frame;
37  import org.orekit.orbits.FieldOrbit;
38  import org.orekit.propagation.BoundedPropagator;
39  import org.orekit.propagation.FieldAbstractPropagator;
40  import org.orekit.propagation.FieldAdditionalDataProvider;
41  import org.orekit.propagation.FieldBoundedPropagator;
42  import org.orekit.propagation.FieldEphemerisGenerator;
43  import org.orekit.propagation.FieldSpacecraftState;
44  import org.orekit.propagation.events.FieldEventDetector;
45  import org.orekit.propagation.events.FieldEventState;
46  import org.orekit.propagation.events.FieldEventState.EventOccurrence;
47  import org.orekit.propagation.sampling.FieldOrekitStepInterpolator;
48  import org.orekit.time.FieldAbsoluteDate;
49  import org.orekit.utils.FieldPVCoordinatesProvider;
50  import org.orekit.utils.ParameterDriver;
51  import org.orekit.utils.ParameterDriversProvider;
52  import org.orekit.utils.TimeStampedFieldPVCoordinates;
53  
54  /** Common handling of {@link org.orekit.propagation.FieldPropagator} methods for analytical propagators.
55   * <p>
56   * This abstract class allows to provide easily the full set of {@link
57   * org.orekit.propagation.FieldPropagator FieldPropagator} methods, including all propagation
58   * modes support and discrete events support for any simple propagation method. Only
59   * two methods must be implemented by derived classes: {@link #propagateOrbit(FieldAbsoluteDate, CalculusFieldElement[])}
60   * and {@link #getMass(FieldAbsoluteDate)}. The first method should perform straightforward
61   * propagation starting from some internally stored initial state up to the specified target date.
62   * </p>
63   * @author Luc Maisonobe
64   * @param <T> type of the field elements
65   */
66  
67  public abstract class FieldAbstractAnalyticalPropagator<T extends CalculusFieldElement<T>>
68      extends FieldAbstractPropagator<T>
69      implements ParameterDriversProvider {
70  
71      /** Provider for attitude computation. */
72      private final FieldPVCoordinatesProvider<T> pvProvider;
73  
74      /** Start date of last propagation. */
75      private FieldAbsoluteDate<T> lastPropagationStart;
76  
77      /** End date of last propagation. */
78      private FieldAbsoluteDate<T> lastPropagationEnd;
79  
80      /** Initialization indicator of events states. */
81      private boolean statesInitialized;
82  
83      /** Indicator for last step. */
84      private boolean isLastStep;
85  
86      /** User-defined event states. */
87      private final Collection<FieldEventState<?, T>> userEventsStates;
88  
89      /** All event states, including internal ones. */
90      private Collection<FieldEventState<?, T>> eventsStates;
91  
92      /** Build a new instance.
93       * @param attitudeProvider provider for attitude computation
94       * @param field field used as default
95       */
96      protected FieldAbstractAnalyticalPropagator(final Field<T> field, final AttitudeProvider attitudeProvider) {
97          super(field);
98          setAttitudeProvider(attitudeProvider);
99          pvProvider           = new FieldLocalPVProvider();
100         lastPropagationStart = FieldAbsoluteDate.getPastInfinity(field);
101         lastPropagationEnd   = FieldAbsoluteDate.getFutureInfinity(field);
102         statesInitialized    = false;
103         userEventsStates     = new ArrayList<>();
104     }
105 
106     /** {@inheritDoc} */
107     @Override
108     public FieldEphemerisGenerator<T> getEphemerisGenerator() {
109         return () -> new FieldBoundedPropagatorView(lastPropagationStart, lastPropagationEnd);
110     }
111 
112     /** {@inheritDoc} */
113     public <D extends FieldEventDetector<T>> void addEventDetector(final D detector) {
114         userEventsStates.add(new FieldEventState<>(detector));
115     }
116 
117     /** {@inheritDoc} */
118     @Override
119     public Collection<FieldEventDetector<T>> getEventDetectors() {
120         final List<FieldEventDetector<T>> list = new ArrayList<>();
121         for (final FieldEventState<?, T> state : userEventsStates) {
122             list.add(state.getEventDetector());
123         }
124         return Collections.unmodifiableCollection(list);
125     }
126 
127     /** {@inheritDoc} */
128     @Override
129     public void clearEventsDetectors() {
130         userEventsStates.clear();
131     }
132 
133     /** {@inheritDoc} */
134     @Override
135     public FieldSpacecraftState<T> propagate(final FieldAbsoluteDate<T> start, final FieldAbsoluteDate<T> target) {
136         try {
137 
138             initializePropagation();
139 
140             lastPropagationStart = start;
141 
142             // Initialize additional states
143             initializeAdditionalData(target);
144 
145             final boolean           isForward = target.compareTo(start) >= 0;
146             FieldSpacecraftState<T> state   = updateAdditionalData(basicPropagate(start));
147 
148             // initialize event detectors
149             eventsStates = getAttitudeProvider().
150                            getFieldEventDetectors(getField()).map(FieldEventState::new).
151                            collect(Collectors.toList());
152             eventsStates.addAll(userEventsStates);
153             for (final FieldEventState<?, T> es : eventsStates) {
154                 es.init(state, target);
155             }
156 
157             // initialize step handlers
158             getMultiplexer().init(state, target);
159 
160             // iterate over the propagation range, need loop due to reset events
161             statesInitialized = false;
162             isLastStep = false;
163             do {
164 
165                 // attempt to advance to the target date
166                 final FieldSpacecraftState<T> previous = state;
167                 final FieldSpacecraftState<T> current = updateAdditionalData(basicPropagate(target));
168                 final FieldBasicStepInterpolator interpolator =
169                         new FieldBasicStepInterpolator(isForward, previous, current);
170 
171                 // accept the step, trigger events and step handlers
172                 state = acceptStep(interpolator, target);
173 
174                 // Update the potential changes in the spacecraft state due to the events
175                 // especially the potential attitude transition
176                 state = updateAdditionalData(basicPropagate(state.getDate()));
177 
178             } while (!isLastStep);
179 
180             // Finalize event detectors
181             for (final FieldEventState<?, T> es : eventsStates) {
182                 es.finish(state);
183             }
184 
185             // return the last computed state
186             lastPropagationEnd = state.getDate();
187             setStartDate(state.getDate());
188             return state;
189 
190         } catch (MathRuntimeException mrte) {
191             throw OrekitException.unwrap(mrte);
192         }
193     }
194 
195     /** Accept a step, triggering events and step handlers.
196      * @param interpolator interpolator for the current step
197      * @param target final propagation time
198      * @return state at the end of the step
199      * @exception MathRuntimeException if an event cannot be located
200      */
201     protected FieldSpacecraftState<T> acceptStep(final FieldBasicStepInterpolator interpolator,
202                                                  final FieldAbsoluteDate<T> target)
203         throws MathRuntimeException {
204 
205         FieldSpacecraftState<T>       previous   = interpolator.getPreviousState();
206         final FieldSpacecraftState<T> current    = interpolator.getCurrentState();
207         FieldBasicStepInterpolator    restricted = interpolator;
208 
209         // initialize the events states if needed
210         if (!statesInitialized) {
211 
212             if (!eventsStates.isEmpty()) {
213                 // initialize the events states
214                 for (final FieldEventState<?, T> state : eventsStates) {
215                     state.reinitializeBegin(interpolator);
216                 }
217             }
218 
219             statesInitialized = true;
220 
221         }
222 
223         // search for next events that may occur during the step
224         final int orderingSign = interpolator.isForward() ? +1 : -1;
225         final Queue<FieldEventState<?, T>> occurringEvents = new PriorityQueue<>(new Comparator<FieldEventState<?, T>>() {
226             /** {@inheritDoc} */
227             @Override
228             public int compare(final FieldEventState<?, T> es0, final FieldEventState<?, T> es1) {
229                 return orderingSign * es0.getEventDate().compareTo(es1.getEventDate());
230             }
231         });
232 
233         boolean doneWithStep = false;
234         resetEvents:
235         do {
236 
237             // Evaluate all event detectors for events
238             occurringEvents.clear();
239             for (final FieldEventState<?, T> state : eventsStates) {
240                 if (state.evaluateStep(interpolator)) {
241                     // the event occurs during the current step
242                     occurringEvents.add(state);
243                 }
244             }
245 
246 
247             do {
248 
249                 eventLoop:
250                 while (!occurringEvents.isEmpty()) {
251 
252                     // handle the chronologically first event
253                     final FieldEventState<?, T> currentEvent = occurringEvents.poll();
254 
255                     // get state at event time
256                     FieldSpacecraftState<T> eventState = restricted.getInterpolatedState(currentEvent.getEventDate());
257 
258                     // restrict the interpolator to the first part of the step, up to the event
259                     restricted = restricted.restrictStep(previous, eventState);
260 
261                     // try to advance all event states to current time
262                     for (final FieldEventState<?, T> state : eventsStates) {
263                         if (state != currentEvent && state.tryAdvance(eventState, interpolator)) {
264                             // we need to handle another event first
265                             // remove event we just updated to prevent heap corruption
266                             occurringEvents.remove(state);
267                             // add it back to update its position in the heap
268                             occurringEvents.add(state);
269                             // re-queue the event we were processing
270                             occurringEvents.add(currentEvent);
271                             continue eventLoop;
272                         }
273                     }
274                     // all event detectors agree we can advance to the current event time
275 
276                     // handle the first part of the step, up to the event
277                     getMultiplexer().handleStep(restricted);
278 
279                     // acknowledge event occurrence
280                     final EventOccurrence<T> occurrence = currentEvent.doEvent(eventState);
281                     final Action action = occurrence.getAction();
282                     isLastStep = action == Action.STOP;
283                     if (isLastStep) {
284 
285                         // ensure the event is after the root if it is returned STOP
286                         // this lets the user integrate to a STOP event and then restart
287                         // integration from the same time.
288                         final FieldSpacecraftState<T> savedState = eventState;
289                         eventState = interpolator.getInterpolatedState(occurrence.getStopDate());
290                         restricted = restricted.restrictStep(savedState, eventState);
291 
292                         // handle the almost zero size last part of the final step, at event time
293                         getMultiplexer().handleStep(restricted);
294                         getMultiplexer().finish(restricted.getCurrentState());
295 
296                     }
297 
298                     if (isLastStep) {
299                         // the event asked to stop integration
300                         return eventState;
301                     }
302 
303                     if (action == Action.RESET_DERIVATIVES || action == Action.RESET_STATE) {
304                         // some event handler has triggered changes that
305                         // invalidate the derivatives, we need to recompute them
306                         final FieldSpacecraftState<T> resetState = occurrence.getNewState();
307                         resetIntermediateState(resetState, interpolator.isForward());
308                         for (final FieldEventState<?, T> fieldEventState: eventsStates) {
309                             fieldEventState.getEventDetector().reset(resetState, target);
310                         }
311                         return resetState;
312                     }
313                     // at this point action == Action.CONTINUE or Action.RESET_EVENTS
314 
315                     // prepare handling of the remaining part of the step
316                     previous = eventState;
317                     restricted = new FieldBasicStepInterpolator(restricted.isForward(), eventState, current);
318 
319                     if (action == Action.RESET_EVENTS) {
320                         continue resetEvents;
321                     }
322 
323                     // at this pint action == Action.CONTINUE
324                     // check if the same event occurs again in the remaining part of the step
325                     if (currentEvent.evaluateStep(restricted)) {
326                         // the event occurs during the current step
327                         occurringEvents.add(currentEvent);
328                     }
329 
330                 }
331 
332                 // last part of the step, after the last event. Advance all detectors to
333                 // the end of the step. Should only detect a new event here if an event
334                 // modified the g function of another detector. Detecting such events here
335                 // is unreliable and RESET_EVENTS should be used instead. Might as well
336                 // re-check here because we have to loop through all the detectors anyway
337                 // and the alternative is to throw an exception.
338                 for (final FieldEventState<?, T> state : eventsStates) {
339                     if (state.tryAdvance(current, interpolator)) {
340                         occurringEvents.add(state);
341                     }
342                 }
343 
344             } while (!occurringEvents.isEmpty());
345 
346             doneWithStep = true;
347         } while (!doneWithStep);
348 
349         isLastStep = target.equals(current.getDate());
350 
351         // handle the remaining part of the step, after all events if any
352         getMultiplexer().handleStep(restricted);
353         if (isLastStep) {
354             getMultiplexer().finish(restricted.getCurrentState());
355         }
356 
357         return current;
358 
359     }
360 
361     /** Get the mass.
362      * @param date target date for the orbit
363      * @return mass mass
364      */
365     protected abstract T getMass(FieldAbsoluteDate<T> date);
366 
367     /** Reset an intermediate state.
368      * @param state new intermediate state to consider
369      * @param forward if true, the intermediate state is valid for
370      * propagations after itself
371      */
372     protected abstract void resetIntermediateState(FieldSpacecraftState<T> state, boolean forward);
373 
374     /** Propagate an orbit up to a specific target date.
375      * @param date target date for the orbit
376      * @param parameters model parameters
377      * @return propagated orbit
378      */
379     public abstract FieldOrbit<T> propagateOrbit(FieldAbsoluteDate<T> date,
380                                                  T[] parameters);
381 
382     /** Propagate an orbit without any fancy features.
383      * <p>This method is similar in spirit to the {@link #propagate} method,
384      * except that it does <strong>not</strong> call any handler during
385      * propagation, nor any discrete events, not additional states. It always
386      * stop exactly at the specified date.</p>
387      * @param date target date for propagation
388      * @return state at specified date
389      */
390     public FieldSpacecraftState<T> basicPropagate(final FieldAbsoluteDate<T> date) {
391         try {
392 
393             // evaluate orbit
394             final FieldOrbit<T> orbit = propagateOrbit(date, getParameters(date.getField(), date.getDate()));
395 
396             // evaluate attitude
397             final FieldAttitude<T> attitude =
398                 getAttitudeProvider().getAttitude(pvProvider, date, orbit.getFrame());
399 
400             // build raw state
401             return new FieldSpacecraftState<>(orbit, attitude).withMass(getMass(date));
402 
403         } catch (OrekitException oe) {
404             throw new OrekitException(oe);
405         }
406     }
407 
408     /** Internal FieldPVCoordinatesProvider<T> for attitude computation. */
409     private class FieldLocalPVProvider implements FieldPVCoordinatesProvider<T> {
410 
411         /** {@inheritDoc} */
412         @Override
413         public TimeStampedFieldPVCoordinates<T> getPVCoordinates(final FieldAbsoluteDate<T> date, final Frame frame) {
414             return propagateOrbit(date, getParameters(date.getField(), date)).getPVCoordinates(frame);
415         }
416 
417     }
418 
419     /** {@link BoundedPropagator} view of the instance. */
420     private class FieldBoundedPropagatorView extends FieldAbstractAnalyticalPropagator<T>
421         implements FieldBoundedPropagator<T> {
422 
423         /** Min date. */
424         private final FieldAbsoluteDate<T> minDate;
425 
426         /** Max date. */
427         private final FieldAbsoluteDate<T> maxDate;
428 
429         /** Simple constructor.
430          * @param startDate start date of the propagation
431          * @param endDate end date of the propagation
432          */
433         FieldBoundedPropagatorView(final FieldAbsoluteDate<T> startDate, final FieldAbsoluteDate<T> endDate) {
434             super(startDate.durationFrom(endDate).getField(), FieldAbstractAnalyticalPropagator.this.getAttitudeProvider());
435             super.resetInitialState(FieldAbstractAnalyticalPropagator.this.getInitialState());
436             if (startDate.compareTo(endDate) <= 0) {
437                 minDate = startDate;
438                 maxDate = endDate;
439             } else {
440                 minDate = endDate;
441                 maxDate = startDate;
442             }
443 
444             try {
445                 // copy the same additional data providers as the original propagator
446                 for (FieldAdditionalDataProvider<?, T> provider : FieldAbstractAnalyticalPropagator.this.getAdditionalDataProviders()) {
447                     addAdditionalDataProvider(provider);
448                 }
449             } catch (OrekitException oe) {
450                 // as the providers are already compatible with each other,
451                 // this should never happen
452                 throw new OrekitInternalError(null);
453             }
454 
455         }
456 
457         /** {@inheritDoc} */
458         @Override
459         public FieldAbsoluteDate<T> getMinDate() {
460             return minDate;
461         }
462 
463         /** {@inheritDoc} */
464         @Override
465         public FieldAbsoluteDate<T> getMaxDate() {
466             return maxDate;
467         }
468 
469         /** {@inheritDoc} */
470         @Override
471         public FieldOrbit<T> propagateOrbit(final FieldAbsoluteDate<T> target,
472                                             final T[] parameters) {
473             return FieldAbstractAnalyticalPropagator.this.propagateOrbit(target, parameters);
474         }
475 
476         /** {@inheritDoc} */
477         @Override
478         public T getMass(final FieldAbsoluteDate<T> date) {
479             return FieldAbstractAnalyticalPropagator.this.getMass(date);
480         }
481 
482         /** {@inheritDoc} */
483         @Override
484         public void resetInitialState(final FieldSpacecraftState<T> state) {
485             super.resetInitialState(state);
486             FieldAbstractAnalyticalPropagator.this.resetInitialState(state);
487         }
488 
489         /** {@inheritDoc} */
490         @Override
491         protected void resetIntermediateState(final FieldSpacecraftState<T> state, final boolean forward) {
492             FieldAbstractAnalyticalPropagator.this.resetIntermediateState(state, forward);
493         }
494 
495         /** {@inheritDoc} */
496         @Override
497         public FieldSpacecraftState<T> getInitialState() {
498             return FieldAbstractAnalyticalPropagator.this.getInitialState();
499         }
500 
501         /** {@inheritDoc} */
502         @Override
503         public Frame getFrame() {
504             return FieldAbstractAnalyticalPropagator.this.getFrame();
505         }
506 
507         /** {@inheritDoc} */
508         @Override
509         public List<ParameterDriver> getParametersDrivers() {
510             return FieldAbstractAnalyticalPropagator.this.getParametersDrivers();
511         }
512     }
513 
514     /** Internal class for local propagation. */
515     private class FieldBasicStepInterpolator implements FieldOrekitStepInterpolator<T> {
516 
517         /** Previous state. */
518         private final FieldSpacecraftState<T> previousState;
519 
520         /** Current state. */
521         private final FieldSpacecraftState<T> currentState;
522 
523         /** Forward propagation indicator. */
524         private final boolean forward;
525 
526         /** Simple constructor.
527          * @param isForward integration direction indicator
528          * @param previousState start of the step
529          * @param currentState end of the step
530          */
531         FieldBasicStepInterpolator(final boolean isForward,
532                                    final FieldSpacecraftState<T> previousState,
533                                    final FieldSpacecraftState<T> currentState) {
534             this.forward             = isForward;
535             this.previousState   = previousState;
536             this.currentState    = currentState;
537         }
538 
539         /** {@inheritDoc} */
540         @Override
541         public FieldSpacecraftState<T> getPreviousState() {
542             return previousState;
543         }
544 
545         /** {@inheritDoc} */
546         @Override
547         public FieldSpacecraftState<T> getCurrentState() {
548             return currentState;
549         }
550 
551         /** {@inheritDoc} */
552         @Override
553         public FieldSpacecraftState<T> getInterpolatedState(final FieldAbsoluteDate<T> date) {
554 
555             // compute the basic spacecraft state
556             final FieldSpacecraftState<T> basicState = basicPropagate(date);
557 
558             // add the additional states
559             return updateAdditionalData(basicState);
560 
561         }
562 
563         /** {@inheritDoc} */
564         @Override
565         public boolean isForward() {
566             return forward;
567         }
568 
569         /** {@inheritDoc} */
570         @Override
571         public FieldBasicStepInterpolator restrictStep(final FieldSpacecraftState<T> newPreviousState,
572                                                        final FieldSpacecraftState<T> newCurrentState) {
573             return new FieldBasicStepInterpolator(forward, newPreviousState, newCurrentState);
574         }
575 
576     }
577 
578 }