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

#include <gsl/gsl_integration.h>
#include <gsl/gsl_interp.h>
#include <gsl/gsl_spline.h>
#include <gsl/gsl_errno.h>

#include <class.h> /* from extern/ */

#include "ccl.h"
#include "ccl_params.h"
#include "ccl_emu17.h"
#include "ccl_emu17_params.h"

/*------ ROUTINE: ccl_cosmology_compute_power_class -----
INPUT: ccl_cosmology * cosmo
*/
static void ccl_free_class_structs(ccl_cosmology *cosmo,
				   struct background *ba,
				   struct thermo *th,
				   struct perturbs *pt,
				   struct transfers *tr,
				   struct primordial *pm,
				   struct spectra *sp,
				   struct nonlinear *nl,
				   struct lensing *le,
				   int *init_arr,
				   int * status)
{
  int i_init=6;
  if(init_arr[i_init--]) {
    if (spectra_free(sp) == _FAILURE_) {
      *status = CCL_ERROR_CLASS;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_free_class_structs(): Error freeing CLASS spectra:%s\n", sp->error_message);
      return;
    }
  }

  if(init_arr[i_init--]) {
    if (transfer_free(tr) == _FAILURE_) {
      *status = CCL_ERROR_CLASS;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_free_class_structs(): Error freeing CLASS transfer:%s\n", tr->error_message);
      return;
    }
  }

  if(init_arr[i_init--]) {
    if (nonlinear_free(nl) == _FAILURE_) {
      *status = CCL_ERROR_CLASS;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_free_class_structs(): Error freeing CLASS nonlinear:%s\n", nl->error_message);
      return;
    }
  }

  if(init_arr[i_init--]) {
    if (primordial_free(pm) == _FAILURE_) {
      *status = CCL_ERROR_CLASS;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_free_class_structs(): Error freeing CLASS pm:%s\n", pm->error_message);
      return;
    }
  }

  if(init_arr[i_init--]) {
    if (perturb_free(pt) == _FAILURE_) {
      *status = CCL_ERROR_CLASS;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_free_class_structs(): Error freeing CLASS pt:%s\n", pt->error_message);
      return;
    }
  }

  if(init_arr[i_init--]) {
    if (thermodynamics_free(th) == _FAILURE_) {
      *status = CCL_ERROR_CLASS;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_free_class_structs(): Error freeing CLASS thermo:%s\n", th->error_message);
      return;
    }
  }

  if(init_arr[i_init--]) {
    if (background_free(ba) == _FAILURE_) {
      *status = CCL_ERROR_CLASS;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_free_class_structs(): Error freeing CLASS bg:%s\n", ba->error_message);
      return;
    }
  }

  return;
}

static void ccl_class_preinit(struct background *ba,
			      struct thermo *th,
			      struct perturbs *pt,
			      struct transfers *tr,
			      struct primordial *pm,
			      struct spectra *sp,
			      struct nonlinear *nl,
			      struct lensing *le)
{
  //pre-initialize all fields that are freed by *_free() routine
  //prevents crashes if *_init()failed and did not initialize all tables freed by *_free()

  //init for background_free
  ba->tau_table = NULL;
  ba->z_table = NULL;
  ba->d2tau_dz2_table = NULL;
  ba->background_table = NULL;
  ba->d2background_dtau2_table = NULL;

  //init for thermodynamics_free
  th->z_table = NULL;
  th->thermodynamics_table = NULL;
  th->d2thermodynamics_dz2_table = NULL;

  //init for perturb_free
  pt->tau_sampling = NULL;
  pt->tp_size = NULL;
  pt->ic_size = NULL;
  pt->k = NULL;
  pt->k_size_cmb = NULL;
  pt->k_size_cl = NULL;
  pt->k_size = NULL;
  pt->sources = NULL;

  //init for primordial_free
  pm->amplitude = NULL;
  pm->tilt = NULL;
  pm->running = NULL;
  pm->lnpk = NULL;
  pm->ddlnpk = NULL;
  pm->is_non_zero = NULL;
  pm->ic_size = NULL;
  pm->ic_ic_size = NULL;
  pm->lnk = NULL;

  //init for nonlinear_free
  nl->k = NULL;
  nl->tau = NULL;
  nl->nl_corr_density = NULL;
  nl->k_nl = NULL;

  //init for transfer_free
  tr->tt_size = NULL;
  tr->l_size_tt = NULL;
  tr->l_size = NULL;
  tr->l = NULL;
  tr->q = NULL;
  tr->k = NULL;
  tr->transfer = NULL;

  //init for spectra_free
  //spectra_free checks all other data fields before freeing
  sp->is_non_zero = NULL;
  sp->ic_size = NULL;
  sp->ic_ic_size = NULL;
}

static void ccl_run_class(ccl_cosmology *cosmo,
			  struct file_content *fc,
			  struct precision* pr,
			  struct background* ba,
			  struct thermo* th,
			  struct perturbs* pt,
			  struct transfers* tr,
			  struct primordial* pm,
			  struct spectra* sp,
			  struct nonlinear* nl,
			  struct lensing* le,
			  struct output* op,
			  int *init_arr,
			  int * status)
{
  ErrorMsg errmsg;            // for error messages
  int i_init=0;
  ccl_class_preinit(ba,th,pt,tr,pm,sp,nl,le);

  if(input_init(fc,pr,ba,th,pt,tr,pm,sp,nl,le,op,errmsg) == _FAILURE_) {
    *status = CCL_ERROR_CLASS;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_class(): Error running CLASS input:%s\n", errmsg);
    return;
  }
  if (background_init(pr,ba) == _FAILURE_) {
    *status = CCL_ERROR_CLASS;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_class(): Error running CLASS background:%s\n", ba->error_message);
    return;
  }
  init_arr[i_init++]=1;
  if (thermodynamics_init(pr,ba,th) == _FAILURE_) {
    *status = CCL_ERROR_CLASS;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_class(): Error running CLASS thermodynamics:%s\n", th->error_message);
    return;
  }
  init_arr[i_init++]=1;
  if (perturb_init(pr,ba,th,pt) == _FAILURE_) {
    *status = CCL_ERROR_CLASS;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_class(): Error running CLASS pertubations:%s\n", pt->error_message);
    return;
  }
  init_arr[i_init++]=1;
  if (primordial_init(pr,pt,pm) == _FAILURE_) {
    *status = CCL_ERROR_CLASS;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_class(): Error running CLASS primordial:%s\n", pm->error_message);
    return;
  }
  init_arr[i_init++]=1;
  if (nonlinear_init(pr,ba,th,pt,pm,nl) == _FAILURE_) {
    *status = CCL_ERROR_CLASS;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_class(): Error running CLASS nonlinear:%s\n", nl->error_message);
    return;
  }
  init_arr[i_init++]=1;
  if (transfer_init(pr,ba,th,pt,nl,tr) == _FAILURE_) {
    *status = CCL_ERROR_CLASS;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_class(): Error running CLASS transfer:%s\n", tr->error_message);
    return;
  }
  init_arr[i_init++]=1;
  if (spectra_init(pr,ba,pt,pm,nl,tr,sp) == _FAILURE_) {
    *status = CCL_ERROR_CLASS;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_class(): Error running CLASS spectra:%s\n", sp->error_message);
    return;
  }
  init_arr[i_init++]=1;
}

static double ccl_get_class_As(ccl_cosmology *cosmo, struct file_content *fc, int position_As,
			       double sigma8, int * status)
{
  //structures for class test run
  struct precision pr;        // for precision parameters
  struct background ba;       // for cosmological background
  struct thermo th;           // for thermodynamics
  struct perturbs pt;         // for source functions
  struct transfers tr;        // for transfer functions
  struct primordial pm;       // for primordial spectra
  struct spectra sp;          // for output spectra
  struct nonlinear nl;        // for non-linear spectra
  struct lensing le;
  struct output op;

  //temporarily overwrite P_k_max_1/Mpc to speed up sigma8 calculation
  double k_max_old = 0.;
  int position_kmax =2;
  double A_s_guess;
  int init_arr[7]={0,0,0,0,0,0,0};

  if (strcmp(fc->name[position_kmax],"P_k_max_1/Mpc")) {
    k_max_old = strtof(fc->value[position_kmax],NULL);
    sprintf(fc->value[position_kmax],"%.15e",10.);
  }
  A_s_guess = 2.43e-9/0.87659*sigma8;
  sprintf(fc->value[position_As],"%.15e",A_s_guess);

  ccl_run_class(cosmo, fc,&pr,&ba,&th,&pt,&tr,&pm,&sp,&nl,&le,&op,init_arr,status);
  if (cosmo->status != CCL_ERROR_CLASS) A_s_guess*=pow(sigma8/sp.sigma8,2.);
  ccl_free_class_structs(cosmo, &ba,&th,&pt,&tr,&pm,&sp,&nl,&le,init_arr,status);

  if (k_max_old >0) {
    sprintf(fc->value[position_kmax],"%.15e",k_max_old);
  }
  return A_s_guess;
}

static void ccl_fill_class_parameters(ccl_cosmology * cosmo, struct file_content * fc,
				      int parser_length, int * status)

{

  // initialize fc fields
  //silences Valgrind's "Conditional jump or move depends on uninitialised value" warning
  for (int i = 0; i< parser_length; i++){
    strcpy(fc->name[i]," ");
    strcpy(fc->value[i]," ");
  }

  strcpy(fc->name[0],"output");
  strcpy(fc->value[0],"mPk");

  strcpy(fc->name[1],"non linear");
  if (cosmo->config.matter_power_spectrum_method == ccl_halofit)
    strcpy(fc->value[1],"Halofit");
  else
    strcpy(fc->value[1],"none");

  strcpy(fc->name[2],"P_k_max_1/Mpc");
  sprintf(fc->value[2],"%.15e",ccl_splines->K_MAX_SPLINE); //in units of 1/Mpc, corroborated with ccl_constants.h

  strcpy(fc->name[3],"z_max_pk");
  sprintf(fc->value[3],"%.15e",1./ccl_splines->A_SPLINE_MINLOG_PK-1.);

  strcpy(fc->name[4],"modes");
  strcpy(fc->value[4],"s");

  strcpy(fc->name[5],"lensing");
  strcpy(fc->value[5],"no");

  // now, copy over cosmology parameters
  strcpy(fc->name[6],"h");
  sprintf(fc->value[6],"%.15e",cosmo->params.h);

  strcpy(fc->name[7],"Omega_cdm");
  sprintf(fc->value[7],"%.15e",cosmo->params.Omega_c);

  strcpy(fc->name[8],"Omega_b");
  sprintf(fc->value[8],"%.15e",cosmo->params.Omega_b);

  strcpy(fc->name[9],"Omega_k");
  sprintf(fc->value[9],"%.15e",cosmo->params.Omega_k);

  strcpy(fc->name[10],"n_s");
  sprintf(fc->value[10],"%.15e",cosmo->params.n_s);

  //cosmological constant?
  // set Omega_Lambda = 0.0 if w !=-1
  if ((cosmo->params.w0 !=-1.0) || (cosmo->params.wa !=0)) {
    strcpy(fc->name[11],"Omega_Lambda");
    sprintf(fc->value[11],"%.15e",0.0);

    strcpy(fc->name[12],"w0_fld");
    sprintf(fc->value[12],"%.15e",cosmo->params.w0);

    strcpy(fc->name[13],"wa_fld");
    sprintf(fc->value[13],"%.15e",cosmo->params.wa);
  }
  //neutrino parameters
  //massless neutrinos
  if (cosmo->params.N_nu_rel > 1.e-4) {
    strcpy(fc->name[14],"N_ur");
    sprintf(fc->value[14],"%.15e",cosmo->params.N_nu_rel);
  }
  else {
    strcpy(fc->name[14],"N_ur");
    sprintf(fc->value[14],"%.15e", 0.);
  }
  if (cosmo->params.N_nu_mass > 0) {
    strcpy(fc->name[15],"N_ncdm");
    sprintf(fc->value[15],"%d",cosmo->params.N_nu_mass);
    strcpy(fc->name[16],"m_ncdm");
    sprintf(fc->value[16],"%f", (cosmo->params.mnu)[0]);
    if (cosmo->params.N_nu_mass >=1){
		for (int i = 1; i < cosmo->params.N_nu_mass; i++) {
			char tmp[20];
			sprintf(tmp,", %f",(cosmo->params.mnu)[i]);
			strcat(fc->value[16],tmp);
		}
	}

  }

  strcpy(fc->name[17],"T_cmb");
  sprintf(fc->value[17],"%.15e",cosmo->params.T_CMB);

  //normalization comes last, so that all other parameters are filled in for determining A_s if sigma8 is specified
  if (isfinite(cosmo->params.sigma8) && isfinite(cosmo->params.A_s)){
      *status = CCL_ERROR_INCONSISTENT;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: class_parameters(): Error initializing CLASS parameters: both sigma8 and A_s defined\n");
    return;
  }
  if (isfinite(cosmo->params.sigma8)) {
    strcpy(fc->name[parser_length-1],"A_s");
    sprintf(fc->value[parser_length-1],"%.15e",ccl_get_class_As(cosmo,fc,parser_length-1,cosmo->params.sigma8, status));
  }
  else if (isfinite(cosmo->params.A_s)) {
    strcpy(fc->name[parser_length-1],"A_s");
    sprintf(fc->value[parser_length-1],"%.15e",cosmo->params.A_s);
  }
  else {
    *status = CCL_ERROR_INCONSISTENT;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: class_parameters(): Error initializing CLASS pararmeters: neither sigma8 nor A_s defined\n");
    return;
  }

}

