001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one or more
003     * contributor license agreements.  See the NOTICE file distributed with
004     * this work for additional information regarding copyright ownership.
005     * The ASF licenses this file to You under the Apache License, Version 2.0
006     * (the "License"); you may not use this file except in compliance with
007     * the License.  You may obtain a copy of the License at
008     *
009     *      http://www.apache.org/licenses/LICENSE-2.0
010     *
011     * Unless required by applicable law or agreed to in writing, software
012     * distributed under the License is distributed on an "AS IS" BASIS,
013     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     * See the License for the specific language governing permissions and
015     * limitations under the License.
016     */
017    
018    package org.apache.commons.math.ode.nonstiff;
019    
020    import org.apache.commons.math.analysis.solvers.UnivariateRealSolver;
021    import org.apache.commons.math.exception.MathIllegalArgumentException;
022    import org.apache.commons.math.exception.MathIllegalStateException;
023    import org.apache.commons.math.ode.ExpandableStatefulODE;
024    import org.apache.commons.math.ode.events.EventHandler;
025    import org.apache.commons.math.ode.sampling.AbstractStepInterpolator;
026    import org.apache.commons.math.ode.sampling.StepHandler;
027    import org.apache.commons.math.util.FastMath;
028    
029    /**
030     * This class implements a Gragg-Bulirsch-Stoer integrator for
031     * Ordinary Differential Equations.
032     *
033     * <p>The Gragg-Bulirsch-Stoer algorithm is one of the most efficient
034     * ones currently available for smooth problems. It uses Richardson
035     * extrapolation to estimate what would be the solution if the step
036     * size could be decreased down to zero.</p>
037     *
038     * <p>
039     * This method changes both the step size and the order during
040     * integration, in order to minimize computation cost. It is
041     * particularly well suited when a very high precision is needed. The
042     * limit where this method becomes more efficient than high-order
043     * embedded Runge-Kutta methods like {@link DormandPrince853Integrator
044     * Dormand-Prince 8(5,3)} depends on the problem. Results given in the
045     * Hairer, Norsett and Wanner book show for example that this limit
046     * occurs for accuracy around 1e-6 when integrating Saltzam-Lorenz
047     * equations (the authors note this problem is <i>extremely sensitive
048     * to the errors in the first integration steps</i>), and around 1e-11
049     * for a two dimensional celestial mechanics problems with seven
050     * bodies (pleiades problem, involving quasi-collisions for which
051     * <i>automatic step size control is essential</i>).
052     * </p>
053     *
054     * <p>
055     * This implementation is basically a reimplementation in Java of the
056     * <a
057     * href="http://www.unige.ch/math/folks/hairer/prog/nonstiff/odex.f">odex</a>
058     * fortran code by E. Hairer and G. Wanner. The redistribution policy
059     * for this code is available <a
060     * href="http://www.unige.ch/~hairer/prog/licence.txt">here</a>, for
061     * convenience, it is reproduced below.</p>
062     * </p>
063     *
064     * <table border="0" width="80%" cellpadding="10" align="center" bgcolor="#E0E0E0">
065     * <tr><td>Copyright (c) 2004, Ernst Hairer</td></tr>
066     *
067     * <tr><td>Redistribution and use in source and binary forms, with or
068     * without modification, are permitted provided that the following
069     * conditions are met:
070     * <ul>
071     *  <li>Redistributions of source code must retain the above copyright
072     *      notice, this list of conditions and the following disclaimer.</li>
073     *  <li>Redistributions in binary form must reproduce the above copyright
074     *      notice, this list of conditions and the following disclaimer in the
075     *      documentation and/or other materials provided with the distribution.</li>
076     * </ul></td></tr>
077     *
078     * <tr><td><strong>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
079     * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
080     * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
081     * FOR A  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
082     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
083     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
084     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
085     * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
086     * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
087     * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
088     * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</strong></td></tr>
089     * </table>
090     *
091     * @version $Id: GraggBulirschStoerIntegrator.java 1176734 2011-09-28 05:56:42Z luc $
092     * @since 1.2
093     */
094    
095    public class GraggBulirschStoerIntegrator extends AdaptiveStepsizeIntegrator {
096    
097        /** Integrator method name. */
098        private static final String METHOD_NAME = "Gragg-Bulirsch-Stoer";
099    
100        /** maximal order. */
101        private int maxOrder;
102    
103        /** step size sequence. */
104        private int[] sequence;
105    
106        /** overall cost of applying step reduction up to iteration k+1, in number of calls. */
107        private int[] costPerStep;
108    
109        /** cost per unit step. */
110        private double[] costPerTimeUnit;
111    
112        /** optimal steps for each order. */
113        private double[] optimalStep;
114    
115        /** extrapolation coefficients. */
116        private double[][] coeff;
117    
118        /** stability check enabling parameter. */
119        private boolean performTest;
120    
121        /** maximal number of checks for each iteration. */
122        private int maxChecks;
123    
124        /** maximal number of iterations for which checks are performed. */
125        private int maxIter;
126    
127        /** stepsize reduction factor in case of stability check failure. */
128        private double stabilityReduction;
129    
130        /** first stepsize control factor. */
131        private double stepControl1;
132    
133        /** second stepsize control factor. */
134        private double stepControl2;
135    
136        /** third stepsize control factor. */
137        private double stepControl3;
138    
139        /** fourth stepsize control factor. */
140        private double stepControl4;
141    
142        /** first order control factor. */
143        private double orderControl1;
144    
145        /** second order control factor. */
146        private double orderControl2;
147    
148        /** use interpolation error in stepsize control. */
149        private boolean useInterpolationError;
150    
151        /** interpolation order control parameter. */
152        private int mudif;
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 (sign is irrelevant, regardless of
159       * integration direction, forward or backward), the last step can
160       * be smaller than this
161       * @param maxStep maximal step (sign is irrelevant, regardless of
162       * integration direction, forward or backward), the last step can
163       * be smaller than this
164       * @param scalAbsoluteTolerance allowed absolute error
165       * @param scalRelativeTolerance allowed relative error
166       */
167      public GraggBulirschStoerIntegrator(final double minStep, final double maxStep,
168                                          final double scalAbsoluteTolerance,
169                                          final double scalRelativeTolerance) {
170        super(METHOD_NAME, minStep, maxStep,
171              scalAbsoluteTolerance, scalRelativeTolerance);
172        setStabilityCheck(true, -1, -1, -1);
173        setStepsizeControl(-1, -1, -1, -1);
174        setOrderControl(-1, -1, -1);
175        setInterpolationControl(true, -1);
176      }
177    
178      /** Simple constructor.
179       * Build a Gragg-Bulirsch-Stoer integrator with the given step
180       * bounds. All tuning parameters are set to their default
181       * values. The default step handler does nothing.
182       * @param minStep minimal step (must be positive even for backward
183       * integration), the last step can be smaller than this
184       * @param maxStep maximal step (must be positive even for backward
185       * integration)
186       * @param vecAbsoluteTolerance allowed absolute error
187       * @param vecRelativeTolerance allowed relative error
188       */
189      public GraggBulirschStoerIntegrator(final double minStep, final double maxStep,
190                                          final double[] vecAbsoluteTolerance,
191                                          final double[] vecRelativeTolerance) {
192        super(METHOD_NAME, minStep, maxStep,
193              vecAbsoluteTolerance, vecRelativeTolerance);
194        setStabilityCheck(true, -1, -1, -1);
195        setStepsizeControl(-1, -1, -1, -1);
196        setOrderControl(-1, -1, -1);
197        setInterpolationControl(true, -1);
198      }
199    
200      /** Set the stability check controls.
201       * <p>The stability check is performed on the first few iterations of
202       * the extrapolation scheme. If this test fails, the step is rejected
203       * and the stepsize is reduced.</p>
204       * <p>By default, the test is performed, at most during two
205       * iterations at each step, and at most once for each of these
206       * iterations. The default stepsize reduction factor is 0.5.</p>
207       * @param performStabilityCheck if true, stability check will be performed,
208         if false, the check will be skipped
209       * @param maxNumIter maximal number of iterations for which checks are
210       * performed (the number of iterations is reset to default if negative
211       * or null)
212       * @param maxNumChecks maximal number of checks for each iteration
213       * (the number of checks is reset to default if negative or null)
214       * @param stepsizeReductionFactor stepsize reduction factor in case of
215       * failure (the factor is reset to default if lower than 0.0001 or
216       * greater than 0.9999)
217       */
218      public void setStabilityCheck(final boolean performStabilityCheck,
219                                    final int maxNumIter, final int maxNumChecks,
220                                    final double stepsizeReductionFactor) {
221    
222        this.performTest = performStabilityCheck;
223        this.maxIter     = (maxNumIter   <= 0) ? 2 : maxNumIter;
224        this.maxChecks   = (maxNumChecks <= 0) ? 1 : maxNumChecks;
225    
226        if ((stepsizeReductionFactor < 0.0001) || (stepsizeReductionFactor > 0.9999)) {
227          this.stabilityReduction = 0.5;
228        } else {
229          this.stabilityReduction = stepsizeReductionFactor;
230        }
231    
232      }
233    
234      /** Set the step size control factors.
235    
236       * <p>The new step size hNew is computed from the old one h by:
237       * <pre>
238       * hNew = h * stepControl2 / (err/stepControl1)^(1/(2k+1))
239       * </pre>
240       * where err is the scaled error and k the iteration number of the
241       * extrapolation scheme (counting from 0). The default values are
242       * 0.65 for stepControl1 and 0.94 for stepControl2.</p>
243       * <p>The step size is subject to the restriction:
244       * <pre>
245       * stepControl3^(1/(2k+1))/stepControl4 <= hNew/h <= 1/stepControl3^(1/(2k+1))
246       * </pre>
247       * The default values are 0.02 for stepControl3 and 4.0 for
248       * stepControl4.</p>
249       * @param control1 first stepsize control factor (the factor is
250       * reset to default if lower than 0.0001 or greater than 0.9999)
251       * @param control2 second stepsize control factor (the factor
252       * is reset to default if lower than 0.0001 or greater than 0.9999)
253       * @param control3 third stepsize control factor (the factor is
254       * reset to default if lower than 0.0001 or greater than 0.9999)
255       * @param control4 fourth stepsize control factor (the factor
256       * is reset to default if lower than 1.0001 or greater than 999.9)
257       */
258      public void setStepsizeControl(final double control1, final double control2,
259                                     final double control3, final double control4) {
260    
261        if ((control1 < 0.0001) || (control1 > 0.9999)) {
262          this.stepControl1 = 0.65;
263        } else {
264          this.stepControl1 = control1;
265        }
266    
267        if ((control2 < 0.0001) || (control2 > 0.9999)) {
268          this.stepControl2 = 0.94;
269        } else {
270          this.stepControl2 = control2;
271        }
272    
273        if ((control3 < 0.0001) || (control3 > 0.9999)) {
274          this.stepControl3 = 0.02;
275        } else {
276          this.stepControl3 = control3;
277        }
278    
279        if ((control4 < 1.0001) || (control4 > 999.9)) {
280          this.stepControl4 = 4.0;
281        } else {
282          this.stepControl4 = control4;
283        }
284    
285      }
286    
287      /** Set the order control parameters.
288       * <p>The Gragg-Bulirsch-Stoer method changes both the step size and
289       * the order during integration, in order to minimize computation
290       * cost. Each extrapolation step increases the order by 2, so the
291       * maximal order that will be used is always even, it is twice the
292       * maximal number of columns in the extrapolation table.</p>
293       * <pre>
294       * order is decreased if w(k-1) <= w(k)   * orderControl1
295       * order is increased if w(k)   <= w(k-1) * orderControl2
296       * </pre>
297       * <p>where w is the table of work per unit step for each order
298       * (number of function calls divided by the step length), and k is
299       * the current order.</p>
300       * <p>The default maximal order after construction is 18 (i.e. the
301       * maximal number of columns is 9). The default values are 0.8 for
302       * orderControl1 and 0.9 for orderControl2.</p>
303       * @param maximalOrder maximal order in the extrapolation table (the
304       * maximal order is reset to default if order <= 6 or odd)
305       * @param control1 first order control factor (the factor is
306       * reset to default if lower than 0.0001 or greater than 0.9999)
307       * @param control2 second order control factor (the factor
308       * is reset to default if lower than 0.0001 or greater than 0.9999)
309       */
310      public void setOrderControl(final int maximalOrder,
311                                  final double control1, final double control2) {
312    
313        if ((maximalOrder <= 6) || (maximalOrder % 2 != 0)) {
314          this.maxOrder = 18;
315        }
316    
317        if ((control1 < 0.0001) || (control1 > 0.9999)) {
318          this.orderControl1 = 0.8;
319        } else {
320          this.orderControl1 = control1;
321        }
322    
323        if ((control2 < 0.0001) || (control2 > 0.9999)) {
324          this.orderControl2 = 0.9;
325        } else {
326          this.orderControl2 = control2;
327        }
328    
329        // reinitialize the arrays
330        initializeArrays();
331    
332      }
333    
334      /** {@inheritDoc} */
335      @Override
336      public void addStepHandler (final StepHandler handler) {
337    
338        super.addStepHandler(handler);
339    
340        // reinitialize the arrays
341        initializeArrays();
342    
343      }
344    
345      /** {@inheritDoc} */
346      @Override
347      public void addEventHandler(final EventHandler function,
348                                  final double maxCheckInterval,
349                                  final double convergence,
350                                  final int maxIterationCount,
351                                  final UnivariateRealSolver solver) {
352        super.addEventHandler(function, maxCheckInterval, convergence,
353                              maxIterationCount, solver);
354    
355        // reinitialize the arrays
356        initializeArrays();
357    
358      }
359    
360      /** Initialize the integrator internal arrays. */
361      private void initializeArrays() {
362    
363        final int size = maxOrder / 2;
364    
365        if ((sequence == null) || (sequence.length != size)) {
366          // all arrays should be reallocated with the right size
367          sequence        = new int[size];
368          costPerStep     = new int[size];
369          coeff           = new double[size][];
370          costPerTimeUnit = new double[size];
371          optimalStep     = new double[size];
372        }
373    
374        // step size sequence: 2, 6, 10, 14, ...
375        for (int k = 0; k < size; ++k) {
376            sequence[k] = 4 * k + 2;
377        }
378    
379        // initialize the order selection cost array
380        // (number of function calls for each column of the extrapolation table)
381        costPerStep[0] = sequence[0] + 1;
382        for (int k = 1; k < size; ++k) {
383          costPerStep[k] = costPerStep[k-1] + sequence[k];
384        }
385    
386        // initialize the extrapolation tables
387        for (int k = 0; k < size; ++k) {
388          coeff[k] = (k > 0) ? new double[k] : null;
389          for (int l = 0; l < k; ++l) {
390            final double ratio = ((double) sequence[k]) / sequence[k-l-1];
391            coeff[k][l] = 1.0 / (ratio * ratio - 1.0);
392          }
393        }
394    
395      }
396    
397      /** Set the interpolation order control parameter.
398       * The interpolation order for dense output is 2k - mudif + 1. The
399       * default value for mudif is 4 and the interpolation error is used
400       * in stepsize control by default.
401    
402       * @param useInterpolationErrorForControl if true, interpolation error is used
403       * for stepsize control
404       * @param mudifControlParameter interpolation order control parameter (the parameter
405       * is reset to default if <= 0 or >= 7)
406       */
407      public void setInterpolationControl(final boolean useInterpolationErrorForControl,
408                                          final int mudifControlParameter) {
409    
410        this.useInterpolationError = useInterpolationErrorForControl;
411    
412        if ((mudifControlParameter <= 0) || (mudifControlParameter >= 7)) {
413          this.mudif = 4;
414        } else {
415          this.mudif = mudifControlParameter;
416        }
417    
418      }
419    
420      /** Update scaling array.
421       * @param y1 first state vector to use for scaling
422       * @param y2 second state vector to use for scaling
423       * @param scale scaling array to update (can be shorter than state)
424       */
425      private void rescale(final double[] y1, final double[] y2, final double[] scale) {
426        if (vecAbsoluteTolerance == null) {
427          for (int i = 0; i < scale.length; ++i) {
428            final double yi = FastMath.max(FastMath.abs(y1[i]), FastMath.abs(y2[i]));
429            scale[i] = scalAbsoluteTolerance + scalRelativeTolerance * yi;
430          }
431        } else {
432          for (int i = 0; i < scale.length; ++i) {
433            final double yi = FastMath.max(FastMath.abs(y1[i]), FastMath.abs(y2[i]));
434            scale[i] = vecAbsoluteTolerance[i] + vecRelativeTolerance[i] * yi;
435          }
436        }
437      }
438    
439      /** Perform integration over one step using substeps of a modified
440       * midpoint method.
441       * @param t0 initial time
442       * @param y0 initial value of the state vector at t0
443       * @param step global step
444       * @param k iteration number (from 0 to sequence.length - 1)
445       * @param scale scaling array (can be shorter than state)
446       * @param f placeholder where to put the state vector derivatives at each substep
447       *          (element 0 already contains initial derivative)
448       * @param yMiddle placeholder where to put the state vector at the middle of the step
449       * @param yEnd placeholder where to put the state vector at the end
450       * @param yTmp placeholder for one state vector
451       * @return true if computation was done properly,
452       *         false if stability check failed before end of computation
453       */
454      private boolean tryStep(final double t0, final double[] y0, final double step, final int k,
455                              final double[] scale, final double[][] f,
456                              final double[] yMiddle, final double[] yEnd,
457                              final double[] yTmp) {
458    
459        final int    n        = sequence[k];
460        final double subStep  = step / n;
461        final double subStep2 = 2 * subStep;
462    
463        // first substep
464        double t = t0 + subStep;
465        for (int i = 0; i < y0.length; ++i) {
466          yTmp[i] = y0[i];
467          yEnd[i] = y0[i] + subStep * f[0][i];
468        }
469        computeDerivatives(t, yEnd, f[1]);
470    
471        // other substeps
472        for (int j = 1; j < n; ++j) {
473    
474          if (2 * j == n) {
475            // save the point at the middle of the step
476            System.arraycopy(yEnd, 0, yMiddle, 0, y0.length);
477          }
478    
479          t += subStep;
480          for (int i = 0; i < y0.length; ++i) {
481            final double middle = yEnd[i];
482            yEnd[i]       = yTmp[i] + subStep2 * f[j][i];
483            yTmp[i]       = middle;
484          }
485    
486          computeDerivatives(t, yEnd, f[j+1]);
487    
488          // stability check
489          if (performTest && (j <= maxChecks) && (k < maxIter)) {
490            double initialNorm = 0.0;
491            for (int l = 0; l < scale.length; ++l) {
492              final double ratio = f[0][l] / scale[l];
493              initialNorm += ratio * ratio;
494            }
495            double deltaNorm = 0.0;
496            for (int l = 0; l < scale.length; ++l) {
497              final double ratio = (f[j+1][l] - f[0][l]) / scale[l];
498              deltaNorm += ratio * ratio;
499            }
500            if (deltaNorm > 4 * FastMath.max(1.0e-15, initialNorm)) {
501              return false;
502            }
503          }
504    
505        }
506    
507        // correction of the last substep (at t0 + step)
508        for (int i = 0; i < y0.length; ++i) {
509          yEnd[i] = 0.5 * (yTmp[i] + yEnd[i] + subStep * f[n][i]);
510        }
511    
512        return true;
513    
514      }
515    
516      /** Extrapolate a vector.
517       * @param offset offset to use in the coefficients table
518       * @param k index of the last updated point
519       * @param diag working diagonal of the Aitken-Neville's
520       * triangle, without the last element
521       * @param last last element
522       */
523      private void extrapolate(final int offset, final int k,
524                               final double[][] diag, final double[] last) {
525    
526        // update the diagonal
527        for (int j = 1; j < k; ++j) {
528          for (int i = 0; i < last.length; ++i) {
529            // Aitken-Neville's recursive formula
530            diag[k-j-1][i] = diag[k-j][i] +
531                             coeff[k+offset][j-1] * (diag[k-j][i] - diag[k-j-1][i]);
532          }
533        }
534    
535        // update the last element
536        for (int i = 0; i < last.length; ++i) {
537          // Aitken-Neville's recursive formula
538          last[i] = diag[0][i] + coeff[k+offset][k-1] * (diag[0][i] - last[i]);
539        }
540      }
541    
542      /** {@inheritDoc} */
543      @Override
544      public void integrate(final ExpandableStatefulODE equations, final double t)
545          throws MathIllegalStateException, MathIllegalArgumentException {
546    
547        sanityChecks(equations, t);
548        setEquations(equations);
549        resetEvaluations();
550        final boolean forward = t > equations.getTime();
551    
552        // create some internal working arrays
553        final double[] y0      = equations.getCompleteState();
554        final double[] y       = y0.clone();
555        final double[] yDot0   = new double[y.length];
556        final double[] y1      = new double[y.length];
557        final double[] yTmp    = new double[y.length];
558        final double[] yTmpDot = new double[y.length];
559    
560        final double[][] diagonal = new double[sequence.length-1][];
561        final double[][] y1Diag = new double[sequence.length-1][];
562        for (int k = 0; k < sequence.length-1; ++k) {
563          diagonal[k] = new double[y.length];
564          y1Diag[k] = new double[y.length];
565        }
566    
567        final double[][][] fk  = new double[sequence.length][][];
568        for (int k = 0; k < sequence.length; ++k) {
569    
570          fk[k]    = new double[sequence[k] + 1][];
571    
572          // all substeps start at the same point, so share the first array
573          fk[k][0] = yDot0;
574    
575          for (int l = 0; l < sequence[k]; ++l) {
576            fk[k][l+1] = new double[y0.length];
577          }
578    
579        }
580    
581        if (y != y0) {
582          System.arraycopy(y0, 0, y, 0, y0.length);
583        }
584    
585        final double[] yDot1 = new double[y0.length];
586        final double[][] yMidDots = new double[1 + 2 * sequence.length][y0.length];
587    
588        // initial scaling
589        final double[] scale = new double[mainSetDimension];
590        rescale(y, y, scale);
591    
592        // initial order selection
593        final double tol =
594            (vecRelativeTolerance == null) ? scalRelativeTolerance : vecRelativeTolerance[0];
595        final double log10R = FastMath.log10(FastMath.max(1.0e-10, tol));
596        int targetIter = FastMath.max(1,
597                                  FastMath.min(sequence.length - 2,
598                                           (int) FastMath.floor(0.5 - 0.6 * log10R)));
599    
600        // set up an interpolator sharing the integrator arrays
601        final AbstractStepInterpolator interpolator =
602                new GraggBulirschStoerStepInterpolator(y, yDot0,
603                                                       y1, yDot1,
604                                                       yMidDots, forward,
605                                                       equations.getPrimaryMapper(),
606                                                       equations.getSecondaryMappers());
607        interpolator.storeTime(equations.getTime());
608    
609        stepStart = equations.getTime();
610        double  hNew             = 0;
611        double  maxError         = Double.MAX_VALUE;
612        boolean previousRejected = false;
613        boolean firstTime        = true;
614        boolean newStep          = true;
615        boolean firstStepAlreadyComputed = false;
616        for (StepHandler handler : stepHandlers) {
617            handler.reset();
618        }
619        setStateInitialized(false);
620        costPerTimeUnit[0] = 0;
621        isLastStep = false;
622        do {
623    
624          double error;
625          boolean reject = false;
626    
627          if (newStep) {
628    
629            interpolator.shift();
630    
631            // first evaluation, at the beginning of the step
632            if (! firstStepAlreadyComputed) {
633              computeDerivatives(stepStart, y, yDot0);
634            }
635    
636            if (firstTime) {
637              hNew = initializeStep(forward, 2 * targetIter + 1, scale,
638                                    stepStart, y, yDot0, yTmp, yTmpDot);
639            }
640    
641            newStep = false;
642    
643          }
644    
645          stepSize = hNew;
646    
647          // step adjustment near bounds
648          if ((forward && (stepStart + stepSize > t)) ||
649              ((! forward) && (stepStart + stepSize < t))) {
650            stepSize = t - stepStart;
651          }
652          final double nextT = stepStart + stepSize;
653          isLastStep = forward ? (nextT >= t) : (nextT <= t);
654    
655          // iterate over several substep sizes
656          int k = -1;
657          for (boolean loop = true; loop; ) {
658    
659            ++k;
660    
661            // modified midpoint integration with the current substep
662            if ( ! tryStep(stepStart, y, stepSize, k, scale, fk[k],
663                           (k == 0) ? yMidDots[0] : diagonal[k-1],
664                           (k == 0) ? y1 : y1Diag[k-1],
665                           yTmp)) {
666    
667              // the stability check failed, we reduce the global step
668              hNew   = FastMath.abs(filterStep(stepSize * stabilityReduction, forward, false));
669              reject = true;
670              loop   = false;
671    
672            } else {
673    
674              // the substep was computed successfully
675              if (k > 0) {
676    
677                // extrapolate the state at the end of the step
678                // using last iteration data
679                extrapolate(0, k, y1Diag, y1);
680                rescale(y, y1, scale);
681    
682                // estimate the error at the end of the step.
683                error = 0;
684                for (int j = 0; j < mainSetDimension; ++j) {
685                  final double e = FastMath.abs(y1[j] - y1Diag[0][j]) / scale[j];
686                  error += e * e;
687                }
688                error = FastMath.sqrt(error / mainSetDimension);
689    
690                if ((error > 1.0e15) || ((k > 1) && (error > maxError))) {
691                  // error is too big, we reduce the global step
692                  hNew   = FastMath.abs(filterStep(stepSize * stabilityReduction, forward, false));
693                  reject = true;
694                  loop   = false;
695                } else {
696    
697                  maxError = FastMath.max(4 * error, 1.0);
698    
699                  // compute optimal stepsize for this order
700                  final double exp = 1.0 / (2 * k + 1);
701                  double fac = stepControl2 / FastMath.pow(error / stepControl1, exp);
702                  final double pow = FastMath.pow(stepControl3, exp);
703                  fac = FastMath.max(pow / stepControl4, FastMath.min(1 / pow, fac));
704                  optimalStep[k]     = FastMath.abs(filterStep(stepSize * fac, forward, true));
705                  costPerTimeUnit[k] = costPerStep[k] / optimalStep[k];
706    
707                  // check convergence
708                  switch (k - targetIter) {
709    
710                  case -1 :
711                    if ((targetIter > 1) && ! previousRejected) {
712    
713                      // check if we can stop iterations now
714                      if (error <= 1.0) {
715                        // convergence have been reached just before targetIter
716                        loop = false;
717                      } else {
718                        // estimate if there is a chance convergence will
719                        // be reached on next iteration, using the
720                        // asymptotic evolution of error
721                        final double ratio = ((double) sequence [targetIter] * sequence[targetIter + 1]) /
722                                             (sequence[0] * sequence[0]);
723                        if (error > ratio * ratio) {
724                          // we don't expect to converge on next iteration
725                          // we reject the step immediately and reduce order
726                          reject = true;
727                          loop   = false;
728                          targetIter = k;
729                          if ((targetIter > 1) &&
730                              (costPerTimeUnit[targetIter-1] <
731                               orderControl1 * costPerTimeUnit[targetIter])) {
732                            --targetIter;
733                          }
734                          hNew = optimalStep[targetIter];
735                        }
736                      }
737                    }
738                    break;
739    
740                  case 0:
741                    if (error <= 1.0) {
742                      // convergence has been reached exactly at targetIter
743                      loop = false;
744                    } else {
745                      // estimate if there is a chance convergence will
746                      // be reached on next iteration, using the
747                      // asymptotic evolution of error
748                      final double ratio = ((double) sequence[k+1]) / sequence[0];
749                      if (error > ratio * ratio) {
750                        // we don't expect to converge on next iteration
751                        // we reject the step immediately
752                        reject = true;
753                        loop = false;
754                        if ((targetIter > 1) &&
755                            (costPerTimeUnit[targetIter-1] <
756                             orderControl1 * costPerTimeUnit[targetIter])) {
757                          --targetIter;
758                        }
759                        hNew = optimalStep[targetIter];
760                      }
761                    }
762                    break;
763    
764                  case 1 :
765                    if (error > 1.0) {
766                      reject = true;
767                      if ((targetIter > 1) &&
768                          (costPerTimeUnit[targetIter-1] <
769                           orderControl1 * costPerTimeUnit[targetIter])) {
770                        --targetIter;
771                      }
772                      hNew = optimalStep[targetIter];
773                    }
774                    loop = false;
775                    break;
776    
777                  default :
778                    if ((firstTime || isLastStep) && (error <= 1.0)) {
779                      loop = false;
780                    }
781                    break;
782    
783                  }
784    
785                }
786              }
787            }
788          }
789    
790          if (! reject) {
791              // derivatives at end of step
792              computeDerivatives(stepStart + stepSize, y1, yDot1);
793          }
794    
795          // dense output handling
796          double hInt = getMaxStep();
797          if (! reject) {
798    
799            // extrapolate state at middle point of the step
800            for (int j = 1; j <= k; ++j) {
801              extrapolate(0, j, diagonal, yMidDots[0]);
802            }
803    
804            final int mu = 2 * k - mudif + 3;
805    
806            for (int l = 0; l < mu; ++l) {
807    
808              // derivative at middle point of the step
809              final int l2 = l / 2;
810              double factor = FastMath.pow(0.5 * sequence[l2], l);
811              int middleIndex = fk[l2].length / 2;
812              for (int i = 0; i < y0.length; ++i) {
813                yMidDots[l+1][i] = factor * fk[l2][middleIndex + l][i];
814              }
815              for (int j = 1; j <= k - l2; ++j) {
816                factor = FastMath.pow(0.5 * sequence[j + l2], l);
817                middleIndex = fk[l2+j].length / 2;
818                for (int i = 0; i < y0.length; ++i) {
819                  diagonal[j-1][i] = factor * fk[l2+j][middleIndex+l][i];
820                }
821                extrapolate(l2, j, diagonal, yMidDots[l+1]);
822              }
823              for (int i = 0; i < y0.length; ++i) {
824                yMidDots[l+1][i] *= stepSize;
825              }
826    
827              // compute centered differences to evaluate next derivatives
828              for (int j = (l + 1) / 2; j <= k; ++j) {
829                for (int m = fk[j].length - 1; m >= 2 * (l + 1); --m) {
830                  for (int i = 0; i < y0.length; ++i) {
831                    fk[j][m][i] -= fk[j][m-2][i];
832                  }
833                }
834              }
835    
836            }
837    
838            if (mu >= 0) {
839    
840              // estimate the dense output coefficients
841              final GraggBulirschStoerStepInterpolator gbsInterpolator
842                = (GraggBulirschStoerStepInterpolator) interpolator;
843              gbsInterpolator.computeCoefficients(mu, stepSize);
844    
845              if (useInterpolationError) {
846                // use the interpolation error to limit stepsize
847                final double interpError = gbsInterpolator.estimateError(scale);
848                hInt = FastMath.abs(stepSize / FastMath.max(FastMath.pow(interpError, 1.0 / (mu+4)),
849                                                    0.01));
850                if (interpError > 10.0) {
851                  hNew = hInt;
852                  reject = true;
853                }
854              }
855    
856            }
857    
858          }
859    
860          if (! reject) {
861    
862            // Discrete events handling
863            interpolator.storeTime(stepStart + stepSize);
864            stepStart = acceptStep(interpolator, y1, yDot1, t);
865    
866            // prepare next step
867            interpolator.storeTime(stepStart);
868            System.arraycopy(y1, 0, y, 0, y0.length);
869            System.arraycopy(yDot1, 0, yDot0, 0, y0.length);
870            firstStepAlreadyComputed = true;
871    
872            int optimalIter;
873            if (k == 1) {
874              optimalIter = 2;
875              if (previousRejected) {
876                optimalIter = 1;
877              }
878            } else if (k <= targetIter) {
879              optimalIter = k;
880              if (costPerTimeUnit[k-1] < orderControl1 * costPerTimeUnit[k]) {
881                optimalIter = k-1;
882              } else if (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[k-1]) {
883                optimalIter = FastMath.min(k+1, sequence.length - 2);
884              }
885            } else {
886              optimalIter = k - 1;
887              if ((k > 2) &&
888                  (costPerTimeUnit[k-2] < orderControl1 * costPerTimeUnit[k-1])) {
889                optimalIter = k - 2;
890              }
891              if (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[optimalIter]) {
892                optimalIter = FastMath.min(k, sequence.length - 2);
893              }
894            }
895    
896            if (previousRejected) {
897              // after a rejected step neither order nor stepsize
898              // should increase
899              targetIter = FastMath.min(optimalIter, k);
900              hNew = FastMath.min(FastMath.abs(stepSize), optimalStep[targetIter]);
901            } else {
902              // stepsize control
903              if (optimalIter <= k) {
904                hNew = optimalStep[optimalIter];
905              } else {
906                if ((k < targetIter) &&
907                    (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[k-1])) {
908                  hNew = filterStep(optimalStep[k] * costPerStep[optimalIter+1] / costPerStep[k],
909                                   forward, false);
910                } else {
911                  hNew = filterStep(optimalStep[k] * costPerStep[optimalIter] / costPerStep[k],
912                                    forward, false);
913                }
914              }
915    
916              targetIter = optimalIter;
917    
918            }
919    
920            newStep = true;
921    
922          }
923    
924          hNew = FastMath.min(hNew, hInt);
925          if (! forward) {
926            hNew = -hNew;
927          }
928    
929          firstTime = false;
930    
931          if (reject) {
932            isLastStep = false;
933            previousRejected = true;
934          } else {
935            previousRejected = false;
936          }
937    
938        } while (!isLastStep);
939    
940        // dispatch results
941        equations.setTime(stepStart);
942        equations.setCompleteState(y);
943    
944        resetInternalState();
945    
946      }
947    
948    }