This answer is helpful, but I would like to know how to pass multiple parameters of different types to the ODE model, perhaps in a struct. For my immediate use case, I need to be able to pass one std::array<double, 6>, two std::vector<std::vector<double>> and two two double scalars for a total of four parameters to be passed. In the linked example, as well as in harmonic_oscillator.cpp, there is only a single double passed parameter. Thanks.
Here's an example of the struct I would need passed to the ODE force model and used within the rate equations.
struct T
{
std::array<double, 6> IC;
double S;
double M;
std::vector<std::vector<double>> C;
std::vector<std::vector<double>> WT;
};
I believe I've come up with a struct solution that works, but am not sure if it has any variable/memory scope no-no's. Here's an example:
#include <vector>
#include <boost/numeric/odeint.hpp>
// define structure
struct T
{
std::array<double, 6> IC;
double S;
};
// force model
class harm_osc
{
struct T T1;
public:
harm_osc(struct T G) : T1(G) {}
void operator() ( const std::vector< double > &x , std::vector< double > &dxdt , const double /* t */ )
{
dxdt[0] = x[1];
dxdt[1] = -x[0] - T1.IC[0]*x[1] + T1.S;
}
};
// print integrated state solution
void write_solution( const std::vector< double > &x , const double t )
{
printf("%-6.2f %-6.2f %-6.2f\n", t, x[0], x[1]);
}
// problem setup
int main()
{
std::vector< double > x(2);
x[0] = 1.0;
x[1] = 0.0;
struct T T2;
T2.IC = {0.15, 0.15, 0.15, 0.15, 0.15, 0.15};
T2.S = 0.0;
harm_osc ho(T2);
boost::numeric::odeint::integrate(ho, x, 0.0, 10.0, 0.1, write_solution);
}
Related
I am testing with Eigen::VectorXd as state_type for boost::odeint. I use this code:
#include <Eigen/Eigen>
#include <boost/numeric/odeint.hpp>
#include <boost/numeric/odeint/external/eigen/eigen_algebra.hpp>
#include <iostream>
#include <vector>
template<class T>
struct push_back_state_and_time
{
std::vector<T>& m_states;
std::vector< double >& m_times;
push_back_state_and_time( std::vector<T> &states ,std::vector<double> × )
: m_states(states) , m_times(times) { }
void operator()(const T &x ,double t )
{
m_states.push_back(x);
m_times.push_back(t);
}
};
template<class T>
struct write_state
{
void operator() ( const T &x, const double t ) {
std::cout << t << "\t";
for(size_t i = 0; i < x.size(); ++i)
std::cout << x[i] << "\t";
std::cout << std::endl;
};
};
template<class T>
class odeClass {
private:
double Ka, Kel, Vd;
public:
odeClass(double ka, double kel, double vd) : Ka(ka), Kel(kel), Vd(vd) {};
void operator() (const T &x, T &dxdt, const double t) {
dxdt[0] = - Ka * x[0];
dxdt[1] = Ka * x[0] - Kel * x[1];
};
};
void testODE_Eigen() {
double Ka = 0.195, Vd = 13.8, Kel = 0.79 / Vd;
Eigen::VectorXd x(2);
x << 40 / Vd, 0.0;
odeClass<Eigen::VectorXd> myClass(Ka, Kel, Vd);
boost::numeric::odeint::runge_kutta4<Eigen::VectorXd, double, Eigen::VectorXd, double, boost::numeric::odeint::vector_space_algebra> stepper;
size_t steps = integrate_adaptive( stepper, myClass, x ,0.0 ,100.0 ,0.5 ,write_state<Eigen::VectorXd>() );
}
void testODE_Vector() {
double Ka = 0.195, Vd = 13.8, Kel = 0.79 / Vd;
std::vector<double> x = { 40 / Vd, 0.0 };
odeClass<std::vector<double>> myClass(Ka, Kel, Vd);
boost::numeric::odeint::runge_kutta4<std::vector<double>> stepper;
size_t steps = integrate_adaptive( stepper, myClass, x ,0.0 ,100.0 ,0.5 ,write_state<std::vector<double>>() );
}
int main()
{
testODE_Eigen();
return 0;
}
When running the function testODE_Vector();, it works perfectly, but when runningtestODE_Eigen();` I get the first integration step, one assertion stop: "Assertion failed: index >= 0 && index < size(), file C:\Eigen-3.3.7\Eigen\src\Core\DenseCoeffsBase.h, line 408.", and a windows message saying that the application has stop working, with no clue, if a use Code::Blocks. If I run the same on Visual Studio 2017 console application, I get one error saying Cannot write on a memory address without printing anything.
Any clues?
Thank you.
It looks like you are failing an assertion inside the Eigen library you are using. With a message like Assertion failed: index >= 0 && index < size() the library is probably trying to iterate over a vector internally and checks that the vector is valid before trying to access it. I would check the objects you are passing in and make sure they are valid.
It looks like one of the main differences in the two function testODE_Vector() and testODE_Eigen() is the way that you create that x. I'm not sure what this line
x << 40 / Vd, 0.0; is intended to do, but I would start there and verify that the value of x is right before it's passed into integrate_adaptive
My answer is a little late but in case someone else runs into this issue, here's what I found.
The issue seems to be that OdeInt can't handle properly the dynamic size with Eigen vectors and matrices. Therefore when creating dxdt, it creates an empty dynamic matrix or vector. This leads to an issue in your operator overload, where you try to access an element of dxdt where it contains none.
A quick fix I found was to use the resize() function (or conservativeResize()) to make sure dxdt has the proper size:
void operator() (const T &x, T &dxdt, const double t) {
dxdt.resize(x.size())
dxdt[0] = - Ka * x[0];
dxdt[1] = Ka * x[0] - Kel * x[1];
};
Note that if you want to use matrices instead of vectors you will have to use x.rows() and x.cols() instead of x.size().
I am trying to port the Harmonic Oscillator tutorial from ODEINT to Eigen, so that I could use VectorXd for state vectors. I cannot, however, make it compile.
I've been following some questions, for instance this is the closest except that I don't use any stepper here.
This is the code:
#include <iostream>
#include <vector>
#include <Eigen/Dense>
#include <boost/numeric/odeint.hpp>
typedef Eigen::VectorXd state_type;
// was vector<double>
const double gam = 0.15;
/* The rhs of x' = f(x) defined as a class */
class harm_osc
{
public:
void operator() ( const state_type &x , state_type &dxdt , const double /* t */ )
{
dxdt(0) = x(1);
dxdt(1) = -x(0) - gam*x(1);
// dxdt[0] = x[1];
// dxdt[1] = -x[0] - gam*x[1];
}
};
/* 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);
// dxdt[0] = x[1];
// dxdt[1] = -x[0] - gam*x[1];
}
void printer(const state_type &x , const double t)
{
// std::cout << t << "," << x[0] << "," << x[1] << std::endl;
std::cout << t << "," << x(0) << "," << x(1) << std::endl;
};
int main(int argc, const char * argv[])
{
state_type x(2);
x(0) = 1.0;
x(1) = 0.0;
// x[0] = 1.0;
// x[1] = 0.0;
std::cout << ">>>> FUNCTION" << std::endl;
// boost::numeric::odeint::integrate(harmonic_oscillator, x, 0.0, 10.0, 0.1, printer);
// boost::numeric::odeint::runge_kutta4<state_type, double, state_type, double, boost::numeric::odeint::vector_space_algebra> stepper();
boost::numeric::odeint::integrate<double, decltype(harmonic_oscillator), state_type, double, decltype(printer)>(harmonic_oscillator, x, 0.0, 10.0, 0.1, printer);
std::cout << ">>>> CLASS" << std::endl;
x(0) = 1.0;
x(1) = 0.0;
// x[0] = 1.0;
// x[1] = 0.0;
harm_osc ho;
boost::numeric::odeint::integrate<double, decltype(harmonic_oscillator), state_type, double, decltype(printer)>(ho, x, 0.0, 10.0, 0.1, printer);
return 0;
}
The compiler complains about No matching function for call to 'begin' in range_algebra.hpp from ODEINT in both class and function versions of integrate. As a matter of fact, Eigen matrices have no begin/end.
I've tried to play with the template parameters (as you see) with no avail.
Any hint?
Assertion failed using Eigen from repo
Using the latest Eigen from the repo (not the latest stable version), I can, as suggested, compile it and run. However, it fails an assertion in the integrate call tree:
Assertion failed: (index >= 0 && index < size()), function operator(), file eigen/include/eigen3/Eigen/src/Core/DenseCoeffsBase.h, line 427.
The call that fails is dxdt(0) = x(1); and subsequently in DenseCoeffsBase.h:
EIGEN_DEVICE_FUNC
EIGEN_STRONG_INLINE Scalar&
operator()(Index index)
{
eigen_assert(index >= 0 && index < size()); // <---- INDEX IS 0, SIZE IS 0
return coeffRef(index);
}
Is it possible that ODEINT is trying to default-construct a VectorXd? I followed the path to my ODE call and dxdt is actually NULL:
(lldb) e dxdt
(state_type) $1 = {
Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > = {
m_storage = {
m_data = 0x0000000000000000
m_rows = 0
}
}
}
What is worse is that when using resizeLike to allow resizing dxdt, in the second step (so the first real computation of integrate) I have a x with NULL values:
(lldb) e dxdt
(state_type) $0 = {
Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > = {
m_storage = {
m_data = 0x0000000000000000
m_rows = 0
}
}
}
(lldb) e x
(state_type) $1 = {
Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > = {
m_storage = {
m_data = 0x0000000000000000
m_rows = 0
}
}
}
I found that ODEINT actually works fine with Eigen... only it is not documented, as far as I can see.
Digging around ODEINT's code, I have found a promising eigen.hpp header deep in the external directory.
And lo and behold, it works flawlessly:
#include <fstream>
#include <iostream>
#include <vector>
#include <boost/numeric/odeint/external/eigen/eigen.hpp>
#include <Eigen/Eigenvalues>
#define FMT_HEADER_ONLY
#include "fmt/core.h"
#include "fmt/format.h"
#include "fmt/format-inl.h"
#include "fmt/printf.h"
using namespace std;
int main(int argc, char *argv[])
{
Eigen::VectorXd v;
v.resize(2);
typedef Eigen::VectorXd state_type;
const double gam = 0.15;
v(0) = 1.0;
v(1) = 1.1;
auto harmonic_oscillator = [&](const state_type &x, state_type &dxdt, const double t)
{
dxdt[0] = x[1];
dxdt[1] = -x[0] - gam*x[1];
};
auto printer = [&](const state_type &x, const double t)
{
fmt::print(out, "time: {} state: {}\n", t, x);
};
odeint::integrate(harmonic_oscillator, v, 0.0 , 10.0 , 0.01, printer);
return 0;
}
Hope it helps others.
As I know function call operator overloading is used to pass arbitrary number of parameter to a function inside a class. Is it also possible to pass a 2-Dimensional array ?
something like this in the following code:Edited
class harm_osc {
vector< vector<double> >& m_gam;
public:
harm_osc( vector< vector<double> > &gam ) : m_gam(gam) { }
void operator() ( const state_type &x , state_type &dxdt , const double /* t */ )
{
dxdt[0] = m_gam[0][0]*x[1];
dxdt[1] = m_gam[1][0]*x[0] - m_gam[1][1]*x[1];
}
};
When we have a large system of equations there are a lot of parameters.
Here an example from odeint library with some modification, with a single parameter:Edited
#include <iostream>
#include <vector>
#include <boost/numeric/odeint.hpp>
using namespace std;
/* The type of container used to hold the state vector */
typedef vector< double > state_type;
/* The rhs of x' = f(x) defined as a class */
class harm_osc {
vector< vector<double> >& m_gam;
public:
harm_osc( vector< vector<double> > &gam ) : m_gam(gam) { }
void operator() ( const state_type &x , state_type &dxdt , const double /* t */ )
{
dxdt[0] = m_gam[0][0]*x[1];
dxdt[1] = m_gam[1][0]*x[0] - m_gam[2][2]*x[1];
}
};
int main(int /* argc */ , char** /* argv */ )
{
using namespace boost::numeric::odeint;
//[ state_initialization
state_type x(2);
x[0] = 1.0; // start at x=1.0, p=0.0
x[1] = 0.0;
//]
//[ integration_class
vector< vector<double> > par {{1.,0.},{-1.,0.15}};
harm_osc ho(par);
size_t steps = integrate( ho ,
x , 0.0 , 10.0 , 0.1);
//]
}
Thanks for any guide or comment.
If I compile the code below, I get an:
microsoft visual studio 12.0\vc\include\xrefwrap(58): error C2064: term does not evaluate to a function taking 2 arguments
In the call to accumulate algorithm, if I change the code to function<double(double, Position const&) > f = bind(sum, placeholders::_1, bind(mem_fn(&Position::getBalance), placeholders::_2));double sum = accumulate(balances.begin(), balances.end(), 0., f); everything compiles fine. I also tried to use a non member function, but it doesn't work neither.
class Position
{
private:
double m_balance;
public:
Position(double balance) :
m_balance(balance)
{}
double getBalance() const
{
return m_balance;
}
};
static double sum(double v1, double v2)
{
return v1 + v2;
}
int main(int argc, char ** argv)
{
std::vector< Position > balances;
for (size_t i = 0; i < 10; i++)
{
balances.push_back(Position(i));
}
double sum = accumulate(balances.begin(), balances.end(), 0., bind(sum, placeholders::_1, bind(mem_fn(&Position::getBalance), placeholders::_2)));
cout << sum << endl;
return 0;
}
This will fix it:
double sum = accumulate(balances.cbegin(),
balances.cend(),
0.0 ,
std::bind(std::plus<>(),
placeholders::_1,
std::bind(&Position::getBalance, placeholders::_2)));
or we could be kind to our fellow programmers:
auto add_balance = [](auto x, auto& position) {
return x + position.getBalance();
};
double sum = accumulate(balances.cbegin(),
balances.cend(),
0.0 ,
add_balance);
Or of course we can inline the lambda. There's no performance difference. Which one seems clearer will be a matter of personal preference.
double sum = accumulate(balances.cbegin(),
balances.cend(),
0.0 ,
[](auto x, auto& position)
{
return x + position.getBalance();
});
Or we can write a complex functor to do a similar job. This was the pre-lambda way:
template<class Op>
struct PositionOp
{
using mfp_type = double (Position::*)() const;
PositionOp(Op op, mfp_type mfp) : op(op), mfp(mfp) {}
template<class T>
auto operator()(T x, const Position& pos) const {
return op(x, (pos.*mfp)());
}
Op op;
mfp_type mfp;
};
template<class Op>
auto position_op(Op op, double (Position::*mfp)() const)
{
return PositionOp<Op>(op, mfp);
}
...
double sum = accumulate(balances.cbegin(),
balances.cend(),
0.0 ,
position_op(std::plus<>(), &Position::getBalance));
... but I hope you'll agree that this is ghastly.
I have an ODE of type:
x'(t) = a(t)x+g(t)
Which I am trying to solve. The only GSL ODE example isn't very helpful because the only coefficient (\mu) is not time dependent.
This question has been answered on the GSL mailing list however the answer is very unclear - g(t) is ignored and it has not been explained how to incorporate a(t) into func ( should it be passed in *params?).
Is there any example I can see where such an ODE is solved using GSL?
UPDATE: As has been pointed out below, this has been answered on the GSL mailing list. Here is a full example program of how this is done:
#include <stdio.h>
#include <math.h>
#include "gsl/gsl_errno.h"
#include "gsl/gsl_matrix.h"
#include "gsl/gsl_odeiv2.h"
int func(double t, const double y[], double f[], void *params) {
f[0] = -t* y[0];
return GSL_SUCCESS;
}
int jac(double t, const double y[], double *dfdy, double dfdt[], void
*params) {
gsl_matrix_view dfdy_mat = gsl_matrix_view_array(dfdy, 1, 1);
gsl_matrix * m = &dfdy_mat.matrix;
gsl_matrix_set(m, 0, 0, -t);
dfdt[0] = -1;
return GSL_SUCCESS;
}
int main(void) {
double mu = 0;
gsl_odeiv2_system sys = { func, jac, 1, &mu };
gsl_odeiv2_driver * d = gsl_odeiv2_driver_alloc_y_new(&sys,
gsl_odeiv2_step_rk1imp, 1e-7, 1e-7, 0.0);
int i;
double t = 0.0, t1 = 2.0;
const double x0 = 1.0;
double y[1] = {x0};
const int N = 100;
printf("time\t \tapprox solution \t exact solution\n");
for (i = 0; i <= N; i++) {
double ti = i * (t1 / N);
int status = gsl_odeiv2_driver_apply(d, &t, ti, y);
if (status != GSL_SUCCESS) {
printf("error, return value=%d\n", status);
break;
}
printf("%.5e\t%.5e\t\t%.5e\n", t, y[0], x0*exp(-0.5*t*t));
}
gsl_odeiv2_driver_free(d);
printf("\n...and done!\n");
return 0;
}
If you are not restricted to the GSL and/or C you can use http://odeint.com - a modern C++ library for ODEs. Odeint is part of boost, so it might be already installed on your system or can easily be installed be most of the package managers for Linux distributions.
You can simply define your coefficients and the ODE and use for example the RK4 method:
double coef_a( double t ) { /* return a(t) */ };
double coef_g( double t ) { /* return b(t) */ };
typedef std::array< double , 1 > state_type;
double ode( state_type const &x , state_type &dxdt , double )
{
dxdt[0] = coef_a( t ) * x[0] + coef_g( t );
}
state_type x;
double t_state , t_end , dt;
// initialize x
integrate_const( runge_kutta< state_type >() , ode , x , t_start , dt , t_end );