View Javadoc
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    *      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  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  
23  package org.hipparchus.ode.nonstiff;
24  
25  import org.hipparchus.CalculusFieldElement;
26  import org.hipparchus.Field;
27  import org.hipparchus.ode.FieldEquationsMapper;
28  import org.hipparchus.ode.FieldODEStateAndDerivative;
29  import org.hipparchus.ode.nonstiff.interpolators.DormandPrince853FieldStateInterpolator;
30  import org.hipparchus.util.FastMath;
31  import org.hipparchus.util.MathArrays;
32  
33  
34  /**
35   * This class implements the 8(5,3) Dormand-Prince integrator for Ordinary
36   * Differential Equations.
37   *
38   * <p>This integrator is an embedded Runge-Kutta integrator
39   * of order 8(5,3) used in local extrapolation mode (i.e. the solution
40   * is computed using the high order formula) with stepsize control
41   * (and automatic step initialization) and continuous output. This
42   * method uses 12 functions evaluations per step for integration and 4
43   * evaluations for interpolation. However, since the first
44   * interpolation evaluation is the same as the first integration
45   * evaluation of the next step, we have included it in the integrator
46   * rather than in the interpolator and specified the method was an
47   * <i>fsal</i>. Hence, despite we have 13 stages here, the cost is
48   * really 12 evaluations per step even if no interpolation is done,
49   * and the overcost of interpolation is only 3 evaluations.</p>
50   *
51   * <p>This method is based on an 8(6) method by Dormand and Prince
52   * (i.e. order 8 for the integration and order 6 for error estimation)
53   * modified by Hairer and Wanner to use a 5th order error estimator
54   * with 3rd order correction. This modification was introduced because
55   * the original method failed in some cases (wrong steps can be
56   * accepted when step size is too large, for example in the
57   * Brusselator problem) and also had <i>severe difficulties when
58   * applied to problems with discontinuities</i>. This modification is
59   * explained in the second edition of the first volume (Nonstiff
60   * Problems) of the reference book by Hairer, Norsett and Wanner:
61   * <i>Solving Ordinary Differential Equations</i> (Springer-Verlag,
62   * ISBN 3-540-56670-8).</p>
63   *
64   * @param <T> the type of the field elements
65   */
66  
67  public class DormandPrince853FieldIntegrator<T extends CalculusFieldElement<T>>
68      extends EmbeddedRungeKuttaFieldIntegrator<T> {
69  
70      /** Name of integration scheme. */
71      public static final String METHOD_NAME = DormandPrince853Integrator.METHOD_NAME;
72  
73      /** Simple constructor.
74       * Build an eighth order Dormand-Prince integrator with the given step bounds
75       * @param field field to which the time and state vector elements belong
76       * @param minStep minimal step (sign is irrelevant, regardless of
77       * integration direction, forward or backward), the last step can
78       * be smaller than this
79       * @param maxStep maximal step (sign is irrelevant, regardless of
80       * integration direction, forward or backward), the last step can
81       * be smaller than this
82       * @param scalAbsoluteTolerance allowed absolute error
83       * @param scalRelativeTolerance allowed relative error
84       */
85      public DormandPrince853FieldIntegrator(final Field<T> field,
86                                             final double minStep, final double maxStep,
87                                             final double scalAbsoluteTolerance,
88                                             final double scalRelativeTolerance) {
89          super(field, METHOD_NAME, 12,
90                minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
91      }
92  
93      /** Simple constructor.
94       * Build an eighth order Dormand-Prince integrator with the given step bounds
95       * @param field field to which the time and state vector elements belong
96       * @param minStep minimal step (sign is irrelevant, regardless of
97       * integration direction, forward or backward), the last step can
98       * be smaller than this
99       * @param maxStep maximal step (sign is irrelevant, regardless of
100      * integration direction, forward or backward), the last step can
101      * be smaller than this
102      * @param vecAbsoluteTolerance allowed absolute error
103      * @param vecRelativeTolerance allowed relative error
104      */
105     public DormandPrince853FieldIntegrator(final Field<T> field,
106                                            final double minStep, final double maxStep,
107                                            final double[] vecAbsoluteTolerance,
108                                            final double[] vecRelativeTolerance) {
109         super(field, DormandPrince853Integrator.METHOD_NAME, 12,
110               minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
111     }
112 
113     /** {@inheritDoc} */
114     @Override
115     public T[] getC() {
116 
117         final T sqrt6 = getField().getOne().newInstance(6).sqrt();
118 
119         final T[] c = MathArrays.buildArray(getField(), 15);
120         c[ 0] = sqrt6.add(-6).divide(-67.5);
121         c[ 1] = sqrt6.add(-6).divide(-45.0);
122         c[ 2] = sqrt6.add(-6).divide(-30.0);
123         c[ 3] = sqrt6.add( 6).divide( 30.0);
124         c[ 4] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 1, 3);
125         c[ 5] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 1, 4);
126         c[ 6] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 4, 13);
127         c[ 7] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 127, 195);
128         c[ 8] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 3, 5);
129         c[ 9] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 6, 7);
130         c[10] = getField().getOne();
131         c[11] = getField().getOne();
132         c[12] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 1.0, 10.0);
133         c[13] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 1.0, 5.0);
134         c[14] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),7.0, 9.0);
135 
136         return c;
137 
138     }
139 
140     /** {@inheritDoc} */
141     @Override
142     public T[][] getA() {
143 
144         final T sqrt6 = getField().getOne().newInstance(6).sqrt();
145 
146         final T[][] a = MathArrays.buildArray(getField(), 15, -1);
147         for (int i = 0; i < a.length; ++i) {
148             a[i] = MathArrays.buildArray(getField(), i + 1);
149         }
150 
151         a[ 0][ 0] = sqrt6.add(-6).divide(-67.5);
152 
153         a[ 1][ 0] = sqrt6.add(-6).divide(-180);
154         a[ 1][ 1] = sqrt6.add(-6).divide( -60);
155 
156         a[ 2][ 0] = sqrt6.add(-6).divide(-120);
157         a[ 2][ 1] = getField().getZero();
158         a[ 2][ 2] = sqrt6.add(-6).divide( -40);
159 
160         a[ 3][ 0] = sqrt6.multiply(107).add(462).divide( 3000);
161         a[ 3][ 1] = getField().getZero();
162         a[ 3][ 2] = sqrt6.multiply(197).add(402).divide(-1000);
163         a[ 3][ 3] = sqrt6.multiply( 73).add(168).divide(  375);
164 
165         a[ 4][ 0] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 1, 27);
166         a[ 4][ 1] = getField().getZero();
167         a[ 4][ 2] = getField().getZero();
168         a[ 4][ 3] = sqrt6.add( 16).divide( 108);
169         a[ 4][ 4] = sqrt6.add(-16).divide(-108);
170 
171         a[ 5][ 0] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 19, 512);
172         a[ 5][ 1] = getField().getZero();
173         a[ 5][ 2] = getField().getZero();
174         a[ 5][ 3] = sqrt6.multiply( 23).add(118).divide(1024);
175         a[ 5][ 4] = sqrt6.multiply(-23).add(118).divide(1024);
176         a[ 5][ 5] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), -9, 512);
177 
178         a[ 6][ 0] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 13772, 371293);
179         a[ 6][ 1] = getField().getZero();
180         a[ 6][ 2] = getField().getZero();
181         a[ 6][ 3] = sqrt6.multiply( 4784).add(51544).divide(371293);
182         a[ 6][ 4] = sqrt6.multiply(-4784).add(51544).divide(371293);
183         a[ 6][ 5] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), -5688, 371293);
184         a[ 6][ 6] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),  3072, 371293);
185 
186         a[ 7][ 0] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 58656157643.0, 93983540625.0);
187         a[ 7][ 1] = getField().getZero();
188         a[ 7][ 2] = getField().getZero();
189         a[ 7][ 3] = sqrt6.multiply(-318801444819.0).add(-1324889724104.0).divide(626556937500.0);
190         a[ 7][ 4] = sqrt6.multiply( 318801444819.0).add(-1324889724104.0).divide(626556937500.0);
191         a[ 7][ 5] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 96044563816.0, 3480871875.0);
192         a[ 7][ 6] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 5682451879168.0, 281950621875.0);
193         a[ 7][ 7] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), -165125654.0, 3796875.0);
194 
195         a[ 8][ 0] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),8909899.0, 18653125.0);
196         a[ 8][ 1] = getField().getZero();
197         a[ 8][ 2] = getField().getZero();
198         a[ 8][ 3] = sqrt6.multiply(-1137963.0).add(-4521408.0).divide(2937500.0);
199         a[ 8][ 4] = sqrt6.multiply( 1137963.0).add(-4521408.0).divide(2937500.0);
200         a[ 8][ 5] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 96663078.0, 4553125.0);
201         a[ 8][ 6] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 2107245056.0, 137915625.0);
202         a[ 8][ 7] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), -4913652016.0, 147609375.0);
203         a[ 8][ 8] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), -78894270.0, 3880452869.0);
204 
205         a[ 9][ 0] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), -20401265806.0, 21769653311.0);
206         a[ 9][ 1] = getField().getZero();
207         a[ 9][ 2] = getField().getZero();
208         a[ 9][ 3] = sqrt6.multiply( 94326.0).add(354216.0).divide(112847.0);
209         a[ 9][ 4] = sqrt6.multiply(-94326.0).add(354216.0).divide(112847.0);
210         a[ 9][ 5] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), -43306765128.0, 5313852383.0);
211         a[ 9][ 6] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), -20866708358144.0, 1126708119789.0);
212         a[ 9][ 7] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 14886003438020.0, 654632330667.0);
213         a[ 9][ 8] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 35290686222309375.0, 14152473387134411.0);
214         a[ 9][ 9] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), -1477884375.0, 485066827.0);
215 
216         a[10][ 0] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 39815761.0, 17514443.0);
217         a[10][ 1] = getField().getZero();
218         a[10][ 2] = getField().getZero();
219         a[10][ 3] = sqrt6.multiply(-960905.0).add(-3457480.0).divide(551636.0);
220         a[10][ 4] = sqrt6.multiply( 960905.0).add(-3457480.0).divide(551636.0);
221         a[10][ 5] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), -844554132.0, 47026969.0);
222         a[10][ 6] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),8444996352.0, 302158619.0);
223         a[10][ 7] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), -2509602342.0, 877790785.0);
224         a[10][ 8] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), -28388795297996250.0, 3199510091356783.0);
225         a[10][ 9] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 226716250.0, 18341897.0);
226         a[10][10] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 1371316744.0, 2131383595.0);
227 
228         // the following stage is both for interpolation and the first stage in next step
229         // (the coefficients are identical to the B array)
230         a[11][ 0] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 104257.0, 1920240.0);
231         a[11][ 1] = getField().getZero();
232         a[11][ 2] = getField().getZero();
233         a[11][ 3] = getField().getZero();
234         a[11][ 4] = getField().getZero();
235         a[11][ 5] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 3399327.0, 763840.0);
236         a[11][ 6] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 66578432.0, 35198415.0);
237         a[11][ 7] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), -1674902723.0, 288716400.0);
238         a[11][ 8] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 54980371265625.0, 176692375811392.0);
239         a[11][ 9] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), -734375.0, 4826304.0);
240         a[11][10] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 171414593.0, 851261400.0);
241         a[11][11] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 137909.0, 3084480.0);
242 
243         // the following stages are for interpolation only
244         a[12][ 0] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),       13481885573.0, 240030000000.0);
245         a[12][ 1] = getField().getZero();
246         a[12][ 2] = getField().getZero();
247         a[12][ 3] = getField().getZero();
248         a[12][ 4] = getField().getZero();
249         a[12][ 5] = getField().getZero();
250         a[12][ 6] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),      139418837528.0, 549975234375.0);
251         a[12][ 7] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),   -11108320068443.0, 45111937500000.0);
252         a[12][ 8] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), -1769651421925959.0, 14249385146080000.0);
253         a[12][ 9] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),          57799439.0, 377055000.0);
254         a[12][10] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),      793322643029.0, 96734250000000.0);
255         a[12][11] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),        1458939311.0, 192780000000.0);
256         a[12][12]  = FieldExplicitRungeKuttaIntegrator.fraction(getField(),             -4149.0, 500000.0);
257 
258         a[13][ 0] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),     1595561272731.0, 50120273500000.0);
259         a[13][ 1] = getField().getZero();
260         a[13][ 2] = getField().getZero();
261         a[13][ 3] = getField().getZero();
262         a[13][ 4] = getField().getZero();
263         a[13][ 5] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),      975183916491.0, 34457688031250.0);
264         a[13][ 6] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),    38492013932672.0, 718912673015625.0);
265         a[13][ 7] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), -1114881286517557.0, 20298710767500000.0);
266         a[13][ 8] = getField().getZero();
267         a[13][ 9] = getField().getZero();
268         a[13][10] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),    -2538710946863.0, 23431227861250000.0);
269         a[13][11] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),        8824659001.0, 23066716781250.0);
270         a[13][12] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),      -11518334563.0, 33831184612500.0);
271         a[13][13] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),        1912306948.0, 13532473845.0);
272 
273         a[14][ 0] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),      -13613986967.0, 31741908048.0);
274         a[14][ 1] = getField().getZero();
275         a[14][ 2] = getField().getZero();
276         a[14][ 3] = getField().getZero();
277         a[14][ 4] = getField().getZero();
278         a[14][ 5] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),       -4755612631.0, 1012344804.0);
279         a[14][ 6] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),    42939257944576.0, 5588559685701.0);
280         a[14][ 7] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),    77881972900277.0, 19140370552944.0);
281         a[14][ 8] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),    22719829234375.0, 63689648654052.0);
282         a[14][ 9] = getField().getZero();
283         a[14][10] = getField().getZero();
284         a[14][11] = getField().getZero();
285         a[14][12] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),       -1199007803.0, 857031517296.0);
286         a[14][13] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),      157882067000.0, 53564469831.0);
287         a[14][14] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),     -290468882375.0, 31741908048.0);
288 
289         return a;
290 
291     }
292 
293     /** {@inheritDoc} */
294     @Override
295     public T[] getB() {
296         final T[] b = MathArrays.buildArray(getField(), 16);
297         b[ 0] = FieldExplicitRungeKuttaIntegrator.fraction(getField(), 104257, 1920240);
298         b[ 1] = getField().getZero();
299         b[ 2] = getField().getZero();
300         b[ 3] = getField().getZero();
301         b[ 4] = getField().getZero();
302         b[ 5] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),         3399327.0,          763840.0);
303         b[ 6] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),        66578432.0,        35198415.0);
304         b[ 7] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),     -1674902723.0,       288716400.0);
305         b[ 8] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),  54980371265625.0, 176692375811392.0);
306         b[ 9] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),         -734375.0,         4826304.0);
307         b[10] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),       171414593.0,       851261400.0);
308         b[11] = FieldExplicitRungeKuttaIntegrator.fraction(getField(),          137909.0,         3084480.0);
309         b[12] = getField().getZero();
310         b[13] = getField().getZero();
311         b[14] = getField().getZero();
312         b[15] = getField().getZero();
313         return b;
314     }
315 
316     /** {@inheritDoc} */
317     @Override
318     protected DormandPrince853FieldStateInterpolator<T>
319         createInterpolator(final boolean forward, T[][] yDotK,
320                            final FieldODEStateAndDerivative<T> globalPreviousState,
321                            final FieldODEStateAndDerivative<T> globalCurrentState, final FieldEquationsMapper<T> mapper) {
322         return new DormandPrince853FieldStateInterpolator<>(getField(), forward, yDotK,
323                                                             globalPreviousState, globalCurrentState,
324                                                             globalPreviousState, globalCurrentState,
325                                                             mapper);
326     }
327 
328     /** {@inheritDoc} */
329     @Override
330     public int getOrder() {
331         return 8;
332     }
333 
334     /** {@inheritDoc} */
335     @Override
336     protected double estimateError(final T[][] yDotK, final T[] y0, final T[] y1, final T h) {
337 
338         final StepsizeHelper helper = getStepSizeHelper();
339         double error1 = 0;
340         double error2 = 0;
341 
342         for (int j = 0; j < helper.getMainSetDimension(); ++j) {
343             final double errSum1 = DormandPrince853Integrator.E1_01 * yDotK[ 0][j].getReal() + DormandPrince853Integrator.E1_06 * yDotK[ 5][j].getReal() +
344                                    DormandPrince853Integrator.E1_07 * yDotK[ 6][j].getReal() + DormandPrince853Integrator.E1_08 * yDotK[ 7][j].getReal() +
345                                    DormandPrince853Integrator.E1_09 * yDotK[ 8][j].getReal() + DormandPrince853Integrator.E1_10 * yDotK[ 9][j].getReal() +
346                                    DormandPrince853Integrator.E1_11 * yDotK[10][j].getReal() + DormandPrince853Integrator.E1_12 * yDotK[11][j].getReal();
347             final double errSum2 = DormandPrince853Integrator.E2_01 * yDotK[ 0][j].getReal() + DormandPrince853Integrator.E2_06 * yDotK[ 5][j].getReal() +
348                                    DormandPrince853Integrator.E2_07 * yDotK[ 6][j].getReal() + DormandPrince853Integrator.E2_08 * yDotK[ 7][j].getReal() +
349                                    DormandPrince853Integrator.E2_09 * yDotK[ 8][j].getReal() + DormandPrince853Integrator.E2_10 * yDotK[ 9][j].getReal() +
350                                    DormandPrince853Integrator.E2_11 * yDotK[10][j].getReal() + DormandPrince853Integrator.E2_12 * yDotK[11][j].getReal();
351             final double tol     = helper.getTolerance(j, FastMath.max(FastMath.abs(y0[j].getReal()), FastMath.abs(y1[j].getReal())));
352             final double ratio1  = errSum1 / tol;
353             error1        += ratio1 * ratio1;
354             final double ratio2  = errSum2 / tol;
355             error2        += ratio2 * ratio2;
356 
357         }
358 
359         double den = error1 + 0.01 * error2;
360         if (den <= 0.0) {
361             den = 1.0;
362         }
363 
364         return FastMath.abs(h.getReal()) * error1 / FastMath.sqrt(helper.getMainSetDimension() * den);
365 
366     }
367 
368 }