1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.propagation.numerical;
18
19
20 import org.hipparchus.analysis.differentiation.Gradient;
21 import org.hipparchus.exception.LocalizedCoreFormats;
22 import org.hipparchus.linear.DecompositionSolver;
23 import org.hipparchus.linear.MatrixUtils;
24 import org.hipparchus.linear.QRDecomposition;
25 import org.hipparchus.linear.RealMatrix;
26 import org.hipparchus.util.Precision;
27 import org.orekit.attitudes.AttitudeProvider;
28 import org.orekit.attitudes.AttitudeProviderModifier;
29 import org.orekit.errors.OrekitException;
30 import org.orekit.forces.ForceModel;
31 import org.orekit.orbits.Orbit;
32 import org.orekit.orbits.OrbitParamsType;
33 import org.orekit.orbits.PositionAngleType;
34 import org.orekit.propagation.FieldSpacecraftState;
35 import org.orekit.propagation.SpacecraftState;
36 import org.orekit.propagation.integration.AdditionalDerivativesProvider;
37 import org.orekit.propagation.integration.CombinedDerivatives;
38 import org.orekit.utils.DataDictionary;
39 import org.orekit.utils.drivers.ParameterDriver;
40
41 import java.io.IOException;
42 import java.io.ObjectInputStream;
43 import java.io.Serial;
44 import java.util.HashMap;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.Objects;
48
49
50
51
52
53
54
55 abstract class AbstractStateTransitionMatrixGenerator implements AdditionalDerivativesProvider {
56
57
58 protected static final int SPACE_DIMENSION = 3;
59
60
61 private static final double THRESHOLD = Precision.SAFE_MIN;
62
63
64 private final String stmName;
65
66
67 private final List<ForceModel> forceModels;
68
69
70 private final AttitudeProvider attitudeProvider;
71
72
73 private final Map<String, PartialsObserver> partialsObservers;
74
75
76 private final int stateDimension;
77
78
79 private final int dimension;
80
81
82
83
84
85
86
87 AbstractStateTransitionMatrixGenerator(final String stmName, final List<ForceModel> forceModels,
88 final AttitudeProvider attitudeProvider, final int stateDimension) {
89 this.stmName = stmName;
90 this.forceModels = forceModels;
91 this.attitudeProvider = attitudeProvider;
92 this.stateDimension = stateDimension;
93 this.dimension = stateDimension * stateDimension;
94 this.partialsObservers = new HashMap<>();
95 }
96
97
98
99
100
101
102
103
104
105
106 void addObserver(final String name, final PartialsObserver observer) {
107 partialsObservers.put(name, observer);
108 }
109
110
111 @Override
112 public String getName() {
113 return stmName;
114 }
115
116
117 @Override
118 public int getDimension() {
119 return dimension;
120 }
121
122
123
124
125
126 public int getStateDimension() {
127 return stateDimension;
128 }
129
130
131
132
133
134 protected List<ForceModel> getForceModels() {
135 return forceModels;
136 }
137
138
139
140
141
142 protected Map<String, PartialsObserver> getPartialsObservers() {
143 return partialsObservers;
144 }
145
146
147
148
149
150
151 private DecompositionSolver getDecompositionSolver(final RealMatrix matrix) {
152 return new QRDecomposition(matrix, THRESHOLD).getSolver();
153 }
154
155
156
157
158
159
160
161
162
163
164
165
166 SpacecraftState setInitialStateTransitionMatrix(final SpacecraftState state, final RealMatrix dYdY0,
167 final OrbitParamsType orbitParamsType,
168 final PositionAngleType positionAngleType) {
169
170 final RealMatrix nonNullDYdY0;
171 if (dYdY0 == null) {
172 nonNullDYdY0 = MatrixUtils.createRealIdentityMatrix(getStateDimension());
173 } else {
174 if (dYdY0.getRowDimension() != getStateDimension() ||
175 dYdY0.getColumnDimension() != getStateDimension()) {
176 throw new OrekitException(LocalizedCoreFormats.DIMENSIONS_MISMATCH_2x2,
177 dYdY0.getRowDimension(), dYdY0.getColumnDimension(),
178 getStateDimension(), getStateDimension());
179 }
180 nonNullDYdY0 = dYdY0;
181 }
182
183
184 final RealMatrix dCdY0;
185 if (state.isOrbitDefined()) {
186 final RealMatrix dYdC = MatrixUtils.createRealIdentityMatrix(getStateDimension());
187 final Orbit orbit = orbitParamsType.convertType(state.getOrbit());
188 final double[][] jacobian = new double[6][6];
189 orbit.getJacobianWrtCartesian(positionAngleType, jacobian);
190 dYdC.setSubMatrix(jacobian, 0, 0);
191 final DecompositionSolver decomposition = getDecompositionSolver(dYdC);
192 dCdY0 = decomposition.solve(nonNullDYdY0);
193 } else {
194 dCdY0 = nonNullDYdY0;
195 }
196
197
198 return state.addAdditionalData(getName(), flatten(dCdY0));
199
200 }
201
202
203
204
205
206
207 double[] flatten(final RealMatrix matrix) {
208 final double[] flat = new double[getDimension()];
209 int k = 0;
210 for (int i = 0; i < getStateDimension(); ++i) {
211 for (int j = 0; j < getStateDimension(); ++j) {
212 flat[k++] = matrix.getEntry(i, j);
213 }
214 }
215 return flat;
216 }
217
218
219 @Override
220 public boolean yields(final SpacecraftState state) {
221 return !state.hasAdditionalData(getName());
222 }
223
224
225 public CombinedDerivatives combinedDerivatives(final SpacecraftState state) {
226 final double[] factor = computePartials(state);
227
228
229 final double[] p = state.getAdditionalState(getName());
230 final double[] pDot = new double[p.length];
231
232
233 multiplyMatrix(factor, p, pDot, getStateDimension());
234
235 return new CombinedDerivatives(pDot, null);
236
237 }
238
239
240
241
242
243
244
245 abstract void multiplyMatrix(double[] factor, double[] x, double[] y, int columns);
246
247
248
249
250
251 double[] computePartials(final SpacecraftState state) {
252
253
254 final double[] factor = new double[(stateDimension - SPACE_DIMENSION) * stateDimension];
255 final Map<String, double[]> partialsDictionary = new HashMap<>();
256
257
258 final AttitudeProvider equivalentAttitudeProvider = wrapAttitudeProviderIfPossible();
259 final NumericalGradientConverter posOnlyConverter = new NumericalGradientConverter(state, SPACE_DIMENSION, equivalentAttitudeProvider);
260 final NumericalGradientConverter fullConverter = buildFullConverter(state, equivalentAttitudeProvider, posOnlyConverter);
261 final SpacecraftState stateForParameters = state.withAdditionalData(new LocalDoubleArrayDictionary(state.getAdditionalDataValues()));
262
263 for (final ForceModel forceModel : getForceModels()) {
264
265 final NumericalGradientConverter converter = forceModel.dependsOnPositionOnly() ? posOnlyConverter : fullConverter;
266 final FieldSpacecraftState<Gradient> dsState = converter.getState(forceModel);
267 final Gradient[] parameters = converter.getParametersAtStateDate(dsState, forceModel);
268
269
270 final Gradient[] ratesPartials = computeRatesPartialsAndUpdateFactor(forceModel, dsState, parameters, factor);
271
272
273 updateFactorForParameters(forceModel, converter, ratesPartials, partialsDictionary, stateForParameters, factor);
274
275 }
276
277 return factor;
278
279 }
280
281
282
283
284
285
286
287
288
289 private NumericalGradientConverter buildFullConverter(final SpacecraftState state, final AttitudeProvider provider,
290 final NumericalGradientConverter positionOnlyConverter) {
291 if (getForceModels().stream().allMatch(ForceModel::dependsOnPositionOnly)) {
292 return positionOnlyConverter;
293 }
294
295 final boolean keepAdditionalData = state.getAdditionalDataValues().getData().stream().anyMatch(data -> !Objects.equals(data.getKey(), getName()));
296 return new NumericalGradientConverter(state, getStateDimension(), provider, keepAdditionalData);
297 }
298
299
300
301
302
303
304
305
306
307
308 abstract Gradient[] computeRatesPartialsAndUpdateFactor(ForceModel forceModel,
309 FieldSpacecraftState<Gradient> fieldState,
310 Gradient[] parameters, double[] factor);
311
312
313
314
315
316
317
318
319
320
321 private void updateFactorForParameters(final ForceModel forceModel, final NumericalGradientConverter converter,
322 final Gradient[] ratesPartials, final Map<String, double[]> partialsDictionary,
323 final SpacecraftState state, final double[] factor) {
324 int paramsIndex = converter.getFreeStateParameters();
325 for (ParameterDriver driver : forceModel.getParametersDrivers()) {
326 if (driver.isSelected()) {
327
328 updateDictionaryEntry(partialsDictionary, driver.getName(), ratesPartials, paramsIndex);
329 ++paramsIndex;
330 }
331 }
332
333
334 for (Map.Entry<String, PartialsObserver> observersEntry : getPartialsObservers().entrySet()) {
335 observersEntry.getValue().partialsComputed(state, factor,
336 partialsDictionary.getOrDefault(observersEntry.getKey(), new double[ratesPartials.length]));
337 }
338 }
339
340
341
342
343
344
345
346
347 private void updateDictionaryEntry(final Map<String, double[]> partialsDictionary, final String name,
348 final Gradient[] ratesPartials, final int paramsIndex) {
349
350 partialsDictionary.putIfAbsent(name, new double[ratesPartials.length]);
351
352
353 final double[] increment = partialsDictionary.get(name);
354 for (int i = 0; i < ratesPartials.length; ++i) {
355 increment[i] += ratesPartials[i].getGradient()[paramsIndex];
356 }
357 partialsDictionary.replace(name, increment);
358 }
359
360
361
362
363
364
365
366
367 AttitudeProvider wrapAttitudeProviderIfPossible() {
368 if (forceModels.stream().anyMatch(ForceModel::dependsOnAttitudeRate)) {
369
370 return attitudeProvider;
371 } else {
372
373 return AttitudeProviderModifier.getFrozenAttitudeProvider(attitudeProvider);
374 }
375 }
376
377
378 @FunctionalInterface
379 public interface PartialsObserver {
380
381
382
383
384
385
386
387 void partialsComputed(SpacecraftState state, double[] factor, double[] partials);
388
389 }
390
391
392
393
394 private static class LocalDoubleArrayDictionary extends DataDictionary {
395
396
397 @Serial
398 private static final long serialVersionUID = 1L;
399
400
401 private transient Map<String, Object> objectMap;
402
403
404
405
406
407 LocalDoubleArrayDictionary(final DataDictionary inputDictionary) {
408 super(inputDictionary);
409 objectMap = toMap();
410 }
411
412
413
414
415
416
417
418
419 @Serial
420 private void readObject(final ObjectInputStream ois) throws IOException, ClassNotFoundException {
421 ois.defaultReadObject();
422 objectMap = toMap();
423 }
424
425 @Override
426 public Object get(final String key) {
427 return objectMap.get(key);
428 }
429 }
430 }
431