Skip to content

Commit 20d5be1

Browse files
tluettmslayoo
andauthored
terminal velocity of ice particles (#1562)
Co-authored-by: Sylwester Arabas <[email protected]>
1 parent d702555 commit 20d5be1

File tree

24 files changed

+509
-44
lines changed

24 files changed

+509
-44
lines changed

PySDM/attributes/physics/relative_fall_velocity.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Attributes for tracking droplet velocity
2+
Attributes for tracking particle velocity
33
"""
44

55
from PySDM.attributes.impl import (
@@ -29,13 +29,13 @@ def __init__(self, builder):
2929
class RelativeFallVelocity(DerivedAttribute):
3030
def __init__(self, builder):
3131
self.momentum = builder.get_attribute("relative fall momentum")
32-
self.water_mass = builder.get_attribute("water mass")
32+
self.signed_water_mass = builder.get_attribute("signed water mass")
3333

3434
super().__init__(
3535
builder,
3636
name="relative fall velocity",
37-
dependencies=(self.momentum, self.water_mass),
37+
dependencies=(self.momentum, self.signed_water_mass),
3838
)
3939

4040
def recalculate(self):
41-
self.data.ratio(self.momentum.get(), self.water_mass.get())
41+
self.data.ratio(self.momentum.get(), self.signed_water_mass.get())

PySDM/attributes/physics/terminal_velocity.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,27 @@
1313
class TerminalVelocity(DerivedAttribute):
1414
def __init__(self, builder):
1515
self.radius = builder.get_attribute("radius")
16-
dependencies = [self.radius]
16+
self.signed_water_mass = builder.get_attribute("signed water mass")
17+
self.cell_id = builder.get_attribute("cell id")
18+
dependencies = [self.radius, self.signed_water_mass, self.cell_id]
1719
super().__init__(builder, name="terminal velocity", dependencies=dependencies)
1820

19-
self.approximation = builder.formulae.terminal_velocity_class(
21+
self.approximation_liquid = builder.formulae.terminal_velocity_class(
22+
builder.particulator
23+
)
24+
self.approximation_ice = builder.formulae.terminal_velocity_ice_class(
2025
builder.particulator
2126
)
2227

2328
def recalculate(self):
24-
self.approximation(self.data, self.radius.get())
29+
self.approximation_liquid(self.data, self.radius.get())
30+
# TODO #1605 order of functions calls changes result. approximation_liquid will override
31+
# approximation_ice when mixed-phase spheres shape active
32+
if self.formulae.particle_shape_and_density.supports_mixed_phase():
33+
self.approximation_ice(
34+
self.data,
35+
self.signed_water_mass.get(),
36+
self.cell_id.get(),
37+
self.particulator.environment["T"],
38+
self.particulator.environment["p"],
39+
)

PySDM/backends/impl_numba/methods/terminal_velocity_methods.py

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,38 +10,40 @@
1010

1111

1212
class TerminalVelocityMethods(BackendMethods):
13+
1314
@cached_property
14-
def _interpolation_body(self):
15+
def _gunn_and_kinzer_interpolation_body(self):
1516
@numba.njit(**self.default_jit_flags)
1617
def body(output, radius, factor, b, c):
1718
for i in numba.prange(len(radius)): # pylint: disable=not-an-iterable
18-
if radius[i] < 0:
19-
output[i] = 0
20-
else:
19+
if radius[i] > 0:
2120
r_id = int(factor * radius[i])
2221
r_rest = ((factor * radius[i]) % 1) / factor
2322
output[i] = b[r_id] + r_rest * c[r_id]
23+
elif radius[i] == 0:
24+
output[i] = 0
2425

2526
return body
2627

27-
def interpolation(self, *, output, radius, factor, b, c):
28-
return self._interpolation_body(
28+
def gunn_and_kinzer_interpolation(self, *, output, radius, factor, b, c):
29+
return self._gunn_and_kinzer_interpolation_body(
2930
output.data, radius.data, factor, b.data, c.data
3031
)
3132

3233
@cached_property
33-
def _terminal_velocity_body(self):
34+
def _rogers_and_yau_terminal_velocity_body(self):
3435
v_term = self.formulae.terminal_velocity.v_term
3536

3637
@numba.njit(**self.default_jit_flags)
3738
def body(*, values, radius):
3839
for i in numba.prange(len(values)): # pylint: disable=not-an-iterable
39-
values[i] = v_term(radius[i])
40+
if radius[i] >= 0.0:
41+
values[i] = v_term(radius[i])
4042

4143
return body
4244

43-
def terminal_velocity(self, *, values, radius):
44-
self._terminal_velocity_body(values=values, radius=radius)
45+
def rogers_and_yau_terminal_velocity(self, *, values, radius):
46+
self._rogers_and_yau_terminal_velocity_body(values=values, radius=radius)
4547

4648
@cached_property
4749
def _power_series_body(self):
@@ -62,3 +64,64 @@ def power_series(self, *, values, radius, num_terms, prefactors, powers):
6264
prefactors=prefactors,
6365
powers=powers,
6466
)
67+
68+
def terminal_velocity_columnar_ice_crystals(
69+
self, *, values, signed_water_mass, cell_id, temperature, pressure
70+
):
71+
self._terminal_velocity_columnar_ice_crystals_body(
72+
values=values,
73+
signed_water_mass=signed_water_mass,
74+
cell_id=cell_id,
75+
temperature=temperature,
76+
pressure=pressure,
77+
)
78+
79+
@cached_property
80+
def _terminal_velocity_columnar_ice_crystals_body(self):
81+
v_base_term = self.formulae.terminal_velocity_ice.v_base_term
82+
atmospheric_correction_factor = (
83+
self.formulae.terminal_velocity_ice.atmospheric_correction_factor
84+
)
85+
86+
@numba.njit(**self.default_jit_flags)
87+
def body(*, values, signed_water_mass, cell_id, temperature, pressure):
88+
for i in numba.prange(len(values)): # pylint: disable=not-an-iterable
89+
if signed_water_mass[i] < 0:
90+
cid = cell_id[i]
91+
correction = atmospheric_correction_factor(
92+
temperature[cid], pressure[cid]
93+
)
94+
values[i] = v_base_term(-signed_water_mass[i]) * correction
95+
96+
return body
97+
98+
def terminal_velocity_ice_spheres(
99+
self, *, values, signed_water_mass, cell_id, temperature, pressure
100+
): # pylint: disable=unused-argument
101+
self._terminal_velocity_ice_spheres_body(
102+
values=values,
103+
signed_water_mass=signed_water_mass,
104+
cell_id=cell_id,
105+
temperature=temperature,
106+
)
107+
108+
@cached_property
109+
def _terminal_velocity_ice_spheres_body(self):
110+
v_base_term = self.formulae.terminal_velocity_ice.v_base_term
111+
stokes_prefactor = self.formulae.terminal_velocity_ice.stokes_regime
112+
formulae = self.formulae_flattened
113+
114+
def body(*, values, signed_water_mass, cell_id, temperature):
115+
for i in numba.prange(len(values)): # pylint: disable=not-an-iterable
116+
if signed_water_mass[i] < 0:
117+
cid = cell_id[i]
118+
radius = formulae.particle_shape_and_density__mass_to_radius(
119+
signed_water_mass[i]
120+
)
121+
dynamic_viscosity = formulae.air_dynamic_viscosity__eta_air(
122+
temperature[cid]
123+
)
124+
prefactor = stokes_prefactor(radius, dynamic_viscosity)
125+
values[i] = v_base_term(prefactor, radius)
126+
127+
return body

PySDM/backends/impl_thrust_rtc/methods/terminal_velocity_methods.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def __linear_collection_efficiency_body(self):
6969
)
7070

7171
@cached_property
72-
def __interpolation_body(self):
72+
def __gunn_and_kinzer_interpolation_body(self):
7373
# TODO #599 r<0
7474
return trtc.For(
7575
("output", "radius", "factor", "a", "b"),
@@ -126,9 +126,9 @@ def linear_collection_efficiency(
126126
)
127127

128128
@nice_thrust(**NICE_THRUST_FLAGS)
129-
def interpolation(self, *, output, radius, factor, b, c):
129+
def gunn_and_kinzer_interpolation(self, *, output, radius, factor, b, c):
130130
factor_device = trtc.DVInt64(factor)
131-
self.__interpolation_body.launch_n(
131+
self.__gunn_and_kinzer_interpolation_body.launch_n(
132132
len(radius), (output.data, radius.data, factor_device, b.data, c.data)
133133
)
134134

@@ -157,7 +157,7 @@ def power_series(self, *, values, radius, num_terms, prefactors, powers):
157157
)
158158

159159
@cached_property
160-
def __terminal_velocity_body(self):
160+
def __rogers_and_yau_terminal_velocity_body(self):
161161
return trtc.For(
162162
param_names=("values", "radius"),
163163
name_iter="i",
@@ -171,5 +171,7 @@ def __terminal_velocity_body(self):
171171
)
172172

173173
@nice_thrust(**NICE_THRUST_FLAGS)
174-
def terminal_velocity(self, *, values, radius):
175-
self.__terminal_velocity_body.launch_n(n=values.size(), args=[values, radius])
174+
def rogers_and_yau_terminal_velocity(self, *, values, radius):
175+
self.__rogers_and_yau_terminal_velocity_body.launch_n(
176+
n=values.size(), args=[values, radius]
177+
)

PySDM/dynamics/relaxed_velocity.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def register(self, builder):
6363
"relative fall momentum"
6464
)
6565
self.terminal_vel_attr: Attribute = builder.get_attribute("terminal velocity")
66-
self.water_mass_attr: Attribute = builder.get_attribute("water mass")
66+
self.water_mass_attr: Attribute = builder.get_attribute("signed water mass")
6767
self.sqrt_radius_attr: Attribute = builder.get_attribute(
6868
"square root of radius"
6969
)

PySDM/dynamics/terminal_velocity/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@
55
from PySDM.dynamics.terminal_velocity.gunn_and_kinzer import GunnKinzer1949
66
from PySDM.dynamics.terminal_velocity.power_series import PowerSeries
77
from PySDM.dynamics.terminal_velocity.rogers_and_yau import RogersYau
8+
from PySDM.dynamics.terminal_velocity.columnar_ice_crystal import ColumnarIceCrystal
9+
from PySDM.dynamics.terminal_velocity.spheres_ice import IceSphere
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""
2+
[Spichtinger & Gierens 2009](https://doi.org/10.5194/acp-9-685-2009)
3+
Eq. (18) .Assumed shape is columnar based on empirical parameterizations of
4+
[Heymsfield & Iaquinta (2000)](https://doi.org/10.1175/1520-0469(2000)057%3C0916:CCTV%3E2.0.CO;2)
5+
[Barthazy & Schefold (2006)](https://doi.org/10.1016/j.atmosres.2005.12.009)
6+
"""
7+
8+
9+
class ColumnarIceCrystal: # pylint: disable=too-few-public-methods,too-many-arguments
10+
def __init__(self, particulator):
11+
self.particulator = particulator
12+
13+
def __call__(self, output, signed_water_mass, cell_id, temperature, pressure):
14+
self.particulator.backend.terminal_velocity_columnar_ice_crystals(
15+
values=output.data,
16+
signed_water_mass=signed_water_mass.data,
17+
cell_id=cell_id.data,
18+
temperature=temperature.data,
19+
pressure=pressure.data,
20+
)

PySDM/dynamics/terminal_velocity/gunn_and_kinzer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ def __call__(self, output, radius):
131131
f"Radii can be interpolated up to {self.maximum_radius} m"
132132
+ f" (max value of {r_max} m within input data)"
133133
)
134-
self.particulator.backend.interpolation(
134+
self.particulator.backend.gunn_and_kinzer_interpolation(
135135
output=output, radius=radius, factor=self.factor, b=self.a, c=self.b
136136
)
137137

PySDM/dynamics/terminal_velocity/rogers_and_yau.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ def __init__(self, particulator):
88
self.particulator = particulator
99

1010
def __call__(self, output, radius):
11-
self.particulator.backend.terminal_velocity(
11+
self.particulator.backend.rogers_and_yau_terminal_velocity(
1212
values=output.data,
1313
radius=radius.data,
1414
)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""
2+
terminal velocity of solid ice spheres
3+
"""
4+
5+
6+
class IceSphere: # pylint: disable=too-few-public-methods,too-many-arguments
7+
def __init__(self, particulator):
8+
self.particulator = particulator
9+
10+
def __call__(self, output, signed_water_mass, cell_id, temperature, pressure):
11+
self.particulator.backend.terminal_velocity_ice_spheres(
12+
values=output.data,
13+
signed_water_mass=signed_water_mass.data,
14+
cell_id=cell_id.data,
15+
temperature=temperature.data,
16+
pressure=pressure.data,
17+
)

0 commit comments

Comments
 (0)