static void ccl_cosmology_compute_power_class(ccl_cosmology * cosmo, int * status)
{
  struct precision pr;        // for precision parameters
  struct background ba;       // for cosmological background
  struct thermo th;           // for thermodynamics
  struct perturbs pt;         // for source functions
  struct transfers tr;        // for transfer functions
  struct primordial pm;       // for primordial spectra
  struct spectra sp;          // for output spectra
  struct nonlinear nl;        // for non-linear spectra
  struct lensing le;
  struct output op;
  struct file_content fc;

  ErrorMsg errmsg; // for error messages
  // generate file_content structure
  // CLASS configuration parameters will be passed through this structure,
  // to avoid writing and reading .ini files for every call
  int parser_length = 20;
  int init_arr[7]={0,0,0,0,0,0,0};
  if (parser_init(&fc,parser_length,"none",errmsg) == _FAILURE_) {
    *status = CCL_ERROR_CLASS;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_class(): parser init error:%s\n", errmsg);
    return;
  }

  ccl_fill_class_parameters(cosmo,&fc,parser_length, status);

  if (*status != CCL_ERROR_CLASS)
    ccl_run_class(cosmo, &fc,&pr,&ba,&th,&pt,&tr,&pm,&sp,&nl,&le,&op,init_arr,status);

  if (*status == CCL_ERROR_CLASS) {
    //printed error message while running CLASS
    ccl_free_class_structs(cosmo, &ba,&th,&pt,&tr,&pm,&sp,&nl,&le,init_arr,status);
    return;
  }

  if (parser_free(&fc)== _FAILURE_) {
    *status = CCL_ERROR_CLASS;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_class(): Error freeing CLASS parser\n");
    ccl_free_class_structs(cosmo, &ba,&th,&pt,&tr,&pm,&sp,&nl,&le,init_arr,status);
    return;
  }

  //These are the limits of the splining range
  cosmo->data.k_min_lin=2*exp(sp.ln_k[0]);
  cosmo->data.k_max_lin=ccl_splines->K_MAX_SPLINE;

  //CLASS calculations done - now allocate CCL splines
  double kmin = cosmo->data.k_min_lin;
  double kmax = ccl_splines->K_MAX_SPLINE;
  //Compute nk from number of decades and N_K = # k per decade
  double ndecades = log10(kmax) - log10(kmin);
  int nk = (int)ceil(ndecades*ccl_splines->N_K);
  double amin = ccl_splines->A_SPLINE_MINLOG_PK;
  double amax = ccl_splines->A_SPLINE_MAX;
  int na = ccl_splines->A_SPLINE_NA_PK+ccl_splines->A_SPLINE_NLOG_PK-1;

  // The x array is initially k, but will later
  // be overwritten with log(k)
  double * x = ccl_log_spacing(kmin, kmax, nk);
  double * a = ccl_linlog_spacing(amin, ccl_splines->A_SPLINE_MIN_PK, amax, ccl_splines->A_SPLINE_NLOG_PK, ccl_splines->A_SPLINE_NA_PK);
  double * y2d_lin = malloc(nk * na * sizeof(double));
  double * y2d_nl = malloc(nk * na * sizeof(double));

  //If error, store status, we will free later
  if (a==NULL|| x==NULL || y2d_lin==NULL || y2d_nl==NULL) {
    *status = CCL_ERROR_SPLINE;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_class(): memory allocation error\n");
  }

  //Status flags
  int newstatus=0;
  int pwstatus=0;

  //If not, proceed
  if(!*status){

    // After this loop x will contain log(k)
    // all in Mpc, not Mpc/h units!
    double psout_l,ic;
    for (int i=0; i<nk; i++) {
      for (int j = 0; j < na; j++) {
	//The 2D interpolation routines access the function values y_{k_ia_j} with the following ordering:
	//y_ij = y2d[j*N_k + i]
	//with i = 0,...,N_k-1 and j = 0,...,N_a-1.
	newstatus |= spectra_pk_at_k_and_z(&ba, &pm, &sp,x[i],1./a[j]-1., &psout_l,&ic);
	y2d_lin[j*nk+i] = log(psout_l);
      }
      x[i] = log(x[i]);
    }

    //If error, store status, we will free later
    if(newstatus) {
      *status = CCL_ERROR_CLASS;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_class(): Error computing CLASS power spectrum\n");
    }

  }

  //If no error, proceed
  if(!*status) {

    gsl_spline2d * log_power = gsl_spline2d_alloc(PLIN_SPLINE_TYPE, nk,na);
    pwstatus = gsl_spline2d_init(log_power, x, a, y2d_lin,nk,na);

    //If not, proceed
    if(!pwstatus){
      cosmo->data.p_lin = log_power;
    } else {
      gsl_spline2d_free(log_power);
      *status = CCL_ERROR_SPLINE;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_class(): Error creating log_power spline\n");
    }

  }

  if(*status){ //Linear power spec failed, so we return without proceeding to nonlinear.
    free(x);
    free(a);
    free(y2d_nl);
    free(y2d_lin);
    ccl_free_class_structs(cosmo, &ba,&th,&pt,&tr,&pm,&sp,&nl,&le,init_arr,status);
    return;
  }

  //Non-linear power
  //At the moment KMIN can't be less than CLASS's kmin in the nonlinear case.

  //If error, store status, we will free later
  if (kmin<(exp(sp.ln_k[0]))) {
    *status = CCL_ERROR_CLASS;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_class(): K_MIN is less than CLASS's kmin. Not yet supported for nonlinear P(k).\n");
  }

  //If not, proceed
  if(!*status){

    //These are the limits of the splining range
    cosmo->data.k_min_nl=2*exp(sp.ln_k[0]);
    cosmo->data.k_max_nl=ccl_splines->K_MAX_SPLINE;

    if(cosmo->config.matter_power_spectrum_method==ccl_halofit) {

      double psout_nl;
      for (int i=0; i<nk; i++) {
	for (int j = 0; j < na; j++) {
	  newstatus |= spectra_pk_nl_at_k_and_z(&ba, &pm, &sp,exp(x[i]),1./a[j]-1.,&psout_nl);
	  y2d_nl[j*nk+i] = log(psout_nl);
	}
      }
    }

    if(newstatus){
      *status = CCL_ERROR_CLASS;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_class(): Error computing CLASS power spectrum\n");
    }

  }

  if(!*status){

    gsl_spline2d * log_power_nl = gsl_spline2d_alloc(PNL_SPLINE_TYPE, nk,na);
    pwstatus = gsl_spline2d_init(log_power_nl, x, a, y2d_nl,nk,na);

    if(!pwstatus){
      cosmo->data.p_nl = log_power_nl;
    } else {
      gsl_spline2d_free(log_power_nl);
      *status = CCL_ERROR_SPLINE;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_class(): Error creating log_power_nl spline\n");
    }

  }

  ccl_free_class_structs(cosmo, &ba,&th,&pt,&tr,&pm,&sp,&nl,&le,init_arr,status);
  free(x);
  free(a);
  free(y2d_nl);
  free(y2d_lin);

  return;

}

/* BCM correction
   See Schneider & Teyssier (2015) for details of the model.
   The equations inside this function correspond to those in that
   work for:
   gf -> G(k)
   scomp -> S(k)
*/
double ccl_bcm_model_fka(ccl_cosmology * cosmo, double k, double a, int *status){

  double fka;
  double b0;
  double bfunc, bfunc4;
  double kg;
  double gf,scomp;
  double kh;
  double z;

  z=1./a-1.;
  kh = k/cosmo->params.h; //convert to h/Mpc
  b0 = 0.105*cosmo->params.bcm_log10Mc-1.27; //Eq. 4.4
  bfunc = b0/(1.+pow(z/2.3,2.5)); //Eq. 4.3
  bfunc4 = (1-bfunc)*(1-bfunc)*(1-bfunc)*(1-bfunc); //B^4(z)
  kg = 0.7*bfunc4*pow(cosmo->params.bcm_etab,-1.6); //Eq. 4.3
  gf = bfunc/(1+pow(kh/kg,3.))+1.-bfunc; //Eq. 4.2, k in h/Mpc
  scomp = 1+(kh/cosmo->params.bcm_ks)*(kh/cosmo->params.bcm_ks); //Eq 4.5, k in h/Mpc
  fka = gf*scomp;
  return fka;
}

void ccl_cosmology_write_power_class_z(char *filename, ccl_cosmology * cosmo, double z, int * status)
{
  struct precision pr;        // for precision parameters
  struct background ba;       // for cosmological background
  struct thermo th;           // for thermodynamics
  struct perturbs pt;         // for source functions
  struct transfers tr;        // for transfer functions
  struct primordial pm;       // for primordial spectra
  struct spectra sp;          // for output spectra
  struct nonlinear nl;        // for non-linear spectra
  struct lensing le;
  struct output op;
  struct file_content fc;

  ErrorMsg errmsg; // for error messages
  // generate file_content structure
  // CLASS configuration parameters will be passed through this structure,
  // to avoid writing and reading .ini files for every call
  int parser_length = 20;
  int init_arr[7]={0,0,0,0,0,0,0};
  if (parser_init(&fc,parser_length,"none",errmsg) == _FAILURE_) {
    *status = CCL_ERROR_CLASS;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: write_power_class_z(): parser init error:%s\n", errmsg);
    return;
  }

  ccl_fill_class_parameters(cosmo,&fc,parser_length, status);

  if (*status != CCL_ERROR_CLASS)
    ccl_run_class(cosmo, &fc,&pr,&ba,&th,&pt,&tr,&pm,&sp,&nl,&le,&op,init_arr,status);

  if (*status == CCL_ERROR_CLASS) {
    //printed error message while running CLASS
    ccl_free_class_structs(cosmo, &ba,&th,&pt,&tr,&pm,&sp,&nl,&le,init_arr,status);
    return;
  }
  if (parser_free(&fc)== _FAILURE_) {
    *status = CCL_ERROR_CLASS;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: write_power_class_z(): Error freeing CLASS parser\n");
    ccl_free_class_structs(cosmo, &ba,&th,&pt,&tr,&pm,&sp,&nl,&le,init_arr,status);
    return;
  }
  FILE *f;
  f = fopen(filename,"w");
  if (!f){
    *status = CCL_ERROR_CLASS;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: write_power_class_z(): Error opening output file\n");
    ccl_free_class_structs(cosmo, &ba,&th,&pt,&tr,&pm,&sp,&nl,&le,init_arr,status);
    fclose(f);
    return;
  }
  double psout_l,ic;
  int s=0;
  for (int i=0; i<sp.ln_k_size; i++) {
    s |= spectra_pk_at_k_and_z(&ba, &pm, &sp,exp(sp.ln_k[i]),z, &psout_l,&ic);
    fprintf(f,"%e %e\n",exp(sp.ln_k[i]),psout_l);
  }
  fclose(f);
  if(s) {
    *status = CCL_ERROR_CLASS;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: write_power_class_z(): Error writing CLASS power spectrum\n");
  }

  ccl_free_class_structs(cosmo, &ba,&th,&pt,&tr,&pm,&sp,&nl,&le,init_arr,status);
}

typedef struct {
  double rsound;
  double zeq;
  double keq;
  double zdrag;
  double kSilk;
  double rsound_approx;
  double th2p7;
  double alphac;
  double alphab;
  double betac;
  double betab;
  double bnode;
} eh_struct;

static eh_struct *eh_struct_new(ccl_parameters *params)
{
  //////
  // Computes Eisenstein & Hu parameters for
  // P_k and r_sound
  // see astro-ph/9709112 for the relevant equations
  double OMh2,OBh2;
  double th2p7;
  eh_struct *eh=malloc(sizeof(eh_struct));
  if(eh==NULL)
    return NULL;

  OMh2=params->Omega_m*params->h*params->h; //Cosmo params scaled by h^2
  OBh2=params->Omega_b*params->h*params->h;
  th2p7=params->T_CMB/2.7; //This is Theta_{2.7} in E&Hu notation
  eh->th2p7=th2p7; //This is Theta_{2.7} in E&Hu notation
  eh->zeq=2.5E4*OMh2/pow(th2p7,4);// Eq 2
  eh->keq=0.0746*OMh2/(params->h*th2p7*th2p7);//Eq. 3

  //These group corresponds to Eq. 4
  double b1,b2;
  b1=0.313*pow(OMh2,-0.419)*(1+0.607*pow(OMh2,0.674));
  b2=0.238*pow(OMh2,0.223);
  eh->zdrag=1291*pow(OMh2,0.251)*(1+b1*pow(OBh2,b2))/(1+0.659*pow(OMh2,0.828));

  //These are the baryon-to-photon ratios
  //at equality (Req) and drag (Rd) epochs
  //Eq. 5
  double Req,Rd;
  Req=31.5*OBh2*1000./(eh->zeq*pow(th2p7,4));
  Rd=31.5*OBh2*1000./((1+eh->zdrag)*pow(th2p7,4));
  eh->rsound=2/(3*eh->keq)*sqrt(6/Req)*
    log((sqrt(1+Rd)+sqrt(Rd+Req))/(1+sqrt(Req)));

  //This is Eq. 7 (but in h/Mpc)
  eh->kSilk=1.6*pow(OBh2,0.52)*pow(OMh2,0.73)*(1+pow(10.4*OMh2,-0.95))/params->h;

  //These are Eqs. 11
  double a1,a2,b_frac;
  a1=pow(46.9*OMh2,0.670)*(1+pow(32.1*OMh2,-0.532));
  a2=pow(12.0*OMh2,0.424)*(1+pow(45.0*OMh2,-0.582));
  b_frac=OBh2/OMh2;
  eh->alphac=pow(a1,-b_frac)*pow(a2,-b_frac*b_frac*b_frac);

  //These are Eqs. 12
  double bb1,bb2;
  bb1=0.944/(1+pow(458*OMh2,-0.708));
  bb2=pow(0.395*OMh2,-0.0266);
  eh->betac=1/(1+bb1*(pow(1-b_frac,bb2)-1));

  double y=eh->zeq/(1+eh->zdrag);
  double sqy=sqrt(1+y);
  double gy=y*(-6*sqy+(2+3*y)*log((sqy+1)/(sqy-1))); //Eq 15
  //Baryon suppression Eq. 14
  eh->alphab=2.07*eh->keq*eh->rsound*pow(1+Rd,-0.75)*gy;

  //Baryon envelope shift Eq. 24
  eh->betab=0.5+b_frac+(3-2*b_frac)*sqrt(pow(17.2*OMh2,2)+1);

  //Node shift parameter Eq. 23
  eh->bnode=8.41*pow(OMh2,0.435);

  //Approx for the sound horizon, Eq. 26
  eh->rsound_approx=params->h*44.5*log(9.83/OMh2)/
    sqrt(1+10*pow(OBh2,0.75));

  return eh;
}

