AdamsFieldIntegrator.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */

  21. package org.hipparchus.ode.nonstiff;

  22. import org.hipparchus.Field;
  23. import org.hipparchus.CalculusFieldElement;
  24. import org.hipparchus.exception.MathIllegalArgumentException;
  25. import org.hipparchus.exception.MathIllegalStateException;
  26. import org.hipparchus.linear.Array2DRowFieldMatrix;
  27. import org.hipparchus.linear.FieldMatrix;
  28. import org.hipparchus.ode.FieldEquationsMapper;
  29. import org.hipparchus.ode.FieldExpandableODE;
  30. import org.hipparchus.ode.FieldODEState;
  31. import org.hipparchus.ode.FieldODEStateAndDerivative;
  32. import org.hipparchus.ode.LocalizedODEFormats;
  33. import org.hipparchus.ode.MultistepFieldIntegrator;
  34. import org.hipparchus.ode.nonstiff.interpolators.AdamsFieldStateInterpolator;
  35. import org.hipparchus.util.MathArrays;


  36. /** Base class for {@link AdamsBashforthFieldIntegrator Adams-Bashforth} and
  37.  * {@link AdamsMoultonFieldIntegrator Adams-Moulton} integrators.
  38.  * @param <T> the type of the field elements
  39.  */
  40. public abstract class AdamsFieldIntegrator<T extends CalculusFieldElement<T>> extends MultistepFieldIntegrator<T> {

  41.     /** Transformer. */
  42.     private final AdamsNordsieckFieldTransformer<T> transformer;

  43.     /**
  44.      * Build an Adams integrator with the given order and step control parameters.
  45.      * @param field field to which the time and state vector elements belong
  46.      * @param name name of the method
  47.      * @param nSteps number of steps of the method excluding the one being computed
  48.      * @param order order of the method
  49.      * @param minStep minimal step (sign is irrelevant, regardless of
  50.      * integration direction, forward or backward), the last step can
  51.      * be smaller than this
  52.      * @param maxStep maximal step (sign is irrelevant, regardless of
  53.      * integration direction, forward or backward), the last step can
  54.      * be smaller than this
  55.      * @param scalAbsoluteTolerance allowed absolute error
  56.      * @param scalRelativeTolerance allowed relative error
  57.      * @exception MathIllegalArgumentException if order is 1 or less
  58.      */
  59.     protected AdamsFieldIntegrator(final Field<T> field, final String name, final int nSteps, final int order,
  60.                                    final double minStep, final double maxStep, final double scalAbsoluteTolerance,
  61.                                    final double scalRelativeTolerance)
  62.         throws MathIllegalArgumentException {
  63.         super(field, name, nSteps, order, minStep, maxStep,
  64.               scalAbsoluteTolerance, scalRelativeTolerance);
  65.         transformer = AdamsNordsieckFieldTransformer.getInstance(field, nSteps);
  66.     }

  67.     /**
  68.      * Build an Adams integrator with the given order and step control parameters.
  69.      * @param field field to which the time and state vector elements belong
  70.      * @param name name of the method
  71.      * @param nSteps number of steps of the method excluding the one being computed
  72.      * @param order order of the method
  73.      * @param minStep minimal step (sign is irrelevant, regardless of
  74.      * integration direction, forward or backward), the last step can
  75.      * be smaller than this
  76.      * @param maxStep maximal step (sign is irrelevant, regardless of
  77.      * integration direction, forward or backward), the last step can
  78.      * be smaller than this
  79.      * @param vecAbsoluteTolerance allowed absolute error
  80.      * @param vecRelativeTolerance allowed relative error
  81.      * @exception IllegalArgumentException if order is 1 or less
  82.      */
  83.     protected AdamsFieldIntegrator(final Field<T> field, final String name, final int nSteps, final int order,
  84.                                    final double minStep, final double maxStep, final double[] vecAbsoluteTolerance,
  85.                                    final double[] vecRelativeTolerance)
  86.         throws IllegalArgumentException {
  87.         super(field, name, nSteps, order, minStep, maxStep,
  88.               vecAbsoluteTolerance, vecRelativeTolerance);
  89.         transformer = AdamsNordsieckFieldTransformer.getInstance(field, nSteps);
  90.     }

  91.     /** {@inheritDoc} */
  92.     @Override
  93.     public FieldODEStateAndDerivative<T> integrate(final FieldExpandableODE<T> equations,
  94.                                                    final FieldODEState<T> initialState,
  95.                                                    final T finalTime)
  96.         throws MathIllegalArgumentException, MathIllegalStateException {

  97.         sanityChecks(initialState, finalTime);
  98.         setStepStart(initIntegration(equations, initialState, finalTime));
  99.         final boolean forward = finalTime.subtract(initialState.getTime()).getReal() > 0;

  100.         // compute the initial Nordsieck vector using the configured starter integrator
  101.         start(equations, getStepStart(), finalTime);

  102.         // reuse the step that was chosen by the starter integrator
  103.         FieldODEStateAndDerivative<T> stepStart = getStepStart();
  104.         FieldODEStateAndDerivative<T> stepEnd   =
  105.                         AdamsFieldStateInterpolator.taylor(equations.getMapper(), stepStart,
  106.                                                            stepStart.getTime().add(getStepSize()),
  107.                                                            getStepSize(), scaled, nordsieck);

  108.         // main integration loop
  109.         setIsLastStep(false);
  110.         final T[] y = stepStart.getCompleteState();
  111.         do {

  112.             T[] predictedY = null;
  113.             final T[] predictedScaled = MathArrays.buildArray(getField(), y.length);
  114.             Array2DRowFieldMatrix<T> predictedNordsieck = null;
  115.             double error = 10;
  116.             while (error >= 1.0) {

  117.                 // predict a first estimate of the state at step end
  118.                 predictedY = stepEnd.getCompleteState();

  119.                 // evaluate the derivative
  120.                 final T[] yDot = computeDerivatives(stepEnd.getTime(), predictedY);

  121.                 // predict Nordsieck vector at step end
  122.                 for (int j = 0; j < predictedScaled.length; ++j) {
  123.                     predictedScaled[j] = getStepSize().multiply(yDot[j]);
  124.                 }
  125.                 predictedNordsieck = updateHighOrderDerivativesPhase1(nordsieck);
  126.                 updateHighOrderDerivativesPhase2(scaled, predictedScaled, predictedNordsieck);

  127.                 // evaluate error
  128.                 error = errorEstimation(y, stepEnd.getTime(), predictedY, predictedScaled, predictedNordsieck);
  129.                 if (Double.isNaN(error)) {
  130.                     throw new MathIllegalStateException(LocalizedODEFormats.NAN_APPEARING_DURING_INTEGRATION,
  131.                                                         stepEnd.getTime().getReal());
  132.                 }

  133.                 if (error >= 1.0) {
  134.                     // reject the step and attempt to reduce error by stepsize control
  135.                     final double factor = computeStepGrowShrinkFactor(error);
  136.                     rescale(getStepSizeHelper().filterStep(getStepSize().multiply(factor), forward, false));
  137.                     stepEnd = AdamsFieldStateInterpolator.taylor(equations.getMapper(), getStepStart(),
  138.                                                                  getStepStart().getTime().add(getStepSize()),
  139.                                                                  getStepSize(),
  140.                                                                  scaled,
  141.                                                                  nordsieck);

  142.                 }
  143.             }

  144.             final AdamsFieldStateInterpolator<T> interpolator =
  145.                             finalizeStep(getStepSize(), predictedY, predictedScaled, predictedNordsieck,
  146.                                          forward, getStepStart(), stepEnd, equations.getMapper());

  147.             // discrete events handling
  148.             setStepStart(acceptStep(interpolator, finalTime));
  149.             scaled    = interpolator.getScaled();
  150.             nordsieck = interpolator.getNordsieck();

  151.             if (!isLastStep()) {

  152.                 if (resetOccurred()) {

  153.                     // some events handler has triggered changes that
  154.                     // invalidate the derivatives, we need to restart from scratch
  155.                     start(equations, getStepStart(), finalTime);

  156.                     final T  nextT      = getStepStart().getTime().add(getStepSize());
  157.                     final boolean nextIsLast = forward ?
  158.                                                nextT.subtract(finalTime).getReal() >= 0 :
  159.                                                nextT.subtract(finalTime).getReal() <= 0;
  160.                     final T hNew = nextIsLast ? finalTime.subtract(getStepStart().getTime()) : getStepSize();

  161.                     rescale(hNew);
  162.                     System.arraycopy(getStepStart().getCompleteState(), 0, y, 0, y.length);

  163.                 } else {

  164.                     // stepsize control for next step
  165.                     final double  factor     = computeStepGrowShrinkFactor(error);
  166.                     final T       scaledH    = getStepSize().multiply(factor);
  167.                     final T       nextT      = getStepStart().getTime().add(scaledH);
  168.                     final boolean nextIsLast = forward ?
  169.                                                nextT.subtract(finalTime).getReal() >= 0 :
  170.                                                nextT.subtract(finalTime).getReal() <= 0;
  171.                     T hNew = getStepSizeHelper().filterStep(scaledH, forward, nextIsLast);

  172.                     final T       filteredNextT      = getStepStart().getTime().add(hNew);
  173.                     final boolean filteredNextIsLast = forward ?
  174.                                                        filteredNextT.subtract(finalTime).getReal() >= 0 :
  175.                                                        filteredNextT.subtract(finalTime).getReal() <= 0;
  176.                     if (filteredNextIsLast) {
  177.                         hNew = finalTime.subtract(getStepStart().getTime());
  178.                     }

  179.                     rescale(hNew);
  180.                     System.arraycopy(predictedY, 0, y, 0, y.length);

  181.                 }

  182.                 stepEnd = AdamsFieldStateInterpolator.taylor(equations.getMapper(), getStepStart(),
  183.                                                              getStepStart().getTime().add(getStepSize()),
  184.                                                              getStepSize(), scaled, nordsieck);

  185.             }

  186.         } while (!isLastStep());

  187.         final FieldODEStateAndDerivative<T> finalState = getStepStart();
  188.         setStepStart(null);
  189.         setStepSize(null);
  190.         return finalState;

  191.     }

  192.     /** {@inheritDoc} */
  193.     @Override
  194.     protected Array2DRowFieldMatrix<T> initializeHighOrderDerivatives(final T h, final T[] t,
  195.                                                                       final T[][] y,
  196.                                                                       final T[][] yDot) {
  197.         return transformer.initializeHighOrderDerivatives(h, t, y, yDot);
  198.     }

  199.     /** Update the high order scaled derivatives for Adams integrators (phase 1).
  200.      * <p>The complete update of high order derivatives has a form similar to:
  201.      * \[
  202.      * r_{n+1} = (s_1(n) - s_1(n+1)) P^{-1} u + P^{-1} A P r_n
  203.      * \]
  204.      * this method computes the P<sup>-1</sup> A P r<sub>n</sub> part.</p>
  205.      * @param highOrder high order scaled derivatives
  206.      * (h<sup>2</sup>/2 y'', ... h<sup>k</sup>/k! y(k))
  207.      * @return updated high order derivatives
  208.      * @see #updateHighOrderDerivativesPhase2(CalculusFieldElement[], CalculusFieldElement[], Array2DRowFieldMatrix)
  209.      */
  210.     public Array2DRowFieldMatrix<T> updateHighOrderDerivativesPhase1(final Array2DRowFieldMatrix<T> highOrder) {
  211.         return transformer.updateHighOrderDerivativesPhase1(highOrder);
  212.     }

  213.     /** Update the high order scaled derivatives Adams integrators (phase 2).
  214.      * <p>The complete update of high order derivatives has a form similar to:
  215.      * \[
  216.      * r_{n+1} = (s_1(n) - s_1(n+1)) P^{-1} u + P^{-1} A P r_n
  217.      * \]
  218.      * this method computes the (s<sub>1</sub>(n) - s<sub>1</sub>(n+1)) P<sup>-1</sup> u part.</p>
  219.      * <p>Phase 1 of the update must already have been performed.</p>
  220.      * @param start first order scaled derivatives at step start
  221.      * @param end first order scaled derivatives at step end
  222.      * @param highOrder high order scaled derivatives, will be modified
  223.      * (h<sup>2</sup>/2 y'', ... h<sup>k</sup>/k! y(k))
  224.      * @see #updateHighOrderDerivativesPhase1(Array2DRowFieldMatrix)
  225.      */
  226.     public void updateHighOrderDerivativesPhase2(final T[] start, final T[] end,
  227.                                                  final Array2DRowFieldMatrix<T> highOrder) {
  228.         transformer.updateHighOrderDerivativesPhase2(start, end, highOrder);
  229.     }

  230.     /** Estimate error.
  231.      * @param previousState state vector at step start
  232.      * @param predictedTime time at step end
  233.      * @param predictedState predicted state vector at step end
  234.      * @param predictedScaled predicted value of the scaled derivatives at step end
  235.      * @param predictedNordsieck predicted value of the Nordsieck vector at step end
  236.      * @return estimated normalized local discretization error
  237.      * @since 2.0
  238.      */
  239.     protected abstract double errorEstimation(T[] previousState, T predictedTime,
  240.                                               T[] predictedState, T[] predictedScaled,
  241.                                               FieldMatrix<T> predictedNordsieck);

  242.     /** Finalize the step.
  243.      * @param stepSize step size used in the scaled and Nordsieck arrays
  244.      * @param predictedState predicted state at end of step
  245.      * @param predictedScaled predicted first scaled derivative
  246.      * @param predictedNordsieck predicted Nordsieck vector
  247.      * @param isForward integration direction indicator
  248.      * @param globalPreviousState start of the global step
  249.      * @param globalCurrentState end of the global step
  250.      * @param equationsMapper mapper for ODE equations primary and secondary components
  251.      * @return step interpolator
  252.      * @since 2.0
  253.      */
  254.     protected abstract AdamsFieldStateInterpolator<T> finalizeStep(T stepSize, T[] predictedState,
  255.                                                                    T[] predictedScaled, Array2DRowFieldMatrix<T> predictedNordsieck,
  256.                                                                    boolean isForward,
  257.                                                                    FieldODEStateAndDerivative<T> globalPreviousState,
  258.                                                                    FieldODEStateAndDerivative<T> globalCurrentState,
  259.                                                                    FieldEquationsMapper<T> equationsMapper);

  260. }