User-friendly Python interface to ODE solvers

Many Fortran and C libraries to solve ODEs are cumbersome to use due to numerous inputs and outputs. We developed a Python package that wraps existing ODE solvers in a consistent, easy-to-use class interface. Options are given sensible defaults, which may be overridden. By establishing a common interface, a user can easily try different solvers without having to learn the intricacies of each solver.

The ODE function can be written in Python, C, or Fortran. The solver directly calls the Fortran/C function, eliminating the Python overhead during integration. This yields the high performance required for large 2D and 3D problems.

Download

Source code is available under the GPL:
ode-0.1a4.tar.gz (242K)

This is a fairly early release, so there are undoubtedly bugs and rough edges. Feedback to <mrgates2 @ uiuc.edu> is welcome.

Presentations

Current solvers

LSODE — Livermore Solver for ODEs
Adams and BDF methods with stiffness detection.
RKC — Runge-Kutta-Chebyshev method
Stabilized explicit method for use with mildly stiff problems.
Forward Euler
Backward Euler
Trapezoid
Fourth-order Runge-Kutta
Suite of common integrators, implemented in Python.

Interface comparison

Here is a comparison of the Python and Fortran code to drive the LSODE integrator, with corresponding sections shaded in color. Note the simplicity and straight-forwardness of the Python implementation.

Python Fortran
Interface Standardized Solver-specific
Options Defaults, can override Specify numerous options
Memory Automatically managed Size based on problem and method
Errors Raises exceptions Explicitly check
Sample code
from ode.lsode import lsode
import myode
t0 = 0.
tf = 0.7
y0 = myode.initial_condition( t0 )

try:
    solver = lsode( myode.f, t0, y0 )
    solver.reltol = 1e-3
    solver.set_jacobian_bandwidth( 19*19, 19*19 )
    y = solver.integrate( tf )

    print "Steps",   solver.steps
    print "F evals", solver.function_evals

except Exception, e:
    print e
integer neq, lrw, liw
parameter (neq=6859, liw=6879, lrw=7496909)
integer istate, itask, itol, iopt, mf, iwork(liw)
double precision atol, rtol, t0, tf,   rwork(lrw), y(neq) 

itol     = 1
rtol     = 1.0d-3 
atol     = 1.0d-3
iopt     = 0 
mf       = 25
itask    = 1 
istate   = 1
iwork(1) = 19*19 
iwork(2) = 19*19

t0 = 0.0
tf = 0.7 
call initial_condition( neq, t0, y ) 

call dlsode( f, neq, y, t0, tf, itol, rtol, atol,
  itask, istate, iopt, rwork, lrw, iwork, liw, jac, mf )

if (istate .lt. 0) then
  write(6,*) 'Failed istate=', istate
else
  write (6,*) 'Steps  ', iwork(11)
  write (6,*) 'F evals', iwork(12)
endif 

History

This work started as a summer internship at Lawrence Livermore National Lab in 2005. Steven Lee, my mentor, gave much of the vision for the project. Pat Miller was our Python guru who answered many questions. After leaving LLNL, I re-implemented the interface and added some integrators written in Python. The new interface is a bit slimmer and uses NumPy instead of Numeric.