static double tkEH_0(double keq,double k,double a,double b)
{
  //////
  // Eisentstein & Hu's Tk_0
  // see astro-ph/9709112 for the relevant equations
  double q=k/(13.41*keq); //Eq 10
  double c=14.2/a+386./(1+69.9*pow(q,1.08)); //Eq 20
  double l=log(M_E+1.8*b*q); //Change of var for Eq 19
  return l/(l+c*q*q); //Returns Eq 19
}

static double tkEH_c(eh_struct *eh,double k)
{
  //////
  // Eisenstein & Hu's Tk_c
  // see astro-ph/9709112 for the relevant equations
  double f=1/(1+pow(k*eh->rsound/5.4,4)); //Eq 18
  return f*tkEH_0(eh->keq,k,1,eh->betac)+
    (1-f)*tkEH_0(eh->keq,k,eh->alphac,eh->betac); //Returns Eq 17
}

static double jbes0(double x)
{
  double jl;
  double ax2=x*x;

  if(ax2<1e-4) jl=1-ax2*(1-ax2/20.)/6.;
  else jl=sin(x)/x;

  return jl;
}

static double tkEH_b(eh_struct *eh,double k)
{
  //////
  // Eisenstein & Hu's Tk_b (Eq 21)
  // see astro-ph/9709112 for the relevant equations
  double x_bessel,part1,part2,jbes;
  double x=k*eh->rsound;

  //First term of Eq. 21
  if(k==0) x_bessel=0;
  else {
    x_bessel=x*pow(1+eh->bnode*eh->bnode*eh->bnode/(x*x*x),-1./3.);
  }

  part1=tkEH_0(eh->keq,k,1,1)/(1+pow(x/5.2,2));

  //Second term of Eq. 21
  if(k==0)
    part2=0;
  else
    part2=eh->alphab/(1+pow(eh->betab/x,3))*exp(-pow(k/eh->kSilk,1.4));

  return jbes0(x_bessel)*(part1+part2);
}

static double tsqr_EH(ccl_parameters *params,eh_struct * eh,double k,int wiggled)
{
  //////
  // Eisenstein & Hu's Tk_c
  // see astro-ph/9709112 for the relevant equations
  // Notice the last parameter in eh_power controls
  // whether to introduce wiggles (BAO) in the power spectrum.
  // We do this by default when obtaining the power spectrum.
  double tk;
  double b_frac=params->Omega_b/params->Omega_m;
  if(wiggled)
    //Case with baryons (Eq 8)
    tk=b_frac*tkEH_b(eh,k)+(1-b_frac)*tkEH_c(eh,k);
  else {
    //Zero baryon case (sec 4.2)
    double OMh2=params->Omega_m*params->h*params->h;
    // Compute Eq. 31
    double alpha_gamma=1-0.328*log(431*OMh2)*b_frac+0.38*log(22.3*OMh2)*b_frac*b_frac;
    // Compute Eq. 30
    double gamma_eff=params->Omega_m*params->h*(alpha_gamma+(1-alpha_gamma)/
						(1+pow(0.43*k*eh->rsound_approx,4)));
    // Compute Eq. 28 (assume k in h/Mpc)
    double q=k*eh->th2p7*eh->th2p7/gamma_eff;
    // Compute Eqs. 29
    double l0=log(2*M_E+1.8*q);
    double c0=14.2+731/(1+62.5*q);
    tk=l0/(l0+c0*q*q); //T_0 of Eq. 29
  }

  return tk*tk; //Return T_0^2
}

static double eh_power(ccl_parameters *params,eh_struct *eh,double k,int wiggled)
{
  //Wavenumber in units of Mpc^-1
  double kinvh=k/params->h; //Changed to h/Mpc
  return pow(k,params->n_s)*tsqr_EH(params,eh,kinvh,wiggled);
}
static void ccl_cosmology_compute_power_eh(ccl_cosmology * cosmo, int * status)
{
  //These are the limits of the splining range
  cosmo->data.k_min_lin = ccl_splines->K_MIN;
  cosmo->data.k_min_nl = ccl_splines->K_MIN;
  cosmo->data.k_max_lin = ccl_splines->K_MAX;
  cosmo->data.k_max_nl = ccl_splines->K_MAX;
  double kmin = cosmo->data.k_min_lin;
  double kmax = ccl_splines->K_MAX;

  // Compute nk from number of decades and N_K = # k per decade
  double ndecades = log10(kmax) - log10(kmin);
  int nk = (int)ceil(ndecades*ccl_splines->N_K);

  // Compute na using predefined spline spacing
  double amin = ccl_splines->A_SPLINE_MINLOG_PK;
  double amax = ccl_splines->A_SPLINE_MAX;
  int na = ccl_splines->A_SPLINE_NA_PK + ccl_splines->A_SPLINE_NLOG_PK - 1;

  // Exit if sigma8 wasn't specified
  if (isnan(cosmo->params.sigma8)) {
    *status = CCL_ERROR_INCONSISTENT;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_eh(): sigma8 was not set, but is required for E&H method\n");
    return;
  }

  // New struct for EH parameters
  eh_struct *eh = eh_struct_new(&(cosmo->params));
  if (eh == NULL) {
    *status = CCL_ERROR_MEMORY;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_eh(): memory allocation error\n");
    return;
  }

  // Build grids in k and a that P(k, a) will be evaluated on
  // NB: The x array is initially k, but will later be overwritten with log(k)
  double * x = ccl_log_spacing(kmin, kmax, nk);
  double * y = malloc(sizeof(double)*nk);
  double * a = ccl_linlog_spacing(amin, ccl_splines->A_SPLINE_MIN_PK,
                                  amax, ccl_splines->A_SPLINE_NLOG_PK,
                                  ccl_splines->A_SPLINE_NA_PK);
  double * y2d = malloc(nk * na * sizeof(double));
  if (a==NULL || y==NULL || x==NULL || y2d==NULL) {
    free(eh);free(x);free(y);
    free(a);free(y2d);
    *status = CCL_ERROR_MEMORY;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_eh(): memory allocation error\n");
    return;
  }

  // Calculate P(k) on k grid. After this loop, x will contain log(k) and y
  // will contain log(pk) [which has not yet been normalized]
  // Notice the last parameter in eh_power controls
  // whether to introduce wiggles (BAO) in the power spectrum.
  // We do this by default.
  for (int i=0; i<nk; i++) {
    y[i] = log(eh_power(&cosmo->params, eh, x[i], 1));
    x[i] = log(x[i]);
  }

  // Apply growth factor, D(a), to P(k) and store in 2D (k, a) array
  double gfac, g2;
  gsl_spline2d *log_power_lin = gsl_spline2d_alloc(PLIN_SPLINE_TYPE, nk,na);
  for (int j = 0; j < na; j++) {
    gfac = ccl_growth_factor(cosmo, a[j], status);
    g2 = 2.*log(gfac);
    for (int i=0; i<nk; i++) {
      y2d[j*nk+i] = y[i]+g2;
    } // end loop over k
  } // end loop over a

  // Check that ccl_growth_factor didn't fail
  if (*status) {
    free(eh); free(x); free(y); free(a); free(y2d);
    gsl_spline2d_free(log_power_lin);
    return;
  }

  // Initialize a 2D spline over P(k, a) [which is still unnormalized by sigma8]
  int splinstatus = gsl_spline2d_init(log_power_lin, x, a, y2d,nk,na);
  if (splinstatus) {
    free(eh); free(x); free(y); free(a); free(y2d);
    gsl_spline2d_free(log_power_lin);
    *status = CCL_ERROR_SPLINE;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_eh(): Error creating log_power_lin spline\n");
    return;
  }

  // Calculate sigma8 for the unnormalized P(k), using the standard
  // ccl_sigma8() function
  cosmo->data.p_lin = log_power_lin;
  cosmo->computed_power = true; // Temporarily set this to true
  double sigma8 = ccl_sigma8(cosmo, status);
  cosmo->computed_power = false;

  // Check that ccl_sigma8 didn't fail
  if (*status) {
    free(eh); free(x); free(y); free(a); free(y2d);
    gsl_spline2d_free(log_power_lin);
    return;
  }

  // Calculate normalization factor using computed value of sigma8, then
  // recompute P(k, a) using this normalization
  double log_normalization_factor = 2*(log(cosmo->params.sigma8) - log(sigma8));
  for (int i=0; i < nk; i++) {
    y[i] += log_normalization_factor;
  }
  for (int j = 0; j < na; j++) {
    gfac = ccl_growth_factor(cosmo, a[j], status);
    g2 = 2.*log(gfac);
    for (int i=0; i<nk; i++) {
      y2d[j*nk+i] = y[i]+g2; // Replace previous values
    } // end k loop
  } // end a loop

  splinstatus = gsl_spline2d_init(log_power_lin, x, a, y2d, nk, na);
  if (splinstatus) {
    free(eh); free(x); free(y); free(a); free(y2d);
    gsl_spline2d_free(log_power_lin);
    *status = CCL_ERROR_SPLINE;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_eh(): Error creating log_power_lin spline\n");
    return;
  }
  else
    cosmo->data.p_lin = log_power_lin;

  // Allocate a 2D spline for the nonlinear P(k) [which is just a copy of the
  // linear one for E&H]
  gsl_spline2d * log_power_nl = gsl_spline2d_alloc(PNL_SPLINE_TYPE, nk, na);
  splinstatus = gsl_spline2d_init(log_power_nl, x, a, y2d, nk, na);

  if (splinstatus) {
    free(eh); free(x); free(y); free(a); free(y2d);
    gsl_spline2d_free(log_power_lin);
    gsl_spline2d_free(log_power_nl);
    *status = CCL_ERROR_SPLINE;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_eh(): Error creating log_power_nl spline\n");
    return;
  }
  else
    cosmo->data.p_nl = log_power_nl;

  // Free temporary arrays
  free(eh); free(x); free(y); free(a); free(y2d);
}

/*------ ROUTINE: tsqr_BBKS -----
INPUT: ccl_parameters and k wavenumber in 1/Mpc
TASK: provide the square of the BBKS transfer function with baryonic correction
NOTE: Bardeen et al. (1986) as implemented in Sugiyama (1995)
*/

static double tsqr_BBKS(ccl_parameters * params, double k)
{
  double q = k/(params->Omega_m*params->h*params->h*exp(-params->Omega_b*(1.0+pow(2.*params->h,.5)/params->Omega_m)));
  return pow(log(1.+2.34*q)/(2.34*q),2.0)/pow(1.+3.89*q+pow(16.1*q,2.0)+pow(5.46*q,3.0)+pow(6.71*q,4.0),0.5);
}

/*------ ROUTINE: bbks_power -----
INPUT: ccl_parameters and k wavenumber in 1/Mpc
TASK: provide the BBKS power spectrum with baryonic correction at single k
*/

//Calculate Normalization see Cosmology Notes 8.105 (TODO: whose comment is this?)
static double bbks_power(ccl_parameters * params, double k)
{
  return pow(k,params->n_s)*tsqr_BBKS(params, k);
}

/*------ ROUTINE: ccl_cosmology_compute_bbks_power -----
INPUT: cosmology
TASK: provide spline for the BBKS power spectrum with baryonic correction
*/

