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