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.math4.legacy.ode.nonstiff;
019
020import org.apache.commons.math4.legacy.core.Field;
021import org.apache.commons.math4.legacy.core.RealFieldElement;
022import org.apache.commons.math4.legacy.ode.FieldEquationsMapper;
023import org.apache.commons.math4.legacy.ode.FieldODEStateAndDerivative;
024import org.apache.commons.math4.legacy.core.MathArrays;
025
026
027/**
028 * This class implements the 5(4) Higham and Hall integrator for
029 * Ordinary Differential Equations.
030 *
031 * <p>This integrator is an embedded Runge-Kutta integrator
032 * of order 5(4) used in local extrapolation mode (i.e. the solution
033 * is computed using the high order formula) with stepsize control
034 * (and automatic step initialization) and continuous output. This
035 * method uses 7 functions evaluations per step.</p>
036 *
037 * @param <T> the type of the field elements
038 * @since 3.6
039 */
040
041public class HighamHall54FieldIntegrator<T extends RealFieldElement<T>>
042    extends EmbeddedRungeKuttaFieldIntegrator<T> {
043
044    /** Integrator method name. */
045    private static final String METHOD_NAME = "Higham-Hall 5(4)";
046
047    /** Error weights Butcher array. */
048    private final T[] e ;
049
050    /** Simple constructor.
051     * Build a fifth order Higham and Hall integrator with the given step bounds
052     * @param field field to which the time and state vector elements belong
053     * @param minStep minimal step (sign is irrelevant, regardless of
054     * integration direction, forward or backward), the last step can
055     * be smaller than this
056     * @param maxStep maximal step (sign is irrelevant, regardless of
057     * integration direction, forward or backward), the last step can
058     * be smaller than this
059     * @param scalAbsoluteTolerance allowed absolute error
060     * @param scalRelativeTolerance allowed relative error
061     */
062    public HighamHall54FieldIntegrator(final Field<T> field,
063                                       final double minStep, final double maxStep,
064                                       final double scalAbsoluteTolerance,
065                                       final double scalRelativeTolerance) {
066        super(field, METHOD_NAME, -1,
067              minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
068        e = MathArrays.buildArray(field, 7);
069        e[0] = fraction(-1,  20);
070        e[1] = field.getZero();
071        e[2] = fraction(81, 160);
072        e[3] = fraction(-6,   5);
073        e[4] = fraction(25,  32);
074        e[5] = fraction( 1,  16);
075        e[6] = fraction(-1,  10);
076    }
077
078    /** Simple constructor.
079     * Build a fifth order Higham and Hall integrator with the given step bounds
080     * @param field field to which the time and state vector elements belong
081     * @param minStep minimal step (sign is irrelevant, regardless of
082     * integration direction, forward or backward), the last step can
083     * be smaller than this
084     * @param maxStep maximal step (sign is irrelevant, regardless of
085     * integration direction, forward or backward), the last step can
086     * be smaller than this
087     * @param vecAbsoluteTolerance allowed absolute error
088     * @param vecRelativeTolerance allowed relative error
089     */
090    public HighamHall54FieldIntegrator(final Field<T> field,
091                                       final double minStep, final double maxStep,
092                                       final double[] vecAbsoluteTolerance,
093                                       final double[] vecRelativeTolerance) {
094        super(field, METHOD_NAME, -1,
095              minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
096        e = MathArrays.buildArray(field, 7);
097        e[0] = fraction(-1,  20);
098        e[1] = field.getZero();
099        e[2] = fraction(81, 160);
100        e[3] = fraction(-6,   5);
101        e[4] = fraction(25,  32);
102        e[5] = fraction( 1,  16);
103        e[6] = fraction(-1,  10);
104    }
105
106    /** {@inheritDoc} */
107    @Override
108    public T[] getC() {
109        final T[] c = MathArrays.buildArray(getField(), 6);
110        c[0] = fraction(2, 9);
111        c[1] = fraction(1, 3);
112        c[2] = fraction(1, 2);
113        c[3] = fraction(3, 5);
114        c[4] = getField().getOne();
115        c[5] = getField().getOne();
116        return c;
117    }
118
119    /** {@inheritDoc} */
120    @Override
121    public T[][] getA() {
122        final T[][] a = MathArrays.buildArray(getField(), 6, -1);
123        for (int i = 0; i < a.length; ++i) {
124            a[i] = MathArrays.buildArray(getField(), i + 1);
125        }
126        a[0][0] = fraction(     2,     9);
127        a[1][0] = fraction(     1,    12);
128        a[1][1] = fraction(     1,     4);
129        a[2][0] = fraction(     1,     8);
130        a[2][1] = getField().getZero();
131        a[2][2] = fraction(     3,     8);
132        a[3][0] = fraction(    91,   500);
133        a[3][1] = fraction(   -27,   100);
134        a[3][2] = fraction(    78,   125);
135        a[3][3] = fraction(     8,   125);
136        a[4][0] = fraction(   -11,    20);
137        a[4][1] = fraction(    27,    20);
138        a[4][2] = fraction(    12,     5);
139        a[4][3] = fraction(   -36,     5);
140        a[4][4] = fraction(     5,     1);
141        a[5][0] = fraction(     1,    12);
142        a[5][1] = getField().getZero();
143        a[5][2] = fraction(    27,    32);
144        a[5][3] = fraction(    -4,     3);
145        a[5][4] = fraction(   125,    96);
146        a[5][5] = fraction(     5,    48);
147        return a;
148    }
149
150    /** {@inheritDoc} */
151    @Override
152    public T[] getB() {
153        final T[] b = MathArrays.buildArray(getField(), 7);
154        b[0] = fraction(  1, 12);
155        b[1] = getField().getZero();
156        b[2] = fraction( 27, 32);
157        b[3] = fraction( -4,  3);
158        b[4] = fraction(125, 96);
159        b[5] = fraction(  5, 48);
160        b[6] = getField().getZero();
161        return b;
162    }
163
164    /** {@inheritDoc} */
165    @Override
166    protected HighamHall54FieldStepInterpolator<T>
167        createInterpolator(final boolean forward, T[][] yDotK,
168                           final FieldODEStateAndDerivative<T> globalPreviousState,
169                           final FieldODEStateAndDerivative<T> globalCurrentState, final FieldEquationsMapper<T> mapper) {
170        return new HighamHall54FieldStepInterpolator<>(getField(), forward, yDotK,
171                                                        globalPreviousState, globalCurrentState,
172                                                        globalPreviousState, globalCurrentState,
173                                                        mapper);
174    }
175
176    /** {@inheritDoc} */
177    @Override
178    public int getOrder() {
179        return 5;
180    }
181
182    /** {@inheritDoc} */
183    @Override
184    protected T estimateError(final T[][] yDotK, final T[] y0, final T[] y1, final T h) {
185
186        T error = getField().getZero();
187
188        for (int j = 0; j < mainSetDimension; ++j) {
189            T errSum = yDotK[0][j].multiply(e[0]);
190            for (int l = 1; l < e.length; ++l) {
191                errSum = errSum.add(yDotK[l][j].multiply(e[l]));
192            }
193
194            final T yScale = RealFieldElement.max(y0[j].abs(), y1[j].abs());
195            final T tol    = (vecAbsoluteTolerance == null) ?
196                             yScale.multiply(scalRelativeTolerance).add(scalAbsoluteTolerance) :
197                             yScale.multiply(vecRelativeTolerance[j]).add(vecAbsoluteTolerance[j]);
198            final T ratio  = h.multiply(errSum).divide(tol);
199            error = error.add(ratio.multiply(ratio));
200        }
201
202        return error.divide(mainSetDimension).sqrt();
203    }
204}