cloudnetpy 1.61.16__py3-none-any.whl → 1.62.0__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.
@@ -10,6 +10,7 @@ import cloudnetpy.categorize.atmos
10
10
  from cloudnetpy import utils
11
11
  from cloudnetpy.categorize import droplet, falling, freezing, insects, melting
12
12
  from cloudnetpy.categorize.containers import ClassData, ClassificationResult
13
+ from cloudnetpy.constants import T0
13
14
 
14
15
 
15
16
  def classify_measurements(data: dict) -> ClassificationResult:
@@ -36,6 +37,14 @@ def classify_measurements(data: dict) -> ClassificationResult:
36
37
  implementation compared to the original Cloudnet methodology.
37
38
  Especially methods classifying insects, melting layer and liquid droplets.
38
39
 
40
+ Explanation of bits:
41
+ - bit 0: Liquid droplets
42
+ - bit 1: Falling hydrometeors
43
+ - bit 2: Freezing region
44
+ - bit 3: Melting layer
45
+ - bit 4: Aerosols
46
+ - bit 5: Insects
47
+
39
48
  """
40
49
  obs = ClassData(data)
41
50
  bits: list[np.ndarray] = [np.array([])] * 6
@@ -73,6 +82,8 @@ def classify_measurements(data: dict) -> ClassificationResult:
73
82
  bits = _filter_insects(bits)
74
83
  bits[4] = _find_aerosols(obs, bits[1], bits[0])
75
84
  bits[4][filtered_ice] = False
85
+ bits = _fix_super_cold_liquid(obs, bits)
86
+
76
87
  return ClassificationResult(
77
88
  category_bits=_bits_to_integer(bits),
78
89
  is_rain=obs.is_rain,
@@ -82,6 +93,15 @@ def classify_measurements(data: dict) -> ClassificationResult:
82
93
  )
83
94
 
84
95
 
96
+ def _fix_super_cold_liquid(obs: ClassData, bits: list) -> list:
97
+ """Supercooled liquid droplets do not exist in atmosphere below around -38 C."""
98
+ t_limit = T0 - 38
99
+ super_cold_liquid = np.where((obs.tw < t_limit) & bits[0])
100
+ bits[0][super_cold_liquid] = False
101
+ bits[1][super_cold_liquid] = True
102
+ return bits
103
+
104
+
85
105
  def _remove_false_radar_liquid(
86
106
  liquid_from_radar: np.ndarray,
87
107
  liquid_from_lidar: np.ndarray,
@@ -87,12 +87,15 @@ def ceilo2nc(
87
87
  if ceilo_obj.instrument is None or ceilo_obj.instrument.model is None:
88
88
  msg = "Failed to read ceilometer model"
89
89
  raise RuntimeError(msg)
90
- if "cl61" in ceilo_obj.instrument.model.lower():
91
- # This kind of screening could be used with other ceilometers as well:
90
+ if any(
91
+ model in ceilo_obj.instrument.model.lower()
92
+ for model in ("cl61", "chm15k", "chm15kx", "cl51", "cl31")
93
+ ):
92
94
  mask = ceilo_obj.data["beta_smooth"].mask
93
95
  ceilo_obj.data["beta"] = ma.masked_where(mask, ceilo_obj.data["beta_raw"])
94
96
  ceilo_obj.data["beta"][ceilo_obj.data["beta"] <= 0] = ma.masked
95
- ceilo_obj.data["depolarisation"].mask = ceilo_obj.data["beta"].mask
97
+ if "depolarisation" in ceilo_obj.data:
98
+ ceilo_obj.data["depolarisation"].mask = ceilo_obj.data["beta"].mask
96
99
  ceilo_obj.screen_depol()
97
100
  ceilo_obj.screen_invalid_values()
98
101
  ceilo_obj.prepare_data()
@@ -47,12 +47,16 @@ def ws2nc(
47
47
  ws: WS
48
48
  if site_meta["name"] == "Palaiseau":
49
49
  ws = PalaiseauWS(weather_station_file, site_meta)
50
+ elif site_meta["name"] == "Bucharest":
51
+ ws = BucharestWS(weather_station_file, site_meta)
50
52
  elif site_meta["name"] == "Granada":
51
53
  ws = GranadaWS(weather_station_file, site_meta)
52
54
  elif site_meta["name"] == "Kenttärova":
53
55
  ws = KenttarovaWS(weather_station_file, site_meta)
54
56
  elif site_meta["name"] == "Hyytiälä":
55
57
  ws = HyytialaWS(weather_station_file, site_meta)
58
+ elif site_meta["name"] == "Galați":
59
+ ws = GalatiWS(weather_station_file, site_meta)
56
60
  else:
57
61
  msg = "Unsupported site"
58
62
  raise ValueError(msg) # noqa: TRY301
@@ -66,6 +70,7 @@ def ws2nc(
66
70
  ws.convert_pressure()
67
71
  ws.convert_rainfall_rate()
68
72
  ws.convert_rainfall_amount()
73
+ ws.normalize_rainfall_amount()
69
74
  ws.calculate_rainfall_amount()
70
75
  attributes = output.add_time_attribute(ATTRIBUTES, ws.date)
71
76
  output.update_attributes(ws.data, attributes)
@@ -75,9 +80,11 @@ def ws2nc(
75
80
 
76
81
 
77
82
  class WS(CloudnetInstrument):
78
- def __init__(self):
83
+ def __init__(self, site_meta: dict):
79
84
  super().__init__()
80
85
  self._data: dict
86
+ self.site_meta = site_meta
87
+ self.instrument = instruments.GENERIC_WEATHER_STATION
81
88
 
82
89
  date: list[str]
83
90
 
@@ -111,6 +118,15 @@ class WS(CloudnetInstrument):
111
118
  x for ind, x in enumerate(self._data[key]) if ind in valid_ind
112
119
  ]
113
120
 
121
+ @staticmethod
122
+ def format_data(data: dict) -> dict:
123
+ for key, value in data.items():
124
+ new_value = np.array(value)
125
+ if key != "time":
126
+ new_value = ma.masked_where(np.isnan(new_value), new_value)
127
+ data[key] = new_value
128
+ return data
129
+
114
130
  def convert_temperature_and_humidity(self) -> None:
115
131
  temperature_kelvins = atmos_utils.c2k(self.data["air_temperature"][:])
116
132
  self.data["air_temperature"].data = temperature_kelvins
@@ -123,6 +139,17 @@ class WS(CloudnetInstrument):
123
139
  def convert_pressure(self) -> None:
124
140
  self.data["air_pressure"].data = self.data["air_pressure"][:] * 100 # hPa to Pa
125
141
 
142
+ def normalize_rainfall_amount(self) -> None:
143
+ if "rainfall_amount" in self.data:
144
+ amount = self.data["rainfall_amount"][:]
145
+ offset = 0
146
+ for i in range(1, len(amount)):
147
+ if amount[i] + offset < amount[i - 1]:
148
+ offset += amount[i - 1]
149
+ amount[i] += offset
150
+ amount -= amount[0]
151
+ self.data["rainfall_amount"].data = amount
152
+
126
153
  def convert_time(self) -> None:
127
154
  pass
128
155
 
@@ -132,50 +159,30 @@ class WS(CloudnetInstrument):
132
159
 
133
160
  class PalaiseauWS(WS):
134
161
  def __init__(self, filenames: list[str], site_meta: dict):
135
- super().__init__()
136
- if len(filenames) != 1:
137
- raise ValueError
138
- self.filename = filenames[0]
139
- self.site_meta = site_meta
140
- self.instrument = instruments.GENERIC_WEATHER_STATION
162
+ super().__init__(site_meta)
163
+ self.filenames = filenames
141
164
  self._data = self._read_data()
142
165
 
143
166
  def _read_data(self) -> dict:
144
167
  timestamps, values, header = [], [], []
145
- with open(self.filename, encoding="latin-1") as f:
146
- data = f.readlines()
147
- for row in data:
148
- splat = row.split()
149
- try:
150
- timestamp = datetime.datetime.strptime(
151
- splat[0],
152
- "%Y-%m-%dT%H:%M:%SZ",
153
- ).replace(tzinfo=datetime.timezone.utc)
154
- temp: list[str | float] = list(splat)
155
- temp[1:] = [float(x) for x in temp[1:]]
156
- values.append(temp)
157
- timestamps.append(timestamp)
158
- except ValueError:
159
- header.append("".join(splat))
160
-
161
- # Simple validation for now:
162
- expected_identifiers = [
163
- "DateTime(yyyy-mm-ddThh:mm:ssZ)",
164
- "Windspeed(m/s)",
165
- "Winddirection(degres)",
166
- "Airtemperature(°C)",
167
- "Relativehumidity(%)",
168
- "Pressure(hPa)",
169
- "Precipitationrate(mm/min)",
170
- "24-hrcumulatedprecipitationsince00UT(mm)",
171
- ]
172
- column_titles = [row for row in header if "Col." in row]
173
- error_msg = "Unexpected weather station file format"
174
- if len(column_titles) != len(expected_identifiers):
175
- raise ValueError(error_msg)
176
- for title, identifier in zip(column_titles, expected_identifiers, strict=True):
177
- if identifier not in title:
178
- raise ValueError(error_msg)
168
+ for filename in self.filenames:
169
+ with open(filename, encoding="latin-1") as f:
170
+ data = f.readlines()
171
+ for row in data:
172
+ if not (columns := row.split()):
173
+ continue
174
+ if row.startswith("#"):
175
+ header_row = "".join(columns)
176
+ if header_row not in header:
177
+ header.append(header_row)
178
+ else:
179
+ timestamp = datetime.datetime.strptime(
180
+ columns[0], "%Y-%m-%dT%H:%M:%SZ"
181
+ ).replace(tzinfo=datetime.timezone.utc)
182
+ values.append([timestamp] + [float(x) for x in columns[1:]])
183
+ timestamps.append(timestamp)
184
+
185
+ self._validate_header(header)
179
186
  return {"time": timestamps, "values": values}
180
187
 
181
188
  def convert_time(self) -> None:
@@ -212,15 +219,39 @@ class PalaiseauWS(WS):
212
219
  self.data["rainfall_amount"][:] / 1000
213
220
  ) # mm -> m
214
221
 
222
+ @staticmethod
223
+ def _validate_header(header: list[str]) -> None:
224
+ expected_identifiers = [
225
+ "DateTime(yyyy-mm-ddThh:mm:ssZ)",
226
+ "Windspeed(m/s)",
227
+ "Winddirection(deg",
228
+ "Airtemperature",
229
+ "Relativehumidity(%)",
230
+ "Pressure(hPa)",
231
+ "Precipitationrate(mm/min)",
232
+ "precipitation",
233
+ ]
234
+ column_titles = [row for row in header if "Col." in row]
235
+ error_msg = "Unexpected weather station file format"
236
+ if len(column_titles) != len(expected_identifiers):
237
+ raise ValueError(error_msg)
238
+ for title, identifier in zip(column_titles, expected_identifiers, strict=True):
239
+ if identifier not in title:
240
+ raise ValueError(error_msg)
241
+
242
+
243
+ class BucharestWS(PalaiseauWS):
244
+ def convert_rainfall_rate(self) -> None:
245
+ rainfall_rate = self.data["rainfall_rate"][:]
246
+ self.data["rainfall_rate"].data = rainfall_rate * MM_H_TO_M_S
247
+
215
248
 
216
249
  class GranadaWS(WS):
217
250
  def __init__(self, filenames: list[str], site_meta: dict):
218
251
  if len(filenames) != 1:
219
252
  raise ValueError
220
- super().__init__()
253
+ super().__init__(site_meta)
221
254
  self.filename = filenames[0]
222
- self.site_meta = site_meta
223
- self.instrument = instruments.GENERIC_WEATHER_STATION
224
255
  self._data = self._read_data()
225
256
 
226
257
  def _read_data(self) -> dict:
@@ -264,10 +295,8 @@ class GranadaWS(WS):
264
295
 
265
296
  class KenttarovaWS(WS):
266
297
  def __init__(self, filenames: list[str], site_meta: dict):
267
- super().__init__()
298
+ super().__init__(site_meta)
268
299
  self.filenames = filenames
269
- self.site_meta = site_meta
270
- self.instrument = instruments.GENERIC_WEATHER_STATION
271
300
  self._data = self._read_data()
272
301
 
273
302
  def _read_data(self) -> dict:
@@ -302,12 +331,7 @@ class KenttarovaWS(WS):
302
331
  merged = {key: [*merged[key], *data[key]] for key in merged}
303
332
  else:
304
333
  merged = data
305
- for key, value in merged.items():
306
- new_value = np.array(value)
307
- if key != "time":
308
- new_value = ma.masked_where(np.isnan(new_value), new_value)
309
- merged[key] = new_value
310
- return merged
334
+ return self.format_data(merged)
311
335
 
312
336
  def convert_rainfall_rate(self) -> None:
313
337
  # Rainfall rate is 10-minute averaged in mm h-1
@@ -327,10 +351,8 @@ class HyytialaWS(WS):
327
351
  """
