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