3

I was trying to implement a simple ode to test whether boost.odeint is supporting the usage of boost.units. However my example is failing at compilation. Is it my code, or doesn't boost.odeint support boost.Units for dimensional analysis?

#include<boost/units/systems/si.hpp>
#include<boost/numeric/odeint.hpp>

typedef boost::units::quantity<boost::units::si::length,double> state_type;
typedef boost::units::quantity<velocity,double> dev_type;
typedef boost::units::quantity<boost::units::si::time,double> time_type;


using namespace boost::units::si;

void exponential_decay(const state_type &x, dev_type &dxdt, time_type t){
    dxdt = -0.0001*x/second;
}

int main(){
    state_type startValue = 10*meter;

    using namespace boost::numeric::odeint;
    auto steps = integrate(exponential_decay, startValue, 0.0*second,
            10.0*second, 0.1*second);

    return 0;
}
Mojomoko
  • 471
  • 5
  • 12
  • Relevant http://www.boost.org/doc/libs/1_66_0/libs/numeric/odeint/doc/html/boost_numeric_odeint/tutorial/using_boost__units.html – sehe Feb 28 '18 at 03:11

2 Answers2

2

So, after initially failing to find a working set of state_type, algebra and operations¹ @Arash supplied the missing link.

However, for completeness, you do not need to bring the heavy machinery (fusion_algebra.hpp). These are for the more general cases, e.g. where the state type is not a single value.

In this simple case, all that was really required was to specify the algebra, instead of going with the default. This involves declaring a stepper_type, like:

using stepper_type = runge_kutta4<length_type, double, velocity_type,
                            time_type, vector_space_algebra>;

Next, we pick an itegration algorithm overload that allows us to supply that stepper:

Live On Coliru

#include <boost/numeric/odeint.hpp>
#include <boost/units/systems/si.hpp>

typedef boost::units::quantity<boost::units::si::length, double> length_type;
typedef boost::units::quantity<boost::units::si::velocity, double> velocity_type;
typedef boost::units::quantity<boost::units::si::time, double> time_type;

namespace si = boost::units::si;

void exponential_decay(const length_type &x, velocity_type &dxdt, time_type /*t*/) { dxdt = -0.0001 * x / si::second; }

int main() {
    using stepper_type = boost::numeric::odeint::runge_kutta4<
        length_type, double, velocity_type, time_type, boost::numeric::odeint::vector_space_algebra>;

    length_type startValue = 10 * si::meter;
    auto steps = integrate_const(
            stepper_type{}, exponential_decay,
            startValue, 0.0 * si::second, 10.0 * si::second,
            0.1 * si::second);

    std::cout << "Steps: " << steps << "\n";
}

Prints

Steps: 100

¹ from just looking at http://www.boost.org/doc/libs/1_66_0/libs/numeric/odeint/doc/html/boost_numeric_odeint/odeint_in_detail/state_types__algebras_and_operations.html and http://www.boost.org/doc/libs/1_66_0/doc/html/boost_units/Quantities.html

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Completely rewrote the answer after learning about [this](http://www.boost.org/doc/libs/1_66_0/libs/numeric/odeint/doc/html/boost_numeric_odeint/tutorial/using_boost__units.html) – sehe Feb 28 '18 at 03:34
1

Use boost::fusion to fix the problem.

#include <iostream>
#include <vector>

#include <boost/numeric/odeint.hpp>
#include <boost/numeric/odeint/algebra/fusion_algebra.hpp>
#include <boost/numeric/odeint/algebra/fusion_algebra_dispatcher.hpp>

#include <boost/units/systems/si/length.hpp>
#include <boost/units/systems/si/time.hpp>
#include <boost/units/systems/si/velocity.hpp>
#include <boost/units/systems/si/acceleration.hpp>
#include <boost/units/systems/si/io.hpp>

#include <boost/fusion/container.hpp>

using namespace std;
using namespace boost::numeric::odeint;
namespace fusion = boost::fusion;
namespace units = boost::units;
namespace si = boost::units::si;

typedef units::quantity< si::time , double > time_type;
typedef units::quantity< si::length , double > length_type;
typedef units::quantity< si::velocity , double > velocity_type;
typedef units::quantity< si::acceleration , double > acceleration_type;
typedef units::quantity< si::frequency , double > frequency_type;

typedef fusion::vector< length_type  > state_type;
typedef fusion::vector< velocity_type > deriv_type;

void exponential_decay( const state_type &x , deriv_type &dxdt , time_type t )
{
    fusion::at_c< 0 >( dxdt ) = (-0.0001/si::second)* fusion::at_c< 0 >( x );
}

void observer(const state_type &x,const time_type &t )
{
}

int main( int argc , char**argv )
{
    typedef runge_kutta_dopri5< state_type , double , deriv_type , time_type > stepper_type;

    state_type startValue( 1.0 * si::meter );

    auto steps=integrate_adaptive(
        make_dense_output( 1.0e-6 , 1.0e-6 , stepper_type()),
        exponential_decay,
        startValue , 0.0 * si::second , 100.0 * si::second , 0.1 * si::second , observer);

    std::cout<<"steps: "<<steps<<std::endl;

    return 0;
}
Arash
  • 2,114
  • 11
  • 16
  • "fix" the problem? More like to adapt the types :) This is awesome stuff. I'll have to read this several times over. – sehe Feb 28 '18 at 03:03
  • It''ll be interesting to see whether the compiler is able to optimize away the type overhead (after inlining). – sehe Feb 28 '18 at 03:05
  • So, after reading through the relevant bits, I concluded that in this case, the `fusion_algebra` is not required, and `vector_space_algebra` can save a bit of complexity, and a *lot* of compile time. Thanks for the help! – sehe Feb 28 '18 at 03:33
  • @sehe, Thanks for checking the overheads and other details. – Arash Feb 28 '18 at 12:48