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.
Files changed (95) hide show
  1. cloudnetpy/categorize/atmos.py +46 -14
  2. cloudnetpy/categorize/atmos_utils.py +11 -1
  3. cloudnetpy/categorize/categorize.py +38 -21
  4. cloudnetpy/categorize/classify.py +31 -9
  5. cloudnetpy/categorize/containers.py +19 -7
  6. cloudnetpy/categorize/droplet.py +24 -8
  7. cloudnetpy/categorize/falling.py +17 -7
  8. cloudnetpy/categorize/freezing.py +19 -5
  9. cloudnetpy/categorize/insects.py +27 -14
  10. cloudnetpy/categorize/lidar.py +38 -36
  11. cloudnetpy/categorize/melting.py +19 -9
  12. cloudnetpy/categorize/model.py +28 -9
  13. cloudnetpy/categorize/mwr.py +4 -2
  14. cloudnetpy/categorize/radar.py +58 -22
  15. cloudnetpy/cloudnetarray.py +15 -6
  16. cloudnetpy/concat_lib.py +39 -16
  17. cloudnetpy/constants.py +7 -0
  18. cloudnetpy/datasource.py +39 -19
  19. cloudnetpy/instruments/basta.py +6 -2
  20. cloudnetpy/instruments/campbell_scientific.py +33 -16
  21. cloudnetpy/instruments/ceilo.py +30 -13
  22. cloudnetpy/instruments/ceilometer.py +76 -37
  23. cloudnetpy/instruments/cl61d.py +8 -3
  24. cloudnetpy/instruments/cloudnet_instrument.py +2 -1
  25. cloudnetpy/instruments/copernicus.py +27 -14
  26. cloudnetpy/instruments/disdrometer/common.py +51 -32
  27. cloudnetpy/instruments/disdrometer/parsivel.py +79 -48
  28. cloudnetpy/instruments/disdrometer/thies.py +10 -6
  29. cloudnetpy/instruments/galileo.py +23 -12
  30. cloudnetpy/instruments/hatpro.py +27 -11
  31. cloudnetpy/instruments/instruments.py +4 -1
  32. cloudnetpy/instruments/lufft.py +20 -11
  33. cloudnetpy/instruments/mira.py +60 -49
  34. cloudnetpy/instruments/mrr.py +31 -20
  35. cloudnetpy/instruments/nc_lidar.py +15 -6
  36. cloudnetpy/instruments/nc_radar.py +31 -22
  37. cloudnetpy/instruments/pollyxt.py +36 -21
  38. cloudnetpy/instruments/radiometrics.py +32 -18
  39. cloudnetpy/instruments/rpg.py +48 -22
  40. cloudnetpy/instruments/rpg_reader.py +39 -30
  41. cloudnetpy/instruments/vaisala.py +39 -27
  42. cloudnetpy/instruments/weather_station.py +15 -11
  43. cloudnetpy/metadata.py +3 -1
  44. cloudnetpy/model_evaluation/file_handler.py +31 -21
  45. cloudnetpy/model_evaluation/metadata.py +3 -1
  46. cloudnetpy/model_evaluation/model_metadata.py +1 -1
  47. cloudnetpy/model_evaluation/plotting/plot_tools.py +20 -15
  48. cloudnetpy/model_evaluation/plotting/plotting.py +114 -64
  49. cloudnetpy/model_evaluation/products/advance_methods.py +48 -28
  50. cloudnetpy/model_evaluation/products/grid_methods.py +44 -19
  51. cloudnetpy/model_evaluation/products/model_products.py +22 -18
  52. cloudnetpy/model_evaluation/products/observation_products.py +15 -9
  53. cloudnetpy/model_evaluation/products/product_resampling.py +14 -4
  54. cloudnetpy/model_evaluation/products/tools.py +16 -7
  55. cloudnetpy/model_evaluation/statistics/statistical_methods.py +28 -15
  56. cloudnetpy/model_evaluation/tests/e2e/conftest.py +3 -3
  57. cloudnetpy/model_evaluation/tests/e2e/process_cf/main.py +9 -5
  58. cloudnetpy/model_evaluation/tests/e2e/process_cf/tests.py +14 -13
  59. cloudnetpy/model_evaluation/tests/e2e/process_iwc/main.py +9 -5
  60. cloudnetpy/model_evaluation/tests/e2e/process_iwc/tests.py +14 -13
  61. cloudnetpy/model_evaluation/tests/e2e/process_lwc/main.py +9 -5
  62. cloudnetpy/model_evaluation/tests/e2e/process_lwc/tests.py +14 -13
  63. cloudnetpy/model_evaluation/tests/unit/conftest.py +11 -11
  64. cloudnetpy/model_evaluation/tests/unit/test_advance_methods.py +33 -27
  65. cloudnetpy/model_evaluation/tests/unit/test_grid_methods.py +83 -83
  66. cloudnetpy/model_evaluation/tests/unit/test_model_products.py +23 -21
  67. cloudnetpy/model_evaluation/tests/unit/test_observation_products.py +24 -25
  68. cloudnetpy/model_evaluation/tests/unit/test_plot_tools.py +40 -39
  69. cloudnetpy/model_evaluation/tests/unit/test_plotting.py +12 -11
  70. cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py +30 -30
  71. cloudnetpy/model_evaluation/tests/unit/test_tools.py +18 -17
  72. cloudnetpy/model_evaluation/utils.py +3 -2
  73. cloudnetpy/output.py +45 -19
  74. cloudnetpy/plotting/plot_meta.py +35 -11
  75. cloudnetpy/plotting/plotting.py +172 -104
  76. cloudnetpy/products/classification.py +20 -8
  77. cloudnetpy/products/der.py +25 -10
  78. cloudnetpy/products/drizzle.py +41 -26
  79. cloudnetpy/products/drizzle_error.py +10 -5
  80. cloudnetpy/products/drizzle_tools.py +43 -24
  81. cloudnetpy/products/ier.py +10 -5
  82. cloudnetpy/products/iwc.py +16 -9
  83. cloudnetpy/products/lwc.py +34 -12
  84. cloudnetpy/products/mwr_multi.py +4 -1
  85. cloudnetpy/products/mwr_single.py +4 -1
  86. cloudnetpy/products/product_tools.py +33 -10
  87. cloudnetpy/utils.py +175 -74
  88. cloudnetpy/version.py +1 -1
  89. {cloudnetpy-1.55.20.dist-info → cloudnetpy-1.55.22.dist-info}/METADATA +11 -10
  90. cloudnetpy-1.55.22.dist-info/RECORD +114 -0
  91. docs/source/conf.py +2 -2
  92. cloudnetpy-1.55.20.dist-info/RECORD +0 -114
  93. {cloudnetpy-1.55.20.dist-info → cloudnetpy-1.55.22.dist-info}/LICENSE +0 -0
  94. {cloudnetpy-1.55.20.dist-info → cloudnetpy-1.55.22.dist-info}/WHEEL +0 -0
  95. {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 not f"_{model}_" in n]
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, product: str, advance: bool, model: 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
- assert cycles is not None
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 = matplotlib.colormaps["YlGnBu"]
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, y: ma.MaskedArray, data: np.ndarray
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
- if not c_names:
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, save_path, show, casedate, [product, model_run, "group"]
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(image_name, save_path, show, casedate, [name, "pair"])
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(image_name, save_path, show, casedate, [name, "single"])
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
- if model_missing and obs_missing:
367
- raise ValueError("No data in either dataset")
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
- name = ""
372
- name = _get_stat_titles(name, product, variable_info)
408
+ name_new = ""
409
+ name_new = _get_stat_titles(name_new, product, variable_info)
373
410
  day_stat = DayStatistics(
374
- stat, [product, model_name, name], model_data, data
411
+ statistics,
412
+ [product, model_name, name_new],
413
+ model_data,
414
+ data,
375
415
  )
376
- if "error" in stat:
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
- stat,
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 as e:
392
- logging.error(e)
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, save_path, show, casedate, [name, stat, model_run]
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(True, "major")
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(ax, day_stat: DayStatistics, axes: tuple, variable_info):
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(True, "major")
609
- ax.xaxis.grid(True, "major")
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(ax, field_name: str, product: str, variable_info, model_name: str = ""):
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
- title = f"{variable_info.name}"
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
- title = f"{name}"
746
- return title
796
+ return f"{name}"