Click here to Skip to main content
15,896,557 members
Articles / Programming Languages / CUDA

odeint v2 - Solving ordinary differential equations in C++

Rate me:
Please Sign up or sign in to vote.
4.64/5 (18 votes)
19 Oct 2011CPOL19 min read 130.2K   2.8K   34  
odeint v2 - Solving ordinary differential equations in C++
/*
 * taylor.hpp
 *
 *  Created on: Apr 2, 2011
 *      Author: karsten
 */

#ifndef BOOST_NUMERIC_ODEINT_STEPPER_TAYLOR_HPP_
#define BOOST_NUMERIC_ODEINT_STEPPER_TAYLOR_HPP_

#include <ostream>
#include <cmath>
#include <boost/array.hpp>

#include <iostream>
using namespace std;
#define tab "\t"

// general boost includes
#include <boost/static_assert.hpp>
#include <boost/math/special_functions/binomial.hpp>
#include <boost/math/special_functions/factorials.hpp>

// fusion includes
#include <boost/fusion/include/is_sequence.hpp>
#include <boost/fusion/include/size.hpp>
#include <boost/fusion/include/at.hpp>
#include <boost/fusion/include/transform.hpp>

// mpl includes
#include <boost/mpl/range_c.hpp>
#include <boost/mpl/for_each.hpp>

// proto includes
#include <boost/proto/proto.hpp>

/*
 * OK # dt entfernen aus context
 * OK # do_step zu try_step
 * OK # Skalierung
 *   OK -- dt_fac # Namen für Skalierungsfaktor finden
 *   OK # Faktor einschleusen in context
 *   OK # Nach jedem Durchgang einer Ordnung Skalierungsfaktor neu berechnen und schon
 *     berechnete Ableitungen skalieren
 * # Fehler als Member parameter
 * # Fehler anhand der hoechsten Ableitung berechnen
 * # Schrittweite bestimmen
 * # Schritt ausfuehren
 */


namespace boost {
namespace numeric {
namespace odeint {


namespace taylor_adf
{

	namespace proto = boost::proto;
	namespace fusion = boost::fusion;
	namespace proto = boost::proto;

	template< typename I > struct placeholder : I {};

	proto::terminal< placeholder< mpl::size_t< 0 > > >::type const arg1 = {};
	proto::terminal< placeholder< mpl::size_t< 1 > > >::type const arg2 = {};
	proto::terminal< placeholder< mpl::size_t< 2 > > >::type const arg3 = {};

	template< typename I >
	std::ostream& operator<<( std::ostream &s , const placeholder< I > &p )
	{
		s << "placeholder<" << I::value << ">";
		return s;
	}


	/*
	 * eventuel array< double , n > dt mit potenzen anlegen
	 */
	template< class State , size_t MaxOrder >
	struct taylor_context
	{
		typedef State state_type;
		typedef boost::array< state_type , MaxOrder > deriv_type;

		size_t which;
		const state_type &x;
		deriv_type &derivs;
		double &dt_fac;

		taylor_context( size_t _which , const state_type &_x , deriv_type &_derivs , double &_dt_fac )
		: which( _which ) , x( _x ) , derivs( _derivs ) , dt_fac( _dt_fac ) { }
	};


	template< typename Grammar >
	struct plus_transform : proto::transform< plus_transform< Grammar > >
	{
		template< typename Expr , typename State , typename Data >
		struct impl : proto::transform_impl< Expr , State , Data >
		{
			typedef double result_type;

			result_type operator ()(
					typename impl::expr_param expr ,
					typename impl::state_param state ,
					typename impl::data_param data ) const
			{
//				cout << "Plus transform" << endl;
				return Grammar()( proto::left( expr ) , state , data )
                     + Grammar()( proto::right( expr ) , state , data );
			}
		};
	};


	template< typename Grammar >
	struct minus_transform : proto::transform< minus_transform< Grammar > >
	{
		template< typename Expr , typename State , typename Data >
		struct impl : proto::transform_impl< Expr , State , Data >
		{
			typedef double result_type;

			result_type operator ()(
					typename impl::expr_param expr ,
					typename impl::state_param state ,
					typename impl::data_param data ) const
			{
//				cout << "Minus transform" << endl;
				return Grammar()( proto::left( expr ) , state , data )
                     - Grammar()( proto::right( expr ) , state , data );
			}
		};
	};



