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
18package org.apache.commons.math.ode.sampling;
19
20import java.io.IOException;
21import java.io.ObjectInput;
22import java.io.ObjectOutput;
23
24import org.apache.commons.math.ode.DerivativeException;
25
26/** This abstract class represents an interpolator over the last step
27 * during an ODE integration.
28 *
29 * <p>The various ODE integrators provide objects extending this class
30 * to the step handlers. The handlers can use these objects to
31 * retrieve the state vector at intermediate times between the
32 * previous and the current grid points (dense output).</p>
33 *
34 * @see org.apache.commons.math.ode.FirstOrderIntegrator
35 * @see org.apache.commons.math.ode.SecondOrderIntegrator
36 * @see StepHandler
37 *
38 * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 févr. 2011) $
39 * @since 1.2
40 *
41 */
42
43public abstract class AbstractStepInterpolator
44  implements StepInterpolator {
45
46  /** current time step */
47  protected double h;
48
49  /** current state */
50  protected double[] currentState;
51
52  /** interpolated time */
53  protected double interpolatedTime;
54
55  /** interpolated state */
56  protected double[] interpolatedState;
57
58  /** interpolated derivatives */
59  protected double[] interpolatedDerivatives;
60
61  /** global previous time */
62  private double globalPreviousTime;
63
64  /** global current time */
65  private double globalCurrentTime;
66
67  /** soft previous time */
68  private double softPreviousTime;
69
70  /** soft current time */
71  private double softCurrentTime;
72
73  /** indicate if the step has been finalized or not. */
74  private boolean finalized;
75
76  /** integration direction. */
77  private boolean forward;
78
79  /** indicator for dirty state. */
80  private boolean dirtyState;
81
82
83  /** Simple constructor.
84   * This constructor builds an instance that is not usable yet, the
85   * {@link #reinitialize} method should be called before using the
86   * instance in order to initialize the internal arrays. This
87   * constructor is used only in order to delay the initialization in
88   * some cases. As an example, the {@link
89   * org.apache.commons.math.ode.nonstiff.EmbeddedRungeKuttaIntegrator}
90   * class uses the prototyping design pattern to create the step
91   * interpolators by cloning an uninitialized model and latter
92   * initializing the copy.
93   */
94  protected AbstractStepInterpolator() {
95    globalPreviousTime      = Double.NaN;
96    globalCurrentTime       = Double.NaN;
97    softPreviousTime        = Double.NaN;
98    softCurrentTime         = Double.NaN;
99    h                       = Double.NaN;
100    interpolatedTime        = Double.NaN;
101    currentState            = null;
102    interpolatedState       = null;
103    interpolatedDerivatives = null;
104    finalized               = false;
105    this.forward            = true;
106    this.dirtyState         = true;
107  }
108
109  /** Simple constructor.
110   * @param y reference to the integrator array holding the state at
111   * the end of the step
112   * @param forward integration direction indicator
113   */
114  protected AbstractStepInterpolator(final double[] y, final boolean forward) {
115
116    globalPreviousTime = Double.NaN;
117    globalCurrentTime  = Double.NaN;
118    softPreviousTime   = Double.NaN;
119    softCurrentTime    = Double.NaN;
120    h                  = Double.NaN;
121    interpolatedTime   = Double.NaN;
122
123    currentState            = y;
124    interpolatedState       = new double[y.length];
125    interpolatedDerivatives = new double[y.length];
126
127    finalized         = false;
128    this.forward      = forward;
129    this.dirtyState   = true;
130
131  }
132
133  /** Copy constructor.
134
135   * <p>The copied interpolator should have been finalized before the
136   * copy, otherwise the copy will not be able to perform correctly
137   * any derivative computation and will throw a {@link
138   * NullPointerException} later. Since we don't want this constructor
139   * to throw the exceptions finalization may involve and since we
140   * don't want this method to modify the state of the copied
141   * interpolator, finalization is <strong>not</strong> done
142   * automatically, it remains under user control.</p>
143   *
144   * <p>The copy is a deep copy: its arrays are separated from the
145   * original arrays of the instance.</p>
146   *
147   * @param interpolator interpolator to copy from.
148   *
149   */
150  protected AbstractStepInterpolator(final AbstractStepInterpolator interpolator) {
151
152    globalPreviousTime = interpolator.globalPreviousTime;
153    globalCurrentTime  = interpolator.globalCurrentTime;
154    softPreviousTime   = interpolator.softPreviousTime;
155    softCurrentTime    = interpolator.softCurrentTime;
156    h                  = interpolator.h;
157    interpolatedTime   = interpolator.interpolatedTime;
158
159    if (interpolator.currentState != null) {
160      currentState            = interpolator.currentState.clone();
161      interpolatedState       = interpolator.interpolatedState.clone();
162      interpolatedDerivatives = interpolator.interpolatedDerivatives.clone();
163    } else {
164      currentState            = null;
165      interpolatedState       = null;
166      interpolatedDerivatives = null;
167    }
168
169    finalized  = interpolator.finalized;
170    forward    = interpolator.forward;
171    dirtyState = interpolator.dirtyState;
172
173  }
174
175  /** Reinitialize the instance
176   * @param y reference to the integrator array holding the state at
177   * the end of the step
178   * @param isForward integration direction indicator
179   */
180  protected void reinitialize(final double[] y, final boolean isForward) {
181
182    globalPreviousTime = Double.NaN;
183    globalCurrentTime  = Double.NaN;
184    softPreviousTime   = Double.NaN;
185    softCurrentTime    = Double.NaN;
186    h                  = Double.NaN;
187    interpolatedTime   = Double.NaN;
188
189    currentState            = y;
190    interpolatedState       = new double[y.length];
191    interpolatedDerivatives = new double[y.length];
192
193    finalized         = false;
194    this.forward      = isForward;
195    this.dirtyState   = true;
196
197  }
198
199  /** {@inheritDoc} */
200   public StepInterpolator copy() throws DerivativeException {
201
202     // finalize the step before performing copy
203     finalizeStep();
204
205     // create the new independent instance
206     return doCopy();
207
208   }
209
210   /** Really copy the finalized instance.
211    * <p>This method is called by {@link #copy()} after the
212    * step has been finalized. It must perform a deep copy
213    * to have an new instance completely independent for the
214    * original instance.
215    * @return a copy of the finalized instance
216    */
217   protected abstract StepInterpolator doCopy();
218
219  /** Shift one step forward.
220   * Copy the current time into the previous time, hence preparing the
221   * interpolator for future calls to {@link #storeTime storeTime}
222   */
223  public void shift() {
224    globalPreviousTime = globalCurrentTime;
225    softPreviousTime   = globalPreviousTime;
226    softCurrentTime    = globalCurrentTime;
227  }
228
229  /** Store the current step time.
230   * @param t current time
231   */
232  public void storeTime(final double t) {
233
234    globalCurrentTime = t;
235    softCurrentTime   = globalCurrentTime;
236    h                 = globalCurrentTime - globalPreviousTime;
237    setInterpolatedTime(t);
238
239    // the step is not finalized anymore
240    finalized  = false;
241
242  }
243
244  /** Restrict step range to a limited part of the global step.
245   * <p>
246   * This method can be used to restrict a step and make it appear
247   * as if the original step was smaller. Calling this method
248   * <em>only</em> changes the value returned by {@link #getPreviousTime()},
249   * it does not change any other property
250   * </p>
251   * @param softPreviousTime start of the restricted step
252   * @since 2.2
253   */
254  public void setSoftPreviousTime(final double softPreviousTime) {
255      this.softPreviousTime = softPreviousTime;
256  }
257
258  /** Restrict step range to a limited part of the global step.
259   * <p>
260   * This method can be used to restrict a step and make it appear
261   * as if the original step was smaller. Calling this method
262   * <em>only</em> changes the value returned by {@link #getCurrentTime()},
263   * it does not change any other property
264   * </p>
265   * @param softCurrentTime end of the restricted step
266   * @since 2.2
267   */
268  public void setSoftCurrentTime(final double softCurrentTime) {
269      this.softCurrentTime  = softCurrentTime;
270  }
271
272  /**
273   * Get the previous global grid point time.
274   * @return previous global grid point time
275   * @since 2.2
276   */
277  public double getGlobalPreviousTime() {
278    return globalPreviousTime;
279  }
280
281  /**
282   * Get the current global grid point time.
283   * @return current global grid point time
284   * @since 2.2
285   */
286  public double getGlobalCurrentTime() {
287    return globalCurrentTime;
288  }
289
290  /**
291   * Get the previous soft grid point time.
292   * @return previous soft grid point time
293   * @see #setSoftPreviousTime(double)
294   */
295  public double getPreviousTime() {
296    return softPreviousTime;
297  }
298
299  /**
300   * Get the current soft grid point time.
301   * @return current soft grid point time
302   * @see #setSoftCurrentTime(double)
303   */
304  public double getCurrentTime() {
305    return softCurrentTime;
306  }
307
308  /** {@inheritDoc} */
309  public double getInterpolatedTime() {
310    return interpolatedTime;
311  }
312
313  /** {@inheritDoc} */
314  public void setInterpolatedTime(final double time) {
315      interpolatedTime = time;
316      dirtyState       = true;
317  }
318
319  /** {@inheritDoc} */
320  public boolean isForward() {
321    return forward;
322  }
323
324  /** Compute the state and derivatives at the interpolated time.
325   * This is the main processing method that should be implemented by
326   * the derived classes to perform the interpolation.
327   * @param theta normalized interpolation abscissa within the step
328   * (theta is zero at the previous time step and one at the current time step)
329   * @param oneMinusThetaH time gap between the interpolated time and
330   * the current time
331   * @throws DerivativeException this exception is propagated to the caller if the
332   * underlying user function triggers one
333   */
334  protected abstract void computeInterpolatedStateAndDerivatives(double theta,
335                                                                 double oneMinusThetaH)
336    throws DerivativeException;
337
338  /** {@inheritDoc} */
339  public double[] getInterpolatedState() throws DerivativeException {
340
341      // lazy evaluation of the state
342      if (dirtyState) {
343          final double oneMinusThetaH = globalCurrentTime - interpolatedTime;
344          final double theta = (h == 0) ? 0 : (h - oneMinusThetaH) / h;
345          computeInterpolatedStateAndDerivatives(theta, oneMinusThetaH);
346          dirtyState = false;
347      }
348
349      return interpolatedState;
350
351  }
352
353  /** {@inheritDoc} */
354  public double[] getInterpolatedDerivatives() throws DerivativeException {
355
356      // lazy evaluation of the state
357      if (dirtyState) {
358          final double oneMinusThetaH = globalCurrentTime - interpolatedTime;
359          final double theta = (h == 0) ? 0 : (h - oneMinusThetaH) / h;
360          computeInterpolatedStateAndDerivatives(theta, oneMinusThetaH);
361          dirtyState = false;
362      }
363
364      return interpolatedDerivatives;
365
366  }
367
368  /**
369   * Finalize the step.
370   *
371   * <p>Some embedded Runge-Kutta integrators need fewer functions
372   * evaluations than their counterpart step interpolators. These
373   * interpolators should perform the last evaluations they need by
374   * themselves only if they need them. This method triggers these
375   * extra evaluations. It can be called directly by the user step
376   * handler and it is called automatically if {@link
377   * #setInterpolatedTime} is called.</p>
378   *
379   * <p>Once this method has been called, <strong>no</strong> other
380   * evaluation will be performed on this step. If there is a need to
381   * have some side effects between the step handler and the
382   * differential equations (for example update some data in the
383   * equations once the step has been done), it is advised to call
384   * this method explicitly from the step handler before these side
385   * effects are set up. If the step handler induces no side effect,
386   * then this method can safely be ignored, it will be called
387   * transparently as needed.</p>
388   *
389   * <p><strong>Warning</strong>: since the step interpolator provided
390   * to the step handler as a parameter of the {@link
391   * StepHandler#handleStep handleStep} is valid only for the duration
392   * of the {@link StepHandler#handleStep handleStep} call, one cannot
393   * simply store a reference and reuse it later. One should first
394   * finalize the instance, then copy this finalized instance into a
395   * new object that can be kept.</p>
396   *
397   * <p>This method calls the protected <code>doFinalize</code> method
398   * if it has never been called during this step and set a flag
399   * indicating that it has been called once. It is the <code>
400   * doFinalize</code> method which should perform the evaluations.
401   * This wrapping prevents from calling <code>doFinalize</code> several
402   * times and hence evaluating the differential equations too often.
403   * Therefore, subclasses are not allowed not reimplement it, they
404   * should rather reimplement <code>doFinalize</code>.</p>
405   *
406   * @throws DerivativeException this exception is propagated to the
407   * caller if the underlying user function triggers one
408   */
409  public final void finalizeStep()
410    throws DerivativeException {
411    if (! finalized) {
412      doFinalize();
413      finalized = true;
414    }
415  }
416
417  /**
418   * Really finalize the step.
419   * The default implementation of this method does nothing.
420   * @throws DerivativeException this exception is propagated to the
421   * caller if the underlying user function triggers one
422   */
423  protected void doFinalize()
424    throws DerivativeException {
425  }
426
427  /** {@inheritDoc} */
428  public abstract void writeExternal(ObjectOutput out)
429    throws IOException;
430
431  /** {@inheritDoc} */
432  public abstract void readExternal(ObjectInput in)
433    throws IOException, ClassNotFoundException;
434
435  /** Save the base state of the instance.
436   * This method performs step finalization if it has not been done
437   * before.
438   * @param out stream where to save the state
439   * @exception IOException in case of write error
440   */
441  protected void writeBaseExternal(final ObjectOutput out)
442    throws IOException {
443
444    if (currentState == null) {
445        out.writeInt(-1);
446    } else {
447        out.writeInt(currentState.length);
448    }
449    out.writeDouble(globalPreviousTime);
450    out.writeDouble(globalCurrentTime);
451    out.writeDouble(softPreviousTime);
452    out.writeDouble(softCurrentTime);
453    out.writeDouble(h);
454    out.writeBoolean(forward);
455
456    if (currentState != null) {
457        for (int i = 0; i < currentState.length; ++i) {
458            out.writeDouble(currentState[i]);
459        }
460    }
461
462    out.writeDouble(interpolatedTime);
463
464    // we do not store the interpolated state,
465    // it will be recomputed as needed after reading
466
467    // finalize the step (and don't bother saving the now true flag)
468    try {
469      finalizeStep();
470    } catch (DerivativeException e) {
471        IOException ioe = new IOException(e.getLocalizedMessage());
472        ioe.initCause(e);
473        throw ioe;
474    }
475
476  }
477
478  /** Read the base state of the instance.
479   * This method does <strong>neither</strong> set the interpolated
480   * time nor state. It is up to the derived class to reset it
481   * properly calling the {@link #setInterpolatedTime} method later,
482   * once all rest of the object state has been set up properly.
483   * @param in stream where to read the state from
484   * @return interpolated time be set later by the caller
485   * @exception IOException in case of read error
486   */
487  protected double readBaseExternal(final ObjectInput in)
488    throws IOException {
489
490    final int dimension = in.readInt();
491    globalPreviousTime  = in.readDouble();
492    globalCurrentTime   = in.readDouble();
493    softPreviousTime    = in.readDouble();
494    softCurrentTime     = in.readDouble();
495    h                   = in.readDouble();
496    forward             = in.readBoolean();
497    dirtyState          = true;
498
499    if (dimension < 0) {
500        currentState = null;
501    } else {
502        currentState  = new double[dimension];
503        for (int i = 0; i < currentState.length; ++i) {
504            currentState[i] = in.readDouble();
505        }
506    }
507
508    // we do NOT handle the interpolated time and state here
509    interpolatedTime        = Double.NaN;
510    interpolatedState       = (dimension < 0) ? null : new double[dimension];
511    interpolatedDerivatives = (dimension < 0) ? null : new double[dimension];
512
513    finalized = true;
514
515    return in.readDouble();
516
517  }
518
519}
520