cloudnetpy 1.61.8__py3-none-any.whl → 1.61.10__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.
@@ -1,4 +1,6 @@
1
+ import csv
1
2
  import datetime
3
+ import math
2
4
 
3
5
  import numpy as np
4
6
  from numpy import ma
@@ -15,7 +17,7 @@ from cloudnetpy.utils import datetime2decimal_hours
15
17
 
16
18
 
17
19
  def ws2nc(
18
- weather_station_file: str,
20
+ weather_station_file: str | list[str],
19
21
  output_file: str,
20
22
  site_meta: dict,
21
23
  uuid: str | None = None,
@@ -38,12 +40,16 @@ def ws2nc(
38
40
  WeatherStationDataError : Unable to read the file.
39
41
  ValidTimeStampError: No valid timestamps found.
40
42
  """
43
+ if not isinstance(weather_station_file, list):
44
+ weather_station_file = [weather_station_file]
41
45
  try:
42
46
  ws: WS
43
47
  if site_meta["name"] == "Palaiseau":
44
48
  ws = PalaiseauWS(weather_station_file, site_meta)
45
49
  elif site_meta["name"] == "Granada":
46
50
  ws = GranadaWS(weather_station_file, site_meta)
51
+ elif site_meta["name"] == "Kenttärova":
52
+ ws = KenttarovaWS(weather_station_file, site_meta)
47
53
  else:
48
54
  msg = "Unsupported site"
49
55
  raise ValueError(msg) # noqa: TRY301
@@ -81,9 +87,11 @@ class WS(CloudnetInstrument):
81
87
 
82
88
 
83
89
  class PalaiseauWS(WS):
84
- def __init__(self, filename: str, site_meta: dict):
90
+ def __init__(self, filenames: list[str], site_meta: dict):
85
91
  super().__init__()
86
- self.filename = filename
92
+ if len(filenames) != 1:
93
+ raise ValueError
94
+ self.filename = filenames[0]
87
95
  self.site_meta = site_meta
88
96
  self.instrument = instruments.GENERIC_WEATHER_STATION
89
97
  self._data = self._read_data()
@@ -176,9 +184,11 @@ class PalaiseauWS(WS):
176
184
 
177
185
 
178
186
  class GranadaWS(WS):
179
- def __init__(self, filename: str, site_meta: dict):
187
+ def __init__(self, filenames: list[str], site_meta: dict):
188
+ if len(filenames) != 1:
189
+ raise ValueError
180
190
  super().__init__()
181
- self.filename = filename
191
+ self.filename = filenames[0]
182
192
  self.site_meta = site_meta
183
193
  self.instrument = instruments.GENERIC_WEATHER_STATION
184
194
  self._data = self._read_data()
@@ -262,6 +272,94 @@ class GranadaWS(WS):
262
272
  ) # mm -> m
263
273
 
264
274
 
275
+ class KenttarovaWS(WS):
276
+ def __init__(self, filenames: list[str], site_meta: dict):
277
+ super().__init__()
278
+ self.filenames = filenames
279
+ self.site_meta = site_meta
280
+ self.instrument = instruments.GENERIC_WEATHER_STATION
281
+ self._data = self._read_data()
282
+
283
+ def _read_data(self) -> dict:
284
+ merged: dict = {}
285
+ for filename in self.filenames:
286
+ with open(filename, newline="") as f:
287
+ reader = csv.DictReader(f)
288
+ raw_data: dict = {key: [] for key in reader.fieldnames} # type: ignore[union-attr]
289
+ for row in reader:
290
+ for key, value in row.items():
291
+ parsed_value: float | datetime.datetime
292
+ if key == "Read time (UTC+2)":
293
+ parsed_value = datetime.datetime.strptime(
294
+ value, "%Y-%m-%d %H:%M:%S"
295
+ ) - datetime.timedelta(hours=2)
296
+ else:
297
+ try:
298
+ parsed_value = float(value)
299
+ except ValueError:
300
+ parsed_value = math.nan
301
+ raw_data[key].append(parsed_value)
302
+ data = {
303
+ "time": raw_data["Read time (UTC+2)"],
304
+ "air_temperature": raw_data["Temp 2m (C)"],
305
+ "relative_humidity": raw_data["Humidity 2m (%)"],
306
+ "air_pressure": raw_data["Pressure (hPa)"],
307
+ "wind_speed": raw_data["Wind speed (m/s)"],
308
+ "wind_direction": raw_data["Wind dir (deg)"],
309
+ "rainfall_rate": raw_data["Precipitation (?)"],
310
+ }
311
+ if merged:
312
+ merged = {key: [*merged[key], *data[key]] for key in merged}
313
+ else:
314
+ merged = data
315
+ for key, value in merged.items():
316
+ new_value = np.array(value)
317
+ if key != "time":
318
+ new_value = ma.masked_where(np.isnan(new_value), new_value)
319
+ merged[key] = new_value
320
+ return merged
321
+
322
+ def convert_time(self) -> None:
323
+ pass
324
+
325
+ def screen_timestamps(self, date: str) -> None:
326
+ dates = [str(d.date()) for d in self._data["time"]]
327
+ valid_ind = [ind for ind, d in enumerate(dates) if d == date]
328
+ if not valid_ind:
329
+ raise ValidTimeStampError
330
+ for key in self._data:
331
+ self._data[key] = [
332
+ x for ind, x in enumerate(self._data[key]) if ind in valid_ind
333
+ ]
334
+
335
+ def add_date(self) -> None:
336
+ first_date = self._data["time"][0].date()
337
+ self.date = [
338
+ str(first_date.year),
339
+ str(first_date.month).zfill(2),
340
+ str(first_date.day).zfill(2),
341
+ ]
342
+
343
+ def add_data(self) -> None:
344
+ for key, value in self._data.items():
345
+ parsed = datetime2decimal_hours(value) if key == "time" else ma.array(value)
346
+ self.data[key] = CloudnetArray(parsed, key)
347
+ self.data["rainfall_amount"] = CloudnetArray(
348
+ ma.cumsum(self._data["rainfall_rate"]), "rainfall_amount"
349
+ )
350
+
351
+ def convert_units(self) -> None:
352
+ temperature_kelvins = atmos_utils.c2k(self.data["air_temperature"][:])
353
+ self.data["air_temperature"].data = temperature_kelvins
354
+ self.data["relative_humidity"].data = self.data["relative_humidity"][:] / 100
355
+ self.data["air_pressure"].data = self.data["air_pressure"][:] * 100 # hPa -> Pa
356
+ rainfall_rate = self.data["rainfall_rate"][:]
357
+ self.data["rainfall_rate"].data = rainfall_rate / 60 # m/min -> m/s
358
+ self.data["rainfall_amount"].data = (
359
+ self.data["rainfall_amount"][:] / 1000
360
+ ) # mm -> m
361
+
362
+
265
363
  ATTRIBUTES = {
266
364
  "rainfall_amount": MetaData(
267
365
  long_name="Rainfall amount",
cloudnetpy/version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  MAJOR = 1
2
2
  MINOR = 61
3
- PATCH = 8
3
+ PATCH = 10
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.8
3
+ Version: 1.61.10
4
4
  Summary: Python package for Cloudnet processing
5
5
  Author: Simo Tukiainen
6
6
  License: MIT License
@@ -8,7 +8,7 @@ cloudnetpy/metadata.py,sha256=v_VDo2vbdTxB0zIsfP69IcrwSKiRlLpsGdq6JPI4CoA,5306
8
8
  cloudnetpy/output.py,sha256=WoVTNuxni0DUr163vZ-_mDr1brXhY15XSlGMrq9Aoqw,14700
9
9
  cloudnetpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  cloudnetpy/utils.py,sha256=-8x7LQ6WDHxf2lDZfhG50WYe2iSVLQObnVXZG46JzKI,28468
11
- cloudnetpy/version.py,sha256=7PDpEj1po5am9CIPhurRGIIOfqJgJsvFLhUmiGRQQk4,72
11
+ cloudnetpy/version.py,sha256=HAuT8BUx0-n1kWtWI9imBuHkv_T3xyyM0iVTBUyqsk8,73
12
12
  cloudnetpy/categorize/__init__.py,sha256=gP5q3Vis1y9u9OWgA_idlbjfWXYN_S0IBSWdwBhL_uU,69
13
13
  cloudnetpy/categorize/atmos.py,sha256=fWW8ye_8HZASRAiYwURFKWzcGOYIA2RKeVxCq0lVOuM,12389
14
14
  cloudnetpy/categorize/atmos_utils.py,sha256=wndpwJxc2-QnNTkV8tc8I11Vs_WkNz9sVMX1fuGgUC4,3777
@@ -47,7 +47,7 @@ cloudnetpy/instruments/rpg.py,sha256=yQpcKcgzRvVvkl6NhKvo4PUkv9nZ69_hzzPpS2Ei-Is
47
47
  cloudnetpy/instruments/rpg_reader.py,sha256=LAdXL3TmD5QzQbqtPOcemZji_qkXwmw6a6F8NmF6Zg8,11355
48
48
  cloudnetpy/instruments/toa5.py,sha256=1JnuYViD8c_tHJZ9lf4OU44iepEkXHsXOzDfVf_b0qc,1759
49
49
  cloudnetpy/instruments/vaisala.py,sha256=ektdXoID2X_V9H5Zp1fgHTUBapFMSyPVEWW_aoR6DEY,14655
50
- cloudnetpy/instruments/weather_station.py,sha256=0ajUvZ4BQIr_HXwBgEek2rf3Oorqp4L-t95gIUVK9vM,9650
50
+ cloudnetpy/instruments/weather_station.py,sha256=seJi1nMsxfRr_TG0We8zjtiSVR7c6sHT90YRLnaFhSo,13814
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=WiL-vCjw9Gmb5irvW3AXddsyprp8MGOfqcVAlfy0zpc,25521
@@ -108,8 +108,8 @@ cloudnetpy/products/mie_lu_tables.nc,sha256=It4fYpqJXlqOgL8jeZ-PxGzP08PMrELIDVe5
108
108
  cloudnetpy/products/mwr_tools.py,sha256=RuzokxxqXlTGk7XAOrif_FDPUJdf0j_wJgNq-7a_nK8,4684
109
109
  cloudnetpy/products/product_tools.py,sha256=rhx_Ru9FLlQqCNM-awoiHx18-Aq1eBwL9LiUaQoJs6k,10412
110
110
  docs/source/conf.py,sha256=IKiFWw6xhUd8NrCg0q7l596Ck1d61XWeVjIFHVSG9Og,1490
111
- cloudnetpy-1.61.8.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
112
- cloudnetpy-1.61.8.dist-info/METADATA,sha256=H7pCr4-e4fCepPEWkWBVLPvmGhmDMIxmelMF85JABLA,5784
113
- cloudnetpy-1.61.8.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
114
- cloudnetpy-1.61.8.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
115
- cloudnetpy-1.61.8.dist-info/RECORD,,
111
+ cloudnetpy-1.61.10.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
112
+ cloudnetpy-1.61.10.dist-info/METADATA,sha256=1t87X79T8kRt6_5_bFL0E6-aYOfa64mHdAUH78qaQag,5785
113
+ cloudnetpy-1.61.10.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
114
+ cloudnetpy-1.61.10.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
115
+ cloudnetpy-1.61.10.dist-info/RECORD,,