disdrodb 0.1.4__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/_version.py +2 -2
- disdrodb/api/create_directories.py +0 -2
- disdrodb/cli/disdrodb_create_summary.py +10 -0
- disdrodb/cli/disdrodb_create_summary_station.py +10 -0
- disdrodb/constants.py +1 -1
- 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/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/l0b_processing.py +70 -15
- disdrodb/l0/readers/LPM/ARM/ARM_LPM.py +1 -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/BRAZIL/CHUVA_PARSIVEL2.py +10 -14
- disdrodb/l0/readers/PARSIVEL2/BRAZIL/GOAMAZON_PARSIVEL2.py +10 -14
- 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/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/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 +136 -83
- disdrodb/l1/filters.py +25 -28
- disdrodb/l1/processing.py +11 -13
- disdrodb/l1_env/routines.py +46 -17
- disdrodb/l2/empirical_dsd.py +6 -0
- disdrodb/l2/processing.py +2 -2
- disdrodb/metadata/geolocation.py +0 -2
- disdrodb/psd/fitting.py +16 -13
- disdrodb/routines/l2.py +35 -23
- disdrodb/routines/wrappers.py +5 -0
- disdrodb/scattering/axis_ratio.py +90 -84
- disdrodb/scattering/permittivity.py +6 -0
- disdrodb/summary/routines.py +38 -12
- disdrodb/utils/attrs.py +2 -0
- disdrodb/utils/encoding.py +5 -0
- disdrodb/utils/time.py +2 -2
- disdrodb/viz/plots.py +24 -1
- {disdrodb-0.1.4.dist-info → disdrodb-0.1.5.dist-info}/METADATA +2 -1
- {disdrodb-0.1.4.dist-info → disdrodb-0.1.5.dist-info}/RECORD +85 -65
- {disdrodb-0.1.4.dist-info → disdrodb-0.1.5.dist-info}/WHEEL +0 -0
- {disdrodb-0.1.4.dist-info → disdrodb-0.1.5.dist-info}/entry_points.txt +0 -0
- {disdrodb-0.1.4.dist-info → disdrodb-0.1.5.dist-info}/licenses/LICENSE +0 -0
- {disdrodb-0.1.4.dist-info → disdrodb-0.1.5.dist-info}/top_level.txt +0 -0
disdrodb/l1/fall_velocity.py
CHANGED
|
@@ -14,10 +14,14 @@
|
|
|
14
14
|
# You should have received a copy of the GNU General Public License
|
|
15
15
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
16
|
# -----------------------------------------------------------------------------.
|
|
17
|
-
"""Theoretical models to estimate the
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
"""Theoretical models to estimate the raindrop fall velocity based on drop diameter in mm."""
|
|
20
18
|
import numpy as np
|
|
19
|
+
import xarray as xr
|
|
20
|
+
|
|
21
|
+
from disdrodb.constants import DIAMETER_DIMENSION
|
|
22
|
+
from disdrodb.l0.l0b_processing import ensure_valid_geolocation
|
|
23
|
+
from disdrodb.l1_env.routines import load_env_dataset
|
|
24
|
+
from disdrodb.utils.warnings import suppress_warnings
|
|
21
25
|
|
|
22
26
|
|
|
23
27
|
def get_fall_velocity_atlas_1973(diameter):
|
|
@@ -53,7 +57,7 @@ def get_fall_velocity_atlas_1973(diameter):
|
|
|
53
57
|
|
|
54
58
|
"""
|
|
55
59
|
fall_velocity = 9.65 - 10.3 * np.exp(-0.6 * diameter) # clip to 0 !
|
|
56
|
-
fall_velocity =
|
|
60
|
+
fall_velocity = fall_velocity.clip(min=0, max=None)
|
|
57
61
|
return fall_velocity
|
|
58
62
|
|
|
59
63
|
|
|
@@ -80,6 +84,7 @@ def get_fall_velocity_brandes_2002(diameter):
|
|
|
80
84
|
|
|
81
85
|
"""
|
|
82
86
|
fall_velocity = -0.1021 + 4.932 * diameter - 0.9551 * diameter**2 + 0.07934 * diameter**3 - 0.002362 * diameter**4
|
|
87
|
+
fall_velocity = fall_velocity.clip(min=0, max=None)
|
|
83
88
|
return fall_velocity
|
|
84
89
|
|
|
85
90
|
|
|
@@ -107,6 +112,7 @@ def get_fall_velocity_uplinger_1981(diameter):
|
|
|
107
112
|
"""
|
|
108
113
|
# Valid between 0.1 and 7 mm
|
|
109
114
|
fall_velocity = 4.874 * diameter * np.exp(-0.195 * diameter)
|
|
115
|
+
fall_velocity = fall_velocity.clip(min=0, max=None)
|
|
110
116
|
return fall_velocity
|
|
111
117
|
|
|
112
118
|
|
|
@@ -133,6 +139,7 @@ def get_fall_velocity_van_dijk_2002(diameter):
|
|
|
133
139
|
|
|
134
140
|
"""
|
|
135
141
|
fall_velocity = -0.254 + 5.03 * diameter - 0.912 * diameter**2 + 0.0561 * diameter**3
|
|
142
|
+
fall_velocity = fall_velocity.clip(min=0, max=None)
|
|
136
143
|
return fall_velocity
|
|
137
144
|
|
|
138
145
|
|
|
@@ -147,9 +154,10 @@ def get_fall_velocity_beard_1976(diameter, ds_env):
|
|
|
147
154
|
A dataset containing the following environmental variables:
|
|
148
155
|
- 'altitude' : Altitude in meters (m).
|
|
149
156
|
- 'latitude' : Latitude in degrees.
|
|
150
|
-
- 'temperature' : Temperature in degrees
|
|
157
|
+
- 'temperature' : Temperature in degrees Kelvin (K).
|
|
151
158
|
- 'relative_humidity' : Relative humidity in percentage (%).
|
|
152
159
|
- 'sea_level_air_pressure' : Sea level air pressure in Pascals (Pa).
|
|
160
|
+
- 'air_pressure': Air pressure in Pascals (Pa).
|
|
153
161
|
- 'lapse_rate' : Lapse rate in degrees Celsius per meter (°C/m).
|
|
154
162
|
|
|
155
163
|
Returns
|
|
@@ -166,121 +174,171 @@ def get_fall_velocity_beard_1976(diameter, ds_env):
|
|
|
166
174
|
latitude=ds_env["latitude"],
|
|
167
175
|
temperature=ds_env["temperature"],
|
|
168
176
|
relative_humidity=ds_env["relative_humidity"],
|
|
169
|
-
|
|
177
|
+
air_pressure=ds_env.get("air_pressure", None),
|
|
170
178
|
sea_level_air_pressure=ds_env["sea_level_air_pressure"],
|
|
171
179
|
lapse_rate=ds_env["lapse_rate"],
|
|
172
180
|
)
|
|
181
|
+
fall_velocity = fall_velocity.clip(min=0, max=None)
|
|
173
182
|
return fall_velocity
|
|
174
183
|
|
|
175
184
|
|
|
176
|
-
|
|
177
|
-
""
|
|
185
|
+
RAINDROP_FALL_VELOCITY_MODELS = {
|
|
186
|
+
"Atlas1973": get_fall_velocity_atlas_1973,
|
|
187
|
+
"Beard1976": get_fall_velocity_beard_1976,
|
|
188
|
+
"Brandes2002": get_fall_velocity_brandes_2002,
|
|
189
|
+
"Uplinger1981": get_fall_velocity_uplinger_1981,
|
|
190
|
+
"VanDijk2002": get_fall_velocity_van_dijk_2002,
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def available_raindrop_fall_velocity_models():
|
|
195
|
+
"""Return a list of the available raindrop fall velocity models."""
|
|
196
|
+
return list(RAINDROP_FALL_VELOCITY_MODELS)
|
|
178
197
|
|
|
179
|
-
|
|
198
|
+
|
|
199
|
+
def check_raindrop_fall_velocity_model(model):
|
|
200
|
+
"""Check validity of the specified raindrop fall velocity model."""
|
|
201
|
+
available_models = available_raindrop_fall_velocity_models()
|
|
202
|
+
if model not in available_models:
|
|
203
|
+
raise ValueError(f"{model} is an invalid raindrop fall velocity model. Valid models: {available_models}.")
|
|
204
|
+
return model
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def get_raindrop_fall_velocity_model(model):
|
|
208
|
+
"""Return the specified raindrop fall velocity model.
|
|
180
209
|
|
|
181
210
|
Parameters
|
|
182
211
|
----------
|
|
183
|
-
|
|
184
|
-
The
|
|
185
|
-
|
|
186
|
-
The default value to use for invalid altitude values. Defaults to 0.
|
|
187
|
-
default_latitude : float, optional
|
|
188
|
-
The default value to use for invalid latitude values. Defaults to 0.
|
|
189
|
-
default_longitude : float, optional
|
|
190
|
-
The default value to use for invalid longitude values. Defaults to 0.
|
|
212
|
+
model : str
|
|
213
|
+
The model to use for calculating the rain drop fall velocity. Available models are:
|
|
214
|
+
'Atlas1973', 'Beard1976', 'Brandes2002', 'Uplinger1981', 'VanDijk2002'.
|
|
191
215
|
|
|
192
216
|
Returns
|
|
193
217
|
-------
|
|
194
|
-
|
|
195
|
-
|
|
218
|
+
callable
|
|
219
|
+
A function which compute the raindrop fall velocity model
|
|
220
|
+
given the rain drop diameter in mm.
|
|
196
221
|
|
|
222
|
+
Notes
|
|
223
|
+
-----
|
|
224
|
+
This function serves as a wrapper to various raindrop fall velocity models.
|
|
225
|
+
It returns the appropriate model based on the `model` parameter.
|
|
197
226
|
"""
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
ds["altitude"] = ds["altitude"].where(~invalid_altitude, default_altitude)
|
|
201
|
-
|
|
202
|
-
invalid_lat = np.logical_or(np.isnan(ds["latitude"]), ds["latitude"] == -9999)
|
|
203
|
-
ds["latitude"] = ds["latitude"].where(~invalid_lat, default_latitude)
|
|
227
|
+
model = check_raindrop_fall_velocity_model(model)
|
|
228
|
+
return RAINDROP_FALL_VELOCITY_MODELS[model]
|
|
204
229
|
|
|
205
|
-
invalid_lon = np.logical_or(np.isnan(ds["longitude"]), ds["longitude"] == -9999)
|
|
206
|
-
ds["longitude"] = ds["longitude"].where(~invalid_lon, default_longitude)
|
|
207
|
-
return ds
|
|
208
230
|
|
|
209
|
-
|
|
210
|
-
def get_raindrop_fall_velocity(diameter, method, ds_env=None):
|
|
231
|
+
def get_raindrop_fall_velocity(diameter, model, ds_env=None):
|
|
211
232
|
"""Calculate the fall velocity of raindrops based on their diameter.
|
|
212
233
|
|
|
213
234
|
Parameters
|
|
214
235
|
----------
|
|
215
236
|
diameter : array-like
|
|
216
237
|
The diameter of the raindrops in millimeters.
|
|
217
|
-
|
|
218
|
-
The
|
|
238
|
+
model : str
|
|
239
|
+
The model to use for calculating the raindrop fall velocity. Must be one of the following:
|
|
219
240
|
'Atlas1973', 'Beard1976', 'Brandes2002', 'Uplinger1981', 'VanDijk2002'.
|
|
220
241
|
ds_env : xr.Dataset, optional
|
|
242
|
+
Only required if model is 'Beard1976'.
|
|
221
243
|
A dataset containing the following environmental variables:
|
|
222
|
-
- 'altitude'
|
|
223
|
-
- 'latitude'
|
|
224
|
-
- 'temperature' : Temperature in degrees
|
|
244
|
+
- 'altitude' (m)
|
|
245
|
+
- 'latitude' (°)
|
|
246
|
+
- 'temperature' : Temperature in degrees Kelvin (K).
|
|
225
247
|
- 'relative_humidity' : Relative humidity. A value between 0 and 1.
|
|
226
248
|
- 'sea_level_air_pressure' : Sea level air pressure in Pascals (Pa).
|
|
227
249
|
- 'lapse_rate' : Lapse rate in degrees Celsius per meter (°C/m).
|
|
228
|
-
|
|
250
|
+
If not specified, sensible default values are used.
|
|
229
251
|
|
|
230
252
|
Returns
|
|
231
253
|
-------
|
|
232
|
-
fall_velocity :
|
|
233
|
-
The calculated fall velocities
|
|
254
|
+
fall_velocity : xr.DataArray
|
|
255
|
+
The calculated raindrop fall velocities per diameter.
|
|
234
256
|
|
|
235
257
|
Notes
|
|
236
258
|
-----
|
|
237
|
-
The 'Beard1976'
|
|
238
|
-
These parameters can be provided through the `ds_env` argument.
|
|
259
|
+
The 'Beard1976' model requires additional environmental parameters.
|
|
260
|
+
These parameters can be provided through the `ds_env` argument.
|
|
261
|
+
If not provided, default values are be used.
|
|
262
|
+
|
|
263
|
+
For D < 0.12, Atlas1973 relationship results output V = 0 m/s !
|
|
264
|
+
For D < 0.05, VanDijk2002 relationship results output V = 0 m/s !
|
|
265
|
+
For D < 0.02, Brandes relationship results output V = 0 m/s !
|
|
266
|
+
|
|
239
267
|
"""
|
|
240
|
-
# Input diameter in mm
|
|
241
|
-
dict_methods = {
|
|
242
|
-
"Atlas1973": get_fall_velocity_atlas_1973,
|
|
243
|
-
"Beard1976": get_fall_velocity_beard_1976,
|
|
244
|
-
"Brandes2002": get_fall_velocity_brandes_2002,
|
|
245
|
-
"Uplinger1981": get_fall_velocity_uplinger_1981,
|
|
246
|
-
"VanDijk2002": get_fall_velocity_van_dijk_2002,
|
|
247
|
-
}
|
|
248
268
|
# Check valid method
|
|
249
|
-
|
|
250
|
-
if method not in dict_methods:
|
|
251
|
-
raise ValueError(f"{method} is an invalid fall velocity method. Valid methods: {available_methods}.")
|
|
252
|
-
# Copy diameter
|
|
253
|
-
diameter = diameter.copy()
|
|
254
|
-
# Initialize ds_env if None
|
|
255
|
-
# if ds_env is None:
|
|
256
|
-
# ds_env = load_env_dataset(ds_env)
|
|
269
|
+
model = check_raindrop_fall_velocity_model(model)
|
|
257
270
|
|
|
258
|
-
#
|
|
271
|
+
# Copy diameter
|
|
272
|
+
if isinstance(diameter, xr.DataArray):
|
|
273
|
+
diameter = diameter.copy()
|
|
274
|
+
else:
|
|
275
|
+
diameter = np.atleast_1d(diameter)
|
|
276
|
+
diameter = xr.DataArray(diameter, dims=DIAMETER_DIMENSION, coords={DIAMETER_DIMENSION: diameter.copy()})
|
|
277
|
+
|
|
278
|
+
# Initialize ds_env if None and method == "Beard1976"
|
|
279
|
+
if model == "Beard1976":
|
|
280
|
+
if ds_env is None:
|
|
281
|
+
ds_env = load_env_dataset()
|
|
282
|
+
|
|
283
|
+
# Ensure valid altitude and geolocation
|
|
284
|
+
# - altitude required by Beard
|
|
285
|
+
# - latitude required for gravity
|
|
286
|
+
for coord in ["altitude", "latitude"]:
|
|
287
|
+
ds_env = ensure_valid_geolocation(ds_env, coord=coord, errors="raise")
|
|
259
288
|
|
|
260
|
-
# Ensure valid altitude and geolocation (if missing set defaults)
|
|
261
|
-
# - altitude required by Beard
|
|
262
|
-
# - latitude required for gravity
|
|
263
|
-
ds_env = ensure_valid_coordinates(ds_env)
|
|
264
289
|
# Retrieve fall velocity
|
|
265
|
-
func =
|
|
266
|
-
|
|
267
|
-
|
|
290
|
+
func = get_raindrop_fall_velocity_model(model)
|
|
291
|
+
with suppress_warnings(): # e.g. when diameter = 0 for Beard1976
|
|
292
|
+
fall_velocity = func(diameter, ds_env=ds_env) if model == "Beard1976" else func(diameter)
|
|
293
|
+
|
|
294
|
+
# Set to NaN for diameter outside [0, 10)
|
|
295
|
+
fall_velocity = fall_velocity.where(diameter < 10).where(diameter > 0)
|
|
296
|
+
# Ensure fall velocity is > 0 to avoid division by zero
|
|
297
|
+
# - Some models, at small diameter, can return negative/zero fall velocity
|
|
298
|
+
fall_velocity = fall_velocity.where(fall_velocity > 0)
|
|
268
299
|
|
|
300
|
+
# Add attributes
|
|
301
|
+
fall_velocity.name = "fall_velocity"
|
|
302
|
+
fall_velocity.attrs["units"] = "m/s"
|
|
303
|
+
fall_velocity.attrs["model"] = model
|
|
304
|
+
return fall_velocity.squeeze()
|
|
269
305
|
|
|
270
|
-
|
|
271
|
-
|
|
306
|
+
|
|
307
|
+
def get_raindrop_fall_velocity_from_ds(ds, ds_env=None, model="Beard1976"):
|
|
308
|
+
"""Compute the raindrop fall velocity.
|
|
272
309
|
|
|
273
310
|
Parameters
|
|
274
311
|
----------
|
|
275
312
|
ds : xarray.Dataset
|
|
276
|
-
DISDRODB
|
|
277
|
-
|
|
278
|
-
|
|
313
|
+
DISDRODB dataset with the ``'diameter_bin_center'`` coordinate.
|
|
314
|
+
The ``'altitude'`` and ``'latitude'`` coordinates are used if ``model='Beard1976'``.
|
|
315
|
+
model : str, optional
|
|
316
|
+
Model to compute rain drop fall velocity.
|
|
317
|
+
The default model is ``"Beard1976"``.
|
|
318
|
+
ds_env : xr.Dataset, optional
|
|
319
|
+
Only required if model is 'Beard1976'.
|
|
320
|
+
A dataset containing the following environmental variables:
|
|
321
|
+
- 'temperature' : Temperature in degrees Kelvin (K).
|
|
322
|
+
- 'relative_humidity' : Relative humidity. A value between 0 and 1.
|
|
323
|
+
- 'sea_level_air_pressure' : Sea level air pressure in Pascals (Pa).
|
|
324
|
+
- 'lapse_rate' : Lapse rate in degrees Celsius per meter (°C/m).
|
|
325
|
+
If not specified, sensible default values are used.
|
|
279
326
|
|
|
280
327
|
Returns
|
|
281
328
|
-------
|
|
282
|
-
xarray.
|
|
283
|
-
|
|
329
|
+
xarray.DataArray
|
|
330
|
+
Rain drop fall velocity DataArray.
|
|
331
|
+
|
|
332
|
+
Notes
|
|
333
|
+
-----
|
|
334
|
+
The 'Beard1976' model requires additional environmental parameters.
|
|
335
|
+
These parameters can be provided through the `ds_env` argument.
|
|
336
|
+
If not provided, default values are be used.
|
|
337
|
+
|
|
338
|
+
For D < 0.12, Atlas1973 relationship results output V = 0 m/s
|
|
339
|
+
For D < 0.05, VanDijk2002 relationship results output V = 0 m/s
|
|
340
|
+
For D < 0.02, Brandes relationship results output V = 0 m/s
|
|
341
|
+
|
|
284
342
|
"""
|
|
285
343
|
from disdrodb.constants import DIAMETER_DIMENSION
|
|
286
344
|
from disdrodb.l1_env.routines import load_env_dataset
|
|
@@ -289,18 +347,13 @@ def get_dataset_fall_velocity(ds, method="Brandes2002"):
|
|
|
289
347
|
if DIAMETER_DIMENSION not in ds.dims:
|
|
290
348
|
raise ValueError(f"Diameter dimension '{DIAMETER_DIMENSION}' not found in dataset dimensions.")
|
|
291
349
|
|
|
292
|
-
# Retrieve
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
default_geolocation = {"altitude": 0, "latitude": 0, "longitude": 0}
|
|
298
|
-
dataset_coords = {key: ds[key] for key in default_geolocation if key in ds}
|
|
299
|
-
default_geolocation.update(dataset_coords)
|
|
300
|
-
ds = ds.assign_coords(default_geolocation)
|
|
350
|
+
# Retrieve ENV dataset
|
|
351
|
+
# - It checks and includes default geolocation if missing
|
|
352
|
+
# - For mobile disdrometer, infill missing geolocation with backward and forward filling
|
|
353
|
+
if ds_env is None:
|
|
354
|
+
ds_env = load_env_dataset(ds)
|
|
301
355
|
|
|
302
|
-
#
|
|
303
|
-
|
|
356
|
+
# Compute raindrop fall velocity
|
|
357
|
+
fall_velocity = get_raindrop_fall_velocity(diameter=ds["diameter_bin_center"], model=model, ds_env=ds_env) # mn
|
|
304
358
|
|
|
305
|
-
fall_velocity = get_raindrop_fall_velocity(diameter_bin_center, method=method, ds_env=ds_env)
|
|
306
359
|
return fall_velocity
|
disdrodb/l1/filters.py
CHANGED
|
@@ -57,17 +57,15 @@ def filter_diameter_bins(ds, minimum_diameter=None, maximum_diameter=None):
|
|
|
57
57
|
ds["diameter_bin_upper"] > minimum_diameter,
|
|
58
58
|
ds["diameter_bin_lower"] < maximum_diameter,
|
|
59
59
|
)
|
|
60
|
-
|
|
61
|
-
# Select bins with diameter values entirely inside the specified min/max values
|
|
62
|
-
# valid_indices = np.logical_and(
|
|
63
|
-
# ds["diameter_bin_lower"] >= minimum_diameter,
|
|
64
|
-
# ds["diameter_bin_upper"] <= maximum_diameter,
|
|
65
|
-
# )
|
|
66
60
|
ds = ds.isel({DIAMETER_DIMENSION: valid_indices})
|
|
61
|
+
|
|
62
|
+
if ds.sizes[DIAMETER_DIMENSION] == 0:
|
|
63
|
+
msg = f"Filtering using {minimum_diameter=} removes all diameter bins."
|
|
64
|
+
raise ValueError(msg)
|
|
67
65
|
return ds
|
|
68
66
|
|
|
69
67
|
|
|
70
|
-
def filter_velocity_bins(ds, minimum_velocity=
|
|
68
|
+
def filter_velocity_bins(ds, minimum_velocity=None, maximum_velocity=None):
|
|
71
69
|
"""
|
|
72
70
|
Filter the dataset to include only velocity bins within specified bounds.
|
|
73
71
|
|
|
@@ -77,10 +75,10 @@ def filter_velocity_bins(ds, minimum_velocity=0, maximum_velocity=12):
|
|
|
77
75
|
The dataset containing velocity bin data.
|
|
78
76
|
minimum_velocity : float, optional
|
|
79
77
|
The minimum velocity to include in the filter, in meters per second.
|
|
80
|
-
Defaults to
|
|
78
|
+
Defaults to the minimum value in `ds["velocity_bin_lower"]`.
|
|
81
79
|
maximum_velocity : float, optional
|
|
82
80
|
The maximum velocity to include in the filter, in meters per second.
|
|
83
|
-
Defaults to
|
|
81
|
+
Defaults to the maximum value in `ds["velocity_bin_upper"]`.
|
|
84
82
|
|
|
85
83
|
Returns
|
|
86
84
|
-------
|
|
@@ -103,16 +101,14 @@ def filter_velocity_bins(ds, minimum_velocity=0, maximum_velocity=12):
|
|
|
103
101
|
ds["velocity_bin_lower"] < maximum_velocity,
|
|
104
102
|
)
|
|
105
103
|
|
|
106
|
-
# Select bins with velocity values entirely inside the specified min/max values
|
|
107
|
-
# valid_indices = np.logical_and(
|
|
108
|
-
# ds["velocity_bin_lower"] >= minimum_velocity,
|
|
109
|
-
# ds["velocity_bin_upper"] <= maximum_velocity,
|
|
110
|
-
# )
|
|
111
104
|
ds = ds.isel({VELOCITY_DIMENSION: valid_indices})
|
|
105
|
+
if ds.sizes[VELOCITY_DIMENSION] == 0:
|
|
106
|
+
msg = f"Filtering using {minimum_velocity=} removes all velocity bins."
|
|
107
|
+
raise ValueError(msg)
|
|
112
108
|
return ds
|
|
113
109
|
|
|
114
110
|
|
|
115
|
-
def
|
|
111
|
+
def define_raindrop_spectrum_mask(
|
|
116
112
|
drop_number,
|
|
117
113
|
fall_velocity,
|
|
118
114
|
above_velocity_fraction=None,
|
|
@@ -130,29 +126,29 @@ def define_spectrum_mask(
|
|
|
130
126
|
drop_number : xarray.DataArray
|
|
131
127
|
Array of drop counts per diameter and velocity bins.
|
|
132
128
|
fall_velocity : array-like
|
|
133
|
-
The expected terminal fall velocities for drops of given sizes.
|
|
129
|
+
The expected terminal fall velocities for rain drops of given sizes.
|
|
134
130
|
above_velocity_fraction : float, optional
|
|
135
|
-
Fraction of terminal fall velocity above which drops are considered too fast.
|
|
131
|
+
Fraction of terminal fall velocity above which rain drops are considered too fast.
|
|
136
132
|
Either specify ``above_velocity_fraction`` or ``above_velocity_tolerance``.
|
|
137
133
|
above_velocity_tolerance : float, optional
|
|
138
|
-
Absolute tolerance above which drops terminal fall velocities are considered too fast.
|
|
134
|
+
Absolute tolerance above which rain drops terminal fall velocities are considered too fast.
|
|
139
135
|
Either specify ``above_velocity_fraction`` or ``above_velocity_tolerance``.
|
|
140
136
|
below_velocity_fraction : float, optional
|
|
141
|
-
Fraction of terminal fall velocity below which drops are considered too slow.
|
|
137
|
+
Fraction of terminal fall velocity below which rain drops are considered too slow.
|
|
142
138
|
Either specify ``below_velocity_fraction`` or ``below_velocity_tolerance``.
|
|
143
139
|
below_velocity_tolerance : float, optional
|
|
144
|
-
Absolute tolerance below which drops terminal fall velocities are considered too slow.
|
|
140
|
+
Absolute tolerance below which rain drops terminal fall velocities are considered too slow.
|
|
145
141
|
Either specify ``below_velocity_fraction`` or ``below_velocity_tolerance``.
|
|
146
142
|
maintain_smallest : bool, optional
|
|
147
|
-
If True, ensures that the small drops in the spectrum are retained in the mask.
|
|
148
|
-
The smallest drops are characterized by ``small_diameter_threshold``
|
|
143
|
+
If True, ensures that the small rain drops in the spectrum are retained in the mask.
|
|
144
|
+
The smallest rain drops are characterized by ``small_diameter_threshold``
|
|
149
145
|
and ``small_velocity_threshold`` arguments.
|
|
150
146
|
Defaults to False.
|
|
151
147
|
small_diameter_threshold : float, optional
|
|
152
|
-
The diameter threshold to use for keeping the smallest drop.
|
|
148
|
+
The diameter threshold to use for keeping the smallest rain drop.
|
|
153
149
|
Defaults to 1 mm.
|
|
154
150
|
small_velocity_threshold : float, optional
|
|
155
|
-
The fall velocity threshold to use for keeping the smallest drops.
|
|
151
|
+
The fall velocity threshold to use for keeping the smallest rain drops.
|
|
156
152
|
Defaults to 2.5 m/s.
|
|
157
153
|
|
|
158
154
|
Returns
|
|
@@ -178,6 +174,7 @@ def define_spectrum_mask(
|
|
|
178
174
|
above_fall_velocity = fall_velocity + above_velocity_tolerance
|
|
179
175
|
else:
|
|
180
176
|
above_fall_velocity = np.inf
|
|
177
|
+
|
|
181
178
|
if below_velocity_fraction is not None:
|
|
182
179
|
below_fall_velocity = fall_velocity * (1 - below_velocity_fraction)
|
|
183
180
|
elif below_velocity_tolerance is not None:
|
|
@@ -191,15 +188,15 @@ def define_spectrum_mask(
|
|
|
191
188
|
|
|
192
189
|
# Define mask
|
|
193
190
|
mask = np.logical_and(
|
|
194
|
-
|
|
195
|
-
|
|
191
|
+
velocity_upper > below_fall_velocity,
|
|
192
|
+
velocity_lower < above_fall_velocity,
|
|
196
193
|
)
|
|
197
194
|
|
|
198
195
|
# Maintant smallest drops
|
|
199
196
|
if maintain_smallest_drops:
|
|
200
197
|
mask_smallest = np.logical_and(
|
|
201
|
-
drop_number["diameter_bin_upper"]
|
|
202
|
-
drop_number["velocity_bin_upper"]
|
|
198
|
+
drop_number["diameter_bin_upper"] <= small_diameter_threshold,
|
|
199
|
+
drop_number["velocity_bin_upper"] <= small_velocity_threshold,
|
|
203
200
|
)
|
|
204
201
|
mask = np.logical_or(mask, mask_smallest)
|
|
205
202
|
|
disdrodb/l1/processing.py
CHANGED
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
import xarray as xr
|
|
20
20
|
|
|
21
21
|
from disdrodb.constants import DIAMETER_DIMENSION, VELOCITY_DIMENSION
|
|
22
|
-
from disdrodb.l1.fall_velocity import
|
|
23
|
-
from disdrodb.l1.filters import
|
|
22
|
+
from disdrodb.l1.fall_velocity import get_raindrop_fall_velocity_from_ds
|
|
23
|
+
from disdrodb.l1.filters import define_raindrop_spectrum_mask, filter_diameter_bins, filter_velocity_bins
|
|
24
24
|
from disdrodb.l1.resampling import add_sample_interval
|
|
25
25
|
from disdrodb.l1_env.routines import load_env_dataset
|
|
26
26
|
from disdrodb.l2.empirical_dsd import ( # TODO: maybe move out of L2
|
|
@@ -34,7 +34,7 @@ from disdrodb.utils.writer import finalize_product
|
|
|
34
34
|
def generate_l1(
|
|
35
35
|
ds,
|
|
36
36
|
# Fall velocity option
|
|
37
|
-
|
|
37
|
+
fall_velocity_model="Beard1976",
|
|
38
38
|
# Diameter-Velocity Filtering Options
|
|
39
39
|
minimum_diameter=0,
|
|
40
40
|
maximum_diameter=10,
|
|
@@ -54,7 +54,7 @@ def generate_l1(
|
|
|
54
54
|
----------
|
|
55
55
|
ds : xarray.Dataset
|
|
56
56
|
DISDRODB L0C dataset.
|
|
57
|
-
|
|
57
|
+
fall_velocity_model : str, optional
|
|
58
58
|
Method to compute fall velocity.
|
|
59
59
|
The default method is ``"Beard1976"``.
|
|
60
60
|
minimum_diameter : float, optional
|
|
@@ -106,7 +106,9 @@ def generate_l1(
|
|
|
106
106
|
|
|
107
107
|
# ---------------------------------------------------------------------------
|
|
108
108
|
# Retrieve ENV dataset or take defaults
|
|
109
|
-
#
|
|
109
|
+
# - Used only for Beard fall velocity currently !
|
|
110
|
+
# - It checks and includes default geolocation if missing
|
|
111
|
+
# - For mobile disdrometer, infill missing geolocation with backward and forward filling
|
|
110
112
|
ds_env = load_env_dataset(ds)
|
|
111
113
|
|
|
112
114
|
# ---------------------------------------------------------------------------
|
|
@@ -120,7 +122,7 @@ def generate_l1(
|
|
|
120
122
|
ds_l1 = add_sample_interval(ds_l1, sample_interval=sample_interval)
|
|
121
123
|
|
|
122
124
|
# Add L0C coordinates that might got lost
|
|
123
|
-
if "time_qc" in
|
|
125
|
+
if "time_qc" in ds:
|
|
124
126
|
ds_l1 = ds_l1.assign_coords({"time_qc": ds["time_qc"]})
|
|
125
127
|
|
|
126
128
|
# -------------------------------------------------------------------------------------------
|
|
@@ -128,7 +130,7 @@ def generate_l1(
|
|
|
128
130
|
if sensor_name in ["PARSIVEL", "PARSIVEL2"]:
|
|
129
131
|
# - Remove first two bins because never reports data !
|
|
130
132
|
# - If not removed, can alter e.g. L2M model fitting
|
|
131
|
-
ds_l1 = filter_diameter_bins(ds=ds_l1, minimum_diameter=0.
|
|
133
|
+
ds_l1 = filter_diameter_bins(ds=ds_l1, minimum_diameter=0.2495) # it includes the 0.2495-0.3745 bin
|
|
132
134
|
|
|
133
135
|
# - Filter diameter bins
|
|
134
136
|
ds_l1 = filter_diameter_bins(ds=ds_l1, minimum_diameter=minimum_diameter, maximum_diameter=maximum_diameter)
|
|
@@ -138,16 +140,12 @@ def generate_l1(
|
|
|
138
140
|
|
|
139
141
|
# -------------------------------------------------------------------------------------------
|
|
140
142
|
# Compute fall velocity
|
|
141
|
-
ds_l1["fall_velocity"] =
|
|
142
|
-
diameter=ds_l1["diameter_bin_center"],
|
|
143
|
-
method=fall_velocity_method,
|
|
144
|
-
ds_env=ds_env, # mm
|
|
145
|
-
)
|
|
143
|
+
ds_l1["fall_velocity"] = get_raindrop_fall_velocity_from_ds(ds=ds_l1, ds_env=ds_env, model=fall_velocity_model)
|
|
146
144
|
|
|
147
145
|
# -------------------------------------------------------------------------------------------
|
|
148
146
|
# Define filtering mask according to fall velocity
|
|
149
147
|
if has_velocity_dimension:
|
|
150
|
-
mask =
|
|
148
|
+
mask = define_raindrop_spectrum_mask(
|
|
151
149
|
drop_number=ds_l1["raw_drop_number"],
|
|
152
150
|
fall_velocity=ds_l1["fall_velocity"],
|
|
153
151
|
above_velocity_fraction=above_velocity_fraction,
|
disdrodb/l1_env/routines.py
CHANGED
|
@@ -15,39 +15,68 @@
|
|
|
15
15
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
16
|
# -----------------------------------------------------------------------------.
|
|
17
17
|
"""Core functions for DISDRODB ENV production."""
|
|
18
|
+
import numpy as np
|
|
18
19
|
import xarray as xr
|
|
19
20
|
|
|
20
21
|
from disdrodb.constants import GEOLOCATION_COORDS
|
|
22
|
+
from disdrodb.l0.l0b_processing import ensure_valid_geolocation
|
|
23
|
+
from disdrodb.utils.logger import log_warning
|
|
24
|
+
|
|
25
|
+
DEFAULT_GEOLOCATION = {
|
|
26
|
+
"latitude": 46.159346,
|
|
27
|
+
"longitude": 8.774586,
|
|
28
|
+
"altitude": 0,
|
|
29
|
+
}
|
|
21
30
|
|
|
22
31
|
|
|
23
32
|
def get_default_environment_dataset():
|
|
24
33
|
"""Define defaults values for the ENV dataset."""
|
|
25
34
|
ds_env = xr.Dataset()
|
|
26
|
-
ds_env["sea_level_air_pressure"] = 101_325
|
|
27
|
-
ds_env["gas_constant_dry_air"] = 287.04
|
|
28
|
-
ds_env["lapse_rate"] = 0.0065
|
|
29
|
-
ds_env["relative_humidity"] = 0.95 #
|
|
30
|
-
ds_env["temperature"] = 20 + 273.15
|
|
35
|
+
ds_env["sea_level_air_pressure"] = 101_325 # Pa
|
|
36
|
+
ds_env["gas_constant_dry_air"] = 287.04 # J kg⁻¹ K⁻¹
|
|
37
|
+
ds_env["lapse_rate"] = 0.0065 # K m⁻¹
|
|
38
|
+
ds_env["relative_humidity"] = 0.95 # 0-1 !
|
|
39
|
+
ds_env["temperature"] = 20 + 273.15 # K
|
|
40
|
+
ds_env["water_density"] = 1000 # kg m⁻³ (T == 10 --> 999.7, T == 20 --> 998.2)
|
|
41
|
+
# get_water_density(temperature=temperature, air_pressure=air_pressure
|
|
31
42
|
return ds_env
|
|
32
43
|
|
|
33
44
|
|
|
34
|
-
def _assign_geolocation(ds_src, dst_dst):
|
|
45
|
+
def _assign_geolocation(ds_src, dst_dst, logger=None):
|
|
46
|
+
dict_coords = {}
|
|
47
|
+
for coord in GEOLOCATION_COORDS:
|
|
48
|
+
if coord in ds_src:
|
|
49
|
+
# Check geolocation validity
|
|
50
|
+
ds_src = ensure_valid_geolocation(ds_src, coord=coord, errors="coerce")
|
|
51
|
+
# Assign valid geolocation (or default one if invalid)
|
|
52
|
+
if "time" not in ds_src[coord].dims:
|
|
53
|
+
dict_coords[coord] = ds_src[coord] if not np.isnan(ds_src[coord]) else DEFAULT_GEOLOCATION[coord]
|
|
54
|
+
else: # If coordinates varies over time, infill NaN over time with forward and backward filling
|
|
55
|
+
dict_coords[coord] = ds_src[coord].ffill(dim="time").bfill(dim="time")
|
|
56
|
+
else:
|
|
57
|
+
dict_coords[coord] = DEFAULT_GEOLOCATION[coord]
|
|
58
|
+
log_warning(
|
|
59
|
+
logger=logger,
|
|
60
|
+
msg=f"{coord} not available. Setting {coord}={DEFAULT_GEOLOCATION[coord]}",
|
|
61
|
+
verbose=False,
|
|
62
|
+
)
|
|
35
63
|
|
|
36
|
-
|
|
64
|
+
# Assign geolocation
|
|
37
65
|
dst_dst = dst_dst.assign_coords(dict_coords)
|
|
38
66
|
return dst_dst
|
|
39
67
|
|
|
40
68
|
|
|
41
|
-
def load_env_dataset(ds):
|
|
69
|
+
def load_env_dataset(ds=None, logger=None):
|
|
42
70
|
"""Load the ENV dataset."""
|
|
43
|
-
# TODO: Retrieve relative_humidity and temperature from
|
|
71
|
+
# TODO: Retrieve relative_humidity, lapse_rate and temperature from DISDRODB-ENV product
|
|
72
|
+
|
|
73
|
+
# Load default environment dataset
|
|
44
74
|
ds_env = get_default_environment_dataset()
|
|
45
|
-
|
|
46
|
-
#
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
#
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
ds_env = _assign_geolocation(ds_src=ds, dst_dst=ds_env)
|
|
75
|
+
|
|
76
|
+
# Assign geolocation if input dataset provided
|
|
77
|
+
if ds is not None:
|
|
78
|
+
ds_env = _assign_geolocation(ds_src=ds, dst_dst=ds_env, logger=logger)
|
|
79
|
+
# Otherwise add default geolocation
|
|
80
|
+
else:
|
|
81
|
+
ds_env = ds_env.assign_coords(DEFAULT_GEOLOCATION)
|
|
53
82
|
return ds_env
|
disdrodb/l2/empirical_dsd.py
CHANGED
|
@@ -236,6 +236,12 @@ def get_effective_sampling_area(sensor_name, diameter):
|
|
|
236
236
|
if sensor_name == "RD80":
|
|
237
237
|
sampling_area = 0.005 # m2
|
|
238
238
|
return sampling_area
|
|
239
|
+
if sensor_name == "SWS250": # TODO: L * (B - diameter / 2) ?
|
|
240
|
+
# Table 29 of the manual that the sample volume is 400cm3, path length?
|
|
241
|
+
# Distance between the end of the hood heaters is 291 mm.
|
|
242
|
+
# Adding a factor of 1.5 for better representation of the Tx-Rx distance: L= 436 mm.
|
|
243
|
+
sampling_area = 0.0091 # m2
|
|
244
|
+
return sampling_area
|
|
239
245
|
raise NotImplementedError(f"Effective sampling area for {sensor_name} must yet to be specified in the software.")
|
|
240
246
|
|
|
241
247
|
|
disdrodb/l2/processing.py
CHANGED
|
@@ -441,7 +441,7 @@ def generate_l2m(
|
|
|
441
441
|
diameter_spacing=0.05,
|
|
442
442
|
# Processing options
|
|
443
443
|
ds_env=None,
|
|
444
|
-
|
|
444
|
+
fall_velocity_model="Beard1976",
|
|
445
445
|
# Filtering options
|
|
446
446
|
minimum_ndrops=1,
|
|
447
447
|
minimum_nbins=3,
|
|
@@ -548,7 +548,7 @@ def generate_l2m(
|
|
|
548
548
|
drop_number_concentration = psd(diameter)
|
|
549
549
|
|
|
550
550
|
# Retrieve fall velocity for each new diameter bin
|
|
551
|
-
velocity = get_raindrop_fall_velocity(diameter=diameter,
|
|
551
|
+
velocity = get_raindrop_fall_velocity(diameter=diameter, model=fall_velocity_model, ds_env=ds_env) # mm
|
|
552
552
|
|
|
553
553
|
# Compute integral parameters
|
|
554
554
|
ds_params = compute_integral_parameters(
|
disdrodb/metadata/geolocation.py
CHANGED