PySDM_examples.Szumowski_et_al_1998.gui_viewer

  1import warnings
  2
  3import numpy as np
  4from matplotlib import pyplot, rcParams
  5from open_atmos_jupyter_utils import save_and_make_link
  6from PySDM_examples.Szumowski_et_al_1998.plots import (
  7    _ImagePlot,
  8    _SpectrumPlot,
  9    _TemperaturePlot,
 10    _TerminalVelocityPlot,
 11    _TimeseriesPlot,
 12)
 13from PySDM_examples.utils.widgets import (
 14    Box,
 15    Button,
 16    Dropdown,
 17    HBox,
 18    IntRangeSlider,
 19    IntSlider,
 20    Layout,
 21    Output,
 22    Play,
 23    VBox,
 24    clear_output,
 25    display,
 26    jslink,
 27)
 28
 29from PySDM.physics import constants as const
 30
 31
 32class GUIViewer:
 33    def __init__(self, storage, settings):
 34        self.storage = storage
 35        self.settings = settings
 36
 37        self.play = Play(interval=1000)
 38        self.step_slider = IntSlider(continuous_update=False, description="t/dt_out:")
 39        self.product_select = Dropdown()
 40        self.spectrum_select = Dropdown()
 41        self.plots_box = Box()
 42
 43        self.timeseriesOutput = None
 44        self.timeseriesPlot = None
 45        self.plot_box = None
 46        self.spectrum_box = None
 47        self.outputs = None
 48        self.plots = None
 49        self.spectrumOutputs = None
 50        self.spectrumPlots = None
 51        self.products = None
 52
 53        self.slider = {}
 54        self.lines = {"X": [{}, {}], "Z": [{}, {}]}
 55        for xz in ("X", "Z"):
 56            self.slider[xz] = IntRangeSlider(
 57                min=0,
 58                max=1,
 59                description=f"{xz}",
 60                continuous_update=False,
 61                orientation="horizontal" if xz == "X" else "vertical",
 62            )
 63
 64        self.reinit({})
 65
 66    def clear(self):
 67        self.plots_box.children = ()
 68
 69    def reinit(self, products):
 70        self.products = products
 71        self.product_select.options = tuple(
 72            (f"{val.name} [{val.unit}]", key)
 73            for key, val in sorted(self.products.items(), key=lambda item: item[1].name)
 74            if len(val.shape) == 2
 75        )
 76        opts = [
 77            ("particle size spectra", "size"),
 78            ("terminal velocity", "terminal velocity"),
 79        ]
 80        if "freezable specific concentration" in products:
 81            opts.append(("freezing temperature spectra", "temperature"))
 82        if "immersed surface area" in products:
 83            pass  # TODO #599
 84        self.spectrum_select.options = tuple(opts)
 85
 86        r_bins = self.settings.r_bins_edges.copy()
 87        const.convert_to(r_bins, const.si.micrometres)
 88        self.spectrumOutputs = {}
 89        self.spectrumPlots = {}
 90        for key in ("size", "terminal velocity", "temperature"):
 91            self.spectrumOutputs[key] = Output()
 92            with self.spectrumOutputs[key]:
 93                self.spectrumPlots[key] = (
 94                    _SpectrumPlot(r_bins, self.settings.spectrum_per_mass_of_dry_air)
 95                    if key == "size"
 96                    else (
 97                        _TemperaturePlot(
 98                            self.settings.T_bins_edges, self.settings.formulae
 99                        )
100                        if key == "temperature"
101                        else _TerminalVelocityPlot(
102                            self.settings.terminal_velocity_radius_bin_edges,
103                            self.settings.formulae,
104                        )
105                    )
106                )
107                clear_output()
108
109        self.timeseriesOutput = Output()
110        with self.timeseriesOutput:
111            default_figsize = rcParams["figure.figsize"]
112            fig_kw = {"figsize": (2.5 * default_figsize[0], default_figsize[1] / 2)}
113            fig, ax = pyplot.subplots(1, 1, **fig_kw)
114            self.timeseriesPlot = _TimeseriesPlot(
115                fig, ax, self.settings.output_steps * self.settings.dt
116            )
117            clear_output()
118
119        self.plots = {}
120        self.outputs = {}
121        for key, product in products.items():
122            if len(product.shape) == 2:
123                self.outputs[key] = Output()
124                with self.outputs[key]:
125                    fig, ax = pyplot.subplots(1, 1)
126                    self.plots[key] = _ImagePlot(
127                        fig,
128                        ax,
129                        self.settings.grid,
130                        self.settings.size,
131                        product,
132                        show=True,
133                        lines=True,
134                    )
135                    clear_output()
136
137        self.plot_box = Box()
138        self.spectrum_box = Box()
139        if len(products.keys()) > 0:
140            layout_flex_end = Layout(display="flex", justify_content="flex-end")
141            save_map = Button(icon="save")
142            save_map.on_click(self.handle_save_map)
143            save_spe = Button(icon="save")
144            save_spe.on_click(self.handle_save_spe)
145            self.plots_box.children = (
146                VBox(
147                    children=(
148                        HBox(
149                            children=(
150                                VBox(
151                                    children=(
152                                        Box(
153                                            layout=layout_flex_end,
154                                            children=(save_map, self.product_select),
155                                        ),
156                                        HBox((self.slider["Z"], self.plot_box)),
157                                        HBox(
158                                            (self.slider["X"],), layout=layout_flex_end
159                                        ),
160                                    )
161                                ),
162                                VBox(
163                                    layout=Layout(),
164                                    children=(
165                                        Box(
166                                            children=(save_spe, self.spectrum_select),
167                                            layout=layout_flex_end,
168                                        ),
169                                        self.spectrum_box,
170                                    ),
171                                ),
172                            )
173                        ),
174                        HBox((self.timeseriesOutput,)),
175                    )
176                ),
177            )
178
179        for widget in (self.step_slider, self.play):
180            widget.value = 0
181            widget.max = len(self.settings.output_steps) - 1
182
183        for j, xz in enumerate(("X", "Z")):
184            slider = self.slider[xz]
185            mx = self.settings.grid[j]
186            slider.max = mx
187            slider.value = (0, mx)
188
189        self.replot()
190
191    def handle_save_map(self, _):
192        display(save_and_make_link(self.plots[self.product_select.value].fig))
193
194    def handle_save_spe(self, _):
195        display(save_and_make_link(self.spectrumPlots[self.spectrum_select.value].fig))
196
197    def replot(self, *_):
198        selectedImage = self.product_select.value
199        if not (selectedImage is None or selectedImage not in self.plots):
200            self.replot_image()
201            self.outputs[selectedImage].clear_output(wait=True)
202            with self.outputs[selectedImage]:
203                display(self.plots[selectedImage].fig)
204
205        selectedSpectrum = self.spectrum_select.value
206        if not (selectedSpectrum is None or selectedSpectrum not in self.spectrumPlots):
207            self.replot_spectra()
208            self.spectrumOutputs[selectedSpectrum].clear_output(wait=True)
209            with self.spectrumOutputs[selectedSpectrum]:
210                display(self.spectrumPlots[selectedSpectrum].fig)
211
212        self.update_timeseries()
213        self.timeseriesOutput.clear_output(wait=True)
214        with self.timeseriesOutput:
215            display(self.timeseriesPlot.fig)
216
217    def update_spectra(self):
218        selected = self.spectrum_select.value
219        self.spectrum_box.children = [self.spectrumOutputs[selected]]
220        plot = self.spectrumPlots[selected]
221
222        step = self.step_slider.value
223
224        xrange = slice(*self.slider["X"].value)
225        yrange = slice(*self.slider["Z"].value)
226
227        if selected == "size":
228            for key in ("Particles Wet Size Spectrum", "Particles Dry Size Spectrum"):
229                if xrange.start == xrange.stop or yrange.start == yrange.stop:
230                    continue
231                try:
232                    data = self.storage.load(key, self.settings.output_steps[step])
233                    data = data[xrange, yrange, :]
234                    data = np.mean(np.mean(data, axis=0), axis=0)
235                    data = np.concatenate(((0,), data))
236                    if key == "Particles Wet Size Spectrum":
237                        plot.update_wet(data, step)
238                    if key == "Particles Dry Size Spectrum":
239                        plot.update_dry(data)
240                except self.storage.Exception:
241                    pass
242        elif selected == "terminal velocity":
243            try:
244                data = self.storage.load(
245                    "radius binned number averaged terminal velocity",
246                    self.settings.output_steps[step],
247                )
248
249                data = data[xrange, yrange, :]
250                data = np.where(data != 0, data, np.nan)
251                try:
252                    with warnings.catch_warnings(record=True) as _:
253                        warnings.simplefilter("ignore")
254                        data_min = np.nanmin(np.nanmin(data, axis=0), axis=0)
255                        data_max = np.nanmax(np.nanmax(data, axis=0), axis=0)
256                except RuntimeWarning:
257                    pass
258                plot.update(data_min, data_max, step)
259            except self.storage.Exception:
260                pass
261        elif selected == "temperature":
262            try:
263                dT = abs(self.settings.T_bins_edges[1] - self.settings.T_bins_edges[0])
264                np.testing.assert_allclose(np.diff(self.settings.T_bins_edges), dT)
265
266                conc = self.storage.load(
267                    "particle specific concentration", self.settings.output_steps[step]
268                )
269                # TODO #705: assert unit == mg^-1
270                conc = conc[xrange, yrange]
271
272                data = self.storage.load(
273                    "freezable specific concentration", self.settings.output_steps[step]
274                )
275                data = data[xrange, yrange, :]
276
277                data = np.sum(np.sum(data, axis=0), axis=0) / np.sum(
278                    np.sum(conc, axis=0), axis=0
279                )
280                data = np.concatenate(((0,), dT * np.cumsum(data[::-1])))[::-1]
281
282                plot.update(data, step)
283            except self.storage.Exception:
284                pass
285        else:
286            raise NotImplementedError()
287
288    def replot_spectra(self, *_):
289        self.update_spectra()
290
291        selected = self.product_select.value
292        if selected is None or selected not in self.plots:
293            return
294        self.plots[selected].update_lines(
295            self.slider["X"].value, self.slider["Z"].value
296        )
297
298        self.outputs[selected].clear_output(wait=True)
299
300        key = self.spectrum_select.value
301        self.spectrumOutputs[key].clear_output(wait=True)
302        with self.outputs[selected]:
303            display(self.plots[selected].fig)
304        with self.spectrumOutputs[key]:
305            display(self.spectrumPlots[key].fig)
306
307    def update_image(self):
308        selected = self.product_select.value
309
310        if selected in self.outputs:
311            self.plot_box.children = [self.outputs[selected]]
312
313        step = self.step_slider.value
314        try:
315            data = self.storage.load(selected, self.settings.output_steps[step])
316        except self.storage.Exception:
317            data = None
318
319        self.plots[selected].update(
320            data, step, self.storage.data_range(selected) if data is not None else None
321        )
322
323    def replot_image(self, *_):
324        selected = self.product_select.value
325        if selected is None or selected not in self.plots:
326            return
327
328        self.update_image()
329        self.outputs[selected].clear_output(wait=True)
330        with self.outputs[selected]:
331            display(self.plots[selected].fig)
332
333    def update_timeseries(self):
334        try:
335            data = self.storage.load("surf_precip")
336        except self.storage.Exception:
337            data = None
338        self.timeseriesPlot.update(
339            data, self.storage.data_range("surf_precip") if data is not None else None
340        )
341
342    def replot_timeseries(self):
343        self.update_timeseries()
344        self.timeseriesOutput.clear_output(wait=True)
345        with self.timeseriesOutput:
346            display(self.timeseriesPlot.fig)
347
348    def box(self):
349        jslink((self.play, "value"), (self.step_slider, "value"))
350        self.step_slider.observe(self.replot, "value")
351        self.product_select.observe(self.replot_image, "value")
352        self.spectrum_select.observe(self.replot_spectra, "value")
353        for xz in ("X", "Z"):
354            self.slider[xz].observe(self.replot_spectra, "value")
355        return VBox([Box([self.play, self.step_slider]), self.plots_box])
class GUIViewer:
 33class GUIViewer:
 34    def __init__(self, storage, settings):
 35        self.storage = storage
 36        self.settings = settings
 37
 38        self.play = Play(interval=1000)
 39        self.step_slider = IntSlider(continuous_update=False, description="t/dt_out:")
 40        self.product_select = Dropdown()
 41        self.spectrum_select = Dropdown()
 42        self.plots_box = Box()
 43
 44        self.timeseriesOutput = None
 45        self.timeseriesPlot = None
 46        self.plot_box = None
 47        self.spectrum_box = None
 48        self.outputs = None
 49        self.plots = None
 50        self.spectrumOutputs = None
 51        self.spectrumPlots = None
 52        self.products = None
 53
 54        self.slider = {}
 55        self.lines = {"X": [{}, {}], "Z": [{}, {}]}
 56        for xz in ("X", "Z"):
 57            self.slider[xz] = IntRangeSlider(
 58                min=0,
 59                max=1,
 60                description=f"{xz}",
 61                continuous_update=False,
 62                orientation="horizontal" if xz == "X" else "vertical",
 63            )
 64
 65        self.reinit({})
 66
 67    def clear(self):
 68        self.plots_box.children = ()
 69
 70    def reinit(self, products):
 71        self.products = products
 72        self.product_select.options = tuple(
 73            (f"{val.name} [{val.unit}]", key)
 74            for key, val in sorted(self.products.items(), key=lambda item: item[1].name)
 75            if len(val.shape) == 2
 76        )
 77        opts = [
 78            ("particle size spectra", "size"),
 79            ("terminal velocity", "terminal velocity"),
 80        ]
 81        if "freezable specific concentration" in products:
 82            opts.append(("freezing temperature spectra", "temperature"))
 83        if "immersed surface area" in products:
 84            pass  # TODO #599
 85        self.spectrum_select.options = tuple(opts)
 86
 87        r_bins = self.settings.r_bins_edges.copy()
 88        const.convert_to(r_bins, const.si.micrometres)
 89        self.spectrumOutputs = {}
 90        self.spectrumPlots = {}
 91        for key in ("size", "terminal velocity", "temperature"):
 92            self.spectrumOutputs[key] = Output()
 93            with self.spectrumOutputs[key]:
 94                self.spectrumPlots[key] = (
 95                    _SpectrumPlot(r_bins, self.settings.spectrum_per_mass_of_dry_air)
 96                    if key == "size"
 97                    else (
 98                        _TemperaturePlot(
 99                            self.settings.T_bins_edges, self.settings.formulae
100                        )
101                        if key == "temperature"
102                        else _TerminalVelocityPlot(
103                            self.settings.terminal_velocity_radius_bin_edges,
104                            self.settings.formulae,
105                        )
106                    )
107                )
108                clear_output()
109
110        self.timeseriesOutput = Output()
111        with self.timeseriesOutput:
112            default_figsize = rcParams["figure.figsize"]
113            fig_kw = {"figsize": (2.5 * default_figsize[0], default_figsize[1] / 2)}
114            fig, ax = pyplot.subplots(1, 1, **fig_kw)
115            self.timeseriesPlot = _TimeseriesPlot(
116                fig, ax, self.settings.output_steps * self.settings.dt
117            )
118            clear_output()
119
120        self.plots = {}
121        self.outputs = {}
122        for key, product in products.items():
123            if len(product.shape) == 2:
124                self.outputs[key] = Output()
125                with self.outputs[key]:
126                    fig, ax = pyplot.subplots(1, 1)
127                    self.plots[key] = _ImagePlot(
128                        fig,
129                        ax,
130                        self.settings.grid,
131                        self.settings.size,
132                        product,
133                        show=True,
134                        lines=True,
135                    )
136                    clear_output()
137
138        self.plot_box = Box()
139        self.spectrum_box = Box()
140        if len(products.keys()) > 0:
141            layout_flex_end = Layout(display="flex", justify_content="flex-end")
142            save_map = Button(icon="save")
143            save_map.on_click(self.handle_save_map)
144            save_spe = Button(icon="save")
145            save_spe.on_click(self.handle_save_spe)
146            self.plots_box.children = (
147                VBox(
148                    children=(
149                        HBox(
150                            children=(
151                                VBox(
152                                    children=(
153                                        Box(
154                                            layout=layout_flex_end,
155                                            children=(save_map, self.product_select),
156                                        ),
157                                        HBox((self.slider["Z"], self.plot_box)),
158                                        HBox(
159                                            (self.slider["X"],), layout=layout_flex_end
160                                        ),
161                                    )
162                                ),
163                                VBox(
164                                    layout=Layout(),
165                                    children=(
166                                        Box(
167                                            children=(save_spe, self.spectrum_select),
168                                            layout=layout_flex_end,
169                                        ),
170                                        self.spectrum_box,
171                                    ),
172                                ),
173                            )
174                        ),
175                        HBox((self.timeseriesOutput,)),
176                    )
177                ),
178            )
179
180        for widget in (self.step_slider, self.play):
181            widget.value = 0
182            widget.max = len(self.settings.output_steps) - 1
183
184        for j, xz in enumerate(("X", "Z")):
185            slider = self.slider[xz]
186            mx = self.settings.grid[j]
187            slider.max = mx
188            slider.value = (0, mx)
189
190        self.replot()
191
192    def handle_save_map(self, _):
193        display(save_and_make_link(self.plots[self.product_select.value].fig))
194
195    def handle_save_spe(self, _):
196        display(save_and_make_link(self.spectrumPlots[self.spectrum_select.value].fig))
197
198    def replot(self, *_):
199        selectedImage = self.product_select.value
200        if not (selectedImage is None or selectedImage not in self.plots):
201            self.replot_image()
202            self.outputs[selectedImage].clear_output(wait=True)
203            with self.outputs[selectedImage]:
204                display(self.plots[selectedImage].fig)
205
206        selectedSpectrum = self.spectrum_select.value
207        if not (selectedSpectrum is None or selectedSpectrum not in self.spectrumPlots):
208            self.replot_spectra()
209            self.spectrumOutputs[selectedSpectrum].clear_output(wait=True)
210            with self.spectrumOutputs[selectedSpectrum]:
211                display(self.spectrumPlots[selectedSpectrum].fig)
212
213        self.update_timeseries()
214        self.timeseriesOutput.clear_output(wait=True)
215        with self.timeseriesOutput:
216            display(self.timeseriesPlot.fig)
217
218    def update_spectra(self):
219        selected = self.spectrum_select.value
220        self.spectrum_box.children = [self.spectrumOutputs[selected]]
221        plot = self.spectrumPlots[selected]
222
223        step = self.step_slider.value
224
225        xrange = slice(*self.slider["X"].value)
226        yrange = slice(*self.slider["Z"].value)
227
228        if selected == "size":
229            for key in ("Particles Wet Size Spectrum", "Particles Dry Size Spectrum"):
230                if xrange.start == xrange.stop or yrange.start == yrange.stop:
231                    continue
232                try:
233                    data = self.storage.load(key, self.settings.output_steps[step])
234                    data = data[xrange, yrange, :]
235                    data = np.mean(np.mean(data, axis=0), axis=0)
236                    data = np.concatenate(((0,), data))
237                    if key == "Particles Wet Size Spectrum":
238                        plot.update_wet(data, step)
239                    if key == "Particles Dry Size Spectrum":
240                        plot.update_dry(data)
241                except self.storage.Exception:
242                    pass
243        elif selected == "terminal velocity":
244            try:
245                data = self.storage.load(
246                    "radius binned number averaged terminal velocity",
247                    self.settings.output_steps[step],
248                )
249
250                data = data[xrange, yrange, :]
251                data = np.where(data != 0, data, np.nan)
252                try:
253                    with warnings.catch_warnings(record=True) as _:
254                        warnings.simplefilter("ignore")
255                        data_min = np.nanmin(np.nanmin(data, axis=0), axis=0)
256                        data_max = np.nanmax(np.nanmax(data, axis=0), axis=0)
257                except RuntimeWarning:
258                    pass
259                plot.update(data_min, data_max, step)
260            except self.storage.Exception:
261                pass
262        elif selected == "temperature":
263            try:
264                dT = abs(self.settings.T_bins_edges[1] - self.settings.T_bins_edges[0])
265                np.testing.assert_allclose(np.diff(self.settings.T_bins_edges), dT)
266
267                conc = self.storage.load(
268                    "particle specific concentration", self.settings.output_steps[step]
269                )
270                # TODO #705: assert unit == mg^-1
271                conc = conc[xrange, yrange]
272
273                data = self.storage.load(
274                    "freezable specific concentration", self.settings.output_steps[step]
275                )
276                data = data[xrange, yrange, :]
277
278                data = np.sum(np.sum(data, axis=0), axis=0) / np.sum(
279                    np.sum(conc, axis=0), axis=0
280                )
281                data = np.concatenate(((0,), dT * np.cumsum(data[::-1])))[::-1]
282
283                plot.update(data, step)
284            except self.storage.Exception:
285                pass
286        else:
287            raise NotImplementedError()
288
289    def replot_spectra(self, *_):
290        self.update_spectra()
291
292        selected = self.product_select.value
293        if selected is None or selected not in self.plots:
294            return
295        self.plots[selected].update_lines(
296            self.slider["X"].value, self.slider["Z"].value
297        )
298
299        self.outputs[selected].clear_output(wait=True)
300
301        key = self.spectrum_select.value
302        self.spectrumOutputs[key].clear_output(wait=True)
303        with self.outputs[selected]:
304            display(self.plots[selected].fig)
305        with self.spectrumOutputs[key]:
306            display(self.spectrumPlots[key].fig)
307
308    def update_image(self):
309        selected = self.product_select.value
310
311        if selected in self.outputs:
312            self.plot_box.children = [self.outputs[selected]]
313
314        step = self.step_slider.value
315        try:
316            data = self.storage.load(selected, self.settings.output_steps[step])
317        except self.storage.Exception:
318            data = None
319
320        self.plots[selected].update(
321            data, step, self.storage.data_range(selected) if data is not None else None
322        )
323
324    def replot_image(self, *_):
325        selected = self.product_select.value
326        if selected is None or selected not in self.plots:
327            return
328
329        self.update_image()
330        self.outputs[selected].clear_output(wait=True)
331        with self.outputs[selected]:
332            display(self.plots[selected].fig)
333
334    def update_timeseries(self):
335        try:
336            data = self.storage.load("surf_precip")
337        except self.storage.Exception:
338            data = None
339        self.timeseriesPlot.update(
340            data, self.storage.data_range("surf_precip") if data is not None else None
341        )
342
343    def replot_timeseries(self):
344        self.update_timeseries()
345        self.timeseriesOutput.clear_output(wait=True)
346        with self.timeseriesOutput:
347            display(self.timeseriesPlot.fig)
348
349    def box(self):
350        jslink((self.play, "value"), (self.step_slider, "value"))
351        self.step_slider.observe(self.replot, "value")
352        self.product_select.observe(self.replot_image, "value")
353        self.spectrum_select.observe(self.replot_spectra, "value")
354        for xz in ("X", "Z"):
355            self.slider[xz].observe(self.replot_spectra, "value")
356        return VBox([Box([self.play, self.step_slider]), self.plots_box])
GUIViewer(storage, settings)
34    def __init__(self, storage, settings):
35        self.storage = storage
36        self.settings = settings
37
38        self.play = Play(interval=1000)
39        self.step_slider = IntSlider(continuous_update=False, description="t/dt_out:")
40        self.product_select = Dropdown()
41        self.spectrum_select = Dropdown()
42        self.plots_box = Box()
43
44        self.timeseriesOutput = None
45        self.timeseriesPlot = None
46        self.plot_box = None
47        self.spectrum_box = None
48        self.outputs = None
49        self.plots = None
50        self.spectrumOutputs = None
51        self.spectrumPlots = None
52        self.products = None
53
54        self.slider = {}
55        self.lines = {"X": [{}, {}], "Z": [{}, {}]}
56        for xz in ("X", "Z"):
57            self.slider[xz] = IntRangeSlider(
58                min=0,
59                max=1,
60                description=f"{xz}",
61                continuous_update=False,
62                orientation="horizontal" if xz == "X" else "vertical",
63            )
64
65        self.reinit({})
storage
settings
play
step_slider
product_select
spectrum_select
plots_box
timeseriesOutput
timeseriesPlot
plot_box
spectrum_box
outputs
plots
spectrumOutputs
spectrumPlots
products
slider
lines
def clear(self):
67    def clear(self):
68        self.plots_box.children = ()
def reinit(self, products):
 70    def reinit(self, products):
 71        self.products = products
 72        self.product_select.options = tuple(
 73            (f"{val.name} [{val.unit}]", key)
 74            for key, val in sorted(self.products.items(), key=lambda item: item[1].name)
 75            if len(val.shape) == 2
 76        )
 77        opts = [
 78            ("particle size spectra", "size"),
 79            ("terminal velocity", "terminal velocity"),
 80        ]
 81        if "freezable specific concentration" in products:
 82            opts.append(("freezing temperature spectra", "temperature"))
 83        if "immersed surface area" in products:
 84            pass  # TODO #599
 85        self.spectrum_select.options = tuple(opts)
 86
 87        r_bins = self.settings.r_bins_edges.copy()
 88        const.convert_to(r_bins, const.si.micrometres)
 89        self.spectrumOutputs = {}
 90        self.spectrumPlots = {}
 91        for key in ("size", "terminal velocity", "temperature"):
 92            self.spectrumOutputs[key] = Output()
 93            with self.spectrumOutputs[key]:
 94                self.spectrumPlots[key] = (
 95                    _SpectrumPlot(r_bins, self.settings.spectrum_per_mass_of_dry_air)
 96                    if key == "size"
 97                    else (
 98                        _TemperaturePlot(
 99                            self.settings.T_bins_edges, self.settings.formulae
100                        )
101                        if key == "temperature"
102                        else _TerminalVelocityPlot(
103                            self.settings.terminal_velocity_radius_bin_edges,
104                            self.settings.formulae,
105                        )
106                    )
107                )
108                clear_output()
109
110        self.timeseriesOutput = Output()
111        with self.timeseriesOutput:
112            default_figsize = rcParams["figure.figsize"]
113            fig_kw = {"figsize": (2.5 * default_figsize[0], default_figsize[1] / 2)}
114            fig, ax = pyplot.subplots(1, 1, **fig_kw)
115            self.timeseriesPlot = _TimeseriesPlot(
116                fig, ax, self.settings.output_steps * self.settings.dt
117            )
118            clear_output()
119
120        self.plots = {}
121        self.outputs = {}
122        for key, product in products.items():
123            if len(product.shape) == 2:
124                self.outputs[key] = Output()
125                with self.outputs[key]:
126                    fig, ax = pyplot.subplots(1, 1)
127                    self.plots[key] = _ImagePlot(
128                        fig,
129                        ax,
130                        self.settings.grid,
131                        self.settings.size,
132                        product,
133                        show=True,
134                        lines=True,
135                    )
136                    clear_output()
137
138        self.plot_box = Box()
139        self.spectrum_box = Box()
140        if len(products.keys()) > 0:
141            layout_flex_end = Layout(display="flex", justify_content="flex-end")
142            save_map = Button(icon="save")
143            save_map.on_click(self.handle_save_map)
144            save_spe = Button(icon="save")
145            save_spe.on_click(self.handle_save_spe)
146            self.plots_box.children = (
147                VBox(
148                    children=(
149                        HBox(
150                            children=(
151                                VBox(
152                                    children=(
153                                        Box(
154                                            layout=layout_flex_end,
155                                            children=(save_map, self.product_select),
156                                        ),
157                                        HBox((self.slider["Z"], self.plot_box)),
158                                        HBox(
159                                            (self.slider["X"],), layout=layout_flex_end
160                                        ),
161                                    )
162                                ),
163                                VBox(
164                                    layout=Layout(),
165                                    children=(
166                                        Box(
167                                            children=(save_spe, self.spectrum_select),
168                                            layout=layout_flex_end,
169                                        ),
170                                        self.spectrum_box,
171                                    ),
172                                ),
173                            )
174                        ),
175                        HBox((self.timeseriesOutput,)),
176                    )
177                ),
178            )
179
180        for widget in (self.step_slider, self.play):
181            widget.value = 0
182            widget.max = len(self.settings.output_steps) - 1
183
184        for j, xz in enumerate(("X", "Z")):
185            slider = self.slider[xz]
186            mx = self.settings.grid[j]
187            slider.max = mx
188            slider.value = (0, mx)
189
190        self.replot()
def handle_save_map(self, _):
192    def handle_save_map(self, _):
193        display(save_and_make_link(self.plots[self.product_select.value].fig))
def handle_save_spe(self, _):
195    def handle_save_spe(self, _):
196        display(save_and_make_link(self.spectrumPlots[self.spectrum_select.value].fig))
def replot(self, *_):
198    def replot(self, *_):
199        selectedImage = self.product_select.value
200        if not (selectedImage is None or selectedImage not in self.plots):
201            self.replot_image()
202            self.outputs[selectedImage].clear_output(wait=True)
203            with self.outputs[selectedImage]:
204                display(self.plots[selectedImage].fig)
205
206        selectedSpectrum = self.spectrum_select.value
207        if not (selectedSpectrum is None or selectedSpectrum not in self.spectrumPlots):
208            self.replot_spectra()
209            self.spectrumOutputs[selectedSpectrum].clear_output(wait=True)
210            with self.spectrumOutputs[selectedSpectrum]:
211                display(self.spectrumPlots[selectedSpectrum].fig)
212
213        self.update_timeseries()
214        self.timeseriesOutput.clear_output(wait=True)
215        with self.timeseriesOutput:
216            display(self.timeseriesPlot.fig)
def update_spectra(self):
218    def update_spectra(self):
219        selected = self.spectrum_select.value
220        self.spectrum_box.children = [self.spectrumOutputs[selected]]
221        plot = self.spectrumPlots[selected]
222
223        step = self.step_slider.value
224
225        xrange = slice(*self.slider["X"].value)
226        yrange = slice(*self.slider["Z"].value)
227
228        if selected == "size":
229            for key in ("Particles Wet Size Spectrum", "Particles Dry Size Spectrum"):
230                if xrange.start == xrange.stop or yrange.start == yrange.stop:
231                    continue
232                try:
233                    data = self.storage.load(key, self.settings.output_steps[step])
234                    data = data[xrange, yrange, :]
235                    data = np.mean(np.mean(data, axis=0), axis=0)
236                    data = np.concatenate(((0,), data))
237                    if key == "Particles Wet Size Spectrum":
238                        plot.update_wet(data, step)
239                    if key == "Particles Dry Size Spectrum":
240                        plot.update_dry(data)
241                except self.storage.Exception:
242                    pass
243        elif selected == "terminal velocity":
244            try:
245                data = self.storage.load(
246                    "radius binned number averaged terminal velocity",
247                    self.settings.output_steps[step],
248                )
249
250                data = data[xrange, yrange, :]
251                data = np.where(data != 0, data, np.nan)
252                try:
253                    with warnings.catch_warnings(record=True) as _:
254                        warnings.simplefilter("ignore")
255                        data_min = np.nanmin(np.nanmin(data, axis=0), axis=0)
256                        data_max = np.nanmax(np.nanmax(data, axis=0), axis=0)
257                except RuntimeWarning:
258                    pass
259                plot.update(data_min, data_max, step)
260            except self.storage.Exception:
261                pass
262        elif selected == "temperature":
263            try:
264                dT = abs(self.settings.T_bins_edges[1] - self.settings.T_bins_edges[0])
265                np.testing.assert_allclose(np.diff(self.settings.T_bins_edges), dT)
266
267                conc = self.storage.load(
268                    "particle specific concentration", self.settings.output_steps[step]
269                )
270                # TODO #705: assert unit == mg^-1
271                conc = conc[xrange, yrange]
272
273                data = self.storage.load(
274                    "freezable specific concentration", self.settings.output_steps[step]
275                )
276                data = data[xrange, yrange, :]
277
278                data = np.sum(np.sum(data, axis=0), axis=0) / np.sum(
279                    np.sum(conc, axis=0), axis=0
280                )
281                data = np.concatenate(((0,), dT * np.cumsum(data[::-1])))[::-1]
282
283                plot.update(data, step)
284            except self.storage.Exception:
285                pass
286        else:
287            raise NotImplementedError()
def replot_spectra(self, *_):
289    def replot_spectra(self, *_):
290        self.update_spectra()
291
292        selected = self.product_select.value
293        if selected is None or selected not in self.plots:
294            return
295        self.plots[selected].update_lines(
296            self.slider["X"].value, self.slider["Z"].value
297        )
298
299        self.outputs[selected].clear_output(wait=True)
300
301        key = self.spectrum_select.value
302        self.spectrumOutputs[key].clear_output(wait=True)
303        with self.outputs[selected]:
304            display(self.plots[selected].fig)
305        with self.spectrumOutputs[key]:
306            display(self.spectrumPlots[key].fig)
def update_image(self):
308    def update_image(self):
309        selected = self.product_select.value
310
311        if selected in self.outputs:
312            self.plot_box.children = [self.outputs[selected]]
313
314        step = self.step_slider.value
315        try:
316            data = self.storage.load(selected, self.settings.output_steps[step])
317        except self.storage.Exception:
318            data = None
319
320        self.plots[selected].update(
321            data, step, self.storage.data_range(selected) if data is not None else None
322        )
def replot_image(self, *_):
324    def replot_image(self, *_):
325        selected = self.product_select.value
326        if selected is None or selected not in self.plots:
327            return
328
329        self.update_image()
330        self.outputs[selected].clear_output(wait=True)
331        with self.outputs[selected]:
332            display(self.plots[selected].fig)
def update_timeseries(self):
334    def update_timeseries(self):
335        try:
336            data = self.storage.load("surf_precip")
337        except self.storage.Exception:
338            data = None
339        self.timeseriesPlot.update(
340            data, self.storage.data_range("surf_precip") if data is not None else None
341        )
def replot_timeseries(self):
343    def replot_timeseries(self):
344        self.update_timeseries()
345        self.timeseriesOutput.clear_output(wait=True)
346        with self.timeseriesOutput:
347            display(self.timeseriesPlot.fig)
def box(self):
349    def box(self):
350        jslink((self.play, "value"), (self.step_slider, "value"))
351        self.step_slider.observe(self.replot, "value")
352        self.product_select.observe(self.replot_image, "value")
353        self.spectrum_select.observe(self.replot_spectra, "value")
354        for xz in ("X", "Z"):
355            self.slider[xz].observe(self.replot_spectra, "value")
356        return VBox([Box([self.play, self.step_slider]), self.plots_box])