dynamics

# dynamics.py
# ------------------------------------------------------------------------------

 
Classes
       
Dynamics

 
class Dynamics()
    Class for solving the initial value problem for systems of ordinary 
differential equations. For uniformity all methods return - besides a 
list/stack or dict of solutions at each time point - (a suggestion for) 
the next time point in the time stepping, including those methods that are 
dependent on externally controlled time stepping and do not have their 
own time stepping. The methods of this class support state vectors which 
are either dicts or lists/stacks or 'd' arrays. Method "matrixexp" is a 
bit special and takes (=requires) a column vector from the misclib.Matrix 
class as the input state vector.
 
Instances are punched out with a construct like (breakpoints is optional, 
cf tiptoe function):
    solution = Dynamics(model, state [, breakpoints]) 
where "model" is the name of the function of the time and the state vector 
that returns the values of the derivatives. "state" can be a dict, a stack, 
a list, a 'd' array or a Matrix column vector (the only solver method 
capable of handling the latter is matrixexp). The different solvers 
(methods of solution) are called like 
    t = tstart
    while t <= tfin:
        tnext    = t + deltat
        t, state = solution.method(t, tnext) 
where "method" is the name of the solver method; euler, runge_kutta4 etc. 
 
If the state vector is the result put out by the program using this class, 
the corresponding time to put out is "tnext" - if derivatives are the 
result put out, then "t" is more correct, or it may even depend on whether 
the solution method is explicit or implicit...
 
The advantage in using dicts is - besides more clarity due to the 
possibility of using text strings rather than indices to identify the 
elements of the derivatives vector and the state vector - is that the 
order in which the different elements of the vectors are treated in the 
function called by the methods in Dynamics is not crucial!
 
Even if some of the solver methods belonging to this class handles 
reasonably stiff problems very well, you might consider the StiffDynamics 
class for problems which are very stiff. The solver methods of the 
StiffDynamics class are rather slow, though.
 
The user if referred to Dahlquist, Bjorck & Anderson Ch. 8 for the theory 
behind the majority of the methods used in this class.
 
  Methods defined here:
__init__(self, model, state, breakpoints=None)
Initiate the model and the state dict/list/array, and initiate 
history lists used by some of the methods.
abm2(self, t, tnext, tolf=1.4901161193847656e-08, maxniter=4)
Adams-Bashforth-Moulton 2:nd order, a predictor-corrector method. The 
algorithm is claimed to be more accurate for a reasonably large number 
of iterations but is also claimed to be more stable for a smaller number 
of iterations. Different orders of Adams-Bashforth-Moulton may have 
different accuracy and stability properties - this is the reason for 
three abmX methods being here. abm2 uses one computation of the 
model/derivative function per iteration.
 
'tolf' is the maximum allowed fractional difference between the sum of 
the absolute values of the state vector variables for a prediction and 
a correction. 'maxniter' is the maximum number of iterations made. 
The computation is NOT stopped if convergence is not reached, but a 
warning will be issued to stdout.
 
This method uses the first time step twice to build its history at 
the very outset. After that the historic points will differ from 
one another.
abm3(self, t, tnext, tolf=1.4901161193847656e-08, maxniter=4)
Adams-Bashforth-Moulton 3:rd order, a predictor-corrector method. The 
algorithm is claimed to be more accurate for a reasonably large number 
of iterations but is also claimed to be more stable for a smaller number 
of iterations. Different orders of Adams-Bashforth-Moulton may have 
different accuracy and stability properties - this is the reason for 
three abmX methods being here. abm3 uses one computation of the 
model/derivative function per iteration.
 
'tolf' is the maximum allowed fractional difference between the sum of 
the absolute values of the state vector variables for a prediction and 
a correction. 'maxniter' is the maximum number of iterations made. 
The computation is NOT stopped if convergence is not reached, but a 
warning will be issued to stdout.
 
This method uses abm2 to build up its history before it has created 
its own history. Cf. abm2 for details.
abm4(self, t, tnext, tolf=1.4901161193847656e-08, maxniter=4)
Adams-Bashforth-Moulton 4:th order, a predictor-corrector method. The 
algorithm is claimed to be more accurate for a reasonably large number 
of iterations but is also claimed to be more stable for a smaller number 
of iterations. Different orders of Adams-Bashforth-Moulton may have 
different accuracy and stability properties - this is the reason for 
three abmX methods being here. abm4 uses one computation of the 
model/derivative function per iteration.
 
