GraggBulirschStoerIntegrator.java

  1. /*
  2.  * Licensed to the Hipparchus project 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 Hipparchus project 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.  *      https://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.hipparchus.ode.nonstiff;

  18. import org.hipparchus.exception.MathIllegalArgumentException;
  19. import org.hipparchus.exception.MathIllegalStateException;
  20. import org.hipparchus.ode.ExpandableODE;
  21. import org.hipparchus.ode.LocalizedODEFormats;
  22. import org.hipparchus.ode.ODEState;
  23. import org.hipparchus.ode.ODEStateAndDerivative;
  24. import org.hipparchus.ode.nonstiff.interpolators.GraggBulirschStoerStateInterpolator;
  25. import org.hipparchus.util.FastMath;

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

  88. public class GraggBulirschStoerIntegrator extends AdaptiveStepsizeIntegrator {

  89.     /** Name of integration scheme. */
  90.     public static final String METHOD_NAME = "Gragg-Bulirsch-Stoer";

  91.     /** maximal order. */
  92.     private int maxOrder;

  93.     /** step size sequence. */
  94.     private int[] sequence;

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

  97.     /** cost per unit step. */
  98.     private double[] costPerTimeUnit;

  99.     /** optimal steps for each order. */
  100.     private double[] optimalStep;

  101.     /** extrapolation coefficients. */
  102.     private double[][] coeff;

  103.     /** stability check enabling parameter. */
  104.     private boolean performTest;

  105.     /** maximal number of checks for each iteration. */
  106.     private int maxChecks;

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

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

  111.     /** first stepsize control factor. */
  112.     private double stepControl1;

  113.     /** second stepsize control factor. */
  114.     private double stepControl2;

  115.     /** third stepsize control factor. */
  116.     private double stepControl3;

  117.     /** fourth stepsize control factor. */
  118.     private double stepControl4;

  119.     /** first order control factor. */
  120.     private double orderControl1;

  121.     /** second order control factor. */
  122.     private double orderControl2;

  123.     /** use interpolation error in stepsize control. */
  124.     private boolean useInterpolationError;

  125.     /** interpolation order control parameter. */
  126.     private int mudif;

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

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

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

  192.         this.performTest = performStabilityCheck;
  193.         this.maxIter     = (maxNumIter   <= 0) ? 2 : maxNumIter;
  194.         this.maxChecks   = (maxNumChecks <= 0) ? 1 : maxNumChecks;

  195.         if ((stepsizeReductionFactor < 0.0001) || (stepsizeReductionFactor > 0.9999)) {
  196.             this.stabilityReduction = 0.5;
  197.         } else {
  198.             this.stabilityReduction = stepsizeReductionFactor;
  199.         }

  200.     }

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

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

  226.         if ((control1 < 0.0001) || (control1 > 0.9999)) {
  227.             this.stepControl1 = 0.65;
  228.         } else {
  229.             this.stepControl1 = control1;
  230.         }

  231.         if ((control2 < 0.0001) || (control2 > 0.9999)) {
  232.             this.stepControl2 = 0.94;
  233.         } else {
  234.             this.stepControl2 = control2;
  235.         }

  236.         if ((control3 < 0.0001) || (control3 > 0.9999)) {
  237.             this.stepControl3 = 0.02;
  238.         } else {
  239.             this.stepControl3 = control3;
  240.         }

  241.         if ((control4 < 1.0001) || (control4 > 999.9)) {
  242.             this.stepControl4 = 4.0;
  243.         } else {
  244.             this.stepControl4 = control4;
  245.         }

  246.     }

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

  272.         if (maximalOrder > 6 && maximalOrder % 2 == 0) {
  273.             this.maxOrder = maximalOrder;
  274.         } else {
  275.             this.maxOrder = 18;
  276.         }

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

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

  287.         // reinitialize the arrays
  288.         initializeArrays();

  289.     }

  290.     /** Initialize the integrator internal arrays. */
  291.     private void initializeArrays() {

  292.         final int size = maxOrder / 2;

  293.         if ((sequence == null) || (sequence.length != size)) {
  294.             // all arrays should be reallocated with the right size
  295.             sequence        = new int[size];
  296.             costPerStep     = new int[size];
  297.             coeff           = new double[size][];
  298.             costPerTimeUnit = new double[size];
  299.             optimalStep     = new double[size];
  300.         }

  301.         // step size sequence: 2, 6, 10, 14, ...
  302.         for (int k = 0; k < size; ++k) {
  303.             sequence[k] = 4 * k + 2;
  304.         }

  305.         // initialize the order selection cost array
  306.         // (number of function calls for each column of the extrapolation table)
  307.         costPerStep[0] = sequence[0] + 1;
  308.         for (int k = 1; k < size; ++k) {
  309.             costPerStep[k] = costPerStep[k - 1] + sequence[k];
  310.         }

  311.         // initialize the extrapolation tables
  312.         for (int k = 0; k < size; ++k) {
  313.             coeff[k] = (k > 0) ? new double[k] : null;
  314.             for (int l = 0; l < k; ++l) {
  315.                 final double ratio = ((double) sequence[k]) / sequence[k - l - 1];
  316.                 coeff[k][l] = 1.0 / (ratio * ratio - 1.0);
  317.             }
  318.         }

  319.     }

  320.     /** Set the interpolation order control parameter.
  321.      * The interpolation order for dense output is 2k - mudif + 1. The
  322.      * default value for mudif is 4 and the interpolation error is used
  323.      * in stepsize control by default.

  324.      * @param useInterpolationErrorForControl if true, interpolation error is used
  325.      * for stepsize control
  326.      * @param mudifControlParameter interpolation order control parameter (the parameter
  327.      * is reset to default if &lt;= 0 or &gt;= 7)
  328.      */
  329.     public void setInterpolationControl(final boolean useInterpolationErrorForControl,
  330.                                         final int mudifControlParameter) {

  331.         this.useInterpolationError = useInterpolationErrorForControl;

  332.         if ((mudifControlParameter <= 0) || (mudifControlParameter >= 7)) {
  333.             this.mudif = 4;
  334.         } else {
  335.             this.mudif = mudifControlParameter;
  336.         }

  337.     }

  338.     /** Update scaling array.
  339.      * @param y1 first state vector to use for scaling
  340.      * @param y2 second state vector to use for scaling
  341.      * @param scale scaling array to update (can be shorter than state)
  342.      */
  343.     private void rescale(final double[] y1, final double[] y2, final double[] scale) {
  344.         final StepsizeHelper helper = getStepSizeHelper();
  345.         for (int i = 0; i < scale.length; ++i) {
  346.             scale[i] = helper.getTolerance(i, FastMath.max(FastMath.abs(y1[i]), FastMath.abs(y2[i])));
  347.         }
  348.     }

  349.     /** Perform integration over one step using substeps of a modified
  350.      * midpoint method.
  351.      * @param t0 initial time
  352.      * @param y0 initial value of the state vector at t0
  353.      * @param step global step
  354.      * @param k iteration number (from 0 to sequence.length - 1)
  355.      * @param scale scaling array (can be shorter than state)
  356.      * @param f placeholder where to put the state vector derivatives at each substep
  357.      *          (element 0 already contains initial derivative)
  358.      * @param yMiddle placeholder where to put the state vector at the middle of the step
  359.      * @param yEnd placeholder where to put the state vector at the end
  360.      * @return true if computation was done properly,
  361.      *         false if stability check failed before end of computation
  362.      * @exception MathIllegalStateException if the number of functions evaluations is exceeded
  363.      * @exception MathIllegalArgumentException if arrays dimensions do not match equations settings
  364.      */
  365.     private boolean tryStep(final double t0, final double[] y0, final double step, final int k,
  366.                             final double[] scale, final double[][] f,
  367.                             final double[] yMiddle, final double[] yEnd)
  368.         throws MathIllegalArgumentException, MathIllegalStateException {

  369.         final int    n        = sequence[k];
  370.         final double subStep  = step / n;
  371.         final double subStep2 = 2 * subStep;

  372.         // first substep
  373.         double t = t0 + subStep;
  374.         for (int i = 0; i < y0.length; ++i) {
  375.             yEnd[i] = y0[i] + subStep * f[0][i];
  376.         }
  377.         f[1] = computeDerivatives(t, yEnd);

  378.         // other substeps
  379.         final double[] yTmp = y0.clone();
  380.         for (int j = 1; j < n; ++j) {

  381.             if (2 * j == n) {
  382.                 // save the point at the middle of the step
  383.                 System.arraycopy(yEnd, 0, yMiddle, 0, y0.length);
  384.             }

  385.             t += subStep;
  386.             for (int i = 0; i < y0.length; ++i) {
  387.                 final double middle = yEnd[i];
  388.                 yEnd[i]       = yTmp[i] + subStep2 * f[j][i];
  389.                 yTmp[i]       = middle;
  390.             }

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

  392.             // stability check
  393.             if (performTest && (j <= maxChecks) && (k < maxIter)) {
  394.                 double initialNorm = 0.0;
  395.                 for (int l = 0; l < scale.length; ++l) {
  396.                     final double ratio = f[0][l] / scale[l];
  397.                     initialNorm += ratio * ratio;
  398.                 }
  399.                 double deltaNorm = 0.0;
  400.                 for (int l = 0; l < scale.length; ++l) {
  401.                     final double ratio = (f[j + 1][l] - f[0][l]) / scale[l];
  402.                     deltaNorm += ratio * ratio;
  403.                 }
  404.                 if (deltaNorm > 4 * FastMath.max(1.0e-15, initialNorm)) {
  405.                     return false;
  406.                 }
  407.             }

  408.         }

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

  413.         return true;

  414.     }

  415.     /** Extrapolate a vector.
  416.      * @param offset offset to use in the coefficients table
  417.      * @param k index of the last updated point
  418.      * @param diag working diagonal of the Aitken-Neville's
  419.      * triangle, without the last element
  420.      * @param last last element
  421.      */
  422.     private void extrapolate(final int offset, final int k,
  423.                              final double[][] diag, final double[] last) {

  424.         // update the diagonal
  425.         for (int j = 1; j < k; ++j) {
  426.             for (int i = 0; i < last.length; ++i) {
  427.                 // Aitken-Neville's recursive formula
  428.                 diag[k - j - 1][i] = diag[k - j][i] +
  429.                                 coeff[k + offset][j - 1] * (diag[k - j][i] - diag[k - j - 1][i]);
  430.             }
  431.         }

  432.         // update the last element
  433.         for (int i = 0; i < last.length; ++i) {
  434.             // Aitken-Neville's recursive formula
  435.             last[i] = diag[0][i] + coeff[k + offset][k - 1] * (diag[0][i] - last[i]);
  436.         }
  437.     }

  438.     /** {@inheritDoc} */
  439.     @Override
  440.     public ODEStateAndDerivative integrate(final ExpandableODE equations,
  441.                                            final ODEState initialState, final double finalTime)
  442.         throws MathIllegalArgumentException, MathIllegalStateException {

  443.         sanityChecks(initialState, finalTime);
  444.         setStepStart(initIntegration(equations, initialState, finalTime));
  445.         final boolean forward = finalTime > initialState.getTime();

  446.         // create some internal working arrays
  447.         double[]         y        = getStepStart().getCompleteState();
  448.         final double[]   y1       = new double[y.length];
  449.         final double[][] diagonal = new double[sequence.length - 1][];
  450.         final double[][] y1Diag   = new double[sequence.length - 1][];
  451.         for (int k = 0; k < sequence.length - 1; ++k) {
  452.             diagonal[k] = new double[y.length];
  453.             y1Diag[k]   = new double[y.length];
  454.         }

  455.         final double[][][] fk = new double[sequence.length][][];
  456.         for (int k = 0; k < sequence.length; ++k) {
  457.             fk[k] = new double[sequence[k] + 1][];
  458.         }

  459.         // scaled derivatives at the middle of the step $\tau$
  460.         // (element k is $h^{k} d^{k}y(\tau)/dt^{k}$ where h is step size...)
  461.         final double[][] yMidDots = new double[1 + 2 * sequence.length][y.length];

  462.         // initial scaling
  463.         final int mainSetDimension = getStepSizeHelper().getMainSetDimension();
  464.         final double[] scale = new double[mainSetDimension];
  465.         rescale(y, y, scale);

  466.         // initial order selection
  467.         final double tol    = getStepSizeHelper().getRelativeTolerance(0);
  468.         final double log10R = FastMath.log10(FastMath.max(1.0e-10, tol));
  469.         int targetIter = FastMath.max(1,
  470.                                       FastMath.min(sequence.length - 2,
  471.                                                    (int) FastMath.floor(0.5 - 0.6 * log10R)));

  472.         double  hNew                     = 0;
  473.         double  maxError                 = Double.MAX_VALUE;
  474.         boolean previousRejected         = false;
  475.         boolean firstTime                = true;
  476.         boolean newStep                  = true;
  477.         costPerTimeUnit[0] = 0;
  478.         setIsLastStep(false);
  479.         do {

  480.             double error;
  481.             boolean reject = false;

  482.             if (newStep) {

  483.                 // first evaluation, at the beginning of the step
  484.                 final double[] yDot0 = getStepStart().getCompleteDerivative();
  485.                 for (int k = 0; k < sequence.length; ++k) {
  486.                     // all sequences start from the same point, so we share the derivatives
  487.                     fk[k][0] = yDot0;
  488.                 }

  489.                 if (firstTime) {
  490.                     hNew = initializeStep(forward, 2 * targetIter + 1, scale,
  491.                                           getStepStart());
  492.                 }

  493.                 newStep = false;

  494.             }

  495.             setStepSize(hNew);

  496.             // step adjustment near bounds
  497.             if (forward) {
  498.                 if (getStepStart().getTime() + getStepSize() >= finalTime) {
  499.                     setStepSize(finalTime - getStepStart().getTime());
  500.                 }
  501.             } else {
  502.                 if (getStepStart().getTime() + getStepSize() <= finalTime) {
  503.                     setStepSize(finalTime - getStepStart().getTime());
  504.                 }
  505.             }
  506.             final double nextT = getStepStart().getTime() + getStepSize();
  507.             setIsLastStep(forward ? (nextT >= finalTime) : (nextT <= finalTime));

  508.             // iterate over several substep sizes
  509.             int k = -1;
  510.             for (boolean loop = true; loop; ) {

  511.                 ++k;

  512.                 // modified midpoint integration with the current substep
  513.                 if ( ! tryStep(getStepStart().getTime(), y, getStepSize(), k, scale, fk[k],
  514.                                (k == 0) ? yMidDots[0] : diagonal[k - 1],
  515.                                (k == 0) ? y1 : y1Diag[k - 1])) {

  516.                     // the stability check failed, we reduce the global step
  517.                     hNew   = FastMath.abs(getStepSizeHelper().filterStep(getStepSize() * stabilityReduction, forward, false));
  518.                     reject = true;
  519.                     loop   = false;

  520.                 } else {

  521.                     // the substep was computed successfully
  522.                     if (k > 0) {

  523.                         // extrapolate the state at the end of the step
  524.                         // using last iteration data
  525.                         extrapolate(0, k, y1Diag, y1);
  526.                         rescale(y, y1, scale);

  527.                         // estimate the error at the end of the step.
  528.                         error = 0;
  529.                         for (int j = 0; j < mainSetDimension; ++j) {
  530.                             final double e = FastMath.abs(y1[j] - y1Diag[0][j]) / scale[j];
  531.                             error += e * e;
  532.                         }
  533.                         error = FastMath.sqrt(error / mainSetDimension);
  534.                         if (Double.isNaN(error)) {
  535.                             throw new MathIllegalStateException(LocalizedODEFormats.NAN_APPEARING_DURING_INTEGRATION,
  536.                                                                 nextT);
  537.                         }

  538.                         if ((error > 1.0e15) || ((k > 1) && (error > maxError))) {
  539.                             // error is too big, we reduce the global step
  540.                             hNew   = FastMath.abs(getStepSizeHelper().filterStep(getStepSize() * stabilityReduction, forward, false));
  541.                             reject = true;
  542.                             loop   = false;
  543.                         } else {

  544.                             maxError = FastMath.max(4 * error, 1.0);

  545.                             // compute optimal stepsize for this order
  546.                             final double exp = 1.0 / (2 * k + 1);
  547.                             double fac = stepControl2 / FastMath.pow(error / stepControl1, exp);
  548.                             final double pow = FastMath.pow(stepControl3, exp);
  549.                             fac = FastMath.max(pow / stepControl4, FastMath.min(1 / pow, fac));
  550.                             final boolean acceptSmall = k < targetIter;
  551.                             optimalStep[k]     = FastMath.abs(getStepSizeHelper().filterStep(getStepSize() * fac, forward, acceptSmall));
  552.                             costPerTimeUnit[k] = costPerStep[k] / optimalStep[k];

  553.                             // check convergence
  554.                             switch (k - targetIter) {

  555.                                 case -1 :
  556.                                     if ((targetIter > 1) && ! previousRejected) {

  557.                                         // check if we can stop iterations now
  558.                                         if (error <= 1.0) {
  559.                                             // convergence have been reached just before targetIter
  560.                                             loop = false;
  561.                                         } else {
  562.                                             // estimate if there is a chance convergence will
  563.                                             // be reached on next iteration, using the
  564.                                             // asymptotic evolution of error
  565.                                             final double ratio = ((double) sequence [targetIter] * sequence[targetIter + 1]) /
  566.                                                             (sequence[0] * sequence[0]);
  567.                                             if (error > ratio * ratio) {
  568.                                                 // we don't expect to converge on next iteration
  569.                                                 // we reject the step immediately and reduce order
  570.                                                 reject = true;
  571.                                                 loop   = false;
  572.                                                 targetIter = k;
  573.                                                 if ((targetIter > 1) &&
  574.                                                     (costPerTimeUnit[targetIter - 1] <
  575.                                                                     orderControl1 * costPerTimeUnit[targetIter])) {
  576.                                                     --targetIter;
  577.                                                 }
  578.                                                 hNew = getStepSizeHelper().filterStep(optimalStep[targetIter], forward, false);
  579.                                             }
  580.                                         }
  581.                                     }
  582.                                     break;

  583.                                 case 0:
  584.                                     if (error <= 1.0) {
  585.                                         // convergence has been reached exactly at targetIter
  586.                                         loop = false;
  587.                                     } else {
  588.                                         // estimate if there is a chance convergence will
  589.                                         // be reached on next iteration, using the
  590.                                         // asymptotic evolution of error
  591.                                         final double ratio = ((double) sequence[k + 1]) / sequence[0];
  592.                                         if (error > ratio * ratio) {
  593.                                             // we don't expect to converge on next iteration
  594.                                             // we reject the step immediately
  595.                                             reject = true;
  596.                                             loop = false;
  597.                                             if ((targetIter > 1) &&
  598.                                                  (costPerTimeUnit[targetIter - 1] <
  599.                                                                  orderControl1 * costPerTimeUnit[targetIter])) {
  600.                                                 --targetIter;
  601.                                             }
  602.                                             hNew = getStepSizeHelper().filterStep(optimalStep[targetIter], forward, false);
  603.                                         }
  604.                                     }
  605.                                     break;

  606.                                 case 1 :
  607.                                     if (error > 1.0) {
  608.                                         reject = true;
  609.                                         if ((targetIter > 1) &&
  610.                                             (costPerTimeUnit[targetIter - 1] <
  611.                                                             orderControl1 * costPerTimeUnit[targetIter])) {
  612.                                             --targetIter;
  613.                                         }
  614.                                         hNew = getStepSizeHelper().filterStep(optimalStep[targetIter], forward, false);
  615.                                     }
  616.                                     loop = false;
  617.                                     break;

  618.                                 default :
  619.                                     if ((firstTime || isLastStep()) && (error <= 1.0)) {
  620.                                         loop = false;
  621.                                     }
  622.                                     break;

  623.                             }

  624.                         }
  625.                     }
  626.                 }
  627.             }

  628.             // dense output handling
  629.             double hInt = getMaxStep();
  630.             final GraggBulirschStoerStateInterpolator interpolator;
  631.             if (! reject) {

  632.                 // extrapolate state at middle point of the step
  633.                 for (int j = 1; j <= k; ++j) {
  634.                     extrapolate(0, j, diagonal, yMidDots[0]);
  635.                 }

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

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

  638.                     // derivative at middle point of the step
  639.                     final int l2 = l / 2;
  640.                     double factor = FastMath.pow(0.5 * sequence[l2], l);
  641.                     int middleIndex = fk[l2].length / 2;
  642.                     for (int i = 0; i < y.length; ++i) {
  643.                         yMidDots[l + 1][i] = factor * fk[l2][middleIndex + l][i];
  644.                     }
  645.                     for (int j = 1; j <= k - l2; ++j) {
  646.                         factor = FastMath.pow(0.5 * sequence[j + l2], l);
  647.                         middleIndex = fk[l2 + j].length / 2;
  648.                         for (int i = 0; i < y.length; ++i) {
  649.                             diagonal[j - 1][i] = factor * fk[l2 + j][middleIndex + l][i];
  650.                         }
  651.                         extrapolate(l2, j, diagonal, yMidDots[l + 1]);
  652.                     }
  653.                     for (int i = 0; i < y.length; ++i) {
  654.                         yMidDots[l + 1][i] *= getStepSize();
  655.                     }

  656.                     // compute centered differences to evaluate next derivatives
  657.                     for (int j = (l + 1) / 2; j <= k; ++j) {
  658.                         for (int m = fk[j].length - 1; m >= 2 * (l + 1); --m) {
  659.                             for (int i = 0; i < y.length; ++i) {
  660.                                 fk[j][m][i] -= fk[j][m - 2][i];
  661.                             }
  662.                         }
  663.                     }

  664.                 }

  665.                 // state at end of step
  666.                 final ODEStateAndDerivative stepEnd =
  667.                     equations.getMapper().mapStateAndDerivative(nextT, y1, computeDerivatives(nextT, y1));

  668.                 // set up interpolator covering the full step
  669.                 interpolator = new GraggBulirschStoerStateInterpolator(forward,
  670.                                                                        getStepStart(), stepEnd,
  671.                                                                        getStepStart(), stepEnd,
  672.                                                                        equations.getMapper(),
  673.                                                                        yMidDots, mu);

  674.                 if (mu >= 0 && useInterpolationError) {
  675.                     // use the interpolation error to limit stepsize
  676.                     final double interpError = interpolator.estimateError(scale);
  677.                     hInt = FastMath.abs(getStepSize() /
  678.                                         FastMath.max(FastMath.pow(interpError, 1.0 / (mu + 4)), 0.01));
  679.                     if (interpError > 10.0) {
  680.                         hNew   = getStepSizeHelper().filterStep(hInt, forward, false);
  681.                         reject = true;
  682.                     }
  683.                 }

  684.             } else {
  685.                 interpolator = null;
  686.             }

  687.             if (! reject) {

  688.                 // Discrete events handling
  689.                 setStepStart(acceptStep(interpolator, finalTime));

  690.                 // prepare next step
  691.                 // beware that y1 is not always valid anymore here,
  692.                 // as some event may have triggered a reset
  693.                 // so we need to copy the new step start set previously
  694.                 y = getStepStart().getCompleteState();

  695.                 int optimalIter;
  696.                 if (k == 1) {
  697.                     optimalIter = 2;
  698.                     if (previousRejected) {
  699.                         optimalIter = 1;
  700.                     }
  701.                 } else if (k <= targetIter) {
  702.                     optimalIter = k;
  703.                     if (costPerTimeUnit[k - 1] < orderControl1 * costPerTimeUnit[k]) {
  704.                         optimalIter = k - 1;
  705.                     } else if (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[k - 1]) {
  706.                         optimalIter = FastMath.min(k + 1, sequence.length - 2);
  707.                     }
  708.                 } else {
  709.                     optimalIter = k - 1;
  710.                     if ((k > 2) && (costPerTimeUnit[k - 2] < orderControl1 * costPerTimeUnit[k - 1])) {
  711.                         optimalIter = k - 2;
  712.                     }
  713.                     if (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[optimalIter]) {
  714.                         optimalIter = FastMath.min(k, sequence.length - 2);
  715.                     }
  716.                 }

  717.                 if (previousRejected) {
  718.                     // after a rejected step neither order nor stepsize
  719.                     // should increase
  720.                     targetIter = FastMath.min(optimalIter, k);
  721.                     hNew = FastMath.min(FastMath.abs(getStepSize()), optimalStep[targetIter]);
  722.                 } else {
  723.                     // stepsize control
  724.                     if (optimalIter <= k) {
  725.                         hNew = getStepSizeHelper().filterStep(optimalStep[optimalIter], forward, false);
  726.                     } else {
  727.                         if ((k < targetIter) &&
  728.                                         (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[k - 1])) {
  729.                             hNew = getStepSizeHelper().
  730.                                    filterStep(optimalStep[k] * costPerStep[optimalIter + 1] / costPerStep[k], forward, false);
  731.                         } else {
  732.                             hNew = getStepSizeHelper().
  733.                                    filterStep(optimalStep[k] * costPerStep[optimalIter] / costPerStep[k], forward, false);
  734.                         }
  735.                     }

  736.                     targetIter = optimalIter;

  737.                 }

  738.                 newStep = true;

  739.             }

  740.             hNew = FastMath.min(hNew, hInt);
  741.             if (! forward) {
  742.                 hNew = -hNew;
  743.             }

  744.             firstTime = false;

  745.             if (reject) {
  746.                 setIsLastStep(false);
  747.                 previousRejected = true;
  748.             } else {
  749.                 previousRejected = false;
  750.             }

  751.         } while (!isLastStep());

  752.         final ODEStateAndDerivative finalState = getStepStart();
  753.         resetInternalState();
  754.         return finalState;

  755.     }

  756. }