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