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