gsl_odeiv2 in c++ class: Template wrapper for int(…) ode

匿名 (未验证) 提交于 2019-12-03 01:06:02

问题:

I am currently using gsl_odeiv2 methods inside my classes to solve differential equations. But because of the well known memberfunction problem I can not define my ode-system inside the class. I am currently using a workaround: I define my ode in a global namespace:

ODE.hpp: #include "EoS.hpp"  #include <gsl/gsl_math.h> #include <gsl/gsl_errno.h>  namespace ODEs {     struct tov_eq_params {EoS *eos;};     int tov_eq(double, const double *, double *, void *); }  ODE.cpp: namespace ODEs {     int tov_eq(double r, const double *PM, double *dPdM, void *p) {         struct tov_eq_params * params = (struct tov_eq_params *)p;         EoS *eos = (params->eos);         ...     return GSL_SUCCESS     } } 

and use a pointer to an object of a coustom type (class EoS) as parameter. Inside the class that solves the ode I use:

... struct tov_eq_params comp_tov_params = {(this->star_eos)}; gsl_odeiv2_evolve *comp_tov_evolve = gsl_odeiv2_evolve_alloc(3); gsl_odeiv2_system comp_tov_system = {tov_eq, NULL, 3,&comp_tov_params}; ... 

to initalise my system. This works fine but is a bit messy because I need to declare my differential equations in a global namespace.

I know that it is possible to use template wrappers for gsl_functions stackoverflow.com/questions/.../how-to-avoid-static-member-function-when-using-gsl-with-c/... to use them in C++ Classes. I actually use the wrapper described there to define functions for gsl_integration Methods within my classes and it works perfectly and is much cleaner and less code to write. For example: I can use my star_eos Object from above direcly inside the function:

  auto dBf = [=](double r)->double{         return 4 * M_PI * gsl_pow_2(r) * (this->star_eos)->nbar(this->P(r)) * sqrt(this->expLambda(r))* 1e54;     };     gsl_function_pp<decltype(dBf)> dBfp(dBf);     gsl_function *dB = static_cast<gsl_function*>(&dBfp); 

I tried to write such a template wrapper for the int(double r, const double *PM, double *dPdM, void *p) functions that gsl_odeiv2_system needs but I failed because I am new to C++ and did not fully understand its template/static_cast mechanisms.

Is there someone who uses gsl_odeiv methods and its ode systems with a template wrapper? Or can someone come up with a template similar to the one described above for gsl_functions but for the int(...) ode.

回答1:

Thinking about how I got it working with the differential equations set in a global namespace I found a solution for my problem. I now have a working wrapper. In a global namespace I have the following:

//gsl_wrapper.hpp #include <iostream> #include <vector> #include <functional>  #include <gsl/gsl_math.h> #include <gsl/gsl_errno.h>  namespace gsl_wrapper {      class ode_System{     public:         ode_System(int);         int dim;         std::function<double (double, const double *, double *, int)> *df;      };      struct ode_struct {ode_System *ode;};     int ode(double, const double *, double *, void *); }  //gsl_wrapper.cpp #include "../../include/gsl_wrapper.hpp"  namespace gsl_wrapper {      ode_System::ode_System(int dim) {         this->dim=dim;     }      int ode(double r, const double *f, double *df, void *p) {         struct ode_struct * params = (struct ode_struct *)p;         ode_System *odeFunc = (params->ode);          int dim = odeFunc->dim;         std::function<double (double, const double *, double *, int)> dfeq=*(odeFunc->df);          for(int i=0;i<dim;i++){             df[i] = dfeq(r,f,df,i);         }          return GSL_SUCCESS;     }  }; 

So I bassically have all my specific information stored in my new class ode_System, which has an int dim to specify the systems dimensions and a pointer so a std::function object. This object represents the mathematica differential equation system.

Inside my class, where I want to solve a differential equation using gsl_odeiv2, I define that system using a lambda function:

std::function<double (double, const double *, double *, int)> dPMeq = [=](double r , const double * PM, double *dPdM, int i)->double{     double df;     switch ( i )     {         case 0:             df = ... // df_1/dr             break;         case 1:              df = ... // df_2/dr             break;         case 2:              df = ... // df_3/dr             break;         default:             GSL_ERROR_VAL ("comp_tov_eq:", GSL_FAILURE,GSL_FAILURE);             df = 0;     }     return df; }; 

The above system represents a coupled system of 3 differential equations. I then declare a ode_System object with the right dimension and set its function pointer df to my defined system. Then I only need a structure with a reference to that system and done: I can use my differential equation defined inside my class with gsl_odeiv2_system:

 ode_System tov(3);  tov.df= &dPMeq;  struct ode_struct comp_tov_params = {&tov};  gsl_odeiv2_evolve *comp_tov_evolve = gsl_odeiv2_evolve_alloc(3);  gsl_odeiv2_system comp_tov_system = {ode, NULL, 3, &comp_tov_params};  ... 

As far as I can tell this works just as well (or bad) as the implementation I presented in my question. It can use some clean up but in principle this works fine for me.

But if someone knows a better way to do this please feel free to share it!



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!