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