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.time;
18
19 import org.hipparchus.CalculusFieldElement;
20 import org.hipparchus.Field;
21 import org.hipparchus.util.FastMath;
22 import org.orekit.errors.OrekitIllegalArgumentException;
23 import org.orekit.errors.OrekitInternalError;
24 import org.orekit.errors.OrekitMessages;
25 import org.orekit.utils.ImmutableFieldTimeStampedCache;
26
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.List;
31 import java.util.Optional;
32 import java.util.stream.Collectors;
33 import java.util.stream.Stream;
34
35 /**
36 * Abstract class for time interpolator.
37 *
38 * @param <T> interpolated time stamped type
39 * @param <KK> type of the field element
40 *
41 * @author Vincent Cucchietti
42 */
43 public abstract class AbstractFieldTimeInterpolator<T extends FieldTimeStamped<KK>, KK extends CalculusFieldElement<KK>>
44 implements FieldTimeInterpolator<T, KK> {
45
46 /** Default extrapolation time threshold: 1ms. */
47 public static final double DEFAULT_EXTRAPOLATION_THRESHOLD_SEC = 1e-3;
48
49 /** Default number of interpolation points. */
50 public static final int DEFAULT_INTERPOLATION_POINTS = 2;
51
52 /** The extrapolation threshold beyond which the propagation will fail. */
53 private final double extrapolationThreshold;
54
55 /** Neighbor size. */
56 private final int interpolationPoints;
57
58 /**
59 * Constructor.
60 *
61 * @param interpolationPoints number of interpolation points
62 * @param extrapolationThreshold extrapolation threshold beyond which the propagation will fail
63 */
64 public AbstractFieldTimeInterpolator(final int interpolationPoints, final double extrapolationThreshold) {
65 this.interpolationPoints = interpolationPoints;
66 this.extrapolationThreshold = extrapolationThreshold;
67 }
68
69 /**
70 * Method checking if given interpolator is compatible with given sample size.
71 *
72 * @param interpolator interpolator
73 * @param sampleSize sample size
74 * @param <T> type of the field elements
75 */
76 public static <T extends CalculusFieldElement<T>> void checkInterpolatorCompatibilityWithSampleSize(
77 final FieldTimeInterpolator<? extends FieldTimeStamped<T>, T> interpolator,
78 final int sampleSize) {
79
80 // Retrieve all sub-interpolators (or a singleton list with given interpolator if there are no sub-interpolators)
81 final List<FieldTimeInterpolator<? extends FieldTimeStamped<T>, T>> subInterpolators =
82 interpolator.getSubInterpolators();
83 for (final FieldTimeInterpolator<? extends FieldTimeStamped<T>, T> subInterpolator : subInterpolators) {
84 if (sampleSize < subInterpolator.getNbInterpolationPoints()) {
85 throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA, sampleSize);
86 }
87 }
88 }
89
90 /** {@inheritDoc} */
91 @Override
92 public T interpolate(final FieldAbsoluteDate<KK> interpolationDate, final Stream<T> sample) {
93 return interpolate(interpolationDate, sample.collect(Collectors.toList()));
94 }
95
96 /** {@inheritDoc}. */
97 @Override
98 public T interpolate(final FieldAbsoluteDate<KK> interpolationDate, final Collection<T> sample) {
99 final InterpolationData interpolationData = new InterpolationData(interpolationDate, sample);
100 return interpolate(interpolationData);
101 }
102
103 /**
104 * Get the central date to use to find neighbors while taking into account extrapolation threshold.
105 *
106 * @param date interpolation date
107 * @param cachedSamples cached samples
108 * @param threshold extrapolation threshold
109 * @param <T> type of time stamped element
110 * @param <KK> type of calculus field element
111 *
112 * @return central date to use to find neighbors
113 * @since 12.0.1
114 */
115 public static <T extends FieldTimeStamped<KK>, KK extends CalculusFieldElement<KK>> FieldAbsoluteDate<KK> getCentralDate(
116 final FieldAbsoluteDate<KK> date,
117 final ImmutableFieldTimeStampedCache<T, KK> cachedSamples,
118 final double threshold) {
119 return getCentralDate(
120 date,
121 cachedSamples.getEarliest().getDate(),
122 cachedSamples.getLatest().getDate(),
123 threshold);
124 }
125
126 /**
127 * Get the central date to use to find neighbors while taking into account extrapolation threshold.
128 *
129 * @param date interpolation date
130 * @param minDate earliest date in the sample.
131 * @param maxDate latest date in the sample.
132 * @param threshold extrapolation threshold
133 * @param <KK> type of calculus field element
134 *
135 * @return central date to use to find neighbors
136 * @since 12.0.1
137 */
138 public static <KK extends CalculusFieldElement<KK>> FieldAbsoluteDate<KK> getCentralDate(
139 final FieldAbsoluteDate<KK> date,
140 final FieldAbsoluteDate<KK> minDate,
141 final FieldAbsoluteDate<KK> maxDate,
142 final double threshold) {
143 final FieldAbsoluteDate<KK> central;
144
145 if (date.compareTo(minDate) < 0 && FastMath.abs(date.durationFrom(minDate)).getReal() <= threshold) {
146 // avoid TimeStampedCacheException as we are still within the tolerance before minDate
147 central = minDate;
148 } else if (date.compareTo(maxDate) > 0 && FastMath.abs(date.durationFrom(maxDate)).getReal() <= threshold) {
149 // avoid TimeStampedCacheException as we are still within the tolerance after maxDate
150 central = maxDate;
151 } else {
152 central = date;
153 }
154
155 return central;
156 }
157
158 /** {@inheritDoc} */
159 public List<FieldTimeInterpolator<? extends FieldTimeStamped<KK>, KK>> getSubInterpolators() {
160 return Collections.singletonList(this);
161 }
162
163 /** {@inheritDoc} */
164 public int getNbInterpolationPoints() {
165 final List<FieldTimeInterpolator<? extends FieldTimeStamped<KK>, KK>> subInterpolators = getSubInterpolators();
166 // In case the interpolator does not have sub interpolators
167 if (subInterpolators.size() == 1) {
168 return interpolationPoints;
169 }
170 // Otherwise find maximum number of interpolation points among sub interpolators
171 else {
172 final Optional<Integer> optionalMaxNbInterpolationPoints =
173 subInterpolators.stream().map(FieldTimeInterpolator::getNbInterpolationPoints).max(Integer::compareTo);
174 if (optionalMaxNbInterpolationPoints.isPresent()) {
175 return optionalMaxNbInterpolationPoints.get();
176 } else {
177 // This should never happen
178 throw new OrekitInternalError(null);
179 }
180 }
181 }
182
183 /** {@inheritDoc} */
184 public double getExtrapolationThreshold() {
185 return extrapolationThreshold;
186 }
187
188 /**
189 * Add all lowest level sub interpolators to the sub interpolator list.
190 *
191 * @param subInterpolator optional sub interpolator to add
192 * @param subInterpolators list of sub interpolators
193 * @param <S> type of the field element
194 */
195 protected <S extends CalculusFieldElement<S>> void addOptionalSubInterpolatorIfDefined(
196 final FieldTimeInterpolator<? extends FieldTimeStamped<S>, S> subInterpolator,
197 final List<FieldTimeInterpolator<? extends FieldTimeStamped<S>, S>> subInterpolators) {
198 // Add all lowest level sub interpolators
199 if (subInterpolator != null) {
200 subInterpolators.addAll(subInterpolator.getSubInterpolators());
201 }
202 }
203
204 /**
205 * Interpolate instance from given interpolation data.
206 *
207 * @param interpolationData interpolation data
208 *
209 * @return interpolated instance from given interpolation data.
210 */
211 protected abstract T interpolate(InterpolationData interpolationData);
212
213 /**
214 * Get the time parameter which lies between [0:1] by normalizing the difference between interpolating time and previous
215 * date by the Δt between tabulated values.
216 *
217 * @param interpolatingTime time at which we want to interpolate a value (between previous and next tabulated dates)
218 * @param previousDate previous tabulated value date
219 * @param nextDate next tabulated value date
220 *
221 * @return time parameter which lies between [0:1]
222 */
223 protected KK getTimeParameter(final FieldAbsoluteDate<KK> interpolatingTime,
224 final FieldAbsoluteDate<KK> previousDate,
225 final FieldAbsoluteDate<KK> nextDate) {
226
227 return interpolatingTime.durationFrom(previousDate).divide(nextDate.getDate().durationFrom(previousDate));
228 }
229
230 /**
231 * Nested class used to store interpolation data.
232 * <p>
233 * It makes the interpolator thread safe.
234 */
235 public class InterpolationData {
236
237 /** Interpolation date. */
238 private final FieldAbsoluteDate<KK> interpolationDate;
239
240 /** Unmodifiable list of neighbors. */
241 private final List<T> neighborList;
242
243 /** Field of the element. */
244 private final Field<KK> field;
245
246 /** Fielded zero. */
247 private final KK zero;
248
249 /** Fielded one. */
250 private final KK one;
251
252 /**
253 * Constructor.
254 *
255 * @param interpolationDate interpolation date
256 * @param sample time stamped sample
257 */
258 protected InterpolationData(final FieldAbsoluteDate<KK> interpolationDate, final Collection<T> sample) {
259 // Handle specific case that is not handled by the immutable time stamped cache constructor
260 if (sample.isEmpty()) {
261 throw new OrekitIllegalArgumentException(OrekitMessages.NOT_ENOUGH_DATA, 0);
262 }
263
264 // TODO performance: create neighborsList without copying sample.
265 if (sample.size() == interpolationPoints) {
266 // shortcut for simple case
267 // copy list to make neighborList immutable
268 this.neighborList = Collections.unmodifiableList(new ArrayList<>(sample));
269 } else {
270 // else, select sample.
271
272 // Create immutable time stamped cache
273 final ImmutableFieldTimeStampedCache<T, KK> cachedSamples =
274 new ImmutableFieldTimeStampedCache<>(interpolationPoints, sample);
275
276 // Find neighbors
277 final FieldAbsoluteDate<KK> central =
278 AbstractFieldTimeInterpolator.getCentralDate(
279 interpolationDate,
280 cachedSamples,
281 extrapolationThreshold);
282 final Stream<T> neighborsStream = cachedSamples.getNeighbors(central);
283
284 // Convert to unmodifiable list
285 neighborList = Collections.unmodifiableList(neighborsStream.collect(Collectors.toList()));
286 }
287
288 // Extract field and useful terms
289 this.field = interpolationDate.getField();
290 this.zero = field.getZero();
291 this.one = field.getOne();
292
293 // Store interpolation date
294 this.interpolationDate = interpolationDate;
295 }
296
297 /**
298 * Get the central date to use to find neighbors while taking into account extrapolation threshold.
299 *
300 * @param date interpolation date
301 *
302 * @return central date to use to find neighbors
303 *
304 * @deprecated This method appears to be unused and may be removed in Orekit 13.0.
305 * Please Comment on forum.orekit.org if you have a use case for this method.
306 */
307 @Deprecated
308 protected FieldAbsoluteDate<KK> getCentralDate(final FieldAbsoluteDate<KK> date) {
309 return AbstractFieldTimeInterpolator.getCentralDate(
310 date,
311 neighborList.get(0).getDate(),
312 neighborList.get(neighborList.size() - 1).getDate(),
313 extrapolationThreshold);
314 }
315
316 /** Get interpolation date.
317 * @return interpolation date
318 */
319 public FieldAbsoluteDate<KK> getInterpolationDate() {
320 return interpolationDate;
321 }
322
323 /** Get cached samples.
324 * @return cached samples
325 *
326 * @deprecated This method appears to be unused and may be removed in Orekit 13.0.
327 * Please Comment on forum.orekit.org if you have a use case for this method.
328 */
329 @Deprecated
330 public ImmutableFieldTimeStampedCache<T, KK> getCachedSamples() {
331 return new ImmutableFieldTimeStampedCache<>(
332 interpolationPoints,
333 getNeighborList());
334 }
335
336 /** Get neighbor list.
337 * @return neighbor list
338 */
339 public List<T> getNeighborList() {
340 return neighborList;
341 }
342
343 /** Get field.
344 * @return field
345 */
346 public Field<KK> getField() {
347 return field;
348 }
349
350 /** Get zero.
351 * @return zero
352 */
353 public KK getZero() {
354 return zero;
355 }
356
357 /** Get one.
358 * @return one
359 */
360 public KK getOne() {
361 return one;
362 }
363 }
364 }