disdrodb 0.1.2__py3-none-any.whl → 0.1.4__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 +68 -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 +177 -24
- disdrodb/api/configs.py +3 -3
- disdrodb/api/info.py +13 -13
- disdrodb/api/io.py +281 -22
- disdrodb/api/path.py +184 -195
- disdrodb/api/search.py +18 -9
- disdrodb/cli/disdrodb_create_summary.py +103 -0
- 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_l0a_station.py +1 -1
- disdrodb/cli/disdrodb_run_l0b.py +1 -1
- disdrodb/cli/disdrodb_run_l0b_station.py +3 -3
- disdrodb/cli/disdrodb_run_l0c.py +1 -1
- disdrodb/cli/disdrodb_run_l0c_station.py +3 -3
- 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/configs.py +149 -4
- disdrodb/constants.py +61 -0
- disdrodb/data_transfer/download_data.py +127 -11
- 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/issue/writer.py +2 -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 +37 -32
- disdrodb/l0/l0b_nc_processing.py +118 -8
- disdrodb/l0/l0b_processing.py +30 -65
- disdrodb/l0/l0c_processing.py +369 -259
- disdrodb/l0/readers/LPM/ARM/ARM_LPM.py +7 -0
- 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/ARM/ARM_PARSIVEL2.py +4 -0
- disdrodb/l0/readers/PARSIVEL2/BELGIUM/ILVO.py +168 -0
- disdrodb/l0/readers/PARSIVEL2/CANADA/UQAM_NC.py +69 -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/{NETHERLANDS/DELFT.py → MPI/BCO_PARSIVEL2.py} +41 -71
- disdrodb/l0/readers/PARSIVEL2/MPI/BOWTIE.py +220 -0
- disdrodb/l0/readers/PARSIVEL2/NASA/APU.py +120 -0
- disdrodb/l0/readers/PARSIVEL2/NASA/LPVEX.py +109 -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 +5 -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/USA/C3WE.py +146 -0
- disdrodb/l0/readers/PWS100/FRANCE/ENPC_PWS100.py +105 -99
- disdrodb/l0/readers/PWS100/FRANCE/ENPC_PWS100_SIRTA.py +151 -0
- disdrodb/l1/__init__.py +5 -0
- disdrodb/l1/fall_velocity.py +46 -0
- disdrodb/l1/filters.py +34 -20
- disdrodb/l1/processing.py +46 -45
- disdrodb/l1/resampling.py +77 -66
- disdrodb/l1_env/routines.py +18 -3
- disdrodb/l2/__init__.py +7 -0
- disdrodb/l2/empirical_dsd.py +58 -10
- disdrodb/l2/processing.py +268 -117
- disdrodb/metadata/checks.py +132 -125
- disdrodb/metadata/standards.py +3 -1
- disdrodb/psd/fitting.py +631 -345
- disdrodb/psd/models.py +9 -6
- disdrodb/routines/__init__.py +54 -0
- disdrodb/{l0/routines.py → routines/l0.py} +316 -355
- disdrodb/{l1/routines.py → routines/l1.py} +76 -116
- disdrodb/routines/l2.py +1019 -0
- disdrodb/{routines.py → routines/wrappers.py} +98 -10
- disdrodb/scattering/__init__.py +16 -4
- disdrodb/scattering/axis_ratio.py +61 -37
- disdrodb/scattering/permittivity.py +504 -0
- disdrodb/scattering/routines.py +746 -184
- disdrodb/summary/__init__.py +17 -0
- disdrodb/summary/routines.py +4196 -0
- disdrodb/utils/archiving.py +434 -0
- disdrodb/utils/attrs.py +68 -125
- disdrodb/utils/cli.py +5 -5
- disdrodb/utils/compression.py +30 -1
- disdrodb/utils/dask.py +121 -9
- disdrodb/utils/dataframe.py +61 -7
- disdrodb/utils/decorators.py +31 -0
- disdrodb/utils/directories.py +35 -15
- disdrodb/utils/encoding.py +37 -19
- disdrodb/{l2 → utils}/event.py +15 -173
- disdrodb/utils/logger.py +14 -7
- disdrodb/utils/manipulations.py +81 -0
- disdrodb/utils/routines.py +166 -0
- disdrodb/utils/subsetting.py +214 -0
- disdrodb/utils/time.py +35 -177
- disdrodb/utils/writer.py +20 -7
- disdrodb/utils/xarray.py +5 -4
- disdrodb/viz/__init__.py +13 -0
- disdrodb/viz/plots.py +398 -0
- {disdrodb-0.1.2.dist-info → disdrodb-0.1.4.dist-info}/METADATA +4 -3
- {disdrodb-0.1.2.dist-info → disdrodb-0.1.4.dist-info}/RECORD +139 -98
- {disdrodb-0.1.2.dist-info → disdrodb-0.1.4.dist-info}/entry_points.txt +2 -0
- disdrodb/l1/encoding_attrs.py +0 -642
- disdrodb/l2/processing_options.py +0 -213
- disdrodb/l2/routines.py +0 -868
- /disdrodb/l0/readers/PARSIVEL/SLOVENIA/{UL_FGG.py → UL.py} +0 -0
- {disdrodb-0.1.2.dist-info → disdrodb-0.1.4.dist-info}/WHEEL +0 -0
- {disdrodb-0.1.2.dist-info → disdrodb-0.1.4.dist-info}/licenses/LICENSE +0 -0
- {disdrodb-0.1.2.dist-info → disdrodb-0.1.4.dist-info}/top_level.txt +0 -0
disdrodb/l2/processing.py
CHANGED
|
@@ -19,12 +19,13 @@
|
|
|
19
19
|
import numpy as np
|
|
20
20
|
import xarray as xr
|
|
21
21
|
|
|
22
|
-
from disdrodb.
|
|
22
|
+
from disdrodb.constants import DIAMETER_DIMENSION
|
|
23
23
|
from disdrodb.l1.fall_velocity import get_raindrop_fall_velocity
|
|
24
24
|
from disdrodb.l1_env.routines import load_env_dataset
|
|
25
25
|
from disdrodb.l2.empirical_dsd import (
|
|
26
|
+
BINS_METRICS,
|
|
27
|
+
add_bins_metrics,
|
|
26
28
|
compute_integral_parameters,
|
|
27
|
-
compute_qc_bins_metrics,
|
|
28
29
|
compute_spectrum_parameters,
|
|
29
30
|
get_drop_average_velocity,
|
|
30
31
|
get_drop_number_concentration,
|
|
@@ -35,10 +36,9 @@ from disdrodb.l2.empirical_dsd import (
|
|
|
35
36
|
)
|
|
36
37
|
from disdrodb.psd import create_psd, estimate_model_parameters
|
|
37
38
|
from disdrodb.psd.fitting import compute_gof_stats
|
|
38
|
-
from disdrodb.utils.attrs import set_attrs
|
|
39
39
|
from disdrodb.utils.decorators import check_pytmatrix_availability
|
|
40
|
-
from disdrodb.utils.encoding import set_encodings
|
|
41
40
|
from disdrodb.utils.time import ensure_sample_interval_in_seconds
|
|
41
|
+
from disdrodb.utils.writer import finalize_product
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
def define_diameter_array(diameter_min=0, diameter_max=10, diameter_spacing=0.05):
|
|
@@ -109,26 +109,122 @@ def define_velocity_array(ds):
|
|
|
109
109
|
return velocity
|
|
110
110
|
|
|
111
111
|
|
|
112
|
+
####--------------------------------------------------------------------------
|
|
113
|
+
#### Timesteps filtering functions
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def select_timesteps_with_drops(ds, minimum_ndrops=0):
|
|
117
|
+
"""Select timesteps with at least the specified number of drops."""
|
|
118
|
+
valid_timesteps = ds["N"].to_numpy() >= minimum_ndrops
|
|
119
|
+
if not valid_timesteps.any().item():
|
|
120
|
+
raise ValueError(f"No timesteps with N >= {minimum_ndrops}.")
|
|
121
|
+
if "time" in ds.dims:
|
|
122
|
+
ds = ds.isel(time=valid_timesteps, drop=False)
|
|
123
|
+
return ds
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def select_timesteps_with_minimum_nbins(ds, minimum_nbins):
|
|
127
|
+
"""Select timesteps with at least the specified number of diameter bins with drops."""
|
|
128
|
+
if minimum_nbins == 0:
|
|
129
|
+
return ds
|
|
130
|
+
valid_timesteps = ds["Nbins"].to_numpy() >= minimum_nbins
|
|
131
|
+
if not valid_timesteps.any().item():
|
|
132
|
+
raise ValueError(f"No timesteps with Nbins >= {minimum_nbins}.")
|
|
133
|
+
if "time" in ds.dims:
|
|
134
|
+
ds = ds.isel(time=valid_timesteps, drop=False)
|
|
135
|
+
return ds
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def select_timesteps_with_minimum_rain_rate(ds, minimum_rain_rate):
|
|
139
|
+
"""Select timesteps with at least the specified rain rate."""
|
|
140
|
+
if minimum_rain_rate == 0:
|
|
141
|
+
return ds
|
|
142
|
+
# Ensure dimensionality of R is 1
|
|
143
|
+
# - Collapse velocity_method
|
|
144
|
+
dims_to_agg = set(ds["R"].dims) - {"time"}
|
|
145
|
+
da_r = ds["R"].max(dim=dims_to_agg)
|
|
146
|
+
# Determine valid timesteps
|
|
147
|
+
valid_timesteps = da_r.to_numpy() >= minimum_rain_rate
|
|
148
|
+
if not valid_timesteps.any().item():
|
|
149
|
+
raise ValueError(f"No timesteps with rain rate (R) >= {minimum_rain_rate} mm/hr.")
|
|
150
|
+
if "time" in ds.dims:
|
|
151
|
+
ds = ds.isel(time=valid_timesteps, drop=False)
|
|
152
|
+
return ds
|
|
153
|
+
|
|
154
|
+
|
|
112
155
|
####--------------------------------------------------------------------------
|
|
113
156
|
#### L2 Empirical Parameters
|
|
114
157
|
|
|
115
158
|
|
|
116
|
-
def
|
|
159
|
+
def _ensure_present(container, required, kind):
|
|
160
|
+
"""Raise a ValueError if any of `required` are missing from the `container`."""
|
|
161
|
+
missing = [item for item in required if item not in container]
|
|
162
|
+
if missing:
|
|
163
|
+
raise ValueError(f"Dataset is missing required {kind}: {', '.join(missing)}")
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def check_l2e_input_dataset(ds):
|
|
167
|
+
"""Check dataset validity for L2E production."""
|
|
168
|
+
from disdrodb.scattering import RADAR_OPTIONS
|
|
169
|
+
|
|
170
|
+
# Check minimum required variables, coordinates and dimensions are presents
|
|
171
|
+
required_variables = ["drop_number", "fall_velocity"]
|
|
172
|
+
required_coords = [
|
|
173
|
+
"diameter_bin_center",
|
|
174
|
+
"diameter_bin_width",
|
|
175
|
+
"sample_interval",
|
|
176
|
+
]
|
|
177
|
+
required_attributes = ["sensor_name"]
|
|
178
|
+
required_dims = [DIAMETER_DIMENSION]
|
|
179
|
+
_ensure_present(list(ds.data_vars), required=required_variables, kind="variables")
|
|
180
|
+
_ensure_present(list(ds.coords), required=required_coords, kind="coords")
|
|
181
|
+
_ensure_present(list(ds.dims), required=required_dims, kind="dimensions")
|
|
182
|
+
_ensure_present(list(ds.attrs), required=required_attributes, kind="attributes")
|
|
183
|
+
|
|
184
|
+
# Remove dimensions and coordinates generated by L2E routine
|
|
185
|
+
# - This allow to recursively repass L2E product to the generate_l2e function
|
|
186
|
+
unallowed_dims = [dim for dim in ds.dims if dim in ["source", "velocity_method", *RADAR_OPTIONS]]
|
|
187
|
+
ds = ds.drop_dims(unallowed_dims)
|
|
188
|
+
unallowed_coords = [coord for coord in ds.coords if coord in ["source", "velocity_method", *RADAR_OPTIONS]]
|
|
189
|
+
ds = ds.drop_vars(unallowed_coords)
|
|
190
|
+
return ds
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def generate_l2e(
|
|
194
|
+
ds,
|
|
195
|
+
ds_env=None,
|
|
196
|
+
compute_spectra=False,
|
|
197
|
+
compute_percentage_contribution=False,
|
|
198
|
+
minimum_ndrops=1,
|
|
199
|
+
minimum_nbins=1,
|
|
200
|
+
minimum_rain_rate=0.01,
|
|
201
|
+
):
|
|
117
202
|
"""Generate the DISDRODB L2E dataset from the DISDRODB L1 dataset.
|
|
118
203
|
|
|
119
204
|
Parameters
|
|
120
205
|
----------
|
|
121
206
|
ds : xarray.Dataset
|
|
122
207
|
DISDRODB L1 dataset.
|
|
208
|
+
Alternatively, a xarray dataset with at least:
|
|
209
|
+
|
|
210
|
+
- variables: drop_number, fall_velocity
|
|
211
|
+
- dimension: DIAMETER_DIMENSION
|
|
212
|
+
- coordinates: diameter_bin_center, diameter_bin_width, sample_interval
|
|
213
|
+
- attributes: sensor_name
|
|
214
|
+
|
|
123
215
|
ds_env : xarray.Dataset, optional
|
|
124
|
-
|
|
125
|
-
|
|
216
|
+
Environmental dataset used for fall velocity and water density estimates.
|
|
217
|
+
If None, a default environment dataset will be loaded.
|
|
126
218
|
|
|
127
219
|
Returns
|
|
128
220
|
-------
|
|
129
221
|
xarray.Dataset
|
|
130
|
-
|
|
222
|
+
DISDRODB L2E dataset.
|
|
131
223
|
"""
|
|
224
|
+
# Check and prepapre input dataset
|
|
225
|
+
ds = check_l2e_input_dataset(ds)
|
|
226
|
+
|
|
227
|
+
# -------------------------------------------------------
|
|
132
228
|
# Initialize L2E dataset
|
|
133
229
|
ds_l2 = xr.Dataset()
|
|
134
230
|
|
|
@@ -137,24 +233,20 @@ def generate_l2_empirical(ds, ds_env=None, compute_spectra=False):
|
|
|
137
233
|
|
|
138
234
|
# -------------------------------------------------------
|
|
139
235
|
#### Preprocessing
|
|
140
|
-
#
|
|
141
|
-
|
|
142
|
-
# - Regularization can be done at the end
|
|
143
|
-
ds = ds.isel(time=ds["N"] > 0)
|
|
236
|
+
# Select timesteps with at least the specified number of drops
|
|
237
|
+
ds = select_timesteps_with_drops(ds, minimum_ndrops=minimum_ndrops)
|
|
144
238
|
|
|
145
|
-
#
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
239
|
+
# Add bins metrics to resampled data if missing
|
|
240
|
+
ds = add_bins_metrics(ds)
|
|
241
|
+
|
|
242
|
+
# Remove timesteps with not enough bins with drops
|
|
243
|
+
ds = select_timesteps_with_minimum_nbins(ds, minimum_nbins=minimum_nbins)
|
|
149
244
|
|
|
150
245
|
# Retrieve ENV dataset or take defaults
|
|
151
246
|
# --> Used for fall velocity and water density estimates
|
|
152
247
|
if ds_env is None:
|
|
153
248
|
ds_env = load_env_dataset(ds)
|
|
154
|
-
|
|
155
|
-
# TODO: Derive water density as function of ENV (temperature, ...)
|
|
156
|
-
# --> (T == 10){density_water <- 999.7}else if(T == 20){density_water <- 998.2}else{density_water <- 995.7}
|
|
157
|
-
water_density = 1000 # kg / m3
|
|
249
|
+
water_density = ds_env.get("water_density", 1000) # kg / m3
|
|
158
250
|
|
|
159
251
|
# Determine if the velocity dimension is available
|
|
160
252
|
has_velocity_dimension = "velocity_bin_center" in ds.dims
|
|
@@ -172,6 +264,7 @@ def generate_l2_empirical(ds, ds_env=None, compute_spectra=False):
|
|
|
172
264
|
|
|
173
265
|
# Copy relevant L1 variables to L2 product
|
|
174
266
|
variables = [
|
|
267
|
+
"raw_drop_number", # 2D V x D
|
|
175
268
|
"drop_number", # 2D V x D
|
|
176
269
|
"drop_counts", # 1D D
|
|
177
270
|
"sample_interval",
|
|
@@ -184,6 +277,7 @@ def generate_l2_empirical(ds, ds_env=None, compute_spectra=False):
|
|
|
184
277
|
|
|
185
278
|
variables = [var for var in variables if var in ds]
|
|
186
279
|
ds_l2.update(ds[variables])
|
|
280
|
+
ds_l2.update(ds[BINS_METRICS])
|
|
187
281
|
|
|
188
282
|
# -------------------------------------------------------------------------------------------
|
|
189
283
|
# Compute and add drop average velocity if an optical disdrometer (i.e OTT Parsivel or ThiesLPM)
|
|
@@ -206,7 +300,7 @@ def generate_l2_empirical(ds, ds_env=None, compute_spectra=False):
|
|
|
206
300
|
ds_l2["drop_number_concentration"] = drop_number_concentration
|
|
207
301
|
|
|
208
302
|
# -------------------------------------------------------
|
|
209
|
-
#### Compute
|
|
303
|
+
#### Compute R, LWC, KE and Z spectra
|
|
210
304
|
if compute_spectra:
|
|
211
305
|
ds_spectrum = compute_spectrum_parameters(
|
|
212
306
|
drop_number_concentration,
|
|
@@ -217,6 +311,10 @@ def generate_l2_empirical(ds, ds_env=None, compute_spectra=False):
|
|
|
217
311
|
)
|
|
218
312
|
ds_l2.update(ds_spectrum)
|
|
219
313
|
|
|
314
|
+
if compute_percentage_contribution:
|
|
315
|
+
# TODO: Implement percentage contribution computation
|
|
316
|
+
pass
|
|
317
|
+
|
|
220
318
|
# ----------------------------------------------------------------------------
|
|
221
319
|
#### Compute L2 integral parameters from drop_number_concentration
|
|
222
320
|
ds_parameters = compute_integral_parameters(
|
|
@@ -242,7 +340,7 @@ def generate_l2_empirical(ds, ds_env=None, compute_spectra=False):
|
|
|
242
340
|
|
|
243
341
|
# -------------------------------------------------------
|
|
244
342
|
#### Compute KE integral parameters directly from drop_number
|
|
245
|
-
#
|
|
343
|
+
# The kinetic energy variables can be computed using the actual measured fall velocity by the sensor.
|
|
246
344
|
if has_velocity_dimension:
|
|
247
345
|
ds_ke = get_kinetic_energy_variables_from_drop_number(
|
|
248
346
|
drop_number=drop_number,
|
|
@@ -261,24 +359,18 @@ def generate_l2_empirical(ds, ds_env=None, compute_spectra=False):
|
|
|
261
359
|
for var in ke_vars:
|
|
262
360
|
ds_parameters[var] = ds_ke[var]
|
|
263
361
|
|
|
264
|
-
# ----------------------------------------------------------------------------
|
|
265
|
-
#### Finalize L2 Dataset
|
|
266
362
|
# Add DSD integral parameters
|
|
267
363
|
ds_l2.update(ds_parameters)
|
|
268
364
|
|
|
269
|
-
#
|
|
270
|
-
####
|
|
271
|
-
|
|
272
|
-
attrs_dict = get_attrs_dict()
|
|
273
|
-
ds_l2 = set_attrs(ds_l2, attrs_dict=attrs_dict)
|
|
274
|
-
|
|
275
|
-
# Add variables encoding
|
|
276
|
-
encoding_dict = get_encoding_dict()
|
|
277
|
-
ds_l2 = set_encodings(ds_l2, encoding_dict=encoding_dict)
|
|
365
|
+
# ----------------------------------------------------------------------------
|
|
366
|
+
#### Finalize L2 Dataset
|
|
367
|
+
ds_l2 = select_timesteps_with_minimum_rain_rate(ds_l2, minimum_rain_rate=minimum_rain_rate)
|
|
278
368
|
|
|
279
369
|
# Add global attributes
|
|
280
370
|
ds_l2.attrs = attrs
|
|
281
371
|
|
|
372
|
+
# Add variables attributes and encodings
|
|
373
|
+
ds_l2 = finalize_product(ds_l2, product="L2E")
|
|
282
374
|
return ds_l2
|
|
283
375
|
|
|
284
376
|
|
|
@@ -286,22 +378,74 @@ def generate_l2_empirical(ds, ds_env=None, compute_spectra=False):
|
|
|
286
378
|
#### L2 Model Parameters
|
|
287
379
|
|
|
288
380
|
|
|
289
|
-
def
|
|
381
|
+
def _get_default_optimization(psd_model):
|
|
382
|
+
"""PSD model defaults."""
|
|
383
|
+
defaults = {
|
|
384
|
+
"ExponentialPSD": "ML",
|
|
385
|
+
"GammaPSD": "ML",
|
|
386
|
+
"LognormalPSD": "ML",
|
|
387
|
+
"NormalizedGammaPSD": "GS",
|
|
388
|
+
}
|
|
389
|
+
optimization = defaults[psd_model]
|
|
390
|
+
return optimization
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def check_l2m_input_dataset(ds):
|
|
394
|
+
"""Check dataset validity for L2M production."""
|
|
395
|
+
# Retrieve drop_number concentration (if not available) from drop_number
|
|
396
|
+
# --> This allow people to use directly L1 datasets to generate L2M datasets
|
|
397
|
+
if "drop_number_concentration" not in ds:
|
|
398
|
+
if "drop_number" in ds:
|
|
399
|
+
check_l2e_input_dataset(ds)
|
|
400
|
+
sample_interval = ensure_sample_interval_in_seconds(ds["sample_interval"])
|
|
401
|
+
sampling_area = get_effective_sampling_area(
|
|
402
|
+
sensor_name=ds.attrs["sensor_name"],
|
|
403
|
+
diameter=ds["diameter_bin_center"] / 1000,
|
|
404
|
+
) # m2
|
|
405
|
+
# Compute drop number concentration (Nt) [#/m3/mm]
|
|
406
|
+
ds["drop_number_concentration"] = get_drop_number_concentration(
|
|
407
|
+
drop_number=ds["drop_number"],
|
|
408
|
+
velocity=define_velocity_array(ds), # fall_velocity (and optionally also velocity_bin_center)
|
|
409
|
+
diameter_bin_width=ds["diameter_bin_width"], # mm
|
|
410
|
+
sample_interval=sample_interval,
|
|
411
|
+
sampling_area=sampling_area,
|
|
412
|
+
)
|
|
413
|
+
else:
|
|
414
|
+
raise ValueError("Please provide DISDRODB L1 or L2E dataset !")
|
|
415
|
+
|
|
416
|
+
# Check minimum required variables, coordinates and dimensions are presents
|
|
417
|
+
required_variables = ["drop_number_concentration"]
|
|
418
|
+
required_coords = [
|
|
419
|
+
"diameter_bin_center",
|
|
420
|
+
"diameter_bin_width",
|
|
421
|
+
"diameter_bin_lower",
|
|
422
|
+
"diameter_bin_upper",
|
|
423
|
+
"sample_interval",
|
|
424
|
+
]
|
|
425
|
+
required_dims = [DIAMETER_DIMENSION]
|
|
426
|
+
_ensure_present(list(ds.data_vars), required=required_variables, kind="variables")
|
|
427
|
+
_ensure_present(list(ds.coords), required=required_coords, kind="coords")
|
|
428
|
+
_ensure_present(list(ds.dims), required=required_dims, kind="dimensions")
|
|
429
|
+
return ds
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def generate_l2m(
|
|
290
433
|
ds,
|
|
291
|
-
|
|
292
|
-
|
|
434
|
+
psd_model,
|
|
435
|
+
# Fitting options
|
|
436
|
+
optimization=None,
|
|
437
|
+
optimization_kwargs=None,
|
|
293
438
|
# PSD discretization
|
|
294
439
|
diameter_min=0,
|
|
295
440
|
diameter_max=10,
|
|
296
441
|
diameter_spacing=0.05,
|
|
297
|
-
#
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
optimization_kwargs=None,
|
|
442
|
+
# Processing options
|
|
443
|
+
ds_env=None,
|
|
444
|
+
fall_velocity_method="Beard1976",
|
|
301
445
|
# Filtering options
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
446
|
+
minimum_ndrops=1,
|
|
447
|
+
minimum_nbins=3,
|
|
448
|
+
minimum_rain_rate=0.01,
|
|
305
449
|
# GOF metrics options
|
|
306
450
|
gof_metrics=True,
|
|
307
451
|
):
|
|
@@ -316,6 +460,8 @@ def generate_l2_model(
|
|
|
316
460
|
----------
|
|
317
461
|
ds : xarray.Dataset
|
|
318
462
|
DISDRODB L2E dataset.
|
|
463
|
+
psd_model : str
|
|
464
|
+
The PSD model to fit. See ``disdrodb.psd.available_psd_models()``.
|
|
319
465
|
ds_env : xarray.Dataset, optional
|
|
320
466
|
Environmental dataset used for fall velocity and water density estimates.
|
|
321
467
|
If None, a default environment dataset will be loaded.
|
|
@@ -325,13 +471,14 @@ def generate_l2_model(
|
|
|
325
471
|
Maximum PSD diameter. The default value is 8 mm.
|
|
326
472
|
diameter_spacing : float, optional
|
|
327
473
|
PSD diameter spacing. The default value is 0.05 mm.
|
|
328
|
-
psd_model : str
|
|
329
|
-
The PSD model to fit. See ``available_psd_models()``.
|
|
330
474
|
optimization : str, optional
|
|
331
475
|
The fitting optimization procedure. Either "GS" (Grid Search), "ML (Maximum Likelihood)
|
|
332
476
|
or "MOM" (Method of Moments).
|
|
333
477
|
optimization_kwargs : dict, optional
|
|
334
478
|
Dictionary with arguments to customize the fitting procedure.
|
|
479
|
+
minimum_nbins: int
|
|
480
|
+
Minimum number of bins with drops required to fit the PSD model.
|
|
481
|
+
The default value is 5.
|
|
335
482
|
gof_metrics : bool, optional
|
|
336
483
|
Whether to add goodness-of-fit metrics to the output dataset. The default is True.
|
|
337
484
|
|
|
@@ -340,51 +487,37 @@ def generate_l2_model(
|
|
|
340
487
|
xarray.Dataset
|
|
341
488
|
DISDRODB L2M dataset.
|
|
342
489
|
"""
|
|
343
|
-
|
|
344
|
-
####
|
|
345
|
-
|
|
346
|
-
|
|
490
|
+
####------------------------------------------------------.
|
|
491
|
+
#### Define default PSD model and optimization
|
|
492
|
+
psd_model = "NormalizedGammaPSD" if psd_model is None else psd_model
|
|
493
|
+
optimization = _get_default_optimization(psd_model) if optimization is None else optimization
|
|
347
494
|
|
|
348
495
|
# ----------------------------------------------------------------------------.
|
|
496
|
+
#### Preprocessing
|
|
349
497
|
# Retrieve attributes
|
|
350
498
|
attrs = ds.attrs.copy()
|
|
351
499
|
|
|
352
|
-
#
|
|
353
|
-
|
|
354
|
-
# TODO --> Add into ds_env !
|
|
355
|
-
# --> (T == 10){density_water <- 999.7}else if(T == 20){density_water <- 998.2}else{density_water <- 995.7}
|
|
356
|
-
water_density = 1000 # kg / m3
|
|
500
|
+
# Check and prepare dataset
|
|
501
|
+
ds = check_l2m_input_dataset(ds)
|
|
357
502
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
if "Nbins" not in ds:
|
|
362
|
-
# Add bins statistics
|
|
363
|
-
ds.update(compute_qc_bins_metrics(ds))
|
|
503
|
+
# Retrieve measurement interval
|
|
504
|
+
# - If dataset is opened with decode_timedelta=False, sample_interval is already in seconds !
|
|
505
|
+
sample_interval = ensure_sample_interval_in_seconds(ds["sample_interval"])
|
|
364
506
|
|
|
365
|
-
#
|
|
366
|
-
|
|
507
|
+
# Select timesteps with at least the specified number of drops
|
|
508
|
+
ds = select_timesteps_with_drops(ds, minimum_ndrops=minimum_ndrops)
|
|
367
509
|
|
|
368
|
-
#
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
510
|
+
# Add bins metrics if missing
|
|
511
|
+
ds = add_bins_metrics(ds)
|
|
512
|
+
|
|
513
|
+
# Remove timesteps with not enough bins with drops
|
|
514
|
+
ds = select_timesteps_with_minimum_nbins(ds, minimum_nbins=minimum_nbins)
|
|
372
515
|
|
|
373
516
|
# Retrieve ENV dataset or take defaults
|
|
374
517
|
# --> Used for fall velocity and water density estimates
|
|
375
518
|
if ds_env is None:
|
|
376
519
|
ds_env = load_env_dataset(ds)
|
|
377
|
-
|
|
378
|
-
####------------------------------------------------------.
|
|
379
|
-
#### Define default PSD optimization arguments
|
|
380
|
-
if psd_model is None and optimization is None:
|
|
381
|
-
psd_model = "NormalizedGammaPSD"
|
|
382
|
-
optimization = "GS"
|
|
383
|
-
optimization_kwargs = {
|
|
384
|
-
"target": "ND",
|
|
385
|
-
"transformation": "identity",
|
|
386
|
-
"error_order": 1, # MAE
|
|
387
|
-
}
|
|
520
|
+
water_density = ds_env.get("water_density", 1000) # kg / m3
|
|
388
521
|
|
|
389
522
|
####------------------------------------------------------.
|
|
390
523
|
#### Retrieve PSD parameters
|
|
@@ -394,11 +527,7 @@ def generate_l2_model(
|
|
|
394
527
|
optimization=optimization,
|
|
395
528
|
optimization_kwargs=optimization_kwargs,
|
|
396
529
|
)
|
|
397
|
-
|
|
398
|
-
####------------------------------------------------------.
|
|
399
|
-
#### Mask timesteps with few bins if asked
|
|
400
|
-
if mask_timesteps_with_few_bins:
|
|
401
|
-
ds_psd_params = ds_psd_params.where(valid_timesteps)
|
|
530
|
+
psd_fitting_attrs = ds_psd_params.attrs
|
|
402
531
|
|
|
403
532
|
####-------------------------------------------------------
|
|
404
533
|
#### Create PSD
|
|
@@ -415,10 +544,6 @@ def generate_l2_model(
|
|
|
415
544
|
)
|
|
416
545
|
diameter_bin_width = diameter["diameter_bin_width"]
|
|
417
546
|
|
|
418
|
-
# Retrieve time of integration
|
|
419
|
-
# - If dataset is opened with decode_timedelta=False, sample_interval is already in seconds !
|
|
420
|
-
sample_interval = ensure_sample_interval_in_seconds(ds["sample_interval"])
|
|
421
|
-
|
|
422
547
|
# Retrieve drop number concentration
|
|
423
548
|
drop_number_concentration = psd(diameter)
|
|
424
549
|
|
|
@@ -442,22 +567,29 @@ def generate_l2_model(
|
|
|
442
567
|
|
|
443
568
|
# Add GOF statistics if asked
|
|
444
569
|
if gof_metrics:
|
|
445
|
-
ds_gof = compute_gof_stats(
|
|
570
|
+
ds_gof = compute_gof_stats(
|
|
571
|
+
obs=ds["drop_number_concentration"], # empirical N(D)
|
|
572
|
+
pred=psd(ds["diameter_bin_center"]), # fitted N(D) on empirical diameter bins !
|
|
573
|
+
)
|
|
446
574
|
ds_params.update(ds_gof)
|
|
447
575
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
ds_params =
|
|
576
|
+
# Add empirical drop_number_concentration and fall velocity
|
|
577
|
+
# - To reuse output dataset to create another L2M dataset or to compute other GOF metrics
|
|
578
|
+
ds_params["drop_number_concentration"] = ds["drop_number_concentration"]
|
|
579
|
+
ds_params["fall_velocity"] = ds["fall_velocity"]
|
|
580
|
+
ds_params["N"] = ds["N"]
|
|
581
|
+
ds_params.update(ds[BINS_METRICS])
|
|
453
582
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
ds_params =
|
|
583
|
+
#### ----------------------------------------------------------------------------.
|
|
584
|
+
#### Finalize dataset
|
|
585
|
+
ds_params = select_timesteps_with_minimum_rain_rate(ds_params, minimum_rain_rate=minimum_rain_rate)
|
|
457
586
|
|
|
458
587
|
# Add global attributes
|
|
459
588
|
ds_params.attrs = attrs
|
|
460
|
-
ds_params.attrs
|
|
589
|
+
ds_params.attrs.update(psd_fitting_attrs)
|
|
590
|
+
|
|
591
|
+
# Add variables attributes and encodings
|
|
592
|
+
ds_params = finalize_product(ds_params, product="L2M")
|
|
461
593
|
|
|
462
594
|
# Return dataset
|
|
463
595
|
return ds_params
|
|
@@ -470,10 +602,14 @@ def generate_l2_model(
|
|
|
470
602
|
@check_pytmatrix_availability
|
|
471
603
|
def generate_l2_radar(
|
|
472
604
|
ds,
|
|
473
|
-
|
|
474
|
-
|
|
605
|
+
frequency=None,
|
|
606
|
+
num_points=1024,
|
|
475
607
|
diameter_max=10,
|
|
476
|
-
|
|
608
|
+
canting_angle_std=7,
|
|
609
|
+
axis_ratio_model="Thurai2007",
|
|
610
|
+
permittivity_model="Turner2016",
|
|
611
|
+
water_temperature=10,
|
|
612
|
+
elevation_angle=0,
|
|
477
613
|
parallel=True,
|
|
478
614
|
):
|
|
479
615
|
"""Simulate polarimetric radar variables from empirical drop number concentration or the estimated PSD.
|
|
@@ -482,15 +618,31 @@ def generate_l2_radar(
|
|
|
482
618
|
----------
|
|
483
619
|
ds : xarray.Dataset
|
|
484
620
|
Dataset containing the drop number concentration variable or the PSD parameters.
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
621
|
+
frequency : str, float, or list of str and float, optional
|
|
622
|
+
Frequencies in GHz for which to compute the radar parameters.
|
|
623
|
+
Alternatively, also strings can be used to specify common radar frequencies.
|
|
624
|
+
If ``None``, the common radar frequencies will be used.
|
|
625
|
+
See ``disdrodb.scattering.available_radar_bands()``.
|
|
626
|
+
num_points: int or list of integer, optional
|
|
627
|
+
Number of bins into which discretize the PSD.
|
|
628
|
+
diameter_max : float or list of float, optional
|
|
629
|
+
Maximum diameter. The default value is 10 mm.
|
|
488
630
|
canting_angle_std : float or list of float, optional
|
|
489
631
|
Standard deviation of the canting angle. The default value is 7.
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
632
|
+
axis_ratio_model : str or list of str, optional
|
|
633
|
+
Models to compute the axis ratio. The default model is ``Thurai2007``.
|
|
634
|
+
See available models with ``disdrodb.scattering.available_axis_ratio_models()``.
|
|
635
|
+
permittivity_model : str str or list of str, optional
|
|
636
|
+
Permittivity model to use to compute the refractive index and the
|
|
637
|
+
rayleigh_dielectric_factor. The default is ``Turner2016``.
|
|
638
|
+
See available models with ``disdrodb.scattering.available_permittivity_models()``.
|
|
639
|
+
water_temperature : float or list of float, optional
|
|
640
|
+
Water temperature in degree Celsius to be used in the permittivity model.
|
|
641
|
+
The default is 10 degC.
|
|
642
|
+
elevation_angle : float or list of float, optional
|
|
643
|
+
Radar elevation angles in degrees.
|
|
644
|
+
Specify 90 degrees for vertically pointing radars.
|
|
645
|
+
The default is 0 degrees.
|
|
494
646
|
parallel : bool, optional
|
|
495
647
|
Whether to compute radar variables in parallel.
|
|
496
648
|
The default value is ``True``.
|
|
@@ -507,22 +659,21 @@ def generate_l2_radar(
|
|
|
507
659
|
# Retrieve radar variables from L2E drop number concentration or from estimated L2M PSD model
|
|
508
660
|
ds_radar = get_radar_parameters(
|
|
509
661
|
ds=ds,
|
|
510
|
-
|
|
511
|
-
|
|
662
|
+
frequency=frequency,
|
|
663
|
+
num_points=num_points,
|
|
512
664
|
diameter_max=diameter_max,
|
|
513
|
-
|
|
665
|
+
canting_angle_std=canting_angle_std,
|
|
666
|
+
axis_ratio_model=axis_ratio_model,
|
|
667
|
+
permittivity_model=permittivity_model,
|
|
668
|
+
water_temperature=water_temperature,
|
|
669
|
+
elevation_angle=elevation_angle,
|
|
514
670
|
parallel=parallel,
|
|
515
671
|
)
|
|
516
672
|
|
|
517
673
|
#### ----------------------------------------------------------------------------.
|
|
518
|
-
####
|
|
519
|
-
# Add variables attributes
|
|
520
|
-
|
|
521
|
-
ds_radar = set_attrs(ds_radar, attrs_dict=attrs_dict)
|
|
522
|
-
|
|
523
|
-
# Add variables encoding
|
|
524
|
-
encoding_dict = get_encoding_dict()
|
|
525
|
-
ds_radar = set_encodings(ds_radar, encoding_dict=encoding_dict)
|
|
674
|
+
#### Finalize dataset
|
|
675
|
+
# Add variables attributes and encodings
|
|
676
|
+
ds_radar = finalize_product(ds_radar)
|
|
526
677
|
|
|
527
678
|
# Return dataset
|
|
528
679
|
return ds_radar
|