static void ccl_cosmology_compute_power_bbks(ccl_cosmology * cosmo, int * status)
{
  //These are the limits of the splining range
  cosmo->data.k_min_lin=ccl_splines->K_MIN;
  cosmo->data.k_min_nl=ccl_splines->K_MIN;
  cosmo->data.k_max_lin=ccl_splines->K_MAX;
  cosmo->data.k_max_nl=ccl_splines->K_MAX;
  double kmin = cosmo->data.k_min_lin;
  double kmax = ccl_splines->K_MAX;
  //Compute nk from number of decades and N_K = # k per decade
  double ndecades = log10(kmax) - log10(kmin);
  int nk = (int)ceil(ndecades*ccl_splines->N_K);
  double amin = ccl_splines->A_SPLINE_MINLOG_PK;
  double amax = ccl_splines->A_SPLINE_MAX;
  int na = ccl_splines->A_SPLINE_NA_PK+ccl_splines->A_SPLINE_NLOG_PK-1;

  // Exit if sigma8 wasn't specified
  if (isnan(cosmo->params.sigma8)) {
    *status = CCL_ERROR_INCONSISTENT;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_bbks(): sigma8 not set, required for BBKS\n");
    return; //Return without anything to free
  }

  // The x array is initially k, but will later
  // be overwritten with log(k)
  double * x = ccl_log_spacing(kmin, kmax, nk);
  double * y = malloc(sizeof(double)*nk);
  double * a = ccl_linlog_spacing(amin, ccl_splines->A_SPLINE_MIN_PK, amax, ccl_splines->A_SPLINE_NLOG_PK, ccl_splines->A_SPLINE_NA_PK);
  double * y2d = malloc(nk * na * sizeof(double));

  //If error, store status, we will free later
  if (a==NULL||y==NULL|| x==NULL || y2d==0) {
    *status=CCL_ERROR_MEMORY;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_bbks(): memory allocation error\n");
  }

  //Status flags
  int splinstatus=0;

  if(!*status){

    // After this loop x will contain log(k)
    for (int i=0; i<nk; i++) {
      y[i] = log(bbks_power(&cosmo->params, x[i]));
      x[i] = log(x[i]);
    }

    for (int j = 0; j < na; j++) {
      double gfac = ccl_growth_factor(cosmo,a[j], status);
      double g2 = 2.*log(gfac);
      for (int i=0; i<nk; i++) {
	y2d[j*nk+i] = y[i]+g2;
      }
    }
  }

  if(!*status){

    // Initialize a 2D spline over P(k, a) [which is still unnormalized by sigma8]
    gsl_spline2d * log_power_lin_unnorm = gsl_spline2d_alloc(PLIN_SPLINE_TYPE, nk,na);
    splinstatus = gsl_spline2d_init(log_power_lin_unnorm, x, a, y2d,nk,na);

    //If not, proceed
    if (!splinstatus) {
      cosmo->data.p_lin=log_power_lin_unnorm;
      cosmo->computed_power=true;
    } else {
      gsl_spline2d_free(log_power_lin_unnorm);
      *status = CCL_ERROR_SPLINE;
      ccl_cosmology_set_status_message(cosmo,"ccl_power.c: ccl_cosmology_compute_power_bbks(): Error creating log_power_lin spline\n");
    }
  }

  if(*status){
    free(x); free(y); free(a); free(y2d);
    return;
  }

  // Calculate sigma8 for the unnormalized P(k), using the standard
  // ccl_sigma8() function
  double sigma8 = ccl_sigma8(cosmo,status);
  cosmo->computed_power=false;

  // Check that ccl_sigma8 didn't fail
  if (!*status) {

    // Calculate normalization factor using computed value of sigma8, then
    // recompute P(k, a) using this normalization
    double log_normalization_factor = 2*(log(cosmo->params.sigma8) - log(sigma8));
    for (int i=0; i<nk; i++) {
      y[i] += log_normalization_factor;
    }
    for (int j = 0; j < na; j++) {
      double gfac = ccl_growth_factor(cosmo,a[j], status);
      double g2 = 2.*log(gfac);
      for (int i=0; i<nk; i++) {
	y2d[j*nk+i] = y[i]+g2;
      }
    }
  }

  //Check growth didn't fail
  if (!*status) {

    gsl_spline2d * log_power_lin = gsl_spline2d_alloc(PLIN_SPLINE_TYPE, nk,na);
    splinstatus = gsl_spline2d_init(log_power_lin, x, a, y2d,nk,na);

    if (!splinstatus) {
      cosmo->data.p_lin=log_power_lin;
    } else {
      gsl_spline2d_free(log_power_lin);
      *status = CCL_ERROR_SPLINE;
      ccl_cosmology_set_status_message(cosmo,"ccl_power.c: ccl_cosmology_compute_power_bbks(): Error creating log_power_lin spline\n");
    }
  }

  if(!*status){
    // Allocate a 2D spline for the nonlinear P(k)
    //[which is just a copy of the linear one for BBKS]
    gsl_spline2d * log_power_nl = gsl_spline2d_alloc(PNL_SPLINE_TYPE, nk,na);
    splinstatus = gsl_spline2d_init(log_power_nl, x, a, y2d,nk,na);
    if (!splinstatus) {
      cosmo->data.p_nl = log_power_nl;
    } else {
      gsl_spline2d_free(log_power_nl);
      *status = CCL_ERROR_SPLINE;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_bbks(): Error creating log_power_nl spline\n");
    }
  }

  free(x); free(y); free(a); free(y2d);
  return;
}

/*------ ROUTINE: ccl_cosmology_compute_power_emu -----
INPUT: cosmology
TASK: provide spline for the emulated power spectrum from Cosmic EMU
*/

static void ccl_cosmology_compute_power_emu(ccl_cosmology * cosmo, int * status)
{

  struct precision pr;        // for precision parameters
  struct background ba;       // for cosmological background
  struct thermo th;           // for thermodynamics
  struct perturbs pt;         // for source functions
  struct transfers tr;        // for transfer functions
  struct primordial pm;       // for primordial spectra
  struct spectra sp;          // for output spectra
  struct nonlinear nl;        // for non-linear spectra
  struct lensing le;
  struct output op;
  struct file_content fc;

  double Omeganuh2_eq;
  double w0wacomb = -cosmo->params.w0 - cosmo->params.wa;

  ErrorMsg errmsg; // for error messages
  // generate file_content structure
  // Configuration parameters will be passed through this structure,
  // to avoid writing and reading .ini files for every call
  int parser_length = 20;
  int init_arr[7]={0,0,0,0,0,0,0};

  //Check initialization
  if (parser_init(&fc,parser_length,"none",errmsg) == _FAILURE_) {
    *status = CCL_ERROR_CLASS;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_class(): parser init error:%s\n", errmsg);
  }

  // Check ranges to see if the cosmology is valid
  if((cosmo->params.h<0.55) || (cosmo->params.h>0.85)){
    *status=CCL_ERROR_INCONSISTENT;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_emu(): h is outside allowed range\n");
  }
  if(w0wacomb<8.1e-3){ //0.3^4
    *status=CCL_ERROR_INCONSISTENT;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_emu(): w0 and wa do not satisfy the emulator bound\n");
  }
  if(cosmo->params.Omega_n_mass*cosmo->params.h*cosmo->params.h>0.01){
    *status=CCL_ERROR_INCONSISTENT;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_emu(): Omega_nu does not satisfy the emulator bound\n");
  }

  // Check to see if sigma8 was defined
  if(isnan(cosmo->params.sigma8)){
    *status=CCL_ERROR_INCONSISTENT;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_emu(): sigma8 is not defined; specify sigma8 instead of A_s\n");
  }

  //If one of the previous test was unsuccessful, quit:
  if(*status) return;

  // Check if the cosmology has been set up with equal neutrino masses for the emulator
  // If not, check if the user has forced redistribution of masses and if so do this.
  if(cosmo->params.N_nu_mass>0) {
    if (cosmo->config.emulator_neutrinos_method == ccl_emu_strict){
      if (cosmo->params.N_nu_mass==3){
	//double diff1 = pow((cosmo->params.mnu[0] - cosmo->params.mnu[1]) * (cosmo->params.mnu[0] - cosmo->params.mnu[1]), 0.5);
	//double diff2 = pow((cosmo->params.mnu[1] - cosmo->params.mnu[2]) * (cosmo->params.mnu[1] - cosmo->params.mnu[2]), 0.5);
	//double diff3 = pow((cosmo->params.mnu[2] - cosmo->params.mnu[0]) * (cosmo->params.mnu[2] - cosmo->params.mnu[0]), 0.5);
	//if (diff1>1e-12 || diff2>1e-12 || diff3>1e-12){
	if (cosmo->params.mnu[0] != cosmo->params.mnu[1] || cosmo->params.mnu[0] != cosmo->params.mnu[2] || cosmo->params.mnu[1] != cosmo->params.mnu[2]){
	  *status = CCL_ERROR_INCONSISTENT;
	  ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_emu(): In the default configuration, you must pass a list of 3 equal neutrino masses or pass a sum and set mnu_type = ccl_mnu_sum_equal. If you wish to over-ride this, set config->emulator_neutrinos_method = 'ccl_emu_equalize'. This will force the neutrinos to be of equal mass but will result in internal inconsistencies.\n");
	}
      } else if (cosmo->params.N_nu_mass!=3){
	*status = CCL_ERROR_INCONSISTENT;
	ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_emu(): In the default configuration, you must pass a list of 3 equal neutrino masses or pass a sum and set mnu_type = ccl_mnu_sum_equal. If you wish to over-ride this, set config->emulator_neutrinos_method = 'ccl_emu_equalize'. This will force the neutrinos to be of equal mass but will result in internal inconsistencies.\n");
      }
    } else if (cosmo->config.emulator_neutrinos_method == ccl_emu_equalize){
      // Reset the masses to equal
      double mnu_eq[3] = {cosmo->params.sum_nu_masses / 3., cosmo->params.sum_nu_masses / 3., cosmo->params.sum_nu_masses / 3.};
      Omeganuh2_eq = ccl_Omeganuh2(1.0, 3, mnu_eq, cosmo->params.T_CMB, cosmo->data.accelerator, status);
    }
  } else {
    if(fabs(cosmo->params.N_nu_rel - 3.04)>1.e-6){
      *status=CCL_ERROR_INCONSISTENT;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_emu(): Set Neff = 3.04 for cosmic emulator predictions in absence of massive neutrinos.\n");
    }
  }

  if(!*status){
    // Prepare to run CLASS for linear scales
    ccl_fill_class_parameters(cosmo,&fc,parser_length, status);
  }

  if (!*status){
    ccl_run_class(cosmo, &fc,&pr,&ba,&th,&pt,&tr,&pm,&sp,&nl,&le,&op,init_arr,status);
  }

  if (*status){
    ccl_free_class_structs(cosmo, &ba,&th,&pt,&tr,&pm,&sp,&nl,&le,init_arr,status);
    if (parser_free(&fc)== _FAILURE_) {
      *status = CCL_ERROR_CLASS;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_class(): Error freeing CLASS parser\n");
    }
    return;
  }

  //These are the limits of the splining range
  cosmo->data.k_min_lin=2*exp(sp.ln_k[0]);
  cosmo->data.k_max_lin=ccl_splines->K_MAX_SPLINE;
  //CLASS calculations done - now allocate CCL splines
  double kmin = cosmo->data.k_min_lin;
  double kmax = ccl_splines->K_MAX_SPLINE;
  //Compute nk from number of decades and N_K = # k per decade
  double ndecades = log10(kmax) - log10(kmin);
  int nk = (int)ceil(ndecades*ccl_splines->N_K);
  double amin = ccl_splines->A_SPLINE_MINLOG_PK;
  double amax = ccl_splines->A_SPLINE_MAX;
  int na = ccl_splines->A_SPLINE_NA_PK+ccl_splines->A_SPLINE_NLOG_PK-1;

  // The x array is initially k, but will later
  // be overwritten with log(k)
  double * x = ccl_log_spacing(kmin, kmax, nk);
  double * a = ccl_linlog_spacing(amin, ccl_splines->A_SPLINE_MIN_PK, amax, ccl_splines->A_SPLINE_NLOG_PK, ccl_splines->A_SPLINE_NA_PK);
  double * y2d_lin = malloc(nk * na * sizeof(double));
  if (a==NULL|| x==NULL || y2d_lin==NULL) {
    *status = CCL_ERROR_SPLINE;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_class(): memory allocation error\n");
  }

  if(!*status){
    // After this loop x will contain log(k), y will contain log(P_nl), z will contain log(P_lin)
    // all in Mpc, not Mpc/h units!
    double psout_l,ic;
    int s=0;
    for (int i=0; i<nk; i++) {
      for (int j = 0; j < na; j++) {
	//The 2D interpolation routines access the function values y_{k_ia_j} with the following ordering:
	//y_ij = y2d[j*N_k + i]
	//with i = 0,...,N_k-1 and j = 0,...,N_a-1.
	s |= spectra_pk_at_k_and_z(&ba, &pm, &sp,x[i],1./a[j]-1., &psout_l,&ic);
	y2d_lin[j*nk+i] = log(psout_l);
      }
      x[i] = log(x[i]);
    }
    if(s) {
      *status = CCL_ERROR_CLASS;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_emu(): Error computing CLASS power spectrum\n");
    }
  }

  if(!*status){

    gsl_spline2d * log_power = gsl_spline2d_alloc(PLIN_SPLINE_TYPE, nk,na);
    int pwstatus = gsl_spline2d_init(log_power, x, a, y2d_lin,nk,na);
    if (!pwstatus) {
      cosmo->data.p_lin = log_power;
    } else {
      gsl_spline2d_free(log_power);
      *status = CCL_ERROR_SPLINE;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_emu(): Error creating log_power spline\n");
    }
  }

  if(*status){ //Linear power spectrum failed, so quit.
      free(x);
      free(a);
      free(y2d_lin);
      ccl_free_class_structs(cosmo, &ba,&th,&pt,&tr,&pm,&sp,&nl,&le,init_arr,status);
      return;
  }

  //Now start the NL computation with the emulator
  //These are the limits of the splining range
  cosmo->data.k_min_nl=K_MIN_EMU;
  cosmo->data.k_max_nl=K_MAX_EMU;
  amin = A_MIN_EMU; //limit of the emulator
  amax = ccl_splines->A_SPLINE_MAX;
  na = ccl_splines->A_SPLINE_NA_PK;
  // The x array is initially k, but will later
  // be overwritten with log(k)
  double * logx= malloc(NK_EMU*sizeof(double));
  double * y;
  double * xstar = malloc(9 * sizeof(double));
  double * aemu = ccl_linear_spacing(amin,amax, na);
  double * y2d = malloc(NK_EMU * na * sizeof(double));
  if (aemu==NULL || y2d==NULL || logx==NULL || xstar==NULL){
    *status=CCL_ERROR_MEMORY;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_emu(): memory allocation error\n");
  }

  if(!*status){

    //For each redshift:
    for (int j = 0; j < na; j++){

      //Turn cosmology into xstar:
      xstar[0] = (cosmo->params.Omega_c+cosmo->params.Omega_b)*cosmo->params.h*cosmo->params.h;
      xstar[1] = cosmo->params.Omega_b*cosmo->params.h*cosmo->params.h;
      xstar[2] = cosmo->params.sigma8;
      xstar[3] = cosmo->params.h;
      xstar[4] = cosmo->params.n_s;
      xstar[5] = cosmo->params.w0;
      xstar[6] = cosmo->params.wa;
      if ((cosmo->params.N_nu_mass>0) && (cosmo->config.emulator_neutrinos_method == ccl_emu_equalize)){
	xstar[7] = Omeganuh2_eq;
      } else {
        xstar[7] = cosmo->params.Omega_n_mass*cosmo->params.h*cosmo->params.h;
      }
      xstar[8] = 1./aemu[j]-1;
      //Need to have this here because otherwise overwritten by emu in each loop

      //Call emulator at this redshift
      ccl_pkemu(xstar,&y, status, cosmo);
      if (y == NULL){
	*status = CCL_ERROR_MEMORY;
	ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_emu(): Error obtaining emulator predictions\n");
      }
      for (int i=0; i<NK_EMU; i++){
	logx[i] = log(mode[i]);
	y2d[j*NK_EMU+i] = log(y[i]);
      }
    }
  }

  if(!*status){

    gsl_spline2d * log_power_nl = gsl_spline2d_alloc(PLIN_SPLINE_TYPE, NK_EMU,na);
    int splinstatus = gsl_spline2d_init(log_power_nl, logx, aemu, y2d,NK_EMU,na);
    //Note the minimum k of the spline is different from the linear one.

    if (!splinstatus){
      cosmo->data.p_nl = log_power_nl;
    } else {
      gsl_spline2d_free(log_power_nl);
      *status = CCL_ERROR_SPLINE;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power_emu(): Error creating log_power spline\n");
    }
  }

  free(x); free(a); free(y);
  free(xstar); free(logx); free(aemu);
  free(y2d_lin); free(y2d);
}

