cloudnetpy 1.75.1__py3-none-any.whl → 1.76.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.
@@ -6,6 +6,7 @@ import logging
6
6
  import os
7
7
  import re
8
8
  from operator import attrgetter
9
+ from pathlib import Path
9
10
  from typing import Any, NamedTuple
10
11
 
11
12
  import numpy as np
@@ -18,8 +19,8 @@ from cloudnetpy.metadata import MetaData
18
19
 
19
20
 
20
21
  def radiometrics2nc(
21
- full_path: str,
22
- output_file: str,
22
+ full_path: str | os.PathLike,
23
+ output_file: str | os.PathLike,
23
24
  site_meta: dict,
24
25
  uuid: str | None = None,
25
26
  date: str | datetime.date | None = None,
@@ -46,19 +47,11 @@ def radiometrics2nc(
46
47
  """
47
48
  if isinstance(date, str):
48
49
  date = datetime.date.fromisoformat(date)
49
-
50
50
  if os.path.isdir(full_path):
51
- valid_filenames = utils.get_sorted_filenames(full_path, ".csv")
51
+ valid_filenames = list(Path(full_path).iterdir())
52
52
  else:
53
- valid_filenames = [full_path]
54
-
55
- objs = []
56
- for filename in valid_filenames:
57
- obj = Radiometrics(filename)
58
- obj.read_raw_data()
59
- obj.read_data()
60
- objs.append(obj)
61
-
53
+ valid_filenames = [Path(full_path)]
54
+ objs = [_read_file(filename) for filename in valid_filenames]
62
55
  radiometrics = RadiometricsCombined(objs, site_meta)
63
56
  radiometrics.screen_time(date)
64
57
  radiometrics.sort_timestamps()
@@ -81,15 +74,16 @@ class Record(NamedTuple):
81
74
  values: dict[str, Any]
82
75
 
83
76
 
84
- class Radiometrics:
85
- """Reader for level 2 files of Radiometrics microwave radiometers.
77
+ class RadiometricsMP:
78
+ """Reader for level 2 files (*.csv) from Radiometrics MP-3000A and similar
79
+ microwave radiometers.
86
80
 
87
81
  References:
88
82
  Radiometrics (2008). Profiler Operator's Manual: MP-3000A, MP-2500A,
89
83
  MP-1500A, MP-183A.
90
84
  """
91
85
 
92
- def __init__(self, filename: str):
86
+ def __init__(self, filename: Path):
93
87
  self.filename = filename
94
88
  self.raw_data: list[Record] = []
95
89
  self.data: dict = {}
@@ -152,12 +146,19 @@ class Radiometrics:
152
146
  ahs = []
153
147
  ah_times = []
154
148
  block_titles = {}
149
+ skip_procs = set()
155
150
  for record in self.raw_data:
156
151
  if record.block_type == 100:
157
152
  block_type = int(record.values["Record Type"]) - 1
158
153
  title = record.values["Title"]
159
154
  block_titles[block_type] = title
160
155
  if title := block_titles.get(record.block_type + record.block_index):
156
+ # "LV2 Processor" values "Zenith" and "0.00:90.00" should be OK
157
+ # but "Angle20(N)" and similar should be skipped.
158
+ proc = record.values["LV2 Processor"]
159
+ if proc.startswith("Angle"):
160
+ skip_procs.add(proc)
161
+ continue
161
162
  if title == "Temperature (K)":
162
163
  temp_times.append(record.timestamp)
163
164
  temps.append(
@@ -194,6 +195,9 @@ class Radiometrics:
194
195
  irt = record.values["Tir(K)"]
195
196
  irts.append([float(irt)])
196
197
  elif record.block_type == 300:
198
+ if skip_procs:
199
+ skip_procs.pop()
200
+ continue
197
201
  lwp = record.values["Int. Liquid(mm)"]
198
202
  iwv = record.values["Int. Vapor(cm)"]
199
203
  times.append(record.timestamp)
@@ -224,13 +228,69 @@ class Radiometrics:
224
228
  )
225
229
 
226
230
 
231
+ class RadiometricsWVR:
232
+ """Reader for *.los files from Radiometrics WVR-1100 microwave
233
+ radiometer.
234
+ """
235
+
236
+ def __init__(self, filename: Path):
237
+ self.filename = filename
238
+ self.raw_data: dict = {}
239
+ self.data: dict = {}
240
+ self.instrument = instruments.RADIOMETRICS
241
+ self.ranges: list[str] = []
242
+
243
+ def read_raw_data(self) -> None:
244
+ with open(self.filename, encoding="utf8") as infile:
245
+ for line in infile:
246
+ columns = line.split()
247
+ if columns[:2] == ["date", "time"]:
248
+ headers = columns
249
+ break
250
+ else:
251
+ msg = "No headers found"
252
+ raise RuntimeError(msg)
253
+ for key in headers:
254
+ self.raw_data[key] = []
255
+ for line in infile:
256
+ for key, value in zip(headers, line.split(), strict=False):
257
+ parsed_value: Any
258
+ if key == "date":
259
+ month, day, year = map(int, value.split("/"))
260
+ if year < 100:
261
+ year += 2000
262
+ parsed_value = datetime.date(year, month, day)
263
+ elif key == "time":
264
+ hour, minute, second = map(int, value.split(":"))
265
+ parsed_value = datetime.time(hour, minute, second)
266
+ else:
267
+ parsed_value = float(value)
268
+ self.raw_data[key].append(parsed_value)
269
+
270
+ def read_data(self) -> None:
271
+ self.data["time"] = np.array(
272
+ [
273
+ datetime.datetime.combine(date, time)
274
+ for date, time in zip(
275
+ self.raw_data["date"], self.raw_data["time"], strict=False
276
+ )
277
+ ],
278
+ dtype="datetime64[s]",
279
+ )
280
+ self.data["lwp"] = np.array(self.raw_data["LiqCM"]) * 10 # cm => kg m-2
281
+ self.data["iwv"] = np.array(self.raw_data["VapCM"]) * 10 # cm => kg m-2
282
+ is_zenith = np.abs(np.array(self.raw_data["ELact"]) - 90.0) < 1.0
283
+ for key in self.data:
284
+ self.data[key] = self.data[key][is_zenith]
285
+
286
+
227
287
  class RadiometricsCombined:
228
288
  site_meta: dict
229
289
  data: dict
230
290
  date: datetime.date | None
231
291
  instrument: instruments.Instrument
232
292
 
233
- def __init__(self, objs: list[Radiometrics], site_meta: dict):
293
+ def __init__(self, objs: list[RadiometricsMP | RadiometricsWVR], site_meta: dict):
234
294
  self.site_meta = site_meta
235
295
  self.data = {}
236
296
  self.date = None
@@ -240,9 +300,10 @@ class RadiometricsCombined:
240
300
  raise InconsistentDataError(msg)
241
301
  for key in obj.data:
242
302
  self.data = utils.append_data(self.data, key, obj.data[key])
243
- ranges = [float(x) for x in objs[0].ranges]
244
- self.data["range"] = np.array(ranges) * 1000 # m => km
245
- self.data["height"] = self.data["range"] + self.site_meta["altitude"]
303
+ if objs[0].ranges:
304
+ ranges = [float(x) for x in objs[0].ranges]
305
+ self.data["range"] = np.array(ranges) * 1000 # m => km
306
+ self.data["height"] = self.data["range"] + self.site_meta["altitude"]
246
307
  self.instrument = instruments.RADIOMETRICS
247
308
 
248
309
  def screen_time(self, expected_date: datetime.date | None) -> None:
@@ -289,6 +350,19 @@ class RadiometricsCombined:
289
350
  self.data[name] = CloudnetArray(float(value), key)
290
351
 
291
352
 
353
+ def _read_file(filename: Path) -> RadiometricsMP | RadiometricsWVR:
354
+ with open(filename) as f:
355
+ first_line = f.readline()
356
+ obj = (
357
+ RadiometricsWVR(filename)
358
+ if "RETRIEVAL COEFFICIENTS" in first_line
359
+ else RadiometricsMP(filename)
360
+ )
361
+ obj.read_raw_data()
362
+ obj.read_data()
363
+ return obj
364
+
365
+
292
366
  def _parse_datetime(text: str) -> datetime.datetime:
293
367
  date, time = text.split()
294
368
  month, day, year = map(int, date.split("/"))
cloudnetpy/version.py CHANGED
@@ -1,4 +1,4 @@
1
1
  MAJOR = 1
2
- MINOR = 75
3
- PATCH = 1
2
+ MINOR = 76
3
+ PATCH = 0
4
4
  __version__ = f"{MAJOR}.{MINOR}.{PATCH}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cloudnetpy
3
- Version: 1.75.1
3
+ Version: 1.76.0
4
4
  Summary: Python package for Cloudnet processing
5
5
  Author: Simo Tukiainen
6
6
  License: MIT License
@@ -9,7 +9,7 @@ cloudnetpy/metadata.py,sha256=lO7BCbVAzFoH3Nq-VuezYX0f7MnbG1Zp11g5GSiuQwM,6189
9
9
  cloudnetpy/output.py,sha256=l0LoOhcGCBrg2EJ4NT1xZ7-UKWdV7X7yQ0fJmhkwJVc,15829
10
10
  cloudnetpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  cloudnetpy/utils.py,sha256=SSZWk82c4nkAiTcLdOKGVvxt5ovETdAMn_TLxVeYpBY,33473
12
- cloudnetpy/version.py,sha256=tMFs6L1VKyLJzgBVt3-cNkUiP4Yz4Ld_Cu8UkEL_7U4,72
12
+ cloudnetpy/version.py,sha256=vHHlyjedUCAOK4akFXnc6Xk6GWb-5gg4KD85wtoljYA,72
13
13
  cloudnetpy/categorize/__init__.py,sha256=s-SJaysvVpVVo5kidiruWQO6p3gv2TXwY1wEHYO5D6I,44
14
14
  cloudnetpy/categorize/atmos_utils.py,sha256=RcmbKxm2COkE7WEya0mK3yX5rzUbrewRVh3ekm01RtM,10598
15
15
  cloudnetpy/categorize/attenuation.py,sha256=Y_-fzmQTltWTqIZTulJhovC7a6ifpMcaAazDJcnMIOc,990
@@ -49,7 +49,7 @@ cloudnetpy/instruments/mrr.py,sha256=eeAzCp3CiHGauywjwvMUAFwZ4vBOZMcd3IlF8KsrLQo
49
49
  cloudnetpy/instruments/nc_lidar.py,sha256=5gQG9PApnNPrHmS9_zanl8HEYIQuGRpbnzC3wfTcOyQ,1705
50
50
  cloudnetpy/instruments/nc_radar.py,sha256=HlaZeH5939R86ukF8K-P4Kfzb5-CpLB15LU2u94C5eI,7330
51
51
  cloudnetpy/instruments/pollyxt.py,sha256=U3g-ttmcs02LuLwVOydP3GjeNcmDyoYQroB-leIGdHY,10060
52
- cloudnetpy/instruments/radiometrics.py,sha256=ySG4a042XkgjMTG8d20oAPNvFvw9bMwwiqS3zv-JF_w,11825
52
+ cloudnetpy/instruments/radiometrics.py,sha256=__H7KFBcXT0FP8lis3P25MuMa5-S8GrTzKtazMKO678,14822
53
53
  cloudnetpy/instruments/rain_e_h3.py,sha256=9TdpP4UzMBNIt2iE2GL6K9dFldzTHPLOrU8Q3tcosCU,5317
54
54
  cloudnetpy/instruments/rpg.py,sha256=m3-xLJ-w2T7Ip7jBveWsGrts4tmNvdc-Lb4HebvHQjQ,17319
55
55
  cloudnetpy/instruments/rpg_reader.py,sha256=ThztFuVrWxhmWVAfZTfQDeUiKK1XMTbtv08IBe8GK98,11364
@@ -116,10 +116,10 @@ cloudnetpy/products/lwc.py,sha256=sl6Al2tuH3KkCBrPbWTmuz3jlD5UQJ4D6qBsn1tt2CQ,18
116
116
  cloudnetpy/products/mie_lu_tables.nc,sha256=It4fYpqJXlqOgL8jeZ-PxGzP08PMrELIDVe55y9ob58,16637951
117
117
  cloudnetpy/products/mwr_tools.py,sha256=8HPZpQMTojKZP1JS1S83IE0sxmbDE9bxlaWoqmGnUZE,6199
118
118
  cloudnetpy/products/product_tools.py,sha256=uu4l6reuGbPcW3TgttbaSrqIKbyYGhBVTdnC7opKvmg,11101
119
- cloudnetpy-1.75.1.dist-info/licenses/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
119
+ cloudnetpy-1.76.0.dist-info/licenses/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
120
120
  docs/source/conf.py,sha256=IKiFWw6xhUd8NrCg0q7l596Ck1d61XWeVjIFHVSG9Og,1490
121
- cloudnetpy-1.75.1.dist-info/METADATA,sha256=E9p1xLzJFAIa_z2emwjuzlVEHuWB1YiYuY1z9pn1ZR4,5796
122
- cloudnetpy-1.75.1.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
123
- cloudnetpy-1.75.1.dist-info/entry_points.txt,sha256=HhY7LwCFk4qFgDlXx_Fy983ZTd831WlhtdPIzV-Y3dY,51
124
- cloudnetpy-1.75.1.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
125
- cloudnetpy-1.75.1.dist-info/RECORD,,
121
+ cloudnetpy-1.76.0.dist-info/METADATA,sha256=sefr7DyZ7S4JNZcT_TvGj_b9hxEZOXJp887yXKW0tkg,5796
122
+ cloudnetpy-1.76.0.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
123
+ cloudnetpy-1.76.0.dist-info/entry_points.txt,sha256=HhY7LwCFk4qFgDlXx_Fy983ZTd831WlhtdPIzV-Y3dY,51
124
+ cloudnetpy-1.76.0.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
125
+ cloudnetpy-1.76.0.dist-info/RECORD,,