Skip to content

Commit 4de8327

Browse files
authored
add STP option to ParticleSizeSpectrum* products (#1346)
1 parent f35385f commit 4de8327

File tree

3 files changed

+63
-15
lines changed

3 files changed

+63
-15
lines changed

PySDM/products/impl/concentration_product.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,23 @@
77

88

99
class ConcentrationProduct(MomentProduct):
10+
@staticmethod
11+
def check_ctor_arguments(specific, stp):
12+
if stp and specific:
13+
raise ValueError(
14+
"std-temperature-and-pressure precludes specific conc. option"
15+
)
16+
1017
def __init__(self, *, unit: str, name: str, specific: bool, stp: bool):
1118
"""
1219
`stp` toggles expressing the concentration in terms of standard temperature
1320
and pressure conditions (ground level of the ICAO standard atmosphere, zero humidity)
1421
"""
22+
self.check_ctor_arguments(specific, stp)
1523
super().__init__(unit=unit, name=name)
1624
self.specific = specific
1725
self.stp = stp
1826
self.rho_stp = None
19-
if self.stp and self.specific:
20-
raise ValueError(
21-
"std-temperature-and-pressure precludes specific conc. option"
22-
)
2327

2428
def register(self, builder):
2529
super().register(builder)

PySDM/products/size_spectral/particle_size_spectrum.py

+32-5
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,26 @@
77
import numpy as np
88

99
from PySDM.products.impl.spectrum_moment_product import SpectrumMomentProduct
10+
from PySDM.products.impl.concentration_product import ConcentrationProduct
1011

1112

1213
class ParticleSizeSpectrum(SpectrumMomentProduct, ABC):
13-
def __init__(self, *, radius_bins_edges, name, unit, dry=False, specific=False):
14+
def __init__(
15+
self,
16+
*,
17+
stp: bool,
18+
radius_bins_edges,
19+
name: str,
20+
unit: str,
21+
dry: bool = False,
22+
specific: bool = False,
23+
):
24+
ConcentrationProduct.check_ctor_arguments(specific, stp)
1425
self.volume_attr = "dry volume" if dry else "volume"
1526
self.radius_bins_edges = radius_bins_edges
1627
self.specific = specific
28+
self.stp = stp
29+
self.rho_stp = None
1730
super().__init__(name=name, unit=unit, attr_unit="m^3")
1831

1932
def register(self, builder):
@@ -29,6 +42,7 @@ def register(self, builder):
2942
super().register(builder)
3043

3144
self.shape = (*builder.particulator.mesh.grid, len(self.attr_bins_edges) - 1)
45+
self.rho_stp = builder.formulae.constants.rho_STP
3246

3347
def _impl(self, **kwargs):
3448
vals = np.empty([self.particulator.mesh.n_cell, len(self.attr_bins_edges) - 1])
@@ -42,37 +56,50 @@ def _impl(self, **kwargs):
4256

4357
vals[:] /= self.particulator.mesh.dv
4458

45-
if self.specific:
59+
if self.specific or self.stp:
4660
self._download_to_buffer(self.particulator.environment["rhod"])
4761

4862
for i in range(len(self.attr_bins_edges) - 1):
4963
dr = self.formulae.trivia.radius(
5064
volume=self.attr_bins_edges[i + 1]
5165
) - self.formulae.trivia.radius(volume=self.attr_bins_edges[i])
5266
vals[:, i] /= dr
53-
if self.specific:
67+
if self.specific or self.stp:
5468
vals[:, i] /= self.buffer.ravel()
69+
if self.stp:
70+
vals[:, i] *= self.rho_stp
5571

5672
return np.squeeze(vals.reshape(self.shape))
5773

5874

5975
class ParticleSizeSpectrumPerMassOfDryAir(ParticleSizeSpectrum):
60-
def __init__(self, radius_bins_edges, dry=False, name=None, unit="kg^-1 m^-1"):
76+
def __init__(
77+
self,
78+
*,
79+
radius_bins_edges,
80+
dry=False,
81+
name=None,
82+
unit="kg^-1 m^-1",
83+
):
6184
super().__init__(
6285
radius_bins_edges=radius_bins_edges,
6386
dry=dry,
6487
specific=True,
6588
name=name,
6689
unit=unit,
90+
stp=False,
6791
)
6892

6993

7094
class ParticleSizeSpectrumPerVolume(ParticleSizeSpectrum):
71-
def __init__(self, radius_bins_edges, dry=False, name=None, unit="m^-3 m^-1"):
95+
def __init__(
96+
self, *, radius_bins_edges, dry=False, name=None, unit="m^-3 m^-1", stp=False
97+
):
7298
super().__init__(
7399
radius_bins_edges=radius_bins_edges,
74100
dry=dry,
75101
specific=False,
76102
name=name,
77103
unit=unit,
104+
stp=stp,
78105
)

tests/unit_tests/products/test_particle_size_spectrum.py

+23-6
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,25 @@
1111
ParticleSizeSpectrumPerMassOfDryAir,
1212
ParticleSizeSpectrumPerVolume,
1313
)
14+
from PySDM.products.size_spectral.particle_size_spectrum import ParticleSizeSpectrum
1415

