cloudnetpy 1.49.9__py3-none-any.whl → 1.87.3__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 (116) hide show
  1. cloudnetpy/categorize/__init__.py +1 -2
  2. cloudnetpy/categorize/atmos_utils.py +297 -67
  3. cloudnetpy/categorize/attenuation.py +31 -0
  4. cloudnetpy/categorize/attenuations/__init__.py +37 -0
  5. cloudnetpy/categorize/attenuations/gas_attenuation.py +30 -0
  6. cloudnetpy/categorize/attenuations/liquid_attenuation.py +84 -0
  7. cloudnetpy/categorize/attenuations/melting_attenuation.py +78 -0
  8. cloudnetpy/categorize/attenuations/rain_attenuation.py +84 -0
  9. cloudnetpy/categorize/categorize.py +332 -156
  10. cloudnetpy/categorize/classify.py +127 -125
  11. cloudnetpy/categorize/containers.py +107 -76
  12. cloudnetpy/categorize/disdrometer.py +40 -0
  13. cloudnetpy/categorize/droplet.py +23 -21
  14. cloudnetpy/categorize/falling.py +53 -24
  15. cloudnetpy/categorize/freezing.py +25 -12
  16. cloudnetpy/categorize/insects.py +35 -23
  17. cloudnetpy/categorize/itu.py +243 -0
  18. cloudnetpy/categorize/lidar.py +36 -41
  19. cloudnetpy/categorize/melting.py +34 -26
  20. cloudnetpy/categorize/model.py +84 -37
  21. cloudnetpy/categorize/mwr.py +18 -14
  22. cloudnetpy/categorize/radar.py +215 -102
  23. cloudnetpy/cli.py +578 -0
  24. cloudnetpy/cloudnetarray.py +43 -89
  25. cloudnetpy/concat_lib.py +218 -78
  26. cloudnetpy/constants.py +28 -10
  27. cloudnetpy/datasource.py +61 -86
  28. cloudnetpy/exceptions.py +49 -20
  29. cloudnetpy/instruments/__init__.py +5 -0
  30. cloudnetpy/instruments/basta.py +29 -12
  31. cloudnetpy/instruments/bowtie.py +135 -0
  32. cloudnetpy/instruments/ceilo.py +138 -115
  33. cloudnetpy/instruments/ceilometer.py +164 -80
  34. cloudnetpy/instruments/cl61d.py +21 -5
  35. cloudnetpy/instruments/cloudnet_instrument.py +74 -36
  36. cloudnetpy/instruments/copernicus.py +108 -30
  37. cloudnetpy/instruments/da10.py +54 -0
  38. cloudnetpy/instruments/disdrometer/common.py +126 -223
  39. cloudnetpy/instruments/disdrometer/parsivel.py +453 -94
  40. cloudnetpy/instruments/disdrometer/thies.py +254 -87
  41. cloudnetpy/instruments/fd12p.py +201 -0
  42. cloudnetpy/instruments/galileo.py +65 -23
  43. cloudnetpy/instruments/hatpro.py +123 -49
  44. cloudnetpy/instruments/instruments.py +113 -1
  45. cloudnetpy/instruments/lufft.py +39 -17
  46. cloudnetpy/instruments/mira.py +268 -61
  47. cloudnetpy/instruments/mrr.py +187 -0
  48. cloudnetpy/instruments/nc_lidar.py +19 -8
  49. cloudnetpy/instruments/nc_radar.py +109 -55
  50. cloudnetpy/instruments/pollyxt.py +135 -51
  51. cloudnetpy/instruments/radiometrics.py +313 -59
  52. cloudnetpy/instruments/rain_e_h3.py +171 -0
  53. cloudnetpy/instruments/rpg.py +321 -189
  54. cloudnetpy/instruments/rpg_reader.py +74 -40
  55. cloudnetpy/instruments/toa5.py +49 -0
  56. cloudnetpy/instruments/vaisala.py +95 -343
  57. cloudnetpy/instruments/weather_station.py +774 -105
  58. cloudnetpy/metadata.py +90 -19
  59. cloudnetpy/model_evaluation/file_handler.py +55 -52
  60. cloudnetpy/model_evaluation/metadata.py +46 -20
  61. cloudnetpy/model_evaluation/model_metadata.py +1 -1
  62. cloudnetpy/model_evaluation/plotting/plot_tools.py +32 -37
  63. cloudnetpy/model_evaluation/plotting/plotting.py +327 -117
  64. cloudnetpy/model_evaluation/products/advance_methods.py +92 -83
  65. cloudnetpy/model_evaluation/products/grid_methods.py +88 -63
  66. cloudnetpy/model_evaluation/products/model_products.py +43 -35
  67. cloudnetpy/model_evaluation/products/observation_products.py +41 -35
  68. cloudnetpy/model_evaluation/products/product_resampling.py +17 -7
  69. cloudnetpy/model_evaluation/products/tools.py +29 -20
  70. cloudnetpy/model_evaluation/statistics/statistical_methods.py +30 -20
  71. cloudnetpy/model_evaluation/tests/e2e/conftest.py +3 -3
  72. cloudnetpy/model_evaluation/tests/e2e/process_cf/main.py +9 -5
  73. cloudnetpy/model_evaluation/tests/e2e/process_cf/tests.py +15 -14
  74. cloudnetpy/model_evaluation/tests/e2e/process_iwc/main.py +9 -5
  75. cloudnetpy/model_evaluation/tests/e2e/process_iwc/tests.py +15 -14
  76. cloudnetpy/model_evaluation/tests/e2e/process_lwc/main.py +9 -5
  77. cloudnetpy/model_evaluation/tests/e2e/process_lwc/tests.py +15 -14
  78. cloudnetpy/model_evaluation/tests/unit/conftest.py +42 -41
  79. cloudnetpy/model_evaluation/tests/unit/test_advance_methods.py +41 -48
  80. cloudnetpy/model_evaluation/tests/unit/test_grid_methods.py +216 -194
  81. cloudnetpy/model_evaluation/tests/unit/test_model_products.py +23 -21
  82. cloudnetpy/model_evaluation/tests/unit/test_observation_products.py +37 -38
  83. cloudnetpy/model_evaluation/tests/unit/test_plot_tools.py +43 -40
  84. cloudnetpy/model_evaluation/tests/unit/test_plotting.py +30 -36
  85. cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py +68 -31
  86. cloudnetpy/model_evaluation/tests/unit/test_tools.py +33 -26
  87. cloudnetpy/model_evaluation/utils.py +2 -1
  88. cloudnetpy/output.py +170 -111
  89. cloudnetpy/plotting/__init__.py +2 -1
  90. cloudnetpy/plotting/plot_meta.py +562 -822
  91. cloudnetpy/plotting/plotting.py +1142 -704
  92. cloudnetpy/products/__init__.py +1 -0
  93. cloudnetpy/products/classification.py +370 -88
  94. cloudnetpy/products/der.py +85 -55
  95. cloudnetpy/products/drizzle.py +77 -34
  96. cloudnetpy/products/drizzle_error.py +15 -11
  97. cloudnetpy/products/drizzle_tools.py +79 -59
  98. cloudnetpy/products/epsilon.py +211 -0
  99. cloudnetpy/products/ier.py +27 -50
  100. cloudnetpy/products/iwc.py +55 -48
  101. cloudnetpy/products/lwc.py +96 -70
  102. cloudnetpy/products/mwr_tools.py +186 -0
  103. cloudnetpy/products/product_tools.py +170 -128
  104. cloudnetpy/utils.py +455 -240
  105. cloudnetpy/version.py +2 -2
  106. {cloudnetpy-1.49.9.dist-info → cloudnetpy-1.87.3.dist-info}/METADATA +44 -40
  107. cloudnetpy-1.87.3.dist-info/RECORD +127 -0
  108. {cloudnetpy-1.49.9.dist-info → cloudnetpy-1.87.3.dist-info}/WHEEL +1 -1
  109. cloudnetpy-1.87.3.dist-info/entry_points.txt +2 -0
  110. docs/source/conf.py +2 -2
  111. cloudnetpy/categorize/atmos.py +0 -361
  112. cloudnetpy/products/mwr_multi.py +0 -68
  113. cloudnetpy/products/mwr_single.py +0 -75
  114. cloudnetpy-1.49.9.dist-info/RECORD +0 -112
  115. {cloudnetpy-1.49.9.dist-info → cloudnetpy-1.87.3.dist-info/licenses}/LICENSE +0 -0
  116. {cloudnetpy-1.49.9.dist-info → cloudnetpy-1.87.3.dist-info}/top_level.txt +0 -0