/*------ ROUTINE: ccl_cosmology_compute_power -----
INPUT: ccl_cosmology * cosmo
TASK: compute power spectrum
*/
void ccl_cosmology_compute_power(ccl_cosmology * cosmo, int * status)
{

  if (cosmo->computed_power) return;
    switch(cosmo->config.transfer_function_method){
        case ccl_bbks:
	  ccl_cosmology_compute_power_bbks(cosmo,status);
	  break;
        case ccl_eisenstein_hu:
	  ccl_cosmology_compute_power_eh(cosmo,status);
	  break;
        case ccl_boltzmann_class:
	  ccl_cosmology_compute_power_class(cosmo,status);
	  break;
        case ccl_emulator:
	  ccl_cosmology_compute_power_emu(cosmo,status);
	  break;
        default:
	  *status = CCL_ERROR_INCONSISTENT;
	  ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_cosmology_compute_power(): Unknown or non-implemented transfer function method: %d \n", cosmo->config.transfer_function_method);
    }

    ccl_check_status(cosmo,status);
    if (*status==0){
      cosmo->computed_power = true;
    }
  return;
}

/*------ ROUTINE: ccl_power_extrapol_highk -----
INPUT: ccl_cosmology * cosmo, a, k [1/Mpc]
TASK: extrapolate power spectrum at high k
*/
static double ccl_power_extrapol_highk(ccl_cosmology * cosmo, double k, double a,
				       gsl_spline2d * powerspl, double kmax_spline, int * status)
{
  double log_p_1;
  double deltak=1e-2; //step for numerical derivative;
  double deriv_pk_kmid,deriv2_pk_kmid;
  double lkmid;
  double lpk_kmid;

  lkmid = log(kmax_spline)-2*deltak;

  int gslstatus =  gsl_spline2d_eval_e(powerspl, lkmid,a,NULL ,NULL ,&lpk_kmid);
  if(gslstatus != GSL_SUCCESS) {
    ccl_raise_gsl_warning(gslstatus, "ccl_power.c: ccl_power_extrapol_highk():");
    *status = CCL_ERROR_SPLINE_EV;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_power_extrapol_highk(): Spline evaluation error\n");
    return NAN;
  }
  //GSL derivatives
  gslstatus = gsl_spline2d_eval_deriv_x_e (powerspl, lkmid, a, NULL,NULL,&deriv_pk_kmid);
  if(gslstatus != GSL_SUCCESS) {
    ccl_raise_gsl_warning(gslstatus, "ccl_power.c: ccl_power_extrapol_highk():");
    *status = CCL_ERROR_SPLINE_EV;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_power_extrapol_highk(): Spline evaluation error\n");
    return NAN;
  }
  gslstatus = gsl_spline2d_eval_deriv_xx_e (powerspl, lkmid, a, NULL,NULL,&deriv2_pk_kmid);
  if(gslstatus != GSL_SUCCESS) {
    ccl_raise_gsl_warning(gslstatus, "ccl_power.c: ccl_power_extrapol_highk():");
    *status = CCL_ERROR_SPLINE_EV;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_power_extrapol_highk(): Spline evaluation error\n");
    return NAN;
  }
  log_p_1=lpk_kmid+deriv_pk_kmid*(log(k)-lkmid)+deriv2_pk_kmid/2.*(log(k)-lkmid)*(log(k)-lkmid);

  return log_p_1;

}

/*------ ROUTINE: ccl_power_extrapol_lowk -----
INPUT: ccl_cosmology * cosmo, a, k [1/Mpc]
TASK: extrapolate power spectrum at low k
*/
static double ccl_power_extrapol_lowk(ccl_cosmology * cosmo, double k, double a,
				      gsl_spline2d * powerspl, double kmin_spline, int * status)
{
  double log_p_1;
  double deltak=1e-2; //safety step
  double lkmin=log(kmin_spline)+deltak;
  double lpk_kmin;
  int gslstatus = gsl_spline2d_eval_e(powerspl,lkmin,a,NULL,NULL,&lpk_kmin);

  if(gslstatus != GSL_SUCCESS) {
    ccl_raise_gsl_warning(gslstatus, "ccl_power.c: ccl_power_extrapol_lowk():");
    *status=CCL_ERROR_SPLINE_EV;
    ccl_cosmology_set_status_message(cosmo,"ccl_power.c: ccl_power_extrapol_lowk(): Spline evaluation error\n");
    return NAN;
  }

  return lpk_kmin+cosmo->params.n_s*(log(k)-lkmin);
}

/*------ ROUTINE: ccl_linear_matter_power -----
INPUT: ccl_cosmology * cosmo, k [1/Mpc],a
TASK: compute the linear power spectrum at a given redshift
      by rescaling using the growth function
*/

double ccl_linear_matter_power(ccl_cosmology * cosmo, double k, double a, int * status)

{
  if ((cosmo->config.transfer_function_method == ccl_emulator) && (a<A_MIN_EMU)){
    *status = CCL_ERROR_INCONSISTENT;
    ccl_cosmology_set_status_message(cosmo, "ccl_power.c: the cosmic emulator cannot be used above a=%f\n",A_MIN_EMU);
    return NAN;
  }

  if (!cosmo->computed_power) ccl_cosmology_compute_power(cosmo, status);
  // Return if compilation failed
  //if (cosmo->data.p_lin == NULL) return NAN;
  if (!cosmo->computed_power) return NAN;

  double log_p_1;
  int gslstatus;

  if(a<ccl_splines->A_SPLINE_MINLOG_PK) {  //Extrapolate linearly at high redshift
    double pk0=ccl_linear_matter_power(cosmo,k,ccl_splines->A_SPLINE_MINLOG_PK,status);
    double gf=ccl_growth_factor(cosmo,a,status)/ccl_growth_factor(cosmo,ccl_splines->A_SPLINE_MINLOG_PK,status);

    return pk0*gf*gf;
  }
  if (*status!=CCL_ERROR_INCONSISTENT){
    if(k<=cosmo->data.k_min_lin) {
      log_p_1=ccl_power_extrapol_lowk(cosmo,k,a,cosmo->data.p_lin,cosmo->data.k_min_lin,status);

      return exp(log_p_1);
    }
    else if(k<cosmo->data.k_max_lin){
      gslstatus = gsl_spline2d_eval_e(cosmo->data.p_lin, log(k), a,NULL,NULL,&log_p_1);
      if(gslstatus != GSL_SUCCESS) {
        ccl_raise_gsl_warning(gslstatus, "ccl_power.c: ccl_linear_matter_power():");
        *status = CCL_ERROR_SPLINE_EV;
        ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_linear_matter_power(): Spline evaluation error\n");
        return NAN;
      }
      else {
        return exp(log_p_1);
      }
    }
    else { //Extrapolate using log derivative
      log_p_1 = ccl_power_extrapol_highk(cosmo,k,a,cosmo->data.p_lin,cosmo->data.k_max_lin,status);
      return exp(log_p_1);
    }
  }

  return exp(log_p_1);
}

/*------ ROUTINE: ccl_nonlin_matter_power -----
INPUT: ccl_cosmology * cosmo, a, k [1/Mpc]
TASK: compute the nonlinear power spectrum at a given redshift
*/

double ccl_nonlin_matter_power(ccl_cosmology * cosmo, double k, double a, int *status)
{
  double log_p_1, pk;

  switch(cosmo->config.matter_power_spectrum_method) {

  //If the matter PS specified was linear, then do the linear compuation
  case ccl_linear:
    return ccl_linear_matter_power(cosmo,k,a,status);

  case ccl_halofit:
    if (!cosmo->computed_power)
      ccl_cosmology_compute_power(cosmo, status);
    if (cosmo->data.p_nl == NULL) return NAN; // Return if computation failed

    if(a<ccl_splines->A_SPLINE_MINLOG_PK) { //Extrapolate linearly at high redshift
      double pk0=ccl_nonlin_matter_power(cosmo,k,ccl_splines->A_SPLINE_MINLOG_PK,status);
      double gf=ccl_growth_factor(cosmo,a,status)/ccl_growth_factor(cosmo,ccl_splines->A_SPLINE_MINLOG_PK,status);
      return pk0*gf*gf;
    }
		break;

  case ccl_emu:
    if ((cosmo->config.transfer_function_method == ccl_emulator) && (a<A_MIN_EMU)){
      *status = CCL_ERROR_EMULATOR_BOUND;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: the cosmic emulator cannot be used above z=2\n");
      return NAN;
    }

    // Compute power spectrum if needed; return if computation failed
    if (!cosmo->computed_power){
      ccl_cosmology_compute_power(cosmo,status);
    }
    if (cosmo->data.p_nl == NULL) return NAN;
		break;

  default:
    ccl_raise_warning(
			CCL_ERROR_NOT_IMPLEMENTED,
      "config.matter_power_spectrum_method = %d not yet supported "
			"continuing with linear power spectrum\n", cosmo->config.matter_power_spectrum_method);
    cosmo->config.matter_power_spectrum_method=ccl_linear;
    return ccl_linear_matter_power(cosmo,k,a,status);
  } // end switch

  // if we get here, try to evaluate the power spectrum
  // we need to account for bounds below and above
  if (k <= cosmo->data.k_min_nl) {
    // we assume no baryonic effects below k_min_nl
    log_p_1 = ccl_power_extrapol_lowk(cosmo, k, a, cosmo->data.p_nl, cosmo->data.k_min_nl, status);
    return exp(log_p_1);
  }

  if (k < cosmo->data.k_max_nl) {
    int gslstatus = gsl_spline2d_eval_e(cosmo->data.p_nl, log(k), a, NULL ,NULL, &log_p_1);
    if (gslstatus != GSL_SUCCESS) {
      ccl_raise_gsl_warning(gslstatus, "ccl_power.c: ccl_nonlin_matter_power():");
      *status = CCL_ERROR_SPLINE_EV;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_nonlin_matter_power(): Spline evaluation error\n");
      return NAN;
    } else {
      pk = exp(log_p_1);
    }
  } else {
    // Extrapolate NL regime using log derivative
    log_p_1 = ccl_power_extrapol_highk(cosmo, k, a, cosmo->data.p_nl, cosmo->data.k_max_nl, status);
    pk = exp(log_p_1);
  }

	// Add baryonic correction
  if (cosmo->config.baryons_power_spectrum_method == ccl_bcm) {
    int pwstatus = 0;
    double fbcm = ccl_bcm_model_fka(cosmo, k, a, &pwstatus);
    pk *= fbcm;
    if (pwstatus) {
      *status = CCL_ERROR_SPLINE_EV;
      ccl_cosmology_set_status_message(cosmo, "ccl_power.c: ccl_nonlin_matter_power(): Error in BCM correction\n");
      return NAN;
    }
  }

  return pk;
}

