View Javadoc
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 org.apache.commons.math4.legacy.ode.events;
19  
20  
21  import org.apache.commons.math4.legacy.analysis.solvers.BrentSolver;
22  import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
23  import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
24  import org.apache.commons.math4.legacy.exception.NoBracketingException;
25  import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
26  import org.apache.commons.math4.legacy.ode.ExpandableStatefulODE;
27  import org.apache.commons.math4.legacy.ode.FirstOrderDifferentialEquations;
28  import org.apache.commons.math4.legacy.ode.SecondaryEquations;
29  import org.apache.commons.math4.legacy.ode.nonstiff.DormandPrince853Integrator;
30  import org.apache.commons.math4.legacy.ode.nonstiff.LutherIntegrator;
31  import org.apache.commons.math4.legacy.ode.sampling.AbstractStepInterpolator;
32  import org.apache.commons.math4.legacy.ode.sampling.DummyStepInterpolator;
33  import org.junit.Assert;
34  import org.junit.Test;
35  
36  public class EventStateTest {
37  
38      // JIRA: MATH-322
39      @Test
40      public void closeEvents() throws MaxCountExceededException, NoBracketingException {
41  
42          final double r1  = 90.0;
43          final double r2  = 135.0;
44          final double gap = r2 - r1;
45  
46          final double tolerance = 0.1;
47          EventState es = new EventState(new CloseEventsGenerator(r1, r2), 1.5 * gap,
48                                         tolerance, 100,
49                                         new BrentSolver(tolerance));
50          es.setExpandable(new ExpandableStatefulODE(new FirstOrderDifferentialEquations() {
51              @Override
52              public int getDimension() {
53                  return 0;
54              }
55              @Override
56              public void computeDerivatives(double t, double[] y, double[] yDot) {
57              }
58          }));
59  
60          AbstractStepInterpolator interpolator =
61              new DummyStepInterpolator(new double[0], new double[0], true);
62          interpolator.storeTime(r1 - 2.5 * gap);
63          interpolator.shift();
64          interpolator.storeTime(r1 - 1.5 * gap);
65          es.reinitializeBegin(interpolator);
66  
67          interpolator.shift();
68          interpolator.storeTime(r1 - 0.5 * gap);
69          Assert.assertFalse(es.evaluateStep(interpolator));
70  
71          interpolator.shift();
72          interpolator.storeTime(0.5 * (r1 + r2));
73          Assert.assertTrue(es.evaluateStep(interpolator));
74          Assert.assertEquals(r1, es.getEventTime(), tolerance);
75          es.stepAccepted(es.getEventTime(), new double[0]);
76  
77          interpolator.shift();
78          interpolator.storeTime(r2 + 0.4 * gap);
79          Assert.assertTrue(es.evaluateStep(interpolator));
80          Assert.assertEquals(r2, es.getEventTime(), tolerance);
81      }
82  
83      // Jira: MATH-695
84      @Test
85      public void testIssue695()
86          throws DimensionMismatchException, NumberIsTooSmallException,
87                 MaxCountExceededException, NoBracketingException {
88  
89          FirstOrderDifferentialEquations equation = new FirstOrderDifferentialEquations() {
90  
91              @Override
92              public int getDimension() {
93                  return 1;
94              }
95  
96              @Override
97              public void computeDerivatives(double t, double[] y, double[] yDot) {
98                  yDot[0] = 1.0;
99              }
100         };
101 
102         DormandPrince853Integrator integrator = new DormandPrince853Integrator(0.001, 1000, 1.0e-14, 1.0e-14);
103         integrator.addEventHandler(new ResettingEvent(10.99), 0.1, 1.0e-9, 1000);
104         integrator.addEventHandler(new ResettingEvent(11.01), 0.1, 1.0e-9, 1000);
105         integrator.setInitialStepSize(3.0);
106 
107         double target = 30.0;
108         double[] y = new double[1];
109         double tEnd = integrator.integrate(equation, 0.0, y, target, y);
110         Assert.assertEquals(target, tEnd, 1.0e-10);
111         Assert.assertEquals(32.0, y[0], 1.0e-10);
112     }
113 
114     private static final class ResettingEvent implements EventHandler {
115 
116         private static double lastTriggerTime = Double.NEGATIVE_INFINITY;
117         private final double tEvent;
118 
119         ResettingEvent(final double tEvent) {
120             this.tEvent = tEvent;
121         }
122 
123         @Override
124         public void init(double t0, double[] y0, double t) {
125         }
126 
127         @Override
128         public double g(double t, double[] y) {
129             // the bug corresponding to issue 695 causes the g function
130             // to be called at obsolete times t despite an event
131             // occurring later has already been triggered.
132             // When this occurs, the following assertion is violated
133             Assert.assertTrue("going backward in time! (" + t + " < " + lastTriggerTime + ")",
134                               t >= lastTriggerTime);
135             return t - tEvent;
136         }
137 
138         @Override
139         public Action eventOccurred(double t, double[] y, boolean increasing) {
140             // remember in a class variable when the event was triggered
141             lastTriggerTime = t;
142             return Action.RESET_STATE;
143         }
144 
145         @Override
146         public void resetState(double t, double[] y) {
147             y[0] += 1.0;
148         }
149     }
150 
151     // Jira: MATH-965
152     @Test
153     public void testIssue965()
154         throws DimensionMismatchException, NumberIsTooSmallException,
155                MaxCountExceededException, NoBracketingException {
156 
157         ExpandableStatefulODE equation =
158                 new ExpandableStatefulODE(new FirstOrderDifferentialEquations() {
159 
160             @Override
161             public int getDimension() {
162                 return 1;
163             }
164 
165             @Override
166             public void computeDerivatives(double t, double[] y, double[] yDot) {
167                 yDot[0] = 2.0;
168             }
169         });
170         equation.setTime(0.0);
171         equation.setPrimaryState(new double[1]);
172         equation.addSecondaryEquations(new SecondaryEquations() {
173 
174             @Override
175             public int getDimension() {
176                 return 1;
177             }
178 
179             @Override
180             public void computeDerivatives(double t, double[] primary,
181                                            double[] primaryDot, double[] secondary,
182                                            double[] secondaryDot) {
183                 secondaryDot[0] = -3.0;
184             }
185         });
186         int index = equation.getSecondaryMappers()[0].getFirstIndex();
187 
188         DormandPrince853Integrator integrator = new DormandPrince853Integrator(0.001, 1000, 1.0e-14, 1.0e-14);
189         integrator.addEventHandler(new SecondaryStateEvent(index, -3.0), 0.1, 1.0e-9, 1000);
190         integrator.setInitialStepSize(3.0);
191 
192         integrator.integrate(equation, 30.0);
193         Assert.assertEquals( 1.0, equation.getTime(), 1.0e-10);
194         Assert.assertEquals( 2.0, equation.getPrimaryState()[0], 1.0e-10);
195         Assert.assertEquals(-3.0, equation.getSecondaryState(0)[0], 1.0e-10);
196     }
197 
198     private static final class SecondaryStateEvent implements EventHandler {
199 
200         private int index;
201         private final double target;
202 
203         SecondaryStateEvent(final int index, final double target) {
204             this.index  = index;
205             this.target = target;
206         }
207 
208         @Override
209         public void init(double t0, double[] y0, double t) {
210         }
211 
212         @Override
213         public double g(double t, double[] y) {
214             return y[index] - target;
215         }
216 
217         @Override
218         public Action eventOccurred(double t, double[] y, boolean increasing) {
219             return Action.STOP;
220         }
221 
222         @Override
223         public void resetState(double t, double[] y) {
224         }
225     }
226 
227     @Test
228     public void testEventsCloserThanThreshold()
229         throws DimensionMismatchException, NumberIsTooSmallException,
230                MaxCountExceededException, NoBracketingException {
231 
232         FirstOrderDifferentialEquations equation = new FirstOrderDifferentialEquations() {
233 
234             @Override
235             public int getDimension() {
236                 return 1;
237             }
238 
239             @Override
240             public void computeDerivatives(double t, double[] y, double[] yDot) {
241                 yDot[0] = 1.0;
242             }
243         };
244 
245         LutherIntegrator integrator = new LutherIntegrator(20.0);
246         CloseEventsGenerator eventsGenerator =
247                         new CloseEventsGenerator(9.0 - 1.0 / 128, 9.0 + 1.0 / 128);
248         integrator.addEventHandler(eventsGenerator, 1.0, 0.02, 1000);
249         double[] y = new double[1];
250         double tEnd = integrator.integrate(equation, 0.0, y, 100.0, y);
251         Assert.assertEquals( 2, eventsGenerator.getCount());
252         Assert.assertEquals( 9.0 + 1.0 / 128, tEnd, 1.0 / 32.0);
253     }
254 
255     private static final class CloseEventsGenerator implements EventHandler {
256 
257         private final double r1;
258         private final double r2;
259         private int count;
260 
261         CloseEventsGenerator(final double r1, final double r2) {
262             this.r1    = r1;
263             this.r2    = r2;
264             this.count = 0;
265         }
266 
267         @Override
268         public void init(double t0, double[] y0, double t) {
269         }
270 
271         @Override
272         public void resetState(double t, double[] y) {
273         }
274 
275         @Override
276         public double g(double t, double[] y) {
277             return (t - r1) * (r2 - t);
278         }
279 
280         @Override
281         public Action eventOccurred(double t, double[] y, boolean increasing) {
282             return ++count < 2 ? Action.CONTINUE : Action.STOP;
283         }
284 
285         public int getCount() {
286             return count;
287         }
288     }
289 }