Skip to content

Commit 9cc2e06

Browse files
authored
switching from volume to mass condensation coordinates, handling dm_dt(r_dr_dt) in particle_shape_and_density (#1538)
1 parent d39182a commit 9cc2e06

File tree

21 files changed

+209
-133
lines changed

21 files changed

+209
-133
lines changed

.github/workflows/tests.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -271,8 +271,8 @@ jobs:
271271
python -m pytest --durations=10 -v -p no:unraisableexception -We tests/examples_tests/test_tests_completeness.py
272272
273273
# TODO #1207
274-
- if: startsWith(matrix.platform, 'windows-')
274+
- if: startsWith(matrix.platform, 'macos-13')
275275
run: python -m pytest --durations=10 -v -p no:unraisableexception -We tests/examples_tests/test_run* -k "not Rozanski_and_Sonntag_1982" --suite ${{ matrix.test-suite }}
276276

277-
- if: ( ! startsWith(matrix.platform, 'windows-') )
277+
- if: ( ! startsWith(matrix.platform, 'macos-13') )
278278
run: python -m pytest --durations=10 -v -p no:unraisableexception -We tests/examples_tests/test_run* --suite ${{ matrix.test-suite }}

PySDM/backends/impl_numba/methods/condensation_methods.py

+21-12
Original file line numberDiff line numberDiff line change
@@ -390,14 +390,18 @@ def minfun( # pylint: disable=too-many-arguments,too-many-locals
390390
K,
391391
ventilation_factor,
392392
):
393-
volume = formulae.condensation_coordinate__volume(x_new)
393+
if x_new > formulae.condensation_coordinate__x_max():
394+
return x_old - x_new
395+
mass_new = formulae.condensation_coordinate__mass(x_new)
396+
volume_new = formulae.particle_shape_and_density__mass_to_volume(mass_new)
397+
r_new = formulae.trivia__radius(volume_new)
394398
RH_eq = formulae.hygroscopicity__RH_eq(
395-
formulae.trivia__radius(volume),
399+
r_new,
396400
temperature,
397401
kappa,
398402
rd3,
399403
formulae.surface_tension__sigma(
400-
temperature, volume, formulae.constants.PI_4_3 * rd3, f_org
404+
temperature, volume_new, formulae.constants.PI_4_3 * rd3, f_org
401405
),
402406
)
403407
r_dr_dt = formulae.drop_growth__r_dr_dt(
@@ -410,10 +414,11 @@ def minfun( # pylint: disable=too-many-arguments,too-many-locals
410414
K,
411415
ventilation_factor,
412416
)
417+
dm_dt = formulae.particle_shape_and_density__dm_dt(r=r_new, r_dr_dt=r_dr_dt)
413418
return (
414419
x_old
415420
- x_new
416-
+ timestep * formulae.condensation_coordinate__dx_dt(x_new, r_dr_dt)
421+
+ timestep * formulae.condensation_coordinate__dx_dt(mass_new, dm_dt)
417422
)
418423

419424
@numba.njit(**jit_flags)
@@ -440,15 +445,17 @@ def calculate_ml_new( # pylint: disable=too-many-branches,too-many-arguments,to
440445
lambdaK = formulae.diffusion_kinetics__lambdaK(T, p)
441446
lambdaD = formulae.diffusion_kinetics__lambdaD(DTp, T)
442447
for drop in cell_idx:
448+
if attributes.water_mass[drop] <= 0:
449+
continue
443450
v_drop = formulae.particle_shape_and_density__mass_to_volume(
444451
attributes.water_mass[drop]
445452
)
446-
if v_drop <= 0:
447-
continue
448-
x_old = formulae.condensation_coordinate__x(v_drop)
453+
x_old = formulae.condensation_coordinate__x(attributes.water_mass[drop])
449454
r_old = formulae.trivia__radius(v_drop)
450455
x_insane = formulae.condensation_coordinate__x(
451-
attributes.vdry[drop] / 100
456+
formulae.particle_shape_and_density__volume_to_mass(
457+
attributes.vdry[drop] / 100
458+
)
452459
)
453460
rd3 = attributes.vdry[drop] / formulae.constants.PI_4_3
454461
sgm = formulae.surface_tension__sigma(
@@ -492,8 +499,12 @@ def calculate_ml_new( # pylint: disable=too-many-branches,too-many-arguments,to
492499
Kr,
493500
ventilation_factor,
494501
)
502+
mass_old = formulae.condensation_coordinate__mass(x_old)
503+
dm_dt_old = formulae.particle_shape_and_density__dm_dt(
504+
r=r_old, r_dr_dt=r_dr_dt_old
505+
)
495506
dx_old = timestep * formulae.condensation_coordinate__dx_dt(
496-
x_old, r_dr_dt_old
507+
mass_old, dm_dt_old
497508
)
498509
else:
499510
dx_old = 0.0
@@ -561,9 +572,7 @@ def calculate_ml_new( # pylint: disable=too-many-branches,too-many-arguments,to
561572
else:
562573
x_new = x_old
563574

564-
mass_new = formulae.particle_shape_and_density__volume_to_mass(
565-
formulae.condensation_coordinate__volume(x_new)
566-
)
575+
mass_new = formulae.condensation_coordinate__mass(x_new)
567576
mass_cr = formulae.particle_shape_and_density__volume_to_mass(
568577
attributes.v_cr[drop]
569578
)

PySDM/backends/impl_numba/test_helpers/scipy_ode_condensation_solver.py

+20-41
Original file line numberDiff line numberDiff line change
@@ -92,15 +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 (
96-
np.sum(
97-
n
98-
* jit_formulae.particle_shape_and_density__volume_to_mass(
99-
jit_formulae.condensation_coordinate__volume(x)
100-
)
101-
)
102-
/ m_d_mean
103-
)
95+
return np.sum(n * jit_formulae.condensation_coordinate__mass(x)) / m_d_mean
10496

10597
@numba.njit(**{**JIT_FLAGS, **{"parallel": False}})
10698
def _impl( # pylint: disable=too-many-arguments,too-many-locals
@@ -133,8 +125,10 @@ def _impl( # pylint: disable=too-many-arguments,too-many-locals
133125
diffusivity=DTp,
134126
density=air_density,
135127
)
128+
sum_n_dm_dt = 0
136129
for i, x_i in enumerate(x):
137-
v = jit_formulae.condensation_coordinate__volume(x_i)
130+
m = jit_formulae.condensation_coordinate__mass(x_i)
131+
v = jit_formulae.particle_shape_and_density__mass_to_volume(m)
138132
r = jit_formulae.trivia__radius(v)
139133
Dr = jit_formulae.diffusion_kinetics__D(DTp, r, lambdaD)
140134
Kr = jit_formulae.diffusion_kinetics__K(KTp, r, lambdaK)
@@ -145,34 +139,23 @@ def _impl( # pylint: disable=too-many-arguments,too-many-locals
145139
)
146140
)
147141
sgm = jit_formulae.surface_tension__sigma(T, v, dry_volume[i], f_org[i])
148-
dy_dt[idx_x + i] = jit_formulae.condensation_coordinate__dx_dt(
149-
x_i,
150-
jit_formulae.drop_growth__r_dr_dt(
151-
jit_formulae.hygroscopicity__RH_eq(
152-
r, T, kappa[i], dry_volume[i] / PI_4_3, sgm
153-
),
154-
T,
155-
RH,
156-
lv,
157-
pvs,
158-
Dr,
159-
Kr,
160-
ventilation_factor,
142+
r_dr_dt = jit_formulae.drop_growth__r_dr_dt(
143+
jit_formulae.hygroscopicity__RH_eq(
144+
r, T, kappa[i], dry_volume[i] / PI_4_3, sgm
161145
),
146+
T,
147+
RH,
148+
lv,
149+
pvs,
150+
Dr,
151+
Kr,
152+
ventilation_factor,
162153
)
163-
d_water_vapour_mixing_ratio__dt = (
164-
dot_water_vapour_mixing_ratio
165-
- np.sum(
166-
n
167-
* jit_formulae.particle_shape_and_density__volume_to_mass(
168-
jit_formulae.condensation_coordinate__volume(x)
169-
)
170-
* dy_dt[idx_x:]
171-
)
172-
/ m_d_mean
173-
)
154+
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)
156+
sum_n_dm_dt += n[i] * dm_dt
174157
dy_dt[idx_thd] = dot_thd + jit_formulae.state_variable_triplet__dthd_dt(
175-
rhod, thd, T, d_water_vapour_mixing_ratio__dt, lv
158+
rhod, thd, T, dot_water_vapour_mixing_ratio - sum_n_dm_dt / m_d_mean, lv
176159
)
177160

178161
@numba.njit(**{**JIT_FLAGS, **{"parallel": False}})
@@ -252,9 +235,7 @@ def solve( # pylint: disable=too-many-arguments,too-many-locals,unused-argument
252235
y0 = np.empty(n_sd_in_cell + idx_x)
253236
y0[idx_thd] = thd
254237
y0[idx_x:] = jit_formulae.condensation_coordinate__x(
255-
jit_formulae.particle_shape_and_density__mass_to_volume(
256-
attributes.water_mass[cell_idx]
257-
)
238+
attributes.water_mass[cell_idx]
258239
)
259240
total_water_mixing_ratio = (
260241
water_vapour_mixing_ratio
@@ -301,9 +282,7 @@ def solve( # pylint: disable=too-many-arguments,too-many-locals,unused-argument
301282
m_new = 0
302283
for i in range(n_sd_in_cell):
303284
attributes.water_mass[cell_idx[i]] = (
304-
jit_formulae.particle_shape_and_density__volume_to_mass(
305-
jit_formulae.condensation_coordinate__volume(y1[idx_x + i])
306-
)
285+
jit_formulae.condensation_coordinate__mass(y1[idx_x + i])
307286
)
308287
m_new += (
309288
attributes.multiplicity[cell_idx[i]]

PySDM/backends/impl_thrust_rtc/methods/condensation_methods.py

+18-11
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,15 @@ 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-
auto vol = {phys.condensation_coordinate.volume.c_inline(x="x_new")};
104-
auto r_new = {phys.trivia.radius.c_inline(volume="vol")};
103+
if (x_new > {phys.condensation_coordinate.x_max.c_inline()}) {{
104+
return {args("x_old")} - x_new;
105+
}}
106+
auto m_new = {phys.condensation_coordinate.mass.c_inline(x="x_new")};
107+
auto v_new = {phys.particle_shape_and_density.mass_to_volume.c_inline(mass="m_new")};
108+
auto r_new = {phys.trivia.radius.c_inline(volume="v_new")};
105109
auto sgm = {phys.surface_tension.sigma.c_inline(
106110
T=args('_T'),
107-
v_wet="vol",
111+
v_wet="v_new",
108112
v_dry=f"const.PI_4_3 * {args('rd3')}",
109113
f_org=args("f_org")
110114
)};
@@ -125,8 +129,11 @@ def __update_drop_masses(self):
125129
K=args("Kr"),
126130
ventilation_factor=args("ventilation_factor"),
127131
)};
132+
auto dm_dt = {phys.particle_shape_and_density.dm_dt.c_inline(
133+
r="r_new", r_dr_dt="r_dr_dt"
134+
)};
128135
return {args("x_old")} - x_new + {args("dt")} * {
129-
phys.condensation_coordinate.dx_dt.c_inline(x="x_new", r_dr_dt="r_dr_dt")
136+
phys.condensation_coordinate.dx_dt.c_inline(m="m_new", dm_dt="dm_dt")
130137
};
131138
}}
132139
}};
@@ -145,9 +152,10 @@ def __update_drop_masses(self):
145152
auto v_old = {phys.particle_shape_and_density.mass_to_volume.c_inline(
146153
mass="water_mass[i]"
147154
)};
148-
auto x_old = {phys.condensation_coordinate.x.c_inline(volume="v_old")};
155+
auto x_old = {phys.condensation_coordinate.x.c_inline(mass="water_mass[i]")};
149156
auto r_old = {phys.trivia.radius.c_inline(volume="v_old")};
150-
auto x_insane = {phys.condensation_coordinate.x.c_inline(volume="vdry[i]/100")};
157+
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")};
151159
auto rd3 = vdry[i] / {const.PI_4_3};
152160
auto sgm = {phys.surface_tension.sigma.c_inline(
153161
T="_T", v_wet="v", v_dry="vdry[i]", f_org="_f_org[i]"
@@ -161,6 +169,7 @@ def __update_drop_masses(self):
161169
real_type qrt_re_times_cbrt_sc=0;
162170
real_type ventilation_factor=0;
163171
real_type r_dr_dt_old=0;
172+
real_type dm_dt_old=0;
164173
real_type dx_old=0;
165174
166175
real_type x_new = 0;
@@ -184,8 +193,9 @@ def __update_drop_masses(self):
184193
RH_eq="RH_eq", T="_T", RH="_RH", lv="_lv", pvs="_pvs", D="Dr", K="Kr",
185194
ventilation_factor="ventilation_factor",
186195
)};
196+
dm_dt_old = {phys.particle_shape_and_density.dm_dt.c_inline(r="r_old", r_dr_dt="r_dr_dt_old")};
187197
dx_old = dt * {phys.condensation_coordinate.dx_dt.c_inline(
188-
x="x_old", r_dr_dt="r_dr_dt_old"
198+
m="water_mass[i]", dm_dt="dm_dt_old"
189199
)};
190200
}}
191201
else {{
@@ -239,10 +249,7 @@ def __update_drop_masses(self):
239249
x_new = x_old;
240250
}}
241251
}}
242-
auto v_new = {phys.condensation_coordinate.volume.c_inline(x="x_new")};
243-
water_mass[i] = {phys.particle_shape_and_density.volume_to_mass.c_inline(
244-
volume="v_new"
245-
)};
252+
water_mass[i] = {phys.condensation_coordinate.mass.c_inline(x="x_new")};
246253
""".replace(
247254
"real_type", self._get_c_type()
248255
),

PySDM/formulae.py

+8-5
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ def __init__( # pylint: disable=too-many-locals
3030
constants: Optional[dict] = None,
3131
seed: int = None,
3232
fastmath: bool = True,
33-
condensation_coordinate: str = "VolumeLogarithm",
33+
condensation_coordinate: str = "WaterMassLogarithm",
3434
saturation_vapour_pressure: str = "FlatauWalkoCotton",
3535
latent_heat: str = "Kirchhoff",
3636
hygroscopicity: str = "KappaKoehlerLeadingTerms",
@@ -203,9 +203,11 @@ def _formula(func, constants, dimensional_analysis, **kw):
203203
source = re.sub(r"\n\s+\):", "):", source)
204204
loc = {}
205205
for arg_name in special_params:
206-
source = source.replace(
207-
f"def {func.__name__}({arg_name},", f"def {func.__name__}("
208-
)
206+
for sep in ",", ")":
207+
source = source.replace(
208+
f"def {func.__name__}({arg_name}{sep}",
209+
f"def {func.__name__}({')' if sep == ')' else ''}",
210+
)
209211

210212
extras = func.__extras if hasattr(func, "__extras") else {}
211213
exec( # pylint:disable=exec-used
@@ -281,7 +283,8 @@ def _c_inline(fun, return_type=None, constants=None, **args):
281283
if stripped.startswith("//"):
282284
continue
283285
if stripped.startswith('"""'):
284-
in_docstring = True
286+
if not (stripped.endswith('"""') and len(stripped) >= 6):
287+
in_docstring = True
285288
continue
286289
if stripped.startswith("def "):
287290
continue

PySDM/physics/condensation_coordinate/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
definitions of particle-size coordinates for the condensation solver
33
"""
44

5-
from .volume import Volume
6-
from .volume_logarithm import VolumeLogarithm
5+
from .water_mass import WaterMass
6+
from .water_mass_logarithm import WaterMassLogarithm

PySDM/physics/condensation_coordinate/volume.py

-22
This file was deleted.

PySDM/physics/condensation_coordinate/volume_logarithm.py

-27
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""
2+
particle water mass as condensation coordinate (i.e. no transformation)
3+
"""
4+
5+
6+
class WaterMass:
7+
def __init__(self, _):
8+
pass
9+
10+
@staticmethod
11+
def dx_dt(m, dm_dt): # pylint: disable=unused-argument
12+
return dm_dt
13+
14+
@staticmethod
15+
def mass(x):
16+
return x
17+
18+
@staticmethod
19+
def x(mass):
20+
return mass
21+
22+
@staticmethod
23+
def x_max(const):
24+
"""1 kg droplet!"""
25+
return const.ONE

0 commit comments

Comments
 (0)