disdrodb 0.1.3__py3-none-any.whl → 0.1.5__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 +4 -0
- disdrodb/_version.py +2 -2
- disdrodb/api/checks.py +70 -47
- disdrodb/api/configs.py +0 -2
- disdrodb/api/create_directories.py +0 -2
- disdrodb/api/info.py +3 -3
- disdrodb/api/io.py +48 -8
- disdrodb/api/path.py +116 -133
- disdrodb/api/search.py +12 -3
- disdrodb/cli/disdrodb_create_summary.py +113 -0
- disdrodb/cli/disdrodb_create_summary_station.py +11 -1
- disdrodb/cli/disdrodb_run_l0a_station.py +1 -1
- disdrodb/cli/disdrodb_run_l0b_station.py +2 -2
- disdrodb/cli/disdrodb_run_l0c_station.py +2 -2
- disdrodb/cli/disdrodb_run_l1_station.py +2 -2
- disdrodb/cli/disdrodb_run_l2e_station.py +2 -2
- disdrodb/cli/disdrodb_run_l2m_station.py +2 -2
- disdrodb/constants.py +1 -1
- disdrodb/data_transfer/download_data.py +123 -7
- disdrodb/etc/products/L1/global.yaml +1 -1
- disdrodb/etc/products/L2E/5MIN.yaml +1 -0
- disdrodb/etc/products/L2E/global.yaml +1 -1
- disdrodb/etc/products/L2M/GAMMA_GS_ND_MAE.yaml +6 -0
- disdrodb/etc/products/L2M/GAMMA_ML.yaml +1 -1
- disdrodb/etc/products/L2M/LOGNORMAL_GS_LOG_ND_MAE.yaml +6 -0
- disdrodb/etc/products/L2M/LOGNORMAL_GS_ND_MAE.yaml +6 -0
- disdrodb/etc/products/L2M/LOGNORMAL_ML.yaml +8 -0
- disdrodb/etc/products/L2M/global.yaml +11 -3
- disdrodb/issue/writer.py +2 -0
- disdrodb/l0/check_configs.py +49 -16
- disdrodb/l0/configs/LPM/l0a_encodings.yml +2 -2
- disdrodb/l0/configs/LPM/l0b_cf_attrs.yml +2 -2
- disdrodb/l0/configs/LPM/l0b_encodings.yml +2 -2
- disdrodb/l0/configs/LPM/raw_data_format.yml +2 -2
- disdrodb/l0/configs/PWS100/l0b_encodings.yml +1 -0
- disdrodb/l0/configs/SWS250/bins_diameter.yml +108 -0
- disdrodb/l0/configs/SWS250/bins_velocity.yml +83 -0
- disdrodb/l0/configs/SWS250/l0a_encodings.yml +18 -0
- disdrodb/l0/configs/SWS250/l0b_cf_attrs.yml +72 -0
- disdrodb/l0/configs/SWS250/l0b_encodings.yml +155 -0
- disdrodb/l0/configs/SWS250/raw_data_format.yml +148 -0
- disdrodb/l0/l0a_processing.py +10 -5
- disdrodb/l0/l0b_nc_processing.py +10 -6
- disdrodb/l0/l0b_processing.py +92 -72
- disdrodb/l0/l0c_processing.py +369 -251
- disdrodb/l0/readers/LPM/ARM/ARM_LPM.py +8 -1
- disdrodb/l0/readers/LPM/AUSTRALIA/MELBOURNE_2007_LPM.py +2 -2
- disdrodb/l0/readers/LPM/BELGIUM/ULIEGE.py +256 -0
- disdrodb/l0/readers/LPM/BRAZIL/CHUVA_LPM.py +2 -2
- disdrodb/l0/readers/LPM/BRAZIL/GOAMAZON_LPM.py +2 -2
- disdrodb/l0/readers/LPM/GERMANY/DWD.py +491 -0
- disdrodb/l0/readers/LPM/ITALY/GID_LPM.py +2 -2
- disdrodb/l0/readers/LPM/ITALY/GID_LPM_W.py +2 -2
- disdrodb/l0/readers/LPM/KIT/CHWALA.py +2 -2
- disdrodb/l0/readers/LPM/SLOVENIA/ARSO.py +107 -12
- disdrodb/l0/readers/LPM/SLOVENIA/UL.py +3 -3
- disdrodb/l0/readers/LPM/SWITZERLAND/INNERERIZ_LPM.py +2 -2
- disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010.py +5 -14
- disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010_UF.py +5 -14
- disdrodb/l0/readers/PARSIVEL/SLOVENIA/UL.py +117 -8
- disdrodb/l0/readers/PARSIVEL2/ARM/ARM_PARSIVEL2.py +4 -0
- disdrodb/l0/readers/PARSIVEL2/BRAZIL/CHUVA_PARSIVEL2.py +10 -14
- disdrodb/l0/readers/PARSIVEL2/BRAZIL/GOAMAZON_PARSIVEL2.py +10 -14
- disdrodb/l0/readers/PARSIVEL2/CANADA/UQAM_NC.py +69 -0
- disdrodb/l0/readers/PARSIVEL2/DENMARK/DTU.py +8 -14
- disdrodb/l0/readers/PARSIVEL2/DENMARK/EROSION_raw.py +382 -0
- disdrodb/l0/readers/PARSIVEL2/FINLAND/FMI_PARSIVEL2.py +4 -0
- disdrodb/l0/readers/PARSIVEL2/FRANCE/OSUG.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/GREECE/NOA.py +127 -0
- disdrodb/l0/readers/PARSIVEL2/ITALY/HYDROX.py +239 -0
- disdrodb/l0/readers/PARSIVEL2/MPI/BCO_PARSIVEL2.py +136 -0
- disdrodb/l0/readers/PARSIVEL2/MPI/BOWTIE.py +220 -0
- disdrodb/l0/readers/PARSIVEL2/NASA/LPVEX.py +109 -0
- disdrodb/l0/readers/PARSIVEL2/NCAR/FARM_PARSIVEL2.py +5 -11
- disdrodb/l0/readers/PARSIVEL2/NCAR/PERILS_MIPS.py +4 -17
- disdrodb/l0/readers/PARSIVEL2/NCAR/RELAMPAGO_PARSIVEL2.py +5 -14
- disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_PJ.py +10 -13
- disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_SB.py +10 -13
- disdrodb/l0/readers/PARSIVEL2/NETHERLANDS/DELFT_NC.py +3 -0
- disdrodb/l0/readers/PARSIVEL2/PHILIPPINES/PANGASA.py +232 -0
- disdrodb/l0/readers/PARSIVEL2/SPAIN/CENER.py +6 -18
- disdrodb/l0/readers/PARSIVEL2/SPAIN/GRANADA.py +120 -0
- disdrodb/l0/readers/PARSIVEL2/USA/C3WE.py +7 -25
- disdrodb/l0/readers/PWS100/AUSTRIA/HOAL.py +321 -0
- disdrodb/l0/readers/SW250/BELGIUM/KMI.py +239 -0
- disdrodb/l1/beard_model.py +31 -129
- disdrodb/l1/fall_velocity.py +156 -57
- disdrodb/l1/filters.py +25 -28
- disdrodb/l1/processing.py +12 -14
- disdrodb/l1_env/routines.py +46 -17
- disdrodb/l2/empirical_dsd.py +6 -0
- disdrodb/l2/processing.py +3 -3
- disdrodb/metadata/checks.py +132 -125
- disdrodb/metadata/geolocation.py +0 -2
- disdrodb/psd/fitting.py +180 -210
- disdrodb/psd/models.py +1 -1
- disdrodb/routines/__init__.py +54 -0
- disdrodb/{l0/routines.py → routines/l0.py} +288 -418
- disdrodb/{l1/routines.py → routines/l1.py} +60 -92
- disdrodb/{l2/routines.py → routines/l2.py} +284 -485
- disdrodb/{routines.py → routines/wrappers.py} +100 -7
- disdrodb/scattering/axis_ratio.py +95 -85
- disdrodb/scattering/permittivity.py +24 -0
- disdrodb/scattering/routines.py +56 -36
- disdrodb/summary/routines.py +147 -45
- disdrodb/utils/archiving.py +434 -0
- disdrodb/utils/attrs.py +2 -0
- disdrodb/utils/cli.py +5 -5
- disdrodb/utils/dask.py +62 -1
- disdrodb/utils/decorators.py +31 -0
- disdrodb/utils/encoding.py +10 -1
- disdrodb/{l2 → utils}/event.py +1 -66
- disdrodb/utils/logger.py +1 -1
- disdrodb/utils/manipulations.py +22 -12
- disdrodb/utils/routines.py +166 -0
- disdrodb/utils/time.py +5 -293
- disdrodb/utils/xarray.py +3 -0
- disdrodb/viz/plots.py +109 -15
- {disdrodb-0.1.3.dist-info → disdrodb-0.1.5.dist-info}/METADATA +3 -2
- {disdrodb-0.1.3.dist-info → disdrodb-0.1.5.dist-info}/RECORD +124 -96
- {disdrodb-0.1.3.dist-info → disdrodb-0.1.5.dist-info}/entry_points.txt +1 -0
- {disdrodb-0.1.3.dist-info → disdrodb-0.1.5.dist-info}/WHEEL +0 -0
- {disdrodb-0.1.3.dist-info → disdrodb-0.1.5.dist-info}/licenses/LICENSE +0 -0
- {disdrodb-0.1.3.dist-info → disdrodb-0.1.5.dist-info}/top_level.txt +0 -0
|
@@ -89,7 +89,7 @@ def disdrodb_run_l2e_station(
|
|
|
89
89
|
Format: <...>/DISDRODB
|
|
90
90
|
If not specified, uses path specified in the DISDRODB active configuration.
|
|
91
91
|
"""
|
|
92
|
-
from disdrodb.l2
|
|
92
|
+
from disdrodb.routines.l2 import run_l2e_station
|
|
93
93
|
from disdrodb.utils.dask import close_dask_cluster, initialize_dask_cluster
|
|
94
94
|
|
|
95
95
|
data_archive_dir = parse_archive_dir(data_archive_dir)
|
|
@@ -98,7 +98,7 @@ def disdrodb_run_l2e_station(
|
|
|
98
98
|
# -------------------------------------------------------------------------.
|
|
99
99
|
# If parallel=True, set the dask environment
|
|
100
100
|
if parallel:
|
|
101
|
-
cluster, client = initialize_dask_cluster(minimum_memory="
|
|
101
|
+
cluster, client = initialize_dask_cluster(minimum_memory="4GB")
|
|
102
102
|
|
|
103
103
|
# -------------------------------------------------------------------------.
|
|
104
104
|
run_l2e_station(
|
|
@@ -89,7 +89,7 @@ def disdrodb_run_l2m_station(
|
|
|
89
89
|
Format: <...>/DISDRODB
|
|
90
90
|
If not specified, uses path specified in the DISDRODB active configuration.
|
|
91
91
|
"""
|
|
92
|
-
from disdrodb.l2
|
|
92
|
+
from disdrodb.routines.l2 import run_l2m_station
|
|
93
93
|
from disdrodb.utils.dask import close_dask_cluster, initialize_dask_cluster
|
|
94
94
|
|
|
95
95
|
data_archive_dir = parse_archive_dir(data_archive_dir)
|
|
@@ -98,7 +98,7 @@ def disdrodb_run_l2m_station(
|
|
|
98
98
|
# -------------------------------------------------------------------------.
|
|
99
99
|
# If parallel=True, set the dask environment
|
|
100
100
|
if parallel:
|
|
101
|
-
cluster, client = initialize_dask_cluster()
|
|
101
|
+
cluster, client = initialize_dask_cluster(minimum_memory="4GB")
|
|
102
102
|
|
|
103
103
|
# -------------------------------------------------------------------------.
|
|
104
104
|
run_l2m_station(
|
disdrodb/constants.py
CHANGED
|
@@ -41,7 +41,7 @@ COORDINATES = [
|
|
|
41
41
|
"time",
|
|
42
42
|
"sample_interval",
|
|
43
43
|
]
|
|
44
|
-
OPTICAL_SENSORS = ["PARSIVEL", "PARSIVEL2", "LPM", "PWS100"]
|
|
44
|
+
OPTICAL_SENSORS = ["PARSIVEL", "PARSIVEL2", "LPM", "PWS100", "SWS250"]
|
|
45
45
|
IMPACT_SENSORS = ["RD80"]
|
|
46
46
|
|
|
47
47
|
PRODUCTS = ["RAW", "L0A", "L0B", "L0C", "L1", "L2E", "L2M"]
|
|
@@ -239,7 +239,7 @@ def check_consistent_station_name(metadata_filepath, station_name):
|
|
|
239
239
|
return station_name
|
|
240
240
|
|
|
241
241
|
|
|
242
|
-
def download_station_data(metadata_filepath: str, data_archive_dir: str, force: bool = False) -> None:
|
|
242
|
+
def download_station_data(metadata_filepath: str, data_archive_dir: str, force: bool = False, verbose=True) -> None:
|
|
243
243
|
"""Download and unzip the station data .
|
|
244
244
|
|
|
245
245
|
Parameters
|
|
@@ -275,17 +275,27 @@ def download_station_data(metadata_filepath: str, data_archive_dir: str, force:
|
|
|
275
275
|
raise ValueError(f"Invalid disdrodb_data_url '{disdrodb_data_url}' for station {station_name}")
|
|
276
276
|
|
|
277
277
|
# Download files
|
|
278
|
-
# - Option 1: Download
|
|
278
|
+
# - Option 1: Download ZIP file containing all station raw data
|
|
279
279
|
if disdrodb_data_url.startswith("https://zenodo.org/") or disdrodb_data_url.startswith("https://cloudnet.fmi.fi/"):
|
|
280
280
|
download_zip_file(url=disdrodb_data_url, dst_dir=station_dir, force=force)
|
|
281
|
+
|
|
281
282
|
# - Option 2: Recursive download from a web server via HTTP or HTTPS.
|
|
282
283
|
elif disdrodb_data_url.startswith("http"):
|
|
283
|
-
download_web_server_data(url=disdrodb_data_url, dst_dir=station_dir, force=force, verbose=
|
|
284
|
+
download_web_server_data(url=disdrodb_data_url, dst_dir=station_dir, force=force, verbose=verbose)
|
|
285
|
+
# - Retry to be more sure that all data have been downloaded
|
|
286
|
+
download_web_server_data(url=disdrodb_data_url, dst_dir=station_dir, force=True, verbose=verbose)
|
|
287
|
+
|
|
288
|
+
# - Option 3: Recursive download from a ftp server
|
|
289
|
+
elif disdrodb_data_url.startswith("ftp"):
|
|
290
|
+
download_ftp_server_data(url=disdrodb_data_url, dst_dir=station_dir, force=force, verbose=verbose)
|
|
291
|
+
# - Retry to be more sure that all data have been downloaded
|
|
292
|
+
download_ftp_server_data(url=disdrodb_data_url, dst_dir=station_dir, force=True, verbose=verbose)
|
|
293
|
+
|
|
284
294
|
else:
|
|
285
295
|
raise NotImplementedError(f"Open a GitHub Issue to enable the download of data from {disdrodb_data_url}.")
|
|
286
296
|
|
|
287
297
|
|
|
288
|
-
|
|
298
|
+
####--------------------------------------------------------------------.
|
|
289
299
|
#### Download from Web Server via HTTP or HTTPS
|
|
290
300
|
|
|
291
301
|
|
|
@@ -301,9 +311,17 @@ def download_web_server_data(url: str, dst_dir: str, force=True, verbose=True) -
|
|
|
301
311
|
3. Compute cut-dirs so that only the last segment of the path remains locally.
|
|
302
312
|
4. Build and run the wget command.
|
|
303
313
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
314
|
+
Parameters
|
|
315
|
+
----------
|
|
316
|
+
url : str
|
|
317
|
+
HTTPS URL pointing to webserver folder. Example: "https://ruisdael.citg.tudelft.nl/parsivel/PAR001_Cabauw/"
|
|
318
|
+
dst_dir : str
|
|
319
|
+
Local directory where to download the file (DISDRODB station data directory).
|
|
320
|
+
force : bool, optional
|
|
321
|
+
If ``True``, re-download new/updated files (skip unchanged ones).
|
|
322
|
+
If ``False``, keep existing files untouched.
|
|
323
|
+
verbose : bool, optional
|
|
324
|
+
Print wget output (default is True).
|
|
307
325
|
"""
|
|
308
326
|
# 1. Ensure wget exists
|
|
309
327
|
ensure_wget_available()
|
|
@@ -393,6 +411,104 @@ def build_webserver_wget_command(url: str, cut_dirs: int, dst_dir: str, force: b
|
|
|
393
411
|
return cmd
|
|
394
412
|
|
|
395
413
|
|
|
414
|
+
####--------------------------------------------------------------------.
|
|
415
|
+
#### Download from FTP Server
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def build_ftp_server_wget_command(
|
|
419
|
+
url: str,
|
|
420
|
+
cut_dirs: int,
|
|
421
|
+
dst_dir: str,
|
|
422
|
+
force: bool,
|
|
423
|
+
verbose: bool,
|
|
424
|
+
) -> list[str]:
|
|
425
|
+
"""Construct the wget command list for FTP recursive download.
|
|
426
|
+
|
|
427
|
+
Parameters
|
|
428
|
+
----------
|
|
429
|
+
url : str
|
|
430
|
+
FTP URL to download from.
|
|
431
|
+
cut_dirs : int
|
|
432
|
+
Number of leading path components to strip.
|
|
433
|
+
dst_dir : str
|
|
434
|
+
Local destination directory.
|
|
435
|
+
force : bool
|
|
436
|
+
If True, re-download newer files (--timestamping).
|
|
437
|
+
If False, keep existing files untouched (--no-clobber).
|
|
438
|
+
verbose : bool
|
|
439
|
+
If False, suppress wget output (-q).
|
|
440
|
+
"""
|
|
441
|
+
cmd = ["wget"] # base command
|
|
442
|
+
|
|
443
|
+
if not verbose:
|
|
444
|
+
cmd.append("-q") # quiet mode --> no output except errors
|
|
445
|
+
|
|
446
|
+
cmd += [
|
|
447
|
+
"-r", # recursive --> traverse into subdirectories
|
|
448
|
+
"-np", # no parent --> don't ascend to higher-level dirs
|
|
449
|
+
"-nH", # no host dirs --> avoid creating ftp.example.com/ locally
|
|
450
|
+
f"--cut-dirs={cut_dirs}", # strip N leading path components
|
|
451
|
+
]
|
|
452
|
+
|
|
453
|
+
if force:
|
|
454
|
+
cmd.append("--timestamping") # download if remote file is newer
|
|
455
|
+
else:
|
|
456
|
+
cmd.append("--no-clobber") # skip files that already exist
|
|
457
|
+
|
|
458
|
+
cmd += [
|
|
459
|
+
"-P", # specify local destination directory
|
|
460
|
+
dst_dir,
|
|
461
|
+
f"ftp://anonymous:disdrodb@{url}", # target FTP URL
|
|
462
|
+
]
|
|
463
|
+
return cmd
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
def download_ftp_server_data(url: str, dst_dir: str, force: bool = False, verbose: bool = True) -> None:
|
|
467
|
+
"""Download data from an FTP server with anonymous login.
|
|
468
|
+
|
|
469
|
+
Parameters
|
|
470
|
+
----------
|
|
471
|
+
url : str
|
|
472
|
+
FTP server URL pointing to a folder. Example: "ftp://ftp.example.com/path/to/data/"
|
|
473
|
+
dst_dir : str
|
|
474
|
+
Local directory where to download the file (DISDRODB station data directory).
|
|
475
|
+
force : bool, optional
|
|
476
|
+
If ``True``, re-download new/updated files (skip unchanged ones).
|
|
477
|
+
If ``False``, keep existing files untouched.
|
|
478
|
+
verbose : bool, optional
|
|
479
|
+
Print wget output (default is True).
|
|
480
|
+
"""
|
|
481
|
+
ensure_wget_available()
|
|
482
|
+
|
|
483
|
+
# Ensure trailing slash
|
|
484
|
+
url = ensure_trailing_slash(url)
|
|
485
|
+
|
|
486
|
+
# Compute cut-dirs so files land directly in dst_dir
|
|
487
|
+
cut_dirs = compute_cut_dirs(url)
|
|
488
|
+
|
|
489
|
+
# Make destination directory
|
|
490
|
+
os.makedirs(dst_dir, exist_ok=True)
|
|
491
|
+
|
|
492
|
+
# Build wget command
|
|
493
|
+
cmd = build_ftp_server_wget_command(
|
|
494
|
+
url,
|
|
495
|
+
cut_dirs=cut_dirs,
|
|
496
|
+
dst_dir=dst_dir,
|
|
497
|
+
force=force,
|
|
498
|
+
verbose=verbose,
|
|
499
|
+
)
|
|
500
|
+
# Run wget
|
|
501
|
+
try:
|
|
502
|
+
subprocess.run(cmd, check=True)
|
|
503
|
+
except subprocess.CalledProcessError as e:
|
|
504
|
+
raise subprocess.CalledProcessError(
|
|
505
|
+
returncode=e.returncode,
|
|
506
|
+
cmd=e.cmd,
|
|
507
|
+
output=e.output,
|
|
508
|
+
stderr=e.stderr,
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
|
|
396
512
|
####--------------------------------------------------------------------.
|
|
397
513
|
#### Download from Zenodo
|
|
398
514
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
radar_enabled: True
|
|
@@ -1,12 +1,20 @@
|
|
|
1
|
-
temporal_resolutions: ["1MIN"
|
|
2
|
-
models:
|
|
1
|
+
temporal_resolutions: ["1MIN"] #, "5MIN", "10MIN"]
|
|
2
|
+
models:
|
|
3
|
+
[
|
|
4
|
+
"GAMMA_ML",
|
|
5
|
+
"GAMMA_GS_ND_MAE",
|
|
6
|
+
"NGAMMA_GS_LOG_ND_MAE",
|
|
7
|
+
"NGAMMA_GS_ND_MAE",
|
|
8
|
+
"LOGNORMAL_ML",
|
|
9
|
+
"LOGNORMAL_GS_ND_MAE",
|
|
10
|
+
]
|
|
3
11
|
archive_options:
|
|
4
12
|
strategy: time_block
|
|
5
13
|
strategy_options:
|
|
6
14
|
freq: month
|
|
7
15
|
folder_partitioning: ""
|
|
8
16
|
product_options:
|
|
9
|
-
|
|
17
|
+
fall_velocity_model: "Beard1976"
|
|
10
18
|
diameter_min: 0
|
|
11
19
|
diameter_max: 10
|
|
12
20
|
diameter_spacing: 0.05
|
disdrodb/issue/writer.py
CHANGED
|
@@ -120,9 +120,11 @@ def create_station_issue(data_source, campaign_name, station_name, metadata_arch
|
|
|
120
120
|
)
|
|
121
121
|
if os.path.exists(issue_filepath):
|
|
122
122
|
raise ValueError("A issue YAML file already exists at {issue_filepath}.")
|
|
123
|
+
|
|
123
124
|
# Create issue dir if not existing
|
|
124
125
|
issue_dir = os.path.dirname(issue_filepath)
|
|
125
126
|
os.makedirs(issue_dir, exist_ok=True)
|
|
127
|
+
|
|
126
128
|
# Write issue file
|
|
127
129
|
write_issue(filepath=issue_filepath)
|
|
128
130
|
print(f"An empty issue YAML file for station {station_name} has been created .")
|
disdrodb/l0/check_configs.py
CHANGED
|
@@ -22,7 +22,7 @@ import os
|
|
|
22
22
|
from typing import Optional, Union
|
|
23
23
|
|
|
24
24
|
import numpy as np
|
|
25
|
-
from pydantic import BaseModel, ValidationError, field_validator, model_validator
|
|
25
|
+
from pydantic import BaseModel, Field, ValidationError, field_validator, model_validator
|
|
26
26
|
|
|
27
27
|
from disdrodb.api.configs import available_sensor_names, get_sensor_configs_dir, read_config_file
|
|
28
28
|
from disdrodb.l0.standards import (
|
|
@@ -47,7 +47,7 @@ CONFIG_FILES_LIST = [
|
|
|
47
47
|
]
|
|
48
48
|
|
|
49
49
|
|
|
50
|
-
def
|
|
50
|
+
def check_yaml_files_exists(sensor_name: str) -> None:
|
|
51
51
|
"""Check if all L0 config YAML files exist.
|
|
52
52
|
|
|
53
53
|
Parameters
|
|
@@ -64,7 +64,7 @@ def _check_yaml_files_exists(sensor_name: str) -> None:
|
|
|
64
64
|
raise FileNotFoundError(f"Missing YAML files {missing_keys_text} in {config_dir} for sensor {sensor_name}.")
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
def
|
|
67
|
+
def check_variable_consistency(sensor_name: str) -> None:
|
|
68
68
|
"""
|
|
69
69
|
Check variable consistency across config files.
|
|
70
70
|
|
|
@@ -126,7 +126,7 @@ def _schema_error(object_to_validate: Union[str, list], schema: BaseModel, messa
|
|
|
126
126
|
|
|
127
127
|
|
|
128
128
|
class L0BEncodingSchema(BaseModel):
|
|
129
|
-
"""Pydantic model for DISDRODB
|
|
129
|
+
"""Pydantic model for DISDRODB netCDF encodings."""
|
|
130
130
|
|
|
131
131
|
contiguous: bool
|
|
132
132
|
dtype: str
|
|
@@ -134,7 +134,7 @@ class L0BEncodingSchema(BaseModel):
|
|
|
134
134
|
complevel: int
|
|
135
135
|
shuffle: bool
|
|
136
136
|
fletcher32: bool
|
|
137
|
-
|
|
137
|
+
FillValue: Optional[Union[int, float]] = Field(default=None, alias="_FillValue")
|
|
138
138
|
chunksizes: Optional[Union[int, list[int]]]
|
|
139
139
|
|
|
140
140
|
# if contiguous=False, chunksizes specified, otherwise should be not !
|
|
@@ -167,6 +167,39 @@ class L0BEncodingSchema(BaseModel):
|
|
|
167
167
|
raise ValueError("'fletcher32' must be set to False if 'contiguous' is True")
|
|
168
168
|
return values
|
|
169
169
|
|
|
170
|
+
# if dtype is integer/unsigned integer, _FillValue must be specified and valid
|
|
171
|
+
@model_validator(mode="before")
|
|
172
|
+
def check_integer_fillvalue(cls, values):
|
|
173
|
+
"""Check that integer dtypes have valid _FillValue."""
|
|
174
|
+
dtype = values.get("dtype")
|
|
175
|
+
fill_value = values.get("_FillValue", None)
|
|
176
|
+
integer_types = ["int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64"]
|
|
177
|
+
# Check if dtype is an integer type
|
|
178
|
+
if dtype in integer_types:
|
|
179
|
+
# _FillValue must be specified for integer types
|
|
180
|
+
if fill_value is None:
|
|
181
|
+
raise ValueError(f"'_FillValue' must be specified for integer dtype '{dtype}'")
|
|
182
|
+
|
|
183
|
+
# Check that _FillValue is within valid range for the dtype
|
|
184
|
+
dtype_info = np.iinfo(dtype)
|
|
185
|
+
max_value = dtype_info.max
|
|
186
|
+
# min_value = dtype_info.min
|
|
187
|
+
|
|
188
|
+
# if not (min_value <= fill_value <= max_value):
|
|
189
|
+
# raise ValueError(
|
|
190
|
+
# f"'_FillValue' ({fill_value}) is out of range for dtype '{dtype}'. "
|
|
191
|
+
# f"Valid range is [{min_value}, {max_value}]",
|
|
192
|
+
# )
|
|
193
|
+
|
|
194
|
+
# Check that _FillValue corresponds to the maximum allowed value
|
|
195
|
+
if fill_value != max_value:
|
|
196
|
+
raise ValueError(
|
|
197
|
+
f"'_FillValue' ({fill_value}) should be set to the maximum allowed value "
|
|
198
|
+
f"({max_value}) for integer dtype '{dtype}'",
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
return values
|
|
202
|
+
|
|
170
203
|
|
|
171
204
|
def check_l0b_encoding(sensor_name: str) -> None:
|
|
172
205
|
"""Check ``l0b_encodings.yml`` file based on the schema defined in the class ``L0BEncodingSchema``.
|
|
@@ -176,7 +209,7 @@ def check_l0b_encoding(sensor_name: str) -> None:
|
|
|
176
209
|
sensor_name : str
|
|
177
210
|
Name of the sensor.
|
|
178
211
|
"""
|
|
179
|
-
data = read_config_file(sensor_name, product="
|
|
212
|
+
data = read_config_file(sensor_name, product="L0B", filename="l0b_encodings.yml")
|
|
180
213
|
|
|
181
214
|
# check that the second level of the dictionary match the schema
|
|
182
215
|
for key, value in data.items():
|
|
@@ -234,7 +267,7 @@ class RawDataFormatSchema(BaseModel):
|
|
|
234
267
|
return None
|
|
235
268
|
|
|
236
269
|
|
|
237
|
-
def
|
|
270
|
+
def check_raw_data_format(sensor_name: str) -> None:
|
|
238
271
|
"""Check ``raw_data_format.yml`` file based on the schema defined in the class ``RawDataFormatSchema``.
|
|
239
272
|
|
|
240
273
|
Parameters
|
|
@@ -253,7 +286,7 @@ def _check_raw_data_format(sensor_name: str) -> None:
|
|
|
253
286
|
)
|
|
254
287
|
|
|
255
288
|
|
|
256
|
-
def
|
|
289
|
+
def check_cf_attributes(sensor_name: str) -> None:
|
|
257
290
|
"""Check that the ``l0b_cf_attrs.yml`` description, long_name and units values are strings.
|
|
258
291
|
|
|
259
292
|
Parameters
|
|
@@ -268,7 +301,7 @@ def _check_cf_attributes(sensor_name: str) -> None:
|
|
|
268
301
|
raise ValueError(f"Wrong value for {key} in {var} for sensor {sensor_name}.")
|
|
269
302
|
|
|
270
303
|
|
|
271
|
-
def
|
|
304
|
+
def check_bin_consistency(sensor_name: str) -> None:
|
|
272
305
|
"""Check bin consistency from config file.
|
|
273
306
|
|
|
274
307
|
Do not check the first and last bin !
|
|
@@ -313,7 +346,7 @@ def _check_bin_consistency(sensor_name: str) -> None:
|
|
|
313
346
|
)
|
|
314
347
|
|
|
315
348
|
|
|
316
|
-
def
|
|
349
|
+
def check_raw_array(sensor_name: str) -> None:
|
|
317
350
|
"""Check raw array consistency from config file.
|
|
318
351
|
|
|
319
352
|
Parameters
|
|
@@ -363,14 +396,14 @@ def check_sensor_configs(sensor_name: str) -> None:
|
|
|
363
396
|
sensor_name : str
|
|
364
397
|
Name of the sensor.
|
|
365
398
|
"""
|
|
366
|
-
|
|
367
|
-
|
|
399
|
+
check_yaml_files_exists(sensor_name)
|
|
400
|
+
check_variable_consistency(sensor_name)
|
|
368
401
|
check_l0b_encoding(sensor_name=sensor_name)
|
|
369
402
|
check_l0a_encoding(sensor_name=sensor_name)
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
403
|
+
check_raw_data_format(sensor_name=sensor_name)
|
|
404
|
+
check_cf_attributes(sensor_name=sensor_name)
|
|
405
|
+
check_bin_consistency(sensor_name=sensor_name)
|
|
406
|
+
check_raw_array(sensor_name=sensor_name)
|
|
374
407
|
|
|
375
408
|
|
|
376
409
|
def check_all_sensors_configs() -> None:
|
|
@@ -15,7 +15,7 @@ reflectivity: "float32"
|
|
|
15
15
|
quality_index: "float32" # 'uint8'
|
|
16
16
|
max_hail_diameter: "float32"
|
|
17
17
|
laser_status: "float32" # 'uint8'
|
|
18
|
-
|
|
18
|
+
static_signal_status: "float32" # 'uint8'
|
|
19
19
|
laser_temperature_analog_status: "float32" # 'uint8'
|
|
20
20
|
laser_temperature_digital_status: "float32" # 'uint8'
|
|
21
21
|
laser_current_analog_status: "float32" # 'uint8'
|
|
@@ -29,7 +29,7 @@ current_heating_house_status: "float32" # 'uint8'
|
|
|
29
29
|
current_heating_heads_status: "float32" # 'uint8'
|
|
30
30
|
current_heating_carriers_status: "float32" # 'uint8'
|
|
31
31
|
control_output_laser_power_status: "float32" # 'uint8'
|
|
32
|
-
|
|
32
|
+
reserved_status: "float32" # 'uint8'
|
|
33
33
|
temperature_interior: "float32" # 'uint16'
|
|
34
34
|
laser_temperature: "float32" # 'uint16'
|
|
35
35
|
laser_current_average: "float32" # 'uint16'
|
|
@@ -66,7 +66,7 @@ laser_status:
|
|
|
66
66
|
description: Status Laser (OK/on:0, off:1)
|
|
67
67
|
long_name: Status laser
|
|
68
68
|
units: ""
|
|
69
|
-
|
|
69
|
+
static_signal_status:
|
|
70
70
|
description: Static signal (OK:0, Error:1)
|
|
71
71
|
long_name: Static signal
|
|
72
72
|
units: ""
|
|
@@ -122,7 +122,7 @@ control_output_laser_power_status:
|
|
|
122
122
|
description: Status Control output laser power (OK:0, warning:1)
|
|
123
123
|
long_name: Status control output laser
|
|
124
124
|
units: ""
|
|
125
|
-
|
|
125
|
+
reserved_status:
|
|
126
126
|
description: Reserve Status (0)
|
|
127
127
|
long_name: Reserve status
|
|
128
128
|
units: ""
|
|
@@ -184,7 +184,7 @@ laser_status:
|
|
|
184
184
|
contiguous: false
|
|
185
185
|
chunksizes: 5000
|
|
186
186
|
_FillValue: 255
|
|
187
|
-
|
|
187
|
+
static_signal_status:
|
|
188
188
|
dtype: uint8
|
|
189
189
|
zlib: true
|
|
190
190
|
complevel: 3
|
|
@@ -310,7 +310,7 @@ control_output_laser_power_status:
|
|
|
310
310
|
contiguous: false
|
|
311
311
|
chunksizes: 5000
|
|
312
312
|
_FillValue: 255
|
|
313
|
-
|
|
313
|
+
reserved_status:
|
|
314
314
|
dtype: uint8
|
|
315
315
|
zlib: true
|
|
316
316
|
complevel: 3
|
|
@@ -213,7 +213,7 @@ laser_status:
|
|
|
213
213
|
- 0
|
|
214
214
|
- 1
|
|
215
215
|
field_number: "22"
|
|
216
|
-
|
|
216
|
+
static_signal_status:
|
|
217
217
|
n_digits: 1
|
|
218
218
|
n_characters: 1
|
|
219
219
|
n_decimals: 1
|
|
@@ -389,7 +389,7 @@ control_output_laser_power_status:
|
|
|
389
389
|
- 0
|
|
390
390
|
- 1
|
|
391
391
|
field_number: "36"
|
|
392
|
-
|
|
392
|
+
reserved_status:
|
|
393
393
|
n_digits: 1
|
|
394
394
|
n_characters: 1
|
|
395
395
|
n_decimals: 1
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
center:
|
|
2
|
+
0: 0.2
|
|
3
|
+
1: 0.45
|
|
4
|
+
2: 0.55
|
|
5
|
+
3: 0.65
|
|
6
|
+
4: 0.75
|
|
7
|
+
5: 0.85
|
|
8
|
+
6: 0.95
|
|
9
|
+
7: 1.05
|
|
10
|
+
8: 1.15
|
|
11
|
+
9: 1.25
|
|
12
|
+
10: 1.35
|
|
13
|
+
11: 1.5
|
|
14
|
+
12: 1.75
|
|
15
|
+
13: 2.08
|
|
16
|
+
14: 2.475
|
|
17
|
+
15: 2.945
|
|
18
|
+
16: 3.5
|
|
19
|
+
17: 4.16
|
|
20
|
+
18: 4.95
|
|
21
|
+
19: 5.89
|
|
22
|
+
20: 6.7
|
|
23
|
+
bounds:
|
|
24
|
+
0:
|
|
25
|
+
- 0.0
|
|
26
|
+
- 0.4
|
|
27
|
+
1:
|
|
28
|
+
- 0.4
|
|
29
|
+
- 0.5
|
|
30
|
+
2:
|
|
31
|
+
- 0.5
|
|
32
|
+
- 0.6
|
|
33
|
+
3:
|
|
34
|
+
- 0.6
|
|
35
|
+
- 0.7
|
|
36
|
+
4:
|
|
37
|
+
- 0.7
|
|
38
|
+
- 0.8
|
|
39
|
+
5:
|
|
40
|
+
- 0.8
|
|
41
|
+
- 0.9
|
|
42
|
+
6:
|
|
43
|
+
- 0.9
|
|
44
|
+
- 1.0
|
|
45
|
+
7:
|
|
46
|
+
- 1.0
|
|
47
|
+
- 1.1
|
|
48
|
+
8:
|
|
49
|
+
- 1.1
|
|
50
|
+
- 1.2
|
|
51
|
+
9:
|
|
52
|
+
- 1.2
|
|
53
|
+
- 1.3
|
|
54
|
+
10:
|
|
55
|
+
- 1.3
|
|
56
|
+
- 1.4
|
|
57
|
+
11:
|
|
58
|
+
- 1.4
|
|
59
|
+
- 1.6
|
|
60
|
+
12:
|
|
61
|
+
- 1.6
|
|
62
|
+
- 1.9
|
|
63
|
+
13:
|
|
64
|
+
- 1.9
|
|
65
|
+
- 2.26
|
|
66
|
+
14:
|
|
67
|
+
- 2.26
|
|
68
|
+
- 2.69
|
|
69
|
+
15:
|
|
70
|
+
- 2.69
|
|
71
|
+
- 3.2
|
|
72
|
+
16:
|
|
73
|
+
- 3.2
|
|
74
|
+
- 3.8
|
|
75
|
+
17:
|
|
76
|
+
- 3.8
|
|
77
|
+
- 4.52
|
|
78
|
+
18:
|
|
79
|
+
- 4.52
|
|
80
|
+
- 5.38
|
|
81
|
+
19:
|
|
82
|
+
- 5.38
|
|
83
|
+
- 6.4
|
|
84
|
+
20:
|
|
85
|
+
- 6.4
|
|
86
|
+
- 7.0
|
|
87
|
+
width:
|
|
88
|
+
0: 0.4
|
|
89
|
+
1: 0.1
|
|
90
|
+
2: 0.1
|
|
91
|
+
3: 0.1
|
|
92
|
+
4: 0.1
|
|
93
|
+
5: 0.1
|
|
94
|
+
6: 0.1
|
|
95
|
+
7: 0.1
|
|
96
|
+
8: 0.1
|
|
97
|
+
9: 0.1
|
|
98
|
+
10: 0.1
|
|
99
|
+
11: 0.2
|
|
100
|
+
12: 0.3
|
|
101
|
+
13: 0.36
|
|
102
|
+
14: 0.43
|
|
103
|
+
15: 0.51
|
|
104
|
+
16: 0.6
|
|
105
|
+
17: 0.72
|
|
106
|
+
18: 0.86
|
|
107
|
+
19: 1.02
|
|
108
|
+
20: 0.6
|