|
| 1 | +/* The following routines implement all of the fitting formulae in |
| 2 | +Eisenstein \& Hu (1997) */ |
| 3 | + |
| 4 | +/* There are two sets of routines here. The first set, |
| 5 | +
|
| 6 | + TFfit_hmpc(), TFset_parameters(), and TFfit_onek(), |
| 7 | +
|
| 8 | +calculate the transfer function for an arbitrary CDM+baryon universe using |
| 9 | +the fitting formula in Section 3 of the paper. The second set, |
| 10 | +
|
| 11 | + TFsound_horizon_fit(), TFk_peak(), TFnowiggles(), and TFzerobaryon(), |
| 12 | +
|
| 13 | +calculate other quantities given in Section 4 of the paper. */ |
| 14 | + |
| 15 | +#include <math.h> |
| 16 | +#include <stdio.h> |
| 17 | +#include<stdlib.h> |
| 18 | + |
| 19 | +void TFset_parameters(float omega0hh, float f_baryon, float Tcmb); |
| 20 | +float TFfit_onek(float k, float *tf_baryon, float *tf_cdm); |
| 21 | + |
| 22 | +void TFfit_hmpc(float omega0, float f_baryon, float hubble, float Tcmb, |
| 23 | + int numk, float *k, float *tf_full, float *tf_baryon, float *tf_cdm); |
| 24 | + |
| 25 | +float TFsound_horizon_fit(float omega0, float f_baryon, float hubble); |
| 26 | +float TFk_peak(float omega0, float f_baryon, float hubble); |
| 27 | +float TFnowiggles(float omega0, float f_baryon, float hubble, |
| 28 | + float Tcmb, float k_hmpc); |
| 29 | +float TFzerobaryon(float omega0, float hubble, float Tcmb, float k_hmpc); |
| 30 | + |
| 31 | +/* ------------------------ DRIVER ROUTINE --------------------------- */ |
| 32 | +/* The following is an example of a driver routine you might use. */ |
| 33 | +/* Basically, the driver routine needs to call TFset_parameters() to |
| 34 | +set all the scalar parameters, and then call TFfit_onek() for each |
| 35 | +wavenumber k you desire. */ |
| 36 | + |
| 37 | +/* While the routines use Mpc^-1 units internally, this driver has been |
| 38 | +written to take an array of wavenumbers in units of h Mpc^-1. On the |
| 39 | +other hand, if you want to use Mpc^-1 externally, you can do this by |
| 40 | +altering the variables you pass to the driver: |
| 41 | + omega0 -> omega0*hubble*hubble, hubble -> 1.0 */ |
| 42 | + |
| 43 | +/* INPUT: omega0 -- the matter density (baryons+CDM) in units of critical |
| 44 | + f_baryon -- the ratio of baryon density to matter density |
| 45 | + hubble -- the Hubble constant, in units of 100 km/s/Mpc |
| 46 | + Tcmb -- the CMB temperature in Kelvin. T<=0 uses the COBE value 2.728. |
| 47 | + numk -- the length of the following zero-offset array |
| 48 | + k[] -- the array of wavevectors k[0..numk-1] */ |
| 49 | + |
| 50 | +/* INPUT/OUTPUT: There are three output arrays of transfer functions. |
| 51 | +All are zero-offset and, if used, must have storage [0..numk-1] declared |
| 52 | +in the calling program. However, if you substitute the NULL pointer for |
| 53 | +one or more of the arrays, then that particular transfer function won't |
| 54 | +be outputted. The transfer functions are: |
| 55 | +
|
| 56 | + tf_full[] -- The full fitting formula, eq. (16), for the matter |
| 57 | + transfer function. |
| 58 | + tf_baryon[] -- The baryonic piece of the full fitting formula, eq. 21. |
| 59 | + tf_cdm[] -- The CDM piece of the full fitting formula, eq. 17. */ |
| 60 | + |
| 61 | +/* Again, you can set these pointers to NULL in the function call if |
| 62 | +you don't want a particular output. */ |
| 63 | + |
| 64 | +/* Various intermediate scalar quantities are stored in global variables, |
| 65 | +so that you might more easily access them. However, this also means that |
| 66 | +you would be better off not simply #include'ing this file in your programs, |
| 67 | +but rather compiling it separately, calling only the driver, and using |
| 68 | +extern declarations to access the intermediate quantities. */ |
| 69 | + |
| 70 | +/* ----------------------------- DRIVER ------------------------------- */ |
| 71 | + |
| 72 | +void TFfit_hmpc(float omega0, float f_baryon, float hubble, float Tcmb, |
| 73 | + int numk, float *k, float *tf_full, float *tf_baryon, float *tf_cdm) |
| 74 | +/* Remember: k[0..numk-1] is in units of h Mpc^-1. */ |
| 75 | +{ |
| 76 | + int j; |
| 77 | + float tf_thisk, baryon_piece, cdm_piece; |
| 78 | + |
| 79 | + TFset_parameters(omega0*hubble*hubble, f_baryon, Tcmb); |
| 80 | + |
| 81 | + for (j=0;j<numk;j++) { |
| 82 | + tf_thisk = TFfit_onek(k[j]*hubble, &baryon_piece, &cdm_piece); |
| 83 | + if (tf_full!=NULL) tf_full[j] = tf_thisk; |
| 84 | + if (tf_baryon!=NULL) tf_baryon[j] = baryon_piece; |
| 85 | + if (tf_cdm!=NULL) tf_cdm[j] = cdm_piece; |
| 86 | + } |
| 87 | + return; |
| 88 | +} |
| 89 | + |
| 90 | +/* ------------------------ FITTING FORMULAE ROUTINES ----------------- */ |
| 91 | + |
| 92 | +/* There are two routines here. TFset_parameters() sets all the scalar |
| 93 | +parameters, while TFfit_onek() calculates the transfer function for a |
| 94 | +given wavenumber k. TFfit_onek() may be called many times after a single |
| 95 | +call to TFset_parameters() */ |
| 96 | + |
| 97 | +/* Global variables -- We've left many of the intermediate results as |
| 98 | +global variables in case you wish to access them, e.g. by declaring |
| 99 | +them as extern variables in your main program. */ |
| 100 | +/* Note that all internal scales are in Mpc, without any Hubble constants! */ |
| 101 | + |
| 102 | +float omhh, /* Omega_matter*h^2 */ |
| 103 | + obhh, /* Omega_baryon*h^2 */ |
| 104 | + theta_cmb, /* Tcmb in units of 2.7 K */ |
| 105 | + z_equality, /* Redshift of matter-radiation equality, really 1+z */ |
| 106 | + k_equality, /* Scale of equality, in Mpc^-1 */ |
| 107 | + z_drag, /* Redshift of drag epoch */ |
| 108 | + R_drag, /* Photon-baryon ratio at drag epoch */ |
| 109 | + R_equality, /* Photon-baryon ratio at equality epoch */ |
| 110 | + sound_horizon, /* Sound horizon at drag epoch, in Mpc */ |
| 111 | + k_silk, /* Silk damping scale, in Mpc^-1 */ |
| 112 | + alpha_c, /* CDM suppression */ |
| 113 | + beta_c, /* CDM log shift */ |
| 114 | + alpha_b, /* Baryon suppression */ |
| 115 | + beta_b, /* Baryon envelope shift */ |
| 116 | + beta_node, /* Sound horizon shift */ |
| 117 | + k_peak, /* Fit to wavenumber of first peak, in Mpc^-1 */ |
| 118 | + sound_horizon_fit, /* Fit to sound horizon, in Mpc */ |
| 119 | + alpha_gamma; /* Gamma suppression in approximate TF */ |
| 120 | + |
| 121 | +/* Convenience from Numerical Recipes in C, 2nd edition */ |
| 122 | +static float sqrarg; |
| 123 | +#define SQR(a) ((sqrarg=(a)) == 0.0 ? 0.0 : sqrarg*sqrarg) |
| 124 | +static float cubearg; |
| 125 | +#define CUBE(a) ((cubearg=(a)) == 0.0 ? 0.0 : cubearg*cubearg*cubearg) |
| 126 | +static float pow4arg; |
| 127 | +#define POW4(a) ((pow4arg=(a)) == 0.0 ? 0.0 : pow4arg*pow4arg*pow4arg*pow4arg) |
| 128 | + /* Yes, I know the last one isn't optimal; it doesn't appear much */ |
| 129 | + |
| 130 | +void TFset_parameters(float omega0hh, float f_baryon, float Tcmb) |
| 131 | +/* Set all the scalars quantities for Eisenstein & Hu 1997 fitting formula */ |
| 132 | +/* Input: omega0hh -- The density of CDM and baryons, in units of critical dens, |
| 133 | + multiplied by the square of the Hubble constant, in units |
| 134 | + of 100 km/s/Mpc */ |
| 135 | +/* f_baryon -- The fraction of baryons to CDM */ |
| 136 | +/* Tcmb -- The temperature of the CMB in Kelvin. Tcmb<=0 forces use |
| 137 | + of the COBE value of 2.728 K. */ |
| 138 | +/* Output: Nothing, but set many global variables used in TFfit_onek(). |
| 139 | +You can access them yourself, if you want. */ |
| 140 | +/* Note: Units are always Mpc, never h^-1 Mpc. */ |
| 141 | +{ |
| 142 | + float z_drag_b1, z_drag_b2; |
| 143 | + float alpha_c_a1, alpha_c_a2, beta_c_b1, beta_c_b2, alpha_b_G, y; |
| 144 | + |
| 145 | + if (f_baryon<=0.0 || omega0hh<=0.0) { |
| 146 | + fprintf(stderr, "TFset_parameters(): Illegal input.\n"); |
| 147 | + exit(1); |
| 148 | + } |
| 149 | + omhh = omega0hh; |
| 150 | + obhh = omhh*f_baryon; |
| 151 | + if (Tcmb<=0.0) Tcmb=2.728; /* COBE FIRAS */ |
| 152 | + theta_cmb = Tcmb/2.7; |
| 153 | + |
| 154 | + z_equality = 2.50e4*omhh/POW4(theta_cmb); /* Really 1+z */ |
| 155 | + k_equality = 0.0746*omhh/SQR(theta_cmb); |
| 156 | + |
| 157 | + z_drag_b1 = 0.313*pow(omhh,-0.419)*(1+0.607*pow(omhh,0.674)); |
| 158 | + z_drag_b2 = 0.238*pow(omhh,0.223); |
| 159 | + z_drag = 1291*pow(omhh,0.251)/(1+0.659*pow(omhh,0.828))* |
| 160 | + (1+z_drag_b1*pow(obhh,z_drag_b2)); |
| 161 | + |
| 162 | + R_drag = 31.5*obhh/POW4(theta_cmb)*(1000/(1+z_drag)); |
| 163 | + R_equality = 31.5*obhh/POW4(theta_cmb)*(1000/z_equality); |
| 164 | + |
| 165 | + sound_horizon = 2./3./k_equality*sqrt(6./R_equality)* |
| 166 | + log((sqrt(1+R_drag)+sqrt(R_drag+R_equality))/(1+sqrt(R_equality))); |
| 167 | + |
| 168 | + k_silk = 1.6*pow(obhh,0.52)*pow(omhh,0.73)*(1+pow(10.4*omhh,-0.95)); |
| 169 | + |
| 170 | + alpha_c_a1 = pow(46.9*omhh,0.670)*(1+pow(32.1*omhh,-0.532)); |
| 171 | + alpha_c_a2 = pow(12.0*omhh,0.424)*(1+pow(45.0*omhh,-0.582)); |
| 172 | + alpha_c = pow(alpha_c_a1,-f_baryon)* |
| 173 | + pow(alpha_c_a2,-CUBE(f_baryon)); |
| 174 | + |
| 175 | + beta_c_b1 = 0.944/(1+pow(458*omhh,-0.708)); |
| 176 | + beta_c_b2 = pow(0.395*omhh, -0.0266); |
| 177 | + beta_c = 1.0/(1+beta_c_b1*(pow(1-f_baryon, beta_c_b2)-1)); |
| 178 | + |
| 179 | + y = z_equality/(1+z_drag); |
| 180 | + alpha_b_G = y*(-6.*sqrt(1+y)+(2.+3.*y)*log((sqrt(1+y)+1)/(sqrt(1+y)-1))); |
| 181 | + alpha_b = 2.07*k_equality*sound_horizon*pow(1+R_drag,-0.75)*alpha_b_G; |
| 182 | + |
| 183 | + beta_node = 8.41*pow(omhh, 0.435); |
| 184 | + beta_b = 0.5+f_baryon+(3.-2.*f_baryon)*sqrt(pow(17.2*omhh,2.0)+1); |
| 185 | + |
| 186 | + k_peak = 2.5*3.14159*(1+0.217*omhh)/sound_horizon; |
| 187 | + sound_horizon_fit = 44.5*log(9.83/omhh)/sqrt(1+10.0*pow(obhh,0.75)); |
| 188 | + |
| 189 | + alpha_gamma = 1-0.328*log(431.0*omhh)*f_baryon + 0.38*log(22.3*omhh)* |
| 190 | + SQR(f_baryon); |
| 191 | + |
| 192 | + return; |
| 193 | +} |
| 194 | + |
| 195 | +float TFfit_onek(float k, float *tf_baryon, float *tf_cdm) |
| 196 | +/* Input: k -- Wavenumber at which to calculate transfer function, in Mpc^-1. |
| 197 | + *tf_baryon, *tf_cdm -- Input value not used; replaced on output if |
| 198 | + the input was not NULL. */ |
| 199 | +/* Output: Returns the value of the full transfer function fitting formula. |
| 200 | + This is the form given in Section 3 of Eisenstein & Hu (1997). |
| 201 | + *tf_baryon -- The baryonic contribution to the full fit. |
| 202 | + *tf_cdm -- The CDM contribution to the full fit. */ |
| 203 | +/* Notes: Units are Mpc, not h^-1 Mpc. */ |
| 204 | +{ |
| 205 | + float T_c_ln_beta, T_c_ln_nobeta, T_c_C_alpha, T_c_C_noalpha; |
| 206 | + float q, xx, xx_tilde, q_eff; |
| 207 | + float T_c_f, T_c, s_tilde, T_b_T0, T_b, f_baryon, T_full; |
| 208 | + float T_0_L0, T_0_C0, T_0, gamma_eff; |
| 209 | + float T_nowiggles_L0, T_nowiggles_C0, T_nowiggles; |
| 210 | + |
| 211 | + k = fabs(k); /* Just define negative k as positive */ |
| 212 | + if (k==0.0) { |
| 213 | + if (tf_baryon!=NULL) *tf_baryon = 1.0; |
| 214 | + if (tf_cdm!=NULL) *tf_cdm = 1.0; |
| 215 | + return 1.0; |
| 216 | + } |
| 217 | + |
| 218 | + q = k/13.41/k_equality; |
| 219 | + xx = k*sound_horizon; |
| 220 | + |
| 221 | + T_c_ln_beta = log(2.718282+1.8*beta_c*q); |
| 222 | + T_c_ln_nobeta = log(2.718282+1.8*q); |
| 223 | + T_c_C_alpha = 14.2/alpha_c + 386.0/(1+69.9*pow(q,1.08)); |
| 224 | + T_c_C_noalpha = 14.2 + 386.0/(1+69.9*pow(q,1.08)); |
| 225 | + |
| 226 | + T_c_f = 1.0/(1.0+POW4(xx/5.4)); |
| 227 | + T_c = T_c_f*T_c_ln_beta/(T_c_ln_beta+T_c_C_noalpha*SQR(q)) + |
| 228 | + (1-T_c_f)*T_c_ln_beta/(T_c_ln_beta+T_c_C_alpha*SQR(q)); |
| 229 | + |
| 230 | + s_tilde = sound_horizon*pow(1+CUBE(beta_node/xx),-1./3.); |
| 231 | + xx_tilde = k*s_tilde; |
| 232 | + |
| 233 | + T_b_T0 = T_c_ln_nobeta/(T_c_ln_nobeta+T_c_C_noalpha*SQR(q)); |
| 234 | + T_b = sin(xx_tilde)/(xx_tilde)*(T_b_T0/(1+SQR(xx/5.2))+ |
| 235 | + alpha_b/(1+CUBE(beta_b/xx))*exp(-pow(k/k_silk,1.4))); |
| 236 | + |
| 237 | + f_baryon = obhh/omhh; |
| 238 | + T_full = f_baryon*T_b + (1-f_baryon)*T_c; |
| 239 | + |
| 240 | + /* Now to store these transfer functions */ |
| 241 | + if (tf_baryon!=NULL) *tf_baryon = T_b; |
| 242 | + if (tf_cdm!=NULL) *tf_cdm = T_c; |
| 243 | + return T_full; |
| 244 | +} |
| 245 | + |
| 246 | +/* ======================= Approximate forms =========================== */ |
| 247 | + |
| 248 | +float TFsound_horizon_fit(float omega0, float f_baryon, float hubble) |
| 249 | +/* Input: omega0 -- CDM density, in units of critical density |
| 250 | + f_baryon -- Baryon fraction, the ratio of baryon to CDM density. |
| 251 | + hubble -- Hubble constant, in units of 100 km/s/Mpc |
| 252 | +/* Output: The approximate value of the sound horizon, in h^-1 Mpc. */ |
| 253 | +/* Note: If you prefer to have the answer in units of Mpc, use hubble -> 1 |
| 254 | +and omega0 -> omega0*hubble^2. */ |
| 255 | +{ |
| 256 | + float omhh, sound_horizon_fit_mpc; |
| 257 | + omhh = omega0*hubble*hubble; |
| 258 | + sound_horizon_fit_mpc = |
| 259 | + 44.5*log(9.83/omhh)/sqrt(1+10.0*pow(omhh*f_baryon,0.75)); |
| 260 | + return sound_horizon_fit_mpc*hubble; |
| 261 | +} |
| 262 | + |
| 263 | +float TFk_peak(float omega0, float f_baryon, float hubble) |
| 264 | +/* Input: omega0 -- CDM density, in units of critical density |
| 265 | + f_baryon -- Baryon fraction, the ratio of baryon to CDM density. |
| 266 | + hubble -- Hubble constant, in units of 100 km/s/Mpc |
| 267 | +/* Output: The approximate location of the first baryonic peak, in h Mpc^-1 */ |
| 268 | +/* Note: If you prefer to have the answer in units of Mpc^-1, use hubble -> 1 |
| 269 | +and omega0 -> omega0*hubble^2. */ |
| 270 | +{ |
| 271 | + float omhh, k_peak_mpc; |
| 272 | + omhh = omega0*hubble*hubble; |
| 273 | + k_peak_mpc = 2.5*3.14159*(1+0.217*omhh)/TFsound_horizon_fit(omhh,f_baryon,1.0); |
| 274 | + return k_peak_mpc/hubble; |
| 275 | +} |
| 276 | + |
| 277 | +float TFnowiggles(float omega0, float f_baryon, float hubble, |
| 278 | + float Tcmb, float k_hmpc) |
| 279 | +/* Input: omega0 -- CDM density, in units of critical density |
| 280 | + f_baryon -- Baryon fraction, the ratio of baryon to CDM density. |
| 281 | + hubble -- Hubble constant, in units of 100 km/s/Mpc |
| 282 | + Tcmb -- Temperature of the CMB in Kelvin; Tcmb<=0 forces use of |
| 283 | + COBE FIRAS value of 2.728 K |
| 284 | + k_hmpc -- Wavenumber in units of (h Mpc^-1). */ |
| 285 | +/* Output: The value of an approximate transfer function that captures the |
| 286 | +non-oscillatory part of a partial baryon transfer function. In other words, |
| 287 | +the baryon oscillations are left out, but the suppression of power below |
| 288 | +the sound horizon is included. See equations (30) and (31). */ |
| 289 | +/* Note: If you prefer to use wavenumbers in units of Mpc^-1, use hubble -> 1 |
| 290 | +and omega0 -> omega0*hubble^2. */ |
| 291 | +{ |
| 292 | + float k, omhh, theta_cmb, k_equality, q, xx, alpha_gamma, gamma_eff; |
| 293 | + float q_eff, T_nowiggles_L0, T_nowiggles_C0; |
| 294 | + |
| 295 | + k = k_hmpc*hubble; /* Convert to Mpc^-1 */ |
| 296 | + omhh = omega0*hubble*hubble; |
| 297 | + if (Tcmb<=0.0) Tcmb=2.728; /* COBE FIRAS */ |
| 298 | + theta_cmb = Tcmb/2.7; |
| 299 | + |
| 300 | + k_equality = 0.0746*omhh/SQR(theta_cmb); |
| 301 | + q = k/13.41/k_equality; |
| 302 | + xx = k*TFsound_horizon_fit(omhh, f_baryon, 1.0); |
| 303 | + |
| 304 | + alpha_gamma = 1-0.328*log(431.0*omhh)*f_baryon + 0.38*log(22.3*omhh)* |
| 305 | + SQR(f_baryon); |
| 306 | + gamma_eff = omhh*(alpha_gamma+(1-alpha_gamma)/(1+POW4(0.43*xx))); |
| 307 | + q_eff = q*omhh/gamma_eff; |
| 308 | + |
| 309 | + T_nowiggles_L0 = log(2.0*2.718282+1.8*q_eff); |
| 310 | + T_nowiggles_C0 = 14.2 + 731.0/(1+62.5*q_eff); |
| 311 | + return T_nowiggles_L0/(T_nowiggles_L0+T_nowiggles_C0*SQR(q_eff)); |
| 312 | +} |
| 313 | + |
| 314 | +/* ======================= Zero Baryon Formula =========================== */ |
| 315 | + |
| 316 | +float TFzerobaryon(float omega0, float hubble, float Tcmb, float k_hmpc) |
| 317 | +/* Input: omega0 -- CDM density, in units of critical density |
| 318 | + hubble -- Hubble constant, in units of 100 km/s/Mpc |
| 319 | + Tcmb -- Temperature of the CMB in Kelvin; Tcmb<=0 forces use of |
| 320 | + COBE FIRAS value of 2.728 K |
| 321 | + k_hmpc -- Wavenumber in units of (h Mpc^-1). */ |
| 322 | +/* Output: The value of the transfer function for a zero-baryon universe. */ |
| 323 | +/* Note: If you prefer to use wavenumbers in units of Mpc^-1, use hubble -> 1 |
| 324 | +and omega0 -> omega0*hubble^2. */ |
| 325 | +{ |
| 326 | + float k, omhh, theta_cmb, k_equality, q, T_0_L0, T_0_C0; |
| 327 | + |
| 328 | + k = k_hmpc*hubble; /* Convert to Mpc^-1 */ |
| 329 | + omhh = omega0*hubble*hubble; |
| 330 | + if (Tcmb<=0.0) Tcmb=2.728; /* COBE FIRAS */ |
| 331 | + theta_cmb = Tcmb/2.7; |
| 332 | + |
| 333 | + k_equality = 0.0746*omhh/SQR(theta_cmb); |
| 334 | + q = k/13.41/k_equality; |
| 335 | + |
| 336 | + T_0_L0 = log(2.0*2.718282+1.8*q); |
| 337 | + T_0_C0 = 14.2 + 731.0/(1+62.5*q); |
| 338 | + return T_0_L0/(T_0_L0+T_0_C0*q*q); |
| 339 | +} |
0 commit comments