Skip to content

Commit ea9144e

Browse files
introduce multiple choices for activation criteria in ActivableFraction product; sanitize saturation vs. supersaturation naming and units across the codebase (#1595)
Co-authored-by: AgnieszkaZaba <[email protected]> Co-authored-by: Agnieszka Żaba <[email protected]>
1 parent 20c8340 commit ea9144e

File tree

51 files changed

+4989
-8473
lines changed

Some content is hidden

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

51 files changed

+4989
-8473
lines changed

PySDM/attributes/impl/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@
1111
from .maximum_attribute import MaximumAttribute
1212
from .attribute_registry import register_attribute, get_attribute_class
1313
from .intensive_attribute import IntensiveAttribute
14+
from .temperature_variation_option_attribute import TemperatureVariationOptionAttribute
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"""common code for attributes offering an option to neglect temperature variation,
2+
intended for use with Parcel environment only"""
3+
4+
5+
class TemperatureVariationOptionAttribute: # pylint: disable=too-few-public-methods
6+
"""base class"""
7+
8+
def __init__(self, builder, neglect_temperature_variations: bool):
9+
if neglect_temperature_variations:
10+
assert builder.particulator.environment.mesh.dimension == 0
11+
self.neglect_temperature_variations = neglect_temperature_variations
12+
self.initial_temperature = (
13+
builder.particulator.Storage.from_ndarray(
14+
builder.particulator.environment["T"].to_ndarray()
15+
)
16+
if neglect_temperature_variations
17+
else None
18+
)

PySDM/attributes/physics/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
"""
44

55
from .area import Area
6-
from .critical_supersaturation import CriticalSupersaturation
6+
from .critical_saturation import CriticalSaturation
77
from .critical_volume import CriticalVolume, WetToCriticalVolumeRatio
88
from .dry_radius import DryRadius
99
from .dry_volume import DryVolume
10-
from .equilibrium_supersaturation import EquilibriumSupersaturation
10+
from .equilibrium_saturation import EquilibriumSaturation
1111
from .heat import Heat
1212
from .hygroscopicity import Kappa, KappaTimesDryVolume
1313
from .water_mass import SignedWaterMass
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""
2+
kappa-Koehler critical saturation calculated for either initial or actual environment temperature
3+
"""
4+
5+
from PySDM.attributes.impl import (
6+
DerivedAttribute,
7+
register_attribute,
8+
TemperatureVariationOptionAttribute,
9+
)
10+
11+
12+
@register_attribute()
13+
class CriticalSaturation(DerivedAttribute, TemperatureVariationOptionAttribute):
14+
def __init__(self, builder, neglect_temperature_variations=False):
15+
assert builder.particulator.mesh.dimension == 0
16+
17+
self.v_crit = builder.get_attribute("critical volume")
18+
self.v_dry = builder.get_attribute("dry volume")
19+
self.kappa = builder.get_attribute("kappa")
20+
self.f_org = builder.get_attribute("dry volume organic fraction")
21+
TemperatureVariationOptionAttribute.__init__(
22+
self, builder, neglect_temperature_variations
23+
)
24+
DerivedAttribute.__init__(
25+
self,
26+
builder=builder,
27+
name="critical saturation",
28+
dependencies=(self.v_crit, self.kappa, self.v_dry, self.f_org),
29+
)
30+
31+
def recalculate(self):
32+
temperature = (
33+
self.initial_temperature
34+
if self.neglect_temperature_variations
35+
else self.particulator.environment["T"]
36+
)
37+
r_cr = self.formulae.trivia.radius(self.v_crit.data.data)
38+
rd3 = self.v_dry.data.data / self.formulae.constants.PI_4_3
39+
sgm = self.formulae.surface_tension.sigma(
40+
temperature.data,
41+
self.v_crit.data.data,
42+
self.v_dry.data.data,
43+
self.f_org.data.data,
44+
)
45+
46+
self.data.data[:] = self.formulae.hygroscopicity.RH_eq(
47+
r_cr, T=temperature.data, kp=self.kappa.data.data, rd3=rd3, sgm=sgm
48+
)
49+
50+
51+
@register_attribute()
52+
class CriticalSaturationNeglectingTemperatureVariations(CriticalSaturation):
53+
def __init__(self, builder):
54+
super().__init__(builder, neglect_temperature_variations=True)

PySDM/attributes/physics/critical_supersaturation.py

Lines changed: 0 additions & 37 deletions
This file was deleted.
Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,88 @@
11
"""
2-
critical wet volume (kappa-Koehler, computed using actual temperature)
2+
critical wet volume (kappa-Koehler, computed using actual or initial temperature)
33
"""
44

5-
from PySDM.attributes.impl import DerivedAttribute, register_attribute
5+
from PySDM.attributes.impl import (
6+
DerivedAttribute,
7+
register_attribute,
8+
TemperatureVariationOptionAttribute,
9+
)
610

711

812
@register_attribute()
9-
class CriticalVolume(DerivedAttribute):
10-
def __init__(self, builder):
13+
class CriticalVolume(DerivedAttribute, TemperatureVariationOptionAttribute):
14+
def __init__(self, builder, neglect_temperature_variations=False):
1115
self.cell_id = builder.get_attribute("cell id")
1216
self.v_dry = builder.get_attribute("dry volume")
1317
self.v_wet = builder.get_attribute("volume")
1418
self.kappa = builder.get_attribute("kappa")
1519
self.f_org = builder.get_attribute("dry volume organic fraction")
1620
self.environment = builder.particulator.environment
1721
self.particles = builder.particulator
22+
1823
dependencies = [self.v_dry, self.v_wet, self.cell_id]
19-
super().__init__(builder, name="critical volume", dependencies=dependencies)
24+
TemperatureVariationOptionAttribute.__init__(
25+
self, builder, neglect_temperature_variations
26+
)
27+
DerivedAttribute.__init__(
28+
self, builder, name="critical volume", dependencies=dependencies
29+
)
2030

2131
def recalculate(self):
32+
temperature = (
33+
self.initial_temperature
34+
if self.neglect_temperature_variations
35+
else self.environment["T"]
36+
)
2237
self.particulator.backend.critical_volume(
2338
v_cr=self.data,
2439
kappa=self.kappa.get(),
2540
f_org=self.f_org.get(),
2641
v_dry=self.v_dry.get(),
2742
v_wet=self.v_wet.get(),
28-
T=self.environment["T"],
43+
T=temperature,
2944
cell=self.cell_id.get(),
3045
)
3146

3247

3348
@register_attribute()
34-
class WetToCriticalVolumeRatio(DerivedAttribute):
49+
class CriticalVolumeNeglectingTemperatureVariations(CriticalVolume):
3550
def __init__(self, builder):
36-
self.critical_volume = builder.get_attribute("critical volume")
51+
super().__init__(builder, neglect_temperature_variations=True)
52+
53+
54+
@register_attribute()
55+
class WetToCriticalVolumeRatio(DerivedAttribute):
56+
def __init__(
57+
self,
58+
builder,
59+
neglect_temperature_variations=False,
60+
name="wet to critical volume ratio",
61+
):
62+
self.critical_volume = builder.get_attribute(
63+
"critical volume"
64+
+ (
65+
" neglecting temperature variations"
66+
if neglect_temperature_variations
67+
else ""
68+
)
69+
)
3770
self.volume = builder.get_attribute("volume")
3871
super().__init__(
3972
builder,
40-
name="wet to critical volume ratio",
73+
name=name,
4174
dependencies=(self.critical_volume, self.volume),
4275
)
4376

4477
def recalculate(self):
4578
self.data.ratio(self.volume.get(), self.critical_volume.get())
79+
80+
81+
@register_attribute()
82+
class WetToCriticalVolumeRatioNeglectingTemperatureVariations(WetToCriticalVolumeRatio):
83+
def __init__(self, builder):
84+
super().__init__(
85+
builder,
86+
neglect_temperature_variations=True,
87+
name="wet to critical volume ratio neglecting temperature variations",
88+
)

PySDM/attributes/physics/equilibrium_supersaturation.py renamed to PySDM/attributes/physics/equilibrium_saturation.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
"""
2-
kappa-Koehler equilibrium supersaturation calculated for actual environment temperature
2+
kappa-Koehler equilibrium saturation calculated for actual environment temperature
33
"""
44

55
from PySDM.attributes.impl import DerivedAttribute, register_attribute
66

77

88
@register_attribute()
9-
class EquilibriumSupersaturation(DerivedAttribute):
9+
class EquilibriumSaturation(DerivedAttribute):
1010
def __init__(self, builder):
1111
self.r_wet = builder.get_attribute("radius")
1212
self.v_wet = builder.get_attribute("volume")
@@ -16,7 +16,7 @@ def __init__(self, builder):
1616

1717
super().__init__(
1818
builder=builder,
19-
name="equilibrium supersaturation",
19+
name="equilibrium saturation",
2020
dependencies=(self.kappa, self.v_dry, self.f_org, self.r_wet),
2121
)
2222

PySDM/environments/parcel.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Zero-dimensional adiabatic parcel framework
33
"""
44

5-
from typing import List, Optional
5+
from typing import List, Optional, Union
66

77
import numpy as np
88

@@ -25,7 +25,7 @@ def __init__(
2525
p0: float,
2626
initial_water_vapour_mixing_ratio: float,
2727
T0: float,
28-
w: [float, callable],
28+
w: Union[float, callable],
2929
z0: float = 0,
3030
mixed_phase=False,
3131
variables: Optional[List[str]] = None,

PySDM/products/condensation/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
from .activable_fraction import ActivableFraction
66
from .condensation_timestep import CondensationTimestepMax, CondensationTimestepMin
77
from .event_rates import ActivatingRate, DeactivatingRate, RipeningRate
8-
from .peak_supersaturation import PeakSupersaturation
8+
from .peak_saturation import PeakSaturation

PySDM/products/condensation/activable_fraction.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,38 @@
11
"""
2-
fraction of particles with critical supersaturation lower than a given supersaturation
2+
fraction of particles with critical saturation lower than a given saturation
33
(passed as keyword argument while calling `get()`)
44
"""
55

6+
import numpy as np
67
from PySDM.products.impl import MomentProduct, register_product
78

89

910
@register_product()
1011
class ActivableFraction(MomentProduct):
11-
def __init__(self, unit="dimensionless", name=None):
12+
def __init__(
13+
self, unit="dimensionless", name=None, filter_attr="critical saturation"
14+
):
1215
super().__init__(name=name, unit=unit)
16+
self.filter_attr = filter_attr
1317

1418
def register(self, builder):
1519
super().register(builder)
16-
builder.request_attribute("critical supersaturation")
20+
builder.request_attribute(self.filter_attr)
1721

1822
def _impl(self, **kwargs):
19-
s_max = kwargs["S_max"]
23+
if self.filter_attr.startswith("critical saturation"):
24+
s_max = kwargs["S_max"]
25+
assert not np.isfinite(s_max) or 0 < s_max < 1.1
26+
filter_range = (0, s_max)
27+
elif self.filter_attr.startswith("wet to critical volume ratio"):
28+
filter_range = (1, np.inf)
29+
else:
30+
assert False
2031
self._download_moment_to_buffer(
2132
attr="volume",
2233
rank=0,
23-
filter_range=(0, 1 + s_max / 100),
24-
filter_attr="critical supersaturation",
34+
filter_range=filter_range,
35+
filter_attr=self.filter_attr,
2536
)
2637
frac = self.buffer.copy()
2738
self._download_moment_to_buffer(attr="volume", rank=0)

0 commit comments

Comments
 (0)