Search code examples
c++odegsl

GSL ODE solver returns "ERROR: integration limits and/or step direction not consistent"


I'm writing a C++ code to solve the Lorentz force ODE, and I'm using the GSL library. I set an integer as the number of particles to be simulated and solved by the ODE, and print the states (x,y,z,vx,vy,vz) at each step. The initial values of position and velocity values are configured as random values from a GSL library. The code works for 1 particle, but returns "ERROR: integration limits and/or step direction not consistent" for any number of primaries > 1. I have tried different options, but haven't so far figured out what's wrong with my script. Below is the full code. I'd really appreciate any feedback on how to overcome this. Thanks a lot in advance.

Br, Leonardo.

#include<iostream>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_odeiv2.h>

using namespace std;

int lorentzODE (double t, const double s[], double f[], void *params);

int main(){

   int noPrimaries = 2;
   double q = 1.0;                           // Particle charge
   double m = 1.0;                           // Particle mass
   double B[3] = {0.0, 0.0, 1.0};            // Magnetic field
   double E[3] = {0.1, 0.0, 0.5};            // Electric field

   double x0[3];                 // Initial position
   double v0[3];                 // Initial velocity

   const gsl_rng_type * T = gsl_rng_ranlxs0;
   gsl_rng * r = gsl_rng_alloc(T);
   gsl_rng_set(r, (unsigned long) time(NULL));

   double t0 = 0.0;
   double tf = 100.0;
   double dt = 0.05;

   double system[6];                  // Initial state vector
   int status;                       // status of driver function
   double paramsB[8];
   paramsB[0] = q;
   paramsB[1] = m;
   paramsB[2] = B[0];
   paramsB[3] = B[1];
   paramsB[4] = B[2];
   paramsB[5] = E[0];
   paramsB[6] = E[1];
   paramsB[7] = E[2];

   const string &solverMethod = "RK8PD";
   const double h = 1.0e-06;
   const double epsAbs = 1.0e-08;
   const double epsRel = 1.0e-10;

   // Integrator configuration
   double t, t_next;
   gsl_odeiv2_system odeSystem;
   odeSystem.function = lorentzODE;
   odeSystem.dimension = 6;
   odeSystem.params = paramsB;

   // Algorithm option
   gsl_odeiv2_driver *drv;
   drv = gsl_odeiv2_driver_alloc_y_new(&odeSystem, gsl_odeiv2_step_rk8pd, h, epsAbs, epsRel);

   for(int i=0;i<noPrimaries;i++){

      x0[0] = gsl_rng_uniform (r);
      x0[1] = gsl_rng_uniform (r);
      x0[2] = gsl_rng_uniform (r);
      v0[0] = gsl_rng_uniform (r);
      v0[1] = gsl_rng_uniform (r);
      v0[2] = gsl_rng_uniform (r);
      system[0] = x0[0]; system[1] = x0[1]; system[2] = x0[2];
      system[3] = v0[0]; system[4] = v0[1]; system[5] = v0[2];

      for(t_next = t0+dt; t_next<tf; t_next += dt){
        
         status = gsl_odeiv2_driver_apply(drv, &t, t_next, system);
         if(status != GSL_SUCCESS){
            printf("Error: status = %d\n", status);
            break;
         }
         printf("%.5e %.5e %.5e %.5e %.5e %.5e %.5e\n", t, system[0], system[1], system[2], system[3], system[4], system[5]);
      }
      gsl_odeiv2_driver_free(drv);
   }
}

int lorentzODE (double t, const double s[], double f[], void *params){
  (void)(t); /* avoid unused parameter warning */
  double *lparams = (double *)params;

  double q  = lparams[0];
  double m  = lparams[1];
  double mu = q/m;

  double Bx = lparams[2];
  double By = lparams[3];
  double Bz = lparams[4];

  double Ex = lparams[5];
  double Ey = lparams[6];
  double Ez = lparams[7];

  f[0] = s[3];
  f[1] = s[4];
  f[2] = s[5];
  f[3] = mu*(Bz*s[4] - By*s[5] + Ex);
  f[4] = mu*(Bx*s[5] - Bz*s[3] + Ey);
  f[5] = mu*(By*s[3] - Bx*s[4] + Ez);
  return GSL_SUCCESS;
}

