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.analysis.differentiation.GradientField;
22 import org.hipparchus.linear.MatrixUtils;
23 import org.hipparchus.linear.RealMatrix;
24 import org.hipparchus.linear.RealVector;
25 import org.hipparchus.ode.events.Action;
26 import org.hipparchus.util.FastMath;
27 import org.orekit.attitudes.Attitude;
28 import org.orekit.attitudes.AttitudeProvider;
29 import org.orekit.forces.ForceModel;
30 import org.orekit.frames.Frame;
31 import org.orekit.orbits.CartesianOrbit;
32 import org.orekit.orbits.Orbit;
33 import org.orekit.propagation.FieldSpacecraftState;
34 import org.orekit.propagation.SpacecraftState;
35 import org.orekit.propagation.events.EventDetector;
36 import org.orekit.propagation.events.FieldEventDetector;
37 import org.orekit.propagation.events.handlers.EventHandler;
38 import org.orekit.time.AbsoluteDate;
39 import org.orekit.utils.AbsolutePVCoordinates;
40 import org.orekit.utils.DataDictionary;
41 import org.orekit.utils.DerivativeStateUtils;
42 import org.orekit.utils.PVCoordinates;
43 import org.orekit.utils.TimeStampedPVCoordinates;
44
45 import java.util.Arrays;
46 import java.util.List;
47 import java.util.stream.Collectors;
48 import java.util.stream.Stream;
49
50
51
52
53
54
55
56
57
58
59 class SwitchEventHandler implements EventHandler {
60
61
62 private final EventHandler wrappedHandler;
63
64
65 private final NumericalPropagationHarvester matricesHarvester;
66
67
68 private final NumericalTimeDerivativesEquations timeDerivativesEquations;
69
70
71 private final AttitudeProvider attitudeProvider;
72
73
74 private boolean isForward;
75
76
77 private FieldEventDetector<Gradient> switchFieldDetector;
78
79
80
81
82
83
84
85
86 SwitchEventHandler(final EventHandler wrappedHandler, final NumericalPropagationHarvester matricesHarvester,
87 final NumericalTimeDerivativesEquations timeDerivativesEquations,
88 final AttitudeProvider attitudeProvider) {
89 this.wrappedHandler = wrappedHandler;
90 this.matricesHarvester = matricesHarvester;
91 this.timeDerivativesEquations = timeDerivativesEquations;
92 this.attitudeProvider = attitudeProvider;
93 }
94
95 @Override
96 public SpacecraftState resetState(final EventDetector detector, final SpacecraftState oldState) {
97 if (switchFieldDetector == null) {
98 return wrappedHandler.resetState(detector, oldState);
99 } else {
100 final SpacecraftState newState = updateState(oldState);
101 switchFieldDetector = null;
102 return newState;
103 }
104 }
105
106 @Override
107 public void init(final SpacecraftState initialState, final AbsoluteDate target, final EventDetector detector) {
108 isForward = initialState.getDate().isBeforeOrEqualTo(target);
109 switchFieldDetector = null;
110 EventHandler.super.init(initialState, target, detector);
111 }
112
113 @Override
114 public Action eventOccurred(final SpacecraftState s, final EventDetector detector, final boolean increasing) {
115 final Action action = wrappedHandler.eventOccurred(s, detector, increasing);
116 if (action == Action.RESET_DERIVATIVES) {
117 switchFieldDetector = findSwitchDetector(detector, s);
118 if (switchFieldDetector != null) {
119 return Action.RESET_STATE;
120 }
121 }
122 return action;
123 }
124
125
126
127
128
129
130
131 private FieldEventDetector<Gradient> findSwitchDetector(final EventDetector detector, final SpacecraftState state) {
132 final int variablesNumber = matricesHarvester.getStateDimension() + 1;
133 final GradientField field = GradientField.getField(variablesNumber);
134 Stream<FieldEventDetector<Gradient>> fieldDetectorsStream = attitudeProvider.getFieldEventDetectors(field);
135 for (final ForceModel forceModel : timeDerivativesEquations.getForceModels()) {
136 fieldDetectorsStream = Stream.concat(fieldDetectorsStream, forceModel.getFieldEventDetectors(field));
137 }
138 final List<FieldEventDetector<Gradient>> fieldDetectors = fieldDetectorsStream
139 .filter(fieldDetector -> !fieldDetector.dependsOnTimeOnly()).collect(Collectors.toList());
140 final FieldSpacecraftState<Gradient> fieldState = new FieldSpacecraftState<>(field, state);
141 fieldDetectors.forEach(fieldDetector -> fieldDetector.init(fieldState, fieldState.getDate()));
142 final double g = detector.g(state);
143 return findSwitchDetector(g, fieldState, fieldDetectors);
144 }
145
146
147
148
149
150
151
152
153 private FieldEventDetector<Gradient> findSwitchDetector(final double g, final FieldSpacecraftState<Gradient> fieldState,
154 final List<FieldEventDetector<Gradient>> fieldDetectors) {
155 for (final FieldEventDetector<Gradient> fieldDetector : fieldDetectors) {
156 final Gradient fieldG = fieldDetector.g(fieldState);
157 if (FastMath.abs(fieldG.getValue() - g) < 1e-11) {
158 return fieldDetector;
159 }
160 }
161 return null;
162 }
163
164
165
166
167
168
169 private SpacecraftState updateState(final SpacecraftState stateAtSwitch) {
170 final RealMatrix cartesianFactorMatrix = computeUpdateMatrix(stateAtSwitch);
171
172 final String stmName = matricesHarvester.getStmName();
173 final RealMatrix oldStm = matricesHarvester.toSquareMatrix(stateAtSwitch.getAdditionalState(stmName));
174 final RealMatrix stm = cartesianFactorMatrix.multiply(oldStm).add(oldStm);
175 final DataDictionary additionalData = new DataDictionary(stateAtSwitch.getAdditionalDataValues());
176 additionalData.put(stmName, matricesHarvester.toArray(stm.getData()));
177
178 if (!matricesHarvester.getJacobiansColumnsNames().isEmpty()) {
179 for (final String parameterName : matricesHarvester.getJacobiansColumnsNames()) {
180 final RealVector oldJacobian = MatrixUtils.createRealVector(stateAtSwitch.getAdditionalState(parameterName));
181 final RealVector jacobian = cartesianFactorMatrix.operate(oldJacobian).add(oldJacobian);
182 additionalData.put(parameterName, jacobian.toArray());
183 }
184 }
185 return stateAtSwitch.withAdditionalData(additionalData);
186 }
187
188
189
190
191
192
193 private RealMatrix computeUpdateMatrix(final SpacecraftState state) {
194 final double twoThreshold = switchFieldDetector.getThreshold().getValue() * 2.;
195 final double dt = isForward ? -twoThreshold : twoThreshold;
196 final SpacecraftState stateBefore = shift(state, dt);
197 final double[] derivativesBefore = timeDerivativesEquations.computeTimeDerivatives(stateBefore);
198 final SpacecraftState stateAfter = shift(state, -dt);
199 final double[] derivativesAfter = timeDerivativesEquations.computeTimeDerivatives(stateAfter);
200 final double[] deltaDerivatives = new double[7];
201 for (int i = 3; i < 7; i++) {
202 deltaDerivatives[i] = derivativesAfter[i] - derivativesBefore[i];
203 }
204 final Gradient g = evaluateG(buildCartesianState(state, state.getPVCoordinates()), derivativesBefore[6]);
205 final double gLastDerivative = g.getPartialDerivative(7);
206 final double gDot = isForward ? gLastDerivative : -gLastDerivative;
207 final double[] gGradientState = Arrays.copyOfRange(g.getGradient(), 0, 7);
208 final RealVector lhs = MatrixUtils.createRealVector(deltaDerivatives);
209 final RealVector rhs = MatrixUtils.createRealVector(gGradientState).mapMultiply(1. / gDot);
210 return lhs.outerProduct(rhs);
211 }
212
213
214
215
216
217
218
219 private SpacecraftState shift(final SpacecraftState stateAtSwitch, final double dt) {
220 final PVCoordinates pvCoordinates = stateAtSwitch.getPVCoordinates();
221 final AbsoluteDate shiftedDate = stateAtSwitch.getDate().shiftedBy(dt);
222 final TimeStampedPVCoordinates shiftedPV = new TimeStampedPVCoordinates(shiftedDate,
223 pvCoordinates.getPosition().add(pvCoordinates.getVelocity().scalarMultiply(dt)), pvCoordinates.getVelocity());
224 return buildCartesianState(stateAtSwitch, shiftedPV);
225 }
226
227
228
229
230
231
232
233 private SpacecraftState buildCartesianState(final SpacecraftState templateState,
234 final TimeStampedPVCoordinates pvCoordinates) {
235 final SpacecraftState state;
236 final Frame frame = templateState.getFrame();
237 if (templateState.isOrbitDefined()) {
238 final Orbit templateOrbit = templateState.getOrbit();
239 final CartesianOrbit orbit = new CartesianOrbit(pvCoordinates, frame, templateOrbit.getMu());
240 final Attitude attitude = attitudeProvider.getAttitude(orbit, pvCoordinates.getDate(), frame);
241 state = new SpacecraftState(orbit, attitude);
242 } else {
243 final AbsolutePVCoordinates absolutePVCoordinates = new AbsolutePVCoordinates(frame, pvCoordinates);
244 final Attitude attitude = attitudeProvider.getAttitude(absolutePVCoordinates,
245 pvCoordinates.getDate(), frame);
246 state = new SpacecraftState(absolutePVCoordinates, attitude);
247 }
248 return state.withMass(templateState.getMass())
249 .withAdditionalData(templateState.getAdditionalDataValues())
250 .withAdditionalStatesDerivatives(templateState.getAdditionalStatesDerivatives());
251 }
252
253
254
255
256
257
258
259 private Gradient evaluateG(final SpacecraftState state, final double massRate) {
260 Gradient time = Gradient.variable(8, 7, 0);
261 if (!isForward) {
262 time = time.negate();
263 }
264 final GradientField field = time.getField();
265 FieldSpacecraftState<Gradient> fieldState = DerivativeStateUtils.buildSpacecraftStateGradient(field,
266 state, attitudeProvider);
267 fieldState = fieldState.shiftedBy(time);
268 final Gradient shiftedMass = time.multiply(massRate).add(state.getMass());
269 fieldState = fieldState.withMass(shiftedMass);
270 return switchFieldDetector.g(fieldState);
271 }
272
273 }
274