cloudnetpy 1.55.20__py3-none-any.whl → 1.55.22__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cloudnetpy/categorize/atmos.py +46 -14
- cloudnetpy/categorize/atmos_utils.py +11 -1
- cloudnetpy/categorize/categorize.py +38 -21
- cloudnetpy/categorize/classify.py +31 -9
- cloudnetpy/categorize/containers.py +19 -7
- cloudnetpy/categorize/droplet.py +24 -8
- cloudnetpy/categorize/falling.py +17 -7
- cloudnetpy/categorize/freezing.py +19 -5
- cloudnetpy/categorize/insects.py +27 -14
- cloudnetpy/categorize/lidar.py +38 -36
- cloudnetpy/categorize/melting.py +19 -9
- cloudnetpy/categorize/model.py +28 -9
- cloudnetpy/categorize/mwr.py +4 -2
- cloudnetpy/categorize/radar.py +58 -22
- cloudnetpy/cloudnetarray.py +15 -6
- cloudnetpy/concat_lib.py +39 -16
- cloudnetpy/constants.py +7 -0
- cloudnetpy/datasource.py +39 -19
- cloudnetpy/instruments/basta.py +6 -2
- cloudnetpy/instruments/campbell_scientific.py +33 -16
- cloudnetpy/instruments/ceilo.py +30 -13
- cloudnetpy/instruments/ceilometer.py +76 -37
- cloudnetpy/instruments/cl61d.py +8 -3
- cloudnetpy/instruments/cloudnet_instrument.py +2 -1
- cloudnetpy/instruments/copernicus.py +27 -14
- cloudnetpy/instruments/disdrometer/common.py +51 -32
- cloudnetpy/instruments/disdrometer/parsivel.py +79 -48
- cloudnetpy/instruments/disdrometer/thies.py +10 -6
- cloudnetpy/instruments/galileo.py +23 -12
- cloudnetpy/instruments/hatpro.py +27 -11
- cloudnetpy/instruments/instruments.py +4 -1
- cloudnetpy/instruments/lufft.py +20 -11
- cloudnetpy/instruments/mira.py +60 -49
- cloudnetpy/instruments/mrr.py +31 -20
- cloudnetpy/instruments/nc_lidar.py +15 -6
- cloudnetpy/instruments/nc_radar.py +31 -22
- cloudnetpy/instruments/pollyxt.py +36 -21
- cloudnetpy/instruments/radiometrics.py +32 -18
- cloudnetpy/instruments/rpg.py +48 -22
- cloudnetpy/instruments/rpg_reader.py +39 -30
- cloudnetpy/instruments/vaisala.py +39 -27
- cloudnetpy/instruments/weather_station.py +15 -11
- cloudnetpy/metadata.py +3 -1
- cloudnetpy/model_evaluation/file_handler.py +31 -21
- cloudnetpy/model_evaluation/metadata.py +3 -1
- cloudnetpy/model_evaluation/model_metadata.py +1 -1
- cloudnetpy/model_evaluation/plotting/plot_tools.py +20 -15
- cloudnetpy/model_evaluation/plotting/plotting.py +114 -64
- cloudnetpy/model_evaluation/products/advance_methods.py +48 -28
- cloudnetpy/model_evaluation/products/grid_methods.py +44 -19
- cloudnetpy/model_evaluation/products/model_products.py +22 -18
- cloudnetpy/model_evaluation/products/observation_products.py +15 -9
- cloudnetpy/model_evaluation/products/product_resampling.py +14 -4
- cloudnetpy/model_evaluation/products/tools.py +16 -7
- cloudnetpy/model_evaluation/statistics/statistical_methods.py +28 -15
- cloudnetpy/model_evaluation/tests/e2e/conftest.py +3 -3
- cloudnetpy/model_evaluation/tests/e2e/process_cf/main.py +9 -5
- cloudnetpy/model_evaluation/tests/e2e/process_cf/tests.py +14 -13
- cloudnetpy/model_evaluation/tests/e2e/process_iwc/main.py +9 -5
- cloudnetpy/model_evaluation/tests/e2e/process_iwc/tests.py +14 -13
- cloudnetpy/model_evaluation/tests/e2e/process_lwc/main.py +9 -5
- cloudnetpy/model_evaluation/tests/e2e/process_lwc/tests.py +14 -13
- cloudnetpy/model_evaluation/tests/unit/conftest.py +11 -11
- cloudnetpy/model_evaluation/tests/unit/test_advance_methods.py +33 -27
- cloudnetpy/model_evaluation/tests/unit/test_grid_methods.py +83 -83
- cloudnetpy/model_evaluation/tests/unit/test_model_products.py +23 -21
- cloudnetpy/model_evaluation/tests/unit/test_observation_products.py +24 -25
- cloudnetpy/model_evaluation/tests/unit/test_plot_tools.py +40 -39
- cloudnetpy/model_evaluation/tests/unit/test_plotting.py +12 -11
- cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py +30 -30
- cloudnetpy/model_evaluation/tests/unit/test_tools.py +18 -17
- cloudnetpy/model_evaluation/utils.py +3 -2
- cloudnetpy/output.py +45 -19
- cloudnetpy/plotting/plot_meta.py +35 -11
- cloudnetpy/plotting/plotting.py +172 -104
- cloudnetpy/products/classification.py +20 -8
- cloudnetpy/products/der.py +25 -10
- cloudnetpy/products/drizzle.py +41 -26
- cloudnetpy/products/drizzle_error.py +10 -5
- cloudnetpy/products/drizzle_tools.py +43 -24
- cloudnetpy/products/ier.py +10 -5
- cloudnetpy/products/iwc.py +16 -9
- cloudnetpy/products/lwc.py +34 -12
- cloudnetpy/products/mwr_multi.py +4 -1
- cloudnetpy/products/mwr_single.py +4 -1
- cloudnetpy/products/product_tools.py +33 -10
- cloudnetpy/utils.py +175 -74
- cloudnetpy/version.py +1 -1
- {cloudnetpy-1.55.20.dist-info → cloudnetpy-1.55.22.dist-info}/METADATA +11 -10
- cloudnetpy-1.55.22.dist-info/RECORD +114 -0
- docs/source/conf.py +2 -2
- cloudnetpy-1.55.20.dist-info/RECORD +0 -114
- {cloudnetpy-1.55.20.dist-info → cloudnetpy-1.55.22.dist-info}/LICENSE +0 -0
- {cloudnetpy-1.55.20.dist-info → cloudnetpy-1.55.22.dist-info}/WHEEL +0 -0
- {cloudnetpy-1.55.20.dist-info → cloudnetpy-1.55.22.dist-info}/top_level.txt +0 -0
@@ -1,4 +1,4 @@
|
|
1
|
-
import matplotlib
|
1
|
+
import matplotlib as mpl
|
2
2
|
import netCDF4
|
3
3
|
import numpy as np
|
4
4
|
from matplotlib.colors import ListedColormap
|
@@ -12,17 +12,18 @@ def parse_wanted_names(
|
|
12
12
|
name: str,
|
13
13
|
model: str,
|
14
14
|
variables: list | None = None,
|
15
|
+
*,
|
15
16
|
advance: bool = False,
|
16
17
|
) -> tuple[list, list]:
|
17
18
|
"""Returns standard and advection lists of product types to plot"""
|
18
19
|
if variables:
|
19
20
|
names = variables
|
20
21
|
else:
|
21
|
-
names = parse_dataset_keys(nc_file, name, advance, model)
|
22
|
+
names = parse_dataset_keys(nc_file, name, advance=advance, model=model)
|
22
23
|
standard_n = [n for n in names if name in n and "adv" not in n]
|
23
24
|
standard_n = sort_model2first_element(standard_n, model)
|
24
25
|
advection_n = [n for n in names if name in n and "adv" in n]
|
25
|
-
model_names = [n for n in names if f"{model}_" in n and
|
26
|
+
model_names = [n for n in names if f"{model}_" in n and f"_{model}_" not in n]
|
26
27
|
for i, model_n in enumerate(model_names):
|
27
28
|
advection_n.insert(0 + i, model_n)
|
28
29
|
if len(advection_n) < len(standard_n):
|
@@ -33,18 +34,18 @@ def parse_wanted_names(
|
|
33
34
|
|
34
35
|
|
35
36
|
def parse_dataset_keys(
|
36
|
-
nc_file: str,
|
37
|
+
nc_file: str,
|
38
|
+
product: str,
|
39
|
+
*,
|
40
|
+
advance: bool,
|
41
|
+
model: str,
|
37
42
|
) -> list:
|
38
43
|
names = list(netCDF4.Dataset(nc_file).variables.keys())
|
39
44
|
a_names = ["cirrus", "snow"]
|
40
45
|
model_vars = []
|
41
46
|
for n in names:
|
42
|
-
if model not in n:
|
43
|
-
model_vars.append(n)
|
44
|
-
elif model in n and product not in n:
|
47
|
+
if model not in n or (model in n and product not in n):
|
45
48
|
model_vars.append(n)
|
46
|
-
else:
|
47
|
-
continue
|
48
49
|
if not advance:
|
49
50
|
for a in a_names:
|
50
51
|
for n in names:
|
@@ -85,10 +86,12 @@ def read_data_characters(nc_file: str, name: str, model: str) -> tuple:
|
|
85
86
|
x = reshape_1d2nd(x, data)
|
86
87
|
try:
|
87
88
|
y = nc.variables[f"{model}_height"][:]
|
88
|
-
except KeyError:
|
89
|
+
except KeyError as err:
|
89
90
|
model_info = MODELS[model]
|
90
91
|
cycles = model_info.cycle
|
91
|
-
|
92
|
+
if cycles is None:
|
93
|
+
msg = f"Invalid model: {model}"
|
94
|
+
raise RuntimeError(msg) from err
|
92
95
|
cycles_split = [x.strip() for x in cycles.split(",")]
|
93
96
|
cycle = [cycle for cycle in cycles_split if cycle in name]
|
94
97
|
y = nc.variables[f"{model}_{cycle[0]}_height"][:]
|
@@ -102,7 +105,7 @@ def read_data_characters(nc_file: str, name: str, model: str) -> tuple:
|
|
102
105
|
return data, x, y
|
103
106
|
|
104
107
|
|
105
|
-
def mask_small_values(data: ma.MaskedArray, name: str):
|
108
|
+
def mask_small_values(data: ma.MaskedArray, name: str) -> ma.MaskedArray:
|
106
109
|
data[data <= 0] = ma.masked
|
107
110
|
if "lwc" in name:
|
108
111
|
data[data < 1e-5] = ma.masked
|
@@ -127,14 +130,14 @@ def create_segment_values(arrays: list) -> tuple:
|
|
127
130
|
new_array[new_array == 3] = 2
|
128
131
|
new_array[new_array == 4] = 3
|
129
132
|
|
130
|
-
colors =
|
133
|
+
colors = mpl.colormaps["YlGnBu"]
|
131
134
|
newcolors = colors(np.linspace(0, 1, 256))
|
132
135
|
# No data, model, both, observation
|
133
136
|
cmap = ListedColormap(["white", "khaki", newcolors[90], newcolors[140]])
|
134
137
|
return new_array, cmap
|
135
138
|
|
136
139
|
|
137
|
-
def set_yaxis(ax, max_y: float, min_y: float = 0.0):
|
140
|
+
def set_yaxis(ax, max_y: float, min_y: float = 0.0) -> None:
|
138
141
|
ax.set_ylim(min_y, max_y)
|
139
142
|
ax.set_ylabel("Height (km)", fontsize=13)
|
140
143
|
|
@@ -150,7 +153,9 @@ def rolling_mean(data: ma.MaskedArray, n: int = 4) -> np.ndarray:
|
|
150
153
|
|
151
154
|
|
152
155
|
def change2one_dim_axes(
|
153
|
-
x: ma.MaskedArray,
|
156
|
+
x: ma.MaskedArray,
|
157
|
+
y: ma.MaskedArray,
|
158
|
+
data: np.ndarray,
|
154
159
|
) -> tuple:
|
155
160
|
# If any mask in x or y, change 2d to 1d axes values
|
156
161
|
# Common shape need to match 2d data.
|
@@ -4,6 +4,7 @@ import sys
|
|
4
4
|
|
5
5
|
import matplotlib.pyplot as plt
|
6
6
|
import numpy as np
|
7
|
+
from matplotlib.colorbar import Colorbar
|
7
8
|
from matplotlib.patches import Patch
|
8
9
|
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
9
10
|
from numpy import ma
|
@@ -21,6 +22,7 @@ def generate_L3_day_plots(
|
|
21
22
|
nc_file: str,
|
22
23
|
product: str,
|
23
24
|
model: str,
|
25
|
+
*,
|
24
26
|
title: bool = True,
|
25
27
|
var_list: list | None = None,
|
26
28
|
fig_type: str | None = "group",
|
@@ -28,7 +30,7 @@ def generate_L3_day_plots(
|
|
28
30
|
save_path: str | None = None,
|
29
31
|
image_name: str | None = None,
|
30
32
|
show: bool | None = False,
|
31
|
-
):
|
33
|
+
) -> None:
|
32
34
|
"""Generate visualizations for level 3 dayscale products.
|
33
35
|
With figure type visualizations can be subplot in group, pair, single or
|
34
36
|
statistic of given product. In group fig_type all different methods are plot
|
@@ -38,7 +40,9 @@ def generate_L3_day_plots(
|
|
38
40
|
Single fig_type will plot each product variable in a own figure.
|
39
41
|
Statistic fig_type will plot select statistical method of all product method
|
40
42
|
in same fig.
|
43
|
+
|
41
44
|
Args:
|
45
|
+
----
|
42
46
|
nc_file (str): Path to source file
|
43
47
|
product (str): Name of product wanted to plot
|
44
48
|
model (str): Name of model which downsampling was done with
|
@@ -63,7 +67,9 @@ def generate_L3_day_plots(
|
|
63
67
|
In case of model cycles, cycles are visualized in their on figures same
|
64
68
|
way as an individual model run would be visualized in its own in a group
|
65
69
|
figure.
|
70
|
+
|
66
71
|
Examples:
|
72
|
+
--------
|
67
73
|
>>> from cloudnetpy.model_evaluation.plotting.plotting
|
68
74
|
import generate_L3_day_plots
|
69
75
|
>>> l3_day_file = 'cf_ecmwf.nc'
|
@@ -76,6 +82,11 @@ def generate_L3_day_plots(
|
|
76
82
|
>>> generate_L3_day_plots(l3_day_file, product, model,
|
77
83
|
>>> fig_type='statistic', stats=['error'])
|
78
84
|
"""
|
85
|
+
|
86
|
+
def _check_cycle_names():
|
87
|
+
if not c_names:
|
88
|
+
raise AttributeError
|
89
|
+
|
79
90
|
cls = __import__("plotting")
|
80
91
|
model_info = MODELS[model]
|
81
92
|
model_name = model_info.model_name
|
@@ -85,8 +96,7 @@ def generate_L3_day_plots(
|
|
85
96
|
try:
|
86
97
|
cycle_names, cycles = p_tools.sort_cycles(names, model)
|
87
98
|
for i, c_names in enumerate(cycle_names):
|
88
|
-
|
89
|
-
raise AttributeError
|
99
|
+
_check_cycle_names()
|
90
100
|
params = [
|
91
101
|
product,
|
92
102
|
c_names,
|
@@ -95,10 +105,8 @@ def generate_L3_day_plots(
|
|
95
105
|
model_name,
|
96
106
|
save_path,
|
97
107
|
image_name,
|
98
|
-
show,
|
99
|
-
cycles[i],
|
100
|
-
title,
|
101
108
|
]
|
109
|
+
kwargs = {"show": show, "title": title, "cycle": cycles[i]}
|
102
110
|
if fig_type == "statistic":
|
103
111
|
params = [
|
104
112
|
product,
|
@@ -109,11 +117,8 @@ def generate_L3_day_plots(
|
|
109
117
|
stats,
|
110
118
|
save_path,
|
111
119
|
image_name,
|
112
|
-
show,
|
113
|
-
cycles[i],
|
114
|
-
title,
|
115
120
|
]
|
116
|
-
getattr(cls, f"get_{fig_type}_plots")(*params)
|
121
|
+
getattr(cls, f"get_{fig_type}_plots")(*params, **kwargs)
|
117
122
|
except AttributeError:
|
118
123
|
params = [
|
119
124
|
product,
|
@@ -123,10 +128,8 @@ def generate_L3_day_plots(
|
|
123
128
|
model_name,
|
124
129
|
save_path,
|
125
130
|
image_name,
|
126
|
-
show,
|
127
|
-
"",
|
128
|
-
title,
|
129
131
|
]
|
132
|
+
kwargs = {"show": show, "title": title}
|
130
133
|
if fig_type == "statistic":
|
131
134
|
params = [
|
132
135
|
product,
|
@@ -137,11 +140,8 @@ def generate_L3_day_plots(
|
|
137
140
|
stats,
|
138
141
|
save_path,
|
139
142
|
image_name,
|
140
|
-
show,
|
141
|
-
"",
|
142
|
-
title,
|
143
143
|
]
|
144
|
-
getattr(cls, f"get_{fig_type}_plots")(*params)
|
144
|
+
getattr(cls, f"get_{fig_type}_plots")(*params, **kwargs)
|
145
145
|
|
146
146
|
|
147
147
|
def get_group_plots(
|
@@ -152,16 +152,19 @@ def get_group_plots(
|
|
152
152
|
model_name: str,
|
153
153
|
save_path: str,
|
154
154
|
image_name: str,
|
155
|
+
*,
|
155
156
|
show: bool,
|
156
157
|
cycle: str = "",
|
157
158
|
title: bool = True,
|
158
159
|
include_xlimits: bool = False,
|
159
|
-
):
|
160
|
+
) -> None:
|
160
161
|
"""Group subplot visualization for both standard and advection downsampling.
|
161
162
|
Generates group subplot figure for product with model and all different
|
162
163
|
downsampling methods. Generates separated figures for standard and advection
|
163
164
|
timegrids. All model cycles if any will be generated to their own figures.
|
165
|
+
|
164
166
|
Args:
|
167
|
+
----
|
165
168
|
product (str): Name of the product
|
166
169
|
names (list): List of variables to be visualized to same fig
|
167
170
|
nc_file (str): Path to a source file
|
@@ -194,7 +197,11 @@ def get_group_plots(
|
|
194
197
|
fig.text(0.64, 0.885, f"Cycle: {cycle}", fontsize=13)
|
195
198
|
model_run = f"{model}_{cycle}"
|
196
199
|
cloud_plt.handle_saving(
|
197
|
-
image_name,
|
200
|
+
image_name,
|
201
|
+
save_path,
|
202
|
+
casedate,
|
203
|
+
[product, model_run, "group"],
|
204
|
+
show=show,
|
198
205
|
)
|
199
206
|
|
200
207
|
|
@@ -206,16 +213,19 @@ def get_pair_plots(
|
|
206
213
|
model_name: str,
|
207
214
|
save_path: str,
|
208
215
|
image_name: str,
|
209
|
-
show: bool,
|
210
216
|
cycle: str = "",
|
217
|
+
*,
|
218
|
+
show: bool = False,
|
211
219
|
title: bool = True,
|
212
220
|
include_xlimits: bool = False,
|
213
|
-
):
|
221
|
+
) -> None:
|
214
222
|
"""Pair subplots of model and product method.
|
215
223
|
In upper subplot is model product and lower subplot one of the
|
216
224
|
downsampled method of select product. Function generates all product methods
|
217
225
|
in a given nc-file in loop.
|
226
|
+
|
218
227
|
Args:
|
228
|
+
----
|
219
229
|
product (str): Name of the product
|
220
230
|
names (list): List of variables to be visualized to same fig
|
221
231
|
nc_file (str): Path to a source file
|
@@ -248,7 +258,13 @@ def get_pair_plots(
|
|
248
258
|
casedate = cloud_plt.set_labels(fig, ax[-1], nc_file)
|
249
259
|
if len(cycle) > 1:
|
250
260
|
fig.text(0.64, 0.889, f"Cycle: {cycle}", fontsize=13)
|
251
|
-
cloud_plt.handle_saving(
|
261
|
+
cloud_plt.handle_saving(
|
262
|
+
image_name,
|
263
|
+
save_path,
|
264
|
+
casedate,
|
265
|
+
[name, "pair"],
|
266
|
+
show=show,
|
267
|
+
)
|
252
268
|
|
253
269
|
|
254
270
|
def get_single_plots(
|
@@ -259,13 +275,16 @@ def get_single_plots(
|
|
259
275
|
model_name: str,
|
260
276
|
save_path: str,
|
261
277
|
image_name: str,
|
278
|
+
*,
|
262
279
|
show: bool,
|
263
280
|
cycle: str = "",
|
264
281
|
title: bool = True,
|
265
282
|
include_xlimits: bool = False,
|
266
|
-
):
|
283
|
+
) -> None:
|
267
284
|
"""Generates figures of each product variable from given file in loop.
|
285
|
+
|
268
286
|
Args:
|
287
|
+
----
|
269
288
|
product (str): Name of the product
|
270
289
|
names (list): List of variables to be visualized to same fig
|
271
290
|
nc_file (str): Path to a source file
|
@@ -293,10 +312,16 @@ def get_single_plots(
|
|
293
312
|
fig.text(0.64, 0.9, f"{model_name} cycle: {cycle}", fontsize=13)
|
294
313
|
else:
|
295
314
|
fig.text(0.64, 0.9, f"{model_name}", fontsize=13)
|
296
|
-
cloud_plt.handle_saving(
|
315
|
+
cloud_plt.handle_saving(
|
316
|
+
image_name,
|
317
|
+
save_path,
|
318
|
+
casedate,
|
319
|
+
[name, "single"],
|
320
|
+
show=show,
|
321
|
+
)
|
297
322
|
|
298
323
|
|
299
|
-
def plot_colormesh(ax, data: np.ndarray, axes: tuple, variable_info):
|
324
|
+
def plot_colormesh(ax, data: np.ndarray, axes: tuple, variable_info) -> None:
|
300
325
|
vmin, vmax = variable_info.plot_range
|
301
326
|
if variable_info.plot_scale == "logarithmic":
|
302
327
|
data, vmin, vmax = cloud_plt.lin2log(data, vmin, vmax)
|
@@ -306,7 +331,7 @@ def plot_colormesh(ax, data: np.ndarray, axes: tuple, variable_info):
|
|
306
331
|
colorbar = init_colorbar(pl, ax)
|
307
332
|
if variable_info.plot_scale == "logarithmic":
|
308
333
|
tick_labels = cloud_plt.generate_log_cbar_ticklabel_list(vmin, vmax)
|
309
|
-
colorbar.set_ticks(np.arange(vmin, vmax + 1))
|
334
|
+
colorbar.set_ticks(np.arange(vmin, vmax + 1).tolist())
|
310
335
|
colorbar.ax.set_yticklabels(tick_labels)
|
311
336
|
ax.set_facecolor("white")
|
312
337
|
colorbar.set_label(variable_info.clabel, fontsize=13)
|
@@ -321,10 +346,11 @@ def get_statistic_plots(
|
|
321
346
|
stats: list,
|
322
347
|
save_path: str,
|
323
348
|
image_name: str,
|
349
|
+
*,
|
324
350
|
show: bool,
|
325
351
|
cycle: str = "",
|
326
352
|
title: bool = True,
|
327
|
-
):
|
353
|
+
) -> None:
|
328
354
|
"""Statistical subplots for day scale products.
|
329
355
|
Statistical analysis can be done by day scale with relative error ('error'),
|
330
356
|
total data area analysis ('area'), histogram ('hist') or vertical profiles
|
@@ -332,7 +358,9 @@ def get_statistic_plots(
|
|
332
358
|
per statistical method for a select product. All different downsampled method
|
333
359
|
are in a same fig. Standard and advection timegrids are separated to own figs
|
334
360
|
as well as different cycle runs.
|
361
|
+
|
335
362
|
Args:
|
363
|
+
----
|
336
364
|
product (str): Name of the product
|
337
365
|
names (list): List of variables to be visualized to same fig
|
338
366
|
nc_file (str): Path to a source file
|
@@ -345,11 +373,22 @@ def get_statistic_plots(
|
|
345
373
|
show (bool): Show figure before saving if True
|
346
374
|
cycle (str): Name of cycle if exists
|
347
375
|
"""
|
348
|
-
# pylint: disable=too-many-branches
|
349
|
-
# pylint: disable=too-many-nested-blocks
|
350
376
|
model_run = model
|
351
377
|
name = ""
|
352
378
|
j = 0
|
379
|
+
|
380
|
+
def _check_data():
|
381
|
+
if model_missing and obs_missing:
|
382
|
+
_raise()
|
383
|
+
|
384
|
+
def _check_data2():
|
385
|
+
if "error" in stat and np.all(day_stat.model_stat.mask is True):
|
386
|
+
_raise()
|
387
|
+
|
388
|
+
def _raise():
|
389
|
+
err_msg = f"No data in {model_name} or observation"
|
390
|
+
raise ValueError(err_msg)
|
391
|
+
|
353
392
|
for stat in stats:
|
354
393
|
try:
|
355
394
|
obs_missing = False
|
@@ -363,33 +402,32 @@ def get_statistic_plots(
|
|
363
402
|
data, x, y = p_tools.read_data_characters(nc_file, name, model)
|
364
403
|
if np.all(data.mask is True):
|
365
404
|
obs_missing = True
|
366
|
-
|
367
|
-
|
368
|
-
if product == "cf" and stat == "error":
|
369
|
-
stat = "aerror"
|
405
|
+
_check_data()
|
406
|
+
statistics = "aerror" if product == "cf" and stat == "error" else stat
|
370
407
|
if j > 0:
|
371
|
-
|
372
|
-
|
408
|
+
name_new = ""
|
409
|
+
name_new = _get_stat_titles(name_new, product, variable_info)
|
373
410
|
day_stat = DayStatistics(
|
374
|
-
|
411
|
+
statistics,
|
412
|
+
[product, model_name, name_new],
|
413
|
+
model_data,
|
414
|
+
data,
|
375
415
|
)
|
376
|
-
|
377
|
-
if np.all(day_stat.model_stat.mask is True):
|
378
|
-
raise ValueError("No data to calculate relative error")
|
416
|
+
_check_data2()
|
379
417
|
initialize_statistic_plots(
|
380
418
|
j,
|
381
419
|
len(names) - 1,
|
382
420
|
ax[j - 1],
|
383
|
-
|
421
|
+
statistics,
|
384
422
|
day_stat,
|
385
423
|
model_data,
|
386
424
|
data,
|
387
425
|
(x, y),
|
388
426
|
variable_info,
|
389
|
-
title,
|
427
|
+
title=title,
|
390
428
|
)
|
391
|
-
except ValueError
|
392
|
-
logging.
|
429
|
+
except ValueError:
|
430
|
+
logging.exception("Exception occurred")
|
393
431
|
if stat not in ("hist", "vertical"):
|
394
432
|
casedate = cloud_plt.set_labels(fig, ax[j - 1], nc_file, sub_title=title)
|
395
433
|
else:
|
@@ -401,7 +439,11 @@ def get_statistic_plots(
|
|
401
439
|
fig.text(0.64, 0.885, f"Cycle: {cycle}", fontsize=13)
|
402
440
|
model_run = f"{model}_{cycle}"
|
403
441
|
cloud_plt.handle_saving(
|
404
|
-
image_name,
|
442
|
+
image_name,
|
443
|
+
save_path,
|
444
|
+
casedate,
|
445
|
+
[name, stat, model_run],
|
446
|
+
show=show,
|
405
447
|
)
|
406
448
|
|
407
449
|
|
@@ -415,9 +457,10 @@ def initialize_statistic_plots(
|
|
415
457
|
obs: ma.MaskedArray,
|
416
458
|
args: tuple,
|
417
459
|
variable_info,
|
460
|
+
*,
|
418
461
|
title: bool = True,
|
419
462
|
include_xlimits: bool = False,
|
420
|
-
):
|
463
|
+
) -> None:
|
421
464
|
if method in ("error", "aerror"):
|
422
465
|
plot_relative_error(ax, day_stat.model_stat.T, args, method)
|
423
466
|
if title:
|
@@ -473,7 +516,7 @@ def initialize_statistic_plots(
|
|
473
516
|
)
|
474
517
|
|
475
518
|
|
476
|
-
def plot_relative_error(ax, error: ma.MaskedArray, axes: tuple, method: str):
|
519
|
+
def plot_relative_error(ax, error: ma.MaskedArray, axes: tuple, method: str) -> None:
|
477
520
|
pl = ax.pcolormesh(*axes, error[:-1, :-1].T, cmap="RdBu_r", vmin=-50, vmax=50)
|
478
521
|
colorbar = init_colorbar(pl, ax)
|
479
522
|
colorbar.set_label("%", fontsize=13)
|
@@ -507,13 +550,14 @@ def plot_data_area(
|
|
507
550
|
model: ma.MaskedArray,
|
508
551
|
obs: ma.MaskedArray,
|
509
552
|
axes: tuple,
|
553
|
+
*,
|
510
554
|
title: bool = True,
|
511
|
-
):
|
555
|
+
) -> None:
|
512
556
|
data, cmap = p_tools.create_segment_values([model.mask, obs.mask])
|
513
557
|
pl = ax.pcolormesh(*axes, data, cmap=cmap)
|
514
558
|
if title:
|
515
559
|
colorbar = init_colorbar(pl, ax)
|
516
|
-
colorbar.set_ticks(np.arange(1, 1, 3))
|
560
|
+
colorbar.set_ticks(np.arange(1, 1, 3).tolist())
|
517
561
|
ax.set_title(f"{day_stat.title}", fontsize=14)
|
518
562
|
ax.set_facecolor("black")
|
519
563
|
legend_elements = [
|
@@ -530,7 +574,7 @@ def plot_data_area(
|
|
530
574
|
)
|
531
575
|
|
532
576
|
|
533
|
-
def plot_histogram(ax, day_stat: DayStatistics, variable_info):
|
577
|
+
def plot_histogram(ax, day_stat: DayStatistics, variable_info) -> None:
|
534
578
|
weights = np.ones_like(day_stat.model_stat) / float(len(day_stat.model_stat))
|
535
579
|
hist_bins = np.histogram(day_stat.observation_stat, density=True)[-1]
|
536
580
|
ax.hist(
|
@@ -544,7 +588,7 @@ def plot_histogram(ax, day_stat: DayStatistics, variable_info):
|
|
544
588
|
)
|
545
589
|
|
546
590
|
weights = np.ones_like(day_stat.observation_stat) / float(
|
547
|
-
len(day_stat.observation_stat)
|
591
|
+
len(day_stat.observation_stat),
|
548
592
|
)
|
549
593
|
ax.hist(
|
550
594
|
day_stat.observation_stat,
|
@@ -559,17 +603,19 @@ def plot_histogram(ax, day_stat: DayStatistics, variable_info):
|
|
559
603
|
if variable_info.plot_scale == "logarithmic":
|
560
604
|
ax.ticklabel_format(axis="x", style="sci", scilimits=(0, 0))
|
561
605
|
ax.set_ylabel("Relative frequency %", fontsize=13)
|
562
|
-
ax.yaxis.grid(
|
606
|
+
ax.yaxis.grid(which="major")
|
563
607
|
ax.set_title(f"{day_stat.title[-1]}", fontsize=14)
|
564
608
|
|
565
609
|
|
566
|
-
def plot_vertical_profile(
|
610
|
+
def plot_vertical_profile(
|
611
|
+
ax,
|
612
|
+
day_stat: DayStatistics,
|
613
|
+
axes: tuple,
|
614
|
+
variable_info,
|
615
|
+
) -> None:
|
567
616
|
mrm = p_tools.rolling_mean(day_stat.model_stat)
|
568
617
|
orm = p_tools.rolling_mean(day_stat.observation_stat)
|
569
|
-
if len(axes[-1].shape) > 1
|
570
|
-
axes = axes[-1][0]
|
571
|
-
else:
|
572
|
-
axes = axes[-1]
|
618
|
+
axes = axes[-1][0] if len(axes[-1].shape) > 1 else axes[-1]
|
573
619
|
ax.plot(day_stat.model_stat, axes, "o", markersize=5.5, color="k")
|
574
620
|
ax.plot(day_stat.observation_stat, axes, "o", markersize=5.5, color="k")
|
575
621
|
ax.plot(
|
@@ -605,8 +651,8 @@ def plot_vertical_profile(ax, day_stat: DayStatistics, axes: tuple, variable_inf
|
|
605
651
|
ax.set_xlabel(variable_info.x_title, fontsize=13)
|
606
652
|
if variable_info.plot_scale == "logarithmic":
|
607
653
|
ax.ticklabel_format(axis="x", style="sci", scilimits=(0, 0))
|
608
|
-
ax.yaxis.grid(
|
609
|
-
ax.xaxis.grid(
|
654
|
+
ax.yaxis.grid(which="major")
|
655
|
+
ax.xaxis.grid(which="major")
|
610
656
|
|
611
657
|
|
612
658
|
def initialize_figure(n_subplots: int, stat: str = "") -> tuple:
|
@@ -659,13 +705,19 @@ def initialize_figure(n_subplots: int, stat: str = "") -> tuple:
|
|
659
705
|
return fig, axes
|
660
706
|
|
661
707
|
|
662
|
-
def init_colorbar(plot, axis):
|
708
|
+
def init_colorbar(plot, axis) -> Colorbar:
|
663
709
|
divider = make_axes_locatable(axis)
|
664
710
|
cax = divider.append_axes("right", size="1%", pad=0.25)
|
665
711
|
return plt.colorbar(plot, fraction=1.0, ax=axis, cax=cax)
|
666
712
|
|
667
713
|
|
668
|
-
def _set_title(
|
714
|
+
def _set_title(
|
715
|
+
ax,
|
716
|
+
field_name: str,
|
717
|
+
product: str,
|
718
|
+
variable_info,
|
719
|
+
model_name: str = "",
|
720
|
+
) -> None:
|
669
721
|
"""Generates subtitles for different product types"""
|
670
722
|
parts = field_name.split("_")
|
671
723
|
if parts[0] == product:
|
@@ -705,8 +757,7 @@ def _get_iwc_title(field_name: str, variable_info) -> str:
|
|
705
757
|
|
706
758
|
|
707
759
|
def _get_product_title(variable_info) -> str:
|
708
|
-
|
709
|
-
return title
|
760
|
+
return f"{variable_info.name}"
|
710
761
|
|
711
762
|
|
712
763
|
def _get_stat_titles(field_name: str, product: str, variable_info) -> str:
|
@@ -742,5 +793,4 @@ def _get_iwc_title_stat(field_name: str, variable_info) -> str:
|
|
742
793
|
|
743
794
|
def _get_product_title_stat(variable_info) -> str:
|
744
795
|
name = variable_info.name
|
745
|
-
|
746
|
-
return title
|
796
|
+
return f"{name}"
|