1   /* Copyright 2002-2025 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.propagation;
18  
19  import java.util.Collection;
20  import java.util.List;
21  
22  import org.hipparchus.geometry.euclidean.threed.Rotation;
23  import org.hipparchus.geometry.euclidean.threed.Vector3D;
24  import org.hipparchus.linear.RealMatrix;
25  import org.orekit.attitudes.AttitudeProvider;
26  import org.orekit.attitudes.FrameAlignedProvider;
27  import org.orekit.frames.Frame;
28  import org.orekit.frames.Frames;
29  import org.orekit.orbits.PositionAngleType;
30  import org.orekit.propagation.events.EventDetector;
31  import org.orekit.propagation.sampling.OrekitFixedStepHandler;
32  import org.orekit.propagation.sampling.OrekitStepHandler;
33  import org.orekit.propagation.sampling.StepHandlerMultiplexer;
34  import org.orekit.time.AbsoluteDate;
35  import org.orekit.utils.DoubleArrayDictionary;
36  import org.orekit.utils.PVCoordinatesProvider;
37  import org.orekit.utils.TimeStampedPVCoordinates;
38  
39  /** This interface provides a way to propagate an orbit at any time.
40   *
41   * <p>This interface is the top-level abstraction for orbit propagation.
42   * It only allows propagation to a predefined date.
43   * It is implemented by analytical models which have no time limit,
44   * by orbit readers based on external data files, by numerical integrators
45   * using rich force models and by continuous models built after numerical
46   * integration has been completed and dense output data as been
47   * gathered.</p>
48   * <p>Note that one single propagator cannot be called from multiple threads.
49   * Its configuration can be changed as there is at least a {@link
50   * #resetInitialState(SpacecraftState)} method, and even propagators that do
51   * not support resetting state (like the {@link
52   * org.orekit.propagation.analytical.tle.TLEPropagator TLEPropagator} do
53   * cache some internal data during computation. However, as long as they
54   * are configured with independent building blocks (mainly event handlers
55   * and step handlers that may preserve some internal state), and as long
56   * as they are called from one thread only, they <em>can</em> be used in
57   * multi-threaded applications. Synchronizing several propagators to run in
58   * parallel is also possible using {@link PropagatorsParallelizer}.</p>
59   * @author Luc Maisonobe
60   * @author V&eacute;ronique Pommier-Maurussane
61   *
62   */
63  
64  public interface Propagator extends PVCoordinatesProvider {
65  
66      /** Default mass. */
67      double DEFAULT_MASS = 1000.0;
68  
69      /**
70       * Get a default law using the given frames.
71       *
72       * @param frames the set of frames to use.
73       * @return attitude law.
74       */
75      static AttitudeProvider getDefaultLaw(final Frames frames) {
76          return new FrameAlignedProvider(Rotation.IDENTITY, frames.getEME2000());
77      }
78  
79      /** Get the multiplexer holding all step handlers.
80       * @return multiplexer holding all step handlers
81       * @since 11.0
82       */
83      StepHandlerMultiplexer getMultiplexer();
84  
85      /** Remove all step handlers.
86       * <p>This convenience method is equivalent to call {@code getMultiplexer().clear()}</p>
87       * @see #getMultiplexer()
88       * @see StepHandlerMultiplexer#clear()
89       * @since 11.0
90       */
91      default void clearStepHandlers() {
92          getMultiplexer().clear();
93      }
94  
95      /** Set a single handler for fixed stepsizes.
96       * <p>This convenience method is equivalent to call {@code getMultiplexer().clear()}
97       * followed by {@code getMultiplexer().add(h, handler)}</p>
98       * @param h fixed stepsize (s)
99       * @param handler handler called at the end of each finalized step
100      * @see #getMultiplexer()
101      * @see StepHandlerMultiplexer#add(double, OrekitFixedStepHandler)
102      * @since 11.0
103      */
104     default void setStepHandler(final double h, final OrekitFixedStepHandler handler) {
105         getMultiplexer().clear();
106         getMultiplexer().add(h, handler);
107     }
108 
109     /** Set a single handler for variable stepsizes.
110      * <p>This convenience method is equivalent to call {@code getMultiplexer().clear()}
111      * followed by {@code getMultiplexer().add(handler)}</p>
112      * @param handler handler called at the end of each finalized step
113      * @see #getMultiplexer()
114      * @see StepHandlerMultiplexer#add(OrekitStepHandler)
115      * @since 11.0
116      */
117     default void setStepHandler(final OrekitStepHandler handler) {
118         getMultiplexer().clear();
119         getMultiplexer().add(handler);
120     }
121 
122     /**
123      * Set up an ephemeris generator that will monitor the propagation for building
124      * an ephemeris from it once completed.
125      *
126      * <p>
127      * This generator can be used when the user needs fast random access to the orbit
128      * state at any time between the initial and target times. A typical example is the
129      * implementation of search and iterative algorithms that may navigate forward and
130      * backward inside the propagation range before finding their result even if the
131      * propagator used is integration-based and only goes from one initial time to one
132      * target time.
133      * </p>
134      * <p>
135      * Beware that when used with integration-based propagators, the generator will
136      * store <strong>all</strong> intermediate results. It is therefore memory intensive
137      * for long integration-based ranges and high precision/short time steps. When
138      * used with analytical propagators, the generator only stores start/stop time
139      * and a reference to the analytical propagator itself to call it back as needed,
140      * so it is less memory intensive.
141      * </p>
142      * <p>
143      * The returned ephemeris generator will be initially empty, it will be filled
144      * with propagation data when a subsequent call to either {@link #propagate(AbsoluteDate)
145      * propagate(target)} or {@link #propagate(AbsoluteDate, AbsoluteDate)
146      * propagate(start, target)} is called. The proper way to use this method is
147      * therefore to do:
148      * </p>
149      * <pre>
150      *   EphemerisGenerator generator = propagator.getEphemerisGenerator();
151      *   propagator.propagate(target);
152      *   BoundedPropagator ephemeris = generator.getGeneratedEphemeris();
153      * </pre>
154      * @return ephemeris generator
155      */
156     EphemerisGenerator getEphemerisGenerator();
157 
158     /** Get the propagator initial state.
159      * @return initial state
160      */
161     SpacecraftState getInitialState();
162 
163     /** Reset the propagator initial state.
164      * @param state new initial state to consider
165      */
166     void resetInitialState(SpacecraftState state);
167 
168     /** Add a set of user-specified data to be computed along with the orbit propagation.
169      * @param additionalDataProvider provider for additional data
170      */
171     void addAdditionalDataProvider(AdditionalDataProvider<?> additionalDataProvider);
172 
173     /** Get an unmodifiable list of providers for additional data.
174      * @return providers for the additional data
175      */
176     List<AdditionalDataProvider<?>> getAdditionalDataProviders();
177 
178     /** Check if an additional data is managed.
179      * <p>
180      * Managed data are the ones for which the propagators know how to compute
181      * its evolution. They correspond to additional data for which a
182      * {@link AdditionalDataProvider provider} has been registered by calling the
183      * {@link #addAdditionalDataProvider(AdditionalDataProvider) addAdditionalDataProvider} method.
184      * </p>
185      * <p>
186      * Additional data that are present in the {@link #getInitialState() initial state}
187      * but have no evolution method registered are <em>not</em> considered as managed data.
188      * These unmanaged additional data are not lost during propagation, though. Their
189      * value are piecewise constant between state resets that may change them if some
190      * event handler {@link
191      * org.orekit.propagation.events.handlers.EventHandler#resetState(EventDetector,
192      * SpacecraftState) resetState} method is called at an event occurrence and happens
193      * to change the unmanaged additional data.
194      * </p>
195      * @param name name of the additional data
196      * @return true if the additional data is managed
197      */
198     boolean isAdditionalDataManaged(String name);
199 
200     /** Get all the names of all managed additional data.
201      * @return names of all managed additional data
202      */
203     String[] getManagedAdditionalData();
204 
205     /** Add an event detector.
206      * @param detector event detector to add
207      * @see #clearEventsDetectors()
208      * @see #getEventDetectors()
209      * @param <T> class type for the generic version
210      */
211     <T extends EventDetector> void addEventDetector(T detector);
212 
213     /** Get all the events detectors that have been added.
214      * @return an unmodifiable collection of the added detectors
215      * @see #addEventDetector(EventDetector)
216      * @see #clearEventsDetectors()
217      */
218     Collection<EventDetector> getEventDetectors();
219 
220     /** Remove all events detectors.
221      * @see #addEventDetector(EventDetector)
222      * @see #getEventDetectors()
223      */
224     void clearEventsDetectors();
225 
226     /** Get attitude provider.
227      * @return attitude provider
228      */
229     AttitudeProvider getAttitudeProvider();
230 
231     /** Set attitude provider.
232      * @param attitudeProvider attitude provider
233      */
234     void setAttitudeProvider(AttitudeProvider attitudeProvider);
235 
236     /** Get the frame in which the orbit is propagated.
237      * <p>
238      * The propagation frame is the definition frame of the initial
239      * state, so this method should be called after this state has
240      * been set, otherwise it may return null.
241      * </p>
242      * @return frame in which the orbit is propagated
243      * @see #resetInitialState(SpacecraftState)
244      */
245     Frame getFrame();
246 
247     /** Set up computation of State Transition Matrix and Jacobians matrix with respect to parameters.
248      * <p>
249      * If this method is called, both State Transition Matrix and Jacobians with respect to the
250      * force models parameters that will be selected when propagation starts will be automatically
251      * computed, and the harvester will allow to retrieve them.
252      * </p>
253      * <p>
254      * The arguments for initial matrices <em>must</em> be compatible with the {@link org.orekit.orbits.OrbitType
255      * orbit type} and {@link PositionAngleType position angle} that will be used by the propagator.
256      * </p>
257      * <p>
258      * The default implementation throws an exception as the method is not supported by all propagators.
259      * </p>
260      * @param stmName State Transition Matrix state name
261      * @param initialStm initial State Transition Matrix ∂Y/∂Y₀,
262      * if null (which is the most frequent case), assumed to be 6x6 identity
263      * @param initialJacobianColumns initial columns of the Jacobians matrix with respect to parameters,
264      * if null or if some selected parameters are missing from the dictionary, the corresponding
265      * initial column is assumed to be 0
266      * @return harvester to retrieve computed matrices during and after propagation
267      * @since 11.1
268      */
269     default MatricesHarvester setupMatricesComputation(final String stmName, final RealMatrix initialStm,
270                                                        final DoubleArrayDictionary initialJacobianColumns) {
271         throw new UnsupportedOperationException();
272     }
273 
274     /** Propagate towards a target date.
275      * <p>Simple propagators use only the target date as the specification for
276      * computing the propagated state. More feature rich propagators can consider
277      * other information and provide different operating modes or G-stop
278      * facilities to stop at pinpointed events occurrences. In these cases, the
279      * target date is only a hint, not a mandatory objective.</p>
280      * @param target target date towards which orbit state should be propagated
281      * @return propagated state
282      */
283     SpacecraftState propagate(AbsoluteDate target);
284 
285     /** Propagate from a start date towards a target date.
286      * <p>Those propagators use a start date and a target date to
287      * compute the propagated state. For propagators using event detection mechanism,
288      * if the provided start date is different from the initial state date, a first,
289      * simple propagation is performed, without processing any event computation.
290      * Then complete propagation is performed from start date to target date.</p>
291      * @param start start date from which orbit state should be propagated
292      * @param target target date to which orbit state should be propagated
293      * @return propagated state
294      */
295     SpacecraftState propagate(AbsoluteDate start, AbsoluteDate target);
296 
297     /** {@inheritDoc} */
298     @Override
299     default TimeStampedPVCoordinates getPVCoordinates(AbsoluteDate date, Frame frame) {
300         return propagate(date).getPVCoordinates(frame);
301     }
302 
303     /** {@inheritDoc} */
304     @Override
305     default Vector3D getPosition(AbsoluteDate date, Frame frame) {
306         return propagate(date).getPosition(frame);
307     }
308 
309 }