cloudnetpy 1.61.5__py3-none-any.whl → 1.61.6__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.
- cloudnetpy/instruments/disdrometer/thies.py +2 -1
- cloudnetpy/instruments/toa5.py +8 -4
- cloudnetpy/instruments/weather_station.py +118 -4
- cloudnetpy/products/mwr_tools.py +1 -0
- cloudnetpy/version.py +1 -1
- {cloudnetpy-1.61.5.dist-info → cloudnetpy-1.61.6.dist-info}/METADATA +1 -1
- {cloudnetpy-1.61.5.dist-info → cloudnetpy-1.61.6.dist-info}/RECORD +10 -10
- {cloudnetpy-1.61.5.dist-info → cloudnetpy-1.61.6.dist-info}/LICENSE +0 -0
- {cloudnetpy-1.61.5.dist-info → cloudnetpy-1.61.6.dist-info}/WHEEL +0 -0
- {cloudnetpy-1.61.5.dist-info → cloudnetpy-1.61.6.dist-info}/top_level.txt +0 -0
@@ -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
|
-
|
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:
|
cloudnetpy/instruments/toa5.py
CHANGED
@@ -4,7 +4,9 @@ from os import PathLike
|
|
4
4
|
from typing import Any
|
5
5
|
|
6
6
|
|
7
|
-
def read_toa5(
|
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
|
-
|
22
|
-
|
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
|
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
|
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
|
-
) #
|
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 = {
|
cloudnetpy/products/mwr_tools.py
CHANGED
cloudnetpy/version.py
CHANGED
@@ -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=0TlHm71YtSrKXBsRKctitnhQrvZPE-ulEVeAQW-oK58,27398
|
11
|
-
cloudnetpy/version.py,sha256=
|
11
|
+
cloudnetpy/version.py,sha256=EsOOkDhmmN4gMdVOGcAyBa9FEtyJpkC4FPZ6YnoxVgA,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
|
@@ -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=
|
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=
|
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=
|
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=
|
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.
|
112
|
-
cloudnetpy-1.61.
|
113
|
-
cloudnetpy-1.61.
|
114
|
-
cloudnetpy-1.61.
|
115
|
-
cloudnetpy-1.61.
|
111
|
+
cloudnetpy-1.61.6.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
|
112
|
+
cloudnetpy-1.61.6.dist-info/METADATA,sha256=0LfEZSv2s7N1rWbBerVyMlCKhFGupgn6KqjvJvZxDa4,5784
|
113
|
+
cloudnetpy-1.61.6.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
114
|
+
cloudnetpy-1.61.6.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
|
115
|
+
cloudnetpy-1.61.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|