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.integration;
18  
19  import java.util.Arrays;
20  import java.util.Collections;
21  import java.util.List;
22  import java.util.stream.Stream;
23  
24  import org.hipparchus.CalculusFieldElement;
25  import org.hipparchus.Field;
26  import org.hipparchus.ode.FieldDenseOutputModel;
27  import org.hipparchus.ode.FieldODEStateAndDerivative;
28  import org.orekit.attitudes.AttitudeProvider;
29  import org.orekit.attitudes.AttitudeProviderModifier;
30  import org.orekit.errors.OrekitException;
31  import org.orekit.errors.OrekitMessages;
32  import org.orekit.frames.Frame;
33  import org.orekit.orbits.FieldOrbit;
34  import org.orekit.propagation.FieldAdditionalDataProvider;
35  import org.orekit.propagation.FieldBoundedPropagator;
36  import org.orekit.propagation.FieldSpacecraftState;
37  import org.orekit.propagation.PropagationType;
38  import org.orekit.propagation.analytical.FieldAbstractAnalyticalPropagator;
39  import org.orekit.propagation.events.EventDetector;
40  import org.orekit.propagation.events.FieldEventDetector;
41  import org.orekit.time.FieldAbsoluteDate;
42  import org.orekit.utils.FieldDataDictionary;
43  import org.orekit.utils.ParameterDriver;
44  
45  /** This class stores sequentially generated orbital parameters for
46   * later retrieval.
47   *
48   * <p>
49   * Instances of this class are built automatically when the {@link
50   * org.orekit.propagation.FieldPropagator#getEphemerisGenerator()
51   * getEphemerisGenerator} method has been called. They are created when propagation is over.
52   * Random access to any intermediate state of the orbit throughout the propagation range is
53   * possible afterwards through this object.
54   * </p>
55   * <p>
56   * A typical use case is for numerically integrated orbits, which can be used by
57   * algorithms that need to wander around according to their own algorithm without
58   * cumbersome tight links with the integrator.
59   * </p>
60   * <p>
61   * As this class implements the {@link org.orekit.propagation.Propagator Propagator}
62   * interface, it can itself be used in batch mode to build another instance of the
63   * same type. This is however not recommended since it would be a waste of resources.
64   * </p>
65   * <p>
66   * Note that this class stores all intermediate states along with interpolation
67   * models, so it may be memory intensive.
68   * </p>
69   *
70   * @see org.orekit.propagation.numerical.NumericalPropagator
71   * @author Mathieu Rom&eacute;ro
72   * @author Luc Maisonobe
73   * @author V&eacute;ronique Pommier-Maurussane
74   * @param <T> type of the field elements
75   */
76  public class FieldIntegratedEphemeris <T extends CalculusFieldElement<T>>
77      extends FieldAbstractAnalyticalPropagator<T> implements FieldBoundedPropagator<T> {
78  
79      /** Event detection requires evaluating the state slightly before / past an event. */
80      private static final double EXTRAPOLATION_TOLERANCE = 1.0;
81  
82      /** Mapper between raw double components and spacecraft state. */
83      private final FieldStateMapper<T> mapper;
84  
85      /** Type of orbit to output (mean or osculating).
86       * <p>
87       * This is used only in the case of semianalitical propagators where there is a clear separation between
88       * mean and short periodic elements. It is ignored by the Numerical propagator.
89       * </p>
90       */
91      private final PropagationType type;
92  
93      /** Start date of the integration (can be min or max). */
94      private final FieldAbsoluteDate<T> startDate;
95  
96      /** First date of the range. */
97      private final FieldAbsoluteDate<T> minDate;
98  
99      /** Last date of the range. */
100     private final FieldAbsoluteDate<T> maxDate;
101 
102     /** Underlying raw mathematical model. */
103     private final FieldDenseOutputModel<T> model;
104 
105     /** Unmanaged additional states that must be simply copied. */
106     private final FieldDataDictionary<T> unmanaged;
107 
108     /** Names of additional equations.
109      * @since 11.2
110      */
111     private final String[] equations;
112 
113     /** Dimensions of additional equations.
114      * @since 11.2
115      */
116     private final int[] dimensions;
117 
118     /** Creates a new instance of IntegratedEphemeris.
119      * @param startDate Start date of the integration (can be minDate or maxDate)
120      * @param minDate first date of the range
121      * @param maxDate last date of the range
122      * @param mapper mapper between raw double components and spacecraft state
123      * @param attitudeProvider attitude provider
124      * @param type type of orbit to output (mean or osculating)
125      * @param model underlying raw mathematical model
126      * @param unmanaged unmanaged additional states that must be simply copied
127      * @param providers generators for pre-integrated states
128      * @param equations names of additional equations
129      * @param dimensions dimensions of additional equations
130      * @since 13.0
131      */
132     public FieldIntegratedEphemeris(final FieldAbsoluteDate<T> startDate,
133                                     final FieldAbsoluteDate<T> minDate, final FieldAbsoluteDate<T> maxDate,
134                                     final FieldStateMapper<T> mapper, final AttitudeProvider attitudeProvider,
135                                     final PropagationType type, final FieldDenseOutputModel<T> model,
136                                     final FieldDataDictionary<T> unmanaged,
137                                     final List<FieldAdditionalDataProvider<?, T>> providers,
138                                     final String[] equations, final int[] dimensions) {
139 
140         super(startDate.getField(), attitudeProvider);
141 
142         this.startDate = startDate;
143         this.minDate   = minDate;
144         this.maxDate   = maxDate;
145         this.mapper    = mapper;
146         this.type      = type;
147         this.model     = model;
148         this.unmanaged = unmanaged;
149 
150         // set up the pre-integrated providers
151         for (final FieldAdditionalDataProvider<?, T> provider : providers) {
152             addAdditionalDataProvider(provider);
153         }
154 
155         this.equations  = equations.clone();
156         this.dimensions = dimensions.clone();
157 
158         // set up initial state
159         super.resetInitialState(getInitialState());
160 
161         // remove event detectors in attitude provider
162         setAttitudeProvider(new AttitudeProviderModifier() {
163             @Override
164             public AttitudeProvider getUnderlyingAttitudeProvider() {
165                 return attitudeProvider;
166             }
167 
168             @Override
169             public Stream<EventDetector> getEventDetectors() {
170                 return Stream.of();
171             }
172 
173             @Override
174             public <W extends CalculusFieldElement<W>> Stream<FieldEventDetector<W>> getFieldEventDetectors(final Field<W> field) {
175                 return Stream.of();
176             }
177         });
178     }
179 
180     /** Interpolate the model at some date.
181      * @param date desired interpolation date
182      * @return state interpolated at date
183           * of supported range
184      */
185     private FieldODEStateAndDerivative<T> getInterpolatedState(final FieldAbsoluteDate<T> date) {
186 
187         // compare using double precision instead of FieldAbsoluteDate<T>.compareTo(...)
188         // because time is expressed as a double when searching for events
189         if (date.compareTo(minDate.shiftedBy(-EXTRAPOLATION_TOLERANCE)) < 0) {
190             // date is outside of supported range
191             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_BEFORE,
192                     date, minDate, maxDate, minDate.durationFrom(date).getReal());
193         }
194         if (date.compareTo(maxDate.shiftedBy(EXTRAPOLATION_TOLERANCE)) > 0) {
195             // date is outside of supported range
196             throw new OrekitException(OrekitMessages.OUT_OF_RANGE_EPHEMERIDES_DATE_AFTER,
197                     date, minDate, maxDate, date.durationFrom(maxDate).getReal());
198         }
199 
200         return model.getInterpolatedState(date.durationFrom(startDate));
201 
202     }
203 
204     /** {@inheritDoc} */
205     @Override
206     public FieldSpacecraftState<T> basicPropagate(final FieldAbsoluteDate<T> date) {
207         final FieldODEStateAndDerivative<T> os = getInterpolatedState(date);
208         FieldSpacecraftState<T> state = mapper.mapArrayToState(mapper.mapDoubleToDate(os.getTime(), date),
209                                                                os.getPrimaryState(), os.getPrimaryDerivative(),
210                                                                type);
211         for (FieldDataDictionary<T>.Entry initial : unmanaged.getData()) {
212             state = state.addAdditionalData(initial.getKey(), initial.getValue());
213         }
214         return state;
215     }
216 
217     /** {@inheritDoc} */
218     @Override
219     public FieldOrbit<T> propagateOrbit(final FieldAbsoluteDate<T> date,
220                                         final T[] parameters) {
221         return basicPropagate(date).getOrbit();
222     }
223 
224     /** {@inheritDoc} */
225     @Override
226     protected T getMass(final FieldAbsoluteDate<T> date) {
227         return basicPropagate(date).getMass();
228     }
229 
230     /** Get the first date of the range.
231      * @return the first date of the range
232      */
233     @Override
234     public FieldAbsoluteDate<T> getMinDate() {
235         return minDate;
236     }
237 
238     /** Get the last date of the range.
239      * @return the last date of the range
240      */
241     @Override
242     public FieldAbsoluteDate<T> getMaxDate() {
243         return maxDate;
244     }
245 
246     @Override
247     public Frame getFrame() {
248         return this.mapper.getFrame();
249     }
250 
251     /** {@inheritDoc} */
252     @Override
253     public void resetInitialState(final FieldSpacecraftState<T> state) {
254         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
255     }
256 
257     /** {@inheritDoc} */
258     @Override
259     protected void resetIntermediateState(final FieldSpacecraftState<T> state, final boolean forward) {
260         throw new OrekitException(OrekitMessages.NON_RESETABLE_STATE);
261     }
262 
263     /** {@inheritDoc} */
264     @Override
265     public FieldSpacecraftState<T> getInitialState() {
266         return updateAdditionalData(basicPropagate(getMinDate()));
267     }
268 
269     /** {@inheritDoc} */
270     @Override
271     public FieldSpacecraftState<T> updateAdditionalData(final FieldSpacecraftState<T> original) {
272 
273         FieldSpacecraftState<T> updated = super.updateAdditionalData(original);
274 
275         if (equations.length > 0) {
276             final FieldODEStateAndDerivative<T> osd                = getInterpolatedState(updated.getDate());
277             final T[]                           combinedState      = osd.getSecondaryState(1);
278             final T[]                           combinedDerivative = osd.getSecondaryDerivative(1);
279             int index = 0;
280             for (int i = 0; i < equations.length; ++i) {
281                 final T[] state      = Arrays.copyOfRange(combinedState,      index, index + dimensions[i]);
282                 final T[] derivative = Arrays.copyOfRange(combinedDerivative, index, index + dimensions[i]);
283                 updated = updated.
284                         addAdditionalData(equations[i], state).
285                           addAdditionalStateDerivative(equations[i], derivative);
286                 index += dimensions[i];
287             }
288         }
289 
290         return updated;
291 
292     }
293 
294     /** {@inheritDoc} */
295     @Override
296     public List<ParameterDriver> getParametersDrivers() {
297         // Integrated Ephemeris propagation model does not have parameter drivers.
298         return Collections.emptyList();
299     }
300 
301 }