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.math.ode;
19  
20  import org.apache.commons.math.ode.DerivativeException;
21  import org.apache.commons.math.ode.DormandPrince853Integrator;
22  import org.apache.commons.math.ode.DummyStepHandler;
23  import org.apache.commons.math.ode.FirstOrderIntegrator;
24  import org.apache.commons.math.ode.IntegratorException;
25  import org.apache.commons.math.ode.StepHandler;
26  import org.apache.commons.math.ode.StepInterpolator;
27  import org.apache.commons.math.ode.SwitchingFunction;
28  
29  import junit.framework.*;
30  
31  public class DormandPrince853IntegratorTest
32    extends TestCase {
33  
34    public DormandPrince853IntegratorTest(String name) {
35      super(name);
36    }
37  
38    public void testDimensionCheck() {
39      try  {
40        TestProblem1 pb = new TestProblem1();
41        DormandPrince853Integrator integrator = new DormandPrince853Integrator(0.0, 1.0,
42                                                                               1.0e-10, 1.0e-10);
43        integrator.integrate(pb,
44                             0.0, new double[pb.getDimension()+10],
45                             1.0, new double[pb.getDimension()+10]);
46        fail("an exception should have been thrown");
47      } catch(DerivativeException de) {
48        fail("wrong exception caught");
49      } catch(IntegratorException ie) {
50      }
51    }
52  
53    public void testNullIntervalCheck() {
54      try  {
55        TestProblem1 pb = new TestProblem1();
56        DormandPrince853Integrator integrator = new DormandPrince853Integrator(0.0, 1.0,
57                                                                               1.0e-10, 1.0e-10);
58        integrator.integrate(pb,
59                             0.0, new double[pb.getDimension()],
60                             0.0, new double[pb.getDimension()]);
61        fail("an exception should have been thrown");
62      } catch(DerivativeException de) {
63        fail("wrong exception caught");
64      } catch(IntegratorException ie) {
65      }
66    }
67  
68    public void testMinStep()
69      throws DerivativeException, IntegratorException {
70  
71      try {
72        TestProblem1 pb = new TestProblem1();
73        double minStep = 0.1 * (pb.getFinalTime() - pb.getInitialTime());
74        double maxStep = pb.getFinalTime() - pb.getInitialTime();
75        double[] vecAbsoluteTolerance = { 1.0e-15, 1.0e-16 };
76        double[] vecRelativeTolerance = { 1.0e-15, 1.0e-16 };
77  
78        FirstOrderIntegrator integ = new DormandPrince853Integrator(minStep, maxStep,
79                                                                    vecAbsoluteTolerance,
80                                                                    vecRelativeTolerance);
81        TestProblemHandler handler = new TestProblemHandler(pb, integ);
82        integ.setStepHandler(handler);
83        integ.integrate(pb,
84                        pb.getInitialTime(), pb.getInitialState(),
85                        pb.getFinalTime(), new double[pb.getDimension()]);
86        fail("an exception should have been thrown");
87      } catch(DerivativeException de) {
88        fail("wrong exception caught");
89      } catch(IntegratorException ie) {
90      }
91  
92    }
93  
94    public void testIncreasingTolerance()
95      throws DerivativeException, IntegratorException {
96  
97      int previousCalls = Integer.MAX_VALUE;
98      for (int i = -12; i < -2; ++i) {
99        TestProblem1 pb = new TestProblem1();
100       double minStep = 0;
101       double maxStep = pb.getFinalTime() - pb.getInitialTime();
102       double scalAbsoluteTolerance = Math.pow(10.0, i);
103       double scalRelativeTolerance = 0.01 * scalAbsoluteTolerance;
104 
105       FirstOrderIntegrator integ = new DormandPrince853Integrator(minStep, maxStep,
106                                                                   scalAbsoluteTolerance,
107                                                                   scalRelativeTolerance);
108       TestProblemHandler handler = new TestProblemHandler(pb, integ);
109       integ.setStepHandler(handler);
110       integ.integrate(pb,
111                       pb.getInitialTime(), pb.getInitialState(),
112                       pb.getFinalTime(), new double[pb.getDimension()]);
113 
114       // the 1.3 factor is only valid for this test
115       // and has been obtained from trial and error
116       // there is no general relation between local and global errors
117       assertTrue(handler.getMaximalValueError() < (1.3 * scalAbsoluteTolerance));
118       assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
119 
120       int calls = pb.getCalls();
121       assertTrue(calls <= previousCalls);
122       previousCalls = calls;
123 
124     }
125 
126   }
127 
128   public void testSwitchingFunctions()
129     throws DerivativeException, IntegratorException {
130 
131     TestProblem4 pb = new TestProblem4();
132     double minStep = 0;
133     double maxStep = pb.getFinalTime() - pb.getInitialTime();
134     double scalAbsoluteTolerance = 1.0e-9;
135     double scalRelativeTolerance = 0.01 * scalAbsoluteTolerance;
136 
137     FirstOrderIntegrator integ = new DormandPrince853Integrator(minStep, maxStep,
138                                                                 scalAbsoluteTolerance,
139                                                                 scalRelativeTolerance);
140     TestProblemHandler handler = new TestProblemHandler(pb, integ);
141     integ.setStepHandler(handler);
142     SwitchingFunction[] functions = pb.getSwitchingFunctions();
143     for (int l = 0; l < functions.length; ++l) {
144       integ.addSwitchingFunction(functions[l],
145                                  Double.POSITIVE_INFINITY, 1.0e-8 * maxStep, 1000);
146     }
147     integ.integrate(pb,
148                     pb.getInitialTime(), pb.getInitialState(),
149                     pb.getFinalTime(), new double[pb.getDimension()]);
150 
151     assertTrue(handler.getMaximalValueError() < 5.0e-8);
152     assertEquals(0, handler.getMaximalTimeError(), 1.0e-12);
153     assertEquals(12.0, handler.getLastTime(), 1.0e-8 * maxStep);
154 
155   }
156 
157   public void testKepler()
158     throws DerivativeException, IntegratorException {
159 
160     final TestProblem3 pb  = new TestProblem3(0.9);
161     double minStep = 0;
162     double maxStep = pb.getFinalTime() - pb.getInitialTime();
163     double scalAbsoluteTolerance = 1.0e-8;
164     double scalRelativeTolerance = scalAbsoluteTolerance;
165 
166     FirstOrderIntegrator integ = new DormandPrince853Integrator(minStep, maxStep,
167                                                                 scalAbsoluteTolerance,
168                                                                 scalRelativeTolerance);
169     integ.setStepHandler(new KeplerHandler(pb));
170     integ.integrate(pb,
171                     pb.getInitialTime(), pb.getInitialState(),
172                     pb.getFinalTime(), new double[pb.getDimension()]);
173 
174     assertTrue(pb.getCalls() < 2900);
175 
176   }
177 
178   public void testVariableSteps()
179     throws DerivativeException, IntegratorException {
180 
181     final TestProblem3 pb  = new TestProblem3(0.9);
182     double minStep = 0;
183     double maxStep = pb.getFinalTime() - pb.getInitialTime();
184     double scalAbsoluteTolerance = 1.0e-8;
185     double scalRelativeTolerance = scalAbsoluteTolerance;
186 
187     FirstOrderIntegrator integ = new DormandPrince853Integrator(minStep, maxStep,
188                                                                scalAbsoluteTolerance,
189                                                                scalRelativeTolerance);
190     integ.setStepHandler(new VariableHandler());
191     integ.integrate(pb,
192                     pb.getInitialTime(), pb.getInitialState(),
193                     pb.getFinalTime(), new double[pb.getDimension()]);
194     assertEquals("Dormand-Prince 8 (5, 3)", integ.getName());
195   }
196 
197   public void testNoDenseOutput()
198     throws DerivativeException, IntegratorException {
199     TestProblem1 pb1 = new TestProblem1();
200     TestProblem1 pb2 = (TestProblem1) pb1.clone();
201     double minStep = 0.1 * (pb1.getFinalTime() - pb1.getInitialTime());
202     double maxStep = pb1.getFinalTime() - pb1.getInitialTime();
203     double scalAbsoluteTolerance = 1.0e-4;
204     double scalRelativeTolerance = 1.0e-4;
205 
206     FirstOrderIntegrator integ = new DormandPrince853Integrator(minStep, maxStep,
207                                                                 scalAbsoluteTolerance,
208                                                                 scalRelativeTolerance);
209     integ.setStepHandler(DummyStepHandler.getInstance());
210     integ.integrate(pb1,
211                     pb1.getInitialTime(), pb1.getInitialState(),
212                     pb1.getFinalTime(), new double[pb1.getDimension()]);
213     int callsWithoutDenseOutput = pb1.getCalls();
214 
215     integ.setStepHandler(new InterpolatingStepHandler());
216     integ.integrate(pb2,
217                     pb2.getInitialTime(), pb2.getInitialState(),
218                     pb2.getFinalTime(), new double[pb2.getDimension()]);
219     int callsWithDenseOutput = pb2.getCalls();
220 
221     assertTrue(callsWithDenseOutput > callsWithoutDenseOutput);
222 
223   }
224 
225   public void testUnstableDerivative()
226   throws DerivativeException, IntegratorException {
227     final StepProblem stepProblem = new StepProblem(0.0, 1.0, 2.0);
228     FirstOrderIntegrator integ =
229       new DormandPrince853Integrator(0.1, 10, 1.0e-12, 0.0);
230     integ.addSwitchingFunction(stepProblem, 1.0, 1.0e-12, 1000);
231     double[] y = { Double.NaN };
232     integ.integrate(stepProblem, 0.0, new double[] { 0.0 }, 10.0, y);
233     assertEquals(8.0, y[0], 1.0e-12);
234   }
235 
236   private static class KeplerHandler implements StepHandler {
237     public KeplerHandler(TestProblem3 pb) {
238       this.pb = pb;
239       reset();
240     }
241     public boolean requiresDenseOutput() {
242       return true;
243     }
244     public void reset() {
245       nbSteps = 0;
246       maxError = 0;
247     }
248     public void handleStep(StepInterpolator interpolator,
249                            boolean isLast)
250     throws DerivativeException {
251 
252       ++nbSteps;
253       for (int a = 1; a < 10; ++a) {
254 
255         double prev   = interpolator.getPreviousTime();
256         double curr   = interpolator.getCurrentTime();
257         double interp = ((10 - a) * prev + a * curr) / 10;
258         interpolator.setInterpolatedTime(interp);
259 
260         double[] interpolatedY = interpolator.getInterpolatedState ();
261         double[] theoreticalY  = pb.computeTheoreticalState(interpolator.getInterpolatedTime());
262         double dx = interpolatedY[0] - theoreticalY[0];
263         double dy = interpolatedY[1] - theoreticalY[1];
264         double error = dx * dx + dy * dy;
265         if (error > maxError) {
266           maxError = error;
267         }
268       }
269       if (isLast) {
270         assertTrue(maxError < 2.4e-10);
271         assertTrue(nbSteps < 150);
272       }
273     }
274     private int nbSteps;
275     private double maxError;
276     private TestProblem3 pb;
277   }
278 
279   private static class VariableHandler implements StepHandler {
280     public VariableHandler() {
281       reset();
282     }
283     public boolean requiresDenseOutput() {
284       return false;
285     }
286     public void reset() {
287       firstTime = true;
288       minStep = 0;
289       maxStep = 0;
290     }
291     public void handleStep(StepInterpolator interpolator,
292                            boolean isLast) {
293 
294       double step = Math.abs(interpolator.getCurrentTime()
295                              - interpolator.getPreviousTime());
296       if (firstTime) {
297         minStep   = Math.abs(step);
298         maxStep   = minStep;
299         firstTime = false;
300       } else {
301         if (step < minStep) {
302           minStep = step;
303         }
304         if (step > maxStep) {
305           maxStep = step;
306         }
307       }
308 
309       if (isLast) {
310         assertTrue(minStep < (1.0 / 100.0));
311         assertTrue(maxStep > (1.0 / 2.0));
312       }
313     }
314     private boolean firstTime = true;
315     private double  minStep = 0;
316     private double  maxStep = 0;
317   }
318 
319   private static class InterpolatingStepHandler implements StepHandler {
320     public boolean requiresDenseOutput() {
321       return true;
322     }
323     public void reset() {
324     }
325     public void handleStep(StepInterpolator interpolator,
326                            boolean isLast)
327     throws DerivativeException {
328       double prev = interpolator.getPreviousTime();
329       double curr = interpolator.getCurrentTime();
330       interpolator.setInterpolatedTime(0.5*(prev + curr));
331     }
332   }
333 
334   public static Test suite() {
335     return new TestSuite(DormandPrince853IntegratorTest.class);
336   }
337 
338 }