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 /**
25 * This class implements an interpolator for the Gragg-Bulirsch-Stoer
26 * integrator.
27 *
28 * <p>This interpolator compute dense output inside the last step
29 * produced by a Gragg-Bulirsch-Stoer integrator.</p>
30 *
31 * <p>
32 * This implementation is basically a reimplementation in Java of the
33 * <a
34 * href="http://www.unige.ch/math/folks/hairer/prog/nonstiff/odex.f">odex</a>
35 * fortran code by E. Hairer and G. Wanner. The redistribution policy
36 * for this code is available <a
37 * href="http://www.unige.ch/~hairer/prog/licence.txt">here</a>, for
38 * convenience, it is reproduced below.</p>
39 * </p>
40 *
41 * <table border="0" width="80%" cellpadding="10" align="center" bgcolor="#E0E0E0">
42 * <tr><td>Copyright (c) 2004, Ernst Hairer</td></tr>
43 *
44 * <tr><td>Redistribution and use in source and binary forms, with or
45 * without modification, are permitted provided that the following
46 * conditions are met:
47 * <ul>
48 * <li>Redistributions of source code must retain the above copyright
49 * notice, this list of conditions and the following disclaimer.</li>
50 * <li>Redistributions in binary form must reproduce the above copyright
51 * notice, this list of conditions and the following disclaimer in the
52 * documentation and/or other materials provided with the distribution.</li>
53 * </ul></td></tr>
54 *
55 * <tr><td><strong>THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
56 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
57 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
58 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
59 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
60 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
61 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
62 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
63 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
64 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
65 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</strong></td></tr>
66 * </table>
67 *
68 * @see GraggBulirschStoerIntegrator
69 * @version $Revision: 620312 $ $Date: 2008-02-10 12:28:59 -0700 (Sun, 10 Feb 2008) $
70 * @author E. Hairer and G. Wanner (fortran version)
71 * @since 1.2
72 */
73
74 class GraggBulirschStoerStepInterpolator
75 extends AbstractStepInterpolator {
76
77 /** Slope at the beginning of the step. */
78 private double[] y0Dot;
79
80 /** State at the end of the step. */
81 private double[] y1;
82
83 /** Slope at the end of the step. */
84 private double[] y1Dot;
85
86 /** Derivatives at the middle of the step.
87 * element 0 is state at midpoint, element 1 is first derivative ...
88 */
89 private double[][] yMidDots;
90
91 /** Interpolation polynoms. */
92 private double[][] polynoms;
93
94 /** Error coefficients for the interpolation. */
95 private double[] errfac;
96
97 /** Degree of the interpolation polynoms. */
98 private int currentDegree;
99
100 /** Reallocate the internal tables.
101 * Reallocate the internal tables in order to be able to handle
102 * interpolation polynoms up to the given degree
103 * @param maxDegree maximal degree to handle
104 */
105 private void resetTables(int maxDegree) {
106
107 if (maxDegree < 0) {
108 polynoms = null;
109 errfac = null;
110 currentDegree = -1;
111 } else {
112
113 double[][] newPols = new double[maxDegree + 1][];
114 if (polynoms != null) {
115 System.arraycopy(polynoms, 0, newPols, 0, polynoms.length);
116 for (int i = polynoms.length; i < newPols.length; ++i) {
117 newPols[i] = new double[currentState.length];
118 }
119 } else {
120 for (int i = 0; i < newPols.length; ++i) {
121 newPols[i] = new double[currentState.length];
122 }
123 }
124 polynoms = newPols;
125
126 // initialize the error factors array for interpolation
127 if (maxDegree <= 4) {
128 errfac = null;
129 } else {
130 errfac = new double[maxDegree - 4];
131 for (int i = 0; i < errfac.length; ++i) {
132 int ip5 = i + 5;
133 errfac[i] = 1.0 / (ip5 * ip5);
134 double e = 0.5 * Math.sqrt (((double) (i + 1)) / ip5);
135 for (int j = 0; j <= i; ++j) {
136 errfac[i] *= e / (j + 1);
137 }
138 }
139 }
140
141 currentDegree = 0;
142
143 }
144
145 }
146
147 /** Simple constructor.
148 * This constructor should not be used directly, it is only intended
149 * for the serialization process.
150 */
151 public GraggBulirschStoerStepInterpolator() {
152 y0Dot = null;
153 y1 = null;
154 y1Dot = null;
155 yMidDots = null;
156 resetTables(-1);
157 }
158
159 /** Simple constructor.
160 * @param y reference to the integrator array holding the current state
161 * @param y0Dot reference to the integrator array holding the slope
162 * at the beginning of the step
163 * @param y1 reference to the integrator array holding the state at
164 * the end of the step
165 * @param y1Dot reference to the integrator array holding the slope
166 * at theend of the step
167 * @param yMidDots reference to the integrator array holding the
168 * derivatives at the middle point of the step
169 * @param forward integration direction indicator
170 */
171 public GraggBulirschStoerStepInterpolator(double[] y, double[] y0Dot,
172 double[] y1, double[] y1Dot,
173 double[][] yMidDots,
174 boolean forward) {
175
176 super(y, forward);
177 this.y0Dot = y0Dot;
178 this.y1 = y1;
179 this.y1Dot = y1Dot;
180 this.yMidDots = yMidDots;
181
182 resetTables(yMidDots.length + 4);
183
184 }
185
186 /** Copy constructor.
187 * @param interpolator interpolator to copy from. The copy is a deep
188 * copy: its arrays are separated from the original arrays of the
189 * instance
190 */
191 public GraggBulirschStoerStepInterpolator
192 (GraggBulirschStoerStepInterpolator interpolator) {
193
194 super(interpolator);
195
196 int dimension = currentState.length;
197
198 // the interpolator has been finalized,
199 // the following arrays are not needed anymore
200 y0Dot = null;
201 y1 = null;
202 y1Dot = null;
203 yMidDots = null;
204
205 // copy the interpolation polynoms (up to the current degree only)
206 if (interpolator.polynoms == null) {
207 polynoms = null;
208 currentDegree = -1;
209 } else {
210 resetTables(interpolator.currentDegree);
211 for (int i = 0; i < polynoms.length; ++i) {
212 polynoms[i] = new double[dimension];
213 System.arraycopy(interpolator.polynoms[i], 0,
214 polynoms[i], 0, dimension);
215 }
216 currentDegree = interpolator.currentDegree;
217 }
218
219 }
220
221 /** Really copy the finalized instance.
222 * @return a copy of the finalized instance
223 */
224 protected StepInterpolator doCopy() {
225 return new GraggBulirschStoerStepInterpolator(this);
226 }
227
228
229 /** Compute the interpolation coefficients for dense output.
230 * @param mu degree of the interpolation polynom
231 * @param h current step
232 */
233 public void computeCoefficients(int mu, double h) {
234
235 if ((polynoms == null) || (polynoms.length <= (mu + 4))) {
236 resetTables(mu + 4);
237 }
238
239 currentDegree = mu + 4;
240
241 for (int i = 0; i < currentState.length; ++i) {
242
243 double yp0 = h * y0Dot[i];
244 double yp1 = h * y1Dot[i];
245 double ydiff = y1[i] - currentState[i];
246 double aspl = ydiff - yp1;
247 double bspl = yp0 - ydiff;
248
249 polynoms[0][i] = currentState[i];
250 polynoms[1][i] = ydiff;
251 polynoms[2][i] = aspl;
252 polynoms[3][i] = bspl;
253
254 if (mu < 0) {
255 return;
256 }
257
258 // compute the remaining coefficients
259 double ph0 = 0.5 * (currentState[i] + y1[i]) + 0.125 * (aspl + bspl);
260 polynoms[4][i] = 16 * (yMidDots[0][i] - ph0);
261
262 if (mu > 0) {
263 double ph1 = ydiff + 0.25 * (aspl - bspl);
264 polynoms[5][i] = 16 * (yMidDots[1][i] - ph1);
265
266 if (mu > 1) {
267 double ph2 = yp1 - yp0;
268 polynoms[6][i] = 16 * (yMidDots[2][i] - ph2 + polynoms[4][i]);
269
270 if (mu > 2) {
271 double ph3 = 6 * (bspl - aspl);
272 polynoms[7][i] = 16 * (yMidDots[3][i] - ph3 + 3 * polynoms[5][i]);
273
274 for (int j = 4; j <= mu; ++j) {
275 double fac1 = 0.5 * j * (j - 1);
276 double fac2 = 2 * fac1 * (j - 2) * (j - 3);
277 polynoms[j+4][i] =
278 16 * (yMidDots[j][i] + fac1 * polynoms[j+2][i] - fac2 * polynoms[j][i]);
279 }
280
281 }
282 }
283 }
284 }
285
286 }
287
288 /** Estimate interpolation error.
289 * @param scale scaling array
290 * @return estimate of the interpolation error
291 */
292 public double estimateError(double[] scale) {
293 double error = 0;
294 if (currentDegree >= 5) {
295 for (int i = 0; i < currentState.length; ++i) {
296 double e = polynoms[currentDegree][i] / scale[i];
297 error += e * e;
298 }
299 error = Math.sqrt(error / currentState.length) * errfac[currentDegree-5];
300 }
301 return error;
302 }
303
304 /** Compute the state at the interpolated time.
305 * This is the main processing method that should be implemented by
306 * the derived classes to perform the interpolation.
307 * @param theta normalized interpolation abscissa within the step
308 * (theta is zero at the previous time step and one at the current time step)
309 * @param oneMinusThetaH time gap between the interpolated time and
310 * the current time
311 * @throws DerivativeException this exception is propagated to the caller if the
312 * underlying user function triggers one
313 */
314 protected void computeInterpolatedState(double theta,
315 double oneMinusThetaH)
316 throws DerivativeException {
317
318 int dimension = currentState.length;
319
320 double oneMinusTheta = 1.0 - theta;
321 double theta05 = theta - 0.5;
322 double t4 = theta * oneMinusTheta;
323 t4 = t4 * t4;
324
325 for (int i = 0; i < dimension; ++i) {
326 interpolatedState[i] = polynoms[0][i] +
327 theta * (polynoms[1][i] +
328 oneMinusTheta * (polynoms[2][i] * theta +
329 polynoms[3][i] * oneMinusTheta));
330
331 if (currentDegree > 3) {
332 double c = polynoms[currentDegree][i];
333 for (int j = currentDegree - 1; j > 3; --j) {
334 c = polynoms[j][i] + c * theta05 / (j - 3);
335 }
336 interpolatedState[i] += t4 * c;
337 }
338 }
339
340 }
341
342 /** Save the state of the instance.
343 * @param out stream where to save the state
344 * @exception IOException in case of write error
345 */
346 public void writeExternal(ObjectOutput out)
347 throws IOException {
348
349 int dimension = currentState.length;
350
351 // save the state of the base class
352 writeBaseExternal(out);
353
354 // save the local attributes (but not the temporary vectors)
355 out.writeInt(currentDegree);
356 for (int k = 0; k <= currentDegree; ++k) {
357 for (int l = 0; l < dimension; ++l) {
358 out.writeDouble(polynoms[k][l]);
359 }
360 }
361
362 }
363
364 /** Read the state of the instance.
365 * @param in stream where to read the state from
366 * @exception IOException in case of read error
367 */
368 public void readExternal(ObjectInput in)
369 throws IOException {
370
371 // read the base class
372 double t = readBaseExternal(in);
373 int dimension = currentState.length;
374
375 // read the local attributes
376 int degree = in.readInt();
377 resetTables(degree);
378 currentDegree = degree;
379
380 for (int k = 0; k <= currentDegree; ++k) {
381 for (int l = 0; l < dimension; ++l) {
382 polynoms[k][l] = in.readDouble();
383 }
384 }
385
386 try {
387 // we can now set the interpolated time and state
388 setInterpolatedTime(t);
389 } catch (DerivativeException e) {
390 throw new IOException(e.getMessage());
391 }
392
393 }
394
395 /** Serializable version identifier */
396 private static final long serialVersionUID = 7320613236731409847L;
397
398 }