AbstractStepInterpolator.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 java.io.IOException;
  19. import java.io.ObjectInput;
  20. import java.io.ObjectOutput;

  21. import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
  22. import org.apache.commons.math4.legacy.ode.EquationsMapper;

  23. /** This abstract class represents an interpolator over the last step
  24.  * during an ODE integration.
  25.  *
  26.  * <p>The various ODE integrators provide objects extending this class
  27.  * to the step handlers. The handlers can use these objects to
  28.  * retrieve the state vector at intermediate times between the
  29.  * previous and the current grid points (dense output).</p>
  30.  *
  31.  * @see org.apache.commons.math4.legacy.ode.FirstOrderIntegrator
  32.  * @see org.apache.commons.math4.legacy.ode.SecondOrderIntegrator
  33.  * @see StepHandler
  34.  *
  35.  * @since 1.2
  36.  *
  37.  */

  38. public abstract class AbstractStepInterpolator
  39.   implements StepInterpolator {

  40.   /** current time step. */
  41.   protected double h;

  42.   /** current state. */
  43.   protected double[] currentState;

  44.   /** interpolated time. */
  45.   protected double interpolatedTime;

  46.   /** interpolated state. */
  47.   protected double[] interpolatedState;

  48.   /** interpolated derivatives. */
  49.   protected double[] interpolatedDerivatives;

  50.   /** interpolated primary state. */
  51.   protected double[] interpolatedPrimaryState;

  52.   /** interpolated primary derivatives. */
  53.   protected double[] interpolatedPrimaryDerivatives;

  54.   /** interpolated secondary state. */
  55.   protected double[][] interpolatedSecondaryState;

  56.   /** interpolated secondary derivatives. */
  57.   protected double[][] interpolatedSecondaryDerivatives;

  58.   /** global previous time. */
  59.   private double globalPreviousTime;

  60.   /** global current time. */
  61.   private double globalCurrentTime;

  62.   /** soft previous time. */
  63.   private double softPreviousTime;

  64.   /** soft current time. */
  65.   private double softCurrentTime;

  66.   /** indicate if the step has been finalized or not. */
  67.   private boolean finalized;

  68.   /** integration direction. */
  69.   private boolean forward;

  70.   /** indicator for dirty state. */
  71.   private boolean dirtyState;

  72.   /** Equations mapper for the primary equations set. */
  73.   private EquationsMapper primaryMapper;

  74.   /** Equations mappers for the secondary equations sets. */
  75.   private EquationsMapper[] secondaryMappers;

  76.   /** Simple constructor.
  77.    * This constructor builds an instance that is not usable yet, the
  78.    * {@link #reinitialize} method should be called before using the
  79.    * instance in order to initialize the internal arrays. This
  80.    * constructor is used only in order to delay the initialization in
  81.    * some cases. As an example, the {@link
  82.    * org.apache.commons.math4.legacy.ode.nonstiff.EmbeddedRungeKuttaIntegrator}
  83.    * class uses the prototyping design pattern to create the step
  84.    * interpolators by cloning an uninitialized model and latter
  85.    * initializing the copy.
  86.    */
  87.   protected AbstractStepInterpolator() {
  88.     globalPreviousTime = Double.NaN;
  89.     globalCurrentTime  = Double.NaN;
  90.     softPreviousTime   = Double.NaN;
  91.     softCurrentTime    = Double.NaN;
  92.     h                  = Double.NaN;
  93.     interpolatedTime   = Double.NaN;
  94.     currentState       = null;
  95.     finalized          = false;
  96.     this.forward       = true;
  97.     this.dirtyState    = true;
  98.     primaryMapper      = null;
  99.     secondaryMappers   = null;
  100.     allocateInterpolatedArrays(-1);
  101.   }

  102.   /** Simple constructor.
  103.    * @param y reference to the integrator array holding the state at
  104.    * the end of the step
  105.    * @param forward integration direction indicator
  106.    * @param primaryMapper equations mapper for the primary equations set
  107.    * @param secondaryMappers equations mappers for the secondary equations sets
  108.    */
  109.   protected AbstractStepInterpolator(final double[] y, final boolean forward,
  110.                                      final EquationsMapper primaryMapper,
  111.                                      final EquationsMapper[] secondaryMappers) {

  112.     globalPreviousTime    = Double.NaN;
  113.     globalCurrentTime     = Double.NaN;
  114.     softPreviousTime      = Double.NaN;
  115.     softCurrentTime       = Double.NaN;
  116.     h                     = Double.NaN;
  117.     interpolatedTime      = Double.NaN;
  118.     currentState          = y;
  119.     finalized             = false;
  120.     this.forward          = forward;
  121.     this.dirtyState       = true;
  122.     this.primaryMapper    = primaryMapper;
  123.     this.secondaryMappers = (secondaryMappers == null) ? null : secondaryMappers.clone();
  124.     allocateInterpolatedArrays(y.length);
  125.   }

  126.   /** Copy constructor.

  127.    * <p>The copied interpolator should have been finalized before the
  128.    * copy, otherwise the copy will not be able to perform correctly
  129.    * any derivative computation and will throw a {@link
  130.    * NullPointerException} later. Since we don't want this constructor
  131.    * to throw the exceptions finalization may involve and since we
  132.    * don't want this method to modify the state of the copied
  133.    * interpolator, finalization is <strong>not</strong> done
  134.    * automatically, it remains under user control.</p>

  135.    * <p>The copy is a deep copy: its arrays are separated from the
  136.    * original arrays of the instance.</p>

  137.    * @param interpolator interpolator to copy from.

  138.    */
  139.   protected AbstractStepInterpolator(final AbstractStepInterpolator interpolator) {

  140.     globalPreviousTime = interpolator.globalPreviousTime;
  141.     globalCurrentTime  = interpolator.globalCurrentTime;
  142.     softPreviousTime   = interpolator.softPreviousTime;
  143.     softCurrentTime    = interpolator.softCurrentTime;
  144.     h                  = interpolator.h;
  145.     interpolatedTime   = interpolator.interpolatedTime;

  146.     if (interpolator.currentState == null) {
  147.         currentState     = null;
  148.         primaryMapper    = null;
  149.         secondaryMappers = null;
  150.         allocateInterpolatedArrays(-1);
  151.     } else {
  152.       currentState                     = interpolator.currentState.clone();
  153.       interpolatedState                = interpolator.interpolatedState.clone();
  154.       interpolatedDerivatives          = interpolator.interpolatedDerivatives.clone();
  155.       interpolatedPrimaryState         = interpolator.interpolatedPrimaryState.clone();
  156.       interpolatedPrimaryDerivatives   = interpolator.interpolatedPrimaryDerivatives.clone();
  157.       interpolatedSecondaryState       = new double[interpolator.interpolatedSecondaryState.length][];
  158.       interpolatedSecondaryDerivatives = new double[interpolator.interpolatedSecondaryDerivatives.length][];
  159.       for (int i = 0; i < interpolatedSecondaryState.length; ++i) {
  160.           interpolatedSecondaryState[i]       = interpolator.interpolatedSecondaryState[i].clone();
  161.           interpolatedSecondaryDerivatives[i] = interpolator.interpolatedSecondaryDerivatives[i].clone();
  162.       }
  163.     }

  164.     finalized        = interpolator.finalized;
  165.     forward          = interpolator.forward;
  166.     dirtyState       = interpolator.dirtyState;
  167.     primaryMapper    = interpolator.primaryMapper;
  168.     secondaryMappers = (interpolator.secondaryMappers == null) ?
  169.                        null : interpolator.secondaryMappers.clone();
  170.   }

  171.   /** Allocate the various interpolated states arrays.
  172.    * @param dimension total dimension (negative if arrays should be set to null)
  173.    */
  174.   private void allocateInterpolatedArrays(final int dimension) {
  175.       if (dimension < 0) {
  176.           interpolatedState                = null;
  177.           interpolatedDerivatives          = null;
  178.           interpolatedPrimaryState         = null;
  179.           interpolatedPrimaryDerivatives   = null;
  180.           interpolatedSecondaryState       = null;
  181.           interpolatedSecondaryDerivatives = null;
  182.       } else {
  183.           interpolatedState                = new double[dimension];
  184.           interpolatedDerivatives          = new double[dimension];
  185.           interpolatedPrimaryState         = new double[primaryMapper.getDimension()];
  186.           interpolatedPrimaryDerivatives   = new double[primaryMapper.getDimension()];
  187.           if (secondaryMappers == null) {
  188.               interpolatedSecondaryState       = null;
  189.               interpolatedSecondaryDerivatives = null;
  190.           } else {
  191.               interpolatedSecondaryState       = new double[secondaryMappers.length][];
  192.               interpolatedSecondaryDerivatives = new double[secondaryMappers.length][];
  193.               for (int i = 0; i < secondaryMappers.length; ++i) {
  194.                   interpolatedSecondaryState[i]       = new double[secondaryMappers[i].getDimension()];
  195.                   interpolatedSecondaryDerivatives[i] = new double[secondaryMappers[i].getDimension()];
  196.               }
  197.           }
  198.       }
  199.   }

  200.   /** Reinitialize the instance.
  201.    * @param y reference to the integrator array holding the state at the end of the step
  202.    * @param isForward integration direction indicator
  203.    * @param primary equations mapper for the primary equations set
  204.    * @param secondary equations mappers for the secondary equations sets
  205.    */
  206.   protected void reinitialize(final double[] y, final boolean isForward,
  207.                               final EquationsMapper primary,
  208.                               final EquationsMapper[] secondary) {

  209.     globalPreviousTime    = Double.NaN;
  210.     globalCurrentTime     = Double.NaN;
  211.     softPreviousTime      = Double.NaN;
  212.     softCurrentTime       = Double.NaN;
  213.     h                     = Double.NaN;
  214.     interpolatedTime      = Double.NaN;
  215.     currentState          = y;
  216.     finalized             = false;
  217.     this.forward          = isForward;
  218.     this.dirtyState       = true;
  219.     this.primaryMapper    = primary;
  220.     this.secondaryMappers = secondary.clone();
  221.     allocateInterpolatedArrays(y.length);
  222.   }

  223.   /** {@inheritDoc} */
  224.   @Override
  225.    public StepInterpolator copy() throws MaxCountExceededException {

  226.      // finalize the step before performing copy
  227.      finalizeStep();

  228.      // create the new independent instance
  229.      return doCopy();
  230.    }

  231.    /** Really copy the finalized instance.
  232.     * <p>This method is called by {@link #copy()} after the
  233.     * step has been finalized. It must perform a deep copy
  234.     * to have an new instance completely independent for the
  235.     * original instance.
  236.     * @return a copy of the finalized instance
  237.     */
  238.    protected abstract StepInterpolator doCopy();

  239.   /** Shift one step forward.
  240.    * Copy the current time into the previous time, hence preparing the
  241.    * interpolator for future calls to {@link #storeTime storeTime}
  242.    */
  243.   public void shift() {
  244.     globalPreviousTime = globalCurrentTime;
  245.     softPreviousTime   = globalPreviousTime;
  246.     softCurrentTime    = globalCurrentTime;
  247.   }

  248.   /** Store the current step time.
  249.    * @param t current time
  250.    */
  251.   public void storeTime(final double t) {

  252.     globalCurrentTime = t;
  253.     softCurrentTime   = globalCurrentTime;
  254.     h                 = globalCurrentTime - globalPreviousTime;
  255.     setInterpolatedTime(t);

  256.     // the step is not finalized anymore
  257.     finalized  = false;
  258.   }

  259.   /** Restrict step range to a limited part of the global step.
  260.    * <p>
  261.    * This method can be used to restrict a step and make it appear
  262.    * as if the original step was smaller. Calling this method
  263.    * <em>only</em> changes the value returned by {@link #getPreviousTime()},
  264.    * it does not change any other property
  265.    * </p>
  266.    * @param softPreviousTime start of the restricted step
  267.    * @since 2.2
  268.    */
  269.   public void setSoftPreviousTime(final double softPreviousTime) {
  270.       this.softPreviousTime = softPreviousTime;
  271.   }

  272.   /** Restrict step range to a limited part of the global step.
  273.    * <p>
  274.    * This method can be used to restrict a step and make it appear
  275.    * as if the original step was smaller. Calling this method
  276.    * <em>only</em> changes the value returned by {@link #getCurrentTime()},
  277.    * it does not change any other property
  278.    * </p>
  279.    * @param softCurrentTime end of the restricted step
  280.    * @since 2.2
  281.    */
  282.   public void setSoftCurrentTime(final double softCurrentTime) {
  283.       this.softCurrentTime  = softCurrentTime;
  284.   }

  285.   /**
  286.    * Get the previous global grid point time.
  287.    * @return previous global grid point time
  288.    */
  289.   public double getGlobalPreviousTime() {
  290.     return globalPreviousTime;
  291.   }

  292.   /**
  293.    * Get the current global grid point time.
  294.    * @return current global grid point time
  295.    */
  296.   public double getGlobalCurrentTime() {
  297.     return globalCurrentTime;
  298.   }

  299.   /**
  300.    * Get the previous soft grid point time.
  301.    * @return previous soft grid point time
  302.    * @see #setSoftPreviousTime(double)
  303.    */
  304.   @Override
  305. public double getPreviousTime() {
  306.     return softPreviousTime;
  307.   }

  308.   /**
  309.    * Get the current soft grid point time.
  310.    * @return current soft grid point time
  311.    * @see #setSoftCurrentTime(double)
  312.    */
  313.   @Override
  314. public double getCurrentTime() {
  315.     return softCurrentTime;
  316.   }

  317.   /** {@inheritDoc} */
  318.   @Override
  319.   public double getInterpolatedTime() {
  320.     return interpolatedTime;
  321.   }

  322.   /** {@inheritDoc} */
  323.   @Override
  324.   public void setInterpolatedTime(final double time) {
  325.       interpolatedTime = time;
  326.       dirtyState       = true;
  327.   }

  328.   /** {@inheritDoc} */
  329.   @Override
  330.   public boolean isForward() {
  331.     return forward;
  332.   }

  333.   /** Compute the state and derivatives at the interpolated time.
  334.    * This is the main processing method that should be implemented by
  335.    * the derived classes to perform the interpolation.
  336.    * @param theta normalized interpolation abscissa within the step
  337.    * (theta is zero at the previous time step and one at the current time step)
  338.    * @param oneMinusThetaH time gap between the interpolated time and
  339.    * the current time
  340.    * @exception MaxCountExceededException if the number of functions evaluations is exceeded
  341.    */
  342.   protected abstract void computeInterpolatedStateAndDerivatives(double theta,
  343.                                                                  double oneMinusThetaH)
  344.       throws MaxCountExceededException;

  345.   /** Lazy evaluation of complete interpolated state.
  346.    * @exception MaxCountExceededException if the number of functions evaluations is exceeded
  347.    */
  348.   private void evaluateCompleteInterpolatedState()
  349.       throws MaxCountExceededException {
  350.       // lazy evaluation of the state
  351.       if (dirtyState) {
  352.           final double oneMinusThetaH = globalCurrentTime - interpolatedTime;
  353.           final double theta = (h == 0) ? 0 : (h - oneMinusThetaH) / h;
  354.           computeInterpolatedStateAndDerivatives(theta, oneMinusThetaH);
  355.           dirtyState = false;
  356.       }
  357.   }

  358.   /** {@inheritDoc} */
  359.   @Override
  360.   public double[] getInterpolatedState() throws MaxCountExceededException {
  361.       evaluateCompleteInterpolatedState();
  362.       primaryMapper.extractEquationData(interpolatedState,
  363.                                         interpolatedPrimaryState);
  364.       return interpolatedPrimaryState;
  365.   }

  366.   /** {@inheritDoc} */
  367.   @Override
  368.   public double[] getInterpolatedDerivatives() throws MaxCountExceededException {
  369.       evaluateCompleteInterpolatedState();
  370.       primaryMapper.extractEquationData(interpolatedDerivatives,
  371.                                         interpolatedPrimaryDerivatives);
  372.       return interpolatedPrimaryDerivatives;
  373.   }

  374.   /** {@inheritDoc} */
  375.   @Override
  376.   public double[] getInterpolatedSecondaryState(final int index) throws MaxCountExceededException {
  377.       evaluateCompleteInterpolatedState();
  378.       secondaryMappers[index].extractEquationData(interpolatedState,
  379.                                                   interpolatedSecondaryState[index]);
  380.       return interpolatedSecondaryState[index];
  381.   }

  382.   /** {@inheritDoc} */
  383.   @Override
  384.   public double[] getInterpolatedSecondaryDerivatives(final int index) throws MaxCountExceededException {
  385.       evaluateCompleteInterpolatedState();
  386.       secondaryMappers[index].extractEquationData(interpolatedDerivatives,
  387.                                                   interpolatedSecondaryDerivatives[index]);
  388.       return interpolatedSecondaryDerivatives[index];
  389.   }

  390.   /**
  391.    * Finalize the step.

  392.    * <p>Some embedded Runge-Kutta integrators need fewer functions
  393.    * evaluations than their counterpart step interpolators. These
  394.    * interpolators should perform the last evaluations they need by
  395.    * themselves only if they need them. This method triggers these
  396.    * extra evaluations. It can be called directly by the user step
  397.    * handler and it is called automatically if {@link
  398.    * #setInterpolatedTime} is called.</p>

  399.    * <p>Once this method has been called, <strong>no</strong> other
  400.    * evaluation will be performed on this step. If there is a need to
  401.    * have some side effects between the step handler and the
  402.    * differential equations (for example update some data in the
  403.    * equations once the step has been done), it is advised to call
  404.    * this method explicitly from the step handler before these side
  405.    * effects are set up. If the step handler induces no side effect,
  406.    * then this method can safely be ignored, it will be called
  407.    * transparently as needed.</p>

  408.    * <p><strong>Warning</strong>: since the step interpolator provided
  409.    * to the step handler as a parameter of the {@link
  410.    * StepHandler#handleStep handleStep} is valid only for the duration
  411.    * of the {@link StepHandler#handleStep handleStep} call, one cannot
  412.    * simply store a reference and reuse it later. One should first
  413.    * finalize the instance, then copy this finalized instance into a
  414.    * new object that can be kept.</p>

  415.    * <p>This method calls the protected <code>doFinalize</code> method
  416.    * if it has never been called during this step and set a flag
  417.    * indicating that it has been called once. It is the <code>
  418.    * doFinalize</code> method which should perform the evaluations.
  419.    * This wrapping prevents from calling <code>doFinalize</code> several
  420.    * times and hence evaluating the differential equations too often.
  421.    * Therefore, subclasses are not allowed not reimplement it, they
  422.    * should rather reimplement <code>doFinalize</code>.</p>

  423.    * @exception MaxCountExceededException if the number of functions evaluations is exceeded

  424.    */
  425.   public final void finalizeStep() throws MaxCountExceededException {
  426.     if (! finalized) {
  427.       doFinalize();
  428.       finalized = true;
  429.     }
  430.   }

  431.   /**
  432.    * Really finalize the step.
  433.    * The default implementation of this method does nothing.
  434.    * @exception MaxCountExceededException if the number of functions evaluations is exceeded
  435.    */
  436.   protected void doFinalize() throws MaxCountExceededException {
  437.   }

  438.   /** {@inheritDoc} */
  439.   @Override
  440.   public abstract void writeExternal(ObjectOutput out)
  441.     throws IOException;

  442.   /** {@inheritDoc} */
  443.   @Override
  444.   public abstract void readExternal(ObjectInput in)
  445.     throws IOException, ClassNotFoundException;

  446.   /** Save the base state of the instance.
  447.    * This method performs step finalization if it has not been done
  448.    * before.
  449.    * @param out stream where to save the state
  450.    * @exception IOException in case of write error
  451.    */
  452.   protected void writeBaseExternal(final ObjectOutput out)
  453.     throws IOException {

  454.     if (currentState == null) {
  455.         out.writeInt(-1);
  456.     } else {
  457.         out.writeInt(currentState.length);
  458.     }
  459.     out.writeDouble(globalPreviousTime);
  460.     out.writeDouble(globalCurrentTime);
  461.     out.writeDouble(softPreviousTime);
  462.     out.writeDouble(softCurrentTime);
  463.     out.writeDouble(h);
  464.     out.writeBoolean(forward);
  465.     out.writeObject(primaryMapper);
  466.     out.write(secondaryMappers.length);
  467.     for (final EquationsMapper  mapper : secondaryMappers) {
  468.         out.writeObject(mapper);
  469.     }

  470.     if (currentState != null) {
  471.         for (int i = 0; i < currentState.length; ++i) {
  472.             out.writeDouble(currentState[i]);
  473.         }
  474.     }

  475.     out.writeDouble(interpolatedTime);

  476.     // we do not store the interpolated state,
  477.     // it will be recomputed as needed after reading

  478.     try {
  479.         // finalize the step (and don't bother saving the now true flag)
  480.         finalizeStep();
  481.     } catch (MaxCountExceededException mcee) {
  482.         final IOException ioe = new IOException(mcee.getLocalizedMessage());
  483.         ioe.initCause(mcee);
  484.         throw ioe;
  485.     }
  486.   }

  487.   /** Read the base state of the instance.
  488.    * This method does <strong>neither</strong> set the interpolated
  489.    * time nor state. It is up to the derived class to reset it
  490.    * properly calling the {@link #setInterpolatedTime} method later,
  491.    * once all rest of the object state has been set up properly.
  492.    * @param in stream where to read the state from
  493.    * @return interpolated time to be set later by the caller
  494.    * @exception IOException in case of read error
  495.    * @exception ClassNotFoundException if an equation mapper class
  496.    * cannot be found
  497.    */
  498.   protected double readBaseExternal(final ObjectInput in)
  499.     throws IOException, ClassNotFoundException {

  500.     final int dimension = in.readInt();
  501.     globalPreviousTime  = in.readDouble();
  502.     globalCurrentTime   = in.readDouble();
  503.     softPreviousTime    = in.readDouble();
  504.     softCurrentTime     = in.readDouble();
  505.     h                   = in.readDouble();
  506.     forward             = in.readBoolean();
  507.     primaryMapper       = (EquationsMapper) in.readObject();
  508.     secondaryMappers    = new EquationsMapper[in.read()];
  509.     for (int i = 0; i < secondaryMappers.length; ++i) {
  510.         secondaryMappers[i] = (EquationsMapper) in.readObject();
  511.     }
  512.     dirtyState          = true;

  513.     if (dimension < 0) {
  514.         currentState = null;
  515.     } else {
  516.         currentState  = new double[dimension];
  517.         for (int i = 0; i < currentState.length; ++i) {
  518.             currentState[i] = in.readDouble();
  519.         }
  520.     }

  521.     // we do NOT handle the interpolated time and state here
  522.     interpolatedTime = Double.NaN;
  523.     allocateInterpolatedArrays(dimension);

  524.     finalized = true;

  525.     return in.readDouble();
  526.   }
  527. }