cloudnetpy/metadata.py CHANGED
@@ -4,7 +4,8 @@ from typing import NamedTuple
4
4
 
5
5
 
6
6
  class MetaData(NamedTuple):
7
- long_name: str | None = None
7
+ long_name: str
8
+ dimensions: tuple[str, ...] | None
8
9
  standard_name: str | None = None
9
10
  units: str | None = None
10
11
  comment: str | None = None
@@ -15,6 +16,7 @@ class MetaData(NamedTuple):
15
16
  axis: str | None = None
16
17
  calendar: str | None = None
17
18
  source: str | None = None
19
+ source_instrument_pid: str | None = None
18
20
 
19
21
 
20
22
  COMMON_ATTRIBUTES = {
@@ -23,30 +25,38 @@ COMMON_ATTRIBUTES = {
23
25
  axis="T",
24
26
  standard_name="time",
25
27
  calendar="standard",
28
+ dimensions=("time",),
26
29
  ),
27
30
  "height": MetaData(
28
31
  long_name="Height above mean sea level",
29
32
  standard_name="height_above_mean_sea_level",
30
33
  units="m",
34
+ dimensions=("range",),
31
35
  ),
32
36
  "range": MetaData(
33
37
  long_name="Range from instrument",
34
38
  axis="Z",
35
39
  units="m",
36
40
  comment="Distance from instrument to centre of each range bin.",
41
+ dimensions=("range",),
37
42
  ),
38
43
  "latitude": MetaData(
39
44
  long_name="Latitude of site",
40
45
  units="degree_north",
41
46
  standard_name="latitude",
47
+ dimensions=("time",),
42
48
  ),
43
49
  "longitude": MetaData(
44
50
  long_name="Longitude of site",
45
51
  units="degree_east",
46
52
  standard_name="longitude",
53
+ dimensions=("time",),
47
54
  ),
48
55
  "altitude": MetaData(
49
- long_name="Altitude of site", standard_name="altitude", units="m"
56
+ long_name="Altitude of site",
57
+ standard_name="altitude",
58
+ units="m",
59
+ dimensions=("time",),
50
60
  ),
51
61
  "Zh": MetaData(
52
62
  long_name="Radar reflectivity factor",
@@ -54,6 +64,7 @@ COMMON_ATTRIBUTES = {
54
64
  comment="Calibrated reflectivity. Calibration convention: in the absence\n"
55
65
  "of attenuation, a cloud at 273 K containing one million 100-micron droplets\n"
56
66
  "per cubic metre will have a reflectivity of 0 dBZ at all frequencies.",
67
+ dimensions=("time", "range"),
57
68
  ),
58
69
  "width": MetaData(
59
70
  long_name="Spectral width",
@@ -62,6 +73,7 @@ COMMON_ATTRIBUTES = {
62
73
  "This parameter is the standard deviation of the reflectivity-weighted\n"
63
74
  "velocities in the radar pulse volume."
64
75
  ),
76
+ dimensions=("time", "range"),
65
77
  ),
66
78
  "v": MetaData(
67
79
  long_name="Doppler velocity",
@@ -70,91 +82,150 @@ COMMON_ATTRIBUTES = {
70
82
  "This parameter is the radial component of the velocity, with positive\n"
71
83
  "velocities are away from the radar."
72
84
  ),
85
+ dimensions=("time", "range"),
73
86
  ),
74
87
  "ldr": MetaData(
75
88
  long_name="Linear depolarisation ratio",
76
89
  units="dB",
77
90
  comment="This parameter is the ratio of cross-polar to co-polar reflectivity.",
91
+ dimensions=("time", "range"),
92
+ ),
93
+ "sldr": MetaData(
94
+ long_name="Slanted linear depolarisation ratio",
95
+ units="dB",
96
+ dimensions=("time", "range"),
78
97
  ),
79
- "sldr": MetaData(long_name="Slanted linear depolarisation ratio", units="dB"),
80
98
  "lwp": MetaData(
81
99
  long_name="Liquid water path",
82
100
  units="kg m-2",
83
101
  standard_name="atmosphere_cloud_liquid_water_content",
102
+ dimensions=("time",),
84
103
  ),
85
104
  "iwv": MetaData(
86
105
  long_name="Integrated water vapour",
87
106
  units="kg m-2",
88
107
  standard_name="atmosphere_mass_content_of_water_vapor",
108
+ dimensions=("time",),
89
109
  ),
90
110
  "kurtosis": MetaData(
91
111
  long_name="Kurtosis of spectra",
92
112
  units="1",
113
+ dimensions=("time", "range"),
114
+ ),
115
+ "skewness": MetaData(
116
+ long_name="Skewness of spectra",
117
+ units="1",
118
+ dimensions=("time", "range"),
119
+ ),
120
+ "nyquist_velocity": MetaData(
121
+ long_name="Nyquist velocity",
122
+ units="m s-1",
123
+ dimensions=("time", "range"),
124
+ ),
125
+ "radar_frequency": MetaData(
126
+ long_name="Radar transmit frequency", units="GHz", dimensions=None
93
127
  ),
94
- "nyquist_velocity": MetaData(long_name="Nyquist velocity", units="m s-1"),
95
- "radar_frequency": MetaData(long_name="Radar transmit frequency", units="GHz"),
96
128
  "beta": MetaData(
97
129
  long_name="Attenuated backscatter coefficient",
98
130
  units="sr-1 m-1",
99
131
  comment="SNR-screened attenuated backscatter coefficient.",
132
+ dimensions=("time", "range"),
100
133
  ),
101
134
  "beta_raw": MetaData(
102
135
  long_name="Attenuated backscatter coefficient",
103
136
  units="sr-1 m-1",
104
137
  comment="Non-screened attenuated backscatter coefficient.",
138
+ dimensions=("time", "range"),
105
139
  ),
106
140
  "beta_smooth": MetaData(
107
141
  long_name="Attenuated backscatter coefficient",
108
142
  units="sr-1 m-1",
109
143
  comment="SNR-screened attenuated backscatter coefficient.\n"
110
144
  "Weak background smoothed using Gaussian 2D-kernel.",
145
+ dimensions=("time", "range"),
111
146
  ),
112
- "wavelength": MetaData(
113
- long_name="Laser wavelength",
114
- units="nm",
115
- ),
147
+ "wavelength": MetaData(long_name="Laser wavelength", units="nm", dimensions=None),
116
148
  "zenith_angle": MetaData(
117
149
  long_name="Zenith angle",
118
150
  units="degree",
119
151
  standard_name="zenith_angle",
120
152
  comment="Angle to the local vertical. A value of zero is directly overhead.",
153
+ dimensions=("time",),
154
+ ),
155
+ "ir_zenith_angle": MetaData(
156
+ long_name="Infrared sensor zenith angle",
157
+ units="degree",
158
+ comment="90=horizon, 0=zenith",
159
+ dimensions=("time",),
121
160
  ),
122
161
  "azimuth_angle": MetaData(
123
162
  long_name="Azimuth angle",
124
163
  standard_name="sensor_azimuth_angle",
125
164
  units="degree",
126
165
  comment="Angle between North and the line of sight, measured clockwise.",
166
+ dimensions=("time",),
127
167
  ),
128
- "temperature": MetaData(
129
- long_name="Temperature",
130
- units="K",
131
- ),
132
- "pressure": MetaData(
133
- long_name="Pressure",
134
- units="Pa",
135
- ),
168
+ "temperature": MetaData(long_name="Temperature", units="K", dimensions=("time",)),
169
+ "pressure": MetaData(long_name="Pressure", units="Pa", dimensions=("time",)),
136
170
  "SNR": MetaData(
137
- long_name="Signal-to-noise ratio",
138
- units="dB",
171
+ long_name="Signal-to-noise ratio", units="dB", dimensions=("time", "range")
139
172
  ),
140
173
  "relative_humidity": MetaData(
141
174
  long_name="Relative humidity",
142
175
  standard_name="relative_humidity",
143
176
  units="1",
177
+ dimensions=("time",),
178
+ ),
179
+ "absolute_humidity": MetaData(
180
+ long_name="Absolute humidity",
181
+ standard_name="mass_concentration_of_water_vapor_in_air",
182
+ units="kg m-3",
183
+ dimensions=("time", "range"),
144
184
  ),
145
185
  "wind_speed": MetaData(
146
186
  long_name="Wind speed",
147
187
  standard_name="wind_speed",
148
188
  units="m s-1",
189
+ dimensions=("time",),
149
190
  ),
150
191
  "wind_direction": MetaData(
151
192
  long_name="Wind direction",
152
193
  standard_name="wind_from_direction",
153
194
  units="degree",
195
+ dimensions=("time",),
154
196
  ),
155
197
  "rainfall_rate": MetaData(
156
198
  long_name="Rainfall rate",
157
199
  standard_name="rainfall_rate",
158
200
  units="m s-1",
201
+ dimensions=("time",),
202
+ ),
203
+ "rainfall_amount": MetaData(
204
+ long_name="Rainfall amount",
205
+ standard_name="thickness_of_rainfall_amount",
206
+ units="m",
207
+ comment="Cumulated precipitation since 00:00 UTC",
208
+ dimensions=("time",),
209
+ ),
210
+ "air_temperature": MetaData(
211
+ long_name="Air temperature",
212
+ standard_name="air_temperature",
213
+ units="K",
214
+ dimensions=("time",),
215
+ ),
216
+ "air_pressure": MetaData(
217
+ long_name="Air pressure",
218
+ standard_name="air_pressure",
219
+ units="Pa",
220
+ dimensions=("time",),
221
+ ),
222
+ "snr_limit": MetaData(
223
+ long_name="SNR limit",
224
+ units="dB",
225
+ comment="SNR threshold used in data screening.",
226
+ dimensions=None,
227
+ ),
228
+ "synop_WaWa": MetaData(
229
+ long_name="Synop code WaWa", units="1", dimensions=("time",)
159
230
  ),
160
231
  }
@@ -1,7 +1,10 @@
1
1
  import os
2
2
  from datetime import datetime
3
+ from os import PathLike
4
+ from uuid import UUID
3
5
 
4
6
  import netCDF4
7
+ import numpy.typing as npt
5
8
 
6
9
  from cloudnetpy import output, utils
7
10
 
@@ -10,12 +13,11 @@ from .metadata import (
10
13
  MODEL_ATTRIBUTES,
11
14
  MODEL_L3_ATTRIBUTES,
12
15
  REGRID_PRODUCT_ATTRIBUTES,
13
- MetaData,
14
16
  )
15
17
  from .products.model_products import ModelManager
16
18
 
17
19
 
18
- def update_attributes(model_downsample_variables: dict, attributes: dict):
20
+ def update_attributes(model_downsample_variables: dict, attributes: dict) -> None:
19
21
  """Overrides existing Cloudnet-ME Array-attributes.
20
22
  Overrides existing attributes using hard-coded values.
21
23
  New attributes are added.
@@ -33,11 +35,11 @@ def update_attributes(model_downsample_variables: dict, attributes: dict):
33
35
  model_downsample_variables[key].set_attributes(MODEL_ATTRIBUTES[key])
34
36
  elif "_".join(key_parts[0:-1]) in REGRID_PRODUCT_ATTRIBUTES:
35
37
  model_downsample_variables[key].set_attributes(
36
- REGRID_PRODUCT_ATTRIBUTES["_".join(key_parts[0:-1])]
38
+ REGRID_PRODUCT_ATTRIBUTES["_".join(key_parts[0:-1])],
37
39
  )
38
40
  elif "_".join(key_parts[0:-2]) in REGRID_PRODUCT_ATTRIBUTES:
39
41
  model_downsample_variables[key].set_attributes(
40
- REGRID_PRODUCT_ATTRIBUTES["_".join(key_parts[0:-2])]
42
+ REGRID_PRODUCT_ATTRIBUTES["_".join(key_parts[0:-2])],
41
43
  )
42
44
  elif (
43
45
  "_".join(key_parts[1:]) in MODEL_L3_ATTRIBUTES
@@ -45,29 +47,29 @@ def update_attributes(model_downsample_variables: dict, attributes: dict):
45
47
  ):
46
48
  try:
47
49
  model_downsample_variables[key].set_attributes(
48
- MODEL_L3_ATTRIBUTES["_".join(key_parts[1:])]
50
+ MODEL_L3_ATTRIBUTES["_".join(key_parts[1:])],
49
51
  )
50
52
  except KeyError:
51
53
  model_downsample_variables[key].set_attributes(
52
- MODEL_L3_ATTRIBUTES["_".join(key_parts[2:])]
54
+ MODEL_L3_ATTRIBUTES["_".join(key_parts[2:])],
53
55
  )
54
56
  elif "_".join(key_parts[1:]) in CYCLE_ATTRIBUTES:
55
57
  model_downsample_variables[key].set_attributes(
56
- CYCLE_ATTRIBUTES["_".join(key_parts[1:])]
58
+ CYCLE_ATTRIBUTES["_".join(key_parts[1:])],
57
59
  )
58
60
  elif "_".join(key_parts[2:]) in CYCLE_ATTRIBUTES:
59
61
  model_downsample_variables[key].set_attributes(
60
- CYCLE_ATTRIBUTES["_".join(key_parts[2:])]
62
+ CYCLE_ATTRIBUTES["_".join(key_parts[2:])],
61
63
  )
62
64
 
63
65
 
64
66
  def save_downsampled_file(
65
67
  id_mark: str,
66
- file_name: str,
68
+ file_name: str | PathLike,
67
69
  objects: tuple,
68
- files: tuple,
69
- uuid: str | None,
70
- ):
70
+ files: tuple[list[str | PathLike], str | PathLike],
71
+ uuid: UUID,
72
+ ) -> None:
71
73
  """Saves a standard downsampled day product file.
72
74
 
73
75
  Args:
@@ -83,35 +85,31 @@ def save_downsampled_file(
83
85
  """
84
86
  obj = objects[0]
85
87
  dimensions = {"time": len(obj.time), "level": len(obj.data["level"][:])}
86
- root_group = output.init_file(file_name, dimensions, obj.data, uuid)
87
- _augment_global_attributes(root_group)
88
- uuid = root_group.file_uuid
89
- root_group.cloudnet_file_type = id_mark.split("-")[0]
90
- root_group.title = (
91
- f"Downsampled {id_mark.capitalize().replace('_', ' of ')} "
92
- f"from {obj.dataset.location}"
93
- )
94
- _add_source(root_group, objects, files)
95
- output.copy_global(obj.dataset, root_group, ("location", "day", "month", "year"))
96
- try:
97
- obj.dataset.day
98
- except AttributeError:
99
- root_group.year, root_group.month, root_group.day = obj.date
100
- output.merge_history(root_group, id_mark, {"l3": obj})
101
- root_group.close()
102
- return uuid
103
-
104
-
105
- def add_var2ncfile(obj: ModelManager, file_name: str):
106
- nc_file = netCDF4.Dataset(file_name, "r+", format="NETCDF4_CLASSIC")
107
- _write_vars2nc(nc_file, obj.data)
108
- nc_file.close()
109
-
110
-
111
- def _write_vars2nc(rootgrp: netCDF4.Dataset, cloudnet_variables: dict):
88
+ with output.init_file(file_name, dimensions, obj.data, uuid) as root_group:
89
+ _augment_global_attributes(root_group)
90
+ root_group.cloudnet_file_type = "l3-" + id_mark.split("_")[0]
91
+ root_group.title = (
92
+ f"Downsampled {id_mark.capitalize().replace('_', ' of ')} "
93
+ f"from {obj.dataset.location}"
94
+ )
95
+ _add_source(root_group, objects, files)
96
+ output.copy_global(
97
+ obj.dataset, root_group, ("location", "day", "month", "year")
98
+ )
99
+ if not hasattr(obj.dataset, "day"):
100
+ root_group.year, root_group.month, root_group.day = obj.date
101
+ output.merge_history(root_group, id_mark, obj)
102
+
103
+
104
+ def add_var2ncfile(obj: ModelManager, file_name: str | PathLike) -> None:
105
+ with netCDF4.Dataset(file_name, "r+", format="NETCDF4_CLASSIC") as nc_file:
106
+ _write_vars2nc(nc_file, obj.data)
107
+
108
+
109
+ def _write_vars2nc(rootgrp: netCDF4.Dataset, cloudnet_variables: dict) -> None:
112
110
  """Iterates over Cloudnet-ME instances and write to given rootgrp."""
113
111
 
114
- def _get_dimensions(array):
112
+ def _get_dimensions(array: npt.NDArray) -> tuple:
115
113
  """Finds correct dimensions for a variable."""
116
114
  if utils.isscalar(array):
117
115
  return ()
@@ -119,8 +117,8 @@ def _write_vars2nc(rootgrp: netCDF4.Dataset, cloudnet_variables: dict):
119
117
  file_dims = rootgrp.dimensions
120
118
  array_dims = array.shape
121
119
  for length in array_dims:
122
- dim = [key for key in file_dims.keys() if file_dims[key].size == length][0]
123
- variable_size = variable_size + (dim,)
120
+ dim = [key for key in file_dims if file_dims[key].size == length][0] # noqa: RUF015
121
+ variable_size = (*variable_size, dim)
124
122
  return variable_size
125
123
 
126
124
  for key in cloudnet_variables:
@@ -128,7 +126,10 @@ def _write_vars2nc(rootgrp: netCDF4.Dataset, cloudnet_variables: dict):
128
126
  size = _get_dimensions(obj.data)
129
127
  try:
130
128
  nc_variable = rootgrp.createVariable(
131
- obj.name, obj.data_type, size, zlib=True
129
+ obj.name,
130
+ obj.data_type,
131
+ size,
132
+ zlib=True,
132
133
  )
133
134
  nc_variable[:] = obj.data
134
135
  for attr in obj.fetch_attributes():
@@ -137,13 +138,12 @@ def _write_vars2nc(rootgrp: netCDF4.Dataset, cloudnet_variables: dict):
137
138
  continue
138
139
 
139
140
 
140
- def _augment_global_attributes(root_group: netCDF4.Dataset):
141
+ def _augment_global_attributes(root_group: netCDF4.Dataset) -> None:
141
142
  root_group.Conventions = "CF-1.8"
142
- # root_group.cloudnetme_version = version.__version__
143
143
 
144
144
 
145
- def _add_source(root_ground: netCDF4.Dataset, objects: tuple, files: tuple):
146
- """Generates source info for multiple files"""
145
+ def _add_source(root_ground: netCDF4.Dataset, objects: tuple, files: tuple) -> None:
146
+ """Generates source info for multiple files."""
147
147
  model, obs = objects
148
148
  model_files, obs_file = files
149
149
  source = f"Observation file: {os.path.basename(obs_file)}"
@@ -154,18 +154,21 @@ def _add_source(root_ground: netCDF4.Dataset, objects: tuple, files: tuple):
154
154
  if i < len(model_files) - 1:
155
155
  source += "\n"
156
156
  root_ground.source = source
157
- root_ground.source_file_uuids = output.get_source_uuids(model, obs)
157
+ root_ground.source_file_uuids = output.get_source_uuids([model, obs])
158
158
 
159
159
 
160
160
  def add_time_attribute(date: datetime) -> dict:
161
- """ "Adds time attribute with correct units.
161
+ """Adds time attribute with correct units.
162
+
162
163
  Args:
163
164
  attributes: Attributes of variables.
164
165
  date: Date as Y M D 0 0 0.
166
+
165
167
  Returns:
166
168
  dict: Same attributes with 'time' attribute added.
167
169
  """
168
- d = date.strftime("%y.%m.%d")
169
- attributes = {}
170
- attributes["time"] = MetaData(units=f"hours since {d} 00:00:00", long_name="")
171
- return attributes
170
+ return {
171
+ "time": MODEL_ATTRIBUTES["time"]._replace(
172
+ units=f"hours since {date:%Y-%m-%d} 00:00:00 +00:00",
173
+ ),
174
+ }
@@ -1,27 +1,24 @@
1
- from typing import NamedTuple
2
-
3
-
4
- class MetaData(NamedTuple):
5
- long_name: str
6
- units: str
7
- comment: str | None = None
8
- standard_name: str | None = None
9
- axis: str | None = None
10
- positive: str | None = None
11
-
1
+ from cloudnetpy.metadata import MetaData
12
2
 
13
3
  MODEL_ATTRIBUTES = {
14
4
  "time": MetaData(
15
- units="decimal hours since midnight",
5
+ units="",
16
6
  long_name="Time UTC",
17
7
  axis="T",
8
+ calendar="standard",
9
+ dimensions=("time",),
10
+ ),
11
+ "latitude": MetaData(
12
+ long_name="Latitude of grid point", units="degree_north", dimensions=None
13
+ ),
14
+ "longitude": MetaData(
15
+ long_name="Longitude of grid point", units="degree_east", dimensions=None
18
16
  ),
19
- "latitude": MetaData(long_name="Latitude of grid point", units="dergees_north"),
20
- "longitude": MetaData(long_name="Longitude of grid point", units="degrees_east"),
21
17
  "horizontal_resolution": MetaData(
22
18
  long_name="Horizontal resolution of model",
23
19
  units="km",
24
20
  comment="Distance between two grid point",
21
+ dimensions=None,
25
22
  ),
26
23
  "level": MetaData(
27
24
  long_name="Model level",
@@ -29,6 +26,7 @@ MODEL_ATTRIBUTES = {
29
26
  comment="Level 1 describes the highest height from ground.",
30
27
  axis="Z",
31
28
  positive="down",
29
+ dimensions=("level",),
32
30
  ),
33
31
  }
34
32
 
@@ -41,6 +39,7 @@ CYCLE_ATTRIBUTES = {
41
39
  "which it was taken. Note that the profiles in this file may be taken\n"
42
40
  "from more than one forecast."
43
41
  ),
42
+ dimensions=("time",),
44
43
  ),
45
44
  "height": MetaData(
46
45
  long_name="Height above ground",
@@ -50,16 +49,21 @@ CYCLE_ATTRIBUTES = {
50
49
  "specific humidity."
51
50
  ),
52
51
  positive="up",
52
+ dimensions=("time", "level"),
53
53
  ),
54
- "pressure": MetaData(long_name="Pressure", units="Pa"),
55
- "temperature": MetaData(long_name="Temperature", units="K"),
54
+ "pressure": MetaData(long_name="Pressure", units="Pa", dimensions=None),
55
+ "temperature": MetaData(long_name="Temperature", units="K", dimensions=None),
56
56
  "uwind": MetaData(
57
- long_name="Zonal wind", units="m s-1", standard_name="eastward_wind"
57
+ long_name="Zonal wind",
58
+ units="m s-1",
59
+ standard_name="eastward_wind",
60
+ dimensions=None,
58
61
  ),
59
62
  "vwind": MetaData(
60
63
  long_name="Meridional wind",
61
64
  units="m s-1",
62
65
  standard_name="northward_wind",
66
+ dimensions=None,
63
67
  ),
64
68
  "wwind": MetaData(
65
69
  long_name="Vertical wind",
@@ -69,13 +73,15 @@ CYCLE_ATTRIBUTES = {
69
73
  "The vertical wind has been calculated from omega (Pa s-1),\n"
70
74
  "height and pressure using: w=omega*dz/dp"
71
75
  ),
76
+ dimensions=None,
72
77
  ),
73
78
  "omega": MetaData(
74
79
  long_name="Vertical wind in pressure coordinates",
75
80
  units="PA s-1",
76
81
  standard_name="omega",
82
+ dimensions=None,
77
83
  ),
78
- "q": MetaData(long_name="Specific humidity", units="1"),
84
+ "q": MetaData(long_name="Specific humidity", units="1", dimensions=None),
79
85
  "rh": MetaData(
80
86
  long_name="Relative humidity",
81
87
  units="1",
@@ -83,28 +89,36 @@ CYCLE_ATTRIBUTES = {
83
89
  "With respect to liquid above 0 degrees C and with respect to ice\n"
84
90
  "below 0 degrees C"
85
91
  ),
92
+ dimensions=None,
86
93
  ),
87
94
  }
88
95
 
89
96
  MODEL_L3_ATTRIBUTES = {
90
- "cf": MetaData(long_name="Cloud fraction of model grid point", units="1"),
97
+ "cf": MetaData(
98
+ long_name="Cloud fraction of model grid point",
99
+ units="1",
100
+ dimensions=("time", "level"),
101
+ ),
91
102
  "cf_cirrus": MetaData(
92
103
  long_name="Cloud fraction of model grid point with filtered cirrus fraction",
93
104
  units="1",
94
105
  comment="High level cirrus fraction is reduce do to lack if a radar\n"
95
106
  "capability to observe correctly particles small and high.",
107
+ dimensions=("time", "level"),
96
108
  ),
97
109
  "iwc": MetaData(
98
110
  long_name="Ice water content of model grid point",
99
111
  units="kg m-3",
100
112
  comment="Calculated using model ice water mixing ration, pressure\n"
101
113
  "and temperature: qi*P/287*T",
114
+ dimensions=("time", "level"),
102
115
  ),
103
116
  "lwc": MetaData(
104
117
  long_name="Liquid water content of model grid point",
105
118
  units="kg m-3",
106
119
  comment="Calculated using model liquid water mixing ration, pressure\n"
107
120
  "and temperature: ql*P/287*T",
121
+ dimensions=("time", "level"),
108
122
  ),
109
123
  }
110
124
 
@@ -115,8 +129,9 @@ REGRID_PRODUCT_ATTRIBUTES = {
115
129
  comment=(
116
130
  "Cloud fraction generated from observations and by volume,\n"
117
131
  "averaged onto the models grid with height and time. Volume is\n"
118
- "space withing four grid points."
132
+ "space within four grid points."
119
133
  ),
134
+ dimensions=("time", "level"),
120
135
  ),
121
136
  "cf_A": MetaData(
122
137
  long_name="Observed cloud fraction by area",
@@ -126,6 +141,7 @@ REGRID_PRODUCT_ATTRIBUTES = {
126
141
  "averaged onto the models grid with height and time. Area is\n"
127
142
  "sum of time columns with any cloud fraction."
128
143
  ),
144
+ dimensions=("time", "level"),
129
145
  ),
130
146
  "cf_V_adv": MetaData(
131
147
  long_name="Observed cloud fraction by advection volume",
@@ -135,6 +151,7 @@ REGRID_PRODUCT_ATTRIBUTES = {
135
151
  "except that model winds were used to estimate the time taken to advect\n"
136
152
  "airflow a distance equivalent to the models horizontal resolution."
137
153
  ),
154
+ dimensions=("time", "level"),
138
155
  ),
139
156
  "cf_A_adv": MetaData(
140
157
  long_name="Observed cloud fraction by advection area",
@@ -144,6 +161,7 @@ REGRID_PRODUCT_ATTRIBUTES = {
144
161
  "except that model winds were used to estimate the time taken to advect\n"
145
162
  "airflow a distance equivalent to the models horizontal resolution."
146
163
  ),
164
+ dimensions=("time", "level"),
147
165
  ),
148
166
  "iwc": MetaData(
149
167
  long_name="Observed ice water content reshaped to model dimensions by"
@@ -155,6 +173,7 @@ REGRID_PRODUCT_ATTRIBUTES = {
155
173
  "The formula has been applied where the categorization data has\n"
156
174
  "diagnosed that the radar echo is due to ice."
157
175
  ),
176
+ dimensions=("time", "level"),
158
177
  ),
159
178
  "iwc_att": MetaData(
160
179
  long_name="Observed ice water content with attenuation reshaped to model grid"
@@ -165,6 +184,7 @@ REGRID_PRODUCT_ATTRIBUTES = {
165
184
  "except that profiles with uncorrected attenuation of the radar\n"
166
185
  "reflectivity were included."
167
186
  ),
187
+ dimensions=("time", "level"),
168
188
  ),
169
189
  "iwc_rain": MetaData(
170
190
  long_name="Observed ice water content with raining reshaped to model grid"
@@ -175,6 +195,7 @@ REGRID_PRODUCT_ATTRIBUTES = {
175
195
  "including attenuation, iwc_att, except that profiles with rain\n"
176
196
  "at the surface were also included."
177
197
  ),
198
+ dimensions=("time", "level"),
178
199
  ),
179
200
  "iwc_adv": MetaData(
180
201
  long_name="Observed ice water content reshaped to model advection grid"
@@ -185,6 +206,7 @@ REGRID_PRODUCT_ATTRIBUTES = {
185
206
  "except that model winds were used to estimate the time taken to advect\n"
186
207
  "the flow a distance equivalent to the models horizontal resolution."
187
208
  ),
209
+ dimensions=("time", "level"),
188
210
  ),
189
211
  "iwc_att_adv": MetaData(
190
212
  long_name="Observed ice water content with attenuation reshaped to model"
@@ -195,6 +217,7 @@ REGRID_PRODUCT_ATTRIBUTES = {
195
217
  "iwc_adv, except that profiles with uncorrected attenuation of the radar\n"
196
218
  "reflectivity were included."
197
219
  ),
220
+ dimensions=("time", "level"),
198
221
  ),
199
222
  "iwc_rain_adv": MetaData(
200
223
  long_name="Observed ice water content with raining reshaped to model"
@@ -205,6 +228,7 @@ REGRID_PRODUCT_ATTRIBUTES = {
205
228
  "including attenuation, iwc_att_adv, except that profiles with rain\n"
206
229
  "at the surface were also included."
207
230
  ),
231
+ dimensions=("time", "level"),
208
232
  ),
209
233
  "lwc": MetaData(
210
234
  long_name="Observed liquid water content reshaped to model grid by averaging",
@@ -214,6 +238,7 @@ REGRID_PRODUCT_ATTRIBUTES = {
214
238
  "pixels where the categorization data has diagnosed that liquid water \n"
215
239
  "is present, averaged onto the model grid with height and time."
216
240
  ),
241
+ dimensions=("time", "level"),
217
242
  ),
218
243
  "lwc_adv": MetaData(
219
244
  long_name="Observed liquid water content reshaped to model advection grid by "
@@ -224,5 +249,6 @@ REGRID_PRODUCT_ATTRIBUTES = {
224
249
  "lwc, except that model winds were used to estimate the time taken to\n"
225
250
  "advect flow over the models grid points."
226
251
  ),
252
+ dimensions=("time", "level"),
227
253
  ),
228
254
  }
@@ -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",
34
34
  level=65,