cloudnetpy 1.60.2__tar.gz → 1.60.4__tar.gz

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 (120) hide show
  1. {cloudnetpy-1.60.2/cloudnetpy.egg-info → cloudnetpy-1.60.4}/PKG-INFO +1 -1
  2. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/model_metadata.py +1 -1
  3. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/plotting/plotting.py +31 -9
  4. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/products/advance_methods.py +35 -49
  5. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/products/grid_methods.py +45 -62
  6. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/unit/conftest.py +31 -30
  7. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/unit/test_advance_methods.py +0 -25
  8. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/unit/test_grid_methods.py +88 -85
  9. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/plotting/plotting.py +5 -4
  10. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/version.py +1 -1
  11. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4/cloudnetpy.egg-info}/PKG-INFO +1 -1
  12. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/LICENSE +0 -0
  13. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/MANIFEST.in +0 -0
  14. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/README.md +0 -0
  15. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/__init__.py +0 -0
  16. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/categorize/__init__.py +0 -0
  17. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/categorize/atmos.py +0 -0
  18. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/categorize/atmos_utils.py +0 -0
  19. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/categorize/categorize.py +0 -0
  20. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/categorize/classify.py +0 -0
  21. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/categorize/containers.py +0 -0
  22. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/categorize/disdrometer.py +0 -0
  23. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/categorize/droplet.py +0 -0
  24. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/categorize/falling.py +0 -0
  25. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/categorize/freezing.py +0 -0
  26. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/categorize/insects.py +0 -0
  27. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/categorize/lidar.py +0 -0
  28. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/categorize/melting.py +0 -0
  29. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/categorize/model.py +0 -0
  30. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/categorize/mwr.py +0 -0
  31. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/categorize/radar.py +0 -0
  32. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/cloudnetarray.py +0 -0
  33. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/concat_lib.py +0 -0
  34. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/constants.py +0 -0
  35. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/datasource.py +0 -0
  36. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/exceptions.py +0 -0
  37. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/__init__.py +0 -0
  38. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/basta.py +0 -0
  39. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/campbell_scientific.py +0 -0
  40. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/ceilo.py +0 -0
  41. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/ceilometer.py +0 -0
  42. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/cl61d.py +0 -0
  43. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/cloudnet_instrument.py +0 -0
  44. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/copernicus.py +0 -0
  45. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/disdrometer/__init__.py +0 -0
  46. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/disdrometer/common.py +0 -0
  47. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/disdrometer/parsivel.py +0 -0
  48. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/disdrometer/thies.py +0 -0
  49. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/galileo.py +0 -0
  50. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/hatpro.py +0 -0
  51. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/instruments.py +0 -0
  52. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/lufft.py +0 -0
  53. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/mira.py +0 -0
  54. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/mrr.py +0 -0
  55. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/nc_lidar.py +0 -0
  56. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/nc_radar.py +0 -0
  57. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/pollyxt.py +0 -0
  58. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/radiometrics.py +0 -0
  59. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/rpg.py +0 -0
  60. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/rpg_reader.py +0 -0
  61. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/vaisala.py +0 -0
  62. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/instruments/weather_station.py +0 -0
  63. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/metadata.py +0 -0
  64. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/__init__.py +0 -0
  65. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/file_handler.py +0 -0
  66. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/metadata.py +0 -0
  67. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/plotting/__init__.py +0 -0
  68. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/plotting/plot_meta.py +0 -0
  69. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/plotting/plot_tools.py +0 -0
  70. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/products/__init__.py +0 -0
  71. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/products/model_products.py +0 -0
  72. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/products/observation_products.py +0 -0
  73. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/products/product_resampling.py +0 -0
  74. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/products/tools.py +0 -0
  75. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/statistics/__init__.py +0 -0
  76. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/statistics/statistical_methods.py +0 -0
  77. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/__init__.py +0 -0
  78. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/e2e/__init__.py +0 -0
  79. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/e2e/conftest.py +0 -0
  80. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/e2e/process_cf/__init__.py +0 -0
  81. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/e2e/process_cf/main.py +0 -0
  82. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/e2e/process_cf/tests.py +0 -0
  83. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/__init__.py +0 -0
  84. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/main.py +0 -0
  85. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/e2e/process_iwc/tests.py +0 -0
  86. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/__init__.py +0 -0
  87. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/main.py +0 -0
  88. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/e2e/process_lwc/tests.py +0 -0
  89. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/unit/__init__.py +0 -0
  90. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/unit/test_model_products.py +0 -0
  91. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/unit/test_observation_products.py +0 -0
  92. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/unit/test_plot_tools.py +0 -0
  93. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/unit/test_plotting.py +0 -0
  94. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py +0 -0
  95. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/tests/unit/test_tools.py +0 -0
  96. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/model_evaluation/utils.py +0 -0
  97. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/output.py +0 -0
  98. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/plotting/__init__.py +0 -0
  99. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/plotting/plot_meta.py +0 -0
  100. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/products/__init__.py +0 -0
  101. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/products/classification.py +0 -0
  102. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/products/der.py +0 -0
  103. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/products/drizzle.py +0 -0
  104. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/products/drizzle_error.py +0 -0
  105. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/products/drizzle_tools.py +0 -0
  106. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/products/ier.py +0 -0
  107. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/products/iwc.py +0 -0
  108. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/products/lwc.py +0 -0
  109. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/products/mie_lu_tables.nc +0 -0
  110. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/products/mwr_tools.py +0 -0
  111. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/products/product_tools.py +0 -0
  112. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/py.typed +0 -0
  113. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy/utils.py +0 -0
  114. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy.egg-info/SOURCES.txt +0 -0
  115. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy.egg-info/dependency_links.txt +0 -0
  116. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy.egg-info/requires.txt +0 -0
  117. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/cloudnetpy.egg-info/top_level.txt +0 -0
  118. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/docs/source/conf.py +0 -0
  119. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/pyproject.toml +0 -0
  120. {cloudnetpy-1.60.2 → cloudnetpy-1.60.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cloudnetpy
3
- Version: 1.60.2
3
+ Version: 1.60.4
4
4
  Summary: Python package for Cloudnet processing
5
5
  Author: Simo Tukiainen
6
6
  License: MIT License
@@ -28,7 +28,7 @@ MODELS = {
28
28
  level=88,
29
29
  cycle="1-12, 7-18",
30
30
  ),
31
- "harmonie": ModelMetaData(
31
+ "harmonie-fmi-6-11": ModelMetaData(
32
32
  model_name="HARMONIE-AROME",
33
33
  long_name="the HIRLAM–ALADIN Research on Mesoscale Operational NWP in Euromed", # noqa: RUF001
34
34
  level=65,
@@ -17,7 +17,7 @@ import cloudnetpy.model_evaluation.plotting.plot_tools as p_tools
17
17
  from cloudnetpy.model_evaluation.model_metadata import MODELS
18
18
  from cloudnetpy.model_evaluation.plotting.plot_meta import ATTRIBUTES
19
19
  from cloudnetpy.model_evaluation.statistics.statistical_methods import DayStatistics
20
- from cloudnetpy.plotting.plotting import get_log_cbar_tick_labels, lin2log
20
+ from cloudnetpy.plotting.plotting import Dimensions, get_log_cbar_tick_labels, lin2log
21
21
 
22
22
  sys.path.append(os.path.dirname(os.path.abspath(__file__)))
23
23
 
@@ -34,7 +34,7 @@ def generate_L3_day_plots(
34
34
  save_path: str | None = None,
35
35
  image_name: str | None = None,
36
36
  show: bool | None = False,
37
- ) -> None:
37
+ ) -> list[Dimensions]:
38
38
  """Generate visualizations for level 3 dayscale products.
39
39
  With figure type visualizations can be subplot in group, pair, single or
40
40
  statistic of given product. In group fig_type all different methods are plot
@@ -93,7 +93,11 @@ def generate_L3_day_plots(
93
93
  model_info = MODELS[model]
94
94
  model_name = model_info.model_name
95
95
  name_set = p_tools.parse_wanted_names(nc_file, product, model, var_list)
96
- for names in name_set:
96
+ unique_tuples = {tuple(lst) for lst in name_set}
97
+ name_set_unique = tuple(list(tup) for tup in unique_tuples)
98
+
99
+ dimensions = []
100
+ for names in name_set_unique:
97
101
  if len(names) > 0:
98
102
  try:
99
103
  cycle_names, cycles = p_tools.sort_cycles(names, model)
@@ -120,7 +124,11 @@ def generate_L3_day_plots(
120
124
  save_path,
121
125
  image_name,
122
126
  ]
123
- getattr(cls, f"get_{fig_type}_plots")(*params, **kwargs)
127
+ figs, axes = getattr(cls, f"get_{fig_type}_plots")(
128
+ *params, **kwargs
129
+ )
130
+ for fig, ax in zip(figs, axes, strict=False):
131
+ dimensions.append(Dimensions(fig, ax))
124
132
  except AttributeError:
125
133
  params = [
126
134
  product,
@@ -143,7 +151,10 @@ def generate_L3_day_plots(
143
151
  save_path,
144
152
  image_name,
145
153
  ]
146
- getattr(cls, f"get_{fig_type}_plots")(*params, **kwargs)
154
+ figs, axes = getattr(cls, f"get_{fig_type}_plots")(*params, **kwargs)
155
+ for fig, ax in zip(figs, axes, strict=False):
156
+ dimensions.append(Dimensions(fig, ax))
157
+ return dimensions
147
158
 
148
159
 
149
160
  def get_group_plots(
@@ -159,7 +170,7 @@ def get_group_plots(
159
170
  cycle: str = "",
160
171
  title: bool = True,
161
172
  include_xlimits: bool = False,
162
- ) -> None:
173
+ ) -> tuple:
163
174
  """Group subplot visualization for both standard and advection downsampling.
164
175
  Generates group subplot figure for product with model and all different
165
176
  downsampling methods. Generates separated figures for standard and advection
@@ -204,6 +215,7 @@ def get_group_plots(
204
215
  [product, model_run, "group"],
205
216
  show=show,
206
217
  )
218
+ return fig, ax
207
219
 
208
220
 
209
221
  def get_pair_plots(
@@ -280,7 +292,7 @@ def get_single_plots(
280
292
  cycle: str = "",
281
293
  title: bool = True,
282
294
  include_xlimits: bool = False,
283
- ) -> None:
295
+ ) -> tuple[list, list]:
284
296
  """Generates figures of each product variable from given file in loop.
285
297
 
286
298
  Args:
@@ -295,9 +307,13 @@ def get_single_plots(
295
307
  cycle (str): Name of cycle if exists
296
308
  title (bool): True or False if wanted to add title to subfig
297
309
  """
310
+ figs = []
311
+ axes = []
298
312
  variable_info = ATTRIBUTES[product]
299
- for _, name in enumerate(names):
313
+ for name in names:
300
314
  fig, ax = initialize_figure(1)
315
+ figs.append(fig)
316
+ axes.append(ax)
301
317
  set_yax(ax[0], 12, ylabel=None)
302
318
  set_xax(ax[0], include_xlimits=include_xlimits)
303
319
 
@@ -318,6 +334,7 @@ def get_single_plots(
318
334
  [name, "single"],
319
335
  show=show,
320
336
  )
337
+ return figs, axes
321
338
 
322
339
 
323
340
  def plot_colormesh(ax, data: np.ndarray, axes: tuple, variable_info) -> None:
@@ -349,7 +366,7 @@ def get_statistic_plots(
349
366
  show: bool,
350
367
  cycle: str = "",
351
368
  title: bool = True,
352
- ) -> None:
369
+ ) -> tuple:
353
370
  """Statistical subplots for day scale products.
354
371
  Statistical analysis can be done by day scale with relative error ('error'),
355
372
  total data area analysis ('area'), histogram ('hist') or vertical profiles
@@ -387,12 +404,16 @@ def get_statistic_plots(
387
404
  err_msg = f"No data in {model_name} or observation"
388
405
  raise ValueError(err_msg)
389
406
 
407
+ figs = []
408
+ axes = []
390
409
  for stat in stats:
391
410
  try:
392
411
  obs_missing = False
393
412
  model_missing = False
394
413
  variable_info = ATTRIBUTES[product]
395
414
  fig, ax = initialize_figure(len(names) - 1, stat)
415
+ figs.append(fig)
416
+ axes.append(ax)
396
417
  model_data, _, _ = p_tools.read_data_characters(nc_file, names[0], model)
397
418
  if np.all(model_data.mask is True):
398
419
  model_missing = True
@@ -443,6 +464,7 @@ def get_statistic_plots(
443
464
  [name, stat, model_run],
444
465
  show=show,
445
466
  )
467
+ return figs, axes
446
468
 
447
469
 
448
470
  def initialize_statistic_plots(
@@ -2,8 +2,9 @@ import importlib
2
2
  import logging
3
3
 
4
4
  import numpy as np
5
+ import scipy.special
6
+ import scipy.stats
5
7
  from numpy import ma
6
- from scipy.special import gamma
7
8
 
8
9
  import cloudnetpy.utils as cl_tools
9
10
  from cloudnetpy.datasource import DataSource
@@ -44,7 +45,7 @@ class AdvanceProductMethods(DataSource):
44
45
  name = f"get_advance_{self.product}"
45
46
  getattr(cls, name)(self)
46
47
  except AttributeError as error:
47
- logging.warning("No advance method for %s: %s", self.product, error)
48
+ logging.debug("No advance method for %s: %s", self.product, error)
48
49
 
49
50
  def get_advance_cf(self) -> None:
50
51
  self.cf_cirrus_filter()
@@ -60,24 +61,36 @@ class AdvanceProductMethods(DataSource):
60
61
  cf_filtered = self.filter_high_iwc_low_cf(cf, iwc, lwc)
61
62
  cloud_iwc, ice_ind = self.find_ice_in_clouds(cf_filtered, iwc, lwc)
62
63
  variance_iwc = self.iwc_variance(h, ice_ind)
63
- # Looks suspicious, check me:
64
+
64
65
  for i, ind in enumerate(zip(ice_ind[0], ice_ind[-1], strict=True)):
65
- iwc_dist = self.calculate_iwc_distribution(cloud_iwc[i], variance_iwc[i])
66
+ try:
67
+ iwc_dist = self.calculate_iwc_distribution(
68
+ cloud_iwc[i], variance_iwc[i]
69
+ )
70
+ except ValueError:
71
+ continue
72
+
66
73
  p_iwc = self.gamma_distribution(iwc_dist, variance_iwc[i], cloud_iwc[i])
74
+
75
+ if np.isinf(p_iwc).any():
76
+ cf_filtered[ind] = ma.masked
77
+ continue
78
+
67
79
  if np.sum(p_iwc) == 0 or p_iwc[-1] > 0.01 * np.sum(p_iwc):
68
- cf_filtered[ind] = np.nan
80
+ cf_filtered[ind] = ma.masked
69
81
  continue
70
- obs_index = self.get_observation_index(
71
- iwc_dist,
72
- tZT,
73
- tT,
74
- tZ,
75
- t,
76
- float(t_screened[ind]),
77
- float(z_sen[ind]),
82
+
83
+ min_iwc = 10 ** (
84
+ tZT * z_sen[ind] * t_screened[ind]
85
+ + tT * t_screened[ind]
86
+ + tZ * z_sen[ind]
87
+ + t
78
88
  )
89
+ obs_index = iwc_dist > min_iwc
79
90
  cf_filtered[ind] = self.filter_cirrus(p_iwc, obs_index, cf_filtered[ind])
91
+
80
92
  cf_filtered[cf_filtered < 0.05] = ma.masked
93
+
81
94
  self._model_obj.append_data(
82
95
  cf_filtered,
83
96
  f"{self._model_obj.model}{self._model_obj.cycle}_cf_cirrus",
@@ -132,7 +145,7 @@ class AdvanceProductMethods(DataSource):
132
145
  iwc: np.ndarray,
133
146
  lwc: np.ndarray,
134
147
  ) -> np.ndarray:
135
- cf_filtered = np.copy(cf)
148
+ cf_filtered = ma.copy(cf)
136
149
  weird_ind = (iwc / cf > 0.5e-3) & (cf < 0.001)
137
150
  weird_ind = weird_ind | (iwc == 0) & (lwc == 0) & (cf == 0)
138
151
  cf_filtered[weird_ind] = ma.masked
@@ -199,7 +212,9 @@ class AdvanceProductMethods(DataSource):
199
212
  n_std: int = 5,
200
213
  n_dist: int = 250,
201
214
  ) -> np.ndarray:
202
- finish = cloud_iwc + n_std * (np.sqrt(f_variance_iwc) * cloud_iwc)
215
+ finish = cloud_iwc + n_std * (ma.sqrt(f_variance_iwc) * cloud_iwc)
216
+ if isinstance(finish, ma.MaskedArray) and finish.mask.all():
217
+ raise ValueError
203
218
  iwc_dist = np.arange(0, finish, finish / (n_dist - 1))
204
219
  if cloud_iwc < iwc_dist[2]:
205
220
  finish = cloud_iwc * 10
@@ -208,40 +223,11 @@ class AdvanceProductMethods(DataSource):
208
223
 
209
224
  @staticmethod
210
225
  def gamma_distribution(
211
- iwc_dist: np.ndarray,
212
- f_variance_iwc: float,
213
- cloud_iwc: float,
214
- ) -> np.ndarray:
215
- def calculate_gamma_dist() -> float:
216
- alpha = 1 / f_variance_iwc
217
- return (
218
- 1
219
- / gamma(alpha)
220
- * (alpha / cloud_iwc) ** alpha
221
- * iwc_dist[i] ** (alpha - 1)
222
- * ma.exp(-(alpha * iwc_dist[i] / cloud_iwc))
223
- )
224
-
225
- p_iwc = np.zeros(iwc_dist.shape)
226
- for i in range(len(iwc_dist)):
227
- p_iwc[i] = calculate_gamma_dist()
228
- return p_iwc
229
-
230
- @staticmethod
231
- def get_observation_index(
232
- iwc_dist: np.ndarray,
233
- tZT: float,
234
- tT: float,
235
- tZ: float,
236
- t: np.ndarray,
237
- temperature: float,
238
- z_sen: float,
226
+ iwc_dist: np.ndarray, f_variance_iwc: float, cloud_iwc: float
239
227
  ) -> np.ndarray:
240
- def calculate_min_iwc() -> np.ndarray:
241
- return 10 ** (tZT * z_sen * temperature + tT * temperature + tZ * z_sen + t)
242
-
243
- iwc_min = calculate_min_iwc()
244
- return iwc_dist > iwc_min
228
+ alpha = 1 / f_variance_iwc
229
+ theta = cloud_iwc / alpha
230
+ return scipy.stats.gamma.pdf(iwc_dist, a=alpha, scale=theta)
245
231
 
246
232
  @staticmethod
247
233
  def filter_cirrus(
@@ -249,4 +235,4 @@ class AdvanceProductMethods(DataSource):
249
235
  obs_index: np.ndarray,
250
236
  cf_filtered: np.ndarray,
251
237
  ) -> np.ndarray:
252
- return (np.sum(p_iwc * obs_index) / np.sum(p_iwc)) * cf_filtered
238
+ return (ma.sum(p_iwc * obs_index) / ma.sum(p_iwc)) * cf_filtered
@@ -119,56 +119,35 @@ class ProductGrid:
119
119
 
120
120
  def _cf_method_storage(self) -> tuple[dict, dict]:
121
121
  cf_dict = {
122
- "cf_V": np.zeros(self._model_height.shape),
123
- "cf_A": np.zeros(self._model_height.shape),
122
+ "cf_V": ma.zeros(self._model_height.shape),
123
+ "cf_A": ma.zeros(self._model_height.shape),
124
124
  }
125
125
  cf_adv_dict = {
126
- "cf_V_adv": np.zeros(self._model_height.shape),
127
- "cf_A_adv": np.zeros(self._model_height.shape),
126
+ "cf_V_adv": ma.zeros(self._model_height.shape),
127
+ "cf_A_adv": ma.zeros(self._model_height.shape),
128
128
  }
129
129
  return cf_dict, cf_adv_dict
130
130
 
131
131
  def _iwc_method_storage(self) -> tuple[dict, dict]:
132
132
  iwc_dict = {
133
- "iwc": np.zeros(self._model_height.shape),
134
- "iwc_att": np.zeros(self._model_height.shape),
135
- "iwc_rain": np.zeros(self._model_height.shape),
133
+ "iwc": ma.zeros(self._model_height.shape),
134
+ "iwc_att": ma.zeros(self._model_height.shape),
135
+ "iwc_rain": ma.zeros(self._model_height.shape),
136
136
  }
137
137
  iwc_adv_dict = {
138
- "iwc_adv": np.zeros(self._model_height.shape),
139
- "iwc_att_adv": np.zeros(self._model_height.shape),
140
- "iwc_rain_adv": np.zeros(self._model_height.shape),
138
+ "iwc_adv": ma.zeros(self._model_height.shape),
139
+ "iwc_att_adv": ma.zeros(self._model_height.shape),
140
+ "iwc_rain_adv": ma.zeros(self._model_height.shape),
141
141
  }
142
142
  return iwc_dict, iwc_adv_dict
143
143
 
144
144
  def _product_method_storage(self) -> tuple[dict, dict]:
145
- product_dict = {f"{self._obs_obj.obs}": np.zeros(self._model_height.shape)}
145
+ product_dict = {f"{self._obs_obj.obs}": ma.zeros(self._model_height.shape)}
146
146
  product_adv_dict = {
147
- f"{self._obs_obj.obs}_adv": np.zeros(self._model_height.shape),
147
+ f"{self._obs_obj.obs}_adv": ma.zeros(self._model_height.shape),
148
148
  }
149
149
  return product_dict, product_adv_dict
150
150
 
151
- @staticmethod
152
- def _regrid_cf(storage: dict, i: int, j: int, data: np.ndarray | None) -> dict:
153
- """Calculates average cloud fraction value to grid point"""
154
- for key, downsample in storage.items():
155
- if data is not None:
156
- if isinstance(data, ma.MaskedArray) and data.mask.all():
157
- downsample[i, j] = np.nan
158
- elif not np.isnan(data).all():
159
- downsample[i, j] = np.nanmean(data)
160
- else:
161
- downsample[i, j] = np.nan
162
- if "_A" in key and (
163
- not np.isnan(data).all()
164
- and not (isinstance(data, ma.MaskedArray) and data.mask.all())
165
- ):
166
- downsample[i, j] = tl.average_column_sum(data)
167
- else:
168
- downsample[i, j] = np.nan
169
- storage[key] = downsample
170
- return storage
171
-
172
151
  def _reshape_data_to_window(
173
152
  self,
174
153
  ind: np.ndarray,
@@ -181,6 +160,17 @@ class ProductGrid:
181
160
  return self._obs_data[ind].reshape(window_size)
182
161
  return None
183
162
 
163
+ @staticmethod
164
+ def _regrid_cf(storage: dict, i: int, j: int, data: np.ndarray) -> dict:
165
+ """Calculates average cloud fraction value to grid point."""
166
+ data_ma = ma.array(data) if not isinstance(data, ma.MaskedArray) else data
167
+ for key, downsample in storage.items():
168
+ downsample[i, j] = ma.mean(data_ma)
169
+ if "_A" in key and not data_ma.mask.all():
170
+ downsample[i, j] = tl.average_column_sum(data_ma)
171
+ storage[key] = downsample
172
+ return storage
173
+
184
174
  def _regrid_iwc(
185
175
  self,
186
176
  storage: dict,
@@ -189,40 +179,33 @@ class ProductGrid:
189
179
  ind_rain: np.ndarray,
190
180
  ind_no_rain: np.ndarray,
191
181
  ) -> dict:
192
- """Calculates average iwc value for grid point"""
193
- for key, downsample in storage.items():
194
- if not self._obs_data[ind_no_rain].mask.all():
195
- if np.isnan(self._obs_data[ind_no_rain]).all():
196
- downsample[i, j] = np.nan
197
- else:
198
- no_rain_data = self._obs_data[ind_no_rain]
199
- if no_rain_data.size > 0:
200
- downsample[i, j] = np.nanmean(self._obs_data[ind_no_rain])
201
- else:
202
- downsample[i, j] = np.nan
203
- elif "rain" in key and not self._obs_data[ind_rain].mask.all():
204
- downsample[i, j] = np.nanmean(self._obs_data[ind_rain])
205
- else:
206
- downsample[i, j] = np.nan
182
+ """Calculates average iwc value for each grid point."""
183
+ for key, down_sample in storage.items():
184
+ down_sample[i, j] = ma.masked
185
+ if "rain" not in key:
186
+ no_rain_data = self._obs_data[ind_no_rain]
187
+ if ind_no_rain.any() and not no_rain_data.mask.all():
188
+ down_sample[i, j] = ma.mean(no_rain_data)
189
+ if "rain" in key:
190
+ rain_data = self._obs_data[ind_rain]
191
+ if ind_rain.any() and not rain_data.mask.all():
192
+ down_sample[i, j] = ma.mean(rain_data)
207
193
  if "att" in key:
208
- iwc_att = self._obs_obj.data["iwc_att"][:]
209
- if iwc_att[ind_no_rain].mask.all():
210
- downsample[i, j] = np.nan
211
- else:
212
- downsample[i, j] = np.nanmean(iwc_att[ind_no_rain])
213
- storage[key] = downsample
194
+ no_rain_att_data = self._obs_obj.data["iwc_att"][ind_no_rain]
195
+ if ind_no_rain.any() and not no_rain_att_data.mask.all():
196
+ down_sample[i, j] = ma.mean(no_rain_att_data)
197
+ storage[key] = down_sample
214
198
  return storage
215
199
 
216
200
  def _regrid_product(self, storage: dict, i: int, j: int, ind: np.ndarray) -> dict:
217
- """Calculates average of standard product value to grid point"""
201
+ """Calculates average of standard product value for each grid point."""
218
202
  for key, down_sample in storage.items():
219
- if not self._obs_data[ind].mask.all() and ind.any():
220
- if np.isnan(self._obs_data[ind]).all():
221
- down_sample[i, j] = np.nan
222
- else:
223
- down_sample[i, j] = np.nanmean(self._obs_data[ind])
224
- else:
225
- down_sample[i, j] = np.nan
203
+ obs_data_selected = ma.masked_invalid(self._obs_data[ind])
204
+ down_sample[i, j] = (
205
+ ma.mean(obs_data_selected)
206
+ if not obs_data_selected.mask.all()
207
+ else ma.masked
208
+ )
226
209
  storage[key] = down_sample
227
210
  return storage
228
211
 
@@ -3,6 +3,7 @@ from datetime import date
3
3
  import netCDF4
4
4
  import numpy as np
5
5
  import pytest
6
+ from numpy import ma
6
7
 
7
8
 
8
9
  @pytest.fixture(scope="session")
@@ -28,7 +29,7 @@ def model_file(tmpdir_factory, file_metadata) -> str:
28
29
  root_grp.createDimension("level", level)
29
30
  _create_global_attributes(root_grp, file_metadata)
30
31
  var = root_grp.createVariable("time", "f8", "time")
31
- var[:] = np.array([2, 6, 10])
32
+ var[:] = ma.array([2, 6, 10])
32
33
  var = root_grp.createVariable("level", "f8", "level")
33
34
  var[:] = level
34
35
  var = root_grp.createVariable("latitude", "f8")
@@ -38,24 +39,24 @@ def model_file(tmpdir_factory, file_metadata) -> str:
38
39
  var = root_grp.createVariable("horizontal_resolution", "f8")
39
40
  var[:] = 9
40
41
  var = root_grp.createVariable("height", "f8", ("time", "level"))
41
- var[:] = np.array([[10, 14], [8, 14], [9, 15]])
42
+ var[:] = ma.array([[10, 14], [8, 14], [9, 15]])
42
43
  var.units = "km"
43
44
  var = root_grp.createVariable("forecast_time", "f8", "time")
44
- var[:] = np.array([1, 5, 10])
45
+ var[:] = ma.array([1, 5, 10])
45
46
  var = root_grp.createVariable("cloud_fraction", "f8", ("time", "level"))
46
- var[:] = np.array([[0, 2], [3, 6], [5, 8]])
47
+ var[:] = ma.array([[0, 2], [3, 6], [5, 8]])
47
48
  var = root_grp.createVariable("qi", "f8", ("time", "level"))
48
- var[:] = np.array([[0.01, 0.00], [0.02, 0.03], [0.06, 0.08]])
49
+ var[:] = ma.array([[0.01, 0.00], [0.02, 0.03], [0.06, 0.08]])
49
50
  var = root_grp.createVariable("ql", "f8", ("time", "level"))
50
- var[:] = np.array([[0.008, 0.09], [0.004, 0.007], [0.0001, 0.0002]])
51
+ var[:] = ma.array([[0.008, 0.09], [0.004, 0.007], [0.0001, 0.0002]])
51
52
  var = root_grp.createVariable("temperature", "f8", ("time", "level"))
52
- var[:] = np.array([[300, 301], [302, 299], [305, 298]])
53
+ var[:] = ma.array([[300, 301], [302, 299], [305, 298]])
53
54
  var = root_grp.createVariable("pressure", "f8", ("time", "level"))
54
- var[:] = np.array([[1000, 1001], [1010, 1003], [1020, 1005]])
55
+ var[:] = ma.array([[1000, 1001], [1010, 1003], [1020, 1005]])
55
56
  var = root_grp.createVariable("uwind", "f8", ("time", "level"))
56
- var[:] = np.array([[1, 2], [2, 2], [3, 1]])
57
+ var[:] = ma.array([[1, 2], [2, 2], [3, 1]])
57
58
  var = root_grp.createVariable("vwind", "f8", ("time", "level"))
58
- var[:] = np.array([[3, 1], [2, 1], [5, 2]])
59
+ var[:] = ma.array([[3, 1], [2, 1], [5, 2]])
59
60
  root_grp.close()
60
61
  return file_name
61
62
 
@@ -70,9 +71,9 @@ def obs_file(tmpdir_factory, file_metadata) -> str:
70
71
  root_grp.createDimension("height", height)
71
72
  _create_global_attributes(root_grp, file_metadata)
72
73
  var = root_grp.createVariable("time", "f8", "time")
73
- var[:] = np.array([0, 2, 4, 6, 8, 10])
74
+ var[:] = ma.array([0, 2, 4, 6, 8, 10])
74
75
  var = root_grp.createVariable("height", "f8", "height")
75
- var[:] = np.array([8, 9, 12, 15])
76
+ var[:] = ma.array([8, 9, 12, 15])
76
77
  var.units = "km"
77
78
  var = root_grp.createVariable("latitude", "f8")
78
79
  var[:] = 1
@@ -84,11 +85,11 @@ def obs_file(tmpdir_factory, file_metadata) -> str:
84
85
  var = root_grp.createVariable("radar_frequency", "f8")
85
86
  var[:] = 35.5
86
87
  var = root_grp.createVariable("rainrate", "i4", "time")
87
- var[:] = np.array([1, 2, 0, 10, 5, 13])
88
+ var[:] = ma.array([1, 2, 0, 10, 5, 13])
88
89
  var = root_grp.createVariable("Z_sensitivity", "f8", "height")
89
- var[:] = np.array([0.1, 0.2, 0.0, 1.0])
90
+ var[:] = ma.array([0.1, 0.2, 0.0, 1.0])
90
91
  var = root_grp.createVariable("category_bits", "i4", ("time", "height"))
91
- var[:] = np.array(
92
+ var[:] = ma.array(
92
93
  [
93
94
  [0, 1, 2, 0],
94
95
  [2, 8, 4, 1],
@@ -99,7 +100,7 @@ def obs_file(tmpdir_factory, file_metadata) -> str:
99
100
  ],
100
101
  )
101
102
  var = root_grp.createVariable("quality_bits", "i4", ("time", "height"))
102
- var[:] = np.array(
103
+ var[:] = ma.array(
103
104
  [
104
105
  [0, 1, 2, 4],
105
106
  [8, 16, 32, 16],
@@ -110,7 +111,7 @@ def obs_file(tmpdir_factory, file_metadata) -> str:
110
111
  ],
111
112
  )
112
113
  var = root_grp.createVariable("iwc", "f8", ("time", "height"))
113
- var[:] = np.array(
114
+ var[:] = ma.array(
114
115
  [
115
116
  [0.01, 0.02, 0.06, 0.01],
116
117
  [0.02, 0.06, 0.00, 0.03],
@@ -121,7 +122,7 @@ def obs_file(tmpdir_factory, file_metadata) -> str:
121
122
  ],
122
123
  )
123
124
  var = root_grp.createVariable("iwc_retrieval_status", "f8", ("time", "height"))
124
- var[:] = np.array(
125
+ var[:] = ma.array(
125
126
  [
126
127
  [1, 2, 6, 1],
127
128
  [2, 6, 5, 3],
@@ -132,7 +133,7 @@ def obs_file(tmpdir_factory, file_metadata) -> str:
132
133
  ],
133
134
  )
134
135
  var = root_grp.createVariable("lwc", "f8", ("time", "height"))
135
- var[:] = np.array(
136
+ var[:] = ma.array(
136
137
  [
137
138
  [0.08, 0.04, 0.01, 0.08],
138
139
  [0.04, 0.01, 0.09, 0.07],
@@ -143,7 +144,7 @@ def obs_file(tmpdir_factory, file_metadata) -> str:
143
144
  ],
144
145
  )
145
146
  var = root_grp.createVariable("data", "i4", ("time", "height"))
146
- var[:] = np.array(
147
+ var[:] = ma.array(
147
148
  [
148
149
  [2, 4, 3, 6],
149
150
  [7, 1, 9, 7],
@@ -167,7 +168,7 @@ def regrid_file(tmpdir_factory, file_metadata) -> str:
167
168
  root_grp.createDimension("level", level)
168
169
  _create_global_attributes(root_grp, file_metadata)
169
170
  var = root_grp.createVariable("time", "f8", "time")
170
- var[:] = np.array([2, 6, 10])
171
+ var[:] = ma.array([2, 6, 10])
171
172
  var = root_grp.createVariable("level", "f8", "level")
172
173
  var[:] = level
173
174
  var = root_grp.createVariable("latitude", "f8")
@@ -177,23 +178,23 @@ def regrid_file(tmpdir_factory, file_metadata) -> str:
177
178
  var = root_grp.createVariable("horizontal_resolution", "f8")
178
179
  var[:] = 9
179
180
  var = root_grp.createVariable("ecmwf_height", "f8", ("time", "level"))
180
- var[:] = np.array([[10, 14], [8, 14], [9, 15]])
181
+ var[:] = ma.array([[10, 14], [8, 14], [9, 15]])
181
182
  var = root_grp.createVariable("ecmwf_forecast_time", "f8", "time")
182
- var[:] = np.array([1, 5, 10])
183
+ var[:] = ma.array([1, 5, 10])
183
184
  var = root_grp.createVariable("ecmwf_cf", "f8", ("time", "level"))
184
- var[:] = np.array([[0, 2], [3, 6], [5, 8]])
185
+ var[:] = ma.array([[0, 2], [3, 6], [5, 8]])
185
186
  var = root_grp.createVariable("ecmwf_cf_cirrus", "f8", ("time", "level"))
186
- var[:] = np.array([[0, 2], [3, 6], [5, 7]])
187
+ var[:] = ma.array([[0, 2], [3, 6], [5, 7]])
187
188
  var = root_grp.createVariable("ecmwf_cf_snow", "f8", ("time", "level"))
188
- var[:] = np.array([[0, 2], [4, 6], [5, 8]])
189
+ var[:] = ma.array([[0, 2], [4, 6], [5, 8]])
189
190
  var = root_grp.createVariable("cf_ecmwf", "f8", ("time", "level"))
190
- var[:] = np.array([[0, 2], [3, 6], [5, 8]])
191
+ var[:] = ma.array([[0, 2], [3, 6], [5, 8]])
191
192
  var = root_grp.createVariable("cf_adv_ecmwf", "f8", ("time", "level"))
192
- var[:] = np.array([[0, 2], [3, 6], [5, 8]])
193
+ var[:] = ma.array([[0, 2], [3, 6], [5, 8]])
193
194
  var = root_grp.createVariable("temperature", "f8", ("time", "level"))
194
- var[:] = np.array([[300, 301], [302, 299], [305, 298]])
195
+ var[:] = ma.array([[300, 301], [302, 299], [305, 298]])
195
196
  var = root_grp.createVariable("pressure", "f8", ("time", "level"))
196
- var[:] = np.array([[1000, 1001], [1010, 1003], [1020, 1005]])
197
+ var[:] = ma.array([[1000, 1001], [1010, 1003], [1020, 1005]])
197
198
  root_grp.close()
198
199
  return file_name
199
200
 
@@ -248,31 +248,6 @@ def test_gamma_distribution(obs_file, model_file) -> None:
248
248
  testing.assert_array_almost_equal(x, compare)
249
249
 
250
250
 
251
- def test_get_observation_index(obs_file, model_file) -> None:
252
- obs = ObservationManager(PRODUCT, str(obs_file))
253
- model = ModelManager(str(model_file), MODEL, OUTPUT_FILE, PRODUCT)
254
- adv_pro = AdvanceProductMethods(model, str(model_file), obs)
255
- tZT = 0.01
256
- z_sen = 0.02
257
- temperature = -13
258
- tT = 0.04
259
- tZ = 0.05
260
- t = 0.06
261
- min_iwc = 10 ** (tZT * z_sen * temperature + tT * temperature + tZ * z_sen + t)
262
- iwc_dist = np.array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6])
263
- compare = iwc_dist > min_iwc
264
- x = adv_pro.get_observation_index(
265
- iwc_dist,
266
- tZT,
267
- tT,
268
- tZ,
269
- np.array([t]),
270
- temperature,
271
- z_sen,
272
- )
273
- testing.assert_array_almost_equal(x, compare)
274
-
275
-
276
251
  def test_filter_cirrus(obs_file, model_file) -> None:
277
252
  obs = ObservationManager(PRODUCT, str(obs_file))
278
253
  model = ModelManager(str(model_file), MODEL, OUTPUT_FILE, PRODUCT)