328
352
 
329
353
  def __init__(self, filenames: list[str], site_meta: dict):
330
- super().__init__()
354
+ super().__init__(site_meta)
331
355
  self.filename = filenames[0]
332
- self.site_meta = site_meta
333
- self.instrument = instruments.GENERIC_WEATHER_STATION
334
356
  self._data = self._read_data()
335
357
 
336
358
  def _read_data(self) -> dict:
@@ -380,12 +402,7 @@ class HyytialaWS(WS):
380
402
  "wind_direction": raw_data["WD/ds"],
381
403
  "rainfall_rate": raw_data["AaNRT/mm"],
382
404
  }
383
- for key, value in data.items():
384
- new_value = np.array(value)
385
- if key != "time":
386
- new_value = ma.masked_where(np.isnan(new_value), new_value)
387
- data[key] = new_value
388
- return data
405
+ return self.format_data(data)
389
406
 
390
407
  def convert_pressure(self) -> None:
391
408
  self.data["air_pressure"].data = (
@@ -393,6 +410,43 @@ class HyytialaWS(WS):
393
410
  ) # kPa to Pa
394
411
 
395
412
 
413
+ class GalatiWS(WS):
414
+ def __init__(self, filenames: list[str], site_meta: dict):
415
+ super().__init__(site_meta)
416
+ self.filename = filenames[0]
417
+ self._data = self._read_data()
418
+
419
+ def _read_data(self) -> dict:
420
+ with open(self.filename, newline="") as f:
421
+ reader = csv.DictReader(f)
422
+ raw_data: dict = {key: [] for key in reader.fieldnames} # type: ignore[union-attr]
423
+ for row in reader:
424
+ for key, value in row.items():
425
+ parsed_value: float | datetime.datetime
426
+ if key == "TimeStamp":
427
+ parsed_value = datetime.datetime.strptime(
428
+ value, "%Y-%m-%d %H:%M:%S.%f"
429
+ )
430
+ else:
431
+ try:
432
+ parsed_value = float(value)
433
+ except ValueError:
434
+ parsed_value = math.nan
435
+ raw_data[key].append(parsed_value)
436
+ data = {
437
+ "time": raw_data["TimeStamp"],
438
+ "air_temperature": raw_data["Temperature"],
439
+ "relative_humidity": raw_data["RH"],
440
+ "air_pressure": raw_data["Atmospheric_pressure"],
441
+ "rainfall_rate": raw_data["Precipitations"],
442
+ }
443
+ return self.format_data(data)
444
+
445
+ def convert_pressure(self) -> None:
446
+ mmHg2Pa = 133.322
447
+ self.data["air_pressure"].data = self.data["air_pressure"][:] * mmHg2Pa
448
+
449
+
396
450
  ATTRIBUTES = {
397
451
  "rainfall_amount": MetaData(
398
452
  long_name="Rainfall amount",
@@ -678,8 +678,16 @@ class Plot1D(Plot):
678
678
  def _plot_moving_average(self, figure_data: FigureData) -> None:
679
679
  time = figure_data.time.copy()
680
680
  data = self._data_orig.copy()
681
- data, time = self._get_unmasked_values(data, time)
682
- sma = self._calculate_moving_average(data, time, window=5)
681
+ good_values = ~ma.getmaskarray(data)
682
+ data = data[good_values]
683
+ time = time[good_values]
684
+ if self.sub_plot.variable.name == "wind_direction":
685
+ wind_speed = figure_data.file["wind_speed"][good_values]
686
+ sma = self._calculate_average_wind_direction(
687
+ wind_speed, data, time, window=15
688
+ )
689
+ else:
690
+ sma = self._calculate_moving_average(data, time, window=5)
683
691
  gap_time = _get_max_gap_in_minutes(figure_data)
684
692
  gaps = self._find_time_gap_indices(time, max_gap_min=gap_time)
685
693
  if len(gaps) > 0:
@@ -699,16 +707,6 @@ class Plot1D(Plot):
699
707
  line_width = np.median(np.diff(time)) * 1000
700
708
  return min(max(line_width, 0.25), 0.9)
701
709
 
702
- @staticmethod
703
- def _get_unmasked_values(
704
- data: ma.MaskedArray,
705
- time: ndarray,
706
- ) -> tuple[ndarray, ndarray]:
707
- if not ma.is_masked(data):
708
- return data, time
709
- good_values = ~data.mask
710
- return data[good_values], time[good_values]
711
-
712
710
  @staticmethod
713
711
  def _get_bad_zenith_profiles(figure_data: FigureData) -> ndarray:
714
712
  zenith_limit = 5
@@ -745,6 +743,24 @@ class Plot1D(Plot):
745
743
  edge = window_size // 2
746
744
  return np.pad(sma, (edge, edge - 1), mode="constant", constant_values=np.nan)
747
745
 
746
+ @classmethod
747
+ def _calculate_average_wind_direction(
748
+ cls,
749
+ wind_speed: ndarray,
750
+ wind_direction: ndarray,
751
+ time: ndarray,
752
+ window: float = 5,
753
+ ) -> ndarray:
754
+ angle = np.deg2rad(wind_direction)
755
+ u = wind_speed * np.cos(angle)
756
+ v = wind_speed * np.sin(angle)
757
+ avg_u = cls._calculate_moving_average(u, time, window)
758
+ avg_v = cls._calculate_moving_average(v, time, window)
759
+ data = np.rad2deg(np.arctan2(avg_v, avg_u)) % 360
760
+ wrap = np.where(np.abs(np.diff(data)) > 300)[0]
761
+ data[wrap] = np.nan
762
+ return data
763
+
748
764
 
749
765
  def generate_figure(
750
766
  filename: os.PathLike | str,
cloudnetpy/version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  MAJOR = 1
2
- MINOR = 61
3
- PATCH = 16
2
+ MINOR = 62
3
+ PATCH = 0
4
4
  __version__ = f"{MAJOR}.{MINOR}.{PATCH}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cloudnetpy
3
- Version: 1.61.16
3
+ Version: 1.62.0
4
4
  Summary: Python package for Cloudnet processing
5
5
  Author: Simo Tukiainen
6
6
  License: MIT License
@@ -8,12 +8,12 @@ cloudnetpy/metadata.py,sha256=v_VDo2vbdTxB0zIsfP69IcrwSKiRlLpsGdq6JPI4CoA,5306
8
8
  cloudnetpy/output.py,sha256=YkCaxVkG_Mt2hng_IVnhygHteV4UMKzKALkeFZwFJL8,14822
9
9
  cloudnetpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  cloudnetpy/utils.py,sha256=JV0Fawnme1HoZgoiidV3eIzsn6vx0AEjBNmI1CcrBsA,28517
11
- cloudnetpy/version.py,sha256=GO9xFdIg8VR1hdOJ3fERTqkbnoe6P85YNUOxd1OjCq4,73
11
+ cloudnetpy/version.py,sha256=zim9VT3-QVKAuQTn9k3jQwSlP2Slu9g5kvrN_-965A8,72
12
12
  cloudnetpy/categorize/__init__.py,sha256=gP5q3Vis1y9u9OWgA_idlbjfWXYN_S0IBSWdwBhL_uU,69
13
13
  cloudnetpy/categorize/atmos.py,sha256=G4DmEJCt1FAPYyt7oXzBH47JTeb5lUOGDakkviOXblE,12390
14
14
  cloudnetpy/categorize/atmos_utils.py,sha256=64uenj2uxj3P3Blaq_pBN1pBjcF-X4LYNt-uTOjvevg,3778
15
15
  cloudnetpy/categorize/categorize.py,sha256=aoIxbBEwUFO-Xx_oofKM68aL0KEJuGi3OaWMKCCuUK8,17827
16
- cloudnetpy/categorize/classify.py,sha256=l8XoO42GJysio5ODX6qoxWHD9RqtMyz_-T8ZOpOkMxU,9219
16
+ cloudnetpy/categorize/classify.py,sha256=a-0bVCtynGfORnDGTsPuzqkuDeOOR_OMz5ai9NsMuic,9870
17
17
  cloudnetpy/categorize/containers.py,sha256=aL_55tTDYjICS_TnG1u0FwBeXDS0S4mfDMU0kY_DUbs,4312
18
18
  cloudnetpy/categorize/disdrometer.py,sha256=keU3pFvKtk840A0oLwAyNDuqOCswBPJEKf2bV0YWyA8,2004
19
19
  cloudnetpy/categorize/droplet.py,sha256=894VHdL9ScaB8f1oxXwM2la4ShXd-uWywQDINoaoiD8,8687
@@ -28,7 +28,7 @@ cloudnetpy/categorize/radar.py,sha256=C4R74E_jmLOJqXLrfhdrAitHRHHA79UYuChz9VLxy5
28
28
  cloudnetpy/instruments/__init__.py,sha256=_jejVwi_viSZehmAOkEqTNI-0-exGgAJ_bHW1IRRwTI,398
29
29
  cloudnetpy/instruments/basta.py,sha256=_OTnySd36ktvxk_swWBzbv_H4AVGlkF_Ce3KtPGD1rE,3758
30
30
  cloudnetpy/instruments/campbell_scientific.py,sha256=2WHfBKQjtRSl0AqvtPeX7G8Hdi3Dn0WbvoAppFOMbA8,5270
31
- cloudnetpy/instruments/ceilo.py,sha256=-QZNgdTiFmz0G57CU_gZ1cQtYzppgkFJqjndfleefH0,8924
31
+ cloudnetpy/instruments/ceilo.py,sha256=vmKRY-NLB042BLpmJn4sfufEwK5xAI2vOXLRr7K85bU,8981
32
32
  cloudnetpy/instruments/ceilometer.py,sha256=-aPEZs_r0Gxeu53PHeWAkZMB2BUdauS47tkL7RFxo6k,12078
33
33
  cloudnetpy/instruments/cl61d.py,sha256=g6DNBFju3wYhLFl32DKmC8pUup7y-EupXoUU0fuoGGA,1990
34
34
  cloudnetpy/instruments/cloudnet_instrument.py,sha256=RG5HJxGM6p0F-IGyr85fvOizcMmgx48OeD_XeIsrgSU,3367
@@ -47,7 +47,7 @@ cloudnetpy/instruments/rpg.py,sha256=siPmiyOGdB_OtlnIiP0PAt_cySnped0clLLGnyzw02o
47
47
  cloudnetpy/instruments/rpg_reader.py,sha256=2eYu-tBd0QyreUKqJT726aIMbA29aIxXK-UJCkOXMLM,11356
48
48
  cloudnetpy/instruments/toa5.py,sha256=CfmmBMv5iMGaWHIGBK01Rw24cuXC1R1RMNTXkmsm340,1760
49
49
  cloudnetpy/instruments/vaisala.py,sha256=GGuA_v4S7kR9yApSr1-d0ETzNj4ehEZ7-pD1-AdPYRE,14662
50
- cloudnetpy/instruments/weather_station.py,sha256=1vRMErPWQET5IoiCRxd2-DUbFDP_RFGdfC91PaDf6So,15221
50
+ cloudnetpy/instruments/weather_station.py,sha256=SHuDUFNximD0vyuKTE1fHO8twjuIXElVsN32tt09CUo,17371
51
51
  cloudnetpy/instruments/disdrometer/__init__.py,sha256=lyjwttWvFvuwYxEkusoAvgRcbBmglmOp5HJOpXUqLWo,93
52
52
  cloudnetpy/instruments/disdrometer/common.py,sha256=g52iK2aNp3Z88kovUmGVpC54NZomPa9D871gzO0AmQ4,9267
53
53
  cloudnetpy/instruments/disdrometer/parsivel.py,sha256=HJZrEysQkx9MiIVPDV25CYHpXi_SjgZlgO-otoaKK34,25640
@@ -94,7 +94,7 @@ cloudnetpy/model_evaluation/tests/unit/test_statistical_methods.py,sha256=Ra3r4V
94
94
  cloudnetpy/model_evaluation/tests/unit/test_tools.py,sha256=Ia_VrLdV2NstX5gbx_3AZTOAlrgLAy_xFZ8fHYVX0xI,3817
95
95
  cloudnetpy/plotting/__init__.py,sha256=lg9Smn4BI0dVBgnDLC3JVJ4GmwoSnO-qoSd4ApvwV6Y,107
96
96
  cloudnetpy/plotting/plot_meta.py,sha256=JHrr-4A9fhqdi_tQFe6mR4Fdry3hkI-lmmVu5Ny2vco,15979
97
- cloudnetpy/plotting/plotting.py,sha256=uOYm_-3NVjVLQ8cRB6zjIW54FzuVHcVNOUR0LTLGgDc,32936
97
+ cloudnetpy/plotting/plotting.py,sha256=952e2NJXvJF7_VMzU5ZRQCeIWOUVgvi810hRzKb9cdE,33584
98
98
  cloudnetpy/products/__init__.py,sha256=2hRb5HG9hNrxH1if5laJkLeFeaZCd5W1q3hh4ewsX0E,273
99
99
  cloudnetpy/products/classification.py,sha256=bNG8W1CMgGoUBpXopQjYAW3F-uEJGyojXb4A5jmErHo,7921
100
100
  cloudnetpy/products/der.py,sha256=sITzQbvutU5u1D16hDG2Ke7XB9gBxSP22OLy2Yhi1zI,12446
@@ -108,8 +108,8 @@ cloudnetpy/products/mie_lu_tables.nc,sha256=It4fYpqJXlqOgL8jeZ-PxGzP08PMrELIDVe5
108
108
  cloudnetpy/products/mwr_tools.py,sha256=3esU5cG5GI2WVmOENqrJ0FbMuxLegADv7q8TB0RorGg,4674
109
109
  cloudnetpy/products/product_tools.py,sha256=VNw2diJj30POz68-3qNVkJP7r9AUspT_d1Fp0BbeIx8,10414
110
110
  docs/source/conf.py,sha256=IKiFWw6xhUd8NrCg0q7l596Ck1d61XWeVjIFHVSG9Og,1490
111
- cloudnetpy-1.61.16.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
112
- cloudnetpy-1.61.16.dist-info/METADATA,sha256=vwWCuMNtULjIu2yR0V3ql1dC6_ITghYCfEWyW1oDL0I,5785
113
- cloudnetpy-1.61.16.dist-info/WHEEL,sha256=Wyh-_nZ0DJYolHNn1_hMa4lM7uDedD_RGVwbmTjyItk,91
114
- cloudnetpy-1.61.16.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
115
- cloudnetpy-1.61.16.dist-info/RECORD,,
111
+ cloudnetpy-1.62.0.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
112
+ cloudnetpy-1.62.0.dist-info/METADATA,sha256=BIWI6z_98OC0XNiVwVJKjB2IZ_6za4MCkAN0eLeafyE,5784
113
+ cloudnetpy-1.62.0.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
114
+ cloudnetpy-1.62.0.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
115
+ cloudnetpy-1.62.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (71.1.0)
2
+ Generator: setuptools (72.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5