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