cloudnetpy 1.61.5__py3-none-any.whl → 1.61.7__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.
@@ -65,6 +65,9 @@ def copernicus2nc(
65
65
  ) as temp_file:
66
66
  nc_filename = temp_file.name
67
67
  valid_filenames = utils.get_sorted_filenames(raw_files, ".nc")
68
+ valid_filenames = utils.get_files_with_variables(
69
+ valid_filenames, ["time", "ZED_HC"]
70
+ )
68
71
  valid_filenames = utils.get_files_with_common_range(valid_filenames)
69
72
  variables = list(keymap.keys())
70
73
  concat_lib.concatenate_files(
@@ -151,7 +151,8 @@ class Thies(Disdrometer):
151
151
  with open(filename) as file:
152
152
  first_line = file.readline()
153
153
  if "TOA5" in first_line:
154
- for row in read_toa5(filename):
154
+ units, process, rows = read_toa5(filename)
155
+ for row in rows:
155
156
  self._read_line(row["RawString"], row["TIMESTAMP"])
156
157
  else:
157
158
  with open(filename) as file:
@@ -64,6 +64,9 @@ def galileo2nc(
64
64
  ) as temp_file:
65
65
  nc_filename = temp_file.name
66
66
  valid_filenames = utils.get_sorted_filenames(raw_files, ".nc")
67
+ valid_filenames = utils.get_files_with_variables(
68
+ valid_filenames, ["time", "ZED_HC"]
69
+ )
67
70
  valid_filenames = utils.get_files_with_common_range(valid_filenames)
68
71
  variables = list(keymap.keys())
69
72
  concat_lib.concatenate_files(
@@ -4,7 +4,9 @@ from os import PathLike
4
4
  from typing import Any
5
5
 
6
6
 
7
- def read_toa5(filename: str | PathLike) -> list[dict[str, Any]]:
7
+ def read_toa5(
8
+ filename: str | PathLike,
9
+ ) -> tuple[dict[str, str], dict[str, str], list[dict[str, Any]]]:
8
10
  """Read ASCII data from Campbell Scientific datalogger such as CR1000.
9
11
 
10
12
  References
@@ -18,9 +20,11 @@ def read_toa5(filename: str | PathLike) -> list[dict[str, Any]]:
18
20
  msg = "Invalid TOA5 file"
19
21
  raise ValueError(msg)
20
22
  header_line = next(reader)
21
- _units_line = next(reader)
22
- _process_line = next(reader)
23
+ units_line = next(reader)
24
+ process_line = next(reader)
23
25
  output = []
26
+ units = dict(zip(header_line, units_line, strict=False))
27
+ process = dict(zip(header_line, process_line, strict=False))
24
28
 
25
29
  row_template: dict[str, Any] = {}
26
30
  for header in header_line:
@@ -42,4 +46,4 @@ def read_toa5(filename: str | PathLike) -> list[dict[str, Any]]:
42
46
  else:
43
47
  row[key] = parsed_value
44
48
  output.append(row)
45
- return output
49
+ return units, process, output
@@ -1,5 +1,6 @@
1
1
  import datetime
2
2
 
3
+ import numpy as np
3
4
  from numpy import ma
4
5
 
5
6
  from cloudnetpy import output
@@ -8,6 +9,7 @@ from cloudnetpy.cloudnetarray import CloudnetArray
8
9
  from cloudnetpy.exceptions import ValidTimeStampError, WeatherStationDataError
9
10
  from cloudnetpy.instruments import instruments
10
11
  from cloudnetpy.instruments.cloudnet_instrument import CloudnetInstrument
12
+ from cloudnetpy.instruments.toa5 import read_toa5
11
13
  from cloudnetpy.metadata import MetaData
12
14
  from cloudnetpy.utils import datetime2decimal_hours
13
15
 
@@ -19,7 +21,7 @@ def ws2nc(
19
21
  uuid: str | None = None,
20
22
  date: str | None = None,
21
23
  ) -> str:
22
- """Converts weather-station data into Cloudnet Level 1b netCDF file.
24
+ """Converts weather station data into Cloudnet Level 1b netCDF file.
23
25
 
24
26
  Args:
25
27
  weather_station_file: Filename of weather-station ASCII file.
@@ -37,7 +39,14 @@ def ws2nc(
37
39
  ValidTimeStampError: No valid timestamps found.
38
40
  """
39
41
  try:
40
- ws = WS(weather_station_file, site_meta)
42
+ ws: WS
43
+ if site_meta["name"] == "Palaiseau":
44
+ ws = PalaiseauWS(weather_station_file, site_meta)
45
+ elif site_meta["name"] == "Granada":
46
+ ws = GranadaWS(weather_station_file, site_meta)
47
+ else:
48
+ msg = "Unsupported site"
49
+ raise ValueError(msg) # noqa: TRY301
41
50
  if date is not None:
42
51
  ws.screen_timestamps(date)
43
52
  ws.convert_time()
@@ -53,11 +62,29 @@ def ws2nc(
53
62
 
54
63
 
55
64
  class WS(CloudnetInstrument):
65
+ date: list[str]
66
+
67
+ def convert_time(self) -> None:
68
+ pass
69
+
70
+ def screen_timestamps(self, date: str) -> None:
71
+ pass
72
+
73
+ def add_date(self) -> None:
74
+ pass
75
+
76
+ def add_data(self) -> None:
77
+ pass
78
+
79
+ def convert_units(self) -> None:
80
+ pass
81
+
82
+
83
+ class PalaiseauWS(WS):
56
84
  def __init__(self, filename: str, site_meta: dict):
57
85
  super().__init__()
58
86
  self.filename = filename
59
87
  self.site_meta = site_meta
60
- self.date: list[str] = []
61
88
  self.instrument = instruments.GENERIC_WEATHER_STATION
62
89
  self._data = self._read_data()
63
90
 
@@ -145,7 +172,94 @@ class WS(CloudnetInstrument):
145
172
  self.data["rainfall_rate"].data = rainfall_rate / 60 / 1000 # mm/min -> m/s
146
173
  self.data["rainfall_amount"].data = (
147
174
  self.data["rainfall_amount"][:] / 1000
148
- ) # m -> mm
175
+ ) # mm -> m
176
+
177
+
178
+ class GranadaWS(WS):
179
+ def __init__(self, filename: str, site_meta: dict):
180
+ super().__init__()
181
+ self.filename = filename
182
+ self.site_meta = site_meta
183
+ self.instrument = instruments.GENERIC_WEATHER_STATION
184
+ self._data = self._read_data()
185
+
186
+ def _read_data(self) -> dict:
187
+ keymap = {
188
+ "TIMESTAMP": "time",
189
+ "air_t_Avg": "air_temperature",
190
+ "rh_Avg": "relative_humidity",
191
+ "pressure_Avg": "air_pressure",
192
+ "wind_speed_avg": "wind_speed",
193
+ "wind_dir_avg": "wind_direction",
194
+ "rain_Tot": "rainfall_rate",
195
+ }
196
+ expected_units = {
197
+ "air_t_Avg": "degC",
198
+ "rh_Avg": "%",
199
+ "pressure_Avg": "hPa",
200
+ "wind_speed_avg": "m/s",
201
+ "wind_dir_avg": "Deg",
202
+ "rain_Tot": "mm",
203
+ }
204
+ units, process, rows = read_toa5(self.filename)
205
+ for key in units:
206
+ if key in expected_units and expected_units[key] != units[key]:
207
+ msg = (
208
+ f"Expected {key} to have units {expected_units[key]},"
209
+ f" got {units[key]} instead"
210
+ )
211
+ raise ValueError(msg)
212
+
213
+ data: dict[str, list] = {keymap[key]: [] for key in units if key in keymap}
214
+ for row in rows:
215
+ for key, value in row.items():
216
+ if key not in keymap:
217
+ continue
218
+ parsed = value
219
+ if keymap[key] != "time":
220
+ parsed = float(value)
221
+ data[keymap[key]].append(parsed)
222
+ return data
223
+
224
+ def convert_time(self) -> None:
225
+ pass
226
+
227
+ def screen_timestamps(self, date: str) -> None:
228
+ dates = [str(d.date()) for d in self._data["time"]]
229
+ valid_ind = [ind for ind, d in enumerate(dates) if d == date]
230
+ if not valid_ind:
231
+ raise ValidTimeStampError
232
+ for key in self._data:
233
+ self._data[key] = [
234
+ x for ind, x in enumerate(self._data[key]) if ind in valid_ind
235
+ ]
236
+
237
+ def add_date(self) -> None:
238
+ first_date = self._data["time"][0].date()
239
+ self.date = [
240
+ str(first_date.year),
241
+ str(first_date.month).zfill(2),
242
+ str(first_date.day).zfill(2),
243
+ ]
244
+
245
+ def add_data(self) -> None:
246
+ for key, value in self._data.items():
247
+ parsed = datetime2decimal_hours(value) if key == "time" else np.array(value)
248
+ self.data[key] = CloudnetArray(parsed, key)
249
+ self.data["rainfall_amount"] = CloudnetArray(
250
+ np.cumsum(self._data["rainfall_rate"]), "rainfall_amount"
251
+ )
252
+
253
+ def convert_units(self) -> None:
254
+ temperature_kelvins = atmos_utils.c2k(self.data["air_temperature"][:])
255
+ self.data["air_temperature"].data = temperature_kelvins
256
+ self.data["relative_humidity"].data = self.data["relative_humidity"][:] / 100
257
+ self.data["air_pressure"].data = self.data["air_pressure"][:] * 100 # hPa -> Pa
258
+ rainfall_rate = self.data["rainfall_rate"][:]
259
+ self.data["rainfall_rate"].data = rainfall_rate / 60 / 1000 # mm/min -> m/s
260
+ self.data["rainfall_amount"].data = (
261
+ self.data["rainfall_amount"][:] / 1000
262
+ ) # mm -> m
149
263
 
150
264
 
151
265
  ATTRIBUTES = {
@@ -130,3 +130,4 @@ class Mwr:
130
130
  )
131
131
  self.nc_l2.source_file_uuids = self.nc_l1c.file_uuid
132
132
  self.nc_l2.mwrpy_version = mwrpy_version
133
+ self.nc_l2.instrument_pid = self.nc_l1c.instrument_pid
cloudnetpy/utils.py CHANGED
@@ -972,6 +972,19 @@ def get_files_with_common_range(filenames: list) -> list:
972
972
  return [file for i, file in enumerate(filenames) if n_range[i] == most_common]
973
973
 
974
974
 
975
+ def get_files_with_variables(filenames: list, variables: list[str]) -> list:
976
+ """Returns files where all variables exist."""
977
+ valid_files = []
978
+ for file in filenames:
979
+ with netCDF4.Dataset(file) as nc:
980
+ for variable in variables:
981
+ if variable not in nc.variables:
982
+ break
983
+ else:
984
+ valid_files.append(file)
985
+ return valid_files
986
+
987
+
975
988
  def is_all_masked(array: np.ndarray) -> bool:
976
989
  """Tests if all values are masked."""
977
990
  return ma.isMaskedArray(array) and hasattr(array, "mask") and array.mask.all()
cloudnetpy/version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  MAJOR = 1
2
2
  MINOR = 61
3
- PATCH = 5
3
+ PATCH = 7
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.5
3
+ Version: 1.61.7
4
4
  Summary: Python package for Cloudnet processing
5
5
  Author: Simo Tukiainen
6
6
  License: MIT License
@@ -7,8 +7,8 @@ cloudnetpy/exceptions.py,sha256=wrI0bZTwmS5C_cqOmvlJ8XJSEFyzuD1eD4voGJc_Gjg,1584
7
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
- cloudnetpy/utils.py,sha256=0TlHm71YtSrKXBsRKctitnhQrvZPE-ulEVeAQW-oK58,27398
11
- cloudnetpy/version.py,sha256=wWSbFMdT54VpsZJNxnh15y-5LNveGZg6cV6si07cF60,72
10
+ cloudnetpy/utils.py,sha256=DuUbtGJJTwEwrDxx6aLkos4qL-Lrdchnzjx4gv5dZiU,27814
11
+ cloudnetpy/version.py,sha256=kTFWe1O0uMTPg6LtGazqdKp2KOo0w7j9KaWIUOArJx4,72
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
@@ -32,8 +32,8 @@ cloudnetpy/instruments/ceilo.py,sha256=56zhNSx5cyqV9ToeDUhXzwOUQK32oa9Vng2VsPXLs
32
32
  cloudnetpy/instruments/ceilometer.py,sha256=-aPEZs_r0Gxeu53PHeWAkZMB2BUdauS47tkL7RFxo6k,12078
33
33
  cloudnetpy/instruments/cl61d.py,sha256=ycJGvUqNU2KHhECbrSehtWRnvg1vKFHhvMeQpjpdCI4,1989
34
34
  cloudnetpy/instruments/cloudnet_instrument.py,sha256=RG5HJxGM6p0F-IGyr85fvOizcMmgx48OeD_XeIsrgSU,3367
35
- cloudnetpy/instruments/copernicus.py,sha256=AT0AtMhGSKzPWEqXsfAda6zeaw4g0Jr5dqIyfeu4FP0,6327
36
- cloudnetpy/instruments/galileo.py,sha256=FyFYh1JhWed1d6yvpsv3rdBzHUTjkfOPw5HEQz4Wssw,4545
35
+ cloudnetpy/instruments/copernicus.py,sha256=fpTulQ4IQQmr_u6ykBOTI4J_ZtfjyUhFUmM4qrwdl_Q,6467
36
+ cloudnetpy/instruments/galileo.py,sha256=r3ly7ZgzlRhCqqX-oKtkE6Ed8zJuHY9zPqNMSzW7X_A,4685
37
37
  cloudnetpy/instruments/hatpro.py,sha256=QD7Gn607Q2ASAlSJbY8Fu37TOOHTR1VrAPVfy8ehylg,8513
38
38
  cloudnetpy/instruments/instruments.py,sha256=jG5TYnZ8bdCZXnI303ZsaJBEdSKaIjKMbkGtnq6kQX0,3261
39
39
  cloudnetpy/instruments/lufft.py,sha256=nozeiMRMz7I6q_FwmlxDGhWeJlqTuNh6ru39-M4K3BI,3629
@@ -45,13 +45,13 @@ cloudnetpy/instruments/pollyxt.py,sha256=SccV9htZ5MWrK7JEleOr4hbmeTr-lKktUzAt7H9
45
45
  cloudnetpy/instruments/radiometrics.py,sha256=2ofeZ6KJ_JOWTd3UA-wSzJpM5cjN7R4jZeBLJCQKEYc,7624
46
46
  cloudnetpy/instruments/rpg.py,sha256=U8nEOlOI74f2lk2w4C4xKZCrW6AkDZpQZYE3yv7SNHE,17130
47
47
  cloudnetpy/instruments/rpg_reader.py,sha256=LAdXL3TmD5QzQbqtPOcemZji_qkXwmw6a6F8NmF6Zg8,11355
48
- cloudnetpy/instruments/toa5.py,sha256=xYJYEVNykCWqIsESno0eBIWqkYb-LHXjFjUp3EoqGDU,1565
48
+ cloudnetpy/instruments/toa5.py,sha256=1JnuYViD8c_tHJZ9lf4OU44iepEkXHsXOzDfVf_b0qc,1759
49
49
  cloudnetpy/instruments/vaisala.py,sha256=GzESZvboOoXzWmmr9dC-y6oM6ogc-M-zT3KmBTaD0LI,14512
50
- cloudnetpy/instruments/weather_station.py,sha256=gTY3Y5UATqJo9Gld4hm7WdsKBwcF8WgNTIK2nOfl3Nc,5739
50
+ cloudnetpy/instruments/weather_station.py,sha256=0ajUvZ4BQIr_HXwBgEek2rf3Oorqp4L-t95gIUVK9vM,9650
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
54
- cloudnetpy/instruments/disdrometer/thies.py,sha256=lNR5ahOKIsO_gcpmbYZwh2UP2aawpQ5J9RrrnPKFFIE,10046
54
+ cloudnetpy/instruments/disdrometer/thies.py,sha256=8V_1wx-8ncBJKs40e1_kvpOh3wj5UIl8YwvkVHf34MA,10086
55
55
  cloudnetpy/model_evaluation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
56
  cloudnetpy/model_evaluation/file_handler.py,sha256=oUGIblcEWLLv16YKUch-M5KA-dGRAcuHa-9anP3xtX4,6447
57
57
  cloudnetpy/model_evaluation/metadata.py,sha256=7ZL87iDbaQJIMu8wfnMvb01cGVPkl8RtvEm_tt9uIHE,8413
@@ -105,11 +105,11 @@ cloudnetpy/products/ier.py,sha256=IcGPlQahbwJjp3vOOrxWSYW2FPzbSV0KQL5eYECc4kU,77
105
105
  cloudnetpy/products/iwc.py,sha256=MUPuVKWgqOuuLRCGk3QY74uBZB_7P1qlinlP8nEvz9o,10124
106
106
  cloudnetpy/products/lwc.py,sha256=TbIR6kMwjbm63ed5orB1pkqx9ZBm8C5TF2JmT8WKdKI,18794
107
107
  cloudnetpy/products/mie_lu_tables.nc,sha256=It4fYpqJXlqOgL8jeZ-PxGzP08PMrELIDVe55y9ob58,16637951
108
- cloudnetpy/products/mwr_tools.py,sha256=PRm5aCULccUehU-Byk55wYhhEHseMjoAjGBu5TSyHao,4621
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.5.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
112
- cloudnetpy-1.61.5.dist-info/METADATA,sha256=yj6xgn1999ZwuQIreZDzaDsYYfX9lnjDmA0oTL93Y4M,5784
113
- cloudnetpy-1.61.5.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
114
- cloudnetpy-1.61.5.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
115
- cloudnetpy-1.61.5.dist-info/RECORD,,
111
+ cloudnetpy-1.61.7.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
112
+ cloudnetpy-1.61.7.dist-info/METADATA,sha256=FcyxQIL3n-BVcQGqXwfpT51ahxWRkCEfcuADunGOQ6U,5784
113
+ cloudnetpy-1.61.7.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
114
+ cloudnetpy-1.61.7.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
115
+ cloudnetpy-1.61.7.dist-info/RECORD,,