Skip to content

Commit 58e7aac

Browse files
committed
move some logic into cool_components.h
1 parent 524b939 commit 58e7aac

2 files changed

Lines changed: 196 additions & 177 deletions

File tree

src/cooling/cool_components.h

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/*! \file
2+
* \brief Declarations of cooling functions. */
3+
4+
#pragma once
5+
6+
#include <cmath>
7+
8+
#include "../global/global.h"
9+
10+
/*! @defgroup coolcomp Cooling Component Logic
11+
*
12+
* The goal here is to collect logic for modelling cooling. Cooling recipes are
13+
* subsequently constructed from the one or more components.
14+
*/
15+
/** @{ */
16+
17+
namespace cool_component
18+
{
19+
20+
/*! Primordial hydrogen/helium cooling curve (derived according to Katz et al. 1996.) */
21+
inline __device__ Real primordial_cool(Real n, Real T)
22+
{
23+
Real n_h, Y, y, g_ff, cool;
24+
Real n_h0, n_hp, n_he0, n_hep, n_hepp, n_e, n_e_old;
25+
Real alpha_hp, alpha_hep, alpha_d, alpha_hepp, gamma_eh0, gamma_ehe0, gamma_ehep;
26+
Real le_h0, le_hep, li_h0, li_he0, li_hep, lr_hp, lr_hep, lr_hepp, ld_hep, l_ff;
27+
Real gamma_lh0, gamma_lhe0, gamma_lhep, e_h0, e_he0, e_hep, H;
28+
int heat_flag, n_iter;
29+
Real diff, tol;
30+
31+
// set flag to 1 for photoionization & heating
32+
heat_flag = 0;
33+
34+
// Real X = 0.76; //hydrogen abundance by mass
35+
Y = 0.24; // helium abundance by mass
36+
y = Y / (4 - 4 * Y);
37+
38+
// set the hydrogen number density
39+
n_h = n;
40+
41+
// calculate the recombination and collisional ionization rates
42+
// (Table 2 from Katz 1996)
43+
alpha_hp = (8.4e-11) * (1.0 / sqrt(T)) * pow((T / 1e3), (-0.2)) * (1.0 / (1.0 + pow((T / 1e6), (0.7))));
44+
alpha_hep = (1.5e-10) * (pow(T, (-0.6353)));
45+
alpha_d = (1.9e-3) * (pow(T, (-1.5))) * exp(-470000.0 / T) * (1.0 + 0.3 * exp(-94000.0 / T));
46+
alpha_hepp = (3.36e-10) * (1.0 / sqrt(T)) * pow((T / 1e3), (-0.2)) * (1.0 / (1.0 + pow((T / 1e6), (0.7))));
47+
gamma_eh0 = (5.85e-11) * sqrt(T) * exp(-157809.1 / T) * (1.0 / (1.0 + sqrt(T / 1e5)));
48+
gamma_ehe0 = (2.38e-11) * sqrt(T) * exp(-285335.4 / T) * (1.0 / (1.0 + sqrt(T / 1e5)));
49+
gamma_ehep = (5.68e-12) * sqrt(T) * exp(-631515.0 / T) * (1.0 / (1.0 + sqrt(T / 1e5)));
50+
// externally evaluated integrals for photoionization rates
51+
// assumed J(nu) = 10^-22 (nu_L/nu)
52+
gamma_lh0 = 3.19851e-13;
53+
gamma_lhe0 = 3.13029e-13;
54+
gamma_lhep = 2.00541e-14;
55+
// externally evaluated integrals for heating rates
56+
e_h0 = 2.4796e-24;
57+
e_he0 = 6.86167e-24;
58+
e_hep = 6.21868e-25;
59+
60+
// assuming no photoionization, solve equations for number density of
61+
// each species
62+
n_e = n_h; // as a first guess, use the hydrogen number density
63+
n_iter = 20;
64+
diff = 1.0;
65+
tol = 1.0e-6;
66+
if (heat_flag) {
67+
for (int i = 0; i < n_iter; i++) {
68+
n_e_old = n_e;
69+
n_h0 = n_h * alpha_hp / (alpha_hp + gamma_eh0 + gamma_lh0 / n_e);
70+
n_hp = n_h - n_h0;
71+
n_hep = y * n_h /
72+
(1.0 + (alpha_hep + alpha_d) / (gamma_ehe0 + gamma_lhe0 / n_e) +
73+
(gamma_ehep + gamma_lhep / n_e) / alpha_hepp);
74+
n_he0 = n_hep * (alpha_hep + alpha_d) / (gamma_ehe0 + gamma_lhe0 / n_e);
75+
n_hepp = n_hep * (gamma_ehep + gamma_lhep / n_e) / alpha_hepp;
76+
n_e = n_hp + n_hep + 2 * n_hepp;
77+
diff = fabs(n_e_old - n_e);
78+
if (diff < tol) {
79+
break;
80+
}
81+
}
82+
} else {
83+
n_h0 = n_h * alpha_hp / (alpha_hp + gamma_eh0);
84+
n_hp = n_h - n_h0;
85+
n_hep = y * n_h / (1.0 + (alpha_hep + alpha_d) / (gamma_ehe0) + (gamma_ehep) / alpha_hepp);
86+
n_he0 = n_hep * (alpha_hep + alpha_d) / (gamma_ehe0);
87+
n_hepp = n_hep * (gamma_ehep) / alpha_hepp;
88+
n_e = n_hp + n_hep + 2 * n_hepp;
89+
}
90+
91+
// using number densities, calculate cooling rates for
92+
// various processes (Table 1 from Katz 1996)
93+
le_h0 = (7.50e-19) * exp(-118348.0 / T) * (1.0 / (1.0 + sqrt(T / 1e5))) * n_e * n_h0;
94+
le_hep = (5.54e-17) * pow(T, (-0.397)) * exp(-473638.0 / T) * (1.0 / (1.0 + sqrt(T / 1e5))) * n_e * n_hep;
95+
li_h0 = (1.27e-21) * sqrt(T) * exp(-157809.1 / T) * (1.0 / (1.0 + sqrt(T / 1e5))) * n_e * n_h0;
96+
li_he0 = (9.38e-22) * sqrt(T) * exp(-285335.4 / T) * (1.0 / (1.0 + sqrt(T / 1e5))) * n_e * n_he0;
97+
li_hep = (4.95e-22) * sqrt(T) * exp(-631515.0 / T) * (1.0 / (1.0 + sqrt(T / 1e5))) * n_e * n_hep;
98+
lr_hp = (8.70e-27) * sqrt(T) * pow((T / 1e3), (-0.2)) * (1.0 / (1.0 + pow((T / 1e6), (0.7)))) * n_e * n_hp;
99+
lr_hep = (1.55e-26) * pow(T, (0.3647)) * n_e * n_hep;
100+
lr_hepp = (3.48e-26) * sqrt(T) * pow((T / 1e3), (-0.2)) * (1.0 / (1.0 + pow((T / 1e6), (0.7)))) * n_e * n_hepp;
101+
ld_hep = (1.24e-13) * pow(T, (-1.5)) * exp(-470000.0 / T) * (1.0 + 0.3 * exp(-94000.0 / T)) * n_e * n_hep;
102+
g_ff = 1.1 + 0.34 * exp(-(5.5 - log(T)) * (5.5 - log(T)) / 3.0); // Gaunt factor
103+
l_ff = (1.42e-27) * g_ff * sqrt(T) * (n_hp + n_hep + 4 * n_hepp) * n_e;
104+
105+
// calculate total cooling rate (erg s^-1 cm^-3)
106+
cool = le_h0 + le_hep + li_h0 + li_he0 + li_hep + lr_hp + lr_hep + lr_hepp + ld_hep + l_ff;
107+
108+
// calculate total photoionization heating rate
109+
H = 0.0;
110+
if (heat_flag) {
111+
H = n_h0 * e_h0 + n_he0 * e_he0 + n_hep * e_hep;
112+
}
113+
114+
cool -= H;
115+
116+
return cool;
117+
}
118+
119+
/*! \brief computes the cooling rate, based on an analytic fit to a solar metallicity
120+
* CIE cooling curve calculated using Cloudy. For log10T, this returns 0
121+
*
122+
* \return The cooling rate, lambda, in units of erg s^-1 cm^3 (it is NEVER negative)
123+
*
124+
* \note
125+
* It may not be necessary to use __forceinline__, I just used it to ensure I didn't harm existing
126+
* performance
127+
*
128+
* \note
129+
* The actual formula for the fit is first described in the appendix of
130+
* (Schneider & Robertson 2018)[https://ui.adsabs.harvard.edu/abs/2018ApJ...860..135S/abstract
131+
*/
132+
__forceinline__ __device__ Real analytic_cie_lambda(Real log10T)
133+
{
134+
// fit to CIE cooling function
135+
if (log10T < 4.0) {
136+
return 0.0;
137+
} else if (log10T >= 4.0 && log10T < 5.9) {
138+
return pow(10.0, (-1.3 * (log10T - 5.25) * (log10T - 5.25) - 21.25));
139+
} else if (log10T >= 5.9 && log10T < 7.4) {
140+
return pow(10.0, (0.7 * (log10T - 7.1) * (log10T - 7.1) - 22.8));
141+
} else {
142+
return pow(10.0, (0.45 * log10T - 26.065));
143+
}
144+
}
145+
146+
/*! Encapsulates our model and configuration for photoelectric heating
147+
*
148+
* This implements a very simple model
149+
* - we apply uniform photoelectric heating (over all space and time) to all gas at temperatures
150+
* below 1e4 K
151+
* - this model is described within
152+
* [Kim & Ostriker 2015](https://ui.adsabs.harvard.edu/abs/2015ApJ...802...99K/abstract)
153+
*
154+
* @note
155+
* In the future, one could imagine implementing a more sophisticated model like TIGRESS
156+
* - For example the amount of heating could be coupled with the properties of clusters
157+
* within the simulation volume
158+
* - If we started to model varying mmw, we could also adopt the TIGRESS strategy to more
159+
* smoothly turn off heating at higher temperatures
160+
*/
161+
struct PhotoelectricHeatingModel {
162+
/*! This theoretically represents the mean density in the simulation volume. A value of 0.0
163+
* indicates that there is no heating.
164+
*
165+
* @note
166+
* I can't remember the precise interpretation, but I think the idea may be that it may be
167+
* used because it loosely relates to the rate of star formation...
168+
*/
169+
double n_av_cgs = 0.0;
170+
171+
bool is_active() const { return n_av_cgs != 0.0; }
172+
173+
/*! \brief computes the heating rate per unit volume, erg /s / cm^3.
174+
*
175+
* This **NEVER** returns a negative value.
176+
*/
177+
__device__ Real operator()(Real n, Real T) const { return (T < 1e4) ? n * n_av_cgs * 1.0e-26 : 0.0; }
178+
};
179+
180+
} // namespace cool_component
181+
182+
/** @}*/ // end of group

0 commit comments

Comments
 (0)