package org.spaceroots.mantissa.ode;

import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.IOException;

/** This abstract class represents an interpolator over the last step
 * during an ODE integration.
 *
 * 

The various ODE integrators provide objects extending this class * to the step handlers. The handlers can use these objects to * retrieve the state vector at intermediate times between the * previous and the current grid points (dense output).

* * @see FirstOrderIntegrator * @see SecondOrderIntegrator * @see StepHandler * * @version $Id: AbstractStepInterpolator.java 1709 2006-12-03 21:16:50Z luc $ * @author L. Maisonobe * */ public abstract class AbstractStepInterpolator implements StepInterpolator, Cloneable { /** previous time */ protected double previousTime; /** current time */ protected double currentTime; /** current time step */ protected double h; /** current state */ protected double[] currentState; /** interpolated time */ protected double interpolatedTime; /** interpolated state */ protected double[] interpolatedState; /** indicate if the step has been finalized or not. */ private boolean finalized; /** integration direction. */ private boolean forward; /** 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. As an example, the {@link * RungeKuttaFehlbergIntegrator} uses the prototyping design pattern * to create the step interpolators by cloning an uninitialized * model and latter initializing the copy. */ protected AbstractStepInterpolator() { previousTime = Double.NaN; currentTime = Double.NaN; h = Double.NaN; interpolatedTime = Double.NaN; currentState = null; interpolatedState = null; finalized = false; this.forward = true; } /** Simple constructor. * @param y reference to the integrator array holding the state at * the end of the step * @param forward integration direction indicator */ protected AbstractStepInterpolator(double[] y, boolean forward) { previousTime = Double.NaN; currentTime = Double.NaN; h = Double.NaN; interpolatedTime = Double.NaN; currentState = y; interpolatedState = new double[y.length]; finalized = false; this.forward = forward; } /** Copy constructor. *

The copied interpolator should have been finalized before the * copy, otherwise the copy will not be able to perform correctly * any derivative computation 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 not done * automatically, it remains under user control.

*

The copy is a deep copy: its arrays are separated from the * original arrays of the instance.

* @param interpolator interpolator to copy from. */ protected AbstractStepInterpolator(AbstractStepInterpolator interpolator) { previousTime = interpolator.previousTime; currentTime = interpolator.currentTime; h = interpolator.h; interpolatedTime = interpolator.interpolatedTime; if (interpolator.currentState != null) { currentState = (double[]) interpolator.currentState.clone(); interpolatedState = (double[]) interpolator.interpolatedState.clone(); } else { currentState = null; interpolatedState = null; } finalized = interpolator.finalized; forward = interpolator.forward; } /** Reinitialize the instance * @param y reference to the integrator array holding the state at * the end of the step * @param forward integration direction indicator */ protected void reinitialize(double[] y, boolean forward) { previousTime = Double.NaN; currentTime = Double.NaN; h = Double.NaN; interpolatedTime = Double.NaN; currentState = y; interpolatedState = new double[y.length]; finalized = false; this.forward = forward; } /** Copy the instance. *

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 not done automatically, it * remains under user control.

*

The copy is a deep copy: its arrays are separated from the * original arrays of the instance.

*

This method has been redeclared as public instead of protected.

* @return a copy of the instance. */ public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException cnse) { // should never happen return null; } } /** Shift one step forward. * Copy the current time into the previous time, hence preparing the * interpolator for future calls to {@link #storeTime storeTime} */ public void shift() { previousTime = currentTime; } /** Store the current step time. * @param t current time */ public void storeTime(double t) { currentTime = t; h = currentTime - previousTime; interpolatedTime = t; System.arraycopy(currentState, 0, interpolatedState, 0, currentState.length); // the step is not finalized anymore finalized = false; } /** * Get the previous grid point time. * @return previous grid point time */ public double getPreviousTime() { return previousTime; } /** * Get the current grid point time. * @return current grid point time */ public double getCurrentTime() { return currentTime; } /** * Get the time of the interpolated point. * If {@link #setInterpolatedTime} has not been called, it returns * the current grid point time. * @return interpolation point time */ public double getInterpolatedTime() { return interpolatedTime; } /** * Set the time of the interpolated point. *

Setting the time outside of the current step is now allowed * (it was not allowed up to version 5.4 of Mantissa), but should be * used with care since the accuracy of the interpolator will * probably be very poor far from this step. This allowance has been * added to simplify implementation of search algorithms near the * step endpoints.

* @param time time of the interpolated point * @throws DerivativeException if this call induces an automatic * step finalization that throws one */ public void setInterpolatedTime(double time) throws DerivativeException { interpolatedTime = time; double oneMinusThetaH = currentTime - interpolatedTime; computeInterpolatedState((h - oneMinusThetaH) / h, oneMinusThetaH); } /** Check if the natural integration direction is forward. *

This method provides the integration direction as specified by the * integrator itself, it avoid some nasty problems in degenerated * cases like null steps due to cancellation at step initialization, * step control or switching function triggering.

* @return true if the integration variable (time) increases during * integration */ public boolean isForward() { return forward; } /** Compute the state at the interpolated time. * This is the main processing method that should be implemented by * the derived classes to perform the interpolation. * @param theta normalized interpolation abscissa within the step * (theta is zero at the previous time step and one at the current time step) * @param oneMinusThetaH time gap between the interpolated time and * the current time * @throws DerivativeException this exception is propagated to the caller if the * underlying user function triggers one */ protected abstract void computeInterpolatedState(double theta, double oneMinusThetaH) throws DerivativeException; /** * Get the state vector of the interpolated point. * @return state vector at time {@link #getInterpolatedTime} */ public double[] getInterpolatedState() { return (double[]) interpolatedState.clone(); } /** * Finalize the step. *

Some Runge-Kutta-Fehlberg integrators need fewer functions * evaluations than their counterpart step interpolators. These * interpolators should perform the last evaluations they need by * themselves only if they need them. This method triggers these * extra evaluations. It can be called directly by the user step * handler and it is called automatically if {@link * #setInterpolatedTime} is called.

*

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

*

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

*

This method calls the protected {@link #doFinalize doFinalize} * method if it has never been called during this step and set a * flag indicating that it has been called once. It is the {@link * #doFinalize doFinalize} method which should perform the * evaluations. This wrapping prevents from calling {@link * #doFinalize doFinalize} several times and hence evaluating the * differential equations too often. Therefore, subclasses are not * allowed not reimplement it, they should rather reimplement {@link * #doFinalize doFinalize}.

* @throws DerivativeException this exception is propagated to the * caller if the underlying user function triggers one */ public final void finalizeStep() throws DerivativeException { if (! finalized) { doFinalize(); finalized = true; } } /** * Really finalize the step. * The default implementation of this method does nothing. * @throws DerivativeException this exception is propagated to the * caller if the underlying user function triggers one */ protected void doFinalize() throws DerivativeException { } public abstract void writeExternal(ObjectOutput out) throws IOException; public abstract void readExternal(ObjectInput in) throws IOException; /** Save the base state of the instance. * This method performs step finalization if it has not been done * before. * @param out stream where to save the state * @exception IOException in case of write error */ protected void writeBaseExternal(ObjectOutput out) throws IOException { out.writeInt(currentState.length); out.writeDouble(previousTime); out.writeDouble(currentTime); out.writeDouble(h); out.writeBoolean(forward); for (int i = 0; i < currentState.length; ++i) { out.writeDouble(currentState[i]); } out.writeDouble(interpolatedTime); // we do not store the interpolated state, // it will be recomputed as needed after reading // finalize the step (and don't bother saving the now true flag) try { finalizeStep(); } catch (DerivativeException e) { IOException ioe = new IOException(); ioe.initCause(e); throw ioe; } } /** Read the base state of the instance. * This method does neither set the interpolated * time nor state. It is up to the derived class to reset it * properly calling the {@link #setInterpolatedTime} method later, * once all rest of the object state has been set up properly. * @param in stream where to read the state from * @return interpolated time be set later by the caller * @exception IOException in case of read error */ protected double readBaseExternal(ObjectInput in) throws IOException { int dimension = in.readInt(); previousTime = in.readDouble(); currentTime = in.readDouble(); h = in.readDouble(); forward = in.readBoolean(); currentState = new double[dimension]; for (int i = 0; i < currentState.length; ++i) { currentState[i] = in.readDouble(); } // we do NOT handle the interpolated time and state here interpolatedTime = Double.NaN; interpolatedState = new double[dimension]; finalized = true; return in.readDouble(); } }

Ads help maintain this website.