1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.orekit.control.indirect.shooting;
18
19 import org.hipparchus.CalculusFieldElement;
20 import org.hipparchus.Field;
21 import org.hipparchus.analysis.differentiation.Gradient;
22 import org.hipparchus.analysis.differentiation.GradientField;
23 import org.hipparchus.geometry.euclidean.threed.FieldVector3D;
24 import org.hipparchus.linear.LUDecomposition;
25 import org.hipparchus.linear.MatrixUtils;
26 import org.hipparchus.linear.RealMatrix;
27 import org.hipparchus.ode.FieldOrdinaryDifferentialEquation;
28 import org.hipparchus.ode.nonstiff.FieldExplicitRungeKuttaIntegrator;
29 import org.hipparchus.util.FastMath;
30 import org.hipparchus.util.MathArrays;
31 import org.orekit.attitudes.FieldAttitude;
32 import org.orekit.control.indirect.adjoint.CartesianAdjointDerivativesProvider;
33 import org.orekit.control.indirect.adjoint.FieldCartesianAdjointDerivativesProvider;
34 import org.orekit.control.indirect.adjoint.cost.ControlSwitchDetector;
35 import org.orekit.control.indirect.adjoint.cost.FieldControlSwitchDetector;
36 import org.orekit.control.indirect.shooting.propagation.AdjointDynamicsProvider;
37 import org.orekit.control.indirect.shooting.propagation.ShootingPropagationSettings;
38 import org.orekit.forces.ForceModel;
39 import org.orekit.frames.Frame;
40 import org.orekit.orbits.FieldCartesianOrbit;
41 import org.orekit.propagation.FieldSpacecraftState;
42 import org.orekit.propagation.SpacecraftState;
43 import org.orekit.propagation.events.DateDetector;
44 import org.orekit.propagation.events.EventsLogger;
45 import org.orekit.propagation.events.FieldEventDetector;
46 import org.orekit.propagation.integration.FieldAdditionalDerivativesProvider;
47 import org.orekit.propagation.integration.FieldCombinedDerivatives;
48 import org.orekit.propagation.numerical.NumericalPropagator;
49 import org.orekit.propagation.sampling.PropagationStepRecorder;
50 import org.orekit.time.AbsoluteDate;
51 import org.orekit.time.FieldAbsoluteDate;
52 import org.orekit.utils.FieldAbsolutePVCoordinates;
53 import org.orekit.utils.FieldPVCoordinates;
54
55 import java.util.Arrays;
56 import java.util.List;
57 import java.util.stream.Collectors;
58
59
60
61
62
63
64
65
66
67
68 public abstract class AbstractFixedInitialCartesianSingleShooting extends AbstractIndirectShooting {
69
70
71 private final SpacecraftState initialSpacecraftStateTemplate;
72
73
74
75
76 private final PropagationStepRecorder propagationStepRecorder;
77
78
79 private final EventsLogger eventsLogger;
80
81
82 private double[] scales;
83
84
85
86
87
88
89 protected AbstractFixedInitialCartesianSingleShooting(final ShootingPropagationSettings propagationSettings,
90 final SpacecraftState initialSpacecraftStateTemplate) {
91 super(propagationSettings);
92 this.initialSpacecraftStateTemplate = initialSpacecraftStateTemplate;
93 this.propagationStepRecorder = new PropagationStepRecorder();
94 this.eventsLogger = new EventsLogger();
95 }
96
97
98
99
100
101 private double[] getUnitScales() {
102 final double[] unitScales = new double[getPropagationSettings().getAdjointDynamicsProvider().getDimension()];
103 Arrays.fill(unitScales, 1.);
104 return unitScales;
105 }
106
107
108
109
110
111 protected double[] getScales() {
112 return scales;
113 }
114
115
116
117
118
119 public abstract int getMaximumIterationCount();
120
121
122 @Override
123 public ShootingBoundaryOutput solve(final double initialMass, final double[] initialGuess) {
124 return solve(initialMass, initialGuess, getUnitScales());
125 }
126
127
128
129
130
131
132
133
134
135 public ShootingBoundaryOutput solve(final double initialMass, final double[] initialGuess,
136 final double[] userScales) {
137 scales = userScales.clone();
138 final SpacecraftState initialState = createStateWithMassAndAdjoint(initialMass, initialGuess);
139 final ShootingBoundaryOutput initialGuessSolution = computeCandidateSolution(initialState, 0);
140 if (initialGuessSolution.isConverged()) {
141 return initialGuessSolution;
142 } else {
143 return iterate(initialGuessSolution);
144 }
145 }
146
147
148 @Override
149 protected NumericalPropagator buildPropagator(final SpacecraftState initialState) {
150 final NumericalPropagator propagator = super.buildPropagator(initialState);
151 propagator.setStepHandler(propagationStepRecorder);
152 final CartesianAdjointDerivativesProvider derivativesProvider = (CartesianAdjointDerivativesProvider)
153 getPropagationSettings().getAdjointDynamicsProvider().buildAdditionalDerivativesProvider();
154 eventsLogger.clearLoggedEvents();
155 derivativesProvider.getCost().getEventDetectors()
156 .forEach(detector -> propagator.addEventDetector(eventsLogger.monitorDetector(detector)));
157 return propagator;
158 }
159
160
161
162
163
164
165
166 public abstract ShootingBoundaryOutput computeCandidateSolution(SpacecraftState initialState, int iterationCount);
167
168
169
170
171
172
173 private ShootingBoundaryOutput iterate(final ShootingBoundaryOutput initialGuess) {
174 int iterationCount = 1;
175 SpacecraftState initialState = initialGuess.getInitialState();
176 ShootingBoundaryOutput candidateSolution = initialGuess;
177 final String adjointName = getPropagationSettings().getAdjointDynamicsProvider().getAdjointName();
178 double[] initialAdjoint = initialState.getAdditionalState(adjointName).clone();
179 final double initialMass = initialState.getMass();
180 while (iterationCount < getMaximumIterationCount()) {
181 if (candidateSolution.isConverged()) {
182 return candidateSolution;
183 }
184 final FieldSpacecraftState<Gradient> fieldInitialState = createFieldInitialStateWithMassAndAdjoint(initialMass,
185 initialAdjoint);
186 initialState = fieldInitialState.toSpacecraftState();
187 final FieldSpacecraftState<Gradient> fieldTerminalState = propagateField(fieldInitialState);
188 initialAdjoint = updateShootingVariables(initialAdjoint, fieldTerminalState);
189 if (Double.isNaN(initialAdjoint[0])) {
190 return candidateSolution;
191 } else {
192 candidateSolution = computeCandidateSolution(initialState, iterationCount);
193 }
194 iterationCount++;
195 }
196 return candidateSolution;
197 }
198
199
200
201
202
203
204
205 private FieldSpacecraftState<Gradient> propagateField(final FieldSpacecraftState<Gradient> fieldInitialState) {
206 final FieldAbsoluteDate<Gradient> initialDate = fieldInitialState.getDate();
207 final FieldExplicitRungeKuttaIntegrator<Gradient> fieldIntegrator = buildFieldIntegrator(fieldInitialState);
208 final FieldOrdinaryDifferentialEquation<Gradient> fieldODE = buildFieldODE(fieldInitialState.getDate());
209 final AdjointDynamicsProvider dynamicsProvider = getPropagationSettings().getAdjointDynamicsProvider();
210 AbsoluteDate date = initialDate.toAbsoluteDate();
211 Gradient[] integrationState = formatToArray(fieldInitialState, dynamicsProvider.getAdjointName());
212
213 final List<EventsLogger.LoggedEvent> loggedEvents = eventsLogger.getLoggedEvents();
214 final List<AbsoluteDate> stepDates = propagationStepRecorder.copyStates().stream().map(SpacecraftState::getDate)
215 .collect(Collectors.toList());
216 for (final AbsoluteDate stepDate: stepDates) {
217 final Gradient time = initialDate.durationFrom(date).negate();
218 final Gradient nextTime = initialDate.durationFrom(stepDate).negate();
219 integrationState = fieldIntegrator.singleStep(fieldODE, time, integrationState, nextTime);
220
221 if (!loggedEvents.isEmpty()) {
222 for (final EventsLogger.LoggedEvent loggedEvent: loggedEvents) {
223 final SpacecraftState loggedState = loggedEvent.getState();
224 if (loggedEvent.getEventDetector() instanceof ControlSwitchDetector && loggedState.getDate().isEqualTo(stepDate)) {
225 final ControlSwitchDetector switchDetector = (ControlSwitchDetector) loggedEvent.getEventDetector();
226 integrationState = findSwitchEventAndUpdateState(date, integrationState,
227 initialDate.toAbsoluteDate(), switchDetector, dynamicsProvider);
228 }
229 }
230 }
231 date = new AbsoluteDate(stepDate);
232 }
233 return formatFromArray(date, integrationState);
234 }
235
236
237
238
239
240
241 protected SpacecraftState createInitialStateWithMass(final double initialMass) {
242 return initialSpacecraftStateTemplate.withMass(initialMass);
243 }
244
245
246
247
248
249
250
251 private SpacecraftState createStateWithMassAndAdjoint(final double initialMass, final double[] initialAdjoint) {
252 final String adjointName = getPropagationSettings().getAdjointDynamicsProvider().getAdjointName();
253 return createInitialStateWithMass(initialMass).addAdditionalData(adjointName, initialAdjoint);
254 }
255
256
257
258
259
260
261
262 protected FieldSpacecraftState<Gradient> createFieldInitialStateWithMassAndAdjoint(final double initialMass,
263 final double[] initialAdjoint) {
264 final int parametersNumber = initialAdjoint.length;
265 final GradientField field = GradientField.getField(parametersNumber);
266 final FieldSpacecraftState<Gradient> stateWithoutAdjoint = new FieldSpacecraftState<>(field,
267 createInitialStateWithMass(initialMass));
268 final Gradient[] fieldInitialAdjoint = MathArrays.buildArray(field, initialAdjoint.length);
269 for (int i = 0; i < parametersNumber; i++) {
270 fieldInitialAdjoint[i] = Gradient.variable(parametersNumber, i, 0.).multiply(getScales()[i]).add(initialAdjoint[i]);
271 }
272 final String adjointName = getPropagationSettings().getAdjointDynamicsProvider().getAdjointName();
273 return stateWithoutAdjoint.addAdditionalData(adjointName, fieldInitialAdjoint);
274 }
275
276
277
278
279
280
281
282
283
284
285 protected <T extends CalculusFieldElement<T>> FieldSpacecraftState<T> createFieldState(final FieldAbsoluteDate<T> date,
286 final T[] cartesian,
287 final T mass,
288 final T[] adjoint) {
289 final Field<T> field = mass.getField();
290 final Frame frame = getPropagationSettings().getPropagationFrame();
291 final FieldVector3D<T> position = new FieldVector3D<>(Arrays.copyOfRange(cartesian, 0, 3));
292 final FieldVector3D<T> velocity = new FieldVector3D<>(Arrays.copyOfRange(cartesian, 3, 6));
293 final FieldPVCoordinates<T> pvCoordinates = new FieldPVCoordinates<>(position, velocity);
294 final FieldSpacecraftState<T> stateWithoutAdjoint;
295 if (initialSpacecraftStateTemplate.isOrbitDefined()) {
296 final T mu = field.getZero().newInstance(initialSpacecraftStateTemplate.getOrbit().getMu());
297 final FieldCartesianOrbit<T> orbit = new FieldCartesianOrbit<>(pvCoordinates, frame, date, mu);
298 final FieldAttitude<T> attitude = getPropagationSettings().getAttitudeProvider().getAttitude(orbit,
299 orbit.getDate(), orbit.getFrame());
300 stateWithoutAdjoint = new FieldSpacecraftState<>(orbit, attitude).withMass(mass);
301 } else {
302 final FieldAbsolutePVCoordinates<T> absolutePVCoordinates = new FieldAbsolutePVCoordinates<>(frame, date,
303 pvCoordinates);
304 final FieldAttitude<T> attitude = getPropagationSettings().getAttitudeProvider().getAttitude(absolutePVCoordinates,
305 absolutePVCoordinates.getDate(), absolutePVCoordinates.getFrame());
306 stateWithoutAdjoint = new FieldSpacecraftState<>(absolutePVCoordinates, attitude).withMass(mass);
307 }
308 final String adjointName = getPropagationSettings().getAdjointDynamicsProvider().getAdjointName();
309 return stateWithoutAdjoint.addAdditionalData(adjointName, adjoint);
310 }
311
312
313
314
315
316
317
318 protected abstract double[] updateShootingVariables(double[] originalShootingVariables,
319 FieldSpacecraftState<Gradient> fieldTerminalState);
320
321
322
323
324
325
326
327
328 protected <T extends CalculusFieldElement<T>> FieldOrdinaryDifferentialEquation<T> buildFieldODE(final FieldAbsoluteDate<T> referenceDate) {
329 final Field<T> field = referenceDate.getField();
330 final AdjointDynamicsProvider adjointDynamicsProvider = getPropagationSettings().getAdjointDynamicsProvider();
331 final FieldAdditionalDerivativesProvider<T> derivativesProvider = adjointDynamicsProvider
332 .buildFieldAdditionalDerivativesProvider(field);
333
334 return new FieldOrdinaryDifferentialEquation<T>() {
335 @Override
336 public int getDimension() {
337 return 7 + adjointDynamicsProvider.getDimension();
338 }
339
340 @Override
341 public T[] computeDerivatives(final T t, final T[] y) {
342
343 final T[] cartesian = Arrays.copyOfRange(y, 0, 6);
344 final FieldAbsoluteDate<T> date = referenceDate.shiftedBy(t);
345 final Field<T> field = date.getField();
346 final T zero = field.getZero();
347 final T[] adjoint = Arrays.copyOfRange(y, 7, y.length);
348 final FieldSpacecraftState<T> state = createFieldState(date, cartesian, y[6], adjoint);
349
350 final T[] yDot = MathArrays.buildArray(field, getDimension());
351 yDot[0] = zero.add(y[3]);
352 yDot[1] = zero.add(y[4]);
353 yDot[2] = zero.add(y[5]);
354 FieldVector3D<T> totalAcceleration = FieldVector3D.getZero(field);
355 for (final ForceModel forceModel: getPropagationSettings().getForceModels()) {
356 final FieldVector3D<T> acceleration = forceModel.acceleration(state, forceModel.getParameters(field));
357 totalAcceleration = totalAcceleration.add(acceleration);
358 }
359 yDot[3] = totalAcceleration.getX();
360 yDot[4] = totalAcceleration.getY();
361 yDot[5] = totalAcceleration.getZ();
362 final FieldCombinedDerivatives<T> combinedDerivatives = derivativesProvider.combinedDerivatives(state);
363 final T[] cartesianDotContribution = combinedDerivatives.getMainStateDerivativesIncrements();
364 for (int i = 0; i < cartesianDotContribution.length; i++) {
365 yDot[i] = yDot[i].add(cartesianDotContribution[i]);
366 }
367 final T[] adjointDot = combinedDerivatives.getAdditionalDerivatives();
368 System.arraycopy(adjointDot, 0, yDot, yDot.length - adjointDot.length, adjointDot.length);
369 return yDot;
370 }
371 };
372 }
373
374
375
376
377
378
379
380 private Gradient[] formatToArray(final FieldSpacecraftState<Gradient> fieldState,
381 final String adjointName) {
382 final Gradient[] adjoint = fieldState.getAdditionalState(adjointName);
383 final int adjointDimension = adjoint.length;
384 final Gradient[] integrationState = MathArrays.buildArray(fieldState.getMass().getField(), 7 + adjointDimension);
385 final FieldPVCoordinates<Gradient> pvCoordinates = fieldState.getPVCoordinates();
386 System.arraycopy(pvCoordinates.getPosition().toArray(), 0, integrationState, 0, 3);
387 System.arraycopy(pvCoordinates.getVelocity().toArray(), 0, integrationState, 3, 3);
388 integrationState[6] = fieldState.getMass();
389 System.arraycopy(adjoint, 0, integrationState, 7, adjointDimension);
390 return integrationState;
391 }
392
393
394
395
396
397
398
399 private FieldSpacecraftState<Gradient> formatFromArray(final AbsoluteDate date, final Gradient[] integrationState) {
400 final Gradient[] terminalCartesian = Arrays.copyOfRange(integrationState, 0, 6);
401 final Gradient[] terminalAdjoint = Arrays.copyOfRange(integrationState, 7, integrationState.length);
402 final Field<Gradient> field = terminalCartesian[0].getField();
403 return createFieldState(new FieldAbsoluteDate<>(field, date), terminalCartesian, integrationState[6],
404 terminalAdjoint);
405 }
406
407
408
409
410
411
412
413
414
415
416
417 private Gradient[] findSwitchEventAndUpdateState(final AbsoluteDate date, final Gradient[] integrationState,
418 final AbsoluteDate referenceDate,
419 final ControlSwitchDetector switchDetector,
420 final AdjointDynamicsProvider dynamicsProvider) {
421 final int shootingVariables = dynamicsProvider.getDimension();
422 final int increasedVariables = shootingVariables + 1;
423 final GradientField increasedVariablesField = GradientField.getField(increasedVariables);
424 final FieldCartesianAdjointDerivativesProvider<Gradient> fieldDerivativesProvider =
425 (FieldCartesianAdjointDerivativesProvider<Gradient>) dynamicsProvider.buildFieldAdditionalDerivativesProvider(increasedVariablesField);
426 final List<FieldEventDetector<Gradient>> fieldEventDetectors = fieldDerivativesProvider.getCost()
427 .getFieldEventDetectors(increasedVariablesField).collect(Collectors.toList());
428 final FieldSpacecraftState<Gradient> fieldState = formatFromArray(date, integrationState);
429 final double expectedG = switchDetector.g(fieldState.toSpacecraftState());
430 for (final FieldEventDetector<Gradient> fieldEventDetector : fieldEventDetectors) {
431 if (fieldEventDetector instanceof FieldControlSwitchDetector) {
432 final double actualG = fieldEventDetector.g(fieldState).getReal();
433 if (FastMath.abs(actualG - expectedG) < 1e-10) {
434 return updateStateWithTaylorMapInversion(date, integrationState, referenceDate,
435 fieldEventDetector, dynamicsProvider.getAdjointName());
436 }
437 }
438 }
439 return integrationState;
440 }
441
442
443
444
445
446
447
448
449
450
451 private Gradient[] updateStateWithTaylorMapInversion(final AbsoluteDate date, final Gradient[] integrationState,
452 final AbsoluteDate initialDate,
453 final FieldEventDetector<Gradient> fieldDetector,
454 final String adjointName) {
455
456 final Gradient threshold = fieldDetector.getThreshold();
457 final int increasedVariables = threshold.getFreeParameters();
458 final GradientField increasedVariablesField = GradientField.getField(increasedVariables);
459 final Gradient[] increasedVariablesArray = MathArrays.buildArray(increasedVariablesField,
460 integrationState.length);
461 for (int i = 0; i < integrationState.length; i++) {
462 increasedVariablesArray[i] = integrationState[i].stackVariable();
463 }
464 final Gradient dt = Gradient.variable(increasedVariables, increasedVariables - 1, 1);
465
466 final Gradient gBefore = evaluateG(date, increasedVariablesArray, initialDate, fieldDetector, dt, adjointName);
467 final Gradient[] mapBeforeSwitch = invertTaylorMap(increasedVariablesArray, gBefore);
468
469 for (int i = 0; i < integrationState.length; i++) {
470 increasedVariablesArray[i] = mapBeforeSwitch[i].stackVariable();
471 }
472 final boolean isForward = date.isAfter(initialDate);
473 final double tinyStep = FastMath.min(DateDetector.DEFAULT_THRESHOLD, threshold.getValue());
474 final double timeShift = isForward ? tinyStep : -tinyStep;
475 final FieldSpacecraftState<Gradient> stateAfterSwitch = formatFromArray(date, increasedVariablesArray)
476 .shiftedBy(timeShift);
477 final Gradient[] stateAfterSwitchArray = formatToArray(stateAfterSwitch, adjointName);
478
479 final AbsoluteDate shiftedDate = date.shiftedBy(timeShift);
480 final Gradient gAfter = evaluateG(shiftedDate, stateAfterSwitchArray, initialDate, fieldDetector, dt.negate(),
481 adjointName);
482 return invertTaylorMap(increasedVariablesArray, gAfter);
483 }
484
485
486
487
488
489
490
491
492
493
494
495 private Gradient evaluateG(final AbsoluteDate date, final Gradient[] increasedVariablesArray,
496 final AbsoluteDate initialDate, final FieldEventDetector<Gradient> fieldDetector,
497 final Gradient dt, final String adjointName) {
498 final Field<Gradient> field = increasedVariablesArray[0].getField();
499 final FieldAbsoluteDate<Gradient> referenceDate = new FieldAbsoluteDate<>(field, initialDate);
500 final FieldOrdinaryDifferentialEquation<Gradient> fieldODE = buildFieldODE(referenceDate);
501 final double duration = date.durationFrom(initialDate);
502 final Gradient[] derivatives = fieldODE.computeDerivatives(field.getZero().newInstance(duration),
503 increasedVariablesArray);
504 final FieldSpacecraftState<Gradient> fieldState = formatFromArray(date, increasedVariablesArray);
505 final FieldSpacecraftState<Gradient> fieldStateWithAdjointDerivative = fieldState.addAdditionalStateDerivative(adjointName,
506 Arrays.copyOfRange(derivatives, derivatives.length - 7, derivatives.length));
507 return fieldDetector.g(fieldStateWithAdjointDerivative.shiftedBy(dt));
508 }
509
510
511
512
513
514
515
516
517 private static Gradient[] invertTaylorMap(final Gradient[] state, final Gradient g) {
518
519 final int increasedGradientDimension = g.getFreeParameters();
520 final RealMatrix rightMatrix = MatrixUtils.createRealIdentityMatrix(increasedGradientDimension);
521 rightMatrix.setRow(rightMatrix.getRowDimension() - 1, g.getGradient());
522 final LUDecomposition luDecomposition = new LUDecomposition(rightMatrix, 0.);
523 final RealMatrix inverted = luDecomposition.getSolver().getInverse();
524 final double[][] leftMatrixCoefficients = new double[state.length][];
525 for (int i = 0; i < state.length; i++) {
526 leftMatrixCoefficients[i] = state[i].getGradient();
527 }
528 final RealMatrix product = MatrixUtils.createRealMatrix(leftMatrixCoefficients).multiply(inverted);
529
530 final int gradientDimension = increasedGradientDimension - 1;
531 final GradientField field = GradientField.getField(gradientDimension);
532 final Gradient[] outputState = MathArrays.buildArray(field, state.length);
533 for (int i = 0; i < outputState.length; i++) {
534 final double[] gradient = new double[gradientDimension];
535 System.arraycopy(product.getRow(i), 0, gradient, 0, gradientDimension);
536 outputState[i] = new Gradient(state[i].getValue(), gradient);
537 }
538 return outputState;
539 }
540
541 }