36 Locally isothermal LP07 disc profiles and Monte Carlo generator helper.
38 All radii and masses are expressed in the provided code unit system.
41 units: shamrock.UnitSystem
51 rotation: RotationMode =
"subkeplerian"
52 inner_tapering: bool =
False
53 outer_tapering: bool =
False
55 def __post_init__(self) -> None:
56 if self.rin >= self.rout:
57 raise ValueError(
"rin must be smaller than rout")
58 if self.center_mass <= 0:
59 raise ValueError(
"center_mass must be positive")
60 if self.disc_mass <= 0:
61 raise ValueError(
"disc_mass must be positive")
62 if self.
rotation not in _VALID_ROTATIONS:
63 raise ValueError(f
"rotation must be one of {_VALID_ROTATIONS}, got {self.rotation!r}")
65 def _G(self) -> float:
66 return shamrock.Constants(self.
units).G()
68 def _get_sigma(self) -> Any:
70 raise NotImplementedError(
"outer_tapering is not implemented yet")
77 def sigma(r: float) -> float:
80 sigma_0 *= 1 - (rin / r) ** 0.5
81 return sigma_0 * (r / r0) ** (-p)
83 return maybe_njit(sigma)
85 def _get_vtheta_kepler(self) -> Any:
87 center_mass = self.center_mass
89 def vtheta_kepler(r: float) -> float:
90 return (G * center_mass / r) ** 0.5
92 return maybe_njit(vtheta_kepler)
94 def _get_omega_k(self, vtheta_kepler: Any) -> Any:
95 def omega_k(r: float) -> float:
96 return vtheta_kepler(r) / r
98 return maybe_njit(omega_k)
100 def _get_cs(self, vtheta_kepler: Any) -> Any:
104 cs_in = H_r_0 * vtheta_kepler(r0)
106 def cs(r: float) -> float:
107 return ((r / r0) ** (-q)) * cs_in
109 return maybe_njit(cs)
111 def _get_H(self, cs: Any, omega_k: Any) -> Any:
114 def H(r: float) -> float:
115 return H_factor * cs(r) / omega_k(r)
119 def _get_vtheta_subkeplerian(self, vtheta_kepler: Any, cs: Any) -> Any:
120 p, q = self.
p, self.
q
122 def vtheta(r: float) -> float:
123 return ((vtheta_kepler(r) ** 2) - (2 * p + q) * cs(r) ** 2) ** 0.5
125 return maybe_njit(vtheta)
127 def _get_velocity_vertical(self, vtheta_kepler: Any, cs: Any) -> Any:
128 p, q = self.
p, self.
q
130 def vtheta_rz(r: float, z: float) -> float:
131 gm_r = vtheta_kepler(r) ** 2
132 term2 = -(p + q + 3.0 / 2.0) * cs(r) ** 2
133 r2z2_sqrt = (r**2 + z**2) ** 0.5
134 term3 = -gm_r * r * (2 * q) * (1 / r - 1 / r2z2_sqrt)
135 return (gm_r + term2 + term3) ** 0.5
137 vtheta_rz = maybe_njit(vtheta_rz)
140 x, y, z = pos[0], pos[1], pos[2]
141 r = (x**2 + y**2) ** 0.5
142 v_mag = vtheta_rz(r, z)
143 return (v_mag * (-y / r), v_mag * (x / r), 0.0)
145 return maybe_njit(velocity)
147 def _get_rotation(self, vtheta_kepler: Any, cs: Any) -> tuple[Any, Any]:
149 return vtheta_kepler,
None
156 Get the profiles of the disc.
161 cs = self.
_get_cs(vtheta_kepler)
162 H = self.
_get_H(cs, omega_k)
168 vtheta_kepler=vtheta_kepler,
177 Get the mass of a single particle from the total mass & number of particles.
179 return self.disc_mass / npart
183 Get the sound speed at the reference radius.
192 random_seed: int = 666,
193 init_h_factor: float = 0.8,
196 Make a SPH generator for the disc.
201 "disc_mass": self.disc_mass,
204 "sigma_profile": profiles.sigma,
205 "H_profile": profiles.H,
206 "random_seed": random_seed,
207 "init_h_factor": init_h_factor,
209 if profiles.velocity
is not None:
210 kwargs[
"velocity_field"] = profiles.velocity
212 kwargs[
"rot_profile"] = profiles.vtheta
213 if profiles.cs_field
is not None:
214 kwargs[
"cs_field"] = profiles.cs_field
216 kwargs[
"cs_profile"] = profiles.cs
217 return setup.make_generator_disc_mc(**kwargs)