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.DoubleArrayDictionary;
38 import org.orekit.utils.ParameterDriver;
39 import org.orekit.utils.TimeSpanMap;
40
41 import java.util.HashMap;
42 import java.util.List;
43 import java.util.Map;
44
45
46
47
48
49
50
51 abstract class AbstractStateTransitionMatrixGenerator implements AdditionalDerivativesProvider {
52
53
54 protected static final int SPACE_DIMENSION = 3;
55
56
57 private static final double THRESHOLD = Precision.SAFE_MIN;
58
59
60 private final String stmName;
61
62
63 private final List<ForceModel> forceModels;
64
65
66 private final AttitudeProvider attitudeProvider;
67
68
69 private final Map<String, PartialsObserver> partialsObservers;
70
71
72 private final int stateDimension;
73
74
75 private final int dimension;
76
77
78
79
80
81
82
83 AbstractStateTransitionMatrixGenerator(final String stmName, final List<ForceModel> forceModels,
84 final AttitudeProvider attitudeProvider, final int stateDimension) {
85 this.stmName = stmName;
86 this.forceModels = forceModels;
87 this.attitudeProvider = attitudeProvider;
88 this.stateDimension = stateDimension;
89 this.dimension = stateDimension * stateDimension;
90 this.partialsObservers = new HashMap<>();
91 }
92
93
94
95
96
97
98
99
100
101
102 void addObserver(final String name, final PartialsObserver observer) {
103 partialsObservers.put(name, observer);
104 }
105
106
107 @Override
108 public String getName() {
109 return stmName;
110 }
111
112
113 @Override
114 public int getDimension() {
115 return dimension;
116 }
117
118
119
120
121
122 public int getStateDimension() {
123 return stateDimension;
124 }
125
126
127
128
129
130 protected List<ForceModel> getForceModels() {
131 return forceModels;
132 }
133
134
135
136
137
138 protected Map<String, PartialsObserver> getPartialsObservers() {
139 return partialsObservers;
140 }
141
142
143
144
145
146
147 private DecompositionSolver getDecompositionSolver(final RealMatrix matrix) {
148 return new QRDecomposition(matrix, THRESHOLD).getSolver();
149 }
150
151
152
153
154
155
156
157
158
159
160
161
162 SpacecraftState setInitialStateTransitionMatrix(final SpacecraftState state, final RealMatrix dYdY0,
163 final OrbitType orbitType,
164 final PositionAngleType positionAngleType) {
165
166 final RealMatrix nonNullDYdY0;
167 if (dYdY0 == null) {
168 nonNullDYdY0 = MatrixUtils.createRealIdentityMatrix(getStateDimension());
169 } else {
170 if (dYdY0.getRowDimension() != getStateDimension() ||
171 dYdY0.getColumnDimension() != getStateDimension()) {
172 throw new OrekitException(LocalizedCoreFormats.DIMENSIONS_MISMATCH_2x2,
173 dYdY0.getRowDimension(), dYdY0.getColumnDimension(),
174 getStateDimension(), getStateDimension());
175 }
176 nonNullDYdY0 = dYdY0;
177 }
178
179
180 final RealMatrix dCdY0;
181 if (state.isOrbitDefined()) {
182 final RealMatrix dYdC = MatrixUtils.createRealIdentityMatrix(getStateDimension());
183 final Orbit orbit = orbitType.convertType(state.getOrbit());
184 final double[][] jacobian = new double[6][6];
185 orbit.getJacobianWrtCartesian(positionAngleType, jacobian);
186 dYdC.setSubMatrix(jacobian, 0, 0);
187 final DecompositionSolver decomposition = getDecompositionSolver(dYdC);
188 dCdY0 = decomposition.solve(nonNullDYdY0);
189 } else {
190 dCdY0 = nonNullDYdY0;
191 }
192
193
194 return state.addAdditionalData(getName(), flatten(dCdY0));
195
196 }
197
198
199
200
201
202
203 double[] flatten(final RealMatrix matrix) {
204 final double[] flat = new double[getDimension()];
205 int k = 0;
206 for (int i = 0; i < getStateDimension(); ++i) {
207 for (int j = 0; j < getStateDimension(); ++j) {
208 flat[k++] = matrix.getEntry(i, j);
209 }
210 }
211 return flat;
212 }
213
214
215 @Override
216 public boolean yields(final SpacecraftState state) {
217 return !state.hasAdditionalData(getName());
218 }
219
220
221 public CombinedDerivatives combinedDerivatives(final SpacecraftState state) {
222 final double[] factor = computePartials(state);
223
224
225 final double[] p = state.getAdditionalState(getName());
226 final double[] pDot = new double[p.length];
227
228
229 multiplyMatrix(factor, p, pDot, getStateDimension());
230
231 return new CombinedDerivatives(pDot, null);
232
233 }
234
235
236
237
238
239
240
241 abstract void multiplyMatrix(double[] factor, double[] x, double[] y, int columns);
242
243
244
245
246
247 double[] computePartials(final SpacecraftState state) {
248
249
250 final double[] factor = new double[(stateDimension - SPACE_DIMENSION) * stateDimension];
251 final DoubleArrayDictionary partialsDictionary = new DoubleArrayDictionary();
252
253
254 final AttitudeProvider equivalentAttitudeProvider = wrapAttitudeProviderIfPossible();
255 final boolean isThereAnyForceNotDependingOnlyOnPosition = getForceModels().stream().anyMatch(force -> !force.dependsOnPositionOnly());
256 final NumericalGradientConverter posOnlyConverter = new NumericalGradientConverter(state, SPACE_DIMENSION, equivalentAttitudeProvider);
257 final NumericalGradientConverter fullConverter = isThereAnyForceNotDependingOnlyOnPosition ?
258 new NumericalGradientConverter(state, getStateDimension(), equivalentAttitudeProvider) : posOnlyConverter;
259
260 for (final ForceModel forceModel : getForceModels()) {
261
262 final NumericalGradientConverter converter = forceModel.dependsOnPositionOnly() ? posOnlyConverter : fullConverter;
263 final FieldSpacecraftState<Gradient> dsState = converter.getState(forceModel);
264 final Gradient[] parameters = converter.getParametersAtStateDate(dsState, forceModel);
265
266
267 final Gradient[] ratesPartials = computeRatesPartialsAndUpdateFactor(forceModel, dsState, parameters, factor);
268
269
270 updateFactorForParameters(forceModel, converter, ratesPartials, partialsDictionary, state, factor);
271
272 }
273
274 return factor;
275
276 }
277
278
279
280
281
282
283
284
285
286
287 abstract Gradient[] computeRatesPartialsAndUpdateFactor(ForceModel forceModel,
288 FieldSpacecraftState<Gradient> fieldState,
289 Gradient[] parameters, double[] factor);
290
291
292
293
294
295
296
297
298
299
300 private void updateFactorForParameters(final ForceModel forceModel, final NumericalGradientConverter converter,
301 final Gradient[] ratesPartials, final DoubleArrayDictionary partialsDictionary,
302 final SpacecraftState state, final double[] factor) {
303 int paramsIndex = converter.getFreeStateParameters();
304 for (ParameterDriver driver : forceModel.getParametersDrivers()) {
305 if (driver.isSelected()) {
306
307
308 for (TimeSpanMap.Span<String> span = driver.getNamesSpanMap().getFirstSpan(); span != null; span = span.next()) {
309 updateDictionaryEntry(partialsDictionary, span, ratesPartials, paramsIndex);
310 ++paramsIndex;
311 }
312 }
313 }
314
315
316 for (Map.Entry<String, PartialsObserver> observersEntry : getPartialsObservers().entrySet()) {
317 final DoubleArrayDictionary.Entry entry = partialsDictionary.getEntry(observersEntry.getKey());
318 observersEntry.getValue().partialsComputed(state, factor, entry == null ? new double[ratesPartials.length] : entry.getValue());
319 }
320 }
321
322
323
324
325
326
327
328
329 private void updateDictionaryEntry(final DoubleArrayDictionary partialsDictionary, final TimeSpanMap.Span<String> span,
330 final Gradient[] ratesPartials, final int paramsIndex) {
331
332 DoubleArrayDictionary.Entry entry = partialsDictionary.getEntry(span.getData());
333 if (entry == null) {
334
335 partialsDictionary.put(span.getData(), new double[ratesPartials.length]);
336 entry = partialsDictionary.getEntry(span.getData());
337 }
338
339
340 final double[] increment = new double[ratesPartials.length];
341 for (int i = 0; i < ratesPartials.length; ++i) {
342 increment[i] = ratesPartials[i].getGradient()[paramsIndex];
343 }
344 entry.increment(increment);
345 }
346
347
348
349
350
351
352
353
354 AttitudeProvider wrapAttitudeProviderIfPossible() {
355 if (forceModels.stream().anyMatch(ForceModel::dependsOnAttitudeRate)) {
356
357 return attitudeProvider;
358 } else {
359
360 return AttitudeProviderModifier.getFrozenAttitudeProvider(attitudeProvider);
361 }
362 }
363
364
365 @FunctionalInterface
366 public interface PartialsObserver {
367
368
369
370
371
372
373
374 void partialsComputed(SpacecraftState state, double[] factor, double[] partials);
375
376 }
377
378 }
379