disdrodb 0.1.2__py3-none-any.whl → 0.1.3__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.
- disdrodb/__init__.py +64 -34
- disdrodb/_config.py +5 -4
- disdrodb/_version.py +16 -3
- disdrodb/accessor/__init__.py +20 -0
- disdrodb/accessor/methods.py +125 -0
- disdrodb/api/checks.py +139 -9
- disdrodb/api/configs.py +4 -2
- disdrodb/api/info.py +10 -10
- disdrodb/api/io.py +237 -18
- disdrodb/api/path.py +81 -75
- disdrodb/api/search.py +6 -6
- disdrodb/cli/disdrodb_create_summary_station.py +91 -0
- disdrodb/cli/disdrodb_run_l0.py +1 -1
- disdrodb/cli/disdrodb_run_l0_station.py +1 -1
- disdrodb/cli/disdrodb_run_l0b.py +1 -1
- disdrodb/cli/disdrodb_run_l0b_station.py +1 -1
- disdrodb/cli/disdrodb_run_l0c.py +1 -1
- disdrodb/cli/disdrodb_run_l0c_station.py +1 -1
- disdrodb/cli/disdrodb_run_l2e_station.py +1 -1
- disdrodb/configs.py +149 -4
- disdrodb/constants.py +61 -0
- disdrodb/data_transfer/download_data.py +5 -5
- disdrodb/etc/configs/attributes.yaml +339 -0
- disdrodb/etc/configs/encodings.yaml +473 -0
- disdrodb/etc/products/L1/global.yaml +13 -0
- disdrodb/etc/products/L2E/10MIN.yaml +12 -0
- disdrodb/etc/products/L2E/1MIN.yaml +1 -0
- disdrodb/etc/products/L2E/global.yaml +22 -0
- disdrodb/etc/products/L2M/10MIN.yaml +12 -0
- disdrodb/etc/products/L2M/GAMMA_ML.yaml +8 -0
- disdrodb/etc/products/L2M/NGAMMA_GS_LOG_ND_MAE.yaml +6 -0
- disdrodb/etc/products/L2M/NGAMMA_GS_ND_MAE.yaml +6 -0
- disdrodb/etc/products/L2M/NGAMMA_GS_Z_MAE.yaml +6 -0
- disdrodb/etc/products/L2M/global.yaml +26 -0
- disdrodb/l0/__init__.py +13 -0
- disdrodb/l0/configs/LPM/l0b_cf_attrs.yml +4 -4
- disdrodb/l0/configs/PARSIVEL/l0b_cf_attrs.yml +1 -1
- disdrodb/l0/configs/PARSIVEL/l0b_encodings.yml +3 -3
- disdrodb/l0/configs/PARSIVEL/raw_data_format.yml +1 -1
- disdrodb/l0/configs/PARSIVEL2/l0b_cf_attrs.yml +5 -5
- disdrodb/l0/configs/PARSIVEL2/l0b_encodings.yml +3 -3
- disdrodb/l0/configs/PARSIVEL2/raw_data_format.yml +1 -1
- disdrodb/l0/configs/PWS100/l0b_cf_attrs.yml +4 -4
- disdrodb/l0/configs/PWS100/raw_data_format.yml +1 -1
- disdrodb/l0/l0a_processing.py +30 -30
- disdrodb/l0/l0b_nc_processing.py +108 -2
- disdrodb/l0/l0b_processing.py +4 -4
- disdrodb/l0/l0c_processing.py +5 -13
- disdrodb/l0/readers/LPM/NETHERLANDS/DELFT_LPM_NC.py +66 -0
- disdrodb/l0/readers/LPM/SLOVENIA/{CRNI_VRH.py → UL.py} +3 -0
- disdrodb/l0/readers/LPM/SWITZERLAND/INNERERIZ_LPM.py +195 -0
- disdrodb/l0/readers/PARSIVEL/GPM/PIERS.py +0 -2
- disdrodb/l0/readers/PARSIVEL/JAPAN/JMA.py +4 -1
- disdrodb/l0/readers/PARSIVEL/NCAR/PECAN_MOBILE.py +1 -1
- disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2009.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/BELGIUM/ILVO.py +168 -0
- disdrodb/l0/readers/PARSIVEL2/DENMARK/DTU.py +165 -0
- disdrodb/l0/readers/PARSIVEL2/FINLAND/FMI_PARSIVEL2.py +69 -0
- disdrodb/l0/readers/PARSIVEL2/FRANCE/ENPC_PARSIVEL2.py +255 -134
- disdrodb/l0/readers/PARSIVEL2/FRANCE/OSUG.py +525 -0
- disdrodb/l0/readers/PARSIVEL2/FRANCE/SIRTA_PARSIVEL2.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/GPM/GCPEX.py +9 -7
- disdrodb/l0/readers/PARSIVEL2/KIT/BURKINA_FASO.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/KIT/TEAMX.py +123 -0
- disdrodb/l0/readers/PARSIVEL2/NASA/APU.py +120 -0
- disdrodb/l0/readers/PARSIVEL2/NCAR/FARM_PARSIVEL2.py +1 -0
- disdrodb/l0/readers/PARSIVEL2/NCAR/PECAN_FP3.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/NCAR/PERILS_MIPS.py +126 -0
- disdrodb/l0/readers/PARSIVEL2/NCAR/PERILS_PIPS.py +165 -0
- disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_P2.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_PIPS.py +20 -12
- disdrodb/l0/readers/PARSIVEL2/NETHERLANDS/DELFT_NC.py +2 -0
- disdrodb/l0/readers/PARSIVEL2/SPAIN/CENER.py +144 -0
- disdrodb/l0/readers/PARSIVEL2/SPAIN/CR1000DL.py +201 -0
- disdrodb/l0/readers/PARSIVEL2/SPAIN/LIAISE.py +137 -0
- disdrodb/l0/readers/PARSIVEL2/{NETHERLANDS/DELFT.py → USA/C3WE.py} +65 -85
- disdrodb/l0/readers/PWS100/FRANCE/ENPC_PWS100.py +105 -99
- disdrodb/l0/readers/PWS100/FRANCE/ENPC_PWS100_SIRTA.py +151 -0
- disdrodb/l0/routines.py +105 -14
- disdrodb/l1/__init__.py +5 -0
- disdrodb/l1/filters.py +34 -20
- disdrodb/l1/processing.py +45 -44
- disdrodb/l1/resampling.py +77 -66
- disdrodb/l1/routines.py +35 -43
- disdrodb/l1_env/routines.py +18 -3
- disdrodb/l2/__init__.py +7 -0
- disdrodb/l2/empirical_dsd.py +58 -10
- disdrodb/l2/event.py +27 -120
- disdrodb/l2/processing.py +267 -116
- disdrodb/l2/routines.py +618 -254
- disdrodb/metadata/standards.py +3 -1
- disdrodb/psd/fitting.py +463 -144
- disdrodb/psd/models.py +8 -5
- disdrodb/routines.py +3 -3
- disdrodb/scattering/__init__.py +16 -4
- disdrodb/scattering/axis_ratio.py +56 -36
- disdrodb/scattering/permittivity.py +486 -0
- disdrodb/scattering/routines.py +701 -159
- disdrodb/summary/__init__.py +17 -0
- disdrodb/summary/routines.py +4120 -0
- disdrodb/utils/attrs.py +68 -125
- disdrodb/utils/compression.py +30 -1
- disdrodb/utils/dask.py +59 -8
- disdrodb/utils/dataframe.py +61 -7
- disdrodb/utils/directories.py +35 -15
- disdrodb/utils/encoding.py +33 -19
- disdrodb/utils/logger.py +13 -6
- disdrodb/utils/manipulations.py +71 -0
- disdrodb/utils/subsetting.py +214 -0
- disdrodb/utils/time.py +165 -19
- disdrodb/utils/writer.py +20 -7
- disdrodb/utils/xarray.py +2 -4
- disdrodb/viz/__init__.py +13 -0
- disdrodb/viz/plots.py +327 -0
- {disdrodb-0.1.2.dist-info → disdrodb-0.1.3.dist-info}/METADATA +3 -2
- {disdrodb-0.1.2.dist-info → disdrodb-0.1.3.dist-info}/RECORD +121 -88
- {disdrodb-0.1.2.dist-info → disdrodb-0.1.3.dist-info}/entry_points.txt +1 -0
- disdrodb/l1/encoding_attrs.py +0 -642
- disdrodb/l2/processing_options.py +0 -213
- /disdrodb/l0/readers/PARSIVEL/SLOVENIA/{UL_FGG.py → UL.py} +0 -0
- {disdrodb-0.1.2.dist-info → disdrodb-0.1.3.dist-info}/WHEEL +0 -0
- {disdrodb-0.1.2.dist-info → disdrodb-0.1.3.dist-info}/licenses/LICENSE +0 -0
- {disdrodb-0.1.2.dist-info → disdrodb-0.1.3.dist-info}/top_level.txt +0 -0
disdrodb/l0/routines.py
CHANGED
|
@@ -21,14 +21,13 @@
|
|
|
21
21
|
import datetime
|
|
22
22
|
import logging
|
|
23
23
|
import os
|
|
24
|
+
import shutil
|
|
24
25
|
import time
|
|
25
26
|
from typing import Optional
|
|
26
27
|
|
|
27
28
|
import dask
|
|
28
29
|
|
|
29
|
-
from disdrodb.api.checks import check_sensor_name
|
|
30
|
-
|
|
31
|
-
# Directory
|
|
30
|
+
from disdrodb.api.checks import check_sensor_name, check_station_inputs
|
|
32
31
|
from disdrodb.api.create_directories import (
|
|
33
32
|
create_l0_directory_structure,
|
|
34
33
|
create_logs_directory,
|
|
@@ -41,6 +40,7 @@ from disdrodb.api.path import (
|
|
|
41
40
|
define_l0b_filename,
|
|
42
41
|
define_l0c_filename,
|
|
43
42
|
define_metadata_filepath,
|
|
43
|
+
define_partitioning_tree,
|
|
44
44
|
)
|
|
45
45
|
from disdrodb.api.search import get_required_product
|
|
46
46
|
from disdrodb.configs import get_data_archive_dir, get_folder_partitioning, get_metadata_archive_dir
|
|
@@ -53,7 +53,7 @@ from disdrodb.l0.l0a_processing import (
|
|
|
53
53
|
)
|
|
54
54
|
from disdrodb.l0.l0b_nc_processing import sanitize_ds
|
|
55
55
|
from disdrodb.l0.l0b_processing import (
|
|
56
|
-
|
|
56
|
+
generate_l0b,
|
|
57
57
|
set_l0b_encodings,
|
|
58
58
|
write_l0b,
|
|
59
59
|
)
|
|
@@ -63,6 +63,7 @@ from disdrodb.l0.l0c_processing import (
|
|
|
63
63
|
retrieve_possible_measurement_intervals,
|
|
64
64
|
)
|
|
65
65
|
from disdrodb.metadata import read_station_metadata
|
|
66
|
+
from disdrodb.utils.attrs import set_disdrodb_attrs
|
|
66
67
|
from disdrodb.utils.decorators import delayed_if_parallel, single_threaded_if_parallel
|
|
67
68
|
|
|
68
69
|
# Logger
|
|
@@ -73,8 +74,6 @@ from disdrodb.utils.logger import (
|
|
|
73
74
|
log_error,
|
|
74
75
|
log_info,
|
|
75
76
|
)
|
|
76
|
-
|
|
77
|
-
# log_warning,
|
|
78
77
|
from disdrodb.utils.writer import write_product
|
|
79
78
|
from disdrodb.utils.yaml import read_yaml
|
|
80
79
|
|
|
@@ -124,7 +123,7 @@ def _generate_l0a(
|
|
|
124
123
|
# Log start processing
|
|
125
124
|
msg = f"{product} processing of {filename} has started."
|
|
126
125
|
log_info(logger=logger, msg=msg, verbose=verbose)
|
|
127
|
-
|
|
126
|
+
success_flag = False
|
|
128
127
|
##------------------------------------------------------------------------.
|
|
129
128
|
### - Read raw file into a dataframe and sanitize for L0A format
|
|
130
129
|
try:
|
|
@@ -144,6 +143,12 @@ def _generate_l0a(
|
|
|
144
143
|
filepath = os.path.join(folder_path, filename)
|
|
145
144
|
write_l0a(df=df, filepath=filepath, force=force, logger=logger, verbose=verbose)
|
|
146
145
|
|
|
146
|
+
##--------------------------------------------------------------------.
|
|
147
|
+
#### - Define logger file final directory
|
|
148
|
+
if folder_partitioning != "":
|
|
149
|
+
log_dst_dir = define_file_folder_path(df, data_dir=logs_dir, folder_partitioning=folder_partitioning)
|
|
150
|
+
os.makedirs(log_dst_dir, exist_ok=True)
|
|
151
|
+
|
|
147
152
|
##--------------------------------------------------------------------.
|
|
148
153
|
# Clean environment
|
|
149
154
|
del df
|
|
@@ -151,6 +156,7 @@ def _generate_l0a(
|
|
|
151
156
|
# Log end processing
|
|
152
157
|
msg = f"{product} processing of {filename} has ended."
|
|
153
158
|
log_info(logger=logger, msg=msg, verbose=verbose)
|
|
159
|
+
success_flag = True
|
|
154
160
|
|
|
155
161
|
# Otherwise log the error
|
|
156
162
|
except Exception as e:
|
|
@@ -161,6 +167,13 @@ def _generate_l0a(
|
|
|
161
167
|
# Close the file logger
|
|
162
168
|
close_logger(logger)
|
|
163
169
|
|
|
170
|
+
# Move logger file to correct partitioning directory
|
|
171
|
+
if success_flag and folder_partitioning != "" and logger_filepath is not None:
|
|
172
|
+
# Move logger file to correct partitioning directory
|
|
173
|
+
dst_filepath = os.path.join(log_dst_dir, os.path.basename(logger_filepath))
|
|
174
|
+
shutil.move(logger_filepath, dst_filepath)
|
|
175
|
+
logger_filepath = dst_filepath
|
|
176
|
+
|
|
164
177
|
# Return the logger file path
|
|
165
178
|
return logger_filepath
|
|
166
179
|
|
|
@@ -200,6 +213,7 @@ def _generate_l0b(
|
|
|
200
213
|
# Log start processing
|
|
201
214
|
msg = f"{product} processing of {filename} has started."
|
|
202
215
|
log_info(logger=logger, msg=msg, verbose=verbose)
|
|
216
|
+
success_flag = False
|
|
203
217
|
|
|
204
218
|
##------------------------------------------------------------------------.
|
|
205
219
|
# Retrieve sensor name
|
|
@@ -209,11 +223,11 @@ def _generate_l0b(
|
|
|
209
223
|
##------------------------------------------------------------------------.
|
|
210
224
|
try:
|
|
211
225
|
# Read L0A Apache Parquet file
|
|
212
|
-
df = read_l0a_dataframe(filepath,
|
|
226
|
+
df = read_l0a_dataframe(filepath, debugging_mode=debugging_mode)
|
|
213
227
|
|
|
214
228
|
# -----------------------------------------------------------------.
|
|
215
229
|
# Create xarray Dataset
|
|
216
|
-
ds =
|
|
230
|
+
ds = generate_l0b(df=df, metadata=metadata, logger=logger, verbose=verbose)
|
|
217
231
|
|
|
218
232
|
# -----------------------------------------------------------------.
|
|
219
233
|
# Write L0B netCDF4 dataset
|
|
@@ -222,6 +236,12 @@ def _generate_l0b(
|
|
|
222
236
|
filepath = os.path.join(folder_path, filename)
|
|
223
237
|
write_l0b(ds, filepath=filepath, force=force)
|
|
224
238
|
|
|
239
|
+
##--------------------------------------------------------------------.
|
|
240
|
+
#### - Define logger file final directory
|
|
241
|
+
if folder_partitioning != "":
|
|
242
|
+
log_dst_dir = define_file_folder_path(ds, data_dir=logs_dir, folder_partitioning=folder_partitioning)
|
|
243
|
+
os.makedirs(log_dst_dir, exist_ok=True)
|
|
244
|
+
|
|
225
245
|
##--------------------------------------------------------------------.
|
|
226
246
|
# Clean environment
|
|
227
247
|
del ds, df
|
|
@@ -229,6 +249,7 @@ def _generate_l0b(
|
|
|
229
249
|
# Log end processing
|
|
230
250
|
msg = f"{product} processing of {filename} has ended."
|
|
231
251
|
log_info(logger=logger, msg=msg, verbose=verbose)
|
|
252
|
+
success_flag = True
|
|
232
253
|
|
|
233
254
|
# Otherwise log the error
|
|
234
255
|
except Exception as e:
|
|
@@ -239,10 +260,19 @@ def _generate_l0b(
|
|
|
239
260
|
# Close the file logger
|
|
240
261
|
close_logger(logger)
|
|
241
262
|
|
|
263
|
+
# Move logger file to correct partitioning directory
|
|
264
|
+
if success_flag and folder_partitioning != "" and logger_filepath is not None:
|
|
265
|
+
# Move logger file to correct partitioning directory
|
|
266
|
+
dst_filepath = os.path.join(log_dst_dir, os.path.basename(logger_filepath))
|
|
267
|
+
shutil.move(logger_filepath, dst_filepath)
|
|
268
|
+
logger_filepath = dst_filepath
|
|
269
|
+
|
|
242
270
|
# Return the logger file path
|
|
243
271
|
return logger_filepath
|
|
244
272
|
|
|
245
273
|
|
|
274
|
+
@delayed_if_parallel
|
|
275
|
+
@single_threaded_if_parallel
|
|
246
276
|
def _generate_l0b_from_nc(
|
|
247
277
|
filepath,
|
|
248
278
|
data_dir,
|
|
@@ -282,6 +312,7 @@ def _generate_l0b_from_nc(
|
|
|
282
312
|
# Log start processing
|
|
283
313
|
msg = f"{product} processing of {filename} has started."
|
|
284
314
|
log_info(logger=logger, msg=msg, verbose=verbose)
|
|
315
|
+
success_flag = False
|
|
285
316
|
|
|
286
317
|
##------------------------------------------------------------------------.
|
|
287
318
|
### - Read raw netCDF and sanitize for L0B format
|
|
@@ -303,6 +334,12 @@ def _generate_l0b_from_nc(
|
|
|
303
334
|
filepath = os.path.join(folder_path, filename)
|
|
304
335
|
write_l0b(ds, filepath=filepath, force=force)
|
|
305
336
|
|
|
337
|
+
##--------------------------------------------------------------------.
|
|
338
|
+
#### - Define logger file final directory
|
|
339
|
+
if folder_partitioning != "":
|
|
340
|
+
log_dst_dir = define_file_folder_path(ds, data_dir=logs_dir, folder_partitioning=folder_partitioning)
|
|
341
|
+
os.makedirs(log_dst_dir, exist_ok=True)
|
|
342
|
+
|
|
306
343
|
##--------------------------------------------------------------------.
|
|
307
344
|
# Clean environment
|
|
308
345
|
del ds
|
|
@@ -310,6 +347,7 @@ def _generate_l0b_from_nc(
|
|
|
310
347
|
# Log end processing
|
|
311
348
|
msg = f"L0B processing of {filename} has ended."
|
|
312
349
|
log_info(logger=logger, msg=msg, verbose=verbose)
|
|
350
|
+
success_flag = True
|
|
313
351
|
|
|
314
352
|
# Otherwise log the error
|
|
315
353
|
except Exception as e:
|
|
@@ -320,6 +358,13 @@ def _generate_l0b_from_nc(
|
|
|
320
358
|
# Close the file logger
|
|
321
359
|
close_logger(logger)
|
|
322
360
|
|
|
361
|
+
# Move logger file to correct partitioning directory
|
|
362
|
+
if success_flag and folder_partitioning != "" and logger_filepath is not None:
|
|
363
|
+
# Move logger file to correct partitioning directory
|
|
364
|
+
dst_filepath = os.path.join(log_dst_dir, os.path.basename(logger_filepath))
|
|
365
|
+
shutil.move(logger_filepath, dst_filepath)
|
|
366
|
+
logger_filepath = dst_filepath
|
|
367
|
+
|
|
323
368
|
# Return the logger file path
|
|
324
369
|
return logger_filepath
|
|
325
370
|
|
|
@@ -358,6 +403,7 @@ def _generate_l0c(
|
|
|
358
403
|
# Log start processing
|
|
359
404
|
msg = f"{product} processing for {day} has started."
|
|
360
405
|
log_info(logger=logger, msg=msg, verbose=verbose)
|
|
406
|
+
success_flag = False
|
|
361
407
|
|
|
362
408
|
##------------------------------------------------------------------------.
|
|
363
409
|
### Core computation
|
|
@@ -388,21 +434,35 @@ def _generate_l0c(
|
|
|
388
434
|
|
|
389
435
|
# Set encodings
|
|
390
436
|
ds = set_l0b_encodings(ds=ds, sensor_name=sensor_name)
|
|
437
|
+
# Update global attributes
|
|
438
|
+
ds = set_disdrodb_attrs(ds, product=product)
|
|
391
439
|
|
|
392
|
-
# Define filepath
|
|
440
|
+
# Define product filepath
|
|
393
441
|
filename = define_l0c_filename(ds, campaign_name=campaign_name, station_name=station_name)
|
|
394
442
|
folder_path = define_file_folder_path(ds, data_dir=data_dir, folder_partitioning=folder_partitioning)
|
|
395
443
|
filepath = os.path.join(folder_path, filename)
|
|
396
444
|
|
|
397
445
|
# Write to disk
|
|
398
|
-
write_product(ds,
|
|
446
|
+
write_product(ds, filepath=filepath, force=force)
|
|
399
447
|
|
|
400
448
|
# Clean environment
|
|
401
449
|
del ds
|
|
402
450
|
|
|
451
|
+
##--------------------------------------------------------------------.
|
|
452
|
+
#### - Define logger file final directory
|
|
453
|
+
if folder_partitioning != "":
|
|
454
|
+
print(day)
|
|
455
|
+
dirtree = define_partitioning_tree(
|
|
456
|
+
time=datetime.datetime.strptime("2022-03-22", "%Y-%m-%d"),
|
|
457
|
+
folder_partitioning=folder_partitioning,
|
|
458
|
+
)
|
|
459
|
+
log_dst_dir = os.path.join(logs_dir, dirtree)
|
|
460
|
+
os.makedirs(log_dst_dir, exist_ok=True)
|
|
461
|
+
|
|
403
462
|
# Log end processing
|
|
404
463
|
msg = f"{product} processing for {day} has ended."
|
|
405
464
|
log_info(logger=logger, msg=msg, verbose=verbose)
|
|
465
|
+
success_flag = True
|
|
406
466
|
|
|
407
467
|
##--------------------------------------------------------------------.
|
|
408
468
|
# Otherwise log the error
|
|
@@ -414,6 +474,13 @@ def _generate_l0c(
|
|
|
414
474
|
# Close the file logger
|
|
415
475
|
close_logger(logger)
|
|
416
476
|
|
|
477
|
+
# Move logger file to correct partitioning directory
|
|
478
|
+
if success_flag and folder_partitioning != "" and logger_filepath is not None:
|
|
479
|
+
# Move logger file to correct partitioning directory
|
|
480
|
+
dst_filepath = os.path.join(log_dst_dir, os.path.basename(logger_filepath))
|
|
481
|
+
shutil.move(logger_filepath, dst_filepath)
|
|
482
|
+
logger_filepath = dst_filepath
|
|
483
|
+
|
|
417
484
|
# Return the logger file path
|
|
418
485
|
return logger_filepath
|
|
419
486
|
|
|
@@ -474,6 +541,15 @@ def run_l0a_station(
|
|
|
474
541
|
data_archive_dir = get_data_archive_dir(data_archive_dir)
|
|
475
542
|
metadata_archive_dir = get_metadata_archive_dir(metadata_archive_dir)
|
|
476
543
|
|
|
544
|
+
# Check valid data_source, campaign_name, and station_name
|
|
545
|
+
check_station_inputs(
|
|
546
|
+
metadata_archive_dir=metadata_archive_dir,
|
|
547
|
+
data_source=data_source,
|
|
548
|
+
campaign_name=campaign_name,
|
|
549
|
+
station_name=station_name,
|
|
550
|
+
)
|
|
551
|
+
|
|
552
|
+
# ------------------------------------------------------------------------.
|
|
477
553
|
# Read metadata
|
|
478
554
|
metadata = read_station_metadata(
|
|
479
555
|
metadata_archive_dir=metadata_archive_dir,
|
|
@@ -652,7 +728,7 @@ def run_l0b_station(
|
|
|
652
728
|
and multi-threading will be automatically exploited to speed up I/O tasks.
|
|
653
729
|
debugging_mode : bool, optional
|
|
654
730
|
If ``True``, the amount of data processed will be reduced.
|
|
655
|
-
Only
|
|
731
|
+
Only 100 rows sampled from 3 L0A files will be processed. The default value is ``False``.
|
|
656
732
|
remove_l0a: bool, optional
|
|
657
733
|
Whether to remove the processed L0A files. The default value is ``False``.
|
|
658
734
|
data_archive_dir : str, optional
|
|
@@ -669,6 +745,13 @@ def run_l0b_station(
|
|
|
669
745
|
# Retrieve DISDRODB Metadata Archive directory
|
|
670
746
|
metadata_archive_dir = get_metadata_archive_dir(metadata_archive_dir)
|
|
671
747
|
|
|
748
|
+
# Check valid data_source, campaign_name, and station_name
|
|
749
|
+
check_station_inputs(
|
|
750
|
+
metadata_archive_dir=metadata_archive_dir,
|
|
751
|
+
data_source=data_source,
|
|
752
|
+
campaign_name=campaign_name,
|
|
753
|
+
station_name=station_name,
|
|
754
|
+
)
|
|
672
755
|
# -----------------------------------------------------------------.
|
|
673
756
|
# Retrieve metadata
|
|
674
757
|
metadata = read_station_metadata(
|
|
@@ -731,7 +814,7 @@ def run_l0b_station(
|
|
|
731
814
|
# If no data available, print error message and return None
|
|
732
815
|
if flag_not_available_data:
|
|
733
816
|
msg = (
|
|
734
|
-
f"{product} processing of {data_source} {campaign_name} {station_name}"
|
|
817
|
+
f"{product} processing of {data_source} {campaign_name} {station_name} "
|
|
735
818
|
+ f"has not been launched because of missing {required_product} data."
|
|
736
819
|
)
|
|
737
820
|
print(msg)
|
|
@@ -899,6 +982,14 @@ def run_l0c_station(
|
|
|
899
982
|
# Retrieve DISDRODB Metadata Archive directory
|
|
900
983
|
metadata_archive_dir = get_metadata_archive_dir(metadata_archive_dir)
|
|
901
984
|
|
|
985
|
+
# Check valid data_source, campaign_name, and station_name
|
|
986
|
+
check_station_inputs(
|
|
987
|
+
metadata_archive_dir=metadata_archive_dir,
|
|
988
|
+
data_source=data_source,
|
|
989
|
+
campaign_name=campaign_name,
|
|
990
|
+
station_name=station_name,
|
|
991
|
+
)
|
|
992
|
+
|
|
902
993
|
# ------------------------------------------------------------------------.
|
|
903
994
|
# Start processing
|
|
904
995
|
t_i = time.time()
|
|
@@ -957,7 +1048,7 @@ def run_l0c_station(
|
|
|
957
1048
|
# If no data available, print error message and return None
|
|
958
1049
|
if flag_not_available_data:
|
|
959
1050
|
msg = (
|
|
960
|
-
f"{product} processing of {data_source} {campaign_name} {station_name}"
|
|
1051
|
+
f"{product} processing of {data_source} {campaign_name} {station_name} "
|
|
961
1052
|
+ f"has not been launched because of missing {required_product} data."
|
|
962
1053
|
)
|
|
963
1054
|
print(msg)
|
disdrodb/l1/__init__.py
CHANGED
|
@@ -15,3 +15,8 @@
|
|
|
15
15
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
16
|
# -----------------------------------------------------------------------------.
|
|
17
17
|
"""DISDRODB L1 module."""
|
|
18
|
+
from disdrodb.l1.processing import generate_l1
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"generate_l1",
|
|
22
|
+
]
|
disdrodb/l1/filters.py
CHANGED
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
import numpy as np
|
|
20
20
|
import xarray as xr
|
|
21
21
|
|
|
22
|
+
from disdrodb.constants import DIAMETER_DIMENSION, VELOCITY_DIMENSION
|
|
23
|
+
|
|
22
24
|
|
|
23
25
|
def filter_diameter_bins(ds, minimum_diameter=None, maximum_diameter=None):
|
|
24
26
|
"""
|
|
@@ -29,10 +31,10 @@ def filter_diameter_bins(ds, minimum_diameter=None, maximum_diameter=None):
|
|
|
29
31
|
ds : xarray.Dataset
|
|
30
32
|
The dataset containing diameter bin data.
|
|
31
33
|
minimum_diameter : float, optional
|
|
32
|
-
The minimum diameter to
|
|
34
|
+
The minimum diameter to be included, in millimeters.
|
|
33
35
|
Defaults to the minimum value in `ds["diameter_bin_lower"]`.
|
|
34
36
|
maximum_diameter : float, optional
|
|
35
|
-
The maximum diameter to
|
|
37
|
+
The maximum diameter to be included, in millimeters.
|
|
36
38
|
Defaults to the maximum value in `ds["diameter_bin_upper"]`.
|
|
37
39
|
|
|
38
40
|
Returns
|
|
@@ -40,22 +42,28 @@ def filter_diameter_bins(ds, minimum_diameter=None, maximum_diameter=None):
|
|
|
40
42
|
xarray.Dataset
|
|
41
43
|
The filtered dataset containing only the specified diameter bins.
|
|
42
44
|
"""
|
|
45
|
+
# Put data into memory
|
|
46
|
+
ds["diameter_bin_lower"] = ds["diameter_bin_lower"].compute()
|
|
47
|
+
ds["diameter_bin_upper"] = ds["diameter_bin_upper"].compute()
|
|
48
|
+
|
|
43
49
|
# Initialize default arguments
|
|
44
50
|
if minimum_diameter is None:
|
|
45
51
|
minimum_diameter = ds["diameter_bin_lower"].min().item()
|
|
46
52
|
if maximum_diameter is None:
|
|
47
53
|
maximum_diameter = ds["diameter_bin_upper"].max().item()
|
|
48
|
-
|
|
54
|
+
|
|
55
|
+
# Select bins which overlap the specified diameters
|
|
49
56
|
valid_indices = np.logical_and(
|
|
50
|
-
ds["
|
|
51
|
-
ds["
|
|
52
|
-
)
|
|
53
|
-
ds = ds.isel({"diameter_bin_center": valid_indices})
|
|
54
|
-
# Update history
|
|
55
|
-
history = ds.attrs.get("history", "")
|
|
56
|
-
ds.attrs["history"] = (
|
|
57
|
-
history + f" Selected drops with diameters between {minimum_diameter} and {maximum_diameter} mm \n"
|
|
57
|
+
ds["diameter_bin_upper"] > minimum_diameter,
|
|
58
|
+
ds["diameter_bin_lower"] < maximum_diameter,
|
|
58
59
|
)
|
|
60
|
+
|
|
61
|
+
# Select bins with diameter values entirely inside the specified min/max values
|
|
62
|
+
# valid_indices = np.logical_and(
|
|
63
|
+
# ds["diameter_bin_lower"] >= minimum_diameter,
|
|
64
|
+
# ds["diameter_bin_upper"] <= maximum_diameter,
|
|
65
|
+
# )
|
|
66
|
+
ds = ds.isel({DIAMETER_DIMENSION: valid_indices})
|
|
59
67
|
return ds
|
|
60
68
|
|
|
61
69
|
|
|
@@ -79,22 +87,28 @@ def filter_velocity_bins(ds, minimum_velocity=0, maximum_velocity=12):
|
|
|
79
87
|
xarray.Dataset
|
|
80
88
|
The filtered dataset containing only the specified velocity bins.
|
|
81
89
|
"""
|
|
90
|
+
# Put data into memory
|
|
91
|
+
ds["velocity_bin_lower"] = ds["velocity_bin_lower"].compute()
|
|
92
|
+
ds["velocity_bin_upper"] = ds["velocity_bin_upper"].compute()
|
|
93
|
+
|
|
82
94
|
# Initialize default arguments
|
|
83
95
|
if minimum_velocity is None:
|
|
84
96
|
minimum_velocity = ds["velocity_bin_lower"].min().item()
|
|
85
97
|
if maximum_velocity is None:
|
|
86
98
|
maximum_velocity = ds["velocity_bin_upper"].max().item()
|
|
87
|
-
|
|
99
|
+
|
|
100
|
+
# Select bins which overlap the specified velocities
|
|
88
101
|
valid_indices = np.logical_and(
|
|
89
|
-
ds["
|
|
90
|
-
ds["
|
|
91
|
-
)
|
|
92
|
-
ds = ds.isel({"velocity_bin_center": valid_indices})
|
|
93
|
-
# Update history
|
|
94
|
-
history = ds.attrs.get("history", "")
|
|
95
|
-
ds.attrs["history"] = (
|
|
96
|
-
history + f" Selected drops with fall velocity between {minimum_velocity} and {maximum_velocity} m/s \n"
|
|
102
|
+
ds["velocity_bin_upper"] > minimum_velocity,
|
|
103
|
+
ds["velocity_bin_lower"] < maximum_velocity,
|
|
97
104
|
)
|
|
105
|
+
|
|
106
|
+
# Select bins with velocity values entirely inside the specified min/max values
|
|
107
|
+
# valid_indices = np.logical_and(
|
|
108
|
+
# ds["velocity_bin_lower"] >= minimum_velocity,
|
|
109
|
+
# ds["velocity_bin_upper"] <= maximum_velocity,
|
|
110
|
+
# )
|
|
111
|
+
ds = ds.isel({VELOCITY_DIMENSION: valid_indices})
|
|
98
112
|
return ds
|
|
99
113
|
|
|
100
114
|
|
disdrodb/l1/processing.py
CHANGED
|
@@ -16,22 +16,19 @@
|
|
|
16
16
|
# -----------------------------------------------------------------------------.
|
|
17
17
|
"""Core functions for DISDRODB L1 production."""
|
|
18
18
|
|
|
19
|
-
|
|
20
19
|
import xarray as xr
|
|
21
20
|
|
|
22
|
-
from disdrodb import DIAMETER_DIMENSION, VELOCITY_DIMENSION
|
|
23
|
-
from disdrodb.l1.encoding_attrs import get_attrs_dict, get_encoding_dict
|
|
21
|
+
from disdrodb.constants import DIAMETER_DIMENSION, VELOCITY_DIMENSION
|
|
24
22
|
from disdrodb.l1.fall_velocity import get_raindrop_fall_velocity
|
|
25
23
|
from disdrodb.l1.filters import define_spectrum_mask, filter_diameter_bins, filter_velocity_bins
|
|
26
24
|
from disdrodb.l1.resampling import add_sample_interval
|
|
27
25
|
from disdrodb.l1_env.routines import load_env_dataset
|
|
28
26
|
from disdrodb.l2.empirical_dsd import ( # TODO: maybe move out of L2
|
|
29
|
-
|
|
27
|
+
add_bins_metrics,
|
|
30
28
|
get_min_max_diameter,
|
|
31
29
|
)
|
|
32
|
-
from disdrodb.utils.attrs import set_attrs
|
|
33
|
-
from disdrodb.utils.encoding import set_encodings
|
|
34
30
|
from disdrodb.utils.time import ensure_sample_interval_in_seconds, infer_sample_interval
|
|
31
|
+
from disdrodb.utils.writer import finalize_product
|
|
35
32
|
|
|
36
33
|
|
|
37
34
|
def generate_l1(
|
|
@@ -51,7 +48,7 @@ def generate_l1(
|
|
|
51
48
|
small_velocity_threshold=2.5, # 3
|
|
52
49
|
maintain_smallest_drops=True,
|
|
53
50
|
):
|
|
54
|
-
"""Generate
|
|
51
|
+
"""Generate DISDRODB L1 Dataset from DISDRODB L0C Dataset.
|
|
55
52
|
|
|
56
53
|
Parameters
|
|
57
54
|
----------
|
|
@@ -88,17 +85,17 @@ def generate_l1(
|
|
|
88
85
|
xarray.Dataset
|
|
89
86
|
DISRODB L1 dataset.
|
|
90
87
|
"""
|
|
91
|
-
# Take as input an L0 !
|
|
92
|
-
|
|
93
88
|
# Retrieve source attributes
|
|
94
89
|
attrs = ds.attrs.copy()
|
|
95
90
|
|
|
96
91
|
# Determine if the velocity dimension is available
|
|
97
92
|
has_velocity_dimension = VELOCITY_DIMENSION in ds.dims
|
|
98
93
|
|
|
99
|
-
#
|
|
100
|
-
|
|
94
|
+
# Retrieve sensor_name
|
|
95
|
+
# - If not present, don't drop Parsivels first two bins
|
|
96
|
+
sensor_name = attrs.get("sensor_name", "")
|
|
101
97
|
|
|
98
|
+
# ---------------------------------------------------------------------------
|
|
102
99
|
# Retrieve sample interval
|
|
103
100
|
# --> sample_interval is a coordinate of L0C products
|
|
104
101
|
if "sample_interval" in ds:
|
|
@@ -107,39 +104,52 @@ def generate_l1(
|
|
|
107
104
|
# This line is not called in the DISDRODB processing chain !
|
|
108
105
|
sample_interval = infer_sample_interval(ds, verbose=False)
|
|
109
106
|
|
|
110
|
-
# Re-add sample interval as coordinate (in seconds)
|
|
111
|
-
ds = add_sample_interval(ds, sample_interval=sample_interval)
|
|
112
|
-
|
|
113
107
|
# ---------------------------------------------------------------------------
|
|
114
108
|
# Retrieve ENV dataset or take defaults
|
|
115
109
|
# --> Used only for Beard fall velocity currently !
|
|
116
110
|
ds_env = load_env_dataset(ds)
|
|
117
111
|
|
|
112
|
+
# ---------------------------------------------------------------------------
|
|
113
|
+
# Initialize L1 dataset
|
|
114
|
+
ds_l1 = xr.Dataset()
|
|
115
|
+
|
|
116
|
+
# Add raw_drop_number variable to L1 dataset
|
|
117
|
+
ds_l1["raw_drop_number"] = ds["raw_drop_number"]
|
|
118
|
+
|
|
119
|
+
# Add sample interval as coordinate (in seconds)
|
|
120
|
+
ds_l1 = add_sample_interval(ds_l1, sample_interval=sample_interval)
|
|
121
|
+
|
|
122
|
+
# Add L0C coordinates that might got lost
|
|
123
|
+
if "time_qc" in ds_l1:
|
|
124
|
+
ds_l1 = ds_l1.assign_coords({"time_qc": ds["time_qc"]})
|
|
125
|
+
|
|
118
126
|
# -------------------------------------------------------------------------------------------
|
|
119
127
|
# Filter dataset by diameter and velocity bins
|
|
128
|
+
if sensor_name in ["PARSIVEL", "PARSIVEL2"]:
|
|
129
|
+
# - Remove first two bins because never reports data !
|
|
130
|
+
# - If not removed, can alter e.g. L2M model fitting
|
|
131
|
+
ds_l1 = filter_diameter_bins(ds=ds_l1, minimum_diameter=0.312) # it includes the 0.2495-0.3745 bin
|
|
132
|
+
|
|
120
133
|
# - Filter diameter bins
|
|
121
|
-
|
|
134
|
+
ds_l1 = filter_diameter_bins(ds=ds_l1, minimum_diameter=minimum_diameter, maximum_diameter=maximum_diameter)
|
|
122
135
|
# - Filter velocity bins
|
|
123
136
|
if has_velocity_dimension:
|
|
124
|
-
|
|
137
|
+
ds_l1 = filter_velocity_bins(ds=ds_l1, minimum_velocity=minimum_velocity, maximum_velocity=maximum_velocity)
|
|
125
138
|
|
|
126
139
|
# -------------------------------------------------------------------------------------------
|
|
127
140
|
# Compute fall velocity
|
|
128
|
-
fall_velocity = get_raindrop_fall_velocity(
|
|
129
|
-
diameter=
|
|
141
|
+
ds_l1["fall_velocity"] = get_raindrop_fall_velocity(
|
|
142
|
+
diameter=ds_l1["diameter_bin_center"],
|
|
130
143
|
method=fall_velocity_method,
|
|
131
144
|
ds_env=ds_env, # mm
|
|
132
145
|
)
|
|
133
146
|
|
|
134
|
-
# Add fall velocity
|
|
135
|
-
ds_l1["fall_velocity"] = fall_velocity
|
|
136
|
-
|
|
137
147
|
# -------------------------------------------------------------------------------------------
|
|
138
148
|
# Define filtering mask according to fall velocity
|
|
139
149
|
if has_velocity_dimension:
|
|
140
150
|
mask = define_spectrum_mask(
|
|
141
|
-
drop_number=
|
|
142
|
-
fall_velocity=fall_velocity,
|
|
151
|
+
drop_number=ds_l1["raw_drop_number"],
|
|
152
|
+
fall_velocity=ds_l1["fall_velocity"],
|
|
143
153
|
above_velocity_fraction=above_velocity_fraction,
|
|
144
154
|
above_velocity_tolerance=above_velocity_tolerance,
|
|
145
155
|
below_velocity_fraction=below_velocity_fraction,
|
|
@@ -152,14 +162,14 @@ def generate_l1(
|
|
|
152
162
|
# -------------------------------------------------------------------------------------------
|
|
153
163
|
# Retrieve drop number and drop_counts arrays
|
|
154
164
|
if has_velocity_dimension:
|
|
155
|
-
drop_number =
|
|
165
|
+
drop_number = ds_l1["raw_drop_number"].where(mask) # 2D (diameter, velocity)
|
|
156
166
|
drop_counts = drop_number.sum(dim=VELOCITY_DIMENSION) # 1D (diameter)
|
|
157
|
-
drop_counts_raw =
|
|
167
|
+
drop_counts_raw = ds_l1["raw_drop_number"].sum(dim=VELOCITY_DIMENSION) # 1D (diameter)
|
|
158
168
|
|
|
159
169
|
else:
|
|
160
|
-
drop_number =
|
|
161
|
-
drop_counts =
|
|
162
|
-
drop_counts_raw =
|
|
170
|
+
drop_number = ds_l1["raw_drop_number"] # 1D (diameter)
|
|
171
|
+
drop_counts = ds_l1["raw_drop_number"] # 1D (diameter)
|
|
172
|
+
drop_counts_raw = ds_l1["raw_drop_number"]
|
|
163
173
|
|
|
164
174
|
# Add drop number and drop_counts
|
|
165
175
|
ds_l1["drop_number"] = drop_number
|
|
@@ -173,30 +183,21 @@ def generate_l1(
|
|
|
173
183
|
ds_l1["Dmin"] = min_drop_diameter
|
|
174
184
|
ds_l1["Dmax"] = max_drop_diameter
|
|
175
185
|
ds_l1["N"] = drop_counts.sum(dim=DIAMETER_DIMENSION)
|
|
176
|
-
ds_l1["
|
|
186
|
+
ds_l1["Nraw"] = drop_counts_raw.sum(dim=DIAMETER_DIMENSION)
|
|
187
|
+
ds_l1["Nremoved"] = ds_l1["Nraw"] - ds_l1["N"]
|
|
177
188
|
|
|
178
189
|
# Add bins statistics
|
|
179
|
-
ds_l1
|
|
190
|
+
ds_l1 = add_bins_metrics(ds_l1)
|
|
180
191
|
|
|
181
192
|
# -------------------------------------------------------------------------------------------
|
|
182
193
|
# Add quality flags
|
|
183
194
|
# TODO: snow_flags, insects_flag, ...
|
|
184
195
|
|
|
185
|
-
# -------------------------------------------------------------------------------------------
|
|
186
|
-
#### Add L0C coordinates that might got lost
|
|
187
|
-
if "time_qc" in ds:
|
|
188
|
-
ds_l1 = ds_l1.assign_coords({"time_qc": ds["time_qc"]})
|
|
189
|
-
|
|
190
196
|
#### ----------------------------------------------------------------------------.
|
|
191
|
-
####
|
|
192
|
-
# Add variables attributes
|
|
193
|
-
attrs_dict = get_attrs_dict()
|
|
194
|
-
ds_l1 = set_attrs(ds_l1, attrs_dict=attrs_dict)
|
|
195
|
-
|
|
196
|
-
# Add variables encoding
|
|
197
|
-
encoding_dict = get_encoding_dict()
|
|
198
|
-
ds_l1 = set_encodings(ds_l1, encoding_dict=encoding_dict)
|
|
199
|
-
|
|
197
|
+
#### Finalize dataset
|
|
200
198
|
# Add global attributes
|
|
201
199
|
ds_l1.attrs = attrs
|
|
200
|
+
|
|
201
|
+
# Add variables attributes and encodings
|
|
202
|
+
ds_l1 = finalize_product(ds_l1, product="L1")
|
|
202
203
|
return ds_l1
|