StepNormalizer.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.  *      http://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.apache.commons.math4.legacy.ode.sampling;

  18. import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
  19. import org.apache.commons.math4.core.jdkmath.JdkMath;
  20. import org.apache.commons.numbers.core.Precision;

  21. /**
  22.  * This class wraps an object implementing {@link FixedStepHandler}
  23.  * into a {@link StepHandler}.

  24.  * <p>This wrapper allows to use fixed step handlers with general
  25.  * integrators which cannot guaranty their integration steps will
  26.  * remain constant and therefore only accept general step
  27.  * handlers.</p>
  28.  *
  29.  * <p>The stepsize used is selected at construction time. The {@link
  30.  * FixedStepHandler#handleStep handleStep} method of the underlying
  31.  * {@link FixedStepHandler} object is called at normalized times. The
  32.  * normalized times can be influenced by the {@link StepNormalizerMode} and
  33.  * {@link StepNormalizerBounds}.</p>
  34.  *
  35.  * <p>There is no constraint on the integrator, it can use any time step
  36.  * it needs (time steps longer or shorter than the fixed time step and
  37.  * non-integer ratios are all allowed).</p>
  38.  *
  39.  * <table border="1" style="text-align: center">
  40.  * <caption>Examples (step size = 0.5)</caption>
  41.  * <tr style="background-color: #CCCCFF"><td style="font-size: x-large">Examples (step size = 0.5)</td></tr>
  42.  * <tr style="background-color: #EEEEFF; font-size: large"><td>Start time</td><td>End time</td>
  43.  *  <td>Direction</td><td>{@link StepNormalizerMode Mode}</td>
  44.  *  <td>{@link StepNormalizerBounds Bounds}</td><td>Output</td></tr>
  45.  * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>0.8, 1.3, 1.8, 2.3, 2.8</td></tr>
  46.  * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>0.3, 0.8, 1.3, 1.8, 2.3, 2.8</td></tr>
  47.  * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>0.8, 1.3, 1.8, 2.3, 2.8, 3.1</td></tr>
  48.  * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>0.3, 0.8, 1.3, 1.8, 2.3, 2.8, 3.1</td></tr>
  49.  * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
  50.  * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>0.3, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
  51.  * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.1</td></tr>
  52.  * <tr><td>0.3</td><td>3.1</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>0.3, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.1</td></tr>
  53.  * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
  54.  * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
  55.  * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
  56.  * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
  57.  * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
  58.  * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
  59.  * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
  60.  * <tr><td>0.0</td><td>3.0</td><td>forward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0</td></tr>
  61.  * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>2.6, 2.1, 1.6, 1.1, 0.6</td></tr>
  62.  * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>3.1, 2.6, 2.1, 1.6, 1.1, 0.6</td></tr>
  63.  * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>2.6, 2.1, 1.6, 1.1, 0.6, 0.3</td></tr>
  64.  * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>3.1, 2.6, 2.1, 1.6, 1.1, 0.6, 0.3</td></tr>
  65.  * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5</td></tr>
  66.  * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>3.1, 3.0, 2.5, 2.0, 1.5, 1.0, 0.5</td></tr>
  67.  * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.3</td></tr>
  68.  * <tr><td>3.1</td><td>0.3</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>3.1, 3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.3</td></tr>
  69.  * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
  70.  * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
  71.  * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
  72.  * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#INCREMENT INCREMENT}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
  73.  * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#NEITHER NEITHER}</td><td>2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
  74.  * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#FIRST FIRST}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
  75.  * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#LAST LAST}</td><td>2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
  76.  * <tr><td>3.0</td><td>0.0</td><td>backward</td><td>{@link StepNormalizerMode#MULTIPLES MULTIPLES}</td><td>{@link StepNormalizerBounds#BOTH BOTH}</td><td>3.0, 2.5, 2.0, 1.5, 1.0, 0.5, 0.0</td></tr>
  77.  * </table>
  78.  *
  79.  * @see StepHandler
  80.  * @see FixedStepHandler
  81.  * @see StepNormalizerMode
  82.  * @see StepNormalizerBounds
  83.  * @since 1.2
  84.  */

  85. public class StepNormalizer implements StepHandler {
  86.     /** Fixed time step. */
  87.     private double h;

  88.     /** Underlying step handler. */
  89.     private final FixedStepHandler handler;

  90.     /** First step time. */
  91.     private double firstTime;

  92.     /** Last step time. */
  93.     private double lastTime;

  94.     /** Last state vector. */
  95.     private double[] lastState;

  96.     /** Last derivatives vector. */
  97.     private double[] lastDerivatives;

  98.     /** Integration direction indicator. */
  99.     private boolean forward;

  100.     /** The step normalizer bounds settings to use. */
  101.     private final StepNormalizerBounds bounds;

  102.     /** The step normalizer mode to use. */
  103.     private final StepNormalizerMode mode;

  104.     /** Simple constructor. Uses {@link StepNormalizerMode#INCREMENT INCREMENT}
  105.      * mode, and {@link StepNormalizerBounds#FIRST FIRST} bounds setting, for
  106.      * backwards compatibility.
  107.      * @param h fixed time step (sign is not used)
  108.      * @param handler fixed time step handler to wrap
  109.      */
  110.     public StepNormalizer(final double h, final FixedStepHandler handler) {
  111.         this(h, handler, StepNormalizerMode.INCREMENT,
  112.              StepNormalizerBounds.FIRST);
  113.     }

  114.     /** Simple constructor. Uses {@link StepNormalizerBounds#FIRST FIRST}
  115.      * bounds setting.
  116.      * @param h fixed time step (sign is not used)
  117.      * @param handler fixed time step handler to wrap
  118.      * @param mode step normalizer mode to use
  119.      * @since 3.0
  120.      */
  121.     public StepNormalizer(final double h, final FixedStepHandler handler,
  122.                           final StepNormalizerMode mode) {
  123.         this(h, handler, mode, StepNormalizerBounds.FIRST);
  124.     }

  125.     /** Simple constructor. Uses {@link StepNormalizerMode#INCREMENT INCREMENT}
  126.      * mode.
  127.      * @param h fixed time step (sign is not used)
  128.      * @param handler fixed time step handler to wrap
  129.      * @param bounds step normalizer bounds setting to use
  130.      * @since 3.0
  131.      */
  132.     public StepNormalizer(final double h, final FixedStepHandler handler,
  133.                           final StepNormalizerBounds bounds) {
  134.         this(h, handler, StepNormalizerMode.INCREMENT, bounds);
  135.     }

  136.     /** Simple constructor.
  137.      * @param h fixed time step (sign is not used)
  138.      * @param handler fixed time step handler to wrap
  139.      * @param mode step normalizer mode to use
  140.      * @param bounds step normalizer bounds setting to use
  141.      * @since 3.0
  142.      */
  143.     public StepNormalizer(final double h, final FixedStepHandler handler,
  144.                           final StepNormalizerMode mode,
  145.                           final StepNormalizerBounds bounds) {
  146.         this.h          = JdkMath.abs(h);
  147.         this.handler    = handler;
  148.         this.mode       = mode;
  149.         this.bounds     = bounds;
  150.         firstTime       = Double.NaN;
  151.         lastTime        = Double.NaN;
  152.         lastState       = null;
  153.         lastDerivatives = null;
  154.         forward         = true;
  155.     }

  156.     /** {@inheritDoc} */
  157.     @Override
  158.     public void init(double t0, double[] y0, double t) {

  159.         firstTime       = Double.NaN;
  160.         lastTime        = Double.NaN;
  161.         lastState       = null;
  162.         lastDerivatives = null;
  163.         forward         = true;

  164.         // initialize the underlying handler
  165.         handler.init(t0, y0, t);
  166.     }

  167.     /**
  168.      * Handle the last accepted step.
  169.      * @param interpolator interpolator for the last accepted step. For
  170.      * efficiency purposes, the various integrators reuse the same
  171.      * object on each call, so if the instance wants to keep it across
  172.      * all calls (for example to provide at the end of the integration a
  173.      * continuous model valid throughout the integration range), it
  174.      * should build a local copy using the clone method and store this
  175.      * copy.
  176.      * @param isLast true if the step is the last one
  177.      * @exception MaxCountExceededException if the interpolator throws one because
  178.      * the number of functions evaluations is exceeded
  179.      */
  180.     @Override
  181.     public void handleStep(final StepInterpolator interpolator, final boolean isLast)
  182.         throws MaxCountExceededException {
  183.         // The first time, update the last state with the start information.
  184.         if (lastState == null) {
  185.             firstTime = interpolator.getPreviousTime();
  186.             lastTime = interpolator.getPreviousTime();
  187.             interpolator.setInterpolatedTime(lastTime);
  188.             lastState = interpolator.getInterpolatedState().clone();
  189.             lastDerivatives = interpolator.getInterpolatedDerivatives().clone();

  190.             // Take the integration direction into account.
  191.             forward = interpolator.getCurrentTime() >= lastTime;
  192.             if (!forward) {
  193.                 h = -h;
  194.             }
  195.         }

  196.         // Calculate next normalized step time.
  197.         double nextTime = (mode == StepNormalizerMode.INCREMENT) ?
  198.                           lastTime + h :
  199.                           (JdkMath.floor(lastTime / h) + 1) * h;
  200.         if (mode == StepNormalizerMode.MULTIPLES &&
  201.             Precision.equals(nextTime, lastTime, 1)) {
  202.             nextTime += h;
  203.         }

  204.         // Process normalized steps as long as they are in the current step.
  205.         boolean nextInStep = isNextInStep(nextTime, interpolator);
  206.         while (nextInStep) {
  207.             // Output the stored previous step.
  208.             doNormalizedStep(false);

  209.             // Store the next step as last step.
  210.             storeStep(interpolator, nextTime);

  211.             // Move on to the next step.
  212.             nextTime += h;
  213.             nextInStep = isNextInStep(nextTime, interpolator);
  214.         }

  215.         if (isLast) {
  216.             // There will be no more steps. The stored one should be given to
  217.             // the handler. We may have to output one more step. Only the last
  218.             // one of those should be flagged as being the last.
  219.             boolean addLast = bounds.lastIncluded() &&
  220.                               lastTime != interpolator.getCurrentTime();
  221.             doNormalizedStep(!addLast);
  222.             if (addLast) {
  223.                 storeStep(interpolator, interpolator.getCurrentTime());
  224.                 doNormalizedStep(true);
  225.             }
  226.         }
  227.     }

  228.     /**
  229.      * Returns a value indicating whether the next normalized time is in the
  230.      * current step.
  231.      * @param nextTime the next normalized time
  232.      * @param interpolator interpolator for the last accepted step, to use to
  233.      * get the end time of the current step
  234.      * @return value indicating whether the next normalized time is in the
  235.      * current step
  236.      */
  237.     private boolean isNextInStep(double nextTime,
  238.                                  StepInterpolator interpolator) {
  239.         return forward ?
  240.                nextTime <= interpolator.getCurrentTime() :
  241.                nextTime >= interpolator.getCurrentTime();
  242.     }

  243.     /**
  244.      * Invokes the underlying step handler for the current normalized step.
  245.      * @param isLast true if the step is the last one
  246.      */
  247.     private void doNormalizedStep(boolean isLast) {
  248.         if (!bounds.firstIncluded() && firstTime == lastTime) {
  249.             return;
  250.         }
  251.         handler.handleStep(lastTime, lastState, lastDerivatives, isLast);
  252.     }

  253.     /** Stores the interpolated information for the given time in the current
  254.      * state.
  255.      * @param interpolator interpolator for the last accepted step, to use to
  256.      * get the interpolated information
  257.      * @param t the time for which to store the interpolated information
  258.      * @exception MaxCountExceededException if the interpolator throws one because
  259.      * the number of functions evaluations is exceeded
  260.      */
  261.     private void storeStep(StepInterpolator interpolator, double t)
  262.         throws MaxCountExceededException {
  263.         lastTime = t;
  264.         interpolator.setInterpolatedTime(lastTime);
  265.         System.arraycopy(interpolator.getInterpolatedState(), 0,
  266.                          lastState, 0, lastState.length);
  267.         System.arraycopy(interpolator.getInterpolatedDerivatives(), 0,
  268.                          lastDerivatives, 0, lastDerivatives.length);
  269.     }
  270. }