ODEAdaptiveSolver


package odeadapt;

import Jama.*;

/**
 * adaptive solver that uses a special strategy to get a new step size<br>
 * <br>
 * basic strategy: <br>
 *     integrate with actual stepsize in one step<br>
 *     integrate with half of actual stepsize in two steps<br>
 *     estimate the error to get new stepsize
 */

public class ODEAdaptiveSolver extends ODESolver {
  
  protected double DEFAULT_TOLERANCE = 1.0/1000000.0;
  
  protected ODESingleStepSolver solv; 
  protected int order;     // order of solver = order of ode1
  
  // end point of integration
  protected double tEnd;
  // current value of step size 
  protected double stepSize;
  
  // max. error
  protected double tol;
  // factor for error estimation				  
  protected double gamma;
  // safety factor for step size
  protected double safety = 95.0/100.0;
  // maximal expansion factor of step size
  protected double maxFac = 5.0;

  /**
   * construct adaptive solver for a given ODE defining<br>
   *   end time of integration<br>
   *   maximal local error<br>
   *   a single step solver for the integration in one and two half steps
   */
  public ODEAdaptiveSolver(ODE ode, double tEnd, ODESingleStepSolver solv) {
    super(ode); // sets t, x
    this.order = solv.order;
    this.solv = solv;
    
    if (tEnd > ode.t0) {
      this.tEnd = tEnd;
    } else {
      // tEnd is smaller than t0!
      throw new IllegalArgumentException("Start time " + ode.t0 +
					 " is later than end time " + tEnd);
    }
    
    // try to integrate in one step at first
    stepSize = tEnd - ode.t0;
    gamma = 1.0 / (1.0 - Math.pow(2.0, -order));
  }

  /**
   * construct adaptive solver for a given ODE defining<br>
   *   end time of integration<br>
   *   maximal local error<br>
   *   use Runge-Kutta-4 solver as single step solver
   */
  public ODEAdaptiveSolver(ODE ode, double tEnd) {
    this(ode, tEnd, new ODESolverRK4(ode));
  }

  /**
   * integrate until t+dt<br>
   * return number of steps needed
   */
  public int nextStep(double dt) throws StepsizeTooSmallException {
    // old stepSize could be too small while approaching tEnd from last step!
    // therefore: try with one step
    // alternatively: use standard initial step size
    stepSize = dt;
    int nSteps = 0;
    tEnd = t + dt;
    while (t < tEnd - tol) {
      nextStep();
      nSteps++;
    }
    return nSteps;
  }

  /**
   * proceed one step, using an appropriate step size
   */
  public void nextStep() throws StepsizeTooSmallException {
    boolean stepAccepted = false;
    while (!stepAccepted) {
      // solve in one large step
      solv.t = t;
      solv.x = x.copy();
      solv.nextStep(stepSize);
      Matrix xOneStep = solv.x;
      
      // solve in two small steps
      solv.t = t;
      solv.x = x.copy();
      solv.nextStep(stepSize / 2.0);
      solv.nextStep(stepSize / 2.0);
      
      // estimate error
      // diff = gamma * (x2 - x1)
      Matrix diff = solv.x.minus(xOneStep).timesEquals(gamma);
      double error = diff.normF();
      
      // compute new stepsize
      double hRel = Math.pow(tol / error, 1.0 / (1.0 + order));
      double stepSizeNew = stepSize * hRel;
      
      // throw exception, if step size gets too small
      if (stepSizeNew < tol) {
	throw new StepsizeTooSmallException("Step size " + stepSize +
				       " is smaller than tolerance " + tol);
      }
      if (hRel < 1) {
	// step rejected, use smaller step size
	stepSize = safety * stepSizeNew;
      } else {
	// step accepted, use new values
	stepAccepted = true;
	t += stepSize;
	x = xOneStep.plus(diff);
	
	// increase step size
	if (hRel > maxFac) { // don't expand too much ! 
	  stepSize *= maxFac;
	} else {
	  stepSize = safety * stepSizeNew;
	}
	stepSize = Math.min(stepSize, tEnd - t); // don't overshoot!
      }
    }
  }

  /**
   * set absolute tolerance<br>
   * default usually: 1e-6
   */
  public void setAbsoluteTolerance(double t) {
    tol = t;
  }

  /**
   * set maximal factor m<br>
   * new stepsize is smaller than m *old_stepsize <br>
   * should be larger than 1
   */
  public void setMaxFac(double m) {
    maxFac = m;
  }

  /**
   * set safety factor s<br>
   * multiplies proposed new stepsize<br>
   * should be smaller than 1 <br>
   * usually approx. 1
   */
  public void setSafety(double s) {
    safety = s;
  }
}

previous    contents     next

Peter Junglas 20.12.1999