1516

1617
class TestParticleSizeSpectrum:
1718
@staticmethod
1819
@pytest.mark.parametrize(
19-
"product_class, specific, unit",
20+
"product_class, specific, unit, stp",
2021
(
21-
(ParticleSizeSpectrumPerVolume, False, "1.0 / meter ** 4"),
22-
(ParticleSizeSpectrumPerMassOfDryAir, True, "1.0 / kilogram / meter"),
22+
(ParticleSizeSpectrumPerVolume, False, "1.0 / meter ** 4", True),
23+
(ParticleSizeSpectrumPerVolume, False, "1.0 / meter ** 4", False),
24+
(
25+
ParticleSizeSpectrumPerMassOfDryAir,
26+
True,
27+
"1.0 / kilogram / meter",
28+
False,
29+
),
2330
),
2431
)
25-
def test_specific_flag(product_class, specific, unit, backend_instance):
32+
def test_specific_flag(product_class, specific, unit, backend_instance, stp):
2633
"""checks if the reported concentration is correctly normalised per
2734
volume or mass of air, and per bin size"""
2835
# arrange
@@ -35,7 +42,9 @@ def test_specific_flag(product_class, specific, unit, backend_instance):
3542
n_sd = 1
3643
box = Box(dt=np.nan, dv=dv)
3744
builder = Builder(n_sd=n_sd, backend=backend_instance, environment=box)
38-
sut = product_class(radius_bins_edges=(min_size, max_size))
45+
sut = product_class(
46+
radius_bins_edges=(min_size, max_size), **({"stp": stp} if stp else {})
47+
)
3948
builder.build(
4049
products=(sut,),
4150
attributes={
@@ -56,7 +65,8 @@ def test_specific_flag(product_class, specific, unit, backend_instance):
5665
desired=multiplicity
5766
/ dv
5867
/ (max_size - min_size)
59-
/ (rhod if specific else 1),
68+
/ (rhod if specific or stp else 1)
69+
* (backend_instance.formulae.constants.rho_STP if stp else 1),
6070
significant=10,
6171
)
6272

@@ -103,3 +113,10 @@ def test_dry_flag(product_class, dry, backend_instance):
103113
/ (rhod if sut.specific else 1),
104114
significant=10,
105115
)
116+
117+
@staticmethod
118+
def test_stp_flag_incompatible_with_specific():
119+
with pytest.raises(Exception, match="precludes specific"):
120+
ParticleSizeSpectrum(
121+
stp=True, specific=True, name="", unit="", radius_bins_edges=()
122+
)

0 commit comments

Comments
 (0)