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
018package org.apache.commons.math3.ode.nonstiff;
019
020import org.apache.commons.math3.Field;
021import org.apache.commons.math3.RealFieldElement;
022import org.apache.commons.math3.ode.FieldEquationsMapper;
023import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
024import org.apache.commons.math3.util.MathArrays;
025import org.apache.commons.math3.util.MathUtils;
026
027
028/**
029 * This class implements the 5(4) Dormand-Prince integrator for Ordinary
030 * Differential Equations.
031
032 * <p>This integrator is an embedded Runge-Kutta integrator
033 * of order 5(4) used in local extrapolation mode (i.e. the solution
034 * is computed using the high order formula) with stepsize control
035 * (and automatic step initialization) and continuous output. This
036 * method uses 7 functions evaluations per step. However, since this
037 * is an <i>fsal</i>, the last evaluation of one step is the same as
038 * the first evaluation of the next step and hence can be avoided. So
039 * the cost is really 6 functions evaluations per step.</p>
040 *
041 * <p>This method has been published (whithout the continuous output
042 * that was added by Shampine in 1986) in the following article :
043 * <pre>
044 *  A family of embedded Runge-Kutta formulae
045 *  J. R. Dormand and P. J. Prince
046 *  Journal of Computational and Applied Mathematics
047 *  volume 6, no 1, 1980, pp. 19-26
048 * </pre></p>
049 *
050 * @param <T> the type of the field elements
051 * @since 3.6
052 */
053
054public class DormandPrince54FieldIntegrator<T extends RealFieldElement<T>>
055    extends EmbeddedRungeKuttaFieldIntegrator<T> {
056
057    /** Integrator method name. */
058    private static final String METHOD_NAME = "Dormand-Prince 5(4)";
059
060    /** Error array, element 1. */
061    private final T e1;
062
063    // element 2 is zero, so it is neither stored nor used
064
065    /** Error array, element 3. */
066    private final T e3;
067
068    /** Error array, element 4. */
069    private final T e4;
070
071    /** Error array, element 5. */
072    private final T e5;
073
074    /** Error array, element 6. */
075    private final T e6;
076
077    /** Error array, element 7. */
078    private final T e7;
079
080    /** Simple constructor.
081     * Build a fifth order Dormand-Prince integrator with the given step bounds
082     * @param field field to which the time and state vector elements belong
083     * @param minStep minimal step (sign is irrelevant, regardless of
084     * integration direction, forward or backward), the last step can
085     * be smaller than this
086     * @param maxStep maximal step (sign is irrelevant, regardless of
087     * integration direction, forward or backward), the last step can
088     * be smaller than this
089     * @param scalAbsoluteTolerance allowed absolute error
090     * @param scalRelativeTolerance allowed relative error
091     */
092    public DormandPrince54FieldIntegrator(final Field<T> field,
093                                          final double minStep, final double maxStep,
094                                          final double scalAbsoluteTolerance,
095                                          final double scalRelativeTolerance) {
096        super(field, METHOD_NAME, 6,
097              minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
098        e1 = fraction(    71,  57600);
099        e3 = fraction(   -71,  16695);
100        e4 = fraction(    71,   1920);
101        e5 = fraction(-17253, 339200);
102        e6 = fraction(    22,    525);
103        e7 = fraction(    -1,     40);
104    }
105
106    /** Simple constructor.
107     * Build a fifth order Dormand-Prince integrator with the given step bounds
108     * @param field field to which the time and state vector elements belong
109     * @param minStep minimal step (sign is irrelevant, regardless of
110     * integration direction, forward or backward), the last step can
111     * be smaller than this
112     * @param maxStep maximal step (sign is irrelevant, regardless of
113     * integration direction, forward or backward), the last step can
114     * be smaller than this
115     * @param vecAbsoluteTolerance allowed absolute error
116     * @param vecRelativeTolerance allowed relative error
117     */
118    public DormandPrince54FieldIntegrator(final Field<T> field,
119                                          final double minStep, final double maxStep,
120                                          final double[] vecAbsoluteTolerance,
121                                          final double[] vecRelativeTolerance) {
122        super(field, METHOD_NAME, 6,
123              minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
124        e1 = fraction(    71,  57600);
125        e3 = fraction(   -71,  16695);
126        e4 = fraction(    71,   1920);
127        e5 = fraction(-17253, 339200);
128        e6 = fraction(    22,    525);
129        e7 = fraction(    -1,     40);
130    }
131
132    /** {@inheritDoc} */
133    public T[] getC() {
134        final T[] c = MathArrays.buildArray(getField(), 6);
135        c[0] = fraction(1,  5);
136        c[1] = fraction(3, 10);
137        c[2] = fraction(4,  5);
138        c[3] = fraction(8,  9);
139        c[4] = getField().getOne();
140        c[5] = getField().getOne();
141        return c;
142    }
143
144    /** {@inheritDoc} */
145    public T[][] getA() {
146        final T[][] a = MathArrays.buildArray(getField(), 6, -1);
147        for (int i = 0; i < a.length; ++i) {
148            a[i] = MathArrays.buildArray(getField(), i + 1);
149        }
150        a[0][0] = fraction(     1,     5);
151        a[1][0] = fraction(     3,    40);
152        a[1][1] = fraction(     9,    40);
153        a[2][0] = fraction(    44,    45);
154        a[2][1] = fraction(   -56,    15);
155        a[2][2] = fraction(    32,     9);
156        a[3][0] = fraction( 19372,  6561);
157        a[3][1] = fraction(-25360,  2187);
158        a[3][2] = fraction( 64448,  6561);
159        a[3][3] = fraction(  -212,   729);
160        a[4][0] = fraction(  9017,  3168);
161        a[4][1] = fraction(  -355,    33);
162        a[4][2] = fraction( 46732,  5247);
163        a[4][3] = fraction(    49,   176);
164        a[4][4] = fraction( -5103, 18656);
165        a[5][0] = fraction(    35,   384);
166        a[5][1] = getField().getZero();
167        a[5][2] = fraction(   500,  1113);
168        a[5][3] = fraction(   125,   192);
169        a[5][4] = fraction( -2187,  6784);
170        a[5][5] = fraction(    11,    84);
171        return a;
172    }
173
174    /** {@inheritDoc} */
175    public T[] getB() {
176        final T[] b = MathArrays.buildArray(getField(), 7);
177        b[0] = fraction(   35,   384);
178        b[1] = getField().getZero();
179        b[2] = fraction(  500, 1113);
180        b[3] = fraction(  125,  192);
181        b[4] = fraction(-2187, 6784);
182        b[5] = fraction(   11,   84);
183        b[6] = getField().getZero();
184        return b;
185    }
186
187    /** {@inheritDoc} */
188    @Override
189    protected DormandPrince54FieldStepInterpolator<T>
190        createInterpolator(final boolean forward, T[][] yDotK,
191                           final FieldODEStateAndDerivative<T> globalPreviousState,
192                           final FieldODEStateAndDerivative<T> globalCurrentState, final FieldEquationsMapper<T> mapper) {
193        return new DormandPrince54FieldStepInterpolator<T>(getField(), forward, yDotK,
194                                                           globalPreviousState, globalCurrentState,
195                                                           globalPreviousState, globalCurrentState,
196                                                           mapper);
197    }
198
199    /** {@inheritDoc} */
200    @Override
201    public int getOrder() {
202        return 5;
203    }
204
205    /** {@inheritDoc} */
206    @Override
207    protected T estimateError(final T[][] yDotK, final T[] y0, final T[] y1, final T h) {
208
209        T error = getField().getZero();
210
211        for (int j = 0; j < mainSetDimension; ++j) {
212            final T errSum =     yDotK[0][j].multiply(e1).
213                             add(yDotK[2][j].multiply(e3)).
214                             add(yDotK[3][j].multiply(e4)).
215                             add(yDotK[4][j].multiply(e5)).
216                             add(yDotK[5][j].multiply(e6)).
217                             add(yDotK[6][j].multiply(e7));
218
219            final T yScale = MathUtils.max(y0[j].abs(), y1[j].abs());
220            final T tol    = (vecAbsoluteTolerance == null) ?
221                             yScale.multiply(scalRelativeTolerance).add(scalAbsoluteTolerance) :
222                             yScale.multiply(vecRelativeTolerance[j]).add(vecAbsoluteTolerance[j]);
223            final T ratio  = h.multiply(errSum).divide(tol);
224            error = error.add(ratio.multiply(ratio));
225
226        }
227
228        return error.divide(mainSetDimension).sqrt();
229
230    }
231
232}