GraggBulirschStoerIntegrator.java

  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. package org.apache.commons.math4.legacy.ode.nonstiff;

  18. import org.apache.commons.math4.legacy.analysis.solvers.UnivariateSolver;
  19. import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
  20. import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
  21. import org.apache.commons.math4.legacy.exception.NoBracketingException;
  22. import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
  23. import org.apache.commons.math4.legacy.ode.ExpandableStatefulODE;
  24. import org.apache.commons.math4.legacy.ode.events.EventHandler;
  25. import org.apache.commons.math4.legacy.ode.sampling.AbstractStepInterpolator;
  26. import org.apache.commons.math4.legacy.ode.sampling.StepHandler;
  27. import org.apache.commons.math4.core.jdkmath.JdkMath;

  28. /**
  29.  * This class implements a Gragg-Bulirsch-Stoer integrator for
  30.  * Ordinary Differential Equations.
  31.  *
  32.  * <p>The Gragg-Bulirsch-Stoer algorithm is one of the most efficient
  33.  * ones currently available for smooth problems. It uses Richardson
  34.  * extrapolation to estimate what would be the solution if the step
  35.  * size could be decreased down to zero.</p>
  36.  *
  37.  * <p>
  38.  * This method changes both the step size and the order during
  39.  * integration, in order to minimize computation cost. It is
  40.  * particularly well suited when a very high precision is needed. The
  41.  * limit where this method becomes more efficient than high-order
  42.  * embedded Runge-Kutta methods like {@link DormandPrince853Integrator
  43.  * Dormand-Prince 8(5,3)} depends on the problem. Results given in the
  44.  * Hairer, Norsett and Wanner book show for example that this limit
  45.  * occurs for accuracy around 1e-6 when integrating Saltzam-Lorenz
  46.  * equations (the authors note this problem is <i>extremely sensitive
  47.  * to the errors in the first integration steps</i>), and around 1e-11
  48.  * for a two dimensional celestial mechanics problems with seven
  49.  * bodies (pleiades problem, involving quasi-collisions for which
  50.  * <i>automatic step size control is essential</i>).
  51.  * </p>
  52.  *
  53.  * <p>
  54.  * This implementation is basically a reimplementation in Java of the
  55.  * <a
  56.  * href="http://www.unige.ch/math/folks/hairer/prog/nonstiff/odex.f">odex</a>
  57.  * fortran code by E. Hairer and G. Wanner. The redistribution policy
  58.  * for this code is available <a
  59.  * href="http://www.unige.ch/~hairer/prog/licence.txt">here</a>, for
  60.  * convenience, it is reproduced below.</p>
  61.  *
  62.  * <table border="" style="text-align: center; background-color: #E0E0E0">
  63.  * <caption>odex redistribution policy</caption>
  64.  * <tr><td>Copyright (c) 2004, Ernst Hairer</td></tr>
  65.  *
  66.  * <tr><td>Redistribution and use in source and binary forms, with or
  67.  * without modification, are permitted provided that the following
  68.  * conditions are met:
  69.  * <ul>
  70.  *  <li>Redistributions of source code must retain the above copyright
  71.  *      notice, this list of conditions and the following disclaimer.</li>
  72.  *  <li>Redistributions in binary form must reproduce the above copyright
  73.  *      notice, this list of conditions and the following disclaimer in the
  74.  *      documentation and/or other materials provided with the distribution.</li>
  75.  * </ul></td></tr>
  76.  *
  77.  * <tr><td><strong>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  78.  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
  79.  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  80.  * FOR A  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
  81.  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  82.  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  83.  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  84.  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  85.  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  86.  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  87.  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</strong></td></tr>
  88.  * </table>
  89.  *
  90.  * @since 1.2
  91.  */

  92. public class GraggBulirschStoerIntegrator extends AdaptiveStepsizeIntegrator {

  93.     /** Integrator method name. */
  94.     private static final String METHOD_NAME = "Gragg-Bulirsch-Stoer";

  95.     /** maximal order. */
  96.     private int maxOrder;

  97.     /** step size sequence. */
  98.     private int[] sequence;

  99.     /** overall cost of applying step reduction up to iteration k+1, in number of calls. */
  100.     private int[] costPerStep;

  101.     /** cost per unit step. */
  102.     private double[] costPerTimeUnit;

  103.     /** optimal steps for each order. */
  104.     private double[] optimalStep;

  105.     /** extrapolation coefficients. */
  106.     private double[][] coeff;

  107.     /** stability check enabling parameter. */
  108.     private boolean performTest;

  109.     /** maximal number of checks for each iteration. */
  110.     private int maxChecks;

  111.     /** maximal number of iterations for which checks are performed. */
  112.     private int maxIter;

  113.     /** stepsize reduction factor in case of stability check failure. */
  114.     private double stabilityReduction;

  115.     /** first stepsize control factor. */
  116.     private double stepControl1;

  117.     /** second stepsize control factor. */
  118.     private double stepControl2;

  119.     /** third stepsize control factor. */
  120.     private double stepControl3;

  121.     /** fourth stepsize control factor. */
  122.     private double stepControl4;

  123.     /** first order control factor. */
  124.     private double orderControl1;

  125.     /** second order control factor. */
  126.     private double orderControl2;

  127.     /** use interpolation error in stepsize control. */
  128.     private boolean useInterpolationError;

  129.     /** interpolation order control parameter. */
  130.     private int mudif;

  131.   /** Simple constructor.
  132.    * Build a Gragg-Bulirsch-Stoer integrator with the given step
  133.    * bounds. All tuning parameters are set to their default
  134.    * values. The default step handler does nothing.
  135.    * @param minStep minimal step (sign is irrelevant, regardless of
  136.    * integration direction, forward or backward), the last step can
  137.    * be smaller than this
  138.    * @param maxStep maximal step (sign is irrelevant, regardless of
  139.    * integration direction, forward or backward), the last step can
  140.    * be smaller than this
  141.    * @param scalAbsoluteTolerance allowed absolute error
  142.    * @param scalRelativeTolerance allowed relative error
  143.    */
  144.   public GraggBulirschStoerIntegrator(final double minStep, final double maxStep,
  145.                                       final double scalAbsoluteTolerance,
  146.                                       final double scalRelativeTolerance) {
  147.     super(METHOD_NAME, minStep, maxStep,
  148.           scalAbsoluteTolerance, scalRelativeTolerance);
  149.     setStabilityCheck(true, -1, -1, -1);
  150.     setControlFactors(-1, -1, -1, -1);
  151.     setOrderControl(-1, -1, -1);
  152.     setInterpolationControl(true, -1);
  153.   }

  154.   /** Simple constructor.
  155.    * Build a Gragg-Bulirsch-Stoer integrator with the given step
  156.    * bounds. All tuning parameters are set to their default
  157.    * values. The default step handler does nothing.
  158.    * @param minStep minimal step (must be positive even for backward
  159.    * integration), the last step can be smaller than this
  160.    * @param maxStep maximal step (must be positive even for backward
  161.    * integration)
  162.    * @param vecAbsoluteTolerance allowed absolute error
  163.    * @param vecRelativeTolerance allowed relative error
  164.    */
  165.   public GraggBulirschStoerIntegrator(final double minStep, final double maxStep,
  166.                                       final double[] vecAbsoluteTolerance,
  167.                                       final double[] vecRelativeTolerance) {
  168.     super(METHOD_NAME, minStep, maxStep,
  169.           vecAbsoluteTolerance, vecRelativeTolerance);
  170.     setStabilityCheck(true, -1, -1, -1);
  171.     setControlFactors(-1, -1, -1, -1);
  172.     setOrderControl(-1, -1, -1);
  173.     setInterpolationControl(true, -1);
  174.   }

  175.   /** Set the stability check controls.
  176.    * <p>The stability check is performed on the first few iterations of
  177.    * the extrapolation scheme. If this test fails, the step is rejected
  178.    * and the stepsize is reduced.</p>
  179.    * <p>By default, the test is performed, at most during two
  180.    * iterations at each step, and at most once for each of these
  181.    * iterations. The default stepsize reduction factor is 0.5.</p>
  182.    * @param performStabilityCheck if true, stability check will be performed,
  183.      if false, the check will be skipped
  184.    * @param maxNumIter maximal number of iterations for which checks are
  185.    * performed (the number of iterations is reset to default if negative
  186.    * or null)
  187.    * @param maxNumChecks maximal number of checks for each iteration
  188.    * (the number of checks is reset to default if negative or null)
  189.    * @param stepsizeReductionFactor stepsize reduction factor in case of
  190.    * failure (the factor is reset to default if lower than 0.0001 or
  191.    * greater than 0.9999)
  192.    */
  193.   public void setStabilityCheck(final boolean performStabilityCheck,
  194.                                 final int maxNumIter, final int maxNumChecks,
  195.                                 final double stepsizeReductionFactor) {

  196.     this.performTest = performStabilityCheck;
  197.     this.maxIter     = (maxNumIter   <= 0) ? 2 : maxNumIter;
  198.     this.maxChecks   = (maxNumChecks <= 0) ? 1 : maxNumChecks;

  199.     if (stepsizeReductionFactor < 0.0001 || stepsizeReductionFactor > 0.9999) {
  200.       this.stabilityReduction = 0.5;
  201.     } else {
  202.       this.stabilityReduction = stepsizeReductionFactor;
  203.     }
  204.   }

  205.   /** Set the step size control factors.

  206.    * <p>The new step size hNew is computed from the old one h by:
  207.    * <pre>
  208.    * hNew = h * stepControl2 / (err/stepControl1)^(1/(2k+1))
  209.    * </pre>
  210.    * where err is the scaled error and k the iteration number of the
  211.    * extrapolation scheme (counting from 0). The default values are
  212.    * 0.65 for stepControl1 and 0.94 for stepControl2.
  213.    * <p>The step size is subject to the restriction:
  214.    * <pre>
  215.    * stepControl3^(1/(2k+1))/stepControl4 &lt;= hNew/h &lt;= 1/stepControl3^(1/(2k+1))
  216.    * </pre>
  217.    * The default values are 0.02 for stepControl3 and 4.0 for
  218.    * stepControl4.
  219.    * @param control1 first stepsize control factor (the factor is
  220.    * reset to default if lower than 0.0001 or greater than 0.9999)
  221.    * @param control2 second stepsize control factor (the factor
  222.    * is reset to default if lower than 0.0001 or greater than 0.9999)
  223.    * @param control3 third stepsize control factor (the factor is
  224.    * reset to default if lower than 0.0001 or greater than 0.9999)
  225.    * @param control4 fourth stepsize control factor (the factor
  226.    * is reset to default if lower than 1.0001 or greater than 999.9)
  227.    */
  228.   public void setControlFactors(final double control1, final double control2,
  229.                                 final double control3, final double control4) {

  230.     if (control1 < 0.0001 || control1 > 0.9999) {
  231.       this.stepControl1 = 0.65;
  232.     } else {
  233.       this.stepControl1 = control1;
  234.     }

  235.     if (control2 < 0.0001 || control2 > 0.9999) {
  236.       this.stepControl2 = 0.94;
  237.     } else {
  238.       this.stepControl2 = control2;
  239.     }

  240.     if (control3 < 0.0001 || control3 > 0.9999) {
  241.       this.stepControl3 = 0.02;
  242.     } else {
  243.       this.stepControl3 = control3;
  244.     }

  245.     if (control4 < 1.0001 || control4 > 999.9) {
  246.       this.stepControl4 = 4.0;
  247.     } else {
  248.       this.stepControl4 = control4;
  249.     }
  250.   }

  251.   /** Set the order control parameters.
  252.    * <p>The Gragg-Bulirsch-Stoer method changes both the step size and
  253.    * the order during integration, in order to minimize computation
  254.    * cost. Each extrapolation step increases the order by 2, so the
  255.    * maximal order that will be used is always even, it is twice the
  256.    * maximal number of columns in the extrapolation table.</p>
  257.    * <pre>
  258.    * order is decreased if w(k-1) &lt;= w(k)   * orderControl1
  259.    * order is increased if w(k)   &lt;= w(k-1) * orderControl2
  260.    * </pre>
  261.    * <p>where w is the table of work per unit step for each order
  262.    * (number of function calls divided by the step length), and k is
  263.    * the current order.</p>
  264.    * <p>The default maximal order after construction is 18 (i.e. the
  265.    * maximal number of columns is 9). The default values are 0.8 for
  266.    * orderControl1 and 0.9 for orderControl2.</p>
  267.    * @param maximalOrder maximal order in the extrapolation table (the
  268.    * maximal order is reset to default if order &lt;= 6 or odd)
  269.    * @param control1 first order control factor (the factor is
  270.    * reset to default if lower than 0.0001 or greater than 0.9999)
  271.    * @param control2 second order control factor (the factor
  272.    * is reset to default if lower than 0.0001 or greater than 0.9999)
  273.    */
  274.   public void setOrderControl(final int maximalOrder,
  275.                               final double control1, final double control2) {

  276.     if (maximalOrder <= 6 || (maximalOrder & 1) != 0) {
  277.       this.maxOrder = 18;
  278.     }

  279.     if (control1 < 0.0001 || control1 > 0.9999) {
  280.       this.orderControl1 = 0.8;
  281.     } else {
  282.       this.orderControl1 = control1;
  283.     }

  284.     if (control2 < 0.0001 || control2 > 0.9999) {
  285.       this.orderControl2 = 0.9;
  286.     } else {
  287.       this.orderControl2 = control2;
  288.     }

  289.     // reinitialize the arrays
  290.     initializeArrays();
  291.   }

  292.   /** {@inheritDoc} */
  293.   @Override
  294.   public void addStepHandler (final StepHandler handler) {

  295.     super.addStepHandler(handler);

  296.     // reinitialize the arrays
  297.     initializeArrays();
  298.   }

  299.   /** {@inheritDoc} */
  300.   @Override
  301.   public void addEventHandler(final EventHandler function,
  302.                               final double maxCheckInterval,
  303.                               final double convergence,
  304.                               final int maxIterationCount,
  305.                               final UnivariateSolver solver) {
  306.     super.addEventHandler(function, maxCheckInterval, convergence,
  307.                           maxIterationCount, solver);

  308.     // reinitialize the arrays
  309.     initializeArrays();
  310.   }

  311.   /** Initialize the integrator internal arrays. */
  312.   private void initializeArrays() {

  313.     final int size = maxOrder / 2;

  314.     if (sequence == null || sequence.length != size) {
  315.       // all arrays should be reallocated with the right size
  316.       sequence        = new int[size];
  317.       costPerStep     = new int[size];
  318.       coeff           = new double[size][];
  319.       costPerTimeUnit = new double[size];
  320.       optimalStep     = new double[size];
  321.     }

  322.     // step size sequence: 2, 6, 10, 14, ...
  323.     for (int k = 0; k < size; ++k) {
  324.         sequence[k] = 4 * k + 2;
  325.     }

  326.     // initialize the order selection cost array
  327.     // (number of function calls for each column of the extrapolation table)
  328.     costPerStep[0] = sequence[0] + 1;
  329.     for (int k = 1; k < size; ++k) {
  330.       costPerStep[k] = costPerStep[k-1] + sequence[k];
  331.     }

  332.     // initialize the extrapolation tables
  333.     for (int k = 0; k < size; ++k) {
  334.       coeff[k] = (k > 0) ? new double[k] : null;
  335.       for (int l = 0; l < k; ++l) {
  336.         final double ratio = ((double) sequence[k]) / sequence[k-l-1];
  337.         coeff[k][l] = 1.0 / (ratio * ratio - 1.0);
  338.       }
  339.     }
  340.   }

  341.   /** Set the interpolation order control parameter.
  342.    * The interpolation order for dense output is 2k - mudif + 1. The
  343.    * default value for mudif is 4 and the interpolation error is used
  344.    * in stepsize control by default.

  345.    * @param useInterpolationErrorForControl if true, interpolation error is used
  346.    * for stepsize control
  347.    * @param mudifControlParameter interpolation order control parameter (the parameter
  348.    * is reset to default if &lt;= 0 or &gt;= 7)
  349.    */
  350.   public void setInterpolationControl(final boolean useInterpolationErrorForControl,
  351.                                       final int mudifControlParameter) {

  352.     this.useInterpolationError = useInterpolationErrorForControl;

  353.     if (mudifControlParameter <= 0 || mudifControlParameter >= 7) {
  354.       this.mudif = 4;
  355.     } else {
  356.       this.mudif = mudifControlParameter;
  357.     }
  358.   }

  359.   /** Update scaling array.
  360.    * @param y1 first state vector to use for scaling
  361.    * @param y2 second state vector to use for scaling
  362.    * @param scale scaling array to update (can be shorter than state)
  363.    */
  364.   private void rescale(final double[] y1, final double[] y2, final double[] scale) {
  365.     if (vecAbsoluteTolerance == null) {
  366.       for (int i = 0; i < scale.length; ++i) {
  367.         final double yi = JdkMath.max(JdkMath.abs(y1[i]), JdkMath.abs(y2[i]));
  368.         scale[i] = scalAbsoluteTolerance + scalRelativeTolerance * yi;
  369.       }
  370.     } else {
  371.       for (int i = 0; i < scale.length; ++i) {
  372.         final double yi = JdkMath.max(JdkMath.abs(y1[i]), JdkMath.abs(y2[i]));
  373.         scale[i] = vecAbsoluteTolerance[i] + vecRelativeTolerance[i] * yi;
  374.       }
  375.     }
  376.   }

  377.   /** Perform integration over one step using substeps of a modified
  378.    * midpoint method.
  379.    * @param t0 initial time
  380.    * @param y0 initial value of the state vector at t0
  381.    * @param step global step
  382.    * @param k iteration number (from 0 to sequence.length - 1)
  383.    * @param scale scaling array (can be shorter than state)
  384.    * @param f placeholder where to put the state vector derivatives at each substep
  385.    *          (element 0 already contains initial derivative)
  386.    * @param yMiddle placeholder where to put the state vector at the middle of the step
  387.    * @param yEnd placeholder where to put the state vector at the end
  388.    * @param yTmp placeholder for one state vector
  389.    * @return true if computation was done properly,
  390.    *         false if stability check failed before end of computation
  391.    * @exception MaxCountExceededException if the number of functions evaluations is exceeded
  392.    * @exception DimensionMismatchException if arrays dimensions do not match equations settings
  393.    */
  394.   private boolean tryStep(final double t0, final double[] y0, final double step, final int k,
  395.                           final double[] scale, final double[][] f,
  396.                           final double[] yMiddle, final double[] yEnd,
  397.                           final double[] yTmp)
  398.       throws MaxCountExceededException, DimensionMismatchException {

  399.     final int    n        = sequence[k];
  400.     final double subStep  = step / n;
  401.     final double subStep2 = 2 * subStep;

  402.     // first substep
  403.     double t = t0 + subStep;
  404.     for (int i = 0; i < y0.length; ++i) {
  405.       yTmp[i] = y0[i];
  406.       yEnd[i] = y0[i] + subStep * f[0][i];
  407.     }
  408.     computeDerivatives(t, yEnd, f[1]);

  409.     // other substeps
  410.     for (int j = 1; j < n; ++j) {

  411.       if (2 * j == n) {
  412.         // save the point at the middle of the step
  413.         System.arraycopy(yEnd, 0, yMiddle, 0, y0.length);
  414.       }

  415.       t += subStep;
  416.       for (int i = 0; i < y0.length; ++i) {
  417.         final double middle = yEnd[i];
  418.         yEnd[i]       = yTmp[i] + subStep2 * f[j][i];
  419.         yTmp[i]       = middle;
  420.       }

  421.       computeDerivatives(t, yEnd, f[j+1]);

  422.       // stability check
  423.       if (performTest && j <= maxChecks && k < maxIter) {
  424.         double initialNorm = 0.0;
  425.         for (int l = 0; l < scale.length; ++l) {
  426.           final double ratio = f[0][l] / scale[l];
  427.           initialNorm += ratio * ratio;
  428.         }
  429.         double deltaNorm = 0.0;
  430.         for (int l = 0; l < scale.length; ++l) {
  431.           final double ratio = (f[j+1][l] - f[0][l]) / scale[l];
  432.           deltaNorm += ratio * ratio;
  433.         }
  434.         if (deltaNorm > 4 * JdkMath.max(1.0e-15, initialNorm)) {
  435.           return false;
  436.         }
  437.       }
  438.     }

  439.     // correction of the last substep (at t0 + step)
  440.     for (int i = 0; i < y0.length; ++i) {
  441.       yEnd[i] = 0.5 * (yTmp[i] + yEnd[i] + subStep * f[n][i]);
  442.     }

  443.     return true;
  444.   }

  445.   /** Extrapolate a vector.
  446.    * @param offset offset to use in the coefficients table
  447.    * @param k index of the last updated point
  448.    * @param diag working diagonal of the Aitken-Neville's
  449.    * triangle, without the last element
  450.    * @param last last element
  451.    */
  452.   private void extrapolate(final int offset, final int k,
  453.                            final double[][] diag, final double[] last) {

  454.     // update the diagonal
  455.     for (int j = 1; j < k; ++j) {
  456.       for (int i = 0; i < last.length; ++i) {
  457.         // Aitken-Neville's recursive formula
  458.         diag[k-j-1][i] = diag[k-j][i] +
  459.                          coeff[k+offset][j-1] * (diag[k-j][i] - diag[k-j-1][i]);
  460.       }
  461.     }

  462.     // update the last element
  463.     for (int i = 0; i < last.length; ++i) {
  464.       // Aitken-Neville's recursive formula
  465.       last[i] = diag[0][i] + coeff[k+offset][k-1] * (diag[0][i] - last[i]);
  466.     }
  467.   }

  468.   /** {@inheritDoc} */
  469.   @Override
  470.   public void integrate(final ExpandableStatefulODE equations, final double t)
  471.       throws NumberIsTooSmallException, DimensionMismatchException,
  472.              MaxCountExceededException, NoBracketingException {

  473.     sanityChecks(equations, t);
  474.     setEquations(equations);
  475.     final boolean forward = t > equations.getTime();

  476.     // create some internal working arrays
  477.     final double[] y0      = equations.getCompleteState();
  478.     final double[] y       = y0.clone();
  479.     final double[] yDot0   = new double[y.length];
  480.     final double[] y1      = new double[y.length];
  481.     final double[] yTmp    = new double[y.length];
  482.     final double[] yTmpDot = new double[y.length];

  483.     final double[][] diagonal = new double[sequence.length-1][];
  484.     final double[][] y1Diag = new double[sequence.length-1][];
  485.     for (int k = 0; k < sequence.length-1; ++k) {
  486.       diagonal[k] = new double[y.length];
  487.       y1Diag[k] = new double[y.length];
  488.     }

  489.     final double[][][] fk  = new double[sequence.length][][];
  490.     for (int k = 0; k < sequence.length; ++k) {

  491.       fk[k]    = new double[sequence[k] + 1][];

  492.       // all substeps start at the same point, so share the first array
  493.       fk[k][0] = yDot0;

  494.       for (int l = 0; l < sequence[k]; ++l) {
  495.         fk[k][l+1] = new double[y0.length];
  496.       }
  497.     }

  498.     if (y != y0) {
  499.       System.arraycopy(y0, 0, y, 0, y0.length);
  500.     }

  501.     final double[] yDot1 = new double[y0.length];
  502.     final double[][] yMidDots = new double[1 + 2 * sequence.length][y0.length];

  503.     // initial scaling
  504.     final double[] scale = new double[mainSetDimension];
  505.     rescale(y, y, scale);

  506.     // initial order selection
  507.     final double tol =
  508.         (vecRelativeTolerance == null) ? scalRelativeTolerance : vecRelativeTolerance[0];
  509.     final double log10R = JdkMath.log10(JdkMath.max(1.0e-10, tol));
  510.     int targetIter = JdkMath.max(1,
  511.                               JdkMath.min(sequence.length - 2,
  512.                                        (int) JdkMath.floor(0.5 - 0.6 * log10R)));

  513.     // set up an interpolator sharing the integrator arrays
  514.     final AbstractStepInterpolator interpolator =
  515.             new GraggBulirschStoerStepInterpolator(y, yDot0,
  516.                                                    y1, yDot1,
  517.                                                    yMidDots, forward,
  518.                                                    equations.getPrimaryMapper(),
  519.                                                    equations.getSecondaryMappers());
  520.     interpolator.storeTime(equations.getTime());

  521.     stepStart = equations.getTime();
  522.     double  hNew             = 0;
  523.     double  maxError         = Double.MAX_VALUE;
  524.     boolean previousRejected = false;
  525.     boolean firstTime        = true;
  526.     boolean newStep          = true;
  527.     boolean firstStepAlreadyComputed = false;
  528.     initIntegration(equations.getTime(), y0, t);
  529.     costPerTimeUnit[0] = 0;
  530.     isLastStep = false;
  531.     do {

  532.       double error;
  533.       boolean reject = false;

  534.       if (newStep) {

  535.         interpolator.shift();

  536.         // first evaluation, at the beginning of the step
  537.         if (! firstStepAlreadyComputed) {
  538.           computeDerivatives(stepStart, y, yDot0);
  539.         }

  540.         if (firstTime) {
  541.           hNew = initializeStep(forward, 2 * targetIter + 1, scale,
  542.                                 stepStart, y, yDot0, yTmp, yTmpDot);
  543.         }

  544.         newStep = false;
  545.       }

  546.       stepSize = hNew;

  547.       // step adjustment near bounds
  548.       if ((forward && (stepStart + stepSize > t)) ||
  549.           ((! forward) && (stepStart + stepSize < t))) {
  550.         stepSize = t - stepStart;
  551.       }
  552.       final double nextT = stepStart + stepSize;
  553.       isLastStep = forward ? (nextT >= t) : (nextT <= t);

  554.       // iterate over several substep sizes
  555.       int k = -1;
  556.       for (boolean loop = true; loop;) {

  557.         ++k;

  558.         // modified midpoint integration with the current substep
  559.         if ( ! tryStep(stepStart, y, stepSize, k, scale, fk[k],
  560.                        (k == 0) ? yMidDots[0] : diagonal[k-1],
  561.                        (k == 0) ? y1 : y1Diag[k-1],
  562.                        yTmp)) {

  563.           // the stability check failed, we reduce the global step
  564.           hNew   = JdkMath.abs(filterStep(stepSize * stabilityReduction, forward, false));
  565.           reject = true;
  566.           loop   = false;
  567.         } else {

  568.           // the substep was computed successfully
  569.           if (k > 0) {

  570.             // extrapolate the state at the end of the step
  571.             // using last iteration data
  572.             extrapolate(0, k, y1Diag, y1);
  573.             rescale(y, y1, scale);

  574.             // estimate the error at the end of the step.
  575.             error = 0;
  576.             for (int j = 0; j < mainSetDimension; ++j) {
  577.               final double e = JdkMath.abs(y1[j] - y1Diag[0][j]) / scale[j];
  578.               error += e * e;
  579.             }
  580.             error = JdkMath.sqrt(error / mainSetDimension);

  581.             if (error > 1.0e15 || (k > 1 && error > maxError)) {
  582.               // error is too big, we reduce the global step
  583.               hNew   = JdkMath.abs(filterStep(stepSize * stabilityReduction, forward, false));
  584.               reject = true;
  585.               loop   = false;
  586.             } else {

  587.               maxError = JdkMath.max(4 * error, 1.0);

  588.               // compute optimal stepsize for this order
  589.               final double exp = 1.0 / (2 * k + 1);
  590.               double fac = stepControl2 / JdkMath.pow(error / stepControl1, exp);
  591.               final double pow = JdkMath.pow(stepControl3, exp);
  592.               fac = JdkMath.max(pow / stepControl4, JdkMath.min(1 / pow, fac));
  593.               optimalStep[k]     = JdkMath.abs(filterStep(stepSize * fac, forward, true));
  594.               costPerTimeUnit[k] = costPerStep[k] / optimalStep[k];

  595.               // check convergence
  596.               switch (k - targetIter) {

  597.               case -1 :
  598.                 if (targetIter > 1 && !previousRejected) {

  599.                   // check if we can stop iterations now
  600.                   if (error <= 1.0) {
  601.                     // convergence have been reached just before targetIter
  602.                     loop = false;
  603.                   } else {
  604.                     // estimate if there is a chance convergence will
  605.                     // be reached on next iteration, using the
  606.                     // asymptotic evolution of error
  607.                     final double ratio = ((double) sequence [targetIter] * sequence[targetIter + 1]) /
  608.                                          (sequence[0] * sequence[0]);
  609.                     if (error > ratio * ratio) {
  610.                       // we don't expect to converge on next iteration
  611.                       // we reject the step immediately and reduce order
  612.                       reject = true;
  613.                       loop   = false;
  614.                       targetIter = k;
  615.                       if (targetIter > 1 &&
  616.                           costPerTimeUnit[targetIter-1] <
  617.                           orderControl1 * costPerTimeUnit[targetIter]) {
  618.                         --targetIter;
  619.                       }
  620.                       hNew = optimalStep[targetIter];
  621.                     }
  622.                   }
  623.                 }
  624.                 break;

  625.               case 0:
  626.                 if (error <= 1.0) {
  627.                   // convergence has been reached exactly at targetIter
  628.                   loop = false;
  629.                 } else {
  630.                   // estimate if there is a chance convergence will
  631.                   // be reached on next iteration, using the
  632.                   // asymptotic evolution of error
  633.                   final double ratio = ((double) sequence[k+1]) / sequence[0];
  634.                   if (error > ratio * ratio) {
  635.                     // we don't expect to converge on next iteration
  636.                     // we reject the step immediately
  637.                     reject = true;
  638.                     loop = false;
  639.                     if (targetIter > 1 &&
  640.                         costPerTimeUnit[targetIter-1] <
  641.                         orderControl1 * costPerTimeUnit[targetIter]) {
  642.                       --targetIter;
  643.                     }
  644.                     hNew = optimalStep[targetIter];
  645.                   }
  646.                 }
  647.                 break;

  648.               case 1 :
  649.                 if (error > 1.0) {
  650.                   reject = true;
  651.                   if (targetIter > 1 &&
  652.                       costPerTimeUnit[targetIter-1] <
  653.                       orderControl1 * costPerTimeUnit[targetIter]) {
  654.                     --targetIter;
  655.                   }
  656.                   hNew = optimalStep[targetIter];
  657.                 }
  658.                 loop = false;
  659.                 break;

  660.               default :
  661.                 if ((firstTime || isLastStep) && error <= 1.0) {
  662.                   loop = false;
  663.                 }
  664.                 break;
  665.               }
  666.             }
  667.           }
  668.         }
  669.       }

  670.       if (! reject) {
  671.           // derivatives at end of step
  672.           computeDerivatives(stepStart + stepSize, y1, yDot1);
  673.       }

  674.       // dense output handling
  675.       double hInt = getMaxStep();
  676.       if (! reject) {

  677.         // extrapolate state at middle point of the step
  678.         for (int j = 1; j <= k; ++j) {
  679.           extrapolate(0, j, diagonal, yMidDots[0]);
  680.         }

  681.         final int mu = 2 * k - mudif + 3;

  682.         for (int l = 0; l < mu; ++l) {

  683.           // derivative at middle point of the step
  684.           final int l2 = l / 2;
  685.           double factor = JdkMath.pow(0.5 * sequence[l2], l);
  686.           int middleIndex = fk[l2].length / 2;
  687.           for (int i = 0; i < y0.length; ++i) {
  688.             yMidDots[l+1][i] = factor * fk[l2][middleIndex + l][i];
  689.           }
  690.           for (int j = 1; j <= k - l2; ++j) {
  691.             factor = JdkMath.pow(0.5 * sequence[j + l2], l);
  692.             middleIndex = fk[l2+j].length / 2;
  693.             for (int i = 0; i < y0.length; ++i) {
  694.               diagonal[j-1][i] = factor * fk[l2+j][middleIndex+l][i];
  695.             }
  696.             extrapolate(l2, j, diagonal, yMidDots[l+1]);
  697.           }
  698.           for (int i = 0; i < y0.length; ++i) {
  699.             yMidDots[l+1][i] *= stepSize;
  700.           }

  701.           // compute centered differences to evaluate next derivatives
  702.           for (int j = (l + 1) / 2; j <= k; ++j) {
  703.             for (int m = fk[j].length - 1; m >= 2 * (l + 1); --m) {
  704.               for (int i = 0; i < y0.length; ++i) {
  705.                 fk[j][m][i] -= fk[j][m-2][i];
  706.               }
  707.             }
  708.           }
  709.         }

  710.         if (mu >= 0) {

  711.           // estimate the dense output coefficients
  712.           final GraggBulirschStoerStepInterpolator gbsInterpolator
  713.             = (GraggBulirschStoerStepInterpolator) interpolator;
  714.           gbsInterpolator.computeCoefficients(mu, stepSize);

  715.           if (useInterpolationError) {
  716.             // use the interpolation error to limit stepsize
  717.             final double interpError = gbsInterpolator.estimateError(scale);
  718.             hInt = JdkMath.abs(stepSize / JdkMath.max(JdkMath.pow(interpError, 1.0 / (mu+4)),
  719.                                                 0.01));
  720.             if (interpError > 10.0) {
  721.               hNew = hInt;
  722.               reject = true;
  723.             }
  724.           }
  725.         }
  726.       }

  727.       if (! reject) {

  728.         // Discrete events handling
  729.         interpolator.storeTime(stepStart + stepSize);
  730.         stepStart = acceptStep(interpolator, y1, yDot1, t);

  731.         // prepare next step
  732.         interpolator.storeTime(stepStart);
  733.         System.arraycopy(y1, 0, y, 0, y0.length);
  734.         System.arraycopy(yDot1, 0, yDot0, 0, y0.length);
  735.         firstStepAlreadyComputed = true;

  736.         int optimalIter;
  737.         if (k == 1) {
  738.           optimalIter = 2;
  739.           if (previousRejected) {
  740.             optimalIter = 1;
  741.           }
  742.         } else if (k <= targetIter) {
  743.           optimalIter = k;
  744.           if (costPerTimeUnit[k-1] < orderControl1 * costPerTimeUnit[k]) {
  745.             optimalIter = k-1;
  746.           } else if (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[k-1]) {
  747.             optimalIter = JdkMath.min(k+1, sequence.length - 2);
  748.           }
  749.         } else {
  750.           optimalIter = k - 1;
  751.           if (k > 2 &&
  752.               costPerTimeUnit[k-2] < orderControl1 * costPerTimeUnit[k-1]) {
  753.             optimalIter = k - 2;
  754.           }
  755.           if (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[optimalIter]) {
  756.             optimalIter = JdkMath.min(k, sequence.length - 2);
  757.           }
  758.         }

  759.         if (previousRejected) {
  760.           // after a rejected step neither order nor stepsize
  761.           // should increase
  762.           targetIter = JdkMath.min(optimalIter, k);
  763.           hNew = JdkMath.min(JdkMath.abs(stepSize), optimalStep[targetIter]);
  764.         } else {
  765.           // stepsize control
  766.           if (optimalIter <= k) {
  767.             hNew = optimalStep[optimalIter];
  768.           } else {
  769.             if (k < targetIter &&
  770.                 costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[k-1]) {
  771.               hNew = filterStep(optimalStep[k] * costPerStep[optimalIter+1] / costPerStep[k],
  772.                                forward, false);
  773.             } else {
  774.               hNew = filterStep(optimalStep[k] * costPerStep[optimalIter] / costPerStep[k],
  775.                                 forward, false);
  776.             }
  777.           }

  778.           targetIter = optimalIter;
  779.         }

  780.         newStep = true;
  781.       }

  782.       hNew = JdkMath.min(hNew, hInt);
  783.       if (! forward) {
  784.         hNew = -hNew;
  785.       }

  786.       firstTime = false;

  787.       if (reject) {
  788.         isLastStep = false;
  789.         previousRejected = true;
  790.       } else {
  791.         previousRejected = false;
  792.       }
  793.     } while (!isLastStep);

  794.     // dispatch results
  795.     equations.setTime(stepStart);
  796.     equations.setCompleteState(y);

  797.     resetInternalState();
  798.   }
  799. }