Cholla 3.0.1-dev
Cholla - Massively parallel hydro on GPUs
Loading...
Searching...
No Matches
potentials.h
1#ifndef POTENTIALS
2#define POTENTIALS
3
4// this file contains objects representing (semi-)analytic gravitational potentials
5
6#include <cmath>
7
8#include "../../global/global.h"
9#include "../../utils/error_handling.h"
10
12 Real M_h;
13 Real R_h;
14 Real c_vir;
16 /* function with logarithms used in NFW definitions */
17 __host__ __device__ static Real log_func(Real y) { return log(1 + y) - y / (1 + y); };
18
19 /* Cylindrical radial acceleration */
20 Real gr_halo_D3D(Real R, Real z) const noexcept
21 {
22 Real r = sqrt(R * R + z * z); // spherical radius
23 Real x = r / R_h;
24 Real r_comp = R / r;
25
26 Real A = log_func(x);
27 Real B = 1.0 / (r * r);
28 Real C = GN * M_h / log_func(c_vir);
29
30 return -C * A * B * r_comp;
31 };
32
33 /* vertical acceleration in NFW halo */
34 Real gz_halo_D3D(Real R, Real z)
35 {
36 Real r = sqrt(R * R + z * z); // spherical radius
37 Real x = r / R_h;
38 Real z_comp = z / r;
39
40 Real A = log_func(x);
41 Real B = 1.0 / (r * r);
42 Real C = GN * M_h / log_func(c_vir);
43
44 return -C * A * B * z_comp; // checked with wolfram alpha
45 }
46
47 /* Mass density profile */
48 __host__ __device__ Real rho_halo_D3D(Real R, Real z) const noexcept
49 {
50 // by equating eqn 2.67 from Binney and Tremaine with eqn 2 from
51 // Schneider & Robertson (2018) -- these are alternative forms of Phi --
52 // I find that the rho0 normalization is:
53 Real rho0 = M_h / (4 * M_PI * (R_h * R_h * R_h) * log_func(c_vir));
54
55 Real rdivRh = sqrt(R * R + z * z) / R_h; // spherical radius divided by R_h
56
57 Real rdivRh_p_1 = rdivRh + 1;
58
59 // eqn 2.64 from Binney and Tremmaine:
60 return rho0 / (rdivRh * (rdivRh_p_1 * rdivRh_p_1));
61 }
62
63 /* Potential of NFW halo */
64 __host__ __device__ Real phi_halo_D3D(Real R, Real z) const noexcept
65 {
66 Real r = sqrt(R * R + z * z); // spherical radius
67 Real x = r / R_h;
68 Real C = GN * M_h / (R_h * log_func(c_vir));
69
70 // limit x to non-zero value
71 if (x < 1.0e-9) {
72 x = 1.0e-9;
73 }
74
75 return -C * log(1 + x) / x;
76 };
77};
78
86 Real M_d;
87 Real R_d;
88 Real Z_d;
90 /* Radial acceleration in miyamoto nagai */
91 Real gr_disk_D3D(Real R, Real z) const noexcept
92 {
93 Real A = R_d + sqrt(Z_d * Z_d + z * z);
94 Real B = pow(A * A + R * R, 1.5);
95
96 return -GN * M_d * R / B;
97 };
98
99 // vertical acceleration in miyamoto nagai
100 Real gz_disk_D3D(Real R, Real z) const noexcept
101 {
102 Real a = R_d;
103 Real b = Z_d;
104 Real A = sqrt(b * b + z * z);
105 Real B = a + A;
106 Real C = pow(B * B + R * R, 1.5);
107
108 // checked with wolfram alpha
109 return -GN * M_d * z * B / (A * C);
110 }
111
112 /* Miyamoto-Nagai potential */
113 __host__ __device__ Real phi_disk_D3D(Real R, Real z) const noexcept
114 {
115 Real A = sqrt(z * z + Z_d * Z_d);
116 Real B = R_d + A;
117 Real C = sqrt(R * R + B * B);
118
119 // patel et al. 2017, eqn 2
120 return -GN * M_d / C;
121 };
122
123 /* Mass profile of disk */
124 __host__ __device__ Real rho_disk_D3D(const Real r, const Real z) const noexcept
125 {
126 const Real a = R_d;
127 const Real c = Z_d;
128 const Real b = sqrt(z * z + c * c);
129 const Real d = a + b;
130 const Real s = r * r + d * d;
131 return M_d * c * c * (a * (d * d + r * r) + 3.0 * b * d * d) / (4.0 * M_PI * b * b * b * pow(s, 2.5));
132
133 /* version that was ripped out of Potential_Paris_Galactic::Get_Potential
134 *
135 * const Real rho0 = md * zd * zd / (4.0 * M_PI);
136 * const Real a = sqrt(z * z + Z_d * Z_d);
137 * const Real b = R_d + a;
138 * const Real c = r * r + b * b;
139 * return rho0 * (rd * c + 3.0 * a * b * b) / (a * a * a * pow(c, 2.5));
140 */
141 }
142};
143
144/* Approximates the potential of a Exponential Disk as the sum of 3 MiyamotoNagaiPotential
145 * disks.
146 *
147 * This uses the table from
148 * https://ui.adsabs.harvard.edu/abs/2015MNRAS.448.2934S/abstract
149 * to determine the properties of each component
150 */
152 MiyamotoNagaiPotential comps[3];
153
154 /* Returns a properly configured disk with
155 *
156 * The arguments determine what kind of disk we model:
157 * - when `scale_height` is 0.0, we always model an infinitely thin disk
158 * - when `scale_height>0` and `exponential_scaleheight` is `true`, the vertical
159 * density distribution exponentially decays as `exp(-fabs(z)/scale_height)`
160 * - when `scale_height>0` and `exponential_scaleheight` is `false`, the vertical
161 * density distribution exponentially decays as `sech^2(-fabs(z)/scale_height)`
162 *
163 * \param[in] mass Total mass of the disk
164 * \param[in] scale_length The desired radial exponential scale length (in code units)
165 * \param[in] scale_height The desired scale_height of the disk (in code units)
166 * \param[in] exponential_scaleheight Controls interpretation of scale-height
167 */
168 static ApproxExponentialDisk3MN create(Real mass, Real scale_length, Real scale_height, bool exponential_scaleheight)
169 {
170 if ((mass <= 0) or (scale_length <= 0)) CHOLLA_ERROR("invalid args");
171
172 // step 1: determine the disk thickness parameter b (it's shared by all components)
173 Real b_div_scale_length;
174 if (scale_height < 0.0) {
175 CHOLLA_ERROR("scale_height must be positive");
176 } else if (scale_height == 0.0) {
177 b_div_scale_length = 0.0;
178 } else if (exponential_scaleheight) {
179 Real x = scale_height / scale_length;
180 b_div_scale_length = (-0.269 * x + 1.080) * x + 1.092 * x;
181 } else {
182 Real x = scale_height / scale_length;
183 b_div_scale_length = (-0.033 * x + 0.262) * x + 0.659 * x;
184 }
185 Real b = b_div_scale_length * scale_length;
186
187 if (b_div_scale_length > 3.0) CHOLLA_ERROR("The disk is too thick for this approx");
188
189 // step 2 determine other parameters.
190 // -> we use values from table 1 (although this potential technically
191 // corresponds to negative densities in the outer disk, that's probably
192 // fine for our purposes)
193 const Real k[6][5] = {{-0.0090, 0.0640, -0.1653, 0.1164, 1.9487}, // M_MN0 / mass
194 {0.0173, -0.0903, 0.0877, 0.2029, -1.3077}, // M_MN1 / mass
195 {-0.0051, 0.0287, -0.0361, -0.0544, 0.2242}, // M_MN2 / mass
196 {-0.0358, 0.2610, -0.6987, -0.1193, 2.0074}, // a0 / scale_length
197 {-0.0830, 0.4992, -0.7967, -1.2966, 4.4441}, // a1 / scale_length
198 {-0.0247, 0.1718, -0.4124, -0.5944, 0.7333}}; // a2 / scale_length
199 auto param = [&k, b_div_scale_length](int i) {
200 Real x = b_div_scale_length;
201 return ((((k[i][0] * x + k[i][1]) * x + k[i][2]) * x) + k[i][3]) * x + k[i][4];
202 };
203
205 for (int j = 0; j < 3; j++) {
206 out.comps[j] = MiyamotoNagaiPotential{param(j) * mass, param(j + 3) * scale_length, b};
207 }
208 return out;
209 }
210
211 /* Radial acceleration */
212 Real gr_disk_D3D(Real R, Real z) const noexcept
213 {
214 return (comps[0].gr_disk_D3D(R, z) + comps[1].gr_disk_D3D(R, z) + comps[2].gr_disk_D3D(R, z));
215 }
216
217 /* vertical acceleration */
218 Real gz_disk_D3D(Real R, Real z) const noexcept
219 {
220 return (comps[0].gz_disk_D3D(R, z) + comps[1].gz_disk_D3D(R, z) + comps[2].gz_disk_D3D(R, z));
221 }
222
223 /* computes the potential */
224 __host__ __device__ Real phi_disk_D3D(Real R, Real z) const noexcept
225 {
226 return (comps[0].phi_disk_D3D(R, z) + comps[1].phi_disk_D3D(R, z) + comps[2].phi_disk_D3D(R, z));
227 }
228
229 /* computes the mass profile that corresponds to the potential
230 *
231 * \note
232 * Technically, this may contain negative values. But, that's long as we are not using the results
233 * to directly initialize a gas density profile. This is mostly useful in certain kinds of gravity
234 * solvers.
235 */
236 __host__ __device__ Real rho_disk_D3D(Real R, Real z) const noexcept
237 {
238 return (comps[0].rho_disk_D3D(R, z) + comps[1].rho_disk_D3D(R, z) + comps[2].rho_disk_D3D(R, z));
239 }
240};
241
242// It probably makes more sense for the following class to live in disk_ICs.h.
243// - we currently put the definition here (rather than the in disk_ICs.h) since it holds
244// ApproxExponentialDisk3MN as an attribute
245// - thus if we put this class in the disk_galaxy.h, we would also need to add an include
246// this header file to disk_galaxy.h. That produces issues for any regular .cpp file
247// that (directly or transitively includes) disk_galaxy.h
248
249/* Aggregates properties related to a gas disk
250 *
251 * The radial surface-density distribution satisfies
252 * `Sigma(r) = Sigma_0 * exp(-r_cyl/R_d)
253 */
255 Real M_d;
256 Real R_d;
257 Real H_d;
258 Real T_d;
263 /* A rough approximation for the gravitational potential produced by self-gravity.
264 * - It is generally used to help initialize the circular-velocity in the ICs.
265 * - It is also employed while using the Paris-Galactic gravity solver. In this latter case, it's
266 * critical that this approximation is accurate at the domain boundaries (elsewhere, accuracy
267 * is entirely unimportant).
268 */
269 ApproxExponentialDisk3MN selfgrav_approx_potential;
270
271 GasDiskProps(Real M_d, Real R_d, Real H_d, Real T_d, bool isothermal, Real selfgrav_scale_height_estimate)
272 : M_d(M_d),
273 R_d(R_d),
274 H_d(H_d),
275 T_d(T_d),
277 selfgrav_approx_potential(ApproxExponentialDisk3MN::create(M_d, R_d, selfgrav_scale_height_estimate, true))
278 {
279 }
280
281 /* Returns Sigma_0. This is just
282 * \f$\Sigma_0 = \frac{M_d}{\int Sigma(r)\ dA} = \frac{M_d}{2\pi \int_0^\infty r\ \Sigma\ dr} \f$
283 */
284 Real CentralSurfaceDensity() const noexcept { return M_d / (2 * M_PI * R_d * R_d); }
285
286 /* Compute the surface density at cylindrical radius*/
287 Real surface_density(Real R) const noexcept { return CentralSurfaceDensity() * exp(-R / R_d); };
288};
289
290#endif /* POTENTIALS */
Definition potentials.h:151
Definition potentials.h:254
Real M_d
Definition potentials.h:255
bool isothermal
Definition potentials.h:259
Real H_d
Definition potentials.h:257
Real R_d
Definition potentials.h:256
Real T_d
Definition potentials.h:258
Definition potentials.h:85
Real R_d
Definition potentials.h:87
Real M_d
Definition potentials.h:86
Real Z_d
Definition potentials.h:88
Definition potentials.h:11
Real c_vir
Definition potentials.h:14
Real R_h
Definition potentials.h:13
Real M_h
Definition potentials.h:12