summaryrefslogtreecommitdiff
path: root/src/main/java/org/apache/commons/math3/ode/nonstiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/apache/commons/math3/ode/nonstiff')
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsBashforthFieldIntegrator.java354
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsBashforthIntegrator.java362
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsFieldIntegrator.java145
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsFieldStepInterpolator.java189
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsIntegrator.java136
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsMoultonFieldIntegrator.java416
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsMoultonIntegrator.java421
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsNordsieckFieldTransformer.java363
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsNordsieckTransformer.java361
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdaptiveStepsizeFieldIntegrator.java366
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/AdaptiveStepsizeIntegrator.java375
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaFieldIntegrator.java111
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaFieldStepInterpolator.java136
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaIntegrator.java75
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaStepInterpolator.java136
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54FieldIntegrator.java232
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54FieldStepInterpolator.java249
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54Integrator.java161
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54StepInterpolator.java225
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853FieldIntegrator.java454
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853FieldStepInterpolator.java302
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853Integrator.java286
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853StepInterpolator.java501
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/EmbeddedRungeKuttaFieldIntegrator.java385
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/EmbeddedRungeKuttaIntegrator.java380
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/EulerFieldIntegrator.java96
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/EulerFieldStepInterpolator.java108
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/EulerIntegrator.java72
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/EulerStepInterpolator.java102
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/FieldButcherArrayProvider.java46
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/GillFieldIntegrator.java121
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/GillFieldStepInterpolator.java148
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/GillIntegrator.java74
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/GillStepInterpolator.java151
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerIntegrator.java949
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerStepInterpolator.java407
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54FieldIntegrator.java205
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54FieldStepInterpolator.java116
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54Integrator.java135
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54StepInterpolator.java122
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/LutherFieldIntegrator.java146
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/LutherFieldStepInterpolator.java224
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/LutherIntegrator.java89
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/LutherStepInterpolator.java182
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointFieldIntegrator.java96
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointFieldStepInterpolator.java118
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointIntegrator.java69
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointStepInterpolator.java116
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaFieldIntegrator.java273
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaFieldStepInterpolator.java143
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaIntegrator.java269
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaStepInterpolator.java211
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesFieldIntegrator.java110
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesFieldStepInterpolator.java139
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesIntegrator.java72
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesStepInterpolator.java146
-rw-r--r--src/main/java/org/apache/commons/math3/ode/nonstiff/package-info.java25
57 files changed, 12401 insertions, 0 deletions
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsBashforthFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsBashforthFieldIntegrator.java
new file mode 100644
index 0000000..bec3343
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsBashforthFieldIntegrator.java
@@ -0,0 +1,354 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.linear.Array2DRowFieldMatrix;
+import org.apache.commons.math3.linear.FieldMatrix;
+import org.apache.commons.math3.ode.FieldExpandableODE;
+import org.apache.commons.math3.ode.FieldODEState;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+
+
+/**
+ * This class implements explicit Adams-Bashforth integrators for Ordinary
+ * Differential Equations.
+ *
+ * <p>Adams-Bashforth methods (in fact due to Adams alone) are explicit
+ * multistep ODE solvers. This implementation is a variation of the classical
+ * one: it uses adaptive stepsize to implement error control, whereas
+ * classical implementations are fixed step size. The value of state vector
+ * at step n+1 is a simple combination of the value at step n and of the
+ * derivatives at steps n, n-1, n-2 ... Depending on the number k of previous
+ * steps one wants to use for computing the next value, different formulas
+ * are available:</p>
+ * <ul>
+ * <li>k = 1: y<sub>n+1</sub> = y<sub>n</sub> + h y'<sub>n</sub></li>
+ * <li>k = 2: y<sub>n+1</sub> = y<sub>n</sub> + h (3y'<sub>n</sub>-y'<sub>n-1</sub>)/2</li>
+ * <li>k = 3: y<sub>n+1</sub> = y<sub>n</sub> + h (23y'<sub>n</sub>-16y'<sub>n-1</sub>+5y'<sub>n-2</sub>)/12</li>
+ * <li>k = 4: y<sub>n+1</sub> = y<sub>n</sub> + h (55y'<sub>n</sub>-59y'<sub>n-1</sub>+37y'<sub>n-2</sub>-9y'<sub>n-3</sub>)/24</li>
+ * <li>...</li>
+ * </ul>
+ *
+ * <p>A k-steps Adams-Bashforth method is of order k.</p>
+ *
+ * <h3>Implementation details</h3>
+ *
+ * <p>We define scaled derivatives s<sub>i</sub>(n) at step n as:
+ * <pre>
+ * s<sub>1</sub>(n) = h y'<sub>n</sub> for first derivative
+ * s<sub>2</sub>(n) = h<sup>2</sup>/2 y''<sub>n</sub> for second derivative
+ * s<sub>3</sub>(n) = h<sup>3</sup>/6 y'''<sub>n</sub> for third derivative
+ * ...
+ * s<sub>k</sub>(n) = h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub> for k<sup>th</sup> derivative
+ * </pre></p>
+ *
+ * <p>The definitions above use the classical representation with several previous first
+ * derivatives. Lets define
+ * <pre>
+ * q<sub>n</sub> = [ s<sub>1</sub>(n-1) s<sub>1</sub>(n-2) ... s<sub>1</sub>(n-(k-1)) ]<sup>T</sup>
+ * </pre>
+ * (we omit the k index in the notation for clarity). With these definitions,
+ * Adams-Bashforth methods can be written:
+ * <ul>
+ * <li>k = 1: y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n)</li>
+ * <li>k = 2: y<sub>n+1</sub> = y<sub>n</sub> + 3/2 s<sub>1</sub>(n) + [ -1/2 ] q<sub>n</sub></li>
+ * <li>k = 3: y<sub>n+1</sub> = y<sub>n</sub> + 23/12 s<sub>1</sub>(n) + [ -16/12 5/12 ] q<sub>n</sub></li>
+ * <li>k = 4: y<sub>n+1</sub> = y<sub>n</sub> + 55/24 s<sub>1</sub>(n) + [ -59/24 37/24 -9/24 ] q<sub>n</sub></li>
+ * <li>...</li>
+ * </ul></p>
+ *
+ * <p>Instead of using the classical representation with first derivatives only (y<sub>n</sub>,
+ * s<sub>1</sub>(n) and q<sub>n</sub>), our implementation uses the Nordsieck vector with
+ * higher degrees scaled derivatives all taken at the same step (y<sub>n</sub>, s<sub>1</sub>(n)
+ * and r<sub>n</sub>) where r<sub>n</sub> is defined as:
+ * <pre>
+ * r<sub>n</sub> = [ s<sub>2</sub>(n), s<sub>3</sub>(n) ... s<sub>k</sub>(n) ]<sup>T</sup>
+ * </pre>
+ * (here again we omit the k index in the notation for clarity)
+ * </p>
+ *
+ * <p>Taylor series formulas show that for any index offset i, s<sub>1</sub>(n-i) can be
+ * computed from s<sub>1</sub>(n), s<sub>2</sub>(n) ... s<sub>k</sub>(n), the formula being exact
+ * for degree k polynomials.
+ * <pre>
+ * s<sub>1</sub>(n-i) = s<sub>1</sub>(n) + &sum;<sub>j&gt;0</sub> (j+1) (-i)<sup>j</sup> s<sub>j+1</sub>(n)
+ * </pre>
+ * The previous formula can be used with several values for i to compute the transform between
+ * classical representation and Nordsieck vector. The transform between r<sub>n</sub>
+ * and q<sub>n</sub> resulting from the Taylor series formulas above is:
+ * <pre>
+ * q<sub>n</sub> = s<sub>1</sub>(n) u + P r<sub>n</sub>
+ * </pre>
+ * where u is the [ 1 1 ... 1 ]<sup>T</sup> vector and P is the (k-1)&times;(k-1) matrix built
+ * with the (j+1) (-i)<sup>j</sup> terms with i being the row number starting from 1 and j being
+ * the column number starting from 1:
+ * <pre>
+ * [ -2 3 -4 5 ... ]
+ * [ -4 12 -32 80 ... ]
+ * P = [ -6 27 -108 405 ... ]
+ * [ -8 48 -256 1280 ... ]
+ * [ ... ]
+ * </pre></p>
+ *
+ * <p>Using the Nordsieck vector has several advantages:
+ * <ul>
+ * <li>it greatly simplifies step interpolation as the interpolator mainly applies
+ * Taylor series formulas,</li>
+ * <li>it simplifies step changes that occur when discrete events that truncate
+ * the step are triggered,</li>
+ * <li>it allows to extend the methods in order to support adaptive stepsize.</li>
+ * </ul></p>
+ *
+ * <p>The Nordsieck vector at step n+1 is computed from the Nordsieck vector at step n as follows:
+ * <ul>
+ * <li>y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n) + u<sup>T</sup> r<sub>n</sub></li>
+ * <li>s<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, y<sub>n+1</sub>)</li>
+ * <li>r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub></li>
+ * </ul>
+ * where A is a rows shifting matrix (the lower left part is an identity matrix):
+ * <pre>
+ * [ 0 0 ... 0 0 | 0 ]
+ * [ ---------------+---]
+ * [ 1 0 ... 0 0 | 0 ]
+ * A = [ 0 1 ... 0 0 | 0 ]
+ * [ ... | 0 ]
+ * [ 0 0 ... 1 0 | 0 ]
+ * [ 0 0 ... 0 1 | 0 ]
+ * </pre></p>
+ *
+ * <p>The P<sup>-1</sup>u vector and the P<sup>-1</sup> A P matrix do not depend on the state,
+ * they only depend on k and therefore are precomputed once for all.</p>
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public class AdamsBashforthFieldIntegrator<T extends RealFieldElement<T>> extends AdamsFieldIntegrator<T> {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Adams-Bashforth";
+
+ /**
+ * Build an Adams-Bashforth integrator with the given order and step control parameters.
+ * @param field field to which the time and state vector elements belong
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ * @exception NumberIsTooSmallException if order is 1 or less
+ */
+ public AdamsBashforthFieldIntegrator(final Field<T> field, final int nSteps,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance)
+ throws NumberIsTooSmallException {
+ super(field, METHOD_NAME, nSteps, nSteps, minStep, maxStep,
+ scalAbsoluteTolerance, scalRelativeTolerance);
+ }
+
+ /**
+ * Build an Adams-Bashforth integrator with the given order and step control parameters.
+ * @param field field to which the time and state vector elements belong
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ * @exception IllegalArgumentException if order is 1 or less
+ */
+ public AdamsBashforthFieldIntegrator(final Field<T> field, final int nSteps,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance)
+ throws IllegalArgumentException {
+ super(field, METHOD_NAME, nSteps, nSteps, minStep, maxStep,
+ vecAbsoluteTolerance, vecRelativeTolerance);
+ }
+
+ /** Estimate error.
+ * <p>
+ * Error is estimated by interpolating back to previous state using
+ * the state Taylor expansion and comparing to real previous state.
+ * </p>
+ * @param previousState state vector at step start
+ * @param predictedState predicted state vector at step end
+ * @param predictedScaled predicted value of the scaled derivatives at step end
+ * @param predictedNordsieck predicted value of the Nordsieck vector at step end
+ * @return estimated normalized local discretization error
+ */
+ private T errorEstimation(final T[] previousState,
+ final T[] predictedState,
+ final T[] predictedScaled,
+ final FieldMatrix<T> predictedNordsieck) {
+
+ T error = getField().getZero();
+ for (int i = 0; i < mainSetDimension; ++i) {
+ final T yScale = predictedState[i].abs();
+ final T tol = (vecAbsoluteTolerance == null) ?
+ yScale.multiply(scalRelativeTolerance).add(scalAbsoluteTolerance) :
+ yScale.multiply(vecRelativeTolerance[i]).add(vecAbsoluteTolerance[i]);
+
+ // apply Taylor formula from high order to low order,
+ // for the sake of numerical accuracy
+ T variation = getField().getZero();
+ int sign = predictedNordsieck.getRowDimension() % 2 == 0 ? -1 : 1;
+ for (int k = predictedNordsieck.getRowDimension() - 1; k >= 0; --k) {
+ variation = variation.add(predictedNordsieck.getEntry(k, i).multiply(sign));
+ sign = -sign;
+ }
+ variation = variation.subtract(predictedScaled[i]);
+
+ final T ratio = predictedState[i].subtract(previousState[i]).add(variation).divide(tol);
+ error = error.add(ratio.multiply(ratio));
+
+ }
+
+ return error.divide(mainSetDimension).sqrt();
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldODEStateAndDerivative<T> integrate(final FieldExpandableODE<T> equations,
+ final FieldODEState<T> initialState,
+ final T finalTime)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException {
+
+ sanityChecks(initialState, finalTime);
+ final T t0 = initialState.getTime();
+ final T[] y = equations.getMapper().mapState(initialState);
+ setStepStart(initIntegration(equations, t0, y, finalTime));
+ final boolean forward = finalTime.subtract(initialState.getTime()).getReal() > 0;
+
+ // compute the initial Nordsieck vector using the configured starter integrator
+ start(equations, getStepStart(), finalTime);
+
+ // reuse the step that was chosen by the starter integrator
+ FieldODEStateAndDerivative<T> stepStart = getStepStart();
+ FieldODEStateAndDerivative<T> stepEnd =
+ AdamsFieldStepInterpolator.taylor(stepStart,
+ stepStart.getTime().add(getStepSize()),
+ getStepSize(), scaled, nordsieck);
+
+ // main integration loop
+ setIsLastStep(false);
+ do {
+
+ T[] predictedY = null;
+ final T[] predictedScaled = MathArrays.buildArray(getField(), y.length);
+ Array2DRowFieldMatrix<T> predictedNordsieck = null;
+ T error = getField().getZero().add(10);
+ while (error.subtract(1.0).getReal() >= 0.0) {
+
+ // predict a first estimate of the state at step end
+ predictedY = stepEnd.getState();
+
+ // evaluate the derivative
+ final T[] yDot = computeDerivatives(stepEnd.getTime(), predictedY);
+
+ // predict Nordsieck vector at step end
+ for (int j = 0; j < predictedScaled.length; ++j) {
+ predictedScaled[j] = getStepSize().multiply(yDot[j]);
+ }
+ predictedNordsieck = updateHighOrderDerivativesPhase1(nordsieck);
+ updateHighOrderDerivativesPhase2(scaled, predictedScaled, predictedNordsieck);
+
+ // evaluate error
+ error = errorEstimation(y, predictedY, predictedScaled, predictedNordsieck);
+
+ if (error.subtract(1.0).getReal() >= 0.0) {
+ // reject the step and attempt to reduce error by stepsize control
+ final T factor = computeStepGrowShrinkFactor(error);
+ rescale(filterStep(getStepSize().multiply(factor), forward, false));
+ stepEnd = AdamsFieldStepInterpolator.taylor(getStepStart(),
+ getStepStart().getTime().add(getStepSize()),
+ getStepSize(),
+ scaled,
+ nordsieck);
+
+ }
+ }
+
+ // discrete events handling
+ setStepStart(acceptStep(new AdamsFieldStepInterpolator<T>(getStepSize(), stepEnd,
+ predictedScaled, predictedNordsieck, forward,
+ getStepStart(), stepEnd,
+ equations.getMapper()),
+ finalTime));
+ scaled = predictedScaled;
+ nordsieck = predictedNordsieck;
+
+ if (!isLastStep()) {
+
+ System.arraycopy(predictedY, 0, y, 0, y.length);
+
+ if (resetOccurred()) {
+ // some events handler has triggered changes that
+ // invalidate the derivatives, we need to restart from scratch
+ start(equations, getStepStart(), finalTime);
+ }
+
+ // stepsize control for next step
+ final T factor = computeStepGrowShrinkFactor(error);
+ final T scaledH = getStepSize().multiply(factor);
+ final T nextT = getStepStart().getTime().add(scaledH);
+ final boolean nextIsLast = forward ?
+ nextT.subtract(finalTime).getReal() >= 0 :
+ nextT.subtract(finalTime).getReal() <= 0;
+ T hNew = filterStep(scaledH, forward, nextIsLast);
+
+ final T filteredNextT = getStepStart().getTime().add(hNew);
+ final boolean filteredNextIsLast = forward ?
+ filteredNextT.subtract(finalTime).getReal() >= 0 :
+ filteredNextT.subtract(finalTime).getReal() <= 0;
+ if (filteredNextIsLast) {
+ hNew = finalTime.subtract(getStepStart().getTime());
+ }
+
+ rescale(hNew);
+ stepEnd = AdamsFieldStepInterpolator.taylor(getStepStart(), getStepStart().getTime().add(getStepSize()),
+ getStepSize(), scaled, nordsieck);
+
+ }
+
+ } while (!isLastStep());
+
+ final FieldODEStateAndDerivative<T> finalState = getStepStart();
+ setStepStart(null);
+ setStepSize(null);
+ return finalState;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsBashforthIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsBashforthIntegrator.java
new file mode 100644
index 0000000..af4a435
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsBashforthIntegrator.java
@@ -0,0 +1,362 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.RealMatrix;
+import org.apache.commons.math3.ode.EquationsMapper;
+import org.apache.commons.math3.ode.ExpandableStatefulODE;
+import org.apache.commons.math3.ode.sampling.NordsieckStepInterpolator;
+import org.apache.commons.math3.util.FastMath;
+
+
+/**
+ * This class implements explicit Adams-Bashforth integrators for Ordinary
+ * Differential Equations.
+ *
+ * <p>Adams-Bashforth methods (in fact due to Adams alone) are explicit
+ * multistep ODE solvers. This implementation is a variation of the classical
+ * one: it uses adaptive stepsize to implement error control, whereas
+ * classical implementations are fixed step size. The value of state vector
+ * at step n+1 is a simple combination of the value at step n and of the
+ * derivatives at steps n, n-1, n-2 ... Depending on the number k of previous
+ * steps one wants to use for computing the next value, different formulas
+ * are available:</p>
+ * <ul>
+ * <li>k = 1: y<sub>n+1</sub> = y<sub>n</sub> + h y'<sub>n</sub></li>
+ * <li>k = 2: y<sub>n+1</sub> = y<sub>n</sub> + h (3y'<sub>n</sub>-y'<sub>n-1</sub>)/2</li>
+ * <li>k = 3: y<sub>n+1</sub> = y<sub>n</sub> + h (23y'<sub>n</sub>-16y'<sub>n-1</sub>+5y'<sub>n-2</sub>)/12</li>
+ * <li>k = 4: y<sub>n+1</sub> = y<sub>n</sub> + h (55y'<sub>n</sub>-59y'<sub>n-1</sub>+37y'<sub>n-2</sub>-9y'<sub>n-3</sub>)/24</li>
+ * <li>...</li>
+ * </ul>
+ *
+ * <p>A k-steps Adams-Bashforth method is of order k.</p>
+ *
+ * <h3>Implementation details</h3>
+ *
+ * <p>We define scaled derivatives s<sub>i</sub>(n) at step n as:
+ * <pre>
+ * s<sub>1</sub>(n) = h y'<sub>n</sub> for first derivative
+ * s<sub>2</sub>(n) = h<sup>2</sup>/2 y''<sub>n</sub> for second derivative
+ * s<sub>3</sub>(n) = h<sup>3</sup>/6 y'''<sub>n</sub> for third derivative
+ * ...
+ * s<sub>k</sub>(n) = h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub> for k<sup>th</sup> derivative
+ * </pre></p>
+ *
+ * <p>The definitions above use the classical representation with several previous first
+ * derivatives. Lets define
+ * <pre>
+ * q<sub>n</sub> = [ s<sub>1</sub>(n-1) s<sub>1</sub>(n-2) ... s<sub>1</sub>(n-(k-1)) ]<sup>T</sup>
+ * </pre>
+ * (we omit the k index in the notation for clarity). With these definitions,
+ * Adams-Bashforth methods can be written:
+ * <ul>
+ * <li>k = 1: y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n)</li>
+ * <li>k = 2: y<sub>n+1</sub> = y<sub>n</sub> + 3/2 s<sub>1</sub>(n) + [ -1/2 ] q<sub>n</sub></li>
+ * <li>k = 3: y<sub>n+1</sub> = y<sub>n</sub> + 23/12 s<sub>1</sub>(n) + [ -16/12 5/12 ] q<sub>n</sub></li>
+ * <li>k = 4: y<sub>n+1</sub> = y<sub>n</sub> + 55/24 s<sub>1</sub>(n) + [ -59/24 37/24 -9/24 ] q<sub>n</sub></li>
+ * <li>...</li>
+ * </ul></p>
+ *
+ * <p>Instead of using the classical representation with first derivatives only (y<sub>n</sub>,
+ * s<sub>1</sub>(n) and q<sub>n</sub>), our implementation uses the Nordsieck vector with
+ * higher degrees scaled derivatives all taken at the same step (y<sub>n</sub>, s<sub>1</sub>(n)
+ * and r<sub>n</sub>) where r<sub>n</sub> is defined as:
+ * <pre>
+ * r<sub>n</sub> = [ s<sub>2</sub>(n), s<sub>3</sub>(n) ... s<sub>k</sub>(n) ]<sup>T</sup>
+ * </pre>
+ * (here again we omit the k index in the notation for clarity)
+ * </p>
+ *
+ * <p>Taylor series formulas show that for any index offset i, s<sub>1</sub>(n-i) can be
+ * computed from s<sub>1</sub>(n), s<sub>2</sub>(n) ... s<sub>k</sub>(n), the formula being exact
+ * for degree k polynomials.
+ * <pre>
+ * s<sub>1</sub>(n-i) = s<sub>1</sub>(n) + &sum;<sub>j&gt;0</sub> (j+1) (-i)<sup>j</sup> s<sub>j+1</sub>(n)
+ * </pre>
+ * The previous formula can be used with several values for i to compute the transform between
+ * classical representation and Nordsieck vector. The transform between r<sub>n</sub>
+ * and q<sub>n</sub> resulting from the Taylor series formulas above is:
+ * <pre>
+ * q<sub>n</sub> = s<sub>1</sub>(n) u + P r<sub>n</sub>
+ * </pre>
+ * where u is the [ 1 1 ... 1 ]<sup>T</sup> vector and P is the (k-1)&times;(k-1) matrix built
+ * with the (j+1) (-i)<sup>j</sup> terms with i being the row number starting from 1 and j being
+ * the column number starting from 1:
+ * <pre>
+ * [ -2 3 -4 5 ... ]
+ * [ -4 12 -32 80 ... ]
+ * P = [ -6 27 -108 405 ... ]
+ * [ -8 48 -256 1280 ... ]
+ * [ ... ]
+ * </pre></p>
+ *
+ * <p>Using the Nordsieck vector has several advantages:
+ * <ul>
+ * <li>it greatly simplifies step interpolation as the interpolator mainly applies
+ * Taylor series formulas,</li>
+ * <li>it simplifies step changes that occur when discrete events that truncate
+ * the step are triggered,</li>
+ * <li>it allows to extend the methods in order to support adaptive stepsize.</li>
+ * </ul></p>
+ *
+ * <p>The Nordsieck vector at step n+1 is computed from the Nordsieck vector at step n as follows:
+ * <ul>
+ * <li>y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n) + u<sup>T</sup> r<sub>n</sub></li>
+ * <li>s<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, y<sub>n+1</sub>)</li>
+ * <li>r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub></li>
+ * </ul>
+ * where A is a rows shifting matrix (the lower left part is an identity matrix):
+ * <pre>
+ * [ 0 0 ... 0 0 | 0 ]
+ * [ ---------------+---]
+ * [ 1 0 ... 0 0 | 0 ]
+ * A = [ 0 1 ... 0 0 | 0 ]
+ * [ ... | 0 ]
+ * [ 0 0 ... 1 0 | 0 ]
+ * [ 0 0 ... 0 1 | 0 ]
+ * </pre></p>
+ *
+ * <p>The P<sup>-1</sup>u vector and the P<sup>-1</sup> A P matrix do not depend on the state,
+ * they only depend on k and therefore are precomputed once for all.</p>
+ *
+ * @since 2.0
+ */
+public class AdamsBashforthIntegrator extends AdamsIntegrator {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Adams-Bashforth";
+
+ /**
+ * Build an Adams-Bashforth integrator with the given order and step control parameters.
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ * @exception NumberIsTooSmallException if order is 1 or less
+ */
+ public AdamsBashforthIntegrator(final int nSteps,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance)
+ throws NumberIsTooSmallException {
+ super(METHOD_NAME, nSteps, nSteps, minStep, maxStep,
+ scalAbsoluteTolerance, scalRelativeTolerance);
+ }
+
+ /**
+ * Build an Adams-Bashforth integrator with the given order and step control parameters.
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ * @exception IllegalArgumentException if order is 1 or less
+ */
+ public AdamsBashforthIntegrator(final int nSteps,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance)
+ throws IllegalArgumentException {
+ super(METHOD_NAME, nSteps, nSteps, minStep, maxStep,
+ vecAbsoluteTolerance, vecRelativeTolerance);
+ }
+
+ /** Estimate error.
+ * <p>
+ * Error is estimated by interpolating back to previous state using
+ * the state Taylor expansion and comparing to real previous state.
+ * </p>
+ * @param previousState state vector at step start
+ * @param predictedState predicted state vector at step end
+ * @param predictedScaled predicted value of the scaled derivatives at step end
+ * @param predictedNordsieck predicted value of the Nordsieck vector at step end
+ * @return estimated normalized local discretization error
+ */
+ private double errorEstimation(final double[] previousState,
+ final double[] predictedState,
+ final double[] predictedScaled,
+ final RealMatrix predictedNordsieck) {
+
+ double error = 0;
+ for (int i = 0; i < mainSetDimension; ++i) {
+ final double yScale = FastMath.abs(predictedState[i]);
+ final double tol = (vecAbsoluteTolerance == null) ?
+ (scalAbsoluteTolerance + scalRelativeTolerance * yScale) :
+ (vecAbsoluteTolerance[i] + vecRelativeTolerance[i] * yScale);
+
+ // apply Taylor formula from high order to low order,
+ // for the sake of numerical accuracy
+ double variation = 0;
+ int sign = predictedNordsieck.getRowDimension() % 2 == 0 ? -1 : 1;
+ for (int k = predictedNordsieck.getRowDimension() - 1; k >= 0; --k) {
+ variation += sign * predictedNordsieck.getEntry(k, i);
+ sign = -sign;
+ }
+ variation -= predictedScaled[i];
+
+ final double ratio = (predictedState[i] - previousState[i] + variation) / tol;
+ error += ratio * ratio;
+
+ }
+
+ return FastMath.sqrt(error / mainSetDimension);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void integrate(final ExpandableStatefulODE equations, final double t)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException {
+
+ sanityChecks(equations, t);
+ setEquations(equations);
+ final boolean forward = t > equations.getTime();
+
+ // initialize working arrays
+ final double[] y = equations.getCompleteState();
+ final double[] yDot = new double[y.length];
+
+ // set up an interpolator sharing the integrator arrays
+ final NordsieckStepInterpolator interpolator = new NordsieckStepInterpolator();
+ interpolator.reinitialize(y, forward,
+ equations.getPrimaryMapper(), equations.getSecondaryMappers());
+
+ // set up integration control objects
+ initIntegration(equations.getTime(), y, t);
+
+ // compute the initial Nordsieck vector using the configured starter integrator
+ start(equations.getTime(), y, t);
+ interpolator.reinitialize(stepStart, stepSize, scaled, nordsieck);
+ interpolator.storeTime(stepStart);
+
+ // reuse the step that was chosen by the starter integrator
+ double hNew = stepSize;
+ interpolator.rescale(hNew);
+
+ // main integration loop
+ isLastStep = false;
+ do {
+
+ interpolator.shift();
+ final double[] predictedY = new double[y.length];
+ final double[] predictedScaled = new double[y.length];
+ Array2DRowRealMatrix predictedNordsieck = null;
+ double error = 10;
+ while (error >= 1.0) {
+
+ // predict a first estimate of the state at step end
+ final double stepEnd = stepStart + hNew;
+ interpolator.storeTime(stepEnd);
+ final ExpandableStatefulODE expandable = getExpandable();
+ final EquationsMapper primary = expandable.getPrimaryMapper();
+ primary.insertEquationData(interpolator.getInterpolatedState(), predictedY);
+ int index = 0;
+ for (final EquationsMapper secondary : expandable.getSecondaryMappers()) {
+ secondary.insertEquationData(interpolator.getInterpolatedSecondaryState(index), predictedY);
+ ++index;
+ }
+
+ // evaluate the derivative
+ computeDerivatives(stepEnd, predictedY, yDot);
+
+ // predict Nordsieck vector at step end
+ for (int j = 0; j < predictedScaled.length; ++j) {
+ predictedScaled[j] = hNew * yDot[j];
+ }
+ predictedNordsieck = updateHighOrderDerivativesPhase1(nordsieck);
+ updateHighOrderDerivativesPhase2(scaled, predictedScaled, predictedNordsieck);
+
+ // evaluate error
+ error = errorEstimation(y, predictedY, predictedScaled, predictedNordsieck);
+
+ if (error >= 1.0) {
+ // reject the step and attempt to reduce error by stepsize control
+ final double factor = computeStepGrowShrinkFactor(error);
+ hNew = filterStep(hNew * factor, forward, false);
+ interpolator.rescale(hNew);
+
+ }
+ }
+
+ stepSize = hNew;
+ final double stepEnd = stepStart + stepSize;
+ interpolator.reinitialize(stepEnd, stepSize, predictedScaled, predictedNordsieck);
+
+ // discrete events handling
+ interpolator.storeTime(stepEnd);
+ System.arraycopy(predictedY, 0, y, 0, y.length);
+ stepStart = acceptStep(interpolator, y, yDot, t);
+ scaled = predictedScaled;
+ nordsieck = predictedNordsieck;
+ interpolator.reinitialize(stepEnd, stepSize, scaled, nordsieck);
+
+ if (!isLastStep) {
+
+ // prepare next step
+ interpolator.storeTime(stepStart);
+
+ if (resetOccurred) {
+ // some events handler has triggered changes that
+ // invalidate the derivatives, we need to restart from scratch
+ start(stepStart, y, t);
+ interpolator.reinitialize(stepStart, stepSize, scaled, nordsieck);
+ }
+
+ // stepsize control for next step
+ final double factor = computeStepGrowShrinkFactor(error);
+ final double scaledH = stepSize * factor;
+ final double nextT = stepStart + scaledH;
+ final boolean nextIsLast = forward ? (nextT >= t) : (nextT <= t);
+ hNew = filterStep(scaledH, forward, nextIsLast);
+
+ final double filteredNextT = stepStart + hNew;
+ final boolean filteredNextIsLast = forward ? (filteredNextT >= t) : (filteredNextT <= t);
+ if (filteredNextIsLast) {
+ hNew = t - stepStart;
+ }
+
+ interpolator.rescale(hNew);
+
+ }
+
+ } while (!isLastStep);
+
+ // dispatch results
+ equations.setTime(stepStart);
+ equations.setCompleteState(y);
+
+ resetInternalState();
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsFieldIntegrator.java
new file mode 100644
index 0000000..fcd9397
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsFieldIntegrator.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.linear.Array2DRowFieldMatrix;
+import org.apache.commons.math3.ode.FieldExpandableODE;
+import org.apache.commons.math3.ode.FieldODEState;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.ode.MultistepFieldIntegrator;
+
+
+/** Base class for {@link AdamsBashforthFieldIntegrator Adams-Bashforth} and
+ * {@link AdamsMoultonFieldIntegrator Adams-Moulton} integrators.
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public abstract class AdamsFieldIntegrator<T extends RealFieldElement<T>> extends MultistepFieldIntegrator<T> {
+
+ /** Transformer. */
+ private final AdamsNordsieckFieldTransformer<T> transformer;
+
+ /**
+ * Build an Adams integrator with the given order and step control parameters.
+ * @param field field to which the time and state vector elements belong
+ * @param name name of the method
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param order order of the method
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ * @exception NumberIsTooSmallException if order is 1 or less
+ */
+ public AdamsFieldIntegrator(final Field<T> field, final String name,
+ final int nSteps, final int order,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance)
+ throws NumberIsTooSmallException {
+ super(field, name, nSteps, order, minStep, maxStep,
+ scalAbsoluteTolerance, scalRelativeTolerance);
+ transformer = AdamsNordsieckFieldTransformer.getInstance(field, nSteps);
+ }
+
+ /**
+ * Build an Adams integrator with the given order and step control parameters.
+ * @param field field to which the time and state vector elements belong
+ * @param name name of the method
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param order order of the method
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ * @exception IllegalArgumentException if order is 1 or less
+ */
+ public AdamsFieldIntegrator(final Field<T> field, final String name,
+ final int nSteps, final int order,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance)
+ throws IllegalArgumentException {
+ super(field, name, nSteps, order, minStep, maxStep,
+ vecAbsoluteTolerance, vecRelativeTolerance);
+ transformer = AdamsNordsieckFieldTransformer.getInstance(field, nSteps);
+ }
+
+ /** {@inheritDoc} */
+ public abstract FieldODEStateAndDerivative<T> integrate(final FieldExpandableODE<T> equations,
+ final FieldODEState<T> initialState,
+ final T finalTime)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException;
+
+ /** {@inheritDoc} */
+ @Override
+ protected Array2DRowFieldMatrix<T> initializeHighOrderDerivatives(final T h, final T[] t,
+ final T[][] y,
+ final T[][] yDot) {
+ return transformer.initializeHighOrderDerivatives(h, t, y, yDot);
+ }
+
+ /** Update the high order scaled derivatives for Adams integrators (phase 1).
+ * <p>The complete update of high order derivatives has a form similar to:
+ * <pre>
+ * r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub>
+ * </pre>
+ * this method computes the P<sup>-1</sup> A P r<sub>n</sub> part.</p>
+ * @param highOrder high order scaled derivatives
+ * (h<sup>2</sup>/2 y'', ... h<sup>k</sup>/k! y(k))
+ * @return updated high order derivatives
+ * @see #updateHighOrderDerivativesPhase2(RealFieldElement[], RealFieldElement[], Array2DRowFieldMatrix)
+ */
+ public Array2DRowFieldMatrix<T> updateHighOrderDerivativesPhase1(final Array2DRowFieldMatrix<T> highOrder) {
+ return transformer.updateHighOrderDerivativesPhase1(highOrder);
+ }
+
+ /** Update the high order scaled derivatives Adams integrators (phase 2).
+ * <p>The complete update of high order derivatives has a form similar to:
+ * <pre>
+ * r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub>
+ * </pre>
+ * this method computes the (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u part.</p>
+ * <p>Phase 1 of the update must already have been performed.</p>
+ * @param start first order scaled derivatives at step start
+ * @param end first order scaled derivatives at step end
+ * @param highOrder high order scaled derivatives, will be modified
+ * (h<sup>2</sup>/2 y'', ... h<sup>k</sup>/k! y(k))
+ * @see #updateHighOrderDerivativesPhase1(Array2DRowFieldMatrix)
+ */
+ public void updateHighOrderDerivativesPhase2(final T[] start, final T[] end,
+ final Array2DRowFieldMatrix<T> highOrder) {
+ transformer.updateHighOrderDerivativesPhase2(start, end, highOrder);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsFieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsFieldStepInterpolator.java
new file mode 100644
index 0000000..5de61cc
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsFieldStepInterpolator.java
@@ -0,0 +1,189 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import java.util.Arrays;
+
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.linear.Array2DRowFieldMatrix;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.ode.sampling.AbstractFieldStepInterpolator;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class implements an interpolator for Adams integrators using Nordsieck representation.
+ *
+ * <p>This interpolator computes dense output around the current point.
+ * The interpolation equation is based on Taylor series formulas.
+ *
+ * @see AdamsBashforthFieldIntegrator
+ * @see AdamsMoultonFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+class AdamsFieldStepInterpolator<T extends RealFieldElement<T>> extends AbstractFieldStepInterpolator<T> {
+
+ /** Step size used in the first scaled derivative and Nordsieck vector. */
+ private T scalingH;
+
+ /** Reference state.
+ * <p>Sometimes, the reference state is the same as globalPreviousState,
+ * sometimes it is the same as globalCurrentState, so we use a separate
+ * field to avoid any confusion.
+ * </p>
+ */
+ private final FieldODEStateAndDerivative<T> reference;
+
+ /** First scaled derivative. */
+ private final T[] scaled;
+
+ /** Nordsieck vector. */
+ private final Array2DRowFieldMatrix<T> nordsieck;
+
+ /** Simple constructor.
+ * @param stepSize step size used in the scaled and Nordsieck arrays
+ * @param reference reference state from which Taylor expansion are estimated
+ * @param scaled first scaled derivative
+ * @param nordsieck Nordsieck vector
+ * @param isForward integration direction indicator
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param equationsMapper mapper for ODE equations primary and secondary components
+ */
+ AdamsFieldStepInterpolator(final T stepSize, final FieldODEStateAndDerivative<T> reference,
+ final T[] scaled, final Array2DRowFieldMatrix<T> nordsieck,
+ final boolean isForward,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldEquationsMapper<T> equationsMapper) {
+ this(stepSize, reference, scaled, nordsieck,
+ isForward, globalPreviousState, globalCurrentState,
+ globalPreviousState, globalCurrentState, equationsMapper);
+ }
+
+ /** Simple constructor.
+ * @param stepSize step size used in the scaled and Nordsieck arrays
+ * @param reference reference state from which Taylor expansion are estimated
+ * @param scaled first scaled derivative
+ * @param nordsieck Nordsieck vector
+ * @param isForward integration direction indicator
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param equationsMapper mapper for ODE equations primary and secondary components
+ */
+ private AdamsFieldStepInterpolator(final T stepSize, final FieldODEStateAndDerivative<T> reference,
+ final T[] scaled, final Array2DRowFieldMatrix<T> nordsieck,
+ final boolean isForward,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> equationsMapper) {
+ super(isForward, globalPreviousState, globalCurrentState,
+ softPreviousState, softCurrentState, equationsMapper);
+ this.scalingH = stepSize;
+ this.reference = reference;
+ this.scaled = scaled.clone();
+ this.nordsieck = new Array2DRowFieldMatrix<T>(nordsieck.getData(), false);
+ }
+
+ /** Create a new instance.
+ * @param newForward integration direction indicator
+ * @param newGlobalPreviousState start of the global step
+ * @param newGlobalCurrentState end of the global step
+ * @param newSoftPreviousState start of the restricted step
+ * @param newSoftCurrentState end of the restricted step
+ * @param newMapper equations mapper for the all equations
+ * @return a new instance
+ */
+ @Override
+ protected AdamsFieldStepInterpolator<T> create(boolean newForward,
+ FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ FieldODEStateAndDerivative<T> newSoftPreviousState,
+ FieldODEStateAndDerivative<T> newSoftCurrentState,
+ FieldEquationsMapper<T> newMapper) {
+ return new AdamsFieldStepInterpolator<T>(scalingH, reference, scaled, nordsieck,
+ newForward,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(final FieldEquationsMapper<T> equationsMapper,
+ final T time, final T theta,
+ final T thetaH, final T oneMinusThetaH) {
+ return taylor(reference, time, scalingH, scaled, nordsieck);
+ }
+
+ /** Estimate state by applying Taylor formula.
+ * @param reference reference state
+ * @param time time at which state must be estimated
+ * @param stepSize step size used in the scaled and Nordsieck arrays
+ * @param scaled first scaled derivative
+ * @param nordsieck Nordsieck vector
+ * @return estimated state
+ * @param <S> the type of the field elements
+ */
+ public static <S extends RealFieldElement<S>> FieldODEStateAndDerivative<S> taylor(final FieldODEStateAndDerivative<S> reference,
+ final S time, final S stepSize,
+ final S[] scaled,
+ final Array2DRowFieldMatrix<S> nordsieck) {
+
+ final S x = time.subtract(reference.getTime());
+ final S normalizedAbscissa = x.divide(stepSize);
+
+ S[] stateVariation = MathArrays.buildArray(time.getField(), scaled.length);
+ Arrays.fill(stateVariation, time.getField().getZero());
+ S[] estimatedDerivatives = MathArrays.buildArray(time.getField(), scaled.length);
+ Arrays.fill(estimatedDerivatives, time.getField().getZero());
+
+ // apply Taylor formula from high order to low order,
+ // for the sake of numerical accuracy
+ final S[][] nData = nordsieck.getDataRef();
+ for (int i = nData.length - 1; i >= 0; --i) {
+ final int order = i + 2;
+ final S[] nDataI = nData[i];
+ final S power = normalizedAbscissa.pow(order);
+ for (int j = 0; j < nDataI.length; ++j) {
+ final S d = nDataI[j].multiply(power);
+ stateVariation[j] = stateVariation[j].add(d);
+ estimatedDerivatives[j] = estimatedDerivatives[j].add(d.multiply(order));
+ }
+ }
+
+ S[] estimatedState = reference.getState();
+ for (int j = 0; j < stateVariation.length; ++j) {
+ stateVariation[j] = stateVariation[j].add(scaled[j].multiply(normalizedAbscissa));
+ estimatedState[j] = estimatedState[j].add(stateVariation[j]);
+ estimatedDerivatives[j] =
+ estimatedDerivatives[j].add(scaled[j].multiply(normalizedAbscissa)).divide(x);
+ }
+
+ return new FieldODEStateAndDerivative<S>(time, estimatedState, estimatedDerivatives);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsIntegrator.java
new file mode 100644
index 0000000..dd6f2a1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsIntegrator.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.ode.ExpandableStatefulODE;
+import org.apache.commons.math3.ode.MultistepIntegrator;
+
+
+/** Base class for {@link AdamsBashforthIntegrator Adams-Bashforth} and
+ * {@link AdamsMoultonIntegrator Adams-Moulton} integrators.
+ * @since 2.0
+ */
+public abstract class AdamsIntegrator extends MultistepIntegrator {
+
+ /** Transformer. */
+ private final AdamsNordsieckTransformer transformer;
+
+ /**
+ * Build an Adams integrator with the given order and step control parameters.
+ * @param name name of the method
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param order order of the method
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ * @exception NumberIsTooSmallException if order is 1 or less
+ */
+ public AdamsIntegrator(final String name, final int nSteps, final int order,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance)
+ throws NumberIsTooSmallException {
+ super(name, nSteps, order, minStep, maxStep,
+ scalAbsoluteTolerance, scalRelativeTolerance);
+ transformer = AdamsNordsieckTransformer.getInstance(nSteps);
+ }
+
+ /**
+ * Build an Adams integrator with the given order and step control parameters.
+ * @param name name of the method
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param order order of the method
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ * @exception IllegalArgumentException if order is 1 or less
+ */
+ public AdamsIntegrator(final String name, final int nSteps, final int order,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance)
+ throws IllegalArgumentException {
+ super(name, nSteps, order, minStep, maxStep,
+ vecAbsoluteTolerance, vecRelativeTolerance);
+ transformer = AdamsNordsieckTransformer.getInstance(nSteps);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public abstract void integrate(final ExpandableStatefulODE equations, final double t)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException;
+
+ /** {@inheritDoc} */
+ @Override
+ protected Array2DRowRealMatrix initializeHighOrderDerivatives(final double h, final double[] t,
+ final double[][] y,
+ final double[][] yDot) {
+ return transformer.initializeHighOrderDerivatives(h, t, y, yDot);
+ }
+
+ /** Update the high order scaled derivatives for Adams integrators (phase 1).
+ * <p>The complete update of high order derivatives has a form similar to:
+ * <pre>
+ * r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub>
+ * </pre>
+ * this method computes the P<sup>-1</sup> A P r<sub>n</sub> part.</p>
+ * @param highOrder high order scaled derivatives
+ * (h<sup>2</sup>/2 y'', ... h<sup>k</sup>/k! y(k))
+ * @return updated high order derivatives
+ * @see #updateHighOrderDerivativesPhase2(double[], double[], Array2DRowRealMatrix)
+ */
+ public Array2DRowRealMatrix updateHighOrderDerivativesPhase1(final Array2DRowRealMatrix highOrder) {
+ return transformer.updateHighOrderDerivativesPhase1(highOrder);
+ }
+
+ /** Update the high order scaled derivatives Adams integrators (phase 2).
+ * <p>The complete update of high order derivatives has a form similar to:
+ * <pre>
+ * r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub>
+ * </pre>
+ * this method computes the (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u part.</p>
+ * <p>Phase 1 of the update must already have been performed.</p>
+ * @param start first order scaled derivatives at step start
+ * @param end first order scaled derivatives at step end
+ * @param highOrder high order scaled derivatives, will be modified
+ * (h<sup>2</sup>/2 y'', ... h<sup>k</sup>/k! y(k))
+ * @see #updateHighOrderDerivativesPhase1(Array2DRowRealMatrix)
+ */
+ public void updateHighOrderDerivativesPhase2(final double[] start,
+ final double[] end,
+ final Array2DRowRealMatrix highOrder) {
+ transformer.updateHighOrderDerivativesPhase2(start, end, highOrder);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsMoultonFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsMoultonFieldIntegrator.java
new file mode 100644
index 0000000..2594321
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsMoultonFieldIntegrator.java
@@ -0,0 +1,416 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import java.util.Arrays;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.linear.Array2DRowFieldMatrix;
+import org.apache.commons.math3.linear.FieldMatrixPreservingVisitor;
+import org.apache.commons.math3.ode.FieldExpandableODE;
+import org.apache.commons.math3.ode.FieldODEState;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+
+/**
+ * This class implements implicit Adams-Moulton integrators for Ordinary
+ * Differential Equations.
+ *
+ * <p>Adams-Moulton methods (in fact due to Adams alone) are implicit
+ * multistep ODE solvers. This implementation is a variation of the classical
+ * one: it uses adaptive stepsize to implement error control, whereas
+ * classical implementations are fixed step size. The value of state vector
+ * at step n+1 is a simple combination of the value at step n and of the
+ * derivatives at steps n+1, n, n-1 ... Since y'<sub>n+1</sub> is needed to
+ * compute y<sub>n+1</sub>, another method must be used to compute a first
+ * estimate of y<sub>n+1</sub>, then compute y'<sub>n+1</sub>, then compute
+ * a final estimate of y<sub>n+1</sub> using the following formulas. Depending
+ * on the number k of previous steps one wants to use for computing the next
+ * value, different formulas are available for the final estimate:</p>
+ * <ul>
+ * <li>k = 1: y<sub>n+1</sub> = y<sub>n</sub> + h y'<sub>n+1</sub></li>
+ * <li>k = 2: y<sub>n+1</sub> = y<sub>n</sub> + h (y'<sub>n+1</sub>+y'<sub>n</sub>)/2</li>
+ * <li>k = 3: y<sub>n+1</sub> = y<sub>n</sub> + h (5y'<sub>n+1</sub>+8y'<sub>n</sub>-y'<sub>n-1</sub>)/12</li>
+ * <li>k = 4: y<sub>n+1</sub> = y<sub>n</sub> + h (9y'<sub>n+1</sub>+19y'<sub>n</sub>-5y'<sub>n-1</sub>+y'<sub>n-2</sub>)/24</li>
+ * <li>...</li>
+ * </ul>
+ *
+ * <p>A k-steps Adams-Moulton method is of order k+1.</p>
+ *
+ * <h3>Implementation details</h3>
+ *
+ * <p>We define scaled derivatives s<sub>i</sub>(n) at step n as:
+ * <pre>
+ * s<sub>1</sub>(n) = h y'<sub>n</sub> for first derivative
+ * s<sub>2</sub>(n) = h<sup>2</sup>/2 y''<sub>n</sub> for second derivative
+ * s<sub>3</sub>(n) = h<sup>3</sup>/6 y'''<sub>n</sub> for third derivative
+ * ...
+ * s<sub>k</sub>(n) = h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub> for k<sup>th</sup> derivative
+ * </pre></p>
+ *
+ * <p>The definitions above use the classical representation with several previous first
+ * derivatives. Lets define
+ * <pre>
+ * q<sub>n</sub> = [ s<sub>1</sub>(n-1) s<sub>1</sub>(n-2) ... s<sub>1</sub>(n-(k-1)) ]<sup>T</sup>
+ * </pre>
+ * (we omit the k index in the notation for clarity). With these definitions,
+ * Adams-Moulton methods can be written:
+ * <ul>
+ * <li>k = 1: y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n+1)</li>
+ * <li>k = 2: y<sub>n+1</sub> = y<sub>n</sub> + 1/2 s<sub>1</sub>(n+1) + [ 1/2 ] q<sub>n+1</sub></li>
+ * <li>k = 3: y<sub>n+1</sub> = y<sub>n</sub> + 5/12 s<sub>1</sub>(n+1) + [ 8/12 -1/12 ] q<sub>n+1</sub></li>
+ * <li>k = 4: y<sub>n+1</sub> = y<sub>n</sub> + 9/24 s<sub>1</sub>(n+1) + [ 19/24 -5/24 1/24 ] q<sub>n+1</sub></li>
+ * <li>...</li>
+ * </ul></p>
+ *
+ * <p>Instead of using the classical representation with first derivatives only (y<sub>n</sub>,
+ * s<sub>1</sub>(n+1) and q<sub>n+1</sub>), our implementation uses the Nordsieck vector with
+ * higher degrees scaled derivatives all taken at the same step (y<sub>n</sub>, s<sub>1</sub>(n)
+ * and r<sub>n</sub>) where r<sub>n</sub> is defined as:
+ * <pre>
+ * r<sub>n</sub> = [ s<sub>2</sub>(n), s<sub>3</sub>(n) ... s<sub>k</sub>(n) ]<sup>T</sup>
+ * </pre>
+ * (here again we omit the k index in the notation for clarity)
+ * </p>
+ *
+ * <p>Taylor series formulas show that for any index offset i, s<sub>1</sub>(n-i) can be
+ * computed from s<sub>1</sub>(n), s<sub>2</sub>(n) ... s<sub>k</sub>(n), the formula being exact
+ * for degree k polynomials.
+ * <pre>
+ * s<sub>1</sub>(n-i) = s<sub>1</sub>(n) + &sum;<sub>j&gt;0</sub> (j+1) (-i)<sup>j</sup> s<sub>j+1</sub>(n)
+ * </pre>
+ * The previous formula can be used with several values for i to compute the transform between
+ * classical representation and Nordsieck vector. The transform between r<sub>n</sub>
+ * and q<sub>n</sub> resulting from the Taylor series formulas above is:
+ * <pre>
+ * q<sub>n</sub> = s<sub>1</sub>(n) u + P r<sub>n</sub>
+ * </pre>
+ * where u is the [ 1 1 ... 1 ]<sup>T</sup> vector and P is the (k-1)&times;(k-1) matrix built
+ * with the (j+1) (-i)<sup>j</sup> terms with i being the row number starting from 1 and j being
+ * the column number starting from 1:
+ * <pre>
+ * [ -2 3 -4 5 ... ]
+ * [ -4 12 -32 80 ... ]
+ * P = [ -6 27 -108 405 ... ]
+ * [ -8 48 -256 1280 ... ]
+ * [ ... ]
+ * </pre></p>
+ *
+ * <p>Using the Nordsieck vector has several advantages:
+ * <ul>
+ * <li>it greatly simplifies step interpolation as the interpolator mainly applies
+ * Taylor series formulas,</li>
+ * <li>it simplifies step changes that occur when discrete events that truncate
+ * the step are triggered,</li>
+ * <li>it allows to extend the methods in order to support adaptive stepsize.</li>
+ * </ul></p>
+ *
+ * <p>The predicted Nordsieck vector at step n+1 is computed from the Nordsieck vector at step
+ * n as follows:
+ * <ul>
+ * <li>Y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n) + u<sup>T</sup> r<sub>n</sub></li>
+ * <li>S<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, Y<sub>n+1</sub>)</li>
+ * <li>R<sub>n+1</sub> = (s<sub>1</sub>(n) - S<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub></li>
+ * </ul>
+ * where A is a rows shifting matrix (the lower left part is an identity matrix):
+ * <pre>
+ * [ 0 0 ... 0 0 | 0 ]
+ * [ ---------------+---]
+ * [ 1 0 ... 0 0 | 0 ]
+ * A = [ 0 1 ... 0 0 | 0 ]
+ * [ ... | 0 ]
+ * [ 0 0 ... 1 0 | 0 ]
+ * [ 0 0 ... 0 1 | 0 ]
+ * </pre>
+ * From this predicted vector, the corrected vector is computed as follows:
+ * <ul>
+ * <li>y<sub>n+1</sub> = y<sub>n</sub> + S<sub>1</sub>(n+1) + [ -1 +1 -1 +1 ... &plusmn;1 ] r<sub>n+1</sub></li>
+ * <li>s<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, y<sub>n+1</sub>)</li>
+ * <li>r<sub>n+1</sub> = R<sub>n+1</sub> + (s<sub>1</sub>(n+1) - S<sub>1</sub>(n+1)) P<sup>-1</sup> u</li>
+ * </ul>
+ * where the upper case Y<sub>n+1</sub>, S<sub>1</sub>(n+1) and R<sub>n+1</sub> represent the
+ * predicted states whereas the lower case y<sub>n+1</sub>, s<sub>n+1</sub> and r<sub>n+1</sub>
+ * represent the corrected states.</p>
+ *
+ * <p>The P<sup>-1</sup>u vector and the P<sup>-1</sup> A P matrix do not depend on the state,
+ * they only depend on k and therefore are precomputed once for all.</p>
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public class AdamsMoultonFieldIntegrator<T extends RealFieldElement<T>> extends AdamsFieldIntegrator<T> {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Adams-Moulton";
+
+ /**
+ * Build an Adams-Moulton integrator with the given order and error control parameters.
+ * @param field field to which the time and state vector elements belong
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ * @exception NumberIsTooSmallException if order is 1 or less
+ */
+ public AdamsMoultonFieldIntegrator(final Field<T> field, final int nSteps,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance)
+ throws NumberIsTooSmallException {
+ super(field, METHOD_NAME, nSteps, nSteps + 1, minStep, maxStep,
+ scalAbsoluteTolerance, scalRelativeTolerance);
+ }
+
+ /**
+ * Build an Adams-Moulton integrator with the given order and error control parameters.
+ * @param field field to which the time and state vector elements belong
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ * @exception IllegalArgumentException if order is 1 or less
+ */
+ public AdamsMoultonFieldIntegrator(final Field<T> field, final int nSteps,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance)
+ throws IllegalArgumentException {
+ super(field, METHOD_NAME, nSteps, nSteps + 1, minStep, maxStep,
+ vecAbsoluteTolerance, vecRelativeTolerance);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public FieldODEStateAndDerivative<T> integrate(final FieldExpandableODE<T> equations,
+ final FieldODEState<T> initialState,
+ final T finalTime)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException {
+
+ sanityChecks(initialState, finalTime);
+ final T t0 = initialState.getTime();
+ final T[] y = equations.getMapper().mapState(initialState);
+ setStepStart(initIntegration(equations, t0, y, finalTime));
+ final boolean forward = finalTime.subtract(initialState.getTime()).getReal() > 0;
+
+ // compute the initial Nordsieck vector using the configured starter integrator
+ start(equations, getStepStart(), finalTime);
+
+ // reuse the step that was chosen by the starter integrator
+ FieldODEStateAndDerivative<T> stepStart = getStepStart();
+ FieldODEStateAndDerivative<T> stepEnd =
+ AdamsFieldStepInterpolator.taylor(stepStart,
+ stepStart.getTime().add(getStepSize()),
+ getStepSize(), scaled, nordsieck);
+
+ // main integration loop
+ setIsLastStep(false);
+ do {
+
+ T[] predictedY = null;
+ final T[] predictedScaled = MathArrays.buildArray(getField(), y.length);
+ Array2DRowFieldMatrix<T> predictedNordsieck = null;
+ T error = getField().getZero().add(10);
+ while (error.subtract(1.0).getReal() >= 0.0) {
+
+ // predict a first estimate of the state at step end (P in the PECE sequence)
+ predictedY = stepEnd.getState();
+
+ // evaluate a first estimate of the derivative (first E in the PECE sequence)
+ final T[] yDot = computeDerivatives(stepEnd.getTime(), predictedY);
+
+ // update Nordsieck vector
+ for (int j = 0; j < predictedScaled.length; ++j) {
+ predictedScaled[j] = getStepSize().multiply(yDot[j]);
+ }
+ predictedNordsieck = updateHighOrderDerivativesPhase1(nordsieck);
+ updateHighOrderDerivativesPhase2(scaled, predictedScaled, predictedNordsieck);
+
+ // apply correction (C in the PECE sequence)
+ error = predictedNordsieck.walkInOptimizedOrder(new Corrector(y, predictedScaled, predictedY));
+
+ if (error.subtract(1.0).getReal() >= 0.0) {
+ // reject the step and attempt to reduce error by stepsize control
+ final T factor = computeStepGrowShrinkFactor(error);
+ rescale(filterStep(getStepSize().multiply(factor), forward, false));
+ stepEnd = AdamsFieldStepInterpolator.taylor(getStepStart(),
+ getStepStart().getTime().add(getStepSize()),
+ getStepSize(),
+ scaled,
+ nordsieck);
+ }
+ }
+
+ // evaluate a final estimate of the derivative (second E in the PECE sequence)
+ final T[] correctedYDot = computeDerivatives(stepEnd.getTime(), predictedY);
+
+ // update Nordsieck vector
+ final T[] correctedScaled = MathArrays.buildArray(getField(), y.length);
+ for (int j = 0; j < correctedScaled.length; ++j) {
+ correctedScaled[j] = getStepSize().multiply(correctedYDot[j]);
+ }
+ updateHighOrderDerivativesPhase2(predictedScaled, correctedScaled, predictedNordsieck);
+
+ // discrete events handling
+ stepEnd = new FieldODEStateAndDerivative<T>(stepEnd.getTime(), predictedY, correctedYDot);
+ setStepStart(acceptStep(new AdamsFieldStepInterpolator<T>(getStepSize(), stepEnd,
+ correctedScaled, predictedNordsieck, forward,
+ getStepStart(), stepEnd,
+ equations.getMapper()),
+ finalTime));
+ scaled = correctedScaled;
+ nordsieck = predictedNordsieck;
+
+ if (!isLastStep()) {
+
+ System.arraycopy(predictedY, 0, y, 0, y.length);
+
+ if (resetOccurred()) {
+ // some events handler has triggered changes that
+ // invalidate the derivatives, we need to restart from scratch
+ start(equations, getStepStart(), finalTime);
+ }
+
+ // stepsize control for next step
+ final T factor = computeStepGrowShrinkFactor(error);
+ final T scaledH = getStepSize().multiply(factor);
+ final T nextT = getStepStart().getTime().add(scaledH);
+ final boolean nextIsLast = forward ?
+ nextT.subtract(finalTime).getReal() >= 0 :
+ nextT.subtract(finalTime).getReal() <= 0;
+ T hNew = filterStep(scaledH, forward, nextIsLast);
+
+ final T filteredNextT = getStepStart().getTime().add(hNew);
+ final boolean filteredNextIsLast = forward ?
+ filteredNextT.subtract(finalTime).getReal() >= 0 :
+ filteredNextT.subtract(finalTime).getReal() <= 0;
+ if (filteredNextIsLast) {
+ hNew = finalTime.subtract(getStepStart().getTime());
+ }
+
+ rescale(hNew);
+ stepEnd = AdamsFieldStepInterpolator.taylor(getStepStart(), getStepStart().getTime().add(getStepSize()),
+ getStepSize(), scaled, nordsieck);
+
+ }
+
+ } while (!isLastStep());
+
+ final FieldODEStateAndDerivative<T> finalState = getStepStart();
+ setStepStart(null);
+ setStepSize(null);
+ return finalState;
+
+ }
+
+ /** Corrector for current state in Adams-Moulton method.
+ * <p>
+ * This visitor implements the Taylor series formula:
+ * <pre>
+ * Y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n+1) + [ -1 +1 -1 +1 ... &plusmn;1 ] r<sub>n+1</sub>
+ * </pre>
+ * </p>
+ */
+ private class Corrector implements FieldMatrixPreservingVisitor<T> {
+
+ /** Previous state. */
+ private final T[] previous;
+
+ /** Current scaled first derivative. */
+ private final T[] scaled;
+
+ /** Current state before correction. */
+ private final T[] before;
+
+ /** Current state after correction. */
+ private final T[] after;
+
+ /** Simple constructor.
+ * @param previous previous state
+ * @param scaled current scaled first derivative
+ * @param state state to correct (will be overwritten after visit)
+ */
+ Corrector(final T[] previous, final T[] scaled, final T[] state) {
+ this.previous = previous;
+ this.scaled = scaled;
+ this.after = state;
+ this.before = state.clone();
+ }
+
+ /** {@inheritDoc} */
+ public void start(int rows, int columns,
+ int startRow, int endRow, int startColumn, int endColumn) {
+ Arrays.fill(after, getField().getZero());
+ }
+
+ /** {@inheritDoc} */
+ public void visit(int row, int column, T value) {
+ if ((row & 0x1) == 0) {
+ after[column] = after[column].subtract(value);
+ } else {
+ after[column] = after[column].add(value);
+ }
+ }
+
+ /**
+ * End visiting the Nordsieck vector.
+ * <p>The correction is used to control stepsize. So its amplitude is
+ * considered to be an error, which must be normalized according to
+ * error control settings. If the normalized value is greater than 1,
+ * the correction was too large and the step must be rejected.</p>
+ * @return the normalized correction, if greater than 1, the step
+ * must be rejected
+ */
+ public T end() {
+
+ T error = getField().getZero();
+ for (int i = 0; i < after.length; ++i) {
+ after[i] = after[i].add(previous[i].add(scaled[i]));
+ if (i < mainSetDimension) {
+ final T yScale = MathUtils.max(previous[i].abs(), after[i].abs());
+ final T tol = (vecAbsoluteTolerance == null) ?
+ yScale.multiply(scalRelativeTolerance).add(scalAbsoluteTolerance) :
+ yScale.multiply(vecRelativeTolerance[i]).add(vecAbsoluteTolerance[i]);
+ final T ratio = after[i].subtract(before[i]).divide(tol); // (corrected-predicted)/tol
+ error = error.add(ratio.multiply(ratio));
+ }
+ }
+
+ return error.divide(mainSetDimension).sqrt();
+
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsMoultonIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsMoultonIntegrator.java
new file mode 100644
index 0000000..69d2469
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsMoultonIntegrator.java
@@ -0,0 +1,421 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import java.util.Arrays;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.RealMatrixPreservingVisitor;
+import org.apache.commons.math3.ode.EquationsMapper;
+import org.apache.commons.math3.ode.ExpandableStatefulODE;
+import org.apache.commons.math3.ode.sampling.NordsieckStepInterpolator;
+import org.apache.commons.math3.util.FastMath;
+
+
+/**
+ * This class implements implicit Adams-Moulton integrators for Ordinary
+ * Differential Equations.
+ *
+ * <p>Adams-Moulton methods (in fact due to Adams alone) are implicit
+ * multistep ODE solvers. This implementation is a variation of the classical
+ * one: it uses adaptive stepsize to implement error control, whereas
+ * classical implementations are fixed step size. The value of state vector
+ * at step n+1 is a simple combination of the value at step n and of the
+ * derivatives at steps n+1, n, n-1 ... Since y'<sub>n+1</sub> is needed to
+ * compute y<sub>n+1</sub>, another method must be used to compute a first
+ * estimate of y<sub>n+1</sub>, then compute y'<sub>n+1</sub>, then compute
+ * a final estimate of y<sub>n+1</sub> using the following formulas. Depending
+ * on the number k of previous steps one wants to use for computing the next
+ * value, different formulas are available for the final estimate:</p>
+ * <ul>
+ * <li>k = 1: y<sub>n+1</sub> = y<sub>n</sub> + h y'<sub>n+1</sub></li>
+ * <li>k = 2: y<sub>n+1</sub> = y<sub>n</sub> + h (y'<sub>n+1</sub>+y'<sub>n</sub>)/2</li>
+ * <li>k = 3: y<sub>n+1</sub> = y<sub>n</sub> + h (5y'<sub>n+1</sub>+8y'<sub>n</sub>-y'<sub>n-1</sub>)/12</li>
+ * <li>k = 4: y<sub>n+1</sub> = y<sub>n</sub> + h (9y'<sub>n+1</sub>+19y'<sub>n</sub>-5y'<sub>n-1</sub>+y'<sub>n-2</sub>)/24</li>
+ * <li>...</li>
+ * </ul>
+ *
+ * <p>A k-steps Adams-Moulton method is of order k+1.</p>
+ *
+ * <h3>Implementation details</h3>
+ *
+ * <p>We define scaled derivatives s<sub>i</sub>(n) at step n as:
+ * <pre>
+ * s<sub>1</sub>(n) = h y'<sub>n</sub> for first derivative
+ * s<sub>2</sub>(n) = h<sup>2</sup>/2 y''<sub>n</sub> for second derivative
+ * s<sub>3</sub>(n) = h<sup>3</sup>/6 y'''<sub>n</sub> for third derivative
+ * ...
+ * s<sub>k</sub>(n) = h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub> for k<sup>th</sup> derivative
+ * </pre></p>
+ *
+ * <p>The definitions above use the classical representation with several previous first
+ * derivatives. Lets define
+ * <pre>
+ * q<sub>n</sub> = [ s<sub>1</sub>(n-1) s<sub>1</sub>(n-2) ... s<sub>1</sub>(n-(k-1)) ]<sup>T</sup>
+ * </pre>
+ * (we omit the k index in the notation for clarity). With these definitions,
+ * Adams-Moulton methods can be written:
+ * <ul>
+ * <li>k = 1: y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n+1)</li>
+ * <li>k = 2: y<sub>n+1</sub> = y<sub>n</sub> + 1/2 s<sub>1</sub>(n+1) + [ 1/2 ] q<sub>n+1</sub></li>
+ * <li>k = 3: y<sub>n+1</sub> = y<sub>n</sub> + 5/12 s<sub>1</sub>(n+1) + [ 8/12 -1/12 ] q<sub>n+1</sub></li>
+ * <li>k = 4: y<sub>n+1</sub> = y<sub>n</sub> + 9/24 s<sub>1</sub>(n+1) + [ 19/24 -5/24 1/24 ] q<sub>n+1</sub></li>
+ * <li>...</li>
+ * </ul></p>
+ *
+ * <p>Instead of using the classical representation with first derivatives only (y<sub>n</sub>,
+ * s<sub>1</sub>(n+1) and q<sub>n+1</sub>), our implementation uses the Nordsieck vector with
+ * higher degrees scaled derivatives all taken at the same step (y<sub>n</sub>, s<sub>1</sub>(n)
+ * and r<sub>n</sub>) where r<sub>n</sub> is defined as:
+ * <pre>
+ * r<sub>n</sub> = [ s<sub>2</sub>(n), s<sub>3</sub>(n) ... s<sub>k</sub>(n) ]<sup>T</sup>
+ * </pre>
+ * (here again we omit the k index in the notation for clarity)
+ * </p>
+ *
+ * <p>Taylor series formulas show that for any index offset i, s<sub>1</sub>(n-i) can be
+ * computed from s<sub>1</sub>(n), s<sub>2</sub>(n) ... s<sub>k</sub>(n), the formula being exact
+ * for degree k polynomials.
+ * <pre>
+ * s<sub>1</sub>(n-i) = s<sub>1</sub>(n) + &sum;<sub>j&gt;0</sub> (j+1) (-i)<sup>j</sup> s<sub>j+1</sub>(n)
+ * </pre>
+ * The previous formula can be used with several values for i to compute the transform between
+ * classical representation and Nordsieck vector. The transform between r<sub>n</sub>
+ * and q<sub>n</sub> resulting from the Taylor series formulas above is:
+ * <pre>
+ * q<sub>n</sub> = s<sub>1</sub>(n) u + P r<sub>n</sub>
+ * </pre>
+ * where u is the [ 1 1 ... 1 ]<sup>T</sup> vector and P is the (k-1)&times;(k-1) matrix built
+ * with the (j+1) (-i)<sup>j</sup> terms with i being the row number starting from 1 and j being
+ * the column number starting from 1:
+ * <pre>
+ * [ -2 3 -4 5 ... ]
+ * [ -4 12 -32 80 ... ]
+ * P = [ -6 27 -108 405 ... ]
+ * [ -8 48 -256 1280 ... ]
+ * [ ... ]
+ * </pre></p>
+ *
+ * <p>Using the Nordsieck vector has several advantages:
+ * <ul>
+ * <li>it greatly simplifies step interpolation as the interpolator mainly applies
+ * Taylor series formulas,</li>
+ * <li>it simplifies step changes that occur when discrete events that truncate
+ * the step are triggered,</li>
+ * <li>it allows to extend the methods in order to support adaptive stepsize.</li>
+ * </ul></p>
+ *
+ * <p>The predicted Nordsieck vector at step n+1 is computed from the Nordsieck vector at step
+ * n as follows:
+ * <ul>
+ * <li>Y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n) + u<sup>T</sup> r<sub>n</sub></li>
+ * <li>S<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, Y<sub>n+1</sub>)</li>
+ * <li>R<sub>n+1</sub> = (s<sub>1</sub>(n) - S<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub></li>
+ * </ul>
+ * where A is a rows shifting matrix (the lower left part is an identity matrix):
+ * <pre>
+ * [ 0 0 ... 0 0 | 0 ]
+ * [ ---------------+---]
+ * [ 1 0 ... 0 0 | 0 ]
+ * A = [ 0 1 ... 0 0 | 0 ]
+ * [ ... | 0 ]
+ * [ 0 0 ... 1 0 | 0 ]
+ * [ 0 0 ... 0 1 | 0 ]
+ * </pre>
+ * From this predicted vector, the corrected vector is computed as follows:
+ * <ul>
+ * <li>y<sub>n+1</sub> = y<sub>n</sub> + S<sub>1</sub>(n+1) + [ -1 +1 -1 +1 ... &plusmn;1 ] r<sub>n+1</sub></li>
+ * <li>s<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, y<sub>n+1</sub>)</li>
+ * <li>r<sub>n+1</sub> = R<sub>n+1</sub> + (s<sub>1</sub>(n+1) - S<sub>1</sub>(n+1)) P<sup>-1</sup> u</li>
+ * </ul>
+ * where the upper case Y<sub>n+1</sub>, S<sub>1</sub>(n+1) and R<sub>n+1</sub> represent the
+ * predicted states whereas the lower case y<sub>n+1</sub>, s<sub>n+1</sub> and r<sub>n+1</sub>
+ * represent the corrected states.</p>
+ *
+ * <p>The P<sup>-1</sup>u vector and the P<sup>-1</sup> A P matrix do not depend on the state,
+ * they only depend on k and therefore are precomputed once for all.</p>
+ *
+ * @since 2.0
+ */
+public class AdamsMoultonIntegrator extends AdamsIntegrator {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Adams-Moulton";
+
+ /**
+ * Build an Adams-Moulton integrator with the given order and error control parameters.
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ * @exception NumberIsTooSmallException if order is 1 or less
+ */
+ public AdamsMoultonIntegrator(final int nSteps,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance)
+ throws NumberIsTooSmallException {
+ super(METHOD_NAME, nSteps, nSteps + 1, minStep, maxStep,
+ scalAbsoluteTolerance, scalRelativeTolerance);
+ }
+
+ /**
+ * Build an Adams-Moulton integrator with the given order and error control parameters.
+ * @param nSteps number of steps of the method excluding the one being computed
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ * @exception IllegalArgumentException if order is 1 or less
+ */
+ public AdamsMoultonIntegrator(final int nSteps,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance)
+ throws IllegalArgumentException {
+ super(METHOD_NAME, nSteps, nSteps + 1, minStep, maxStep,
+ vecAbsoluteTolerance, vecRelativeTolerance);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void integrate(final ExpandableStatefulODE equations,final double t)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException {
+
+ sanityChecks(equations, t);
+ setEquations(equations);
+ final boolean forward = t > equations.getTime();
+
+ // initialize working arrays
+ final double[] y0 = equations.getCompleteState();
+ final double[] y = y0.clone();
+ final double[] yDot = new double[y.length];
+ final double[] yTmp = new double[y.length];
+ final double[] predictedScaled = new double[y.length];
+ Array2DRowRealMatrix nordsieckTmp = null;
+
+ // set up two interpolators sharing the integrator arrays
+ final NordsieckStepInterpolator interpolator = new NordsieckStepInterpolator();
+ interpolator.reinitialize(y, forward,
+ equations.getPrimaryMapper(), equations.getSecondaryMappers());
+
+ // set up integration control objects
+ initIntegration(equations.getTime(), y0, t);
+
+ // compute the initial Nordsieck vector using the configured starter integrator
+ start(equations.getTime(), y, t);
+ interpolator.reinitialize(stepStart, stepSize, scaled, nordsieck);
+ interpolator.storeTime(stepStart);
+
+ double hNew = stepSize;
+ interpolator.rescale(hNew);
+
+ isLastStep = false;
+ do {
+
+ double error = 10;
+ while (error >= 1.0) {
+
+ stepSize = hNew;
+
+ // predict a first estimate of the state at step end (P in the PECE sequence)
+ final double stepEnd = stepStart + stepSize;
+ interpolator.setInterpolatedTime(stepEnd);
+ final ExpandableStatefulODE expandable = getExpandable();
+ final EquationsMapper primary = expandable.getPrimaryMapper();
+ primary.insertEquationData(interpolator.getInterpolatedState(), yTmp);
+ int index = 0;
+ for (final EquationsMapper secondary : expandable.getSecondaryMappers()) {
+ secondary.insertEquationData(interpolator.getInterpolatedSecondaryState(index), yTmp);
+ ++index;
+ }
+
+ // evaluate a first estimate of the derivative (first E in the PECE sequence)
+ computeDerivatives(stepEnd, yTmp, yDot);
+
+ // update Nordsieck vector
+ for (int j = 0; j < y0.length; ++j) {
+ predictedScaled[j] = stepSize * yDot[j];
+ }
+ nordsieckTmp = updateHighOrderDerivativesPhase1(nordsieck);
+ updateHighOrderDerivativesPhase2(scaled, predictedScaled, nordsieckTmp);
+
+ // apply correction (C in the PECE sequence)
+ error = nordsieckTmp.walkInOptimizedOrder(new Corrector(y, predictedScaled, yTmp));
+
+ if (error >= 1.0) {
+ // reject the step and attempt to reduce error by stepsize control
+ final double factor = computeStepGrowShrinkFactor(error);
+ hNew = filterStep(stepSize * factor, forward, false);
+ interpolator.rescale(hNew);
+ }
+ }
+
+ // evaluate a final estimate of the derivative (second E in the PECE sequence)
+ final double stepEnd = stepStart + stepSize;
+ computeDerivatives(stepEnd, yTmp, yDot);
+
+ // update Nordsieck vector
+ final double[] correctedScaled = new double[y0.length];
+ for (int j = 0; j < y0.length; ++j) {
+ correctedScaled[j] = stepSize * yDot[j];
+ }
+ updateHighOrderDerivativesPhase2(predictedScaled, correctedScaled, nordsieckTmp);
+
+ // discrete events handling
+ System.arraycopy(yTmp, 0, y, 0, y.length);
+ interpolator.reinitialize(stepEnd, stepSize, correctedScaled, nordsieckTmp);
+ interpolator.storeTime(stepStart);
+ interpolator.shift();
+ interpolator.storeTime(stepEnd);
+ stepStart = acceptStep(interpolator, y, yDot, t);
+ scaled = correctedScaled;
+ nordsieck = nordsieckTmp;
+
+ if (!isLastStep) {
+
+ // prepare next step
+ interpolator.storeTime(stepStart);
+
+ if (resetOccurred) {
+ // some events handler has triggered changes that
+ // invalidate the derivatives, we need to restart from scratch
+ start(stepStart, y, t);
+ interpolator.reinitialize(stepStart, stepSize, scaled, nordsieck);
+
+ }
+
+ // stepsize control for next step
+ final double factor = computeStepGrowShrinkFactor(error);
+ final double scaledH = stepSize * factor;
+ final double nextT = stepStart + scaledH;
+ final boolean nextIsLast = forward ? (nextT >= t) : (nextT <= t);
+ hNew = filterStep(scaledH, forward, nextIsLast);
+
+ final double filteredNextT = stepStart + hNew;
+ final boolean filteredNextIsLast = forward ? (filteredNextT >= t) : (filteredNextT <= t);
+ if (filteredNextIsLast) {
+ hNew = t - stepStart;
+ }
+
+ interpolator.rescale(hNew);
+ }
+
+ } while (!isLastStep);
+
+ // dispatch results
+ equations.setTime(stepStart);
+ equations.setCompleteState(y);
+
+ resetInternalState();
+
+ }
+
+ /** Corrector for current state in Adams-Moulton method.
+ * <p>
+ * This visitor implements the Taylor series formula:
+ * <pre>
+ * Y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n+1) + [ -1 +1 -1 +1 ... &plusmn;1 ] r<sub>n+1</sub>
+ * </pre>
+ * </p>
+ */
+ private class Corrector implements RealMatrixPreservingVisitor {
+
+ /** Previous state. */
+ private final double[] previous;
+
+ /** Current scaled first derivative. */
+ private final double[] scaled;
+
+ /** Current state before correction. */
+ private final double[] before;
+
+ /** Current state after correction. */
+ private final double[] after;
+
+ /** Simple constructor.
+ * @param previous previous state
+ * @param scaled current scaled first derivative
+ * @param state state to correct (will be overwritten after visit)
+ */
+ Corrector(final double[] previous, final double[] scaled, final double[] state) {
+ this.previous = previous;
+ this.scaled = scaled;
+ this.after = state;
+ this.before = state.clone();
+ }
+
+ /** {@inheritDoc} */
+ public void start(int rows, int columns,
+ int startRow, int endRow, int startColumn, int endColumn) {
+ Arrays.fill(after, 0.0);
+ }
+
+ /** {@inheritDoc} */
+ public void visit(int row, int column, double value) {
+ if ((row & 0x1) == 0) {
+ after[column] -= value;
+ } else {
+ after[column] += value;
+ }
+ }
+
+ /**
+ * End visiting the Nordsieck vector.
+ * <p>The correction is used to control stepsize. So its amplitude is
+ * considered to be an error, which must be normalized according to
+ * error control settings. If the normalized value is greater than 1,
+ * the correction was too large and the step must be rejected.</p>
+ * @return the normalized correction, if greater than 1, the step
+ * must be rejected
+ */
+ public double end() {
+
+ double error = 0;
+ for (int i = 0; i < after.length; ++i) {
+ after[i] += previous[i] + scaled[i];
+ if (i < mainSetDimension) {
+ final double yScale = FastMath.max(FastMath.abs(previous[i]), FastMath.abs(after[i]));
+ final double tol = (vecAbsoluteTolerance == null) ?
+ (scalAbsoluteTolerance + scalRelativeTolerance * yScale) :
+ (vecAbsoluteTolerance[i] + vecRelativeTolerance[i] * yScale);
+ final double ratio = (after[i] - before[i]) / tol; // (corrected-predicted)/tol
+ error += ratio * ratio;
+ }
+ }
+
+ return FastMath.sqrt(error / mainSetDimension);
+
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsNordsieckFieldTransformer.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsNordsieckFieldTransformer.java
new file mode 100644
index 0000000..b8f872b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsNordsieckFieldTransformer.java
@@ -0,0 +1,363 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.linear.Array2DRowFieldMatrix;
+import org.apache.commons.math3.linear.ArrayFieldVector;
+import org.apache.commons.math3.linear.FieldDecompositionSolver;
+import org.apache.commons.math3.linear.FieldLUDecomposition;
+import org.apache.commons.math3.linear.FieldMatrix;
+import org.apache.commons.math3.util.MathArrays;
+
+/** Transformer to Nordsieck vectors for Adams integrators.
+ * <p>This class is used by {@link AdamsBashforthIntegrator Adams-Bashforth} and
+ * {@link AdamsMoultonIntegrator Adams-Moulton} integrators to convert between
+ * classical representation with several previous first derivatives and Nordsieck
+ * representation with higher order scaled derivatives.</p>
+ *
+ * <p>We define scaled derivatives s<sub>i</sub>(n) at step n as:
+ * <pre>
+ * s<sub>1</sub>(n) = h y'<sub>n</sub> for first derivative
+ * s<sub>2</sub>(n) = h<sup>2</sup>/2 y''<sub>n</sub> for second derivative
+ * s<sub>3</sub>(n) = h<sup>3</sup>/6 y'''<sub>n</sub> for third derivative
+ * ...
+ * s<sub>k</sub>(n) = h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub> for k<sup>th</sup> derivative
+ * </pre></p>
+ *
+ * <p>With the previous definition, the classical representation of multistep methods
+ * uses first derivatives only, i.e. it handles y<sub>n</sub>, s<sub>1</sub>(n) and
+ * q<sub>n</sub> where q<sub>n</sub> is defined as:
+ * <pre>
+ * q<sub>n</sub> = [ s<sub>1</sub>(n-1) s<sub>1</sub>(n-2) ... s<sub>1</sub>(n-(k-1)) ]<sup>T</sup>
+ * </pre>
+ * (we omit the k index in the notation for clarity).</p>
+ *
+ * <p>Another possible representation uses the Nordsieck vector with
+ * higher degrees scaled derivatives all taken at the same step, i.e it handles y<sub>n</sub>,
+ * s<sub>1</sub>(n) and r<sub>n</sub>) where r<sub>n</sub> is defined as:
+ * <pre>
+ * r<sub>n</sub> = [ s<sub>2</sub>(n), s<sub>3</sub>(n) ... s<sub>k</sub>(n) ]<sup>T</sup>
+ * </pre>
+ * (here again we omit the k index in the notation for clarity)
+ * </p>
+ *
+ * <p>Taylor series formulas show that for any index offset i, s<sub>1</sub>(n-i) can be
+ * computed from s<sub>1</sub>(n), s<sub>2</sub>(n) ... s<sub>k</sub>(n), the formula being exact
+ * for degree k polynomials.
+ * <pre>
+ * s<sub>1</sub>(n-i) = s<sub>1</sub>(n) + &sum;<sub>j&gt;0</sub> (j+1) (-i)<sup>j</sup> s<sub>j+1</sub>(n)
+ * </pre>
+ * The previous formula can be used with several values for i to compute the transform between
+ * classical representation and Nordsieck vector at step end. The transform between r<sub>n</sub>
+ * and q<sub>n</sub> resulting from the Taylor series formulas above is:
+ * <pre>
+ * q<sub>n</sub> = s<sub>1</sub>(n) u + P r<sub>n</sub>
+ * </pre>
+ * where u is the [ 1 1 ... 1 ]<sup>T</sup> vector and P is the (k-1)&times;(k-1) matrix built
+ * with the (j+1) (-i)<sup>j</sup> terms with i being the row number starting from 1 and j being
+ * the column number starting from 1:
+ * <pre>
+ * [ -2 3 -4 5 ... ]
+ * [ -4 12 -32 80 ... ]
+ * P = [ -6 27 -108 405 ... ]
+ * [ -8 48 -256 1280 ... ]
+ * [ ... ]
+ * </pre></p>
+ *
+ * <p>Changing -i into +i in the formula above can be used to compute a similar transform between
+ * classical representation and Nordsieck vector at step start. The resulting matrix is simply
+ * the absolute value of matrix P.</p>
+ *
+ * <p>For {@link AdamsBashforthIntegrator Adams-Bashforth} method, the Nordsieck vector
+ * at step n+1 is computed from the Nordsieck vector at step n as follows:
+ * <ul>
+ * <li>y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n) + u<sup>T</sup> r<sub>n</sub></li>
+ * <li>s<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, y<sub>n+1</sub>)</li>
+ * <li>r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub></li>
+ * </ul>
+ * where A is a rows shifting matrix (the lower left part is an identity matrix):
+ * <pre>
+ * [ 0 0 ... 0 0 | 0 ]
+ * [ ---------------+---]
+ * [ 1 0 ... 0 0 | 0 ]
+ * A = [ 0 1 ... 0 0 | 0 ]
+ * [ ... | 0 ]
+ * [ 0 0 ... 1 0 | 0 ]
+ * [ 0 0 ... 0 1 | 0 ]
+ * </pre></p>
+ *
+ * <p>For {@link AdamsMoultonIntegrator Adams-Moulton} method, the predicted Nordsieck vector
+ * at step n+1 is computed from the Nordsieck vector at step n as follows:
+ * <ul>
+ * <li>Y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n) + u<sup>T</sup> r<sub>n</sub></li>
+ * <li>S<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, Y<sub>n+1</sub>)</li>
+ * <li>R<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub></li>
+ * </ul>
+ * From this predicted vector, the corrected vector is computed as follows:
+ * <ul>
+ * <li>y<sub>n+1</sub> = y<sub>n</sub> + S<sub>1</sub>(n+1) + [ -1 +1 -1 +1 ... &plusmn;1 ] r<sub>n+1</sub></li>
+ * <li>s<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, y<sub>n+1</sub>)</li>
+ * <li>r<sub>n+1</sub> = R<sub>n+1</sub> + (s<sub>1</sub>(n+1) - S<sub>1</sub>(n+1)) P<sup>-1</sup> u</li>
+ * </ul>
+ * where the upper case Y<sub>n+1</sub>, S<sub>1</sub>(n+1) and R<sub>n+1</sub> represent the
+ * predicted states whereas the lower case y<sub>n+1</sub>, s<sub>n+1</sub> and r<sub>n+1</sub>
+ * represent the corrected states.</p>
+ *
+ * <p>We observe that both methods use similar update formulas. In both cases a P<sup>-1</sup>u
+ * vector and a P<sup>-1</sup> A P matrix are used that do not depend on the state,
+ * they only depend on k. This class handles these transformations.</p>
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+public class AdamsNordsieckFieldTransformer<T extends RealFieldElement<T>> {
+
+ /** Cache for already computed coefficients. */
+ private static final Map<Integer,
+ Map<Field<? extends RealFieldElement<?>>,
+ AdamsNordsieckFieldTransformer<? extends RealFieldElement<?>>>> CACHE =
+ new HashMap<Integer, Map<Field<? extends RealFieldElement<?>>,
+ AdamsNordsieckFieldTransformer<? extends RealFieldElement<?>>>>();
+
+ /** Field to which the time and state vector elements belong. */
+ private final Field<T> field;
+
+ /** Update matrix for the higher order derivatives h<sup>2</sup>/2 y'', h<sup>3</sup>/6 y''' ... */
+ private final Array2DRowFieldMatrix<T> update;
+
+ /** Update coefficients of the higher order derivatives wrt y'. */
+ private final T[] c1;
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param n number of steps of the multistep method
+ * (excluding the one being computed)
+ */
+ private AdamsNordsieckFieldTransformer(final Field<T> field, final int n) {
+
+ this.field = field;
+ final int rows = n - 1;
+
+ // compute coefficients
+ FieldMatrix<T> bigP = buildP(rows);
+ FieldDecompositionSolver<T> pSolver =
+ new FieldLUDecomposition<T>(bigP).getSolver();
+
+ T[] u = MathArrays.buildArray(field, rows);
+ Arrays.fill(u, field.getOne());
+ c1 = pSolver.solve(new ArrayFieldVector<T>(u, false)).toArray();
+
+ // update coefficients are computed by combining transform from
+ // Nordsieck to multistep, then shifting rows to represent step advance
+ // then applying inverse transform
+ T[][] shiftedP = bigP.getData();
+ for (int i = shiftedP.length - 1; i > 0; --i) {
+ // shift rows
+ shiftedP[i] = shiftedP[i - 1];
+ }
+ shiftedP[0] = MathArrays.buildArray(field, rows);
+ Arrays.fill(shiftedP[0], field.getZero());
+ update = new Array2DRowFieldMatrix<T>(pSolver.solve(new Array2DRowFieldMatrix<T>(shiftedP, false)).getData());
+
+ }
+
+ /** Get the Nordsieck transformer for a given field and number of steps.
+ * @param field field to which the time and state vector elements belong
+ * @param nSteps number of steps of the multistep method
+ * (excluding the one being computed)
+ * @return Nordsieck transformer for the specified field and number of steps
+ * @param <T> the type of the field elements
+ */
+ @SuppressWarnings("unchecked")
+ public static <T extends RealFieldElement<T>> AdamsNordsieckFieldTransformer<T>
+ getInstance(final Field<T> field, final int nSteps) {
+ synchronized(CACHE) {
+ Map<Field<? extends RealFieldElement<?>>,
+ AdamsNordsieckFieldTransformer<? extends RealFieldElement<?>>> map = CACHE.get(nSteps);
+ if (map == null) {
+ map = new HashMap<Field<? extends RealFieldElement<?>>,
+ AdamsNordsieckFieldTransformer<? extends RealFieldElement<?>>>();
+ CACHE.put(nSteps, map);
+ }
+ @SuppressWarnings("rawtypes") // use rawtype to avoid compilation problems with java 1.5
+ AdamsNordsieckFieldTransformer t = map.get(field);
+ if (t == null) {
+ t = new AdamsNordsieckFieldTransformer<T>(field, nSteps);
+ map.put(field, (AdamsNordsieckFieldTransformer<T>) t);
+ }
+ return (AdamsNordsieckFieldTransformer<T>) t;
+
+ }
+ }
+
+ /** Build the P matrix.
+ * <p>The P matrix general terms are shifted (j+1) (-i)<sup>j</sup> terms
+ * with i being the row number starting from 1 and j being the column
+ * number starting from 1:
+ * <pre>
+ * [ -2 3 -4 5 ... ]
+ * [ -4 12 -32 80 ... ]
+ * P = [ -6 27 -108 405 ... ]
+ * [ -8 48 -256 1280 ... ]
+ * [ ... ]
+ * </pre></p>
+ * @param rows number of rows of the matrix
+ * @return P matrix
+ */
+ private FieldMatrix<T> buildP(final int rows) {
+
+ final T[][] pData = MathArrays.buildArray(field, rows, rows);
+
+ for (int i = 1; i <= pData.length; ++i) {
+ // build the P matrix elements from Taylor series formulas
+ final T[] pI = pData[i - 1];
+ final int factor = -i;
+ T aj = field.getZero().add(factor);
+ for (int j = 1; j <= pI.length; ++j) {
+ pI[j - 1] = aj.multiply(j + 1);
+ aj = aj.multiply(factor);
+ }
+ }
+
+ return new Array2DRowFieldMatrix<T>(pData, false);
+
+ }
+
+ /** Initialize the high order scaled derivatives at step start.
+ * @param h step size to use for scaling
+ * @param t first steps times
+ * @param y first steps states
+ * @param yDot first steps derivatives
+ * @return Nordieck vector at start of first step (h<sup>2</sup>/2 y''<sub>n</sub>,
+ * h<sup>3</sup>/6 y'''<sub>n</sub> ... h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub>)
+ */
+
+ public Array2DRowFieldMatrix<T> initializeHighOrderDerivatives(final T h, final T[] t,
+ final T[][] y,
+ final T[][] yDot) {
+
+ // using Taylor series with di = ti - t0, we get:
+ // y(ti) - y(t0) - di y'(t0) = di^2 / h^2 s2 + ... + di^k / h^k sk + O(h^k)
+ // y'(ti) - y'(t0) = 2 di / h^2 s2 + ... + k di^(k-1) / h^k sk + O(h^(k-1))
+ // we write these relations for i = 1 to i= 1+n/2 as a set of n + 2 linear
+ // equations depending on the Nordsieck vector [s2 ... sk rk], so s2 to sk correspond
+ // to the appropriately truncated Taylor expansion, and rk is the Taylor remainder.
+ // The goal is to have s2 to sk as accurate as possible considering the fact the sum is
+ // truncated and we don't want the error terms to be included in s2 ... sk, so we need
+ // to solve also for the remainder
+ final T[][] a = MathArrays.buildArray(field, c1.length + 1, c1.length + 1);
+ final T[][] b = MathArrays.buildArray(field, c1.length + 1, y[0].length);
+ final T[] y0 = y[0];
+ final T[] yDot0 = yDot[0];
+ for (int i = 1; i < y.length; ++i) {
+
+ final T di = t[i].subtract(t[0]);
+ final T ratio = di.divide(h);
+ T dikM1Ohk = h.reciprocal();
+
+ // linear coefficients of equations
+ // y(ti) - y(t0) - di y'(t0) and y'(ti) - y'(t0)
+ final T[] aI = a[2 * i - 2];
+ final T[] aDotI = (2 * i - 1) < a.length ? a[2 * i - 1] : null;
+ for (int j = 0; j < aI.length; ++j) {
+ dikM1Ohk = dikM1Ohk.multiply(ratio);
+ aI[j] = di.multiply(dikM1Ohk);
+ if (aDotI != null) {
+ aDotI[j] = dikM1Ohk.multiply(j + 2);
+ }
+ }
+
+ // expected value of the previous equations
+ final T[] yI = y[i];
+ final T[] yDotI = yDot[i];
+ final T[] bI = b[2 * i - 2];
+ final T[] bDotI = (2 * i - 1) < b.length ? b[2 * i - 1] : null;
+ for (int j = 0; j < yI.length; ++j) {
+ bI[j] = yI[j].subtract(y0[j]).subtract(di.multiply(yDot0[j]));
+ if (bDotI != null) {
+ bDotI[j] = yDotI[j].subtract(yDot0[j]);
+ }
+ }
+
+ }
+
+ // solve the linear system to get the best estimate of the Nordsieck vector [s2 ... sk],
+ // with the additional terms s(k+1) and c grabbing the parts after the truncated Taylor expansion
+ final FieldLUDecomposition<T> decomposition = new FieldLUDecomposition<T>(new Array2DRowFieldMatrix<T>(a, false));
+ final FieldMatrix<T> x = decomposition.getSolver().solve(new Array2DRowFieldMatrix<T>(b, false));
+
+ // extract just the Nordsieck vector [s2 ... sk]
+ final Array2DRowFieldMatrix<T> truncatedX =
+ new Array2DRowFieldMatrix<T>(field, x.getRowDimension() - 1, x.getColumnDimension());
+ for (int i = 0; i < truncatedX.getRowDimension(); ++i) {
+ for (int j = 0; j < truncatedX.getColumnDimension(); ++j) {
+ truncatedX.setEntry(i, j, x.getEntry(i, j));
+ }
+ }
+ return truncatedX;
+
+ }
+
+ /** Update the high order scaled derivatives for Adams integrators (phase 1).
+ * <p>The complete update of high order derivatives has a form similar to:
+ * <pre>
+ * r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub>
+ * </pre>
+ * this method computes the P<sup>-1</sup> A P r<sub>n</sub> part.</p>
+ * @param highOrder high order scaled derivatives
+ * (h<sup>2</sup>/2 y'', ... h<sup>k</sup>/k! y(k))
+ * @return updated high order derivatives
+ * @see #updateHighOrderDerivativesPhase2(RealFieldElement[], RealFieldElement[], Array2DRowFieldMatrix)
+ */
+ public Array2DRowFieldMatrix<T> updateHighOrderDerivativesPhase1(final Array2DRowFieldMatrix<T> highOrder) {
+ return update.multiply(highOrder);
+ }
+
+ /** Update the high order scaled derivatives Adams integrators (phase 2).
+ * <p>The complete update of high order derivatives has a form similar to:
+ * <pre>
+ * r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub>
+ * </pre>
+ * this method computes the (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u part.</p>
+ * <p>Phase 1 of the update must already have been performed.</p>
+ * @param start first order scaled derivatives at step start
+ * @param end first order scaled derivatives at step end
+ * @param highOrder high order scaled derivatives, will be modified
+ * (h<sup>2</sup>/2 y'', ... h<sup>k</sup>/k! y(k))
+ * @see #updateHighOrderDerivativesPhase1(Array2DRowFieldMatrix)
+ */
+ public void updateHighOrderDerivativesPhase2(final T[] start,
+ final T[] end,
+ final Array2DRowFieldMatrix<T> highOrder) {
+ final T[][] data = highOrder.getDataRef();
+ for (int i = 0; i < data.length; ++i) {
+ final T[] dataI = data[i];
+ final T c1I = c1[i];
+ for (int j = 0; j < dataI.length; ++j) {
+ dataI[j] = dataI[j].add(c1I.multiply(start[j].subtract(end[j])));
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsNordsieckTransformer.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsNordsieckTransformer.java
new file mode 100644
index 0000000..1c54b17
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdamsNordsieckTransformer.java
@@ -0,0 +1,361 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.math3.fraction.BigFraction;
+import org.apache.commons.math3.linear.Array2DRowFieldMatrix;
+import org.apache.commons.math3.linear.Array2DRowRealMatrix;
+import org.apache.commons.math3.linear.ArrayFieldVector;
+import org.apache.commons.math3.linear.FieldDecompositionSolver;
+import org.apache.commons.math3.linear.FieldLUDecomposition;
+import org.apache.commons.math3.linear.FieldMatrix;
+import org.apache.commons.math3.linear.MatrixUtils;
+import org.apache.commons.math3.linear.QRDecomposition;
+import org.apache.commons.math3.linear.RealMatrix;
+
+/** Transformer to Nordsieck vectors for Adams integrators.
+ * <p>This class is used by {@link AdamsBashforthIntegrator Adams-Bashforth} and
+ * {@link AdamsMoultonIntegrator Adams-Moulton} integrators to convert between
+ * classical representation with several previous first derivatives and Nordsieck
+ * representation with higher order scaled derivatives.</p>
+ *
+ * <p>We define scaled derivatives s<sub>i</sub>(n) at step n as:
+ * <pre>
+ * s<sub>1</sub>(n) = h y'<sub>n</sub> for first derivative
+ * s<sub>2</sub>(n) = h<sup>2</sup>/2 y''<sub>n</sub> for second derivative
+ * s<sub>3</sub>(n) = h<sup>3</sup>/6 y'''<sub>n</sub> for third derivative
+ * ...
+ * s<sub>k</sub>(n) = h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub> for k<sup>th</sup> derivative
+ * </pre></p>
+ *
+ * <p>With the previous definition, the classical representation of multistep methods
+ * uses first derivatives only, i.e. it handles y<sub>n</sub>, s<sub>1</sub>(n) and
+ * q<sub>n</sub> where q<sub>n</sub> is defined as:
+ * <pre>
+ * q<sub>n</sub> = [ s<sub>1</sub>(n-1) s<sub>1</sub>(n-2) ... s<sub>1</sub>(n-(k-1)) ]<sup>T</sup>
+ * </pre>
+ * (we omit the k index in the notation for clarity).</p>
+ *
+ * <p>Another possible representation uses the Nordsieck vector with
+ * higher degrees scaled derivatives all taken at the same step, i.e it handles y<sub>n</sub>,
+ * s<sub>1</sub>(n) and r<sub>n</sub>) where r<sub>n</sub> is defined as:
+ * <pre>
+ * r<sub>n</sub> = [ s<sub>2</sub>(n), s<sub>3</sub>(n) ... s<sub>k</sub>(n) ]<sup>T</sup>
+ * </pre>
+ * (here again we omit the k index in the notation for clarity)
+ * </p>
+ *
+ * <p>Taylor series formulas show that for any index offset i, s<sub>1</sub>(n-i) can be
+ * computed from s<sub>1</sub>(n), s<sub>2</sub>(n) ... s<sub>k</sub>(n), the formula being exact
+ * for degree k polynomials.
+ * <pre>
+ * s<sub>1</sub>(n-i) = s<sub>1</sub>(n) + &sum;<sub>j&gt;0</sub> (j+1) (-i)<sup>j</sup> s<sub>j+1</sub>(n)
+ * </pre>
+ * The previous formula can be used with several values for i to compute the transform between
+ * classical representation and Nordsieck vector at step end. The transform between r<sub>n</sub>
+ * and q<sub>n</sub> resulting from the Taylor series formulas above is:
+ * <pre>
+ * q<sub>n</sub> = s<sub>1</sub>(n) u + P r<sub>n</sub>
+ * </pre>
+ * where u is the [ 1 1 ... 1 ]<sup>T</sup> vector and P is the (k-1)&times;(k-1) matrix built
+ * with the (j+1) (-i)<sup>j</sup> terms with i being the row number starting from 1 and j being
+ * the column number starting from 1:
+ * <pre>
+ * [ -2 3 -4 5 ... ]
+ * [ -4 12 -32 80 ... ]
+ * P = [ -6 27 -108 405 ... ]
+ * [ -8 48 -256 1280 ... ]
+ * [ ... ]
+ * </pre></p>
+ *
+ * <p>Changing -i into +i in the formula above can be used to compute a similar transform between
+ * classical representation and Nordsieck vector at step start. The resulting matrix is simply
+ * the absolute value of matrix P.</p>
+ *
+ * <p>For {@link AdamsBashforthIntegrator Adams-Bashforth} method, the Nordsieck vector
+ * at step n+1 is computed from the Nordsieck vector at step n as follows:
+ * <ul>
+ * <li>y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n) + u<sup>T</sup> r<sub>n</sub></li>
+ * <li>s<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, y<sub>n+1</sub>)</li>
+ * <li>r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub></li>
+ * </ul>
+ * where A is a rows shifting matrix (the lower left part is an identity matrix):
+ * <pre>
+ * [ 0 0 ... 0 0 | 0 ]
+ * [ ---------------+---]
+ * [ 1 0 ... 0 0 | 0 ]
+ * A = [ 0 1 ... 0 0 | 0 ]
+ * [ ... | 0 ]
+ * [ 0 0 ... 1 0 | 0 ]
+ * [ 0 0 ... 0 1 | 0 ]
+ * </pre></p>
+ *
+ * <p>For {@link AdamsMoultonIntegrator Adams-Moulton} method, the predicted Nordsieck vector
+ * at step n+1 is computed from the Nordsieck vector at step n as follows:
+ * <ul>
+ * <li>Y<sub>n+1</sub> = y<sub>n</sub> + s<sub>1</sub>(n) + u<sup>T</sup> r<sub>n</sub></li>
+ * <li>S<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, Y<sub>n+1</sub>)</li>
+ * <li>R<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub></li>
+ * </ul>
+ * From this predicted vector, the corrected vector is computed as follows:
+ * <ul>
+ * <li>y<sub>n+1</sub> = y<sub>n</sub> + S<sub>1</sub>(n+1) + [ -1 +1 -1 +1 ... &plusmn;1 ] r<sub>n+1</sub></li>
+ * <li>s<sub>1</sub>(n+1) = h f(t<sub>n+1</sub>, y<sub>n+1</sub>)</li>
+ * <li>r<sub>n+1</sub> = R<sub>n+1</sub> + (s<sub>1</sub>(n+1) - S<sub>1</sub>(n+1)) P<sup>-1</sup> u</li>
+ * </ul>
+ * where the upper case Y<sub>n+1</sub>, S<sub>1</sub>(n+1) and R<sub>n+1</sub> represent the
+ * predicted states whereas the lower case y<sub>n+1</sub>, s<sub>n+1</sub> and r<sub>n+1</sub>
+ * represent the corrected states.</p>
+ *
+ * <p>We observe that both methods use similar update formulas. In both cases a P<sup>-1</sup>u
+ * vector and a P<sup>-1</sup> A P matrix are used that do not depend on the state,
+ * they only depend on k. This class handles these transformations.</p>
+ *
+ * @since 2.0
+ */
+public class AdamsNordsieckTransformer {
+
+ /** Cache for already computed coefficients. */
+ private static final Map<Integer, AdamsNordsieckTransformer> CACHE =
+ new HashMap<Integer, AdamsNordsieckTransformer>();
+
+ /** Update matrix for the higher order derivatives h<sup>2</sup>/2 y'', h<sup>3</sup>/6 y''' ... */
+ private final Array2DRowRealMatrix update;
+
+ /** Update coefficients of the higher order derivatives wrt y'. */
+ private final double[] c1;
+
+ /** Simple constructor.
+ * @param n number of steps of the multistep method
+ * (excluding the one being computed)
+ */
+ private AdamsNordsieckTransformer(final int n) {
+
+ final int rows = n - 1;
+
+ // compute exact coefficients
+ FieldMatrix<BigFraction> bigP = buildP(rows);
+ FieldDecompositionSolver<BigFraction> pSolver =
+ new FieldLUDecomposition<BigFraction>(bigP).getSolver();
+
+ BigFraction[] u = new BigFraction[rows];
+ Arrays.fill(u, BigFraction.ONE);
+ BigFraction[] bigC1 = pSolver.solve(new ArrayFieldVector<BigFraction>(u, false)).toArray();
+
+ // update coefficients are computed by combining transform from
+ // Nordsieck to multistep, then shifting rows to represent step advance
+ // then applying inverse transform
+ BigFraction[][] shiftedP = bigP.getData();
+ for (int i = shiftedP.length - 1; i > 0; --i) {
+ // shift rows
+ shiftedP[i] = shiftedP[i - 1];
+ }
+ shiftedP[0] = new BigFraction[rows];
+ Arrays.fill(shiftedP[0], BigFraction.ZERO);
+ FieldMatrix<BigFraction> bigMSupdate =
+ pSolver.solve(new Array2DRowFieldMatrix<BigFraction>(shiftedP, false));
+
+ // convert coefficients to double
+ update = MatrixUtils.bigFractionMatrixToRealMatrix(bigMSupdate);
+ c1 = new double[rows];
+ for (int i = 0; i < rows; ++i) {
+ c1[i] = bigC1[i].doubleValue();
+ }
+
+ }
+
+ /** Get the Nordsieck transformer for a given number of steps.
+ * @param nSteps number of steps of the multistep method
+ * (excluding the one being computed)
+ * @return Nordsieck transformer for the specified number of steps
+ */
+ public static AdamsNordsieckTransformer getInstance(final int nSteps) {
+ synchronized(CACHE) {
+ AdamsNordsieckTransformer t = CACHE.get(nSteps);
+ if (t == null) {
+ t = new AdamsNordsieckTransformer(nSteps);
+ CACHE.put(nSteps, t);
+ }
+ return t;
+ }
+ }
+
+ /** Get the number of steps of the method
+ * (excluding the one being computed).
+ * @return number of steps of the method
+ * (excluding the one being computed)
+ * @deprecated as of 3.6, this method is not used anymore
+ */
+ @Deprecated
+ public int getNSteps() {
+ return c1.length;
+ }
+
+ /** Build the P matrix.
+ * <p>The P matrix general terms are shifted (j+1) (-i)<sup>j</sup> terms
+ * with i being the row number starting from 1 and j being the column
+ * number starting from 1:
+ * <pre>
+ * [ -2 3 -4 5 ... ]
+ * [ -4 12 -32 80 ... ]
+ * P = [ -6 27 -108 405 ... ]
+ * [ -8 48 -256 1280 ... ]
+ * [ ... ]
+ * </pre></p>
+ * @param rows number of rows of the matrix
+ * @return P matrix
+ */
+ private FieldMatrix<BigFraction> buildP(final int rows) {
+
+ final BigFraction[][] pData = new BigFraction[rows][rows];
+
+ for (int i = 1; i <= pData.length; ++i) {
+ // build the P matrix elements from Taylor series formulas
+ final BigFraction[] pI = pData[i - 1];
+ final int factor = -i;
+ int aj = factor;
+ for (int j = 1; j <= pI.length; ++j) {
+ pI[j - 1] = new BigFraction(aj * (j + 1));
+ aj *= factor;
+ }
+ }
+
+ return new Array2DRowFieldMatrix<BigFraction>(pData, false);
+
+ }
+
+ /** Initialize the high order scaled derivatives at step start.
+ * @param h step size to use for scaling
+ * @param t first steps times
+ * @param y first steps states
+ * @param yDot first steps derivatives
+ * @return Nordieck vector at start of first step (h<sup>2</sup>/2 y''<sub>n</sub>,
+ * h<sup>3</sup>/6 y'''<sub>n</sub> ... h<sup>k</sup>/k! y<sup>(k)</sup><sub>n</sub>)
+ */
+
+ public Array2DRowRealMatrix initializeHighOrderDerivatives(final double h, final double[] t,
+ final double[][] y,
+ final double[][] yDot) {
+
+ // using Taylor series with di = ti - t0, we get:
+ // y(ti) - y(t0) - di y'(t0) = di^2 / h^2 s2 + ... + di^k / h^k sk + O(h^k)
+ // y'(ti) - y'(t0) = 2 di / h^2 s2 + ... + k di^(k-1) / h^k sk + O(h^(k-1))
+ // we write these relations for i = 1 to i= 1+n/2 as a set of n + 2 linear
+ // equations depending on the Nordsieck vector [s2 ... sk rk], so s2 to sk correspond
+ // to the appropriately truncated Taylor expansion, and rk is the Taylor remainder.
+ // The goal is to have s2 to sk as accurate as possible considering the fact the sum is
+ // truncated and we don't want the error terms to be included in s2 ... sk, so we need
+ // to solve also for the remainder
+ final double[][] a = new double[c1.length + 1][c1.length + 1];
+ final double[][] b = new double[c1.length + 1][y[0].length];
+ final double[] y0 = y[0];
+ final double[] yDot0 = yDot[0];
+ for (int i = 1; i < y.length; ++i) {
+
+ final double di = t[i] - t[0];
+ final double ratio = di / h;
+ double dikM1Ohk = 1 / h;
+
+ // linear coefficients of equations
+ // y(ti) - y(t0) - di y'(t0) and y'(ti) - y'(t0)
+ final double[] aI = a[2 * i - 2];
+ final double[] aDotI = (2 * i - 1) < a.length ? a[2 * i - 1] : null;
+ for (int j = 0; j < aI.length; ++j) {
+ dikM1Ohk *= ratio;
+ aI[j] = di * dikM1Ohk;
+ if (aDotI != null) {
+ aDotI[j] = (j + 2) * dikM1Ohk;
+ }
+ }
+
+ // expected value of the previous equations
+ final double[] yI = y[i];
+ final double[] yDotI = yDot[i];
+ final double[] bI = b[2 * i - 2];
+ final double[] bDotI = (2 * i - 1) < b.length ? b[2 * i - 1] : null;
+ for (int j = 0; j < yI.length; ++j) {
+ bI[j] = yI[j] - y0[j] - di * yDot0[j];
+ if (bDotI != null) {
+ bDotI[j] = yDotI[j] - yDot0[j];
+ }
+ }
+
+ }
+
+ // solve the linear system to get the best estimate of the Nordsieck vector [s2 ... sk],
+ // with the additional terms s(k+1) and c grabbing the parts after the truncated Taylor expansion
+ final QRDecomposition decomposition = new QRDecomposition(new Array2DRowRealMatrix(a, false));
+ final RealMatrix x = decomposition.getSolver().solve(new Array2DRowRealMatrix(b, false));
+
+ // extract just the Nordsieck vector [s2 ... sk]
+ final Array2DRowRealMatrix truncatedX = new Array2DRowRealMatrix(x.getRowDimension() - 1, x.getColumnDimension());
+ for (int i = 0; i < truncatedX.getRowDimension(); ++i) {
+ for (int j = 0; j < truncatedX.getColumnDimension(); ++j) {
+ truncatedX.setEntry(i, j, x.getEntry(i, j));
+ }
+ }
+ return truncatedX;
+
+ }
+
+ /** Update the high order scaled derivatives for Adams integrators (phase 1).
+ * <p>The complete update of high order derivatives has a form similar to:
+ * <pre>
+ * r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub>
+ * </pre>
+ * this method computes the P<sup>-1</sup> A P r<sub>n</sub> part.</p>
+ * @param highOrder high order scaled derivatives
+ * (h<sup>2</sup>/2 y'', ... h<sup>k</sup>/k! y(k))
+ * @return updated high order derivatives
+ * @see #updateHighOrderDerivativesPhase2(double[], double[], Array2DRowRealMatrix)
+ */
+ public Array2DRowRealMatrix updateHighOrderDerivativesPhase1(final Array2DRowRealMatrix highOrder) {
+ return update.multiply(highOrder);
+ }
+
+ /** Update the high order scaled derivatives Adams integrators (phase 2).
+ * <p>The complete update of high order derivatives has a form similar to:
+ * <pre>
+ * r<sub>n+1</sub> = (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u + P<sup>-1</sup> A P r<sub>n</sub>
+ * </pre>
+ * this method computes the (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u part.</p>
+ * <p>Phase 1 of the update must already have been performed.</p>
+ * @param start first order scaled derivatives at step start
+ * @param end first order scaled derivatives at step end
+ * @param highOrder high order scaled derivatives, will be modified
+ * (h<sup>2</sup>/2 y'', ... h<sup>k</sup>/k! y(k))
+ * @see #updateHighOrderDerivativesPhase1(Array2DRowRealMatrix)
+ */
+ public void updateHighOrderDerivativesPhase2(final double[] start,
+ final double[] end,
+ final Array2DRowRealMatrix highOrder) {
+ final double[][] data = highOrder.getDataRef();
+ for (int i = 0; i < data.length; ++i) {
+ final double[] dataI = data[i];
+ final double c1I = c1[i];
+ for (int j = 0; j < dataI.length; ++j) {
+ dataI[j] += c1I * (start[j] - end[j]);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdaptiveStepsizeFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdaptiveStepsizeFieldIntegrator.java
new file mode 100644
index 0000000..c8e592b
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdaptiveStepsizeFieldIntegrator.java
@@ -0,0 +1,366 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.ode.AbstractFieldIntegrator;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEState;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.FastMath;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * This abstract class holds the common part of all adaptive
+ * stepsize integrators for Ordinary Differential Equations.
+ *
+ * <p>These algorithms perform integration with stepsize control, which
+ * means the user does not specify the integration step but rather a
+ * tolerance on error. The error threshold is computed as
+ * <pre>
+ * threshold_i = absTol_i + relTol_i * max (abs (ym), abs (ym+1))
+ * </pre>
+ * where absTol_i is the absolute tolerance for component i of the
+ * state vector and relTol_i is the relative tolerance for the same
+ * component. The user can also use only two scalar values absTol and
+ * relTol which will be used for all components.
+ * </p>
+ * <p>
+ * Note that <em>only</em> the {@link FieldODEState#getState() main part}
+ * of the state vector is used for stepsize control. The {@link
+ * FieldODEState#getSecondaryState(int) secondary parts} of the state
+ * vector are explicitly ignored for stepsize control.
+ * </p>
+ *
+ * <p>If the estimated error for ym+1 is such that
+ * <pre>
+ * sqrt((sum (errEst_i / threshold_i)^2 ) / n) < 1
+ * </pre>
+ *
+ * (where n is the main set dimension) then the step is accepted,
+ * otherwise the step is rejected and a new attempt is made with a new
+ * stepsize.</p>
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ *
+ */
+
+public abstract class AdaptiveStepsizeFieldIntegrator<T extends RealFieldElement<T>>
+ extends AbstractFieldIntegrator<T> {
+
+ /** Allowed absolute scalar error. */
+ protected double scalAbsoluteTolerance;
+
+ /** Allowed relative scalar error. */
+ protected double scalRelativeTolerance;
+
+ /** Allowed absolute vectorial error. */
+ protected double[] vecAbsoluteTolerance;
+
+ /** Allowed relative vectorial error. */
+ protected double[] vecRelativeTolerance;
+
+ /** Main set dimension. */
+ protected int mainSetDimension;
+
+ /** User supplied initial step. */
+ private T initialStep;
+
+ /** Minimal step. */
+ private T minStep;
+
+ /** Maximal step. */
+ private T maxStep;
+
+ /** Build an integrator with the given stepsize bounds.
+ * The default step handler does nothing.
+ * @param field field to which the time and state vector elements belong
+ * @param name name of the method
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ public AdaptiveStepsizeFieldIntegrator(final Field<T> field, final String name,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+
+ super(field, name);
+ setStepSizeControl(minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+ resetInternalState();
+
+ }
+
+ /** Build an integrator with the given stepsize bounds.
+ * The default step handler does nothing.
+ * @param field field to which the time and state vector elements belong
+ * @param name name of the method
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ public AdaptiveStepsizeFieldIntegrator(final Field<T> field, final String name,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+
+ super(field, name);
+ setStepSizeControl(minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+ resetInternalState();
+
+ }
+
+ /** Set the adaptive step size control parameters.
+ * <p>
+ * A side effect of this method is to also reset the initial
+ * step so it will be automatically computed by the integrator
+ * if {@link #setInitialStepSize(RealFieldElement) setInitialStepSize}
+ * is not called by the user.
+ * </p>
+ * @param minimalStep minimal step (must be positive even for backward
+ * integration), the last step can be smaller than this
+ * @param maximalStep maximal step (must be positive even for backward
+ * integration)
+ * @param absoluteTolerance allowed absolute error
+ * @param relativeTolerance allowed relative error
+ */
+ public void setStepSizeControl(final double minimalStep, final double maximalStep,
+ final double absoluteTolerance,
+ final double relativeTolerance) {
+
+ minStep = getField().getZero().add(FastMath.abs(minimalStep));
+ maxStep = getField().getZero().add(FastMath.abs(maximalStep));
+ initialStep = getField().getOne().negate();
+
+ scalAbsoluteTolerance = absoluteTolerance;
+ scalRelativeTolerance = relativeTolerance;
+ vecAbsoluteTolerance = null;
+ vecRelativeTolerance = null;
+
+ }
+
+ /** Set the adaptive step size control parameters.
+ * <p>
+ * A side effect of this method is to also reset the initial
+ * step so it will be automatically computed by the integrator
+ * if {@link #setInitialStepSize(RealFieldElement) setInitialStepSize}
+ * is not called by the user.
+ * </p>
+ * @param minimalStep minimal step (must be positive even for backward
+ * integration), the last step can be smaller than this
+ * @param maximalStep maximal step (must be positive even for backward
+ * integration)
+ * @param absoluteTolerance allowed absolute error
+ * @param relativeTolerance allowed relative error
+ */
+ public void setStepSizeControl(final double minimalStep, final double maximalStep,
+ final double[] absoluteTolerance,
+ final double[] relativeTolerance) {
+
+ minStep = getField().getZero().add(FastMath.abs(minimalStep));
+ maxStep = getField().getZero().add(FastMath.abs(maximalStep));
+ initialStep = getField().getOne().negate();
+
+ scalAbsoluteTolerance = 0;
+ scalRelativeTolerance = 0;
+ vecAbsoluteTolerance = absoluteTolerance.clone();
+ vecRelativeTolerance = relativeTolerance.clone();
+
+ }
+
+ /** Set the initial step size.
+ * <p>This method allows the user to specify an initial positive
+ * step size instead of letting the integrator guess it by
+ * itself. If this method is not called before integration is
+ * started, the initial step size will be estimated by the
+ * integrator.</p>
+ * @param initialStepSize initial step size to use (must be positive even
+ * for backward integration ; providing a negative value or a value
+ * outside of the min/max step interval will lead the integrator to
+ * ignore the value and compute the initial step size by itself)
+ */
+ public void setInitialStepSize(final T initialStepSize) {
+ if (initialStepSize.subtract(minStep).getReal() < 0 ||
+ initialStepSize.subtract(maxStep).getReal() > 0) {
+ initialStep = getField().getOne().negate();
+ } else {
+ initialStep = initialStepSize;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void sanityChecks(final FieldODEState<T> eqn, final T t)
+ throws DimensionMismatchException, NumberIsTooSmallException {
+
+ super.sanityChecks(eqn, t);
+
+ mainSetDimension = eqn.getStateDimension();
+
+ if (vecAbsoluteTolerance != null && vecAbsoluteTolerance.length != mainSetDimension) {
+ throw new DimensionMismatchException(mainSetDimension, vecAbsoluteTolerance.length);
+ }
+
+ if (vecRelativeTolerance != null && vecRelativeTolerance.length != mainSetDimension) {
+ throw new DimensionMismatchException(mainSetDimension, vecRelativeTolerance.length);
+ }
+
+ }
+
+ /** Initialize the integration step.
+ * @param forward forward integration indicator
+ * @param order order of the method
+ * @param scale scaling vector for the state vector (can be shorter than state vector)
+ * @param state0 state at integration start time
+ * @param mapper mapper for all the equations
+ * @return first integration step
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception DimensionMismatchException if arrays dimensions do not match equations settings
+ */
+ public T initializeStep(final boolean forward, final int order, final T[] scale,
+ final FieldODEStateAndDerivative<T> state0,
+ final FieldEquationsMapper<T> mapper)
+ throws MaxCountExceededException, DimensionMismatchException {
+
+ if (initialStep.getReal() > 0) {
+ // use the user provided value
+ return forward ? initialStep : initialStep.negate();
+ }
+
+ // very rough first guess : h = 0.01 * ||y/scale|| / ||y'/scale||
+ // this guess will be used to perform an Euler step
+ final T[] y0 = mapper.mapState(state0);
+ final T[] yDot0 = mapper.mapDerivative(state0);
+ T yOnScale2 = getField().getZero();
+ T yDotOnScale2 = getField().getZero();
+ for (int j = 0; j < scale.length; ++j) {
+ final T ratio = y0[j].divide(scale[j]);
+ yOnScale2 = yOnScale2.add(ratio.multiply(ratio));
+ final T ratioDot = yDot0[j].divide(scale[j]);
+ yDotOnScale2 = yDotOnScale2.add(ratioDot.multiply(ratioDot));
+ }
+
+ T h = (yOnScale2.getReal() < 1.0e-10 || yDotOnScale2.getReal() < 1.0e-10) ?
+ getField().getZero().add(1.0e-6) :
+ yOnScale2.divide(yDotOnScale2).sqrt().multiply(0.01);
+ if (! forward) {
+ h = h.negate();
+ }
+
+ // perform an Euler step using the preceding rough guess
+ final T[] y1 = MathArrays.buildArray(getField(), y0.length);
+ for (int j = 0; j < y0.length; ++j) {
+ y1[j] = y0[j].add(yDot0[j].multiply(h));
+ }
+ final T[] yDot1 = computeDerivatives(state0.getTime().add(h), y1);
+
+ // estimate the second derivative of the solution
+ T yDDotOnScale = getField().getZero();
+ for (int j = 0; j < scale.length; ++j) {
+ final T ratioDotDot = yDot1[j].subtract(yDot0[j]).divide(scale[j]);
+ yDDotOnScale = yDDotOnScale.add(ratioDotDot.multiply(ratioDotDot));
+ }
+ yDDotOnScale = yDDotOnScale.sqrt().divide(h);
+
+ // step size is computed such that
+ // h^order * max (||y'/tol||, ||y''/tol||) = 0.01
+ final T maxInv2 = MathUtils.max(yDotOnScale2.sqrt(), yDDotOnScale);
+ final T h1 = maxInv2.getReal() < 1.0e-15 ?
+ MathUtils.max(getField().getZero().add(1.0e-6), h.abs().multiply(0.001)) :
+ maxInv2.multiply(100).reciprocal().pow(1.0 / order);
+ h = MathUtils.min(h.abs().multiply(100), h1);
+ h = MathUtils.max(h, state0.getTime().abs().multiply(1.0e-12)); // avoids cancellation when computing t1 - t0
+ h = MathUtils.max(minStep, MathUtils.min(maxStep, h));
+ if (! forward) {
+ h = h.negate();
+ }
+
+ return h;
+
+ }
+
+ /** Filter the integration step.
+ * @param h signed step
+ * @param forward forward integration indicator
+ * @param acceptSmall if true, steps smaller than the minimal value
+ * are silently increased up to this value, if false such small
+ * steps generate an exception
+ * @return a bounded integration step (h if no bound is reach, or a bounded value)
+ * @exception NumberIsTooSmallException if the step is too small and acceptSmall is false
+ */
+ protected T filterStep(final T h, final boolean forward, final boolean acceptSmall)
+ throws NumberIsTooSmallException {
+
+ T filteredH = h;
+ if (h.abs().subtract(minStep).getReal() < 0) {
+ if (acceptSmall) {
+ filteredH = forward ? minStep : minStep.negate();
+ } else {
+ throw new NumberIsTooSmallException(LocalizedFormats.MINIMAL_STEPSIZE_REACHED_DURING_INTEGRATION,
+ h.abs().getReal(), minStep.getReal(), true);
+ }
+ }
+
+ if (filteredH.subtract(maxStep).getReal() > 0) {
+ filteredH = maxStep;
+ } else if (filteredH.add(maxStep).getReal() < 0) {
+ filteredH = maxStep.negate();
+ }
+
+ return filteredH;
+
+ }
+
+ /** Reset internal state to dummy values. */
+ protected void resetInternalState() {
+ setStepStart(null);
+ setStepSize(minStep.multiply(maxStep).sqrt());
+ }
+
+ /** Get the minimal step.
+ * @return minimal step
+ */
+ public T getMinStep() {
+ return minStep;
+ }
+
+ /** Get the maximal step.
+ * @return maximal step
+ */
+ public T getMaxStep() {
+ return maxStep;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/AdaptiveStepsizeIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdaptiveStepsizeIntegrator.java
new file mode 100644
index 0000000..ab79bbf
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/AdaptiveStepsizeIntegrator.java
@@ -0,0 +1,375 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.exception.util.LocalizedFormats;
+import org.apache.commons.math3.ode.AbstractIntegrator;
+import org.apache.commons.math3.ode.ExpandableStatefulODE;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This abstract class holds the common part of all adaptive
+ * stepsize integrators for Ordinary Differential Equations.
+ *
+ * <p>These algorithms perform integration with stepsize control, which
+ * means the user does not specify the integration step but rather a
+ * tolerance on error. The error threshold is computed as
+ * <pre>
+ * threshold_i = absTol_i + relTol_i * max (abs (ym), abs (ym+1))
+ * </pre>
+ * where absTol_i is the absolute tolerance for component i of the
+ * state vector and relTol_i is the relative tolerance for the same
+ * component. The user can also use only two scalar values absTol and
+ * relTol which will be used for all components.
+ * </p>
+ * <p>
+ * If the Ordinary Differential Equations is an {@link ExpandableStatefulODE
+ * extended ODE} rather than a {@link
+ * org.apache.commons.math3.ode.FirstOrderDifferentialEquations basic ODE}, then
+ * <em>only</em> the {@link ExpandableStatefulODE#getPrimaryState() primary part}
+ * of the state vector is used for stepsize control, not the complete state vector.
+ * </p>
+ *
+ * <p>If the estimated error for ym+1 is such that
+ * <pre>
+ * sqrt((sum (errEst_i / threshold_i)^2 ) / n) < 1
+ * </pre>
+ *
+ * (where n is the main set dimension) then the step is accepted,
+ * otherwise the step is rejected and a new attempt is made with a new
+ * stepsize.</p>
+ *
+ * @since 1.2
+ *
+ */
+
+public abstract class AdaptiveStepsizeIntegrator
+ extends AbstractIntegrator {
+
+ /** Allowed absolute scalar error. */
+ protected double scalAbsoluteTolerance;
+
+ /** Allowed relative scalar error. */
+ protected double scalRelativeTolerance;
+
+ /** Allowed absolute vectorial error. */
+ protected double[] vecAbsoluteTolerance;
+
+ /** Allowed relative vectorial error. */
+ protected double[] vecRelativeTolerance;
+
+ /** Main set dimension. */
+ protected int mainSetDimension;
+
+ /** User supplied initial step. */
+ private double initialStep;
+
+ /** Minimal step. */
+ private double minStep;
+
+ /** Maximal step. */
+ private double maxStep;
+
+ /** Build an integrator with the given stepsize bounds.
+ * The default step handler does nothing.
+ * @param name name of the method
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ public AdaptiveStepsizeIntegrator(final String name,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+
+ super(name);
+ setStepSizeControl(minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+ resetInternalState();
+
+ }
+
+ /** Build an integrator with the given stepsize bounds.
+ * The default step handler does nothing.
+ * @param name name of the method
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ public AdaptiveStepsizeIntegrator(final String name,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+
+ super(name);
+ setStepSizeControl(minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+ resetInternalState();
+
+ }
+
+ /** Set the adaptive step size control parameters.
+ * <p>
+ * A side effect of this method is to also reset the initial
+ * step so it will be automatically computed by the integrator
+ * if {@link #setInitialStepSize(double) setInitialStepSize}
+ * is not called by the user.
+ * </p>
+ * @param minimalStep minimal step (must be positive even for backward
+ * integration), the last step can be smaller than this
+ * @param maximalStep maximal step (must be positive even for backward
+ * integration)
+ * @param absoluteTolerance allowed absolute error
+ * @param relativeTolerance allowed relative error
+ */
+ public void setStepSizeControl(final double minimalStep, final double maximalStep,
+ final double absoluteTolerance,
+ final double relativeTolerance) {
+
+ minStep = FastMath.abs(minimalStep);
+ maxStep = FastMath.abs(maximalStep);
+ initialStep = -1;
+
+ scalAbsoluteTolerance = absoluteTolerance;
+ scalRelativeTolerance = relativeTolerance;
+ vecAbsoluteTolerance = null;
+ vecRelativeTolerance = null;
+
+ }
+
+ /** Set the adaptive step size control parameters.
+ * <p>
+ * A side effect of this method is to also reset the initial
+ * step so it will be automatically computed by the integrator
+ * if {@link #setInitialStepSize(double) setInitialStepSize}
+ * is not called by the user.
+ * </p>
+ * @param minimalStep minimal step (must be positive even for backward
+ * integration), the last step can be smaller than this
+ * @param maximalStep maximal step (must be positive even for backward
+ * integration)
+ * @param absoluteTolerance allowed absolute error
+ * @param relativeTolerance allowed relative error
+ */
+ public void setStepSizeControl(final double minimalStep, final double maximalStep,
+ final double[] absoluteTolerance,
+ final double[] relativeTolerance) {
+
+ minStep = FastMath.abs(minimalStep);
+ maxStep = FastMath.abs(maximalStep);
+ initialStep = -1;
+
+ scalAbsoluteTolerance = 0;
+ scalRelativeTolerance = 0;
+ vecAbsoluteTolerance = absoluteTolerance.clone();
+ vecRelativeTolerance = relativeTolerance.clone();
+
+ }
+
+ /** Set the initial step size.
+ * <p>This method allows the user to specify an initial positive
+ * step size instead of letting the integrator guess it by
+ * itself. If this method is not called before integration is
+ * started, the initial step size will be estimated by the
+ * integrator.</p>
+ * @param initialStepSize initial step size to use (must be positive even
+ * for backward integration ; providing a negative value or a value
+ * outside of the min/max step interval will lead the integrator to
+ * ignore the value and compute the initial step size by itself)
+ */
+ public void setInitialStepSize(final double initialStepSize) {
+ if ((initialStepSize < minStep) || (initialStepSize > maxStep)) {
+ initialStep = -1.0;
+ } else {
+ initialStep = initialStepSize;
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void sanityChecks(final ExpandableStatefulODE equations, final double t)
+ throws DimensionMismatchException, NumberIsTooSmallException {
+
+ super.sanityChecks(equations, t);
+
+ mainSetDimension = equations.getPrimaryMapper().getDimension();
+
+ if ((vecAbsoluteTolerance != null) && (vecAbsoluteTolerance.length != mainSetDimension)) {
+ throw new DimensionMismatchException(mainSetDimension, vecAbsoluteTolerance.length);
+ }
+
+ if ((vecRelativeTolerance != null) && (vecRelativeTolerance.length != mainSetDimension)) {
+ throw new DimensionMismatchException(mainSetDimension, vecRelativeTolerance.length);
+ }
+
+ }
+
+ /** Initialize the integration step.
+ * @param forward forward integration indicator
+ * @param order order of the method
+ * @param scale scaling vector for the state vector (can be shorter than state vector)
+ * @param t0 start time
+ * @param y0 state vector at t0
+ * @param yDot0 first time derivative of y0
+ * @param y1 work array for a state vector
+ * @param yDot1 work array for the first time derivative of y1
+ * @return first integration step
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception DimensionMismatchException if arrays dimensions do not match equations settings
+ */
+ public double initializeStep(final boolean forward, final int order, final double[] scale,
+ final double t0, final double[] y0, final double[] yDot0,
+ final double[] y1, final double[] yDot1)
+ throws MaxCountExceededException, DimensionMismatchException {
+
+ if (initialStep > 0) {
+ // use the user provided value
+ return forward ? initialStep : -initialStep;
+ }
+
+ // very rough first guess : h = 0.01 * ||y/scale|| / ||y'/scale||
+ // this guess will be used to perform an Euler step
+ double ratio;
+ double yOnScale2 = 0;
+ double yDotOnScale2 = 0;
+ for (int j = 0; j < scale.length; ++j) {
+ ratio = y0[j] / scale[j];
+ yOnScale2 += ratio * ratio;
+ ratio = yDot0[j] / scale[j];
+ yDotOnScale2 += ratio * ratio;
+ }
+
+ double h = ((yOnScale2 < 1.0e-10) || (yDotOnScale2 < 1.0e-10)) ?
+ 1.0e-6 : (0.01 * FastMath.sqrt(yOnScale2 / yDotOnScale2));
+ if (! forward) {
+ h = -h;
+ }
+
+ // perform an Euler step using the preceding rough guess
+ for (int j = 0; j < y0.length; ++j) {
+ y1[j] = y0[j] + h * yDot0[j];
+ }
+ computeDerivatives(t0 + h, y1, yDot1);
+
+ // estimate the second derivative of the solution
+ double yDDotOnScale = 0;
+ for (int j = 0; j < scale.length; ++j) {
+ ratio = (yDot1[j] - yDot0[j]) / scale[j];
+ yDDotOnScale += ratio * ratio;
+ }
+ yDDotOnScale = FastMath.sqrt(yDDotOnScale) / h;
+
+ // step size is computed such that
+ // h^order * max (||y'/tol||, ||y''/tol||) = 0.01
+ final double maxInv2 = FastMath.max(FastMath.sqrt(yDotOnScale2), yDDotOnScale);
+ final double h1 = (maxInv2 < 1.0e-15) ?
+ FastMath.max(1.0e-6, 0.001 * FastMath.abs(h)) :
+ FastMath.pow(0.01 / maxInv2, 1.0 / order);
+ h = FastMath.min(100.0 * FastMath.abs(h), h1);
+ h = FastMath.max(h, 1.0e-12 * FastMath.abs(t0)); // avoids cancellation when computing t1 - t0
+ if (h < getMinStep()) {
+ h = getMinStep();
+ }
+ if (h > getMaxStep()) {
+ h = getMaxStep();
+ }
+ if (! forward) {
+ h = -h;
+ }
+
+ return h;
+
+ }
+
+ /** Filter the integration step.
+ * @param h signed step
+ * @param forward forward integration indicator
+ * @param acceptSmall if true, steps smaller than the minimal value
+ * are silently increased up to this value, if false such small
+ * steps generate an exception
+ * @return a bounded integration step (h if no bound is reach, or a bounded value)
+ * @exception NumberIsTooSmallException if the step is too small and acceptSmall is false
+ */
+ protected double filterStep(final double h, final boolean forward, final boolean acceptSmall)
+ throws NumberIsTooSmallException {
+
+ double filteredH = h;
+ if (FastMath.abs(h) < minStep) {
+ if (acceptSmall) {
+ filteredH = forward ? minStep : -minStep;
+ } else {
+ throw new NumberIsTooSmallException(LocalizedFormats.MINIMAL_STEPSIZE_REACHED_DURING_INTEGRATION,
+ FastMath.abs(h), minStep, true);
+ }
+ }
+
+ if (filteredH > maxStep) {
+ filteredH = maxStep;
+ } else if (filteredH < -maxStep) {
+ filteredH = -maxStep;
+ }
+
+ return filteredH;
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public abstract void integrate (ExpandableStatefulODE equations, double t)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException;
+
+ /** {@inheritDoc} */
+ @Override
+ public double getCurrentStepStart() {
+ return stepStart;
+ }
+
+ /** Reset internal state to dummy values. */
+ protected void resetInternalState() {
+ stepStart = Double.NaN;
+ stepSize = FastMath.sqrt(minStep * maxStep);
+ }
+
+ /** Get the minimal step.
+ * @return minimal step
+ */
+ public double getMinStep() {
+ return minStep;
+ }
+
+ /** Get the maximal step.
+ * @return maximal step
+ */
+ public double getMaxStep() {
+ return maxStep;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaFieldIntegrator.java
new file mode 100644
index 0000000..81e6d69
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaFieldIntegrator.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class implements the classical fourth order Runge-Kutta
+ * integrator for Ordinary Differential Equations (it is the most
+ * often used Runge-Kutta method).
+ *
+ * <p>This method is an explicit Runge-Kutta method, its Butcher-array
+ * is the following one :
+ * <pre>
+ * 0 | 0 0 0 0
+ * 1/2 | 1/2 0 0 0
+ * 1/2 | 0 1/2 0 0
+ * 1 | 0 0 1 0
+ * |--------------------
+ * | 1/6 1/3 1/3 1/6
+ * </pre>
+ * </p>
+ *
+ * @see EulerFieldIntegrator
+ * @see GillFieldIntegrator
+ * @see MidpointFieldIntegrator
+ * @see ThreeEighthesFieldIntegrator
+ * @see LutherFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public class ClassicalRungeKuttaFieldIntegrator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldIntegrator<T> {
+
+ /** Simple constructor.
+ * Build a fourth-order Runge-Kutta integrator with the given step.
+ * @param field field to which the time and state vector elements belong
+ * @param step integration step
+ */
+ public ClassicalRungeKuttaFieldIntegrator(final Field<T> field, final T step) {
+ super(field, "classical Runge-Kutta", step);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getC() {
+ final T[] c = MathArrays.buildArray(getField(), 3);
+ c[0] = getField().getOne().multiply(0.5);
+ c[1] = c[0];
+ c[2] = getField().getOne();
+ return c;
+ }
+
+ /** {@inheritDoc} */
+ public T[][] getA() {
+ final T[][] a = MathArrays.buildArray(getField(), 3, -1);
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = MathArrays.buildArray(getField(), i + 1);
+ }
+ a[0][0] = fraction(1, 2);
+ a[1][0] = getField().getZero();
+ a[1][1] = a[0][0];
+ a[2][0] = getField().getZero();
+ a[2][1] = getField().getZero();
+ a[2][2] = getField().getOne();
+ return a;
+ }
+
+ /** {@inheritDoc} */
+ public T[] getB() {
+ final T[] b = MathArrays.buildArray(getField(), 4);
+ b[0] = fraction(1, 6);
+ b[1] = fraction(1, 3);
+ b[2] = b[1];
+ b[3] = b[0];
+ return b;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected ClassicalRungeKuttaFieldStepInterpolator<T>
+ createInterpolator(final boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ return new ClassicalRungeKuttaFieldStepInterpolator<T>(getField(), forward, yDotK,
+ globalPreviousState, globalCurrentState,
+ globalPreviousState, globalCurrentState,
+ mapper);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaFieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaFieldStepInterpolator.java
new file mode 100644
index 0000000..4ad8f4e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaFieldStepInterpolator.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/**
+ * This class implements a step interpolator for the classical fourth
+ * order Runge-Kutta integrator.
+ *
+ * <p>This interpolator allows to compute dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme :
+ * <ul>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub>)
+ * + &theta; (h/6) [ (6 - 9 &theta; + 4 &theta;<sup>2</sup>) y'<sub>1</sub>
+ * + ( 6 &theta; - 4 &theta;<sup>2</sup>) (y'<sub>2</sub> + y'<sub>3</sub>)
+ * + ( -3 &theta; + 4 &theta;<sup>2</sup>) y'<sub>4</sub>
+ * ]
+ * </li>
+ * <li>Using reference point at step end:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub> + h)
+ * + (1 - &theta;) (h/6) [ (-4 &theta;^2 + 5 &theta; - 1) y'<sub>1</sub>
+ * +(4 &theta;^2 - 2 &theta; - 2) (y'<sub>2</sub> + y'<sub>3</sub>)
+ * -(4 &theta;^2 + &theta; + 1) y'<sub>4</sub>
+ * ]
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * where &theta; belongs to [0 ; 1] and where y'<sub>1</sub> to y'<sub>4</sub> are the four
+ * evaluations of the derivatives already computed during the
+ * step.</p>
+ *
+ * @see ClassicalRungeKuttaFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+class ClassicalRungeKuttaFieldStepInterpolator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldStepInterpolator<T> {
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param mapper equations mapper for the all equations
+ */
+ ClassicalRungeKuttaFieldStepInterpolator(final Field<T> field, final boolean forward,
+ final T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ super(field, forward, yDotK,
+ globalPreviousState, globalCurrentState, softPreviousState, softCurrentState,
+ mapper);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected ClassicalRungeKuttaFieldStepInterpolator<T> create(final Field<T> newField, final boolean newForward, final T[][] newYDotK,
+ final FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ final FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ final FieldODEStateAndDerivative<T> newSoftPreviousState,
+ final FieldODEStateAndDerivative<T> newSoftCurrentState,
+ final FieldEquationsMapper<T> newMapper) {
+ return new ClassicalRungeKuttaFieldStepInterpolator<T>(newField, newForward, newYDotK,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(final FieldEquationsMapper<T> mapper,
+ final T time, final T theta,
+ final T thetaH, final T oneMinusThetaH) {
+
+ final T one = time.getField().getOne();
+ final T oneMinusTheta = one.subtract(theta);
+ final T oneMinus2Theta = one.subtract(theta.multiply(2));
+ final T coeffDot1 = oneMinusTheta.multiply(oneMinus2Theta);
+ final T coeffDot23 = theta.multiply(oneMinusTheta).multiply(2);
+ final T coeffDot4 = theta.multiply(oneMinus2Theta).negate();
+ final T[] interpolatedState;
+ final T[] interpolatedDerivatives;
+
+ if (getGlobalPreviousState() != null && theta.getReal() <= 0.5) {
+ final T fourTheta2 = theta.multiply(theta).multiply(4);
+ final T s = thetaH.divide(6.0);
+ final T coeff1 = s.multiply(fourTheta2.subtract(theta.multiply(9)).add(6));
+ final T coeff23 = s.multiply(theta.multiply(6).subtract(fourTheta2));
+ final T coeff4 = s.multiply(fourTheta2.subtract(theta.multiply(3)));
+ interpolatedState = previousStateLinearCombination(coeff1, coeff23, coeff23, coeff4);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot1, coeffDot23, coeffDot23, coeffDot4);
+ } else {
+ final T fourTheta = theta.multiply(4);
+ final T s = oneMinusThetaH.divide(6);
+ final T coeff1 = s.multiply(theta.multiply(fourTheta.negate().add(5)).subtract(1));
+ final T coeff23 = s.multiply(theta.multiply(fourTheta.subtract(2)).subtract(2));
+ final T coeff4 = s.multiply(theta.multiply(fourTheta.negate().subtract(1)).subtract(1));
+ interpolatedState = currentStateLinearCombination(coeff1, coeff23, coeff23, coeff4);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot1, coeffDot23, coeffDot23, coeffDot4);
+ }
+
+ return new FieldODEStateAndDerivative<T>(time, interpolatedState, interpolatedDerivatives);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaIntegrator.java
new file mode 100644
index 0000000..ca915f1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaIntegrator.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+
+/**
+ * This class implements the classical fourth order Runge-Kutta
+ * integrator for Ordinary Differential Equations (it is the most
+ * often used Runge-Kutta method).
+ *
+ * <p>This method is an explicit Runge-Kutta method, its Butcher-array
+ * is the following one :
+ * <pre>
+ * 0 | 0 0 0 0
+ * 1/2 | 1/2 0 0 0
+ * 1/2 | 0 1/2 0 0
+ * 1 | 0 0 1 0
+ * |--------------------
+ * | 1/6 1/3 1/3 1/6
+ * </pre>
+ * </p>
+ *
+ * @see EulerIntegrator
+ * @see GillIntegrator
+ * @see MidpointIntegrator
+ * @see ThreeEighthesIntegrator
+ * @see LutherIntegrator
+ * @since 1.2
+ */
+
+public class ClassicalRungeKuttaIntegrator extends RungeKuttaIntegrator {
+
+ /** Time steps Butcher array. */
+ private static final double[] STATIC_C = {
+ 1.0 / 2.0, 1.0 / 2.0, 1.0
+ };
+
+ /** Internal weights Butcher array. */
+ private static final double[][] STATIC_A = {
+ { 1.0 / 2.0 },
+ { 0.0, 1.0 / 2.0 },
+ { 0.0, 0.0, 1.0 }
+ };
+
+ /** Propagation weights Butcher array. */
+ private static final double[] STATIC_B = {
+ 1.0 / 6.0, 1.0 / 3.0, 1.0 / 3.0, 1.0 / 6.0
+ };
+
+ /** Simple constructor.
+ * Build a fourth-order Runge-Kutta integrator with the given
+ * step.
+ * @param step integration step
+ */
+ public ClassicalRungeKuttaIntegrator(final double step) {
+ super("classical Runge-Kutta", STATIC_C, STATIC_A, STATIC_B,
+ new ClassicalRungeKuttaStepInterpolator(), step);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaStepInterpolator.java
new file mode 100644
index 0000000..296f5b5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/ClassicalRungeKuttaStepInterpolator.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+
+/**
+ * This class implements a step interpolator for the classical fourth
+ * order Runge-Kutta integrator.
+ *
+ * <p>This interpolator allows to compute dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme :
+ * <ul>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub>)
+ * + &theta; (h/6) [ (6 - 9 &theta; + 4 &theta;<sup>2</sup>) y'<sub>1</sub>
+ * + ( 6 &theta; - 4 &theta;<sup>2</sup>) (y'<sub>2</sub> + y'<sub>3</sub>)
+ * + ( -3 &theta; + 4 &theta;<sup>2</sup>) y'<sub>4</sub>
+ * ]
+ * </li>
+ * <li>Using reference point at step end:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub> + h)
+ * + (1 - &theta;) (h/6) [ (-4 &theta;^2 + 5 &theta; - 1) y'<sub>1</sub>
+ * +(4 &theta;^2 - 2 &theta; - 2) (y'<sub>2</sub> + y'<sub>3</sub>)
+ * -(4 &theta;^2 + &theta; + 1) y'<sub>4</sub>
+ * ]
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * where &theta; belongs to [0 ; 1] and where y'<sub>1</sub> to y'<sub>4</sub> are the four
+ * evaluations of the derivatives already computed during the
+ * step.</p>
+ *
+ * @see ClassicalRungeKuttaIntegrator
+ * @since 1.2
+ */
+
+class ClassicalRungeKuttaStepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20111120L;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link RungeKuttaStepInterpolator#reinitialize} method should be
+ * called before using the instance in order to initialize the
+ * internal arrays. This constructor is used only in order to delay
+ * the initialization in some cases. The {@link RungeKuttaIntegrator}
+ * class uses the prototyping design pattern to create the step
+ * interpolators by cloning an uninitialized model and latter initializing
+ * the copy.
+ */
+ // CHECKSTYLE: stop RedundantModifier
+ // the public modifier here is needed for serialization
+ public ClassicalRungeKuttaStepInterpolator() {
+ }
+ // CHECKSTYLE: resume RedundantModifier
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ ClassicalRungeKuttaStepInterpolator(final ClassicalRungeKuttaStepInterpolator interpolator) {
+ super(interpolator);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new ClassicalRungeKuttaStepInterpolator(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta,
+ final double oneMinusThetaH) {
+
+ final double oneMinusTheta = 1 - theta;
+ final double oneMinus2Theta = 1 - 2 * theta;
+ final double coeffDot1 = oneMinusTheta * oneMinus2Theta;
+ final double coeffDot23 = 2 * theta * oneMinusTheta;
+ final double coeffDot4 = -theta * oneMinus2Theta;
+ if ((previousState != null) && (theta <= 0.5)) {
+ final double fourTheta2 = 4 * theta * theta;
+ final double s = theta * h / 6.0;
+ final double coeff1 = s * ( 6 - 9 * theta + fourTheta2);
+ final double coeff23 = s * ( 6 * theta - fourTheta2);
+ final double coeff4 = s * (-3 * theta + fourTheta2);
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot23 = yDotK[1][i] + yDotK[2][i];
+ final double yDot4 = yDotK[3][i];
+ interpolatedState[i] =
+ previousState[i] + coeff1 * yDot1 + coeff23 * yDot23 + coeff4 * yDot4;
+ interpolatedDerivatives[i] =
+ coeffDot1 * yDot1 + coeffDot23 * yDot23 + coeffDot4 * yDot4;
+ }
+ } else {
+ final double fourTheta = 4 * theta;
+ final double s = oneMinusThetaH / 6.0;
+ final double coeff1 = s * ((-fourTheta + 5) * theta - 1);
+ final double coeff23 = s * (( fourTheta - 2) * theta - 2);
+ final double coeff4 = s * ((-fourTheta - 1) * theta - 1);
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot23 = yDotK[1][i] + yDotK[2][i];
+ final double yDot4 = yDotK[3][i];
+ interpolatedState[i] =
+ currentState[i] + coeff1 * yDot1 + coeff23 * yDot23 + coeff4 * yDot4;
+ interpolatedDerivatives[i] =
+ coeffDot1 * yDot1 + coeffDot23 * yDot23 + coeffDot4 * yDot4;
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54FieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54FieldIntegrator.java
new file mode 100644
index 0000000..5cdd828
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54FieldIntegrator.java
@@ -0,0 +1,232 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+
+/**
+ * This class implements the 5(4) Dormand-Prince integrator for Ordinary
+ * Differential Equations.
+
+ * <p>This integrator is an embedded Runge-Kutta integrator
+ * of order 5(4) used in local extrapolation mode (i.e. the solution
+ * is computed using the high order formula) with stepsize control
+ * (and automatic step initialization) and continuous output. This
+ * method uses 7 functions evaluations per step. However, since this
+ * is an <i>fsal</i>, the last evaluation of one step is the same as
+ * the first evaluation of the next step and hence can be avoided. So
+ * the cost is really 6 functions evaluations per step.</p>
+ *
+ * <p>This method has been published (whithout the continuous output
+ * that was added by Shampine in 1986) in the following article :
+ * <pre>
+ * A family of embedded Runge-Kutta formulae
+ * J. R. Dormand and P. J. Prince
+ * Journal of Computational and Applied Mathematics
+ * volume 6, no 1, 1980, pp. 19-26
+ * </pre></p>
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public class DormandPrince54FieldIntegrator<T extends RealFieldElement<T>>
+ extends EmbeddedRungeKuttaFieldIntegrator<T> {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Dormand-Prince 5(4)";
+
+ /** Error array, element 1. */
+ private final T e1;
+
+ // element 2 is zero, so it is neither stored nor used
+
+ /** Error array, element 3. */
+ private final T e3;
+
+ /** Error array, element 4. */
+ private final T e4;
+
+ /** Error array, element 5. */
+ private final T e5;
+
+ /** Error array, element 6. */
+ private final T e6;
+
+ /** Error array, element 7. */
+ private final T e7;
+
+ /** Simple constructor.
+ * Build a fifth order Dormand-Prince integrator with the given step bounds
+ * @param field field to which the time and state vector elements belong
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ public DormandPrince54FieldIntegrator(final Field<T> field,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+ super(field, METHOD_NAME, 6,
+ minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+ e1 = fraction( 71, 57600);
+ e3 = fraction( -71, 16695);
+ e4 = fraction( 71, 1920);
+ e5 = fraction(-17253, 339200);
+ e6 = fraction( 22, 525);
+ e7 = fraction( -1, 40);
+ }
+
+ /** Simple constructor.
+ * Build a fifth order Dormand-Prince integrator with the given step bounds
+ * @param field field to which the time and state vector elements belong
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ public DormandPrince54FieldIntegrator(final Field<T> field,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+ super(field, METHOD_NAME, 6,
+ minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+ e1 = fraction( 71, 57600);
+ e3 = fraction( -71, 16695);
+ e4 = fraction( 71, 1920);
+ e5 = fraction(-17253, 339200);
+ e6 = fraction( 22, 525);
+ e7 = fraction( -1, 40);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getC() {
+ final T[] c = MathArrays.buildArray(getField(), 6);
+ c[0] = fraction(1, 5);
+ c[1] = fraction(3, 10);
+ c[2] = fraction(4, 5);
+ c[3] = fraction(8, 9);
+ c[4] = getField().getOne();
+ c[5] = getField().getOne();
+ return c;
+ }
+
+ /** {@inheritDoc} */
+ public T[][] getA() {
+ final T[][] a = MathArrays.buildArray(getField(), 6, -1);
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = MathArrays.buildArray(getField(), i + 1);
+ }
+ a[0][0] = fraction( 1, 5);
+ a[1][0] = fraction( 3, 40);
+ a[1][1] = fraction( 9, 40);
+ a[2][0] = fraction( 44, 45);
+ a[2][1] = fraction( -56, 15);
+ a[2][2] = fraction( 32, 9);
+ a[3][0] = fraction( 19372, 6561);
+ a[3][1] = fraction(-25360, 2187);
+ a[3][2] = fraction( 64448, 6561);
+ a[3][3] = fraction( -212, 729);
+ a[4][0] = fraction( 9017, 3168);
+ a[4][1] = fraction( -355, 33);
+ a[4][2] = fraction( 46732, 5247);
+ a[4][3] = fraction( 49, 176);
+ a[4][4] = fraction( -5103, 18656);
+ a[5][0] = fraction( 35, 384);
+ a[5][1] = getField().getZero();
+ a[5][2] = fraction( 500, 1113);
+ a[5][3] = fraction( 125, 192);
+ a[5][4] = fraction( -2187, 6784);
+ a[5][5] = fraction( 11, 84);
+ return a;
+ }
+
+ /** {@inheritDoc} */
+ public T[] getB() {
+ final T[] b = MathArrays.buildArray(getField(), 7);
+ b[0] = fraction( 35, 384);
+ b[1] = getField().getZero();
+ b[2] = fraction( 500, 1113);
+ b[3] = fraction( 125, 192);
+ b[4] = fraction(-2187, 6784);
+ b[5] = fraction( 11, 84);
+ b[6] = getField().getZero();
+ return b;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected DormandPrince54FieldStepInterpolator<T>
+ createInterpolator(final boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState, final FieldEquationsMapper<T> mapper) {
+ return new DormandPrince54FieldStepInterpolator<T>(getField(), forward, yDotK,
+ globalPreviousState, globalCurrentState,
+ globalPreviousState, globalCurrentState,
+ mapper);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getOrder() {
+ return 5;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected T estimateError(final T[][] yDotK, final T[] y0, final T[] y1, final T h) {
+
+ T error = getField().getZero();
+
+ for (int j = 0; j < mainSetDimension; ++j) {
+ final T errSum = yDotK[0][j].multiply(e1).
+ add(yDotK[2][j].multiply(e3)).
+ add(yDotK[3][j].multiply(e4)).
+ add(yDotK[4][j].multiply(e5)).
+ add(yDotK[5][j].multiply(e6)).
+ add(yDotK[6][j].multiply(e7));
+
+ final T yScale = MathUtils.max(y0[j].abs(), y1[j].abs());
+ final T tol = (vecAbsoluteTolerance == null) ?
+ yScale.multiply(scalRelativeTolerance).add(scalAbsoluteTolerance) :
+ yScale.multiply(vecRelativeTolerance[j]).add(vecAbsoluteTolerance[j]);
+ final T ratio = h.multiply(errSum).divide(tol);
+ error = error.add(ratio.multiply(ratio));
+
+ }
+
+ return error.divide(mainSetDimension).sqrt();
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54FieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54FieldStepInterpolator.java
new file mode 100644
index 0000000..62a6fa3
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54FieldStepInterpolator.java
@@ -0,0 +1,249 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/**
+ * This class represents an interpolator over the last step during an
+ * ODE integration for the 5(4) Dormand-Prince integrator.
+ *
+ * @see DormandPrince54Integrator
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+class DormandPrince54FieldStepInterpolator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldStepInterpolator<T> {
+
+ /** Last row of the Butcher-array internal weights, element 0. */
+ private final T a70;
+
+ // element 1 is zero, so it is neither stored nor used
+
+ /** Last row of the Butcher-array internal weights, element 2. */
+ private final T a72;
+
+ /** Last row of the Butcher-array internal weights, element 3. */
+ private final T a73;
+
+ /** Last row of the Butcher-array internal weights, element 4. */
+ private final T a74;
+
+ /** Last row of the Butcher-array internal weights, element 5. */
+ private final T a75;
+
+ /** Shampine (1986) Dense output, element 0. */
+ private final T d0;
+
+ // element 1 is zero, so it is neither stored nor used
+
+ /** Shampine (1986) Dense output, element 2. */
+ private final T d2;
+
+ /** Shampine (1986) Dense output, element 3. */
+ private final T d3;
+
+ /** Shampine (1986) Dense output, element 4. */
+ private final T d4;
+
+ /** Shampine (1986) Dense output, element 5. */
+ private final T d5;
+
+ /** Shampine (1986) Dense output, element 6. */
+ private final T d6;
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param mapper equations mapper for the all equations
+ */
+ DormandPrince54FieldStepInterpolator(final Field<T> field, final boolean forward,
+ final T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ super(field, forward, yDotK,
+ globalPreviousState, globalCurrentState, softPreviousState, softCurrentState,
+ mapper);
+ final T one = field.getOne();
+ a70 = one.multiply( 35.0).divide( 384.0);
+ a72 = one.multiply( 500.0).divide(1113.0);
+ a73 = one.multiply( 125.0).divide( 192.0);
+ a74 = one.multiply(-2187.0).divide(6784.0);
+ a75 = one.multiply( 11.0).divide( 84.0);
+ d0 = one.multiply(-12715105075.0).divide( 11282082432.0);
+ d2 = one.multiply( 87487479700.0).divide( 32700410799.0);
+ d3 = one.multiply(-10690763975.0).divide( 1880347072.0);
+ d4 = one.multiply(701980252875.0).divide(199316789632.0);
+ d5 = one.multiply( -1453857185.0).divide( 822651844.0);
+ d6 = one.multiply( 69997945.0).divide( 29380423.0);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected DormandPrince54FieldStepInterpolator<T> create(final Field<T> newField, final boolean newForward, final T[][] newYDotK,
+ final FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ final FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ final FieldODEStateAndDerivative<T> newSoftPreviousState,
+ final FieldODEStateAndDerivative<T> newSoftCurrentState,
+ final FieldEquationsMapper<T> newMapper) {
+ return new DormandPrince54FieldStepInterpolator<T>(newField, newForward, newYDotK,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+ }
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(final FieldEquationsMapper<T> mapper,
+ final T time, final T theta,
+ final T thetaH, final T oneMinusThetaH) {
+
+ // interpolate
+ final T one = time.getField().getOne();
+ final T eta = one.subtract(theta);
+ final T twoTheta = theta.multiply(2);
+ final T dot2 = one.subtract(twoTheta);
+ final T dot3 = theta.multiply(theta.multiply(-3).add(2));
+ final T dot4 = twoTheta.multiply(theta.multiply(twoTheta.subtract(3)).add(1));
+ final T[] interpolatedState;
+ final T[] interpolatedDerivatives;
+ if (getGlobalPreviousState() != null && theta.getReal() <= 0.5) {
+ final T f1 = thetaH;
+ final T f2 = f1.multiply(eta);
+ final T f3 = f2.multiply(theta);
+ final T f4 = f3.multiply(eta);
+ final T coeff0 = f1.multiply(a70).
+ subtract(f2.multiply(a70.subtract(1))).
+ add(f3.multiply(a70.multiply(2).subtract(1))).
+ add(f4.multiply(d0));
+ final T coeff1 = time.getField().getZero();
+ final T coeff2 = f1.multiply(a72).
+ subtract(f2.multiply(a72)).
+ add(f3.multiply(a72.multiply(2))).
+ add(f4.multiply(d2));
+ final T coeff3 = f1.multiply(a73).
+ subtract(f2.multiply(a73)).
+ add(f3.multiply(a73.multiply(2))).
+ add(f4.multiply(d3));
+ final T coeff4 = f1.multiply(a74).
+ subtract(f2.multiply(a74)).
+ add(f3.multiply(a74.multiply(2))).
+ add(f4.multiply(d4));
+ final T coeff5 = f1.multiply(a75).
+ subtract(f2.multiply(a75)).
+ add(f3.multiply(a75.multiply(2))).
+ add(f4.multiply(d5));
+ final T coeff6 = f4.multiply(d6).subtract(f3);
+ final T coeffDot0 = a70.
+ subtract(dot2.multiply(a70.subtract(1))).
+ add(dot3.multiply(a70.multiply(2).subtract(1))).
+ add(dot4.multiply(d0));
+ final T coeffDot1 = time.getField().getZero();
+ final T coeffDot2 = a72.
+ subtract(dot2.multiply(a72)).
+ add(dot3.multiply(a72.multiply(2))).
+ add(dot4.multiply(d2));
+ final T coeffDot3 = a73.
+ subtract(dot2.multiply(a73)).
+ add(dot3.multiply(a73.multiply(2))).
+ add(dot4.multiply(d3));
+ final T coeffDot4 = a74.
+ subtract(dot2.multiply(a74)).
+ add(dot3.multiply(a74.multiply(2))).
+ add(dot4.multiply(d4));
+ final T coeffDot5 = a75.
+ subtract(dot2.multiply(a75)).
+ add(dot3.multiply(a75.multiply(2))).
+ add(dot4.multiply(d5));
+ final T coeffDot6 = dot4.multiply(d6).subtract(dot3);
+ interpolatedState = previousStateLinearCombination(coeff0, coeff1, coeff2, coeff3,
+ coeff4, coeff5, coeff6);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot0, coeffDot1, coeffDot2, coeffDot3,
+ coeffDot4, coeffDot5, coeffDot6);
+ } else {
+ final T f1 = oneMinusThetaH.negate();
+ final T f2 = oneMinusThetaH.multiply(theta);
+ final T f3 = f2.multiply(theta);
+ final T f4 = f3.multiply(eta);
+ final T coeff0 = f1.multiply(a70).
+ subtract(f2.multiply(a70.subtract(1))).
+ add(f3.multiply(a70.multiply(2).subtract(1))).
+ add(f4.multiply(d0));
+ final T coeff1 = time.getField().getZero();
+ final T coeff2 = f1.multiply(a72).
+ subtract(f2.multiply(a72)).
+ add(f3.multiply(a72.multiply(2))).
+ add(f4.multiply(d2));
+ final T coeff3 = f1.multiply(a73).
+ subtract(f2.multiply(a73)).
+ add(f3.multiply(a73.multiply(2))).
+ add(f4.multiply(d3));
+ final T coeff4 = f1.multiply(a74).
+ subtract(f2.multiply(a74)).
+ add(f3.multiply(a74.multiply(2))).
+ add(f4.multiply(d4));
+ final T coeff5 = f1.multiply(a75).
+ subtract(f2.multiply(a75)).
+ add(f3.multiply(a75.multiply(2))).
+ add(f4.multiply(d5));
+ final T coeff6 = f4.multiply(d6).subtract(f3);
+ final T coeffDot0 = a70.
+ subtract(dot2.multiply(a70.subtract(1))).
+ add(dot3.multiply(a70.multiply(2).subtract(1))).
+ add(dot4.multiply(d0));
+ final T coeffDot1 = time.getField().getZero();
+ final T coeffDot2 = a72.
+ subtract(dot2.multiply(a72)).
+ add(dot3.multiply(a72.multiply(2))).
+ add(dot4.multiply(d2));
+ final T coeffDot3 = a73.
+ subtract(dot2.multiply(a73)).
+ add(dot3.multiply(a73.multiply(2))).
+ add(dot4.multiply(d3));
+ final T coeffDot4 = a74.
+ subtract(dot2.multiply(a74)).
+ add(dot3.multiply(a74.multiply(2))).
+ add(dot4.multiply(d4));
+ final T coeffDot5 = a75.
+ subtract(dot2.multiply(a75)).
+ add(dot3.multiply(a75.multiply(2))).
+ add(dot4.multiply(d5));
+ final T coeffDot6 = dot4.multiply(d6).subtract(dot3);
+ interpolatedState = currentStateLinearCombination(coeff0, coeff1, coeff2, coeff3,
+ coeff4, coeff5, coeff6);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot0, coeffDot1, coeffDot2, coeffDot3,
+ coeffDot4, coeffDot5, coeffDot6);
+ }
+ return new FieldODEStateAndDerivative<T>(time, interpolatedState, interpolatedDerivatives);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54Integrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54Integrator.java
new file mode 100644
index 0000000..45229b2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54Integrator.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.util.FastMath;
+
+
+/**
+ * This class implements the 5(4) Dormand-Prince integrator for Ordinary
+ * Differential Equations.
+
+ * <p>This integrator is an embedded Runge-Kutta integrator
+ * of order 5(4) used in local extrapolation mode (i.e. the solution
+ * is computed using the high order formula) with stepsize control
+ * (and automatic step initialization) and continuous output. This
+ * method uses 7 functions evaluations per step. However, since this
+ * is an <i>fsal</i>, the last evaluation of one step is the same as
+ * the first evaluation of the next step and hence can be avoided. So
+ * the cost is really 6 functions evaluations per step.</p>
+ *
+ * <p>This method has been published (whithout the continuous output
+ * that was added by Shampine in 1986) in the following article :
+ * <pre>
+ * A family of embedded Runge-Kutta formulae
+ * J. R. Dormand and P. J. Prince
+ * Journal of Computational and Applied Mathematics
+ * volume 6, no 1, 1980, pp. 19-26
+ * </pre></p>
+ *
+ * @since 1.2
+ */
+
+public class DormandPrince54Integrator extends EmbeddedRungeKuttaIntegrator {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Dormand-Prince 5(4)";
+
+ /** Time steps Butcher array. */
+ private static final double[] STATIC_C = {
+ 1.0/5.0, 3.0/10.0, 4.0/5.0, 8.0/9.0, 1.0, 1.0
+ };
+
+ /** Internal weights Butcher array. */
+ private static final double[][] STATIC_A = {
+ {1.0/5.0},
+ {3.0/40.0, 9.0/40.0},
+ {44.0/45.0, -56.0/15.0, 32.0/9.0},
+ {19372.0/6561.0, -25360.0/2187.0, 64448.0/6561.0, -212.0/729.0},
+ {9017.0/3168.0, -355.0/33.0, 46732.0/5247.0, 49.0/176.0, -5103.0/18656.0},
+ {35.0/384.0, 0.0, 500.0/1113.0, 125.0/192.0, -2187.0/6784.0, 11.0/84.0}
+ };
+
+ /** Propagation weights Butcher array. */
+ private static final double[] STATIC_B = {
+ 35.0/384.0, 0.0, 500.0/1113.0, 125.0/192.0, -2187.0/6784.0, 11.0/84.0, 0.0
+ };
+
+ /** Error array, element 1. */
+ private static final double E1 = 71.0 / 57600.0;
+
+ // element 2 is zero, so it is neither stored nor used
+
+ /** Error array, element 3. */
+ private static final double E3 = -71.0 / 16695.0;
+
+ /** Error array, element 4. */
+ private static final double E4 = 71.0 / 1920.0;
+
+ /** Error array, element 5. */
+ private static final double E5 = -17253.0 / 339200.0;
+
+ /** Error array, element 6. */
+ private static final double E6 = 22.0 / 525.0;
+
+ /** Error array, element 7. */
+ private static final double E7 = -1.0 / 40.0;
+
+ /** Simple constructor.
+ * Build a fifth order Dormand-Prince integrator with the given step bounds
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ public DormandPrince54Integrator(final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+ super(METHOD_NAME, true, STATIC_C, STATIC_A, STATIC_B, new DormandPrince54StepInterpolator(),
+ minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+ }
+
+ /** Simple constructor.
+ * Build a fifth order Dormand-Prince integrator with the given step bounds
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ public DormandPrince54Integrator(final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+ super(METHOD_NAME, true, STATIC_C, STATIC_A, STATIC_B, new DormandPrince54StepInterpolator(),
+ minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getOrder() {
+ return 5;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double estimateError(final double[][] yDotK,
+ final double[] y0, final double[] y1,
+ final double h) {
+
+ double error = 0;
+
+ for (int j = 0; j < mainSetDimension; ++j) {
+ final double errSum = E1 * yDotK[0][j] + E3 * yDotK[2][j] +
+ E4 * yDotK[3][j] + E5 * yDotK[4][j] +
+ E6 * yDotK[5][j] + E7 * yDotK[6][j];
+
+ final double yScale = FastMath.max(FastMath.abs(y0[j]), FastMath.abs(y1[j]));
+ final double tol = (vecAbsoluteTolerance == null) ?
+ (scalAbsoluteTolerance + scalRelativeTolerance * yScale) :
+ (vecAbsoluteTolerance[j] + vecRelativeTolerance[j] * yScale);
+ final double ratio = h * errSum / tol;
+ error += ratio * ratio;
+
+ }
+
+ return FastMath.sqrt(error / mainSetDimension);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54StepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54StepInterpolator.java
new file mode 100644
index 0000000..04aa970
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince54StepInterpolator.java
@@ -0,0 +1,225 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.ode.AbstractIntegrator;
+import org.apache.commons.math3.ode.EquationsMapper;
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+
+/**
+ * This class represents an interpolator over the last step during an
+ * ODE integration for the 5(4) Dormand-Prince integrator.
+ *
+ * @see DormandPrince54Integrator
+ *
+ * @since 1.2
+ */
+
+class DormandPrince54StepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** Last row of the Butcher-array internal weights, element 0. */
+ private static final double A70 = 35.0 / 384.0;
+
+ // element 1 is zero, so it is neither stored nor used
+
+ /** Last row of the Butcher-array internal weights, element 2. */
+ private static final double A72 = 500.0 / 1113.0;
+
+ /** Last row of the Butcher-array internal weights, element 3. */
+ private static final double A73 = 125.0 / 192.0;
+
+ /** Last row of the Butcher-array internal weights, element 4. */
+ private static final double A74 = -2187.0 / 6784.0;
+
+ /** Last row of the Butcher-array internal weights, element 5. */
+ private static final double A75 = 11.0 / 84.0;
+
+ /** Shampine (1986) Dense output, element 0. */
+ private static final double D0 = -12715105075.0 / 11282082432.0;
+
+ // element 1 is zero, so it is neither stored nor used
+
+ /** Shampine (1986) Dense output, element 2. */
+ private static final double D2 = 87487479700.0 / 32700410799.0;
+
+ /** Shampine (1986) Dense output, element 3. */
+ private static final double D3 = -10690763975.0 / 1880347072.0;
+
+ /** Shampine (1986) Dense output, element 4. */
+ private static final double D4 = 701980252875.0 / 199316789632.0;
+
+ /** Shampine (1986) Dense output, element 5. */
+ private static final double D5 = -1453857185.0 / 822651844.0;
+
+ /** Shampine (1986) Dense output, element 6. */
+ private static final double D6 = 69997945.0 / 29380423.0;
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20111120L;
+
+ /** First vector for interpolation. */
+ private double[] v1;
+
+ /** Second vector for interpolation. */
+ private double[] v2;
+
+ /** Third vector for interpolation. */
+ private double[] v3;
+
+ /** Fourth vector for interpolation. */
+ private double[] v4;
+
+ /** Initialization indicator for the interpolation vectors. */
+ private boolean vectorsInitialized;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link #reinitialize} method should be called before using the
+ * instance in order to initialize the internal arrays. This
+ * constructor is used only in order to delay the initialization in
+ * some cases. The {@link EmbeddedRungeKuttaIntegrator} uses the
+ * prototyping design pattern to create the step interpolators by
+ * cloning an uninitialized model and latter initializing the copy.
+ */
+ // CHECKSTYLE: stop RedundantModifier
+ // the public modifier here is needed for serialization
+ public DormandPrince54StepInterpolator() {
+ super();
+ v1 = null;
+ v2 = null;
+ v3 = null;
+ v4 = null;
+ vectorsInitialized = false;
+ }
+ // CHECKSTYLE: resume RedundantModifier
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ DormandPrince54StepInterpolator(final DormandPrince54StepInterpolator interpolator) {
+
+ super(interpolator);
+
+ if (interpolator.v1 == null) {
+
+ v1 = null;
+ v2 = null;
+ v3 = null;
+ v4 = null;
+ vectorsInitialized = false;
+
+ } else {
+
+ v1 = interpolator.v1.clone();
+ v2 = interpolator.v2.clone();
+ v3 = interpolator.v3.clone();
+ v4 = interpolator.v4.clone();
+ vectorsInitialized = interpolator.vectorsInitialized;
+
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new DormandPrince54StepInterpolator(this);
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ public void reinitialize(final AbstractIntegrator integrator,
+ final double[] y, final double[][] yDotK, final boolean forward,
+ final EquationsMapper primaryMapper,
+ final EquationsMapper[] secondaryMappers) {
+ super.reinitialize(integrator, y, yDotK, forward, primaryMapper, secondaryMappers);
+ v1 = null;
+ v2 = null;
+ v3 = null;
+ v4 = null;
+ vectorsInitialized = false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void storeTime(final double t) {
+ super.storeTime(t);
+ vectorsInitialized = false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta,
+ final double oneMinusThetaH) {
+
+ if (! vectorsInitialized) {
+
+ if (v1 == null) {
+ v1 = new double[interpolatedState.length];
+ v2 = new double[interpolatedState.length];
+ v3 = new double[interpolatedState.length];
+ v4 = new double[interpolatedState.length];
+ }
+
+ // no step finalization is needed for this interpolator
+
+ // we need to compute the interpolation vectors for this time step
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot0 = yDotK[0][i];
+ final double yDot2 = yDotK[2][i];
+ final double yDot3 = yDotK[3][i];
+ final double yDot4 = yDotK[4][i];
+ final double yDot5 = yDotK[5][i];
+ final double yDot6 = yDotK[6][i];
+ v1[i] = A70 * yDot0 + A72 * yDot2 + A73 * yDot3 + A74 * yDot4 + A75 * yDot5;
+ v2[i] = yDot0 - v1[i];
+ v3[i] = v1[i] - v2[i] - yDot6;
+ v4[i] = D0 * yDot0 + D2 * yDot2 + D3 * yDot3 + D4 * yDot4 + D5 * yDot5 + D6 * yDot6;
+ }
+
+ vectorsInitialized = true;
+
+ }
+
+ // interpolate
+ final double eta = 1 - theta;
+ final double twoTheta = 2 * theta;
+ final double dot2 = 1 - twoTheta;
+ final double dot3 = theta * (2 - 3 * theta);
+ final double dot4 = twoTheta * (1 + theta * (twoTheta - 3));
+ if ((previousState != null) && (theta <= 0.5)) {
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ interpolatedState[i] =
+ previousState[i] + theta * h * (v1[i] + eta * (v2[i] + theta * (v3[i] + eta * v4[i])));
+ interpolatedDerivatives[i] = v1[i] + dot2 * v2[i] + dot3 * v3[i] + dot4 * v4[i];
+ }
+ } else {
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ interpolatedState[i] =
+ currentState[i] - oneMinusThetaH * (v1[i] - theta * (v2[i] + theta * (v3[i] + eta * v4[i])));
+ interpolatedDerivatives[i] = v1[i] + dot2 * v2[i] + dot3 * v3[i] + dot4 * v4[i];
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853FieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853FieldIntegrator.java
new file mode 100644
index 0000000..3111756
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853FieldIntegrator.java
@@ -0,0 +1,454 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+
+/**
+ * This class implements the 8(5,3) Dormand-Prince integrator for Ordinary
+ * Differential Equations.
+ *
+ * <p>This integrator is an embedded Runge-Kutta integrator
+ * of order 8(5,3) used in local extrapolation mode (i.e. the solution
+ * is computed using the high order formula) with stepsize control
+ * (and automatic step initialization) and continuous output. This
+ * method uses 12 functions evaluations per step for integration and 4
+ * evaluations for interpolation. However, since the first
+ * interpolation evaluation is the same as the first integration
+ * evaluation of the next step, we have included it in the integrator
+ * rather than in the interpolator and specified the method was an
+ * <i>fsal</i>. Hence, despite we have 13 stages here, the cost is
+ * really 12 evaluations per step even if no interpolation is done,
+ * and the overcost of interpolation is only 3 evaluations.</p>
+ *
+ * <p>This method is based on an 8(6) method by Dormand and Prince
+ * (i.e. order 8 for the integration and order 6 for error estimation)
+ * modified by Hairer and Wanner to use a 5th order error estimator
+ * with 3rd order correction. This modification was introduced because
+ * the original method failed in some cases (wrong steps can be
+ * accepted when step size is too large, for example in the
+ * Brusselator problem) and also had <i>severe difficulties when
+ * applied to problems with discontinuities</i>. This modification is
+ * explained in the second edition of the first volume (Nonstiff
+ * Problems) of the reference book by Hairer, Norsett and Wanner:
+ * <i>Solving Ordinary Differential Equations</i> (Springer-Verlag,
+ * ISBN 3-540-56670-8).</p>
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public class DormandPrince853FieldIntegrator<T extends RealFieldElement<T>>
+ extends EmbeddedRungeKuttaFieldIntegrator<T> {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Dormand-Prince 8 (5, 3)";
+
+ /** First error weights array, element 1. */
+ private final T e1_01;
+
+ // elements 2 to 5 are zero, so they are neither stored nor used
+
+ /** First error weights array, element 6. */
+ private final T e1_06;
+
+ /** First error weights array, element 7. */
+ private final T e1_07;
+
+ /** First error weights array, element 8. */
+ private final T e1_08;
+
+ /** First error weights array, element 9. */
+ private final T e1_09;
+
+ /** First error weights array, element 10. */
+ private final T e1_10;
+
+ /** First error weights array, element 11. */
+ private final T e1_11;
+
+ /** First error weights array, element 12. */
+ private final T e1_12;
+
+
+ /** Second error weights array, element 1. */
+ private final T e2_01;
+
+ // elements 2 to 5 are zero, so they are neither stored nor used
+
+ /** Second error weights array, element 6. */
+ private final T e2_06;
+
+ /** Second error weights array, element 7. */
+ private final T e2_07;
+
+ /** Second error weights array, element 8. */
+ private final T e2_08;
+
+ /** Second error weights array, element 9. */
+ private final T e2_09;
+
+ /** Second error weights array, element 10. */
+ private final T e2_10;
+
+ /** Second error weights array, element 11. */
+ private final T e2_11;
+
+ /** Second error weights array, element 12. */
+ private final T e2_12;
+
+ /** Simple constructor.
+ * Build an eighth order Dormand-Prince integrator with the given step bounds
+ * @param field field to which the time and state vector elements belong
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ public DormandPrince853FieldIntegrator(final Field<T> field,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+ super(field, METHOD_NAME, 12,
+ minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+ e1_01 = fraction( 116092271.0, 8848465920.0);
+ e1_06 = fraction( -1871647.0, 1527680.0);
+ e1_07 = fraction( -69799717.0, 140793660.0);
+ e1_08 = fraction( 1230164450203.0, 739113984000.0);
+ e1_09 = fraction(-1980813971228885.0, 5654156025964544.0);
+ e1_10 = fraction( 464500805.0, 1389975552.0);
+ e1_11 = fraction( 1606764981773.0, 19613062656000.0);
+ e1_12 = fraction( -137909.0, 6168960.0);
+ e2_01 = fraction( -364463.0, 1920240.0);
+ e2_06 = fraction( 3399327.0, 763840.0);
+ e2_07 = fraction( 66578432.0, 35198415.0);
+ e2_08 = fraction( -1674902723.0, 288716400.0);
+ e2_09 = fraction( -74684743568175.0, 176692375811392.0);
+ e2_10 = fraction( -734375.0, 4826304.0);
+ e2_11 = fraction( 171414593.0, 851261400.0);
+ e2_12 = fraction( 69869.0, 3084480.0);
+ }
+
+ /** Simple constructor.
+ * Build an eighth order Dormand-Prince integrator with the given step bounds
+ * @param field field to which the time and state vector elements belong
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ public DormandPrince853FieldIntegrator(final Field<T> field,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+ super(field, METHOD_NAME, 12,
+ minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+ e1_01 = fraction( 116092271.0, 8848465920.0);
+ e1_06 = fraction( -1871647.0, 1527680.0);
+ e1_07 = fraction( -69799717.0, 140793660.0);
+ e1_08 = fraction( 1230164450203.0, 739113984000.0);
+ e1_09 = fraction(-1980813971228885.0, 5654156025964544.0);
+ e1_10 = fraction( 464500805.0, 1389975552.0);
+ e1_11 = fraction( 1606764981773.0, 19613062656000.0);
+ e1_12 = fraction( -137909.0, 6168960.0);
+ e2_01 = fraction( -364463.0, 1920240.0);
+ e2_06 = fraction( 3399327.0, 763840.0);
+ e2_07 = fraction( 66578432.0, 35198415.0);
+ e2_08 = fraction( -1674902723.0, 288716400.0);
+ e2_09 = fraction( -74684743568175.0, 176692375811392.0);
+ e2_10 = fraction( -734375.0, 4826304.0);
+ e2_11 = fraction( 171414593.0, 851261400.0);
+ e2_12 = fraction( 69869.0, 3084480.0);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getC() {
+
+ final T sqrt6 = getField().getOne().multiply(6).sqrt();
+
+ final T[] c = MathArrays.buildArray(getField(), 15);
+ c[ 0] = sqrt6.add(-6).divide(-67.5);
+ c[ 1] = sqrt6.add(-6).divide(-45.0);
+ c[ 2] = sqrt6.add(-6).divide(-30.0);
+ c[ 3] = sqrt6.add( 6).divide( 30.0);
+ c[ 4] = fraction(1, 3);
+ c[ 5] = fraction(1, 4);
+ c[ 6] = fraction(4, 13);
+ c[ 7] = fraction(127, 195);
+ c[ 8] = fraction(3, 5);
+ c[ 9] = fraction(6, 7);
+ c[10] = getField().getOne();
+ c[11] = getField().getOne();
+ c[12] = fraction(1.0, 10.0);
+ c[13] = fraction(1.0, 5.0);
+ c[14] = fraction(7.0, 9.0);
+
+ return c;
+
+ }
+
+ /** {@inheritDoc} */
+ public T[][] getA() {
+
+ final T sqrt6 = getField().getOne().multiply(6).sqrt();
+
+ final T[][] a = MathArrays.buildArray(getField(), 15, -1);
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = MathArrays.buildArray(getField(), i + 1);
+ }
+
+ a[ 0][ 0] = sqrt6.add(-6).divide(-67.5);
+
+ a[ 1][ 0] = sqrt6.add(-6).divide(-180);
+ a[ 1][ 1] = sqrt6.add(-6).divide( -60);
+
+ a[ 2][ 0] = sqrt6.add(-6).divide(-120);
+ a[ 2][ 1] = getField().getZero();
+ a[ 2][ 2] = sqrt6.add(-6).divide( -40);
+
+ a[ 3][ 0] = sqrt6.multiply(107).add(462).divide( 3000);
+ a[ 3][ 1] = getField().getZero();
+ a[ 3][ 2] = sqrt6.multiply(197).add(402).divide(-1000);
+ a[ 3][ 3] = sqrt6.multiply( 73).add(168).divide( 375);
+
+ a[ 4][ 0] = fraction(1, 27);
+ a[ 4][ 1] = getField().getZero();
+ a[ 4][ 2] = getField().getZero();
+ a[ 4][ 3] = sqrt6.add( 16).divide( 108);
+ a[ 4][ 4] = sqrt6.add(-16).divide(-108);
+
+ a[ 5][ 0] = fraction(19, 512);
+ a[ 5][ 1] = getField().getZero();
+ a[ 5][ 2] = getField().getZero();
+ a[ 5][ 3] = sqrt6.multiply( 23).add(118).divide(1024);
+ a[ 5][ 4] = sqrt6.multiply(-23).add(118).divide(1024);
+ a[ 5][ 5] = fraction(-9, 512);
+
+ a[ 6][ 0] = fraction(13772, 371293);
+ a[ 6][ 1] = getField().getZero();
+ a[ 6][ 2] = getField().getZero();
+ a[ 6][ 3] = sqrt6.multiply( 4784).add(51544).divide(371293);
+ a[ 6][ 4] = sqrt6.multiply(-4784).add(51544).divide(371293);
+ a[ 6][ 5] = fraction(-5688, 371293);
+ a[ 6][ 6] = fraction( 3072, 371293);
+
+ a[ 7][ 0] = fraction(58656157643.0, 93983540625.0);
+ a[ 7][ 1] = getField().getZero();
+ a[ 7][ 2] = getField().getZero();
+ a[ 7][ 3] = sqrt6.multiply(-318801444819.0).add(-1324889724104.0).divide(626556937500.0);
+ a[ 7][ 4] = sqrt6.multiply( 318801444819.0).add(-1324889724104.0).divide(626556937500.0);
+ a[ 7][ 5] = fraction(96044563816.0, 3480871875.0);
+ a[ 7][ 6] = fraction(5682451879168.0, 281950621875.0);
+ a[ 7][ 7] = fraction(-165125654.0, 3796875.0);
+
+ a[ 8][ 0] = fraction(8909899.0, 18653125.0);
+ a[ 8][ 1] = getField().getZero();
+ a[ 8][ 2] = getField().getZero();
+ a[ 8][ 3] = sqrt6.multiply(-1137963.0).add(-4521408.0).divide(2937500.0);
+ a[ 8][ 4] = sqrt6.multiply( 1137963.0).add(-4521408.0).divide(2937500.0);
+ a[ 8][ 5] = fraction(96663078.0, 4553125.0);
+ a[ 8][ 6] = fraction(2107245056.0, 137915625.0);
+ a[ 8][ 7] = fraction(-4913652016.0, 147609375.0);
+ a[ 8][ 8] = fraction(-78894270.0, 3880452869.0);
+
+ a[ 9][ 0] = fraction(-20401265806.0, 21769653311.0);
+ a[ 9][ 1] = getField().getZero();
+ a[ 9][ 2] = getField().getZero();
+ a[ 9][ 3] = sqrt6.multiply( 94326.0).add(354216.0).divide(112847.0);
+ a[ 9][ 4] = sqrt6.multiply(-94326.0).add(354216.0).divide(112847.0);
+ a[ 9][ 5] = fraction(-43306765128.0, 5313852383.0);
+ a[ 9][ 6] = fraction(-20866708358144.0, 1126708119789.0);
+ a[ 9][ 7] = fraction(14886003438020.0, 654632330667.0);
+ a[ 9][ 8] = fraction(35290686222309375.0, 14152473387134411.0);
+ a[ 9][ 9] = fraction(-1477884375.0, 485066827.0);
+
+ a[10][ 0] = fraction(39815761.0, 17514443.0);
+ a[10][ 1] = getField().getZero();
+ a[10][ 2] = getField().getZero();
+ a[10][ 3] = sqrt6.multiply(-960905.0).add(-3457480.0).divide(551636.0);
+ a[10][ 4] = sqrt6.multiply( 960905.0).add(-3457480.0).divide(551636.0);
+ a[10][ 5] = fraction(-844554132.0, 47026969.0);
+ a[10][ 6] = fraction(8444996352.0, 302158619.0);
+ a[10][ 7] = fraction(-2509602342.0, 877790785.0);
+ a[10][ 8] = fraction(-28388795297996250.0, 3199510091356783.0);
+ a[10][ 9] = fraction(226716250.0, 18341897.0);
+ a[10][10] = fraction(1371316744.0, 2131383595.0);
+
+ // the following stage is both for interpolation and the first stage in next step
+ // (the coefficients are identical to the B array)
+ a[11][ 0] = fraction(104257.0, 1920240.0);
+ a[11][ 1] = getField().getZero();
+ a[11][ 2] = getField().getZero();
+ a[11][ 3] = getField().getZero();
+ a[11][ 4] = getField().getZero();
+ a[11][ 5] = fraction(3399327.0, 763840.0);
+ a[11][ 6] = fraction(66578432.0, 35198415.0);
+ a[11][ 7] = fraction(-1674902723.0, 288716400.0);
+ a[11][ 8] = fraction(54980371265625.0, 176692375811392.0);
+ a[11][ 9] = fraction(-734375.0, 4826304.0);
+ a[11][10] = fraction(171414593.0, 851261400.0);
+ a[11][11] = fraction(137909.0, 3084480.0);
+
+ // the following stages are for interpolation only
+ a[12][ 0] = fraction( 13481885573.0, 240030000000.0);
+ a[12][ 1] = getField().getZero();
+ a[12][ 2] = getField().getZero();
+ a[12][ 3] = getField().getZero();
+ a[12][ 4] = getField().getZero();
+ a[12][ 5] = getField().getZero();
+ a[12][ 6] = fraction( 139418837528.0, 549975234375.0);
+ a[12][ 7] = fraction( -11108320068443.0, 45111937500000.0);
+ a[12][ 8] = fraction(-1769651421925959.0, 14249385146080000.0);
+ a[12][ 9] = fraction( 57799439.0, 377055000.0);
+ a[12][10] = fraction( 793322643029.0, 96734250000000.0);
+ a[12][11] = fraction( 1458939311.0, 192780000000.0);
+ a[12][12] = fraction( -4149.0, 500000.0);
+
+ a[13][ 0] = fraction( 1595561272731.0, 50120273500000.0);
+ a[13][ 1] = getField().getZero();
+ a[13][ 2] = getField().getZero();
+ a[13][ 3] = getField().getZero();
+ a[13][ 4] = getField().getZero();
+ a[13][ 5] = fraction( 975183916491.0, 34457688031250.0);
+ a[13][ 6] = fraction( 38492013932672.0, 718912673015625.0);
+ a[13][ 7] = fraction(-1114881286517557.0, 20298710767500000.0);
+ a[13][ 8] = getField().getZero();
+ a[13][ 9] = getField().getZero();
+ a[13][10] = fraction( -2538710946863.0, 23431227861250000.0);
+ a[13][11] = fraction( 8824659001.0, 23066716781250.0);
+ a[13][12] = fraction( -11518334563.0, 33831184612500.0);
+ a[13][13] = fraction( 1912306948.0, 13532473845.0);
+
+ a[14][ 0] = fraction( -13613986967.0, 31741908048.0);
+ a[14][ 1] = getField().getZero();
+ a[14][ 2] = getField().getZero();
+ a[14][ 3] = getField().getZero();
+ a[14][ 4] = getField().getZero();
+ a[14][ 5] = fraction( -4755612631.0, 1012344804.0);
+ a[14][ 6] = fraction( 42939257944576.0, 5588559685701.0);
+ a[14][ 7] = fraction( 77881972900277.0, 19140370552944.0);
+ a[14][ 8] = fraction( 22719829234375.0, 63689648654052.0);
+ a[14][ 9] = getField().getZero();
+ a[14][10] = getField().getZero();
+ a[14][11] = getField().getZero();
+ a[14][12] = fraction( -1199007803.0, 857031517296.0);
+ a[14][13] = fraction( 157882067000.0, 53564469831.0);
+ a[14][14] = fraction( -290468882375.0, 31741908048.0);
+
+ return a;
+
+ }
+
+ /** {@inheritDoc} */
+ public T[] getB() {
+ final T[] b = MathArrays.buildArray(getField(), 16);
+ b[ 0] = fraction(104257, 1920240);
+ b[ 1] = getField().getZero();
+ b[ 2] = getField().getZero();
+ b[ 3] = getField().getZero();
+ b[ 4] = getField().getZero();
+ b[ 5] = fraction( 3399327.0, 763840.0);
+ b[ 6] = fraction( 66578432.0, 35198415.0);
+ b[ 7] = fraction( -1674902723.0, 288716400.0);
+ b[ 8] = fraction( 54980371265625.0, 176692375811392.0);
+ b[ 9] = fraction( -734375.0, 4826304.0);
+ b[10] = fraction( 171414593.0, 851261400.0);
+ b[11] = fraction( 137909.0, 3084480.0);
+ b[12] = getField().getZero();
+ b[13] = getField().getZero();
+ b[14] = getField().getZero();
+ b[15] = getField().getZero();
+ return b;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected DormandPrince853FieldStepInterpolator<T>
+ createInterpolator(final boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState, final FieldEquationsMapper<T> mapper) {
+ return new DormandPrince853FieldStepInterpolator<T>(getField(), forward, yDotK,
+ globalPreviousState, globalCurrentState,
+ globalPreviousState, globalCurrentState,
+ mapper);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getOrder() {
+ return 8;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected T estimateError(final T[][] yDotK, final T[] y0, final T[] y1, final T h) {
+ T error1 = h.getField().getZero();
+ T error2 = h.getField().getZero();
+
+ for (int j = 0; j < mainSetDimension; ++j) {
+ final T errSum1 = yDotK[ 0][j].multiply(e1_01).
+ add(yDotK[ 5][j].multiply(e1_06)).
+ add(yDotK[ 6][j].multiply(e1_07)).
+ add(yDotK[ 7][j].multiply(e1_08)).
+ add(yDotK[ 8][j].multiply(e1_09)).
+ add(yDotK[ 9][j].multiply(e1_10)).
+ add(yDotK[10][j].multiply(e1_11)).
+ add(yDotK[11][j].multiply(e1_12));
+ final T errSum2 = yDotK[ 0][j].multiply(e2_01).
+ add(yDotK[ 5][j].multiply(e2_06)).
+ add(yDotK[ 6][j].multiply(e2_07)).
+ add(yDotK[ 7][j].multiply(e2_08)).
+ add(yDotK[ 8][j].multiply(e2_09)).
+ add(yDotK[ 9][j].multiply(e2_10)).
+ add(yDotK[10][j].multiply(e2_11)).
+ add(yDotK[11][j].multiply(e2_12));
+
+ final T yScale = MathUtils.max(y0[j].abs(), y1[j].abs());
+ final T tol = vecAbsoluteTolerance == null ?
+ yScale.multiply(scalRelativeTolerance).add(scalAbsoluteTolerance) :
+ yScale.multiply(vecRelativeTolerance[j]).add(vecAbsoluteTolerance[j]);
+ final T ratio1 = errSum1.divide(tol);
+ error1 = error1.add(ratio1.multiply(ratio1));
+ final T ratio2 = errSum2.divide(tol);
+ error2 = error2.add(ratio2.multiply(ratio2));
+ }
+
+ T den = error1.add(error2.multiply(0.01));
+ if (den.getReal() <= 0.0) {
+ den = h.getField().getOne();
+ }
+
+ return h.abs().multiply(error1).divide(den.multiply(mainSetDimension).sqrt());
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853FieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853FieldStepInterpolator.java
new file mode 100644
index 0000000..c706e2a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853FieldStepInterpolator.java
@@ -0,0 +1,302 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class represents an interpolator over the last step during an
+ * ODE integration for the 8(5,3) Dormand-Prince integrator.
+ *
+ * @see DormandPrince853FieldIntegrator
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+class DormandPrince853FieldStepInterpolator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldStepInterpolator<T> {
+
+ /** Interpolation weights.
+ * (beware that only the non-null values are in the table)
+ */
+ private final T[][] d;
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param mapper equations mapper for the all equations
+ */
+ DormandPrince853FieldStepInterpolator(final Field<T> field, final boolean forward,
+ final T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ super(field, forward, yDotK,
+ globalPreviousState, globalCurrentState, softPreviousState, softCurrentState,
+ mapper);
+ // interpolation weights
+ d = MathArrays.buildArray(field, 7, 16);
+
+ // this row is the same as the b array
+ d[0][ 0] = fraction(field, 104257, 1920240);
+ d[0][ 1] = field.getZero();
+ d[0][ 2] = field.getZero();
+ d[0][ 3] = field.getZero();
+ d[0][ 4] = field.getZero();
+ d[0][ 5] = fraction(field, 3399327.0, 763840.0);
+ d[0][ 6] = fraction(field, 66578432.0, 35198415.0);
+ d[0][ 7] = fraction(field, -1674902723.0, 288716400.0);
+ d[0][ 8] = fraction(field, 54980371265625.0, 176692375811392.0);
+ d[0][ 9] = fraction(field, -734375.0, 4826304.0);
+ d[0][10] = fraction(field, 171414593.0, 851261400.0);
+ d[0][11] = fraction(field, 137909.0, 3084480.0);
+ d[0][12] = field.getZero();
+ d[0][13] = field.getZero();
+ d[0][14] = field.getZero();
+ d[0][15] = field.getZero();
+
+ d[1][ 0] = d[0][ 0].negate().add(1);
+ d[1][ 1] = d[0][ 1].negate();
+ d[1][ 2] = d[0][ 2].negate();
+ d[1][ 3] = d[0][ 3].negate();
+ d[1][ 4] = d[0][ 4].negate();
+ d[1][ 5] = d[0][ 5].negate();
+ d[1][ 6] = d[0][ 6].negate();
+ d[1][ 7] = d[0][ 7].negate();
+ d[1][ 8] = d[0][ 8].negate();
+ d[1][ 9] = d[0][ 9].negate();
+ d[1][10] = d[0][10].negate();
+ d[1][11] = d[0][11].negate();
+ d[1][12] = d[0][12].negate(); // really 0
+ d[1][13] = d[0][13].negate(); // really 0
+ d[1][14] = d[0][14].negate(); // really 0
+ d[1][15] = d[0][15].negate(); // really 0
+
+ d[2][ 0] = d[0][ 0].multiply(2).subtract(1);
+ d[2][ 1] = d[0][ 1].multiply(2);
+ d[2][ 2] = d[0][ 2].multiply(2);
+ d[2][ 3] = d[0][ 3].multiply(2);
+ d[2][ 4] = d[0][ 4].multiply(2);
+ d[2][ 5] = d[0][ 5].multiply(2);
+ d[2][ 6] = d[0][ 6].multiply(2);
+ d[2][ 7] = d[0][ 7].multiply(2);
+ d[2][ 8] = d[0][ 8].multiply(2);
+ d[2][ 9] = d[0][ 9].multiply(2);
+ d[2][10] = d[0][10].multiply(2);
+ d[2][11] = d[0][11].multiply(2);
+ d[2][12] = d[0][12].multiply(2).subtract(1); // really -1
+ d[2][13] = d[0][13].multiply(2); // really 0
+ d[2][14] = d[0][14].multiply(2); // really 0
+ d[2][15] = d[0][15].multiply(2); // really 0
+
+ d[3][ 0] = fraction(field, -17751989329.0, 2106076560.0);
+ d[3][ 1] = field.getZero();
+ d[3][ 2] = field.getZero();
+ d[3][ 3] = field.getZero();
+ d[3][ 4] = field.getZero();
+ d[3][ 5] = fraction(field, 4272954039.0, 7539864640.0);
+ d[3][ 6] = fraction(field, -118476319744.0, 38604839385.0);
+ d[3][ 7] = fraction(field, 755123450731.0, 316657731600.0);
+ d[3][ 8] = fraction(field, 3692384461234828125.0, 1744130441634250432.0);
+ d[3][ 9] = fraction(field, -4612609375.0, 5293382976.0);
+ d[3][10] = fraction(field, 2091772278379.0, 933644586600.0);
+ d[3][11] = fraction(field, 2136624137.0, 3382989120.0);
+ d[3][12] = fraction(field, -126493.0, 1421424.0);
+ d[3][13] = fraction(field, 98350000.0, 5419179.0);
+ d[3][14] = fraction(field, -18878125.0, 2053168.0);
+ d[3][15] = fraction(field, -1944542619.0, 438351368.0);
+
+ d[4][ 0] = fraction(field, 32941697297.0, 3159114840.0);
+ d[4][ 1] = field.getZero();
+ d[4][ 2] = field.getZero();
+ d[4][ 3] = field.getZero();
+ d[4][ 4] = field.getZero();
+ d[4][ 5] = fraction(field, 456696183123.0, 1884966160.0);
+ d[4][ 6] = fraction(field, 19132610714624.0, 115814518155.0);
+ d[4][ 7] = fraction(field, -177904688592943.0, 474986597400.0);
+ d[4][ 8] = fraction(field, -4821139941836765625.0, 218016305204281304.0);
+ d[4][ 9] = fraction(field, 30702015625.0, 3970037232.0);
+ d[4][10] = fraction(field, -85916079474274.0, 2800933759800.0);
+ d[4][11] = fraction(field, -5919468007.0, 634310460.0);
+ d[4][12] = fraction(field, 2479159.0, 157936.0);
+ d[4][13] = fraction(field, -18750000.0, 602131.0);
+ d[4][14] = fraction(field, -19203125.0, 2053168.0);
+ d[4][15] = fraction(field, 15700361463.0, 438351368.0);
+
+ d[5][ 0] = fraction(field, 12627015655.0, 631822968.0);
+ d[5][ 1] = field.getZero();
+ d[5][ 2] = field.getZero();
+ d[5][ 3] = field.getZero();
+ d[5][ 4] = field.getZero();
+ d[5][ 5] = fraction(field, -72955222965.0, 188496616.0);
+ d[5][ 6] = fraction(field, -13145744952320.0, 69488710893.0);
+ d[5][ 7] = fraction(field, 30084216194513.0, 56998391688.0);
+ d[5][ 8] = fraction(field, -296858761006640625.0, 25648977082856624.0);
+ d[5][ 9] = fraction(field, 569140625.0, 82709109.0);
+ d[5][10] = fraction(field, -18684190637.0, 18672891732.0);
+ d[5][11] = fraction(field, 69644045.0, 89549712.0);
+ d[5][12] = fraction(field, -11847025.0, 4264272.0);
+ d[5][13] = fraction(field, -978650000.0, 16257537.0);
+ d[5][14] = fraction(field, 519371875.0, 6159504.0);
+ d[5][15] = fraction(field, 5256837225.0, 438351368.0);
+
+ d[6][ 0] = fraction(field, -450944925.0, 17550638.0);
+ d[6][ 1] = field.getZero();
+ d[6][ 2] = field.getZero();
+ d[6][ 3] = field.getZero();
+ d[6][ 4] = field.getZero();
+ d[6][ 5] = fraction(field, -14532122925.0, 94248308.0);
+ d[6][ 6] = fraction(field, -595876966400.0, 2573655959.0);
+ d[6][ 7] = fraction(field, 188748653015.0, 527762886.0);
+ d[6][ 8] = fraction(field, 2545485458115234375.0, 27252038150535163.0);
+ d[6][ 9] = fraction(field, -1376953125.0, 36759604.0);
+ d[6][10] = fraction(field, 53995596795.0, 518691437.0);
+ d[6][11] = fraction(field, 210311225.0, 7047894.0);
+ d[6][12] = fraction(field, -1718875.0, 39484.0);
+ d[6][13] = fraction(field, 58000000.0, 602131.0);
+ d[6][14] = fraction(field, -1546875.0, 39484.0);
+ d[6][15] = fraction(field, -1262172375.0, 8429834.0);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected DormandPrince853FieldStepInterpolator<T> create(final Field<T> newField, final boolean newForward, final T[][] newYDotK,
+ final FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ final FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ final FieldODEStateAndDerivative<T> newSoftPreviousState,
+ final FieldODEStateAndDerivative<T> newSoftCurrentState,
+ final FieldEquationsMapper<T> newMapper) {
+ return new DormandPrince853FieldStepInterpolator<T>(newField, newForward, newYDotK,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+ }
+
+ /** Create a fraction.
+ * @param field field to which the elements belong
+ * @param p numerator
+ * @param q denominator
+ * @return p/q computed in the instance field
+ */
+ private T fraction(final Field<T> field, final double p, final double q) {
+ return field.getZero().add(p).divide(q);
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(final FieldEquationsMapper<T> mapper,
+ final T time, final T theta,
+ final T thetaH, final T oneMinusThetaH)
+ throws MaxCountExceededException {
+
+ final T one = time.getField().getOne();
+ final T eta = one.subtract(theta);
+ final T twoTheta = theta.multiply(2);
+ final T theta2 = theta.multiply(theta);
+ final T dot1 = one.subtract(twoTheta);
+ final T dot2 = theta.multiply(theta.multiply(-3).add(2));
+ final T dot3 = twoTheta.multiply(theta.multiply(twoTheta.subtract(3)).add(1));
+ final T dot4 = theta2.multiply(theta.multiply(theta.multiply(5).subtract(8)).add(3));
+ final T dot5 = theta2.multiply(theta.multiply(theta.multiply(theta.multiply(-6).add(15)).subtract(12)).add(3));
+ final T dot6 = theta2.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply(-7).add(18)).subtract(15)).add(4)));
+ final T[] interpolatedState;
+ final T[] interpolatedDerivatives;
+
+
+ if (getGlobalPreviousState() != null && theta.getReal() <= 0.5) {
+ final T f0 = thetaH;
+ final T f1 = f0.multiply(eta);
+ final T f2 = f1.multiply(theta);
+ final T f3 = f2.multiply(eta);
+ final T f4 = f3.multiply(theta);
+ final T f5 = f4.multiply(eta);
+ final T f6 = f5.multiply(theta);
+ final T[] p = MathArrays.buildArray(time.getField(), 16);
+ final T[] q = MathArrays.buildArray(time.getField(), 16);
+ for (int i = 0; i < p.length; ++i) {
+ p[i] = f0.multiply(d[0][i]).
+ add(f1.multiply(d[1][i])).
+ add(f2.multiply(d[2][i])).
+ add(f3.multiply(d[3][i])).
+ add(f4.multiply(d[4][i])).
+ add(f5.multiply(d[5][i])).
+ add(f6.multiply(d[6][i]));
+ q[i] = d[0][i].
+ add(dot1.multiply(d[1][i])).
+ add(dot2.multiply(d[2][i])).
+ add(dot3.multiply(d[3][i])).
+ add(dot4.multiply(d[4][i])).
+ add(dot5.multiply(d[5][i])).
+ add(dot6.multiply(d[6][i]));
+ }
+ interpolatedState = previousStateLinearCombination(p[0], p[1], p[ 2], p[ 3], p[ 4], p[ 5], p[ 6], p[ 7],
+ p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+ interpolatedDerivatives = derivativeLinearCombination(q[0], q[1], q[ 2], q[ 3], q[ 4], q[ 5], q[ 6], q[ 7],
+ q[8], q[9], q[10], q[11], q[12], q[13], q[14], q[15]);
+ } else {
+ final T f0 = oneMinusThetaH.negate();
+ final T f1 = f0.multiply(theta).negate();
+ final T f2 = f1.multiply(theta);
+ final T f3 = f2.multiply(eta);
+ final T f4 = f3.multiply(theta);
+ final T f5 = f4.multiply(eta);
+ final T f6 = f5.multiply(theta);
+ final T[] p = MathArrays.buildArray(time.getField(), 16);
+ final T[] q = MathArrays.buildArray(time.getField(), 16);
+ for (int i = 0; i < p.length; ++i) {
+ p[i] = f0.multiply(d[0][i]).
+ add(f1.multiply(d[1][i])).
+ add(f2.multiply(d[2][i])).
+ add(f3.multiply(d[3][i])).
+ add(f4.multiply(d[4][i])).
+ add(f5.multiply(d[5][i])).
+ add(f6.multiply(d[6][i]));
+ q[i] = d[0][i].
+ add(dot1.multiply(d[1][i])).
+ add(dot2.multiply(d[2][i])).
+ add(dot3.multiply(d[3][i])).
+ add(dot4.multiply(d[4][i])).
+ add(dot5.multiply(d[5][i])).
+ add(dot6.multiply(d[6][i]));
+ }
+ interpolatedState = currentStateLinearCombination(p[0], p[1], p[ 2], p[ 3], p[ 4], p[ 5], p[ 6], p[ 7],
+ p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+ interpolatedDerivatives = derivativeLinearCombination(q[0], q[1], q[ 2], q[ 3], q[ 4], q[ 5], q[ 6], q[ 7],
+ q[8], q[9], q[10], q[11], q[12], q[13], q[14], q[15]);
+ }
+
+ return new FieldODEStateAndDerivative<T>(time, interpolatedState, interpolatedDerivatives);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853Integrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853Integrator.java
new file mode 100644
index 0000000..895cb88
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853Integrator.java
@@ -0,0 +1,286 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.util.FastMath;
+
+
+/**
+ * This class implements the 8(5,3) Dormand-Prince integrator for Ordinary
+ * Differential Equations.
+ *
+ * <p>This integrator is an embedded Runge-Kutta integrator
+ * of order 8(5,3) used in local extrapolation mode (i.e. the solution
+ * is computed using the high order formula) with stepsize control
+ * (and automatic step initialization) and continuous output. This
+ * method uses 12 functions evaluations per step for integration and 4
+ * evaluations for interpolation. However, since the first
+ * interpolation evaluation is the same as the first integration
+ * evaluation of the next step, we have included it in the integrator
+ * rather than in the interpolator and specified the method was an
+ * <i>fsal</i>. Hence, despite we have 13 stages here, the cost is
+ * really 12 evaluations per step even if no interpolation is done,
+ * and the overcost of interpolation is only 3 evaluations.</p>
+ *
+ * <p>This method is based on an 8(6) method by Dormand and Prince
+ * (i.e. order 8 for the integration and order 6 for error estimation)
+ * modified by Hairer and Wanner to use a 5th order error estimator
+ * with 3rd order correction. This modification was introduced because
+ * the original method failed in some cases (wrong steps can be
+ * accepted when step size is too large, for example in the
+ * Brusselator problem) and also had <i>severe difficulties when
+ * applied to problems with discontinuities</i>. This modification is
+ * explained in the second edition of the first volume (Nonstiff
+ * Problems) of the reference book by Hairer, Norsett and Wanner:
+ * <i>Solving Ordinary Differential Equations</i> (Springer-Verlag,
+ * ISBN 3-540-56670-8).</p>
+ *
+ * @since 1.2
+ */
+
+public class DormandPrince853Integrator extends EmbeddedRungeKuttaIntegrator {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Dormand-Prince 8 (5, 3)";
+
+ /** Time steps Butcher array. */
+ private static final double[] STATIC_C = {
+ (12.0 - 2.0 * FastMath.sqrt(6.0)) / 135.0, (6.0 - FastMath.sqrt(6.0)) / 45.0, (6.0 - FastMath.sqrt(6.0)) / 30.0,
+ (6.0 + FastMath.sqrt(6.0)) / 30.0, 1.0/3.0, 1.0/4.0, 4.0/13.0, 127.0/195.0, 3.0/5.0,
+ 6.0/7.0, 1.0, 1.0
+ };
+
+ /** Internal weights Butcher array. */
+ private static final double[][] STATIC_A = {
+
+ // k2
+ {(12.0 - 2.0 * FastMath.sqrt(6.0)) / 135.0},
+
+ // k3
+ {(6.0 - FastMath.sqrt(6.0)) / 180.0, (6.0 - FastMath.sqrt(6.0)) / 60.0},
+
+ // k4
+ {(6.0 - FastMath.sqrt(6.0)) / 120.0, 0.0, (6.0 - FastMath.sqrt(6.0)) / 40.0},
+
+ // k5
+ {(462.0 + 107.0 * FastMath.sqrt(6.0)) / 3000.0, 0.0,
+ (-402.0 - 197.0 * FastMath.sqrt(6.0)) / 1000.0, (168.0 + 73.0 * FastMath.sqrt(6.0)) / 375.0},
+
+ // k6
+ {1.0 / 27.0, 0.0, 0.0, (16.0 + FastMath.sqrt(6.0)) / 108.0, (16.0 - FastMath.sqrt(6.0)) / 108.0},
+
+ // k7
+ {19.0 / 512.0, 0.0, 0.0, (118.0 + 23.0 * FastMath.sqrt(6.0)) / 1024.0,
+ (118.0 - 23.0 * FastMath.sqrt(6.0)) / 1024.0, -9.0 / 512.0},
+
+ // k8
+ {13772.0 / 371293.0, 0.0, 0.0, (51544.0 + 4784.0 * FastMath.sqrt(6.0)) / 371293.0,
+ (51544.0 - 4784.0 * FastMath.sqrt(6.0)) / 371293.0, -5688.0 / 371293.0, 3072.0 / 371293.0},
+
+ // k9
+ {58656157643.0 / 93983540625.0, 0.0, 0.0,
+ (-1324889724104.0 - 318801444819.0 * FastMath.sqrt(6.0)) / 626556937500.0,
+ (-1324889724104.0 + 318801444819.0 * FastMath.sqrt(6.0)) / 626556937500.0,
+ 96044563816.0 / 3480871875.0, 5682451879168.0 / 281950621875.0,
+ -165125654.0 / 3796875.0},
+
+ // k10
+ {8909899.0 / 18653125.0, 0.0, 0.0,
+ (-4521408.0 - 1137963.0 * FastMath.sqrt(6.0)) / 2937500.0,
+ (-4521408.0 + 1137963.0 * FastMath.sqrt(6.0)) / 2937500.0,
+ 96663078.0 / 4553125.0, 2107245056.0 / 137915625.0,
+ -4913652016.0 / 147609375.0, -78894270.0 / 3880452869.0},
+
+ // k11
+ {-20401265806.0 / 21769653311.0, 0.0, 0.0,
+ (354216.0 + 94326.0 * FastMath.sqrt(6.0)) / 112847.0,
+ (354216.0 - 94326.0 * FastMath.sqrt(6.0)) / 112847.0,
+ -43306765128.0 / 5313852383.0, -20866708358144.0 / 1126708119789.0,
+ 14886003438020.0 / 654632330667.0, 35290686222309375.0 / 14152473387134411.0,
+ -1477884375.0 / 485066827.0},
+
+ // k12
+ {39815761.0 / 17514443.0, 0.0, 0.0,
+ (-3457480.0 - 960905.0 * FastMath.sqrt(6.0)) / 551636.0,
+ (-3457480.0 + 960905.0 * FastMath.sqrt(6.0)) / 551636.0,
+ -844554132.0 / 47026969.0, 8444996352.0 / 302158619.0,
+ -2509602342.0 / 877790785.0, -28388795297996250.0 / 3199510091356783.0,
+ 226716250.0 / 18341897.0, 1371316744.0 / 2131383595.0},
+
+ // k13 should be for interpolation only, but since it is the same
+ // stage as the first evaluation of the next step, we perform it
+ // here at no cost by specifying this is an fsal method
+ {104257.0/1920240.0, 0.0, 0.0, 0.0, 0.0, 3399327.0/763840.0,
+ 66578432.0/35198415.0, -1674902723.0/288716400.0,
+ 54980371265625.0/176692375811392.0, -734375.0/4826304.0,
+ 171414593.0/851261400.0, 137909.0/3084480.0}
+
+ };
+
+ /** Propagation weights Butcher array. */
+ private static final double[] STATIC_B = {
+ 104257.0/1920240.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ 3399327.0/763840.0,
+ 66578432.0/35198415.0,
+ -1674902723.0/288716400.0,
+ 54980371265625.0/176692375811392.0,
+ -734375.0/4826304.0,
+ 171414593.0/851261400.0,
+ 137909.0/3084480.0,
+ 0.0
+ };
+
+ /** First error weights array, element 1. */
+ private static final double E1_01 = 116092271.0 / 8848465920.0;
+
+ // elements 2 to 5 are zero, so they are neither stored nor used
+
+ /** First error weights array, element 6. */
+ private static final double E1_06 = -1871647.0 / 1527680.0;
+
+ /** First error weights array, element 7. */
+ private static final double E1_07 = -69799717.0 / 140793660.0;
+
+ /** First error weights array, element 8. */
+ private static final double E1_08 = 1230164450203.0 / 739113984000.0;
+
+ /** First error weights array, element 9. */
+ private static final double E1_09 = -1980813971228885.0 / 5654156025964544.0;
+
+ /** First error weights array, element 10. */
+ private static final double E1_10 = 464500805.0 / 1389975552.0;
+
+ /** First error weights array, element 11. */
+ private static final double E1_11 = 1606764981773.0 / 19613062656000.0;
+
+ /** First error weights array, element 12. */
+ private static final double E1_12 = -137909.0 / 6168960.0;
+
+
+ /** Second error weights array, element 1. */
+ private static final double E2_01 = -364463.0 / 1920240.0;
+
+ // elements 2 to 5 are zero, so they are neither stored nor used
+
+ /** Second error weights array, element 6. */
+ private static final double E2_06 = 3399327.0 / 763840.0;
+
+ /** Second error weights array, element 7. */
+ private static final double E2_07 = 66578432.0 / 35198415.0;
+
+ /** Second error weights array, element 8. */
+ private static final double E2_08 = -1674902723.0 / 288716400.0;
+
+ /** Second error weights array, element 9. */
+ private static final double E2_09 = -74684743568175.0 / 176692375811392.0;
+
+ /** Second error weights array, element 10. */
+ private static final double E2_10 = -734375.0 / 4826304.0;
+
+ /** Second error weights array, element 11. */
+ private static final double E2_11 = 171414593.0 / 851261400.0;
+
+ /** Second error weights array, element 12. */
+ private static final double E2_12 = 69869.0 / 3084480.0;
+
+ /** Simple constructor.
+ * Build an eighth order Dormand-Prince integrator with the given step bounds
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ public DormandPrince853Integrator(final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+ super(METHOD_NAME, true, STATIC_C, STATIC_A, STATIC_B,
+ new DormandPrince853StepInterpolator(),
+ minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+ }
+
+ /** Simple constructor.
+ * Build an eighth order Dormand-Prince integrator with the given step bounds
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ public DormandPrince853Integrator(final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+ super(METHOD_NAME, true, STATIC_C, STATIC_A, STATIC_B,
+ new DormandPrince853StepInterpolator(),
+ minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getOrder() {
+ return 8;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double estimateError(final double[][] yDotK,
+ final double[] y0, final double[] y1,
+ final double h) {
+ double error1 = 0;
+ double error2 = 0;
+
+ for (int j = 0; j < mainSetDimension; ++j) {
+ final double errSum1 = E1_01 * yDotK[0][j] + E1_06 * yDotK[5][j] +
+ E1_07 * yDotK[6][j] + E1_08 * yDotK[7][j] +
+ E1_09 * yDotK[8][j] + E1_10 * yDotK[9][j] +
+ E1_11 * yDotK[10][j] + E1_12 * yDotK[11][j];
+ final double errSum2 = E2_01 * yDotK[0][j] + E2_06 * yDotK[5][j] +
+ E2_07 * yDotK[6][j] + E2_08 * yDotK[7][j] +
+ E2_09 * yDotK[8][j] + E2_10 * yDotK[9][j] +
+ E2_11 * yDotK[10][j] + E2_12 * yDotK[11][j];
+
+ final double yScale = FastMath.max(FastMath.abs(y0[j]), FastMath.abs(y1[j]));
+ final double tol = (vecAbsoluteTolerance == null) ?
+ (scalAbsoluteTolerance + scalRelativeTolerance * yScale) :
+ (vecAbsoluteTolerance[j] + vecRelativeTolerance[j] * yScale);
+ final double ratio1 = errSum1 / tol;
+ error1 += ratio1 * ratio1;
+ final double ratio2 = errSum2 / tol;
+ error2 += ratio2 * ratio2;
+ }
+
+ double den = error1 + 0.01 * error2;
+ if (den <= 0.0) {
+ den = 1.0;
+ }
+
+ return FastMath.abs(h) * error1 / FastMath.sqrt(mainSetDimension * den);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853StepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853StepInterpolator.java
new file mode 100644
index 0000000..6d6ff6c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/DormandPrince853StepInterpolator.java
@@ -0,0 +1,501 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.ode.AbstractIntegrator;
+import org.apache.commons.math3.ode.EquationsMapper;
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+
+/**
+ * This class represents an interpolator over the last step during an
+ * ODE integration for the 8(5,3) Dormand-Prince integrator.
+ *
+ * @see DormandPrince853Integrator
+ *
+ * @since 1.2
+ */
+
+class DormandPrince853StepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20111120L;
+
+ /** Propagation weights, element 1. */
+ private static final double B_01 = 104257.0 / 1920240.0;
+
+ // elements 2 to 5 are zero, so they are neither stored nor used
+
+ /** Propagation weights, element 6. */
+ private static final double B_06 = 3399327.0 / 763840.0;
+
+ /** Propagation weights, element 7. */
+ private static final double B_07 = 66578432.0 / 35198415.0;
+
+ /** Propagation weights, element 8. */
+ private static final double B_08 = -1674902723.0 / 288716400.0;
+
+ /** Propagation weights, element 9. */
+ private static final double B_09 = 54980371265625.0 / 176692375811392.0;
+
+ /** Propagation weights, element 10. */
+ private static final double B_10 = -734375.0 / 4826304.0;
+
+ /** Propagation weights, element 11. */
+ private static final double B_11 = 171414593.0 / 851261400.0;
+
+ /** Propagation weights, element 12. */
+ private static final double B_12 = 137909.0 / 3084480.0;
+
+ /** Time step for stage 14 (interpolation only). */
+ private static final double C14 = 1.0 / 10.0;
+
+ /** Internal weights for stage 14, element 1. */
+ private static final double K14_01 = 13481885573.0 / 240030000000.0 - B_01;
+
+ // elements 2 to 5 are zero, so they are neither stored nor used
+
+ /** Internal weights for stage 14, element 6. */
+ private static final double K14_06 = 0.0 - B_06;
+
+ /** Internal weights for stage 14, element 7. */
+ private static final double K14_07 = 139418837528.0 / 549975234375.0 - B_07;
+
+ /** Internal weights for stage 14, element 8. */
+ private static final double K14_08 = -11108320068443.0 / 45111937500000.0 - B_08;
+
+ /** Internal weights for stage 14, element 9. */
+ private static final double K14_09 = -1769651421925959.0 / 14249385146080000.0 - B_09;
+
+ /** Internal weights for stage 14, element 10. */
+ private static final double K14_10 = 57799439.0 / 377055000.0 - B_10;
+
+ /** Internal weights for stage 14, element 11. */
+ private static final double K14_11 = 793322643029.0 / 96734250000000.0 - B_11;
+
+ /** Internal weights for stage 14, element 12. */
+ private static final double K14_12 = 1458939311.0 / 192780000000.0 - B_12;
+
+ /** Internal weights for stage 14, element 13. */
+ private static final double K14_13 = -4149.0 / 500000.0;
+
+ /** Time step for stage 15 (interpolation only). */
+ private static final double C15 = 1.0 / 5.0;
+
+
+ /** Internal weights for stage 15, element 1. */
+ private static final double K15_01 = 1595561272731.0 / 50120273500000.0 - B_01;
+
+ // elements 2 to 5 are zero, so they are neither stored nor used
+
+ /** Internal weights for stage 15, element 6. */
+ private static final double K15_06 = 975183916491.0 / 34457688031250.0 - B_06;
+
+ /** Internal weights for stage 15, element 7. */
+ private static final double K15_07 = 38492013932672.0 / 718912673015625.0 - B_07;
+
+ /** Internal weights for stage 15, element 8. */
+ private static final double K15_08 = -1114881286517557.0 / 20298710767500000.0 - B_08;
+
+ /** Internal weights for stage 15, element 9. */
+ private static final double K15_09 = 0.0 - B_09;
+
+ /** Internal weights for stage 15, element 10. */
+ private static final double K15_10 = 0.0 - B_10;
+
+ /** Internal weights for stage 15, element 11. */
+ private static final double K15_11 = -2538710946863.0 / 23431227861250000.0 - B_11;
+
+ /** Internal weights for stage 15, element 12. */
+ private static final double K15_12 = 8824659001.0 / 23066716781250.0 - B_12;
+
+ /** Internal weights for stage 15, element 13. */
+ private static final double K15_13 = -11518334563.0 / 33831184612500.0;
+
+ /** Internal weights for stage 15, element 14. */
+ private static final double K15_14 = 1912306948.0 / 13532473845.0;
+
+ /** Time step for stage 16 (interpolation only). */
+ private static final double C16 = 7.0 / 9.0;
+
+
+ /** Internal weights for stage 16, element 1. */
+ private static final double K16_01 = -13613986967.0 / 31741908048.0 - B_01;
+
+ // elements 2 to 5 are zero, so they are neither stored nor used
+
+ /** Internal weights for stage 16, element 6. */
+ private static final double K16_06 = -4755612631.0 / 1012344804.0 - B_06;
+
+ /** Internal weights for stage 16, element 7. */
+ private static final double K16_07 = 42939257944576.0 / 5588559685701.0 - B_07;
+
+ /** Internal weights for stage 16, element 8. */
+ private static final double K16_08 = 77881972900277.0 / 19140370552944.0 - B_08;
+
+ /** Internal weights for stage 16, element 9. */
+ private static final double K16_09 = 22719829234375.0 / 63689648654052.0 - B_09;
+
+ /** Internal weights for stage 16, element 10. */
+ private static final double K16_10 = 0.0 - B_10;
+
+ /** Internal weights for stage 16, element 11. */
+ private static final double K16_11 = 0.0 - B_11;
+
+ /** Internal weights for stage 16, element 12. */
+ private static final double K16_12 = 0.0 - B_12;
+
+ /** Internal weights for stage 16, element 13. */
+ private static final double K16_13 = -1199007803.0 / 857031517296.0;
+
+ /** Internal weights for stage 16, element 14. */
+ private static final double K16_14 = 157882067000.0 / 53564469831.0;
+
+ /** Internal weights for stage 16, element 15. */
+ private static final double K16_15 = -290468882375.0 / 31741908048.0;
+
+ /** Interpolation weights.
+ * (beware that only the non-null values are in the table)
+ */
+ private static final double[][] D = {
+
+ { -17751989329.0 / 2106076560.0, 4272954039.0 / 7539864640.0,
+ -118476319744.0 / 38604839385.0, 755123450731.0 / 316657731600.0,
+ 3692384461234828125.0 / 1744130441634250432.0, -4612609375.0 / 5293382976.0,
+ 2091772278379.0 / 933644586600.0, 2136624137.0 / 3382989120.0,
+ -126493.0 / 1421424.0, 98350000.0 / 5419179.0,
+ -18878125.0 / 2053168.0, -1944542619.0 / 438351368.0},
+
+ { 32941697297.0 / 3159114840.0, 456696183123.0 / 1884966160.0,
+ 19132610714624.0 / 115814518155.0, -177904688592943.0 / 474986597400.0,
+ -4821139941836765625.0 / 218016305204281304.0, 30702015625.0 / 3970037232.0,
+ -85916079474274.0 / 2800933759800.0, -5919468007.0 / 634310460.0,
+ 2479159.0 / 157936.0, -18750000.0 / 602131.0,
+ -19203125.0 / 2053168.0, 15700361463.0 / 438351368.0},
+
+ { 12627015655.0 / 631822968.0, -72955222965.0 / 188496616.0,
+ -13145744952320.0 / 69488710893.0, 30084216194513.0 / 56998391688.0,
+ -296858761006640625.0 / 25648977082856624.0, 569140625.0 / 82709109.0,
+ -18684190637.0 / 18672891732.0, 69644045.0 / 89549712.0,
+ -11847025.0 / 4264272.0, -978650000.0 / 16257537.0,
+ 519371875.0 / 6159504.0, 5256837225.0 / 438351368.0},
+
+ { -450944925.0 / 17550638.0, -14532122925.0 / 94248308.0,
+ -595876966400.0 / 2573655959.0, 188748653015.0 / 527762886.0,
+ 2545485458115234375.0 / 27252038150535163.0, -1376953125.0 / 36759604.0,
+ 53995596795.0 / 518691437.0, 210311225.0 / 7047894.0,
+ -1718875.0 / 39484.0, 58000000.0 / 602131.0,
+ -1546875.0 / 39484.0, -1262172375.0 / 8429834.0}
+
+ };
+
+ /** Last evaluations. */
+ private double[][] yDotKLast;
+
+ /** Vectors for interpolation. */
+ private double[][] v;
+
+ /** Initialization indicator for the interpolation vectors. */
+ private boolean vectorsInitialized;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link #reinitialize} method should be called before using the
+ * instance in order to initialize the internal arrays. This
+ * constructor is used only in order to delay the initialization in
+ * some cases. The {@link EmbeddedRungeKuttaIntegrator} uses the
+ * prototyping design pattern to create the step interpolators by
+ * cloning an uninitialized model and latter initializing the copy.
+ */
+ // CHECKSTYLE: stop RedundantModifier
+ // the public modifier here is needed for serialization
+ public DormandPrince853StepInterpolator() {
+ super();
+ yDotKLast = null;
+ v = null;
+ vectorsInitialized = false;
+ }
+ // CHECKSTYLE: resume RedundantModifier
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ DormandPrince853StepInterpolator(final DormandPrince853StepInterpolator interpolator) {
+
+ super(interpolator);
+
+ if (interpolator.currentState == null) {
+
+ yDotKLast = null;
+ v = null;
+ vectorsInitialized = false;
+
+ } else {
+
+ final int dimension = interpolator.currentState.length;
+
+ yDotKLast = new double[3][];
+ for (int k = 0; k < yDotKLast.length; ++k) {
+ yDotKLast[k] = new double[dimension];
+ System.arraycopy(interpolator.yDotKLast[k], 0, yDotKLast[k], 0,
+ dimension);
+ }
+
+ v = new double[7][];
+ for (int k = 0; k < v.length; ++k) {
+ v[k] = new double[dimension];
+ System.arraycopy(interpolator.v[k], 0, v[k], 0, dimension);
+ }
+
+ vectorsInitialized = interpolator.vectorsInitialized;
+
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new DormandPrince853StepInterpolator(this);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void reinitialize(final AbstractIntegrator integrator,
+ final double[] y, final double[][] yDotK, final boolean forward,
+ final EquationsMapper primaryMapper,
+ final EquationsMapper[] secondaryMappers) {
+
+ super.reinitialize(integrator, y, yDotK, forward, primaryMapper, secondaryMappers);
+
+ final int dimension = currentState.length;
+
+ yDotKLast = new double[3][];
+ for (int k = 0; k < yDotKLast.length; ++k) {
+ yDotKLast[k] = new double[dimension];
+ }
+
+ v = new double[7][];
+ for (int k = 0; k < v.length; ++k) {
+ v[k] = new double[dimension];
+ }
+
+ vectorsInitialized = false;
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void storeTime(final double t) {
+ super.storeTime(t);
+ vectorsInitialized = false;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta,
+ final double oneMinusThetaH)
+ throws MaxCountExceededException {
+
+ if (! vectorsInitialized) {
+
+ if (v == null) {
+ v = new double[7][];
+ for (int k = 0; k < 7; ++k) {
+ v[k] = new double[interpolatedState.length];
+ }
+ }
+
+ // perform the last evaluations if they have not been done yet
+ finalizeStep();
+
+ // compute the interpolation vectors for this time step
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot6 = yDotK[5][i];
+ final double yDot7 = yDotK[6][i];
+ final double yDot8 = yDotK[7][i];
+ final double yDot9 = yDotK[8][i];
+ final double yDot10 = yDotK[9][i];
+ final double yDot11 = yDotK[10][i];
+ final double yDot12 = yDotK[11][i];
+ final double yDot13 = yDotK[12][i];
+ final double yDot14 = yDotKLast[0][i];
+ final double yDot15 = yDotKLast[1][i];
+ final double yDot16 = yDotKLast[2][i];
+ v[0][i] = B_01 * yDot1 + B_06 * yDot6 + B_07 * yDot7 +
+ B_08 * yDot8 + B_09 * yDot9 + B_10 * yDot10 +
+ B_11 * yDot11 + B_12 * yDot12;
+ v[1][i] = yDot1 - v[0][i];
+ v[2][i] = v[0][i] - v[1][i] - yDotK[12][i];
+ for (int k = 0; k < D.length; ++k) {
+ v[k+3][i] = D[k][0] * yDot1 + D[k][1] * yDot6 + D[k][2] * yDot7 +
+ D[k][3] * yDot8 + D[k][4] * yDot9 + D[k][5] * yDot10 +
+ D[k][6] * yDot11 + D[k][7] * yDot12 + D[k][8] * yDot13 +
+ D[k][9] * yDot14 + D[k][10] * yDot15 + D[k][11] * yDot16;
+ }
+ }
+
+ vectorsInitialized = true;
+
+ }
+
+ final double eta = 1 - theta;
+ final double twoTheta = 2 * theta;
+ final double theta2 = theta * theta;
+ final double dot1 = 1 - twoTheta;
+ final double dot2 = theta * (2 - 3 * theta);
+ final double dot3 = twoTheta * (1 + theta * (twoTheta -3));
+ final double dot4 = theta2 * (3 + theta * (5 * theta - 8));
+ final double dot5 = theta2 * (3 + theta * (-12 + theta * (15 - 6 * theta)));
+ final double dot6 = theta2 * theta * (4 + theta * (-15 + theta * (18 - 7 * theta)));
+
+ if ((previousState != null) && (theta <= 0.5)) {
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ interpolatedState[i] = previousState[i] +
+ theta * h * (v[0][i] +
+ eta * (v[1][i] +
+ theta * (v[2][i] +
+ eta * (v[3][i] +
+ theta * (v[4][i] +
+ eta * (v[5][i] +
+ theta * (v[6][i])))))));
+ interpolatedDerivatives[i] = v[0][i] + dot1 * v[1][i] + dot2 * v[2][i] +
+ dot3 * v[3][i] + dot4 * v[4][i] +
+ dot5 * v[5][i] + dot6 * v[6][i];
+ }
+ } else {
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ interpolatedState[i] = currentState[i] -
+ oneMinusThetaH * (v[0][i] -
+ theta * (v[1][i] +
+ theta * (v[2][i] +
+ eta * (v[3][i] +
+ theta * (v[4][i] +
+ eta * (v[5][i] +
+ theta * (v[6][i])))))));
+ interpolatedDerivatives[i] = v[0][i] + dot1 * v[1][i] + dot2 * v[2][i] +
+ dot3 * v[3][i] + dot4 * v[4][i] +
+ dot5 * v[5][i] + dot6 * v[6][i];
+ }
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doFinalize() throws MaxCountExceededException {
+
+ if (currentState == null) {
+ // we are finalizing an uninitialized instance
+ return;
+ }
+
+ double s;
+ final double[] yTmp = new double[currentState.length];
+ final double pT = getGlobalPreviousTime();
+
+ // k14
+ for (int j = 0; j < currentState.length; ++j) {
+ s = K14_01 * yDotK[0][j] + K14_06 * yDotK[5][j] + K14_07 * yDotK[6][j] +
+ K14_08 * yDotK[7][j] + K14_09 * yDotK[8][j] + K14_10 * yDotK[9][j] +
+ K14_11 * yDotK[10][j] + K14_12 * yDotK[11][j] + K14_13 * yDotK[12][j];
+ yTmp[j] = currentState[j] + h * s;
+ }
+ integrator.computeDerivatives(pT + C14 * h, yTmp, yDotKLast[0]);
+
+ // k15
+ for (int j = 0; j < currentState.length; ++j) {
+ s = K15_01 * yDotK[0][j] + K15_06 * yDotK[5][j] + K15_07 * yDotK[6][j] +
+ K15_08 * yDotK[7][j] + K15_09 * yDotK[8][j] + K15_10 * yDotK[9][j] +
+ K15_11 * yDotK[10][j] + K15_12 * yDotK[11][j] + K15_13 * yDotK[12][j] +
+ K15_14 * yDotKLast[0][j];
+ yTmp[j] = currentState[j] + h * s;
+ }
+ integrator.computeDerivatives(pT + C15 * h, yTmp, yDotKLast[1]);
+
+ // k16
+ for (int j = 0; j < currentState.length; ++j) {
+ s = K16_01 * yDotK[0][j] + K16_06 * yDotK[5][j] + K16_07 * yDotK[6][j] +
+ K16_08 * yDotK[7][j] + K16_09 * yDotK[8][j] + K16_10 * yDotK[9][j] +
+ K16_11 * yDotK[10][j] + K16_12 * yDotK[11][j] + K16_13 * yDotK[12][j] +
+ K16_14 * yDotKLast[0][j] + K16_15 * yDotKLast[1][j];
+ yTmp[j] = currentState[j] + h * s;
+ }
+ integrator.computeDerivatives(pT + C16 * h, yTmp, yDotKLast[2]);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeExternal(final ObjectOutput out)
+ throws IOException {
+
+ try {
+ // save the local attributes
+ finalizeStep();
+ } catch (MaxCountExceededException mcee) {
+ final IOException ioe = new IOException(mcee.getLocalizedMessage());
+ ioe.initCause(mcee);
+ throw ioe;
+ }
+
+ final int dimension = (currentState == null) ? -1 : currentState.length;
+ out.writeInt(dimension);
+ for (int i = 0; i < dimension; ++i) {
+ out.writeDouble(yDotKLast[0][i]);
+ out.writeDouble(yDotKLast[1][i]);
+ out.writeDouble(yDotKLast[2][i]);
+ }
+
+ // save the state of the base class
+ super.writeExternal(out);
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void readExternal(final ObjectInput in)
+ throws IOException, ClassNotFoundException {
+
+ // read the local attributes
+ yDotKLast = new double[3][];
+ final int dimension = in.readInt();
+ yDotKLast[0] = (dimension < 0) ? null : new double[dimension];
+ yDotKLast[1] = (dimension < 0) ? null : new double[dimension];
+ yDotKLast[2] = (dimension < 0) ? null : new double[dimension];
+
+ for (int i = 0; i < dimension; ++i) {
+ yDotKLast[0][i] = in.readDouble();
+ yDotKLast[1][i] = in.readDouble();
+ yDotKLast[2][i] = in.readDouble();
+ }
+
+ // read the base state
+ super.readExternal(in);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/EmbeddedRungeKuttaFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/EmbeddedRungeKuttaFieldIntegrator.java
new file mode 100644
index 0000000..036cf01
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/EmbeddedRungeKuttaFieldIntegrator.java
@@ -0,0 +1,385 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldExpandableODE;
+import org.apache.commons.math3.ode.FieldODEState;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+/**
+ * This class implements the common part of all embedded Runge-Kutta
+ * integrators for Ordinary Differential Equations.
+ *
+ * <p>These methods are embedded explicit Runge-Kutta methods with two
+ * sets of coefficients allowing to estimate the error, their Butcher
+ * arrays are as follows :
+ * <pre>
+ * 0 |
+ * c2 | a21
+ * c3 | a31 a32
+ * ... | ...
+ * cs | as1 as2 ... ass-1
+ * |--------------------------
+ * | b1 b2 ... bs-1 bs
+ * | b'1 b'2 ... b's-1 b's
+ * </pre>
+ * </p>
+ *
+ * <p>In fact, we rather use the array defined by ej = bj - b'j to
+ * compute directly the error rather than computing two estimates and
+ * then comparing them.</p>
+ *
+ * <p>Some methods are qualified as <i>fsal</i> (first same as last)
+ * methods. This means the last evaluation of the derivatives in one
+ * step is the same as the first in the next step. Then, this
+ * evaluation can be reused from one step to the next one and the cost
+ * of such a method is really s-1 evaluations despite the method still
+ * has s stages. This behaviour is true only for successful steps, if
+ * the step is rejected after the error estimation phase, no
+ * evaluation is saved. For an <i>fsal</i> method, we have cs = 1 and
+ * asi = bi for all i.</p>
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public abstract class EmbeddedRungeKuttaFieldIntegrator<T extends RealFieldElement<T>>
+ extends AdaptiveStepsizeFieldIntegrator<T>
+ implements FieldButcherArrayProvider<T> {
+
+ /** Index of the pre-computed derivative for <i>fsal</i> methods. */
+ private final int fsal;
+
+ /** Time steps from Butcher array (without the first zero). */
+ private final T[] c;
+
+ /** Internal weights from Butcher array (without the first empty row). */
+ private final T[][] a;
+
+ /** External weights for the high order method from Butcher array. */
+ private final T[] b;
+
+ /** Stepsize control exponent. */
+ private final T exp;
+
+ /** Safety factor for stepsize control. */
+ private T safety;
+
+ /** Minimal reduction factor for stepsize control. */
+ private T minReduction;
+
+ /** Maximal growth factor for stepsize control. */
+ private T maxGrowth;
+
+ /** Build a Runge-Kutta integrator with the given Butcher array.
+ * @param field field to which the time and state vector elements belong
+ * @param name name of the method
+ * @param fsal index of the pre-computed derivative for <i>fsal</i> methods
+ * or -1 if method is not <i>fsal</i>
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ protected EmbeddedRungeKuttaFieldIntegrator(final Field<T> field, final String name, final int fsal,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+
+ super(field, name, minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+
+ this.fsal = fsal;
+ this.c = getC();
+ this.a = getA();
+ this.b = getB();
+
+ exp = field.getOne().divide(-getOrder());
+
+ // set the default values of the algorithm control parameters
+ setSafety(field.getZero().add(0.9));
+ setMinReduction(field.getZero().add(0.2));
+ setMaxGrowth(field.getZero().add(10.0));
+
+ }
+
+ /** Build a Runge-Kutta integrator with the given Butcher array.
+ * @param field field to which the time and state vector elements belong
+ * @param name name of the method
+ * @param fsal index of the pre-computed derivative for <i>fsal</i> methods
+ * or -1 if method is not <i>fsal</i>
+ * @param minStep minimal step (must be positive even for backward
+ * integration), the last step can be smaller than this
+ * @param maxStep maximal step (must be positive even for backward
+ * integration)
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ protected EmbeddedRungeKuttaFieldIntegrator(final Field<T> field, final String name, final int fsal,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+
+ super(field, name, minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+
+ this.fsal = fsal;
+ this.c = getC();
+ this.a = getA();
+ this.b = getB();
+
+ exp = field.getOne().divide(-getOrder());
+
+ // set the default values of the algorithm control parameters
+ setSafety(field.getZero().add(0.9));
+ setMinReduction(field.getZero().add(0.2));
+ setMaxGrowth(field.getZero().add(10.0));
+
+ }
+
+ /** Create a fraction.
+ * @param p numerator
+ * @param q denominator
+ * @return p/q computed in the instance field
+ */
+ protected T fraction(final int p, final int q) {
+ return getField().getOne().multiply(p).divide(q);
+ }
+
+ /** Create a fraction.
+ * @param p numerator
+ * @param q denominator
+ * @return p/q computed in the instance field
+ */
+ protected T fraction(final double p, final double q) {
+ return getField().getOne().multiply(p).divide(q);
+ }
+
+ /** Create an interpolator.
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param mapper equations mapper for the all equations
+ * @return external weights for the high order method from Butcher array
+ */
+ protected abstract RungeKuttaFieldStepInterpolator<T> createInterpolator(boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ FieldEquationsMapper<T> mapper);
+ /** Get the order of the method.
+ * @return order of the method
+ */
+ public abstract int getOrder();
+
+ /** Get the safety factor for stepsize control.
+ * @return safety factor
+ */
+ public T getSafety() {
+ return safety;
+ }
+
+ /** Set the safety factor for stepsize control.
+ * @param safety safety factor
+ */
+ public void setSafety(final T safety) {
+ this.safety = safety;
+ }
+
+ /** {@inheritDoc} */
+ public FieldODEStateAndDerivative<T> integrate(final FieldExpandableODE<T> equations,
+ final FieldODEState<T> initialState, final T finalTime)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException {
+
+ sanityChecks(initialState, finalTime);
+ final T t0 = initialState.getTime();
+ final T[] y0 = equations.getMapper().mapState(initialState);
+ setStepStart(initIntegration(equations, t0, y0, finalTime));
+ final boolean forward = finalTime.subtract(initialState.getTime()).getReal() > 0;
+
+ // create some internal working arrays
+ final int stages = c.length + 1;
+ T[] y = y0;
+ final T[][] yDotK = MathArrays.buildArray(getField(), stages, -1);
+ final T[] yTmp = MathArrays.buildArray(getField(), y0.length);
+
+ // set up integration control objects
+ T hNew = getField().getZero();
+ boolean firstTime = true;
+
+ // main integration loop
+ setIsLastStep(false);
+ do {
+
+ // iterate over step size, ensuring local normalized error is smaller than 1
+ T error = getField().getZero().add(10);
+ while (error.subtract(1.0).getReal() >= 0) {
+
+ // first stage
+ y = equations.getMapper().mapState(getStepStart());
+ yDotK[0] = equations.getMapper().mapDerivative(getStepStart());
+
+ if (firstTime) {
+ final T[] scale = MathArrays.buildArray(getField(), mainSetDimension);
+ if (vecAbsoluteTolerance == null) {
+ for (int i = 0; i < scale.length; ++i) {
+ scale[i] = y[i].abs().multiply(scalRelativeTolerance).add(scalAbsoluteTolerance);
+ }
+ } else {
+ for (int i = 0; i < scale.length; ++i) {
+ scale[i] = y[i].abs().multiply(vecRelativeTolerance[i]).add(vecAbsoluteTolerance[i]);
+ }
+ }
+ hNew = initializeStep(forward, getOrder(), scale, getStepStart(), equations.getMapper());
+ firstTime = false;
+ }
+
+ setStepSize(hNew);
+ if (forward) {
+ if (getStepStart().getTime().add(getStepSize()).subtract(finalTime).getReal() >= 0) {
+ setStepSize(finalTime.subtract(getStepStart().getTime()));
+ }
+ } else {
+ if (getStepStart().getTime().add(getStepSize()).subtract(finalTime).getReal() <= 0) {
+ setStepSize(finalTime.subtract(getStepStart().getTime()));
+ }
+ }
+
+ // next stages
+ for (int k = 1; k < stages; ++k) {
+
+ for (int j = 0; j < y0.length; ++j) {
+ T sum = yDotK[0][j].multiply(a[k-1][0]);
+ for (int l = 1; l < k; ++l) {
+ sum = sum.add(yDotK[l][j].multiply(a[k-1][l]));
+ }
+ yTmp[j] = y[j].add(getStepSize().multiply(sum));
+ }
+
+ yDotK[k] = computeDerivatives(getStepStart().getTime().add(getStepSize().multiply(c[k-1])), yTmp);
+
+ }
+
+ // estimate the state at the end of the step
+ for (int j = 0; j < y0.length; ++j) {
+ T sum = yDotK[0][j].multiply(b[0]);
+ for (int l = 1; l < stages; ++l) {
+ sum = sum.add(yDotK[l][j].multiply(b[l]));
+ }
+ yTmp[j] = y[j].add(getStepSize().multiply(sum));
+ }
+
+ // estimate the error at the end of the step
+ error = estimateError(yDotK, y, yTmp, getStepSize());
+ if (error.subtract(1.0).getReal() >= 0) {
+ // reject the step and attempt to reduce error by stepsize control
+ final T factor = MathUtils.min(maxGrowth,
+ MathUtils.max(minReduction, safety.multiply(error.pow(exp))));
+ hNew = filterStep(getStepSize().multiply(factor), forward, false);
+ }
+
+ }
+ final T stepEnd = getStepStart().getTime().add(getStepSize());
+ final T[] yDotTmp = (fsal >= 0) ? yDotK[fsal] : computeDerivatives(stepEnd, yTmp);
+ final FieldODEStateAndDerivative<T> stateTmp = new FieldODEStateAndDerivative<T>(stepEnd, yTmp, yDotTmp);
+
+ // local error is small enough: accept the step, trigger events and step handlers
+ System.arraycopy(yTmp, 0, y, 0, y0.length);
+ setStepStart(acceptStep(createInterpolator(forward, yDotK, getStepStart(), stateTmp, equations.getMapper()),
+ finalTime));
+
+ if (!isLastStep()) {
+
+ // stepsize control for next step
+ final T factor = MathUtils.min(maxGrowth,
+ MathUtils.max(minReduction, safety.multiply(error.pow(exp))));
+ final T scaledH = getStepSize().multiply(factor);
+ final T nextT = getStepStart().getTime().add(scaledH);
+ final boolean nextIsLast = forward ?
+ nextT.subtract(finalTime).getReal() >= 0 :
+ nextT.subtract(finalTime).getReal() <= 0;
+ hNew = filterStep(scaledH, forward, nextIsLast);
+
+ final T filteredNextT = getStepStart().getTime().add(hNew);
+ final boolean filteredNextIsLast = forward ?
+ filteredNextT.subtract(finalTime).getReal() >= 0 :
+ filteredNextT.subtract(finalTime).getReal() <= 0;
+ if (filteredNextIsLast) {
+ hNew = finalTime.subtract(getStepStart().getTime());
+ }
+
+ }
+
+ } while (!isLastStep());
+
+ final FieldODEStateAndDerivative<T> finalState = getStepStart();
+ resetInternalState();
+ return finalState;
+
+ }
+
+ /** Get the minimal reduction factor for stepsize control.
+ * @return minimal reduction factor
+ */
+ public T getMinReduction() {
+ return minReduction;
+ }
+
+ /** Set the minimal reduction factor for stepsize control.
+ * @param minReduction minimal reduction factor
+ */
+ public void setMinReduction(final T minReduction) {
+ this.minReduction = minReduction;
+ }
+
+ /** Get the maximal growth factor for stepsize control.
+ * @return maximal growth factor
+ */
+ public T getMaxGrowth() {
+ return maxGrowth;
+ }
+
+ /** Set the maximal growth factor for stepsize control.
+ * @param maxGrowth maximal growth factor
+ */
+ public void setMaxGrowth(final T maxGrowth) {
+ this.maxGrowth = maxGrowth;
+ }
+
+ /** Compute the error ratio.
+ * @param yDotK derivatives computed during the first stages
+ * @param y0 estimate of the step at the start of the step
+ * @param y1 estimate of the step at the end of the step
+ * @param h current step
+ * @return error ratio, greater than 1 if step should be rejected
+ */
+ protected abstract T estimateError(T[][] yDotK, T[] y0, T[] y1, T h);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/EmbeddedRungeKuttaIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/EmbeddedRungeKuttaIntegrator.java
new file mode 100644
index 0000000..098d2e5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/EmbeddedRungeKuttaIntegrator.java
@@ -0,0 +1,380 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.ode.ExpandableStatefulODE;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class implements the common part of all embedded Runge-Kutta
+ * integrators for Ordinary Differential Equations.
+ *
+ * <p>These methods are embedded explicit Runge-Kutta methods with two
+ * sets of coefficients allowing to estimate the error, their Butcher
+ * arrays are as follows :
+ * <pre>
+ * 0 |
+ * c2 | a21
+ * c3 | a31 a32
+ * ... | ...
+ * cs | as1 as2 ... ass-1
+ * |--------------------------
+ * | b1 b2 ... bs-1 bs
+ * | b'1 b'2 ... b's-1 b's
+ * </pre>
+ * </p>
+ *
+ * <p>In fact, we rather use the array defined by ej = bj - b'j to
+ * compute directly the error rather than computing two estimates and
+ * then comparing them.</p>
+ *
+ * <p>Some methods are qualified as <i>fsal</i> (first same as last)
+ * methods. This means the last evaluation of the derivatives in one
+ * step is the same as the first in the next step. Then, this
+ * evaluation can be reused from one step to the next one and the cost
+ * of such a method is really s-1 evaluations despite the method still
+ * has s stages. This behaviour is true only for successful steps, if
+ * the step is rejected after the error estimation phase, no
+ * evaluation is saved. For an <i>fsal</i> method, we have cs = 1 and
+ * asi = bi for all i.</p>
+ *
+ * @since 1.2
+ */
+
+public abstract class EmbeddedRungeKuttaIntegrator
+ extends AdaptiveStepsizeIntegrator {
+
+ /** Indicator for <i>fsal</i> methods. */
+ private final boolean fsal;
+
+ /** Time steps from Butcher array (without the first zero). */
+ private final double[] c;
+
+ /** Internal weights from Butcher array (without the first empty row). */
+ private final double[][] a;
+
+ /** External weights for the high order method from Butcher array. */
+ private final double[] b;
+
+ /** Prototype of the step interpolator. */
+ private final RungeKuttaStepInterpolator prototype;
+
+ /** Stepsize control exponent. */
+ private final double exp;
+
+ /** Safety factor for stepsize control. */
+ private double safety;
+
+ /** Minimal reduction factor for stepsize control. */
+ private double minReduction;
+
+ /** Maximal growth factor for stepsize control. */
+ private double maxGrowth;
+
+ /** Build a Runge-Kutta integrator with the given Butcher array.
+ * @param name name of the method
+ * @param fsal indicate that the method is an <i>fsal</i>
+ * @param c time steps from Butcher array (without the first zero)
+ * @param a internal weights from Butcher array (without the first empty row)
+ * @param b propagation weights for the high order method from Butcher array
+ * @param prototype prototype of the step interpolator to use
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ protected EmbeddedRungeKuttaIntegrator(final String name, final boolean fsal,
+ final double[] c, final double[][] a, final double[] b,
+ final RungeKuttaStepInterpolator prototype,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+
+ super(name, minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+
+ this.fsal = fsal;
+ this.c = c;
+ this.a = a;
+ this.b = b;
+ this.prototype = prototype;
+
+ exp = -1.0 / getOrder();
+
+ // set the default values of the algorithm control parameters
+ setSafety(0.9);
+ setMinReduction(0.2);
+ setMaxGrowth(10.0);
+
+ }
+
+ /** Build a Runge-Kutta integrator with the given Butcher array.
+ * @param name name of the method
+ * @param fsal indicate that the method is an <i>fsal</i>
+ * @param c time steps from Butcher array (without the first zero)
+ * @param a internal weights from Butcher array (without the first empty row)
+ * @param b propagation weights for the high order method from Butcher array
+ * @param prototype prototype of the step interpolator to use
+ * @param minStep minimal step (must be positive even for backward
+ * integration), the last step can be smaller than this
+ * @param maxStep maximal step (must be positive even for backward
+ * integration)
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ protected EmbeddedRungeKuttaIntegrator(final String name, final boolean fsal,
+ final double[] c, final double[][] a, final double[] b,
+ final RungeKuttaStepInterpolator prototype,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+
+ super(name, minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+
+ this.fsal = fsal;
+ this.c = c;
+ this.a = a;
+ this.b = b;
+ this.prototype = prototype;
+
+ exp = -1.0 / getOrder();
+
+ // set the default values of the algorithm control parameters
+ setSafety(0.9);
+ setMinReduction(0.2);
+ setMaxGrowth(10.0);
+
+ }
+
+ /** Get the order of the method.
+ * @return order of the method
+ */
+ public abstract int getOrder();
+
+ /** Get the safety factor for stepsize control.
+ * @return safety factor
+ */
+ public double getSafety() {
+ return safety;
+ }
+
+ /** Set the safety factor for stepsize control.
+ * @param safety safety factor
+ */
+ public void setSafety(final double safety) {
+ this.safety = safety;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void integrate(final ExpandableStatefulODE equations, final double t)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException {
+
+ sanityChecks(equations, t);
+ setEquations(equations);
+ final boolean forward = t > equations.getTime();
+
+ // create some internal working arrays
+ final double[] y0 = equations.getCompleteState();
+ final double[] y = y0.clone();
+ final int stages = c.length + 1;
+ final double[][] yDotK = new double[stages][y.length];
+ final double[] yTmp = y0.clone();
+ final double[] yDotTmp = new double[y.length];
+
+ // set up an interpolator sharing the integrator arrays
+ final RungeKuttaStepInterpolator interpolator = (RungeKuttaStepInterpolator) prototype.copy();
+ interpolator.reinitialize(this, yTmp, yDotK, forward,
+ equations.getPrimaryMapper(), equations.getSecondaryMappers());
+ interpolator.storeTime(equations.getTime());
+
+ // set up integration control objects
+ stepStart = equations.getTime();
+ double hNew = 0;
+ boolean firstTime = true;
+ initIntegration(equations.getTime(), y0, t);
+
+ // main integration loop
+ isLastStep = false;
+ do {
+
+ interpolator.shift();
+
+ // iterate over step size, ensuring local normalized error is smaller than 1
+ double error = 10;
+ while (error >= 1.0) {
+
+ if (firstTime || !fsal) {
+ // first stage
+ computeDerivatives(stepStart, y, yDotK[0]);
+ }
+
+ if (firstTime) {
+ final double[] scale = new double[mainSetDimension];
+ if (vecAbsoluteTolerance == null) {
+ for (int i = 0; i < scale.length; ++i) {
+ scale[i] = scalAbsoluteTolerance + scalRelativeTolerance * FastMath.abs(y[i]);
+ }
+ } else {
+ for (int i = 0; i < scale.length; ++i) {
+ scale[i] = vecAbsoluteTolerance[i] + vecRelativeTolerance[i] * FastMath.abs(y[i]);
+ }
+ }
+ hNew = initializeStep(forward, getOrder(), scale,
+ stepStart, y, yDotK[0], yTmp, yDotK[1]);
+ firstTime = false;
+ }
+
+ stepSize = hNew;
+ if (forward) {
+ if (stepStart + stepSize >= t) {
+ stepSize = t - stepStart;
+ }
+ } else {
+ if (stepStart + stepSize <= t) {
+ stepSize = t - stepStart;
+ }
+ }
+
+ // next stages
+ for (int k = 1; k < stages; ++k) {
+
+ for (int j = 0; j < y0.length; ++j) {
+ double sum = a[k-1][0] * yDotK[0][j];
+ for (int l = 1; l < k; ++l) {
+ sum += a[k-1][l] * yDotK[l][j];
+ }
+ yTmp[j] = y[j] + stepSize * sum;
+ }
+
+ computeDerivatives(stepStart + c[k-1] * stepSize, yTmp, yDotK[k]);
+
+ }
+
+ // estimate the state at the end of the step
+ for (int j = 0; j < y0.length; ++j) {
+ double sum = b[0] * yDotK[0][j];
+ for (int l = 1; l < stages; ++l) {
+ sum += b[l] * yDotK[l][j];
+ }
+ yTmp[j] = y[j] + stepSize * sum;
+ }
+
+ // estimate the error at the end of the step
+ error = estimateError(yDotK, y, yTmp, stepSize);
+ if (error >= 1.0) {
+ // reject the step and attempt to reduce error by stepsize control
+ final double factor =
+ FastMath.min(maxGrowth,
+ FastMath.max(minReduction, safety * FastMath.pow(error, exp)));
+ hNew = filterStep(stepSize * factor, forward, false);
+ }
+
+ }
+
+ // local error is small enough: accept the step, trigger events and step handlers
+ interpolator.storeTime(stepStart + stepSize);
+ System.arraycopy(yTmp, 0, y, 0, y0.length);
+ System.arraycopy(yDotK[stages - 1], 0, yDotTmp, 0, y0.length);
+ stepStart = acceptStep(interpolator, y, yDotTmp, t);
+ System.arraycopy(y, 0, yTmp, 0, y.length);
+
+ if (!isLastStep) {
+
+ // prepare next step
+ interpolator.storeTime(stepStart);
+
+ if (fsal) {
+ // save the last evaluation for the next step
+ System.arraycopy(yDotTmp, 0, yDotK[0], 0, y0.length);
+ }
+
+ // stepsize control for next step
+ final double factor =
+ FastMath.min(maxGrowth, FastMath.max(minReduction, safety * FastMath.pow(error, exp)));
+ final double scaledH = stepSize * factor;
+ final double nextT = stepStart + scaledH;
+ final boolean nextIsLast = forward ? (nextT >= t) : (nextT <= t);
+ hNew = filterStep(scaledH, forward, nextIsLast);
+
+ final double filteredNextT = stepStart + hNew;
+ final boolean filteredNextIsLast = forward ? (filteredNextT >= t) : (filteredNextT <= t);
+ if (filteredNextIsLast) {
+ hNew = t - stepStart;
+ }
+
+ }
+
+ } while (!isLastStep);
+
+ // dispatch results
+ equations.setTime(stepStart);
+ equations.setCompleteState(y);
+
+ resetInternalState();
+
+ }
+
+ /** Get the minimal reduction factor for stepsize control.
+ * @return minimal reduction factor
+ */
+ public double getMinReduction() {
+ return minReduction;
+ }
+
+ /** Set the minimal reduction factor for stepsize control.
+ * @param minReduction minimal reduction factor
+ */
+ public void setMinReduction(final double minReduction) {
+ this.minReduction = minReduction;
+ }
+
+ /** Get the maximal growth factor for stepsize control.
+ * @return maximal growth factor
+ */
+ public double getMaxGrowth() {
+ return maxGrowth;
+ }
+
+ /** Set the maximal growth factor for stepsize control.
+ * @param maxGrowth maximal growth factor
+ */
+ public void setMaxGrowth(final double maxGrowth) {
+ this.maxGrowth = maxGrowth;
+ }
+
+ /** Compute the error ratio.
+ * @param yDotK derivatives computed during the first stages
+ * @param y0 estimate of the step at the start of the step
+ * @param y1 estimate of the step at the end of the step
+ * @param h current step
+ * @return error ratio, greater than 1 if step should be rejected
+ */
+ protected abstract double estimateError(double[][] yDotK,
+ double[] y0, double[] y1,
+ double h);
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerFieldIntegrator.java
new file mode 100644
index 0000000..38516cf
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerFieldIntegrator.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class implements a simple Euler integrator for Ordinary
+ * Differential Equations.
+ *
+ * <p>The Euler algorithm is the simplest one that can be used to
+ * integrate ordinary differential equations. It is a simple inversion
+ * of the forward difference expression :
+ * <code>f'=(f(t+h)-f(t))/h</code> which leads to
+ * <code>f(t+h)=f(t)+hf'</code>. The interpolation scheme used for
+ * dense output is the linear scheme already used for integration.</p>
+ *
+ * <p>This algorithm looks cheap because it needs only one function
+ * evaluation per step. However, as it uses linear estimates, it needs
+ * very small steps to achieve high accuracy, and small steps lead to
+ * numerical errors and instabilities.</p>
+ *
+ * <p>This algorithm is almost never used and has been included in
+ * this package only as a comparison reference for more useful
+ * integrators.</p>
+ *
+ * @see MidpointFieldIntegrator
+ * @see ClassicalRungeKuttaFieldIntegrator
+ * @see GillFieldIntegrator
+ * @see ThreeEighthesFieldIntegrator
+ * @see LutherFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public class EulerFieldIntegrator<T extends RealFieldElement<T>> extends RungeKuttaFieldIntegrator<T> {
+
+ /** Simple constructor.
+ * Build an Euler integrator with the given step.
+ * @param field field to which the time and state vector elements belong
+ * @param step integration step
+ */
+ public EulerFieldIntegrator(final Field<T> field, final T step) {
+ super(field, "Euler", step);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getC() {
+ return MathArrays.buildArray(getField(), 0);
+ }
+
+ /** {@inheritDoc} */
+ public T[][] getA() {
+ return MathArrays.buildArray(getField(), 0, 0);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getB() {
+ final T[] b = MathArrays.buildArray(getField(), 1);
+ b[0] = getField().getOne();
+ return b;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected EulerFieldStepInterpolator<T>
+ createInterpolator(final boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ return new EulerFieldStepInterpolator<T>(getField(), forward, yDotK,
+ globalPreviousState, globalCurrentState,
+ globalPreviousState, globalCurrentState,
+ mapper);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerFieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerFieldStepInterpolator.java
new file mode 100644
index 0000000..0907b8e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerFieldStepInterpolator.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/**
+ * This class implements a linear interpolator for step.
+ *
+ * <p>This interpolator computes dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme :
+ * <ul>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub>) + &theta; h y'
+ * </li>
+ * <li>Using reference point at step end:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub> + h) - (1-&theta;) h y'
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * where &theta; belongs to [0 ; 1] and where y' is the evaluation of
+ * the derivatives already computed during the step.</p>
+ *
+ * @see EulerFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+class EulerFieldStepInterpolator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldStepInterpolator<T> {
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param mapper equations mapper for the all equations
+ */
+ EulerFieldStepInterpolator(final Field<T> field, final boolean forward,
+ final T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ super(field, forward, yDotK,
+ globalPreviousState, globalCurrentState, softPreviousState, softCurrentState,
+ mapper);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected EulerFieldStepInterpolator<T> create(final Field<T> newField, final boolean newForward, final T[][] newYDotK,
+ final FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ final FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ final FieldODEStateAndDerivative<T> newSoftPreviousState,
+ final FieldODEStateAndDerivative<T> newSoftCurrentState,
+ final FieldEquationsMapper<T> newMapper) {
+ return new EulerFieldStepInterpolator<T>(newField, newForward, newYDotK,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(final FieldEquationsMapper<T> mapper,
+ final T time, final T theta,
+ final T thetaH, final T oneMinusThetaH) {
+ final T[] interpolatedState;
+ final T[] interpolatedDerivatives;
+ if (getGlobalPreviousState() != null && theta.getReal() <= 0.5) {
+ interpolatedState = previousStateLinearCombination(thetaH);
+ interpolatedDerivatives = derivativeLinearCombination(time.getField().getOne());
+ } else {
+ interpolatedState = currentStateLinearCombination(oneMinusThetaH.negate());
+ interpolatedDerivatives = derivativeLinearCombination(time.getField().getOne());
+ }
+
+ return new FieldODEStateAndDerivative<T>(time, interpolatedState, interpolatedDerivatives);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerIntegrator.java
new file mode 100644
index 0000000..22c15c5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerIntegrator.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+
+/**
+ * This class implements a simple Euler integrator for Ordinary
+ * Differential Equations.
+ *
+ * <p>The Euler algorithm is the simplest one that can be used to
+ * integrate ordinary differential equations. It is a simple inversion
+ * of the forward difference expression :
+ * <code>f'=(f(t+h)-f(t))/h</code> which leads to
+ * <code>f(t+h)=f(t)+hf'</code>. The interpolation scheme used for
+ * dense output is the linear scheme already used for integration.</p>
+ *
+ * <p>This algorithm looks cheap because it needs only one function
+ * evaluation per step. However, as it uses linear estimates, it needs
+ * very small steps to achieve high accuracy, and small steps lead to
+ * numerical errors and instabilities.</p>
+ *
+ * <p>This algorithm is almost never used and has been included in
+ * this package only as a comparison reference for more useful
+ * integrators.</p>
+ *
+ * @see MidpointIntegrator
+ * @see ClassicalRungeKuttaIntegrator
+ * @see GillIntegrator
+ * @see ThreeEighthesIntegrator
+ * @see LutherIntegrator
+ * @since 1.2
+ */
+
+public class EulerIntegrator extends RungeKuttaIntegrator {
+
+ /** Time steps Butcher array. */
+ private static final double[] STATIC_C = {
+ };
+
+ /** Internal weights Butcher array. */
+ private static final double[][] STATIC_A = {
+ };
+
+ /** Propagation weights Butcher array. */
+ private static final double[] STATIC_B = {
+ 1.0
+ };
+
+ /** Simple constructor.
+ * Build an Euler integrator with the given step.
+ * @param step integration step
+ */
+ public EulerIntegrator(final double step) {
+ super("Euler", STATIC_C, STATIC_A, STATIC_B, new EulerStepInterpolator(), step);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerStepInterpolator.java
new file mode 100644
index 0000000..331cb14
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/EulerStepInterpolator.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+
+/**
+ * This class implements a linear interpolator for step.
+ *
+ * <p>This interpolator computes dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme :
+ * <ul>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub>) + &theta; h y'
+ * </li>
+ * <li>Using reference point at step end:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub> + h) - (1-&theta;) h y'
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * where &theta; belongs to [0 ; 1] and where y' is the evaluation of
+ * the derivatives already computed during the step.</p>
+ *
+ * @see EulerIntegrator
+ * @since 1.2
+ */
+
+class EulerStepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20111120L;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link
+ * org.apache.commons.math3.ode.sampling.AbstractStepInterpolator#reinitialize}
+ * method should be called before using the instance in order to
+ * initialize the internal arrays. This constructor is used only
+ * in order to delay the initialization in some cases. The {@link
+ * RungeKuttaIntegrator} class uses the prototyping design pattern
+ * to create the step interpolators by cloning an uninitialized model
+ * and later initializing the copy.
+ */
+ // CHECKSTYLE: stop RedundantModifier
+ // the public modifier here is needed for serialization
+ public EulerStepInterpolator() {
+ }
+ // CHECKSTYLE: resume RedundantModifier
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ EulerStepInterpolator(final EulerStepInterpolator interpolator) {
+ super(interpolator);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new EulerStepInterpolator(this);
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta,
+ final double oneMinusThetaH) {
+ if ((previousState != null) && (theta <= 0.5)) {
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ interpolatedState[i] = previousState[i] + theta * h * yDotK[0][i];
+ }
+ System.arraycopy(yDotK[0], 0, interpolatedDerivatives, 0, interpolatedDerivatives.length);
+ } else {
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ interpolatedState[i] = currentState[i] - oneMinusThetaH * yDotK[0][i];
+ }
+ System.arraycopy(yDotK[0], 0, interpolatedDerivatives, 0, interpolatedDerivatives.length);
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/FieldButcherArrayProvider.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/FieldButcherArrayProvider.java
new file mode 100644
index 0000000..b37d4cb
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/FieldButcherArrayProvider.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.RealFieldElement;
+
+/** This interface represents an integrator based on Butcher arrays.
+ * @see RungeKuttaFieldIntegrator
+ * @see EmbeddedRungeKuttaFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public interface FieldButcherArrayProvider<T extends RealFieldElement<T>> {
+
+ /** Get the time steps from Butcher array (without the first zero).
+ * @return time steps from Butcher array (without the first zero
+ */
+ T[] getC();
+
+ /** Get the internal weights from Butcher array (without the first empty row).
+ * @return internal weights from Butcher array (without the first empty row)
+ */
+ T[][] getA();
+
+ /** Get the external weights for the high order method from Butcher array.
+ * @return external weights for the high order method from Butcher array
+ */
+ T[] getB();
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/GillFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/GillFieldIntegrator.java
new file mode 100644
index 0000000..d5f7c64
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/GillFieldIntegrator.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+
+
+/**
+ * This class implements the Gill fourth order Runge-Kutta
+ * integrator for Ordinary Differential Equations .
+
+ * <p>This method is an explicit Runge-Kutta method, its Butcher-array
+ * is the following one :
+ * <pre>
+ * 0 | 0 0 0 0
+ * 1/2 | 1/2 0 0 0
+ * 1/2 | (q-1)/2 (2-q)/2 0 0
+ * 1 | 0 -q/2 (2+q)/2 0
+ * |-------------------------------
+ * | 1/6 (2-q)/6 (2+q)/6 1/6
+ * </pre>
+ * where q = sqrt(2)</p>
+ *
+ * @see EulerFieldIntegrator
+ * @see ClassicalRungeKuttaFieldIntegrator
+ * @see MidpointFieldIntegrator
+ * @see ThreeEighthesFieldIntegrator
+ * @see LutherFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public class GillFieldIntegrator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldIntegrator<T> {
+
+ /** Simple constructor.
+ * Build a fourth-order Gill integrator with the given step.
+ * @param field field to which the time and state vector elements belong
+ * @param step integration step
+ */
+ public GillFieldIntegrator(final Field<T> field, final T step) {
+ super(field, "Gill", step);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getC() {
+ final T[] c = MathArrays.buildArray(getField(), 3);
+ c[0] = fraction(1, 2);
+ c[1] = c[0];
+ c[2] = getField().getOne();
+ return c;
+ }
+
+ /** {@inheritDoc} */
+ public T[][] getA() {
+
+ final T two = getField().getZero().add(2);
+ final T sqrtTwo = two.sqrt();
+
+ final T[][] a = MathArrays.buildArray(getField(), 3, -1);
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = MathArrays.buildArray(getField(), i + 1);
+ }
+ a[0][0] = fraction(1, 2);
+ a[1][0] = sqrtTwo.subtract(1).multiply(0.5);
+ a[1][1] = sqrtTwo.subtract(2).multiply(-0.5);
+ a[2][0] = getField().getZero();
+ a[2][1] = sqrtTwo.multiply(-0.5);
+ a[2][2] = sqrtTwo.add(2).multiply(0.5);
+ return a;
+ }
+
+ /** {@inheritDoc} */
+ public T[] getB() {
+
+ final T two = getField().getZero().add(2);
+ final T sqrtTwo = two.sqrt();
+
+ final T[] b = MathArrays.buildArray(getField(), 4);
+ b[0] = fraction(1, 6);
+ b[1] = sqrtTwo.subtract(2).divide(-6);
+ b[2] = sqrtTwo.add(2).divide(6);
+ b[3] = b[0];
+
+ return b;
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected GillFieldStepInterpolator<T>
+ createInterpolator(final boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ return new GillFieldStepInterpolator<T>(getField(), forward, yDotK,
+ globalPreviousState, globalCurrentState,
+ globalPreviousState, globalCurrentState,
+ mapper);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/GillFieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/GillFieldStepInterpolator.java
new file mode 100644
index 0000000..5639ed5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/GillFieldStepInterpolator.java
@@ -0,0 +1,148 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/**
+ * This class implements a step interpolator for the Gill fourth
+ * order Runge-Kutta integrator.
+ *
+ * <p>This interpolator allows to compute dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme :
+ * <ul>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub>)
+ * + &theta; (h/6) [ (6 - 9 &theta; + 4 &theta;<sup>2</sup>) y'<sub>1</sub>
+ * + ( 6 &theta; - 4 &theta;<sup>2</sup>) ((1-1/&radic;2) y'<sub>2</sub> + (1+1/&radic;2)) y'<sub>3</sub>)
+ * + ( - 3 &theta; + 4 &theta;<sup>2</sup>) y'<sub>4</sub>
+ * ]
+ * </li>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub> + h)
+ * - (1 - &theta;) (h/6) [ (1 - 5 &theta; + 4 &theta;<sup>2</sup>) y'<sub>1</sub>
+ * + (2 + 2 &theta; - 4 &theta;<sup>2</sup>) ((1-1/&radic;2) y'<sub>2</sub> + (1+1/&radic;2)) y'<sub>3</sub>)
+ * + (1 + &theta; + 4 &theta;<sup>2</sup>) y'<sub>4</sub>
+ * ]
+ * </li>
+ * </ul>
+ * </p>
+ * where &theta; belongs to [0 ; 1] and where y'<sub>1</sub> to y'<sub>4</sub>
+ * are the four evaluations of the derivatives already computed during
+ * the step.</p>
+ *
+ * @see GillFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+class GillFieldStepInterpolator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldStepInterpolator<T> {
+
+ /** First Gill coefficient. */
+ private final T one_minus_inv_sqrt_2;
+
+ /** Second Gill coefficient. */
+ private final T one_plus_inv_sqrt_2;
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param mapper equations mapper for the all equations
+ */
+ GillFieldStepInterpolator(final Field<T> field, final boolean forward,
+ final T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ super(field, forward, yDotK,
+ globalPreviousState, globalCurrentState, softPreviousState, softCurrentState,
+ mapper);
+ final T sqrt = field.getZero().add(0.5).sqrt();
+ one_minus_inv_sqrt_2 = field.getOne().subtract(sqrt);
+ one_plus_inv_sqrt_2 = field.getOne().add(sqrt);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected GillFieldStepInterpolator<T> create(final Field<T> newField, final boolean newForward, final T[][] newYDotK,
+ final FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ final FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ final FieldODEStateAndDerivative<T> newSoftPreviousState,
+ final FieldODEStateAndDerivative<T> newSoftCurrentState,
+ final FieldEquationsMapper<T> newMapper) {
+ return new GillFieldStepInterpolator<T>(newField, newForward, newYDotK,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(final FieldEquationsMapper<T> mapper,
+ final T time, final T theta,
+ final T thetaH, final T oneMinusThetaH) {
+
+ final T one = time.getField().getOne();
+ final T twoTheta = theta.multiply(2);
+ final T fourTheta2 = twoTheta.multiply(twoTheta);
+ final T coeffDot1 = theta.multiply(twoTheta.subtract(3)).add(1);
+ final T cDot23 = twoTheta.multiply(one.subtract(theta));
+ final T coeffDot2 = cDot23.multiply(one_minus_inv_sqrt_2);
+ final T coeffDot3 = cDot23.multiply(one_plus_inv_sqrt_2);
+ final T coeffDot4 = theta.multiply(twoTheta.subtract(1));
+ final T[] interpolatedState;
+ final T[] interpolatedDerivatives;
+
+ if (getGlobalPreviousState() != null && theta.getReal() <= 0.5) {
+ final T s = thetaH.divide(6.0);
+ final T c23 = s.multiply(theta.multiply(6).subtract(fourTheta2));
+ final T coeff1 = s.multiply(fourTheta2.subtract(theta.multiply(9)).add(6));
+ final T coeff2 = c23.multiply(one_minus_inv_sqrt_2);
+ final T coeff3 = c23.multiply(one_plus_inv_sqrt_2);
+ final T coeff4 = s.multiply(fourTheta2.subtract(theta.multiply(3)));
+ interpolatedState = previousStateLinearCombination(coeff1, coeff2, coeff3, coeff4);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot1, coeffDot2, coeffDot3, coeffDot4);
+ } else {
+ final T s = oneMinusThetaH.divide(-6.0);
+ final T c23 = s.multiply(twoTheta.add(2).subtract(fourTheta2));
+ final T coeff1 = s.multiply(fourTheta2.subtract(theta.multiply(5)).add(1));
+ final T coeff2 = c23.multiply(one_minus_inv_sqrt_2);
+ final T coeff3 = c23.multiply(one_plus_inv_sqrt_2);
+ final T coeff4 = s.multiply(fourTheta2.add(theta).add(1));
+ interpolatedState = currentStateLinearCombination(coeff1, coeff2, coeff3, coeff4);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot1, coeffDot2, coeffDot3, coeffDot4);
+ }
+
+ return new FieldODEStateAndDerivative<T>(time, interpolatedState, interpolatedDerivatives);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/GillIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/GillIntegrator.java
new file mode 100644
index 0000000..5273e02
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/GillIntegrator.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.util.FastMath;
+
+
+/**
+ * This class implements the Gill fourth order Runge-Kutta
+ * integrator for Ordinary Differential Equations .
+
+ * <p>This method is an explicit Runge-Kutta method, its Butcher-array
+ * is the following one :
+ * <pre>
+ * 0 | 0 0 0 0
+ * 1/2 | 1/2 0 0 0
+ * 1/2 | (q-1)/2 (2-q)/2 0 0
+ * 1 | 0 -q/2 (2+q)/2 0
+ * |-------------------------------
+ * | 1/6 (2-q)/6 (2+q)/6 1/6
+ * </pre>
+ * where q = sqrt(2)</p>
+ *
+ * @see EulerIntegrator
+ * @see ClassicalRungeKuttaIntegrator
+ * @see MidpointIntegrator
+ * @see ThreeEighthesIntegrator
+ * @see LutherIntegrator
+ * @since 1.2
+ */
+
+public class GillIntegrator extends RungeKuttaIntegrator {
+
+ /** Time steps Butcher array. */
+ private static final double[] STATIC_C = {
+ 1.0 / 2.0, 1.0 / 2.0, 1.0
+ };
+
+ /** Internal weights Butcher array. */
+ private static final double[][] STATIC_A = {
+ { 1.0 / 2.0 },
+ { (FastMath.sqrt(2.0) - 1.0) / 2.0, (2.0 - FastMath.sqrt(2.0)) / 2.0 },
+ { 0.0, -FastMath.sqrt(2.0) / 2.0, (2.0 + FastMath.sqrt(2.0)) / 2.0 }
+ };
+
+ /** Propagation weights Butcher array. */
+ private static final double[] STATIC_B = {
+ 1.0 / 6.0, (2.0 - FastMath.sqrt(2.0)) / 6.0, (2.0 + FastMath.sqrt(2.0)) / 6.0, 1.0 / 6.0
+ };
+
+ /** Simple constructor.
+ * Build a fourth-order Gill integrator with the given step.
+ * @param step integration step
+ */
+ public GillIntegrator(final double step) {
+ super("Gill", STATIC_C, STATIC_A, STATIC_B, new GillStepInterpolator(), step);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/GillStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/GillStepInterpolator.java
new file mode 100644
index 0000000..72dec69
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/GillStepInterpolator.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class implements a step interpolator for the Gill fourth
+ * order Runge-Kutta integrator.
+ *
+ * <p>This interpolator allows to compute dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme :
+ * <ul>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub>)
+ * + &theta; (h/6) [ (6 - 9 &theta; + 4 &theta;<sup>2</sup>) y'<sub>1</sub>
+ * + ( 6 &theta; - 4 &theta;<sup>2</sup>) ((1-1/&radic;2) y'<sub>2</sub> + (1+1/&radic;2)) y'<sub>3</sub>)
+ * + ( - 3 &theta; + 4 &theta;<sup>2</sup>) y'<sub>4</sub>
+ * ]
+ * </li>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub> + h)
+ * - (1 - &theta;) (h/6) [ (1 - 5 &theta; + 4 &theta;<sup>2</sup>) y'<sub>1</sub>
+ * + (2 + 2 &theta; - 4 &theta;<sup>2</sup>) ((1-1/&radic;2) y'<sub>2</sub> + (1+1/&radic;2)) y'<sub>3</sub>)
+ * + (1 + &theta; + 4 &theta;<sup>2</sup>) y'<sub>4</sub>
+ * ]
+ * </li>
+ * </ul>
+ * </p>
+ * where &theta; belongs to [0 ; 1] and where y'<sub>1</sub> to y'<sub>4</sub>
+ * are the four evaluations of the derivatives already computed during
+ * the step.</p>
+ *
+ * @see GillIntegrator
+ * @since 1.2
+ */
+
+class GillStepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** First Gill coefficient. */
+ private static final double ONE_MINUS_INV_SQRT_2 = 1 - FastMath.sqrt(0.5);
+
+ /** Second Gill coefficient. */
+ private static final double ONE_PLUS_INV_SQRT_2 = 1 + FastMath.sqrt(0.5);
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20111120L;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link
+ * org.apache.commons.math3.ode.sampling.AbstractStepInterpolator#reinitialize}
+ * method should be called before using the instance in order to
+ * initialize the internal arrays. This constructor is used only
+ * in order to delay the initialization in some cases. The {@link
+ * RungeKuttaIntegrator} class uses the prototyping design pattern
+ * to create the step interpolators by cloning an uninitialized model
+ * and later initializing the copy.
+ */
+ // CHECKSTYLE: stop RedundantModifier
+ // the public modifier here is needed for serialization
+ public GillStepInterpolator() {
+ }
+ // CHECKSTYLE: resume RedundantModifier
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ GillStepInterpolator(final GillStepInterpolator interpolator) {
+ super(interpolator);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new GillStepInterpolator(this);
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta,
+ final double oneMinusThetaH) {
+
+ final double twoTheta = 2 * theta;
+ final double fourTheta2 = twoTheta * twoTheta;
+ final double coeffDot1 = theta * (twoTheta - 3) + 1;
+ final double cDot23 = twoTheta * (1 - theta);
+ final double coeffDot2 = cDot23 * ONE_MINUS_INV_SQRT_2;
+ final double coeffDot3 = cDot23 * ONE_PLUS_INV_SQRT_2;
+ final double coeffDot4 = theta * (twoTheta - 1);
+
+ if ((previousState != null) && (theta <= 0.5)) {
+ final double s = theta * h / 6.0;
+ final double c23 = s * (6 * theta - fourTheta2);
+ final double coeff1 = s * (6 - 9 * theta + fourTheta2);
+ final double coeff2 = c23 * ONE_MINUS_INV_SQRT_2;
+ final double coeff3 = c23 * ONE_PLUS_INV_SQRT_2;
+ final double coeff4 = s * (-3 * theta + fourTheta2);
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot2 = yDotK[1][i];
+ final double yDot3 = yDotK[2][i];
+ final double yDot4 = yDotK[3][i];
+ interpolatedState[i] =
+ previousState[i] + coeff1 * yDot1 + coeff2 * yDot2 + coeff3 * yDot3 + coeff4 * yDot4;
+ interpolatedDerivatives[i] =
+ coeffDot1 * yDot1 + coeffDot2 * yDot2 + coeffDot3 * yDot3 + coeffDot4 * yDot4;
+ }
+ } else {
+ final double s = oneMinusThetaH / 6.0;
+ final double c23 = s * (2 + twoTheta - fourTheta2);
+ final double coeff1 = s * (1 - 5 * theta + fourTheta2);
+ final double coeff2 = c23 * ONE_MINUS_INV_SQRT_2;
+ final double coeff3 = c23 * ONE_PLUS_INV_SQRT_2;
+ final double coeff4 = s * (1 + theta + fourTheta2);
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot2 = yDotK[1][i];
+ final double yDot3 = yDotK[2][i];
+ final double yDot4 = yDotK[3][i];
+ interpolatedState[i] =
+ currentState[i] - coeff1 * yDot1 - coeff2 * yDot2 - coeff3 * yDot3 - coeff4 * yDot4;
+ interpolatedDerivatives[i] =
+ coeffDot1 * yDot1 + coeffDot2 * yDot2 + coeffDot3 * yDot3 + coeffDot4 * yDot4;
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerIntegrator.java
new file mode 100644
index 0000000..50a463e
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerIntegrator.java
@@ -0,0 +1,949 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.analysis.solvers.UnivariateSolver;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.ode.ExpandableStatefulODE;
+import org.apache.commons.math3.ode.events.EventHandler;
+import org.apache.commons.math3.ode.sampling.AbstractStepInterpolator;
+import org.apache.commons.math3.ode.sampling.StepHandler;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class implements a Gragg-Bulirsch-Stoer integrator for
+ * Ordinary Differential Equations.
+ *
+ * <p>The Gragg-Bulirsch-Stoer algorithm is one of the most efficient
+ * ones currently available for smooth problems. It uses Richardson
+ * extrapolation to estimate what would be the solution if the step
+ * size could be decreased down to zero.</p>
+ *
+ * <p>
+ * This method changes both the step size and the order during
+ * integration, in order to minimize computation cost. It is
+ * particularly well suited when a very high precision is needed. The
+ * limit where this method becomes more efficient than high-order
+ * embedded Runge-Kutta methods like {@link DormandPrince853Integrator
+ * Dormand-Prince 8(5,3)} depends on the problem. Results given in the
+ * Hairer, Norsett and Wanner book show for example that this limit
+ * occurs for accuracy around 1e-6 when integrating Saltzam-Lorenz
+ * equations (the authors note this problem is <i>extremely sensitive
+ * to the errors in the first integration steps</i>), and around 1e-11
+ * for a two dimensional celestial mechanics problems with seven
+ * bodies (pleiades problem, involving quasi-collisions for which
+ * <i>automatic step size control is essential</i>).
+ * </p>
+ *
+ * <p>
+ * This implementation is basically a reimplementation in Java of the
+ * <a
+ * href="http://www.unige.ch/math/folks/hairer/prog/nonstiff/odex.f">odex</a>
+ * fortran code by E. Hairer and G. Wanner. The redistribution policy
+ * for this code is available <a
+ * href="http://www.unige.ch/~hairer/prog/licence.txt">here</a>, for
+ * convenience, it is reproduced below.</p>
+ * </p>
+ *
+ * <table border="0" width="80%" cellpadding="10" align="center" bgcolor="#E0E0E0">
+ * <tr><td>Copyright (c) 2004, Ernst Hairer</td></tr>
+ *
+ * <tr><td>Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ * <ul>
+ * <li>Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.</li>
+ * <li>Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.</li>
+ * </ul></td></tr>
+ *
+ * <tr><td><strong>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</strong></td></tr>
+ * </table>
+ *
+ * @since 1.2
+ */
+
+public class GraggBulirschStoerIntegrator extends AdaptiveStepsizeIntegrator {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Gragg-Bulirsch-Stoer";
+
+ /** maximal order. */
+ private int maxOrder;
+
+ /** step size sequence. */
+ private int[] sequence;
+
+ /** overall cost of applying step reduction up to iteration k+1, in number of calls. */
+ private int[] costPerStep;
+
+ /** cost per unit step. */
+ private double[] costPerTimeUnit;
+
+ /** optimal steps for each order. */
+ private double[] optimalStep;
+
+ /** extrapolation coefficients. */
+ private double[][] coeff;
+
+ /** stability check enabling parameter. */
+ private boolean performTest;
+
+ /** maximal number of checks for each iteration. */
+ private int maxChecks;
+
+ /** maximal number of iterations for which checks are performed. */
+ private int maxIter;
+
+ /** stepsize reduction factor in case of stability check failure. */
+ private double stabilityReduction;
+
+ /** first stepsize control factor. */
+ private double stepControl1;
+
+ /** second stepsize control factor. */
+ private double stepControl2;
+
+ /** third stepsize control factor. */
+ private double stepControl3;
+
+ /** fourth stepsize control factor. */
+ private double stepControl4;
+
+ /** first order control factor. */
+ private double orderControl1;
+
+ /** second order control factor. */
+ private double orderControl2;
+
+ /** use interpolation error in stepsize control. */
+ private boolean useInterpolationError;
+
+ /** interpolation order control parameter. */
+ private int mudif;
+
+ /** Simple constructor.
+ * Build a Gragg-Bulirsch-Stoer integrator with the given step
+ * bounds. All tuning parameters are set to their default
+ * values. The default step handler does nothing.
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ public GraggBulirschStoerIntegrator(final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+ super(METHOD_NAME, minStep, maxStep,
+ scalAbsoluteTolerance, scalRelativeTolerance);
+ setStabilityCheck(true, -1, -1, -1);
+ setControlFactors(-1, -1, -1, -1);
+ setOrderControl(-1, -1, -1);
+ setInterpolationControl(true, -1);
+ }
+
+ /** Simple constructor.
+ * Build a Gragg-Bulirsch-Stoer integrator with the given step
+ * bounds. All tuning parameters are set to their default
+ * values. The default step handler does nothing.
+ * @param minStep minimal step (must be positive even for backward
+ * integration), the last step can be smaller than this
+ * @param maxStep maximal step (must be positive even for backward
+ * integration)
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ public GraggBulirschStoerIntegrator(final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+ super(METHOD_NAME, minStep, maxStep,
+ vecAbsoluteTolerance, vecRelativeTolerance);
+ setStabilityCheck(true, -1, -1, -1);
+ setControlFactors(-1, -1, -1, -1);
+ setOrderControl(-1, -1, -1);
+ setInterpolationControl(true, -1);
+ }
+
+ /** Set the stability check controls.
+ * <p>The stability check is performed on the first few iterations of
+ * the extrapolation scheme. If this test fails, the step is rejected
+ * and the stepsize is reduced.</p>
+ * <p>By default, the test is performed, at most during two
+ * iterations at each step, and at most once for each of these
+ * iterations. The default stepsize reduction factor is 0.5.</p>
+ * @param performStabilityCheck if true, stability check will be performed,
+ if false, the check will be skipped
+ * @param maxNumIter maximal number of iterations for which checks are
+ * performed (the number of iterations is reset to default if negative
+ * or null)
+ * @param maxNumChecks maximal number of checks for each iteration
+ * (the number of checks is reset to default if negative or null)
+ * @param stepsizeReductionFactor stepsize reduction factor in case of
+ * failure (the factor is reset to default if lower than 0.0001 or
+ * greater than 0.9999)
+ */
+ public void setStabilityCheck(final boolean performStabilityCheck,
+ final int maxNumIter, final int maxNumChecks,
+ final double stepsizeReductionFactor) {
+
+ this.performTest = performStabilityCheck;
+ this.maxIter = (maxNumIter <= 0) ? 2 : maxNumIter;
+ this.maxChecks = (maxNumChecks <= 0) ? 1 : maxNumChecks;
+
+ if ((stepsizeReductionFactor < 0.0001) || (stepsizeReductionFactor > 0.9999)) {
+ this.stabilityReduction = 0.5;
+ } else {
+ this.stabilityReduction = stepsizeReductionFactor;
+ }
+
+ }
+
+ /** Set the step size control factors.
+
+ * <p>The new step size hNew is computed from the old one h by:
+ * <pre>
+ * hNew = h * stepControl2 / (err/stepControl1)^(1/(2k+1))
+ * </pre>
+ * where err is the scaled error and k the iteration number of the
+ * extrapolation scheme (counting from 0). The default values are
+ * 0.65 for stepControl1 and 0.94 for stepControl2.</p>
+ * <p>The step size is subject to the restriction:
+ * <pre>
+ * stepControl3^(1/(2k+1))/stepControl4 <= hNew/h <= 1/stepControl3^(1/(2k+1))
+ * </pre>
+ * The default values are 0.02 for stepControl3 and 4.0 for
+ * stepControl4.</p>
+ * @param control1 first stepsize control factor (the factor is
+ * reset to default if lower than 0.0001 or greater than 0.9999)
+ * @param control2 second stepsize control factor (the factor
+ * is reset to default if lower than 0.0001 or greater than 0.9999)
+ * @param control3 third stepsize control factor (the factor is
+ * reset to default if lower than 0.0001 or greater than 0.9999)
+ * @param control4 fourth stepsize control factor (the factor
+ * is reset to default if lower than 1.0001 or greater than 999.9)
+ */
+ public void setControlFactors(final double control1, final double control2,
+ final double control3, final double control4) {
+
+ if ((control1 < 0.0001) || (control1 > 0.9999)) {
+ this.stepControl1 = 0.65;
+ } else {
+ this.stepControl1 = control1;
+ }
+
+ if ((control2 < 0.0001) || (control2 > 0.9999)) {
+ this.stepControl2 = 0.94;
+ } else {
+ this.stepControl2 = control2;
+ }
+
+ if ((control3 < 0.0001) || (control3 > 0.9999)) {
+ this.stepControl3 = 0.02;
+ } else {
+ this.stepControl3 = control3;
+ }
+
+ if ((control4 < 1.0001) || (control4 > 999.9)) {
+ this.stepControl4 = 4.0;
+ } else {
+ this.stepControl4 = control4;
+ }
+
+ }
+
+ /** Set the order control parameters.
+ * <p>The Gragg-Bulirsch-Stoer method changes both the step size and
+ * the order during integration, in order to minimize computation
+ * cost. Each extrapolation step increases the order by 2, so the
+ * maximal order that will be used is always even, it is twice the
+ * maximal number of columns in the extrapolation table.</p>
+ * <pre>
+ * order is decreased if w(k-1) <= w(k) * orderControl1
+ * order is increased if w(k) <= w(k-1) * orderControl2
+ * </pre>
+ * <p>where w is the table of work per unit step for each order
+ * (number of function calls divided by the step length), and k is
+ * the current order.</p>
+ * <p>The default maximal order after construction is 18 (i.e. the
+ * maximal number of columns is 9). The default values are 0.8 for
+ * orderControl1 and 0.9 for orderControl2.</p>
+ * @param maximalOrder maximal order in the extrapolation table (the
+ * maximal order is reset to default if order <= 6 or odd)
+ * @param control1 first order control factor (the factor is
+ * reset to default if lower than 0.0001 or greater than 0.9999)
+ * @param control2 second order control factor (the factor
+ * is reset to default if lower than 0.0001 or greater than 0.9999)
+ */
+ public void setOrderControl(final int maximalOrder,
+ final double control1, final double control2) {
+
+ if ((maximalOrder <= 6) || (maximalOrder % 2 != 0)) {
+ this.maxOrder = 18;
+ }
+
+ if ((control1 < 0.0001) || (control1 > 0.9999)) {
+ this.orderControl1 = 0.8;
+ } else {
+ this.orderControl1 = control1;
+ }
+
+ if ((control2 < 0.0001) || (control2 > 0.9999)) {
+ this.orderControl2 = 0.9;
+ } else {
+ this.orderControl2 = control2;
+ }
+
+ // reinitialize the arrays
+ initializeArrays();
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addStepHandler (final StepHandler handler) {
+
+ super.addStepHandler(handler);
+
+ // reinitialize the arrays
+ initializeArrays();
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void addEventHandler(final EventHandler function,
+ final double maxCheckInterval,
+ final double convergence,
+ final int maxIterationCount,
+ final UnivariateSolver solver) {
+ super.addEventHandler(function, maxCheckInterval, convergence,
+ maxIterationCount, solver);
+
+ // reinitialize the arrays
+ initializeArrays();
+
+ }
+
+ /** Initialize the integrator internal arrays. */
+ private void initializeArrays() {
+
+ final int size = maxOrder / 2;
+
+ if ((sequence == null) || (sequence.length != size)) {
+ // all arrays should be reallocated with the right size
+ sequence = new int[size];
+ costPerStep = new int[size];
+ coeff = new double[size][];
+ costPerTimeUnit = new double[size];
+ optimalStep = new double[size];
+ }
+
+ // step size sequence: 2, 6, 10, 14, ...
+ for (int k = 0; k < size; ++k) {
+ sequence[k] = 4 * k + 2;
+ }
+
+ // initialize the order selection cost array
+ // (number of function calls for each column of the extrapolation table)
+ costPerStep[0] = sequence[0] + 1;
+ for (int k = 1; k < size; ++k) {
+ costPerStep[k] = costPerStep[k-1] + sequence[k];
+ }
+
+ // initialize the extrapolation tables
+ for (int k = 0; k < size; ++k) {
+ coeff[k] = (k > 0) ? new double[k] : null;
+ for (int l = 0; l < k; ++l) {
+ final double ratio = ((double) sequence[k]) / sequence[k-l-1];
+ coeff[k][l] = 1.0 / (ratio * ratio - 1.0);
+ }
+ }
+
+ }
+
+ /** Set the interpolation order control parameter.
+ * The interpolation order for dense output is 2k - mudif + 1. The
+ * default value for mudif is 4 and the interpolation error is used
+ * in stepsize control by default.
+
+ * @param useInterpolationErrorForControl if true, interpolation error is used
+ * for stepsize control
+ * @param mudifControlParameter interpolation order control parameter (the parameter
+ * is reset to default if <= 0 or >= 7)
+ */
+ public void setInterpolationControl(final boolean useInterpolationErrorForControl,
+ final int mudifControlParameter) {
+
+ this.useInterpolationError = useInterpolationErrorForControl;
+
+ if ((mudifControlParameter <= 0) || (mudifControlParameter >= 7)) {
+ this.mudif = 4;
+ } else {
+ this.mudif = mudifControlParameter;
+ }
+
+ }
+
+ /** Update scaling array.
+ * @param y1 first state vector to use for scaling
+ * @param y2 second state vector to use for scaling
+ * @param scale scaling array to update (can be shorter than state)
+ */
+ private void rescale(final double[] y1, final double[] y2, final double[] scale) {
+ if (vecAbsoluteTolerance == null) {
+ for (int i = 0; i < scale.length; ++i) {
+ final double yi = FastMath.max(FastMath.abs(y1[i]), FastMath.abs(y2[i]));
+ scale[i] = scalAbsoluteTolerance + scalRelativeTolerance * yi;
+ }
+ } else {
+ for (int i = 0; i < scale.length; ++i) {
+ final double yi = FastMath.max(FastMath.abs(y1[i]), FastMath.abs(y2[i]));
+ scale[i] = vecAbsoluteTolerance[i] + vecRelativeTolerance[i] * yi;
+ }
+ }
+ }
+
+ /** Perform integration over one step using substeps of a modified
+ * midpoint method.
+ * @param t0 initial time
+ * @param y0 initial value of the state vector at t0
+ * @param step global step
+ * @param k iteration number (from 0 to sequence.length - 1)
+ * @param scale scaling array (can be shorter than state)
+ * @param f placeholder where to put the state vector derivatives at each substep
+ * (element 0 already contains initial derivative)
+ * @param yMiddle placeholder where to put the state vector at the middle of the step
+ * @param yEnd placeholder where to put the state vector at the end
+ * @param yTmp placeholder for one state vector
+ * @return true if computation was done properly,
+ * false if stability check failed before end of computation
+ * @exception MaxCountExceededException if the number of functions evaluations is exceeded
+ * @exception DimensionMismatchException if arrays dimensions do not match equations settings
+ */
+ private boolean tryStep(final double t0, final double[] y0, final double step, final int k,
+ final double[] scale, final double[][] f,
+ final double[] yMiddle, final double[] yEnd,
+ final double[] yTmp)
+ throws MaxCountExceededException, DimensionMismatchException {
+
+ final int n = sequence[k];
+ final double subStep = step / n;
+ final double subStep2 = 2 * subStep;
+
+ // first substep
+ double t = t0 + subStep;
+ for (int i = 0; i < y0.length; ++i) {
+ yTmp[i] = y0[i];
+ yEnd[i] = y0[i] + subStep * f[0][i];
+ }
+ computeDerivatives(t, yEnd, f[1]);
+
+ // other substeps
+ for (int j = 1; j < n; ++j) {
+
+ if (2 * j == n) {
+ // save the point at the middle of the step
+ System.arraycopy(yEnd, 0, yMiddle, 0, y0.length);
+ }
+
+ t += subStep;
+ for (int i = 0; i < y0.length; ++i) {
+ final double middle = yEnd[i];
+ yEnd[i] = yTmp[i] + subStep2 * f[j][i];
+ yTmp[i] = middle;
+ }
+
+ computeDerivatives(t, yEnd, f[j+1]);
+
+ // stability check
+ if (performTest && (j <= maxChecks) && (k < maxIter)) {
+ double initialNorm = 0.0;
+ for (int l = 0; l < scale.length; ++l) {
+ final double ratio = f[0][l] / scale[l];
+ initialNorm += ratio * ratio;
+ }
+ double deltaNorm = 0.0;
+ for (int l = 0; l < scale.length; ++l) {
+ final double ratio = (f[j+1][l] - f[0][l]) / scale[l];
+ deltaNorm += ratio * ratio;
+ }
+ if (deltaNorm > 4 * FastMath.max(1.0e-15, initialNorm)) {
+ return false;
+ }
+ }
+
+ }
+
+ // correction of the last substep (at t0 + step)
+ for (int i = 0; i < y0.length; ++i) {
+ yEnd[i] = 0.5 * (yTmp[i] + yEnd[i] + subStep * f[n][i]);
+ }
+
+ return true;
+
+ }
+
+ /** Extrapolate a vector.
+ * @param offset offset to use in the coefficients table
+ * @param k index of the last updated point
+ * @param diag working diagonal of the Aitken-Neville's
+ * triangle, without the last element
+ * @param last last element
+ */
+ private void extrapolate(final int offset, final int k,
+ final double[][] diag, final double[] last) {
+
+ // update the diagonal
+ for (int j = 1; j < k; ++j) {
+ for (int i = 0; i < last.length; ++i) {
+ // Aitken-Neville's recursive formula
+ diag[k-j-1][i] = diag[k-j][i] +
+ coeff[k+offset][j-1] * (diag[k-j][i] - diag[k-j-1][i]);
+ }
+ }
+
+ // update the last element
+ for (int i = 0; i < last.length; ++i) {
+ // Aitken-Neville's recursive formula
+ last[i] = diag[0][i] + coeff[k+offset][k-1] * (diag[0][i] - last[i]);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void integrate(final ExpandableStatefulODE equations, final double t)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException {
+
+ sanityChecks(equations, t);
+ setEquations(equations);
+ final boolean forward = t > equations.getTime();
+
+ // create some internal working arrays
+ final double[] y0 = equations.getCompleteState();
+ final double[] y = y0.clone();
+ final double[] yDot0 = new double[y.length];
+ final double[] y1 = new double[y.length];
+ final double[] yTmp = new double[y.length];
+ final double[] yTmpDot = new double[y.length];
+
+ final double[][] diagonal = new double[sequence.length-1][];
+ final double[][] y1Diag = new double[sequence.length-1][];
+ for (int k = 0; k < sequence.length-1; ++k) {
+ diagonal[k] = new double[y.length];
+ y1Diag[k] = new double[y.length];
+ }
+
+ final double[][][] fk = new double[sequence.length][][];
+ for (int k = 0; k < sequence.length; ++k) {
+
+ fk[k] = new double[sequence[k] + 1][];
+
+ // all substeps start at the same point, so share the first array
+ fk[k][0] = yDot0;
+
+ for (int l = 0; l < sequence[k]; ++l) {
+ fk[k][l+1] = new double[y0.length];
+ }
+
+ }
+
+ if (y != y0) {
+ System.arraycopy(y0, 0, y, 0, y0.length);
+ }
+
+ final double[] yDot1 = new double[y0.length];
+ final double[][] yMidDots = new double[1 + 2 * sequence.length][y0.length];
+
+ // initial scaling
+ final double[] scale = new double[mainSetDimension];
+ rescale(y, y, scale);
+
+ // initial order selection
+ final double tol =
+ (vecRelativeTolerance == null) ? scalRelativeTolerance : vecRelativeTolerance[0];
+ final double log10R = FastMath.log10(FastMath.max(1.0e-10, tol));
+ int targetIter = FastMath.max(1,
+ FastMath.min(sequence.length - 2,
+ (int) FastMath.floor(0.5 - 0.6 * log10R)));
+
+ // set up an interpolator sharing the integrator arrays
+ final AbstractStepInterpolator interpolator =
+ new GraggBulirschStoerStepInterpolator(y, yDot0,
+ y1, yDot1,
+ yMidDots, forward,
+ equations.getPrimaryMapper(),
+ equations.getSecondaryMappers());
+ interpolator.storeTime(equations.getTime());
+
+ stepStart = equations.getTime();
+ double hNew = 0;
+ double maxError = Double.MAX_VALUE;
+ boolean previousRejected = false;
+ boolean firstTime = true;
+ boolean newStep = true;
+ boolean firstStepAlreadyComputed = false;
+ initIntegration(equations.getTime(), y0, t);
+ costPerTimeUnit[0] = 0;
+ isLastStep = false;
+ do {
+
+ double error;
+ boolean reject = false;
+
+ if (newStep) {
+
+ interpolator.shift();
+
+ // first evaluation, at the beginning of the step
+ if (! firstStepAlreadyComputed) {
+ computeDerivatives(stepStart, y, yDot0);
+ }
+
+ if (firstTime) {
+ hNew = initializeStep(forward, 2 * targetIter + 1, scale,
+ stepStart, y, yDot0, yTmp, yTmpDot);
+ }
+
+ newStep = false;
+
+ }
+
+ stepSize = hNew;
+
+ // step adjustment near bounds
+ if ((forward && (stepStart + stepSize > t)) ||
+ ((! forward) && (stepStart + stepSize < t))) {
+ stepSize = t - stepStart;
+ }
+ final double nextT = stepStart + stepSize;
+ isLastStep = forward ? (nextT >= t) : (nextT <= t);
+
+ // iterate over several substep sizes
+ int k = -1;
+ for (boolean loop = true; loop; ) {
+
+ ++k;
+
+ // modified midpoint integration with the current substep
+ if ( ! tryStep(stepStart, y, stepSize, k, scale, fk[k],
+ (k == 0) ? yMidDots[0] : diagonal[k-1],
+ (k == 0) ? y1 : y1Diag[k-1],
+ yTmp)) {
+
+ // the stability check failed, we reduce the global step
+ hNew = FastMath.abs(filterStep(stepSize * stabilityReduction, forward, false));
+ reject = true;
+ loop = false;
+
+ } else {
+
+ // the substep was computed successfully
+ if (k > 0) {
+
+ // extrapolate the state at the end of the step
+ // using last iteration data
+ extrapolate(0, k, y1Diag, y1);
+ rescale(y, y1, scale);
+
+ // estimate the error at the end of the step.
+ error = 0;
+ for (int j = 0; j < mainSetDimension; ++j) {
+ final double e = FastMath.abs(y1[j] - y1Diag[0][j]) / scale[j];
+ error += e * e;
+ }
+ error = FastMath.sqrt(error / mainSetDimension);
+
+ if ((error > 1.0e15) || ((k > 1) && (error > maxError))) {
+ // error is too big, we reduce the global step
+ hNew = FastMath.abs(filterStep(stepSize * stabilityReduction, forward, false));
+ reject = true;
+ loop = false;
+ } else {
+
+ maxError = FastMath.max(4 * error, 1.0);
+
+ // compute optimal stepsize for this order
+ final double exp = 1.0 / (2 * k + 1);
+ double fac = stepControl2 / FastMath.pow(error / stepControl1, exp);
+ final double pow = FastMath.pow(stepControl3, exp);
+ fac = FastMath.max(pow / stepControl4, FastMath.min(1 / pow, fac));
+ optimalStep[k] = FastMath.abs(filterStep(stepSize * fac, forward, true));
+ costPerTimeUnit[k] = costPerStep[k] / optimalStep[k];
+
+ // check convergence
+ switch (k - targetIter) {
+
+ case -1 :
+ if ((targetIter > 1) && ! previousRejected) {
+
+ // check if we can stop iterations now
+ if (error <= 1.0) {
+ // convergence have been reached just before targetIter
+ loop = false;
+ } else {
+ // estimate if there is a chance convergence will
+ // be reached on next iteration, using the
+ // asymptotic evolution of error
+ final double ratio = ((double) sequence [targetIter] * sequence[targetIter + 1]) /
+ (sequence[0] * sequence[0]);
+ if (error > ratio * ratio) {
+ // we don't expect to converge on next iteration
+ // we reject the step immediately and reduce order
+ reject = true;
+ loop = false;
+ targetIter = k;
+ if ((targetIter > 1) &&
+ (costPerTimeUnit[targetIter-1] <
+ orderControl1 * costPerTimeUnit[targetIter])) {
+ --targetIter;
+ }
+ hNew = optimalStep[targetIter];
+ }
+ }
+ }
+ break;
+
+ case 0:
+ if (error <= 1.0) {
+ // convergence has been reached exactly at targetIter
+ loop = false;
+ } else {
+ // estimate if there is a chance convergence will
+ // be reached on next iteration, using the
+ // asymptotic evolution of error
+ final double ratio = ((double) sequence[k+1]) / sequence[0];
+ if (error > ratio * ratio) {
+ // we don't expect to converge on next iteration
+ // we reject the step immediately
+ reject = true;
+ loop = false;
+ if ((targetIter > 1) &&
+ (costPerTimeUnit[targetIter-1] <
+ orderControl1 * costPerTimeUnit[targetIter])) {
+ --targetIter;
+ }
+ hNew = optimalStep[targetIter];
+ }
+ }
+ break;
+
+ case 1 :
+ if (error > 1.0) {
+ reject = true;
+ if ((targetIter > 1) &&
+ (costPerTimeUnit[targetIter-1] <
+ orderControl1 * costPerTimeUnit[targetIter])) {
+ --targetIter;
+ }
+ hNew = optimalStep[targetIter];
+ }
+ loop = false;
+ break;
+
+ default :
+ if ((firstTime || isLastStep) && (error <= 1.0)) {
+ loop = false;
+ }
+ break;
+
+ }
+
+ }
+ }
+ }
+ }
+
+ if (! reject) {
+ // derivatives at end of step
+ computeDerivatives(stepStart + stepSize, y1, yDot1);
+ }
+
+ // dense output handling
+ double hInt = getMaxStep();
+ if (! reject) {
+
+ // extrapolate state at middle point of the step
+ for (int j = 1; j <= k; ++j) {
+ extrapolate(0, j, diagonal, yMidDots[0]);
+ }
+
+ final int mu = 2 * k - mudif + 3;
+
+ for (int l = 0; l < mu; ++l) {
+
+ // derivative at middle point of the step
+ final int l2 = l / 2;
+ double factor = FastMath.pow(0.5 * sequence[l2], l);
+ int middleIndex = fk[l2].length / 2;
+ for (int i = 0; i < y0.length; ++i) {
+ yMidDots[l+1][i] = factor * fk[l2][middleIndex + l][i];
+ }
+ for (int j = 1; j <= k - l2; ++j) {
+ factor = FastMath.pow(0.5 * sequence[j + l2], l);
+ middleIndex = fk[l2+j].length / 2;
+ for (int i = 0; i < y0.length; ++i) {
+ diagonal[j-1][i] = factor * fk[l2+j][middleIndex+l][i];
+ }
+ extrapolate(l2, j, diagonal, yMidDots[l+1]);
+ }
+ for (int i = 0; i < y0.length; ++i) {
+ yMidDots[l+1][i] *= stepSize;
+ }
+
+ // compute centered differences to evaluate next derivatives
+ for (int j = (l + 1) / 2; j <= k; ++j) {
+ for (int m = fk[j].length - 1; m >= 2 * (l + 1); --m) {
+ for (int i = 0; i < y0.length; ++i) {
+ fk[j][m][i] -= fk[j][m-2][i];
+ }
+ }
+ }
+
+ }
+
+ if (mu >= 0) {
+
+ // estimate the dense output coefficients
+ final GraggBulirschStoerStepInterpolator gbsInterpolator
+ = (GraggBulirschStoerStepInterpolator) interpolator;
+ gbsInterpolator.computeCoefficients(mu, stepSize);
+
+ if (useInterpolationError) {
+ // use the interpolation error to limit stepsize
+ final double interpError = gbsInterpolator.estimateError(scale);
+ hInt = FastMath.abs(stepSize / FastMath.max(FastMath.pow(interpError, 1.0 / (mu+4)),
+ 0.01));
+ if (interpError > 10.0) {
+ hNew = hInt;
+ reject = true;
+ }
+ }
+
+ }
+
+ }
+
+ if (! reject) {
+
+ // Discrete events handling
+ interpolator.storeTime(stepStart + stepSize);
+ stepStart = acceptStep(interpolator, y1, yDot1, t);
+
+ // prepare next step
+ interpolator.storeTime(stepStart);
+ System.arraycopy(y1, 0, y, 0, y0.length);
+ System.arraycopy(yDot1, 0, yDot0, 0, y0.length);
+ firstStepAlreadyComputed = true;
+
+ int optimalIter;
+ if (k == 1) {
+ optimalIter = 2;
+ if (previousRejected) {
+ optimalIter = 1;
+ }
+ } else if (k <= targetIter) {
+ optimalIter = k;
+ if (costPerTimeUnit[k-1] < orderControl1 * costPerTimeUnit[k]) {
+ optimalIter = k-1;
+ } else if (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[k-1]) {
+ optimalIter = FastMath.min(k+1, sequence.length - 2);
+ }
+ } else {
+ optimalIter = k - 1;
+ if ((k > 2) &&
+ (costPerTimeUnit[k-2] < orderControl1 * costPerTimeUnit[k-1])) {
+ optimalIter = k - 2;
+ }
+ if (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[optimalIter]) {
+ optimalIter = FastMath.min(k, sequence.length - 2);
+ }
+ }
+
+ if (previousRejected) {
+ // after a rejected step neither order nor stepsize
+ // should increase
+ targetIter = FastMath.min(optimalIter, k);
+ hNew = FastMath.min(FastMath.abs(stepSize), optimalStep[targetIter]);
+ } else {
+ // stepsize control
+ if (optimalIter <= k) {
+ hNew = optimalStep[optimalIter];
+ } else {
+ if ((k < targetIter) &&
+ (costPerTimeUnit[k] < orderControl2 * costPerTimeUnit[k-1])) {
+ hNew = filterStep(optimalStep[k] * costPerStep[optimalIter+1] / costPerStep[k],
+ forward, false);
+ } else {
+ hNew = filterStep(optimalStep[k] * costPerStep[optimalIter] / costPerStep[k],
+ forward, false);
+ }
+ }
+
+ targetIter = optimalIter;
+
+ }
+
+ newStep = true;
+
+ }
+
+ hNew = FastMath.min(hNew, hInt);
+ if (! forward) {
+ hNew = -hNew;
+ }
+
+ firstTime = false;
+
+ if (reject) {
+ isLastStep = false;
+ previousRejected = true;
+ } else {
+ previousRejected = false;
+ }
+
+ } while (!isLastStep);
+
+ // dispatch results
+ equations.setTime(stepStart);
+ equations.setCompleteState(y);
+
+ resetInternalState();
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerStepInterpolator.java
new file mode 100644
index 0000000..33c2705
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/GraggBulirschStoerStepInterpolator.java
@@ -0,0 +1,407 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+import org.apache.commons.math3.ode.EquationsMapper;
+import org.apache.commons.math3.ode.sampling.AbstractStepInterpolator;
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class implements an interpolator for the Gragg-Bulirsch-Stoer
+ * integrator.
+ *
+ * <p>This interpolator compute dense output inside the last step
+ * produced by a Gragg-Bulirsch-Stoer integrator.</p>
+ *
+ * <p>
+ * This implementation is basically a reimplementation in Java of the
+ * <a
+ * href="http://www.unige.ch/math/folks/hairer/prog/nonstiff/odex.f">odex</a>
+ * fortran code by E. Hairer and G. Wanner. The redistribution policy
+ * for this code is available <a
+ * href="http://www.unige.ch/~hairer/prog/licence.txt">here</a>, for
+ * convenience, it is reproduced below.</p>
+ * </p>
+ *
+ * <table border="0" width="80%" cellpadding="10" align="center" bgcolor="#E0E0E0">
+ * <tr><td>Copyright (c) 2004, Ernst Hairer</td></tr>
+ *
+ * <tr><td>Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ * <ul>
+ * <li>Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.</li>
+ * <li>Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.</li>
+ * </ul></td></tr>
+ *
+ * <tr><td><strong>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</strong></td></tr>
+ * </table>
+ *
+ * @see GraggBulirschStoerIntegrator
+ * @since 1.2
+ */
+
+class GraggBulirschStoerStepInterpolator
+ extends AbstractStepInterpolator {
+
+ /** Serializable version identifier. */
+ private static final long serialVersionUID = 20110928L;
+
+ /** Slope at the beginning of the step. */
+ private double[] y0Dot;
+
+ /** State at the end of the step. */
+ private double[] y1;
+
+ /** Slope at the end of the step. */
+ private double[] y1Dot;
+
+ /** Derivatives at the middle of the step.
+ * element 0 is state at midpoint, element 1 is first derivative ...
+ */
+ private double[][] yMidDots;
+
+ /** Interpolation polynomials. */
+ private double[][] polynomials;
+
+ /** Error coefficients for the interpolation. */
+ private double[] errfac;
+
+ /** Degree of the interpolation polynomials. */
+ private int currentDegree;
+
+ /** Simple constructor.
+ * This constructor should not be used directly, it is only intended
+ * for the serialization process.
+ */
+ // CHECKSTYLE: stop RedundantModifier
+ // the public modifier here is needed for serialization
+ public GraggBulirschStoerStepInterpolator() {
+ y0Dot = null;
+ y1 = null;
+ y1Dot = null;
+ yMidDots = null;
+ resetTables(-1);
+ }
+ // CHECKSTYLE: resume RedundantModifier
+
+ /** Simple constructor.
+ * @param y reference to the integrator array holding the current state
+ * @param y0Dot reference to the integrator array holding the slope
+ * at the beginning of the step
+ * @param y1 reference to the integrator array holding the state at
+ * the end of the step
+ * @param y1Dot reference to the integrator array holding the slope
+ * at the end of the step
+ * @param yMidDots reference to the integrator array holding the
+ * derivatives at the middle point of the step
+ * @param forward integration direction indicator
+ * @param primaryMapper equations mapper for the primary equations set
+ * @param secondaryMappers equations mappers for the secondary equations sets
+ */
+ GraggBulirschStoerStepInterpolator(final double[] y, final double[] y0Dot,
+ final double[] y1, final double[] y1Dot,
+ final double[][] yMidDots,
+ final boolean forward,
+ final EquationsMapper primaryMapper,
+ final EquationsMapper[] secondaryMappers) {
+
+ super(y, forward, primaryMapper, secondaryMappers);
+ this.y0Dot = y0Dot;
+ this.y1 = y1;
+ this.y1Dot = y1Dot;
+ this.yMidDots = yMidDots;
+
+ resetTables(yMidDots.length + 4);
+
+ }
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ GraggBulirschStoerStepInterpolator(final GraggBulirschStoerStepInterpolator interpolator) {
+
+ super(interpolator);
+
+ final int dimension = currentState.length;
+
+ // the interpolator has been finalized,
+ // the following arrays are not needed anymore
+ y0Dot = null;
+ y1 = null;
+ y1Dot = null;
+ yMidDots = null;
+
+ // copy the interpolation polynomials (up to the current degree only)
+ if (interpolator.polynomials == null) {
+ polynomials = null;
+ currentDegree = -1;
+ } else {
+ resetTables(interpolator.currentDegree);
+ for (int i = 0; i < polynomials.length; ++i) {
+ polynomials[i] = new double[dimension];
+ System.arraycopy(interpolator.polynomials[i], 0,
+ polynomials[i], 0, dimension);
+ }
+ currentDegree = interpolator.currentDegree;
+ }
+
+ }
+
+ /** Reallocate the internal tables.
+ * Reallocate the internal tables in order to be able to handle
+ * interpolation polynomials up to the given degree
+ * @param maxDegree maximal degree to handle
+ */
+ private void resetTables(final int maxDegree) {
+
+ if (maxDegree < 0) {
+ polynomials = null;
+ errfac = null;
+ currentDegree = -1;
+ } else {
+
+ final double[][] newPols = new double[maxDegree + 1][];
+ if (polynomials != null) {
+ System.arraycopy(polynomials, 0, newPols, 0, polynomials.length);
+ for (int i = polynomials.length; i < newPols.length; ++i) {
+ newPols[i] = new double[currentState.length];
+ }
+ } else {
+ for (int i = 0; i < newPols.length; ++i) {
+ newPols[i] = new double[currentState.length];
+ }
+ }
+ polynomials = newPols;
+
+ // initialize the error factors array for interpolation
+ if (maxDegree <= 4) {
+ errfac = null;
+ } else {
+ errfac = new double[maxDegree - 4];
+ for (int i = 0; i < errfac.length; ++i) {
+ final int ip5 = i + 5;
+ errfac[i] = 1.0 / (ip5 * ip5);
+ final double e = 0.5 * FastMath.sqrt (((double) (i + 1)) / ip5);
+ for (int j = 0; j <= i; ++j) {
+ errfac[i] *= e / (j + 1);
+ }
+ }
+ }
+
+ currentDegree = 0;
+
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new GraggBulirschStoerStepInterpolator(this);
+ }
+
+
+ /** Compute the interpolation coefficients for dense output.
+ * @param mu degree of the interpolation polynomial
+ * @param h current step
+ */
+ public void computeCoefficients(final int mu, final double h) {
+
+ if ((polynomials == null) || (polynomials.length <= (mu + 4))) {
+ resetTables(mu + 4);
+ }
+
+ currentDegree = mu + 4;
+
+ for (int i = 0; i < currentState.length; ++i) {
+
+ final double yp0 = h * y0Dot[i];
+ final double yp1 = h * y1Dot[i];
+ final double ydiff = y1[i] - currentState[i];
+ final double aspl = ydiff - yp1;
+ final double bspl = yp0 - ydiff;
+
+ polynomials[0][i] = currentState[i];
+ polynomials[1][i] = ydiff;
+ polynomials[2][i] = aspl;
+ polynomials[3][i] = bspl;
+
+ if (mu < 0) {
+ return;
+ }
+
+ // compute the remaining coefficients
+ final double ph0 = 0.5 * (currentState[i] + y1[i]) + 0.125 * (aspl + bspl);
+ polynomials[4][i] = 16 * (yMidDots[0][i] - ph0);
+
+ if (mu > 0) {
+ final double ph1 = ydiff + 0.25 * (aspl - bspl);
+ polynomials[5][i] = 16 * (yMidDots[1][i] - ph1);
+
+ if (mu > 1) {
+ final double ph2 = yp1 - yp0;
+ polynomials[6][i] = 16 * (yMidDots[2][i] - ph2 + polynomials[4][i]);
+
+ if (mu > 2) {
+ final double ph3 = 6 * (bspl - aspl);
+ polynomials[7][i] = 16 * (yMidDots[3][i] - ph3 + 3 * polynomials[5][i]);
+
+ for (int j = 4; j <= mu; ++j) {
+ final double fac1 = 0.5 * j * (j - 1);
+ final double fac2 = 2 * fac1 * (j - 2) * (j - 3);
+ polynomials[j+4][i] =
+ 16 * (yMidDots[j][i] + fac1 * polynomials[j+2][i] - fac2 * polynomials[j][i]);
+ }
+
+ }
+ }
+ }
+ }
+
+ }
+
+ /** Estimate interpolation error.
+ * @param scale scaling array
+ * @return estimate of the interpolation error
+ */
+ public double estimateError(final double[] scale) {
+ double error = 0;
+ if (currentDegree >= 5) {
+ for (int i = 0; i < scale.length; ++i) {
+ final double e = polynomials[currentDegree][i] / scale[i];
+ error += e * e;
+ }
+ error = FastMath.sqrt(error / scale.length) * errfac[currentDegree - 5];
+ }
+ return error;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta,
+ final double oneMinusThetaH) {
+
+ final int dimension = currentState.length;
+
+ final double oneMinusTheta = 1.0 - theta;
+ final double theta05 = theta - 0.5;
+ final double tOmT = theta * oneMinusTheta;
+ final double t4 = tOmT * tOmT;
+ final double t4Dot = 2 * tOmT * (1 - 2 * theta);
+ final double dot1 = 1.0 / h;
+ final double dot2 = theta * (2 - 3 * theta) / h;
+ final double dot3 = ((3 * theta - 4) * theta + 1) / h;
+
+ for (int i = 0; i < dimension; ++i) {
+
+ final double p0 = polynomials[0][i];
+ final double p1 = polynomials[1][i];
+ final double p2 = polynomials[2][i];
+ final double p3 = polynomials[3][i];
+ interpolatedState[i] = p0 + theta * (p1 + oneMinusTheta * (p2 * theta + p3 * oneMinusTheta));
+ interpolatedDerivatives[i] = dot1 * p1 + dot2 * p2 + dot3 * p3;
+
+ if (currentDegree > 3) {
+ double cDot = 0;
+ double c = polynomials[currentDegree][i];
+ for (int j = currentDegree - 1; j > 3; --j) {
+ final double d = 1.0 / (j - 3);
+ cDot = d * (theta05 * cDot + c);
+ c = polynomials[j][i] + c * d * theta05;
+ }
+ interpolatedState[i] += t4 * c;
+ interpolatedDerivatives[i] += (t4 * cDot + t4Dot * c) / h;
+ }
+
+ }
+
+ if (h == 0) {
+ // in this degenerated case, the previous computation leads to NaN for derivatives
+ // we fix this by using the derivatives at midpoint
+ System.arraycopy(yMidDots[1], 0, interpolatedDerivatives, 0, dimension);
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeExternal(final ObjectOutput out)
+ throws IOException {
+
+ final int dimension = (currentState == null) ? -1 : currentState.length;
+
+ // save the state of the base class
+ writeBaseExternal(out);
+
+ // save the local attributes (but not the temporary vectors)
+ out.writeInt(currentDegree);
+ for (int k = 0; k <= currentDegree; ++k) {
+ for (int l = 0; l < dimension; ++l) {
+ out.writeDouble(polynomials[k][l]);
+ }
+ }
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void readExternal(final ObjectInput in)
+ throws IOException, ClassNotFoundException {
+
+ // read the base class
+ final double t = readBaseExternal(in);
+ final int dimension = (currentState == null) ? -1 : currentState.length;
+
+ // read the local attributes
+ final int degree = in.readInt();
+ resetTables(degree);
+ currentDegree = degree;
+
+ for (int k = 0; k <= currentDegree; ++k) {
+ for (int l = 0; l < dimension; ++l) {
+ polynomials[k][l] = in.readDouble();
+ }
+ }
+
+ // we can now set the interpolated time and state
+ setInterpolatedTime(t);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54FieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54FieldIntegrator.java
new file mode 100644
index 0000000..d7d6f63
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54FieldIntegrator.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+import org.apache.commons.math3.util.MathUtils;
+
+
+/**
+ * This class implements the 5(4) Higham and Hall integrator for
+ * Ordinary Differential Equations.
+ *
+ * <p>This integrator is an embedded Runge-Kutta integrator
+ * of order 5(4) used in local extrapolation mode (i.e. the solution
+ * is computed using the high order formula) with stepsize control
+ * (and automatic step initialization) and continuous output. This
+ * method uses 7 functions evaluations per step.</p>
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public class HighamHall54FieldIntegrator<T extends RealFieldElement<T>>
+ extends EmbeddedRungeKuttaFieldIntegrator<T> {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Higham-Hall 5(4)";
+
+ /** Error weights Butcher array. */
+ private final T[] e ;
+
+ /** Simple constructor.
+ * Build a fifth order Higham and Hall integrator with the given step bounds
+ * @param field field to which the time and state vector elements belong
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ public HighamHall54FieldIntegrator(final Field<T> field,
+ final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+ super(field, METHOD_NAME, -1,
+ minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+ e = MathArrays.buildArray(field, 7);
+ e[0] = fraction(-1, 20);
+ e[1] = field.getZero();
+ e[2] = fraction(81, 160);
+ e[3] = fraction(-6, 5);
+ e[4] = fraction(25, 32);
+ e[5] = fraction( 1, 16);
+ e[6] = fraction(-1, 10);
+ }
+
+ /** Simple constructor.
+ * Build a fifth order Higham and Hall integrator with the given step bounds
+ * @param field field to which the time and state vector elements belong
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ public HighamHall54FieldIntegrator(final Field<T> field,
+ final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+ super(field, METHOD_NAME, -1,
+ minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+ e = MathArrays.buildArray(field, 7);
+ e[0] = fraction(-1, 20);
+ e[1] = field.getZero();
+ e[2] = fraction(81, 160);
+ e[3] = fraction(-6, 5);
+ e[4] = fraction(25, 32);
+ e[5] = fraction( 1, 16);
+ e[6] = fraction(-1, 10);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getC() {
+ final T[] c = MathArrays.buildArray(getField(), 6);
+ c[0] = fraction(2, 9);
+ c[1] = fraction(1, 3);
+ c[2] = fraction(1, 2);
+ c[3] = fraction(3, 5);
+ c[4] = getField().getOne();
+ c[5] = getField().getOne();
+ return c;
+ }
+
+ /** {@inheritDoc} */
+ public T[][] getA() {
+ final T[][] a = MathArrays.buildArray(getField(), 6, -1);
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = MathArrays.buildArray(getField(), i + 1);
+ }
+ a[0][0] = fraction( 2, 9);
+ a[1][0] = fraction( 1, 12);
+ a[1][1] = fraction( 1, 4);
+ a[2][0] = fraction( 1, 8);
+ a[2][1] = getField().getZero();
+ a[2][2] = fraction( 3, 8);
+ a[3][0] = fraction( 91, 500);
+ a[3][1] = fraction( -27, 100);
+ a[3][2] = fraction( 78, 125);
+ a[3][3] = fraction( 8, 125);
+ a[4][0] = fraction( -11, 20);
+ a[4][1] = fraction( 27, 20);
+ a[4][2] = fraction( 12, 5);
+ a[4][3] = fraction( -36, 5);
+ a[4][4] = fraction( 5, 1);
+ a[5][0] = fraction( 1, 12);
+ a[5][1] = getField().getZero();
+ a[5][2] = fraction( 27, 32);
+ a[5][3] = fraction( -4, 3);
+ a[5][4] = fraction( 125, 96);
+ a[5][5] = fraction( 5, 48);
+ return a;
+ }
+
+ /** {@inheritDoc} */
+ public T[] getB() {
+ final T[] b = MathArrays.buildArray(getField(), 7);
+ b[0] = fraction( 1, 12);
+ b[1] = getField().getZero();
+ b[2] = fraction( 27, 32);
+ b[3] = fraction( -4, 3);
+ b[4] = fraction(125, 96);
+ b[5] = fraction( 5, 48);
+ b[6] = getField().getZero();
+ return b;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected HighamHall54FieldStepInterpolator<T>
+ createInterpolator(final boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState, final FieldEquationsMapper<T> mapper) {
+ return new HighamHall54FieldStepInterpolator<T>(getField(), forward, yDotK,
+ globalPreviousState, globalCurrentState,
+ globalPreviousState, globalCurrentState,
+ mapper);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getOrder() {
+ return 5;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected T estimateError(final T[][] yDotK, final T[] y0, final T[] y1, final T h) {
+
+ T error = getField().getZero();
+
+ for (int j = 0; j < mainSetDimension; ++j) {
+ T errSum = yDotK[0][j].multiply(e[0]);
+ for (int l = 1; l < e.length; ++l) {
+ errSum = errSum.add(yDotK[l][j].multiply(e[l]));
+ }
+
+ final T yScale = MathUtils.max(y0[j].abs(), y1[j].abs());
+ final T tol = (vecAbsoluteTolerance == null) ?
+ yScale.multiply(scalRelativeTolerance).add(scalAbsoluteTolerance) :
+ yScale.multiply(vecRelativeTolerance[j]).add(vecAbsoluteTolerance[j]);
+ final T ratio = h.multiply(errSum).divide(tol);
+ error = error.add(ratio.multiply(ratio));
+
+ }
+
+ return error.divide(mainSetDimension).sqrt();
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54FieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54FieldStepInterpolator.java
new file mode 100644
index 0000000..10240fd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54FieldStepInterpolator.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/**
+ * This class represents an interpolator over the last step during an
+ * ODE integration for the 5(4) Higham and Hall integrator.
+ *
+ * @see HighamHall54FieldIntegrator
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+class HighamHall54FieldStepInterpolator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldStepInterpolator<T> {
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param mapper equations mapper for the all equations
+ */
+ HighamHall54FieldStepInterpolator(final Field<T> field, final boolean forward,
+ final T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ super(field, forward, yDotK,
+ globalPreviousState, globalCurrentState, softPreviousState, softCurrentState,
+ mapper);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected HighamHall54FieldStepInterpolator<T> create(final Field<T> newField, final boolean newForward, final T[][] newYDotK,
+ final FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ final FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ final FieldODEStateAndDerivative<T> newSoftPreviousState,
+ final FieldODEStateAndDerivative<T> newSoftCurrentState,
+ final FieldEquationsMapper<T> newMapper) {
+ return new HighamHall54FieldStepInterpolator<T>(newField, newForward, newYDotK,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(final FieldEquationsMapper<T> mapper,
+ final T time, final T theta,
+ final T thetaH, final T oneMinusThetaH) {
+
+ final T bDot0 = theta.multiply(theta.multiply(theta.multiply( -10.0 ).add( 16.0 )).add(-15.0 / 2.0)).add(1);
+ final T bDot1 = time.getField().getZero();
+ final T bDot2 = theta.multiply(theta.multiply(theta.multiply( 135.0 / 2.0).add(-729.0 / 8.0)).add(459.0 / 16.0));
+ final T bDot3 = theta.multiply(theta.multiply(theta.multiply(-120.0 ).add( 152.0 )).add(-44.0 ));
+ final T bDot4 = theta.multiply(theta.multiply(theta.multiply( 125.0 / 2.0).add(-625.0 / 8.0)).add(375.0 / 16.0));
+ final T bDot5 = theta.multiply( 5.0 / 8.0).multiply(theta.multiply(2).subtract(1));
+ final T[] interpolatedState;
+ final T[] interpolatedDerivatives;
+
+ if (getGlobalPreviousState() != null && theta.getReal() <= 0.5) {
+ final T b0 = thetaH.multiply(theta.multiply(theta.multiply(theta.multiply( -5.0 / 2.0).add( 16.0 / 3.0)).add(-15.0 / 4.0)).add(1));
+ final T b1 = time.getField().getZero();
+ final T b2 = thetaH.multiply(theta.multiply(theta.multiply(theta.multiply(135.0 / 8.0).add(-243.0 / 8.0)).add(459.0 / 32.0)));
+ final T b3 = thetaH.multiply(theta.multiply(theta.multiply(theta.multiply(-30.0 ).add( 152.0 / 3.0)).add(-22.0 )));
+ final T b4 = thetaH.multiply(theta.multiply(theta.multiply(theta.multiply(125.0 / 8.0).add(-625.0 / 24.0)).add(375.0 / 32.0)));
+ final T b5 = thetaH.multiply(theta.multiply(theta.multiply( 5.0 / 12.0 ).add( -5.0 / 16.0)));
+ interpolatedState = previousStateLinearCombination(b0, b1, b2, b3, b4, b5);
+ interpolatedDerivatives = derivativeLinearCombination(bDot0, bDot1, bDot2, bDot3, bDot4, bDot5);
+ } else {
+ final T theta2 = theta.multiply(theta);
+ final T h = thetaH.divide(theta);
+ final T b0 = h.multiply( theta.multiply(theta.multiply(theta.multiply(theta.multiply(-5.0 / 2.0).add( 16.0 / 3.0)).add( -15.0 / 4.0)).add( 1.0 )).add( -1.0 / 12.0));
+ final T b1 = time.getField().getZero();
+ final T b2 = h.multiply(theta2.multiply(theta.multiply(theta.multiply( 135.0 / 8.0 ).add(-243.0 / 8.0)).add(459.0 / 32.0)).add( -27.0 / 32.0));
+ final T b3 = h.multiply(theta2.multiply(theta.multiply(theta.multiply( -30.0 ).add( 152.0 / 3.0)).add(-22.0 )).add( 4.0 / 3.0));
+ final T b4 = h.multiply(theta2.multiply(theta.multiply(theta.multiply( 125.0 / 8.0 ).add(-625.0 / 24.0)).add(375.0 / 32.0)).add(-125.0 / 96.0));
+ final T b5 = h.multiply(theta2.multiply(theta.multiply( 5.0 / 12.0 ).add(-5.0 / 16.0)).add( -5.0 / 48.0));
+ interpolatedState = currentStateLinearCombination(b0, b1, b2, b3, b4, b5);
+ interpolatedDerivatives = derivativeLinearCombination(bDot0, bDot1, bDot2, bDot3, bDot4, bDot5);
+ }
+
+ return new FieldODEStateAndDerivative<T>(time, interpolatedState, interpolatedDerivatives);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54Integrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54Integrator.java
new file mode 100644
index 0000000..c48c4f9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54Integrator.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.util.FastMath;
+
+
+/**
+ * This class implements the 5(4) Higham and Hall integrator for
+ * Ordinary Differential Equations.
+ *
+ * <p>This integrator is an embedded Runge-Kutta integrator
+ * of order 5(4) used in local extrapolation mode (i.e. the solution
+ * is computed using the high order formula) with stepsize control
+ * (and automatic step initialization) and continuous output. This
+ * method uses 7 functions evaluations per step.</p>
+ *
+ * @since 1.2
+ */
+
+public class HighamHall54Integrator extends EmbeddedRungeKuttaIntegrator {
+
+ /** Integrator method name. */
+ private static final String METHOD_NAME = "Higham-Hall 5(4)";
+
+ /** Time steps Butcher array. */
+ private static final double[] STATIC_C = {
+ 2.0/9.0, 1.0/3.0, 1.0/2.0, 3.0/5.0, 1.0, 1.0
+ };
+
+ /** Internal weights Butcher array. */
+ private static final double[][] STATIC_A = {
+ {2.0/9.0},
+ {1.0/12.0, 1.0/4.0},
+ {1.0/8.0, 0.0, 3.0/8.0},
+ {91.0/500.0, -27.0/100.0, 78.0/125.0, 8.0/125.0},
+ {-11.0/20.0, 27.0/20.0, 12.0/5.0, -36.0/5.0, 5.0},
+ {1.0/12.0, 0.0, 27.0/32.0, -4.0/3.0, 125.0/96.0, 5.0/48.0}
+ };
+
+ /** Propagation weights Butcher array. */
+ private static final double[] STATIC_B = {
+ 1.0/12.0, 0.0, 27.0/32.0, -4.0/3.0, 125.0/96.0, 5.0/48.0, 0.0
+ };
+
+ /** Error weights Butcher array. */
+ private static final double[] STATIC_E = {
+ -1.0/20.0, 0.0, 81.0/160.0, -6.0/5.0, 25.0/32.0, 1.0/16.0, -1.0/10.0
+ };
+
+ /** Simple constructor.
+ * Build a fifth order Higham and Hall integrator with the given step bounds
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param scalAbsoluteTolerance allowed absolute error
+ * @param scalRelativeTolerance allowed relative error
+ */
+ public HighamHall54Integrator(final double minStep, final double maxStep,
+ final double scalAbsoluteTolerance,
+ final double scalRelativeTolerance) {
+ super(METHOD_NAME, false, STATIC_C, STATIC_A, STATIC_B, new HighamHall54StepInterpolator(),
+ minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
+ }
+
+ /** Simple constructor.
+ * Build a fifth order Higham and Hall integrator with the given step bounds
+ * @param minStep minimal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param maxStep maximal step (sign is irrelevant, regardless of
+ * integration direction, forward or backward), the last step can
+ * be smaller than this
+ * @param vecAbsoluteTolerance allowed absolute error
+ * @param vecRelativeTolerance allowed relative error
+ */
+ public HighamHall54Integrator(final double minStep, final double maxStep,
+ final double[] vecAbsoluteTolerance,
+ final double[] vecRelativeTolerance) {
+ super(METHOD_NAME, false, STATIC_C, STATIC_A, STATIC_B, new HighamHall54StepInterpolator(),
+ minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int getOrder() {
+ return 5;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected double estimateError(final double[][] yDotK,
+ final double[] y0, final double[] y1,
+ final double h) {
+
+ double error = 0;
+
+ for (int j = 0; j < mainSetDimension; ++j) {
+ double errSum = STATIC_E[0] * yDotK[0][j];
+ for (int l = 1; l < STATIC_E.length; ++l) {
+ errSum += STATIC_E[l] * yDotK[l][j];
+ }
+
+ final double yScale = FastMath.max(FastMath.abs(y0[j]), FastMath.abs(y1[j]));
+ final double tol = (vecAbsoluteTolerance == null) ?
+ (scalAbsoluteTolerance + scalRelativeTolerance * yScale) :
+ (vecAbsoluteTolerance[j] + vecRelativeTolerance[j] * yScale);
+ final double ratio = h * errSum / tol;
+ error += ratio * ratio;
+
+ }
+
+ return FastMath.sqrt(error / mainSetDimension);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54StepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54StepInterpolator.java
new file mode 100644
index 0000000..682ec7c
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/HighamHall54StepInterpolator.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+
+/**
+ * This class represents an interpolator over the last step during an
+ * ODE integration for the 5(4) Higham and Hall integrator.
+ *
+ * @see HighamHall54Integrator
+ *
+ * @since 1.2
+ */
+
+class HighamHall54StepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 20111120L;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link
+ * org.apache.commons.math3.ode.sampling.AbstractStepInterpolator#reinitialize}
+ * method should be called before using the instance in order to
+ * initialize the internal arrays. This constructor is used only
+ * in order to delay the initialization in some cases. The {@link
+ * EmbeddedRungeKuttaIntegrator} uses the prototyping design pattern
+ * to create the step interpolators by cloning an uninitialized model
+ * and later initializing the copy.
+ */
+ // CHECKSTYLE: stop RedundantModifier
+ // the public modifier here is needed for serialization
+ public HighamHall54StepInterpolator() {
+ super();
+ }
+ // CHECKSTYLE: resume RedundantModifier
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ HighamHall54StepInterpolator(final HighamHall54StepInterpolator interpolator) {
+ super(interpolator);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new HighamHall54StepInterpolator(this);
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta,
+ final double oneMinusThetaH) {
+
+ final double bDot0 = 1 + theta * (-15.0/2.0 + theta * (16.0 - 10.0 * theta));
+ final double bDot2 = theta * (459.0/16.0 + theta * (-729.0/8.0 + 135.0/2.0 * theta));
+ final double bDot3 = theta * (-44.0 + theta * (152.0 - 120.0 * theta));
+ final double bDot4 = theta * (375.0/16.0 + theta * (-625.0/8.0 + 125.0/2.0 * theta));
+ final double bDot5 = theta * 5.0/8.0 * (2 * theta - 1);
+
+ if ((previousState != null) && (theta <= 0.5)) {
+ final double hTheta = h * theta;
+ final double b0 = hTheta * (1.0 + theta * (-15.0/4.0 + theta * (16.0/3.0 - 5.0/2.0 * theta)));
+ final double b2 = hTheta * ( theta * (459.0/32.0 + theta * (-243.0/8.0 + theta * 135.0/8.0)));
+ final double b3 = hTheta * ( theta * (-22.0 + theta * (152.0/3.0 + theta * -30.0)));
+ final double b4 = hTheta * ( theta * (375.0/32.0 + theta * (-625.0/24.0 + theta * 125.0/8.0)));
+ final double b5 = hTheta * ( theta * (-5.0/16.0 + theta * 5.0/12.0));
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot0 = yDotK[0][i];
+ final double yDot2 = yDotK[2][i];
+ final double yDot3 = yDotK[3][i];
+ final double yDot4 = yDotK[4][i];
+ final double yDot5 = yDotK[5][i];
+ interpolatedState[i] =
+ previousState[i] + b0 * yDot0 + b2 * yDot2 + b3 * yDot3 + b4 * yDot4 + b5 * yDot5;
+ interpolatedDerivatives[i] =
+ bDot0 * yDot0 + bDot2 * yDot2 + bDot3 * yDot3 + bDot4 * yDot4 + bDot5 * yDot5;
+ }
+ } else {
+ final double theta2 = theta * theta;
+ final double b0 = h * (-1.0/12.0 + theta * (1.0 + theta * (-15.0/4.0 + theta * (16.0/3.0 + theta * -5.0/2.0))));
+ final double b2 = h * (-27.0/32.0 + theta2 * (459.0/32.0 + theta * (-243.0/8.0 + theta * 135.0/8.0)));
+ final double b3 = h * (4.0/3.0 + theta2 * (-22.0 + theta * (152.0/3.0 + theta * -30.0)));
+ final double b4 = h * (-125.0/96.0 + theta2 * (375.0/32.0 + theta * (-625.0/24.0 + theta * 125.0/8.0)));
+ final double b5 = h * (-5.0/48.0 + theta2 * (-5.0/16.0 + theta * 5.0/12.0));
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot0 = yDotK[0][i];
+ final double yDot2 = yDotK[2][i];
+ final double yDot3 = yDotK[3][i];
+ final double yDot4 = yDotK[4][i];
+ final double yDot5 = yDotK[5][i];
+ interpolatedState[i] =
+ currentState[i] + b0 * yDot0 + b2 * yDot2 + b3 * yDot3 + b4 * yDot4 + b5 * yDot5;
+ interpolatedDerivatives[i] =
+ bDot0 * yDot0 + bDot2 * yDot2 + bDot3 * yDot3 + bDot4 * yDot4 + bDot5 * yDot5;
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherFieldIntegrator.java
new file mode 100644
index 0000000..7b57b5a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherFieldIntegrator.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+
+
+/**
+ * This class implements the Luther sixth order Runge-Kutta
+ * integrator for Ordinary Differential Equations.
+
+ * <p>
+ * This method is described in H. A. Luther 1968 paper <a
+ * href="http://www.ams.org/journals/mcom/1968-22-102/S0025-5718-68-99876-1/S0025-5718-68-99876-1.pdf">
+ * An explicit Sixth-Order Runge-Kutta Formula</a>.
+ * </p>
+
+ * <p>This method is an explicit Runge-Kutta method, its Butcher-array
+ * is the following one :
+ * <pre>
+ * 0 | 0 0 0 0 0 0
+ * 1 | 1 0 0 0 0 0
+ * 1/2 | 3/8 1/8 0 0 0 0
+ * 2/3 | 8/27 2/27 8/27 0 0 0
+ * (7-q)/14 | ( -21 + 9q)/392 ( -56 + 8q)/392 ( 336 - 48q)/392 ( -63 + 3q)/392 0 0
+ * (7+q)/14 | (-1155 - 255q)/1960 ( -280 - 40q)/1960 ( 0 - 320q)/1960 ( 63 + 363q)/1960 ( 2352 + 392q)/1960 0
+ * 1 | ( 330 + 105q)/180 ( 120 + 0q)/180 ( -200 + 280q)/180 ( 126 - 189q)/180 ( -686 - 126q)/180 ( 490 - 70q)/180
+ * |--------------------------------------------------------------------------------------------------------------------------------------------------
+ * | 1/20 0 16/45 0 49/180 49/180 1/20
+ * </pre>
+ * where q = &radic;21</p>
+ *
+ * @see EulerFieldIntegrator
+ * @see ClassicalRungeKuttaFieldIntegrator
+ * @see GillFieldIntegrator
+ * @see MidpointFieldIntegrator
+ * @see ThreeEighthesFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public class LutherFieldIntegrator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldIntegrator<T> {
+
+ /** Simple constructor.
+ * Build a fourth-order Luther integrator with the given step.
+ * @param field field to which the time and state vector elements belong
+ * @param step integration step
+ */
+ public LutherFieldIntegrator(final Field<T> field, final T step) {
+ super(field, "Luther", step);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getC() {
+ final T q = getField().getZero().add(21).sqrt();
+ final T[] c = MathArrays.buildArray(getField(), 6);
+ c[0] = getField().getOne();
+ c[1] = fraction(1, 2);
+ c[2] = fraction(2, 3);
+ c[3] = q.subtract(7).divide(-14);
+ c[4] = q.add(7).divide(14);
+ c[5] = getField().getOne();
+ return c;
+ }
+
+ /** {@inheritDoc} */
+ public T[][] getA() {
+ final T q = getField().getZero().add(21).sqrt();
+ final T[][] a = MathArrays.buildArray(getField(), 6, -1);
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = MathArrays.buildArray(getField(), i + 1);
+ }
+ a[0][0] = getField().getOne();
+ a[1][0] = fraction(3, 8);
+ a[1][1] = fraction(1, 8);
+ a[2][0] = fraction(8, 27);
+ a[2][1] = fraction(2, 27);
+ a[2][2] = a[2][0];
+ a[3][0] = q.multiply( 9).add( -21).divide( 392);
+ a[3][1] = q.multiply( 8).add( -56).divide( 392);
+ a[3][2] = q.multiply( -48).add( 336).divide( 392);
+ a[3][3] = q.multiply( 3).add( -63).divide( 392);
+ a[4][0] = q.multiply(-255).add(-1155).divide(1960);
+ a[4][1] = q.multiply( -40).add( -280).divide(1960);
+ a[4][2] = q.multiply(-320) .divide(1960);
+ a[4][3] = q.multiply( 363).add( 63).divide(1960);
+ a[4][4] = q.multiply( 392).add( 2352).divide(1960);
+ a[5][0] = q.multiply( 105).add( 330).divide( 180);
+ a[5][1] = fraction(2, 3);
+ a[5][2] = q.multiply( 280).add( -200).divide( 180);
+ a[5][3] = q.multiply(-189).add( 126).divide( 180);
+ a[5][4] = q.multiply(-126).add( -686).divide( 180);
+ a[5][5] = q.multiply( -70).add( 490).divide( 180);
+ return a;
+ }
+
+ /** {@inheritDoc} */
+ public T[] getB() {
+
+ final T[] b = MathArrays.buildArray(getField(), 7);
+ b[0] = fraction( 1, 20);
+ b[1] = getField().getZero();
+ b[2] = fraction(16, 45);
+ b[3] = getField().getZero();
+ b[4] = fraction(49, 180);
+ b[5] = b[4];
+ b[6] = b[0];
+
+ return b;
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected LutherFieldStepInterpolator<T>
+ createInterpolator(final boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ return new LutherFieldStepInterpolator<T>(getField(), forward, yDotK,
+ globalPreviousState, globalCurrentState,
+ globalPreviousState, globalCurrentState,
+ mapper);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherFieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherFieldStepInterpolator.java
new file mode 100644
index 0000000..9e38a96
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherFieldStepInterpolator.java
@@ -0,0 +1,224 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/**
+ * This class represents an interpolator over the last step during an
+ * ODE integration for the 6th order Luther integrator.
+ *
+ * <p>This interpolator computes dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme.</p>
+ *
+ * @see LutherFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+class LutherFieldStepInterpolator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldStepInterpolator<T> {
+
+ /** -49 - 49 q. */
+ private final T c5a;
+
+ /** 392 + 287 q. */
+ private final T c5b;
+
+ /** -637 - 357 q. */
+ private final T c5c;
+
+ /** 833 + 343 q. */
+ private final T c5d;
+
+ /** -49 + 49 q. */
+ private final T c6a;
+
+ /** -392 - 287 q. */
+ private final T c6b;
+
+ /** -637 + 357 q. */
+ private final T c6c;
+
+ /** 833 - 343 q. */
+ private final T c6d;
+
+ /** 49 + 49 q. */
+ private final T d5a;
+
+ /** -1372 - 847 q. */
+ private final T d5b;
+
+ /** 2254 + 1029 q */
+ private final T d5c;
+
+ /** 49 - 49 q. */
+ private final T d6a;
+
+ /** -1372 + 847 q. */
+ private final T d6b;
+
+ /** 2254 - 1029 q */
+ private final T d6c;
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param mapper equations mapper for the all equations
+ */
+ LutherFieldStepInterpolator(final Field<T> field, final boolean forward,
+ final T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ super(field, forward, yDotK,
+ globalPreviousState, globalCurrentState, softPreviousState, softCurrentState,
+ mapper);
+ final T q = field.getZero().add(21).sqrt();
+ c5a = q.multiply( -49).add( -49);
+ c5b = q.multiply( 287).add( 392);
+ c5c = q.multiply( -357).add( -637);
+ c5d = q.multiply( 343).add( 833);
+ c6a = q.multiply( 49).add( -49);
+ c6b = q.multiply( -287).add( 392);
+ c6c = q.multiply( 357).add( -637);
+ c6d = q.multiply( -343).add( 833);
+ d5a = q.multiply( 49).add( 49);
+ d5b = q.multiply( -847).add(-1372);
+ d5c = q.multiply( 1029).add( 2254);
+ d6a = q.multiply( -49).add( 49);
+ d6b = q.multiply( 847).add(-1372);
+ d6c = q.multiply(-1029).add( 2254);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected LutherFieldStepInterpolator<T> create(final Field<T> newField, final boolean newForward, final T[][] newYDotK,
+ final FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ final FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ final FieldODEStateAndDerivative<T> newSoftPreviousState,
+ final FieldODEStateAndDerivative<T> newSoftCurrentState,
+ final FieldEquationsMapper<T> newMapper) {
+ return new LutherFieldStepInterpolator<T>(newField, newForward, newYDotK,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(final FieldEquationsMapper<T> mapper,
+ final T time, final T theta,
+ final T thetaH, final T oneMinusThetaH) {
+
+ // the coefficients below have been computed by solving the
+ // order conditions from a theorem from Butcher (1963), using
+ // the method explained in Folkmar Bornemann paper "Runge-Kutta
+ // Methods, Trees, and Maple", Center of Mathematical Sciences, Munich
+ // University of Technology, February 9, 2001
+ //<http://wwwzenger.informatik.tu-muenchen.de/selcuk/sjam012101.html>
+
+ // the method is implemented in the rkcheck tool
+ // <https://www.spaceroots.org/software/rkcheck/index.html>.
+ // Running it for order 5 gives the following order conditions
+ // for an interpolator:
+ // order 1 conditions
+ // \sum_{i=1}^{i=s}\left(b_{i} \right) =1
+ // order 2 conditions
+ // \sum_{i=1}^{i=s}\left(b_{i} c_{i}\right) = \frac{\theta}{2}
+ // order 3 conditions
+ // \sum_{i=2}^{i=s}\left(b_{i} \sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j} \right)}\right) = \frac{\theta^{2}}{6}
+ // \sum_{i=1}^{i=s}\left(b_{i} c_{i}^{2}\right) = \frac{\theta^{2}}{3}
+ // order 4 conditions
+ // \sum_{i=3}^{i=s}\left(b_{i} \sum_{j=2}^{j=i-1}{\left(a_{i,j} \sum_{k=1}^{k=j-1}{\left(a_{j,k} c_{k} \right)} \right)}\right) = \frac{\theta^{3}}{24}
+ // \sum_{i=2}^{i=s}\left(b_{i} \sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j}^{2} \right)}\right) = \frac{\theta^{3}}{12}
+ // \sum_{i=2}^{i=s}\left(b_{i} c_{i}\sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j} \right)}\right) = \frac{\theta^{3}}{8}
+ // \sum_{i=1}^{i=s}\left(b_{i} c_{i}^{3}\right) = \frac{\theta^{3}}{4}
+ // order 5 conditions
+ // \sum_{i=4}^{i=s}\left(b_{i} \sum_{j=3}^{j=i-1}{\left(a_{i,j} \sum_{k=2}^{k=j-1}{\left(a_{j,k} \sum_{l=1}^{l=k-1}{\left(a_{k,l} c_{l} \right)} \right)} \right)}\right) = \frac{\theta^{4}}{120}
+ // \sum_{i=3}^{i=s}\left(b_{i} \sum_{j=2}^{j=i-1}{\left(a_{i,j} \sum_{k=1}^{k=j-1}{\left(a_{j,k} c_{k}^{2} \right)} \right)}\right) = \frac{\theta^{4}}{60}
+ // \sum_{i=3}^{i=s}\left(b_{i} \sum_{j=2}^{j=i-1}{\left(a_{i,j} c_{j}\sum_{k=1}^{k=j-1}{\left(a_{j,k} c_{k} \right)} \right)}\right) = \frac{\theta^{4}}{40}
+ // \sum_{i=2}^{i=s}\left(b_{i} \sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j}^{3} \right)}\right) = \frac{\theta^{4}}{20}
+ // \sum_{i=3}^{i=s}\left(b_{i} c_{i}\sum_{j=2}^{j=i-1}{\left(a_{i,j} \sum_{k=1}^{k=j-1}{\left(a_{j,k} c_{k} \right)} \right)}\right) = \frac{\theta^{4}}{30}
+ // \sum_{i=2}^{i=s}\left(b_{i} c_{i}\sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j}^{2} \right)}\right) = \frac{\theta^{4}}{15}
+ // \sum_{i=2}^{i=s}\left(b_{i} \left(\sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j} \right)} \right)^{2}\right) = \frac{\theta^{4}}{20}
+ // \sum_{i=2}^{i=s}\left(b_{i} c_{i}^{2}\sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j} \right)}\right) = \frac{\theta^{4}}{10}
+ // \sum_{i=1}^{i=s}\left(b_{i} c_{i}^{4}\right) = \frac{\theta^{4}}{5}
+
+ // The a_{j,k} and c_{k} are given by the integrator Butcher arrays. What remains to solve
+ // are the b_i for the interpolator. They are found by solving the above equations.
+ // For a given interpolator, some equations are redundant, so in our case when we select
+ // all equations from order 1 to 4, we still don't have enough independent equations
+ // to solve from b_1 to b_7. We need to also select one equation from order 5. Here,
+ // we selected the last equation. It appears this choice implied at least the last 3 equations
+ // are fulfilled, but some of the former ones are not, so the resulting interpolator is order 5.
+ // At the end, we get the b_i as polynomials in theta.
+
+ final T coeffDot1 = theta.multiply(theta.multiply(theta.multiply(theta.multiply( 21 ).add( -47 )).add( 36 )).add( -54 / 5.0)).add(1);
+ final T coeffDot2 = time.getField().getZero();
+ final T coeffDot3 = theta.multiply(theta.multiply(theta.multiply(theta.multiply( 112 ).add(-608 / 3.0)).add( 320 / 3.0 )).add(-208 / 15.0));
+ final T coeffDot4 = theta.multiply(theta.multiply(theta.multiply(theta.multiply( -567 / 5.0).add( 972 / 5.0)).add( -486 / 5.0 )).add( 324 / 25.0));
+ final T coeffDot5 = theta.multiply(theta.multiply(theta.multiply(theta.multiply(c5a.divide(5)).add(c5b.divide(15))).add(c5c.divide(30))).add(c5d.divide(150)));
+ final T coeffDot6 = theta.multiply(theta.multiply(theta.multiply(theta.multiply(c6a.divide(5)).add(c6b.divide(15))).add(c6c.divide(30))).add(c6d.divide(150)));
+ final T coeffDot7 = theta.multiply(theta.multiply(theta.multiply( 3.0 ).add( -3 )).add( 3 / 5.0));
+ final T[] interpolatedState;
+ final T[] interpolatedDerivatives;
+
+ if (getGlobalPreviousState() != null && theta.getReal() <= 0.5) {
+
+ final T s = thetaH;
+ final T coeff1 = s.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply( 21 / 5.0).add( -47 / 4.0)).add( 12 )).add( -27 / 5.0)).add(1));
+ final T coeff2 = time.getField().getZero();
+ final T coeff3 = s.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply( 112 / 5.0).add(-152 / 3.0)).add( 320 / 9.0 )).add(-104 / 15.0)));
+ final T coeff4 = s.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply(-567 / 25.0).add( 243 / 5.0)).add( -162 / 5.0 )).add( 162 / 25.0)));
+ final T coeff5 = s.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply(c5a.divide(25)).add(c5b.divide(60))).add(c5c.divide(90))).add(c5d.divide(300))));
+ final T coeff6 = s.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply(c6a.divide(25)).add(c6b.divide(60))).add(c6c.divide(90))).add(c6d.divide(300))));
+ final T coeff7 = s.multiply(theta.multiply(theta.multiply(theta.multiply( 3 / 4.0 ).add( -1 )).add( 3 / 10.0)));
+ interpolatedState = previousStateLinearCombination(coeff1, coeff2, coeff3, coeff4, coeff5, coeff6, coeff7);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot1, coeffDot2, coeffDot3, coeffDot4, coeffDot5, coeffDot6, coeffDot7);
+ } else {
+
+ final T s = oneMinusThetaH;
+ final T coeff1 = s.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply( -21 / 5.0).add( 151 / 20.0)).add( -89 / 20.0)).add( 19 / 20.0)).add(- 1 / 20.0));
+ final T coeff2 = time.getField().getZero();
+ final T coeff3 = s.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply(-112 / 5.0).add( 424 / 15.0)).add( -328 / 45.0)).add( -16 / 45.0)).add(-16 / 45.0));
+ final T coeff4 = s.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply( 567 / 25.0).add( -648 / 25.0)).add( 162 / 25.0))));
+ final T coeff5 = s.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply(d5a.divide(25)).add(d5b.divide(300))).add(d5c.divide(900))).add( -49 / 180.0)).add(-49 / 180.0));
+ final T coeff6 = s.multiply(theta.multiply(theta.multiply(theta.multiply(theta.multiply(d6a.divide(25)).add(d6b.divide(300))).add(d6c.divide(900))).add( -49 / 180.0)).add(-49 / 180.0));
+ final T coeff7 = s.multiply( theta.multiply(theta.multiply(theta.multiply( -3 / 4.0 ).add( 1 / 4.0)).add( -1 / 20.0)).add( -1 / 20.0));
+ interpolatedState = currentStateLinearCombination(coeff1, coeff2, coeff3, coeff4, coeff5, coeff6, coeff7);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot1, coeffDot2, coeffDot3, coeffDot4, coeffDot5, coeffDot6, coeffDot7);
+ }
+
+ return new FieldODEStateAndDerivative<T>(time, interpolatedState, interpolatedDerivatives);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherIntegrator.java
new file mode 100644
index 0000000..d3d0d6a
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherIntegrator.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.util.FastMath;
+
+
+/**
+ * This class implements the Luther sixth order Runge-Kutta
+ * integrator for Ordinary Differential Equations.
+
+ * <p>
+ * This method is described in H. A. Luther 1968 paper <a
+ * href="http://www.ams.org/journals/mcom/1968-22-102/S0025-5718-68-99876-1/S0025-5718-68-99876-1.pdf">
+ * An explicit Sixth-Order Runge-Kutta Formula</a>.
+ * </p>
+
+ * <p>This method is an explicit Runge-Kutta method, its Butcher-array
+ * is the following one :
+ * <pre>
+ * 0 | 0 0 0 0 0 0
+ * 1 | 1 0 0 0 0 0
+ * 1/2 | 3/8 1/8 0 0 0 0
+ * 2/3 | 8/27 2/27 8/27 0 0 0
+ * (7-q)/14 | ( -21 + 9q)/392 ( -56 + 8q)/392 ( 336 - 48q)/392 ( -63 + 3q)/392 0 0
+ * (7+q)/14 | (-1155 - 255q)/1960 ( -280 - 40q)/1960 ( 0 - 320q)/1960 ( 63 + 363q)/1960 ( 2352 + 392q)/1960 0
+ * 1 | ( 330 + 105q)/180 ( 120 + 0q)/180 ( -200 + 280q)/180 ( 126 - 189q)/180 ( -686 - 126q)/180 ( 490 - 70q)/180
+ * |--------------------------------------------------------------------------------------------------------------------------------------------------
+ * | 1/20 0 16/45 0 49/180 49/180 1/20
+ * </pre>
+ * where q = &radic;21</p>
+ *
+ * @see EulerIntegrator
+ * @see ClassicalRungeKuttaIntegrator
+ * @see GillIntegrator
+ * @see MidpointIntegrator
+ * @see ThreeEighthesIntegrator
+ * @since 3.3
+ */
+
+public class LutherIntegrator extends RungeKuttaIntegrator {
+
+ /** Square root. */
+ private static final double Q = FastMath.sqrt(21);
+
+ /** Time steps Butcher array. */
+ private static final double[] STATIC_C = {
+ 1.0, 1.0 / 2.0, 2.0 / 3.0, (7.0 - Q) / 14.0, (7.0 + Q) / 14.0, 1.0
+ };
+
+ /** Internal weights Butcher array. */
+ private static final double[][] STATIC_A = {
+ { 1.0 },
+ { 3.0 / 8.0, 1.0 / 8.0 },
+ { 8.0 / 27.0, 2.0 / 27.0, 8.0 / 27.0 },
+ { ( -21.0 + 9.0 * Q) / 392.0, ( -56.0 + 8.0 * Q) / 392.0, ( 336.0 - 48.0 * Q) / 392.0, (-63.0 + 3.0 * Q) / 392.0 },
+ { (-1155.0 - 255.0 * Q) / 1960.0, (-280.0 - 40.0 * Q) / 1960.0, ( 0.0 - 320.0 * Q) / 1960.0, ( 63.0 + 363.0 * Q) / 1960.0, (2352.0 + 392.0 * Q) / 1960.0 },
+ { ( 330.0 + 105.0 * Q) / 180.0, ( 120.0 + 0.0 * Q) / 180.0, (-200.0 + 280.0 * Q) / 180.0, (126.0 - 189.0 * Q) / 180.0, (-686.0 - 126.0 * Q) / 180.0, (490.0 - 70.0 * Q) / 180.0 }
+ };
+
+ /** Propagation weights Butcher array. */
+ private static final double[] STATIC_B = {
+ 1.0 / 20.0, 0, 16.0 / 45.0, 0, 49.0 / 180.0, 49.0 / 180.0, 1.0 / 20.0
+ };
+
+ /** Simple constructor.
+ * Build a fourth-order Luther integrator with the given step.
+ * @param step integration step
+ */
+ public LutherIntegrator(final double step) {
+ super("Luther", STATIC_C, STATIC_A, STATIC_B, new LutherStepInterpolator(), step);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherStepInterpolator.java
new file mode 100644
index 0000000..207a9ea
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/LutherStepInterpolator.java
@@ -0,0 +1,182 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class represents an interpolator over the last step during an
+ * ODE integration for the 6th order Luther integrator.
+ *
+ * <p>This interpolator computes dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme.</p>
+ *
+ * @see LutherIntegrator
+ * @since 3.3
+ */
+
+class LutherStepInterpolator extends RungeKuttaStepInterpolator {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 20140416L;
+
+ /** Square root. */
+ private static final double Q = FastMath.sqrt(21);
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link
+ * org.apache.commons.math3.ode.sampling.AbstractStepInterpolator#reinitialize}
+ * method should be called before using the instance in order to
+ * initialize the internal arrays. This constructor is used only
+ * in order to delay the initialization in some cases. The {@link
+ * RungeKuttaIntegrator} class uses the prototyping design pattern
+ * to create the step interpolators by cloning an uninitialized model
+ * and later initializing the copy.
+ */
+ // CHECKSTYLE: stop RedundantModifier
+ // the public modifier here is needed for serialization
+ public LutherStepInterpolator() {
+ }
+ // CHECKSTYLE: resume RedundantModifier
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ LutherStepInterpolator(final LutherStepInterpolator interpolator) {
+ super(interpolator);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new LutherStepInterpolator(this);
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta,
+ final double oneMinusThetaH) {
+
+ // the coefficients below have been computed by solving the
+ // order conditions from a theorem from Butcher (1963), using
+ // the method explained in Folkmar Bornemann paper "Runge-Kutta
+ // Methods, Trees, and Maple", Center of Mathematical Sciences, Munich
+ // University of Technology, February 9, 2001
+ //<http://wwwzenger.informatik.tu-muenchen.de/selcuk/sjam012101.html>
+
+ // the method is implemented in the rkcheck tool
+ // <https://www.spaceroots.org/software/rkcheck/index.html>.
+ // Running it for order 5 gives the following order conditions
+ // for an interpolator:
+ // order 1 conditions
+ // \sum_{i=1}^{i=s}\left(b_{i} \right) =1
+ // order 2 conditions
+ // \sum_{i=1}^{i=s}\left(b_{i} c_{i}\right) = \frac{\theta}{2}
+ // order 3 conditions
+ // \sum_{i=2}^{i=s}\left(b_{i} \sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j} \right)}\right) = \frac{\theta^{2}}{6}
+ // \sum_{i=1}^{i=s}\left(b_{i} c_{i}^{2}\right) = \frac{\theta^{2}}{3}
+ // order 4 conditions
+ // \sum_{i=3}^{i=s}\left(b_{i} \sum_{j=2}^{j=i-1}{\left(a_{i,j} \sum_{k=1}^{k=j-1}{\left(a_{j,k} c_{k} \right)} \right)}\right) = \frac{\theta^{3}}{24}
+ // \sum_{i=2}^{i=s}\left(b_{i} \sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j}^{2} \right)}\right) = \frac{\theta^{3}}{12}
+ // \sum_{i=2}^{i=s}\left(b_{i} c_{i}\sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j} \right)}\right) = \frac{\theta^{3}}{8}
+ // \sum_{i=1}^{i=s}\left(b_{i} c_{i}^{3}\right) = \frac{\theta^{3}}{4}
+ // order 5 conditions
+ // \sum_{i=4}^{i=s}\left(b_{i} \sum_{j=3}^{j=i-1}{\left(a_{i,j} \sum_{k=2}^{k=j-1}{\left(a_{j,k} \sum_{l=1}^{l=k-1}{\left(a_{k,l} c_{l} \right)} \right)} \right)}\right) = \frac{\theta^{4}}{120}
+ // \sum_{i=3}^{i=s}\left(b_{i} \sum_{j=2}^{j=i-1}{\left(a_{i,j} \sum_{k=1}^{k=j-1}{\left(a_{j,k} c_{k}^{2} \right)} \right)}\right) = \frac{\theta^{4}}{60}
+ // \sum_{i=3}^{i=s}\left(b_{i} \sum_{j=2}^{j=i-1}{\left(a_{i,j} c_{j}\sum_{k=1}^{k=j-1}{\left(a_{j,k} c_{k} \right)} \right)}\right) = \frac{\theta^{4}}{40}
+ // \sum_{i=2}^{i=s}\left(b_{i} \sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j}^{3} \right)}\right) = \frac{\theta^{4}}{20}
+ // \sum_{i=3}^{i=s}\left(b_{i} c_{i}\sum_{j=2}^{j=i-1}{\left(a_{i,j} \sum_{k=1}^{k=j-1}{\left(a_{j,k} c_{k} \right)} \right)}\right) = \frac{\theta^{4}}{30}
+ // \sum_{i=2}^{i=s}\left(b_{i} c_{i}\sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j}^{2} \right)}\right) = \frac{\theta^{4}}{15}
+ // \sum_{i=2}^{i=s}\left(b_{i} \left(\sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j} \right)} \right)^{2}\right) = \frac{\theta^{4}}{20}
+ // \sum_{i=2}^{i=s}\left(b_{i} c_{i}^{2}\sum_{j=1}^{j=i-1}{\left(a_{i,j} c_{j} \right)}\right) = \frac{\theta^{4}}{10}
+ // \sum_{i=1}^{i=s}\left(b_{i} c_{i}^{4}\right) = \frac{\theta^{4}}{5}
+
+ // The a_{j,k} and c_{k} are given by the integrator Butcher arrays. What remains to solve
+ // are the b_i for the interpolator. They are found by solving the above equations.
+ // For a given interpolator, some equations are redundant, so in our case when we select
+ // all equations from order 1 to 4, we still don't have enough independent equations
+ // to solve from b_1 to b_7. We need to also select one equation from order 5. Here,
+ // we selected the last equation. It appears this choice implied at least the last 3 equations
+ // are fulfilled, but some of the former ones are not, so the resulting interpolator is order 5.
+ // At the end, we get the b_i as polynomials in theta.
+
+ final double coeffDot1 = 1 + theta * ( -54 / 5.0 + theta * ( 36 + theta * ( -47 + theta * 21)));
+ final double coeffDot2 = 0;
+ final double coeffDot3 = theta * (-208 / 15.0 + theta * ( 320 / 3.0 + theta * (-608 / 3.0 + theta * 112)));
+ final double coeffDot4 = theta * ( 324 / 25.0 + theta * ( -486 / 5.0 + theta * ( 972 / 5.0 + theta * -567 / 5.0)));
+ final double coeffDot5 = theta * ((833 + 343 * Q) / 150.0 + theta * ((-637 - 357 * Q) / 30.0 + theta * ((392 + 287 * Q) / 15.0 + theta * (-49 - 49 * Q) / 5.0)));
+ final double coeffDot6 = theta * ((833 - 343 * Q) / 150.0 + theta * ((-637 + 357 * Q) / 30.0 + theta * ((392 - 287 * Q) / 15.0 + theta * (-49 + 49 * Q) / 5.0)));
+ final double coeffDot7 = theta * ( 3 / 5.0 + theta * ( -3 + theta * 3));
+
+ if ((previousState != null) && (theta <= 0.5)) {
+
+ final double coeff1 = 1 + theta * ( -27 / 5.0 + theta * ( 12 + theta * ( -47 / 4.0 + theta * 21 / 5.0)));
+ final double coeff2 = 0;
+ final double coeff3 = theta * (-104 / 15.0 + theta * ( 320 / 9.0 + theta * (-152 / 3.0 + theta * 112 / 5.0)));
+ final double coeff4 = theta * ( 162 / 25.0 + theta * ( -162 / 5.0 + theta * ( 243 / 5.0 + theta * -567 / 25.0)));
+ final double coeff5 = theta * ((833 + 343 * Q) / 300.0 + theta * ((-637 - 357 * Q) / 90.0 + theta * ((392 + 287 * Q) / 60.0 + theta * (-49 - 49 * Q) / 25.0)));
+ final double coeff6 = theta * ((833 - 343 * Q) / 300.0 + theta * ((-637 + 357 * Q) / 90.0 + theta * ((392 - 287 * Q) / 60.0 + theta * (-49 + 49 * Q) / 25.0)));
+ final double coeff7 = theta * ( 3 / 10.0 + theta * ( -1 + theta * ( 3 / 4.0)));
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot2 = yDotK[1][i];
+ final double yDot3 = yDotK[2][i];
+ final double yDot4 = yDotK[3][i];
+ final double yDot5 = yDotK[4][i];
+ final double yDot6 = yDotK[5][i];
+ final double yDot7 = yDotK[6][i];
+ interpolatedState[i] = previousState[i] +
+ theta * h * (coeff1 * yDot1 + coeff2 * yDot2 + coeff3 * yDot3 +
+ coeff4 * yDot4 + coeff5 * yDot5 + coeff6 * yDot6 + coeff7 * yDot7);
+ interpolatedDerivatives[i] = coeffDot1 * yDot1 + coeffDot2 * yDot2 + coeffDot3 * yDot3 +
+ coeffDot4 * yDot4 + coeffDot5 * yDot5 + coeffDot6 * yDot6 + coeffDot7 * yDot7;
+ }
+ } else {
+
+ final double coeff1 = -1 / 20.0 + theta * ( 19 / 20.0 + theta * ( -89 / 20.0 + theta * ( 151 / 20.0 + theta * -21 / 5.0)));
+ final double coeff2 = 0;
+ final double coeff3 = -16 / 45.0 + theta * ( -16 / 45.0 + theta * ( -328 / 45.0 + theta * ( 424 / 15.0 + theta * -112 / 5.0)));
+ final double coeff4 = theta * ( theta * ( 162 / 25.0 + theta * ( -648 / 25.0 + theta * 567 / 25.0)));
+ final double coeff5 = -49 / 180.0 + theta * ( -49 / 180.0 + theta * ((2254 + 1029 * Q) / 900.0 + theta * ((-1372 - 847 * Q) / 300.0 + theta * ( 49 + 49 * Q) / 25.0)));
+ final double coeff6 = -49 / 180.0 + theta * ( -49 / 180.0 + theta * ((2254 - 1029 * Q) / 900.0 + theta * ((-1372 + 847 * Q) / 300.0 + theta * ( 49 - 49 * Q) / 25.0)));
+ final double coeff7 = -1 / 20.0 + theta * ( -1 / 20.0 + theta * ( 1 / 4.0 + theta * ( -3 / 4.0)));
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot2 = yDotK[1][i];
+ final double yDot3 = yDotK[2][i];
+ final double yDot4 = yDotK[3][i];
+ final double yDot5 = yDotK[4][i];
+ final double yDot6 = yDotK[5][i];
+ final double yDot7 = yDotK[6][i];
+ interpolatedState[i] = currentState[i] +
+ oneMinusThetaH * (coeff1 * yDot1 + coeff2 * yDot2 + coeff3 * yDot3 +
+ coeff4 * yDot4 + coeff5 * yDot5 + coeff6 * yDot6 + coeff7 * yDot7);
+ interpolatedDerivatives[i] = coeffDot1 * yDot1 + coeffDot2 * yDot2 + coeffDot3 * yDot3 +
+ coeffDot4 * yDot4 + coeffDot5 * yDot5 + coeffDot6 * yDot6 + coeffDot7 * yDot7;
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointFieldIntegrator.java
new file mode 100644
index 0000000..e0dd8cf
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointFieldIntegrator.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class implements a second order Runge-Kutta integrator for
+ * Ordinary Differential Equations.
+ *
+ * <p>This method is an explicit Runge-Kutta method, its Butcher-array
+ * is the following one :
+ * <pre>
+ * 0 | 0 0
+ * 1/2 | 1/2 0
+ * |----------
+ * | 0 1
+ * </pre>
+ * </p>
+ *
+ * @see EulerFieldIntegrator
+ * @see ClassicalRungeKuttaFieldIntegrator
+ * @see GillFieldIntegrator
+ * @see ThreeEighthesFieldIntegrator
+ * @see LutherFieldIntegrator
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public class MidpointFieldIntegrator<T extends RealFieldElement<T>> extends RungeKuttaFieldIntegrator<T> {
+
+ /** Simple constructor.
+ * Build a midpoint integrator with the given step.
+ * @param field field to which the time and state vector elements belong
+ * @param step integration step
+ */
+ public MidpointFieldIntegrator(final Field<T> field, final T step) {
+ super(field, "midpoint", step);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getC() {
+ final T[] c = MathArrays.buildArray(getField(), 1);
+ c[0] = getField().getOne().multiply(0.5);
+ return c;
+ }
+
+ /** {@inheritDoc} */
+ public T[][] getA() {
+ final T[][] a = MathArrays.buildArray(getField(), 1, 1);
+ a[0][0] = fraction(1, 2);
+ return a;
+ }
+
+ /** {@inheritDoc} */
+ public T[] getB() {
+ final T[] b = MathArrays.buildArray(getField(), 2);
+ b[0] = getField().getZero();
+ b[1] = getField().getOne();
+ return b;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected MidpointFieldStepInterpolator<T>
+ createInterpolator(final boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ return new MidpointFieldStepInterpolator<T>(getField(), forward, yDotK,
+ globalPreviousState, globalCurrentState,
+ globalPreviousState, globalCurrentState,
+ mapper);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointFieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointFieldStepInterpolator.java
new file mode 100644
index 0000000..911e3b2
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointFieldStepInterpolator.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/**
+ * This class implements a step interpolator for second order
+ * Runge-Kutta integrator.
+ *
+ * <p>This interpolator computes dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme :
+ * <ul>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub>) + &theta; h [(1 - &theta;) y'<sub>1</sub> + &theta; y'<sub>2</sub>]
+ * </li>
+ * <li>Using reference point at step end:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub> + h) + (1-&theta;) h [&theta; y'<sub>1</sub> - (1+&theta;) y'<sub>2</sub>]
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * where &theta; belongs to [0 ; 1] and where y'<sub>1</sub> and y'<sub>2</sub> are the two
+ * evaluations of the derivatives already computed during the
+ * step.</p>
+ *
+ * @see MidpointFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+class MidpointFieldStepInterpolator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldStepInterpolator<T> {
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param mapper equations mapper for the all equations
+ */
+ MidpointFieldStepInterpolator(final Field<T> field, final boolean forward,
+ final T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ super(field, forward, yDotK,
+ globalPreviousState, globalCurrentState, softPreviousState, softCurrentState,
+ mapper);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected MidpointFieldStepInterpolator<T> create(final Field<T> newField, final boolean newForward, final T[][] newYDotK,
+ final FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ final FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ final FieldODEStateAndDerivative<T> newSoftPreviousState,
+ final FieldODEStateAndDerivative<T> newSoftCurrentState,
+ final FieldEquationsMapper<T> newMapper) {
+ return new MidpointFieldStepInterpolator<T>(newField, newForward, newYDotK,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(final FieldEquationsMapper<T> mapper,
+ final T time, final T theta,
+ final T thetaH, final T oneMinusThetaH) {
+
+ final T coeffDot2 = theta.multiply(2);
+ final T coeffDot1 = time.getField().getOne().subtract(coeffDot2);
+ final T[] interpolatedState;
+ final T[] interpolatedDerivatives;
+
+ if (getGlobalPreviousState() != null && theta.getReal() <= 0.5) {
+ final T coeff1 = theta.multiply(oneMinusThetaH);
+ final T coeff2 = theta.multiply(thetaH);
+ interpolatedState = previousStateLinearCombination(coeff1, coeff2);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot1, coeffDot2);
+ } else {
+ final T coeff1 = oneMinusThetaH.multiply(theta);
+ final T coeff2 = oneMinusThetaH.multiply(theta.add(1)).negate();
+ interpolatedState = currentStateLinearCombination(coeff1, coeff2);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot1, coeffDot2);
+ }
+
+ return new FieldODEStateAndDerivative<T>(time, interpolatedState, interpolatedDerivatives);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointIntegrator.java
new file mode 100644
index 0000000..fa834a1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointIntegrator.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+
+/**
+ * This class implements a second order Runge-Kutta integrator for
+ * Ordinary Differential Equations.
+ *
+ * <p>This method is an explicit Runge-Kutta method, its Butcher-array
+ * is the following one :
+ * <pre>
+ * 0 | 0 0
+ * 1/2 | 1/2 0
+ * |----------
+ * | 0 1
+ * </pre>
+ * </p>
+ *
+ * @see EulerIntegrator
+ * @see ClassicalRungeKuttaIntegrator
+ * @see GillIntegrator
+ * @see ThreeEighthesIntegrator
+ * @see LutherIntegrator
+ *
+ * @since 1.2
+ */
+
+public class MidpointIntegrator extends RungeKuttaIntegrator {
+
+ /** Time steps Butcher array. */
+ private static final double[] STATIC_C = {
+ 1.0 / 2.0
+ };
+
+ /** Internal weights Butcher array. */
+ private static final double[][] STATIC_A = {
+ { 1.0 / 2.0 }
+ };
+
+ /** Propagation weights Butcher array. */
+ private static final double[] STATIC_B = {
+ 0.0, 1.0
+ };
+
+ /** Simple constructor.
+ * Build a midpoint integrator with the given step.
+ * @param step integration step
+ */
+ public MidpointIntegrator(final double step) {
+ super("midpoint", STATIC_C, STATIC_A, STATIC_B, new MidpointStepInterpolator(), step);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointStepInterpolator.java
new file mode 100644
index 0000000..89a5dc1
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/MidpointStepInterpolator.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+
+/**
+ * This class implements a step interpolator for second order
+ * Runge-Kutta integrator.
+ *
+ * <p>This interpolator computes dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme :
+ * <ul>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub>) + &theta; h [(1 - &theta;) y'<sub>1</sub> + &theta; y'<sub>2</sub>]
+ * </li>
+ * <li>Using reference point at step end:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub> + h) + (1-&theta;) h [&theta; y'<sub>1</sub> - (1+&theta;) y'<sub>2</sub>]
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * where &theta; belongs to [0 ; 1] and where y'<sub>1</sub> and y'<sub>2</sub> are the two
+ * evaluations of the derivatives already computed during the
+ * step.</p>
+ *
+ * @see MidpointIntegrator
+ * @since 1.2
+ */
+
+class MidpointStepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 20111120L;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link
+ * org.apache.commons.math3.ode.sampling.AbstractStepInterpolator#reinitialize}
+ * method should be called before using the instance in order to
+ * initialize the internal arrays. This constructor is used only
+ * in order to delay the initialization in some cases. The {@link
+ * RungeKuttaIntegrator} class uses the prototyping design pattern
+ * to create the step interpolators by cloning an uninitialized model
+ * and later initializing the copy.
+ */
+ // CHECKSTYLE: stop RedundantModifier
+ // the public modifier here is needed for serialization
+ public MidpointStepInterpolator() {
+ }
+ // CHECKSTYLE: resume RedundantModifier
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ MidpointStepInterpolator(final MidpointStepInterpolator interpolator) {
+ super(interpolator);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new MidpointStepInterpolator(this);
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta,
+ final double oneMinusThetaH) {
+
+ final double coeffDot2 = 2 * theta;
+ final double coeffDot1 = 1 - coeffDot2;
+
+ if ((previousState != null) && (theta <= 0.5)) {
+ final double coeff1 = theta * oneMinusThetaH;
+ final double coeff2 = theta * theta * h;
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot2 = yDotK[1][i];
+ interpolatedState[i] = previousState[i] + coeff1 * yDot1 + coeff2 * yDot2;
+ interpolatedDerivatives[i] = coeffDot1 * yDot1 + coeffDot2 * yDot2;
+ }
+ } else {
+ final double coeff1 = oneMinusThetaH * theta;
+ final double coeff2 = oneMinusThetaH * (1.0 + theta);
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot2 = yDotK[1][i];
+ interpolatedState[i] = currentState[i] + coeff1 * yDot1 - coeff2 * yDot2;
+ interpolatedDerivatives[i] = coeffDot1 * yDot1 + coeffDot2 * yDot2;
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaFieldIntegrator.java
new file mode 100644
index 0000000..a97e9f5
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaFieldIntegrator.java
@@ -0,0 +1,273 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.ode.AbstractFieldIntegrator;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldExpandableODE;
+import org.apache.commons.math3.ode.FirstOrderFieldDifferentialEquations;
+import org.apache.commons.math3.ode.FieldODEState;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class implements the common part of all fixed step Runge-Kutta
+ * integrators for Ordinary Differential Equations.
+ *
+ * <p>These methods are explicit Runge-Kutta methods, their Butcher
+ * arrays are as follows :
+ * <pre>
+ * 0 |
+ * c2 | a21
+ * c3 | a31 a32
+ * ... | ...
+ * cs | as1 as2 ... ass-1
+ * |--------------------------
+ * | b1 b2 ... bs-1 bs
+ * </pre>
+ * </p>
+ *
+ * @see EulerFieldIntegrator
+ * @see ClassicalRungeKuttaFieldIntegrator
+ * @see GillFieldIntegrator
+ * @see MidpointFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public abstract class RungeKuttaFieldIntegrator<T extends RealFieldElement<T>>
+ extends AbstractFieldIntegrator<T>
+ implements FieldButcherArrayProvider<T> {
+
+ /** Time steps from Butcher array (without the first zero). */
+ private final T[] c;
+
+ /** Internal weights from Butcher array (without the first empty row). */
+ private final T[][] a;
+
+ /** External weights for the high order method from Butcher array. */
+ private final T[] b;
+
+ /** Integration step. */
+ private final T step;
+
+ /** Simple constructor.
+ * Build a Runge-Kutta integrator with the given
+ * step. The default step handler does nothing.
+ * @param field field to which the time and state vector elements belong
+ * @param name name of the method
+ * @param step integration step
+ */
+ protected RungeKuttaFieldIntegrator(final Field<T> field, final String name, final T step) {
+ super(field, name);
+ this.c = getC();
+ this.a = getA();
+ this.b = getB();
+ this.step = step.abs();
+ }
+
+ /** Create a fraction.
+ * @param p numerator
+ * @param q denominator
+ * @return p/q computed in the instance field
+ */
+ protected T fraction(final int p, final int q) {
+ return getField().getZero().add(p).divide(q);
+ }
+
+ /** Create an interpolator.
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param mapper equations mapper for the all equations
+ * @return external weights for the high order method from Butcher array
+ */
+ protected abstract RungeKuttaFieldStepInterpolator<T> createInterpolator(boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ FieldEquationsMapper<T> mapper);
+
+ /** {@inheritDoc} */
+ public FieldODEStateAndDerivative<T> integrate(final FieldExpandableODE<T> equations,
+ final FieldODEState<T> initialState, final T finalTime)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException {
+
+ sanityChecks(initialState, finalTime);
+ final T t0 = initialState.getTime();
+ final T[] y0 = equations.getMapper().mapState(initialState);
+ setStepStart(initIntegration(equations, t0, y0, finalTime));
+ final boolean forward = finalTime.subtract(initialState.getTime()).getReal() > 0;
+
+ // create some internal working arrays
+ final int stages = c.length + 1;
+ T[] y = y0;
+ final T[][] yDotK = MathArrays.buildArray(getField(), stages, -1);
+ final T[] yTmp = MathArrays.buildArray(getField(), y0.length);
+
+ // set up integration control objects
+ if (forward) {
+ if (getStepStart().getTime().add(step).subtract(finalTime).getReal() >= 0) {
+ setStepSize(finalTime.subtract(getStepStart().getTime()));
+ } else {
+ setStepSize(step);
+ }
+ } else {
+ if (getStepStart().getTime().subtract(step).subtract(finalTime).getReal() <= 0) {
+ setStepSize(finalTime.subtract(getStepStart().getTime()));
+ } else {
+ setStepSize(step.negate());
+ }
+ }
+
+ // main integration loop
+ setIsLastStep(false);
+ do {
+
+ // first stage
+ y = equations.getMapper().mapState(getStepStart());
+ yDotK[0] = equations.getMapper().mapDerivative(getStepStart());
+
+ // next stages
+ for (int k = 1; k < stages; ++k) {
+
+ for (int j = 0; j < y0.length; ++j) {
+ T sum = yDotK[0][j].multiply(a[k-1][0]);
+ for (int l = 1; l < k; ++l) {
+ sum = sum.add(yDotK[l][j].multiply(a[k-1][l]));
+ }
+ yTmp[j] = y[j].add(getStepSize().multiply(sum));
+ }
+
+ yDotK[k] = computeDerivatives(getStepStart().getTime().add(getStepSize().multiply(c[k-1])), yTmp);
+
+ }
+
+ // estimate the state at the end of the step
+ for (int j = 0; j < y0.length; ++j) {
+ T sum = yDotK[0][j].multiply(b[0]);
+ for (int l = 1; l < stages; ++l) {
+ sum = sum.add(yDotK[l][j].multiply(b[l]));
+ }
+ yTmp[j] = y[j].add(getStepSize().multiply(sum));
+ }
+ final T stepEnd = getStepStart().getTime().add(getStepSize());
+ final T[] yDotTmp = computeDerivatives(stepEnd, yTmp);
+ final FieldODEStateAndDerivative<T> stateTmp = new FieldODEStateAndDerivative<T>(stepEnd, yTmp, yDotTmp);
+
+ // discrete events handling
+ System.arraycopy(yTmp, 0, y, 0, y0.length);
+ setStepStart(acceptStep(createInterpolator(forward, yDotK, getStepStart(), stateTmp, equations.getMapper()),
+ finalTime));
+
+ if (!isLastStep()) {
+
+ // stepsize control for next step
+ final T nextT = getStepStart().getTime().add(getStepSize());
+ final boolean nextIsLast = forward ?
+ (nextT.subtract(finalTime).getReal() >= 0) :
+ (nextT.subtract(finalTime).getReal() <= 0);
+ if (nextIsLast) {
+ setStepSize(finalTime.subtract(getStepStart().getTime()));
+ }
+ }
+
+ } while (!isLastStep());
+
+ final FieldODEStateAndDerivative<T> finalState = getStepStart();
+ setStepStart(null);
+ setStepSize(null);
+ return finalState;
+
+ }
+
+ /** Fast computation of a single step of ODE integration.
+ * <p>This method is intended for the limited use case of
+ * very fast computation of only one step without using any of the
+ * rich features of general integrators that may take some time
+ * to set up (i.e. no step handlers, no events handlers, no additional
+ * states, no interpolators, no error control, no evaluations count,
+ * no sanity checks ...). It handles the strict minimum of computation,
+ * so it can be embedded in outer loops.</p>
+ * <p>
+ * This method is <em>not</em> used at all by the {@link #integrate(FieldExpandableODE,
+ * FieldODEState, RealFieldElement)} method. It also completely ignores the step set at
+ * construction time, and uses only a single step to go from {@code t0} to {@code t}.
+ * </p>
+ * <p>
+ * As this method does not use any of the state-dependent features of the integrator,
+ * it should be reasonably thread-safe <em>if and only if</em> the provided differential
+ * equations are themselves thread-safe.
+ * </p>
+ * @param equations differential equations to integrate
+ * @param t0 initial time
+ * @param y0 initial value of the state vector at t0
+ * @param t target time for the integration
+ * (can be set to a value smaller than {@code t0} for backward integration)
+ * @return state vector at {@code t}
+ */
+ public T[] singleStep(final FirstOrderFieldDifferentialEquations<T> equations,
+ final T t0, final T[] y0, final T t) {
+
+ // create some internal working arrays
+ final T[] y = y0.clone();
+ final int stages = c.length + 1;
+ final T[][] yDotK = MathArrays.buildArray(getField(), stages, -1);
+ final T[] yTmp = y0.clone();
+
+ // first stage
+ final T h = t.subtract(t0);
+ yDotK[0] = equations.computeDerivatives(t0, y);
+
+ // next stages
+ for (int k = 1; k < stages; ++k) {
+
+ for (int j = 0; j < y0.length; ++j) {
+ T sum = yDotK[0][j].multiply(a[k-1][0]);
+ for (int l = 1; l < k; ++l) {
+ sum = sum.add(yDotK[l][j].multiply(a[k-1][l]));
+ }
+ yTmp[j] = y[j].add(h.multiply(sum));
+ }
+
+ yDotK[k] = equations.computeDerivatives(t0.add(h.multiply(c[k-1])), yTmp);
+
+ }
+
+ // estimate the state at the end of the step
+ for (int j = 0; j < y0.length; ++j) {
+ T sum = yDotK[0][j].multiply(b[0]);
+ for (int l = 1; l < stages; ++l) {
+ sum = sum.add(yDotK[l][j].multiply(b[l]));
+ }
+ y[j] = y[j].add(h.multiply(sum));
+ }
+
+ return y;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaFieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaFieldStepInterpolator.java
new file mode 100644
index 0000000..7d92d78
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaFieldStepInterpolator.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.ode.sampling.AbstractFieldStepInterpolator;
+import org.apache.commons.math3.util.MathArrays;
+
+/** This class represents an interpolator over the last step during an
+ * ODE integration for Runge-Kutta and embedded Runge-Kutta integrators.
+ *
+ * @see RungeKuttaFieldIntegrator
+ * @see EmbeddedRungeKuttaFieldIntegrator
+ *
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+abstract class RungeKuttaFieldStepInterpolator<T extends RealFieldElement<T>>
+ extends AbstractFieldStepInterpolator<T> {
+
+ /** Field to which the time and state vector elements belong. */
+ private final Field<T> field;
+
+ /** Slopes at the intermediate points. */
+ private final T[][] yDotK;
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param mapper equations mapper for the all equations
+ */
+ protected RungeKuttaFieldStepInterpolator(final Field<T> field, final boolean forward,
+ final T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ super(forward, globalPreviousState, globalCurrentState, softPreviousState, softCurrentState, mapper);
+ this.field = field;
+ this.yDotK = MathArrays.buildArray(field, yDotK.length, -1);
+ for (int i = 0; i < yDotK.length; ++i) {
+ this.yDotK[i] = yDotK[i].clone();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected RungeKuttaFieldStepInterpolator<T> create(boolean newForward,
+ FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ FieldODEStateAndDerivative<T> newSoftPreviousState,
+ FieldODEStateAndDerivative<T> newSoftCurrentState,
+ FieldEquationsMapper<T> newMapper) {
+ return create(field, newForward, yDotK,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+ }
+
+ /** Create a new instance.
+ * @param newField field to which the time and state vector elements belong
+ * @param newForward integration direction indicator
+ * @param newYDotK slopes at the intermediate points
+ * @param newGlobalPreviousState start of the global step
+ * @param newGlobalCurrentState end of the global step
+ * @param newSoftPreviousState start of the restricted step
+ * @param newSoftCurrentState end of the restricted step
+ * @param newMapper equations mapper for the all equations
+ * @return a new instance
+ */
+ protected abstract RungeKuttaFieldStepInterpolator<T> create(Field<T> newField, boolean newForward, T[][] newYDotK,
+ FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ FieldODEStateAndDerivative<T> newSoftPreviousState,
+ FieldODEStateAndDerivative<T> newSoftCurrentState,
+ FieldEquationsMapper<T> newMapper);
+
+ /** Compute a state by linear combination added to previous state.
+ * @param coefficients coefficients to apply to the method staged derivatives
+ * @return combined state
+ */
+ protected final T[] previousStateLinearCombination(final T ... coefficients) {
+ return combine(getPreviousState().getState(),
+ coefficients);
+ }
+
+ /** Compute a state by linear combination added to current state.
+ * @param coefficients coefficients to apply to the method staged derivatives
+ * @return combined state
+ */
+ protected T[] currentStateLinearCombination(final T ... coefficients) {
+ return combine(getCurrentState().getState(),
+ coefficients);
+ }
+
+ /** Compute a state derivative by linear combination.
+ * @param coefficients coefficients to apply to the method staged derivatives
+ * @return combined state
+ */
+ protected T[] derivativeLinearCombination(final T ... coefficients) {
+ return combine(MathArrays.buildArray(field, yDotK[0].length), coefficients);
+ }
+
+ /** Linearly combine arrays.
+ * @param a array to add to
+ * @param coefficients coefficients to apply to the method staged derivatives
+ * @return a itself, as a convenience for fluent API
+ */
+ private T[] combine(final T[] a, final T ... coefficients) {
+ for (int i = 0; i < a.length; ++i) {
+ for (int k = 0; k < coefficients.length; ++k) {
+ a[i] = a[i].add(coefficients[k].multiply(yDotK[k][i]));
+ }
+ }
+ return a;
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaIntegrator.java
new file mode 100644
index 0000000..5f7d5d8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaIntegrator.java
@@ -0,0 +1,269 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+
+import org.apache.commons.math3.exception.DimensionMismatchException;
+import org.apache.commons.math3.exception.MaxCountExceededException;
+import org.apache.commons.math3.exception.NoBracketingException;
+import org.apache.commons.math3.exception.NumberIsTooSmallException;
+import org.apache.commons.math3.ode.AbstractIntegrator;
+import org.apache.commons.math3.ode.ExpandableStatefulODE;
+import org.apache.commons.math3.ode.FirstOrderDifferentialEquations;
+import org.apache.commons.math3.util.FastMath;
+
+/**
+ * This class implements the common part of all fixed step Runge-Kutta
+ * integrators for Ordinary Differential Equations.
+ *
+ * <p>These methods are explicit Runge-Kutta methods, their Butcher
+ * arrays are as follows :
+ * <pre>
+ * 0 |
+ * c2 | a21
+ * c3 | a31 a32
+ * ... | ...
+ * cs | as1 as2 ... ass-1
+ * |--------------------------
+ * | b1 b2 ... bs-1 bs
+ * </pre>
+ * </p>
+ *
+ * @see EulerIntegrator
+ * @see ClassicalRungeKuttaIntegrator
+ * @see GillIntegrator
+ * @see MidpointIntegrator
+ * @since 1.2
+ */
+
+public abstract class RungeKuttaIntegrator extends AbstractIntegrator {
+
+ /** Time steps from Butcher array (without the first zero). */
+ private final double[] c;
+
+ /** Internal weights from Butcher array (without the first empty row). */
+ private final double[][] a;
+
+ /** External weights for the high order method from Butcher array. */
+ private final double[] b;
+
+ /** Prototype of the step interpolator. */
+ private final RungeKuttaStepInterpolator prototype;
+
+ /** Integration step. */
+ private final double step;
+
+ /** Simple constructor.
+ * Build a Runge-Kutta integrator with the given
+ * step. The default step handler does nothing.
+ * @param name name of the method
+ * @param c time steps from Butcher array (without the first zero)
+ * @param a internal weights from Butcher array (without the first empty row)
+ * @param b propagation weights for the high order method from Butcher array
+ * @param prototype prototype of the step interpolator to use
+ * @param step integration step
+ */
+ protected RungeKuttaIntegrator(final String name,
+ final double[] c, final double[][] a, final double[] b,
+ final RungeKuttaStepInterpolator prototype,
+ final double step) {
+ super(name);
+ this.c = c;
+ this.a = a;
+ this.b = b;
+ this.prototype = prototype;
+ this.step = FastMath.abs(step);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void integrate(final ExpandableStatefulODE equations, final double t)
+ throws NumberIsTooSmallException, DimensionMismatchException,
+ MaxCountExceededException, NoBracketingException {
+
+ sanityChecks(equations, t);
+ setEquations(equations);
+ final boolean forward = t > equations.getTime();
+
+ // create some internal working arrays
+ final double[] y0 = equations.getCompleteState();
+ final double[] y = y0.clone();
+ final int stages = c.length + 1;
+ final double[][] yDotK = new double[stages][];
+ for (int i = 0; i < stages; ++i) {
+ yDotK [i] = new double[y0.length];
+ }
+ final double[] yTmp = y0.clone();
+ final double[] yDotTmp = new double[y0.length];
+
+ // set up an interpolator sharing the integrator arrays
+ final RungeKuttaStepInterpolator interpolator = (RungeKuttaStepInterpolator) prototype.copy();
+ interpolator.reinitialize(this, yTmp, yDotK, forward,
+ equations.getPrimaryMapper(), equations.getSecondaryMappers());
+ interpolator.storeTime(equations.getTime());
+
+ // set up integration control objects
+ stepStart = equations.getTime();
+ if (forward) {
+ if (stepStart + step >= t) {
+ stepSize = t - stepStart;
+ } else {
+ stepSize = step;
+ }
+ } else {
+ if (stepStart - step <= t) {
+ stepSize = t - stepStart;
+ } else {
+ stepSize = -step;
+ }
+ }
+ initIntegration(equations.getTime(), y0, t);
+
+ // main integration loop
+ isLastStep = false;
+ do {
+
+ interpolator.shift();
+
+ // first stage
+ computeDerivatives(stepStart, y, yDotK[0]);
+
+ // next stages
+ for (int k = 1; k < stages; ++k) {
+
+ for (int j = 0; j < y0.length; ++j) {
+ double sum = a[k-1][0] * yDotK[0][j];
+ for (int l = 1; l < k; ++l) {
+ sum += a[k-1][l] * yDotK[l][j];
+ }
+ yTmp[j] = y[j] + stepSize * sum;
+ }
+
+ computeDerivatives(stepStart + c[k-1] * stepSize, yTmp, yDotK[k]);
+
+ }
+
+ // estimate the state at the end of the step
+ for (int j = 0; j < y0.length; ++j) {
+ double sum = b[0] * yDotK[0][j];
+ for (int l = 1; l < stages; ++l) {
+ sum += b[l] * yDotK[l][j];
+ }
+ yTmp[j] = y[j] + stepSize * sum;
+ }
+
+ // discrete events handling
+ interpolator.storeTime(stepStart + stepSize);
+ System.arraycopy(yTmp, 0, y, 0, y0.length);
+ System.arraycopy(yDotK[stages - 1], 0, yDotTmp, 0, y0.length);
+ stepStart = acceptStep(interpolator, y, yDotTmp, t);
+
+ if (!isLastStep) {
+
+ // prepare next step
+ interpolator.storeTime(stepStart);
+
+ // stepsize control for next step
+ final double nextT = stepStart + stepSize;
+ final boolean nextIsLast = forward ? (nextT >= t) : (nextT <= t);
+ if (nextIsLast) {
+ stepSize = t - stepStart;
+ }
+ }
+
+ } while (!isLastStep);
+
+ // dispatch results
+ equations.setTime(stepStart);
+ equations.setCompleteState(y);
+
+ stepStart = Double.NaN;
+ stepSize = Double.NaN;
+
+ }
+
+ /** Fast computation of a single step of ODE integration.
+ * <p>This method is intended for the limited use case of
+ * very fast computation of only one step without using any of the
+ * rich features of general integrators that may take some time
+ * to set up (i.e. no step handlers, no events handlers, no additional
+ * states, no interpolators, no error control, no evaluations count,
+ * no sanity checks ...). It handles the strict minimum of computation,
+ * so it can be embedded in outer loops.</p>
+ * <p>
+ * This method is <em>not</em> used at all by the {@link #integrate(ExpandableStatefulODE, double)}
+ * method. It also completely ignores the step set at construction time, and
+ * uses only a single step to go from {@code t0} to {@code t}.
+ * </p>
+ * <p>
+ * As this method does not use any of the state-dependent features of the integrator,
+ * it should be reasonably thread-safe <em>if and only if</em> the provided differential
+ * equations are themselves thread-safe.
+ * </p>
+ * @param equations differential equations to integrate
+ * @param t0 initial time
+ * @param y0 initial value of the state vector at t0
+ * @param t target time for the integration
+ * (can be set to a value smaller than {@code t0} for backward integration)
+ * @return state vector at {@code t}
+ */
+ public double[] singleStep(final FirstOrderDifferentialEquations equations,
+ final double t0, final double[] y0, final double t) {
+
+ // create some internal working arrays
+ final double[] y = y0.clone();
+ final int stages = c.length + 1;
+ final double[][] yDotK = new double[stages][];
+ for (int i = 0; i < stages; ++i) {
+ yDotK [i] = new double[y0.length];
+ }
+ final double[] yTmp = y0.clone();
+
+ // first stage
+ final double h = t - t0;
+ equations.computeDerivatives(t0, y, yDotK[0]);
+
+ // next stages
+ for (int k = 1; k < stages; ++k) {
+
+ for (int j = 0; j < y0.length; ++j) {
+ double sum = a[k-1][0] * yDotK[0][j];
+ for (int l = 1; l < k; ++l) {
+ sum += a[k-1][l] * yDotK[l][j];
+ }
+ yTmp[j] = y[j] + h * sum;
+ }
+
+ equations.computeDerivatives(t0 + c[k-1] * h, yTmp, yDotK[k]);
+
+ }
+
+ // estimate the state at the end of the step
+ for (int j = 0; j < y0.length; ++j) {
+ double sum = b[0] * yDotK[0][j];
+ for (int l = 1; l < stages; ++l) {
+ sum += b[l] * yDotK[l][j];
+ }
+ y[j] += h * sum;
+ }
+
+ return y;
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaStepInterpolator.java
new file mode 100644
index 0000000..1ae7cb9
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/RungeKuttaStepInterpolator.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+import org.apache.commons.math3.ode.AbstractIntegrator;
+import org.apache.commons.math3.ode.EquationsMapper;
+import org.apache.commons.math3.ode.sampling.AbstractStepInterpolator;
+
+/** This class represents an interpolator over the last step during an
+ * ODE integration for Runge-Kutta and embedded Runge-Kutta integrators.
+ *
+ * @see RungeKuttaIntegrator
+ * @see EmbeddedRungeKuttaIntegrator
+ *
+ * @since 1.2
+ */
+
+abstract class RungeKuttaStepInterpolator
+ extends AbstractStepInterpolator {
+
+ /** Previous state. */
+ protected double[] previousState;
+
+ /** Slopes at the intermediate points */
+ protected double[][] yDotK;
+
+ /** Reference to the integrator. */
+ protected AbstractIntegrator integrator;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link #reinitialize} method should be called before using the
+ * instance in order to initialize the internal arrays. This
+ * constructor is used only in order to delay the initialization in
+ * some cases. The {@link RungeKuttaIntegrator} and {@link
+ * EmbeddedRungeKuttaIntegrator} classes use the prototyping design
+ * pattern to create the step interpolators by cloning an
+ * uninitialized model and latter initializing the copy.
+ */
+ protected RungeKuttaStepInterpolator() {
+ previousState = null;
+ yDotK = null;
+ integrator = null;
+ }
+
+ /** Copy constructor.
+
+ * <p>The copied interpolator should have been finalized before the
+ * copy, otherwise the copy will not be able to perform correctly any
+ * interpolation and will throw a {@link NullPointerException}
+ * later. Since we don't want this constructor to throw the
+ * exceptions finalization may involve and since we don't want this
+ * method to modify the state of the copied interpolator,
+ * finalization is <strong>not</strong> done automatically, it
+ * remains under user control.</p>
+
+ * <p>The copy is a deep copy: its arrays are separated from the
+ * original arrays of the instance.</p>
+
+ * @param interpolator interpolator to copy from.
+
+ */
+ RungeKuttaStepInterpolator(final RungeKuttaStepInterpolator interpolator) {
+
+ super(interpolator);
+
+ if (interpolator.currentState != null) {
+
+ previousState = interpolator.previousState.clone();
+
+ yDotK = new double[interpolator.yDotK.length][];
+ for (int k = 0; k < interpolator.yDotK.length; ++k) {
+ yDotK[k] = interpolator.yDotK[k].clone();
+ }
+
+ } else {
+ previousState = null;
+ yDotK = null;
+ }
+
+ // we cannot keep any reference to the equations in the copy
+ // the interpolator should have been finalized before
+ integrator = null;
+
+ }
+
+ /** Reinitialize the instance
+ * <p>Some Runge-Kutta integrators need fewer functions evaluations
+ * than their counterpart step interpolators. So the interpolator
+ * should perform the last evaluations they need by themselves. The
+ * {@link RungeKuttaIntegrator RungeKuttaIntegrator} and {@link
+ * EmbeddedRungeKuttaIntegrator EmbeddedRungeKuttaIntegrator}
+ * abstract classes call this method in order to let the step
+ * interpolator perform the evaluations it needs. These evaluations
+ * will be performed during the call to <code>doFinalize</code> if
+ * any, i.e. only if the step handler either calls the {@link
+ * AbstractStepInterpolator#finalizeStep finalizeStep} method or the
+ * {@link AbstractStepInterpolator#getInterpolatedState
+ * getInterpolatedState} method (for an interpolator which needs a
+ * finalization) or if it clones the step interpolator.</p>
+ * @param rkIntegrator integrator being used
+ * @param y reference to the integrator array holding the state at
+ * the end of the step
+ * @param yDotArray reference to the integrator array holding all the
+ * intermediate slopes
+ * @param forward integration direction indicator
+ * @param primaryMapper equations mapper for the primary equations set
+ * @param secondaryMappers equations mappers for the secondary equations sets
+ */
+ public void reinitialize(final AbstractIntegrator rkIntegrator,
+ final double[] y, final double[][] yDotArray, final boolean forward,
+ final EquationsMapper primaryMapper,
+ final EquationsMapper[] secondaryMappers) {
+ reinitialize(y, forward, primaryMapper, secondaryMappers);
+ this.previousState = null;
+ this.yDotK = yDotArray;
+ this.integrator = rkIntegrator;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void shift() {
+ previousState = currentState.clone();
+ super.shift();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeExternal(final ObjectOutput out)
+ throws IOException {
+
+ // save the state of the base class
+ writeBaseExternal(out);
+
+ // save the local attributes
+ final int n = (currentState == null) ? -1 : currentState.length;
+ for (int i = 0; i < n; ++i) {
+ out.writeDouble(previousState[i]);
+ }
+
+ final int kMax = (yDotK == null) ? -1 : yDotK.length;
+ out.writeInt(kMax);
+ for (int k = 0; k < kMax; ++k) {
+ for (int i = 0; i < n; ++i) {
+ out.writeDouble(yDotK[k][i]);
+ }
+ }
+
+ // we do not save any reference to the equations
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void readExternal(final ObjectInput in)
+ throws IOException, ClassNotFoundException {
+
+ // read the base class
+ final double t = readBaseExternal(in);
+
+ // read the local attributes
+ final int n = (currentState == null) ? -1 : currentState.length;
+ if (n < 0) {
+ previousState = null;
+ } else {
+ previousState = new double[n];
+ for (int i = 0; i < n; ++i) {
+ previousState[i] = in.readDouble();
+ }
+ }
+
+ final int kMax = in.readInt();
+ yDotK = (kMax < 0) ? null : new double[kMax][];
+ for (int k = 0; k < kMax; ++k) {
+ yDotK[k] = (n < 0) ? null : new double[n];
+ for (int i = 0; i < n; ++i) {
+ yDotK[k][i] = in.readDouble();
+ }
+ }
+
+ integrator = null;
+
+ if (currentState != null) {
+ // we can now set the interpolated time and state
+ setInterpolatedTime(t);
+ } else {
+ interpolatedTime = t;
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesFieldIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesFieldIntegrator.java
new file mode 100644
index 0000000..7e91de8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesFieldIntegrator.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+import org.apache.commons.math3.util.MathArrays;
+
+/**
+ * This class implements the 3/8 fourth order Runge-Kutta
+ * integrator for Ordinary Differential Equations.
+ *
+ * <p>This method is an explicit Runge-Kutta method, its Butcher-array
+ * is the following one :
+ * <pre>
+ * 0 | 0 0 0 0
+ * 1/3 | 1/3 0 0 0
+ * 2/3 |-1/3 1 0 0
+ * 1 | 1 -1 1 0
+ * |--------------------
+ * | 1/8 3/8 3/8 1/8
+ * </pre>
+ * </p>
+ *
+ * @see EulerFieldIntegrator
+ * @see ClassicalRungeKuttaFieldIntegrator
+ * @see GillFieldIntegrator
+ * @see MidpointFieldIntegrator
+ * @see LutherFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+public class ThreeEighthesFieldIntegrator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldIntegrator<T> {
+
+ /** Simple constructor.
+ * Build a 3/8 integrator with the given step.
+ * @param field field to which the time and state vector elements belong
+ * @param step integration step
+ */
+ public ThreeEighthesFieldIntegrator(final Field<T> field, final T step) {
+ super(field, "3/8", step);
+ }
+
+ /** {@inheritDoc} */
+ public T[] getC() {
+ final T[] c = MathArrays.buildArray(getField(), 3);
+ c[0] = fraction(1, 3);
+ c[1] = c[0].add(c[0]);
+ c[2] = getField().getOne();
+ return c;
+ }
+
+ /** {@inheritDoc} */
+ public T[][] getA() {
+ final T[][] a = MathArrays.buildArray(getField(), 3, -1);
+ for (int i = 0; i < a.length; ++i) {
+ a[i] = MathArrays.buildArray(getField(), i + 1);
+ }
+ a[0][0] = fraction(1, 3);
+ a[1][0] = a[0][0].negate();
+ a[1][1] = getField().getOne();
+ a[2][0] = getField().getOne();
+ a[2][1] = getField().getOne().negate();
+ a[2][2] = getField().getOne();
+ return a;
+ }
+
+ /** {@inheritDoc} */
+ public T[] getB() {
+ final T[] b = MathArrays.buildArray(getField(), 4);
+ b[0] = fraction(1, 8);
+ b[1] = fraction(3, 8);
+ b[2] = b[1];
+ b[3] = b[0];
+ return b;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected ThreeEighthesFieldStepInterpolator<T>
+ createInterpolator(final boolean forward, T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ return new ThreeEighthesFieldStepInterpolator<T>(getField(), forward, yDotK,
+ globalPreviousState, globalCurrentState,
+ globalPreviousState, globalCurrentState,
+ mapper);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesFieldStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesFieldStepInterpolator.java
new file mode 100644
index 0000000..14a4eb8
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesFieldStepInterpolator.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.Field;
+import org.apache.commons.math3.RealFieldElement;
+import org.apache.commons.math3.ode.FieldEquationsMapper;
+import org.apache.commons.math3.ode.FieldODEStateAndDerivative;
+
+/**
+ * This class implements a step interpolator for the 3/8 fourth
+ * order Runge-Kutta integrator.
+ *
+ * <p>This interpolator allows to compute dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme :
+ * <ul>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub>)
+ * + &theta; (h/8) [ (8 - 15 &theta; + 8 &theta;<sup>2</sup>) y'<sub>1</sub>
+ * + 3 * (15 &theta; - 12 &theta;<sup>2</sup>) y'<sub>2</sub>
+ * + 3 &theta; y'<sub>3</sub>
+ * + (-3 &theta; + 4 &theta;<sup>2</sup>) y'<sub>4</sub>
+ * ]
+ * </li>
+ * <li>Using reference point at step end:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub> + h)
+ * - (1 - &theta;) (h/8) [(1 - 7 &theta; + 8 &theta;<sup>2</sup>) y'<sub>1</sub>
+ * + 3 (1 + &theta; - 4 &theta;<sup>2</sup>) y'<sub>2</sub>
+ * + 3 (1 + &theta;) y'<sub>3</sub>
+ * + (1 + &theta; + 4 &theta;<sup>2</sup>) y'<sub>4</sub>
+ * ]
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * where &theta; belongs to [0 ; 1] and where y'<sub>1</sub> to y'<sub>4</sub> are the four
+ * evaluations of the derivatives already computed during the
+ * step.</p>
+ *
+ * @see ThreeEighthesFieldIntegrator
+ * @param <T> the type of the field elements
+ * @since 3.6
+ */
+
+class ThreeEighthesFieldStepInterpolator<T extends RealFieldElement<T>>
+ extends RungeKuttaFieldStepInterpolator<T> {
+
+ /** Simple constructor.
+ * @param field field to which the time and state vector elements belong
+ * @param forward integration direction indicator
+ * @param yDotK slopes at the intermediate points
+ * @param globalPreviousState start of the global step
+ * @param globalCurrentState end of the global step
+ * @param softPreviousState start of the restricted step
+ * @param softCurrentState end of the restricted step
+ * @param mapper equations mapper for the all equations
+ */
+ ThreeEighthesFieldStepInterpolator(final Field<T> field, final boolean forward,
+ final T[][] yDotK,
+ final FieldODEStateAndDerivative<T> globalPreviousState,
+ final FieldODEStateAndDerivative<T> globalCurrentState,
+ final FieldODEStateAndDerivative<T> softPreviousState,
+ final FieldODEStateAndDerivative<T> softCurrentState,
+ final FieldEquationsMapper<T> mapper) {
+ super(field, forward, yDotK,
+ globalPreviousState, globalCurrentState, softPreviousState, softCurrentState,
+ mapper);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected ThreeEighthesFieldStepInterpolator<T> create(final Field<T> newField, final boolean newForward, final T[][] newYDotK,
+ final FieldODEStateAndDerivative<T> newGlobalPreviousState,
+ final FieldODEStateAndDerivative<T> newGlobalCurrentState,
+ final FieldODEStateAndDerivative<T> newSoftPreviousState,
+ final FieldODEStateAndDerivative<T> newSoftCurrentState,
+ final FieldEquationsMapper<T> newMapper) {
+ return new ThreeEighthesFieldStepInterpolator<T>(newField, newForward, newYDotK,
+ newGlobalPreviousState, newGlobalCurrentState,
+ newSoftPreviousState, newSoftCurrentState,
+ newMapper);
+ }
+
+ /** {@inheritDoc} */
+ @SuppressWarnings("unchecked")
+ @Override
+ protected FieldODEStateAndDerivative<T> computeInterpolatedStateAndDerivatives(final FieldEquationsMapper<T> mapper,
+ final T time, final T theta,
+ final T thetaH, final T oneMinusThetaH) {
+
+ final T coeffDot3 = theta.multiply(0.75);
+ final T coeffDot1 = coeffDot3.multiply(theta.multiply(4).subtract(5)).add(1);
+ final T coeffDot2 = coeffDot3.multiply(theta.multiply(-6).add(5));
+ final T coeffDot4 = coeffDot3.multiply(theta.multiply(2).subtract(1));
+ final T[] interpolatedState;
+ final T[] interpolatedDerivatives;
+
+ if (getGlobalPreviousState() != null && theta.getReal() <= 0.5) {
+ final T s = thetaH.divide(8);
+ final T fourTheta2 = theta.multiply(theta).multiply(4);
+ final T coeff1 = s.multiply(fourTheta2.multiply(2).subtract(theta.multiply(15)).add(8));
+ final T coeff2 = s.multiply(theta.multiply(5).subtract(fourTheta2)).multiply(3);
+ final T coeff3 = s.multiply(theta).multiply(3);
+ final T coeff4 = s.multiply(fourTheta2.subtract(theta.multiply(3)));
+ interpolatedState = previousStateLinearCombination(coeff1, coeff2, coeff3, coeff4);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot1, coeffDot2, coeffDot3, coeffDot4);
+ } else {
+ final T s = oneMinusThetaH.divide(-8);
+ final T fourTheta2 = theta.multiply(theta).multiply(4);
+ final T thetaPlus1 = theta.add(1);
+ final T coeff1 = s.multiply(fourTheta2.multiply(2).subtract(theta.multiply(7)).add(1));
+ final T coeff2 = s.multiply(thetaPlus1.subtract(fourTheta2)).multiply(3);
+ final T coeff3 = s.multiply(thetaPlus1).multiply(3);
+ final T coeff4 = s.multiply(thetaPlus1.add(fourTheta2));
+ interpolatedState = currentStateLinearCombination(coeff1, coeff2, coeff3, coeff4);
+ interpolatedDerivatives = derivativeLinearCombination(coeffDot1, coeffDot2, coeffDot3, coeffDot4);
+ }
+
+ return new FieldODEStateAndDerivative<T>(time, interpolatedState, interpolatedDerivatives);
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesIntegrator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesIntegrator.java
new file mode 100644
index 0000000..c5f8216
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesIntegrator.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+
+/**
+ * This class implements the 3/8 fourth order Runge-Kutta
+ * integrator for Ordinary Differential Equations.
+ *
+ * <p>This method is an explicit Runge-Kutta method, its Butcher-array
+ * is the following one :
+ * <pre>
+ * 0 | 0 0 0 0
+ * 1/3 | 1/3 0 0 0
+ * 2/3 |-1/3 1 0 0
+ * 1 | 1 -1 1 0
+ * |--------------------
+ * | 1/8 3/8 3/8 1/8
+ * </pre>
+ * </p>
+ *
+ * @see EulerIntegrator
+ * @see ClassicalRungeKuttaIntegrator
+ * @see GillIntegrator
+ * @see MidpointIntegrator
+ * @see LutherIntegrator
+ * @since 1.2
+ */
+
+public class ThreeEighthesIntegrator extends RungeKuttaIntegrator {
+
+ /** Time steps Butcher array. */
+ private static final double[] STATIC_C = {
+ 1.0 / 3.0, 2.0 / 3.0, 1.0
+ };
+
+ /** Internal weights Butcher array. */
+ private static final double[][] STATIC_A = {
+ { 1.0 / 3.0 },
+ { -1.0 / 3.0, 1.0 },
+ { 1.0, -1.0, 1.0 }
+ };
+
+ /** Propagation weights Butcher array. */
+ private static final double[] STATIC_B = {
+ 1.0 / 8.0, 3.0 / 8.0, 3.0 / 8.0, 1.0 / 8.0
+ };
+
+ /** Simple constructor.
+ * Build a 3/8 integrator with the given step.
+ * @param step integration step
+ */
+ public ThreeEighthesIntegrator(final double step) {
+ super("3/8", STATIC_C, STATIC_A, STATIC_B, new ThreeEighthesStepInterpolator(), step);
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesStepInterpolator.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesStepInterpolator.java
new file mode 100644
index 0000000..df288fd
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/ThreeEighthesStepInterpolator.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.commons.math3.ode.nonstiff;
+
+import org.apache.commons.math3.ode.sampling.StepInterpolator;
+
+/**
+ * This class implements a step interpolator for the 3/8 fourth
+ * order Runge-Kutta integrator.
+ *
+ * <p>This interpolator allows to compute dense output inside the last
+ * step computed. The interpolation equation is consistent with the
+ * integration scheme :
+ * <ul>
+ * <li>Using reference point at step start:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub>)
+ * + &theta; (h/8) [ (8 - 15 &theta; + 8 &theta;<sup>2</sup>) y'<sub>1</sub>
+ * + 3 * (15 &theta; - 12 &theta;<sup>2</sup>) y'<sub>2</sub>
+ * + 3 &theta; y'<sub>3</sub>
+ * + (-3 &theta; + 4 &theta;<sup>2</sup>) y'<sub>4</sub>
+ * ]
+ * </li>
+ * <li>Using reference point at step end:<br>
+ * y(t<sub>n</sub> + &theta; h) = y (t<sub>n</sub> + h)
+ * - (1 - &theta;) (h/8) [(1 - 7 &theta; + 8 &theta;<sup>2</sup>) y'<sub>1</sub>
+ * + 3 (1 + &theta; - 4 &theta;<sup>2</sup>) y'<sub>2</sub>
+ * + 3 (1 + &theta;) y'<sub>3</sub>
+ * + (1 + &theta; + 4 &theta;<sup>2</sup>) y'<sub>4</sub>
+ * ]
+ * </li>
+ * </ul>
+ * </p>
+ *
+ * where &theta; belongs to [0 ; 1] and where y'<sub>1</sub> to y'<sub>4</sub> are the four
+ * evaluations of the derivatives already computed during the
+ * step.</p>
+ *
+ * @see ThreeEighthesIntegrator
+ * @since 1.2
+ */
+
+class ThreeEighthesStepInterpolator
+ extends RungeKuttaStepInterpolator {
+
+ /** Serializable version identifier */
+ private static final long serialVersionUID = 20111120L;
+
+ /** Simple constructor.
+ * This constructor builds an instance that is not usable yet, the
+ * {@link
+ * org.apache.commons.math3.ode.sampling.AbstractStepInterpolator#reinitialize}
+ * method should be called before using the instance in order to
+ * initialize the internal arrays. This constructor is used only
+ * in order to delay the initialization in some cases. The {@link
+ * RungeKuttaIntegrator} class uses the prototyping design pattern
+ * to create the step interpolators by cloning an uninitialized model
+ * and later initializing the copy.
+ */
+ // CHECKSTYLE: stop RedundantModifier
+ // the public modifier here is needed for serialization
+ public ThreeEighthesStepInterpolator() {
+ }
+ // CHECKSTYLE: resume RedundantModifier
+
+ /** Copy constructor.
+ * @param interpolator interpolator to copy from. The copy is a deep
+ * copy: its arrays are separated from the original arrays of the
+ * instance
+ */
+ ThreeEighthesStepInterpolator(final ThreeEighthesStepInterpolator interpolator) {
+ super(interpolator);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected StepInterpolator doCopy() {
+ return new ThreeEighthesStepInterpolator(this);
+ }
+
+
+ /** {@inheritDoc} */
+ @Override
+ protected void computeInterpolatedStateAndDerivatives(final double theta,
+ final double oneMinusThetaH) {
+
+ final double coeffDot3 = 0.75 * theta;
+ final double coeffDot1 = coeffDot3 * (4 * theta - 5) + 1;
+ final double coeffDot2 = coeffDot3 * (5 - 6 * theta);
+ final double coeffDot4 = coeffDot3 * (2 * theta - 1);
+
+ if ((previousState != null) && (theta <= 0.5)) {
+ final double s = theta * h / 8.0;
+ final double fourTheta2 = 4 * theta * theta;
+ final double coeff1 = s * (8 - 15 * theta + 2 * fourTheta2);
+ final double coeff2 = 3 * s * (5 * theta - fourTheta2);
+ final double coeff3 = 3 * s * theta;
+ final double coeff4 = s * (-3 * theta + fourTheta2);
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot2 = yDotK[1][i];
+ final double yDot3 = yDotK[2][i];
+ final double yDot4 = yDotK[3][i];
+ interpolatedState[i] =
+ previousState[i] + coeff1 * yDot1 + coeff2 * yDot2 + coeff3 * yDot3 + coeff4 * yDot4;
+ interpolatedDerivatives[i] =
+ coeffDot1 * yDot1 + coeffDot2 * yDot2 + coeffDot3 * yDot3 + coeffDot4 * yDot4;
+
+ }
+ } else {
+ final double s = oneMinusThetaH / 8.0;
+ final double fourTheta2 = 4 * theta * theta;
+ final double coeff1 = s * (1 - 7 * theta + 2 * fourTheta2);
+ final double coeff2 = 3 * s * (1 + theta - fourTheta2);
+ final double coeff3 = 3 * s * (1 + theta);
+ final double coeff4 = s * (1 + theta + fourTheta2);
+ for (int i = 0; i < interpolatedState.length; ++i) {
+ final double yDot1 = yDotK[0][i];
+ final double yDot2 = yDotK[1][i];
+ final double yDot3 = yDotK[2][i];
+ final double yDot4 = yDotK[3][i];
+ interpolatedState[i] =
+ currentState[i] - coeff1 * yDot1 - coeff2 * yDot2 - coeff3 * yDot3 - coeff4 * yDot4;
+ interpolatedDerivatives[i] =
+ coeffDot1 * yDot1 + coeffDot2 * yDot2 + coeffDot3 * yDot3 + coeffDot4 * yDot4;
+
+ }
+ }
+
+ }
+
+}
diff --git a/src/main/java/org/apache/commons/math3/ode/nonstiff/package-info.java b/src/main/java/org/apache/commons/math3/ode/nonstiff/package-info.java
new file mode 100644
index 0000000..b2387ce
--- /dev/null
+++ b/src/main/java/org/apache/commons/math3/ode/nonstiff/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/**
+ *
+ * <p>
+ * This package provides classes to solve non-stiff Ordinary Differential Equations problems.
+ * </p>
+ *
+ *
+ */
+package org.apache.commons.math3.ode.nonstiff;