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.math3.ode.nonstiff;
19  
20  
21  import org.apache.commons.math3.exception.DimensionMismatchException;
22  import org.apache.commons.math3.exception.MaxCountExceededException;
23  import org.apache.commons.math3.exception.NoBracketingException;
24  import org.apache.commons.math3.exception.NumberIsTooSmallException;
25  import org.apache.commons.math3.ode.AbstractIntegrator;
26  import org.apache.commons.math3.ode.ExpandableStatefulODE;
27  import org.apache.commons.math3.ode.FirstOrderDifferentialEquations;
28  import org.apache.commons.math3.util.FastMath;
29  
30  /**
31   * This class implements the common part of all fixed step Runge-Kutta
32   * integrators for Ordinary Differential Equations.
33   *
34   * <p>These methods are explicit Runge-Kutta methods, their Butcher
35   * arrays are as follows :
36   * <pre>
37   *    0  |
38   *   c2  | a21
39   *   c3  | a31  a32
40   *   ... |        ...
41   *   cs  | as1  as2  ...  ass-1
42   *       |--------------------------
43   *       |  b1   b2  ...   bs-1  bs
44   * </pre>
45   * </p>
46   *
47   * @see EulerIntegrator
48   * @see ClassicalRungeKuttaIntegrator
49   * @see GillIntegrator
50   * @see MidpointIntegrator
51   * @version $Id: RungeKuttaIntegrator.java 1588769 2014-04-20 14:29:42Z luc $
52   * @since 1.2
53   */
54  
55  public abstract class RungeKuttaIntegrator extends AbstractIntegrator {
56  
57      /** Time steps from Butcher array (without the first zero). */
58      private final double[] c;
59  
60      /** Internal weights from Butcher array (without the first empty row). */
61      private final double[][] a;
62  
63      /** External weights for the high order method from Butcher array. */
64      private final double[] b;
65  
66      /** Prototype of the step interpolator. */
67      private final RungeKuttaStepInterpolator prototype;
68  
69      /** Integration step. */
70      private final double step;
71  
72    /** Simple constructor.
73     * Build a Runge-Kutta integrator with the given
74     * step. The default step handler does nothing.
75     * @param name name of the method
76     * @param c time steps from Butcher array (without the first zero)
77     * @param a internal weights from Butcher array (without the first empty row)
78     * @param b propagation weights for the high order method from Butcher array
79     * @param prototype prototype of the step interpolator to use
80     * @param step integration step
81     */
82    protected RungeKuttaIntegrator(final String name,
83                                   final double[] c, final double[][] a, final double[] b,
84                                   final RungeKuttaStepInterpolator prototype,
85                                   final double step) {
86      super(name);
87      this.c          = c;
88      this.a          = a;
89      this.b          = b;
90      this.prototype  = prototype;
91      this.step       = FastMath.abs(step);
92    }
93  
94    /** {@inheritDoc} */
95    @Override
96    public void integrate(final ExpandableStatefulODE equations, final double t)
97        throws NumberIsTooSmallException, DimensionMismatchException,
98               MaxCountExceededException, NoBracketingException {
99  
100     sanityChecks(equations, t);
101     setEquations(equations);
102     final boolean forward = t > equations.getTime();
103 
104     // create some internal working arrays
105     final double[] y0      = equations.getCompleteState();
106     final double[] y       = y0.clone();
107     final int stages       = c.length + 1;
108     final double[][] yDotK = new double[stages][];
109     for (int i = 0; i < stages; ++i) {
110       yDotK [i] = new double[y0.length];
111     }
112     final double[] yTmp    = y0.clone();
113     final double[] yDotTmp = new double[y0.length];
114 
115     // set up an interpolator sharing the integrator arrays
116     final RungeKuttaStepInterpolator interpolator = (RungeKuttaStepInterpolator) prototype.copy();
117     interpolator.reinitialize(this, yTmp, yDotK, forward,
118                               equations.getPrimaryMapper(), equations.getSecondaryMappers());
119     interpolator.storeTime(equations.getTime());
120 
121     // set up integration control objects
122     stepStart = equations.getTime();
123     stepSize  = forward ? step : -step;
124     initIntegration(equations.getTime(), y0, t);
125 
126     // main integration loop
127     isLastStep = false;
128     do {
129 
130       interpolator.shift();
131 
132       // first stage
133       computeDerivatives(stepStart, y, yDotK[0]);
134 
135       // next stages
136       for (int k = 1; k < stages; ++k) {
137 
138           for (int j = 0; j < y0.length; ++j) {
139               double sum = a[k-1][0] * yDotK[0][j];
140               for (int l = 1; l < k; ++l) {
141                   sum += a[k-1][l] * yDotK[l][j];
142               }
143               yTmp[j] = y[j] + stepSize * sum;
144           }
145 
146           computeDerivatives(stepStart + c[k-1] * stepSize, yTmp, yDotK[k]);
147 
148       }
149 
150       // estimate the state at the end of the step
151       for (int j = 0; j < y0.length; ++j) {
152           double sum    = b[0] * yDotK[0][j];
153           for (int l = 1; l < stages; ++l) {
154               sum    += b[l] * yDotK[l][j];
155           }
156           yTmp[j] = y[j] + stepSize * sum;
157       }
158 
159       // discrete events handling
160       interpolator.storeTime(stepStart + stepSize);
161       System.arraycopy(yTmp, 0, y, 0, y0.length);
162       System.arraycopy(yDotK[stages - 1], 0, yDotTmp, 0, y0.length);
163       stepStart = acceptStep(interpolator, y, yDotTmp, t);
164 
165       if (!isLastStep) {
166 
167           // prepare next step
168           interpolator.storeTime(stepStart);
169 
170           // stepsize control for next step
171           final double  nextT      = stepStart + stepSize;
172           final boolean nextIsLast = forward ? (nextT >= t) : (nextT <= t);
173           if (nextIsLast) {
174               stepSize = t - stepStart;
175           }
176       }
177 
178     } while (!isLastStep);
179 
180     // dispatch results
181     equations.setTime(stepStart);
182     equations.setCompleteState(y);
183 
184     stepStart = Double.NaN;
185     stepSize  = Double.NaN;
186 
187   }
188 
189   /** Fast computation of a single step of ODE integration.
190    * <p>This method is intended for the limited use case of
191    * very fast computation of only one step without using any of the
192    * rich features of general integrators that may take some time
193    * to set up (i.e. no step handlers, no events handlers, no additional
194    * states, no interpolators, no error control, no evaluations count,
195    * no sanity checks ...). It handles the strict minimum of computation,
196    * so it can be embedded in outer loops.</p>
197    * <p>
198    * This method is <em>not</em> used at all by the {@link #integrate(ExpandableStatefulODE, double)}
199    * method. It also completely ignores the step set at construction time, and
200    * uses only a single step to go from {@code t0} to {@code t}.
201    * </p>
202    * <p>
203    * As this method does not use any of the state-dependent features of the integrator,
204    * it should be reasonably thread-safe <em>if and only if</em> the provided differential
205    * equations are themselves thread-safe.
206    * </p>
207    * @param equations differential equations to integrate
208    * @param t0 initial time
209    * @param y0 initial value of the state vector at t0
210    * @param t target time for the integration
211    * (can be set to a value smaller than {@code t0} for backward integration)
212    * @return state vector at {@code t}
213    */
214   public double[] singleStep(final FirstOrderDifferentialEquations equations,
215                              final double t0, final double[] y0, final double t) {
216 
217       // create some internal working arrays
218       final double[] y       = y0.clone();
219       final int stages       = c.length + 1;
220       final double[][] yDotK = new double[stages][];
221       for (int i = 0; i < stages; ++i) {
222           yDotK [i] = new double[y0.length];
223       }
224       final double[] yTmp    = y0.clone();
225 
226       // first stage
227       final double h = t - t0;
228       equations.computeDerivatives(t0, y, yDotK[0]);
229 
230       // next stages
231       for (int k = 1; k < stages; ++k) {
232 
233           for (int j = 0; j < y0.length; ++j) {
234               double sum = a[k-1][0] * yDotK[0][j];
235               for (int l = 1; l < k; ++l) {
236                   sum += a[k-1][l] * yDotK[l][j];
237               }
238               yTmp[j] = y[j] + h * sum;
239           }
240 
241           equations.computeDerivatives(t0 + c[k-1] * h, yTmp, yDotK[k]);
242 
243       }
244 
245       // estimate the state at the end of the step
246       for (int j = 0; j < y0.length; ++j) {
247           double sum = b[0] * yDotK[0][j];
248           for (int l = 1; l < stages; ++l) {
249               sum += b[l] * yDotK[l][j];
250           }
251           y[j] += h * sum;
252       }
253 
254       return y;
255 
256   }
257 
258 }