summaryrefslogtreecommitdiff
path: root/src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerIntegrator.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerIntegrator.java')
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerIntegrator.java949
1 files changed, 949 insertions, 0 deletions
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerIntegrator.java
new file mode 100644
index 0000000..50a463e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerIntegrator.java
@@ -0,0 +1,949 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.analysis.solvers.UnivariateSolver;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.ode.ExpandableStatefulODE;
+import org.apache.commons.math3.ode.events.EventHandler;
+import org.apache.commons.math3.ode.sampling.AbstractStepInterpolator;
+import org.apache.commons.math3.ode.sampling.StepHandler;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class implements a Gragg-Bulirsch-Stoer integrator for
+ * Ordinary Differential Equations.
+ *
+ * <p>The Gragg-Bulirsch-Stoer algorithm is one of the most efficient
+ * ones currently available for smooth problems. It uses Richardson
+ * extrapolation to estimate what would be the solution if the step
+ * size could be decreased down to zero.</p>
+ *
+ * <p>
+ * This method changes both the step size and the order during
+ * integration, in order to minimize computation cost. It is
+ * particularly well suited when a very high precision is needed. The
+ * limit where this method becomes more efficient than high-order
+ * embedded Runge-Kutta methods like {@link DormandPrince853Integrator
+ * Dormand-Prince 8(5,3)} depends on the problem. Results given in the
+ * Hairer, Norsett and Wanner book show for example that this limit
+ * occurs for accuracy around 1e-6 when integrating Saltzam-Lorenz
+ * equations (the authors note this problem is <i>extremely sensitive
+ * to the errors in the first integration steps</i>), and around 1e-11
+ * for a two dimensional celestial mechanics problems with seven
+ * bodies (pleiades problem, involving quasi-collisions for which
+ * <i>automatic step size control is essential</i>).
+ * </p>
+ *
+ * <p>
+ * This implementation is basically a reimplementation in Java of the
+ * <a
+ * href="http://www.unige.ch/math/folks/hairer/prog/nonstiff/odex.f">odex</a>
+ * fortran code by E. Hairer and G. Wanner. The redistribution policy
+ * for this code is available <a
+ * href="http://www.unige.ch/~hairer/prog/licence.txt">here</a>, for
+ * convenience, it is reproduced below.</p>
+ * </p>
+ *
+ * <table border="0" width="80%" cellpadding="10" align="center" bgcolor="#E0E0E0">
+ * <tr><td>Copyright (c) 2004, Ernst Hairer</td></tr>
+ *
+ * <tr><td>Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ * <ul>
+ * <li>Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.</li>
+ * <li>Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.</li>
+ * </ul></td></tr>
+ *
+ * <tr><td><strong>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</strong></td></tr>
+ * </table>
+ *
+ * @since 1.2
+ */
+
+public class GraggBulirschStoerIntegrator extends AdaptiveStepsizeIntegrator {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Gragg-Bulirsch-Stoer";
+
+ /** maximal order. */
+ private int maxOrder;
+
+ /** step size sequence. */
+ private int[] sequence;
+
+ /** overall cost of applying step reduction up to iteration k+1, in number of calls. */
+ private int[] costPerStep;
+
+ /** cost per unit step. */
+ private double[] costPerTimeUnit;
+
+ /** optimal steps for each order. */
+ private double[] optimalStep;
+
+ /** extrapolation coefficients. */
+ private double[][] coeff;
+
+ /** stability check enabling parameter. */
+ private boolean performTest;
+
+ /** maximal number of checks for each iteration. */
+ private int maxChecks;
+
+ /** maximal number of iterations for which checks are performed. */
+ private int maxIter;
+
+ /** stepsize reduction factor in case of stability check failure. */
+ private double stabilityReduction;
+
+ /** first stepsize control factor. */
+ private double stepControl1;
+
+ /** second stepsize control factor. */
+ private double stepControl2;
+
+ /** third stepsize control factor. */
+ private double stepControl3;
+
+ /** fourth stepsize control factor. */
+ private double stepControl4;
+
+ /** first order control factor. */
+ private double orderControl1;
+
+ /** second order control factor. */
+ private double orderControl2;
+
+ /** use interpolation error in stepsize control. */
+ private boolean useInterpolationError;
+
+ /** interpolation order control parameter. */
+ private int mudif;
+
+ /** Simple constructor.
+ * Build a Gragg-Bulirsch-Stoer integrator with the given step
+ * bounds. All tuning parameters are set to their default
+ * values. The default step handler does nothing.
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ public GraggBulirschStoerIntegrator(final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+ super(METHOD_NAME, minStep, maxStep,
+ scalAbsoluteTolerance, scalRelativeTolerance);
+ setStabilityCheck(true, -1, -1, -1);
+ setControlFactors(-1, -1, -1, -1);
+ setOrderControl(-1, -1, -1);
+ setInterpolationControl(true, -1);
+ }
+
+ /** Simple constructor.
+ * Build a Gragg-Bulirsch-Stoer integrator with the given step
+ * bounds. All tuning parameters are set to their default
+ * values. The default step handler does nothing.
+ * @param minStep minimal step (must be positive even for backward
+ * integration), the last step can be smaller than this
+ * @param maxStep maximal step (must be positive even for backward
+ * integration)
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ public GraggBulirschStoerIntegrator(final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+ super(METHOD_NAME, minStep, maxStep,
+ vecAbsoluteTolerance, vecRelativeTolerance);
+ setStabilityCheck(true, -1, -1, -1);
+ setControlFactors(-1, -1, -1, -1);
+ setOrderControl(-1, -1, -1);
+ setInterpolationControl(true, -1);
+ }
+
+ /** Set the stability check controls.
+ * <p>The stability check is performed on the first few iterations of
+ * the extrapolation scheme. If this test fails, the step is rejected
+ * and the stepsize is reduced.</p>
+ * <p>By default, the test is performed, at most during two
+ * iterations at each step, and at most once for each of these
+ * iterations. The default stepsize reduction factor is 0.5.</p>
+ * @param performStabilityCheck if true, stability check will be performed,
+ if false, the check will be skipped
+ * @param maxNumIter maximal number of iterations for which checks are
+ * performed (the number of iterations is reset to default if negative
+ * or null)
+ * @param maxNumChecks maximal number of checks for each iteration
+ * (the number of checks is reset to default if negative or null)
+ * @param stepsizeReductionFactor stepsize reduction factor in case of
+ * failure (the factor is reset to default if lower than 0.0001 or
+ * greater than 0.9999)
+ */
+ public void setStabilityCheck(final boolean performStabilityCheck,
+ final int maxNumIter, final int maxNumChecks,
+ final double stepsizeReductionFactor) {
+
+ this.performTest = performStabilityCheck;
+ this.maxIter = (maxNumIter <= 0) ? 2 : maxNumIter;
+ this.maxChecks = (maxNumChecks <= 0) ? 1 : maxNumChecks;
+
+ if ((stepsizeReductionFactor < 0.0001) || (stepsizeReductionFactor > 0.9999)) {
+ this.stabilityReduction = 0.5;
+ } else {
+ this.stabilityReduction = stepsizeReductionFactor;
+ }
+
+ }
+
+ /** Set the step size control factors.
+
+ * <p>The new step size hNew is computed from the old one h by:
+ * <pre>
+ * hNew = h * stepControl2 / (err/stepControl1)^(1/(2k+1))
+ * </pre>
+ * where err is the scaled error and k the iteration number of the
+ * extrapolation scheme (counting from 0). The default values are
+ * 0.65 for stepControl1 and 0.94 for stepControl2.</p>
+ * <p>The step size is subject to the restriction:
+ * <pre>
+ * stepControl3^(1/(2k+1))/stepControl4 <= hNew/h <= 1/stepControl3^(1/(2k+1))
+ * </pre>
+ * The default values are 0.02 for stepControl3 and 4.0 for
+ * stepControl4.</p>
+ * @param control1 first stepsize control factor (the factor is
+ * reset to default if lower than 0.0001 or greater than 0.9999)
+ * @param control2 second stepsize control factor (the factor
+ * is reset to default if lower than 0.0001 or greater than 0.9999)
+ * @param control3 third stepsize control factor (the factor is
+ * reset to default if lower than 0.0001 or greater than 0.9999)
+ * @param control4 fourth stepsize control factor (the factor
+ * is reset to default if lower than 1.0001 or greater than 999.9)
+ */
+ public void setControlFactors(final double control1, final double control2,
+ final double control3, final double control4) {
+
+ if ((control1 < 0.0001) || (control1 > 0.9999)) {
+ this.stepControl1 = 0.65;
+ } else {
+ this.stepControl1 = control1;
+ }
+
+ if ((control2 < 0.0001) || (control2 > 0.9999)) {
+ this.stepControl2 = 0.94;
+ } else {
+ this.stepControl2 = control2;
+ }
+
+ if ((control3 < 0.0001) || (control3 > 0.9999)) {
+ this.stepControl3 = 0.02;
+ } else {
+ this.stepControl3 = control3;
+ }
+
+ if ((control4 < 1.0001) || (control4 > 999.9)) {
+ this.stepControl4 = 4.0;
+ } else {
+ this.stepControl4 = control4;
+ }
+
+ }
+
+ /** Set the order control parameters.
+ * <p>The Gragg-Bulirsch-Stoer method changes both the step size and
+ * the order during integration, in order to minimize computation
+ * cost. Each extrapolation step increases the order by 2, so the
+ * maximal order that will be used is always even, it is twice the
+ * maximal number of columns in the extrapolation table.</p>
+ * <pre>
+ * order is decreased if w(k-1) <= w(k) * orderControl1
+ * order is increased if w(k) <= w(k-1) * orderControl2
+ * </pre>
+ * <p>where w is the table of work per unit step for each order
+ * (number of function calls divided by the step length), and k is
+ * the current order.</p>
+ * <p>The default maximal order after construction is 18 (i.e. the
+ * maximal number of columns is 9). The default values are 0.8 for
+ * orderControl1 and 0.9 for orderControl2.</p>
+ * @param maximalOrder maximal order in the extrapolation table (the
+ * maximal order is reset to default if order <= 6 or odd)
+ * @param control1 first order control factor (the factor is
+ * reset to default if lower than 0.0001 or greater than 0.9999)
+ * @param control2 second order control factor (the factor
+ * is reset to default if lower than 0.0001 or greater than 0.9999)
+ */
+ public void setOrderControl(final int maximalOrder,
+ final double control1, final double control2) {
+
+ if ((maximalOrder <= 6) || (maximalOrder % 2 != 0)) {
+ this.maxOrder = 18;
+ }
+
+ if ((control1 < 0.0001) || (control1 > 0.9999)) {
+ this.orderControl1 = 0.8;
+ } else {
+ this.orderControl1 = control1;
+ }
+
+ if ((control2 < 0.0001) || (control2 > 0.9999)) {
+ this.orderControl2 = 0.9;
+ } else {
+ this.orderControl2 = control2;
+ }
+
+ // reinitialize the arrays
+ initializeArrays();
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addStepHandler (final StepHandler handler) {
+
+ super.addStepHandler(handler);
+
+ // reinitialize the arrays
+ initializeArrays();
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addEventHandler(final EventHandler function,
+ final double maxCheckInterval,
+ final double convergence,
+ final int maxIterationCount,
+ final UnivariateSolver solver) {
+ super.addEventHandler(function, maxCheckInterval, convergence,
+ maxIterationCount, solver);
+
+ // reinitialize the arrays
+ initializeArrays();
+
+ }
+
+ /** Initialize the integrator internal arrays. */
+ private void initializeArrays() {
+
+ final int size = maxOrder / 2;
+
+ if ((sequence == null) || (sequence.length != size)) {
+ // all arrays should be reallocated with the right size
+ sequence = new int[size];
+ costPerStep = new int[size];
+ coeff = new double[size][];
+ costPerTimeUnit = new double[size];
+ optimalStep = new double[size];
+ }
+
+ // step size sequence: 2, 6, 10, 14, ...
+ for (int k = 0; k < size; ++k) {
+ sequence[k] = 4 * k + 2;
+ }
+
+ // initialize the order selection cost array
+ // (number of function calls for each column of the extrapolation table)
+ costPerStep[0] = sequence[0] + 1;
+ for (int k = 1; k < size; ++k) {
+ costPerStep[k] = costPerStep[k-1] + sequence[k];
+ }
+
+ // initialize the extrapolation tables
+ for (int k = 0; k < size; ++k) {
+ coeff[k] = (k > 0) ? new double[k] : null;
+ for (int l = 0; l < k; ++l) {
+ final double ratio = ((double) sequence[k]) / sequence[k-l-1];
+ coeff[k][l] = 1.0 / (ratio * ratio - 1.0);
+ }
+ }
+
+ }
+
+ /** Set the interpolation order control parameter.
+ * The interpolation order for dense output is 2k - mudif + 1. The
+ * default value for mudif is 4 and the interpolation error is used
+ * in stepsize control by default.
+
+ * @param useInterpolationErrorForControl if true, interpolation error is used
+ * for stepsize control
+ * @param mudifControlParameter interpolation order control parameter (the parameter
+ * is reset to default if <= 0 or >= 7)
+ */
+ public void setInterpolationControl(final boolean useInterpolationErrorForControl,
+ final int mudifControlParameter) {
+
+ this.useInterpolationError = useInterpolationErrorForControl;
+
+ if ((mudifControlParameter <= 0) || (mudifControlParameter >= 7)) {
+ this.mudif = 4;
+ } else {
+ this.mudif = mudifControlParameter;
+ }
+
+ }
+
+ /** Update scaling array.
+ * @param y1 first state vector to use for scaling
+ * @param y2 second state vector to use for scaling
+ * @param scale scaling array to update (can be shorter than state)
+ */
+ private void rescale(final double[] y1, final double[] y2, final double[] scale) {
+ if (vecAbsoluteTolerance == null) {
+ for (int i = 0; i < scale.length; ++i) {
+ final double yi = FastMath.max(FastMath.abs(y1[i]), FastMath.abs(y2[i]));
+ scale[i] = scalAbsoluteTolerance + scalRelativeTolerance * yi;
+ }
+ } else {
+ for (int i = 0; i < scale.length; ++i) {
+ final double yi = FastMath.max(FastMath.abs(y1[i]), FastMath.abs(y2[i]));
+ scale[i] = vecAbsoluteTolerance[i] + vecRelativeTolerance[i] * yi;
+ }
+ }
+ }
+
+ /** Perform integration over one step using substeps of a modified
+ * midpoint method.
+ * @param t0 initial time
+ * @param y0 initial value of the state vector at t0
+ * @param step global step
+ * @param k iteration number (from 0 to sequence.length - 1)
+ * @param scale scaling array (can be shorter than state)
+ * @param f placeholder where to put the state vector derivatives at each substep
+ * (element 0 already contains initial derivative)
+ * @param yMiddle placeholder where to put the state vector at the middle of the step
+ * @param yEnd placeholder where to put the state vector at the end
+ * @param yTmp placeholder for one state vector
+ * @return true if computation was done properly,
+ * false if stability check failed before end of computation
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception DimensionMismatchException if arrays dimensions do not match equations settings
+ */
+ private boolean tryStep(final double t0, final double[] y0, final double step, final int k,
+ final double[] scale, final double[][] f,
+ final double[] yMiddle, final double[] yEnd,
+ final double[] yTmp)
+ throws MaxCountExceededException, DimensionMismatchException {
+
+ final int n = sequence[k];
+ final double subStep = step / n;
+ final double subStep2 = 2 * subStep;
+
+ // first substep
+ double t = t0 + subStep;
+ for (int i = 0; i < y0.length; ++i) {
+ yTmp[i] = y0[i];
+ yEnd[i] = y0[i] + subStep * f[0][i];
+ }
+ computeDerivatives(t, yEnd, f[1]);
+
+ // other substeps
+ for (int j = 1; j < n; ++j) {
+
+ if (2 * j == n) {
+ // save the point at the middle of the step
+ System.arraycopy(yEnd, 0, yMiddle, 0, y0.length);
+ }
+
+ t += subStep;
+ for (int i = 0; i < y0.length; ++i) {
+ final double middle = yEnd[i];
+ yEnd[i] = yTmp[i] + subStep2 * f[j][i];
+ yTmp[i] = middle;
+ }
+
+ computeDerivatives(t, yEnd, f[j+1]);
+
+ // stability check
+ if (performTest && (j <= maxChecks) && (k < maxIter)) {
+ double initialNorm = 0.0;
+ for (int l = 0; l < scale.length; ++l) {
+ final double ratio = f[0][l] / scale[l];
+ initialNorm += ratio * ratio;
+ }
+ double deltaNorm = 0.0;
+ for (int l = 0; l < scale.length; ++l) {
+ final double ratio = (f[j+1][l] - f[0][l]) / scale[l];
+ deltaNorm += ratio * ratio;
+ }
+ if (deltaNorm > 4 * FastMath.max(1.0e-15, initialNorm)) {
+ return false;
+ }
+ }
+
+ }
+
+ // correction of the last substep (at t0 + step)
+ for (int i = 0; i < y0.length; ++i) {
+ yEnd[i] = 0.5 * (yTmp[i] + yEnd[i] + subStep * f[n][i]);
+ }
+
+ return true;
+
+ }
+
+ /** Extrapolate a vector.
+ * @param offset offset to use in the coefficients table
+ * @param k index of the last updated point
+ * @param diag working diagonal of the Aitken-Neville's
+ * triangle, without the last element
+ * @param last last element
+ */
+ private void extrapolate(final int offset, final int k,
+ final double[][] diag, final double[] last) {
+
+ // update the diagonal
+ for (int j = 1; j < k; ++j) {
+ for (int i = 0; i < last.length; ++i) {
+ // Aitken-Neville's recursive formula
+ diag[k-j-1][i] = diag[k-j][i] +
+ coeff[k+offset][j-1] * (diag[k-j][i] - diag[k-j-1][i]);
+ }
+ }
+
+ // update the last element
+ for (int i = 0; i < last.length; ++i) {
+ // Aitken-Neville's recursive formula
+ last[i] = diag[0][i] + coeff[k+offset][k-1] * (diag[0][i] - last[i]);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void integrate(final ExpandableStatefulODE equations, final double t)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException {
+
+ sanityChecks(equations, t);
+ setEquations(equations);
+ final boolean forward = t > equations.getTime();
+
+ // create some internal working arrays
+ final double[] y0 = equations.getCompleteState();
+ final double[] y = y0.clone();
+ final double[] yDot0 = new double[y.length];
+ final double[] y1 = new double[y.length];
+ final double[] yTmp = new double[y.length];
+ final double[] yTmpDot = new double[y.length];
+
+ final double[][] diagonal = new double[sequence.length-1][];
+ final double[][] y1Diag = new double[sequence.length-1][];
+ for (int k = 0; k < sequence.length-1; ++k) {
+ diagonal[k] = new double[y.length];
+ y1Diag[k] = new double[y.length];
+ }
+
+ final double[][][] fk = new double[sequence.length][][];
+ for (int k = 0; k < sequence.length; ++k) {
+
+ fk[k] = new double[sequence[k] + 1][];
+
+ // all substeps start at the same point, so share the first array
+ fk[k][0] = yDot0;
+
+ for (int l = 0; l < sequence[k]; ++l) {
+ fk[k][l+1] = new double[y0.length];
+ }
+
+ }
+
+ if (y != y0) {
+ System.arraycopy(y0, 0, y, 0, y0.length);
+ }
+
+ final double[] yDot1 = new double[y0.length];
+ final double[][] yMidDots = new double[1 + 2 * sequence.length][y0.length];
+
+ // initial scaling
+ final double[] scale = new double[mainSetDimension];
+ rescale(y, y, scale);
+
+ // initial order selection
+ final double tol =
+ (vecRelativeTolerance == null) ? scalRelativeTolerance : vecRelativeTolerance[0];
+ final double log10R = FastMath.log10(FastMath.max(1.0e-10, tol));
+ int targetIter = FastMath.max(1,
+ FastMath.min(sequence.length - 2,
+ (int) FastMath.floor(0.5 - 0.6 * log10R)));
+
+ // set up an interpolator sharing the integrator arrays
+ final AbstractStepInterpolator interpolator =
+ new GraggBulirschStoerStepInterpolator(y, yDot0,
+ y1, yDot1,
+ yMidDots, forward,
+ equations.getPrimaryMapper(),
+ equations.getSecondaryMappers());
+ interpolator.storeTime(equations.getTime());
+
+ stepStart = equations.getTime();
+ double hNew = 0;
+ double maxError = Double.MAX_VALUE;
+ boolean previousRejected = false;
+ boolean firstTime = true;
+ boolean newStep = true;
+ boolean firstStepAlreadyComputed = false;
+ initIntegration(equations.getTime(), y0, t);
+ costPerTimeUnit[0] = 0;
+ isLastStep = false;
+ do {
+
+ double error;
+ boolean reject = false;
+
+ if (newStep) {
+
+ interpolator.shift();
+
+ // first evaluation, at the beginning of the step
+ if (! firstStepAlreadyComputed) {
+ computeDerivatives(stepStart, y, yDot0);
+ }
+
+ if (firstTime) {
+ hNew = initializeStep(forward, 2 * targetIter + 1, scale,
+ stepStart, y, yDot0, yTmp, yTmpDot);
+ }
+
+ newStep = false;
+
+ }
+
+ stepSize = hNew;
+
+ // step adjustment near bounds
+ if ((forward && (stepStart + stepSize > t)) ||
+ ((! forward) && (stepStart + stepSize < t))) {
+ stepSize = t - stepStart;
+ }
+ final double nextT = stepStart + stepSize;
+ isLastStep = forward ? (nextT >= t) : (nextT <= t);
+
+ // iterate over several substep sizes
+ int k = -1;
+ for (boolean loop = true; loop; ) {
+
+ ++k;
+
+ // modified midpoint integration with the current substep
+ if ( ! tryStep(stepStart, y, stepSize, k, scale, fk[k],
+ (k == 0) ? yMidDots[0] : diagonal[k-1],
+ (k == 0) ? y1 : y1Diag[k-1],
+ yTmp)) {
+
+ // the stability check failed, we reduce the global step
+ hNew = FastMath.abs(filterStep(stepSize * stabilityReduction, forward, false));
+ reject = true;
+ loop = false;
+
+ } else {
+
+ // the substep was computed successfully
+ if (k > 0) {
+
+ // extrapolate the state at the end of the step
+ // using last iteration data
+ extrapolate(0, k, y1Diag, y1);
+ rescale(y, y1, scale);
+
+ // estimate the error at the end of the step.
+ error = 0;
+ for (int j = 0; j < mainSetDimension; ++j) {
+ final double e = FastMath.abs(y1[j] - y1Diag[0][j]) / scale[j];
+ error += e * e;
+ }
+ error = FastMath.sqrt(error / mainSetDimension);
+
+ if ((error > 1.0e15) || ((k > 1) && (error > maxError))) {
+ // error is too big, we reduce the global step
+ hNew = FastMath.abs(filterStep(stepSize * stabilityReduction, forward, false));
+ reject = true;
+ loop = false;
+ } else {
+
+ maxError = FastMath.max(4 * error, 1.0);
+
+ // compute optimal stepsize for this order
+ final double exp = 1.0 / (2 * k + 1);
+ double fac = stepControl2 / FastMath.pow(error / stepControl1, exp);
+ final double pow = FastMath.pow(stepControl3, exp);
+ fac = FastMath.max(pow / stepControl4, FastMath.min(1 / pow, fac));
+ optimalStep[k] = FastMath.abs(filterStep(stepSize * fac, forward, true));
+ costPerTimeUnit[k] = costPerStep[k] / optimalStep[k];
+
+ // check convergence
+ switch (k - targetIter) {
+
+ case -1 :
+ if ((targetIter > 1) && ! previousRejected) {
+
+ // check if we can stop iterations now
+ if (error <= 1.0) {
+ // convergence have been reached just before targetIter
+ loop = false;
+ } else {
+ // estimate if there is a chance convergence will
+ // be reached on next iteration, using the
+ // asymptotic evolution of error
+ final double ratio = ((double) sequence [targetIter] * sequence[targetIter + 1]) /
+ (sequence[0] * sequence[0]);
+ if (error > ratio * ratio) {
+ // we don't expect to converge on next iteration
+ // we reject the step immediately and reduce order
+ reject = true;
+ loop = false;
+ targetIter = k;
+ if ((targetIter > 1) &&
+ (costPerTimeUnit[targetIter-1] <
+ orderControl1 * costPerTimeUnit[targetIter])) {
+ --targetIter;
+ }
+ hNew = optimalStep[targetIter];
+ }
+ }
+ }
+ break;
+
+ case 0:
+ if (error <= 1.0) {
+ // convergence has been reached exactly at targetIter
+ loop = false;
+ } else {
+ // estimate if there is a chance convergence will
+ // be reached on next iteration, using the
+ // asymptotic evolution of error
+ final double ratio = ((double) sequence[k+1]) / sequence[0];
+ if (error > ratio * ratio) {
+ // we don't expect to converge on next iteration
+ // we reject the step immediately
+ reject = true;
+ loop = false;
+ if ((targetIter > 1) &&
+ (costPerTimeUnit[targetIter-1] <
+ orderControl1 * costPerTimeUnit[targetIter])) {
+ --targetIter;
+ }
+ hNew = optimalStep[targetIter];
+ }
+ }
+ break;
+
+ case 1 :
+ if (error > 1.0) {
+ reject = true;
+ if ((targetIter > 1) &&
+ (costPerTimeUnit[targetIter-1] <
+ orderControl1 * costPerTimeUnit[targetIter])) {
+ --targetIter;
+ }
+ hNew = optimalStep[targetIter];
+ }
+ loop = false;
+ break;
+
+ default :
+ if ((firstTime || isLastStep) && (error <= 1.0)) {
+ loop = false;
+ }
+ break;
+
+ }
+
+ }
+ }
+ }
+ }
+
+ if (! reject) {
+ // derivatives at end of step
+ computeDerivatives(stepStart + stepSize, y1, yDot1);
+ }
+
+ // dense output handling
+ double hInt = getMaxStep();
+ if (! reject) {
+
+ // extrapolate state at middle point of the step
+ for (int j = 1; j <= k; ++j) {
+ extrapolate(0, j, diagonal, yMidDots[0]);
+ }
+
+ final int mu = 2 * k - mudif + 3;
+
+ for (int l = 0; l < mu; ++l) {
+
+ // derivative at middle point of the step
+ final int l2 = l / 2;
+ double factor = FastMath.pow(0.5 * sequence[l2], l);
+ int middleIndex = fk[l2].length / 2;
+ for (int i = 0; i < y0.length; ++i) {
+ yMidDots[l+1][i] = factor * fk[l2][middleIndex + l][i];
+ }
+ for (int j = 1; j <= k - l2; ++j) {
+ factor = FastMath.pow(0.5 * sequence[j + l2], l);
+ middleIndex = fk[l2+j].length / 2;
+ for (int i = 0; i < y0.length; ++i) {
+ diagonal[j-1][i] = factor * fk[l2+j][middleIndex+l][i];
+ }
+ extrapolate(l2, j, diagonal, yMidDots[l+1]);
+ }
+ for (int i = 0; i < y0.length; ++i) {
+ yMidDots[l+1][i] *= stepSize;
+ }
+
+ // compute centered differences to evaluate next derivatives
+ for (int j = (l + 1) / 2; j <= k; ++j) {
+ for (int m = fk[j].length - 1; m >= 2 * (l + 1); --m) {
+ for (int i = 0; i < y0.length; ++i) {
+ fk[j][m][i] -= fk[j][m-2][i];
+ }
+ }
+ }
+
+ }
+
+ if (mu >= 0) {
+
+ // estimate the dense output coefficients
+ final GraggBulirschStoerStepInterpolator gbsInterpolator
+ = (GraggBulirschStoerStepInterpolator) interpolator;
+ gbsInterpolator.computeCoefficients(mu, stepSize);
+
+ if (useInterpolationError) {
+ // use the interpolation error to limit stepsize
+ final double interpError = gbsInterpolator.estimateError(scale);
+ hInt = FastMath.abs(stepSize / FastMath.max(FastMath.pow(interpError, 1.0 / (mu+4)),
+ 0.01));
+ if (interpError > 10.0) {
+ hNew = hInt;
+ reject = true;
+ }
+ }
+
+ }
+
+ }
+
+ if (! reject) {
+
+ // Discrete events handling
+ interpolator.storeTime(stepStart + stepSize);
+ stepStart = acceptStep(interpolator, y1, yDot1, t);
+
+ // prepare next step
+ interpolator.storeTime(stepStart);
+ System.arraycopy(y1, 0, y, 0, y0.length);
+ System.arraycopy(yDot1, 0, yDot0, 0, y0.length);
+ firstStepAlreadyComputed = true;
+
+ int optimalIter;
+ if (k == 1) {
+ optimalIter = 2;
+ if (previousRejected) {
+ optimalIter = 1;
+ }
+ } else if (k <= targetIter) {
+ optimalIter = k;
+ if (costPerTimeUnit[k-1] < orderControl1 * costPerTimeUnit[k]) {
+ optimalIter = k-1;
+ } else if (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[k-1]) {
+ optimalIter = FastMath.min(k+1, sequence.length - 2);
+ }
+ } else {
+ optimalIter = k - 1;
+ if ((k > 2) &&
+ (costPerTimeUnit[k-2] < orderControl1 * costPerTimeUnit[k-1])) {
+ optimalIter = k - 2;
+ }
+ if (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[optimalIter]) {
+ optimalIter = FastMath.min(k, sequence.length - 2);
+ }
+ }
+
+ if (previousRejected) {
+ // after a rejected step neither order nor stepsize
+ // should increase
+ targetIter = FastMath.min(optimalIter, k);
+ hNew = FastMath.min(FastMath.abs(stepSize), optimalStep[targetIter]);
+ } else {
+ // stepsize control
+ if (optimalIter <= k) {
+ hNew = optimalStep[optimalIter];
+ } else {
+ if ((k < targetIter) &&
+ (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[k-1])) {
+ hNew = filterStep(optimalStep[k] * costPerStep[optimalIter+1] / costPerStep[k],
+ forward, false);
+ } else {
+ hNew = filterStep(optimalStep[k] * costPerStep[optimalIter] / costPerStep[k],
+ forward, false);
+ }
+ }
+
+ targetIter = optimalIter;
+
+ }
+
+ newStep = true;
+
+ }
+
+ hNew = FastMath.min(hNew, hInt);
+ if (! forward) {
+ hNew = -hNew;
+ }
+
+ firstTime = false;
+
+ if (reject) {
+ isLastStep = false;
+ previousRejected = true;
+ } else {
+ previousRejected = false;
+ }
+
+ } while (!isLastStep);
+
+ // dispatch results
+ equations.setTime(stepStart);
+ equations.setCompleteState(y);
+
+ resetInternalState();
+
+ }
+
+}