// Params for sigma(R) integrand
typedef struct {
  ccl_cosmology *cosmo;
  double R;
  int* status;
} SigmaR_pars;

typedef struct {
  ccl_cosmology *cosmo;
  double R;
  int* status;
} SigmaV_pars;

/* --------- ROUTINE: w_tophat ---------
INPUT: kR, ususally a wavenumber multiplied by a smoothing radius
TASK: Output W(x)=[sin(x)-x*cos(x)]*(3/x)^3
*/
static double w_tophat(double kR)
{
  double w;
  double kR2 = kR*kR;

  // This is the Maclaurin expansion of W(x)=[sin(x)-xcos(x)]*(3/x)**3 to O(x^7), with x=kR.
  // Necessary numerically because at low x W(x) relies on the fine cancellation of two terms
  if(kR<0.1) {
     w = 1. + kR2*(-0.1 +
		   kR2*(0.003561429 +
			kR2*(-6.61376e-5 +
			     kR2*(7.51563e-7))));
     /*w =1.-0.1*kR*kR+0.003571429*kR*kR*kR*kR
      -6.61376E-5*kR*kR*kR*kR*kR*kR
      +7.51563E-7*kR*kR*kR*kR*kR*kR*kR*kR;*/
  }
  else
    w = 3.*(sin(kR) - kR*cos(kR))/(kR2*kR);
  return w;
}

// Integrand for sigmaR integral
static double sigmaR_integrand(double lk,void *params)
{
  SigmaR_pars *par=(SigmaR_pars *)params;

  double k=pow(10.,lk);
  double pk=ccl_linear_matter_power(par->cosmo,k, 1.,par->status);
  double kR=k*par->R;
  double w = w_tophat(kR);

  return pk*k*k*k*w*w;
}

// Integrand for sigmaV integral
static double sigmaV_integrand(double lk,void *params)
{
  SigmaV_pars *par=(SigmaV_pars *)params;

  double k=pow(10.,lk);
  double pk=ccl_linear_matter_power(par->cosmo,k, 1.,par->status);
  double kR=k*par->R;
  double w = w_tophat(kR);

  return pk*k*w*w/3.0;
}

/* --------- ROUTINE: ccl_sigmaR ---------
INPUT: cosmology, comoving smoothing radius, scale factor
TASK: compute sigmaR, the variance in the *linear* density field
smoothed with a tophat filter of comoving size R
*/
double ccl_sigmaR(ccl_cosmology *cosmo,double R,double a,int *status)
{
  SigmaR_pars par;
  par.status = status;

  par.cosmo=cosmo;
  par.R=R;
  gsl_integration_cquad_workspace *workspace=gsl_integration_cquad_workspace_alloc(ccl_gsl->N_ITERATION);
  gsl_function F;
  F.function=&sigmaR_integrand;
  F.params=∥
  double sigma_R;
  int gslstatus = gsl_integration_cquad(&F, log10(ccl_splines->K_MIN), log10(ccl_splines->K_MAX),
				                                0.0, ccl_gsl->INTEGRATION_SIGMAR_EPSREL,
                                        workspace,&sigma_R,NULL,NULL);
  if(gslstatus != GSL_SUCCESS) {
    ccl_raise_gsl_warning(gslstatus, "ccl_power.c: ccl_sigmaR():");
    *status |= gslstatus;
  }

  gsl_integration_cquad_workspace_free(workspace);

  return sqrt(sigma_R*M_LN10/(2*M_PI*M_PI))*ccl_growth_factor(cosmo, a, status);
}

/* --------- ROUTINE: ccl_sigmaV ---------
INPUT: cosmology, comoving smoothing radius, scale factor
TASK: compute sigmaV, the variance in the *linear* displacement field
smoothed with a tophat filter of comoving size R
The linear displacement field is the gradient of the linear density field
*/
double ccl_sigmaV(ccl_cosmology *cosmo,double R,double a,int *status)
{
  SigmaV_pars par;
  par.status = status;

  par.cosmo=cosmo;
  par.R=R;
  gsl_integration_cquad_workspace *workspace=gsl_integration_cquad_workspace_alloc(ccl_gsl->N_ITERATION);
  gsl_function F;
  F.function=&sigmaV_integrand;
  F.params=∥
  double sigma_V;
	int gslstatus = gsl_integration_cquad(&F, log10(ccl_splines->K_MIN), log10(ccl_splines->K_MAX),
																				0.0, ccl_gsl->INTEGRATION_SIGMAR_EPSREL,
																				workspace,&sigma_V,NULL,NULL);

  if(gslstatus != GSL_SUCCESS) {
    ccl_raise_gsl_warning(gslstatus, "ccl_power.c: ccl_sigmaV():");
    *status |= gslstatus;
  }

  gsl_integration_cquad_workspace_free(workspace);

  return sqrt(sigma_V*M_LN10/(2*M_PI*M_PI))*ccl_growth_factor(cosmo, a, status);
}

/* --------- ROUTINE: ccl_sigma8 ---------
INPUT: cosmology
TASK: compute sigma8, the variance in the *linear* density field at a=1
smoothed with a tophat filter of comoving size 8 Mpc/h
*/
double ccl_sigma8(ccl_cosmology *cosmo, int *status)
{
  return ccl_sigmaR(cosmo,8/cosmo->params.h, 1.,status);
}


/*--------------------------------------------------------------------
 * $Id$
 *
 * This file is part of libRadtran.
 * Copyright (c) 1997-2012 by Arve Kylling, Bernhard Mayer,
 *                            Claudia Emde, Robert Buras
 *
 * ######### Contact info: http://www.libradtran.org #########
 *
 * 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., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *--------------------------------------------------------------------*/

/* 03.04.2002: Fixed problem with finding correct grid cell for direct beam, AKy. */

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

#ifndef _AIX
#include <getopt.h>
#else
#include <unistd.h>
#endif

#if HAVE_LIBGSL
#include <gsl/gsl_math.h>
#include <gsl/gsl_monte.h>
#include <gsl/gsl_monte_plain.h>
#include <gsl/gsl_monte_miser.h>
#include <gsl/gsl_monte_vegas.h>
#endif

#include "ascii.h"

#define PROGRAM "angres"
#define VERSION "0.3"
#define DATE "3 Feb, 2003"

#define DIM_ANG_INT 2 /* Integration to be made over 2 dimensions */

#ifndef PI
#define PI 3.14159265358979323846264338327
#endif

typedef struct {
  int      nphi;
  int      numu;
  double*  umu;
  double*  phi;
  double** prod;
} mcinput;

/*************************************************************/
/* print usage information                                   */
/*************************************************************/

/*
  Documentation in latex for the User's Guide. Extracted by sdoc2.awk
  and input in the doc/tools.tex.
*/
/*
  <lpdoc>

\subsection{Angular response and tilted surfaces - \codeidx{angres}}
The \code{angres} tool takes a precalculated radiance field and integrates
it over a given angular area using any angular response. Typical usages of
\code{angres} are calculation of radiation on tilted surfaces and estimation
of effects of imperfect angular response functions.

The \code{angres} tool is invoked as follows:
\begin{Verbatim}[fontsize=\footnotesize]
 angres angres_file raddis_file
\end{Verbatim}
The two required input files will be read by the \code{angres} tool.
\begin{description}
\item[angres\_file] is a two column file with the first column holding
     the angle and the second column the angular response, e.g. a
     measured cosine response. To generate standard angular response
     function see the \code{make\_angres\_func} tool.

\item[raddis\_file] holds the radiance distribution as output from uvspec
     with the disort solvers for one single wavelength.
\end{description}
After reading the two input files the angular response will be tilted
and rotated if specified with the \code{-t} and \code{-r} options respectively. Finally
the product of the resulting angular response and radiance distribution
field are integrated using Monte Carlo methods to yield the effective
response. The integration is done for the diffuse radiation field only. To
include the direct contribution the -s and -z options must be set to give
the direction of the sun.

Output is 3 numbers:
\begin{enumerate}
\item The integral of the diffuse radiation field times angular response.
\item Estimated absolute error of the above integral.
\item The integral of the diffuse+direct radiation field times angular response
       (requires that \code{-s} and \code{-z} are specified, otherwise same as first number
\end{enumerate}

The angles in the \file{angres\_file} must be in radians if not
the \code{-a} option is used. The \file{raddis\_file} must contain
output from \code{uvspec} run for one single wavelength with one of
the disort solvers and with \code{phi} and \code{umu} set.
Note that the angles in the \file{angres\_file} must follow the same
conventions as for the disort algorithm. This is different from
that typically used when reporting measurements of the angular response.

The \code{angres} tool accepts the following command line options:

\begin{description}
\item[-h]  show this page.
\item[-c]  number of random points used for Monte Carlo integration.
\item[-i]  The diffuse radiation is assumed to be isotropic.
\item[-a]  angular response angle given in degrees and not cosine of angle.
\item[-r]  rotation angle in degrees.
\item[-t]  tilt angle in degrees.
\item[-s]  solar zenith angle in degrees.
\item[-z]  solar azimuth angle in degrees.
\item[-p]  pgm files are made of the angular response before
      and after tilt and rotate.
\end{description}

Sample \code{angres} input and output files are found in the \file{examples} directory.
The following
\begin{Verbatim}[fontsize=\footnotesize]
 angres examples/ANGRES_1_ANG.DAT  \
        examples/ANGRES_RADDIS_1.DAT -a -t -r 0 -s 32 -z 0
\end{Verbatim}
calculates the radiation on a horisontal surface given the angular response
in \file{examples/ANGRES_1_ANG.DAT}. The input used to calculate the radiance file is
given in the start of \file{examples/ANGRES_RADDIS_1.DAT}.

An example of the use of \code{angres} together with \code{uvspec} is given in
\citet[section 4.6]{mayer2005}.
  </lpdoc>
*/

static void print_usage (char* filename) {
  fprintf (stderr, "%s - %s   \n\n", PROGRAM, VERSION);
  fprintf (stderr, "written by  Arve Kylling,\n");
  fprintf (stderr, "            Norwegian Institute for Air Research (NILU)\n");
  fprintf (stderr, "            P.O. Box 100, 2027 Kjeller, Norway\n");
  fprintf (stderr, "Version %s finished %s\n\n", VERSION, DATE);
  fprintf (stderr, "Be aware that this program is a beta version! Please report any\n");
  fprintf (stderr, "kind of error to the author, including the error message and the\n");
  fprintf (stderr, "database being processed when the error occured. Thanks!\n\n");

  fprintf (stderr, "USAGE: %s [options] angres_file raddis_file \n\n", filename);
  fprintf (stderr, "%s will read the two required input files:\n", PROGRAM);
  fprintf (stderr, "  angres_file is a two column file with the first column holding\n");
  fprintf (stderr, "     the angle and the second column the angular response, e.g. a\n");
  fprintf (stderr, "     measured cosine response.\n");
  fprintf (stderr, "  raddis_file holds the radiance distribution as output from uvspec\n");
  fprintf (stderr, "     with the disort solvers for one single wavelength.\n");
  fprintf (stderr, "After reading the two input files the angular response will be tilted\n");
  fprintf (stderr, "and rotated if specified with the -t and -r options respectively. Finally\n");
  fprintf (stderr, "the product of the resulting angular response and radiance distribution\n");
  fprintf (stderr, "field are integrated using Monte Carlo methods to yield the effective\n");
  fprintf (stderr, "response. The integration is done for the diffuse radiation field only. To\n");
  fprintf (stderr, "include the direct contribution the -s and -z options must be set to give\n");
  fprintf (stderr, "the direction of the sun. \n");
  fprintf (stderr, "\n");
  fprintf (stderr, "Output is 3 numbers:\n");
  fprintf (stderr, "   1: The integral of the diffuse radiation field times angular response.\n");
  fprintf (stderr, "   2: Estimated absolute error of the above integral.\n");
  fprintf (stderr, "   3: The integral of the diffuse+direct radiation field times angular response\n");
  fprintf (stderr, "       (requires that -s and -z are specified, otherwise same as 1).\n");
  fprintf (stderr, "\n");
  fprintf (stderr, "The angles in the angres_file must be in radians if not\n");
  fprintf (stderr, "the -a option is used. The raddis_file must contain\n");
  fprintf (stderr, "output from uvspec run for one single wavelength with one of\n");
  fprintf (stderr, "the disort solvers and with phi and umu set.\n");
  fprintf (stderr, "Note that the angles in the angres_file must follow the same\n");
  fprintf (stderr, "conventions as for the disort algorithm. This is different from\n");
  fprintf (stderr, "that typically used when reporting measurements of the angular response.\n");
  fprintf (stderr, "\n");
  fprintf (stderr, " command line options:\n");
  fprintf (stderr, "  -h  show this page\n");
  fprintf (stderr, "  -c  number of random points used for Monte Carlo integration\n");
  fprintf (stderr, "  -i  The diffuse radiation is assumed to be isotropic\n");
  fprintf (stderr, "  -a  angular response angle given in degrees and not cosine of angle\n");
  fprintf (stderr, "  -r  rotation angle in degrees\n");
  fprintf (stderr, "  -t  tilt angle in degrees\n");
  fprintf (stderr, "  -s  solar zenith angle in degrees\n");
  fprintf (stderr, "  -z  solar azimuth angle in degrees\n");
  fprintf (stderr, "  -p  pgm files are made of the angular response before\n");
  fprintf (stderr, "      and after tilt and rotate\n");
}

