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