001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018package org.apache.commons.math3.ode.nonstiff;
019
020
021import org.apache.commons.math3.exception.DimensionMismatchException;
022import org.apache.commons.math3.exception.MaxCountExceededException;
023import org.apache.commons.math3.exception.NoBracketingException;
024import org.apache.commons.math3.exception.NumberIsTooSmallException;
025import org.apache.commons.math3.ode.AbstractIntegrator;
026import org.apache.commons.math3.ode.ExpandableStatefulODE;
027import org.apache.commons.math3.ode.FirstOrderDifferentialEquations;
028import org.apache.commons.math3.util.FastMath;
029
030/**
031 * This class implements the common part of all fixed step Runge-Kutta
032 * integrators for Ordinary Differential Equations.
033 *
034 * <p>These methods are explicit Runge-Kutta methods, their Butcher
035 * arrays are as follows :
036 * <pre>
037 *    0  |
038 *   c2  | a21
039 *   c3  | a31  a32
040 *   ... |        ...
041 *   cs  | as1  as2  ...  ass-1
042 *       |--------------------------
043 *       |  b1   b2  ...   bs-1  bs
044 * </pre>
045 * </p>
046 *
047 * @see EulerIntegrator
048 * @see ClassicalRungeKuttaIntegrator
049 * @see GillIntegrator
050 * @see MidpointIntegrator
051 * @since 1.2
052 */
053
054public abstract class RungeKuttaIntegrator extends AbstractIntegrator {
055
056    /** Time steps from Butcher array (without the first zero). */
057    private final double[] c;
058
059    /** Internal weights from Butcher array (without the first empty row). */
060    private final double[][] a;
061
062    /** External weights for the high order method from Butcher array. */
063    private final double[] b;
064
065    /** Prototype of the step interpolator. */
066    private final RungeKuttaStepInterpolator prototype;
067
068    /** Integration step. */
069    private final double step;
070
071  /** Simple constructor.
072   * Build a Runge-Kutta integrator with the given
073   * step. The default step handler does nothing.
074   * @param name name of the method
075   * @param c time steps from Butcher array (without the first zero)
076   * @param a internal weights from Butcher array (without the first empty row)
077   * @param b propagation weights for the high order method from Butcher array
078   * @param prototype prototype of the step interpolator to use
079   * @param step integration step
080   */
081  protected RungeKuttaIntegrator(final String name,
082                                 final double[] c, final double[][] a, final double[] b,
083                                 final RungeKuttaStepInterpolator prototype,
084                                 final double step) {
085    super(name);
086    this.c          = c;
087    this.a          = a;
088    this.b          = b;
089    this.prototype  = prototype;
090    this.step       = FastMath.abs(step);
091  }
092
093  /** {@inheritDoc} */
094  @Override
095  public void integrate(final ExpandableStatefulODE equations, final double t)
096      throws NumberIsTooSmallException, DimensionMismatchException,
097             MaxCountExceededException, NoBracketingException {
098
099    sanityChecks(equations, t);
100    setEquations(equations);
101    final boolean forward = t > equations.getTime();
102
103    // create some internal working arrays
104    final double[] y0      = equations.getCompleteState();
105    final double[] y       = y0.clone();
106    final int stages       = c.length + 1;
107    final double[][] yDotK = new double[stages][];
108    for (int i = 0; i < stages; ++i) {
109      yDotK [i] = new double[y0.length];
110    }
111    final double[] yTmp    = y0.clone();
112    final double[] yDotTmp = new double[y0.length];
113
114    // set up an interpolator sharing the integrator arrays
115    final RungeKuttaStepInterpolator interpolator = (RungeKuttaStepInterpolator) prototype.copy();
116    interpolator.reinitialize(this, yTmp, yDotK, forward,
117                              equations.getPrimaryMapper(), equations.getSecondaryMappers());
118    interpolator.storeTime(equations.getTime());
119
120    // set up integration control objects
121    stepStart = equations.getTime();
122    if (forward) {
123        if (stepStart + step >= t) {
124            stepSize = t - stepStart;
125        } else {
126            stepSize = step;
127        }
128    } else {
129        if (stepStart - step <= t) {
130            stepSize = t - stepStart;
131        } else {
132            stepSize = -step;
133        }
134    }
135    initIntegration(equations.getTime(), y0, t);
136
137    // main integration loop
138    isLastStep = false;
139    do {
140
141      interpolator.shift();
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      System.arraycopy(yTmp, 0, y, 0, y0.length);
173      System.arraycopy(yDotK[stages - 1], 0, yDotTmp, 0, y0.length);
174      stepStart = acceptStep(interpolator, y, yDotTmp, t);
175
176      if (!isLastStep) {
177
178          // prepare next step
179          interpolator.storeTime(stepStart);
180
181          // stepsize control for next step
182          final double  nextT      = stepStart + stepSize;
183          final boolean nextIsLast = forward ? (nextT >= t) : (nextT <= t);
184          if (nextIsLast) {
185              stepSize = t - stepStart;
186          }
187      }
188
189    } while (!isLastStep);
190
191    // dispatch results
192    equations.setTime(stepStart);
193    equations.setCompleteState(y);
194
195    stepStart = Double.NaN;
196    stepSize  = Double.NaN;
197
198  }
199
200  /** Fast computation of a single step of ODE integration.
201   * <p>This method is intended for the limited use case of
202   * very fast computation of only one step without using any of the
203   * rich features of general integrators that may take some time
204   * to set up (i.e. no step handlers, no events handlers, no additional
205   * states, no interpolators, no error control, no evaluations count,
206   * no sanity checks ...). It handles the strict minimum of computation,
207   * so it can be embedded in outer loops.</p>
208   * <p>
209   * This method is <em>not</em> used at all by the {@link #integrate(ExpandableStatefulODE, double)}
210   * method. It also completely ignores the step set at construction time, and
211   * uses only a single step to go from {@code t0} to {@code t}.
212   * </p>
213   * <p>
214   * As this method does not use any of the state-dependent features of the integrator,
215   * it should be reasonably thread-safe <em>if and only if</em> the provided differential
216   * equations are themselves thread-safe.
217   * </p>
218   * @param equations differential equations to integrate
219   * @param t0 initial time
220   * @param y0 initial value of the state vector at t0
221   * @param t target time for the integration
222   * (can be set to a value smaller than {@code t0} for backward integration)
223   * @return state vector at {@code t}
224   */
225  public double[] singleStep(final FirstOrderDifferentialEquations equations,
226                             final double t0, final double[] y0, final double t) {
227
228      // create some internal working arrays
229      final double[] y       = y0.clone();
230      final int stages       = c.length + 1;
231      final double[][] yDotK = new double[stages][];
232      for (int i = 0; i < stages; ++i) {
233          yDotK [i] = new double[y0.length];
234      }
235      final double[] yTmp    = y0.clone();
236
237      // first stage
238      final double h = t - t0;
239      equations.computeDerivatives(t0, y, yDotK[0]);
240
241      // next stages
242      for (int k = 1; k < stages; ++k) {
243
244          for (int j = 0; j < y0.length; ++j) {
245              double sum = a[k-1][0] * yDotK[0][j];
246              for (int l = 1; l < k; ++l) {
247                  sum += a[k-1][l] * yDotK[l][j];
248              }
249              yTmp[j] = y[j] + h * sum;
250          }
251
252          equations.computeDerivatives(t0 + c[k-1] * h, yTmp, yDotK[k]);
253
254      }
255
256      // estimate the state at the end of the step
257      for (int j = 0; j < y0.length; ++j) {
258          double sum = b[0] * yDotK[0][j];
259          for (int l = 1; l < stages; ++l) {
260              sum += b[l] * yDotK[l][j];
261          }
262          y[j] += h * sum;
263      }
264
265      return y;
266
267  }
268
269}