Skip to content

Commit 6187706

Browse files
tluettmslayoo
andauthored
new tests for mixed-phase moments, signed-water-mass attribute, radius_to_mass() for mixed phased spheres (#1513)
Co-authored-by: Sylwester Arabas <sylwester.arabas@agh.edu.pl>
1 parent 17cf24a commit 6187706

File tree

28 files changed

+762
-774
lines changed

28 files changed

+762
-774
lines changed

PySDM/attributes/isotopes/moles.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ def __init__(self, builder):
6262
/ M_H2O,
6363
builder.get_attribute("moles_18O"): -(2 * const.M_1H + const.M_18O)
6464
/ M_H2O,
65-
builder.get_attribute("water mass"): 1 / M_H2O,
65+
builder.get_attribute("signed water mass"): 1 / M_H2O,
6666
},
6767
)
6868

PySDM/attributes/physics/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from .equilibrium_supersaturation import EquilibriumSupersaturation
1111
from .heat import Heat
1212
from .hygroscopicity import Kappa, KappaTimesDryVolume
13-
from .water_mass import WaterMass
13+
from .water_mass import SignedWaterMass
1414
from .multiplicity import Multiplicity
1515
from .radius import Radius, SquareRootOfRadius
1616
from .relative_fall_velocity import RelativeFallMomentum, RelativeFallVelocity
Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,63 @@
11
"""
2-
particle mass, derived attribute for coalescence
3-
in simulation involving mixed-phase clouds, positive values correspond to
2+
particle mass attributes
3+
in simulations involving mixed-phase clouds, positive values correspond to
44
liquid water and negative values to ice
55
"""
66

7-
from PySDM.attributes.impl import ExtensiveAttribute, register_attribute
7+
from PySDM.attributes.impl import (
8+
ExtensiveAttribute,
9+
DerivedAttribute,
10+
register_attribute,
11+
)
812

913

1014
@register_attribute()
11-
class WaterMass(ExtensiveAttribute):
15+
class SignedWaterMass(ExtensiveAttribute):
1216
def __init__(self, builder):
13-
super().__init__(builder, name="water mass")
17+
super().__init__(builder, name="signed water mass")
18+
19+
20+
@register_attribute(
21+
name="water mass",
22+
variant=lambda _, formulae: not formulae.particle_shape_and_density.supports_mixed_phase(),
23+
)
24+
class ViewWaterMass(DerivedAttribute):
25+
def __init__(self, builder):
26+
self.signed_water_mass = builder.get_attribute("signed water mass")
27+
28+
super().__init__(
29+
builder,
30+
name="water mass",
31+
dependencies=(self.signed_water_mass,),
32+
)
33+
34+
def mark_updated(self):
35+
self.signed_water_mass.mark_updated()
36+
37+
def allocate(self, idx):
38+
pass
39+
40+
def recalculate(self):
41+
pass
42+
43+
def get(self):
44+
return self.signed_water_mass.data
45+
46+
47+
@register_attribute(
48+
name="water mass",
49+
variant=lambda _, formulae: formulae.particle_shape_and_density.supports_mixed_phase(),
50+
)
51+
class AbsWaterMass(DerivedAttribute):
52+
def __init__(self, builder):
53+
self.signed_water_mass = builder.get_attribute("signed water mass")
54+
55+
super().__init__(
56+
builder,
57+
name="water mass",
58+
dependencies=(self.signed_water_mass,),
59+
)
60+
61+
def recalculate(self):
62+
self.data.fill(self.signed_water_mass.data)
63+
self.data.abs()

PySDM/backends/impl_common/freezing_attributes.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
"""
2-
groups of attributes used in either singular or time-dependent immmersion freezing regimes
2+
groups of attributes used in either singular or time-dependent immersion freezing regimes
33
"""
44

55
from collections import namedtuple
66

77

88
class SingularAttributes(
9-
namedtuple("SingularAttributes", ("freezing_temperature", "water_mass"))
9+
namedtuple(
10+
typename="SingularAttributes",
11+
field_names=("freezing_temperature", "signed_water_mass"),
12+
)
1013
):
1114
"""groups attributes required in singular regime"""
1215

1316
__slots__ = ()
1417

1518

1619
class TimeDependentAttributes(
17-
namedtuple("TimeDependentAttributes", ("immersed_surface_area", "water_mass"))
20+
namedtuple(
21+
typename="TimeDependentAttributes",
22+
field_names=("immersed_surface_area", "signed_water_mass"),
23+
)
1824
):
1925
"""groups attributes required in time-dependent regime"""
2026

