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
|
@@ -20,7 +20,7 @@ import time
|
|
|
20
20
|
from typing import Optional
|
|
21
21
|
|
|
22
22
|
from disdrodb.api.search import available_stations, get_required_product
|
|
23
|
-
from disdrodb.utils.cli import
|
|
23
|
+
from disdrodb.utils.cli import execute_cmd
|
|
24
24
|
|
|
25
25
|
####--------------------------------------------------------------------------.
|
|
26
26
|
#### Run DISDRODB Station Processing
|
|
@@ -239,7 +239,7 @@ def run_l0a_station(
|
|
|
239
239
|
],
|
|
240
240
|
)
|
|
241
241
|
# Execute command
|
|
242
|
-
|
|
242
|
+
execute_cmd(cmd)
|
|
243
243
|
|
|
244
244
|
|
|
245
245
|
def run_l0b_station(
|
|
@@ -323,7 +323,7 @@ def run_l0b_station(
|
|
|
323
323
|
],
|
|
324
324
|
)
|
|
325
325
|
# Execute command
|
|
326
|
-
|
|
326
|
+
execute_cmd(cmd)
|
|
327
327
|
|
|
328
328
|
|
|
329
329
|
def run_l0c_station(
|
|
@@ -407,7 +407,7 @@ def run_l0c_station(
|
|
|
407
407
|
],
|
|
408
408
|
)
|
|
409
409
|
# Execute command
|
|
410
|
-
|
|
410
|
+
execute_cmd(cmd)
|
|
411
411
|
|
|
412
412
|
|
|
413
413
|
def run_l1_station(
|
|
@@ -483,7 +483,7 @@ def run_l1_station(
|
|
|
483
483
|
],
|
|
484
484
|
)
|
|
485
485
|
# Execute command
|
|
486
|
-
|
|
486
|
+
execute_cmd(cmd)
|
|
487
487
|
|
|
488
488
|
|
|
489
489
|
def run_l2e_station(
|
|
@@ -559,7 +559,7 @@ def run_l2e_station(
|
|
|
559
559
|
],
|
|
560
560
|
)
|
|
561
561
|
# Execute command
|
|
562
|
-
|
|
562
|
+
execute_cmd(cmd)
|
|
563
563
|
|
|
564
564
|
|
|
565
565
|
def run_l2m_station(
|
|
@@ -635,7 +635,36 @@ def run_l2m_station(
|
|
|
635
635
|
],
|
|
636
636
|
)
|
|
637
637
|
# Execute command
|
|
638
|
-
|
|
638
|
+
execute_cmd(cmd)
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
def create_summary_station(
|
|
642
|
+
data_source,
|
|
643
|
+
campaign_name,
|
|
644
|
+
station_name,
|
|
645
|
+
parallel=False,
|
|
646
|
+
temporal_resolution="1MIN",
|
|
647
|
+
data_archive_dir=None,
|
|
648
|
+
):
|
|
649
|
+
"""Create summary figures and tables for a DISDRODB station."""
|
|
650
|
+
# Define command
|
|
651
|
+
cmd = " ".join(
|
|
652
|
+
[
|
|
653
|
+
"disdrodb_create_summary_station",
|
|
654
|
+
# Station arguments
|
|
655
|
+
data_source,
|
|
656
|
+
campaign_name,
|
|
657
|
+
station_name,
|
|
658
|
+
"--data_archive_dir",
|
|
659
|
+
str(data_archive_dir),
|
|
660
|
+
"--parallel",
|
|
661
|
+
str(parallel),
|
|
662
|
+
"--temporal_resolution",
|
|
663
|
+
str(temporal_resolution),
|
|
664
|
+
],
|
|
665
|
+
)
|
|
666
|
+
# Execute command
|
|
667
|
+
execute_cmd(cmd)
|
|
639
668
|
|
|
640
669
|
|
|
641
670
|
####--------------------------------------------------------------------------.
|
|
@@ -1409,4 +1438,68 @@ def run_l2m(
|
|
|
1409
1438
|
print(f"{product} processing of {data_source} {campaign_name} {station_name} station ended.")
|
|
1410
1439
|
|
|
1411
1440
|
|
|
1441
|
+
def create_summary(
|
|
1442
|
+
data_sources=None,
|
|
1443
|
+
campaign_names=None,
|
|
1444
|
+
station_names=None,
|
|
1445
|
+
parallel=False,
|
|
1446
|
+
temporal_resolution="1MIN",
|
|
1447
|
+
data_archive_dir=None,
|
|
1448
|
+
metadata_archive_dir=None,
|
|
1449
|
+
):
|
|
1450
|
+
"""Create summary figures and tables for a set of DISDRODB station.
|
|
1451
|
+
|
|
1452
|
+
Parameters
|
|
1453
|
+
----------
|
|
1454
|
+
data_sources : list
|
|
1455
|
+
Name of data source(s) to process.
|
|
1456
|
+
The name(s) must be UPPER CASE.
|
|
1457
|
+
If campaign_names and station are not specified, process all stations.
|
|
1458
|
+
The default value is ``None``.
|
|
1459
|
+
campaign_names : list
|
|
1460
|
+
Name of the campaign(s) to process.
|
|
1461
|
+
The name(s) must be UPPER CASE.
|
|
1462
|
+
The default value is ``None``.
|
|
1463
|
+
station_names : list
|
|
1464
|
+
Station names to process.
|
|
1465
|
+
The default value is ``None``.
|
|
1466
|
+
data_archive_dir : str (optional)
|
|
1467
|
+
The directory path where the DISDRODB Data Archive is located.
|
|
1468
|
+
The directory path must end with ``<...>/DISDRODB``.
|
|
1469
|
+
If ``None``, it uses the ``data_archive_dir`` path specified
|
|
1470
|
+
in the DISDRODB active configuration.
|
|
1471
|
+
"""
|
|
1472
|
+
# Get list of available stations
|
|
1473
|
+
list_info = available_stations(
|
|
1474
|
+
# DISDRODB root directories
|
|
1475
|
+
data_archive_dir=data_archive_dir,
|
|
1476
|
+
metadata_archive_dir=metadata_archive_dir,
|
|
1477
|
+
# Stations arguments
|
|
1478
|
+
data_sources=data_sources,
|
|
1479
|
+
campaign_names=campaign_names,
|
|
1480
|
+
station_names=station_names,
|
|
1481
|
+
# Search options
|
|
1482
|
+
product="L2E",
|
|
1483
|
+
product_kwargs={"rolling": False, "sample_interval": 60},
|
|
1484
|
+
raise_error_if_empty=True,
|
|
1485
|
+
)
|
|
1486
|
+
|
|
1487
|
+
# Loop over stations
|
|
1488
|
+
print(f"Creation of summaries for {len(list_info)} stations has started.")
|
|
1489
|
+
for data_source, campaign_name, station_name in list_info:
|
|
1490
|
+
# Run processing
|
|
1491
|
+
create_summary_station(
|
|
1492
|
+
# DISDRODB root directories
|
|
1493
|
+
data_archive_dir=data_archive_dir,
|
|
1494
|
+
# Station arguments
|
|
1495
|
+
data_source=data_source,
|
|
1496
|
+
campaign_name=campaign_name,
|
|
1497
|
+
station_name=station_name,
|
|
1498
|
+
# Processing option
|
|
1499
|
+
parallel=parallel,
|
|
1500
|
+
temporal_resolution=temporal_resolution,
|
|
1501
|
+
)
|
|
1502
|
+
print("Creation of station summaries has terminated.")
|
|
1503
|
+
|
|
1504
|
+
|
|
1412
1505
|
####--------------------------------------------------------------------------.
|
|
@@ -20,90 +20,6 @@ import numpy as np
|
|
|
20
20
|
import xarray as xr
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
def available_axis_ratio_models():
|
|
24
|
-
"""Return a list of the available drop axis ratio models."""
|
|
25
|
-
return list(AXIS_RATIO_MODELS)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def get_axis_ratio_model(model):
|
|
29
|
-
"""Return the specified drop axis ratio model.
|
|
30
|
-
|
|
31
|
-
Parameters
|
|
32
|
-
----------
|
|
33
|
-
model : str
|
|
34
|
-
The model to use for calculating the axis ratio. Available models are:
|
|
35
|
-
'Thurai2005', 'Thurai2007', 'Battaglia2010', 'Brandes2002',
|
|
36
|
-
'Pruppacher1970', 'Beard1987', 'Andsager1999'.
|
|
37
|
-
|
|
38
|
-
Returns
|
|
39
|
-
-------
|
|
40
|
-
callable
|
|
41
|
-
A function which compute the vertical-to-horizontal axis ratio given a
|
|
42
|
-
particle diameter in mm.
|
|
43
|
-
|
|
44
|
-
Notes
|
|
45
|
-
-----
|
|
46
|
-
This function serves as a wrapper to various axis ratio models for raindrops.
|
|
47
|
-
It returns the appropriate model based on the `model` parameter.
|
|
48
|
-
|
|
49
|
-
Please note that the axis ratio function to be provided to pyTmatrix expects to
|
|
50
|
-
return a horizontal-to-vertical axis ratio !
|
|
51
|
-
|
|
52
|
-
"""
|
|
53
|
-
model = check_axis_ratio_model(model)
|
|
54
|
-
return AXIS_RATIO_MODELS[model]
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def check_axis_ratio_model(model):
|
|
58
|
-
"""Check validity of the specified drop axis ratio model."""
|
|
59
|
-
available_models = available_axis_ratio_models()
|
|
60
|
-
if model not in available_models:
|
|
61
|
-
raise ValueError(f"{model} is an invalid axis-ratio model. Valid models: {available_models}.")
|
|
62
|
-
return model
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def get_axis_ratio(diameter, model):
|
|
66
|
-
"""
|
|
67
|
-
Compute the axis ratio of raindrops using the specified model.
|
|
68
|
-
|
|
69
|
-
Parameters
|
|
70
|
-
----------
|
|
71
|
-
diameter : array-like
|
|
72
|
-
Raindrops diameter in mm.
|
|
73
|
-
model : str
|
|
74
|
-
The axis ratio model to use for calculating the axis ratio. Available models are:
|
|
75
|
-
'Thurai2005', 'Thurai2007', 'Battaglia2010', 'Brandes2002',
|
|
76
|
-
'Pruppacher1970', 'Beard1987', 'Andsager1999'.
|
|
77
|
-
|
|
78
|
-
Returns
|
|
79
|
-
-------
|
|
80
|
-
axis_ratio : array-like
|
|
81
|
-
The vertical-to-horizontal drop axis ratio corresponding to the input diameters.
|
|
82
|
-
Values of 1 indicate spherical particles, while values <1 indicate oblate particles.
|
|
83
|
-
Values >1 means prolate particles.
|
|
84
|
-
|
|
85
|
-
Notes
|
|
86
|
-
-----
|
|
87
|
-
This function serves as a wrapper to various axis ratio models for raindrops.
|
|
88
|
-
It selects and applies the appropriate model based on the `model` parameter.
|
|
89
|
-
|
|
90
|
-
Examples
|
|
91
|
-
--------
|
|
92
|
-
>>> diameter = np.array([0.5, 1.0, 2.0, 3.0])
|
|
93
|
-
>>> axis_ratio = get_axis_ratio(diameter, model="Brandes2002")
|
|
94
|
-
|
|
95
|
-
"""
|
|
96
|
-
# Retrieve axis ratio function
|
|
97
|
-
axis_ratio_func = get_axis_ratio_model(model)
|
|
98
|
-
|
|
99
|
-
# Retrieve axis ratio
|
|
100
|
-
axis_ratio = axis_ratio_func(diameter)
|
|
101
|
-
|
|
102
|
-
# Clip values between 0 and 1
|
|
103
|
-
axis_ratio = np.clip(axis_ratio, 0, 1)
|
|
104
|
-
return axis_ratio
|
|
105
|
-
|
|
106
|
-
|
|
107
23
|
def get_axis_ratio_andsager_1999(diameter):
|
|
108
24
|
"""
|
|
109
25
|
Compute the axis ratio of raindrops using the Andsager et al. (1999) model.
|
|
@@ -295,6 +211,8 @@ def get_axis_ratio_thurai_2005(diameter):
|
|
|
295
211
|
"""
|
|
296
212
|
Compute the axis ratio of raindrops using the Thurai et al. (2005) model.
|
|
297
213
|
|
|
214
|
+
This model is assumed to be valid only for particles up to 5 mm.
|
|
215
|
+
|
|
298
216
|
Parameters
|
|
299
217
|
----------
|
|
300
218
|
diameter : array-like
|
|
@@ -311,7 +229,6 @@ def get_axis_ratio_thurai_2005(diameter):
|
|
|
311
229
|
J. Atmos. Oceanic Technol., 22, 966-978, https://doi.org/10.1175/JTECH1767.1
|
|
312
230
|
|
|
313
231
|
"""
|
|
314
|
-
# Valid between 1 and 5 mm
|
|
315
232
|
axis_ratio = 0.9707 + 4.26e-2 * diameter - 4.29e-2 * diameter**2 + 6.5e-3 * diameter**3 - 3e-4 * diameter**4
|
|
316
233
|
return axis_ratio
|
|
317
234
|
|
|
@@ -350,6 +267,9 @@ def get_axis_ratio_thurai_2007(diameter):
|
|
|
350
267
|
# Combine axis ratio
|
|
351
268
|
axis_ratio_below_1_5 = xr.where(diameter > 0.7, axis_ratio_below_1_5, axis_ratio_below_0_7)
|
|
352
269
|
axis_ratio = xr.where(diameter > 1.5, axis_ratio_above_1_5, axis_ratio_below_1_5)
|
|
270
|
+
|
|
271
|
+
# Ensure np.nan is preserved in arrays
|
|
272
|
+
axis_ratio = xr.where(np.isnan(diameter), np.nan, axis_ratio)
|
|
353
273
|
return axis_ratio
|
|
354
274
|
|
|
355
275
|
|
|
@@ -362,3 +282,93 @@ AXIS_RATIO_MODELS = {
|
|
|
362
282
|
"Beard1987": get_axis_ratio_beard_1987,
|
|
363
283
|
"Andsager1999": get_axis_ratio_andsager_1999,
|
|
364
284
|
}
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def available_axis_ratio_models():
|
|
288
|
+
"""Return a list of the available drop axis ratio models."""
|
|
289
|
+
return list(AXIS_RATIO_MODELS)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def check_axis_ratio_model(model):
|
|
293
|
+
"""Check validity of the specified drop axis ratio model."""
|
|
294
|
+
available_models = available_axis_ratio_models()
|
|
295
|
+
if model not in available_models:
|
|
296
|
+
raise ValueError(f"{model} is an invalid axis-ratio model. Valid models: {available_models}.")
|
|
297
|
+
return model
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def get_axis_ratio_model(model):
|
|
301
|
+
"""Return the specified drop axis ratio model.
|
|
302
|
+
|
|
303
|
+
Parameters
|
|
304
|
+
----------
|
|
305
|
+
model : str
|
|
306
|
+
The model to use for calculating the axis ratio. Available models are:
|
|
307
|
+
'Thurai2005', 'Thurai2007', 'Battaglia2010', 'Brandes2002',
|
|
308
|
+
'Pruppacher1970', 'Beard1987', 'Andsager1999'.
|
|
309
|
+
|
|
310
|
+
Returns
|
|
311
|
+
-------
|
|
312
|
+
callable
|
|
313
|
+
A function which compute the vertical-to-horizontal axis ratio given a
|
|
314
|
+
particle diameter in mm.
|
|
315
|
+
|
|
316
|
+
Notes
|
|
317
|
+
-----
|
|
318
|
+
This function serves as a wrapper to various axis ratio models for raindrops.
|
|
319
|
+
It returns the appropriate model based on the `model` parameter.
|
|
320
|
+
|
|
321
|
+
Please note that the axis ratio function to be provided to pyTmatrix expects to
|
|
322
|
+
return a horizontal-to-vertical axis ratio !
|
|
323
|
+
|
|
324
|
+
"""
|
|
325
|
+
model = check_axis_ratio_model(model)
|
|
326
|
+
return AXIS_RATIO_MODELS[model]
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def get_axis_ratio(diameter, model):
|
|
330
|
+
"""
|
|
331
|
+
Compute the axis ratio of raindrops using the specified model.
|
|
332
|
+
|
|
333
|
+
Parameters
|
|
334
|
+
----------
|
|
335
|
+
diameter : array-like
|
|
336
|
+
Raindrops diameter in mm.
|
|
337
|
+
model : str
|
|
338
|
+
The axis ratio model to use for calculating the axis ratio. Available models are:
|
|
339
|
+
'Thurai2005', 'Thurai2007', 'Battaglia2010', 'Brandes2002',
|
|
340
|
+
'Pruppacher1970', 'Beard1987', 'Andsager1999'.
|
|
341
|
+
|
|
342
|
+
Returns
|
|
343
|
+
-------
|
|
344
|
+
axis_ratio : array-like
|
|
345
|
+
The vertical-to-horizontal drop axis ratio corresponding to the input diameters.
|
|
346
|
+
Values of 1 indicate spherical particles, while values <1 indicate oblate particles.
|
|
347
|
+
Values >1 means prolate particles.
|
|
348
|
+
|
|
349
|
+
Notes
|
|
350
|
+
-----
|
|
351
|
+
This function serves as a wrapper to various axis ratio models for raindrops.
|
|
352
|
+
It selects and applies the appropriate model based on the `model` parameter.
|
|
353
|
+
|
|
354
|
+
Examples
|
|
355
|
+
--------
|
|
356
|
+
>>> diameter = np.array([0.5, 1.0, 2.0, 3.0])
|
|
357
|
+
>>> axis_ratio = get_axis_ratio(diameter, model="Brandes2002")
|
|
358
|
+
|
|
359
|
+
"""
|
|
360
|
+
# Retrieve axis ratio function
|
|
361
|
+
axis_ratio_func = get_axis_ratio_model(model)
|
|
362
|
+
|
|
363
|
+
# Retrieve axis ratio
|
|
364
|
+
axis_ratio = axis_ratio_func(diameter)
|
|
365
|
+
|
|
366
|
+
# Clip values between 0 and 1
|
|
367
|
+
axis_ratio = np.clip(axis_ratio, 0, 1)
|
|
368
|
+
|
|
369
|
+
# Add attributes
|
|
370
|
+
if isinstance(axis_ratio, xr.DataArray):
|
|
371
|
+
axis_ratio.name = "axis_ratio"
|
|
372
|
+
axis_ratio.attrs["units"] = ""
|
|
373
|
+
axis_ratio.attrs["model"] = model
|
|
374
|
+
return axis_ratio
|
|
@@ -124,11 +124,35 @@ def get_refractive_index(temperature, frequency, permittivity_model):
|
|
|
124
124
|
>>> m = get_refractive_index(temperature=temperature, frequency=frequency, permittivity_model="Liebe1991")
|
|
125
125
|
|
|
126
126
|
"""
|
|
127
|
+
# Ensure input is numpy array or xr.DataArray
|
|
128
|
+
frequency = ensure_array(frequency)
|
|
129
|
+
temperature = ensure_array(temperature)
|
|
130
|
+
|
|
131
|
+
# If both inputs are numpy (or dask) arrays with size > 1 → raise error
|
|
132
|
+
if (
|
|
133
|
+
not isinstance(temperature, xr.DataArray)
|
|
134
|
+
and not isinstance(frequency, xr.DataArray)
|
|
135
|
+
and np.size(temperature) > 1
|
|
136
|
+
and np.size(frequency) > 1
|
|
137
|
+
):
|
|
138
|
+
raise ValueError(
|
|
139
|
+
"get_refractive_index does not support broadcasting plain numpy/dask arrays "
|
|
140
|
+
"when both `temperature` and `frequency` have size > 1. "
|
|
141
|
+
"Please provide both input as xarray.DataArray objects "
|
|
142
|
+
"with different dimensions to enable labeled broadcasting.",
|
|
143
|
+
)
|
|
144
|
+
|
|
127
145
|
# Retrieve refractive_index function
|
|
128
146
|
func = get_refractive_index_function(permittivity_model)
|
|
129
147
|
|
|
130
148
|
# Retrieve refractive_index
|
|
131
149
|
refractive_index = func(temperature=temperature, frequency=frequency)
|
|
150
|
+
|
|
151
|
+
# Add attributes
|
|
152
|
+
if isinstance(refractive_index, xr.DataArray):
|
|
153
|
+
refractive_index.name = "refractive_index"
|
|
154
|
+
refractive_index.attrs["units"] = ""
|
|
155
|
+
refractive_index.attrs["model"] = permittivity_model
|
|
132
156
|
return refractive_index
|
|
133
157
|
|
|
134
158
|
|
disdrodb/scattering/routines.py
CHANGED
|
@@ -432,6 +432,41 @@ def load_scatterer(
|
|
|
432
432
|
return scatterer
|
|
433
433
|
|
|
434
434
|
|
|
435
|
+
def precompute_scattering_tables(
|
|
436
|
+
frequency,
|
|
437
|
+
num_points,
|
|
438
|
+
diameter_max,
|
|
439
|
+
canting_angle_std,
|
|
440
|
+
axis_ratio_model,
|
|
441
|
+
permittivity_model,
|
|
442
|
+
water_temperature,
|
|
443
|
+
elevation_angle,
|
|
444
|
+
verbose=True,
|
|
445
|
+
):
|
|
446
|
+
"""Precompute the pyTMatrix scattering tables required for radar variables simulations."""
|
|
447
|
+
from disdrodb.scattering.routines import get_list_simulations_params, load_scatterer
|
|
448
|
+
|
|
449
|
+
# Define parameters for all requested simulations
|
|
450
|
+
list_params = get_list_simulations_params(
|
|
451
|
+
frequency=frequency,
|
|
452
|
+
num_points=num_points,
|
|
453
|
+
diameter_max=diameter_max,
|
|
454
|
+
canting_angle_std=canting_angle_std,
|
|
455
|
+
axis_ratio_model=axis_ratio_model,
|
|
456
|
+
permittivity_model=permittivity_model,
|
|
457
|
+
water_temperature=water_temperature,
|
|
458
|
+
elevation_angle=elevation_angle,
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
# Compute require scattering tables
|
|
462
|
+
for params in list_params:
|
|
463
|
+
# Initialize scattering table
|
|
464
|
+
_ = load_scatterer(
|
|
465
|
+
verbose=verbose,
|
|
466
|
+
**params,
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
|
|
435
470
|
####----------------------------------------------------------------------
|
|
436
471
|
#### Scattering functions
|
|
437
472
|
|
|
@@ -458,12 +493,10 @@ def compute_radar_variables(scatterer):
|
|
|
458
493
|
radar_vars["DBZV"] = 10 * np.log10(radar.refl(scatterer, h_pol=False)) # dBZ
|
|
459
494
|
|
|
460
495
|
radar_vars["ZDR"] = 10 * np.log10(radar.Zdr(scatterer)) # dB
|
|
461
|
-
|
|
462
|
-
radar_vars["ZDR"] = np.nan
|
|
496
|
+
radar_vars["ZDR"] = np.where(np.isfinite(radar_vars["ZDR"]), radar_vars["ZDR"], np.nan)
|
|
463
497
|
|
|
464
498
|
radar_vars["LDR"] = 10 * np.log10(radar.ldr(scatterer)) # dBZ
|
|
465
|
-
|
|
466
|
-
radar_vars["LDR"] = np.nan
|
|
499
|
+
radar_vars["LDR"] = np.where(np.isfinite(radar_vars["LDR"]), radar_vars["LDR"], np.nan)
|
|
467
500
|
|
|
468
501
|
radar_vars["RHOHV"] = radar.rho_hv(scatterer) # deg/km
|
|
469
502
|
radar_vars["DELTAHV"] = radar.delta_hv(scatterer) * 180.0 / np.pi # [deg]
|
|
@@ -482,29 +515,26 @@ def compute_radar_variables(scatterer):
|
|
|
482
515
|
RADAR_VARIABLES = ["DBZH", "DBZV", "ZDR", "LDR", "RHOHV", "DELTAHV", "KDP", "AH", "AV", "ADP"]
|
|
483
516
|
|
|
484
517
|
|
|
485
|
-
def
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
518
|
+
def _try_compute_radar_variables(scatterer):
|
|
519
|
+
with suppress_warnings():
|
|
520
|
+
try:
|
|
521
|
+
radar_vars = compute_radar_variables(scatterer)
|
|
522
|
+
output = np.array(list(radar_vars.values()))
|
|
523
|
+
except Exception:
|
|
524
|
+
output = np.zeros(len(RADAR_VARIABLES)) * np.nan
|
|
525
|
+
return output
|
|
489
526
|
|
|
490
527
|
|
|
491
528
|
def _estimate_empirical_radar_parameters(
|
|
492
529
|
drop_number_concentration,
|
|
493
530
|
bin_edges,
|
|
494
531
|
scatterer,
|
|
495
|
-
output_dictionary,
|
|
496
532
|
):
|
|
497
533
|
# Assign PSD model to the scatterer object
|
|
498
534
|
scatterer.psd = BinnedPSD(bin_edges, drop_number_concentration)
|
|
499
535
|
|
|
500
536
|
# Get radar variables
|
|
501
|
-
|
|
502
|
-
try:
|
|
503
|
-
radar_vars = compute_radar_variables(scatterer)
|
|
504
|
-
output = radar_vars if output_dictionary else np.array(list(radar_vars.values()))
|
|
505
|
-
except Exception:
|
|
506
|
-
output = _initialize_null_output(output_dictionary)
|
|
507
|
-
return output
|
|
537
|
+
return _try_compute_radar_variables(scatterer)
|
|
508
538
|
|
|
509
539
|
|
|
510
540
|
def _estimate_model_radar_parameters(
|
|
@@ -512,23 +542,16 @@ def _estimate_model_radar_parameters(
|
|
|
512
542
|
psd_model,
|
|
513
543
|
psd_parameters_names,
|
|
514
544
|
scatterer,
|
|
515
|
-
output_dictionary,
|
|
516
545
|
):
|
|
517
546
|
# Assign PSD model to the scatterer object
|
|
518
547
|
parameters = dict(zip(psd_parameters_names, parameters))
|
|
519
548
|
scatterer.psd = create_psd(psd_model, parameters)
|
|
520
549
|
|
|
521
550
|
# Get radar variables
|
|
522
|
-
|
|
523
|
-
try:
|
|
524
|
-
radar_vars = compute_radar_variables(scatterer)
|
|
525
|
-
output = radar_vars if output_dictionary else np.array(list(radar_vars.values()))
|
|
526
|
-
except Exception:
|
|
527
|
-
output = _initialize_null_output(output_dictionary)
|
|
528
|
-
return output
|
|
551
|
+
return _try_compute_radar_variables(scatterer)
|
|
529
552
|
|
|
530
553
|
|
|
531
|
-
def
|
|
554
|
+
def select_psd_parameters(ds):
|
|
532
555
|
"""Return a xr.Dataset with only the PSD parameters as variable."""
|
|
533
556
|
psd_model = ds.attrs["disdrodb_psd_model"]
|
|
534
557
|
required_parameters = get_required_parameters(psd_model)
|
|
@@ -587,14 +610,14 @@ def get_model_radar_parameters(
|
|
|
587
610
|
# Retrieve psd model and parameters.
|
|
588
611
|
psd_model = ds.attrs["disdrodb_psd_model"]
|
|
589
612
|
required_parameters = get_required_parameters(psd_model)
|
|
590
|
-
ds_parameters =
|
|
613
|
+
ds_parameters = select_psd_parameters(ds)
|
|
591
614
|
|
|
592
615
|
# Check argument validity
|
|
593
616
|
axis_ratio_model = check_axis_ratio_model(axis_ratio_model)
|
|
594
617
|
permittivity_model = check_permittivity_model(permittivity_model)
|
|
595
618
|
|
|
596
619
|
# Create DataArray with PSD parameters
|
|
597
|
-
da_parameters = ds_parameters.to_array(dim="psd_parameters")
|
|
620
|
+
da_parameters = ds_parameters.to_array(dim="psd_parameters")
|
|
598
621
|
|
|
599
622
|
# Initialize scattering table
|
|
600
623
|
scatterer = load_scatterer(
|
|
@@ -610,7 +633,6 @@ def get_model_radar_parameters(
|
|
|
610
633
|
|
|
611
634
|
# Define kwargs
|
|
612
635
|
kwargs = {
|
|
613
|
-
"output_dictionary": False,
|
|
614
636
|
"psd_model": psd_model,
|
|
615
637
|
"psd_parameters_names": required_parameters,
|
|
616
638
|
"scatterer": scatterer,
|
|
@@ -624,9 +646,10 @@ def get_model_radar_parameters(
|
|
|
624
646
|
input_core_dims=[["psd_parameters"]],
|
|
625
647
|
output_core_dims=[["radar_variables"]],
|
|
626
648
|
vectorize=True,
|
|
627
|
-
dask="
|
|
649
|
+
dask="parallelized",
|
|
628
650
|
dask_gufunc_kwargs={
|
|
629
651
|
"output_sizes": {"radar_variables": len(RADAR_VARIABLES)},
|
|
652
|
+
"allow_rechunk": True,
|
|
630
653
|
}, # lengths of the new output_core_dims dimensions.
|
|
631
654
|
output_dtypes=["float64"],
|
|
632
655
|
)
|
|
@@ -695,7 +718,7 @@ def get_empirical_radar_parameters(
|
|
|
695
718
|
ds = filter_diameter_bins(ds=ds, maximum_diameter=diameter_max)
|
|
696
719
|
|
|
697
720
|
# Define inputs
|
|
698
|
-
da_drop_number_concentration = ds["drop_number_concentration"].compute()
|
|
721
|
+
da_drop_number_concentration = ds["drop_number_concentration"] # .compute()
|
|
699
722
|
|
|
700
723
|
# Set all zeros drop number concentration to np.nan
|
|
701
724
|
# --> Otherwise inf can appear in the output
|
|
@@ -724,11 +747,9 @@ def get_empirical_radar_parameters(
|
|
|
724
747
|
|
|
725
748
|
# Define kwargs
|
|
726
749
|
kwargs = {
|
|
727
|
-
"output_dictionary": False,
|
|
728
750
|
"bin_edges": bin_edges,
|
|
729
751
|
"scatterer": scatterer,
|
|
730
752
|
}
|
|
731
|
-
|
|
732
753
|
# Loop over each PSD (not in parallel --> dask="forbidden")
|
|
733
754
|
# - It costs much more to initiate the scatterer rather than looping over timesteps !
|
|
734
755
|
da_radar = xr.apply_ufunc(
|
|
@@ -738,13 +759,12 @@ def get_empirical_radar_parameters(
|
|
|
738
759
|
input_core_dims=[["diameter_bin_center"]],
|
|
739
760
|
output_core_dims=[["radar_variables"]],
|
|
740
761
|
vectorize=True,
|
|
741
|
-
dask="
|
|
762
|
+
dask="parallelized",
|
|
742
763
|
dask_gufunc_kwargs={
|
|
743
764
|
"output_sizes": {"radar_variables": len(RADAR_VARIABLES)},
|
|
744
765
|
}, # lengths of the new output_core_dims dimensions.
|
|
745
766
|
output_dtypes=["float64"],
|
|
746
767
|
)
|
|
747
|
-
|
|
748
768
|
# Finalize radar dataset (add name, coordinates)
|
|
749
769
|
ds_radar = _finalize_radar_dataset(
|
|
750
770
|
da_radar=da_radar,
|
|
@@ -922,11 +942,11 @@ def get_radar_parameters(
|
|
|
922
942
|
# Model-based simulation
|
|
923
943
|
if "disdrodb_psd_model" in ds.attrs:
|
|
924
944
|
func = get_model_radar_parameters
|
|
925
|
-
ds_subset =
|
|
945
|
+
ds_subset = select_psd_parameters(ds)
|
|
926
946
|
# Empirical PSD simulation
|
|
927
947
|
else:
|
|
928
948
|
func = get_empirical_radar_parameters
|
|
929
|
-
ds_subset = ds[["drop_number_concentration"]]
|
|
949
|
+
ds_subset = ds[["drop_number_concentration"]]
|
|
930
950
|
|
|
931
951
|
# Define default frequencies if not specified
|
|
932
952
|
if frequency is None:
|