'tolf' is the maximum allowed fractional difference between the sum of 
the absolute values of the state vector variables for a prediction and 
a correction. 'maxniter' is the maximum number of iterations made. 
The computation is NOT stopped if convergence is not reached, but a 
warning will be issued to stdout.
 
This method uses abm3 to build up its history before it has created 
its own history. Cf. abm3 for details.
adams_bashforth2(self, t, tnext)
Adams-Bashforth 2:nd order, a linear multistep method. The method 
uses the derivative-function-value history and is fast since it only 
uses one computation of the model/derivative function per time step. 
Some of the discretization error problems are reduced. The method 
should work well for smooth problems, but adams_bashforth4 would 
normally be recommended (this method is used by other methods, a 
fact that is the main reason for it being here in the first place).
 
This method uses the first time step twice to build its history at 
the very outset. After that the historic points will differ from 
one another.
adams_bashforth3(self, t, tnext)
Adams-Bashforth 3:rd order, a linear multistep method. The method 
uses the derivative-function-value history and is fast since it only 
uses one computation of the model/derivative function per time step. 
Some of the discretization error problems are reduced. The method 
should work well for smooth problems, but adams_bashforth4 would 
normally be recommended (this method is used by adams_bashforth4, a 
fact that is the main reason for it being here in the first place). 
 
This method uses adams_bashforth2 to build up its history before 
it has created its own history. Cf. adams_bashforth2 for details.
adams_bashforth4(self, t, tnext)
Adams-Bashforth 4:th order, a linear multistep method. The method 
uses the derivative-function-value history and is fast since it only 
uses one computation of the model/derivative function per time step. 
Some of the discretization error problems are reduced. The method 
should work well for smooth problems. 
 
This method uses adams_bashforth3 to build up its history before 
it has created its own history. Cf. adams_bashforth3 for details.
euler(self, t, tnext)
Simple forward Euler stepping with constant time step. It is fast 
since it only uses uses one computation of the model/derivative 
function per time step, but the numerics may be problematic due to 
the large discretization error - the Euler scheme is only first-order 
correct. Trying to overcome this by using  very short time steps may 
produce a large accumulated round-off error. But it is fast, and 
sometimes useful.
euler_ex(self, t, tnext)
An enhanced Euler solver with one Richardson extrapolation step. 
The solution is first computed using the full time step, then twice 
consequtively using the halved time step. The the solutions are 
combined using the Richardson extrapolation scheme. The model
/derivative function is called twice.
euler_plus(self, t, tnext)
The implicit trapezoidal method
        ynp1 - yn  -  0.5*h*(f(tn, yn) + f(tnp1, ynp1))  =  0 
used in a single-step predictor-corrector scheme using the simple 
Euler scheme for the predictor. It uses two computations of the 
model/derivative function per time step.
heun(self, t, tnext)
Heun's method is really second-order Runge-Kutta. It uses two 
computations of the model/derivative function per time step.
matrixexp(self, t, tnext, tolf=4.4408920985006262e-16, maxterms=128)
Solver using the matrix exponential method. 
 
This solver only works for systems of odes which are linear in the 
state vector. The state vector must be entered as a matrix column 
vector and coeffmatrix as a matrix containing the present (possibly 
time dependent) coefficients, both objects belonging to the 
misclib.Matrix class. The differential equation is:  dY/deltat = C + M*Y
where Y is the state column vector, C is a matrix column vector of 
constants (constant between t and t+deltat), and M is a matrix of 
constant coefficients (constant between t and t+deltat).
 
The solution is Y(t+deltat) = C*deltat + exp(M*Y(t)*deltat), where 
the exponential is expanded in a McLaurin series based on t as the 
zero point in time, using 'tolf' as the fractional tolerance (based 
on the fractional difference between the present sum and the previous) 
and 'maxterms' as the absolute maximum number of terms.
 
