Skip to content

Commit 1e7ed03

Browse files
slayootluettm
andauthored
vapour deposition on ice (temporarily as a separate dynamic - to be combined with condensation); introducing physics.latent_heat_sublimation (and renamings in physics: latent_heat to latent_heat_vapourisation, condensation_coordinate to diffusion_coordinate) (#1389)
Co-authored-by: Tim Luettmer <[email protected]>
1 parent f8f746b commit 1e7ed03

File tree

47 files changed

+5760
-410
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+5760
-410
lines changed

PySDM/backends/impl_numba/methods/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@
1313
from .physics_methods import PhysicsMethods
1414
from .terminal_velocity_methods import TerminalVelocityMethods
1515
from .seeding_methods import SeedingMethods
16+
from .deposition_methods import DepositionMethods

PySDM/backends/impl_numba/methods/condensation_methods.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ def step_impl( # pylint: disable=too-many-arguments,too-many-locals
290290
rhod, T, water_vapour_mixing_ratio
291291
)
292292
pv = formulae.state_variable_triplet__pv(p, water_vapour_mixing_ratio)
293-
lv = formulae.latent_heat__lv(T)
293+
lv = formulae.latent_heat_vapourisation__lv(T)
294294
pvs = formulae.saturation_vapour_pressure__pvs_water(T)
295295
DTp = formulae.diffusion_thermics__D(T, p)
296296
RH = pv / pvs
@@ -390,9 +390,9 @@ def minfun( # pylint: disable=too-many-arguments,too-many-locals
390390
K,
391391
ventilation_factor,
392392
):
393-
if x_new > formulae.condensation_coordinate__x_max():
393+
if x_new > formulae.diffusion_coordinate__x_max():
394394
return x_old - x_new
395-
mass_new = formulae.condensation_coordinate__mass(x_new)
395+
mass_new = formulae.diffusion_coordinate__mass(x_new)
396396
volume_new = formulae.particle_shape_and_density__mass_to_volume(mass_new)
397397
r_new = formulae.trivia__radius(volume_new)
398398
RH_eq = formulae.hygroscopicity__RH_eq(
@@ -418,7 +418,7 @@ def minfun( # pylint: disable=too-many-arguments,too-many-locals
418418
return (
419419
x_old
420420
- x_new
421-
+ timestep * formulae.condensation_coordinate__dx_dt(mass_new, dm_dt)
421+
+ timestep * formulae.diffusion_coordinate__dx_dt(mass_new, dm_dt)
422422
)
423423

424424
@numba.njit(**jit_flags)
@@ -450,9 +450,9 @@ def calculate_ml_new( # pylint: disable=too-many-branches,too-many-arguments,to
450450
v_drop = formulae.particle_shape_and_density__mass_to_volume(
451451
attributes.water_mass[drop]
452452
)
453-
x_old = formulae.condensation_coordinate__x(attributes.water_mass[drop])
453+
x_old = formulae.diffusion_coordinate__x(attributes.water_mass[drop])
454454
r_old = formulae.trivia__radius(v_drop)
455-
x_insane = formulae.condensation_coordinate__x(
455+
x_insane = formulae.diffusion_coordinate__x(
456456
formulae.particle_shape_and_density__volume_to_mass(
457457
attributes.vdry[drop] / 100
458458
)
@@ -499,11 +499,11 @@ def calculate_ml_new( # pylint: disable=too-many-branches,too-many-arguments,to
499499
Kr,
500500
ventilation_factor,
501501
)
502-
mass_old = formulae.condensation_coordinate__mass(x_old)
502+
mass_old = formulae.diffusion_coordinate__mass(x_old)
503503
dm_dt_old = formulae.particle_shape_and_density__dm_dt(
504504
r=r_old, r_dr_dt=r_dr_dt_old
505505
)
506-
dx_old = timestep * formulae.condensation_coordinate__dx_dt(
506+
dx_old = timestep * formulae.diffusion_coordinate__dx_dt(
507507
mass_old, dm_dt_old
508508
)
509509
else:
@@ -572,7 +572,7 @@ def calculate_ml_new( # pylint: disable=too-many-branches,too-many-arguments,to
572572
else:
573573
x_new = x_old
574574

575-
mass_new = formulae.condensation_coordinate__mass(x_new)
575+
mass_new = formulae.diffusion_coordinate__mass(x_new)
576576
mass_cr = formulae.particle_shape_and_density__volume_to_mass(
577577
attributes.v_cr[drop]
578578
)
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
"""basic water vapor deposition on ice for CPU backend, for Howell factor see:
2+
[Howell 1949](https://doi.org/10.1175/1520-0469(1949)006%3C0134:TGOCDI%3E2.0.CO;2)
3+
"""
4+
5+
from functools import cached_property
6+
import numba
7+
import numpy as np
8+
from PySDM.backends.impl_common.backend_methods import BackendMethods
9+
10+
11+
class DepositionMethods(BackendMethods): # pylint:disable=too-few-public-methods
12+
@cached_property
13+
def _deposition(self):
14+
assert self.formulae.particle_shape_and_density.supports_mixed_phase()
15+
16+
formulae = self.formulae_flattened
17+
18+
@numba.njit(**{**self.default_jit_flags, **{"parallel": False}})
19+
def body( # pylint: disable=too-many-arguments
20+
*,
21+
multiplicity,
22+
signed_water_mass,
23+
current_temperature,
24+
current_total_pressure,
25+
current_relative_humidity,
26+
current_water_activity,
27+
current_vapour_mixing_ratio,
28+
current_dry_air_density,
29+
current_dry_potential_temperature,
30+
cell_volume,
31+
time_step,
32+
cell_id,
33+
reynolds_number,
34+
schmidt_number,
35+
# to be modified
36+
predicted_vapour_mixing_ratio,
37+
predicted_dry_potential_temperature,
38+
):
39+
# pylint: disable=too-many-locals
40+
n_sd = len(signed_water_mass)
41+
for i in range(n_sd):
42+
if not formulae.trivia__unfrozen(signed_water_mass[i]):
43+
ice_mass = -signed_water_mass[i]
44+
cid = cell_id[i]
45+
46+
radius = formulae.particle_shape_and_density__mass_to_radius(
47+
signed_water_mass[i]
48+
)
49+
50+
diameter = radius * 2.0
51+
52+
temperature = current_temperature[cid]
53+
pressure = current_total_pressure[cid]
54+
rho = current_dry_air_density[cid]
55+
pvs_ice = formulae.saturation_vapour_pressure__pvs_ice(temperature)
56+
latent_heat_sub = formulae.latent_heat_sublimation__ls(temperature)
57+
58+
capacity = formulae.diffusion_ice_capacity__capacity(diameter)
59+
60+
ventilation_factor = formulae.ventilation__ventilation_coefficient(
61+
sqrt_re_times_cbrt_sc=formulae.trivia__sqrt_re_times_cbrt_sc(
62+
Re=reynolds_number[i],
63+
Sc=schmidt_number[cid],
64+
)
65+
)
66+
67+
Dv_const = formulae.diffusion_thermics__D(temperature, pressure)
68+
lambdaD = formulae.diffusion_ice_kinetics__lambdaD(
69+
temperature, pressure
70+
)
71+
diffusion_coefficient = formulae.diffusion_ice_kinetics__D(
72+
Dv_const, radius, lambdaD, temperature
73+
)
74+
75+
Ka_const = formulae.diffusion_thermics__K(temperature, pressure)
76+
lambdaK = formulae.diffusion_ice_kinetics__lambdaK(
77+
temperature, pressure
78+
)
79+
thermal_conductivity = formulae.diffusion_ice_kinetics__K(
80+
Ka_const, radius, lambdaK, temperature, rho
81+
)
82+
saturation_ratio_ice = (
83+
current_relative_humidity[cid] / current_water_activity[cid]
84+
)
85+
86+
if saturation_ratio_ice == 1:
87+
continue
88+
89+
howell_factor_x_diffcoef_x_rhovsice_x_icess = (
90+
formulae.drop_growth__r_dr_dt(
91+
RH_eq=1,
92+
T=temperature,
93+
RH=saturation_ratio_ice,
94+
lv=latent_heat_sub,
95+
pvs=pvs_ice,
96+
D=diffusion_coefficient,
97+
K=thermal_conductivity,
98+
ventilation_factor=ventilation_factor,
99+
)
100+
* formulae.constants.rho_w
101+
)
102+
103+
dm_dt = (
104+
4
105+
* np.pi
106+
* capacity
107+
* howell_factor_x_diffcoef_x_rhovsice_x_icess
108+
)
109+
110+
delta_rv_i = (
111+
-dm_dt * multiplicity[i] * time_step / (cell_volume * rho)
112+
)
113+
if -delta_rv_i > current_vapour_mixing_ratio[cid]:
114+
assert False
115+
predicted_vapour_mixing_ratio[cid] += delta_rv_i
116+
117+
predicted_dry_potential_temperature[cid] += (
118+
formulae.state_variable_triplet__dthd_dt(
119+
rhod=current_dry_air_density[cid],
120+
thd=current_dry_potential_temperature[cid],
121+
T=temperature,
122+
d_water_vapour_mixing_ratio__dt=delta_rv_i / time_step,
123+
lv=latent_heat_sub,
124+
)
125+
* time_step
126+
)
127+
128+
x_old = formulae.diffusion_coordinate__x(ice_mass)
129+
dx_dt_old = formulae.diffusion_coordinate__dx_dt(ice_mass, dm_dt)
130+
x_new = formulae.trivia__explicit_euler(x_old, time_step, dx_dt_old)
131+
signed_water_mass[i] = -formulae.diffusion_coordinate__mass(x_new)
132+
133+
return body
134+
135+
def deposition( # pylint: disable=too-many-locals
136+
self,
137+
*,
138+
multiplicity,
139+
signed_water_mass,
140+
current_temperature,
141+
current_total_pressure,
142+
current_relative_humidity,
143+
current_water_activity,
144+
current_vapour_mixing_ratio,
145+
current_dry_air_density,
146+
current_dry_potential_temperature,
147+
cell_volume,
148+
time_step,
149+
cell_id,
150+
reynolds_number,
151+
schmidt_number,
152+
predicted_vapour_mixing_ratio,
153+
predicted_dry_potential_temperature,
154+
):
155+
self._deposition(
156+
multiplicity=multiplicity.data,
157+
signed_water_mass=signed_water_mass.data,
158+
current_temperature=current_temperature.data,
159+
current_total_pressure=current_total_pressure.data,
160+
current_relative_humidity=current_relative_humidity.data,
161+
current_water_activity=current_water_activity.data,
162+
current_vapour_mixing_ratio=current_vapour_mixing_ratio.data,
163+
current_dry_air_density=current_dry_air_density.data,
164+
current_dry_potential_temperature=current_dry_potential_temperature.data,
165+
cell_volume=cell_volume,
166+
time_step=time_step,
167+
cell_id=cell_id.data,
168+
reynolds_number=reynolds_number.data,
169+
schmidt_number=schmidt_number.data,
170+
predicted_vapour_mixing_ratio=predicted_vapour_mixing_ratio.data,
171+
predicted_dry_potential_temperature=predicted_dry_potential_temperature.data,
172+
)

PySDM/backends/impl_numba/test_helpers/scipy_ode_condensation_solver.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def _make_solve(formulae): # pylint: disable=too-many-statements,too-many-local
9292

9393
@numba.njit(**{**JIT_FLAGS, **{"parallel": False}})
9494
def _liquid_water_mixing_ratio(n, x, m_d_mean):
95-
return np.sum(n * jit_formulae.condensation_coordinate__mass(x)) / m_d_mean
95+
return np.sum(n * jit_formulae.diffusion_coordinate__mass(x)) / m_d_mean
9696

9797
@numba.njit(**{**JIT_FLAGS, **{"parallel": False}})
9898
def _impl( # pylint: disable=too-many-arguments,too-many-locals
@@ -127,7 +127,7 @@ def _impl( # pylint: disable=too-many-arguments,too-many-locals
127127
)
128128
sum_n_dm_dt = 0
129129
for i, x_i in enumerate(x):
130-
m = jit_formulae.condensation_coordinate__mass(x_i)
130+
m = jit_formulae.diffusion_coordinate__mass(x_i)
131131
v = jit_formulae.particle_shape_and_density__mass_to_volume(m)
132132
r = jit_formulae.trivia__radius(v)
133133
Dr = jit_formulae.diffusion_kinetics__D(DTp, r, lambdaD)
@@ -152,7 +152,7 @@ def _impl( # pylint: disable=too-many-arguments,too-many-locals
152152
ventilation_factor,
153153
)
154154
dm_dt = jit_formulae.particle_shape_and_density__dm_dt(r, r_dr_dt)
155-
dy_dt[idx_x + i] = jit_formulae.condensation_coordinate__dx_dt(m, dm_dt)
155+
dy_dt[idx_x + i] = jit_formulae.diffusion_coordinate__dx_dt(m, dm_dt)
156156
sum_n_dm_dt += n[i] * dm_dt
157157
dy_dt[idx_thd] = dot_thd + jit_formulae.state_variable_triplet__dthd_dt(
158158
rhod, thd, T, dot_water_vapour_mixing_ratio - sum_n_dm_dt / m_d_mean, lv
@@ -211,7 +211,7 @@ def _odesys( # pylint: disable=too-many-arguments,too-many-locals
211211
air_dynamic_viscosity,
212212
rhod,
213213
pvs,
214-
jit_formulae.latent_heat__lv(T),
214+
jit_formulae.latent_heat_vapourisation__lv(T),
215215
)
216216
return dy_dt
217217

@@ -234,7 +234,7 @@ def solve( # pylint: disable=too-many-arguments,too-many-locals,unused-argument
234234
n_sd_in_cell = len(cell_idx)
235235
y0 = np.empty(n_sd_in_cell + idx_x)
236236
y0[idx_thd] = thd
237-
y0[idx_x:] = jit_formulae.condensation_coordinate__x(
237+
y0[idx_x:] = jit_formulae.diffusion_coordinate__x(
238238
attributes.water_mass[cell_idx]
239239
)
240240
total_water_mixing_ratio = (
@@ -282,7 +282,7 @@ def solve( # pylint: disable=too-many-arguments,too-many-locals,unused-argument
282282
m_new = 0
283283
for i in range(n_sd_in_cell):
284284
attributes.water_mass[cell_idx[i]] = (
285-
jit_formulae.condensation_coordinate__mass(y1[idx_x + i])
285+
jit_formulae.diffusion_coordinate__mass(y1[idx_x + i])
286286
)
287287
m_new += (
288288
attributes.multiplicity[cell_idx[i]]

PySDM/backends/impl_thrust_rtc/methods/condensation_methods.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,10 @@ def __update_drop_masses(self):
100100
struct Minfun {{
101101
static __device__ real_type value(real_type x_new, void* args_p) {{
102102
auto args = static_cast<real_type*>(args_p);
103-
if (x_new > {phys.condensation_coordinate.x_max.c_inline()}) {{
103+
if (x_new > {phys.diffusion_coordinate.x_max.c_inline()}) {{
104104
return {args("x_old")} - x_new;
105105
}}
106-
auto m_new = {phys.condensation_coordinate.mass.c_inline(x="x_new")};
106+
auto m_new = {phys.diffusion_coordinate.mass.c_inline(x="x_new")};
107107
auto v_new = {phys.particle_shape_and_density.mass_to_volume.c_inline(mass="m_new")};
108108
auto r_new = {phys.trivia.radius.c_inline(volume="v_new")};
109109
auto sgm = {phys.surface_tension.sigma.c_inline(
@@ -133,7 +133,7 @@ def __update_drop_masses(self):
133133
r="r_new", r_dr_dt="r_dr_dt"
134134
)};
135135
return {args("x_old")} - x_new + {args("dt")} * {
136-
phys.condensation_coordinate.dx_dt.c_inline(m="m_new", dm_dt="dm_dt")
136+
phys.diffusion_coordinate.dx_dt.c_inline(m="m_new", dm_dt="dm_dt")
137137
};
138138
}}
139139
}};
@@ -152,10 +152,10 @@ def __update_drop_masses(self):
152152
auto v_old = {phys.particle_shape_and_density.mass_to_volume.c_inline(
153153
mass="water_mass[i]"
154154
)};
155-
auto x_old = {phys.condensation_coordinate.x.c_inline(mass="water_mass[i]")};
155+
auto x_old = {phys.diffusion_coordinate.x.c_inline(mass="water_mass[i]")};
156156
auto r_old = {phys.trivia.radius.c_inline(volume="v_old")};
157157
auto m_insane = {phys.particle_shape_and_density.volume_to_mass.c_inline(volume="vdry[i] / 100")};
158-
auto x_insane = {phys.condensation_coordinate.x.c_inline(mass="m_insane")};
158+
auto x_insane = {phys.diffusion_coordinate.x.c_inline(mass="m_insane")};
159159
auto rd3 = vdry[i] / {const.PI_4_3};
160160
auto sgm = {phys.surface_tension.sigma.c_inline(
161161
T="_T", v_wet="v", v_dry="vdry[i]", f_org="_f_org[i]"
@@ -194,7 +194,7 @@ def __update_drop_masses(self):
194194
ventilation_factor="ventilation_factor",
195195
)};
196196
dm_dt_old = {phys.particle_shape_and_density.dm_dt.c_inline(r="r_old", r_dr_dt="r_dr_dt_old")};
197-
dx_old = dt * {phys.condensation_coordinate.dx_dt.c_inline(
197+
dx_old = dt * {phys.diffusion_coordinate.dx_dt.c_inline(
198198
m="water_mass[i]", dm_dt="dm_dt_old"
199199
)};
200200
}}
@@ -249,7 +249,7 @@ def __update_drop_masses(self):
249249
x_new = x_old;
250250
}}
251251
}}
252-
water_mass[i] = {phys.condensation_coordinate.mass.c_inline(x="x_new")};
252+
water_mass[i] = {phys.diffusion_coordinate.mass.c_inline(x="x_new")};
253253
""".replace(
254254
"real_type", self._get_c_type()
255255
),
@@ -323,7 +323,7 @@ def __pre(self):
323323
)};
324324
pv[i] = {phys.state_variable_triplet.pv.c_inline(
325325
p='p[i]', water_vapour_mixing_ratio='predicted_water_vapour_mixing_ratio[i]')};
326-
lv[i] = {phys.latent_heat.lv.c_inline(
326+
lv[i] = {phys.latent_heat_vapourisation.lv.c_inline(
327327
T='T[i]')};
328328
pvs[i] = {phys.saturation_vapour_pressure.pvs_water.c_inline(
329329
T='T[i]')};

PySDM/backends/numba.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class Numba( # pylint: disable=too-many-ancestors,duplicate-code
2929
methods.TerminalVelocityMethods,
3030
methods.IsotopeMethods,
3131
methods.SeedingMethods,
32+
methods.DepositionMethods,
3233
):
3334
Storage = ImportedStorage
3435
Random = ImportedRandom
@@ -78,3 +79,4 @@ def __init__(self, formulae=None, double_precision=True, override_jit_flags=None
7879
methods.TerminalVelocityMethods.__init__(self)
7980
methods.IsotopeMethods.__init__(self)
8081
methods.SeedingMethods.__init__(self)
82+
methods.DepositionMethods.__init__(self)

PySDM/dynamics/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@
1616
from PySDM.dynamics.freezing import Freezing
1717
from PySDM.dynamics.relaxed_velocity import RelaxedVelocity
1818
from PySDM.dynamics.seeding import Seeding
19+
from PySDM.dynamics.vapour_deposition_on_ice import VapourDepositionOnIce
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""basic water vapor deposition on ice"""
2+
3+
from PySDM.dynamics.impl import register_dynamic
4+
5+
6+
@register_dynamic()
7+
class VapourDepositionOnIce:
8+
def __init__(self):
9+
"""called by the user while building a particulator"""
10+
self.particulator = None
11+
12+
def register(self, *, builder):
13+
"""called by the builder"""
14+
self.particulator = builder.particulator
15+
assert builder.formulae.particle_shape_and_density.supports_mixed_phase()
16+
builder.request_attribute("Reynolds number")
17+
18+
def __call__(self):
19+
"""called by the particulator during simulation"""
20+
self.particulator.deposition()

0 commit comments

Comments
 (0)