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