DormandPrince853FieldIntegrator.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.math4.legacy.ode.nonstiff;

  18. import org.apache.commons.math4.legacy.core.Field;
  19. import org.apache.commons.math4.legacy.core.RealFieldElement;
  20. import org.apache.commons.math4.legacy.ode.FieldEquationsMapper;
  21. import org.apache.commons.math4.legacy.ode.FieldODEStateAndDerivative;
  22. import org.apache.commons.math4.legacy.core.MathArrays;


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

  56. public class DormandPrince853FieldIntegrator<T extends RealFieldElement<T>>
  57.     extends EmbeddedRungeKuttaFieldIntegrator<T> {

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

  60.     /** First error weights array, element 1. */
  61.     private final T e1_01;

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

  63.     /** First error weights array, element 6. */
  64.     private final T e1_06;

  65.     /** First error weights array, element 7. */
  66.     private final T e1_07;

  67.     /** First error weights array, element 8. */
  68.     private final T e1_08;

  69.     /** First error weights array, element 9. */
  70.     private final T e1_09;

  71.     /** First error weights array, element 10. */
  72.     private final T e1_10;

  73.     /** First error weights array, element 11. */
  74.     private final T e1_11;

  75.     /** First error weights array, element 12. */
  76.     private final T e1_12;


  77.     /** Second error weights array, element 1. */
  78.     private final T e2_01;

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

  80.     /** Second error weights array, element 6. */
  81.     private final T e2_06;

  82.     /** Second error weights array, element 7. */
  83.     private final T e2_07;

  84.     /** Second error weights array, element 8. */
  85.     private final T e2_08;

  86.     /** Second error weights array, element 9. */
  87.     private final T e2_09;

  88.     /** Second error weights array, element 10. */
  89.     private final T e2_10;

  90.     /** Second error weights array, element 11. */
  91.     private final T e2_11;

  92.     /** Second error weights array, element 12. */
  93.     private final T e2_12;

  94.     /** Simple constructor.
  95.      * Build an eighth order Dormand-Prince integrator with the given step bounds
  96.      * @param field field to which the time and state vector elements belong
  97.      * @param minStep minimal step (sign is irrelevant, regardless of
  98.      * integration direction, forward or backward), the last step can
  99.      * be smaller than this
  100.      * @param maxStep maximal step (sign is irrelevant, regardless of
  101.      * integration direction, forward or backward), the last step can
  102.      * be smaller than this
  103.      * @param scalAbsoluteTolerance allowed absolute error
  104.      * @param scalRelativeTolerance allowed relative error
  105.      */
  106.     public DormandPrince853FieldIntegrator(final Field<T> field,
  107.                                            final double minStep, final double maxStep,
  108.                                            final double scalAbsoluteTolerance,
  109.                                            final double scalRelativeTolerance) {
  110.         super(field, METHOD_NAME, 12,
  111.               minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
  112.         e1_01 = fraction(        116092271.0,       8848465920.0);
  113.         e1_06 = fraction(         -1871647.0,          1527680.0);
  114.         e1_07 = fraction(        -69799717.0,        140793660.0);
  115.         e1_08 = fraction(    1230164450203.0,     739113984000.0);
  116.         e1_09 = fraction(-1980813971228885.0, 5654156025964544.0);
  117.         e1_10 = fraction(        464500805.0,       1389975552.0);
  118.         e1_11 = fraction(    1606764981773.0,   19613062656000.0);
  119.         e1_12 = fraction(          -137909.0,          6168960.0);
  120.         e2_01 = fraction(          -364463.0,          1920240.0);
  121.         e2_06 = fraction(          3399327.0,           763840.0);
  122.         e2_07 = fraction(         66578432.0,         35198415.0);
  123.         e2_08 = fraction(      -1674902723.0,        288716400.0);
  124.         e2_09 = fraction(  -74684743568175.0,  176692375811392.0);
  125.         e2_10 = fraction(          -734375.0,          4826304.0);
  126.         e2_11 = fraction(        171414593.0,        851261400.0);
  127.         e2_12 = fraction(            69869.0,          3084480.0);
  128.     }

  129.     /** Simple constructor.
  130.      * Build an eighth order Dormand-Prince integrator with the given step bounds
  131.      * @param field field to which the time and state vector elements belong
  132.      * @param minStep minimal step (sign is irrelevant, regardless of
  133.      * integration direction, forward or backward), the last step can
  134.      * be smaller than this
  135.      * @param maxStep maximal step (sign is irrelevant, regardless of
  136.      * integration direction, forward or backward), the last step can
  137.      * be smaller than this
  138.      * @param vecAbsoluteTolerance allowed absolute error
  139.      * @param vecRelativeTolerance allowed relative error
  140.      */
  141.     public DormandPrince853FieldIntegrator(final Field<T> field,
  142.                                            final double minStep, final double maxStep,
  143.                                            final double[] vecAbsoluteTolerance,
  144.                                            final double[] vecRelativeTolerance) {
  145.         super(field, METHOD_NAME, 12,
  146.               minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
  147.         e1_01 = fraction(        116092271.0,       8848465920.0);
  148.         e1_06 = fraction(         -1871647.0,          1527680.0);
  149.         e1_07 = fraction(        -69799717.0,        140793660.0);
  150.         e1_08 = fraction(    1230164450203.0,     739113984000.0);
  151.         e1_09 = fraction(-1980813971228885.0, 5654156025964544.0);
  152.         e1_10 = fraction(        464500805.0,       1389975552.0);
  153.         e1_11 = fraction(    1606764981773.0,   19613062656000.0);
  154.         e1_12 = fraction(          -137909.0,          6168960.0);
  155.         e2_01 = fraction(          -364463.0,          1920240.0);
  156.         e2_06 = fraction(          3399327.0,           763840.0);
  157.         e2_07 = fraction(         66578432.0,         35198415.0);
  158.         e2_08 = fraction(      -1674902723.0,        288716400.0);
  159.         e2_09 = fraction(  -74684743568175.0,  176692375811392.0);
  160.         e2_10 = fraction(          -734375.0,          4826304.0);
  161.         e2_11 = fraction(        171414593.0,        851261400.0);
  162.         e2_12 = fraction(            69869.0,          3084480.0);
  163.     }

  164.     /** {@inheritDoc} */
  165.     @Override
  166.     public T[] getC() {

  167.         final T sqrt6 = getField().getOne().multiply(6).sqrt();

  168.         final T[] c = MathArrays.buildArray(getField(), 15);
  169.         c[ 0] = sqrt6.add(-6).divide(-67.5);
  170.         c[ 1] = sqrt6.add(-6).divide(-45.0);
  171.         c[ 2] = sqrt6.add(-6).divide(-30.0);
  172.         c[ 3] = sqrt6.add( 6).divide( 30.0);
  173.         c[ 4] = fraction(1, 3);
  174.         c[ 5] = fraction(1, 4);
  175.         c[ 6] = fraction(4, 13);
  176.         c[ 7] = fraction(127, 195);
  177.         c[ 8] = fraction(3, 5);
  178.         c[ 9] = fraction(6, 7);
  179.         c[10] = getField().getOne();
  180.         c[11] = getField().getOne();
  181.         c[12] = fraction(1.0, 10.0);
  182.         c[13] = fraction(1.0, 5.0);
  183.         c[14] = fraction(7.0, 9.0);

  184.         return c;
  185.     }

  186.     /** {@inheritDoc} */
  187.     @Override
  188.     public T[][] getA() {

  189.         final T sqrt6 = getField().getOne().multiply(6).sqrt();

  190.         final T[][] a = MathArrays.buildArray(getField(), 15, -1);
  191.         for (int i = 0; i < a.length; ++i) {
  192.             a[i] = MathArrays.buildArray(getField(), i + 1);
  193.         }

  194.         a[ 0][ 0] = sqrt6.add(-6).divide(-67.5);

  195.         a[ 1][ 0] = sqrt6.add(-6).divide(-180);
  196.         a[ 1][ 1] = sqrt6.add(-6).divide( -60);

  197.         a[ 2][ 0] = sqrt6.add(-6).divide(-120);
  198.         a[ 2][ 1] = getField().getZero();
  199.         a[ 2][ 2] = sqrt6.add(-6).divide( -40);

  200.         a[ 3][ 0] = sqrt6.multiply(107).add(462).divide( 3000);
  201.         a[ 3][ 1] = getField().getZero();
  202.         a[ 3][ 2] = sqrt6.multiply(197).add(402).divide(-1000);
  203.         a[ 3][ 3] = sqrt6.multiply( 73).add(168).divide(  375);

  204.         a[ 4][ 0] = fraction(1, 27);
  205.         a[ 4][ 1] = getField().getZero();
  206.         a[ 4][ 2] = getField().getZero();
  207.         a[ 4][ 3] = sqrt6.add( 16).divide( 108);
  208.         a[ 4][ 4] = sqrt6.add(-16).divide(-108);

  209.         a[ 5][ 0] = fraction(19, 512);
  210.         a[ 5][ 1] = getField().getZero();
  211.         a[ 5][ 2] = getField().getZero();
  212.         a[ 5][ 3] = sqrt6.multiply( 23).add(118).divide(1024);
  213.         a[ 5][ 4] = sqrt6.multiply(-23).add(118).divide(1024);
  214.         a[ 5][ 5] = fraction(-9, 512);

  215.         a[ 6][ 0] = fraction(13772, 371293);
  216.         a[ 6][ 1] = getField().getZero();
  217.         a[ 6][ 2] = getField().getZero();
  218.         a[ 6][ 3] = sqrt6.multiply( 4784).add(51544).divide(371293);
  219.         a[ 6][ 4] = sqrt6.multiply(-4784).add(51544).divide(371293);
  220.         a[ 6][ 5] = fraction(-5688, 371293);
  221.         a[ 6][ 6] = fraction( 3072, 371293);

  222.         a[ 7][ 0] = fraction(58656157643.0, 93983540625.0);
  223.         a[ 7][ 1] = getField().getZero();
  224.         a[ 7][ 2] = getField().getZero();
  225.         a[ 7][ 3] = sqrt6.multiply(-318801444819.0).add(-1324889724104.0).divide(626556937500.0);
  226.         a[ 7][ 4] = sqrt6.multiply( 318801444819.0).add(-1324889724104.0).divide(626556937500.0);
  227.         a[ 7][ 5] = fraction(96044563816.0, 3480871875.0);
  228.         a[ 7][ 6] = fraction(5682451879168.0, 281950621875.0);
  229.         a[ 7][ 7] = fraction(-165125654.0, 3796875.0);

  230.         a[ 8][ 0] = fraction(8909899.0, 18653125.0);
  231.         a[ 8][ 1] = getField().getZero();
  232.         a[ 8][ 2] = getField().getZero();
  233.         a[ 8][ 3] = sqrt6.multiply(-1137963.0).add(-4521408.0).divide(2937500.0);
  234.         a[ 8][ 4] = sqrt6.multiply( 1137963.0).add(-4521408.0).divide(2937500.0);
  235.         a[ 8][ 5] = fraction(96663078.0, 4553125.0);
  236.         a[ 8][ 6] = fraction(2107245056.0, 137915625.0);
  237.         a[ 8][ 7] = fraction(-4913652016.0, 147609375.0);
  238.         a[ 8][ 8] = fraction(-78894270.0, 3880452869.0);

  239.         a[ 9][ 0] = fraction(-20401265806.0, 21769653311.0);
  240.         a[ 9][ 1] = getField().getZero();
  241.         a[ 9][ 2] = getField().getZero();
  242.         a[ 9][ 3] = sqrt6.multiply( 94326.0).add(354216.0).divide(112847.0);
  243.         a[ 9][ 4] = sqrt6.multiply(-94326.0).add(354216.0).divide(112847.0);
  244.         a[ 9][ 5] = fraction(-43306765128.0, 5313852383.0);
  245.         a[ 9][ 6] = fraction(-20866708358144.0, 1126708119789.0);
  246.         a[ 9][ 7] = fraction(14886003438020.0, 654632330667.0);
  247.         a[ 9][ 8] = fraction(35290686222309375.0, 14152473387134411.0);
  248.         a[ 9][ 9] = fraction(-1477884375.0, 485066827.0);

  249.         a[10][ 0] = fraction(39815761.0, 17514443.0);
  250.         a[10][ 1] = getField().getZero();
  251.         a[10][ 2] = getField().getZero();
  252.         a[10][ 3] = sqrt6.multiply(-960905.0).add(-3457480.0).divide(551636.0);
  253.         a[10][ 4] = sqrt6.multiply( 960905.0).add(-3457480.0).divide(551636.0);
  254.         a[10][ 5] = fraction(-844554132.0, 47026969.0);
  255.         a[10][ 6] = fraction(8444996352.0, 302158619.0);
  256.         a[10][ 7] = fraction(-2509602342.0, 877790785.0);
  257.         a[10][ 8] = fraction(-28388795297996250.0, 3199510091356783.0);
  258.         a[10][ 9] = fraction(226716250.0, 18341897.0);
  259.         a[10][10] = fraction(1371316744.0, 2131383595.0);

  260.         // the following stage is both for interpolation and the first stage in next step
  261.         // (the coefficients are identical to the B array)
  262.         a[11][ 0] = fraction(104257.0, 1920240.0);
  263.         a[11][ 1] = getField().getZero();
  264.         a[11][ 2] = getField().getZero();
  265.         a[11][ 3] = getField().getZero();
  266.         a[11][ 4] = getField().getZero();
  267.         a[11][ 5] = fraction(3399327.0, 763840.0);
  268.         a[11][ 6] = fraction(66578432.0, 35198415.0);
  269.         a[11][ 7] = fraction(-1674902723.0, 288716400.0);
  270.         a[11][ 8] = fraction(54980371265625.0, 176692375811392.0);
  271.         a[11][ 9] = fraction(-734375.0, 4826304.0);
  272.         a[11][10] = fraction(171414593.0, 851261400.0);
  273.         a[11][11] = fraction(137909.0, 3084480.0);

  274.         // the following stages are for interpolation only
  275.         a[12][ 0] = fraction(      13481885573.0, 240030000000.0);
  276.         a[12][ 1] = getField().getZero();
  277.         a[12][ 2] = getField().getZero();
  278.         a[12][ 3] = getField().getZero();
  279.         a[12][ 4] = getField().getZero();
  280.         a[12][ 5] = getField().getZero();
  281.         a[12][ 6] = fraction(     139418837528.0, 549975234375.0);
  282.         a[12][ 7] = fraction(  -11108320068443.0, 45111937500000.0);
  283.         a[12][ 8] = fraction(-1769651421925959.0, 14249385146080000.0);
  284.         a[12][ 9] = fraction(         57799439.0, 377055000.0);
  285.         a[12][10] = fraction(     793322643029.0, 96734250000000.0);
  286.         a[12][11] = fraction(       1458939311.0, 192780000000.0);
  287.         a[12][12]  = fraction(            -4149.0, 500000.0);

  288.         a[13][ 0] = fraction(    1595561272731.0, 50120273500000.0);
  289.         a[13][ 1] = getField().getZero();
  290.         a[13][ 2] = getField().getZero();
  291.         a[13][ 3] = getField().getZero();
  292.         a[13][ 4] = getField().getZero();
  293.         a[13][ 5] = fraction(     975183916491.0, 34457688031250.0);
  294.         a[13][ 6] = fraction(   38492013932672.0, 718912673015625.0);
  295.         a[13][ 7] = fraction(-1114881286517557.0, 20298710767500000.0);
  296.         a[13][ 8] = getField().getZero();
  297.         a[13][ 9] = getField().getZero();
  298.         a[13][10] = fraction(   -2538710946863.0, 23431227861250000.0);
  299.         a[13][11] = fraction(       8824659001.0, 23066716781250.0);
  300.         a[13][12] = fraction(     -11518334563.0, 33831184612500.0);
  301.         a[13][13] = fraction(       1912306948.0, 13532473845.0);

  302.         a[14][ 0] = fraction(     -13613986967.0, 31741908048.0);
  303.         a[14][ 1] = getField().getZero();
  304.         a[14][ 2] = getField().getZero();
  305.         a[14][ 3] = getField().getZero();
  306.         a[14][ 4] = getField().getZero();
  307.         a[14][ 5] = fraction(      -4755612631.0, 1012344804.0);
  308.         a[14][ 6] = fraction(   42939257944576.0, 5588559685701.0);
  309.         a[14][ 7] = fraction(   77881972900277.0, 19140370552944.0);
  310.         a[14][ 8] = fraction(   22719829234375.0, 63689648654052.0);
  311.         a[14][ 9] = getField().getZero();
  312.         a[14][10] = getField().getZero();
  313.         a[14][11] = getField().getZero();
  314.         a[14][12] = fraction(      -1199007803.0, 857031517296.0);
  315.         a[14][13] = fraction(     157882067000.0, 53564469831.0);
  316.         a[14][14] = fraction(    -290468882375.0, 31741908048.0);

  317.         return a;
  318.     }

  319.     /** {@inheritDoc} */
  320.     @Override
  321.     public T[] getB() {
  322.         final T[] b = MathArrays.buildArray(getField(), 16);
  323.         b[ 0] = fraction(104257, 1920240);
  324.         b[ 1] = getField().getZero();
  325.         b[ 2] = getField().getZero();
  326.         b[ 3] = getField().getZero();
  327.         b[ 4] = getField().getZero();
  328.         b[ 5] = fraction(        3399327.0,          763840.0);
  329.         b[ 6] = fraction(       66578432.0,        35198415.0);
  330.         b[ 7] = fraction(    -1674902723.0,       288716400.0);
  331.         b[ 8] = fraction( 54980371265625.0, 176692375811392.0);
  332.         b[ 9] = fraction(        -734375.0,         4826304.0);
  333.         b[10] = fraction(      171414593.0,       851261400.0);
  334.         b[11] = fraction(         137909.0,         3084480.0);
  335.         b[12] = getField().getZero();
  336.         b[13] = getField().getZero();
  337.         b[14] = getField().getZero();
  338.         b[15] = getField().getZero();
  339.         return b;
  340.     }

  341.     /** {@inheritDoc} */
  342.     @Override
  343.     protected DormandPrince853FieldStepInterpolator<T>
  344.         createInterpolator(final boolean forward, T[][] yDotK,
  345.                            final FieldODEStateAndDerivative<T> globalPreviousState,
  346.                            final FieldODEStateAndDerivative<T> globalCurrentState, final FieldEquationsMapper<T> mapper) {
  347.         return new DormandPrince853FieldStepInterpolator<>(getField(), forward, yDotK,
  348.                                                             globalPreviousState, globalCurrentState,
  349.                                                             globalPreviousState, globalCurrentState,
  350.                                                             mapper);
  351.     }

  352.     /** {@inheritDoc} */
  353.     @Override
  354.     public int getOrder() {
  355.         return 8;
  356.     }

  357.     /** {@inheritDoc} */
  358.     @Override
  359.     protected T estimateError(final T[][] yDotK, final T[] y0, final T[] y1, final T h) {
  360.         T error1 = h.getField().getZero();
  361.         T error2 = h.getField().getZero();

  362.         for (int j = 0; j < mainSetDimension; ++j) {
  363.             final T errSum1 =      yDotK[ 0][j].multiply(e1_01).
  364.                                add(yDotK[ 5][j].multiply(e1_06)).
  365.                                add(yDotK[ 6][j].multiply(e1_07)).
  366.                                add(yDotK[ 7][j].multiply(e1_08)).
  367.                                add(yDotK[ 8][j].multiply(e1_09)).
  368.                                add(yDotK[ 9][j].multiply(e1_10)).
  369.                                add(yDotK[10][j].multiply(e1_11)).
  370.                                add(yDotK[11][j].multiply(e1_12));
  371.             final T errSum2 =      yDotK[ 0][j].multiply(e2_01).
  372.                                add(yDotK[ 5][j].multiply(e2_06)).
  373.                                add(yDotK[ 6][j].multiply(e2_07)).
  374.                                add(yDotK[ 7][j].multiply(e2_08)).
  375.                                add(yDotK[ 8][j].multiply(e2_09)).
  376.                                add(yDotK[ 9][j].multiply(e2_10)).
  377.                                add(yDotK[10][j].multiply(e2_11)).
  378.                                add(yDotK[11][j].multiply(e2_12));

  379.             final T yScale = RealFieldElement.max(y0[j].abs(), y1[j].abs());
  380.             final T tol = vecAbsoluteTolerance == null ?
  381.                           yScale.multiply(scalRelativeTolerance).add(scalAbsoluteTolerance) :
  382.                           yScale.multiply(vecRelativeTolerance[j]).add(vecAbsoluteTolerance[j]);
  383.             final T ratio1  = errSum1.divide(tol);
  384.             error1        = error1.add(ratio1.multiply(ratio1));
  385.             final T ratio2  = errSum2.divide(tol);
  386.             error2        = error2.add(ratio2.multiply(ratio2));
  387.         }

  388.         T den = error1.add(error2.multiply(0.01));
  389.         if (den.getReal() <= 0.0) {
  390.             den = h.getField().getOne();
  391.         }

  392.         return h.abs().multiply(error1).divide(den.multiply(mainSetDimension).sqrt());
  393.     }
  394. }