	template< typename Grammar >
	struct multiplies_transform : proto::transform< multiplies_transform< Grammar > >
	{
		template< typename Expr , typename State , typename Data >
		struct impl : proto::transform_impl< Expr , State , Data >
		{
			typedef double result_type;

			result_type operator()(
					typename impl::expr_param expr ,
					typename impl::state_param state ,
					typename impl::data_param data ) const
			{
//				cout << "Multiplies transform" << endl;
				typedef typename impl::data data_type;

				double tmp = 0.0;
				for( size_t k=0 ; k<=data.which ; ++k )
				{
					data_type data1( k ,  data.x , data.derivs , data.dt_fac );
					data_type data2( data.which - k ,  data.x , data.derivs , data.dt_fac );

//					tmp += boost::math::binomial_coefficient< double >( data.which , k )
//						* Grammar()( proto::left( expr ) , state , data1 )
//						* Grammar()( proto::right( expr ) , state , data2 );

					tmp += Grammar()( proto::left( expr ) , state , data1 )
						 * Grammar()( proto::right( expr ) , state , data2 );

				}

				return tmp;
			}
		};
	};


	struct terminal_double_transform : proto::transform< terminal_double_transform >
	{
		template< typename Expr , typename State , typename Data >
		struct impl : proto::transform_impl< Expr , State , Data >
		{
			typedef double result_type;

			result_type operator()(
					typename impl::expr_param expr ,
					typename impl::state_param state ,
					typename impl::data_param data ) const
			{
//				cout << "Terminal double transform" << endl;
				return ( data.which == 0 ) ? proto::value( expr ) : 0.0;
			}
		};
	};


	template< typename Index >
	struct terminal_placeholder_transform : proto::transform< terminal_placeholder_transform< Index > >
	{
		template< typename Expr , typename State , typename Data >
		struct impl : proto::transform_impl< Expr , State , Data >
		{
			typedef double result_type;

			result_type operator()(
					typename impl::expr_param expr ,
					typename impl::state_param state ,
					typename impl::data_param data ) const
			{
//				cout << "Terminal placeholder transform" << endl;
				typedef typename impl::expr expr_type;
				typedef typename expr_type::proto_args args_type;
				typedef typename args_type::child0 index_type;
				const size_t index = index_type::value;

				return ( data.which == 0 ) ? data.x[ index ] : data.derivs[ data.which - 1 ][ index ];
			}
		};
	};

	template< typename Grammar >
	struct right_shift_transform : proto::transform< right_shift_transform< Grammar > >
	{
		template< typename Expr , typename State , typename Data >
		struct impl : proto::transform_impl< Expr , State , Data >
		{
			typedef double result_type;

			result_type operator ()(
					typename impl::expr_param expr ,
					typename impl::state_param state ,
					typename impl::data_param data ) const
			{
//				cout << "Right shift transform" << endl;
				return Grammar()( proto::left( expr ) , state , data )
						+ ( data.which == 0 ) ? proto::value( proto::right( expr ) ) : 0.0;
			}
		};
	};

	template< typename Grammar >
	struct left_shift_transform : proto::transform< left_shift_transform< Grammar > >
	{
		template< typename Expr , typename State , typename Data >
		struct impl : proto::transform_impl< Expr , State , Data >
		{
			typedef double result_type;

			result_type operator ()(
					typename impl::expr_param expr ,
					typename impl::state_param state ,
					typename impl::data_param data ) const
			{
//				cout << "Right shift transform" << endl;
				return Grammar()( proto::right( expr ) , state , data )
						+ ( data.which == 0 ) ? proto::value( proto::left( expr ) ) : 0.0;
			}
		};
	};


	template< typename Grammar >
	struct right_scalar_multiplies_transform : proto::transform< right_scalar_multiplies_transform< Grammar > >
	{
		template< typename Expr , typename State , typename Data >
		struct impl : proto::transform_impl< Expr , State , Data >
		{
			typedef double result_type;

			result_type operator ()(
					typename impl::expr_param expr ,
					typename impl::state_param state ,
					typename impl::data_param data ) const
			{
//				cout << "Right scalar multiplies transform" << endl;
				return Grammar()( proto::left( expr ) , state , data ) * proto::value( proto::right( expr ) );
			}
		};
	};

	template< typename Grammar >
	struct left_scalar_multiplies_transform : proto::transform< left_scalar_multiplies_transform< Grammar > >
	{
		template< typename Expr , typename State , typename Data >
		struct impl : proto::transform_impl< Expr , State , Data >
		{
			typedef double result_type;

