AdaptiveStepsizeIntegrator.java

  1. /*
  2.  * Licensed to the Hipparchus project 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 Hipparchus project 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. package org.hipparchus.ode.nonstiff;

  18. import org.hipparchus.exception.MathIllegalArgumentException;
  19. import org.hipparchus.exception.MathIllegalStateException;
  20. import org.hipparchus.ode.AbstractIntegrator;
  21. import org.hipparchus.ode.ODEState;
  22. import org.hipparchus.ode.ODEStateAndDerivative;
  23. import org.hipparchus.util.FastMath;

  24. /**
  25.  * This abstract class holds the common part of all adaptive
  26.  * stepsize integrators for Ordinary Differential Equations.
  27.  *
  28.  * <p>These algorithms perform integration with stepsize control, which
  29.  * means the user does not specify the integration step but rather a
  30.  * tolerance on error. The error threshold is computed as
  31.  * </p>
  32.  * <pre>
  33.  * threshold_i = absTol_i + relTol_i * max (abs (ym), abs (ym+1))
  34.  * </pre>
  35.  * <p>
  36.  * where absTol_i is the absolute tolerance for component i of the
  37.  * state vector and relTol_i is the relative tolerance for the same
  38.  * component. The user can also use only two scalar values absTol and
  39.  * relTol which will be used for all components.
  40.  * </p>
  41.  * <p>
  42.  * If the Ordinary Differential Equations is an {@link org.hipparchus.ode.ExpandableODE
  43.  * extended ODE} rather than a {@link
  44.  * org.hipparchus.ode.OrdinaryDifferentialEquation basic ODE}, then
  45.  * <em>only</em> the {@link org.hipparchus.ode.ExpandableODE#getPrimary() primary part}
  46.  * of the state vector is used for stepsize control, not the complete state vector.
  47.  * </p>
  48.  *
  49.  * <p>If the estimated error for ym+1 is such that</p>
  50.  * <pre>
  51.  * sqrt((sum (errEst_i / threshold_i)^2 ) / n) &lt; 1
  52.  * </pre>
  53.  *
  54.  * <p>(where n is the main set dimension) then the step is accepted,
  55.  * otherwise the step is rejected and a new attempt is made with a new
  56.  * stepsize.</p>
  57.  *
  58.  *
  59.  */

  60. public abstract class AdaptiveStepsizeIntegrator
  61.     extends AbstractIntegrator {

  62.     /** Helper for step size control. */
  63.     private StepsizeHelper stepsizeHelper;

  64.     /** Build an integrator with the given stepsize bounds.
  65.      * The default step handler does nothing.
  66.      * @param name name of the method
  67.      * @param minStep minimal step (sign is irrelevant, regardless of
  68.      * integration direction, forward or backward), the last step can
  69.      * be smaller than this
  70.      * @param maxStep maximal step (sign is irrelevant, regardless of
  71.      * integration direction, forward or backward), the last step can
  72.      * be smaller than this
  73.      * @param scalAbsoluteTolerance allowed absolute error
  74.      * @param scalRelativeTolerance allowed relative error
  75.      */
  76.     protected AdaptiveStepsizeIntegrator(final String name,
  77.                                       final double minStep, final double maxStep,
  78.                                       final double scalAbsoluteTolerance,
  79.                                       final double scalRelativeTolerance) {
  80.         super(name);
  81.         stepsizeHelper = new StepsizeHelper(minStep, maxStep, scalAbsoluteTolerance, scalRelativeTolerance);
  82.         resetInternalState();
  83.     }

  84.     /** Build an integrator with the given stepsize bounds.
  85.      * The default step handler does nothing.
  86.      * @param name name of the method
  87.      * @param minStep minimal step (sign is irrelevant, regardless of
  88.      * integration direction, forward or backward), the last step can
  89.      * be smaller than this
  90.      * @param maxStep maximal step (sign is irrelevant, regardless of
  91.      * integration direction, forward or backward), the last step can
  92.      * be smaller than this
  93.      * @param vecAbsoluteTolerance allowed absolute error
  94.      * @param vecRelativeTolerance allowed relative error
  95.      */
  96.     protected AdaptiveStepsizeIntegrator(final String name,
  97.                                       final double minStep, final double maxStep,
  98.                                       final double[] vecAbsoluteTolerance,
  99.                                       final double[] vecRelativeTolerance) {
  100.         super(name);
  101.         stepsizeHelper = new StepsizeHelper(minStep, maxStep, vecAbsoluteTolerance, vecRelativeTolerance);
  102.         resetInternalState();
  103.     }

  104.     /** Set the adaptive step size control parameters.
  105.      * <p>
  106.      * A side effect of this method is to also reset the initial
  107.      * step so it will be automatically computed by the integrator
  108.      * if {@link #setInitialStepSize(double) setInitialStepSize}
  109.      * is not called by the user.
  110.      * </p>
  111.      * @param minimalStep minimal step (must be positive even for backward
  112.      * integration), the last step can be smaller than this
  113.      * @param maximalStep maximal step (must be positive even for backward
  114.      * integration)
  115.      * @param absoluteTolerance allowed absolute error
  116.      * @param relativeTolerance allowed relative error
  117.      */
  118.     public void setStepSizeControl(final double minimalStep, final double maximalStep,
  119.                                    final double absoluteTolerance,
  120.                                    final double relativeTolerance) {
  121.         stepsizeHelper = new StepsizeHelper(minimalStep, maximalStep, absoluteTolerance, relativeTolerance);
  122.     }

  123.     /** Set the adaptive step size control parameters.
  124.      * <p>
  125.      * A side effect of this method is to also reset the initial
  126.      * step so it will be automatically computed by the integrator
  127.      * if {@link #setInitialStepSize(double) setInitialStepSize}
  128.      * is not called by the user.
  129.      * </p>
  130.      * @param minimalStep minimal step (must be positive even for backward
  131.      * integration), the last step can be smaller than this
  132.      * @param maximalStep maximal step (must be positive even for backward
  133.      * integration)
  134.      * @param absoluteTolerance allowed absolute error
  135.      * @param relativeTolerance allowed relative error
  136.      */
  137.     public void setStepSizeControl(final double minimalStep, final double maximalStep,
  138.                                    final double[] absoluteTolerance,
  139.                                    final double[] relativeTolerance) {
  140.         stepsizeHelper = new StepsizeHelper(minimalStep, maximalStep, absoluteTolerance, relativeTolerance);
  141.     }

  142.     /** Get the stepsize helper.
  143.      * @return stepsize helper
  144.      * @since 2.0
  145.      */
  146.     protected StepsizeHelper getStepSizeHelper() {
  147.         return stepsizeHelper;
  148.     }

  149.     /** Set the initial step size.
  150.      * <p>This method allows the user to specify an initial positive
  151.      * step size instead of letting the integrator guess it by
  152.      * itself. If this method is not called before integration is
  153.      * started, the initial step size will be estimated by the
  154.      * integrator.</p>
  155.      * @param initialStepSize initial step size to use (must be positive even
  156.      * for backward integration ; providing a negative value or a value
  157.      * outside of the min/max step interval will lead the integrator to
  158.      * ignore the value and compute the initial step size by itself)
  159.      */
  160.     public void setInitialStepSize(final double initialStepSize) {
  161.         stepsizeHelper.setInitialStepSize(initialStepSize);
  162.     }

  163.     /** {@inheritDoc} */
  164.     @Override
  165.     protected void sanityChecks(final ODEState initialState, final double t)
  166.                     throws MathIllegalArgumentException {
  167.         super.sanityChecks(initialState, t);
  168.         stepsizeHelper.setMainSetDimension(initialState.getPrimaryStateDimension());
  169.     }

  170.     /** Initialize the integration step.
  171.      * @param forward forward integration indicator
  172.      * @param order order of the method
  173.      * @param scale scaling vector for the state vector (can be shorter than state vector)
  174.      * @param state0 state at integration start time
  175.      * @return first integration step
  176.      * @exception MathIllegalStateException if the number of functions evaluations is exceeded
  177.      * @exception MathIllegalArgumentException if arrays dimensions do not match equations settings
  178.      */
  179.     public double initializeStep(final boolean forward, final int order, final double[] scale,
  180.                                  final ODEStateAndDerivative state0)
  181.         throws MathIllegalArgumentException, MathIllegalStateException {

  182.         if (stepsizeHelper.getInitialStep() > 0) {
  183.             // use the user provided value
  184.             return forward ? stepsizeHelper.getInitialStep() : -stepsizeHelper.getInitialStep();
  185.         }

  186.         // very rough first guess : h = 0.01 * ||y/scale|| / ||y'/scale||
  187.         // this guess will be used to perform an Euler step
  188.         final double[] y0    = state0.getCompleteState();
  189.         final double[] yDot0 = state0.getCompleteDerivative();
  190.         double yOnScale2 = 0;
  191.         double yDotOnScale2 = 0;
  192.         for (int j = 0; j < scale.length; ++j) {
  193.             final double ratio    = y0[j] / scale[j];
  194.             yOnScale2            += ratio * ratio;
  195.             final double ratioDot = yDot0[j] / scale[j];
  196.             yDotOnScale2         += ratioDot * ratioDot;
  197.         }

  198.         double h = ((yOnScale2 < 1.0e-10) || (yDotOnScale2 < 1.0e-10)) ?
  199.                    1.0e-6 : (0.01 * FastMath.sqrt(yOnScale2 / yDotOnScale2));
  200.         if (h > getMaxStep()) {
  201.             h = getMaxStep();
  202.         }
  203.         if (! forward) {
  204.             h = -h;
  205.         }

  206.         // perform an Euler step using the preceding rough guess
  207.         final double[] y1 = new double[y0.length];
  208.         for (int j = 0; j < y0.length; ++j) {
  209.             y1[j] = y0[j] + h * yDot0[j];
  210.         }
  211.         final double[] yDot1 = computeDerivatives(state0.getTime() + h, y1);

  212.         // estimate the second derivative of the solution
  213.         double yDDotOnScale = 0;
  214.         for (int j = 0; j < scale.length; ++j) {
  215.             final double ratioDotDot = (yDot1[j] - yDot0[j]) / scale[j];
  216.             yDDotOnScale += ratioDotDot * ratioDotDot;
  217.         }
  218.         yDDotOnScale = FastMath.sqrt(yDDotOnScale) / h;

  219.         // step size is computed such that
  220.         // h^order * max (||y'/tol||, ||y''/tol||) = 0.01
  221.         final double maxInv2 = FastMath.max(FastMath.sqrt(yDotOnScale2), yDDotOnScale);
  222.         final double h1 = (maxInv2 < 1.0e-15) ?
  223.                            FastMath.max(1.0e-6, 0.001 * FastMath.abs(h)) :
  224.                            FastMath.pow(0.01 / maxInv2, 1.0 / order);
  225.         h = FastMath.min(100.0 * FastMath.abs(h), h1);
  226.         h = FastMath.max(h, 1.0e-12 * FastMath.abs(state0.getTime()));  // avoids cancellation when computing t1 - t0
  227.         if (h < getMinStep()) {
  228.             h = getMinStep();
  229.         }
  230.         if (h > getMaxStep()) {
  231.             h = getMaxStep();
  232.         }
  233.         if (! forward) {
  234.             h = -h;
  235.         }

  236.         return h;

  237.     }

  238.     /** Reset internal state to dummy values. */
  239.     protected void resetInternalState() {
  240.         setStepStart(null);
  241.         setStepSize(stepsizeHelper.getDummyStepsize());
  242.     }

  243.     /** Get the minimal step.
  244.      * @return minimal step
  245.      */
  246.     public double getMinStep() {
  247.         return stepsizeHelper.getMinStep();
  248.     }

  249.     /** Get the maximal step.
  250.      * @return maximal step
  251.      */
  252.     public double getMaxStep() {
  253.         return stepsizeHelper.getMaxStep();
  254.     }

  255. }