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