Skip to content

Commit 20301d7

Browse files
authored
Merge pull request #790 from slayoo/fixes
BDF condensation in pyrcel smoke tests + work in progress on more test coverage for condensation
2 parents 70cdc83 + c7e4827 commit 20301d7

File tree

6 files changed

+145
-17
lines changed

6 files changed

+145
-17
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ repos:
1414
- id: isort
1515

1616
- repo: https://github.com/pre-commit/pre-commit-hooks
17-
rev: v4.1.0
17+
rev: v4.2.0
1818
hooks:
1919
- id: trailing-whitespace
2020
- id: end-of-file-fixer

PySDM/products/size_spectral/water_mixing_ratio.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99

1010
class WaterMixingRatio(MomentProduct):
11-
def __init__(self, radius_range=(0, np.inf), name=None, unit="dimensionless"):
12-
self.radius_range = radius_range
11+
def __init__(self, radius_range=None, name=None, unit="dimensionless"):
12+
self.radius_range = radius_range or (0, np.inf)
1313
self.volume_range = None
1414
super().__init__(unit=unit, name=name)
1515

test-time-requirements.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ ghapi
44
pytest
55

66
# note: if cloning both PySDM and PySDM examples, consider "pip install -e"
7-
PySDM-examples @ git+https://github.com/slayoo/PySDM-examples@35f25d1#egg=PySDM-examples
7+
PySDM-examples @ git+https://github.com/atmos-cloud-sim-uj/PySDM-examples@7ad7cd0#egg=PySDM-examples
88
PyMPDATA @ git+https://github.com/atmos-cloud-sim-uj/PyMPDATA@e7b73a7#egg=PyMPDATA
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring
2+
import numpy as np
3+
import pytest
4+
from matplotlib import pyplot
5+
from scipy import signal
6+
7+
from PySDM import Builder
8+
from PySDM import products as PySDM_products
9+
from PySDM.backends import CPU
10+
from PySDM.backends.impl_numba.test_helpers import bdf
11+
from PySDM.dynamics import AmbientThermodynamics, Condensation
12+
from PySDM.environments import Parcel
13+
from PySDM.initialisation import equilibrate_wet_radii
14+
from PySDM.initialisation.sampling.spectral_sampling import ConstantMultiplicity
15+
from PySDM.initialisation.spectra import Lognormal
16+
from PySDM.physics import si
17+
18+
19+
@pytest.mark.parametrize(
20+
"rtol_thd",
21+
(
22+
pytest.param(1e-6, marks=pytest.mark.xfail(strict=True)),
23+
pytest.param(1e-7, marks=pytest.mark.xfail(strict=True)),
24+
1e-8,
25+
1e-9,
26+
),
27+
)
28+
@pytest.mark.parametrize("rtol_x", (1e-7,))
29+
@pytest.mark.parametrize("adaptive", (True,))
30+
@pytest.mark.parametrize("scheme", ("PySDM",))
31+
def test_single_supersaturation_peak(adaptive, scheme, rtol_x, rtol_thd, plot=False):
32+
# arrange
33+
products = (
34+
PySDM_products.WaterMixingRatio(unit="g/kg", name="ql"),
35+
PySDM_products.PeakSupersaturation(name="S max"),
36+
PySDM_products.AmbientRelativeHumidity(name="RH"),
37+
PySDM_products.ParcelDisplacement(name="z"),
38+
)
39+
env = Parcel(
40+
dt=2 * si.s,
41+
mass_of_dry_air=1e3 * si.kg,
42+
p0=1000 * si.hPa,
43+
q0=22.76 * si.g / si.kg,
44+
w=0.5 * si.m / si.s,
45+
T0=300 * si.K,
46+
)
47+
n_steps = 70
48+
n_sd = 2
49+
kappa = 0.4
50+
spectrum = Lognormal(norm_factor=5000 / si.cm**3, m_mode=50.0 * si.nm, s_geom=2.0)
51+
builder = Builder(backend=CPU(), n_sd=n_sd)
52+
builder.set_environment(env)
53+
builder.add_dynamic(AmbientThermodynamics())
54+
builder.add_dynamic(
55+
Condensation(
56+
adaptive=adaptive,
57+
rtol_x=rtol_x,
58+
rtol_thd=rtol_thd,
59+
)
60+
)
61+
62+
r_dry, concentration = ConstantMultiplicity(spectrum).sample(n_sd)
63+
v_dry = builder.formulae.trivia.volume(radius=r_dry)
64+
r_wet = equilibrate_wet_radii(
65+
r_dry=r_dry, environment=env, kappa_times_dry_volume=kappa * v_dry
66+
)
67+
specific_concentration = concentration / builder.formulae.constants.rho_STP
68+
attributes = {
69+
"n": specific_concentration * env.mass_of_dry_air,
70+
"dry volume": v_dry,
71+
"kappa times dry volume": kappa * v_dry,
72+
"volume": builder.formulae.trivia.volume(radius=r_wet),
73+
}
74+
75+
particulator = builder.build(attributes, products=products)
76+
77+
if scheme == "BDF":
78+
bdf.patch_particulator(particulator)
79+
80+
output = {product.name: [] for product in particulator.products.values()}
81+
output_attributes = {"volume": tuple([] for _ in range(particulator.n_sd))}
82+
83+
# act
84+
for _ in range(n_steps):
85+
particulator.run(steps=1)
86+
for product in particulator.products.values():
87+
value = product.get()
88+
output[product.name].append(value[0])
89+
for key, attr in output_attributes.items():
90+
attr_data = particulator.attributes[key].to_ndarray()
91+
for drop_id in range(particulator.n_sd):
92+
attr[drop_id].append(attr_data[drop_id])
93+
94+
# plot
95+
for drop_id, volume in enumerate(output_attributes["volume"]):
96+
pyplot.semilogx(
97+
particulator.formulae.trivia.radius(volume=np.asarray(volume)) / si.um,
98+
output["z"],
99+
color="black",
100+
label="drop size (bottom axis)",
101+
)
102+
pyplot.xlabel("radius [um]")
103+
pyplot.ylabel("z [m]")
104+
twin = pyplot.twiny()
105+
twin.plot(output["S max"], output["z"], label="S max (top axis)")
106+
twin.plot(np.asarray(output["RH"]) - 1, output["z"], label="ambient RH (top axis)")
107+
twin.legend(loc="upper center")
108+
twin.set_xlim(-0.001, 0.0015)
109+
pyplot.legend(loc="lower right")
110+
pyplot.grid()
111+
pyplot.title(f"rtol_thd={rtol_thd}; rtol_x={rtol_x}")
112+
if plot:
113+
pyplot.show()
114+
115+
# assert
116+
assert signal.argrelextrema(np.asarray(output["RH"]), np.greater)[0].shape[0] == 1

