...one of the most highly
regarded and expertly designed C++ library projects in the
world.

— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards

This is the documentation for a snapshot of the develop branch, built from commit dcea408971.

Home | Libraries | People | FAQ | More |

Imaging, you want to numerically integrate a harmonic oscillator with friction.
The equations of motion are given by *x'' = -x + γ x'*.
Odeint only deals with first order ODEs that have no higher derivatives than
x' involved. However, any higher order ODE can be transformed to a system
of first order ODEs by introducing the new variables *q=x*
and *p=x'* such that *w=(q,p)*. To
apply numerical integration one first has to design the right hand side of
the equation *w' = f(w) = (p,-q+γ p)*:

/* The type of container used to hold the state vector */ typedef std::vector< double > state_type; const double gam = 0.15; /* The rhs of x' = f(x) */ void harmonic_oscillator( const state_type &x , state_type &dxdt , const double /* t */ ) { dxdt[0] = x[1]; dxdt[1] = -x[0] - gam*x[1]; }

Here we chose `vector<double>`

as the state type, but others are also possible, for example `boost::array<double,2>`

. odeint is designed in such a way that
you can easily use your own state types. Next, the ODE is defined which is
in this case a simple function calculating *f(x)*. The
parameter signature of this function is crucial: the integration methods
will always call them in the form ```
f(x,
dxdt,
t)
```

(there are exceptions for some special routines). So, even if there is no
explicit time dependence, one has to define `t`

as a function parameter.

Now, we have to define the initial state from which the integration should start:

state_type x(2); x[0] = 1.0; // start at x=1.0, p=0.0 x[1] = 0.0;

For the integration itself we'll use the `integrate`

function, which is a convenient way to get quick results. It is based on
the error-controlled `runge_kutta54_cash_karp`

stepper (5th order) and uses adaptive step-size.

size_t steps = integrate( harmonic_oscillator , x , 0.0 , 10.0 , 0.1 );

The integrate function expects as parameters the rhs of the ode as defined
above, the initial state `x`

,
the start-and end-time of the integration as well as the initial time step=size.
Note, that `integrate`

uses
an adaptive step-size during the integration steps so the time points will
not be equally spaced. The integration returns the number of steps that were
applied and updates x which is set to the approximate solution of the ODE
at the end of integration.

It is also possible to represent the ode system as a class. The rhs must then be implemented as a functor - a class with an overloaded function call operator:

/* The rhs of x' = f(x) defined as a class */ class harm_osc { double m_gam; public: harm_osc( double gam ) : m_gam(gam) { } void operator() ( const state_type &x , state_type &dxdt , const double /* t */ ) { dxdt[0] = x[1]; dxdt[1] = -x[0] - m_gam*x[1]; } };

which can be used via

harm_osc ho(0.15); steps = integrate( ho , x , 0.0 , 10.0 , 0.1 );

In order to observe the solution during the integration steps all you have to do is to provide a reasonable observer. An example is

struct push_back_state_and_time { std::vector< state_type >& m_states; std::vector< double >& m_times; push_back_state_and_time( std::vector< state_type > &states , std::vector< double > × ) : m_states( states ) , m_times( times ) { } void operator()( const state_type &x , double t ) { m_states.push_back( x ); m_times.push_back( t ); } };

which stores the intermediate steps in a container. Note, the argument structure of the ()-operator: odeint calls the observer exactly in this way, providing the current state and time. Now, you only have to pass this container to the integration function:

vector<state_type> x_vec; vector<double> times; steps = integrate( harmonic_oscillator , x , 0.0 , 10.0 , 0.1 , push_back_state_and_time( x_vec , times ) ); /* output */ for( size_t i=0; i<=steps; i++ ) { cout << times[i] << '\t' << x_vec[i][0] << '\t' << x_vec[i][1] << '\n'; }

That is all. You can use functional libraries like Boost.Lambda or Boost.Phoenix to ease the creation of observer functions.

The full cpp file for this example can be found here: harmonic_oscillator.cpp