static int get_options (int     argc,
                        char**  argv,
                        char*   programname,
                        char*   angresfilename,
                        char*   raddisfilename,
                        int*    pgm,
                        int*    dev,
                        int*    ang,
                        size_t* calls,
                        double* sza,
                        double* phi0,
                        double* theta_t,
                        double* phi_r,
                        int*    incdir,
                        int*    isotropic) {
  int c = 0;

  /* default settings */

  /* save name of program */
  strncpy (programname, argv[0], FILENAME_MAX - 1);

  while ((c = getopt (argc, argv, "ahipc:d:s:t:r:z:")) != EOF) {
    switch (c) {
    case 'h': /* help */
      print_usage (programname);
      return (-1);
      break;
    case 'd':
      *dev = atoi (optarg);
      break;
    case 'a':
      *ang = 1;
      break;
    case 'i':
      *isotropic = 1;
      break;
    case 'p':
      *pgm = 1;
      break;
    case 'c':
      *calls = atoi (optarg);
      break;
    case 'r':
      *phi_r = atof (optarg);
      break;
    case 's':
      *sza    = atof (optarg);
      *incdir = 1;
      break;
    case 'z':
      *phi0   = atof (optarg);
      *incdir = 1;
      break;
    case 't':
      *theta_t = atof (optarg);
      break;
    case '?':
      print_usage (programname);
      return (-1);
      break;
    default:
      print_usage (programname);
      return (-1);
    }
  }

  /* check number of remaining command line arguments */
  if (argc - optind != 2) {
    print_usage (programname);
    return -1;
  }

  /* save command line arguments */
  strncpy (angresfilename, argv[optind + 0], FILENAME_MAX - 1);
  strncpy (raddisfilename, argv[optind + 1], FILENAME_MAX - 1);

  return 0; /* if o.k. */
}

double**
tilt_and_rotate (double* aangres, int nphi, double* phi, int numu, double* umu, int pgm, double theta_t, double phi_r, int dev) {
  int status = 0, i = 0, j = 0, k = 0, it = 0, jt = 0, ia = 0, ja = 0, nt;
  /* double umu_t; */
  double   theta = 0, x = 0, y = 0, z = 0, xp = 0, yp = 0, zp = 0, r = 0;
  double   a11, a12, a13, a21, a22, a23, a31, a32, a33;
  double   rn = 0, theta_n = 0, phi_n = 0;
  double **angres = NULL, **angrestilted = NULL;
  double** angresindcnt = NULL;
  double   delphirad    = 0;
  FILE*    pgmfile      = NULL;

  /* allocate memory for the angular response function and the tilted and rotated one */
  status = ASCII_calloc_double (&angres, numu, nphi);
  if (status != 0) {
    fprintf (stderr, "tilt_and_rotate: error allocating memory for angular response function\n");
    /*    return status; */
  }
  status = ASCII_calloc_double (&angrestilted, numu, nphi);
  if (status != 0) {
    fprintf (stderr, "tilt_and_rotate: error allocating memory for angular response function\n");
    /*    return status; */
  }

  status = ASCII_calloc_double (&angresindcnt, numu, nphi);
  if (status != 0) {
    fprintf (stderr, "tilt_and_rotate: error allocating memory for angular response function\n");
    /*    return status; */
  }

  /* The read angular response is assumed to have no azimuth dependence, */
  /* hence it is the same for all phi                                    */
  for (i = 0; i < numu; i++)
    for (j = 0; j < nphi; j++)
      angres[i][j] = aangres[i];

  if (pgm) {
    if ((pgmfile = fopen ("angres_orginal.pgm", "w")) == NULL) {
      fprintf (stderr, "Error opening angres_orginal.pgm for writing\n");
      return NULL;
    }

    fprintf (pgmfile, "P2\n");
    fprintf (pgmfile, "%d %d\n", nphi, numu);
    fprintf (pgmfile, "256\n");
    for (i = 0; i < numu; i++) {
      for (j = 0; j < nphi; j++) {
        k = (int)(angres[i][j] * 256);
        fprintf (pgmfile, "%d\n", k);
      }
    }
    fclose (pgmfile);
  }

  if (dev == 1)
    fprintf (stdout, "Tilt: %f Rotate: %f\n", theta_t, phi_r);
  /* commented out by RB, because was not used but caused compiler warnings */
  /* umu_t = cos(theta_t*PI/180.0); */

  /* Rotation matrix. */
  /* Eq 4-47., p. 147, Goldstein, Classical Mechanics. psi=0 for our purposes */

  /*  a11 =  cos(phi_r*PI/180.0);                      */
  /*  a12 = -cos(theta_t*PI/180.0)*sin(phi_r*PI/180.0); */
  /*  a13 =  sin(theta_t*PI/180.0)*sin(phi_r*PI/180.0); */
  /*  a21 =  sin(phi_r*PI/180.0);                      */
  /*  a22 =  cos(theta_t*PI/180.0)*cos(phi_r*PI/180.0); */
  /*  a23 = -sin(theta_t*PI/180.0)*cos(phi_r*PI/180.0); */
  /*  a31 = 0;                                        */
  /*  a32 =  sin(theta_t*PI/180.0);                    */
  /*  a33 =  cos(theta_t*PI/180.0);                    */

  /* First only tilt */
  a11 = 1;
  a12 = 0;
  a13 = 0;
  a21 = 0;
  a22 = cos (theta_t * PI / 180.0);
  a23 = -sin (theta_t * PI / 180.0);
  a31 = 0;
  a32 = sin (theta_t * PI / 180.0);
  a33 = cos (theta_t * PI / 180.0);

  if (dev == 1) {
    printf ("a11: %7.3f, a12: %7.3f a13: %7.3f\n", a11, a12, a13);
    printf ("a21: %7.3f, a22: %7.3f a23: %7.3f\n", a21, a22, a23);
    printf ("a31: %7.3f, a32: %7.3f a33: %7.3f\n", a31, a32, a33);
  }
  if (dev == 1)
    fprintf (stdout, "Tilt: %f Rotate: %f\n", theta_t, phi_r);

  /* First tilt the angular response */
  for (i = 0; i < numu; i++) {
    theta = acos (umu[i]);
    for (j = 0; j < nphi; j++) {
      rn = 1;
      xp = rn * cos (phi[j] * PI / 180) * sin (theta);
      yp = rn * sin (phi[j] * PI / 180) * sin (theta);
      zp = rn * cos (theta);
      if (dev == 1) {
        printf ("\nxp:  %7.3f, yp:  %7.3f zp:  %7.3f theta: %7.4f phi: %5.1f r: %6.3f umu: %f\n",
                xp,
                yp,
                zp,
                theta,
                phi[j],
                r,
                umu[i]);
      }

      if (dev == 1)
        fprintf (stdout, "Tilt: %f Rotate: %f\n", theta_t, phi_r);

      x = a11 * xp + a12 * yp + a13 * zp;
      y = a21 * xp + a22 * yp + a23 * zp;
      z = a31 * xp + a32 * yp + a33 * zp;

      rn = sqrt (x * x + y * y + z * z);
      if (rn > 0)
        theta_n = acos (z / rn);
      else
        theta_n = 0;

      if (rn > 0) {
        phi_n = acos (x / (rn * sin (theta_n)));
      } else {
        phi_n = 0;
      }

      if (dev == 1) {
        printf ("x: %7.3f, y: %7.3f z: %7.3f theta: %7.4f phi: %5.1f\n", x, y, z, theta * 180 / PI, phi[j]);
        printf ("rn: %7.3f, theta_n: %7.4f phi_n: %5.1f phi_r: %f\n", rn, theta_n * 180 / PI, phi_n * 180 / PI, phi_r);
      }

      if (dev == 1)
        fprintf (stdout, "Tilt: %f Rotate: %f\n", theta_t, phi_r);

      it = 0;
      ia = 1;

      while ((theta_n * 180 / PI - acos (umu[ia++]) * 180 / PI) < 0 && ia < numu)
        ;
      it = ia - 1; /* This one is fun:-) */

      /* Make sure we get the closest grid point */
      if (fabs (acos (umu[it]) * 180 / PI - theta_n * 180 / PI) > fabs (acos (umu[it - 1]) * 180 / PI - theta_n * 180 / PI))
        it = it - 1;

      jt        = 0;
      ja        = 0;
      delphirad = fabs (phi[1] - phi[0]) * PI / 180;
      while (fabs (phi_n - phi[ja++] * PI / 180) > delphirad) {
        if (dev == 1) {
          printf ("%d %d %d %f %f %f %f\n",
                  j,
                  ja,
                  jt,
                  phi_n,
                  phi[ja - 1] * PI / 180,
                  fabs (phi_n - phi[ja - 1] * PI / 180),
                  delphirad);
        }
      }
      jt                 = ja - 1;
      angrestilted[i][j] = angres[it][jt];
    }
  }

  /* Now we rotate */
  nt = (phi_r * PI / 180) / delphirad;
  if (jt >= nphi)
    jt -= nphi;
  for (i = 0; i < numu; i++) {
    theta = acos (umu[i]);
    for (j = 0; j < nphi; j++) {
      jt = j + nt;
      if (jt >= nphi)
        jt -= nphi;
      if (dev == 1 || dev == 10)
        fprintf (stdout,
                 "%3d %3d nt: %d %3d jt: %3d %3d, nphi: %d, phi_r: %5.1f %3.1f\n",
                 i,
                 j,
                 nt,
                 it,
                 jt,
                 ja - 1,
                 nphi,
                 phi_r,
                 angres[it][jt]);

      angres[i][j] = angrestilted[i][jt];
      if (dev == 1) {
        printf ("angrestilted: %7.4f %7.4f %7.4f\n", acos (umu[it]) * 180 / PI, phi[jt], angrestilted[i][j]);
      }
    }
  }

  if (pgm) {

    if ((pgmfile = fopen ("angres_tilted.pgm", "w")) == NULL) {
      fprintf (stderr, "Error opening angres_tilted.pgm for writing\n");
      return NULL;
    }

    fprintf (pgmfile, "P2\n");
    fprintf (pgmfile, "%d %d\n", nphi, numu);
    fprintf (pgmfile, "256\n");
    for (i = 0; i < numu; i++) {
      for (j = 0; j < nphi; j++) {
        k = (int)(angres[i][j] * 256);
        fprintf (pgmfile, "%d\n", k);
      }
    }
    fclose (pgmfile);
  }

  return angres;
}

double linpol (double x1, double x2, double y1, double y2, double x) {
  /* Linearly interpolate between two points to get wanted y value for x. */
  double y;
  double a = 0, b = 0;

  if (x2 > 0 && x1 > 0 && fabs (x2 - x1) < 0.0000001) {
    y = y1;
  } else {
    a = (y2 - y1) / (x2 - x1);
    b = y1 - a * x1;
    y = a * x + b;
  }
  return y;
}

double mc_func (double* k, size_t dim, void* params) {
  int     i1 = 0, i2 = 0, j1 = 0, j2 = 0;
  double  p1, p2, p3;
  double  umu = 0, phi = 0;
  mcinput lmcinp;

  lmcinp = *(mcinput*)params;

  umu = k[0];
  phi = k[1];
  i2  = 0;
  while (umu > lmcinp.umu[i2++])
    ;
  i1 = i2 - 1;
  if (i2 == 0) {
    i2 = 1;
    i1 = 0;
  }
  if (i2 >= lmcinp.numu) {
    i2 = lmcinp.numu - 1;
    i1 = i2 - 1;
  }

  j2 = 0;
  while (phi > lmcinp.phi[j2++])
    ;
  j1 = j2 - 1;
  if (j2 == 0) {
    j2 = 1;
    j1 = 0;
  }
  if (j2 >= lmcinp.nphi) {
    j2 = lmcinp.nphi - 1;
    j1 = j2 - 1;
  }

  p1 = linpol (lmcinp.phi[j1], lmcinp.phi[j2], lmcinp.prod[i1][j1], lmcinp.prod[i1][j2], phi);
  p2 = linpol (lmcinp.phi[j1], lmcinp.phi[j2], lmcinp.prod[i2][j1], lmcinp.prod[i2][j2], phi);

  p3 = linpol (lmcinp.umu[i1], lmcinp.umu[i2], p1, p2, umu);

  return p3;
}

