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.semianalytical.dsst.utilities;
18
19 import java.util.ArrayList;
20
21 import org.hipparchus.Field;
22 import org.hipparchus.CalculusFieldElement;
23 import org.hipparchus.analysis.interpolation.FieldHermiteInterpolator;
24 import org.hipparchus.util.FastMath;
25 import org.orekit.time.FieldAbsoluteDate;
26
27 /** Interpolated short periodics coefficients.
28 * <p>
29 * Representation of a coefficient that need to be interpolated over time.
30 * </p><p>
31 * The short periodics coefficients can be interpolated for faster computation.
32 * This class stores computed values of the coefficients through the method
33 * {@link #addGridPoint} and gives an interpolated result through the method
34 * {@link #value}.
35 * </p>
36 * @author Nicolas Bernard
37 * @param <T> type of the field elements
38 */
39 public class FieldShortPeriodicsInterpolatedCoefficient <T extends CalculusFieldElement<T>> {
40
41 /**Values of the already computed coefficients.*/
42 private ArrayList<T[]> values;
43
44 /**Grid points.*/
45 private ArrayList<FieldAbsoluteDate<T>> abscissae;
46
47 /**Number of points used in the interpolation.*/
48 private int interpolationPoints;
49
50 /**Index of the latest closest neighbor.*/
51 private int latestClosestNeighbor;
52
53 /**Simple constructor.
54 * @param interpolationPoints number of points used in the interpolation
55 */
56 public FieldShortPeriodicsInterpolatedCoefficient(final int interpolationPoints) {
57 this.interpolationPoints = interpolationPoints;
58 this.abscissae = new ArrayList<>();
59 this.values = new ArrayList<>();
60 this.latestClosestNeighbor = 0;
61 }
62
63 /**Compute the value of the coefficient.
64 * @param date date at which the coefficient should be computed
65 * @return value of the coefficient
66 */
67 public T[] value(final FieldAbsoluteDate<T> date) {
68
69 //Field used by default
70 final Field<T> field = date.getField();
71 final T zero = field.getZero();
72
73 //Get the closest points from the input date
74 final int[] neighbors = getNeighborsIndices(date);
75
76 //Creation and set up of the interpolator
77 final FieldHermiteInterpolator<T> interpolator = new FieldHermiteInterpolator<>();
78 for (int i : neighbors) {
79 interpolator.addSamplePoint(abscissae.get(i).durationFrom(date), values.get(i));
80 }
81
82 //interpolation
83 return interpolator.value(zero);
84
85 }
86
87 /**Find the closest available points from the specified date.
88 * @param date date of interest
89 * @return indices corresponding to the closest points on the time scale
90 */
91 private int[] getNeighborsIndices(final FieldAbsoluteDate<T> date) {
92 final int sizeofNeighborhood = FastMath.min(interpolationPoints, abscissae.size());
93 final int[] neighborsIndices = new int[sizeofNeighborhood];
94
95 //If the size of the complete sample is less than
96 //the desired number of interpolation points,
97 //then the entire sample is considered as the neighborhood
98 if (interpolationPoints >= abscissae.size()) {
99 for (int i = 0; i < sizeofNeighborhood; i++) {
100 neighborsIndices[i] = i;
101 }
102 } else {
103 // get indices around closest neighbor
104 int inf = getClosestNeighbor(date);
105 int sup = inf + 1;
106
107 while (sup - inf < interpolationPoints) {
108 if (inf == 0) { //This means that we have reached the earliest date
109 sup++;
110 } else if (sup >= abscissae.size()) { //This means that we have reached the latest date
111 inf--;
112 } else { //the choice is made between the two next neighbors
113 final T lowerNeighborDistance = FastMath.abs(abscissae.get(inf - 1).durationFrom(date));
114 final T upperNeighborDistance = FastMath.abs(abscissae.get(sup).durationFrom(date));
115
116 if (lowerNeighborDistance.getReal() <= upperNeighborDistance.getReal()) {
117 inf--;
118 } else {
119 sup++;
120 }
121 }
122 }
123
124 for (int i = 0; i < interpolationPoints; ++i) {
125 neighborsIndices[i] = inf + i;
126 }
127
128 }
129
130 return neighborsIndices;
131 }
132
133 /**Find the closest point from a specific date amongst the available points.
134 * @param date date of interest
135 * @return index of the closest abscissa from the date of interest
136 */
137 private int getClosestNeighbor(final FieldAbsoluteDate<T> date) {
138 //the starting point is the latest result of a call to this method.
139 //Indeed, as this class is meant to be called during an integration process
140 //with an input date evolving often continuously in time, there is a high
141 //probability that the result will be the same as for last call of
142 //this method.
143 int closestNeighbor = latestClosestNeighbor;
144
145 //case where the date is before the available points
146 if (date.compareTo(abscissae.get(0)) <= 0) {
147 closestNeighbor = 0;
148 }
149 //case where the date is after the available points
150 else if (date.compareTo(abscissae.get(abscissae.size() - 1)) >= 0) {
151 closestNeighbor = abscissae.size() - 1;
152 }
153 //general case: one is looking for the two consecutives entries that surround the input date
154 //then one choose the closest one
155 else {
156 int lowerBorder = latestClosestNeighbor;
157 int upperBorder = latestClosestNeighbor;
158
159 final int searchDirection = date.compareTo(abscissae.get(latestClosestNeighbor));
160 if (searchDirection > 0) {
161 upperBorder++;
162 while (date.compareTo(abscissae.get(upperBorder)) > 0) {
163 upperBorder++;
164 lowerBorder++;
165 }
166 }
167 else {
168 lowerBorder--;
169 while (date.compareTo(abscissae.get(lowerBorder)) < 0) {
170 upperBorder--;
171 lowerBorder--;
172 }
173 }
174
175 final T lowerDistance = FastMath.abs(date.durationFrom(abscissae.get(lowerBorder)));
176 final T upperDistance = FastMath.abs(date.durationFrom(abscissae.get(upperBorder)));
177
178 closestNeighbor = (lowerDistance.getReal() < upperDistance.getReal()) ? lowerBorder : upperBorder;
179 }
180
181 //The result is stored in order to speed up the next call to the function
182 //Indeed, it is highly likely that the requested result will be the same
183 this.latestClosestNeighbor = closestNeighbor;
184 return closestNeighbor;
185 }
186
187 /** Clear the recorded values from the interpolation grid.
188 */
189 public void clearHistory() {
190 abscissae.clear();
191 values.clear();
192 }
193
194 /** Add a point to the interpolation grid.
195 * @param date abscissa of the point
196 * @param value value of the element
197 */
198 public void addGridPoint(final FieldAbsoluteDate<T> date, final T[] value) {
199 //If the grid is empty, the value is directly added to both arrays
200 if (abscissae.isEmpty()) {
201 abscissae.add(date);
202 values.add(value);
203 }
204 //If the grid already contains this point, only its value is changed
205 else if (abscissae.contains(date)) {
206 values.set(abscissae.indexOf(date), value);
207 }
208 //If the grid does not contain this point, the position of the point
209 //in the grid is computed first
210 else {
211 final int closestNeighbor = getClosestNeighbor(date);
212 final int index = (date.compareTo(abscissae.get(closestNeighbor)) < 0) ? closestNeighbor : closestNeighbor + 1;
213 abscissae.add(index, date);
214 values.add(index, value);
215 }
216 }
217
218 }