1 | /*
|
---|
2 | * Licensed to the Apache Software Foundation (ASF) under one or more
|
---|
3 | * contributor license agreements. See the NOTICE file distributed with
|
---|
4 | * this work for additional information regarding copyright ownership.
|
---|
5 | * The ASF licenses this file to You under the Apache License, Version 2.0
|
---|
6 | * (the "License"); you may not use this file except in compliance with
|
---|
7 | * the License. You may obtain a copy of the License at
|
---|
8 | *
|
---|
9 | * http://www.apache.org/licenses/LICENSE-2.0
|
---|
10 | *
|
---|
11 | * Unless required by applicable law or agreed to in writing, software
|
---|
12 | * distributed under the License is distributed on an "AS IS" BASIS,
|
---|
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
---|
14 | * See the License for the specific language governing permissions and
|
---|
15 | * limitations under the License.
|
---|
16 | */
|
---|
17 |
|
---|
18 | package agents.anac.y2019.harddealer.math3.ode.events;
|
---|
19 |
|
---|
20 | import agents.anac.y2019.harddealer.math3.analysis.UnivariateFunction;
|
---|
21 | import agents.anac.y2019.harddealer.math3.analysis.solvers.AllowedSolution;
|
---|
22 | import agents.anac.y2019.harddealer.math3.analysis.solvers.BracketedUnivariateSolver;
|
---|
23 | import agents.anac.y2019.harddealer.math3.analysis.solvers.PegasusSolver;
|
---|
24 | import agents.anac.y2019.harddealer.math3.analysis.solvers.UnivariateSolver;
|
---|
25 | import agents.anac.y2019.harddealer.math3.analysis.solvers.UnivariateSolverUtils;
|
---|
26 | import agents.anac.y2019.harddealer.math3.exception.MaxCountExceededException;
|
---|
27 | import agents.anac.y2019.harddealer.math3.exception.NoBracketingException;
|
---|
28 | import agents.anac.y2019.harddealer.math3.ode.EquationsMapper;
|
---|
29 | import agents.anac.y2019.harddealer.math3.ode.ExpandableStatefulODE;
|
---|
30 | import agents.anac.y2019.harddealer.math3.ode.sampling.StepInterpolator;
|
---|
31 | import agents.anac.y2019.harddealer.math3.util.FastMath;
|
---|
32 |
|
---|
33 | /** This class handles the state for one {@link EventHandler
|
---|
34 | * event handler} during integration steps.
|
---|
35 | *
|
---|
36 | * <p>Each time the integrator proposes a step, the event handler
|
---|
37 | * switching function should be checked. This class handles the state
|
---|
38 | * of one handler during one integration step, with references to the
|
---|
39 | * state at the end of the preceding step. This information is used to
|
---|
40 | * decide if the handler should trigger an event or not during the
|
---|
41 | * proposed step.</p>
|
---|
42 | *
|
---|
43 | * @since 1.2
|
---|
44 | */
|
---|
45 | public class EventState {
|
---|
46 |
|
---|
47 | /** Event handler. */
|
---|
48 | private final EventHandler handler;
|
---|
49 |
|
---|
50 | /** Maximal time interval between events handler checks. */
|
---|
51 | private final double maxCheckInterval;
|
---|
52 |
|
---|
53 | /** Convergence threshold for event localization. */
|
---|
54 | private final double convergence;
|
---|
55 |
|
---|
56 | /** Upper limit in the iteration count for event localization. */
|
---|
57 | private final int maxIterationCount;
|
---|
58 |
|
---|
59 | /** Equation being integrated. */
|
---|
60 | private ExpandableStatefulODE expandable;
|
---|
61 |
|
---|
62 | /** Time at the beginning of the step. */
|
---|
63 | private double t0;
|
---|
64 |
|
---|
65 | /** Value of the events handler at the beginning of the step. */
|
---|
66 | private double g0;
|
---|
67 |
|
---|
68 | /** Simulated sign of g0 (we cheat when crossing events). */
|
---|
69 | private boolean g0Positive;
|
---|
70 |
|
---|
71 | /** Indicator of event expected during the step. */
|
---|
72 | private boolean pendingEvent;
|
---|
73 |
|
---|
74 | /** Occurrence time of the pending event. */
|
---|
75 | private double pendingEventTime;
|
---|
76 |
|
---|
77 | /** Occurrence time of the previous event. */
|
---|
78 | private double previousEventTime;
|
---|
79 |
|
---|
80 | /** Integration direction. */
|
---|
81 | private boolean forward;
|
---|
82 |
|
---|
83 | /** Variation direction around pending event.
|
---|
84 | * (this is considered with respect to the integration direction)
|
---|
85 | */
|
---|
86 | private boolean increasing;
|
---|
87 |
|
---|
88 | /** Next action indicator. */
|
---|
89 | private EventHandler.Action nextAction;
|
---|
90 |
|
---|
91 | /** Root-finding algorithm to use to detect state events. */
|
---|
92 | private final UnivariateSolver solver;
|
---|
93 |
|
---|
94 | /** Simple constructor.
|
---|
95 | * @param handler event handler
|
---|
96 | * @param maxCheckInterval maximal time interval between switching
|
---|
97 | * function checks (this interval prevents missing sign changes in
|
---|
98 | * case the integration steps becomes very large)
|
---|
99 | * @param convergence convergence threshold in the event time search
|
---|
100 | * @param maxIterationCount upper limit of the iteration count in
|
---|
101 | * the event time search
|
---|
102 | * @param solver Root-finding algorithm to use to detect state events
|
---|
103 | */
|
---|
104 | public EventState(final EventHandler handler, final double maxCheckInterval,
|
---|
105 | final double convergence, final int maxIterationCount,
|
---|
106 | final UnivariateSolver solver) {
|
---|
107 | this.handler = handler;
|
---|
108 | this.maxCheckInterval = maxCheckInterval;
|
---|
109 | this.convergence = FastMath.abs(convergence);
|
---|
110 | this.maxIterationCount = maxIterationCount;
|
---|
111 | this.solver = solver;
|
---|
112 |
|
---|
113 | // some dummy values ...
|
---|
114 | expandable = null;
|
---|
115 | t0 = Double.NaN;
|
---|
116 | g0 = Double.NaN;
|
---|
117 | g0Positive = true;
|
---|
118 | pendingEvent = false;
|
---|
119 | pendingEventTime = Double.NaN;
|
---|
120 | previousEventTime = Double.NaN;
|
---|
121 | increasing = true;
|
---|
122 | nextAction = EventHandler.Action.CONTINUE;
|
---|
123 |
|
---|
124 | }
|
---|
125 |
|
---|
126 | /** Get the underlying event handler.
|
---|
127 | * @return underlying event handler
|
---|
128 | */
|
---|
129 | public EventHandler getEventHandler() {
|
---|
130 | return handler;
|
---|
131 | }
|
---|
132 |
|
---|
133 | /** Set the equation.
|
---|
134 | * @param expandable equation being integrated
|
---|
135 | */
|
---|
136 | public void setExpandable(final ExpandableStatefulODE expandable) {
|
---|
137 | this.expandable = expandable;
|
---|
138 | }
|
---|
139 |
|
---|
140 | /** Get the maximal time interval between events handler checks.
|
---|
141 | * @return maximal time interval between events handler checks
|
---|
142 | */
|
---|
143 | public double getMaxCheckInterval() {
|
---|
144 | return maxCheckInterval;
|
---|
145 | }
|
---|
146 |
|
---|
147 | /** Get the convergence threshold for event localization.
|
---|
148 | * @return convergence threshold for event localization
|
---|
149 | */
|
---|
150 | public double getConvergence() {
|
---|
151 | return convergence;
|
---|
152 | }
|
---|
153 |
|
---|
154 | /** Get the upper limit in the iteration count for event localization.
|
---|
155 | * @return upper limit in the iteration count for event localization
|
---|
156 | */
|
---|
157 | public int getMaxIterationCount() {
|
---|
158 | return maxIterationCount;
|
---|
159 | }
|
---|
160 |
|
---|
161 | /** Reinitialize the beginning of the step.
|
---|
162 | * @param interpolator valid for the current step
|
---|
163 | * @exception MaxCountExceededException if the interpolator throws one because
|
---|
164 | * the number of functions evaluations is exceeded
|
---|
165 | */
|
---|
166 | public void reinitializeBegin(final StepInterpolator interpolator)
|
---|
167 | throws MaxCountExceededException {
|
---|
168 |
|
---|
169 | t0 = interpolator.getPreviousTime();
|
---|
170 | interpolator.setInterpolatedTime(t0);
|
---|
171 | g0 = handler.g(t0, getCompleteState(interpolator));
|
---|
172 | if (g0 == 0) {
|
---|
173 | // excerpt from MATH-421 issue:
|
---|
174 | // If an ODE solver is setup with an EventHandler that return STOP
|
---|
175 | // when the even is triggered, the integrator stops (which is exactly
|
---|
176 | // the expected behavior). If however the user wants to restart the
|
---|
177 | // solver from the final state reached at the event with the same
|
---|
178 | // configuration (expecting the event to be triggered again at a
|
---|
179 | // later time), then the integrator may fail to start. It can get stuck
|
---|
180 | // at the previous event. The use case for the bug MATH-421 is fairly
|
---|
181 | // general, so events occurring exactly at start in the first step should
|
---|
182 | // be ignored.
|
---|
183 |
|
---|
184 | // extremely rare case: there is a zero EXACTLY at interval start
|
---|
185 | // we will use the sign slightly after step beginning to force ignoring this zero
|
---|
186 | final double epsilon = FastMath.max(solver.getAbsoluteAccuracy(),
|
---|
187 | FastMath.abs(solver.getRelativeAccuracy() * t0));
|
---|
188 | final double tStart = t0 + 0.5 * epsilon;
|
---|
189 | interpolator.setInterpolatedTime(tStart);
|
---|
190 | g0 = handler.g(tStart, getCompleteState(interpolator));
|
---|
191 | }
|
---|
192 | g0Positive = g0 >= 0;
|
---|
193 |
|
---|
194 | }
|
---|
195 |
|
---|
196 | /** Get the complete state (primary and secondary).
|
---|
197 | * @param interpolator interpolator to use
|
---|
198 | * @return complete state
|
---|
199 | */
|
---|
200 | private double[] getCompleteState(final StepInterpolator interpolator) {
|
---|
201 |
|
---|
202 | final double[] complete = new double[expandable.getTotalDimension()];
|
---|
203 |
|
---|
204 | expandable.getPrimaryMapper().insertEquationData(interpolator.getInterpolatedState(),
|
---|
205 | complete);
|
---|
206 | int index = 0;
|
---|
207 | for (EquationsMapper secondary : expandable.getSecondaryMappers()) {
|
---|
208 | secondary.insertEquationData(interpolator.getInterpolatedSecondaryState(index++),
|
---|
209 | complete);
|
---|
210 | }
|
---|
211 |
|
---|
212 | return complete;
|
---|
213 |
|
---|
214 | }
|
---|
215 |
|
---|
216 | /** Evaluate the impact of the proposed step on the event handler.
|
---|
217 | * @param interpolator step interpolator for the proposed step
|
---|
218 | * @return true if the event handler triggers an event before
|
---|
219 | * the end of the proposed step
|
---|
220 | * @exception MaxCountExceededException if the interpolator throws one because
|
---|
221 | * the number of functions evaluations is exceeded
|
---|
222 | * @exception NoBracketingException if the event cannot be bracketed
|
---|
223 | */
|
---|
224 | public boolean evaluateStep(final StepInterpolator interpolator)
|
---|
225 | throws MaxCountExceededException, NoBracketingException {
|
---|
226 |
|
---|
227 | try {
|
---|
228 | forward = interpolator.isForward();
|
---|
229 | final double t1 = interpolator.getCurrentTime();
|
---|
230 | final double dt = t1 - t0;
|
---|
231 | if (FastMath.abs(dt) < convergence) {
|
---|
232 | // we cannot do anything on such a small step, don't trigger any events
|
---|
233 | return false;
|
---|
234 | }
|
---|
235 | final int n = FastMath.max(1, (int) FastMath.ceil(FastMath.abs(dt) / maxCheckInterval));
|
---|
236 | final double h = dt / n;
|
---|
237 |
|
---|
238 | final UnivariateFunction f = new UnivariateFunction() {
|
---|
239 | /** {@inheritDoc} */
|
---|
240 | public double value(final double t) throws LocalMaxCountExceededException {
|
---|
241 | try {
|
---|
242 | interpolator.setInterpolatedTime(t);
|
---|
243 | return handler.g(t, getCompleteState(interpolator));
|
---|
244 | } catch (MaxCountExceededException mcee) {
|
---|
245 | throw new LocalMaxCountExceededException(mcee);
|
---|
246 | }
|
---|
247 | }
|
---|
248 | };
|
---|
249 |
|
---|
250 | double ta = t0;
|
---|
251 | double ga = g0;
|
---|
252 | for (int i = 0; i < n; ++i) {
|
---|
253 |
|
---|
254 | // evaluate handler value at the end of the substep
|
---|
255 | final double tb = (i == n - 1) ? t1 : t0 + (i + 1) * h;
|
---|
256 | interpolator.setInterpolatedTime(tb);
|
---|
257 | final double gb = handler.g(tb, getCompleteState(interpolator));
|
---|
258 |
|
---|
259 | // check events occurrence
|
---|
260 | if (g0Positive ^ (gb >= 0)) {
|
---|
261 | // there is a sign change: an event is expected during this step
|
---|
262 |
|
---|
263 | // variation direction, with respect to the integration direction
|
---|
264 | increasing = gb >= ga;
|
---|
265 |
|
---|
266 | // find the event time making sure we select a solution just at or past the exact root
|
---|
267 | final double root;
|
---|
268 | if (solver instanceof BracketedUnivariateSolver<?>) {
|
---|
269 | @SuppressWarnings("unchecked")
|
---|
270 | BracketedUnivariateSolver<UnivariateFunction> bracketing =
|
---|
271 | (BracketedUnivariateSolver<UnivariateFunction>) solver;
|
---|
272 | root = forward ?
|
---|
273 | bracketing.solve(maxIterationCount, f, ta, tb, AllowedSolution.RIGHT_SIDE) :
|
---|
274 | bracketing.solve(maxIterationCount, f, tb, ta, AllowedSolution.LEFT_SIDE);
|
---|
275 | } else {
|
---|
276 | final double baseRoot = forward ?
|
---|
277 | solver.solve(maxIterationCount, f, ta, tb) :
|
---|
278 | solver.solve(maxIterationCount, f, tb, ta);
|
---|
279 | final int remainingEval = maxIterationCount - solver.getEvaluations();
|
---|
280 | BracketedUnivariateSolver<UnivariateFunction> bracketing =
|
---|
281 | new PegasusSolver(solver.getRelativeAccuracy(), solver.getAbsoluteAccuracy());
|
---|
282 | root = forward ?
|
---|
283 | UnivariateSolverUtils.forceSide(remainingEval, f, bracketing,
|
---|
284 | baseRoot, ta, tb, AllowedSolution.RIGHT_SIDE) :
|
---|
285 | UnivariateSolverUtils.forceSide(remainingEval, f, bracketing,
|
---|
286 | baseRoot, tb, ta, AllowedSolution.LEFT_SIDE);
|
---|
287 | }
|
---|
288 |
|
---|
289 | if ((!Double.isNaN(previousEventTime)) &&
|
---|
290 | (FastMath.abs(root - ta) <= convergence) &&
|
---|
291 | (FastMath.abs(root - previousEventTime) <= convergence)) {
|
---|
292 | // we have either found nothing or found (again ?) a past event,
|
---|
293 | // retry the substep excluding this value, and taking care to have the
|
---|
294 | // required sign in case the g function is noisy around its zero and
|
---|
295 | // crosses the axis several times
|
---|
296 | do {
|
---|
297 | ta = forward ? ta + convergence : ta - convergence;
|
---|
298 | ga = f.value(ta);
|
---|
299 | } while ((g0Positive ^ (ga >= 0)) && (forward ^ (ta >= tb)));
|
---|
300 |
|
---|
301 | if (forward ^ (ta >= tb)) {
|
---|
302 | // we were able to skip this spurious root
|
---|
303 | --i;
|
---|
304 | } else {
|
---|
305 | // we can't avoid this root before the end of the step,
|
---|
306 | // we have to handle it despite it is close to the former one
|
---|
307 | // maybe we have two very close roots
|
---|
308 | pendingEventTime = root;
|
---|
309 | pendingEvent = true;
|
---|
310 | return true;
|
---|
311 | }
|
---|
312 | } else if (Double.isNaN(previousEventTime) ||
|
---|
313 | (FastMath.abs(previousEventTime - root) > convergence)) {
|
---|
314 | pendingEventTime = root;
|
---|
315 | pendingEvent = true;
|
---|
316 | return true;
|
---|
317 | } else {
|
---|
318 | // no sign change: there is no event for now
|
---|
319 | ta = tb;
|
---|
320 | ga = gb;
|
---|
321 | }
|
---|
322 |
|
---|
323 | } else {
|
---|
324 | // no sign change: there is no event for now
|
---|
325 | ta = tb;
|
---|
326 | ga = gb;
|
---|
327 | }
|
---|
328 |
|
---|
329 | }
|
---|
330 |
|
---|
331 | // no event during the whole step
|
---|
332 | pendingEvent = false;
|
---|
333 | pendingEventTime = Double.NaN;
|
---|
334 | return false;
|
---|
335 |
|
---|
336 | } catch (LocalMaxCountExceededException lmcee) {
|
---|
337 | throw lmcee.getException();
|
---|
338 | }
|
---|
339 |
|
---|
340 | }
|
---|
341 |
|
---|
342 | /** Get the occurrence time of the event triggered in the current step.
|
---|
343 | * @return occurrence time of the event triggered in the current
|
---|
344 | * step or infinity if no events are triggered
|
---|
345 | */
|
---|
346 | public double getEventTime() {
|
---|
347 | return pendingEvent ?
|
---|
348 | pendingEventTime :
|
---|
349 | (forward ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY);
|
---|
350 | }
|
---|
351 |
|
---|
352 | /** Acknowledge the fact the step has been accepted by the integrator.
|
---|
353 | * @param t value of the independent <i>time</i> variable at the
|
---|
354 | * end of the step
|
---|
355 | * @param y array containing the current value of the state vector
|
---|
356 | * at the end of the step
|
---|
357 | */
|
---|
358 | public void stepAccepted(final double t, final double[] y) {
|
---|
359 |
|
---|
360 | t0 = t;
|
---|
361 | g0 = handler.g(t, y);
|
---|
362 |
|
---|
363 | if (pendingEvent && (FastMath.abs(pendingEventTime - t) <= convergence)) {
|
---|
364 | // force the sign to its value "just after the event"
|
---|
365 | previousEventTime = t;
|
---|
366 | g0Positive = increasing;
|
---|
367 | nextAction = handler.eventOccurred(t, y, !(increasing ^ forward));
|
---|
368 | } else {
|
---|
369 | g0Positive = g0 >= 0;
|
---|
370 | nextAction = EventHandler.Action.CONTINUE;
|
---|
371 | }
|
---|
372 | }
|
---|
373 |
|
---|
374 | /** Check if the integration should be stopped at the end of the
|
---|
375 | * current step.
|
---|
376 | * @return true if the integration should be stopped
|
---|
377 | */
|
---|
378 | public boolean stop() {
|
---|
379 | return nextAction == EventHandler.Action.STOP;
|
---|
380 | }
|
---|
381 |
|
---|
382 | /** Let the event handler reset the state if it wants.
|
---|
383 | * @param t value of the independent <i>time</i> variable at the
|
---|
384 | * beginning of the next step
|
---|
385 | * @param y array were to put the desired state vector at the beginning
|
---|
386 | * of the next step
|
---|
387 | * @return true if the integrator should reset the derivatives too
|
---|
388 | */
|
---|
389 | public boolean reset(final double t, final double[] y) {
|
---|
390 |
|
---|
391 | if (!(pendingEvent && (FastMath.abs(pendingEventTime - t) <= convergence))) {
|
---|
392 | return false;
|
---|
393 | }
|
---|
394 |
|
---|
395 | if (nextAction == EventHandler.Action.RESET_STATE) {
|
---|
396 | handler.resetState(t, y);
|
---|
397 | }
|
---|
398 | pendingEvent = false;
|
---|
399 | pendingEventTime = Double.NaN;
|
---|
400 |
|
---|
401 | return (nextAction == EventHandler.Action.RESET_STATE) ||
|
---|
402 | (nextAction == EventHandler.Action.RESET_DERIVATIVES);
|
---|
403 |
|
---|
404 | }
|
---|
405 |
|
---|
406 | /** Local wrapper to propagate exceptions. */
|
---|
407 | private static class LocalMaxCountExceededException extends RuntimeException {
|
---|
408 |
|
---|
409 | /** Serializable UID. */
|
---|
410 | private static final long serialVersionUID = 20120901L;
|
---|
411 |
|
---|
412 | /** Wrapped exception. */
|
---|
413 | private final MaxCountExceededException wrapped;
|
---|
414 |
|
---|
415 | /** Simple constructor.
|
---|
416 | * @param exception exception to wrap
|
---|
417 | */
|
---|
418 | LocalMaxCountExceededException(final MaxCountExceededException exception) {
|
---|
419 | wrapped = exception;
|
---|
420 | }
|
---|
421 |
|
---|
422 | /** Get the wrapped exception.
|
---|
423 | * @return wrapped exception
|
---|
424 | */
|
---|
425 | public MaxCountExceededException getException() {
|
---|
426 | return wrapped;
|
---|
427 | }
|
---|
428 |
|
---|
429 | }
|
---|
430 |
|
---|
431 | }
|
---|