DormandPrince853Integrator.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.core.jdkmath.JdkMath;


  19. /**
  20.  * This class implements the 8(5,3) Dormand-Prince integrator for Ordinary
  21.  * Differential Equations.
  22.  *
  23.  * <p>This integrator is an embedded Runge-Kutta integrator
  24.  * of order 8(5,3) used in local extrapolation mode (i.e. the solution
  25.  * is computed using the high order formula) with stepsize control
  26.  * (and automatic step initialization) and continuous output. This
  27.  * method uses 12 functions evaluations per step for integration and 4
  28.  * evaluations for interpolation. However, since the first
  29.  * interpolation evaluation is the same as the first integration
  30.  * evaluation of the next step, we have included it in the integrator
  31.  * rather than in the interpolator and specified the method was an
  32.  * <i>fsal</i>. Hence, despite we have 13 stages here, the cost is
  33.  * really 12 evaluations per step even if no interpolation is done,
  34.  * and the overcost of interpolation is only 3 evaluations.</p>
  35.  *
  36.  * <p>This method is based on an 8(6) method by Dormand and Prince
  37.  * (i.e. order 8 for the integration and order 6 for error estimation)
  38.  * modified by Hairer and Wanner to use a 5th order error estimator
  39.  * with 3rd order correction. This modification was introduced because
  40.  * the original method failed in some cases (wrong steps can be
  41.  * accepted when step size is too large, for example in the
  42.  * Brusselator problem) and also had <i>severe difficulties when
  43.  * applied to problems with discontinuities</i>. This modification is
  44.  * explained in the second edition of the first volume (Nonstiff
  45.  * Problems) of the reference book by Hairer, Norsett and Wanner:
  46.  * <i>Solving Ordinary Differential Equations</i> (Springer-Verlag,
  47.  * ISBN 3-540-56670-8).</p>
  48.  *
  49.  * @since 1.2
  50.  */

  51. public class DormandPrince853Integrator extends EmbeddedRungeKuttaIntegrator {

  52.   /** Integrator method name. */
  53.   private static final String METHOD_NAME = "Dormand-Prince 8 (5, 3)";

  54.   /** Time steps Butcher array. */
  55.   private static final double[] STATIC_C = {
  56.     (12.0 - 2.0 * JdkMath.sqrt(6.0)) / 135.0, (6.0 - JdkMath.sqrt(6.0)) / 45.0, (6.0 - JdkMath.sqrt(6.0)) / 30.0,
  57.     (6.0 + JdkMath.sqrt(6.0)) / 30.0, 1.0/3.0, 1.0/4.0, 4.0/13.0, 127.0/195.0, 3.0/5.0,
  58.     6.0/7.0, 1.0, 1.0
  59.   };

  60.   /** Internal weights Butcher array. */
  61.   private static final double[][] STATIC_A = {

  62.     // k2
  63.     {(12.0 - 2.0 * JdkMath.sqrt(6.0)) / 135.0},

  64.     // k3
  65.     {(6.0 - JdkMath.sqrt(6.0)) / 180.0, (6.0 - JdkMath.sqrt(6.0)) / 60.0},

  66.     // k4
  67.     {(6.0 - JdkMath.sqrt(6.0)) / 120.0, 0.0, (6.0 - JdkMath.sqrt(6.0)) / 40.0},

  68.     // k5
  69.     {(462.0 + 107.0 * JdkMath.sqrt(6.0)) / 3000.0, 0.0,
  70.      (-402.0 - 197.0 * JdkMath.sqrt(6.0)) / 1000.0, (168.0 + 73.0 * JdkMath.sqrt(6.0)) / 375.0},

  71.     // k6
  72.     {1.0 / 27.0, 0.0, 0.0, (16.0 + JdkMath.sqrt(6.0)) / 108.0, (16.0 - JdkMath.sqrt(6.0)) / 108.0},

  73.     // k7
  74.     {19.0 / 512.0, 0.0, 0.0, (118.0 + 23.0 * JdkMath.sqrt(6.0)) / 1024.0,
  75.      (118.0 - 23.0 * JdkMath.sqrt(6.0)) / 1024.0, -9.0 / 512.0},

  76.     // k8
  77.     {13772.0 / 371293.0, 0.0, 0.0, (51544.0 + 4784.0 * JdkMath.sqrt(6.0)) / 371293.0,
  78.      (51544.0 - 4784.0 * JdkMath.sqrt(6.0)) / 371293.0, -5688.0 / 371293.0, 3072.0 / 371293.0},

  79.     // k9
  80.     {58656157643.0 / 93983540625.0, 0.0, 0.0,
  81.      (-1324889724104.0 - 318801444819.0 * JdkMath.sqrt(6.0)) / 626556937500.0,
  82.      (-1324889724104.0 + 318801444819.0 * JdkMath.sqrt(6.0)) / 626556937500.0,
  83.      96044563816.0 / 3480871875.0, 5682451879168.0 / 281950621875.0,
  84.      -165125654.0 / 3796875.0},

  85.     // k10
  86.     {8909899.0 / 18653125.0, 0.0, 0.0,
  87.      (-4521408.0 - 1137963.0 * JdkMath.sqrt(6.0)) / 2937500.0,
  88.      (-4521408.0 + 1137963.0 * JdkMath.sqrt(6.0)) / 2937500.0,
  89.      96663078.0 / 4553125.0, 2107245056.0 / 137915625.0,
  90.      -4913652016.0 / 147609375.0, -78894270.0 / 3880452869.0},

  91.     // k11
  92.     {-20401265806.0 / 21769653311.0, 0.0, 0.0,
  93.      (354216.0 + 94326.0 * JdkMath.sqrt(6.0)) / 112847.0,
  94.      (354216.0 - 94326.0 * JdkMath.sqrt(6.0)) / 112847.0,
  95.      -43306765128.0 / 5313852383.0, -20866708358144.0 / 1126708119789.0,
  96.      14886003438020.0 / 654632330667.0, 35290686222309375.0 / 14152473387134411.0,
  97.      -1477884375.0 / 485066827.0},

  98.     // k12
  99.     {39815761.0 / 17514443.0, 0.0, 0.0,
  100.      (-3457480.0 - 960905.0 * JdkMath.sqrt(6.0)) / 551636.0,
  101.      (-3457480.0 + 960905.0 * JdkMath.sqrt(6.0)) / 551636.0,
  102.      -844554132.0 / 47026969.0, 8444996352.0 / 302158619.0,
  103.      -2509602342.0 / 877790785.0, -28388795297996250.0 / 3199510091356783.0,
  104.      226716250.0 / 18341897.0, 1371316744.0 / 2131383595.0},

  105.     // k13 should be for interpolation only, but since it is the same
  106.     // stage as the first evaluation of the next step, we perform it
  107.     // here at no cost by specifying this is an fsal method
  108.     {104257.0/1920240.0, 0.0, 0.0, 0.0, 0.0, 3399327.0/763840.0,
  109.      66578432.0/35198415.0, -1674902723.0/288716400.0,
  110.      54980371265625.0/176692375811392.0, -734375.0/4826304.0,
  111.      171414593.0/851261400.0, 137909.0/3084480.0}
  112.   };

  113.   /** Propagation weights Butcher array. */
  114.   private static final double[] STATIC_B = {
  115.       104257.0/1920240.0,
  116.       0.0,
  117.       0.0,
  118.       0.0,
  119.       0.0,
  120.       3399327.0/763840.0,
  121.       66578432.0/35198415.0,
  122.       -1674902723.0/288716400.0,
  123.       54980371265625.0/176692375811392.0,
  124.       -734375.0/4826304.0,
  125.       171414593.0/851261400.0,
  126.       137909.0/3084480.0,
  127.       0.0
  128.   };

  129.   /** First error weights array, element 1. */
  130.   private static final double E1_01 =         116092271.0 / 8848465920.0;

  131.   // elements 2 to 5 are zero, so they are neither stored nor used

  132.   /** First error weights array, element 6. */
  133.   private static final double E1_06 =          -1871647.0 / 1527680.0;

  134.   /** First error weights array, element 7. */
  135.   private static final double E1_07 =         -69799717.0 / 140793660.0;

  136.   /** First error weights array, element 8. */
  137.   private static final double E1_08 =     1230164450203.0 / 739113984000.0;

  138.   /** First error weights array, element 9. */
  139.   private static final double E1_09 = -1980813971228885.0 / 5654156025964544.0;

  140.   /** First error weights array, element 10. */
  141.   private static final double E1_10 =         464500805.0 / 1389975552.0;

  142.   /** First error weights array, element 11. */
  143.   private static final double E1_11 =     1606764981773.0 / 19613062656000.0;

  144.   /** First error weights array, element 12. */
  145.   private static final double E1_12 =           -137909.0 / 6168960.0;


  146.   /** Second error weights array, element 1. */
  147.   private static final double E2_01 =           -364463.0 / 1920240.0;

  148.   // elements 2 to 5 are zero, so they are neither stored nor used

  149.   /** Second error weights array, element 6. */
  150.   private static final double E2_06 =           3399327.0 / 763840.0;

  151.   /** Second error weights array, element 7. */
  152.   private static final double E2_07 =          66578432.0 / 35198415.0;

  153.   /** Second error weights array, element 8. */
  154.   private static final double E2_08 =       -1674902723.0 / 288716400.0;

  155.   /** Second error weights array, element 9. */
  156.   private static final double E2_09 =   -74684743568175.0 / 176692375811392.0;

  157.   /** Second error weights array, element 10. */
  158.   private static final double E2_10 =           -734375.0 / 4826304.0;

  159.   /** Second error weights array, element 11. */
  160.   private static final double E2_11 =         171414593.0 / 851261400.0;

  161.   /** Second error weights array, element 12. */
  162.   private static final double E2_12 =             69869.0 / 3084480.0;

  163.   /** Simple constructor.
  164.    * Build an eighth order Dormand-Prince integrator with the given step bounds
  165.    * @param minStep minimal step (sign is irrelevant, regardless of
  166.    * integration direction, forward or backward), the last step can
  167.    * be smaller than this
  168.    * @param maxStep maximal step (sign is irrelevant, regardless of
  169.    * integration direction, forward or backward), the last step can
  170.    * be smaller than this
  171.    * @param scalAbsoluteTolerance allowed absolute error
  172.    * @param scalRelativeTolerance allowed relative error
  173.    */
  174.   public DormandPrince853Integrator(final double minStep, final double maxStep,
  175.                                     final double scalAbsoluteTolerance,
  176.                                     final double scalRelativeTolerance) {
  177.     super(METHOD_NAME, true, STATIC_C, STATIC_A, STATIC_B,
  178.           new DormandPrince853StepInterpolator(),
  179.           minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
  180.   }

  181.   /** Simple constructor.
  182.    * Build an eighth order Dormand-Prince integrator with the given step bounds
  183.    * @param minStep minimal step (sign is irrelevant, regardless of
  184.    * integration direction, forward or backward), the last step can
  185.    * be smaller than this
  186.    * @param maxStep maximal step (sign is irrelevant, regardless of
  187.    * integration direction, forward or backward), the last step can
  188.    * be smaller than this
  189.    * @param vecAbsoluteTolerance allowed absolute error
  190.    * @param vecRelativeTolerance allowed relative error
  191.    */
  192.   public DormandPrince853Integrator(final double minStep, final double maxStep,
  193.                                     final double[] vecAbsoluteTolerance,
  194.                                     final double[] vecRelativeTolerance) {
  195.     super(METHOD_NAME, true, STATIC_C, STATIC_A, STATIC_B,
  196.           new DormandPrince853StepInterpolator(),
  197.           minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
  198.   }

  199.   /** {@inheritDoc} */
  200.   @Override
  201.   public int getOrder() {
  202.     return 8;
  203.   }

  204.   /** {@inheritDoc} */
  205.   @Override
  206.   protected double estimateError(final double[][] yDotK,
  207.                                  final double[] y0, final double[] y1,
  208.                                  final double h) {
  209.     double error1 = 0;
  210.     double error2 = 0;

  211.     for (int j = 0; j < mainSetDimension; ++j) {
  212.       final double errSum1 = E1_01 * yDotK[0][j]  + E1_06 * yDotK[5][j] +
  213.                              E1_07 * yDotK[6][j]  + E1_08 * yDotK[7][j] +
  214.                              E1_09 * yDotK[8][j]  + E1_10 * yDotK[9][j] +
  215.                              E1_11 * yDotK[10][j] + E1_12 * yDotK[11][j];
  216.       final double errSum2 = E2_01 * yDotK[0][j]  + E2_06 * yDotK[5][j] +
  217.                              E2_07 * yDotK[6][j]  + E2_08 * yDotK[7][j] +
  218.                              E2_09 * yDotK[8][j]  + E2_10 * yDotK[9][j] +
  219.                              E2_11 * yDotK[10][j] + E2_12 * yDotK[11][j];

  220.       final double yScale = JdkMath.max(JdkMath.abs(y0[j]), JdkMath.abs(y1[j]));
  221.       final double tol = (vecAbsoluteTolerance == null) ?
  222.                          (scalAbsoluteTolerance + scalRelativeTolerance * yScale) :
  223.                          (vecAbsoluteTolerance[j] + vecRelativeTolerance[j] * yScale);
  224.       final double ratio1  = errSum1 / tol;
  225.       error1        += ratio1 * ratio1;
  226.       final double ratio2  = errSum2 / tol;
  227.       error2        += ratio2 * ratio2;
  228.     }

  229.     double den = error1 + 0.01 * error2;
  230.     if (den <= 0.0) {
  231.       den = 1.0;
  232.     }

  233.     return JdkMath.abs(h) * error1 / JdkMath.sqrt(mainSetDimension * den);
  234.   }
  235. }