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