cloudnetpy 1.82.0__py3-none-any.whl → 1.82.2__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/falling.py +3 -2
- cloudnetpy/categorize/melting.py +3 -1
- cloudnetpy/categorize/model.py +1 -1
- cloudnetpy/instruments/pollyxt.py +2 -1
- cloudnetpy/instruments/rpg_reader.py +5 -0
- cloudnetpy/instruments/weather_station.py +12 -0
- cloudnetpy/output.py +38 -32
- cloudnetpy/products/epsilon.py +5 -0
- cloudnetpy/utils.py +1 -1
- cloudnetpy/version.py +1 -1
- {cloudnetpy-1.82.0.dist-info → cloudnetpy-1.82.2.dist-info}/METADATA +1 -1
- {cloudnetpy-1.82.0.dist-info → cloudnetpy-1.82.2.dist-info}/RECORD +16 -16
- {cloudnetpy-1.82.0.dist-info → cloudnetpy-1.82.2.dist-info}/WHEEL +0 -0
- {cloudnetpy-1.82.0.dist-info → cloudnetpy-1.82.2.dist-info}/entry_points.txt +0 -0
- {cloudnetpy-1.82.0.dist-info → cloudnetpy-1.82.2.dist-info}/licenses/LICENSE +0 -0
- {cloudnetpy-1.82.0.dist-info → cloudnetpy-1.82.2.dist-info}/top_level.txt +0 -0
cloudnetpy/categorize/falling.py
CHANGED
@@ -115,10 +115,11 @@ def _fix_liquid_dominated_radar(
|
|
115
115
|
|
116
116
|
|
117
117
|
def _is_z_missing_above_liquid(z: ma.MaskedArray, ind_top: int) -> bool:
|
118
|
-
"""Checks
|
118
|
+
"""Checks if z is masked right above the liquid layer top."""
|
119
119
|
if ind_top == len(z) - 1:
|
120
120
|
return False
|
121
|
-
|
121
|
+
mask = ma.getmaskarray(z)
|
122
|
+
return bool(mask[ind_top + 1])
|
122
123
|
|
123
124
|
|
124
125
|
def _is_z_increasing(z: ma.MaskedArray, ind_base: int, ind_top: int) -> bool:
|
cloudnetpy/categorize/melting.py
CHANGED
@@ -76,7 +76,9 @@ def find_melting_layer(obs: ClassData, *, smooth: bool = True) -> npt.NDArray:
|
|
76
76
|
ldr_prof = obs.ldr[ind, temp_indices]
|
77
77
|
ldr_dprof = ldr_diff[ind, temp_indices]
|
78
78
|
|
79
|
-
if
|
79
|
+
if (ldr_prof is not None and ma.count(ldr_prof) > 3) or (
|
80
|
+
v_prof is not None and ma.count(v_prof) > 3
|
81
|
+
):
|
80
82
|
try:
|
81
83
|
if ldr_prof is None or ldr_dprof is None:
|
82
84
|
msg = "ldr_prof or ldr_dprof is None"
|
cloudnetpy/categorize/model.py
CHANGED
@@ -182,4 +182,4 @@ def _find_model_type(file_name: str | PathLike) -> str:
|
|
182
182
|
def _find_number_of_valid_profiles(array: npt.NDArray) -> int:
|
183
183
|
mask = ma.getmaskarray(array)
|
184
184
|
all_masked_profiles = np.all(mask, axis=1)
|
185
|
-
return np.count_nonzero(~all_masked_profiles)
|
185
|
+
return int(np.count_nonzero(~all_masked_profiles))
|
@@ -246,7 +246,8 @@ def _read_array_from_file_pair(
|
|
246
246
|
|
247
247
|
|
248
248
|
def _only_zeros_or_masked(data: ma.MaskedArray) -> bool:
|
249
|
-
|
249
|
+
mask = ma.getmaskarray(data)
|
250
|
+
return ma.sum(data) == 0 or mask.all()
|
250
251
|
|
251
252
|
|
252
253
|
ATTRIBUTES = {
|
@@ -326,6 +326,11 @@ class HatproBinCombined:
|
|
326
326
|
else:
|
327
327
|
msg = "Only implemented up to 2 files"
|
328
328
|
raise NotImplementedError(msg)
|
329
|
+
|
330
|
+
if arr.dtype.fields is None:
|
331
|
+
msg = "Data has no fields"
|
332
|
+
raise ValueError(msg)
|
333
|
+
|
329
334
|
self.data = {field: arr[field] for field in arr.dtype.fields}
|
330
335
|
|
331
336
|
|
@@ -79,12 +79,14 @@ def ws2nc(
|
|
79
79
|
ws.add_date()
|
80
80
|
ws.add_site_geolocation()
|
81
81
|
ws.add_data()
|
82
|
+
ws.remove_duplicate_timestamps()
|
82
83
|
ws.convert_temperature_and_humidity()
|
83
84
|
ws.convert_pressure()
|
84
85
|
ws.convert_rainfall_rate()
|
85
86
|
ws.convert_rainfall_amount()
|
86
87
|
ws.normalize_cumulative_amount("rainfall_amount")
|
87
88
|
ws.calculate_rainfall_amount()
|
89
|
+
ws.wrap_wind_direction()
|
88
90
|
attributes = output.add_time_attribute({}, ws.date)
|
89
91
|
output.update_attributes(ws.data, attributes)
|
90
92
|
output.save_level1b(ws, output_file, uuid)
|
@@ -148,6 +150,16 @@ class WS(CSVFile):
|
|
148
150
|
def convert_rainfall_amount(self) -> None:
|
149
151
|
pass
|
150
152
|
|
153
|
+
def wrap_wind_direction(self) -> None:
|
154
|
+
if "wind_direction" not in self.data:
|
155
|
+
return
|
156
|
+
# Wrap values little outside of [0, 360), keep original values
|
157
|
+
# otherwise.
|
158
|
+
threshold = 2
|
159
|
+
values = self.data["wind_direction"].data
|
160
|
+
values[(values > -threshold) & (values < 0)] += 360
|
161
|
+
values[(values >= 360) & (values < 360 + threshold)] -= 360
|
162
|
+
|
151
163
|
|
152
164
|
class PalaiseauWS(WS):
|
153
165
|
def __init__(self, filenames: Sequence[str | PathLike], site_meta: dict) -> None:
|
cloudnetpy/output.py
CHANGED
@@ -197,46 +197,52 @@ def get_source_uuids(data: Observations | list[netCDF4.Dataset | DataSource]) ->
|
|
197
197
|
def merge_history(
|
198
198
|
nc: netCDF4.Dataset, file_type: str, data: Observations | DataSource
|
199
199
|
) -> None:
|
200
|
-
"""Merges history fields from one or several files and creates a new record.
|
200
|
+
"""Merges history fields from one or several files and creates a new record."""
|
201
|
+
|
202
|
+
def extract_history(obj: DataSource | Observations) -> list[str]:
|
203
|
+
if hasattr(obj, "dataset") and hasattr(obj.dataset, "history"):
|
204
|
+
history = obj.dataset.history
|
205
|
+
if isinstance(obj, Model):
|
206
|
+
return [history.split("\n")[-1]]
|
207
|
+
return history.split("\n")
|
208
|
+
return []
|
209
|
+
|
210
|
+
histories: list[str] = []
|
211
|
+
if isinstance(data, DataSource):
|
212
|
+
histories.extend(extract_history(data))
|
213
|
+
elif isinstance(data, Observations):
|
214
|
+
for field in fields(data):
|
215
|
+
histories.extend(extract_history(getattr(data, field.name)))
|
201
216
|
|
202
|
-
|
203
|
-
|
204
|
-
file_type: Long description of the file.
|
205
|
-
data: Dictionary of objects with history attribute.
|
217
|
+
# Remove duplicates
|
218
|
+
histories = list(dict.fromkeys(histories))
|
206
219
|
|
207
|
-
|
220
|
+
def parse_time(line: str) -> datetime.datetime:
|
221
|
+
try:
|
222
|
+
return datetime.datetime.strptime(
|
223
|
+
line.split(" - ")[0].strip(), "%Y-%m-%d %H:%M:%S %z"
|
224
|
+
)
|
225
|
+
except ValueError:
|
226
|
+
return datetime.datetime.min.replace(
|
227
|
+
tzinfo=datetime.timezone.utc
|
228
|
+
) # malformed lines to bottom
|
229
|
+
|
230
|
+
histories.sort(key=parse_time, reverse=True)
|
208
231
|
new_record = f"{utils.get_time()} - {file_type} file created"
|
209
|
-
|
210
|
-
if (
|
211
|
-
isinstance(data, DataSource)
|
212
|
-
and hasattr(data, "dataset")
|
213
|
-
and hasattr(data.dataset, "history")
|
214
|
-
):
|
215
|
-
history = data.dataset.history
|
216
|
-
histories.append(history)
|
217
|
-
if isinstance(data, Observations):
|
218
|
-
for field in fields(data):
|
219
|
-
obj = getattr(data, field.name)
|
220
|
-
if hasattr(obj, "dataset") and hasattr(obj.dataset, "history"):
|
221
|
-
history = obj.dataset.history
|
222
|
-
history = history.split("\n")[-1] if isinstance(obj, Model) else history
|
223
|
-
histories.append(history)
|
224
|
-
histories.sort(reverse=True)
|
225
|
-
old_history = [f"\n{history}" for history in histories]
|
226
|
-
old_history_str = "".join(old_history)
|
227
|
-
nc.history = f"{new_record}{old_history_str}"
|
232
|
+
nc.history = new_record + "".join(f"\n{h}" for h in histories)
|
228
233
|
|
229
234
|
|
230
235
|
def add_source_instruments(nc: netCDF4.Dataset, data: Observations) -> None:
|
231
236
|
"""Adds source attribute to categorize file."""
|
232
|
-
sources =
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
+
sources = {
|
238
|
+
src
|
239
|
+
for field in fields(data)
|
240
|
+
for obj in [getattr(data, field.name)]
|
241
|
+
if hasattr(obj, "source")
|
242
|
+
for src in obj.source.split("\n")
|
243
|
+
}
|
237
244
|
if sources:
|
238
|
-
|
239
|
-
nc.source = "".join(formatted_sources)
|
245
|
+
nc.source = "\n".join(sorted(sources))
|
240
246
|
|
241
247
|
|
242
248
|
def init_file(
|
cloudnetpy/products/epsilon.py
CHANGED
@@ -137,6 +137,11 @@ def _horizontal_wind_from_doppler_lidar_file(
|
|
137
137
|
raise ValidTimeStampError
|
138
138
|
t = np.broadcast_to(time[:, None], mask.shape)[~mask]
|
139
139
|
h = np.broadcast_to(height[None, :], mask.shape)[~mask]
|
140
|
+
|
141
|
+
if len(np.unique(t)) < 2 or len(np.unique(h)) < 2:
|
142
|
+
msg = "Not enough unique values for interpolation"
|
143
|
+
raise ValidTimeStampError(msg)
|
144
|
+
|
140
145
|
interp_linear = LinearNDInterpolator(list(zip(t, h, strict=False)), V[~mask])
|
141
146
|
interp_nearest = NearestNDInterpolator(list(zip(t, h, strict=False)), V[~mask])
|
142
147
|
T, H = np.meshgrid(time, height, indexing="ij")
|
cloudnetpy/utils.py
CHANGED
@@ -371,7 +371,7 @@ def interpolate_2d_mask(
|
|
371
371
|
method="linear",
|
372
372
|
)
|
373
373
|
# Preserve mask:
|
374
|
-
mask_fun = RectBivariateSpline(x, y, z
|
374
|
+
mask_fun = RectBivariateSpline(x, y, ma.getmaskarray(z), kx=1, ky=1)
|
375
375
|
mask = mask_fun(x_new, y_new)
|
376
376
|
mask[mask < 0.5] = 0
|
377
377
|
masked_array = ma.array(data, mask=mask.astype(bool))
|
cloudnetpy/version.py
CHANGED
@@ -6,10 +6,10 @@ cloudnetpy/constants.py,sha256=YnoSzZm35NDooJfhlulSJBc7g0eSchT3yGytRaTaJEI,845
|
|
6
6
|
cloudnetpy/datasource.py,sha256=HzvqTTHLCH9GniUsV_IWwyrvvONnFJh0tmBM61hsqxM,6364
|
7
7
|
cloudnetpy/exceptions.py,sha256=ZB3aUwjVRznR0CcZ5sZHrB0yz13URDf52Ksv7G7C7EA,1817
|
8
8
|
cloudnetpy/metadata.py,sha256=CFpXmdEkVPzvLPv2xHIR-aMMQ-TR26KfESYw-98j7sk,7213
|
9
|
-
cloudnetpy/output.py,sha256=
|
9
|
+
cloudnetpy/output.py,sha256=0bybnILsgKHWIuw2GYkqTz2iMCJDZLUN25IQ9o_v3Cg,14968
|
10
10
|
cloudnetpy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
-
cloudnetpy/utils.py,sha256=
|
12
|
-
cloudnetpy/version.py,sha256=
|
11
|
+
cloudnetpy/utils.py,sha256=Qv60_vxknB3f2S3EFtyoD2CBY3N6mgDRObNp2u1oYUc,31806
|
12
|
+
cloudnetpy/version.py,sha256=HExRLf0gQwTK6vqJy9VmjjceEsHaQ0syCstlLDhyoVE,72
|
13
13
|
cloudnetpy/categorize/__init__.py,sha256=gtvzWr0IDRn2oA6yHBvinEhTGTuub-JkrOv93lBsgrE,61
|
14
14
|
cloudnetpy/categorize/atmos_utils.py,sha256=uWc9TABVYPI0sn4H5Az9Jf6NVRaWyEKIi17f0pAJQxE,10679
|
15
15
|
cloudnetpy/categorize/attenuation.py,sha256=Y_-fzmQTltWTqIZTulJhovC7a6ifpMcaAazDJcnMIOc,990
|
@@ -18,13 +18,13 @@ cloudnetpy/categorize/classify.py,sha256=skA9K6Bxh9mFZ_fM4d78zt09BPDzfHLttXle6mF
|
|
18
18
|
cloudnetpy/categorize/containers.py,sha256=PIJwgQos3CxF9BG4hBNLTaZq252FTH0kdgagT31mFmc,5517
|
19
19
|
cloudnetpy/categorize/disdrometer.py,sha256=XNL8kDtBAB12UmgRZ4ayxuVs8e3ghyLO0Hy5XpRgfMU,1966
|
20
20
|
cloudnetpy/categorize/droplet.py,sha256=wnMN9rHNSMZLXNXuYEd-RAS_8eAIIo2vkE7pp3DSTKs,8725
|
21
|
-
cloudnetpy/categorize/falling.py,sha256=
|
21
|
+
cloudnetpy/categorize/falling.py,sha256=ZscFGFPFz_Nlc7PwjVwFDSVOzMGOuCDVAFj7pLvYctQ,4475
|
22
22
|
cloudnetpy/categorize/freezing.py,sha256=gigqpb4qfeQSlKXkrPUwCbMnMsxl74thJWSRW2iHJOg,3796
|
23
23
|
cloudnetpy/categorize/insects.py,sha256=bAqm4kFRtU16RPttsRLedofPd-yfbALNqz26jKlMNUE,5357
|
24
24
|
cloudnetpy/categorize/itu.py,sha256=ffXK27guyRS4d66VWQ2h4UEGjUIhGjPKbFmj7kh698c,10304
|
25
25
|
cloudnetpy/categorize/lidar.py,sha256=CQsDEeQYiuQCfCmJQWrqQvCfmciN1NPZ6uRdt89CZLY,2685
|
26
|
-
cloudnetpy/categorize/melting.py,sha256=
|
27
|
-
cloudnetpy/categorize/model.py,sha256=
|
26
|
+
cloudnetpy/categorize/melting.py,sha256=vhc6zq3L4gp7oEPHMnQlT2m6YBE5-CS5gdTNA7gVHRg,6329
|
27
|
+
cloudnetpy/categorize/model.py,sha256=iFakrXy3npbg4qUrpUGEBEdwBnmlWsMgogPCtfWl7sw,6805
|
28
28
|
cloudnetpy/categorize/mwr.py,sha256=kzSivQuKrsqmFInDLlSM1R2wAG5j-tQebOi_1IwUW_I,1690
|
29
29
|
cloudnetpy/categorize/radar.py,sha256=2mTDa9BLxQeaORm-YPQ1lJyjAKew6NYzjtUvjpIvBYU,16044
|
30
30
|
cloudnetpy/categorize/attenuations/__init__.py,sha256=kIyQEZ6VVO6jJOAndrt7jNU15pm0Cavh5GnDjFmIG1M,1040
|
@@ -49,14 +49,14 @@ cloudnetpy/instruments/mira.py,sha256=XqmbytpeCJ2-hNugxdsXSBUDB8SAUc97_6lo5mHFG8
|
|
49
49
|
cloudnetpy/instruments/mrr.py,sha256=z50VYLOBW2o7enU7FHZYNFQRW2goEQpeGe7-iCBRQtg,6020
|
50
50
|
cloudnetpy/instruments/nc_lidar.py,sha256=PtZauDdI3bX3bv4gIVvV6N53e2Co-ehBL_tByHM9hj8,1713
|
51
51
|
cloudnetpy/instruments/nc_radar.py,sha256=NKsy0mF2Tdem0lNIYgd3Kbe2dOE-38t4f_rosdhBcy8,7368
|
52
|
-
cloudnetpy/instruments/pollyxt.py,sha256=
|
52
|
+
cloudnetpy/instruments/pollyxt.py,sha256=IFq_RJrhgJ79OVyuo48PwYQK_zZ6VZFB_S5bEirRyzs,10566
|
53
53
|
cloudnetpy/instruments/radiometrics.py,sha256=QKfnrZlQ0sFcFjmv1ShnCMTJQv64w4akjK-JAIY4gCg,16116
|
54
54
|
cloudnetpy/instruments/rain_e_h3.py,sha256=fjv3SgeUNx9GisYqLrBnX9AjnO17VtouyoPh12VE9uo,5465
|
55
55
|
cloudnetpy/instruments/rpg.py,sha256=R1rUdeSADvB1IMkGOF1S0rUEJDGEI_19SPrmErZpn5M,18825
|
56
|
-
cloudnetpy/instruments/rpg_reader.py,sha256=
|
56
|
+
cloudnetpy/instruments/rpg_reader.py,sha256=NaOtTxXx20PozNTj1xNvmbsEsAxuplFXRzBiM1_-Zg4,11651
|
57
57
|
cloudnetpy/instruments/toa5.py,sha256=CfmmBMv5iMGaWHIGBK01Rw24cuXC1R1RMNTXkmsm340,1760
|
58
58
|
cloudnetpy/instruments/vaisala.py,sha256=tu7aljkMKep0uCWz-Sd-GuBXF_Yy421a4nHy0ffpMoc,4725
|
59
|
-
cloudnetpy/instruments/weather_station.py,sha256=
|
59
|
+
cloudnetpy/instruments/weather_station.py,sha256=FuaGILEkd4MxXMpLrNGXNUjuuTkMIBf-J7y9oepIsdM,27586
|
60
60
|
cloudnetpy/instruments/disdrometer/__init__.py,sha256=lyjwttWvFvuwYxEkusoAvgRcbBmglmOp5HJOpXUqLWo,93
|
61
61
|
cloudnetpy/instruments/disdrometer/common.py,sha256=WCPRCfAlElUzZpllOSjjWrLG2jgkiRIy0rWz_omFoJQ,10815
|
62
62
|
cloudnetpy/instruments/disdrometer/parsivel.py,sha256=1HIA52f1nGOvSd4SSTr2y3-JT3eKZWwdbMnIMRVvQ_U,25811
|
@@ -110,17 +110,17 @@ cloudnetpy/products/der.py,sha256=UXdAxmmwChVVWSI4QSGAXphfMnbymGRTtGdKWEvh-J4,13
|
|
110
110
|
cloudnetpy/products/drizzle.py,sha256=0h1N_WVjC2GgIkAN-4ydOwl7WJn3psxeqmPHfX8WHhQ,11935
|
111
111
|
cloudnetpy/products/drizzle_error.py,sha256=QN98Io9UsBoEYxKBqfwoS88OGBiK5U5RYnVQjyTWHCI,6220
|
112
112
|
cloudnetpy/products/drizzle_tools.py,sha256=xYMB8Qxp-_wKzMv_XC6u6iMfRnEhEtmDpCHSQAbDToo,11201
|
113
|
-
cloudnetpy/products/epsilon.py,sha256=
|
113
|
+
cloudnetpy/products/epsilon.py,sha256=a796W_OuHxbSiG7yL2pcTArnaMNwYG8eAA8sQ93dIrY,7930
|
114
114
|
cloudnetpy/products/ier.py,sha256=Eb5AK-6l5mN_7vWP1cxcXQzj886zAwDDsHXueUju0N0,6262
|
115
115
|
cloudnetpy/products/iwc.py,sha256=pXl0xOFDD6AzGaAp_GzD2yapjOc7hXKTno9Q5G6HCOo,9826
|
116
116
|
cloudnetpy/products/lwc.py,sha256=xsNiiG6dGKIkWaFk0xWTabc1bZ4ULf6SqcqHs7itAUk,19339
|
117
117
|
cloudnetpy/products/mie_lu_tables.nc,sha256=It4fYpqJXlqOgL8jeZ-PxGzP08PMrELIDVe55y9ob58,16637951
|
118
118
|
cloudnetpy/products/mwr_tools.py,sha256=MMWnp68U7bv157-CPB2VeTQvaR6zl7sexbBT_kJ_pn8,6734
|
119
119
|
cloudnetpy/products/product_tools.py,sha256=eyqIw_0KhlpmmYQE69RpGdRIAOW7JVPlEgkTBp2kdps,11302
|
120
|
-
cloudnetpy-1.82.
|
120
|
+
cloudnetpy-1.82.2.dist-info/licenses/LICENSE,sha256=wcZF72bdaoG9XugpyE95Juo7lBQOwLuTKBOhhtANZMM,1094
|
121
121
|
docs/source/conf.py,sha256=IKiFWw6xhUd8NrCg0q7l596Ck1d61XWeVjIFHVSG9Og,1490
|
122
|
-
cloudnetpy-1.82.
|
123
|
-
cloudnetpy-1.82.
|
124
|
-
cloudnetpy-1.82.
|
125
|
-
cloudnetpy-1.82.
|
126
|
-
cloudnetpy-1.82.
|
122
|
+
cloudnetpy-1.82.2.dist-info/METADATA,sha256=HJ72wMIwKafUkOwTO_Mt8kM3k-9mHdtFzjFMgeWm9vg,5836
|
123
|
+
cloudnetpy-1.82.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
124
|
+
cloudnetpy-1.82.2.dist-info/entry_points.txt,sha256=HhY7LwCFk4qFgDlXx_Fy983ZTd831WlhtdPIzV-Y3dY,51
|
125
|
+
cloudnetpy-1.82.2.dist-info/top_level.txt,sha256=ibSPWRr6ojS1i11rtBFz2_gkIe68mggj7aeswYfaOo0,16
|
126
|
+
cloudnetpy-1.82.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|