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.sampling; 19 20 import java.io.IOException; 21 import java.io.ObjectInput; 22 import java.io.ObjectOutput; 23 24 import 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 fvr. 2011) $ 39 * @since 1.2 40 * 41 */ 42 43 public 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