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 java.io.ObjectInput;
22  import java.io.ObjectOutput;
23  
24  import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
25  import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
26  import org.apache.commons.math4.legacy.exception.NoBracketingException;
27  import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
28  import org.apache.commons.math4.legacy.ode.AbstractIntegrator;
29  import org.apache.commons.math4.legacy.ode.ExpandableStatefulODE;
30  import org.apache.commons.math4.legacy.ode.FirstOrderIntegrator;
31  import org.apache.commons.math4.legacy.ode.TestProblem1;
32  import org.apache.commons.math4.legacy.ode.TestProblem5;
33  import org.apache.commons.math4.legacy.ode.TestProblem6;
34  import org.apache.commons.math4.legacy.ode.TestProblemAbstract;
35  import org.apache.commons.math4.legacy.ode.TestProblemHandler;
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 AdamsMoultonIntegratorTest {
43  
44      @Test(expected=DimensionMismatchException.class)
45      public void dimensionCheck()
46          throws DimensionMismatchException, NumberIsTooSmallException,
47                 MaxCountExceededException, NoBracketingException {
48          TestProblem1 pb = new TestProblem1();
49          FirstOrderIntegrator integ =
50              new AdamsMoultonIntegrator(2, 0.0, 1.0, 1.0e-10, 1.0e-10);
51          integ.integrate(pb,
52                          0.0, new double[pb.getDimension()+10],
53                          1.0, new double[pb.getDimension()+10]);
54      }
55  
56      @Test(expected=NumberIsTooSmallException.class)
57      public void testMinStep()
58              throws DimensionMismatchException, NumberIsTooSmallException,
59              MaxCountExceededException, NoBracketingException {
60  
61            TestProblem1 pb = new TestProblem1();
62            double minStep = 0.1 * (pb.getFinalTime() - pb.getInitialTime());
63            double maxStep = pb.getFinalTime() - pb.getInitialTime();
64            double[] vecAbsoluteTolerance = { 1.0e-15, 1.0e-16 };
65            double[] vecRelativeTolerance = { 1.0e-15, 1.0e-16 };
66  
67            FirstOrderIntegrator integ = new AdamsMoultonIntegrator(4, minStep, maxStep,
68                                                                    vecAbsoluteTolerance,
69                                                                    vecRelativeTolerance);
70            TestProblemHandler handler = new TestProblemHandler(pb, integ);
71            integ.addStepHandler(handler);
72            integ.integrate(pb,
73                            pb.getInitialTime(), pb.getInitialState(),
74                            pb.getFinalTime(), new double[pb.getDimension()]);
75      }
76  
77      @Test
78      public void testIncreasingTolerance()
79              throws DimensionMismatchException, NumberIsTooSmallException,
80              MaxCountExceededException, NoBracketingException {
81  
82          int previousCalls = Integer.MAX_VALUE;
83          for (int i = -12; i < -2; ++i) {
84              TestProblem1 pb = new TestProblem1();
85              double minStep = 0;
86              double maxStep = pb.getFinalTime() - pb.getInitialTime();
87              double scalAbsoluteTolerance = JdkMath.pow(10.0, i);
88              double scalRelativeTolerance = 0.01 * scalAbsoluteTolerance;
89  
90              FirstOrderIntegrator integ = new AdamsMoultonIntegrator(4, minStep, maxStep,
91                                                                      scalAbsoluteTolerance,
92                                                                      scalRelativeTolerance);
93              TestProblemHandler handler = new TestProblemHandler(pb, integ);
94              integ.addStepHandler(handler);
95              integ.integrate(pb,
96                              pb.getInitialTime(), pb.getInitialState(),
97                              pb.getFinalTime(), new double[pb.getDimension()]);
98  
99              // the 0.45 and 8.69 factors are only valid for this test
100             // and has been obtained from trial and error
101             // there is no general relation between local and global errors
102             Assert.assertTrue(handler.getMaximalValueError() > (0.45 * scalAbsoluteTolerance));
103             Assert.assertTrue(handler.getMaximalValueError() < (8.69 * scalAbsoluteTolerance));
104             Assert.assertEquals(0, handler.getMaximalTimeError(), 1.0e-16);
105 
106             int calls = pb.getCalls();
107             Assert.assertEquals(integ.getEvaluations(), calls);
108             Assert.assertTrue(calls <= previousCalls);
109             previousCalls = calls;
110         }
111     }
112 
113     @Test(expected = MaxCountExceededException.class)
114     public void exceedMaxEvaluations()
115             throws DimensionMismatchException, NumberIsTooSmallException,
116             MaxCountExceededException, NoBracketingException {
117 
118         TestProblem1 pb  = new TestProblem1();
119         double range = pb.getFinalTime() - pb.getInitialTime();
120 
121         AdamsMoultonIntegrator integ = new AdamsMoultonIntegrator(2, 0, range, 1.0e-12, 1.0e-12);
122         TestProblemHandler handler = new TestProblemHandler(pb, integ);
123         integ.addStepHandler(handler);
124         integ.setMaxEvaluations(650);
125         integ.integrate(pb,
126                         pb.getInitialTime(), pb.getInitialState(),
127                         pb.getFinalTime(), new double[pb.getDimension()]);
128     }
129 
130     @Test
131     public void backward()
132             throws DimensionMismatchException, NumberIsTooSmallException,
133             MaxCountExceededException, NoBracketingException {
134 
135         TestProblem5 pb = new TestProblem5();
136         double range = JdkMath.abs(pb.getFinalTime() - pb.getInitialTime());
137 
138         FirstOrderIntegrator integ = new AdamsMoultonIntegrator(4, 0, range, 1.0e-12, 1.0e-12);
139         TestProblemHandler handler = new TestProblemHandler(pb, integ);
140         integ.addStepHandler(handler);
141         integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
142                         pb.getFinalTime(), new double[pb.getDimension()]);
143 
144         Assert.assertTrue(handler.getLastError() < 3.0e-9);
145         Assert.assertTrue(handler.getMaximalValueError() < 3.0e-9);
146         Assert.assertEquals(0, handler.getMaximalTimeError(), 1.0e-16);
147         Assert.assertEquals("Adams-Moulton", integ.getName());
148     }
149 
150     @Test
151     public void polynomial()
152             throws DimensionMismatchException, NumberIsTooSmallException,
153             MaxCountExceededException, NoBracketingException {
154         TestProblem6 pb = new TestProblem6();
155         double range = JdkMath.abs(pb.getFinalTime() - pb.getInitialTime());
156 
157         for (int nSteps = 2; nSteps < 8; ++nSteps) {
158             AdamsMoultonIntegrator integ =
159                 new AdamsMoultonIntegrator(nSteps, 1.0e-6 * range, 0.1 * range, 1.0e-5, 1.0e-5);
160             integ.setStarterIntegrator(new PerfectStarter(pb, nSteps));
161             TestProblemHandler handler = new TestProblemHandler(pb, integ);
162             integ.addStepHandler(handler);
163             integ.integrate(pb, pb.getInitialTime(), pb.getInitialState(),
164                             pb.getFinalTime(), new double[pb.getDimension()]);
165             if (nSteps < 5) {
166                 Assert.assertTrue(handler.getMaximalValueError() > 2.2e-05);
167             } else {
168                 Assert.assertTrue(handler.getMaximalValueError() < 1.1e-11);
169             }
170         }
171     }
172 
173     private static final class PerfectStarter extends AbstractIntegrator {
174 
175         private final PerfectInterpolator interpolator;
176         private final int nbSteps;
177 
178         PerfectStarter(final TestProblemAbstract problem, final int nbSteps) {
179             this.interpolator = new PerfectInterpolator(problem);
180             this.nbSteps      = nbSteps;
181         }
182 
183         @Override
184         public void integrate(ExpandableStatefulODE equations, double t) {
185             double tStart = equations.getTime() + 0.01 * (t - equations.getTime());
186             getCounter().increment(nbSteps);
187             for (int i = 0; i < nbSteps; ++i) {
188                 double tK = ((nbSteps - 1 - (i + 1)) * equations.getTime() + (i + 1) * tStart) / (nbSteps - 1);
189                 interpolator.setPreviousTime(interpolator.getCurrentTime());
190                 interpolator.setCurrentTime(tK);
191                 interpolator.setInterpolatedTime(tK);
192                 for (StepHandler handler : getStepHandlers()) {
193                     handler.handleStep(interpolator, i == nbSteps - 1);
194                 }
195             }
196         }
197     }
198 
199     private static final class PerfectInterpolator implements StepInterpolator {
200         private static final long serialVersionUID = 1;
201         private final TestProblemAbstract problem;
202         private double previousTime;
203         private double currentTime;
204         private double interpolatedTime;
205 
206         PerfectInterpolator(final TestProblemAbstract problem) {
207             this.problem          = problem;
208             this.previousTime     = problem.getInitialTime();
209             this.currentTime      = problem.getInitialTime();
210             this.interpolatedTime = problem.getInitialTime();
211         }
212 
213         @Override
214         public void readExternal(ObjectInput arg0) {
215         }
216 
217         @Override
218         public void writeExternal(ObjectOutput arg0) {
219         }
220 
221         @Override
222         public double getPreviousTime() {
223             return previousTime;
224         }
225 
226         public void setPreviousTime(double time) {
227             previousTime = time;
228         }
229 
230         @Override
231         public double getCurrentTime() {
232             return currentTime;
233         }
234 
235         public void setCurrentTime(double time) {
236             currentTime = time;
237         }
238 
239         @Override
240         public double getInterpolatedTime() {
241             return interpolatedTime;
242         }
243 
244         @Override
245         public void setInterpolatedTime(double time) {
246             interpolatedTime = time;
247         }
248 
249         @Override
250         public double[] getInterpolatedState() {
251             return problem.computeTheoreticalState(interpolatedTime);
252         }
253 
254         @Override
255         public double[] getInterpolatedDerivatives() {
256             double[] y = problem.computeTheoreticalState(interpolatedTime);
257             double[] yDot = new double[y.length];
258             problem.computeDerivatives(interpolatedTime, y, yDot);
259             return yDot;
260         }
261 
262         @Override
263         public double[] getInterpolatedSecondaryState(int index) {
264             return null;
265         }
266 
267         @Override
268         public double[] getInterpolatedSecondaryDerivatives(int index) {
269             return null;
270         }
271 
272         @Override
273         public boolean isForward() {
274             return problem.getFinalTime() > problem.getInitialTime();
275         }
276 
277         @Override
278         public StepInterpolator copy() {
279             return this;
280         }
281     }
282 }