blob: aa1b8ae84720fb42802ee5c78fb3d0d6e1387ec3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.math4.legacy.ode.sampling;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Arrays;
import org.apache.commons.math4.legacy.exception.MaxCountExceededException;
import org.apache.commons.math4.legacy.linear.Array2DRowRealMatrix;
import org.apache.commons.math4.legacy.ode.EquationsMapper;
import org.apache.commons.math4.core.jdkmath.JdkMath;
/**
* This class implements an interpolator for integrators using Nordsieck representation.
*
* <p>This interpolator computes dense output around the current point.
* The interpolation equation is based on Taylor series formulas.
*
* @see org.apache.commons.math4.legacy.ode.nonstiff.AdamsBashforthIntegrator
* @see org.apache.commons.math4.legacy.ode.nonstiff.AdamsMoultonIntegrator
* @since 2.0
*/
public class NordsieckStepInterpolator extends AbstractStepInterpolator {
/** Serializable version identifier. */
private static final long serialVersionUID = -7179861704951334960L;
/** State variation. */
protected double[] stateVariation;
/** Step size used in the first scaled derivative and Nordsieck vector. */
private double scalingH;
/** Reference time for all arrays.
* <p>Sometimes, the reference time is the same as previousTime,
* sometimes it is the same as currentTime, so we use a separate
* field to avoid any confusion.
* </p>
*/
private double referenceTime;
/** First scaled derivative. */
private double[] scaled;
/** Nordsieck vector. */
private Array2DRowRealMatrix nordsieck;
/** Simple constructor.
* This constructor builds an instance that is not usable yet, the
* {@link AbstractStepInterpolator#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.
*/
public NordsieckStepInterpolator() {
}
/** Copy constructor.
* @param interpolator interpolator to copy from. The copy is a deep
* copy: its arrays are separated from the original arrays of the
* instance
*/
public NordsieckStepInterpolator(final NordsieckStepInterpolator interpolator) {
super(interpolator);
scalingH = interpolator.scalingH;
referenceTime = interpolator.referenceTime;
if (interpolator.scaled != null) {
scaled = interpolator.scaled.clone();
}
if (interpolator.nordsieck != null) {
nordsieck = new Array2DRowRealMatrix(interpolator.nordsieck.getDataRef(), true);
}
if (interpolator.stateVariation != null) {
stateVariation = interpolator.stateVariation.clone();
}
}
/** {@inheritDoc} */
@Override
protected StepInterpolator doCopy() {
return new NordsieckStepInterpolator(this);
}
/** Reinitialize the instance.
* <p>Beware that all arrays <em>must</em> be references to integrator
* arrays, in order to ensure proper update without copy.</p>
* @param y reference to the integrator array holding the state at
* the end of the step
* @param forward integration direction indicator
* @param primaryMapper equations mapper for the primary equations set
* @param secondaryMappers equations mappers for the secondary equations sets
*/
@Override
public void reinitialize(final double[] y, final boolean forward,
final EquationsMapper primaryMapper,
final EquationsMapper[] secondaryMappers) {
super.reinitialize(y, forward, primaryMapper, secondaryMappers);
stateVariation = new double[y.length];
}
/** Reinitialize the instance.
* <p>Beware that all arrays <em>must</em> be references to integrator
* arrays, in order to ensure proper update without copy.</p>
* @param time time at which all arrays are defined
* @param stepSize step size used in the scaled and Nordsieck arrays
* @param scaledDerivative reference to the integrator array holding the first
* scaled derivative
* @param nordsieckVector reference to the integrator matrix holding the
* Nordsieck vector
*/
public void reinitialize(final double time, final double stepSize,
final double[] scaledDerivative,
final Array2DRowRealMatrix nordsieckVector) {
this.referenceTime = time;
this.scalingH = stepSize;
this.scaled = scaledDerivative;
this.nordsieck = nordsieckVector;
// make sure the state and derivatives will depend on the new arrays
setInterpolatedTime(getInterpolatedTime());
}
/** Rescale the instance.
* <p>Since the scaled and Nordsieck arrays are shared with the caller,
* this method has the side effect of rescaling this arrays in the caller too.</p>
* @param stepSize new step size to use in the scaled and Nordsieck arrays
*/
public void rescale(final double stepSize) {
final double ratio = stepSize / scalingH;
for (int i = 0; i < scaled.length; ++i) {
scaled[i] *= ratio;
}
final double[][] nData = nordsieck.getDataRef();
double power = ratio;
for (int i = 0; i < nData.length; ++i) {
power *= ratio;
final double[] nDataI = nData[i];
for (int j = 0; j < nDataI.length; ++j) {
nDataI[j] *= power;
}
}
scalingH = stepSize;
}
/**
* Get the state vector variation from current to interpolated state.
* <p>This method is aimed at computing y(t<sub>interpolation</sub>)
* -y(t<sub>current</sub>) accurately by avoiding the cancellation errors
* that would occur if the subtraction were performed explicitly.</p>
* <p>The returned vector is a reference to a reused array, so
* it should not be modified and it should be copied if it needs
* to be preserved across several calls.</p>
* @return state vector at time {@link #getInterpolatedTime}
* @see #getInterpolatedDerivatives()
* @exception MaxCountExceededException if the number of functions evaluations is exceeded
*/
public double[] getInterpolatedStateVariation() throws MaxCountExceededException {
// compute and ignore interpolated state
// to make sure state variation is computed as a side effect
getInterpolatedState();
return stateVariation;
}
/** {@inheritDoc} */
@Override
protected void computeInterpolatedStateAndDerivatives(final double theta, final double oneMinusThetaH) {
final double x = interpolatedTime - referenceTime;
final double normalizedAbscissa = x / scalingH;
Arrays.fill(stateVariation, 0.0);
Arrays.fill(interpolatedDerivatives, 0.0);
// apply Taylor formula from high order to low order,
// for the sake of numerical accuracy
final double[][] nData = nordsieck.getDataRef();
for (int i = nData.length - 1; i >= 0; --i) {
final int order = i + 2;
final double[] nDataI = nData[i];
final double power = JdkMath.pow(normalizedAbscissa, order);
for (int j = 0; j < nDataI.length; ++j) {
final double d = nDataI[j] * power;
stateVariation[j] += d;
interpolatedDerivatives[j] += order * d;
}
}
for (int j = 0; j < currentState.length; ++j) {
stateVariation[j] += scaled[j] * normalizedAbscissa;
interpolatedState[j] = currentState[j] + stateVariation[j];
interpolatedDerivatives[j] =
(interpolatedDerivatives[j] + scaled[j] * normalizedAbscissa) / x;
}
}
/** {@inheritDoc} */
@Override
public void writeExternal(final ObjectOutput out)
throws IOException {
// save the state of the base class
writeBaseExternal(out);
// save the local attributes
out.writeDouble(scalingH);
out.writeDouble(referenceTime);
final int n = (currentState == null) ? -1 : currentState.length;
if (scaled == null) {
out.writeBoolean(false);
} else {
out.writeBoolean(true);
for (int j = 0; j < n; ++j) {
out.writeDouble(scaled[j]);
}
}
if (nordsieck == null) {
out.writeBoolean(false);
} else {
out.writeBoolean(true);
out.writeObject(nordsieck);
}
// we don't save state variation, it will be recomputed
}
/** {@inheritDoc} */
@Override
public void readExternal(final ObjectInput in)
throws IOException, ClassNotFoundException {
// read the base class
final double t = readBaseExternal(in);
// read the local attributes
scalingH = in.readDouble();
referenceTime = in.readDouble();
final int n = (currentState == null) ? -1 : currentState.length;
final boolean hasScaled = in.readBoolean();
if (hasScaled) {
scaled = new double[n];
for (int j = 0; j < n; ++j) {
scaled[j] = in.readDouble();
}
} else {
scaled = null;
}
final boolean hasNordsieck = in.readBoolean();
if (hasNordsieck) {
nordsieck = (Array2DRowRealMatrix) in.readObject();
} else {
nordsieck = null;
}
if (hasScaled && hasNordsieck) {
// we can now set the interpolated time and state
stateVariation = new double[n];
setInterpolatedTime(t);
} else {
stateVariation = null;
}
}
}