cloudnetpy 1.61.7__py3-none-any.whl → 1.61.9__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/categorize/categorize.py +31 -26
- cloudnetpy/instruments/ceilo.py +7 -6
- cloudnetpy/instruments/rpg.py +31 -24
- cloudnetpy/instruments/vaisala.py +4 -1
- cloudnetpy/instruments/weather_station.py +103 -5
- cloudnetpy/products/classification.py +32 -29
- cloudnetpy/products/der.py +11 -10
- cloudnetpy/products/ier.py +20 -17
- cloudnetpy/products/iwc.py +20 -16
- cloudnetpy/products/lwc.py +19 -18
- cloudnetpy/utils.py +24 -1
- cloudnetpy/version.py +1 -1
- {cloudnetpy-1.61.7.dist-info → cloudnetpy-1.61.9.dist-info}/METADATA +1 -1
- {cloudnetpy-1.61.7.dist-info → cloudnetpy-1.61.9.dist-info}/RECORD +17 -17
- {cloudnetpy-1.61.7.dist-info → cloudnetpy-1.61.9.dist-info}/LICENSE +0 -0
- {cloudnetpy-1.61.7.dist-info → cloudnetpy-1.61.9.dist-info}/WHEEL +0 -0
- {cloudnetpy-1.61.7.dist-info → cloudnetpy-1.61.9.dist-info}/top_level.txt +0 -0
@@ -289,33 +289,38 @@ COMMENTS = {
|
|
289
289
|
}
|
290
290
|
|
291
291
|
DEFINITIONS = {
|
292
|
-
"category_bits": (
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
292
|
+
"category_bits": utils.bit_field_definition(
|
293
|
+
{
|
294
|
+
0: """Small liquid droplets are present.""",
|
295
|
+
1: """Falling hydrometeors are present; if Bit 2 is set then these
|
296
|
+
are most likely ice particles, otherwise they are drizzle or
|
297
|
+
rain drops.""",
|
298
|
+
2: """Wet-bulb temperature is less than 0 degrees C, implying the
|
299
|
+
phase of Bit-1 particles.""",
|
300
|
+
3: """Melting ice particles are present.""",
|
301
|
+
4: """Aerosol particles are present and visible to the lidar.""",
|
302
|
+
5: """Insects are present and visible to the radar.""",
|
303
|
+
}
|
302
304
|
),
|
303
|
-
"quality_bits": (
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
305
|
+
"quality_bits": utils.bit_field_definition(
|
306
|
+
{
|
307
|
+
0: """An echo is detected by the radar.""",
|
308
|
+
1: """An echo is detected by the lidar.""",
|
309
|
+
2: """The apparent echo detected by the radar is ground clutter or
|
310
|
+
some other non-atmospheric artifact.""",
|
311
|
+
3: """The lidar echo is due to clear-air molecular scattering.""",
|
312
|
+
4: """Liquid water cloud, rainfall or melting ice below this pixel
|
313
|
+
will have caused radar and lidar attenuation; if bit 5 is set
|
314
|
+
then a correction for the radar attenuation has been
|
315
|
+
performed; otherwise do not trust the absolute values of
|
316
|
+
reflectivity factor. No correction is performed for lidar
|
317
|
+
attenuation.""",
|
318
|
+
5: """Radar reflectivity has been corrected for liquid-water
|
319
|
+
attenuation using the microwave radiometer measurements of
|
320
|
+
liquid water path and the lidar estimation of the location of
|
321
|
+
liquid water cloud; be aware that errors in reflectivity may
|
322
|
+
result.""",
|
323
|
+
}
|
319
324
|
),
|
320
325
|
}
|
321
326
|
|
cloudnetpy/instruments/ceilo.py
CHANGED
@@ -4,7 +4,7 @@ from itertools import islice
|
|
4
4
|
import netCDF4
|
5
5
|
from numpy import ma
|
6
6
|
|
7
|
-
from cloudnetpy import output
|
7
|
+
from cloudnetpy import output, utils
|
8
8
|
from cloudnetpy.instruments.campbell_scientific import Cs135
|
9
9
|
from cloudnetpy.instruments.cl61d import Cl61d
|
10
10
|
from cloudnetpy.instruments.lufft import LufftCeilo
|
@@ -225,11 +225,12 @@ ATTRIBUTES = {
|
|
225
225
|
"warning": MetaData(
|
226
226
|
long_name="Warning and Alarm flag",
|
227
227
|
units="1",
|
228
|
-
definition=(
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
228
|
+
definition=utils.status_field_definition(
|
229
|
+
{
|
230
|
+
"0": "Self-check OK",
|
231
|
+
"W": "At least one warning on",
|
232
|
+
"A": "At least one error active.",
|
233
|
+
}
|
233
234
|
),
|
234
235
|
),
|
235
236
|
"warning_flags": MetaData(
|
cloudnetpy/instruments/rpg.py
CHANGED
@@ -346,30 +346,37 @@ def _filter_zenith_angle(zenith: ma.MaskedArray) -> np.ndarray:
|
|
346
346
|
|
347
347
|
|
348
348
|
DEFINITIONS = {
|
349
|
-
"model_number":
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
349
|
+
"model_number": utils.status_field_definition(
|
350
|
+
{
|
351
|
+
0: "Single polarisation radar.",
|
352
|
+
1: "Dual polarisation radar.",
|
353
|
+
}
|
354
|
+
),
|
355
|
+
"dual_polarization": utils.status_field_definition(
|
356
|
+
{
|
357
|
+
0: """Single polarisation radar.""",
|
358
|
+
1: """Dual polarisation radar in linear depolarisation ratio (LDR)
|
359
|
+
mode.""",
|
360
|
+
2: """Dual polarisation radar in simultaneous transmission
|
361
|
+
simultaneous reception (STSR) mode.""",
|
362
|
+
}
|
363
|
+
),
|
364
|
+
"FFT_window": utils.status_field_definition(
|
365
|
+
{
|
366
|
+
0: "Square",
|
367
|
+
1: "Parzen",
|
368
|
+
2: "Blackman",
|
369
|
+
3: "Welch",
|
370
|
+
4: "Slepian2",
|
371
|
+
5: "Slepian3",
|
372
|
+
}
|
373
|
+
),
|
374
|
+
"quality_flag": utils.bit_field_definition(
|
375
|
+
{
|
376
|
+
0: "ADC saturation.",
|
377
|
+
1: "Spectral width too high.",
|
378
|
+
2: "No transmission power levelling.",
|
379
|
+
}
|
373
380
|
),
|
374
381
|
}
|
375
382
|
|
@@ -54,10 +54,13 @@ class VaisalaCeilo(Ceilometer):
|
|
54
54
|
def _read_backscatter(self, lines: list) -> np.ndarray:
|
55
55
|
"""Converts backscatter profile from 2-complement hex to floats."""
|
56
56
|
n_chars = self._hex_conversion_params[0]
|
57
|
-
n_gates =
|
57
|
+
n_gates = len(self.data["range"])
|
58
58
|
profiles = np.zeros((len(lines), n_gates), dtype=int)
|
59
59
|
ran = range(0, n_gates * n_chars, n_chars)
|
60
60
|
for ind, line in enumerate(lines):
|
61
|
+
if int(len(line) / n_chars) != n_gates:
|
62
|
+
logging.warning("Invalid line in raw ceilometer data")
|
63
|
+
continue
|
61
64
|
try:
|
62
65
|
profiles[ind, :] = [int(line[i : i + n_chars], 16) for i in ran]
|
63
66
|
except ValueError:
|
@@ -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,
|
90
|
+
def __init__(self, filenames: list[str], site_meta: dict):
|
85
91
|
super().__init__()
|
86
|
-
|
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,
|
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 =
|
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 / 1000 # mm/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",
|
@@ -2,7 +2,7 @@
|
|
2
2
|
import numpy as np
|
3
3
|
from numpy import ma
|
4
4
|
|
5
|
-
from cloudnetpy import output
|
5
|
+
from cloudnetpy import output, utils
|
6
6
|
from cloudnetpy.categorize import atmos
|
7
7
|
from cloudnetpy.datasource import DataSource
|
8
8
|
from cloudnetpy.metadata import MetaData
|
@@ -140,35 +140,38 @@ COMMENTS = {
|
|
140
140
|
}
|
141
141
|
|
142
142
|
DEFINITIONS = {
|
143
|
-
"target_classification": (
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
143
|
+
"target_classification": utils.status_field_definition(
|
144
|
+
{
|
145
|
+
0: "Clear sky.",
|
146
|
+
1: "Cloud liquid droplets only.",
|
147
|
+
2: "Drizzle or rain.",
|
148
|
+
3: "Drizzle or rain coexisting with cloud liquid droplets.",
|
149
|
+
4: "Ice particles.",
|
150
|
+
5: "Ice coexisting with supercooled liquid droplets.",
|
151
|
+
6: "Melting ice particles.",
|
152
|
+
7: "Melting ice particles coexisting with cloud liquid droplets.",
|
153
|
+
8: "Aerosol particles, no cloud or precipitation.",
|
154
|
+
9: "Insects, no cloud or precipitation.",
|
155
|
+
10: "Aerosol coexisting with insects, no cloud or precipitation.",
|
156
|
+
}
|
156
157
|
),
|
157
|
-
"detection_status": (
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
158
|
+
"detection_status": utils.status_field_definition(
|
159
|
+
{
|
160
|
+
0: """Clear sky.""",
|
161
|
+
1: """Lidar echo only.""",
|
162
|
+
2: """Radar echo but reflectivity may be unreliable as attenuation
|
163
|
+
by rain, melting ice or liquid cloud has not been
|
164
|
+
corrected.""",
|
165
|
+
3: """Good radar and lidar echos.""",
|
166
|
+
4: """No radar echo but rain or liquid cloud beneath mean that
|
167
|
+
attenuation that would be experienced is unknown.""",
|
168
|
+
5: """Good radar echo only.""",
|
169
|
+
6: """No radar echo but known attenuation.""",
|
170
|
+
7: """Radar echo corrected for liquid attenuation using microwave
|
171
|
+
radiometer data.""",
|
172
|
+
8: """Radar ground clutter.""",
|
173
|
+
9: """Lidar clear-air molecular scattering.""",
|
174
|
+
}
|
172
175
|
),
|
173
176
|
}
|
174
177
|
|
cloudnetpy/products/der.py
CHANGED
@@ -225,16 +225,17 @@ class DerSource(DataSource):
|
|
225
225
|
|
226
226
|
|
227
227
|
DEFINITIONS = {
|
228
|
-
"der_retrieval_status": (
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
228
|
+
"der_retrieval_status": utils.status_field_definition(
|
229
|
+
{
|
230
|
+
0: """No data: No cloud observed.""",
|
231
|
+
1: """Reliable retrieval.""",
|
232
|
+
2: """Mix of drops and ice: Droplets and ice crystals coexist within
|
233
|
+
pixel. Z may be biased by large crystals.""",
|
234
|
+
3: """Precipitation in profile: Drizzle and rain affects LWP
|
235
|
+
retrieval of MWR but also the target reflectivity.""",
|
236
|
+
4: """Surrounding ice: Less crucial! Ice crystals in the vicinity of
|
237
|
+
a droplet pixel may also bias its reflectivity.""",
|
238
|
+
}
|
238
239
|
),
|
239
240
|
}
|
240
241
|
|
cloudnetpy/products/ier.py
CHANGED
@@ -130,23 +130,26 @@ COMMENTS = {
|
|
130
130
|
}
|
131
131
|
|
132
132
|
DEFINITIONS = {
|
133
|
-
"ier_retrieval_status": (
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
133
|
+
"ier_retrieval_status": utils.status_field_definition(
|
134
|
+
{
|
135
|
+
0: """No ice present.""",
|
136
|
+
1: """Reliable retrieval.""",
|
137
|
+
2: """Unreliable retrieval due to uncorrected attenuation from
|
138
|
+
liquid water below the ice (no liquid water path measurement
|
139
|
+
available).""",
|
140
|
+
3: """Retrieval performed but radar corrected for liquid attenuation
|
141
|
+
using radiometer liquid water path which is not always
|
142
|
+
accurate.""",
|
143
|
+
4: """Ice detected only by the lidar.""",
|
144
|
+
5: """Ice detected by radar but rain below so no retrieval performed
|
145
|
+
due to very uncertain attenuation.""",
|
146
|
+
6: """Clear sky above rain wet-bulb temperature less than 0degC: if
|
147
|
+
rain attenuation were strong then ice could be present but
|
148
|
+
undetected.""",
|
149
|
+
7: """Drizzle or rain that would have been classified as ice if the
|
150
|
+
wet-bulb temperature were less than 0degC: may be ice if
|
151
|
+
temperature is in error.""",
|
152
|
+
}
|
150
153
|
),
|
151
154
|
}
|
152
155
|
|
cloudnetpy/products/iwc.py
CHANGED
@@ -170,22 +170,26 @@ COMMENTS = {
|
|
170
170
|
}
|
171
171
|
|
172
172
|
DEFINITIONS = {
|
173
|
-
"iwc_retrieval_status": (
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
173
|
+
"iwc_retrieval_status": utils.status_field_definition(
|
174
|
+
{
|
175
|
+
0: """No ice present.""",
|
176
|
+
1: """Reliable retrieval.""",
|
177
|
+
2: """Unreliable retrieval due to uncorrected attenuation from
|
178
|
+
liquid water below the ice (no liquid water path measurement
|
179
|
+
available).""",
|
180
|
+
3: """Retrieval performed but radar corrected for liquid attenuation
|
181
|
+
using radiometer liquid water path which is not always
|
182
|
+
accurate.""",
|
183
|
+
4: """Ice detected only by the lidar.""",
|
184
|
+
5: """Ice detected by radar but rain below so no retrieval performed
|
185
|
+
due to very uncertain attenuation.""",
|
186
|
+
6: """Clear sky above rain and wet-bulb temperature less than 0degC:
|
187
|
+
if rain attenuation is strong, ice could be present but
|
188
|
+
undetected.""",
|
189
|
+
7: """Drizzle or rain that would have been classified as ice if the
|
190
|
+
wet-bulb temperature were less than 0degC: may be ice if
|
191
|
+
temperature is in error.""",
|
192
|
+
}
|
189
193
|
),
|
190
194
|
}
|
191
195
|
|
cloudnetpy/products/lwc.py
CHANGED
@@ -414,24 +414,25 @@ COMMENTS = {
|
|
414
414
|
}
|
415
415
|
|
416
416
|
DEFINITIONS = {
|
417
|
-
"lwc_retrieval_status": (
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
417
|
+
"lwc_retrieval_status": utils.status_field_definition(
|
418
|
+
{
|
419
|
+
0: """No liquid water detected.""",
|
420
|
+
1: """Reliable retrieval.""",
|
421
|
+
2: """Adiabatic retrieval where cloud top has been adjusted to match
|
422
|
+
liquid water path from microwave radiometer because layer is
|
423
|
+
not detected by radar.""",
|
424
|
+
3: """Adiabatic retrieval: new cloud pixels where cloud top has been
|
425
|
+
adjusted to match liquid water path from microwave radiometer
|
426
|
+
because layer is not detected by radar.""",
|
427
|
+
4: """No retrieval: either no liquid water path is available or
|
428
|
+
liquid water path is uncertain.""",
|
429
|
+
5: """No retrieval: liquid water layer detected only by the lidar
|
430
|
+
and liquid water path is unavailable or uncertain: cloud top
|
431
|
+
may be higher than diagnosed cloud top since lidar signal has
|
432
|
+
been attenuated.""",
|
433
|
+
6: """Rain present: cloud extent is difficult to ascertain and
|
434
|
+
liquid water path also uncertain.""",
|
435
|
+
}
|
435
436
|
),
|
436
437
|
}
|
437
438
|
|
cloudnetpy/utils.py
CHANGED
@@ -3,11 +3,12 @@ import datetime
|
|
3
3
|
import logging
|
4
4
|
import os
|
5
5
|
import re
|
6
|
+
import textwrap
|
6
7
|
import uuid
|
7
8
|
import warnings
|
8
9
|
from collections.abc import Iterator
|
9
10
|
from datetime import timezone
|
10
|
-
from typing import Literal
|
11
|
+
from typing import Literal, TypeVar
|
11
12
|
|
12
13
|
import netCDF4
|
13
14
|
import numpy as np
|
@@ -995,3 +996,25 @@ def find_masked_profiles_indices(array: ma.MaskedArray) -> list:
|
|
995
996
|
non_masked_counts = np.ma.count(array, axis=1)
|
996
997
|
masked_profiles_indices = np.where(non_masked_counts == 0)[0]
|
997
998
|
return list(masked_profiles_indices)
|
999
|
+
|
1000
|
+
|
1001
|
+
T = TypeVar("T", int, str)
|
1002
|
+
|
1003
|
+
|
1004
|
+
def _format_definition(kind: str, definitions: dict[T, str]) -> str:
|
1005
|
+
lines = [""]
|
1006
|
+
for key, value in definitions.items():
|
1007
|
+
prefix = f"{kind} {key}: "
|
1008
|
+
indent = " " * len(prefix)
|
1009
|
+
text = " ".join(value.split())
|
1010
|
+
wrapped = textwrap.wrap(prefix + text, subsequent_indent=indent)
|
1011
|
+
lines.extend(wrapped)
|
1012
|
+
return "\n".join(lines)
|
1013
|
+
|
1014
|
+
|
1015
|
+
def status_field_definition(definitions: dict[T, str]) -> str:
|
1016
|
+
return _format_definition("Value", definitions)
|
1017
|
+
|
1018
|
+
|
1019
|
+
def bit_field_definition(definitions: dict[T, str]) -> str:
|
1020
|
+
return _format_definition("Bit", definitions)
|
cloudnetpy/version.py
CHANGED
@@ -7,12 +7,12 @@ 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
|
11
|
-
cloudnetpy/version.py,sha256=
|
10
|
+
cloudnetpy/utils.py,sha256=-8x7LQ6WDHxf2lDZfhG50WYe2iSVLQObnVXZG46JzKI,28468
|
11
|
+
cloudnetpy/version.py,sha256=jCiBK4tcMrQYXPVaBsEM_6y-SUJGsVWPfbgo81jYpFg,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
|
15
|
-
cloudnetpy/categorize/categorize.py,sha256=
|
15
|
+
cloudnetpy/categorize/categorize.py,sha256=cIuspjg76h99Czf0XICpjflZGzmfyUH8LYGGgnbaBM8,17826
|
16
16
|
cloudnetpy/categorize/classify.py,sha256=x7aqPfhw4xuER22sqOb9ES9nijwk1E8b7HF7uaFJD7k,9218
|
17
17
|
cloudnetpy/categorize/containers.py,sha256=j6oSKPeZcq9vFthYaocAw1m6yReRNNPYUQF5UTDq4YM,4232
|
18
18
|
cloudnetpy/categorize/disdrometer.py,sha256=daPB1JgERRqa0Ekxx_LYCP8mDe3XnUYj2VlsIKAB7sE,2003
|
@@ -28,7 +28,7 @@ cloudnetpy/categorize/radar.py,sha256=oaptBCymSPTa1HNYOWURnE0h5oklDOVxQvqAEAbqSQ
|
|
28
28
|
cloudnetpy/instruments/__init__.py,sha256=_jejVwi_viSZehmAOkEqTNI-0-exGgAJ_bHW1IRRwTI,398
|
29
29
|
cloudnetpy/instruments/basta.py,sha256=0zUztUJBXT2nrBTAl3-NLowxu_CYwTU5TgdBq4etj7E,3757
|
30
30
|
cloudnetpy/instruments/campbell_scientific.py,sha256=2WHfBKQjtRSl0AqvtPeX7G8Hdi3Dn0WbvoAppFOMbA8,5270
|
31
|
-
cloudnetpy/instruments/ceilo.py,sha256=
|
31
|
+
cloudnetpy/instruments/ceilo.py,sha256=ZDMrHHGYrboJMC2YMU9E1XsBAPI2eYoovrEsF1fUaRE,8922
|
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
|
@@ -43,11 +43,11 @@ cloudnetpy/instruments/nc_lidar.py,sha256=Q4sJJwiEPthDz0Zb-laISX32jNYzlUBMafxLJi
|
|
43
43
|
cloudnetpy/instruments/nc_radar.py,sha256=cDd20Yz-UeDQp1Gmkk-jGNZ45DangFsCaioeo_kktHQ,6935
|
44
44
|
cloudnetpy/instruments/pollyxt.py,sha256=SccV9htZ5MWrK7JEleOr4hbmeTr-lKktUzAt7H9Xkf8,8932
|
45
45
|
cloudnetpy/instruments/radiometrics.py,sha256=2ofeZ6KJ_JOWTd3UA-wSzJpM5cjN7R4jZeBLJCQKEYc,7624
|
46
|
-
cloudnetpy/instruments/rpg.py,sha256=
|
46
|
+
cloudnetpy/instruments/rpg.py,sha256=yQpcKcgzRvVvkl6NhKvo4PUkv9nZ69_hzzPpS2Ei-Is,17315
|
47
47
|
cloudnetpy/instruments/rpg_reader.py,sha256=LAdXL3TmD5QzQbqtPOcemZji_qkXwmw6a6F8NmF6Zg8,11355
|
48
48
|
cloudnetpy/instruments/toa5.py,sha256=1JnuYViD8c_tHJZ9lf4OU44iepEkXHsXOzDfVf_b0qc,1759
|
49
|
-
cloudnetpy/instruments/vaisala.py,sha256=
|
50
|
-
cloudnetpy/instruments/weather_station.py,sha256=
|
49
|
+
cloudnetpy/instruments/vaisala.py,sha256=ektdXoID2X_V9H5Zp1fgHTUBapFMSyPVEWW_aoR6DEY,14655
|
50
|
+
cloudnetpy/instruments/weather_station.py,sha256=76O3z1932Bidi_1tXHeVJmtoISOJFKB9vFVJnWJj4M0,13822
|
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
|
@@ -96,20 +96,20 @@ cloudnetpy/plotting/__init__.py,sha256=lg9Smn4BI0dVBgnDLC3JVJ4GmwoSnO-qoSd4ApvwV
|
|
96
96
|
cloudnetpy/plotting/plot_meta.py,sha256=cLdCZrhbP-gaobS_zjcf8d2xVALzl7zh2qpttxCHyrg,15983
|
97
97
|
cloudnetpy/plotting/plotting.py,sha256=bve91iM9RcWmKaZOFWxVh2y3DPmupI1944MMYDdv17I,32459
|
98
98
|
cloudnetpy/products/__init__.py,sha256=2hRb5HG9hNrxH1if5laJkLeFeaZCd5W1q3hh4ewsX0E,273
|
99
|
-
cloudnetpy/products/classification.py,sha256=
|
100
|
-
cloudnetpy/products/der.py,sha256=
|
99
|
+
cloudnetpy/products/classification.py,sha256=pzFQtgOKS7g_3LqiAY84EFUUste-VES7CJNgoq2Bs34,7914
|
100
|
+
cloudnetpy/products/der.py,sha256=XZMbqDQUq0E9iBU3Axr-NfUJfRAhjsaGlyxJ4tKyGcw,12444
|
101
101
|
cloudnetpy/products/drizzle.py,sha256=BY2HvJeWt_ps6KKCGXwUUNRTy78q0cQM8bOCCoj8TWA,10803
|
102
102
|
cloudnetpy/products/drizzle_error.py,sha256=4GwlHRtNbk9ks7bGtXCco-wXbcDOKeAQwKmbhzut6Qk,6132
|
103
103
|
cloudnetpy/products/drizzle_tools.py,sha256=UhcJbPa4tXHbuVlegIRfOl5nZ_E6ddKv20aghfP0hdg,10847
|
104
|
-
cloudnetpy/products/ier.py,sha256=
|
105
|
-
cloudnetpy/products/iwc.py,sha256=
|
106
|
-
cloudnetpy/products/lwc.py,sha256=
|
104
|
+
cloudnetpy/products/ier.py,sha256=ZwjyRwh7dJPjz9K5x1HiLFyD0BDNsFx-B7zBAds_ACs,7838
|
105
|
+
cloudnetpy/products/iwc.py,sha256=kdYvOy2-xwY1Qnx8qdyGAhAjMJowh23Iv1JNuNxXNLA,10206
|
106
|
+
cloudnetpy/products/lwc.py,sha256=FOc-dYKM_OTLN1PK9yfApKiKCJYSv82BLPwXJqO2Bqo,18815
|
107
107
|
cloudnetpy/products/mie_lu_tables.nc,sha256=It4fYpqJXlqOgL8jeZ-PxGzP08PMrELIDVe55y9ob58,16637951
|
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.
|
112
|
-
cloudnetpy-1.61.
|
113
|
-
cloudnetpy-1.61.
|
114
|
-
cloudnetpy-1.61.
|
115
|
-
cloudnetpy-1.61.
|
111
|
+
cloudnetpy-1.61.9.dist-info/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
|
112
|
+
cloudnetpy-1.61.9.dist-info/METADATA,sha256=-rbU3tJCXKfgY_2BcIqFh0Ryw9R7rHXhURpuPabJXLQ,5784
|
113
|
+
cloudnetpy-1.61.9.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
114
|
+
cloudnetpy-1.61.9.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
|
115
|
+
cloudnetpy-1.61.9.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|