PySDM/backends/impl_numba/methods/freezing_methods.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,16 @@ def freeze_singular_body(
3939
if attributes.freezing_temperature[i] == 0:
4040
continue
4141
if thaw and frozen_and_above_freezing_point(
42-
attributes.water_mass[i], temperature[cell[i]]
42+
attributes.signed_water_mass[i], temperature[cell[i]]
4343
):
44-
_thaw(attributes.water_mass, i)
44+
_thaw(attributes.signed_water_mass, i)
4545
elif (
4646
unfrozen_and_saturated(
47-
attributes.water_mass[i], relative_humidity[cell[i]]
47+
attributes.signed_water_mass[i], relative_humidity[cell[i]]
4848
)
4949
and temperature[cell[i]] <= attributes.freezing_temperature[i]
5050
):
51-
_freeze(attributes.water_mass, i)
51+
_freeze(attributes.signed_water_mass, i)
5252

5353
self.freeze_singular_body = freeze_singular_body
5454

@@ -68,17 +68,17 @@ def freeze_time_dependent_body( # pylint: disable=unused-argument,too-many-argu
6868
freezing_temperature,
6969
thaw,
7070
):
71-
n_sd = len(attributes.water_mass)
71+
n_sd = len(attributes.signed_water_mass)
7272
for i in numba.prange(n_sd): # pylint: disable=not-an-iterable
7373
if attributes.immersed_surface_area[i] == 0:
7474
continue
7575
cell_id = cell[i]
7676
if thaw and frozen_and_above_freezing_point(
77-
attributes.water_mass[i], temperature[cell_id]
77+
attributes.signed_water_mass[i], temperature[cell_id]
7878
):
79-
_thaw(attributes.water_mass, i)
79+
_thaw(attributes.signed_water_mass, i)
8080
elif unfrozen_and_saturated(
81-
attributes.water_mass[i], relative_humidity[cell_id]
81+
attributes.signed_water_mass[i], relative_humidity[cell_id]
8282
):
8383
rate_assuming_constant_temperature_within_dt = (
8484
j_het(a_w_ice[cell_id]) * attributes.immersed_surface_area[i]
@@ -87,7 +87,7 @@ def freeze_time_dependent_body( # pylint: disable=unused-argument,too-many-argu
8787
r=rate_assuming_constant_temperature_within_dt, dt=timestep
8888
)
8989
if rand[i] < prob:
90-
_freeze(attributes.water_mass, i)
90+
_freeze(attributes.signed_water_mass, i)
9191
# if record_freezing_temperature:
9292
# freezing_temperature[i] = temperature[cell_id]
9393

@@ -99,7 +99,7 @@ def freeze_singular(
9999
self.freeze_singular_body(
100100
SingularAttributes(
101101
freezing_temperature=attributes.freezing_temperature.data,
102-
water_mass=attributes.water_mass.data,
102+
signed_water_mass=attributes.signed_water_mass.data,
103103
),
104104
temperature.data,
105105
relative_humidity.data,
@@ -125,7 +125,7 @@ def freeze_time_dependent(
125125
rand.data,
126126
TimeDependentAttributes(
127127
immersed_surface_area=attributes.immersed_surface_area.data,
128-
water_mass=attributes.water_mass.data,
128+
signed_water_mass=attributes.signed_water_mass.data,
129129
),
130130
timestep,
131131
cell.data,

PySDM/backends/impl_numba/storage.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,3 +208,6 @@ def fill(self, other):
208208

209209
def exp(self):
210210
self.data[:] = np.exp(self.data)
211+
212+
def abs(self):
213+
self.data[:] = np.abs(self.data)

PySDM/backends/impl_thrust_rtc/methods/freezing_methods.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def freeze_time_dependent_body(self):
1818
param_names=(
1919
"rand",
2020
"immersed_surface_area",
21-
"water_mass",
21+
"signed_water_mass",
2222
"timestep",
2323
"cell",
2424
"a_w_ice",
@@ -32,12 +32,12 @@ def freeze_time_dependent_body(self):
3232
return;
3333
}}
3434
if (thaw && {self.formulae.trivia.frozen_and_above_freezing_point.c_inline(
35-
water_mass="water_mass[i]",
35+
signed_water_mass="signed_water_mass[i]",
3636
temperature="temperature[cell[i]]"
3737
)}) {{
38-
water_mass[i] = -1 * water_mass[i];
38+
signed_water_mass[i] = -1 * signed_water_mass[i];
3939
}} else if ({self.formulae.trivia.unfrozen_and_saturated.c_inline(
40-
water_mass="water_mass[i]",
40+
signed_water_mass="signed_water_mass[i]",
4141
relative_humidity="relative_humidity[cell[i]]"
4242
)}) {{
4343
auto rate_assuming_constant_temperature_within_dt = {self.formulae.heterogeneous_ice_nucleation_rate.j_het.c_inline(
@@ -48,7 +48,7 @@ def freeze_time_dependent_body(self):
4848
dt="timestep"
4949
)};
5050
if (rand[i] < prob) {{
51-
water_mass[i] = -1 * water_mass[i];
51+
signed_water_mass[i] = -1 * signed_water_mass[i];
5252
}}
5353
}}
5454
""".replace(
@@ -61,7 +61,7 @@ def freeze_singular_body(self):
6161
return trtc.For(
6262
param_names=(
6363
"freezing_temperature",
64-
"water_mass",
64+
"signed_water_mass",
6565
"temperature",
6666
"relative_humidity",
6767
"cell",
@@ -73,17 +73,17 @@ def freeze_singular_body(self):
7373
return;
7474
}}
7575
if (thaw && {self.formulae.trivia.frozen_and_above_freezing_point.c_inline(
76-
water_mass="water_mass[i]",
76+
signed_water_mass="signed_water_mass[i]",
7777
temperature="temperature[cell[i]]"
7878
)}) {{
79-
water_mass[i] = -1 * water_mass[i];
79+
signed_water_mass[i] = -1 * signed_water_mass[i];
8080
}} else if (
8181
{self.formulae.trivia.unfrozen_and_saturated.c_inline(
82-
water_mass="water_mass[i]",
82+
signed_water_mass="signed_water_mass[i]",
8383
relative_humidity="relative_humidity[cell[i]]"
8484
)} && temperature[cell[i]] <= freezing_temperature[i]
8585
) {{
86-
water_mass[i] = -1 * water_mass[i];
86+
signed_water_mass[i] = -1 * signed_water_mass[i];
8787
}}
8888
""".replace(
8989
"real_type", self._get_c_type()
@@ -99,7 +99,7 @@ def freeze_singular(
9999
n=n_sd,
100100
args=(
101101
attributes.freezing_temperature.data,
102-
attributes.water_mass.data,
102+
attributes.signed_water_mass.data,
103103
temperature.data,
104104
relative_humidity.data,
105105
cell.data,
@@ -128,7 +128,7 @@ def freeze_time_dependent( # pylint: disable=unused-argument
128128
args=(
129129
rand.data,
130130
attributes.immersed_surface_area.data,
131-
attributes.water_mass.data,
131+
attributes.signed_water_mass.data,
132132
self._get_floating_point(timestep),
133133
cell.data,
134134
a_w_ice.data,

PySDM/backends/impl_thrust_rtc/storage.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,19 @@ def divide_if_not_zero(output, divisor):
319319
def exp(output):
320320
Impl.__exp_body.launch_n(output.shape[0], Impl.thrust((output,)))
321321

322+
__abs_body = trtc.For(
323+
("output",),
324+
"i",
325+
"""
326+
output[i] = abs(output[i]);
327+
""",
328+
)
329+
330+
@staticmethod
331+
@nice_thrust(**NICE_THRUST_FLAGS)
332+
def abs(output):
333+
Impl.__abs_body.launch_n(output.shape[0], Impl.thrust((output,)))
334+
322335
class Storage(StorageBase):
323336
FLOAT = BACKEND._get_np_dtype()
324337
INT = np.int64
@@ -554,4 +567,8 @@ def exp(self):
554567
Impl.exp(self)
555568
return self
556569

570+
def abs(self):
571+
Impl.abs(self)
572+
return self
573+
557574
return Storage

PySDM/builder.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,19 @@ def build(
109109
), "implied volume-to-mass conversion is only supported for spherical particles"
110110
attributes["water mass"] = (
111111
self.particulator.formulae.particle_shape_and_density.volume_to_mass(
112-
attributes["volume"]
112+
attributes.pop("volume")
113113
)
114114
)
115-
del attributes["volume"]
116115
self.request_attribute("volume")
117116

117+
if (
118+
"water mass" in attributes
119+
and "signed water mass" not in attributes
120+
and not self.particulator.formulae.particle_shape_and_density.supports_mixed_phase()
121+
):
122+
attributes["signed water mass"] = attributes.pop("water mass")
123+
self.request_attribute("water mass")
124+
118125
self.req_attr = {}
119126
for attr_name in self.req_attr_names:
120127
self._resolve_attribute(attr_name)

PySDM/dynamics/freezing.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def register(self, builder):
2727

2828
assert builder.formulae.particle_shape_and_density.supports_mixed_phase()
2929

30-
builder.request_attribute("volume")
30+
builder.request_attribute("signed water mass")
3131
if self.singular or self.record_freezing_temperature:
3232
builder.request_attribute("freezing temperature")
3333

@@ -59,7 +59,7 @@ def __call__(self):
5959
freezing_temperature=self.particulator.attributes[
6060
"freezing temperature"
6161
],
62-
water_mass=self.particulator.attributes["water mass"],
62+
signed_water_mass=self.particulator.attributes["signed water mass"],
6363
),
6464
temperature=self.particulator.environment["T"],
6565
relative_humidity=self.particulator.environment["RH"],
@@ -74,7 +74,7 @@ def __call__(self):
7474
immersed_surface_area=self.particulator.attributes[
7575
"immersed surface area"
7676
],
77-
water_mass=self.particulator.attributes["water mass"],
77+
signed_water_mass=self.particulator.attributes["signed water mass"],
7878
),
7979
timestep=self.particulator.dt,
8080
cell=self.particulator.attributes["cell id"],
@@ -90,6 +90,6 @@ def __call__(self):
9090
thaw=self.thaw,
9191
)
9292

93-
self.particulator.attributes.mark_updated("water mass")
93+
self.particulator.attributes.mark_updated("signed water mass")
9494
if self.record_freezing_temperature:
9595
self.particulator.attributes.mark_updated("freezing temperature")

0 commit comments

Comments
 (0)