Boost
C++ Libraries
...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 an older version of Boost and was released in 2024. The current version is 1.89.0.
odeint can also be used to solve ordinary differential equations defined on lattices. A prominent example is the Fermi-Pasta-Ulam system [8] . It is a Hamiltonian system of nonlinear coupled harmonic oscillators. The Hamiltonian is
H = Σi pi2/2 + 1/2 ( qi+1 - qi )^2 + β / 4 ( qi+1 - qi )^4
Remarkably, the Fermi-Pasta-Ulam system was the first numerical experiment to be implemented on a computer. It was studied at Los Alamos in 1953 on one of the first computers (a MANIAC I) and it triggered a whole new tree of mathematical and physical science.
Like the Solar System, the FPU is solved again by a symplectic solver, but in this case we can speed up the computation because the q components trivially reduce to dqi / dt = pi. odeint is capable of doing this performance improvement. All you have to do is to call the symplectic solver with an state function for the p components. Here is how this function looks like
typedef vector< double > container_type; struct fpu { const double m_beta; fpu( const double beta = 1.0 ) : m_beta( beta ) { } // system function defining the ODE void operator()( const container_type &q , container_type &dpdt ) const { size_t n = q.size(); double tmp = q[0] - 0.0; double tmp2 = tmp + m_beta * tmp * tmp * tmp; dpdt[0] = -tmp2; for( size_t i=0 ; i<n-1 ; ++i ) { tmp = q[i+1] - q[i]; tmp2 = tmp + m_beta * tmp * tmp * tmp; dpdt[i] += tmp2; dpdt[i+1] = -tmp2; } tmp = - q[n-1]; tmp2 = tmp + m_beta * tmp * tmp * tmp; dpdt[n-1] += tmp2; } // calculates the energy of the system double energy( const container_type &q , const container_type &p ) const { // ... } // calculates the local energy of the system void local_energy( const container_type &q , const container_type &p , container_type &e ) const { // ... } };
You can also use std::array< double , N >
for the state type.
Now, you have to define your initial values and perform the integration:
const size_t n = 64; container_type q( n , 0.0 ) , p( n , 0.0 ); for( size_t i=0 ; i<n ; ++i ) { p[i] = 0.0; q[i] = 32.0 * sin( double( i + 1 ) / double( n + 1 ) * M_PI ); } const double dt = 0.1; typedef symplectic_rkn_sb3a_mclachlan< container_type > stepper_type; fpu fpu_instance( 8.0 ); integrate_const( stepper_type() , fpu_instance , make_pair( boost::ref( q ) , boost::ref( p ) ) , 0.0 , 1000.0 , dt , streaming_observer( cout , fpu_instance , 10 ) );
The observer uses a reference to the system object to calculate the local energies:
struct streaming_observer { std::ostream& m_out; const fpu &m_fpu; size_t m_write_every; size_t m_count; streaming_observer( std::ostream &out , const fpu &f , size_t write_every = 100 ) : m_out( out ) , m_fpu( f ) , m_write_every( write_every ) , m_count( 0 ) { } template< class State > void operator()( const State &x , double t ) { if( ( m_count % m_write_every ) == 0 ) { container_type &q = x.first; container_type &p = x.second; container_type energy( q.size() ); m_fpu.local_energy( q , p , energy ); for( size_t i=0 ; i<q.size() ; ++i ) { m_out << t << "\t" << i << "\t" << q[i] << "\t" << p[i] << "\t" << energy[i] << "\n"; } m_out << "\n"; clog << t << "\t" << accumulate( energy.begin() , energy.end() , 0.0 ) << "\n"; } ++m_count; } };
The full cpp file for this FPU example can be found here fpu.cpp