Solution

  • The problem was solved :) There were two reasons for why it wasn't working:

    • I haven't actually initialized the variable t. Corrected by initializing it inside the first loop;
    • The allocation of the variable drv has to be performed inside the loop as well.

    Thanks Lutz Lehmann for pointing it out. The below is the working code:

    #include<iostream>
    #include <gsl/gsl_rng.h>
    #include <gsl/gsl_errno.h>
    #include <gsl/gsl_vector.h>
    #include <gsl/gsl_matrix.h>
    #include <gsl/gsl_odeiv2.h>
    
    using namespace std;
    
    int lorentzODE (double t, const double s[], double f[], void *params);
    
    int main(){
    
        int noPrimaries = 10;
        double q = 1.0;                           // Particle charge
        double m = 1.0;                           // Particle mass
        double B[3] = {0.0, 0.0, 1.0};            // Magnetic field
        double E[3] = {0.1, 0.0, 0.5};            // Electric field
    
        double x0[3];                             // Initial position
        double v0[3];                             // Initial velocity
    
        const gsl_rng_type * T = gsl_rng_ranlxs0;
        gsl_rng * r = gsl_rng_alloc(T);
        gsl_rng_set(r, (unsigned long) time(NULL));
    
        double t0 = 0.0;
        double tf = 10.0;
        double dt = 0.5;
    
        double system[6];                  // Initial state vector
        int status;                       // status of driver function
        double paramsB[8];
        paramsB[0] = q;
        paramsB[1] = m;
        paramsB[2] = B[0];
        paramsB[3] = B[1];
        paramsB[4] = B[2];
        paramsB[5] = E[0];
        paramsB[6] = E[1];
        paramsB[7] = E[2];
    
        const double h = 1.0e-06;
        const double epsAbs = 1.0e-08;
        const double epsRel = 1.0e-10;
    
        double t, t_next;
        gsl_odeiv2_system odeSystem;
        odeSystem.function = lorentzODE;
        odeSystem.dimension = 6;
        odeSystem.params = paramsB;
    
        gsl_odeiv2_driver *drv;
    
        for(int i=0;i<noPrimaries;i++){
        
            t = t0;
            drv = gsl_odeiv2_driver_alloc_y_new(&odeSystem, gsl_odeiv2_step_rk8pd, h, epsAbs, epsRel);
    
            x0[0] = gsl_rng_uniform (r);
            x0[1] = gsl_rng_uniform (r);
            x0[2] = gsl_rng_uniform (r);
            v0[0] = gsl_rng_uniform (r);
            v0[1] = gsl_rng_uniform (r);
            v0[2] = gsl_rng_uniform (r);
            system[0] = x0[0]; system[1] = x0[1]; system[2] = x0[2];
            system[3] = v0[0]; system[4] = v0[1]; system[5] = v0[2];
    
            for(t_next = t0+dt; t_next<tf; t_next += dt){
    
                status = gsl_odeiv2_driver_apply(drv, &t, t_next, system);
                if(status != GSL_SUCCESS){
                    printf("Error: status = %d\n", status);
                    break;
                }
                printf("%.5e %.5e %.5e %.5e %.5e %.5e %.5e\n", t, system[0], system[1], system[2], system[3], system[4], system[5]);
            }
            gsl_odeiv2_driver_free(drv);
        }
    }
    
    int lorentzODE (double t, const double s[], double f[], void *params){
        (void)(t); /* avoid unused parameter warning */
        double *lparams = (double *)params;
    
        double q  = lparams[0];
        double m  = lparams[1];
        double mu = q/m;
    
        double Bx = lparams[2];
        double By = lparams[3];
        double Bz = lparams[4];
    
        double Ex = lparams[5];
        double Ey = lparams[6];
        double Ez = lparams[7];
    
        f[0] = s[3];
        f[1] = s[4];
        f[2] = s[5];
        f[3] = mu*(Bz*s[4] - By*s[5] + Ex);
        f[4] = mu*(Bx*s[5] - Bz*s[3] + Ey);
        f[5] = mu*(By*s[3] - Bx*s[4] + Ez);
        return GSL_SUCCESS;
    }