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-15) {
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 factorMatrix = computeUpdateMatrix(stateAtSwitch);
171
172 final String stmName = matricesHarvester.getStmName();
173 final RealMatrix oldStm = matricesHarvester.toSquareMatrix(stateAtSwitch.getAdditionalState(stmName));
174 final RealMatrix stm = factorMatrix.multiply(oldStm);
175 final DataDictionary additionalData = new DataDictionary(stateAtSwitch.getAdditionalDataValues());
176 additionalData.remove(stmName);
177 additionalData.put(stmName, matricesHarvester.toArray(stm.getData()));
178
179 if (!matricesHarvester.getJacobiansColumnsNames().isEmpty()) {
180 final RealMatrix oldJacobian = matricesHarvester.getParametersJacobian(stateAtSwitch);
181 final RealMatrix jacobian = factorMatrix.multiply(oldJacobian);
182 int index = 0;
183 for (final String parameterName : matricesHarvester.getJacobiansColumnsNames()) {
184 additionalData.remove(parameterName);
185 additionalData.put(parameterName, jacobian.getColumn(index));
186 index++;
187 }
188 }
189 return stateAtSwitch.withAdditionalData(additionalData);
190 }
191
192
193
194
195
196
197 private RealMatrix computeUpdateMatrix(final SpacecraftState state) {
198 final double twoThreshold = switchFieldDetector.getThreshold().getValue() * 2.;
199 final double dt = isForward ? -twoThreshold : twoThreshold;
200 final SpacecraftState stateBefore = shift(state, dt);
201 final double[] derivativesBefore = timeDerivativesEquations.computeTimeDerivatives(stateBefore);
202 final SpacecraftState stateAfter = shift(state, -dt);
203 final double[] derivativesAfter = timeDerivativesEquations.computeTimeDerivatives(stateAfter);
204 final double[] deltaDerivatives = new double[7];
205 for (int i = 3; i < 7; i++) {
206 deltaDerivatives[i] = derivativesAfter[i] - derivativesBefore[i];
207 }
208 final Gradient g = evaluateG(buildCartesianState(state, state.getPVCoordinates()), derivativesBefore[6]);
209 final double gLastDerivative = g.getPartialDerivative(7);
210 final double gDot = isForward ? gLastDerivative : -gLastDerivative;
211 final double[] gGradientState = Arrays.copyOfRange(g.getGradient(), 0, 7);
212 final RealVector lhs = MatrixUtils.createRealVector(deltaDerivatives);
213 final RealVector rhs = MatrixUtils.createRealVector(gGradientState).mapMultiply(1. / gDot);
214 return MatrixUtils.createRealIdentityMatrix(7).add(lhs.outerProduct(rhs));
215 }
216
217
218
219
220
221
222
223 private SpacecraftState shift(final SpacecraftState stateAtSwitch, final double dt) {
224 final PVCoordinates pvCoordinates = stateAtSwitch.getPVCoordinates();
225 final AbsoluteDate shiftedDate = stateAtSwitch.getDate().shiftedBy(dt);
226 final TimeStampedPVCoordinates shiftedPV = new TimeStampedPVCoordinates(shiftedDate,
227 pvCoordinates.getPosition().add(pvCoordinates.getVelocity().scalarMultiply(dt)), pvCoordinates.getVelocity());
228 return buildCartesianState(stateAtSwitch, shiftedPV);
229 }
230
231
232
233
234
235
236
237 private SpacecraftState buildCartesianState(final SpacecraftState templateState,
238 final TimeStampedPVCoordinates pvCoordinates) {
239 final SpacecraftState state;
240 final Frame frame = templateState.getFrame();
241 if (templateState.isOrbitDefined()) {
242 final Orbit templateOrbit = templateState.getOrbit();
243 final CartesianOrbit orbit = new CartesianOrbit(pvCoordinates, frame, templateOrbit.getMu());
244 final Attitude attitude = attitudeProvider.getAttitude(orbit, pvCoordinates.getDate(), frame);
245 state = new SpacecraftState(orbit, attitude);
246 } else {
247 final AbsolutePVCoordinates absolutePVCoordinates = new AbsolutePVCoordinates(frame, pvCoordinates);
248 final Attitude attitude = attitudeProvider.getAttitude(absolutePVCoordinates,
249 pvCoordinates.getDate(), frame);
250 state = new SpacecraftState(absolutePVCoordinates, attitude);
251 }
252 return state.withMass(templateState.getMass())
253 .withAdditionalData(templateState.getAdditionalDataValues())
254 .withAdditionalStatesDerivatives(templateState.getAdditionalStatesDerivatives());
255 }
256
257
258
259
260
261
262
263 private Gradient evaluateG(final SpacecraftState state, final double massRate) {
264 Gradient time = Gradient.variable(8, 7, 0);
265 if (!isForward) {
266 time = time.negate();
267 }
268 final GradientField field = time.getField();
269 FieldSpacecraftState<Gradient> fieldState = DerivativeStateUtils.buildSpacecraftStateGradient(field,
270 state, attitudeProvider);
271 fieldState = fieldState.shiftedBy(time);
272 final Gradient shiftedMass = time.multiply(massRate).add(state.getMass());
273 fieldState = fieldState.withMass(shiftedMass);
274 return switchFieldDetector.g(fieldState);
275 }
276
277 }
278