			result_type operator ()(
					typename impl::expr_param expr ,
					typename impl::state_param state ,
					typename impl::data_param data ) const
			{
//				cout << "Left scalar multiplies transform" << endl;
				return Grammar()( proto::right( expr ) , state , data ) * proto::value( proto::left( expr ) );
			}
		};
	};





	struct taylor_double_terminal : proto::when< proto::terminal< double > , terminal_double_transform > { };

	template< typename Which >
	struct taylor_placeholder_terminal : proto::when< proto::terminal< placeholder< Which > > , terminal_placeholder_transform< Which > > { };

	template< typename Grammar >
	struct taylor_plus : proto::when< proto::plus< Grammar , Grammar > , plus_transform< Grammar > > { };

	template< typename Grammar >
	struct taylor_minus : proto::when< proto::minus< Grammar , Grammar > , minus_transform< Grammar > > { };

	template< typename Grammar >
	struct taylor_multiplies : proto::when< proto::multiplies< Grammar , Grammar > , multiplies_transform< Grammar > >  { };

	template< typename Grammar >
	struct taylor_shift :
		proto::or_<
			proto::when< proto::plus< Grammar , proto::terminal< double > > , right_shift_transform< Grammar > > ,
			proto::when< proto::plus< proto::terminal< double > , Grammar > , left_shift_transform< Grammar > >
		> { };

	template< typename Grammar >
	struct taylor_scalar_multiplies :
		proto::or_<
			proto::when< proto::multiplies< Grammar , proto::terminal< double > > , right_scalar_multiplies_transform< Grammar > > ,
			proto::when< proto::multiplies< proto::terminal< double > , Grammar > , left_scalar_multiplies_transform< Grammar > >
		> { };





	struct taylor_transform :
	proto::or_
	<
		taylor_shift< taylor_transform > ,
		taylor_scalar_multiplies< taylor_transform > ,
		taylor_double_terminal ,
		taylor_placeholder_terminal< proto::_ > ,
		taylor_plus< taylor_transform > ,
		taylor_minus< taylor_transform > ,
		taylor_multiplies< taylor_transform >
	>
	{ };


	template< class System , class State , size_t Order >
	struct eval_derivs
	{
		typedef State state_type;
		typedef taylor_context< state_type , Order > taylor_context_type;
		typedef typename taylor_context_type::deriv_type deriv_type;

		System m_sys;
		taylor_context_type m_data;

		eval_derivs( System sys , const State &x , deriv_type &derivs , double &dt_fac , size_t which )
		: m_sys( sys ) , m_data( which , x , derivs , dt_fac ) { }

		template< class Index >
		void operator()( Index )
		{
			typedef typename fusion::result_of::at< System , Index >::type expr_type;
			
			/* Mario: changed to make it compile with msvc 9.0
			 * error: invalid type: reference to reference
			 */
			//const expr_type &expr = boost::fusion::at< Index >( m_sys );
			expr_type expr = boost::fusion::at< Index >( m_sys );
			/* end changes */

			double deriv = taylor_transform()( expr , 0.0 , m_data );
			m_data.derivs[ m_data.which ][ Index::value ] = m_data.dt_fac / double( m_data.which + 1 ) * deriv;
		}
	};

	template< class System , class State , size_t Order >
	eval_derivs< System , State , Order > make_eval_derivs( System sys , const State &x , boost::array< State , Order > &derivs , double &dt_fac , size_t i )
	{
		return eval_derivs< System , State , Order >( sys , x , derivs , dt_fac , i );
	}
}

template< size_t N , size_t Order >
class taylor
{
public:

	static const size_t dim = N;
	static const size_t order_value = Order;
	typedef double value_type;
	typedef value_type time_type;
	typedef unsigned short order_type;

	typedef boost::array< value_type , dim > state_type;
	typedef state_type deriv_type;
	typedef boost::array< state_type , order_value > derivs_type;

	taylor( value_type rel_error = 1.0e-14 , value_type abs_error = 1.0e-14 )
	: m_derivs() , m_dt_fac( 1.0 ) , m_rel_error( rel_error ) , m_abs_error( abs_error ) { }

    order_type order( void ) const
    {
    	return order_value;
    }

    order_type stepper_order( void ) const
    {
    	return order_value;
    }

    order_type error_order( void ) const
    {
    	return order_value - 1;
    }