tests/smoke_tests/lowe_et_al_2019/test_fig_2.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,13 @@ def test_peak_supersaturation_and_final_concentration(
3030
*, constants, aerosol, surface_tension, s_max, s_100m, n_100m
3131
):
3232
# arrange
33+
dt = 1 * si.s
34+
w = 0.32 * si.m / si.s
35+
z_max = 200 * si.m
36+
n_steps = int(z_max / w / dt)
37+
dz = z_max / n_steps
3338
settings = Settings(
34-
dz=2 / 0.32 * si.m,
39+
dz=dz,
3540
n_sd_per_mode=32,
3641
model=surface_tension,
3742
aerosol=aerosol,
@@ -44,9 +49,8 @@ def test_peak_supersaturation_and_final_concentration(
4449
output = simulation.run()
4550

4651
# assert
47-
# assert len(output['S_max']) == 2
48-
i_100m = 312
49-
# print(output["z"][i_100m])
52+
i_100m = np.argmin(np.abs(np.asarray(output["z"]) - 100 * si.m))
53+
print(i_100m, output["z"][i_100m])
5054
print(np.nanmax(output["S_max"]), s_max)
5155
print(output["S_max"][i_100m], s_100m)
5256
print(output["n_c_cm3"][i_100m], n_100m)

tests/smoke_tests/pyrcel/test_parcel_example.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,20 @@
66
from PySDM import Formulae
77
from PySDM.initialisation.spectra import Lognormal
88
from PySDM.physics import si
9-
from PySDM.products import AmbientTemperature, ParcelDisplacement, PeakSupersaturation
9+
from PySDM.products import (
10+
AmbientRelativeHumidity,
11+
AmbientTemperature,
12+
ParcelDisplacement,
13+
)
1014

1115

1216
class TestParcelExample:
1317
@staticmethod
1418
@pytest.mark.parametrize("s_max, s_250m, T_250m", ((0.62, 0.139, 272.2),))
15-
@pytest.mark.xfail(strict=True) # TODO #776
16-
# pylint: disable=redefined-outer-name,unused-argument
17-
def test_supersaturation_and_temperature_profile(s_max, s_250m, T_250m):
19+
@pytest.mark.parametrize("scipy_solver", (pytest.param(True), pytest.param(False)))
20+
def test_supersaturation_and_temperature_profile(
21+
s_max, s_250m, T_250m, scipy_solver
22+
):
1823
# arrange
1924
settings = Settings(
2025
dz=1 * si.m,
@@ -38,21 +43,24 @@ def test_supersaturation_and_temperature_profile(s_max, s_250m, T_250m):
3843
settings,
3944
products=(
4045
ParcelDisplacement(name="z"),
41-
PeakSupersaturation(name="S_max", unit="%"),
46+
AmbientRelativeHumidity(name="RH", unit="%"),
4247
AmbientTemperature(name="T"),
4348
),
49+
scipy_solver=scipy_solver,
4450
)
4551

4652
# act
4753
output = simulation.run()
4854

4955
# assert
5056
np.testing.assert_approx_equal(
51-
np.nanmax(output["products"]["S_max"]), s_max, significant=2
52-
)
53-
np.testing.assert_approx_equal(
54-
output["products"]["S_max"][-1], s_250m, significant=2
57+
np.nanmax(np.asarray(output["products"]["RH"])) - 100, s_max, significant=2
5558
)
59+
5660
np.testing.assert_approx_equal(
5761
output["products"]["T"][-1], T_250m, significant=2
5862
)
63+
64+
# TODO #776
65+
# np.testing.assert_approx_equal(output['products']['RH'][-1]-100, s_250m, significant=2)
66+
assert s_250m is not None

0 commit comments

Comments
 (0)