Fast Methods for Cosmological Simulations
FastSim serves as a tool for quick N-body simulations in modified gravity.
ccl_neutrinos.c
Go to the documentation of this file.
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <math.h>
4 #include <string.h>
5 
6 #include <gsl/gsl_errno.h>
7 #include <gsl/gsl_spline.h>
8 #include <gsl/gsl_integration.h>
9 #include <gsl/gsl_const_mksa.h>
10 #include <gsl/gsl_roots.h>
11 
12 #include "ccl.h"
13 #include "ccl_params.h"
14 
15 
16 // Global variable to hold the neutrino phase-space spline
17 gsl_spline* nu_spline=NULL;
18 
19 /* ------- ROUTINE: nu_integrand ------
20 INPUTS: x: dimensionless momentum, *r: pointer to a dimensionless mass / temperature
21 TASK: Integrand of phase-space massive neutrino integral
22 */
23 static double nu_integrand(double x, void *r)
24 {
25  double rat=*((double*)(r));
26  return sqrt(x*x+rat*rat)/(exp(x)+1.0)*x*x;
27 }
28 
29 /* ------- ROUTINE: ccl_calculate_nu_phasespace_spline ------
30 TASK: Get the spline of the result of the phase-space integral required for massive neutrinos.
31 */
32 
33 gsl_spline* calculate_nu_phasespace_spline(int *status) {
34  int N=CCL_NU_MNUT_N;
35  double *mnut = ccl_linear_spacing(log(CCL_NU_MNUT_MIN),log(CCL_NU_MNUT_MAX),N);
36  double *y=malloc(sizeof(double)*CCL_NU_MNUT_N);
37  if (y ==NULL) {
38  // Not setting a status_message here because we can't easily pass a cosmology to this function - message printed in ccl_error.c.
39  *status = CCL_ERROR_NU_INT;
40  }
41 
42  // Check whether ccl_splines and ccl_gsl exist; exit gracefully if they
43  // can't be loaded
44  if(ccl_splines==NULL || ccl_gsl==NULL) ccl_cosmology_read_config();
45  if(ccl_splines==NULL || ccl_gsl==NULL) {
47  "ccl_neutrinos.c: Failed to read config file.");
49  return NULL;
50  }
51 
52  int stat=0, gslstatus;
53  gsl_integration_cquad_workspace * workspace = \
54  gsl_integration_cquad_workspace_alloc(ccl_gsl->N_ITERATION);
55  gsl_function F;
56  F.function = &nu_integrand;
57  for (int i=0; i<CCL_NU_MNUT_N; i++) {
58  double mnut_=exp(mnut[i]);
59  F.params = &(mnut_);
60  gslstatus = gsl_integration_cquad(&F, 0, 1000.0,
63  workspace,&y[i], NULL, NULL);
64  if(gslstatus != GSL_SUCCESS) {
65  ccl_raise_gsl_warning(gslstatus, "ccl_neutrinos.c: calculate_nu_phasespace_spline():");
66  stat |= gslstatus;
67  }
68  }
69  gsl_integration_cquad_workspace_free(workspace);
70  double renorm=1./y[0];
71  for (int i=0; i<CCL_NU_MNUT_N; i++)
72  y[i]*=renorm;
73  gsl_spline* spl = gsl_spline_alloc(A_SPLINE_TYPE, CCL_NU_MNUT_N);
74  stat |= gsl_spline_init(spl, mnut, y, CCL_NU_MNUT_N);
75 
76  // Check for errors in creating the spline
77  if (stat) {
78  // Not setting a status_message here because we can't easily pass a cosmology to this function - message printed in ccl_error.c.
79  *status = CCL_ERROR_NU_INT;
80  free(mnut);
81  free(y);
82  gsl_spline_free(spl);
83  }
84  free(mnut);
85  free(y);
86  return spl;
87 }
88 
89 /* ------- ROUTINE: ccl_nu_phasespace_intg ------
90 INPUTS: accel: pointer to an accelerator which will evaluate the neutrino phasespace spline if defined, mnuOT: the dimensionless mass / temperature of a single massive neutrino
91 TASK: Get the value of the phase space integral at mnuOT
92 */
93 
94 double nu_phasespace_intg(gsl_interp_accel* accel, double mnuOT, int* status)
95 {
96  // Check if the global variable for the phasespace spline has been defined yet:
99 
100  double integral_value =0.;
101 
102  // First check the cases where we are in the limits.
103  if (mnuOT<CCL_NU_MNUT_MIN) {
104  return 7./8.;
105  }
106  else if (mnuOT>CCL_NU_MNUT_MAX) {
107  return 0.2776566337*mnuOT;
108  }
109 
110  // Evaluate the spline - this will use the accelerator if it has been defined.
111  int gslstatus = gsl_spline_eval_e(nu_spline, log(mnuOT),accel, &integral_value);
112  if(gslstatus != GSL_SUCCESS) {
113  ccl_raise_gsl_warning(gslstatus, "ccl_neutrinos.c: nu_phasespace_intg():");
114  *status |= gslstatus;
115  }
116  return integral_value*7./8.;
117 }
118 
119 /* -------- ROUTINE: Omeganuh2 ---------
120 INPUTS: a: scale factor, Nnumass: number of massive neutrino species, mnu: total mass in eV of neutrinos, T_CMB: CMB temperature, accel: pointer to an accelerator which will evaluate the neutrino phasespace spline if defined, status: pointer to status integer.
121 TASK: Compute Omeganu * h^2 as a function of time.
122 !! To all practical purposes, Neff is simply N_nu_mass !!
123 */
124 
125 double ccl_Omeganuh2 (double a, int N_nu_mass, double* mnu, double T_CMB, gsl_interp_accel* accel, int* status)
126 {
127  double Tnu, a4, prefix_massless, mnuone, OmNuh2;
128  double Tnu_eff, mnuOT, intval, prefix_massive;
129  double total_mass; // To check if this is the massless or massive case.
130 
131  // First check if N_nu_mass is 0
132  if (N_nu_mass==0) return 0.0;
133 
134  Tnu=T_CMB*pow(4./11.,1./3.);
135  a4=a*a*a*a;
136  // Check if mnu=0. We assume that in the massless case mnu is a pointer to a single element and that element is 0. This should in principle never be called.
137  if (mnu[0] < 0.00017) { // Limit taken from Lesgourges et al. 2012
138  prefix_massless = NU_CONST * Tnu * Tnu * Tnu * Tnu;
139  return N_nu_mass*prefix_massless*7./8./a4;
140  }
141 
142  // And the remaining massive case.
143  // Tnu_eff is used in the massive case because CLASS uses an effective temperature of nonLCDM components to match to mnu / Omeganu =93.14eV. Tnu_eff = T_ncdm * T_CMB = 0.71611 * T_CMB
144  Tnu_eff = Tnu * TNCDM / (pow(4./11.,1./3.));
145 
146  // Define the prefix using the effective temperature (to get mnu / Omega = 93.14 eV) for the massive case:
147  prefix_massive = NU_CONST * Tnu_eff * Tnu_eff * Tnu_eff * Tnu_eff;
148 
149  OmNuh2 = 0.; // Initialize to 0 - we add to this for each massive neutrino species.
150  for(int i=0; i<N_nu_mass; i++){
151  // Get mass over T (mass (eV) / ((kb eV/s/K) Tnu_eff (K))
152  // This returns the density normalized so that we get nuh2 at a=0
153  mnuOT = mnu[i] / (Tnu_eff/a) * (EV_IN_J / (KBOLTZ));
154 
155  // Get the value of the phase-space integral
156  intval=nu_phasespace_intg(accel,mnuOT, status);
157  OmNuh2 = intval*prefix_massive/a4 + OmNuh2;
158  }
159 
160  return OmNuh2;
161 }
162 
163 /* -------- ROUTINE: Omeganuh2_to_Mnu ---------
164 INPUTS: OmNuh2: neutrino mass density today Omeganu * h^2, label: how you want to split up the masses, see ccl_neutrinos.h for options, T_CMB: CMB temperature, accel: pointer to an accelerator which will evaluate the neutrino phasespace spline if defined, status: pointer to status integer.
165 TASK: Given Omeganuh2 today, the method of splitting into masses, and the temperature of the CMB, output a pointer to the array of neutrino masses (may be length 1 if label asks for sum)
166 */
167 
168 double* ccl_nu_masses(double OmNuh2, ccl_neutrino_mass_splits mass_split, double T_CMB, int* status){
169 
170  double sumnu;
171 
172  sumnu = 93.14 * OmNuh2;
173 
174  // Now split the sum up into three masses depending on the label given:
175 
176  if(mass_split==ccl_nu_normal){
177 
178  double *mnu;
179  mnu = malloc(3*sizeof(double));
180 
181  // See CCL note for how we get these expressions for the neutrino masses in normal and inverted hierarchy.
182  mnu[0] = 2./3.* sumnu - 1./6. * pow(-6. * DELTAM12_sq + 12. * DELTAM13_sq_pos + 4. * sumnu*sumnu, 0.5)
183  - 0.25 * DELTAM12_sq / (2./3.* sumnu - 1./6. * pow(-6. * DELTAM12_sq + 12. * DELTAM13_sq_pos + 4. * sumnu*sumnu, 0.5));
184  mnu[1] = 2./3.* sumnu - 1./6. * pow(-6. * DELTAM12_sq + 12. * DELTAM13_sq_pos + 4. * sumnu*sumnu, 0.5)
185  + 0.25 * DELTAM12_sq / (2./3.* sumnu - 1./6. * pow(-6. * DELTAM12_sq + 12. * DELTAM13_sq_pos + 4. * sumnu*sumnu, 0.5));
186  mnu[2] = -1./3. * sumnu + 1./3 * pow(-6. * DELTAM12_sq + 12. * DELTAM13_sq_pos + 4. * sumnu*sumnu, 0.5);
187  if (mnu[0]<0 || mnu[1]<0 || mnu[2]<0){
188  // The user has provided a sum that is below the physical limit.
189  if (sumnu<1e-14){
190  mnu[0] = 0.;
191  mnu[1] = 0.;
192  mnu[2] = 0.;
193  }else{
194  *status = CCL_ERROR_MNU_UNPHYSICAL;
195  }
196  }
197  ccl_check_status_nocosmo(status);
198  return mnu;
199 
200  } else if (mass_split==ccl_nu_inverted){
201 
202  double *mnu;
203  mnu = malloc(3*sizeof(double));
204  mnu[0] = 2./3.* sumnu - 1./6. * pow(-6. * DELTAM12_sq + 12. * DELTAM13_sq_neg + 4. * sumnu * sumnu, 0.5)
205  - 0.25 * DELTAM12_sq / (2./3.* sumnu - 1./6. * pow(-6. * DELTAM12_sq + 12. * DELTAM13_sq_neg + 4. * sumnu * sumnu, 0.5));
206  mnu[1] = 2./3.* sumnu - 1./6. * pow(-6. * DELTAM12_sq + 12. * DELTAM13_sq_neg + 4. * sumnu*sumnu, 0.5)
207  + 0.25 * DELTAM12_sq / (2./3.* sumnu - 1./6. * pow(-6. * DELTAM12_sq + 12. * DELTAM13_sq_neg + 4. * sumnu*sumnu, 0.5));
208  mnu[2] = -1./3. * sumnu + 1./3 * pow(-6. * DELTAM12_sq + 12. * DELTAM13_sq_neg + 4. * sumnu*sumnu, 0.5);
209  if(mnu[0]<0 || mnu[1]<0 || mnu[2]<0){
210  // The user has provided a sum that is below the physical limit.
211  if (sumnu<1e-14){
212  mnu[0] = 0.;
213  mnu[1] = 0.;
214  mnu[2] = 0.;;
215  }else{
216  *status = CCL_ERROR_MNU_UNPHYSICAL;
217  }
218  }
219 
220  ccl_check_status_nocosmo(status);
221  return mnu;
222 
223  } else if (mass_split==ccl_nu_equal){
224  double *mnu;
225  mnu = malloc(3*sizeof(double));
226  mnu[0] = sumnu/3.;
227  mnu[1] = sumnu/3.;
228  mnu[2] = sumnu/3.;
229 
230  return mnu;
231 
232  } else if (mass_split == ccl_nu_sum){
233 
234  double *mnu;
235  mnu = malloc(sizeof(double));
236 
237  mnu[0] = sumnu;
238 
239  return mnu;
240 
241  } else{
242 
243  printf("WARNING: mass option = %d not yet supported\n continuing with normal hierarchy\n", mass_split);
244  double *mnu;
245  mnu = malloc(3*sizeof(double));
246 
247  if (sumnu>0.59){
248  mnu[0] = (sumnu - 0.59) / 3.;
249  mnu[1] = mnu[0] + 0.009;
250  mnu[2] = mnu[0] + 0.05;
251  }else{
252  *status = CCL_ERROR_MNU_UNPHYSICAL;
253  }
254 
255  ccl_check_status_nocosmo(status);
256  return mnu;
257  }
258 }
double INTEGRATION_NU_EPSREL
Definition: ccl_params.h:70
void ccl_raise_gsl_warning(int gslstatus, const char *msg,...)
Definition: ccl_error.c:75
ccl_neutrino_mass_splits
Definition: ccl_neutrinos.h:22
#define CCL_NU_MNUT_MAX
Definition: ccl_neutrinos.h:13
#define DELTAM12_sq
#define CCL_NU_MNUT_N
Definition: ccl_neutrinos.h:15
#define NU_CONST
Definition: ccl_neutrinos.h:18
#define KBOLTZ
Definition: ccl_constants.h:67
#define CCL_ERROR_MISSING_CONFIG_FILE
Definition: ccl_error.h:28
static double nu_integrand(double x, void *r)
Definition: ccl_neutrinos.c:23
#define A_SPLINE_TYPE
Definition: ccl_constants.h:7
double nu_phasespace_intg(gsl_interp_accel *accel, double mnuOT, int *status)
Definition: ccl_neutrinos.c:94
#define TNCDM
ccl_spline_params * ccl_splines
Definition: ccl_core.c:47
CCL_BEGIN_DECLS double * ccl_linear_spacing(double xmin, double xmax, int N)
Definition: ccl_utils.c:14
#define CCL_ERROR_NU_INT
Definition: ccl_error.h:22
double INTEGRATION_NU_EPSABS
Definition: ccl_params.h:71
ccl_gsl_params * ccl_gsl
Definition: ccl_core.c:48
#define EV_IN_J
Definition: ccl_constants.h:90
#define DELTAM13_sq_pos
Grid< NDIM, T > sqrt(Grid< NDIM, T > lhs)
Definition: grid.h:231
static CCL_BEGIN_DECLS double x[111][8]
void ccl_cosmology_read_config(void)
Definition: ccl_core.c:50
size_t N_ITERATION
Definition: ccl_params.h:55
gsl_spline * nu_spline
Definition: ccl_neutrinos.c:17
double * ccl_nu_masses(double OmNuh2, ccl_neutrino_mass_splits mass_split, double T_CMB, int *status)
#define DELTAM13_sq_neg
void ccl_check_status_nocosmo(int *status)
Definition: ccl_error.c:116
#define CCL_NU_MNUT_MIN
Definition: ccl_neutrinos.h:12
#define CCL_ERROR_MNU_UNPHYSICAL
Definition: ccl_error.h:26
float pow(float base, unsigned long int exp)
Definition: precision.hpp:39
gsl_spline * calculate_nu_phasespace_spline(int *status)
Definition: ccl_neutrinos.c:33
void ccl_raise_exception(int err, const char *msg,...)
Definition: ccl_error.c:35
double ccl_Omeganuh2(double a, int N_nu_mass, double *mnu, double T_CMB, gsl_interp_accel *accel, int *status)