	template< class System >
	void try_step( System sys , state_type &x , time_type &t , time_type &dt )
	{
		BOOST_STATIC_ASSERT(( boost::fusion::traits::is_sequence< System >::value ));
		BOOST_STATIC_ASSERT(( size_t( boost::fusion::result_of::size< System >::value ) == dim ));

		try_step( sys , x , t , x , dt );
	}

	template< class System >
	void try_step( System sys , const state_type &in , time_type &t , state_type &out , time_type &dt )
	{
		BOOST_STATIC_ASSERT(( boost::fusion::traits::is_sequence< System >::value ));
		BOOST_STATIC_ASSERT(( size_t( boost::fusion::result_of::size< System >::value ) == dim ));

		eval_derivs( sys , in , m_derivs , m_dt_fac );

		double max_error = 0.0;
		for( size_t i=0 ; i<dim ; ++i )
		{
			double error = std::abs( m_derivs[order_value-1][i] ) /
					( m_rel_error * std::abs( in[i] ) + m_abs_error );
			max_error = std::max( error , max_error );
		}

		dt = pow( 1.0 / max_error , 1.0 / double( order_value ) );
//		clog << dt << tab << max_error << tab << in[0] << tab << in[1] << tab << in[2] << tab;
//		clog << m_derivs[0][0] << tab << m_derivs[0][1] << tab << m_derivs[0][2] << tab << m_dt_fac << endl;

		for( size_t i=0 ; i<dim ; ++i )
		{
			value_type tmp = 0.0;
			for( size_t k=0 ; k<order_value ; ++k )
				tmp = dt * ( tmp + m_derivs[order_value-1-k][i] );
			out[i] = in[i] + tmp;
		}

//		clog << dt << tab << dt0 << tab << max_error << tab << m_dt_fac;
//		for( size_t j=0 ; j<dim ; ++j ) clog << tab << out[j];
//		clog << endl;

//		clog << endl;
//		for( size_t i=0 ; i<4 ; ++i )
//		{
//			for( size_t j=0 ; j<dim ; ++j )
//				clog << m_derivs[i][j] << "\t";
//			clog << endl;
//		}
//		clog << endl;

		dt *= m_dt_fac;
		t += dt;
	}

	const derivs_type& get_last_derivs( void ) const
	{
		return m_derivs;
	}

	template< class System >
	void eval_derivs( System sys , const state_type &in , derivs_type &der , double &dt_fac ) const
	{
		const double min_error = 1.0e-19;
		const double max_error = 1.0e19;
		const double min_fac = 1.5;
		const double max_fac = 0.6;

		for( size_t i=0 ; i<order_value ; ++i )
		{
			boost::mpl::for_each< boost::mpl::range_c< size_t , 0 , dim > >( make_eval_derivs( sys , in , der , dt_fac , i ) );

//			clog << i << tab << "Deriv : ";
//			for( size_t j=0 ; j<dim ; ++j )
//				clog << tab << der[i][j];
//			clog << endl;

			/*
			 * OK # Fehler bestimmen
			 * OK # Fehler grenzen pruefen
			 * OK  # Falls ja
			 *     OK # dt_fac aendern
			 *     OK # schon berechnete Ableitungen skalieren
			 */
			while( true )
			{
				double err = 0.0;
				for( size_t j=0 ; j<dim ; ++j ) err += std::abs( der[i][j] );
//				clog << i;
//				for( size_t j=0 ; j<dim ; ++j ) clog << tab << der[i][j];
//				clog << tab << err << endl;

				if( err < min_error )
				{
//					clog << i << tab << "min_error : " << err << tab << dt_fac << endl;
					scale_derivs( der , i , min_fac );
					dt_fac *= min_fac;
					continue;
				}
				if( err > max_error )
				{
//					clog << i << tab << "max_error : " << err << tab << dt_fac << endl;
					scale_derivs( der , i , max_fac );
					dt_fac *= max_fac;
					continue;
				}
				break;
			}

		}

	}

	void scale_derivs( derivs_type &der , size_t order , double fac ) const
	{
		double scale = 1.0;
		for( size_t i=0 ; i<=order ; ++i )
		{
			scale *= fac;
			for( size_t j=0 ; j<dim ; ++j )
				der[i][j] *= scale;
		}
	}



private:

	derivs_type m_derivs;
	time_type m_dt_fac;
	value_type m_rel_error;
	value_type m_abs_error;

};





} // namespace odeint
} // namespace numeric
} // namespace boost


#endif /* BOOST_NUMERIC_ODEINT_STEPPER_TAYLOR_HPP_ */

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions