/*
 * BRAINS
 * (B)LR (R)everberation-mapping (A)nalysis (I)n AGNs with (N)ested (S)ampling
 * Yan-Rong Li, liyanrong@ihep.ac.cn
 * Thu, Aug 4, 2016
 */

/*!
 *  \file blr_models.c
 *  \brief BLR models
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <float.h>
#include <string.h>
#include <mpi.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_interp.h>

#include "brains.h"

/* cluster around outer disk face, Lopn_cos1 < Lopn_cos2 */
inline double theta_sample_outer(double gam, double Lopn_cos1, double Lopn_cos2)
{
  return acos(Lopn_cos1 + (Lopn_cos2-Lopn_cos1) * pow(gsl_rng_uniform(gsl_r), gam));
}

/* cluster around equatorial plane, Lopn_cos1 < Lopn_cos2 */
inline double theta_sample_inner(double gam, double Lopn_cos1, double Lopn_cos2)
{
  /* note that cosine is a decreaing function */
  double opn1 = acos(Lopn_cos1), opn2 = acos(Lopn_cos2);
  return opn2 + (opn1-opn2)  * (1.0 - pow(gsl_rng_uniform(gsl_r), 1.0/gam));
}

/*================================================================
 * model 1
 * Brewer et al. (2011)'s model
 *
 * geometry: radial Gamma distribution
 * dynamics: elliptical orbits
 *================================================================
 */
void gen_cloud_sample_model1(const void *pm, int flag_type, int flag_save)
{
  int i, j, nc;
  double r, phi, dis, Lopn_cos, u;
  double x, y, z, xb, yb, zb, zb0, vx, vy, vz, vxb, vyb, vzb;
  double inc, F, beta, mu, k, a, s, rin, sig, Rs;
  double Lphi, Lthe, L, E;
  double V, weight, rnd;
  BLRmodel1 *model = (BLRmodel1 *)pm;
  double Emin, Lmax, Vr, Vr2, Vph, mbh, chi, lambda, q;

  Lopn_cos = cos(model->opn*PI/180.0);
  inc = acos(model->inc);
  beta = model->beta;
  F = model->F;
  mu = exp(model->mu);
  k = model->k;

  if(flag_type == 1) // 1D RM, no dynamical parameters
  {
    mbh = 0.0;
  }
  else
  {
    mbh = exp(model->mbh);
    lambda = model->lambda;
    q = model->q;
  }

  Rs = 3.0e11*mbh/CM_PER_LD; // Schwarzchild radius in a unit of light-days
  a = 1.0/beta/beta;
  s = mu/a;
  rin = mu*F + Rs;
  sig = (1.0-F)*s;

  for(i=0; i<parset.n_cloud_per_task; i++)
  {
// generate a direction of the angular momentum
    Lphi = 2.0*PI * gsl_rng_uniform(gsl_r);
    //Lthe = acos(Lopn_cos + (1.0-Lopn_cos) * gsl_rng_uniform(gsl_r));
    Lthe = theta_sample(1.0, Lopn_cos, 1.0);

    nc = 0;
    r = rcloud_max_set+1.0;
    while(r>rcloud_max_set || r<rcloud_min_set)
    {
      if(nc > 1000)
      {
        printf("# Error, too many tries in generating ridial location of clouds.\n");
        exit(0);
      }
      rnd = gsl_ran_gamma(gsl_r, a, 1.0);
//    r = mu * F + (1.0-F) * gsl_ran_gamma(gsl_r, 1.0/beta/beta, beta*beta*mu);
        r = rin + sig * rnd;
      nc++;
    }
    phi = 2.0*PI * gsl_rng_uniform(gsl_r);

    x = r * cos(phi);
    y = r * sin(phi);
    z = 0.0;

  /*xb =  cos(Lthe)*cos(Lphi) * x + sin(Lphi) * y - sin(Lthe)*cos(Lphi) * z;
    yb = -cos(Lthe)*sin(Lphi) * x + cos(Lphi) * y + sin(Lthe)*sin(Lphi) * z;
    zb =  sin(Lthe) * x + cos(Lthe) * z;*/

    xb =  cos(Lthe)*cos(Lphi) * x + sin(Lphi) * y;
    yb = -cos(Lthe)*sin(Lphi) * x + cos(Lphi) * y;
    zb =  sin(Lthe) * x;

    zb0 = zb;
    if(zb0 < 0.0)
      zb = -zb;

    /* counter-rotate around y */
    x = xb * cos(PI/2.0-inc) + zb * sin(PI/2.0-inc);
    y = yb;
    z =-xb * sin(PI/2.0-inc) + zb * cos(PI/2.0-inc);

    weight = 0.5 + k*(x/r);
    clouds_weight[i] = weight;

#ifndef SpecAstro
    dis = r - x;
    clouds_tau[i] = dis;
    if(flag_type == 1)
    {
      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\n", x, y, z);
      }
      continue;
    }
#else
  switch(flag_type)
  {
    case 1:   /* 1D RM */
      dis = r - x;
      clouds_tau[i] = dis;
      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\n", x, y, z);
      }
      continue;
      break;

    case 2:  /* 2D RM */
      dis = r - x;
      clouds_tau[i] = dis;
      break;

    case 3: /* SA */
      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;

    case 4: /* 1D RM + SA */
      dis = r - x;
      clouds_tau[i] = dis;

      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;

    case 5: /* 2D RM + SA */
      dis = r - x;
      clouds_tau[i] = dis;

      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;
  }

#endif

    /* velocity
     * note that a cloud moves in its orbit plane, whose direction
     * is determined by the direction of its angular momentum.
     */
    Emin = - mbh/r;
    //Ecirc = 0.5 * Emin;
    //Lcirc = sqrt(2.0 * r*r * (Ecirc + mbh/r));

    for(j=0; j<parset.n_vel_per_cloud; j++)
    {
      chi = lambda * gsl_ran_ugaussian(gsl_r);
      E = Emin / (1.0 + exp(-chi));

      Lmax = sqrt(2.0 * r*r * (E + mbh/r));

      if(lambda>1.0e-2)   /* make sure that exp is caculatable. */
        L = Lmax * lambda * log( (exp(1.0/lambda) - 1.0) * gsl_rng_uniform(gsl_r) + 1.0 );
      else
        L = Lmax * (1.0 + lambda * log(gsl_rng_uniform(gsl_r)) );

      Vr2 = 2.0 * (E + mbh/r) - L*L/r/r;
      if(Vr2>=0.0)
      {
        u = gsl_rng_uniform(gsl_r);
        Vr = sqrt(Vr2) * (u<q?-1.0:1.0);
      }
      else
      {
        Vr = 0.0;
      }
      Vph = L/r; /* RM cannot distinguish the orientation of the rotation. */

      vx = Vr * cos(phi) - Vph * sin(phi);
      vy = Vr * sin(phi) + Vph * cos(phi);
      vz = 0.0;

    /*vxb = cos(Lthe)*cos(Lphi) * vx + sin(Lphi) * vy - sin(Lthe)*cos(Lphi) * vz;
      vyb =-cos(Lthe)*sin(Lphi) * vx + cos(Lphi) * vy + sin(Lthe)*sin(Lphi) * vz;
      vzb = sin(Lthe) * vx + cos(Lthe) * vz;*/

      vxb = cos(Lthe)*cos(Lphi) * vx + sin(Lphi) * vy;
      vyb =-cos(Lthe)*sin(Lphi) * vx + cos(Lphi) * vy;
      vzb = sin(Lthe) * vx;

      if(zb0 < 0.0)
        vzb = -vzb;

      vx = vxb * cos(PI/2.0-inc) + vzb * sin(PI/2.0-inc);
      vy = vyb;
      vz =-vxb * sin(PI/2.0-inc) + vzb * cos(PI/2.0-inc);

      V = -vx;  //note the definition of the line-of-sight velocity. positive means a receding
                // velocity relative to the observer.

      clouds_vel[i*parset.n_vel_per_cloud + j] = V;

      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\t%f\t%f\t%f\t%f\n", x, y, z, vx*VelUnit, vy*VelUnit, vz*VelUnit, weight);
      }
    }
  }

  return;
}

/*================================================================
 * model 2
 *
 * geometry: radial Gamma distribution
 * dynamics: elliptical orbits (Gaussian around circular orbits)
 *================================================================
 */
void gen_cloud_sample_model2(const void *pm, int flag_type, int flag_save)
{
  int i, j, nc;
  double r, phi, dis, Lopn_cos;
  double x, y, z, xb, yb, zb, zb0, vx, vy, vz, vxb, vyb, vzb;
  double inc, F, beta, mu, k, a, s, rin, sig, Rs;
  double Lphi, Lthe;
  double V, weight, rnd;
  BLRmodel2 *model = (BLRmodel2 *)pm;
  double Emin, Ecirc, Lcirc, Vcirc, Vr, Vph, mbh, sigr, sigtheta, rhor, rhotheta;

  Lopn_cos = cos(model->opn*PI/180.0);
  inc = acos(model->inc);
  beta = model->beta;
  F = model->F;
  mu = exp(model->mu);
  k = model->k;

  a = 1.0/beta/beta;
  s = mu/a;
  rin=mu*F;
  sig=(1.0-F)*s;

  if(flag_type == 1)  //1D RM no dynamical parameters
  {
    mbh = 0.0;
  }
  else
  {
    mbh = exp(model->mbh);
    sigr = model->sigr;
    sigtheta = model->sigtheta * PI;
  }
  Rs = 3.0e11*mbh/CM_PER_LD; // Schwarzchild radius in a unit of light-days

  a = 1.0/beta/beta;
  s = mu/a;
  rin = mu*F + Rs;
  sig = (1.0-F)*s;

  for(i=0; i<parset.n_cloud_per_task; i++)
  {
    /* generate a direction of the angular momentum */
    Lphi = 2.0*PI * gsl_rng_uniform(gsl_r);
    //Lthe = acos(Lopn_cos + (1.0-Lopn_cos) * gsl_rng_uniform(gsl_r));
    Lthe = theta_sample(1.0, Lopn_cos, 1.0);

    nc = 0;
    r = rcloud_max_set+1.0;
    while(r>rcloud_max_set || r<rcloud_min_set)
    {
      if(nc > 1000)
      {
        printf("# Error, too many tries in generating ridial location of clouds.\n");
        exit(0);
      }
      rnd = gsl_ran_gamma(gsl_r, a, 1.0);
//    r = mu * F + (1.0-F) * gsl_ran_gamma(gsl_r, 1.0/beta/beta, beta*beta*mu);
      r = rin + sig * rnd;
      nc++;
    }
    phi = 2.0*PI * gsl_rng_uniform(gsl_r);

    x = r * cos(phi);
    y = r * sin(phi);
    z = 0.0;

  /*xb =  cos(Lthe)*cos(Lphi) * x + sin(Lphi) * y - sin(Lthe)*cos(Lphi) * z;
    yb = -cos(Lthe)*sin(Lphi) * x + cos(Lphi) * y + sin(Lthe)*sin(Lphi) * z;
    zb =  sin(Lthe) * x + cos(Lthe) * z;*/

    xb =  cos(Lthe)*cos(Lphi) * x + sin(Lphi) * y;
    yb = -cos(Lthe)*sin(Lphi) * x + cos(Lphi) * y;
    zb =  sin(Lthe) * x;

    zb0 = zb;
    if(zb0 < 0.0)
      zb = -zb;

    /* counter-rotate around y */
    x = xb * cos(PI/2.0-inc) + zb * sin(PI/2.0-inc);
    y = yb;
    z =-xb * sin(PI/2.0-inc) + zb * cos(PI/2.0-inc);

    weight = 0.5 + k*(x/r);
    clouds_weight[i] = weight;

#ifndef SpecAstro
    dis = r - x;
    clouds_tau[i] = dis;
    if(flag_type == 1)
    {
      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\n", x, y, z);
      }
      continue;
    }
#else
  switch(flag_type)
  {
    case 1:   /* 1D RM */
      dis = r - x;
      clouds_tau[i] = dis;
      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\n", x, y, z);
      }
      continue;
      break;

    case 2:  /* 2D RM */
      dis = r - x;
      clouds_tau[i] = dis;
      break;

    case 3: /* SA */
      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;

    case 4: /* 1D RM + SA */
      dis = r - x;
      clouds_tau[i] = dis;

      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;

    case 5: /* 2D RM + SA */
      dis = r - x;
      clouds_tau[i] = dis;

      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;
  }

#endif

    /* velocity
     * note that a cloud moves in its orbit plane, whose direction
     * is determined by the direction of its angular momentum.
     */
    Emin = - mbh/r;
    Ecirc = 0.5 * Emin;
    Lcirc = sqrt(2.0 * r*r * (Ecirc + mbh/r));
    Vcirc = Lcirc/r;

    for(j=0; j<parset.n_vel_per_cloud; j++)
    {
      rhor = gsl_ran_ugaussian(gsl_r) * sigr + 1.0;
      rhotheta = gsl_ran_ugaussian(gsl_r) * sigtheta + 0.5*PI;

      Vr = rhor * cos(rhotheta) * Vcirc;
      Vph = rhor * fabs(sin(rhotheta)) * Vcirc; /* RM cannot distinguish the orientation of the rotation. make all clouds co-rotate */

      vx = Vr * cos(phi) - Vph * sin(phi);
      vy = Vr * sin(phi) + Vph * cos(phi);
      vz = 0.0;

    /*vxb = cos(Lthe)*cos(Lphi) * vx + sin(Lphi) * vy - sin(Lthe)*cos(Lphi) * vz;
      vyb =-cos(Lthe)*sin(Lphi) * vx + cos(Lphi) * vy + sin(Lthe)*sin(Lphi) * vz;
      vzb = sin(Lthe) * vx + cos(Lthe) * vz;*/

      vxb = cos(Lthe)*cos(Lphi) * vx + sin(Lphi) * vy;
      vyb =-cos(Lthe)*sin(Lphi) * vx + cos(Lphi) * vy;
      vzb = sin(Lthe) * vx;

      if(zb0 < 0.0)
        vzb = -vzb;

      vx = vxb * cos(PI/2.0-inc) + vzb * sin(PI/2.0-inc);
      vy = vyb;
      vz =-vxb * sin(PI/2.0-inc) + vzb * cos(PI/2.0-inc);

      V = -vx;  //note the definition of the line-of-sight velocity. postive means a receding
                // velocity relative to the observer.

      clouds_vel[i*parset.n_vel_per_cloud + j] = V;

      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\t%f\t%f\t%f\t%f\n", x, y, z, vx*VelUnit, vy*VelUnit, vz*VelUnit, weight);
      }
    }
  }

  return;
}

/*====================================================================
 * model 3
 *
 * geometry: radial power law
 * dynamics:
 *====================================================================
 */
void gen_cloud_sample_model3(const void *pm, int flag_type, int flag_save)
{
  int i, j, nc;
  double r, phi, dis, Lopn_cos;
  double x, y, z, xb, yb, zb, zb0, vx, vy, vz, vxb, vyb, vzb;
  double inc, F, alpha, Rin, k, Rs;
  double Lphi, Lthe, L, E;
  double V, weight, rnd;
  BLRmodel3 *model = (BLRmodel3 *)pm;
  double Emin, Lmax, Vr, Vph, mbh, xi, q;

  Lopn_cos = cos(model->opn*PI/180.0);
  inc = acos(model->inc);
  alpha = model->alpha;
  F = model->F;
  Rin = exp(model->Rin);
  k = model->k;

  if(flag_type == 1)
  {
    mbh = 0.0;
  }
  else
  {
    mbh = exp(model->mbh);
    xi = model->xi;
    q = model->q;
  }
  Rs = 3.0e11*mbh/CM_PER_LD; // Schwarzchild radius in a unit of light-days

  if(F*Rin > rcloud_max_set)
    F = rcloud_max_set/Rin;

  for(i=0; i<parset.n_cloud_per_task; i++)
  {
// generate a direction of the angular momentum
    Lphi = 2.0*PI * gsl_rng_uniform(gsl_r);
    //Lthe = acos(Lopn_cos + (1.0-Lopn_cos) * gsl_rng_uniform(gsl_r));
    Lthe = theta_sample(1.0, Lopn_cos, 1.0);

    nc = 0;
    r = rcloud_max_set+1.0;
    while(r>rcloud_max_set || r<rcloud_min_set)
    {
      if(nc > 1000)
      {
        printf("# Error, too many tries in generating ridial location of clouds.\n");
        exit(0);
      }
      if(fabs(1.0-alpha) > 1.0e-4)
        rnd = pow( gsl_rng_uniform(gsl_r) * ( pow(F, 1.0-alpha) - 1.0) + 1.0,  1.0/(1.0-alpha) );
      else
        rnd = exp( gsl_rng_uniform(gsl_r) * log(F) );
      r = Rin * rnd + Rs;
      nc++;
    }
    phi = 2.0*PI * gsl_rng_uniform(gsl_r);

    x = r * cos(phi);
    y = r * sin(phi);
    z = 0.0;

  /*xb =  cos(Lthe)*cos(Lphi) * x + sin(Lphi) * y - sin(Lthe)*cos(Lphi) * z;
    yb = -cos(Lthe)*sin(Lphi) * x + cos(Lphi) * y + sin(Lthe)*sin(Lphi) * z;
    zb =  sin(Lthe) * x + cos(Lthe) * z;*/

    xb =  cos(Lthe)*cos(Lphi) * x + sin(Lphi) * y;
    yb = -cos(Lthe)*sin(Lphi) * x + cos(Lphi) * y;
    zb =  sin(Lthe) * x;

    zb0 = zb;
    if(zb0 < 0.0)
      zb = -zb;

// counter-rotate around y
    x = xb * cos(PI/2.0-inc) + zb * sin(PI/2.0-inc);
    y = yb;
    z =-xb * sin(PI/2.0-inc) + zb * cos(PI/2.0-inc);

    weight = 0.5 + k*(x/r);
    clouds_weight[i] = weight;

#ifndef SpecAstro
    dis = r - x;
    clouds_tau[i] = dis;
    if(flag_type == 1)
    {
      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\n", x, y, z);
      }
      continue;
    }
#else
  switch(flag_type)
  {
    case 1:   /* 1D RM */
      dis = r - x;
      clouds_tau[i] = dis;
      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\n", x, y, z);
      }
      continue;
      break;

    case 2:  /* 2D RM */
      dis = r - x;
      clouds_tau[i] = dis;
      break;

    case 3: /* SA */
      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;

    case 4: /* 1D RM + SA */
      dis = r - x;
      clouds_tau[i] = dis;

      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;

    case 5: /* 2D RM + SA */
      dis = r - x;
      clouds_tau[i] = dis;

      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;
  }

#endif

// velocity
// note that a cloud moves in its orbit plane, whose direction
// is determined by the direction of its angular momentum.
    Emin = - mbh/r;
    //Ecirc = 0.5 * Emin;
    //Lcirc = sqrt(2.0 * r*r * (Ecirc + mbh/r));

    for(j=0; j<parset.n_vel_per_cloud; j++)
    {

      E = Emin / 2.0;
      Lmax = sqrt(2.0 * r*r * (E + mbh/r));
      L = Lmax;

      Vr = xi * sqrt(2.0*mbh/r);
      if(q <= 0.5)
        Vr = -Vr;
      Vph = L/r; //RM cannot distinguish the orientation of the rotation.

      vx = Vr * cos(phi) - Vph * sin(phi);
      vy = Vr * sin(phi) + Vph * cos(phi);
      vz = 0.0;

    /*vxb = cos(Lthe)*cos(Lphi) * vx + sin(Lphi) * vy - sin(Lthe)*cos(Lphi) * vz;
      vyb =-cos(Lthe)*sin(Lphi) * vx + cos(Lphi) * vy + sin(Lthe)*sin(Lphi) * vz;
      vzb = sin(Lthe) * vx + cos(Lthe) * vz;*/

      vxb = cos(Lthe)*cos(Lphi) * vx + sin(Lphi) * vy;
      vyb =-cos(Lthe)*sin(Lphi) * vx + cos(Lphi) * vy;
      vzb = sin(Lthe) * vx;

      if(zb0 < 0.0)
        vzb = -vzb;

      vx = vxb * cos(PI/2.0-inc) + vzb * sin(PI/2.0-inc);
      vy = vyb;
      vz =-vxb * sin(PI/2.0-inc) + vzb * cos(PI/2.0-inc);

      V = -vx;  //note the definition of the line-of-sight velocity. postive means a receding
                // velocity relative to the observer.

      clouds_vel[i*parset.n_vel_per_cloud + j] = V;

      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\t%f\t%f\t%f\t%f\n", x, y, z, vx*VelUnit, vy*VelUnit, vz*VelUnit, weight);
      }
    }
  }

  return;
}

/*!
 * model 4.
 * generate cloud sample.
 */
void gen_cloud_sample_model4(const void *pm, int flag_type, int flag_save)
{
  int i, j, nc;
  double r, phi, dis, Lopn_cos;
  double x, y, z, xb, yb, zb, zb0, vx, vy, vz, vxb, vyb, vzb;
  double inc, F, alpha, Rin, k, Rs;
  double Lphi, Lthe, L, E;
  double V, weight, rnd;
  BLRmodel4 *model = (BLRmodel4 *)pm;
  double Emin, Lmax, Vr, Vph, mbh, xi, q;

  Lopn_cos = cos(model->opn*PI/180.0);
  inc = acos(model->inc);
  alpha = model->alpha;
  F = model->F;
  Rin = exp(model->Rin);
  k = model->k;

  if(flag_type == 1)  // 1D RM, no dynamical parameters
  {
    mbh = 0.0;
  }
  else
  {
    mbh = exp(model->mbh);
    xi = model->xi;
    q = model->q;
  }
  Rs = 3.0e11*mbh/CM_PER_LD; // Schwarzchild radius in a unit of light-days

  if(Rin*F > rcloud_max_set)
    F = rcloud_max_set/Rin;

  for(i=0; i<parset.n_cloud_per_task; i++)
  {
    /* generate a direction of the angular momentum   */
    Lphi = 2.0*PI * gsl_rng_uniform(gsl_r);
    //Lthe = acos(Lopn_cos + (1.0-Lopn_cos) * gsl_rng_uniform(gsl_r));
    Lthe = theta_sample(1.0, Lopn_cos, 1.0);

    nc = 0;
    r = rcloud_max_set+1.0;
    while(r>rcloud_max_set || r<rcloud_min_set)
    {
      if(nc > 1000)
      {
        printf("# Error, too many tries in generating ridial location of clouds.\n");
        exit(0);
      }
      if(fabs(1.0-alpha) > 1.0e-4)
        rnd = pow( gsl_rng_uniform(gsl_r) * ( pow(F, 1.0-alpha) - 1.0) + 1.0,  1.0/(1.0-alpha) );
      else
        rnd = exp( gsl_rng_uniform(gsl_r) * log(F) );
      r = Rin * rnd + Rs;
      nc++;
    }
    phi = 2.0*PI * gsl_rng_uniform(gsl_r);

    x = r * cos(phi);
    y = r * sin(phi);
    z = 0.0;

  /*xb =  cos(Lthe)*cos(Lphi) * x + sin(Lphi) * y - sin(Lthe)*cos(Lphi) * z;
    yb = -cos(Lthe)*sin(Lphi) * x + cos(Lphi) * y + sin(Lthe)*sin(Lphi) * z;
    zb =  sin(Lthe) * x + cos(Lthe) * z; */

    xb =  cos(Lthe)*cos(Lphi) * x + sin(Lphi) * y;
    yb = -cos(Lthe)*sin(Lphi) * x + cos(Lphi) * y;
    zb =  sin(Lthe) * x;

    zb0 = zb;
    if(zb0 < 0.0)
      zb = -zb;

    /* counter-rotate around y */
    x = xb * cos(PI/2.0-inc) + zb * sin(PI/2.0-inc);
    y = yb;
    z =-xb * sin(PI/2.0-inc) + zb * cos(PI/2.0-inc);

    weight = 0.5 + k*(x/r);
    clouds_weight[i] = weight;

#ifndef SpecAstro
    dis = r - x;
    clouds_tau[i] = dis;
    if(flag_type == 1)
    {
      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\n", x, y, z);
      }
      continue;
    }
#else
  switch(flag_type)
  {
    case 1:   /* 1D RM */
      dis = r - x;
      clouds_tau[i] = dis;
      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\n", x, y, z);
      }
      continue;
      break;

    case 2:  /* 2D RM */
      dis = r - x;
      clouds_tau[i] = dis;
      break;

    case 3: /* SA */
      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;

    case 4: /* 1D RM + SA */
      dis = r - x;
      clouds_tau[i] = dis;

      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;

    case 5: /* 2D RM + SA */
      dis = r - x;
      clouds_tau[i] = dis;

      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;
  }

#endif

// velocity
// note that a cloud moves in its orbit plane, whose direction
// is determined by the direction of its angular momentum.
    Emin = - mbh/r;
    //Ecirc = 0.5 * Emin;
    //Lcirc = sqrt(2.0 * r*r * (Ecirc + mbh/r));

    for(j=0; j<parset.n_vel_per_cloud; j++)
    {

      E = Emin / 2.0;
      Lmax = sqrt(2.0 * r*r * (E + mbh/r));
      L = Lmax;

      Vr = xi * sqrt(2.0*mbh/r);
      if(q<=0.5)
        Vr = -Vr;
      Vph = sqrt(1.0-2.0*xi*xi) * L/r; //RM cannot distinguish the orientation of the rotation.

      vx = Vr * cos(phi) - Vph * sin(phi);
      vy = Vr * sin(phi) + Vph * cos(phi);
      vz = 0.0;

    /*vxb = cos(Lthe)*cos(Lphi) * vx + sin(Lphi) * vy - sin(Lthe)*cos(Lphi) * vz;
      vyb =-cos(Lthe)*sin(Lphi) * vx + cos(Lphi) * vy + sin(Lthe)*sin(Lphi) * vz;
      vzb = sin(Lthe) * vx + cos(Lthe) * vz;*/

      vxb = cos(Lthe)*cos(Lphi) * vx + sin(Lphi) * vy;
      vyb =-cos(Lthe)*sin(Lphi) * vx + cos(Lphi) * vy;
      vzb = sin(Lthe) * vx;

      if(zb0 < 0.0)
        vzb = -vzb;

      vx = vxb * cos(PI/2.0-inc) + vzb * sin(PI/2.0-inc);
      vy = vyb;
      vz =-vxb * sin(PI/2.0-inc) + vzb * cos(PI/2.0-inc);

      V = -vx;  //note the definition of the line-of-sight velocity. postive means a receding
                // velocity relative to the observer.

      clouds_vel[i*parset.n_vel_per_cloud + j] = V;

      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\t%f\t%f\t%f\t%f\n", x, y, z, vx*VelUnit, vy*VelUnit, vz*VelUnit, weight);
      }
    }
  }

  return;
}

/*====================================================================
 * model 5
 *
 * geometry: double power-law
 * dynamics: elliptical orbits and inflow/outflow as in Pancoast's model
 *====================================================================
 */
void gen_cloud_sample_model5(const void *pm, int flag_type, int flag_save)
{
  int i, j, nc;
  double r, phi, cos_phi, sin_phi, dis, Lopn_cos;
  double x, y, z, xb, yb, zb, zb0;
  double inc, Fin, Fout, alpha, k, gam, mu, xi;
  double mbh, fellip, fflow, sigr_circ, sigthe_circ, sigr_rad, sigthe_rad, theta_rot, sig_turb;
  double Lphi, Lthe, V, Vr, Vph, Vkep, rhoV, theV;
  double cos_Lphi, sin_Lphi, cos_Lthe, sin_Lthe, cos_inc_cmp, sin_inc_cmp;
  double weight, rndr, rnd, rnd_frac, rnd_xi, frac1, frac2, ratio, Rs, g;
  double vx, vy, vz, vxb, vyb, vzb;
  BLRmodel5 *model = (BLRmodel5 *)pm;

  Lopn_cos = cos(model->opn*PI/180.0); /* cosine of openning angle */
  inc = acos(model->inc);         /* inclination angle in rad */
  alpha = model->alpha;
  Fin = model->Fin;
  Fout = exp(model->Fout);
  mu = exp(model->mu);            /* mean radius */
  k = model->k;
  gam = model->gam;
  xi = model->xi;

  if(flag_type == 1) // 1D RM, no dynamical parameters
  {
    mbh = 0.0;
  }
  else
  {
    mbh = exp(model->mbh);
    fellip = model->fellip;
    fflow = model->fflow;
    sigr_circ = exp(model->sigr_circ);
    sigthe_circ = exp(model->sigthe_circ);
    sigr_rad = exp(model->sigr_rad);
    sigthe_rad = exp(model->sigthe_rad);
    theta_rot = model->theta_rot*PI/180.0;
    sig_turb = exp(model->sig_turb);
  }
  Rs = 3.0e11*mbh/CM_PER_LD; // Schwarzchild radius in a unit of light-days

  if(mu*Fout > rcloud_max_set)
    Fout = rcloud_max_set/mu;

  frac1 = 1.0/(alpha+1.0) * (1.0 - pow(Fin,   alpha+1.0));
  frac2 = 1.0/(alpha-1.0) * (1.0 - pow(Fout, -alpha+1.0));
  ratio = frac1/(frac1 + frac2);

  sin_inc_cmp = cos(inc);//sin(PI/2.0 - inc);
  cos_inc_cmp = sin(inc);//cos(PI/2.0 - inc);

  for(i=0; i<parset.n_cloud_per_task; i++)
  {
    /* generate a direction of the angular momentum of the orbit  */
    Lphi = 2.0*PI * gsl_rng_uniform(gsl_r);
    //Lthe = acos(Lopn_cos + (1.0-Lopn_cos) * pow(gsl_rng_uniform(gsl_r), gam));
    Lthe = theta_sample(gam, Lopn_cos, 1.0);

    cos_Lphi = cos(Lphi);
    sin_Lphi = sin(Lphi);
    cos_Lthe = cos(Lthe);
    sin_Lthe = sin(Lthe);

    nc = 0;
    r = rcloud_max_set+1.0;
    while(r>rcloud_max_set || r<rcloud_min_set)
    {
      if(nc > 1000)
      {
        printf("# Error, too many tries in generating ridial location of clouds.\n");
        exit(0);
      }

      rnd_frac = gsl_rng_uniform(gsl_r);
      rnd = gsl_rng_uniform(gsl_r);
      if(rnd_frac < ratio)
      {
        rndr = pow( 1.0 - rnd * (1.0 - pow(Fin, alpha+1.0)), 1.0/(1.0+alpha));
      }
      else
      {
        rndr = pow( 1.0 - rnd * (1.0 - pow(Fout, -alpha+1.0)), 1.0/(1.0-alpha));
      }
      r = rndr*mu + Rs;
      nc++;
    }
    phi = 2.0*PI * gsl_rng_uniform(gsl_r);
    cos_phi = cos(phi);
    sin_phi = sin(phi);

    /* Polar coordinates to Cartesian coordinate */
    x = r * cos_phi;
    y = r * sin_phi;
    z = 0.0;

/* right-handed framework
 * first rotate around y axis by an angle of Lthe, then rotate around z axis
 * by an angle of Lphi
 */
  /*xb = cos(Lthe)*cos(Lphi) * x + sin(Lphi) * y - sin(Lthe)*cos(Lphi) * z;
    yb =-cos(Lthe)*sin(Lphi) * x + cos(Lphi) * y + sin(Lthe)*sin(Lphi) * z;
    zb = sin(Lthe) * x + cos(Lthe) * z;*/

    xb = cos_Lthe*cos_Lphi * x + sin_Lphi * y;
    yb =-cos_Lthe*sin_Lphi * x + cos_Lphi * y;
    zb = sin_Lthe * x;

    zb0 = zb;
    rnd_xi = gsl_rng_uniform(gsl_r);
    if( (rnd_xi < 1.0 - xi) && zb0 < 0.0)
      zb = -zb;

// counter-rotate around y, LOS is x-axis
    x = xb * cos_inc_cmp + zb * sin_inc_cmp;
    y = yb;
    z =-xb * sin_inc_cmp + zb * cos_inc_cmp;

    weight = 0.5 + k*(x/r);
    clouds_weight[i] = weight;

#ifndef SpecAstro
    dis = r - x;
    clouds_tau[i] = dis;
    if(flag_type == 1)
    {
      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\n", x, y, z);
      }
      continue;
    }
#else
  switch(flag_type)
  {
    case 1:   /* 1D RM */
      dis = r - x;
      clouds_tau[i] = dis;
      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\n", x, y, z);
      }
      continue;
      break;

    case 2:  /* 2D RM */
      dis = r - x;
      clouds_tau[i] = dis;
      break;

    case 3: /* SA */
      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;

    case 4: /* 1D RM + SA */
      dis = r - x;
      clouds_tau[i] = dis;

      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;

    case 5: /* 2D RM + SA */
      dis = r - x;
      clouds_tau[i] = dis;

      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;
  }

#endif

    Vkep = sqrt(mbh/r);

    for(j=0; j<parset.n_vel_per_cloud; j++)
    {
      rnd = gsl_rng_uniform(gsl_r);

      if(rnd < fellip)
      {
        rhoV = (gsl_ran_ugaussian(gsl_r) * sigr_circ  + 1.0) * Vkep;
        theV = (gsl_ran_ugaussian(gsl_r) * sigthe_circ + 0.5) * PI;
      }
      else
      {
        if(fflow <= 0.5)
        {
          rhoV = (gsl_ran_ugaussian(gsl_r) * sigr_rad  + 1.0) * Vkep;
          theV = (gsl_ran_ugaussian(gsl_r) * sigthe_rad + 1.0) * PI + theta_rot;
        }
        else
        {
          rhoV = (gsl_ran_ugaussian(gsl_r) * sigr_rad  + 1.0) * Vkep;
          theV = (gsl_ran_ugaussian(gsl_r) * sigthe_rad ) * PI + theta_rot;
        }
      }

      Vr = sqrt(2.0) * rhoV * cos(theV);
      Vph = rhoV * fabs(sin(theV)); /* make all clouds co-rotate */

      vx = Vr * cos_phi - Vph * sin_phi;
      vy = Vr * sin_phi + Vph * cos_phi;
      vz = 0.0;

    /*vxb = cos(Lthe)*cos(Lphi) * vx + sin(Lphi) * vy - sin(Lthe)*cos(Lphi) * vz;
      vyb =-cos(Lthe)*sin(Lphi) * vx + cos(Lphi) * vy + sin(Lthe)*sin(Lphi) * vz;
      vzb = sin(Lthe) * vx + cos(Lthe) * vz;*/

      vxb = cos_Lthe*cos_Lphi * vx + sin_Lphi * vy;
      vyb =-cos_Lthe*sin_Lphi * vx + cos_Lphi * vy;
      vzb = sin_Lthe * vx;

      if((rnd_xi < 1.0-xi) && zb0 < 0.0)
        vzb = -vzb;

      vx = vxb * cos_inc_cmp + vzb * sin_inc_cmp;
      vy = vyb;
      vz =-vxb * sin_inc_cmp + vzb * cos_inc_cmp;

      V = -vx;  //note the definition of the line-of-sight velocity. postive means a receding
                // velocity relative to the observer.

      V += gsl_ran_ugaussian(gsl_r) * sig_turb * Vkep; // add turbulence velocity

      if(fabs(V) >= C_Unit) // make sure that the velocity is smaller than speed of light
        V = 0.9999*C_Unit * (V>0.0?1.0:-1.0);

      g = sqrt( (1.0 + V/C_Unit) / (1.0 - V/C_Unit) ) / sqrt(1.0 - Rs/r); //relativistic effects
      V = (g-1.0)*C_Unit;

      clouds_vel[i*parset.n_vel_per_cloud + j] = V;

      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\t%f\t%f\t%f\t%f\n", x, y, z, vx*VelUnit, vy*VelUnit, vz*VelUnit, weight);
      }
    }
  }

  return;
}

/*================================================================
 * model 6
 * Pancoast et al. (2014)'s model
 *
 * geometry: radial Gamma distribution
 * dynamics: ellipitcal orbits and inflow/outflow
 *================================================================
 */
void gen_cloud_sample_model6(const void *pm, int flag_type, int flag_save)
{
  int i, j, nc;
  double r, phi, cos_phi, sin_phi, dis, Lopn_cos;
  double x, y, z, xb, yb, zb, zb0, vx, vy, vz, vxb, vyb, vzb;
  double V, rhoV, theV, Vr, Vph, Vkep, Rs, g;
  double inc, F, beta, mu, k, gam, xi, a, s, sig, rin;
  double mbh, fellip, fflow, sigr_circ, sigthe_circ, sigr_rad, sigthe_rad, theta_rot, sig_turb;
  double Lphi, Lthe, sin_Lphi, cos_Lphi, sin_Lthe, cos_Lthe, sin_inc_cmp, cos_inc_cmp;
  double weight, rnd, rnd_xi;
  BLRmodel6 *model = (BLRmodel6 *)pm;

  Lopn_cos = cos(model->opn*PI/180.0); /* cosine of openning angle */
  inc = acos(model->inc);         /* inclination angle in rad */
  beta = model->beta;
  F = model->F;
  mu = exp(model->mu);                 /* mean radius */
  k = model->k;
  gam = model-> gam;
  xi = model->xi;

  if(flag_type == 1) // 1D RM, no mbh parameters
  {
    mbh = 0.0;
  }
  else
  {
    mbh = exp(model->mbh);
    fellip = model->fellip;
    fflow = model->fflow;
    sigr_circ = exp(model->sigr_circ);
    sigthe_circ = exp(model->sigthe_circ);
    sigr_rad = exp(model->sigr_rad);
    sigthe_rad = exp(model->sigthe_rad);
    theta_rot = model->theta_rot*PI/180.0;
    sig_turb = exp(model->sig_turb);
  }
  Rs = 3.0e11*mbh/CM_PER_LD; // Schwarzchild radius in a unit of light-days

  a = 1.0/beta/beta;
  s = mu/a;
  rin=mu*F + Rs;  // include Scharzschild radius
  sig=(1.0-F)*s;

  sin_inc_cmp = cos(inc); //sin(PI/2.0 - inc);
  cos_inc_cmp = sin(inc); //cos(PI/2.0 - inc);

  for(i=0; i<parset.n_cloud_per_task; i++)
  {
// generate a direction of the angular momentum of the orbit
    Lphi = 2.0*PI * gsl_rng_uniform(gsl_r);
    //Lthe = acos(Lopn_cos + (1.0-Lopn_cos) * pow(gsl_rng_uniform(gsl_r), gam));
    Lthe = theta_sample(gam, Lopn_cos, 1.0);

    sin_Lphi = sin(Lphi);
    cos_Lphi = cos(Lphi);
    sin_Lthe = sin(Lthe);
    cos_Lthe = cos(Lthe);

    nc = 0;
    r = rcloud_max_set+1.0;
    while(r>rcloud_max_set || r<rcloud_min_set)
    {
      if(nc > 1000)
      {
        printf("# Error, too many tries in generating ridial location of clouds.\n");
        exit(0);
      }
      rnd = gsl_ran_gamma(gsl_r, a, 1.0);
//    r = mu * F + (1.0-F) * gsl_ran_gamma(gsl_r, 1.0/beta/beta, beta*beta*mu);
      r = rin + sig * rnd;
      nc++;
    }
    phi = 2.0*PI * gsl_rng_uniform(gsl_r);
    cos_phi = cos(phi);
    sin_phi = sin(phi);

    /* Polar coordinates to Cartesian coordinate */
    x = r * cos_phi;
    y = r * sin_phi;
    z = 0.0;

/* right-handed framework
 * first rotate around y axis by an angle of Lthe, then rotate around z axis
 * by an angle of Lphi
 */
  /*xb = cos(Lthe)*cos(Lphi) * x + sin(Lphi) * y - sin(Lthe)*cos(Lphi) * z;
    yb =-cos(Lthe)*sin(Lphi) * x + cos(Lphi) * y + sin(Lthe)*sin(Lphi) * z;
    zb = sin(Lthe) * x + cos(Lthe) * z; */

    xb = cos_Lthe*cos_Lphi * x + sin_Lphi * y;
    yb =-cos_Lthe*sin_Lphi * x + cos_Lphi * y;
    zb = sin_Lthe * x;

    zb0 = zb;
    rnd_xi = gsl_rng_uniform(gsl_r);
    if( (rnd_xi < 1.0 - xi) && zb0 < 0.0)
      zb = -zb;

// counter-rotate around y, LOS is x-axis
    /* x = xb * cos(PI/2.0-inc) + zb * sin(PI/2.0-inc);
       y = yb;
       z =-xb * sin(PI/2.0-inc) + zb * cos(PI/2.0-inc); */

    x = xb * cos_inc_cmp + zb * sin_inc_cmp;
    y = yb;
    z =-xb * sin_inc_cmp + zb * cos_inc_cmp;

    weight = 0.5 + k*(x/r);
    clouds_weight[i] = weight;

#ifndef SpecAstro
    dis = r - x;
    clouds_tau[i] = dis;
    if(flag_type == 1)  // 1D RM
    {
      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\n", x, y, z);
      }
      continue;
    }
#else
  switch(flag_type)
  {
    case 1:   /* 1D RM */
      dis = r - x;
      clouds_tau[i] = dis;
      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\n", x, y, z);
      }
      continue;
      break;

    case 2:  /* 2D RM */
      dis = r - x;
      clouds_tau[i] = dis;
      break;

    case 3: /* SA */
      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;

    case 4: /* 1D RM + SA */
      dis = r - x;
      clouds_tau[i] = dis;

      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;

    case 5: /* 2D RM + SA */
      dis = r - x;
      clouds_tau[i] = dis;

      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;
  }

#endif

    Vkep = sqrt(mbh/r);

    for(j=0; j<parset.n_vel_per_cloud; j++)
    {
      rnd = gsl_rng_uniform(gsl_r);

      if(rnd < fellip)
      {
        rhoV = (gsl_ran_ugaussian(gsl_r) * sigr_circ  + 1.0) * Vkep;
        theV =  (gsl_ran_ugaussian(gsl_r) * sigthe_circ + 0.5)*PI;
      }
      else
      {
        if(fflow <= 0.5) /* inflow */
        {
          rhoV = (gsl_ran_ugaussian(gsl_r) * sigr_rad   + 1.0) * Vkep;
          theV = (gsl_ran_ugaussian(gsl_r) * sigthe_rad + 1.0) * PI + theta_rot;
        }
        else     /* outflow */
        {
          rhoV = (gsl_ran_ugaussian(gsl_r) * sigr_rad  + 1.0) * Vkep;
          theV = (gsl_ran_ugaussian(gsl_r) * sigthe_rad) * PI + theta_rot;
        }
      }

      Vr = sqrt(2.0) * rhoV * cos(theV);
      Vph = rhoV * fabs(sin(theV)); /* make all clouds co-rotate */

      vx = Vr * cos_phi - Vph * sin_phi;
      vy = Vr * sin_phi + Vph * cos_phi;
      vz = 0.0;

    /*vxb = cos(Lthe)*cos(Lphi) * vx + sin(Lphi) * vy - sin(Lthe)*cos(Lphi) * vz;
      vyb =-cos(Lthe)*sin(Lphi) * vx + cos(Lphi) * vy + sin(Lthe)*sin(Lphi) * vz;
      vzb = sin(Lthe) * vx + cos(Lthe) * vz;*/

      vxb = cos_Lthe*cos_Lphi * vx + sin_Lphi * vy;
      vyb =-cos_Lthe*sin_Lphi * vx + cos_Lphi * vy;
      vzb = sin_Lthe * vx;

      if((rnd_xi < 1.0-xi) && zb0 < 0.0)
        vzb = -vzb;

      /*vx = vxb * cos(PI/2.0-inc) + vzb * sin(PI/2.0-inc);
      vy = vyb;
      vz =-vxb * sin(PI/2.0-inc) + vzb * cos(PI/2.0-inc);*/

      vx = vxb * cos_inc_cmp + vzb * sin_inc_cmp;
      vy = vyb;
      vz =-vxb * sin_inc_cmp + vzb * cos_inc_cmp;

      V = -vx;  //note the definition of the line-of-sight velocity. postive means a receding
                // velocity relative to the observer.

      V += gsl_ran_ugaussian(gsl_r) * sig_turb * Vkep; // add turbulence velocity

      if(fabs(V) >= C_Unit) // make sure that the velocity is smaller than speed of light
        V = 0.9999*C_Unit * (V>0.0?1.0:-1.0);

      g = sqrt( (1.0 + V/C_Unit) / (1.0 - V/C_Unit) ) / sqrt(1.0 - Rs/r); //relativistic effects
      V = (g-1.0)*C_Unit;

      clouds_vel[i*parset.n_vel_per_cloud + j] = V;

      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\t%f\t%f\t%f\t%f\n", x, y, z, vx*VelUnit, vy*VelUnit, vz*VelUnit, weight);
      }
    }
  }

  return;
}

/*================================================================
 * model 7
 * shadowed model
 *
 * geometry: radial Gamma distribution
 * dynamics: elliptical orbits and inflow/outflow as in Pancoast'model
 *================================================================
 */
void gen_cloud_sample_model7(const void *pm, int flag_type, int flag_save)
{
  int i, j, nc, num_sh;
  double r, phi, dis, Lopn_cos, cos_phi, sin_phi;
  double x, y, z, xb, yb, zb, zb0, vx, vy, vz, vxb, vyb, vzb, Rs, g, sig_turb;
  double V, rhoV, theV, Vr, Vph, Vkep;
  double inc, F, beta, mu, k, gam, xi, a, s, sig, rin;
  double mbh, fellip, fflow, sigr_circ, sigthe_circ, sigr_rad, sigthe_rad, theta_rot;
  double Lphi, Lthe, sin_Lphi, cos_Lphi, sin_Lthe, cos_Lthe, sin_inc_cmp, cos_inc_cmp;
  double weight, rnd, rnd_xi;
  BLRmodel7 *model = (BLRmodel7 *)pm;

  Lopn_cos = cos(model->opn*PI/180.0); /* cosine of openning angle */
  inc = acos(model->inc);         /* inclination angle in rad */
  beta = model->beta;
  F = model->F;
  mu = exp(model->mu);                 /* mean radius */
  k = model->k;
  gam = model-> gam;
  xi = model->xi;

  if(flag_type == 1) // 1D RM, no dyamical parameters
  {
    mbh = 0.0;
  }
  else
  {
    mbh = exp(model->mbh);
    fellip = model->fellip;
    fflow = model->fflow;
    sigr_circ = exp(model->sigr_circ);
    sigthe_circ = exp(model->sigthe_circ);
    sigr_rad = exp(model->sigr_rad);
    sigthe_rad = exp(model->sigthe_rad);
    theta_rot = model->theta_rot*PI/180.0;
    sig_turb = exp(model->sig_turb);
  }
  Rs = 3.0e11*mbh/CM_PER_LD; // Schwarzchild radius in a unit of light-days

  a = 1.0/beta/beta;
  s = mu/a;
  rin=Rs + mu*F;
  sig=(1.0-F)*s;

  sin_inc_cmp = cos(inc); //sin(PI/2.0 - inc);
  cos_inc_cmp = sin(inc); //cos(PI/2.0 - inc);

  num_sh = (int)(parset.n_cloud_per_task * model->fsh);

  for(i=0; i<num_sh; i++)
  {
// generate a direction of the angular momentum of the orbit
    Lphi = 2.0*PI * gsl_rng_uniform(gsl_r);
    //Lthe = acos(Lopn_cos + (1.0-Lopn_cos) * pow(gsl_rng_uniform(gsl_r), gam));
    Lthe = theta_sample(gam, Lopn_cos, 1.0);

    sin_Lphi = sin(Lphi);
    cos_Lphi = cos(Lphi);
    sin_Lthe = sin(Lthe);
    cos_Lthe = cos(Lthe);

    nc = 0;
    r = rcloud_max_set+1.0;
    while(r>rcloud_max_set || r<rcloud_min_set)
    {
      if(nc > 1000)
      {
        printf("# Error, too many tries in generating ridial location of clouds.\n");
        exit(0);
      }
      rnd = gsl_ran_gamma(gsl_r, a, 1.0);
//    r = mu * F + (1.0-F) * gsl_ran_gamma(gsl_r, 1.0/beta/beta, beta*beta*mu);
      r = rin + sig * rnd;
      nc++;
    }
    phi = 2.0*PI * gsl_rng_uniform(gsl_r);
    cos_phi = cos(phi);
    sin_phi = sin(phi);

    /* Polar coordinates to Cartesian coordinate */
    x = r * cos_phi;
    y = r * sin_phi;
    z = 0.0;

/* right-handed framework
 * first rotate around y axis by an angle of Lthe, then rotate around z axis
 * by an angle of Lphi
 */
  /*xb = cos(Lthe)*cos(Lphi) * x + sin(Lphi) * y - sin(Lthe)*cos(Lphi) * z;
    yb =-cos(Lthe)*sin(Lphi) * x + cos(Lphi) * y + sin(Lthe)*sin(Lphi) * z;
    zb = sin(Lthe) * x + cos(Lthe) * z; */

    xb = cos_Lthe*cos_Lphi * x + sin_Lphi * y;
    yb =-cos_Lthe*sin_Lphi * x + cos_Lphi * y;
    zb = sin_Lthe * x;

    zb0 = zb;
    rnd_xi = gsl_rng_uniform(gsl_r);
    if( (rnd_xi < 1.0 - xi) && zb0 < 0.0)
      zb = -zb;

// counter-rotate around y, LOS is x-axis
    x = xb * cos_inc_cmp + zb * sin_inc_cmp;
    y = yb;
    z =-xb * sin_inc_cmp + zb * cos_inc_cmp;

    weight = 0.5 + k*(x/r);
    clouds_weight[i] = weight;

#ifndef SpecAstro
    dis = r - x;
    clouds_tau[i] = dis;
    if(flag_type == 1)
    {
      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\n", x, y, z);
      }
      continue;
    }
#else
  switch(flag_type)
  {
    case 1:   /* 1D RM */
      dis = r - x;
      clouds_tau[i] = dis;
      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\n", x, y, z);
      }
      continue;
      break;

    case 2:  /* 2D RM */
      dis = r - x;
      clouds_tau[i] = dis;
      break;

    case 3: /* SA */
      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;

    case 4: /* 1D RM + SA */
      dis = r - x;
      clouds_tau[i] = dis;

      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;

    case 5: /* 2D RM + SA */
      dis = r - x;
      clouds_tau[i] = dis;

      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;
  }

#endif

    Vkep = sqrt(mbh/r);

    for(j=0; j<parset.n_vel_per_cloud; j++)
    {
      rnd = gsl_rng_uniform(gsl_r);

      if(rnd < fellip)
      {
        rhoV = (gsl_ran_ugaussian(gsl_r) * sigr_circ  + 1.0) * Vkep;
        theV = (gsl_ran_ugaussian(gsl_r) * sigthe_circ + 0.5) * PI;
      }
      else
      {
        if(fflow <= 0.5)
        {
          rhoV = (gsl_ran_ugaussian(gsl_r) * sigr_rad  + 1.0) * Vkep;
          theV = (gsl_ran_ugaussian(gsl_r) * sigthe_rad + 1.0) *PI + theta_rot;
        }
        else
        {
          rhoV = (gsl_ran_ugaussian(gsl_r) * sigr_rad  + 1.0) * Vkep;
          theV = (gsl_ran_ugaussian(gsl_r) * sigthe_rad) * PI + theta_rot;
        }
      }

      Vr = sqrt(2.0) * rhoV * cos(theV);
      Vph = rhoV * fabs(sin(theV)); /* make all clouds co-rotate */

      vx = Vr * cos_phi - Vph * sin_phi;
      vy = Vr * sin_phi + Vph * cos_phi;
      vz = 0.0;

    /*vxb = cos(Lthe)*cos(Lphi) * vx + sin(Lphi) * vy - sin(Lthe)*cos(Lphi) * vz;
      vyb =-cos(Lthe)*sin(Lphi) * vx + cos(Lphi) * vy + sin(Lthe)*sin(Lphi) * vz;
      vzb = sin(Lthe) * vx + cos(Lthe) * vz;*/

      vxb = cos_Lthe*cos_Lphi * vx + sin_Lphi * vy;
      vyb =-cos_Lthe*sin_Lphi * vx + cos_Lphi * vy;
      vzb = sin_Lthe * vx;

      if((rnd_xi < 1.0-xi) && zb0 < 0.0)
        vzb = -vzb;

      vx = vxb * cos_inc_cmp + vzb * sin_inc_cmp;
      vy = vyb;
      vz =-vxb * sin_inc_cmp + vzb * cos_inc_cmp;

      V = -vx;  //note the definition of the line-of-sight velocity. postive means a receding
                // velocity relative to the observer.

      V += gsl_ran_ugaussian(gsl_r) * sig_turb * Vkep;

      if(fabs(V) >= C_Unit) // make sure that the velocity is smaller than speed of light
        V = 0.9999*C_Unit * (V>0.0?1.0:-1.0);

      g = sqrt( (1.0 + V/C_Unit) / (1.0 - V/C_Unit) ) / sqrt(1.0 - Rs/r); //relativistic effects
      V = (g-1.0)*C_Unit;

      clouds_vel[i*parset.n_vel_per_cloud + j] = V;

      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\t%f\t%f\t%f\t%f\n", x, y, z, vx*VelUnit, vy*VelUnit, vz*VelUnit, weight);
      }
    }
  }

  //second BLR region
  rin = exp(model->mu_un) * model->F_un + Rs;
  a = 1.0/(model->beta_un * model->beta_un);
  sig = exp(model->mu_un) * (1.0-model->F_un)/a;
  double Lopn_cos_un1, Lopn_cos_un2;
  Lopn_cos_un1 = Lopn_cos;
  if(model->opn + model->opn_un < 90.0)
    Lopn_cos_un2 = cos((model->opn + model->opn_un)/180.0*PI);
  else
    Lopn_cos_un2 = 0.0;

  if(flag_type != 1)  // 1D RM, no dynamical parameters
  {
    fellip = model->fellip_un;
    fflow = model->fflow_un;
  }

  for(i=num_sh; i<parset.n_cloud_per_task; i++)
  {
// generate a direction of the angular momentum of the orbit
    Lphi = 2.0*PI * gsl_rng_uniform(gsl_r);
    //Lthe = acos(Lopn_cos_un2 + (Lopn_cos_un1-Lopn_cos_un2) * pow(gsl_rng_uniform(gsl_r), gam));
    //Lthe = acos(Lopn_cos + (1.0-Lopn_cos) * pow(gsl_rng_uniform(gsl_r), gam));
    Lthe = theta_sample(gam, Lopn_cos_un2, Lopn_cos_un1);

    sin_Lphi = sin(Lphi);
    cos_Lphi = cos(Lphi);
    sin_Lthe = sin(Lthe);
    cos_Lthe = cos(Lthe);

    nc = 0;
    r = rcloud_max_set+1.0;
    while(r>rcloud_max_set || r<rcloud_min_set)
    {
      if(nc > 1000)
      {
        printf("# Error, too many tries in generating ridial location of clouds.\n");
        exit(0);
      }
      rnd = gsl_ran_gamma(gsl_r, a, 1.0);
//    r = mu * F + (1.0-F) * gsl_ran_gamma(gsl_r, 1.0/beta/beta, beta*beta*mu);
      r = rin + sig * rnd;
      nc++;
    }
    phi = 2.0*PI * gsl_rng_uniform(gsl_r);
    cos_phi = cos(phi);
    sin_phi = sin(phi);

    /* Polar coordinates to Cartesian coordinate */
    x = r * cos_phi;
    y = r * sin_phi;
    z = 0.0;

/* right-handed framework
 * first rotate around y axis by an angle of Lthe, then rotate around z axis
 * by an angle of Lphi
 */
  /*xb = cos(Lthe)*cos(Lphi) * x + sin(Lphi) * y - sin(Lthe)*cos(Lphi) * z;
    yb =-cos(Lthe)*sin(Lphi) * x + cos(Lphi) * y + sin(Lthe)*sin(Lphi) * z;
    zb = sin(Lthe) * x + cos(Lthe) * z; */

    xb = cos_Lthe*cos_Lphi * x + sin_Lphi * y;
    yb =-cos_Lthe*sin_Lphi * x + cos_Lphi * y;
    zb = sin_Lthe * x;

    zb0 = zb;
    rnd_xi = gsl_rng_uniform(gsl_r);
    if( (rnd_xi < 1.0 - xi) && zb0 < 0.0)
      zb = -zb;

// counter-rotate around y, LOS is x-axis
    x = xb * cos_inc_cmp + zb * sin_inc_cmp;
    y = yb;
    z =-xb * sin_inc_cmp + zb * cos_inc_cmp;

    weight = 0.5 + k*(x/r);
    clouds_weight[i] = weight;

#ifndef SpecAstro
    dis = r - x;
    clouds_tau[i] = dis;
    if(flag_type == 1)
    {
      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\n", x, y, z);
      }
      continue;
    }
#else
  switch(flag_type)
  {
    case 1:   /* 1D RM */
      dis = r - x;
      clouds_tau[i] = dis;
      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\n", x, y, z);
      }
      continue;
      break;

    case 2:  /* 2D RM */
      dis = r - x;
      clouds_tau[i] = dis;
      break;

    case 3: /* SA */
      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;

    case 4: /* 1D RM + SA */
      dis = r - x;
      clouds_tau[i] = dis;

      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;

    case 5: /* 2D RM + SA */
      dis = r - x;
      clouds_tau[i] = dis;

      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;
  }

#endif

    Vkep = sqrt(mbh/r);

    for(j=0; j<parset.n_vel_per_cloud; j++)
    {
      rnd = gsl_rng_uniform(gsl_r);

      if(rnd < fellip)
      {
        rhoV = (gsl_ran_ugaussian(gsl_r) * sigr_circ  + 1.0) * Vkep;
        theV = (gsl_ran_ugaussian(gsl_r) * sigthe_circ + 0.5) * PI;
      }
      else
      {
        if(fflow <= 0.5)
        {
          rhoV = (gsl_ran_ugaussian(gsl_r) * sigr_rad  + 1.0) * Vkep;
          theV = (gsl_ran_ugaussian(gsl_r) * sigthe_rad + 1.0) *PI + theta_rot;
        }
        else
        {
          rhoV = (gsl_ran_ugaussian(gsl_r) * sigr_rad  + 1.0) * Vkep;
          theV = (gsl_ran_ugaussian(gsl_r) * sigthe_rad) * PI + theta_rot;
        }
      }

      Vr = sqrt(2.0) * rhoV * cos(theV);
      Vph = rhoV * fabs(sin(theV)); /* make all clouds co-rotate */

      vx = Vr * cos_phi - Vph * sin_phi;
      vy = Vr * sin_phi + Vph * cos_phi;
      vz = 0.0;

    /*vxb = cos(Lthe)*cos(Lphi) * vx + sin(Lphi) * vy - sin(Lthe)*cos(Lphi) * vz;
      vyb =-cos(Lthe)*sin(Lphi) * vx + cos(Lphi) * vy + sin(Lthe)*sin(Lphi) * vz;
      vzb = sin(Lthe) * vx + cos(Lthe) * vz;*/

      vxb = cos_Lthe*cos_Lphi * vx + sin_Lphi * vy;
      vyb =-cos_Lthe*sin_Lphi * vx + cos_Lphi * vy;
      vzb = sin_Lthe * vx;

      if((rnd_xi < 1.0-xi) && zb0 < 0.0)
        vzb = -vzb;

      vx = vxb * cos_inc_cmp + vzb * sin_inc_cmp;
      vy = vyb;
      vz =-vxb * sin_inc_cmp + vzb * cos_inc_cmp;

      V = -vx;  //note the definition of the line-of-sight velocity. postive means a receding
                // velocity relative to the observer.

      V += gsl_ran_ugaussian(gsl_r) * sig_turb * Vkep;

      if(fabs(V) >= C_Unit) // make sure that the velocity is smaller than speed of light
        V = 0.9999*C_Unit * (V>0.0?1.0:-1.0);

      g = sqrt( (1.0 + V/C_Unit) / (1.0 - V/C_Unit) ) / sqrt(1.0 - Rs/r); //relativistic effects
      V = (g-1.0)*C_Unit;

      clouds_vel[i*parset.n_vel_per_cloud + j] = V;

      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\t%f\t%f\t%f\t%f\n", x, y, z, vx*VelUnit, vy*VelUnit, vz*VelUnit, weight);
      }
    }
  }

  return;
}

/*
 * model 8, generate cloud sample.
 * a disk wind model.
 */
void gen_cloud_sample_model8(const void *pm, int flag_type, int flag_save)
{
  int i;
  double theta_min, theta_max, r_min, r_max, Rblr, Rv, mbh, alpha, gamma, xi, lambda, k;
  double rnd, r, r0, theta, phi, xb, yb, zb, x, y, z, l, weight, sin_inc_cmp, cos_inc_cmp, inc;
  double dis, density, vl, vesc, Vr, Vph, vx, vy, vz, vxb, vyb, vzb, V, rnd_xi, zb0, lmax, R;
  double v0=6.0/VelUnit;
  BLRmodel8 *model=(BLRmodel8 *)pm;

  theta_min = model->theta_min/180.0 * PI;
  theta_max = model->dtheta_max/180.0*PI + theta_min;
  theta_max = fmin(theta_max, 0.5*PI);
  model->dtheta_max = (theta_max-theta_min)/PI*180;

  r_min = exp(model->r_min);
  r_max = model->fr_max * r_min;
  if(r_max > 0.5*rcloud_max_set) r_max = 0.5*rcloud_max_set;
  model->fr_max = r_max/r_min;

  gamma = model->gamma;
  alpha = model->alpha;
  lambda = model->lamda;

  k = model->k;
  xi = model->xi;

  Rv = exp(model->Rv);
  Rblr = exp(model->Rblr);
  if(Rblr < r_max) Rblr = r_max;
  model->Rblr = log(Rblr);
  inc = acos(model->inc);

  mbh = exp(model->mbh);

  sin_inc_cmp = cos(inc); //sin(PI/2.0 - inc);
  cos_inc_cmp = sin(inc); //cos(PI/2.0 - inc);

  for(i=0; i<parset.n_cloud_per_task; i++)
  {
    rnd = gsl_rng_uniform(gsl_r);
    r0 = r_min +  rnd * (r_max - r_min);
    theta = theta_min + (theta_max - theta_min) * pow(rnd, gamma);
    /* the maximum allowed value of l */
    lmax = -r0*sin(theta) + sqrt(r0*r0*sin(theta)*sin(theta) + (Rblr*Rblr - r0*r0));
    l = gsl_rng_uniform(gsl_r) * lmax;

    r = l * sin(theta) + r0;
    if(gsl_rng_uniform(gsl_r) < 0.5)
      zb = l * cos(theta);
    else
      zb =-l * cos(theta);

    phi = gsl_rng_uniform(gsl_r) * 2.0*PI;

    xb = r * cos(phi);
    yb = r * sin(phi);

    zb0 = zb;
    rnd_xi = gsl_rng_uniform(gsl_r);
    if( (rnd_xi < 1.0 - xi) && zb0 < 0.0)
      zb = -zb;

    vesc = sqrt(2.0*mbh/r0);
    vl = v0 + (vesc - v0) * pow(l/Rv, alpha)/(1.0 + pow(l/Rv, alpha));
    density = pow(r0, lambda)/vl;

    // counter-rotate around y, LOS is x-axis
    x = xb * cos_inc_cmp + zb * sin_inc_cmp;
    y = yb;
    z =-xb * sin_inc_cmp + zb * cos_inc_cmp;

    R = sqrt(r*r + zb*zb);
    weight = 0.5 + k*(x/R);
    clouds_weight[i] = weight * density;

#ifndef SpecAstro
    dis = R - x;
    clouds_tau[i] = dis;
    if(flag_type == 1)
    {
      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\n", x, y, z);
      }
      continue;
    }
#else
  switch(flag_type)
  {
    case 1:   /* 1D RM */
      dis = R - x;
      clouds_tau[i] = dis;
      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\n", x, y, z);
      }
      continue;
      break;

    case 2:  /* 2D RM */
      dis = R - x;
      clouds_tau[i] = dis;
      break;

    case 3: /* SA */
      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;

    case 4: /* 1D RM + SA */
      dis = R - x;
      clouds_tau[i] = dis;

      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;

    case 5: /* 2D RM + SA */
      dis = R - x;
      clouds_tau[i] = dis;

      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;
  }

#endif

    Vr = vl * sin(theta);
    vzb = vl * cos(theta);
    Vph = sqrt(mbh*r0) / r;

    vxb = Vr * cos(phi) - Vph * sin(phi);
    vyb = Vr * sin(phi) + Vph * cos(phi);

    if(zb < 0.0)
      vzb = -vzb;

    vx = vxb * cos_inc_cmp + vzb * sin_inc_cmp;
    vy = vyb;
    vz =-vxb * sin_inc_cmp + vzb * cos_inc_cmp;

    V = -vx;  //note the definition of the line-of-sight velocity. postive means a receding
                // velocity relative to the observer.
    clouds_vel[i] = V;

    if(flag_save && thistask==roottask)
    {
      if(i%(icr_cloud_save) == 0)
        fprintf(fcloud_out, "%f\t%f\t%f\t%f\t%f\t%f\t%f\n", x, y, z, vx*VelUnit, vy*VelUnit, vz*VelUnit, weight);
    }
  }

  return;
}

/*
 * model 9, generate cloud sample.
 * same with the Graivty Collaboration's model about 3C 273 published in Nature (2018, 563, 657),
 * except that the prior of inclination is uniform in cosine.
 *
 */
void gen_cloud_sample_model9(const void *pm, int flag_type, int flag_save)
{
  int i, j, nc;
  double r, phi, dis, Lopn_cos;
  double x, y, z, xb, yb, zb, vx, vy, vz, vxb, vyb, vzb;
  double inc, F, beta, mu, a, s, rin, sig;
  double Lphi, Lthe, Vkep, Rs, g;
  double V, weight, rnd, sin_inc_cmp, cos_inc_cmp;
  BLRmodel9 *model = (BLRmodel9 *)pm;
  double Vr, Vph, mbh;

  Lopn_cos = cos(model->opn*PI/180.0);
  //Lopn = model->opn*PI/180.0;
  inc = acos(model->inc);
  beta = model->beta;
  F = model->F;
  mu = exp(model->mu);

  if(flag_type == 1)  // 1D RM, no dynmaical parameters
  {
    mbh = 0.0;
  }
  else
  {
    mbh = exp(model->mbh);
  }
  Rs = 3.0e11*mbh/CM_PER_LD; // Schwarzchild radius in a unit of light-days

  a = 1.0/beta/beta;
  s = mu/a;
  rin = mu*F + Rs;
  sig = (1.0-F)*s;

  sin_inc_cmp = cos(inc); //sin(PI/2.0 - inc);
  cos_inc_cmp = sin(inc); //cos(PI/2.0 - inc);

  for(i=0; i<parset.n_cloud_per_task; i++)
  {
// generate a direction of the angular momentum
    Lphi = 2.0*PI * gsl_rng_uniform(gsl_r);
    //Lthe = acos(Lopn_cos + (1.0-Lopn_cos) * gsl_rng_uniform(gsl_r));
    //Lthe = gsl_rng_uniform(gsl_r) * Lopn;
    Lthe = theta_sample(1.0, Lopn_cos, 1.0);

    nc = 0;
    r = rcloud_max_set+1.0;
    while(r>rcloud_max_set || r<rcloud_min_set)
    {
      if(nc > 1000)
      {
        printf("# Error, too many tries in generating ridial location of clouds.\n");
        exit(0);
      }
      rnd = gsl_ran_gamma(gsl_r, a, 1.0);
//    r = mu * F + (1.0-F) * gsl_ran_gamma(gsl_r, 1.0/beta/beta, beta*beta*mu);
        r = rin + sig * rnd;
      nc++;
    }
    phi = 2.0*PI * gsl_rng_uniform(gsl_r);

    x = r * cos(phi);
    y = r * sin(phi);
    z = 0.0;

  /*xb =  cos(Lthe)*cos(Lphi) * x + sin(Lphi) * y - sin(Lthe)*cos(Lphi) * z;
    yb = -cos(Lthe)*sin(Lphi) * x + cos(Lphi) * y + sin(Lthe)*sin(Lphi) * z;
    zb =  sin(Lthe) * x + cos(Lthe) * z;*/

    xb =  cos(Lthe)*cos(Lphi) * x + sin(Lphi) * y;
    yb = -cos(Lthe)*sin(Lphi) * x + cos(Lphi) * y;
    zb =  sin(Lthe) * x;

    /* counter-rotate around y */
    //x = xb * cos(PI/2.0-inc) + zb * sin(PI/2.0-inc);
    //y = yb;
    //z =-xb * sin(PI/2.0-inc) + zb * cos(PI/2.0-inc);
    x = xb * cos_inc_cmp + zb * sin_inc_cmp;
    y = yb;
    z =-xb * sin_inc_cmp + zb * cos_inc_cmp;

    weight = 1.0;
    clouds_weight[i] = weight;

#ifndef SpecAstro
    dis = r - x;
    clouds_tau[i] = dis;
    if(flag_type == 1)
    {
      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\n", x, y, z);
      }
      continue;
    }
#else
  switch(flag_type)
  {
    case 1:   /* 1D RM */
      dis = r - x;
      clouds_tau[i] = dis;
      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\n", x, y, z);
      }
      continue;
      break;

    case 2:  /* 2D RM */
      dis = r - x;
      clouds_tau[i] = dis;
      break;

    case 3: /* SA */
      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;

    case 4: /* 1D RM + SA */
      dis = r - x;
      clouds_tau[i] = dis;

      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;

    case 5: /* 2D RM + SA */
      dis = r - x;
      clouds_tau[i] = dis;

      clouds_alpha[i] = y;
      clouds_beta[i] = z;
      break;
  }

#endif

    /* velocity
     * note that a cloud moves in its orbit plane, whose direction
     * is determined by the direction of its angular momentum.
     */
    Vkep = sqrt(mbh/r);

    for(j=0; j<parset.n_vel_per_cloud; j++)
    {
      Vr = 0.0;
      Vph = Vkep; /* RM cannot distinguish the orientation of the rotation. */

      vx = Vr * cos(phi) - Vph * sin(phi);
      vy = Vr * sin(phi) + Vph * cos(phi);
      vz = 0.0;

    /*vxb = cos(Lthe)*cos(Lphi) * vx + sin(Lphi) * vy - sin(Lthe)*cos(Lphi) * vz;
      vyb =-cos(Lthe)*sin(Lphi) * vx + cos(Lphi) * vy + sin(Lthe)*sin(Lphi) * vz;
      vzb = sin(Lthe) * vx + cos(Lthe) * vz;*/

      vxb = cos(Lthe)*cos(Lphi) * vx + sin(Lphi) * vy;
      vyb =-cos(Lthe)*sin(Lphi) * vx + cos(Lphi) * vy;
      vzb = sin(Lthe) * vx;

      //vx = vxb * cos(PI/2.0-inc) + vzb * sin(PI/2.0-inc);
      //vy = vyb;
      //vz =-vxb * sin(PI/2.0-inc) + vzb * cos(PI/2.0-inc);
      vx = vxb * cos_inc_cmp + vzb * sin_inc_cmp;
      vy = vyb;
      vz =-vxb * sin_inc_cmp + vzb * cos_inc_cmp;

      V = -vx;  //note the definition of the line-of-sight velocity. positive means a receding
                // velocity relative to the observer.

      if(fabs(V) >= C_Unit) // make sure that the velocity is smaller than speed of light
        V = 0.9999*C_Unit * (V>0.0?1.0:-1.0);

      g = (1.0 + V/C_Unit) / sqrt( (1.0 - V*V/C_Unit/C_Unit) ) / sqrt(1.0 - Rs/r); //relativistic effects
      V = (g-1.0)*C_Unit;

      clouds_vel[i*parset.n_vel_per_cloud + j] = V;

      if(flag_save && thistask==roottask)
      {
        if(i%(icr_cloud_save) == 0)
          fprintf(fcloud_out, "%f\t%f\t%f\t%f\t%f\t%f\t%f\n", x, y, z, vx*VelUnit, vy*VelUnit, vz*VelUnit, weight);
      }
    }
  }

  return;
}

void restart_action_1d(int iflag)
{
  /* at present, nothing needs to do */
  /*
  FILE *fp;
  char str[200];

  sprintf(str, "%s/data/clouds_%04d.txt", parset.file_dir, thistask);

  if(iflag == 0)  // write
    fp = fopen(str, "wb");
  else           // read
    fp = fopen(str, "rb");

  if(fp == NULL)
  {
    printf("# Cannot open file %s.\n", str);
  }

  if(iflag == 0)
  {
    printf("# Writing restart at task %d.\n", thistask);
  }
  else
  {
    printf("# Reading restart at task %d.\n", thistask);
  }
  fclose(fp);*/
  return;
}

void restart_action_2d(int iflag)
{
  restart_action_1d(iflag);
}

#ifdef SpecAstro
void restart_action_sa(int iflag)
{
  restart_action_1d(iflag);
}
#endif


/***************************************************************************
    File                 : MyParser.h
    Project              : QtiPlot
    --------------------------------------------------------------------
    Copyright            : (C) 2006 by Ion Vasilief
    Email (use @ for *)  : ion_vasilief*yahoo.fr
    Description          : Parser class based on muParser

 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *  This program is free software; you can redistribute it and/or modify   *
 *  it under the terms of the GNU General Public License as published by   *
 *  the Free Software Foundation; either version 2 of the License, or      *
 *  (at your option) any later version.                                    *
 *                                                                         *
 *  This program is distributed in the hope that it will be useful,        *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
 *  GNU General Public License for more details.                           *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the Free Software           *
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor,                    *
 *   Boston, MA  02110-1301  USA                                           *
 *                                                                         *
 ***************************************************************************/
#ifndef MYPARSER_H
#define MYPARSER_H

#include <muParser.h>
#include <muParserDLL.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_sf.h>

#include <qstringlist.h>

using namespace mu;

/*!\brief Mathematical parser class based on muParser.
 *
 * \section future Future Plans
 * Eliminate in favour of Script/ScriptingEnv.
 * This will allow you to use e.g. Python's global variables and functions everywhere.
 * Before this happens, a cleaner and more generic solution for accessing the current ScriptingEnv
 * should be implemented (maybe by making it a property of Project; see ApplicationWindow).
 */
class MyParser : public Parser
{
public:
	MyParser();

	static QStringList functionsList();
	static QString explainFunction(int index);

	static double bessel_J0(double x)
		{
		return gsl_sf_bessel_J0 (x);
		}

	static double bessel_J1(double x)
		{
		return gsl_sf_bessel_J1 (x);
		}

	static double bessel_Jn(double x, double n)
		{
		return gsl_sf_bessel_Jn ((int)n, x);
		}

	static double bessel_Y0(double x)
		{
		return gsl_sf_bessel_Y0 (x);
		}

	static double bessel_Y1(double x)
		{
		return gsl_sf_bessel_Y1 (x);
		}
	static double bessel_Yn(double x, double n)
		{
		return gsl_sf_bessel_Yn ((int)n, x);
		}
	static double beta(double a, double b)
		{
		return gsl_sf_beta (a, b);
		}
	static double erf(double x)
		{
		return gsl_sf_erf (x);
		}
	static double erfc(double x)
		{
		return gsl_sf_erfc (x);
		}
	static double erfz(double x)
		{
		return gsl_sf_erf_Z (x);
		}
	static double erfq(double x)
		{
		return gsl_sf_erf_Q (x);
		}
	static double gamma(double x)
		{
		return gsl_sf_gamma (x);
		}
	static double gammaln(double x)
		{
		return gsl_sf_lngamma (x);
		}
	static double hazard(double x)
		{
		return gsl_sf_hazard (x);
		}
};

#endif


#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <Python.h>
#include <numpy/arrayobject.h>

#include <math.h>

#ifdef __INTEL_COMPILER
#include <mkl_cblas.h>
#else
#include <cblas.h>
#define kmp_set_blocktime(k)
#endif

static inline int min (int a, int b) { return a < b ? a : b; }
static inline int max (int a, int b) { return a < b ? b : a; }

static inline int square(int x) { return x*x; }

static void max_filter_1d(const float *vals, float *out_vals, int32_t *I,
                          int s, int step, int n, float a, float b) {
  int i;
  for (i = 0; i < n; i++) {
    float max_val = -INFINITY;
    int argmax     = 0;
    int first      = max(0, i-s);
    int last       = min(n-1, i+s);
    int j;
    for (j = first; j <= last; j++) {
      float val = *(vals + j*step) - a*square(i-j) - b*(i-j);
      if (val > max_val) {
        max_val = val;
        argmax  = j;
      }
    }
    *(out_vals + i*step) = max_val;
    *(I + i*step) = argmax;
  }
}

PyObject * deformation_cost (PyArrayObject * pydata, float ax, float bx, float ay, float by, int s) {
  npy_intp * dims = PyArray_DIMS(pydata);
  npy_intp * stride = PyArray_STRIDES(pydata);

  if (PyArray_NDIM(pydata) != 2) {
    PyErr_SetString(PyExc_TypeError, "data must be 2 dimensional.");
    return NULL;
  }

  if (PyArray_DESCR(pydata)->type_num != NPY_FLOAT) {
    PyErr_SetString(PyExc_TypeError, "data must be single precision floating point.");
    return NULL;
  }

  if (stride[0] != dims[1]*sizeof(float)) {
    PyErr_SetString(PyExc_TypeError, "Stride[0] must be sizeof(float).");
    return NULL;
  }

  if (stride[1] != sizeof(float)) {
    PyErr_SetString(PyExc_TypeError, "Stride[1] must be Dims[0]*sizeof(float).");
    return NULL;
  }

  PyArrayObject * pydeformed = (PyArrayObject*)PyArray_SimpleNew((npy_intp)2, dims, NPY_FLOAT);
  PyArrayObject * pyIx = (PyArrayObject*)PyArray_SimpleNew((npy_intp)2, dims, NPY_INT32);
  PyArrayObject * pyIy = (PyArrayObject*)PyArray_SimpleNew((npy_intp)2, dims, NPY_INT32);

  float *tmpM = (float *)calloc(dims[0]*dims[1], sizeof(float));
  int32_t *tmpIx = (int32_t*)calloc(dims[0]*dims[1], sizeof(int32_t));
  int32_t *tmpIy = (int32_t*)calloc(dims[0]*dims[1], sizeof(int32_t));

  int x, y;

  for (y = 0; y < dims[0]; y++)
    max_filter_1d((float*)PyArray_GETPTR2(pydata, y, 0), tmpM+y*dims[1], tmpIx+y*dims[1], s, 1, dims[1], ax, bx);

  for (x = 0; x < dims[1]; x++)
    max_filter_1d(tmpM+x, (float*)PyArray_GETPTR2(pydeformed, 0, x), tmpIy+x, s, dims[1], dims[0], ay, by);

  for (x = 0; x < dims[1]; ++x) {
    for (y = 0; y < dims[0]; ++y) {
      *(int32_t*)PyArray_GETPTR2(pyIy, y, x) = tmpIy[y*dims[1]+x];
      *(int32_t*)PyArray_GETPTR2(pyIx, y, x) = tmpIx[tmpIy[y*dims[1]+x]*dims[1]+x];
    }
  }

  free(tmpM);
  free(tmpIx);
  free(tmpIy);

  return Py_BuildValue("NNN", pydeformed, pyIx, pyIy);
}

PyObject * filter_image (PyArrayObject * pyfeatures, PyArrayObject * pyfilter, float bias, int width, int height) {
    npy_intp * features_dims = PyArray_DIMS(pyfeatures);
    npy_intp * filter_dims = PyArray_DIMS(pyfilter);
    int a, b, l;
    PyArrayObject * pyfiltered = NULL;
    npy_intp * features_stride = PyArray_STRIDES(pyfeatures);
    npy_intp * filtered_stride = NULL;
    npy_intp filtered_dims[2] = {0, 0};
    int tight_width;
    int tight_height;

    if (PyArray_NDIM(pyfeatures) != 3) {
        PyErr_SetString(PyExc_TypeError, "Features must be 3 dimensional.");
        return NULL;
    }

    if (PyArray_NDIM(pyfilter) != 3) {
        PyErr_SetString(PyExc_TypeError, "Filter must be 3 dimensional.");
        return NULL;
    }

    if (PyArray_DESCR(pyfeatures)->type_num != NPY_FLOAT) {
        PyErr_SetString(PyExc_TypeError, "Features must be single precision floating point.");
        return NULL;
    }

    if (PyArray_DESCR(pyfilter)->type_num != NPY_FLOAT) {
        PyErr_SetString(PyExc_TypeError, "Filter must be a single precision floating point.");
        return NULL;
    }

    if (features_dims[2] != 32) {
        PyErr_SetString(PyExc_TypeError, "features' feature dimsionality should be 32.");
        return NULL;
    }

    if (filter_dims[2] != 32) {
        PyErr_SetString(PyExc_TypeError, "filters' feature dimensionality should be 32.");
        return NULL;
    }

    tight_height = features_dims[0]-filter_dims[0]+1;
    tight_width = features_dims[1]-filter_dims[1]+1;

    filtered_dims[0] = height ? height : tight_height;
    filtered_dims[1] = width ? width : tight_width;

    if (filtered_dims[0] < 1 || filtered_dims[1] < 1) {
        PyErr_SetString(PyExc_TypeError, "Input features are too small for filter.");
        return NULL;
    }

    #pragma omp critical
    pyfiltered = (PyArrayObject*)PyArray_SimpleNew((npy_intp)2, filtered_dims, NPY_FLOAT);

    filtered_stride = PyArray_STRIDES(pyfiltered);

    /* zero out array */
    for (a = 0; a < tight_height; ++a) {
        for (b = 0; b < tight_width; ++b) {
            *(float*)PyArray_GETPTR2(pyfiltered, a, b) = -bias;
        }
    }

    /* iterate over filter which should be tiny compared to the image */
    int i;
    int stride_src = features_stride[1]/sizeof(float);
    int stride_dst = filtered_stride[1]/sizeof(float);
    for (i = 0; i < filter_dims[0]; ++i) {
        int j;
        for (j = 0; j < filter_dims[1]; ++j) {
            int k;
            for (k = 0; k < tight_height; ++k) {
                float * out = (float*)PyArray_GETPTR2(pyfiltered, k, 0);
                /* for each layer */
                for (l = 0; l < 32; ++l) {
                    float weight = *(float*)PyArray_GETPTR3(pyfilter, i, j, l);
                    float * in = (float*)PyArray_GETPTR3(pyfeatures, i+k, j, l);
                    cblas_saxpy(tight_width, weight, in, stride_src, out, stride_dst);
                }
            }
        }
    }

    for (a = tight_height; a < filtered_dims[0]; ++a) {
        for (b = 0; b < filtered_dims[1]; ++b) {
            *(float*)PyArray_GETPTR2(pyfiltered, a, b) = -INFINITY;
        }
    }

    for (a = 0; a < tight_height; ++a) {
        for (b = tight_width; b < filtered_dims[1]; ++b) {
            *(float*)PyArray_GETPTR2(pyfiltered, a, b) = -INFINITY;
        }
    }

    return Py_BuildValue("N", pyfiltered);
}

static PyObject * DeformationCost(PyObject * self, PyObject * args)
{
    PyArrayObject * pydata;
    float ax = 0.0f, bx = 0.0f, ay = 0.0f, by = 0.0f;
    int s = 0;
    if (!PyArg_ParseTuple(args, "O!ffffi", &PyArray_Type, &pydata, &ax, &bx, &ay, &by, &s))
        return NULL;
    return deformation_cost(pydata, ax, bx, ay, by, s);
}

static PyObject * FilterImage(PyObject * self, PyObject * args)
{
    PyArrayObject * pyfeatures;
    PyArrayObject * pyfilter;
    float bias = 0.0f;
    int width = 0;
    int height = 0;
    if (!PyArg_ParseTuple(args, "O!O!|fii", &PyArray_Type, &pyfeatures, &PyArray_Type, &pyfilter, &bias, &width, &height))
        return NULL;
    return filter_image(pyfeatures, pyfilter, bias, width, height);
}

static PyObject * FilterImages(PyObject * self, PyObject * args)
{
    PyObject * pyfeatures_list;
    PyObject * pydims_list = NULL;
    PyArrayObject * pyfilter;
    float bias = 0.0f;
    int numfilters;
    int numdims = 0;
    int i;
    PyObject ** objs = NULL;
    PyObject ** results = NULL;
    PyObject * pyresults_list;
    if (!PyArg_ParseTuple(args, "O!O!|fO!", &PyList_Type, &pyfeatures_list, &PyArray_Type, &pyfilter, &bias, &PyList_Type, &pydims_list))
        return NULL;

    numfilters = PyList_Size(pyfeatures_list);

    if (pydims_list) {
        numdims = PyList_Size(pydims_list);
    }

    if (numdims && numdims != numfilters) {
        PyErr_SetString(PyExc_TypeError, "If pad dims are specified, then it must be the same length as the features list.");
        return NULL;
    }

    objs = (PyObject**)calloc(numfilters, sizeof(PyObject*));
    int* widths = (int*)calloc(numfilters, sizeof(int));
    int* heights = (int*)calloc(numfilters, sizeof(int));
    results = (PyObject**)calloc(numfilters, sizeof(PyObject*));

    for (i = 0; i < numfilters; ++i) {
        objs[i] = PyList_GetItem(pyfeatures_list, i);
        if (!PyArray_Check(objs[i])) {
            free(objs);
            free(widths);
            free(heights);
            free(results);
            PyErr_SetString(PyExc_TypeError, "Must contain a list of numpy arrays.");
            return NULL;
        }

        if (pydims_list) {
            PyObject * dims = PyList_GetItem(pydims_list, i);
            if (!PyTuple_Check(dims) || 2 != PyTuple_Size(dims)) {
                free(objs);
                free(widths);
                free(heights);
                free(results);
                PyErr_SetString(PyExc_TypeError, "Must contain a list of tuples.");
                return NULL;
            }

            heights[i] = PyInt_AsLong(PyTuple_GetItem(dims, 0));
            widths[i] = PyInt_AsLong(PyTuple_GetItem(dims, 1));
        } else {
            widths[i] = 0;
            heights[i] = 0;
        }
    }

    kmp_set_blocktime(0);
    #pragma omp parallel for schedule(dynamic)
    for (i = 0; i < numfilters; ++i) {
        PyArrayObject * pyfeatures = (PyArrayObject*)objs[i];
        results[i] = filter_image(pyfeatures, pyfilter, bias, widths[i], heights[i]);
    }

    free(objs);
    free(widths);
    free(heights);

    pyresults_list = PyList_New(numfilters);

    for (i = 0; i < numfilters; ++i) {
        PyList_SetItem(pyresults_list, i, results[i]);
    }

    free(results);

    return Py_BuildValue("N", pyresults_list);
}

#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
    PyModuleDef_HEAD_INIT,
    "_detection",
    "Native convolution detection routine.",
    -1,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL
};
#endif

#if PY_MAJOR_VERSION < 3
static PyMethodDef _detection_methods[] = {
    {"FilterImage", FilterImage, METH_VARARGS, "Compute a 2D cross correlation between a filter and image features.  Optionally add bias term."},
    {"FilterImages", FilterImages, METH_VARARGS, "Compute a 2D cross correlation between a filter and several image features in parallel.  Optionally add bias term."},
    {"DeformationCost", DeformationCost, METH_VARARGS, "Compute a fast bounded distance transform for the deformation cost."},
    {NULL}
};
#endif

#if PY_MAJOR_VERSION >= 3
PyMODINIT_FUNC
PyInit__detection(void)
#else
PyMODINIT_FUNC
init_detection(void)
#endif
{
    import_array();

#if PY_MAJOR_VERSION >= 3
    PyObject *m = PyModule_Create(&moduledef);
#else
    Py_InitModule3("_detection", _detection_methods, "Native convolution detection routine.");
#endif

#if PY_MAJOR_VERSION >= 3
    return m;
#endif
}


#ifndef _MU_NSQ_
#define _MU_NSQ_

#include <vector>
#include <iostream>
#include <nuSQuIDS/nuSQuIDS.h>
#include <gsl/gsl_deriv.h>
#include "exCross.h"

namespace musquids {
using nusquids::nuSQUIDS;
using nusquids::marray;
/// \brief This class implements muon energy loss into nuSQuIDS.
class muSQUIDS: public nuSQUIDS {
  private:
    std::vector<double> inv_lambda;
    marray<double,2> dScalardE;
    /// The following variables are to evaluate the derivative of the scalar flux
    std::shared_ptr<gsl_spline> scalar_spline;
    std::shared_ptr<gsl_interp_accel> scalar_spline_acc;
    std::vector<double> tmp_scalar_state;
  protected:
    /// \brief Here we will calculate the muon flux derivative
    void AddToPreDerive(double x){
      for(size_t si=0; si<dScalardE.extent(0); si++){
        for(size_t ei; ei<dScalardE.extent(1); ei++){
          tmp_scalar_state[ei] = state[ei].scalar[si];
        }
        gsl_spline_init(scalar_spline.get(),E_range.get_data(),tmp_scalar_state.data(),ne);
        gsl_interp_accel_reset(scalar_spline_acc.get());
        for(size_t ei; ei<dScalardE.extent(1); ei++){
          dScalardE[si][ei] = gsl_spline_eval_deriv(scalar_spline.get(),E_range[ei],scalar_spline_acc.get());
        }
      }
    }
  protected:
    double EnergyLoss(double Emuon) const {
      // From T. Gaisser Cosmic Ray book
      // From http://pdg.lbl.gov/2015/AtomicNuclearProperties/
      // Good from Emuon>10Gev
      double RadDensH=4.405e5; // gr/cm^2
      // current_density in gr/cm^3
      // coeffcients in MeV/cm^2
      return (((-1.9-0.08*log(Emuon/params.muon_mass))-(Emuon/params.MeV)/RadDensH)*current_density)*(params.MeV/params.cm);
    }
    double Fmue(double Emuon, double Enu) const {
      // Fits to muon decay spectrum including spin
      double z = Enu/Emuon;
      return (1.79779466e-02*pow(z,4)+1.20239959e+01*pow(z,3.)-2.38837016e+01*z*z+1.17861335e+01*z+5.85725324e-02)/Emuon;
    }
    double Fmumu(double Emuon, double Enu) const {
      // Fits to muon decay spectrum including spin
      double z = Enu/Emuon;
      return (-0.24794224*pow(z,4.)+4.51300659*pow(z,3.)-6.2556965*z*z-0.03647084*z+2.02480429)/Emuon;
    }
    double lambda(double Emuon) const {
      return Emuon*params.muon_lifetime/params.muon_mass;
    }
  protected:
    // These scalar functions will manage the muon decay and energy loss
    double GammaScalar(unsigned int ei,unsigned int index_scalar) const {
      double muon_decay_term=inv_lambda[ei];
      return nuSQUIDS::GammaScalar(ei,index_scalar) + muon_decay_term;
    }
    double InteractionsScalar(unsigned int ei,unsigned int index_scalar) const {
      double muon_energy_loss_terms=EnergyLoss(E_range[ei])*dScalardE[index_scalar][ei];
      return nuSQUIDS::InteractionsScalar(ei,index_scalar) + muon_energy_loss_terms;
    }
    // This rho function will add the neutrinos from muon decay
    squids::SU_vector InteractionsRho(unsigned int ei,unsigned int index_rho) const {
      squids::SU_vector from_muon_decay_terms(nsun);
      double muon_decay_to_muon_integral = 0.;
      double muon_decay_to_e_integral = 0.;
      unsigned int other_index_rho = (index_rho == 0) ? 1 : 0;
      for(unsigned int em = ei+1; em < ne; em++){ // loop in the tau neutrino energies
        muon_decay_to_muon_integral += state[em].scalar[index_rho]*Fmumu(E_range[em],E_range[ei])*inv_lambda[em]*delE[em-1];
        muon_decay_to_e_integral += state[em].scalar[other_index_rho]*Fmue(E_range[em],E_range[ei])*inv_lambda[em]*delE[em-1];
      }
      from_muon_decay_terms += evol_b1_proj[index_rho][1][ei]*muon_decay_to_muon_integral;
      from_muon_decay_terms += evol_b1_proj[index_rho][0][ei]*muon_decay_to_e_integral;
      return nuSQUIDS::InteractionsRho(ei,index_rho) + from_muon_decay_terms;
    }
  public:
    muSQUIDS(){}
    muSQUIDS(marray<double,1> E_range,
             int numneu=3, nusquids::NeutrinoType NT=nusquids::both,bool iinteraction=true):
      nuSQUIDS(E_range,numneu,NT,iinteraction,std::make_shared<nusquids::NeutrinoDISCrossSectionsFromTablesExtended>()),
      scalar_spline(gsl_spline_alloc(gsl_interp_cspline,E_range.size()),[](gsl_spline* t){ gsl_spline_free(t);}),
      scalar_spline_acc(gsl_interp_accel_alloc(),[](gsl_interp_accel* t){ gsl_interp_accel_free(t);})
    {
      // resetting squids nodes to the right scalar size
      ini(ne,numneu,nrhos,2,Get_t());
      // initializing the muon decay lenght
      inv_lambda.resize(ne);
      for(unsigned int ei=0; ei<ne; ei++)
        inv_lambda[ei] = 1./lambda(E_range[ei]);
      // initializing the scalar derivative matrix
      dScalardE.resize(std::vector<size_t>{nscalars,ne});
      std::fill(dScalardE.begin(),dScalardE.end(),0);
      // initializing the scalar temporary state
      tmp_scalar_state.resize(ne);
      std::fill(tmp_scalar_state.begin(),tmp_scalar_state.end(),0);
    }

    void Set_initial_state(const marray<double,2>& muon_flux,const marray<double,3>& neutrino_state,nusquids::Basis basis)
    {
      nuSQUIDS::Set_initial_state(neutrino_state,basis);

      for(unsigned int ie = 0; ie < ne; ie++){
        for(unsigned int ir = 0; ir < nscalars; ir++){
          state[ie].scalar[ir] = muon_flux[ie][ir];
        }
      }
    }

    double GetMuonFlux(unsigned int ie, unsigned int irho){
      return state[ie].scalar[irho];
    }
};

} // close musquids namespace

#endif


#include <viaio/Vlib.h>
#include <viaio/VImage.h>

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <gsl/gsl_matrix.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_cdf.h>
#include <gsl/gsl_errno.h>
#include <gsl/gsl_math.h>
#include "gsl_utils.h"

#define ROUND(x) (int)(((x) >= 0) ? ((x) + 0.5) : ((x) - 0.5))

void dfs(VFloat** Dfs,gsl_matrix_float* pinvX,gsl_matrix_float* X,gsl_matrix_float* con,int numlags)
{
  int numcon = 1;
  int n = X->size2;
  /* these vals represent the matlab interval 2:n */
  int k1 = 1;
  int k2 = n-1;
  int i;

  /* X^T */
  gsl_matrix_float* transX = gsl_matrix_float_alloc(X->size2, X->size1);
  gsl_matrix_float_transpose_memcpy(transX,X);

  gsl_vector_float* CorX2 = gsl_vector_float_alloc(numcon);
  gsl_vector_float_set_zero(CorX2);

  /* cpinvX=contrast^T*pinvX */
  gsl_matrix_float* cpinvX = fmat_x_mat(con, pinvX, NULL);

  /* CovX0=cpinvX*cpinvX' */
  gsl_matrix_float* CovX0 = fmat_x_matT(cpinvX,cpinvX,NULL);

  if(numlags == 1) {
    /* CovX1=cpinvX(:,k1)*cpinvX(:,k1-1)' */
    gsl_matrix_float_view sub1 =
      gsl_matrix_float_submatrix(cpinvX,0,k1,cpinvX->size1,k2);
    gsl_matrix_float_view sub2 =
      gsl_matrix_float_submatrix(cpinvX,0,k1-1,cpinvX->size1,k2);
    gsl_matrix_float* CovX1 = fmat_x_matT(&sub1.matrix, &sub2.matrix, NULL);

    gsl_vector_float_view CovX1diag = gsl_matrix_float_diagonal(CovX1);
    gsl_vector_float_view CovX0diag = gsl_matrix_float_diagonal(CovX0);

    gsl_vector_float_div (&CovX1diag.vector, &CovX0diag.vector);
    gsl_vector_float_mul (&CovX1diag.vector, &CovX1diag.vector);

    gsl_vector_float_memcpy (CorX2, &CovX1diag.vector);
  }
  else {
    int lag;
    gsl_matrix_float_view sub1, sub2;
    gsl_matrix_float* CovX1 = gsl_matrix_float_alloc(cpinvX->size1,cpinvX->size1);
    for(lag=0;lag<numlags;lag++){
      sub1 = gsl_matrix_float_submatrix(cpinvX,0,0,cpinvX->size1,k2-lag);
      sub2 = gsl_matrix_float_submatrix(cpinvX,0,lag+1,cpinvX->size1,k2-lag);
      fmat_x_matT(&sub1.matrix, &sub2.matrix,CovX1);

      gsl_vector_float_view CovX1diag = gsl_matrix_float_diagonal(CovX1);
      gsl_vector_float_view CovX0diag = gsl_matrix_float_diagonal(CovX0);

      gsl_vector_float_div (&CovX1diag.vector, &CovX0diag.vector);
      gsl_vector_float_mul (&CovX1diag.vector, &CovX1diag.vector);

      gsl_vector_float_add(CorX2,&CovX1diag.vector);
    }
    gsl_matrix_float_free(CovX1);
  }

  float dfresid  = n-rank(transX);
  float dfmin = dfresid;

  for (i=0;i<numcon;i++) {
    (*Dfs)[i] = (VFloat)ROUND(dfresid / (1 + 2*gsl_vector_float_get(CorX2,i)));
    if ((*Dfs)[i]<dfmin) dfmin = (*Dfs)[i];
  }
  (*Dfs)[numcon] = dfresid;

  /*
    if(dfmin < 100)
    fprintf(stderr, "  warning, dfmin= %f\n",dfmin);
  */

  gsl_matrix_float_free(cpinvX);
  gsl_matrix_float_free(transX);
  gsl_matrix_float_free(CovX0);
  gsl_vector_float_free(CorX2);
}


/* eigen/nonsymm.c
 *
 * Copyright (C) 2006 Patrick Alken
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#include <config.h>
#include <stdlib.h>
#include <math.h>
#include <gsl/gsl_eigen.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_blas.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_vector_complex.h>
#include <gsl/gsl_matrix.h>

/*
 * This module computes the eigenvalues of a real nonsymmetric
 * matrix, using the double shift Francis method.
 *
 * See the references in francis.c.
 *
 * This module gets the matrix ready by balancing it and
 * reducing it to Hessenberg form before passing it to the
 * francis module.
 */

/*
gsl_eigen_nonsymm_alloc()

Allocate a workspace for solving the nonsymmetric eigenvalue problem.
The size of this workspace is O(2n)

Inputs: n - size of matrix

Return: pointer to workspace
*/

gsl_eigen_nonsymm_workspace *
gsl_eigen_nonsymm_alloc(const size_t n)
{
  gsl_eigen_nonsymm_workspace *w;

  if (n == 0)
    {
      GSL_ERROR_NULL ("matrix dimension must be positive integer",
                      GSL_EINVAL);
    }

  w = (gsl_eigen_nonsymm_workspace *)
      malloc (sizeof (gsl_eigen_nonsymm_workspace));

  if (w == 0)
    {
      GSL_ERROR_NULL ("failed to allocate space for workspace", GSL_ENOMEM);
    }

  w->size = n;
  w->Z = NULL;
  w->do_balance = 0;

  w->diag = gsl_vector_alloc(n);

  if (w->diag == 0)
    {
      GSL_ERROR_NULL ("failed to allocate space for balancing vector", GSL_ENOMEM);
    }

  w->tau = gsl_vector_alloc(n);

  if (w->tau == 0)
    {
      GSL_ERROR_NULL ("failed to allocate space for hessenberg coefficients", GSL_ENOMEM);
    }

  w->francis_workspace_p = gsl_eigen_francis_alloc();

  if (w->francis_workspace_p == 0)
    {
      GSL_ERROR_NULL ("failed to allocate space for francis workspace", GSL_ENOMEM);
    }

  return (w);
} /* gsl_eigen_nonsymm_alloc() */

/*
gsl_eigen_nonsymm_free()
  Free workspace w
*/

void
gsl_eigen_nonsymm_free (gsl_eigen_nonsymm_workspace * w)
{
  gsl_vector_free(w->tau);

  gsl_vector_free(w->diag);

  gsl_eigen_francis_free(w->francis_workspace_p);

  free(w);
} /* gsl_eigen_nonsymm_free() */

/*
gsl_eigen_nonsymm_params()
  Set some parameters which define how we solve the eigenvalue
problem.

Inputs: compute_t - 1 if we want to compute T, 0 if not
        balance   - 1 if we want to balance the matrix, 0 if not
        w         - nonsymm workspace
*/

void
gsl_eigen_nonsymm_params (const int compute_t, const int balance,
                          gsl_eigen_nonsymm_workspace *w)
{
  gsl_eigen_francis_T(compute_t, w->francis_workspace_p);
  w->do_balance = balance;
} /* gsl_eigen_nonsymm_params() */

/*
gsl_eigen_nonsymm()

Solve the nonsymmetric eigenvalue problem

A x = \lambda x

for the eigenvalues \lambda using the Francis method.

Here we compute the real Schur form

T = Z^t A Z

with the diagonal blocks of T giving us the eigenvalues.
Z is a matrix of Schur vectors which is not computed by
this algorithm. See gsl_eigen_nonsymm_Z().

Inputs: A    - general real matrix
        eval - where to store eigenvalues
        w    - workspace

Return: success or error

Notes: If T is computed, it is stored in A on output. Otherwise
       the diagonal of A contains the 1-by-1 and 2-by-2 eigenvalue
       blocks.
*/

int
gsl_eigen_nonsymm (gsl_matrix * A, gsl_vector_complex * eval,
                   gsl_eigen_nonsymm_workspace * w)
{
  const size_t N = A->size1;

  /* check matrix and vector sizes */

  if (N != A->size2)
    {
      GSL_ERROR ("matrix must be square to compute eigenvalues", GSL_ENOTSQR);
    }
  else if (eval->size != N)
    {
      GSL_ERROR ("eigenvalue vector must match matrix size", GSL_EBADLEN);
    }
  else
    {
      int s;

      if (w->do_balance)
        {
          /* balance the matrix */
          gsl_linalg_balance_matrix(A, w->diag);
        }

      /* compute the Hessenberg reduction of A */
      gsl_linalg_hessenberg(A, w->tau);

      if (w->Z)
        {
          /*
           * initialize the matrix Z to U, which is the matrix used
           * to construct the Hessenberg reduction.
           */

          /* compute U and store it in Z */
          gsl_linalg_hessenberg_unpack(A, w->tau, w->Z);

          /* find the eigenvalues and Schur vectors */
          s = gsl_eigen_francis_Z(A, eval, w->Z, w->francis_workspace_p);

          if (w->do_balance)
            {
              /*
               * The Schur vectors in Z are the vectors for the balanced
               * matrix. We now must undo the balancing to get the
               * vectors for the original matrix A.
               */
              gsl_linalg_balance_accum(w->Z, w->diag);
            }
        }
      else
        {
          /* find the eigenvalues only */
          s = gsl_eigen_francis(A, eval, w->francis_workspace_p);
        }

      w->n_evals = w->francis_workspace_p->n_evals;

      return s;
    }
} /* gsl_eigen_nonsymm() */

/*
gsl_eigen_nonsymm_Z()

Solve the nonsymmetric eigenvalue problem

A x = \lambda x

for the eigenvalues \lambda.

Here we compute the real Schur form

T = Z^t A Z

with the diagonal blocks of T giving us the eigenvalues.
Z is the matrix of Schur vectors.

Inputs: A    - general real matrix
        eval - where to store eigenvalues
        Z    - where to store Schur vectors
        w    - workspace

Return: success or error

Notes: If T is computed, it is stored in A on output. Otherwise
       the diagonal of A contains the 1-by-1 and 2-by-2 eigenvalue
       blocks.
*/

int
gsl_eigen_nonsymm_Z (gsl_matrix * A, gsl_vector_complex * eval,
                     gsl_matrix * Z, gsl_eigen_nonsymm_workspace * w)
{
  /* check matrix and vector sizes */

  if (A->size1 != A->size2)
    {
      GSL_ERROR ("matrix must be square to compute eigenvalues", GSL_ENOTSQR);
    }
  else if (eval->size != A->size1)
    {
      GSL_ERROR ("eigenvalue vector must match matrix size", GSL_EBADLEN);
    }
  else if ((Z->size1 != Z->size2) || (Z->size1 != A->size1))
    {
      GSL_ERROR ("Z matrix has wrong dimensions", GSL_EBADLEN);
    }
  else
    {
      int s;

      w->Z = Z;

      s = gsl_eigen_nonsymm(A, eval, w);

      w->Z = NULL;

      return s;
    }
} /* gsl_eigen_nonsymm_Z() */


/*
 * halo.h
 *
 *  Created on: Oct 23, 2012
 *      Author: cgiocoli
 *
 *      This class implement the non-linear power spectrum using the Halo Model
 */

#ifndef POWERCDMHM_H_
#define POWERCDMHM_H_
#include <math.h>
#include <cosmo.h>
#include <halo.h>
#include <utilities.h>

#ifdef ENABLE_GSL

#include <gsl/gsl_errno.h>
#include <gsl/gsl_integration.h>
#include <gsl/gsl_sf.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_spline.h>
#include <gsl/gsl_rng.h>

/**
 * \brief Class for calculating the non-linear power spectrum using ****** Halo Model.
 *  To use this class you must enable GSL.
 */
class POWERCDMHM{
  public:
  POWERCDMHM(COSMOLOGY *co, double redshift, double minHalomass, double sigma=0.25,int cmRelation=0,double slope=-0.1);

  double nonlinpowerCDMHM(double k);
  double nonlinpowerCDMHM1Halo(double k);
  double nonlinpowerCDMHM2Halo(double k);
  double nonlinKAPPApowerCDMHM(double l, double zs);
  double nonlinKAPPApowerCDMHM1Halo(double l, double zs);
  double nonlinKAPPApowerCDMHM2Halo(double l, double zs);
  double linKAPPApowerCDMHM(double l, double zs);
  double nonlinfitKAPPApowerCDMHM(double l, double zs);
  virtual ~POWERCDMHM ();

  protected:
  gsl_function intPk1,intPk2;
  double Pklin,Pk1,Pk2;
  double Pk20;
  double weight (double z1, double z2);
  double weight (double z0);

  struct weightfnc{
    std::vector<double> ai;
    std::vector<double> wi;
  };
  weightfnc wgf; // weight function
  void Initweight(double zs);
  private:
  float *xf,*wf;
};
#endif

#endif /* POWERCDMHM_H_ */