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.math.ode.nonstiff;
19  
20  
21  import org.apache.commons.math.ode.AbstractIntegrator;
22  import org.apache.commons.math.ode.DerivativeException;
23  import org.apache.commons.math.ode.FirstOrderDifferentialEquations;
24  import org.apache.commons.math.ode.IntegratorException;
25  import org.apache.commons.math.ode.events.CombinedEventsManager;
26  import org.apache.commons.math.ode.sampling.AbstractStepInterpolator;
27  import org.apache.commons.math.ode.sampling.DummyStepInterpolator;
28  import org.apache.commons.math.ode.sampling.StepHandler;
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 $Revision: 811833 $ $Date: 2009-09-06 12:27:50 -0400 (Sun, 06 Sep 2009) $
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       = Math.abs(step);
92    }
93  
94    /** {@inheritDoc} */
95    public double integrate(final FirstOrderDifferentialEquations equations,
96                            final double t0, final double[] y0,
97                            final double t, final double[] y)
98    throws DerivativeException, IntegratorException {
99  
100     sanityChecks(equations, t0, y0, t, y);
101     setEquations(equations);
102     resetEvaluations();
103     final boolean forward = t > t0;
104 
105     // create some internal working arrays
106     final int stages = c.length + 1;
107     if (y != y0) {
108       System.arraycopy(y0, 0, y, 0, y0.length);
109     }
110     final double[][] yDotK = new double[stages][];
111     for (int i = 0; i < stages; ++i) {
112       yDotK [i] = new double[y0.length];
113     }
114     final double[] yTmp = new double[y0.length];
115 
116     // set up an interpolator sharing the integrator arrays
117     AbstractStepInterpolator interpolator;
118     if (requiresDenseOutput() || (! eventsHandlersManager.isEmpty())) {
119       final RungeKuttaStepInterpolator rki = (RungeKuttaStepInterpolator) prototype.copy();
120       rki.reinitialize(this, yTmp, yDotK, forward);
121       interpolator = rki;
122     } else {
123       interpolator = new DummyStepInterpolator(yTmp, forward);
124     }
125     interpolator.storeTime(t0);
126 
127     // set up integration control objects
128     stepStart = t0;
129     stepSize  = forward ? step : -step;
130     for (StepHandler handler : stepHandlers) {
131         handler.reset();
132     }
133     CombinedEventsManager manager = addEndTimeChecker(t0, t, eventsHandlersManager);
134     boolean lastStep = false;
135 
136     // main integration loop
137     while (!lastStep) {
138 
139       interpolator.shift();
140 
141       for (boolean loop = true; loop;) {
142 
143         // first stage
144         computeDerivatives(stepStart, y, yDotK[0]);
145 
146         // next stages
147         for (int k = 1; k < stages; ++k) {
148 
149           for (int j = 0; j < y0.length; ++j) {
150             double sum = a[k-1][0] * yDotK[0][j];
151             for (int l = 1; l < k; ++l) {
152               sum += a[k-1][l] * yDotK[l][j];
153             }
154             yTmp[j] = y[j] + stepSize * sum;
155           }
156 
157           computeDerivatives(stepStart + c[k-1] * stepSize, yTmp, yDotK[k]);
158 
159         }
160 
161         // estimate the state at the end of the step
162         for (int j = 0; j < y0.length; ++j) {
163           double sum    = b[0] * yDotK[0][j];
164           for (int l = 1; l < stages; ++l) {
165             sum    += b[l] * yDotK[l][j];
166           }
167           yTmp[j] = y[j] + stepSize * sum;
168         }
169 
170         // discrete events handling
171         interpolator.storeTime(stepStart + stepSize);
172         if (manager.evaluateStep(interpolator)) {
173             final double dt = manager.getEventTime() - stepStart;
174             if (Math.abs(dt) <= Math.ulp(stepStart)) {
175                 // rejecting the step would lead to a too small next step, we accept it
176                 loop = false;
177             } else {
178                 // reject the step to match exactly the next switch time
179                 stepSize = dt;
180             }
181         } else {
182           loop = false;
183         }
184 
185       }
186 
187       // the step has been accepted
188       final double nextStep = stepStart + stepSize;
189       System.arraycopy(yTmp, 0, y, 0, y0.length);
190       manager.stepAccepted(nextStep, y);
191       lastStep = manager.stop();
192 
193       // provide the step data to the step handler
194       interpolator.storeTime(nextStep);
195       for (StepHandler handler : stepHandlers) {
196           handler.handleStep(interpolator, lastStep);
197       }
198       stepStart = nextStep;
199 
200       if (manager.reset(stepStart, y) && ! lastStep) {
201         // some events handler has triggered changes that
202         // invalidate the derivatives, we need to recompute them
203         computeDerivatives(stepStart, y, yDotK[0]);
204       }
205 
206       // make sure step size is set to default before next step
207       stepSize = forward ? step : -step;
208 
209     }
210 
211     final double stopTime = stepStart;
212     stepStart = Double.NaN;
213     stepSize  = Double.NaN;
214     return stopTime;
215 
216   }
217 
218 }