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