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.nonstiff;
19  
20  
21  import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
22  import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
23  import org.apache.commons.math4.legacy.exception.NoBracketingException;
24  import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
25  import org.apache.commons.math4.legacy.ode.FirstOrderDifferentialEquations;
26  import org.apache.commons.math4.legacy.ode.FirstOrderIntegrator;
27  import org.apache.commons.math4.legacy.ode.TestProblem1;
28  import org.apache.commons.math4.legacy.ode.TestProblem2;
29  import org.apache.commons.math4.legacy.ode.TestProblem3;
30  import org.apache.commons.math4.legacy.ode.TestProblem4;
31  import org.apache.commons.math4.legacy.ode.TestProblem5;
32  import org.apache.commons.math4.legacy.ode.TestProblem6;
33  import org.apache.commons.math4.legacy.ode.TestProblemAbstract;
34  import org.apache.commons.math4.legacy.ode.TestProblemHandler;
35  import org.apache.commons.math4.legacy.ode.events.EventHandler;
36  import org.apache.commons.math4.legacy.ode.sampling.StepHandler;
37  import org.apache.commons.math4.legacy.ode.sampling.StepInterpolator;
38  import org.apache.commons.math4.core.jdkmath.JdkMath;
39  import org.junit.Assert;
40  import org.junit.Test;
41  
42  public class LutherIntegratorTest {
43  
44      @Test
45      public void testMissedEndEvent()
46              throws DimensionMismatchException, NumberIsTooSmallException,
47              MaxCountExceededException, NoBracketingException {
48          final double   t0     = 1878250320.0000029;
49          final double   tEvent = 1878250379.9999986;
50          final double[] k      = { 1.0e-4, 1.0e-5, 1.0e-6 };
51          FirstOrderDifferentialEquations ode = new FirstOrderDifferentialEquations() {
52  
53              @Override
54              public int getDimension() {
55                  return k.length;
56              }
57  
58              @Override
59              public void computeDerivatives(double t, double[] y, double[] yDot) {
60                  for (int i = 0; i < y.length; ++i) {
61                      yDot[i] = k[i] * y[i];
62                  }
63              }
64          };
65  
66          LutherIntegrator integrator = new LutherIntegrator(60.0);
67  
68          double[] y0   = new double[k.length];
69          for (int i = 0; i < y0.length; ++i) {
70              y0[i] = i + 1;
71          }
72          double[] y    = new double[k.length];
73  
74          double finalT = integrator.integrate(ode, t0, y0, tEvent, y);
75          Assert.assertEquals(tEvent, finalT, 1.0e-15);
76          for (int i = 0; i < y.length; ++i) {
77              Assert.assertEquals(y0[i] * JdkMath.exp(k[i] * (finalT - t0)), y[i], 1.0e-15);
78          }
79  
80          integrator.addEventHandler(new EventHandler() {
81  
82              @Override
83              public void init(double t0, double[] y0, double t) {
84              }
85  
86              @Override
87              public void resetState(double t, double[] y) {
88              }
89  
90              @Override
91              public double g(double t, double[] y) {
92                  return t - tEvent;
93              }
94  
95              @Override
96              public Action eventOccurred(double t, double[] y, boolean increasing) {
97                  Assert.assertEquals(tEvent, t, 1.0e-15);
98                  return Action.CONTINUE;
99              }
100         }, Double.POSITIVE_INFINITY, 1.0e-20, 100);
101         finalT = integrator.integrate(ode, t0, y0, tEvent + 120, y);
102         Assert.assertEquals(tEvent + 120, finalT, 1.0e-15);
103         for (int i = 0; i < y.length; ++i) {
104             Assert.assertEquals(y0[i] * JdkMath.exp(k[i] * (finalT - t0)), y[i], 1.0e-15);
105         }
106     }
107 
108     @Test
109     public void testSanityChecks()
110             throws DimensionMismatchException, NumberIsTooSmallException,
111             MaxCountExceededException, NoBracketingException {
112         try  {
113             TestProblem1 pb = new TestProblem1();
114             new LutherIntegrator(0.01).integrate(pb,
115                                                  0.0, new double[pb.getDimension()+10],
116                                                  1.0, new double[pb.getDimension()]);
117             Assert.fail("an exception should have been thrown");
118         } catch(DimensionMismatchException ie) {
119         }
120         try  {
121             TestProblem1 pb = new TestProblem1();
122             new LutherIntegrator(0.01).integrate(pb,
123                                                  0.0, new double[pb.getDimension()],
124                                                  1.0, new double[pb.getDimension()+10]);
125             Assert.fail("an exception should have been thrown");
126         } catch(DimensionMismatchException ie) {
127         }
128         try  {
129             TestProblem1 pb = new TestProblem1();
130             new LutherIntegrator(0.01).integrate(pb,
131                                                  0.0, new double[pb.getDimension()],
132                                                  0.0, new double[pb.getDimension()]);
133             Assert.fail("an exception should have been thrown");
134         } catch(NumberIsTooSmallException ie) {
135         }
136     }
137 
138     @Test
139     public void testDecreasingSteps()
140             throws DimensionMismatchException, NumberIsTooSmallException,
141             MaxCountExceededException, NoBracketingException {
142 
143         for (TestProblemAbstract pb : new TestProblemAbstract[] {
144             new TestProblem1(), new TestProblem2(), new TestProblem3(),
145             new TestProblem4(), new TestProblem5(), new TestProblem6()
146         }) {
147 
148             double previousValueError = Double.NaN;
149             double previousTimeError = Double.NaN;
150             for (int i = 4; i < 10; ++i) {
151 
152                 double step = (pb.getFinalTime() - pb.getInitialTime()) * JdkMath.pow(2.0, -i);
153 
154                 FirstOrderIntegrator integ = new LutherIntegrator(step);
155                 TestProblemHandler handler = new TestProblemHandler(pb, integ);
156                 integ.addStepHandler(handler);
157                 EventHandler[] functions = pb.getEventsHandlers();
158                 for (int l = 0; l < functions.length; ++l) {
159                     integ.addEventHandler(functions[l],
160                                           Double.POSITIVE_INFINITY, 1.0e-6 * step, 1000);
161                 }
162                 Assert.assertEquals(functions.length, integ.getEventHandlers().size());
163                 double stopTime = integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
164                                                   pb.getFinalTime(), new double[pb.getDimension()]);
165                 if (functions.length == 0) {
166                     Assert.assertEquals(pb.getFinalTime(), stopTime, 1.0e-10);
167                 }
168 
169                 double error = handler.getMaximalValueError();
170                 if (i > 4) {
171                     Assert.assertTrue(error < 1.01 * JdkMath.abs(previousValueError));
172                 }
173                 previousValueError = error;
174 
175                 double timeError = handler.getMaximalTimeError();
176                 if (i > 4) {
177                     Assert.assertTrue(timeError <= JdkMath.abs(previousTimeError));
178                 }
179                 previousTimeError = timeError;
180 
181                 integ.clearEventHandlers();
182                 Assert.assertEquals(0, integ.getEventHandlers().size());
183             }
184         }
185     }
186 
187     @Test
188     public void testSmallStep()
189             throws DimensionMismatchException, NumberIsTooSmallException,
190             MaxCountExceededException, NoBracketingException {
191 
192         TestProblem1 pb = new TestProblem1();
193         double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.001;
194 
195         FirstOrderIntegrator integ = new LutherIntegrator(step);
196         TestProblemHandler handler = new TestProblemHandler(pb, integ);
197         integ.addStepHandler(handler);
198         integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
199                         pb.getFinalTime(), new double[pb.getDimension()]);
200 
201         Assert.assertTrue(handler.getLastError() < 9.0e-17);
202         Assert.assertTrue(handler.getMaximalValueError() < 4.0e-15);
203         Assert.assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
204         Assert.assertEquals("Luther", integ.getName());
205     }
206 
207     @Test
208     public void testBigStep()
209             throws DimensionMismatchException, NumberIsTooSmallException,
210             MaxCountExceededException, NoBracketingException {
211 
212         TestProblem1 pb = new TestProblem1();
213         double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.2;
214 
215         FirstOrderIntegrator integ = new LutherIntegrator(step);
216         TestProblemHandler handler = new TestProblemHandler(pb, integ);
217         integ.addStepHandler(handler);
218         integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
219                         pb.getFinalTime(), new double[pb.getDimension()]);
220 
221         Assert.assertTrue(handler.getLastError() > 0.00002);
222         Assert.assertTrue(handler.getMaximalValueError() > 0.001);
223         Assert.assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
224     }
225 
226     @Test
227     public void testBackward()
228             throws DimensionMismatchException, NumberIsTooSmallException,
229             MaxCountExceededException, NoBracketingException {
230 
231         TestProblem5 pb = new TestProblem5();
232         double step = JdkMath.abs(pb.getFinalTime() - pb.getInitialTime()) * 0.001;
233 
234         FirstOrderIntegrator integ = new LutherIntegrator(step);
235         TestProblemHandler handler = new TestProblemHandler(pb, integ);
236         integ.addStepHandler(handler);
237         integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
238                         pb.getFinalTime(), new double[pb.getDimension()]);
239 
240         Assert.assertTrue(handler.getLastError() < 3.0e-13);
241         Assert.assertTrue(handler.getMaximalValueError() < 5.0e-13);
242         Assert.assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
243         Assert.assertEquals("Luther", integ.getName());
244     }
245 
246     @Test
247     public void testKepler()
248             throws DimensionMismatchException, NumberIsTooSmallException,
249             MaxCountExceededException, NoBracketingException {
250 
251         final TestProblem3 pb  = new TestProblem3(0.9);
252         double step = (pb.getFinalTime() - pb.getInitialTime()) * 0.0003;
253 
254         FirstOrderIntegrator integ = new LutherIntegrator(step);
255         integ.addStepHandler(new KeplerHandler(pb));
256         integ.integrate(pb,
257                         pb.getInitialTime(), pb.getInitialState(),
258                         pb.getFinalTime(), new double[pb.getDimension()]);
259     }
260 
261     private static final class KeplerHandler implements StepHandler {
262         KeplerHandler(TestProblem3 pb) {
263             this.pb = pb;
264             maxError = 0;
265         }
266         @Override
267         public void init(double t0, double[] y0, double t) {
268             maxError = 0;
269         }
270         @Override
271         public void handleStep(StepInterpolator interpolator, boolean isLast) {
272 
273             double[] interpolatedY = interpolator.getInterpolatedState ();
274             double[] theoreticalY  = pb.computeTheoreticalState(interpolator.getCurrentTime());
275             double dx = interpolatedY[0] - theoreticalY[0];
276             double dy = interpolatedY[1] - theoreticalY[1];
277             double error = dx * dx + dy * dy;
278             if (error > maxError) {
279                 maxError = error;
280             }
281             if (isLast) {
282                 Assert.assertTrue(maxError < 2.2e-7);
283             }
284         }
285         private double maxError = 0;
286         private TestProblem3 pb;
287     }
288 
289     @Test
290     public void testStepSize()
291             throws DimensionMismatchException, NumberIsTooSmallException,
292             MaxCountExceededException, NoBracketingException {
293         final double step = 1.23456;
294         FirstOrderIntegrator integ = new LutherIntegrator(step);
295         integ.addStepHandler(new StepHandler() {
296             @Override
297             public void handleStep(StepInterpolator interpolator, boolean isLast) {
298                 if (! isLast) {
299                     Assert.assertEquals(step,
300                                         interpolator.getCurrentTime() - interpolator.getPreviousTime(),
301                                         1.0e-12);
302                 }
303             }
304             @Override
305             public void init(double t0, double[] y0, double t) {
306             }
307         });
308         integ.integrate(new FirstOrderDifferentialEquations() {
309             @Override
310             public void computeDerivatives(double t, double[] y, double[] dot) {
311                 dot[0] = 1.0;
312             }
313             @Override
314             public int getDimension() {
315                 return 1;
316             }
317         }, 0.0, new double[] { 0.0 }, 5.0, new double[1]);
318     }
319 
320     @Test
321     public void testSingleStep() {
322 
323         final TestProblem3 pb  = new TestProblem3(0.9);
324         double h = (pb.getFinalTime() - pb.getInitialTime()) * 0.0003;
325 
326         RungeKuttaIntegrator integ = new LutherIntegrator(Double.NaN);
327         double   t = pb.getInitialTime();
328         double[] y = pb.getInitialState();
329         for (int i = 0; i < 100; ++i) {
330             y = integ.singleStep(pb, t, y, t + h);
331             t += h;
332         }
333         double[] yth = pb.computeTheoreticalState(t);
334         double dx = y[0] - yth[0];
335         double dy = y[1] - yth[1];
336         double error = dx * dx + dy * dy;
337         Assert.assertEquals(0.0, error, 1.0e-11);
338     }
339 }