View Javadoc
1   /*
2    * Licensed to the Hipparchus project 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    *      https://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  package org.hipparchus.ode.events;
18  
19  import org.hipparchus.analysis.UnivariateFunction;
20  import org.hipparchus.analysis.solvers.BracketedRealFieldUnivariateSolver;
21  import org.hipparchus.analysis.solvers.BracketedUnivariateSolver;
22  import org.hipparchus.analysis.solvers.BracketingNthOrderBrentSolver;
23  import org.hipparchus.analysis.solvers.FieldBracketingNthOrderBrentSolver;
24  import org.hipparchus.ode.ExpandableODE;
25  import org.hipparchus.ode.FieldExpandableODE;
26  import org.hipparchus.ode.FieldODEIntegrator;
27  import org.hipparchus.ode.FieldODEState;
28  import org.hipparchus.ode.FieldODEStateAndDerivative;
29  import org.hipparchus.ode.FieldOrdinaryDifferentialEquation;
30  import org.hipparchus.ode.ODEIntegrator;
31  import org.hipparchus.ode.ODEState;
32  import org.hipparchus.ode.ODEStateAndDerivative;
33  import org.hipparchus.ode.OrdinaryDifferentialEquation;
34  import org.hipparchus.ode.nonstiff.DormandPrince853FieldIntegrator;
35  import org.hipparchus.ode.nonstiff.DormandPrince853Integrator;
36  import org.hipparchus.util.Binary64;
37  import org.hipparchus.util.Binary64Field;
38  import org.junit.jupiter.api.Test;
39  
40  import static org.junit.jupiter.api.Assertions.assertEquals;
41  import static org.junit.jupiter.api.Assertions.assertTrue;
42  
43  /**
44   * Check events handlers and step handlers are called at consistent times.
45   *
46   * @author Luc Maisonobe
47   */
48  class EventsScheduling {
49  
50      @Test
51      void testForward() {
52          doTest(0.0, 1.0, 32);
53      }
54  
55      @Test
56      void testBackward() {
57          doTest(1.0, 0.0, 32);
58      }
59  
60      @Test
61      void testFieldForward() {
62          doTestField(0.0, 1.0, 32);
63      }
64  
65      @Test
66      void testFieldBackward() {
67          doTestField(1.0, 0.0, 32);
68      }
69  
70      private static void doTest(final double start, final double stop, final int expectedCalls) {
71  
72          final ODEIntegrator integrator =
73                  new DormandPrince853Integrator(10, 100.0, 1e-7, 1e-7);
74  
75          // checker that will be used in both step handler and events handlers
76          // to check they are called in consistent order
77          final ScheduleChecker checker = new ScheduleChecker(start, stop);
78          integrator.addStepHandler((interpolator) -> {
79              checker.callTime(interpolator.getPreviousState().getTime());
80              checker.callTime(interpolator.getCurrentState().getTime());
81          });
82  
83          for (int i = 0; i < 10; ++i) {
84              integrator.addEventDetector(new SimpleDetector(0.0625 * (i + 1), checker,
85                                                             1.0, 1.0e-9, 100));
86          }
87  
88          final OrdinaryDifferentialEquation ode = new OrdinaryDifferentialEquation() {
89              public int getDimension() {
90                  return 1;
91              }
92              public double[] computeDerivatives(double t, double[] y) {
93                  return new double[] { 1 };
94              }
95          };
96  
97          final ODEState initialState = new ODEState(start, new double[] { 0.0 });
98  
99          integrator.integrate(new ExpandableODE(ode), initialState, stop);
100 
101         assertEquals(expectedCalls, checker.calls);
102 
103     }
104 
105     private static void doTestField(final double start, final double stop, final int expectedCalls) {
106 
107         final FieldODEIntegrator<Binary64> integrator =
108                 new DormandPrince853FieldIntegrator<Binary64>(Binary64Field.getInstance(), 10, 100.0, 1e-7, 1e-7);
109 
110         // checker that will be used in both step handler and events handlers
111         // to check they are called in consistent order
112         final ScheduleChecker checker = new ScheduleChecker(start, stop);
113         integrator.addStepHandler((interpolator) -> {
114             checker.callTime(interpolator.getPreviousState().getTime().getReal());
115             checker.callTime(interpolator.getCurrentState().getTime().getReal());
116         });
117 
118         for (int i = 0; i < 10; ++i) {
119             integrator.addEventDetector(new SimpleFieldDetector(0.0625 * (i + 1), checker, 1.0, 1.0e-9, 100));
120         }
121 
122         final FieldOrdinaryDifferentialEquation<Binary64> ode =
123                         new FieldOrdinaryDifferentialEquation<Binary64>() {
124             public int getDimension() {
125                 return 1;
126             }
127             public Binary64[] computeDerivatives(Binary64 t, Binary64[] y) {
128                 return new Binary64[] { Binary64.ONE };
129             }
130         };
131 
132         final FieldODEState<Binary64> initialState =
133                         new FieldODEState<>(new Binary64(start), new Binary64[] { Binary64.ZERO });
134 
135         integrator.integrate(new FieldExpandableODE<>(ode), initialState, new Binary64(stop));
136 
137         assertEquals(expectedCalls, checker.calls);
138 
139     }
140 
141     /** Checker for method calls scheduling. */
142     private static class ScheduleChecker {
143 
144         private final double start;
145         private final double stop;
146         private double last;
147         private int    calls;
148 
149         ScheduleChecker(final double start, final double stop) {
150             this.start = start;
151             this.stop  = stop;
152             this.last  = Double.NaN;
153             this.calls = 0;
154         }
155 
156         void callTime(final double time) {
157             if (!Double.isNaN(last)) {
158                 // check scheduling is always consistent with integration direction
159                 if (start < stop) {
160                     // forward direction
161                     assertTrue(time >= start);
162                     assertTrue(time <= stop);
163                     assertTrue(time >= last);
164                } else {
165                     // backward direction
166                    assertTrue(time <= start);
167                    assertTrue(time >= stop);
168                    assertTrue(time <= last);
169                 }
170             }
171             last = time;
172             ++calls;
173         }
174 
175     }
176 
177     private static class SimpleDetector implements ODEEventDetector {
178 
179         private final AdaptableInterval             maxCheck;
180         private final int                           maxIter;
181         private final BracketingNthOrderBrentSolver solver;
182         private final double                        tEvent;
183         private final ScheduleChecker               checker;
184         SimpleDetector(final double tEvent, final ScheduleChecker checker,
185                        final double maxCheck, final double threshold, final int maxIter) {
186             this.maxCheck  = (s, isForward) -> maxCheck;
187             this.maxIter   = maxIter;
188             this.solver    = new BracketingNthOrderBrentSolver(0, threshold, 0, 5);
189             this.tEvent    = tEvent;
190             this.checker   = checker;
191         }
192 
193         public AdaptableInterval getMaxCheckInterval() {
194             return maxCheck;
195         }
196 
197         public int getMaxIterationCount() {
198             return maxIter;
199         }
200 
201         public BracketedUnivariateSolver<UnivariateFunction> getSolver() {
202             return solver;
203         }
204 
205         public ODEEventHandler getHandler() {
206             return (state, detector, increasing) -> {
207                 checker.callTime(state.getTime());
208                 return Action.CONTINUE;
209             };
210         }
211 
212         @Override
213         public double g(final ODEStateAndDerivative state) {
214             return state.getTime() - tEvent;
215         }
216 
217     }
218 
219     private static class SimpleFieldDetector implements FieldODEEventDetector<Binary64> {
220 
221         private final FieldAdaptableInterval<Binary64>             maxCheck;
222         private final int                                          maxIter;
223         private final BracketedRealFieldUnivariateSolver<Binary64> solver;
224         private final double                                       tEvent;
225         private final ScheduleChecker                              checker;
226 
227         SimpleFieldDetector(final double tEvent, final ScheduleChecker checker,
228                             final double maxCheck, final double threshold, final int maxIter) {
229             this.maxCheck  = (s, isForward) -> maxCheck;
230             this.maxIter   = maxIter;
231             this.solver    = new FieldBracketingNthOrderBrentSolver<>(new Binary64(0),
232                                                                       new Binary64(threshold),
233                                                                       new Binary64(0),
234                                                                       5);
235             this.tEvent    = tEvent;
236             this.checker   = checker;
237         }
238 
239         public FieldAdaptableInterval<Binary64> getMaxCheckInterval() {
240             return maxCheck;
241         }
242 
243         public int getMaxIterationCount() {
244             return maxIter;
245         }
246 
247         public BracketedRealFieldUnivariateSolver<Binary64> getSolver() {
248             return solver;
249         }
250 
251         public FieldODEEventHandler<Binary64> getHandler() {
252             return (state, detector, increasing) -> {
253                 checker.callTime(state.getTime().getReal());
254                 return Action.CONTINUE;
255             };
256         }
257         
258         @Override
259         public Binary64 g(final FieldODEStateAndDerivative<Binary64> state) {
260             return state.getTime().subtract(tEvent);
261         }
262 
263     }
264 
265 }