AND: matrixexp handles stiff systems well and with good accuracy !!!!
modmidpoint(self, t, tnext, nsteps=4)
W.B. Gragg's modified midpoint method. It uses nsteps computations of 
the model/derivative function for each time step.
modmidpoint_ex(self, t, tnext, nsteps=2)
The modified midpoint method using one Richardson extrapolation 
step, so it really uses nsteps + 2*nsteps computations of the 
model/derivative function for each time step!
rkck45(self, t, tnext, individ=False, tolf=3.637978807091713e-12, factor=0.25, maxniter=10)
This solver uses the Runge-Kutta-Cash-&-Karp algorithm (cf. 
http://en.wikipedia.org/wiki/Cash-Karp): fourth and fifth order 
Runge-Kutta with Cash & Karps's coefficients is used in an adaptive 
step size scheme that uses the difference between fourth and fifth 
order solutions to control the stepping. Six computations of the 
model/derivative function are used in each iteration. Very 
efficient, accurate and also stable for most problems.
 
When individ==False, 'tolf' is the maximum allowed fractional difference 
between the sum of the absolute values of the state vector variables 
for the fourth and fifth order solutions. When individ==True, the 
comparison between the fourth and fifth order solutions is made for 
each of the stage variables. 'factor' is the factor by which the 
present time step is multiplied when the fractional difference is 
greater than tolf. 'maxniter' is the maximum number of iterations made. 
The computation is NOT stopped if convergence is not reached, but a 
warning will be issued to stdout.
 
NB. This method uses adaptive step size control which means that it may 
compute the model/derivative function for times prior to what was just 
used in the same single call. This might cause problems if there are 
setups in the model that uses values of the derivatives or states from 
prior calls. Short time steps might make this a minor problem, but 
"ten cuidado"...
rke4(self, t, tnext)
Runge-Kutta - England 4th order version. The algorithm is the same 
as the one used in GASP IV using two halved steps, with the difference 
that adaptive step size control is not used. The model/derivative 
function is computed eight times per full time step.
rkf45(self, t, tnext, individ=False, tolf=2.9103830456733704e-11, factor=0.25, maxniter=10)
The Runge-Kutta-Fehlberg algorithm (inspired by Forsythe-Malcolm-Moler):
fourth and fifth order Runge-Kutta with Fehlberg's coefficients is used 
in an adaptive step size scheme that uses the difference between fourth 
and fifth order solutions to control the stepping. Six computations of 
the model/derivative function are used in each iteration. Efficient, 
accurate and also stable for most problems.
 
When individ==False, 'tolf' is the maximum allowed fractional difference 
between the sum of the absolute values of the state vector variables 
for the fourth and fifth order solutions. When individ==True, the 
comparison between the fourth and fifth order solutions is made for 
each of the stage variables. 'factor' is the factor by which the 
present time step is multiplied when the fractional difference is 
greater than tolf. 'maxniter' is the maximum number of iterations made. 
The computation is NOT stopped if convergence is not reached, but a 
warning will be issued to stdout.
 
NB. This method uses adaptive step size control which means that it may 
compute the model/derivative function for times prior to what was just 
used in the same single call. This might cause problems if there are 
setups in the model that uses values of the derivatives or states from 
prior calls. Short time steps might make this a minor problem, but "ten 
cuidado"...
runge_kutta4(self, t, tnext)
Standard fourth-order Runge-Kutta. It uses four computations of 
the model/derivative function per time step.
tiptoe(self, t, tstep)
Method used to get around known points of discontinuity or points at 
which there is continuity but where the first derivative is not defined 
in the equations defining a set of odes, such as step functions or 
Heaviside functions for the coefficients. It avoids those points by 
allowing the solution to progress in time to a point exactly on the 
singularity and sets off the continued progress from a point just after 
the discontinuity, using he solution at the point of the discontinuity 
as a new starting point just after the discontinuity. "Just after" is 
expressed in terms of machine epsilon. The method is used like this:
 
    t, tnext = solution.tiptoe(t, tstep)
    t, state = solution.method(t, tnext)
 
For tiptoe to be used, breakpoints must be given to the instance object 
punched out from Dynamics. breakpoints may be a single float, a list or 
a tuple. The points will be deleted from the list when they are passed.
trapezoid(self, t, tnext, tolf=1.4901161193847656e-08, maxniter=4)
The implicit trapezoidal method:
      ynp1 - yn  -  0.5*h*(f(tn, yn) + f(tnp1, ynp1))  =  0 
used in a predictor-corrector scheme using the simple Euler 
scheme for the predictor. 
 
'tolf' is the maximum allowed fractional difference between the sum of 
the absolute values of the state vector variables for a prediction and 
a correction. 'maxniter' is the maximum number of iterations made. 
The computation is NOT stopped if convergence is not reached, but a 
warning will be issued to stdout.

Data descriptors defined here:
__dict__
dictionary for instance variables (if defined)
__weakref__
list of weak references to the object (if defined)

 
Data
        MACHEPS = 2.2204460492503131e-16
SQRTMACHEPS = 1.4901161193847656e-08
TINY = 1.4916681462400413e-154
TWOMACHEPS = 4.4408920985006262e-16