double mc_funciso (double* k, size_t dim, void* params) {
  int     i1 = 0, i2 = 0, j1 = 0, j2 = 0;
  double  p1, p2, p3;
  double  umu = 0, phi = 0;
  mcinput lmcinp;

  lmcinp = *(mcinput*)params;

  umu = k[0];
  phi = k[1];
  i2  = 0;
  while (umu > lmcinp.umu[i2++])
    ;
  i1 = i2 - 1;
  if (i2 == 0) {
    i2 = 1;
    i1 = 0;
  }
  if (i2 >= lmcinp.numu) {
    i2 = lmcinp.numu - 1;
    i1 = i2 - 1;
  }

  j2 = 0;
  while (phi > lmcinp.phi[j2++])
    ;
  j1 = j2 - 1;
  if (j2 == 0) {
    j2 = 1;
    j1 = 0;
  }
  if (j2 >= lmcinp.nphi) {
    j2 = lmcinp.nphi - 1;
    j1 = j2 - 1;
  }

  p1 = linpol (lmcinp.phi[j1], lmcinp.phi[j2], lmcinp.prod[i1][j1], lmcinp.prod[i1][j2], phi);
  p2 = linpol (lmcinp.phi[j1], lmcinp.phi[j2], lmcinp.prod[i2][j1], lmcinp.prod[i2][j2], phi);

  p3 = linpol (lmcinp.umu[i1], lmcinp.umu[i2], p1, p2, umu);

  return p3;
}

int main (int argc, char** argv) {
  int    i = 0, j = 0, k = 0, status = 0, pgm = 0, dev = 0, ang = 0, incdir = 0, isotropic = 0;
  int    ia = 0, it = 0, ja = 0, jt = 0;
  double delphirad = 0, dir = 0;

  char programname[FILENAME_MAX]    = "";
  char angresfilename[FILENAME_MAX] = ""; /* File holding angular response      */
  char raddisfilename[FILENAME_MAX] = ""; /* File holding radiance distribution */

  double *phi = NULL, *rphi = NULL, *aumu = NULL, *rumu = NULL, *aangres = NULL;
  double  theta_t = 0, phi_r = 0, sza = 0, phi0 = 0, max = 0;
  double  dphi = 0, dtheta = 0, tmpprod = 0;
  double  uavgso;
  /* commented out by RB, because was not used but caused compiler warnings */
  /* double rfldir, rfldn, flup, uavgso, uavgdn, uavgup; */

  int arows = 0, amin_columns = 0, amax_columns = 0; /* Angular response file      */
  int rrows = 0, rmin_columns = 0, rmax_columns = 0; /* Radiance distribution file */
  int nphi = 0, numu = 0;

  mcinput mcinp;
  /* void *pmcinp; */
  mcinput mcinpiso;
  /* void *pmcinpiso; */

  double **angres = NULL, **avalue = NULL, **rvalue = NULL, **raddis = NULL, **prod = NULL;

  FILE* pgmfile = NULL;

  /* Monte Carlo integration stuff */
  double res = 0, err = 0;

  double xl[DIM_ANG_INT];
  double xu[DIM_ANG_INT];

  /*  size_t calls = 10; */
  size_t calls = 5000000;

#if HAVE_LIBGSL
  const gsl_rng_type* T;
  const gsl_rng_type* Tiso;
  gsl_rng*            r;
  gsl_rng*            riso;

  gsl_monte_function G    = {&mc_func, DIM_ANG_INT, &mcinp};
  gsl_monte_function Giso = {&mc_funciso, DIM_ANG_INT, &mcinpiso};

  gsl_monte_plain_state* s    = gsl_monte_plain_alloc (DIM_ANG_INT);
  gsl_monte_plain_state* siso = gsl_monte_plain_alloc (DIM_ANG_INT);
#endif
  /* commented out by RB, because was not used but caused compiler warnings */
  /* pmcinp    = &mcinp; */
  /* pmcinpiso = &mcinpiso; */

  /* get command line options  */
  status = get_options (argc,
                        argv,
                        programname,
                        angresfilename,
                        raddisfilename,
                        &pgm,
                        &dev,
                        &ang,
                        &calls,
                        &sza,
                        &phi0,
                        &theta_t,
                        &phi_r,
                        &incdir,
                        &isotropic);

  if (status != 0)
    return status;

#if !HAVE_LIBGSL
  fprintf (stderr, "%s was built without the gsl-library. Thus NO SUPPORT\n", programname);
  fprintf (stderr, "for integration of product of angular response and radiance field.\n");
#endif

  /* read angular response file  */
  status = ASCII_file2double (angresfilename, &arows, &amax_columns, &amin_columns, &avalue);

  if (status != 0) {
    fprintf (stderr, "error %d reading input file %s\n", status, angresfilename);
    return status;
  }

  if (amin_columns < 2) {
    fprintf (stderr, "error, input file must have at least two columns\n");
    return -1;
  }

  if (amin_columns != amax_columns) {
    fprintf (stderr, " WARNING! %s is not a rectangular matrix; only the first\n", angresfilename);
    fprintf (stderr, " %d columns will be used for the analysis\n", amin_columns);
  }
  numu = arows;

  /* Read radiance field file  */
  status = ASCII_file2double (raddisfilename, &rrows, &rmax_columns, &rmin_columns, &rvalue);

  if (status != 0) {
    fprintf (stderr, "error %d reading input file %s\n", status, raddisfilename);
    return status;
  }

  /* Get umu and phi from radiance field  */
  rphi = ASCII_row (rvalue, rmax_columns, 1);
  j    = 0;
  for (i = 0; i < rmax_columns; i++) {
    if (rphi[i] >= 0) {
      nphi++;
    }
  }
  phi = (double*)calloc (nphi, sizeof (double));
  j   = 0;
  for (i = 0; i < nphi; i++) {
    if (rphi[i] != NAN) { /* ??? is that correct? Not even NAN == NAN ??? */
      phi[j++] = rphi[i];
    }
  }
  rumu = ASCII_column (rvalue, rrows, 0);
  for (i = 0; i < rrows - 2; i++) {
    rumu[i] = rumu[i + 2];
  } /* Shift to skip two first lines */
  rumu[rrows - 2] = NAN;
  rumu[rrows - 1] = NAN;

  /* allocate memory for the radiance field  */
  status = ASCII_calloc_double (&raddis, numu, nphi);
  if (status != 0) {
    fprintf (stderr, "angres: allocating memory for produce\n");
    return status;
  }
  for (i = 0; i < numu; i++) {
    for (j = 0; j < nphi; j++) {
      raddis[i][j] = rvalue[i + 2][j + 2];
    }
  }
  /* commented out by RB, because was not used but caused compiler warnings */
  /* rfldir = rvalue[0][1]; */
  /* rfldn   = rvalue[0][2]; */
  /* flup   = rvalue[0][3]; */
  uavgso = rvalue[0][4];
  /* uavgdn = rvalue[0][5]; */
  /* uavgup = rvalue[0][6]; */

  aumu = ASCII_column (avalue, arows, 0);
  if (ang)
    for (i = 0; i < numu; i++)
      aumu[i] = cos (aumu[i] * PI / 180);

  aangres = ASCII_column (avalue, arows, 1);

  /* compare umu with similar from angular response file, differences should be small,  */
  /* otherwise interpolate angular response to radiance field.                          */
  for (i = 0; i < numu; i++)
    if (fabs (aumu[i] - rumu[i]) > 0.0001)
      fprintf (stderr, "umu %f %f %f\n", aumu[i], rumu[i], aumu[i] - rumu[i]);

  /* Make diffuse radiation field isotropic */
  if (isotropic) {
    /* Integrate (angular response)*radiance over all angles */

    /* Upper and lower integration bounds for each dimension */
    xl[0] = aumu[0];
    xu[0] = aumu[numu - 1];
    xl[1] = phi[0] * PI / 180;
    xu[1] = phi[nphi - 1] * PI / 180;

    mcinpiso.nphi = nphi;
    mcinpiso.numu = numu;
    mcinpiso.phi  = (double*)calloc (nphi, sizeof (double));
    mcinpiso.umu  = (double*)calloc (numu, sizeof (double));
    status        = ASCII_calloc_double (&mcinpiso.prod, numu, nphi);
    for (i = 0; i < nphi; i++)
      mcinpiso.phi[i] = phi[i] * PI / 180;
    for (i = 0; i < numu; i++)
      mcinpiso.umu[i] = aumu[i];
    for (i = 0; i < numu; i++) {
      for (j = 0; j < nphi; j++) {
        mcinpiso.prod[i][j] = raddis[i][j];
      }
    }

#if HAVE_LIBGSL
    gsl_rng_env_setup();

    Tiso = gsl_rng_default;
    riso = gsl_rng_alloc (Tiso);

    gsl_monte_plain_integrate (&Giso, xl, xu, DIM_ANG_INT, calls, riso, siso, &res, &err);
    gsl_monte_plain_free (siso);
#endif

    dphi    = phi[1] - phi[0];
    dtheta  = fabs (acos (aumu[1]) * 180 / PI - acos (aumu[0]) * 180 / PI);
    tmpprod = dtheta * dphi;

    for (i = 0; i < numu; i++) {
      for (j = 0; j < nphi; j++) {
        raddis[i][j] = res / tmpprod;
      }
    }
  }

  /* Tilt and rotate angular response */
  angres = tilt_and_rotate (aangres, nphi, phi, numu, aumu, pgm, theta_t, phi_r, dev);
  if (angres == NULL) {
    fprintf (stderr, "Error calling tilt_and_rotate()\n");
    return -1;
  }

  /* Multiply angular response and radiance field */
  /* allocate memory for the product              */

  status = ASCII_calloc_double (&prod, numu, nphi);
  if (status != 0) {
    fprintf (stderr, "angres: error allocating memory for product\n");
    return status;
  }
  for (i = 0; i < numu; i++) {
    for (j = 0; j < nphi; j++) {
      prod[i][j] = angres[i][j] * raddis[i][j];
    }
  }

  if (pgm) {

    if ((pgmfile = fopen ("prod.pgm", "w")) == NULL) {
      fprintf (stderr, "Error opening prod.pgm for writing\n");
      return -1;
    }

    fprintf (pgmfile, "P2\n");
    fprintf (pgmfile, "%d %d\n", nphi, numu);
    fprintf (pgmfile, "256\n");
    max = -9999;
    for (i = 0; i < numu; i++)
      for (j = 0; j < nphi; j++)
        if (prod[i][j] > max)
          max = prod[i][j];

    for (i = 0; i < numu; i++)
      for (j = 0; j < nphi; j++) {
        k = (int)(prod[i][j] * 256 / max);
        fprintf (pgmfile, "%d\n", k);
      }

    fclose (pgmfile);

    if ((pgmfile = fopen ("raddis.pgm", "w")) == NULL) {
      fprintf (stderr, "Error opening raddis.pgm for writing\n");
      return -1;
    }

    fprintf (pgmfile, "P2\n");
    fprintf (pgmfile, "%d %d\n", nphi, numu);
    fprintf (pgmfile, "256\n");
    max = -9999;
    for (i = 0; i < numu; i++)
      for (j = 0; j < nphi; j++)
        if (raddis[i][j] > max)
          max = raddis[i][j];

    for (i = 0; i < numu; i++)
      for (j = 0; j < nphi; j++) {
        k = (int)(raddis[i][j] * 256 / max);
        fprintf (pgmfile, "%d\n", k);
      }

    fclose (pgmfile);
  }

  /* Find angular response for direct beam contribution */
  it = 0;
  ia = 0;

  while (((180 - sza) - acos (aumu[ia++]) * 180 / PI) < 0 && ia < numu)
    ;

  it = ia - 1; /* This one is fun:-) */

  /* Make sure we get the closest grid point */
  if (fabs (acos (aumu[it]) * 180 / PI - (180 - sza)) > fabs (acos (aumu[it - 1]) * 180 / PI - (180 - sza)))
    it = it - 1;

  jt        = 0;
  ja        = 0;
  delphirad = fabs (phi[1] - phi[0]) * PI / 180;
  while (fabs (phi0 - phi[ja++]) * PI / 180 > delphirad)
    ;

  jt = ja - 1;

  if (incdir) {
    dir = angres[it][jt] * uavgso * 4 * PI;
  } else {
    dir = 0;
  }

  ASCII_free_double (avalue, arows);
  ASCII_free_double (rvalue, rrows);
  ASCII_free_double (angres, numu);

  /* Integrate (angular response)*radiance over all angles */

  /* Upper and lower integration bounds for each dimension */
  xl[0] = aumu[0];
  xu[0] = aumu[numu - 1];
  xl[1] = phi[0] * PI / 180;
  xu[1] = phi[nphi - 1] * PI / 180;

  mcinp.nphi = nphi;
  mcinp.numu = numu;
  mcinp.phi  = (double*)calloc (nphi, sizeof (double));
  mcinp.umu  = (double*)calloc (numu, sizeof (double));
  status     = ASCII_calloc_double (&mcinp.prod, numu, nphi);
  for (i = 0; i < nphi; i++)
    mcinp.phi[i] = phi[i] * PI / 180;
  for (i = 0; i < numu; i++)
    mcinp.umu[i] = aumu[i];
  for (i = 0; i < numu; i++) {
    for (j = 0; j < nphi; j++) {
      mcinp.prod[i][j] = prod[i][j];
    }
  }

#if HAVE_LIBGSL
  gsl_rng_env_setup();

  T = gsl_rng_default;
  r = gsl_rng_alloc (T);

  gsl_monte_plain_integrate (&G, xl, xu, DIM_ANG_INT, calls, r, s, &res, &err);
  gsl_monte_plain_free (s);
#endif
  /*  fprintf(stdout,"Result: %f,  error: %f\n", res, err); */
  fprintf (stdout, "%f %f %f\n", res, err, res + dir);

  ASCII_free_double (raddis, numu);

  return 0; /* if o.k. */
}