Skip to content

Commit e7f9982

Browse files
add smoke test checking error measures for the Shima_et_al_2009 box example. Closes #327 (#1191)
Co-authored-by: AgnieszkaZaba <56157996+AgnieszkaZaba@users.noreply.github.com> Co-authored-by: Agnieszka Żaba <azaba@agh.edu.pl>
1 parent 8e75c71 commit e7f9982

File tree

4 files changed

+109
-17
lines changed

4 files changed

+109
-17
lines changed

docs/bibliography.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,8 @@
121121
"examples/PySDM_examples/Shima_et_al_2009/__init__.py",
122122
"PySDM/dynamics/collisions/collision.py",
123123
"tutorials/collisions/collisions_playground.ipynb",
124-
"tutorials/wikipedia/sdm.ipynb"
124+
"tutorials/wikipedia/sdm.ipynb",
125+
"tests/smoke_tests/box/shima_et_al_2009/test_convergence.py"
125126
],
126127
"label": "Shima et al. 2009 (Q. J. R. Meteorol. Soc. 135)",
127128
"title": "The super-droplet method for the numerical simulation of clouds and precipitation: a particle-based and probabilistic microphysics model coupled with a non-hydrostatic model"

examples/PySDM_examples/Berry_1967/spectrum_plotter.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ def show(self):
2626
self.ticks()
2727
show_plot()
2828

29-
def plot(self, spectrum, t):
29+
def plot(
30+
self, spectrum, t, label=None, color=None, title=None, add_error_to_label=False
31+
):
3032
settings = self.settings
31-
self.plot_data(settings, t, spectrum)
33+
self.plot_data(settings, t, spectrum, label, color)

examples/PySDM_examples/Shima_et_al_2009/spectrum_plotter.py

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,16 @@ def save(self, file):
8181
self.finish()
8282
pyplot.savefig(file, format=self.format)
8383

84-
def plot(self, spectrum, t):
85-
error = self.plot_analytic_solution(self.settings, t, spectrum)
86-
self.plot_data(self.settings, t, spectrum)
84+
def plot(
85+
self, spectrum, t, label=None, color=None, title=None, add_error_to_label=False
86+
):
87+
error = self.plot_analytic_solution(self.settings, t, spectrum, title)
88+
if label is not None and add_error_to_label:
89+
label += f" error={error:.4g}"
90+
self.plot_data(self.settings, t, spectrum, label, color)
8791
return error
8892

89-
def plot_analytic_solution(self, settings, t, spectrum=None):
93+
def plot_analytic_solution(self, settings, t, spectrum, title):
9094
if t == 0:
9195
analytic_solution = settings.spectrum.size_distribution
9296
else:
@@ -123,11 +127,13 @@ def analytic_solution(x):
123127
if spectrum is not None:
124128
y = spectrum * si.kilograms / si.grams
125129
error = error_measure(y, y_true, x)
126-
self.title = f"error measure: {error:.2f}" # TODO #327 relative error
130+
self.title = (
131+
title or f"error measure: {error:.2f}"
132+
) # TODO #327 relative error
127133
return error
128134
return None
129135

130-
def plot_data(self, settings, t, spectrum):
136+
def plot_data(self, settings, t, spectrum, label, color):
131137
if self.smooth:
132138
scope = self.smooth_scope
133139
if t != 0:
@@ -144,18 +150,16 @@ def plot_data(self, settings, t, spectrum):
144150
self.ax.plot(
145151
(x[:-1] + dx / 2) * si.metres / si.micrometres,
146152
spectrum[:-scope] * si.kilograms / si.grams,
147-
label=f"t = {t}s",
148-
color=self.colors(
149-
t / (self.settings.output_steps[-1] * self.settings.dt)
150-
),
153+
label=label or f"t = {t}s",
154+
color=color
155+
or self.colors(t / (self.settings.output_steps[-1] * self.settings.dt)),
151156
)
152157
else:
153158
self.ax.step(
154159
settings.radius_bins_edges[:-1] * si.metres / si.micrometres,
155160
spectrum * si.kilograms / si.grams,
156161
where="post",
157-
label=f"t = {t}s",
158-
color=self.colors(
159-
t / (self.settings.output_steps[-1] * self.settings.dt)
160-
),
162+
label=label or f"t = {t}s",
163+
color=color
164+
or self.colors(t / (self.settings.output_steps[-1] * self.settings.dt)),
161165
)
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
"""
2+
Tests checking if, for the example case from Fig 4 in
3+
[Shima et al. 2009](https://doi.org/10.1002/qj.441),
4+
the simulations converge towards analytic solution.
5+
"""
6+
7+
from inspect import signature
8+
from itertools import islice
9+
10+
import pytest
11+
from matplotlib import pyplot
12+
from PySDM_examples.Shima_et_al_2009.example import run
13+
from PySDM_examples.Shima_et_al_2009.settings import Settings
14+
from PySDM_examples.Shima_et_al_2009.spectrum_plotter import SpectrumPlotter
15+
16+
from PySDM.physics import si
17+
18+
COLORS = ("red", "green", "blue")
19+
20+
21+
class TestConvergence: # pylint: disable=missing-class-docstring
22+
@staticmethod
23+
@pytest.mark.parametrize(
24+
"adaptive, dt",
25+
(
26+
pytest.param(False, 100 * si.s, marks=pytest.mark.xfail(strict=True)),
27+
(True, 100 * si.s),
28+
pytest.param(False, 50 * si.s, marks=pytest.mark.xfail(strict=True)),
29+
(True, 50 * si.s),
30+
),
31+
)
32+
def test_convergence_with_sd_count(dt, adaptive, plot=False):
33+
"""check if increasing the number of super particles indeed
34+
reduces the error of the simulation (vs. analytic solution)"""
35+
# arrange
36+
settings = Settings(steps=[3600])
37+
settings.adaptive = adaptive
38+
plotter = SpectrumPlotter(settings)
39+
errors = {}
40+
41+
# act
42+
for i, ln2_nsd in enumerate((11, 15, 19)):
43+
settings.dt = dt
44+
settings.n_sd = 2**ln2_nsd
45+
values, _ = run(settings)
46+
47+
title = (
48+
""
49+
if i != 0
50+
else (
51+
f"{settings.dt=} settings.times={settings.steps} {settings.adaptive=}"
52+
)
53+
)
54+
errors[ln2_nsd] = plotter.plot(
55+
**dict(
56+
islice(
57+
{ # supporting older versions of PySDM-examples
58+
"t": settings.steps[-1],
59+
"spectrum": values[tuple(values.keys())[-1]],
60+
"label": f"{ln2_nsd=}",
61+
"color": COLORS[i],
62+
"title": title,
63+
"add_error_to_label": True,
64+
}.items(),
65+
len(signature(plotter.plot).parameters),
66+
)
67+
)
68+
)
69+
70+
# plot
71+
if plot:
72+
plotter.show()
73+
else:
74+
# https://github.com/matplotlib/matplotlib/issues/9970
75+
pyplot.xscale("linear")
76+
pyplot.clf()
77+
78+
# assert monotonicity (i.e., the larger the sd count, the smaller the error)
79+
assert tuple(errors.keys()) == tuple(sorted(errors.keys()))
80+
assert tuple(errors.values()) == tuple(reversed(sorted(errors.values())))
81+
82+
@staticmethod
83+
def test_convergence_with_timestep():
84+
"""ditto for timestep"""
85+
pytest.skip("# TODO #1189")

0 commit comments

Comments
 (0)