MP-Gadget  5.0.1.dev1-76bc7d4726-dirty
omega_nu_single.c
Go to the documentation of this file.
1 #include "omega_nu_single.h"
2 
3 #include <math.h>
4 #include <gsl/gsl_integration.h>
5 #include <gsl/gsl_errno.h>
6 #include <string.h>
7 #include "physconst.h"
8 #include "utils/mymalloc.h"
9 #include "utils/endrun.h"
10 
11 #define HBAR 6.582119e-16 /*hbar in units of eV s*/
12 #define STEFAN_BOLTZMANN 5.670373e-5
13 /*Size of matter density tables*/
14 #define NRHOTAB 200
16 #define FLOAT_ACC 1e-6
18 #define GSL_VAL 200
19 
20 void init_omega_nu(_omega_nu * omnu, const double MNu[], const double a0, const double HubbleParam, const double tcmb0)
21 {
22  int mi;
23  /*Explicitly disable hybrid neutrinos*/
24  omnu->hybnu.enabled=0;
25  /*CMB temperature*/
26  omnu->tcmb0 = tcmb0;
27  /*Neutrino temperature times k_B*/
28  omnu->kBtnu = BOLEVK * TNUCMB * tcmb0;
29  /*Store conversion between rho and omega*/
30  omnu->rhocrit = (3 * HUBBLE * HubbleParam * HUBBLE * HubbleParam)/ (8 * M_PI * GRAVITY);
31  /*First compute which neutrinos are degenerate with each other*/
32  for(mi=0; mi<NUSPECIES; mi++){
33  int mmi;
34  omnu->nu_degeneracies[mi]=0;
35  for(mmi=0; mmi<mi; mmi++){
36  if(fabs(MNu[mi] -MNu[mmi]) < FLOAT_ACC){
37  omnu->nu_degeneracies[mmi]+=1;
38  break;
39  }
40  }
41  if(mmi==mi) {
42  omnu->nu_degeneracies[mi]=1;
43  }
44  }
45  /*Now allocate a table for the species we want*/
46  for(mi=0; mi<NUSPECIES; mi++){
47  if(omnu->nu_degeneracies[mi]) {
48  rho_nu_init(&omnu->RhoNuTab[mi], a0, MNu[mi], omnu->kBtnu);
49  }
50  else
51  omnu->RhoNuTab[mi].loga = 0;
52  }
53 }
54 
55 /* Return the total matter density in neutrinos.
56  * rho_nu and friends are not externally callable*/
57 double get_omega_nu(const _omega_nu * const omnu, const double a)
58 {
59  double rhonu=0;
60  int mi;
61  for(mi=0; mi<NUSPECIES; mi++) {
62  if(omnu->nu_degeneracies[mi] > 0){
63  rhonu += omnu->nu_degeneracies[mi] * rho_nu(&omnu->RhoNuTab[mi], a, omnu->kBtnu);
64  }
65  }
66  return rhonu/omnu->rhocrit;
67 }
68 
69 
70 /* Return the total matter density in neutrinos, excluding that in active particles.*/
71 double get_omega_nu_nopart(const _omega_nu * const omnu, const double a)
72 {
73  double omega_nu = get_omega_nu(omnu, a);
74  double part_nu = get_omega_nu(omnu, 1) * particle_nu_fraction(&omnu->hybnu, a, 0) / (a*a*a);
75  return omega_nu - part_nu;
76 }
77 
78 /*Return the photon density*/
79 double get_omegag(const _omega_nu * const omnu, const double a)
80 {
81  const double omegag = 4*STEFAN_BOLTZMANN/(LIGHTCGS*LIGHTCGS*LIGHTCGS)*pow(omnu->tcmb0,4)/omnu->rhocrit;
82  return omegag/pow(a,4);
83 }
84 
87 #define NU_SW 100
88 
89 /*Note q carries units of eV/c. kT/c has units of eV/c.
90  * M_nu has units of eV Here c=1. */
91 double rho_nu_int(double q, void * params)
92 {
93  double amnu = *((double *)params);
94  double kT = *((double *)params+1);
95  double epsilon = sqrt(q*q+amnu*amnu);
96  double f0 = 1./(exp(q/kT)+1);
97  return q*q*epsilon*f0;
98 }
99 
100 /*Get the conversion factor to go from (eV/c)^4 to g/cm^3
101  * for a **single** neutrino species. */
103 {
104  /*q has units of eV/c, so rho_nu now has units of (eV/c)^4*/
105  double convert=4*M_PI*2; /* The factor of two is for antineutrinos*/
106  /*rho_nu_val now has units of eV^4*/
107  /*To get units of density, divide by (c*hbar)**3 in eV s and cm/s */
108  const double chbar=1./(2*M_PI*LIGHTCGS*HBAR);
109  convert*=(chbar*chbar*chbar);
110  /*Now has units of (eV)/(cm^3)*/
111  /* 1 eV = 1.60217646 × 10-12 g cm^2 s^(-2) */
112  /* So 1eV/c^2 = 1.7826909604927859e-33 g*/
113  /*So this is rho_nu_val in g /cm^3*/
114  convert*=(1.60217646e-12/LIGHTCGS/LIGHTCGS);
115  return convert;
116 }
117 
118 /*Seed a pre-computed table of rho_nu values for speed*/
119 void rho_nu_init(_rho_nu_single * const rho_nu_tab, double a0, const double mnu, const double kBtnu)
120 {
121  int i;
122  double abserr;
123  /* Tabulate to 1e-3, unless earlier requested.
124  * Need early times for growth function.*/
125  if(a0 > 1e-3)
126  a0 = 1e-3;
127  /* Do not need a table when relativistic*/
128  if(a0 * mnu < 1e-6 * kBtnu)
129  a0 = 1e-6 * kBtnu / mnu;
130  /*Make the table over a slightly wider range than requested, in case there is roundoff error*/
131  const double logA0=log(a0)-log(1.2);
132  const double logaf=log(NU_SW*kBtnu/mnu)+log(1.2);
133  gsl_function F;
134  F.function = &rho_nu_int;
135  /*Initialise constants*/
136  rho_nu_tab->mnu = mnu;
137  /*Shortcircuit if we don't need to do the integration*/
138  if(mnu < 1e-6*kBtnu || logaf < logA0)
139  return;
140 
141  /*Allocate memory for arrays*/
142  rho_nu_tab->loga = (double *) mymalloc("rho_nu_table",2*NRHOTAB*sizeof(double));
143  rho_nu_tab->rhonu = rho_nu_tab->loga+NRHOTAB;
144  rho_nu_tab->acc = gsl_interp_accel_alloc();
145  rho_nu_tab->interp=gsl_interp_alloc(gsl_interp_cspline,NRHOTAB);
146  if(!rho_nu_tab->interp || !rho_nu_tab->acc || !rho_nu_tab->loga)
147  endrun(2035,"Could not initialise tables for neutrino matter density\n");
148 
149  gsl_integration_workspace * w = gsl_integration_workspace_alloc (GSL_VAL);
150  for(i=0; i< NRHOTAB; i++){
151  double param[2];
152  rho_nu_tab->loga[i]=logA0+i*(logaf-logA0)/(NRHOTAB-1);
153  param[0]=mnu*exp(rho_nu_tab->loga[i]);
154  param[1] = kBtnu;
155  F.params = &param;
156  gsl_integration_qag (&F, 0, 500*kBtnu,0 , 1e-9,GSL_VAL,6,w,&(rho_nu_tab->rhonu[i]), &abserr);
157  rho_nu_tab->rhonu[i]=rho_nu_tab->rhonu[i]/pow(exp(rho_nu_tab->loga[i]),4)*get_rho_nu_conversion();
158  }
159  gsl_integration_workspace_free (w);
160  gsl_interp_init(rho_nu_tab->interp,rho_nu_tab->loga,rho_nu_tab->rhonu,NRHOTAB);
161  return;
162 }
163 
164 /*Heavily non-relativistic*/
165 static inline double non_rel_rho_nu(const double a, const double kT, const double amnu, const double kTamnu2)
166 {
167  /*The constants are Riemann zetas: 3,5,7 respectively*/
168  return amnu*(kT*kT*kT)/(a*a*a*a)*(1.5*1.202056903159594+kTamnu2*45./4.*1.0369277551433704+2835./32.*kTamnu2*kTamnu2*1.0083492773819229+80325/32.*kTamnu2*kTamnu2*kTamnu2*1.0020083928260826)*get_rho_nu_conversion();
169 }
170 
171 /*Heavily relativistic: we could be more accurate here,
172  * but in practice this will only be called for massless neutrinos, so don't bother.*/
173 static inline double rel_rho_nu(const double a, const double kT)
174 {
175  return 7*pow(M_PI*kT/a,4)/120.*get_rho_nu_conversion();
176 }
177 
178 /*Finds the physical density in neutrinos for a single neutrino species
179  1.878 82(24) x 10-29 h02 g/cm3 = 1.053 94(13) x 104 h02 eV/cm3*/
180 double rho_nu(const _rho_nu_single * rho_nu_tab, const double a, const double kT)
181 {
182  double rho_nu_val;
183  double amnu=a*rho_nu_tab->mnu;
184  const double kTamnu2=(kT*kT/amnu/amnu);
185  /*Do it analytically if we are in a regime where we can
186  * The next term is 141682 (kT/amnu)^8.
187  * At kT/amnu = 8, higher terms are larger and the series stops converging.
188  * Don't go lower than 50 here. */
189  if(NU_SW*NU_SW*kTamnu2 < 1){
190  /*Heavily non-relativistic*/
191  rho_nu_val = non_rel_rho_nu(a, kT, amnu, kTamnu2);
192  }
193  else if(amnu < 1e-6*kT){
194  /*Heavily relativistic*/
195  rho_nu_val=rel_rho_nu(a, kT);
196  }
197  else{
198  const double loga = log(a);
199  /* Deal with early time case. In practice no need to be very accurate
200  * so assume relativistic.*/
201  if (!rho_nu_tab->loga || loga < rho_nu_tab->loga[0])
202  rho_nu_val = rel_rho_nu(a,kT);
203  else
204  rho_nu_val=gsl_interp_eval(rho_nu_tab->interp,rho_nu_tab->loga,rho_nu_tab->rhonu,loga,rho_nu_tab->acc);
205  }
206  return rho_nu_val;
207 }
208 
209 /*The following function definitions are only used for hybrid neutrinos*/
210 
211 /*Fermi-Dirac kernel for below*/
212 double fermi_dirac_kernel(double x, void * params)
213 {
214  return x * x / (exp(x) + 1);
215 }
216 
217 /* Fraction of neutrinos not followed analytically
218  * This is integral f_0(q) q^2 dq between 0 and qc to compute the fraction of OmegaNu which is in particles.*/
219 double nufrac_low(const double qc)
220 {
221  /*These functions are so smooth that we don't need much space*/
222  gsl_integration_workspace * w = gsl_integration_workspace_alloc (100);
223  double abserr;
224  gsl_function F;
225  F.function = &fermi_dirac_kernel;
226  F.params = NULL;
227  double total_fd;
228  gsl_integration_qag (&F, 0, qc, 0, 1e-6,100,GSL_INTEG_GAUSS61, w,&(total_fd), &abserr);
229  /*divided by the total F-D probability (which is 3 Zeta(3)/2 ~ 1.8 if MAX_FERMI_DIRAC is large enough*/
230  total_fd /= 1.5*1.202056903159594;
231  gsl_integration_workspace_free (w);
232  return total_fd;
233 }
234 
235 void init_hybrid_nu(_hybrid_nu * const hybnu, const double mnu[], const double vcrit, const double light, const double nu_crit_time, const double kBtnu)
236 {
237  hybnu->enabled=1;
238  int i;
239  hybnu->nu_crit_time = nu_crit_time;
240  hybnu->vcrit = vcrit / light;
241  for(i=0; i< NUSPECIES; i++) {
242  const double qc = mnu[i] * vcrit / light / kBtnu;
243  hybnu->nufrac_low[i] = nufrac_low(qc);
244  }
245 }
246 
247 /* Returns the fraction of neutrinos currently traced by particles.
248  * When neutrinos are fully analytic at early times, returns 0.
249  * Last argument: neutrino species to use.
250  */
251 double particle_nu_fraction(const _hybrid_nu * const hybnu, const double a, const int i)
252 {
253  /*Return zero if hybrid neutrinos not enabled*/
254  if(!hybnu->enabled)
255  return 0;
256  /*Just use a redshift cut for now. Really we want something more sophisticated,
257  * based on the shot noise and average overdensity.*/
258  if (a > hybnu->nu_crit_time){
259  return hybnu->nufrac_low[i];
260  }
261  return 0;
262 }
263 
264 /*End functions used only for hybrid neutrinos*/
265 
266 /* Return the matter density in neutrino species i.*/
267 double omega_nu_single(const _omega_nu * const omnu, const double a, int i)
268 {
269  /*Deal with case where we want a species degenerate with another one*/
270  if(omnu->nu_degeneracies[i] == 0) {
271  int j;
272  for(j=i; j >=0; j--)
273  if(omnu->nu_degeneracies[j]){
274  i = j;
275  break;
276  }
277  }
278  double omega_nu = rho_nu(&omnu->RhoNuTab[i], a,omnu->kBtnu)/omnu->rhocrit;
279  double omega_part = rho_nu(&omnu->RhoNuTab[i], 1,omnu->kBtnu)/omnu->rhocrit;
280  omega_part *= particle_nu_fraction(&omnu->hybnu, a, i)/(a*a*a);
281  omega_nu -= omega_part;
282  return omega_nu;
283 
284 }
void endrun(int where, const char *fmt,...)
Definition: endrun.c:147
#define mymalloc(name, size)
Definition: mymalloc.h:15
#define NRHOTAB
#define GSL_VAL
void init_hybrid_nu(_hybrid_nu *const hybnu, const double mnu[], const double vcrit, const double light, const double nu_crit_time, const double kBtnu)
void init_omega_nu(_omega_nu *omnu, const double MNu[], const double a0, const double HubbleParam, const double tcmb0)
static double non_rel_rho_nu(const double a, const double kT, const double amnu, const double kTamnu2)
double rho_nu(const _rho_nu_single *rho_nu_tab, const double a, const double kT)
#define FLOAT_ACC
void rho_nu_init(_rho_nu_single *const rho_nu_tab, double a0, const double mnu, const double kBtnu)
#define NU_SW
double omega_nu_single(const _omega_nu *const omnu, const double a, int i)
double get_omegag(const _omega_nu *const omnu, const double a)
double particle_nu_fraction(const _hybrid_nu *const hybnu, const double a, const int i)
static double rel_rho_nu(const double a, const double kT)
double nufrac_low(const double qc)
double get_omega_nu(const _omega_nu *const omnu, const double a)
double fermi_dirac_kernel(double x, void *params)
double get_rho_nu_conversion()
#define STEFAN_BOLTZMANN
#define HBAR
double rho_nu_int(double q, void *params)
double get_omega_nu_nopart(const _omega_nu *const omnu, const double a)
#define TNUCMB
#define NUSPECIES
#define HUBBLE
Definition: physconst.h:25
#define GRAVITY
Definition: physconst.h:5
#define BOLEVK
Definition: physconst.h:13
#define LIGHTCGS
Definition: physconst.h:18
double nu_crit_time
double nufrac_low[NUSPECIES]
double rhocrit
_hybrid_nu hybnu
int nu_degeneracies[NUSPECIES]
_rho_nu_single RhoNuTab[NUSPECIES]
gsl_interp * interp
gsl_interp_accel * acc