disdrodb 0.0.21__py3-none-any.whl → 0.1.0__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 +132 -15
- disdrodb/_config.py +4 -2
- disdrodb/_version.py +9 -4
- disdrodb/api/checks.py +264 -237
- disdrodb/api/configs.py +4 -8
- disdrodb/api/create_directories.py +235 -290
- disdrodb/api/info.py +217 -26
- disdrodb/api/io.py +295 -269
- disdrodb/api/path.py +597 -173
- disdrodb/api/search.py +486 -0
- disdrodb/{metadata/scripts → cli}/disdrodb_check_metadata_archive.py +12 -7
- disdrodb/{utils/pandas.py → cli/disdrodb_data_archive_directory.py} +9 -18
- disdrodb/cli/disdrodb_download_archive.py +86 -0
- disdrodb/cli/disdrodb_download_metadata_archive.py +53 -0
- disdrodb/cli/disdrodb_download_station.py +84 -0
- disdrodb/{api/scripts → cli}/disdrodb_initialize_station.py +22 -10
- disdrodb/cli/disdrodb_metadata_archive_directory.py +32 -0
- disdrodb/{data_transfer/scripts/disdrodb_download_station.py → cli/disdrodb_open_data_archive.py} +22 -22
- disdrodb/cli/disdrodb_open_logs_directory.py +69 -0
- disdrodb/{data_transfer/scripts/disdrodb_upload_station.py → cli/disdrodb_open_metadata_archive.py} +22 -24
- disdrodb/cli/disdrodb_open_metadata_directory.py +71 -0
- disdrodb/cli/disdrodb_open_product_directory.py +74 -0
- disdrodb/cli/disdrodb_open_readers_directory.py +32 -0
- disdrodb/{l0/scripts → cli}/disdrodb_run_l0.py +38 -31
- disdrodb/{l0/scripts → cli}/disdrodb_run_l0_station.py +32 -30
- disdrodb/{l0/scripts → cli}/disdrodb_run_l0a.py +30 -21
- disdrodb/{l0/scripts → cli}/disdrodb_run_l0a_station.py +24 -33
- disdrodb/{l0/scripts → cli}/disdrodb_run_l0b.py +30 -21
- disdrodb/{l0/scripts → cli}/disdrodb_run_l0b_station.py +25 -34
- disdrodb/cli/disdrodb_run_l0c.py +130 -0
- disdrodb/cli/disdrodb_run_l0c_station.py +129 -0
- disdrodb/cli/disdrodb_run_l1.py +122 -0
- disdrodb/cli/disdrodb_run_l1_station.py +121 -0
- disdrodb/cli/disdrodb_run_l2e.py +122 -0
- disdrodb/cli/disdrodb_run_l2e_station.py +122 -0
- disdrodb/cli/disdrodb_run_l2m.py +122 -0
- disdrodb/cli/disdrodb_run_l2m_station.py +122 -0
- disdrodb/cli/disdrodb_upload_archive.py +105 -0
- disdrodb/cli/disdrodb_upload_station.py +98 -0
- disdrodb/configs.py +90 -25
- disdrodb/data_transfer/__init__.py +22 -0
- disdrodb/data_transfer/download_data.py +87 -90
- disdrodb/data_transfer/upload_data.py +64 -37
- disdrodb/data_transfer/zenodo.py +15 -18
- disdrodb/docs.py +1 -1
- disdrodb/issue/__init__.py +17 -4
- disdrodb/issue/checks.py +10 -23
- disdrodb/issue/reader.py +9 -12
- disdrodb/issue/writer.py +14 -17
- disdrodb/l0/__init__.py +17 -26
- disdrodb/l0/check_configs.py +35 -23
- disdrodb/l0/check_standards.py +32 -42
- disdrodb/l0/configs/{Thies_LPM → LPM}/bins_diameter.yml +44 -44
- disdrodb/l0/configs/{Thies_LPM → LPM}/bins_velocity.yml +40 -40
- disdrodb/l0/configs/LPM/l0a_encodings.yml +80 -0
- disdrodb/l0/configs/{Thies_LPM → LPM}/l0b_cf_attrs.yml +62 -59
- disdrodb/l0/configs/{Thies_LPM → LPM}/l0b_encodings.yml +9 -9
- disdrodb/l0/configs/{Thies_LPM → LPM}/raw_data_format.yml +245 -245
- disdrodb/l0/configs/{OTT_Parsivel → PARSIVEL}/bins_diameter.yml +66 -66
- disdrodb/l0/configs/{OTT_Parsivel → PARSIVEL}/bins_velocity.yml +64 -64
- disdrodb/l0/configs/PARSIVEL/l0a_encodings.yml +32 -0
- disdrodb/l0/configs/{OTT_Parsivel → PARSIVEL}/l0b_cf_attrs.yml +22 -20
- disdrodb/l0/configs/{OTT_Parsivel → PARSIVEL}/l0b_encodings.yml +17 -17
- disdrodb/l0/configs/{OTT_Parsivel → PARSIVEL}/raw_data_format.yml +77 -77
- disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/bins_diameter.yml +64 -64
- disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/bins_velocity.yml +64 -64
- disdrodb/l0/configs/PARSIVEL2/l0a_encodings.yml +39 -0
- disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/l0b_cf_attrs.yml +24 -22
- disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/l0b_encodings.yml +20 -20
- disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/raw_data_format.yml +98 -98
- disdrodb/l0/configs/{RD_80 → RD80}/bins_diameter.yml +40 -40
- disdrodb/l0/configs/RD80/l0a_encodings.yml +16 -0
- disdrodb/l0/configs/{RD_80 → RD80}/l0b_cf_attrs.yml +3 -3
- disdrodb/l0/configs/RD80/l0b_encodings.yml +135 -0
- disdrodb/l0/configs/{RD_80 → RD80}/raw_data_format.yml +48 -48
- disdrodb/l0/l0_reader.py +216 -340
- disdrodb/l0/l0a_processing.py +237 -208
- disdrodb/l0/l0b_nc_processing.py +227 -80
- disdrodb/l0/l0b_processing.py +93 -173
- disdrodb/l0/l0c_processing.py +627 -0
- disdrodb/l0/readers/{ARM → LPM/ARM}/ARM_LPM.py +36 -58
- disdrodb/l0/readers/LPM/AUSTRALIA/MELBOURNE_2007_LPM.py +226 -0
- disdrodb/l0/readers/LPM/BRAZIL/CHUVA_LPM.py +185 -0
- disdrodb/l0/readers/LPM/BRAZIL/GOAMAZON_LPM.py +183 -0
- disdrodb/l0/readers/LPM/ITALY/GID_LPM.py +179 -0
- disdrodb/l0/readers/{UK → LPM/UK}/DIVEN.py +14 -35
- disdrodb/l0/readers/PARSIVEL/AUSTRALIA/MELBOURNE_2007_PARSIVEL.py +157 -0
- disdrodb/l0/readers/PARSIVEL/CHINA/CHONGQING.py +113 -0
- disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/ARCTIC_2021.py +40 -57
- disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/COMMON_2011.py +37 -54
- disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/DAVOS_2009_2011.py +34 -51
- disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/EPFL_2009.py +34 -51
- disdrodb/l0/readers/{EPFL/PARADISO_2014.py → PARSIVEL/EPFL/EPFL_ROOF_2008.py} +38 -50
- disdrodb/l0/readers/PARSIVEL/EPFL/EPFL_ROOF_2010.py +105 -0
- disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/EPFL_ROOF_2011.py +34 -51
- disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/EPFL_ROOF_2012.py +33 -51
- disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/GENEPI_2007.py +25 -44
- disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/GRAND_ST_BERNARD_2007.py +25 -44
- disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/GRAND_ST_BERNARD_2007_2.py +25 -44
- disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/HPICONET_2010.py +34 -51
- disdrodb/l0/readers/{EPFL/EPFL_ROOF_2010.py → PARSIVEL/EPFL/HYMEX_LTE_SOP2.py} +37 -50
- disdrodb/l0/readers/PARSIVEL/EPFL/HYMEX_LTE_SOP3.py +111 -0
- disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/HYMEX_LTE_SOP4.py +36 -54
- disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/LOCARNO_2018.py +34 -52
- disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/LOCARNO_2019.py +38 -56
- disdrodb/l0/readers/PARSIVEL/EPFL/PARADISO_2014.py +105 -0
- disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/PARSIVEL_2007.py +27 -45
- disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/PLATO_2019.py +24 -44
- disdrodb/l0/readers/PARSIVEL/EPFL/RACLETS_2019.py +140 -0
- disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/RACLETS_2019_WJF.py +41 -59
- disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/RIETHOLZBACH_2011.py +34 -51
- disdrodb/l0/readers/PARSIVEL/EPFL/SAMOYLOV_2017.py +117 -0
- disdrodb/l0/readers/PARSIVEL/EPFL/SAMOYLOV_2019.py +137 -0
- disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/UNIL_2022.py +42 -55
- disdrodb/l0/readers/PARSIVEL/GPM/IFLOODS.py +104 -0
- disdrodb/l0/readers/{GPM → PARSIVEL/GPM}/LPVEX.py +29 -48
- disdrodb/l0/readers/PARSIVEL/GPM/MC3E.py +184 -0
- disdrodb/l0/readers/PARSIVEL/NCAR/CCOPE_2015.py +113 -0
- disdrodb/l0/readers/{NCAR/VORTEX_SE_2016_P1.py → PARSIVEL/NCAR/OWLES_MIPS.py} +46 -72
- disdrodb/l0/readers/PARSIVEL/NCAR/PECAN_MOBILE.py +125 -0
- disdrodb/l0/readers/{NCAR/OWLES_MIPS.py → PARSIVEL/NCAR/PLOWS_MIPS.py} +45 -64
- disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2009.py +114 -0
- disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010.py +176 -0
- disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010_UF.py +183 -0
- disdrodb/l0/readers/{ARM/ARM_LD.py → PARSIVEL2/ARM/ARM_PARSIVEL2.py} +27 -50
- disdrodb/l0/readers/PARSIVEL2/BRAZIL/CHUVA_PARSIVEL2.py +163 -0
- disdrodb/l0/readers/PARSIVEL2/BRAZIL/GOAMAZON_PARSIVEL2.py +163 -0
- disdrodb/l0/readers/{DENMARK → PARSIVEL2/DENMARK}/EROSION_nc.py +14 -35
- disdrodb/l0/readers/PARSIVEL2/FRANCE/SIRTA_PARSIVEL2.py +119 -0
- disdrodb/l0/readers/PARSIVEL2/GPM/GCPEX.py +104 -0
- disdrodb/l0/readers/PARSIVEL2/GPM/NSSTC.py +176 -0
- disdrodb/l0/readers/PARSIVEL2/ITALY/GID_PARSIVEL2.py +32 -0
- disdrodb/l0/readers/PARSIVEL2/MEXICO/OH_IIUNAM_nc.py +56 -0
- disdrodb/l0/readers/PARSIVEL2/NCAR/PECAN_FP3.py +120 -0
- disdrodb/l0/readers/{NCAR → PARSIVEL2/NCAR}/PECAN_MIPS.py +45 -64
- disdrodb/l0/readers/PARSIVEL2/NCAR/RELAMPAGO_PARSIVEL2.py +181 -0
- disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_PJ.py +160 -0
- disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_SB.py +160 -0
- disdrodb/l0/readers/{NCAR/PLOWS_MIPS.py → PARSIVEL2/NCAR/VORTEX_SE_2016_P1.py} +49 -66
- disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_P2.py +118 -0
- disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_PIPS.py +152 -0
- disdrodb/l0/readers/PARSIVEL2/NETHERLANDS/DELFT.py +166 -0
- disdrodb/l0/readers/{NCAR/RELAMPAGO_RD80.py → RD80/BRAZIL/CHUVA_RD80.py} +36 -60
- disdrodb/l0/readers/{BRAZIL → RD80/BRAZIL}/GOAMAZON_RD80.py +36 -55
- disdrodb/l0/readers/{NCAR → RD80/NCAR}/CINDY_2011_RD80.py +35 -54
- disdrodb/l0/readers/{BRAZIL/CHUVA_RD80.py → RD80/NCAR/RELAMPAGO_RD80.py} +40 -54
- disdrodb/l0/readers/template_reader_raw_netcdf_data.py +62 -0
- disdrodb/l0/readers/{reader_template.py → template_reader_raw_text_data.py} +20 -44
- disdrodb/l0/routines.py +885 -581
- disdrodb/l0/standards.py +72 -236
- disdrodb/l0/template_tools.py +104 -109
- disdrodb/l1/__init__.py +17 -0
- disdrodb/l1/beard_model.py +716 -0
- disdrodb/l1/encoding_attrs.py +620 -0
- disdrodb/l1/fall_velocity.py +260 -0
- disdrodb/l1/filters.py +192 -0
- disdrodb/l1/processing.py +200 -0
- disdrodb/l1/resampling.py +236 -0
- disdrodb/l1/routines.py +357 -0
- disdrodb/l1_env/__init__.py +17 -0
- disdrodb/l1_env/routines.py +38 -0
- disdrodb/l2/__init__.py +17 -0
- disdrodb/l2/empirical_dsd.py +1735 -0
- disdrodb/l2/event.py +388 -0
- disdrodb/l2/processing.py +519 -0
- disdrodb/l2/processing_options.py +213 -0
- disdrodb/l2/routines.py +868 -0
- disdrodb/metadata/__init__.py +9 -2
- disdrodb/metadata/checks.py +165 -118
- disdrodb/metadata/download.py +81 -0
- disdrodb/metadata/geolocation.py +146 -0
- disdrodb/metadata/info.py +20 -13
- disdrodb/metadata/manipulation.py +1 -1
- disdrodb/metadata/reader.py +59 -8
- disdrodb/metadata/search.py +77 -144
- disdrodb/metadata/standards.py +7 -8
- disdrodb/metadata/writer.py +8 -14
- disdrodb/psd/__init__.py +38 -0
- disdrodb/psd/fitting.py +2146 -0
- disdrodb/psd/models.py +774 -0
- disdrodb/routines.py +1176 -0
- disdrodb/scattering/__init__.py +28 -0
- disdrodb/scattering/axis_ratio.py +344 -0
- disdrodb/scattering/routines.py +456 -0
- disdrodb/utils/__init__.py +17 -0
- disdrodb/utils/attrs.py +208 -0
- disdrodb/utils/cli.py +269 -0
- disdrodb/utils/compression.py +60 -42
- disdrodb/utils/dask.py +62 -0
- disdrodb/utils/decorators.py +110 -0
- disdrodb/utils/directories.py +107 -46
- disdrodb/utils/encoding.py +127 -0
- disdrodb/utils/list.py +29 -0
- disdrodb/utils/logger.py +168 -46
- disdrodb/utils/time.py +657 -0
- disdrodb/utils/warnings.py +30 -0
- disdrodb/utils/writer.py +57 -0
- disdrodb/utils/xarray.py +138 -47
- disdrodb/utils/yaml.py +0 -1
- disdrodb/viz/__init__.py +17 -0
- disdrodb/viz/plots.py +17 -0
- disdrodb-0.1.0.dist-info/METADATA +321 -0
- disdrodb-0.1.0.dist-info/RECORD +216 -0
- {disdrodb-0.0.21.dist-info → disdrodb-0.1.0.dist-info}/WHEEL +1 -1
- disdrodb-0.1.0.dist-info/entry_points.txt +30 -0
- disdrodb/data_transfer/scripts/disdrodb_download_archive.py +0 -53
- disdrodb/data_transfer/scripts/disdrodb_upload_archive.py +0 -57
- disdrodb/l0/configs/OTT_Parsivel/l0a_encodings.yml +0 -32
- disdrodb/l0/configs/OTT_Parsivel2/l0a_encodings.yml +0 -39
- disdrodb/l0/configs/RD_80/l0a_encodings.yml +0 -16
- disdrodb/l0/configs/RD_80/l0b_encodings.yml +0 -135
- disdrodb/l0/configs/Thies_LPM/l0a_encodings.yml +0 -80
- disdrodb/l0/io.py +0 -257
- disdrodb/l0/l0_processing.py +0 -1091
- disdrodb/l0/readers/AUSTRALIA/MELBOURNE_2007_OTT.py +0 -178
- disdrodb/l0/readers/AUSTRALIA/MELBOURNE_2007_THIES.py +0 -247
- disdrodb/l0/readers/BRAZIL/CHUVA_LPM.py +0 -204
- disdrodb/l0/readers/BRAZIL/CHUVA_OTT.py +0 -183
- disdrodb/l0/readers/BRAZIL/GOAMAZON_LPM.py +0 -204
- disdrodb/l0/readers/BRAZIL/GOAMAZON_OTT.py +0 -183
- disdrodb/l0/readers/CHINA/CHONGQING.py +0 -131
- disdrodb/l0/readers/EPFL/EPFL_ROOF_2008.py +0 -128
- disdrodb/l0/readers/EPFL/HYMEX_LTE_SOP2.py +0 -127
- disdrodb/l0/readers/EPFL/HYMEX_LTE_SOP3.py +0 -129
- disdrodb/l0/readers/EPFL/RACLETS_2019.py +0 -158
- disdrodb/l0/readers/EPFL/SAMOYLOV_2017.py +0 -136
- disdrodb/l0/readers/EPFL/SAMOYLOV_2019.py +0 -158
- disdrodb/l0/readers/FRANCE/SIRTA_OTT2.py +0 -138
- disdrodb/l0/readers/GPM/GCPEX.py +0 -123
- disdrodb/l0/readers/GPM/IFLOODS.py +0 -123
- disdrodb/l0/readers/GPM/MC3E.py +0 -123
- disdrodb/l0/readers/GPM/NSSTC.py +0 -164
- disdrodb/l0/readers/ITALY/GID.py +0 -199
- disdrodb/l0/readers/MEXICO/OH_IIUNAM_nc.py +0 -92
- disdrodb/l0/readers/NCAR/CCOPE_2015.py +0 -133
- disdrodb/l0/readers/NCAR/PECAN_FP3.py +0 -137
- disdrodb/l0/readers/NCAR/PECAN_MOBILE.py +0 -144
- disdrodb/l0/readers/NCAR/RELAMPAGO_OTT.py +0 -195
- disdrodb/l0/readers/NCAR/SNOWIE_PJ.py +0 -172
- disdrodb/l0/readers/NCAR/SNOWIE_SB.py +0 -179
- disdrodb/l0/readers/NCAR/VORTEX2_2009.py +0 -133
- disdrodb/l0/readers/NCAR/VORTEX2_2010.py +0 -188
- disdrodb/l0/readers/NCAR/VORTEX2_2010_UF.py +0 -191
- disdrodb/l0/readers/NCAR/VORTEX_SE_2016_P2.py +0 -135
- disdrodb/l0/readers/NCAR/VORTEX_SE_2016_PIPS.py +0 -170
- disdrodb/l0/readers/NETHERLANDS/DELFT.py +0 -187
- disdrodb/l0/readers/SPAIN/SBEGUERIA.py +0 -179
- disdrodb/l0/scripts/disdrodb_run_l0b_concat.py +0 -93
- disdrodb/l0/scripts/disdrodb_run_l0b_concat_station.py +0 -85
- disdrodb/utils/netcdf.py +0 -452
- disdrodb/utils/scripts.py +0 -102
- disdrodb-0.0.21.dist-info/AUTHORS.md +0 -18
- disdrodb-0.0.21.dist-info/METADATA +0 -186
- disdrodb-0.0.21.dist-info/RECORD +0 -168
- disdrodb-0.0.21.dist-info/entry_points.txt +0 -15
- /disdrodb/l0/configs/{RD_80 → RD80}/bins_velocity.yml +0 -0
- /disdrodb/l0/manuals/{Thies_LPM.pdf → LPM.pdf} +0 -0
- /disdrodb/l0/manuals/{ODM_470.pdf → ODM470.pdf} +0 -0
- /disdrodb/l0/manuals/{OTT_Parsivel.pdf → PARSIVEL.pdf} +0 -0
- /disdrodb/l0/manuals/{OTT_Parsivel2.pdf → PARSIVEL2.pdf} +0 -0
- /disdrodb/l0/manuals/{PWS_100.pdf → PWS100.pdf} +0 -0
- /disdrodb/l0/manuals/{RD_80.pdf → RD80.pdf} +0 -0
- {disdrodb-0.0.21.dist-info → disdrodb-0.1.0.dist-info/licenses}/LICENSE +0 -0
- {disdrodb-0.0.21.dist-info → disdrodb-0.1.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1735 @@
|
|
|
1
|
+
# -----------------------------------------------------------------------------.
|
|
2
|
+
# Copyright (c) 2021-2023 DISDRODB developers
|
|
3
|
+
#
|
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
# (at your option) any later version.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
16
|
+
# -----------------------------------------------------------------------------.
|
|
17
|
+
"""Functions for computation of DSD parameters.
|
|
18
|
+
|
|
19
|
+
The functions of this module expects xarray.DataArray objects as input.
|
|
20
|
+
Zeros and NaN values input arrays are correctly processed.
|
|
21
|
+
Infinite values should be removed beforehand or otherwise are propagated throughout the computations.
|
|
22
|
+
"""
|
|
23
|
+
import numpy as np
|
|
24
|
+
import xarray as xr
|
|
25
|
+
|
|
26
|
+
from disdrodb import DIAMETER_DIMENSION, VELOCITY_DIMENSION
|
|
27
|
+
from disdrodb.api.checks import check_sensor_name
|
|
28
|
+
from disdrodb.utils.xarray import (
|
|
29
|
+
remove_diameter_coordinates,
|
|
30
|
+
remove_velocity_coordinates,
|
|
31
|
+
xr_get_last_valid_idx,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_drop_volume(diameter):
|
|
36
|
+
"""
|
|
37
|
+
Compute the volume of a droplet assuming it is spherical.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
diameter : float or array-like
|
|
42
|
+
The diameter of the droplet(s). Can be a scalar or an array of diameters.
|
|
43
|
+
|
|
44
|
+
Returns
|
|
45
|
+
-------
|
|
46
|
+
array-like
|
|
47
|
+
The volume of the droplet(s) calculated in cubic units based on the input diameter(s).
|
|
48
|
+
|
|
49
|
+
Notes
|
|
50
|
+
-----
|
|
51
|
+
The volume is calculated using the formula for the volume of a sphere:
|
|
52
|
+
V = (π/6) * d^3, where d is the diameter of the droplet.
|
|
53
|
+
"""
|
|
54
|
+
return np.pi / 6 * diameter**3 # 1/6 = 4/3*(0.5**3)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def get_drop_average_velocity(drop_number):
|
|
58
|
+
r"""
|
|
59
|
+
Calculate the drop average velocity \\( v_m(D))) \\) per diameter class.
|
|
60
|
+
|
|
61
|
+
The average velocity is obtained by weighting by the number of drops in each velocity bin.
|
|
62
|
+
If in a given diameter bin no drops are recorded, the resulting average drop size velocity for
|
|
63
|
+
such bin will be set to NaN.
|
|
64
|
+
|
|
65
|
+
Parameters
|
|
66
|
+
----------
|
|
67
|
+
drop_number : xarray.DataArray
|
|
68
|
+
Array of drop counts \\( n(D,v) \\) per diameter (and velocity, if available) bins
|
|
69
|
+
over the time integration period.
|
|
70
|
+
The DataArray must have the ``velocity_bin_center`` coordinate.
|
|
71
|
+
|
|
72
|
+
Returns
|
|
73
|
+
-------
|
|
74
|
+
average_velocity : xarray.DataArray
|
|
75
|
+
Array of drop average velocity \\( v_m(D))) \\) in m·s⁻¹ .
|
|
76
|
+
At timesteps with zero drop counts, it returns NaN.
|
|
77
|
+
"""
|
|
78
|
+
velocity = xr.ones_like(drop_number) * drop_number["velocity_bin_center"]
|
|
79
|
+
average_velocity = ((velocity * drop_number).sum(dim=VELOCITY_DIMENSION, skipna=False)) / drop_number.sum(
|
|
80
|
+
dim=VELOCITY_DIMENSION,
|
|
81
|
+
skipna=False,
|
|
82
|
+
)
|
|
83
|
+
return average_velocity
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def count_bins_with_drops(ds):
|
|
87
|
+
"""Count the number of diameter bins with data."""
|
|
88
|
+
# Select useful variable
|
|
89
|
+
candidate_variables = ["drop_counts", "drop_number_concentration", "drop_number"]
|
|
90
|
+
available_variables = [var for var in candidate_variables if var in ds]
|
|
91
|
+
if len(available_variables) == 0:
|
|
92
|
+
raise ValueError(f"One of these variables is required: {candidate_variables}")
|
|
93
|
+
da = ds[available_variables[0]]
|
|
94
|
+
if VELOCITY_DIMENSION in da.dims:
|
|
95
|
+
da = da.sum(dim=VELOCITY_DIMENSION)
|
|
96
|
+
# Count number of bins with data
|
|
97
|
+
da = (da > 0).sum(dim=DIAMETER_DIMENSION)
|
|
98
|
+
# TODO: remove this in future !
|
|
99
|
+
if "velocity_method" in da.dims:
|
|
100
|
+
da = da.max(dim="velocity_method")
|
|
101
|
+
return da
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
####-------------------------------------------------------------------------------------------------------------------.
|
|
105
|
+
#### DSD Spectrum, Concentration, Moments
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def get_effective_sampling_area(sensor_name, diameter):
|
|
109
|
+
"""Compute the effective sampling area in m2 of the disdrometer.
|
|
110
|
+
|
|
111
|
+
The diameter must be provided in meters !
|
|
112
|
+
"""
|
|
113
|
+
check_sensor_name(sensor_name)
|
|
114
|
+
if sensor_name in ["PARSIVEL", "PARSIVEL2"]:
|
|
115
|
+
# Calculate sampling area for each diameter bin (S_i)
|
|
116
|
+
L = 180 / 1000 # Length of the Parsivel beam in m (180 mm)
|
|
117
|
+
B = 30 / 1000 # Width of the Parsivel beam in m (30mm)
|
|
118
|
+
sampling_area = L * (B - diameter / 2)
|
|
119
|
+
return sampling_area
|
|
120
|
+
if sensor_name in "LPM":
|
|
121
|
+
# Calculate sampling area for each diameter bin (S_i)
|
|
122
|
+
L = 228 / 1000 # Length of the Parsivel beam in m (228 mm)
|
|
123
|
+
B = 20 / 1000 # Width of the Parsivel beam in m (20 mm)
|
|
124
|
+
sampling_area = L * (B - diameter / 2)
|
|
125
|
+
return sampling_area
|
|
126
|
+
if sensor_name in "RD80":
|
|
127
|
+
sampling_area = 0.005 # m2
|
|
128
|
+
return sampling_area
|
|
129
|
+
raise NotImplementedError(f"Effective sampling area for {sensor_name} must yet to be specified in the software.")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def get_bin_dimensions(xr_obj):
|
|
133
|
+
"""Return the dimensions of the drop spectrum."""
|
|
134
|
+
return sorted([k for k in [DIAMETER_DIMENSION, VELOCITY_DIMENSION] if k in xr_obj.dims])
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def get_drop_number_concentration(drop_number, velocity, diameter_bin_width, sampling_area, sample_interval):
|
|
138
|
+
r"""
|
|
139
|
+
Calculate the volumetric drop number concentration \\( N(D) \\) per diameter class.
|
|
140
|
+
|
|
141
|
+
Computes the drop number concentration \\( N(D) \\) [m⁻³·mm⁻¹] for each diameter
|
|
142
|
+
class based on the measured drop counts and sensor parameters.
|
|
143
|
+
This represents the number of drops per unit volume per unit diameter interval.
|
|
144
|
+
It is also referred to as the drop size distribution N(D) per cubic metre per millimetre [m-3 mm-1]
|
|
145
|
+
|
|
146
|
+
Parameters
|
|
147
|
+
----------
|
|
148
|
+
velocity : xarray.DataArray
|
|
149
|
+
Array of drop fall velocities \\( v(D) \\) corresponding to each diameter bin in meters per second (m/s).
|
|
150
|
+
Typically the estimated fall velocity is used.
|
|
151
|
+
But one can also pass the velocity bin center of the optical disdrometer, which get broadcasted
|
|
152
|
+
along the diameter bin dimension.
|
|
153
|
+
diameter_bin_width : xarray.DataArray
|
|
154
|
+
Width of each diameter bin \\( \\Delta D \\) in millimeters (mm).
|
|
155
|
+
drop_number : xarray.DataArray
|
|
156
|
+
Array of drop counts \\( n(D) or n(D,v) \\) per diameter (and velocity if available)
|
|
157
|
+
bins over the time integration period.
|
|
158
|
+
sample_interval : float or xarray.DataArray
|
|
159
|
+
Time over which the drops are counted \\( \\Delta t \\) in seconds (s).
|
|
160
|
+
sampling_area : float or xarray.DataArray
|
|
161
|
+
The effective sampling area \\( A \\) of the sensor in square meters (m²).
|
|
162
|
+
|
|
163
|
+
Returns
|
|
164
|
+
-------
|
|
165
|
+
drop_number_concentration : xarray.DataArray or ndarray
|
|
166
|
+
Array of drop number concentrations \\( N(D) \\) in m⁻³·mm⁻¹, representing
|
|
167
|
+
the number of drops per unit volume per unit diameter interval.
|
|
168
|
+
|
|
169
|
+
Notes
|
|
170
|
+
-----
|
|
171
|
+
The drop number concentration \\( N(D) \\) is calculated using:
|
|
172
|
+
|
|
173
|
+
.. math::
|
|
174
|
+
|
|
175
|
+
N(D) = \frac{n(D)}{A_{\text{eff}}(D) \\cdot \\Delta D \\cdot \\Delta t \\cdot v(D)}
|
|
176
|
+
|
|
177
|
+
where:
|
|
178
|
+
|
|
179
|
+
- \\( n(D,v) \\): Number of drops counted in diameter (and velocity) bins.
|
|
180
|
+
- \\( A_{\text{eff}}(D) \\): Effective sampling area of the sensor for diameter \\( D \\) in square meters (m²).
|
|
181
|
+
- \\( \\Delta D \\): Diameter bin width in millimeters (mm).
|
|
182
|
+
- \\( \\Delta t \\): Time integration period in seconds (s).
|
|
183
|
+
- \\( v(D) \\): Fall velocity of drops in diameter bin \\( D \\) in meters per second (m/s).
|
|
184
|
+
|
|
185
|
+
The effective sampling area \\( A_{\text{eff}}(D) \\) depends on the sensor and may vary with drop diameter.
|
|
186
|
+
"""
|
|
187
|
+
# Ensure velocity is 2D (diameter, velocity)
|
|
188
|
+
velocity = xr.ones_like(drop_number) * velocity
|
|
189
|
+
|
|
190
|
+
# Compute drop number concentration
|
|
191
|
+
# - For disdrometer with velocity bins
|
|
192
|
+
if VELOCITY_DIMENSION in drop_number.dims:
|
|
193
|
+
drop_number_concentration = (drop_number / velocity).sum(dim=VELOCITY_DIMENSION, skipna=False) / (
|
|
194
|
+
sampling_area * diameter_bin_width * sample_interval
|
|
195
|
+
)
|
|
196
|
+
# - For impact disdrometers
|
|
197
|
+
else:
|
|
198
|
+
drop_number_concentration = (drop_number / velocity) / (sampling_area * diameter_bin_width * sample_interval)
|
|
199
|
+
return drop_number_concentration
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def get_total_number_concentration(drop_number_concentration, diameter_bin_width):
|
|
203
|
+
r"""
|
|
204
|
+
Compute the total number concentration \\( N_t \\) from the drop size distribution.
|
|
205
|
+
|
|
206
|
+
Calculates the total number concentration \\( N_t \\) [m⁻³] by integrating the
|
|
207
|
+
drop number concentration over all diameter bins.
|
|
208
|
+
|
|
209
|
+
Parameters
|
|
210
|
+
----------
|
|
211
|
+
drop_number_concentration : xarray.DataArray
|
|
212
|
+
Array of drop number concentrations \\( N(D) \\) in m⁻³·mm⁻¹.
|
|
213
|
+
diameter_bin_width : xarray.DataArray
|
|
214
|
+
Width of each diameter bin \\( \\Delta D \\) in millimeters (mm).
|
|
215
|
+
|
|
216
|
+
Returns
|
|
217
|
+
-------
|
|
218
|
+
total_number_concentration : xarray.DataArray or ndarray
|
|
219
|
+
Total number concentration \\( N_t \\) in m⁻³, representing the total number
|
|
220
|
+
of drops per unit volume.
|
|
221
|
+
|
|
222
|
+
Notes
|
|
223
|
+
-----
|
|
224
|
+
The total number concentration \\( N_t \\) is calculated by integrating over the diameter bins:
|
|
225
|
+
|
|
226
|
+
.. math::
|
|
227
|
+
|
|
228
|
+
N_t = \\sum_{\text{bins}} N(D) \\cdot \\Delta D
|
|
229
|
+
|
|
230
|
+
where:
|
|
231
|
+
|
|
232
|
+
- \\( N(D) \\): Drop number concentration in each diameter bin [m⁻³·mm⁻¹].
|
|
233
|
+
- \\( \\Delta D \\): Diameter bin width in millimeters (mm).
|
|
234
|
+
|
|
235
|
+
"""
|
|
236
|
+
total_number_concentration = (drop_number_concentration * diameter_bin_width).sum(
|
|
237
|
+
dim=DIAMETER_DIMENSION,
|
|
238
|
+
skipna=False,
|
|
239
|
+
)
|
|
240
|
+
return total_number_concentration
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def get_moment(drop_number_concentration, diameter, diameter_bin_width, moment):
|
|
244
|
+
r"""
|
|
245
|
+
Calculate the m-th moment of the drop size distribution.
|
|
246
|
+
|
|
247
|
+
Computes the m-th moment of the drop size distribution (DSD), denoted as E[D**m],
|
|
248
|
+
where D is the drop diameter and m is the order of the moment. This is useful
|
|
249
|
+
in meteorology and hydrology for characterizing precipitation. For example,
|
|
250
|
+
weather radar measurements correspond to the sixth moment of the DSD (m = 6).
|
|
251
|
+
|
|
252
|
+
Parameters
|
|
253
|
+
----------
|
|
254
|
+
drop_number_concentration : xarray.DataArray
|
|
255
|
+
The drop number concentration N(D) for each diameter bin,
|
|
256
|
+
typically in units of number per cubic meter per millimeter (m⁻³ mm⁻¹).
|
|
257
|
+
diameter : xarray.DataArray
|
|
258
|
+
The equivalent volume diameters D of the drops in each bin, in meters (m).
|
|
259
|
+
diameter_bin_width : xarray.DataArray
|
|
260
|
+
The width dD of each diameter bin, in millimeters (mm).
|
|
261
|
+
moment : int or float
|
|
262
|
+
The order m of the moment to compute.
|
|
263
|
+
|
|
264
|
+
Returns
|
|
265
|
+
-------
|
|
266
|
+
moment_value : xarray.DataArray
|
|
267
|
+
The computed m-th moment of the drop size distribution, typically in units
|
|
268
|
+
dependent on the input units, such as mmᵐ m⁻³.
|
|
269
|
+
|
|
270
|
+
Notes
|
|
271
|
+
-----
|
|
272
|
+
The m-th moment is calculated using the formula:
|
|
273
|
+
|
|
274
|
+
.. math::
|
|
275
|
+
|
|
276
|
+
M_m = \\sum_{\text{bins}} N(D) \\cdot D^m \\cdot dD
|
|
277
|
+
|
|
278
|
+
where:
|
|
279
|
+
|
|
280
|
+
- \\( M_m \\) is the m-th moment of the DSD.
|
|
281
|
+
- \\( N(D) \\) is the drop number concentration for diameter \\( D \\).
|
|
282
|
+
- \\( D^m \\) is the diameter raised to the power of \\( m \\).
|
|
283
|
+
- \\( dD \\) is the diameter bin width.
|
|
284
|
+
|
|
285
|
+
This computation integrates over the drop size distribution to provide a
|
|
286
|
+
scalar value representing the statistical momen
|
|
287
|
+
"""
|
|
288
|
+
return ((diameter * 1000) ** moment * drop_number_concentration * diameter_bin_width).sum(
|
|
289
|
+
dim=DIAMETER_DIMENSION,
|
|
290
|
+
skipna=False,
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
####------------------------------------------------------------------------------------------------------------------
|
|
295
|
+
#### Rain Rate and Accumulation
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def get_rain_rate_from_drop_number(drop_number, sampling_area, diameter, sample_interval):
|
|
299
|
+
r"""
|
|
300
|
+
Compute the rain rate \\( R \\) [mm/h] based on the drop size distribution and drop velocities.
|
|
301
|
+
|
|
302
|
+
This function calculates the rain rate by integrating over the drop size distribution (DSD),
|
|
303
|
+
considering the volume of water falling per unit time and area. It uses the number of drops
|
|
304
|
+
counted in each diameter class, the effective sampling area of the sensor, the diameters of the
|
|
305
|
+
drops, and the time interval over which the drops are counted.
|
|
306
|
+
|
|
307
|
+
Parameters
|
|
308
|
+
----------
|
|
309
|
+
drop_number : xarray.DataArray
|
|
310
|
+
Array representing the number of drops per diameter class
|
|
311
|
+
and, optionally, velocity class \\( n(D, (v)) \\).
|
|
312
|
+
sample_interval : float or xarray.DataArray
|
|
313
|
+
The time duration over which drops are counted \\( \\Delta t \\) in seconds (s).
|
|
314
|
+
sampling_area : float or xarray.DataArray
|
|
315
|
+
The effective sampling area \\( A \\) of the sensor in square meters (m²).
|
|
316
|
+
diameter : xarray.DataArray
|
|
317
|
+
Array of drop diameters \\( D \\) in meters (m).
|
|
318
|
+
|
|
319
|
+
Returns
|
|
320
|
+
-------
|
|
321
|
+
rain_rate : xarray.DataArray
|
|
322
|
+
The computed rain rate \\( R \\) in millimeters per hour (mm/h), which represents the volume
|
|
323
|
+
of water falling per unit area per unit time.
|
|
324
|
+
|
|
325
|
+
Notes
|
|
326
|
+
-----
|
|
327
|
+
The rain rate \\( R \\) is calculated using the following formula:
|
|
328
|
+
|
|
329
|
+
.. math::
|
|
330
|
+
|
|
331
|
+
R = \frac{\\pi}{6} \times 10^{3} \times 3600 \times
|
|
332
|
+
\\sum_{\text{bins}} n(D) \cdot A(D) \cdot D^3 \cdot \\Delta t
|
|
333
|
+
|
|
334
|
+
= \\pi \times 0.6 \times 10^{6} \times
|
|
335
|
+
\\sum_{\text{bins}} n(D) \cdot A(D) \cdot D^3 \cdot \\Delta t
|
|
336
|
+
|
|
337
|
+
= \\pi \times 6 \times 10^{5} \times
|
|
338
|
+
\\sum_{\text{bins}} n(D) \cdot A(D) \cdot D^3 \cdot \\Delta t
|
|
339
|
+
|
|
340
|
+
Where:
|
|
341
|
+
- \\( n(D) \\) is the number of drops in each diameter class.
|
|
342
|
+
- \\( A(D) \\) is the effective sampling area.
|
|
343
|
+
- \\( D \\) is the drop diameter.
|
|
344
|
+
- \\( \\Delta t \\) is the time interval for drop counts.
|
|
345
|
+
|
|
346
|
+
This formula incorporates a conversion factor to express the rain rate in millimeters per hour.
|
|
347
|
+
|
|
348
|
+
In the literature, when the diameter is expected in millimeters, the formula is given
|
|
349
|
+
as:
|
|
350
|
+
.. math::
|
|
351
|
+
|
|
352
|
+
R = \\pi \times {6} \times 10^{-4} \times
|
|
353
|
+
\\sum_{\text{bins}} n(D) \cdot A(D) \cdot D^3 \cdot \\Delta t
|
|
354
|
+
|
|
355
|
+
"""
|
|
356
|
+
dim = get_bin_dimensions(drop_number)
|
|
357
|
+
rain_rate = (
|
|
358
|
+
np.pi
|
|
359
|
+
/ 6
|
|
360
|
+
/ sample_interval
|
|
361
|
+
* (drop_number * (diameter**3 / sampling_area)).sum(dim=dim, skipna=False)
|
|
362
|
+
* 3600
|
|
363
|
+
* 1000
|
|
364
|
+
)
|
|
365
|
+
return rain_rate
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def get_rain_rate(drop_number_concentration, velocity, diameter, diameter_bin_width):
|
|
369
|
+
r"""
|
|
370
|
+
Compute the rain rate \\( R \\) [mm/h] based on the drop size distribution and raindrop velocities.
|
|
371
|
+
|
|
372
|
+
Calculates the rain rate by integrating over the drop size distribution (DSD),
|
|
373
|
+
considering the volume of water falling per unit time and area.
|
|
374
|
+
|
|
375
|
+
Parameters
|
|
376
|
+
----------
|
|
377
|
+
drop_number_concentration : xarray.DataArray
|
|
378
|
+
Array of drop number concentrations \\( N(D) \\) in m⁻³·mm⁻¹.
|
|
379
|
+
velocity : xarray.DataArray
|
|
380
|
+
Array of drop fall velocities \\( v(D) \\) corresponding to each diameter bin in meters per second (m/s).
|
|
381
|
+
diameter : xarray.DataArray
|
|
382
|
+
Array of drop diameters \\( D \\) in meters (m).
|
|
383
|
+
diameter_bin_width : xarray.DataArray
|
|
384
|
+
Width of each diameter bin \\( \\Delta D \\) in millimeters (mm).
|
|
385
|
+
|
|
386
|
+
Returns
|
|
387
|
+
-------
|
|
388
|
+
rain_rate : xarray.DataArray
|
|
389
|
+
The rain rate \\( R \\) in millimeters per hour (mm/h), representing the volume
|
|
390
|
+
of water falling per unit area per unit time.
|
|
391
|
+
|
|
392
|
+
Notes
|
|
393
|
+
-----
|
|
394
|
+
The rain rate \\( R \\) is calculated using:
|
|
395
|
+
|
|
396
|
+
.. math::
|
|
397
|
+
|
|
398
|
+
R = \frac{\\pi}{6} \times 10^{-3} \times 3600 \times
|
|
399
|
+
\\sum_{\text{bins}} N(D) \\cdot v(D) \\cdot D^3 \\cdot \\Delta D
|
|
400
|
+
|
|
401
|
+
where:
|
|
402
|
+
|
|
403
|
+
- \\( N(D) \\): Drop number concentration [m⁻³·mm⁻¹].
|
|
404
|
+
- \\( v(D) \\): Fall velocity of drops in diameter bin \\( D \\) [m/s].
|
|
405
|
+
- \\( D \\): Drop diameter [mm].
|
|
406
|
+
- \\( \\Delta D \\): Diameter bin width [mm].
|
|
407
|
+
- The factor \\( \frac{\\pi}{6} \\) converts the diameter cubed to volume of a sphere.
|
|
408
|
+
- The factor \\( 10^{-3} \\) converts from mm³ to m³.
|
|
409
|
+
- The factor \\( 3600 \\) converts from seconds to hours.
|
|
410
|
+
|
|
411
|
+
"""
|
|
412
|
+
if VELOCITY_DIMENSION in velocity.dims:
|
|
413
|
+
raise ValueError(f"The 'velocity' DataArray must not have the {VELOCITY_DIMENSION} dimension.")
|
|
414
|
+
|
|
415
|
+
rain_rate = (
|
|
416
|
+
6
|
|
417
|
+
* np.pi
|
|
418
|
+
* 1e5
|
|
419
|
+
* (drop_number_concentration * (velocity * diameter**3 * diameter_bin_width)).sum(
|
|
420
|
+
dim=DIAMETER_DIMENSION,
|
|
421
|
+
skipna=False,
|
|
422
|
+
)
|
|
423
|
+
)
|
|
424
|
+
return rain_rate
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def get_rain_rate_spectrum(drop_number_concentration, velocity, diameter):
|
|
428
|
+
r"""
|
|
429
|
+
Compute the rain rate per diameter class.
|
|
430
|
+
|
|
431
|
+
It represents the rain rate as a function of raindrop diameter.
|
|
432
|
+
The total rain rate can be obtained by multiplying the spectrum with
|
|
433
|
+
the diameter bin width and summing over the diameter bins.
|
|
434
|
+
|
|
435
|
+
Parameters
|
|
436
|
+
----------
|
|
437
|
+
drop_number_concentration : xarray.DataArray
|
|
438
|
+
Array of drop number concentrations \\( N(D) \\) in m⁻³·mm⁻¹.
|
|
439
|
+
velocity : xarray.DataArray
|
|
440
|
+
Array of drop fall velocities \\( v(D) \\) corresponding to each diameter bin in meters per second (m/s).
|
|
441
|
+
diameter : xarray.DataArray
|
|
442
|
+
Array of drop diameters \\( D \\) in meters (m).
|
|
443
|
+
|
|
444
|
+
Returns
|
|
445
|
+
-------
|
|
446
|
+
xarray.DataArray
|
|
447
|
+
The rain rate spectrum in millimeters per hour per mm, representing the volume
|
|
448
|
+
of water falling per unit area per unit time per unit diameter.
|
|
449
|
+
|
|
450
|
+
"""
|
|
451
|
+
rain_rate = 6 * np.pi * 1e5 * (drop_number_concentration * (velocity * diameter**3))
|
|
452
|
+
return rain_rate
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
def get_rain_rate_contribution(drop_number_concentration, velocity, diameter, diameter_bin_width):
|
|
456
|
+
r"""Compute the rain rate contribution per diameter class.
|
|
457
|
+
|
|
458
|
+
Parameters
|
|
459
|
+
----------
|
|
460
|
+
drop_number_concentration : xarray.DataArray
|
|
461
|
+
Array of drop number concentrations \\( N(D) \\) in m⁻³·mm⁻¹.
|
|
462
|
+
velocity : xarray.DataArray
|
|
463
|
+
Array of drop fall velocities \\( v(D) \\) corresponding to each diameter bin in meters per second (m/s).
|
|
464
|
+
diameter : xarray.DataArray
|
|
465
|
+
Array of drop diameters \\( D \\) in meters (m).
|
|
466
|
+
diameter_bin_width : xarray.DataArray
|
|
467
|
+
Width of each diameter bin \\( \\Delta D \\) in millimeters (mm).
|
|
468
|
+
|
|
469
|
+
Returns
|
|
470
|
+
-------
|
|
471
|
+
xarray.DataArray
|
|
472
|
+
The rain rate contribution percentage per diameter class.
|
|
473
|
+
|
|
474
|
+
"""
|
|
475
|
+
rain_rate_spectrum = (6 * np.pi * 1e5 * (velocity * diameter**3 * diameter_bin_width)) * drop_number_concentration
|
|
476
|
+
|
|
477
|
+
rain_rate_total = rain_rate_spectrum.sum(dim=DIAMETER_DIMENSION, skipna=False)
|
|
478
|
+
rain_rate_contribution = rain_rate_spectrum / rain_rate_total * 100
|
|
479
|
+
return rain_rate_contribution
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def get_rain_accumulation(rain_rate, sample_interval):
|
|
483
|
+
"""
|
|
484
|
+
Calculate the total rain accumulation over a specified time period.
|
|
485
|
+
|
|
486
|
+
Parameters
|
|
487
|
+
----------
|
|
488
|
+
rain_rate : float or array-like
|
|
489
|
+
The rain rate in millimeters per hour (mm/h).
|
|
490
|
+
sample_interval : int
|
|
491
|
+
The time over which to accumulate rain, specified in seconds.
|
|
492
|
+
|
|
493
|
+
Returns
|
|
494
|
+
-------
|
|
495
|
+
float or numpy.ndarray
|
|
496
|
+
The total rain accumulation in millimeters (mm) over the specified time period.
|
|
497
|
+
|
|
498
|
+
"""
|
|
499
|
+
rain_accumulation = rain_rate / 3600 * sample_interval
|
|
500
|
+
return rain_accumulation
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
####------------------------------------------------------------------------------------------------------------------
|
|
504
|
+
#### Reflectivity
|
|
505
|
+
def get_equivalent_reflectivity_factor(drop_number_concentration, diameter, diameter_bin_width):
|
|
506
|
+
r"""
|
|
507
|
+
Compute the equivalent reflectivity factor in decibels relative to 1 mm⁶·m⁻³ (dBZ).
|
|
508
|
+
|
|
509
|
+
The equivalent reflectivity (in mm⁶·m⁻³) is obtained from the sixth moment of the drop size distribution (DSD).
|
|
510
|
+
The reflectivity factor is expressed in decibels relative to 1 mm⁶·m⁻³ using the formula:
|
|
511
|
+
|
|
512
|
+
.. math::
|
|
513
|
+
|
|
514
|
+
Z = 10 \cdot \log_{10}(z)
|
|
515
|
+
|
|
516
|
+
where \\( z \\) is the reflectivity in linear units of the DSD.
|
|
517
|
+
|
|
518
|
+
To convert back the reflectivity factor to linear units (mm⁶·m⁻³), use the formula:
|
|
519
|
+
|
|
520
|
+
.. math::
|
|
521
|
+
|
|
522
|
+
z = 10^{(Z/10)}
|
|
523
|
+
|
|
524
|
+
Parameters
|
|
525
|
+
----------
|
|
526
|
+
drop_number_concentration : xarray.DataArray
|
|
527
|
+
Array representing the concentration of droplets per diameter class in number per unit volume.
|
|
528
|
+
diameter : xarray.DataArray
|
|
529
|
+
Array of droplet diameters in meters (m).
|
|
530
|
+
diameter_bin_width : xarray.DataArray
|
|
531
|
+
Array representing the width of each diameter bin in millimeters (mm).
|
|
532
|
+
|
|
533
|
+
Returns
|
|
534
|
+
-------
|
|
535
|
+
xarray.DataArray
|
|
536
|
+
The equivalent reflectivity factor in decibels (dBZ).
|
|
537
|
+
|
|
538
|
+
Notes
|
|
539
|
+
-----
|
|
540
|
+
The function computes the sixth moment of the DSD using the formula:
|
|
541
|
+
|
|
542
|
+
.. math::
|
|
543
|
+
|
|
544
|
+
z = \\sum n(D) \cdot D^6 \cdot \\Delta D
|
|
545
|
+
|
|
546
|
+
where \\( n(D) \\) is the drop number concentration, \\( D \\) is the drop diameter, and
|
|
547
|
+
\\( \\Delta D \\) is the diameter bin width.
|
|
548
|
+
|
|
549
|
+
"""
|
|
550
|
+
# Compute reflectivity in mm⁶·m⁻³
|
|
551
|
+
z = (drop_number_concentration * ((diameter * 1000) ** 6 * diameter_bin_width)).sum(
|
|
552
|
+
dim=DIAMETER_DIMENSION,
|
|
553
|
+
skipna=False,
|
|
554
|
+
)
|
|
555
|
+
invalid_mask = z > 0
|
|
556
|
+
z = z.where(invalid_mask)
|
|
557
|
+
# Compute equivalent reflectivity factor in dBZ
|
|
558
|
+
# - np.log10(np.nan) returns -Inf !
|
|
559
|
+
# --> We mask again after the log
|
|
560
|
+
Z = 10 * np.log10(z)
|
|
561
|
+
Z = Z.where(invalid_mask)
|
|
562
|
+
# Clip reflectivity at -60 dBZ
|
|
563
|
+
Z = Z.clip(-60, None)
|
|
564
|
+
return Z
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def get_equivalent_reflectivity_spectrum(drop_number_concentration, diameter):
|
|
568
|
+
r"""
|
|
569
|
+
Compute the equivalent reflectivity per diameter class.
|
|
570
|
+
|
|
571
|
+
The equivalent reflectivity per unit diameter Z(D) [in mm⁶·m⁻³ / mm] is expressed in decibels
|
|
572
|
+
using the formula:
|
|
573
|
+
|
|
574
|
+
.. math::
|
|
575
|
+
|
|
576
|
+
Z(D) = 10 \cdot \log_{10}(z(D))
|
|
577
|
+
|
|
578
|
+
where \\( z(D) \\) is the equivalent reflectivity spectrum in linear units of the DSD.
|
|
579
|
+
|
|
580
|
+
To convert back the reflectivity factor to linear units (mm⁶·m⁻³ / mm), use the formula:
|
|
581
|
+
|
|
582
|
+
.. math::
|
|
583
|
+
|
|
584
|
+
z(D) = 10^{(Z(D)/10)}
|
|
585
|
+
|
|
586
|
+
To obtain the total equivalent reflectivity factor (z) one has to multiply z(D) with the diameter
|
|
587
|
+
bins intervals and summing over the diameter bins.
|
|
588
|
+
|
|
589
|
+
Parameters
|
|
590
|
+
----------
|
|
591
|
+
drop_number_concentration : xarray.DataArray
|
|
592
|
+
Array representing the concentration of droplets per diameter class in number per unit volume.
|
|
593
|
+
diameter : xarray.DataArray
|
|
594
|
+
Array of droplet diameters in meters (m).
|
|
595
|
+
|
|
596
|
+
Returns
|
|
597
|
+
-------
|
|
598
|
+
xarray.DataArray
|
|
599
|
+
The equivalent reflectivity spectrum in decibels (dBZ).
|
|
600
|
+
|
|
601
|
+
"""
|
|
602
|
+
# Compute reflectivity in mm⁶·m⁻³
|
|
603
|
+
z = drop_number_concentration * ((diameter * 1000) ** 6)
|
|
604
|
+
invalid_mask = z > 0
|
|
605
|
+
z = z.where(invalid_mask)
|
|
606
|
+
# Compute equivalent reflectivity factor in dBZ
|
|
607
|
+
# - np.log10(np.nan) returns -Inf !
|
|
608
|
+
# --> We mask again after the log
|
|
609
|
+
Z = 10 * np.log10(z)
|
|
610
|
+
Z = Z.where(invalid_mask)
|
|
611
|
+
# Clip reflectivity at -60 dBZ
|
|
612
|
+
Z = Z.clip(-60, None)
|
|
613
|
+
return Z
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
####------------------------------------------------------------------------------------------------------------------
|
|
617
|
+
#### Liquid Water Content / Mass Parameters
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
def get_liquid_water_spectrum(drop_number_concentration, diameter, water_density=1000):
|
|
621
|
+
"""
|
|
622
|
+
Calculate the mass spectrum W(D) per diameter class.
|
|
623
|
+
|
|
624
|
+
It represents the mass of liquid water as a function of raindrop diameter.
|
|
625
|
+
The integrated liquid water content can be obtained by multiplying
|
|
626
|
+
the spectrum with the diameter bins intervals and summing over the diameter bins.
|
|
627
|
+
|
|
628
|
+
Parameters
|
|
629
|
+
----------
|
|
630
|
+
drop_number_concentration : array-like
|
|
631
|
+
The concentration of droplets (number of droplets per unit volume) in each diameter bin.
|
|
632
|
+
diameter : array-like
|
|
633
|
+
The diameters of the droplets for each bin, in meters (m).
|
|
634
|
+
|
|
635
|
+
Returns
|
|
636
|
+
-------
|
|
637
|
+
array-like
|
|
638
|
+
The calculated rain drop mass spectrum in grams per cubic meter per unit diameter (g/m3/mm).
|
|
639
|
+
|
|
640
|
+
"""
|
|
641
|
+
# Convert water density from kg/m3 to g/m3
|
|
642
|
+
water_density = water_density * 1000
|
|
643
|
+
|
|
644
|
+
# Calculate the mass spectrum (LWC per diameter bin)
|
|
645
|
+
return (np.pi / 6.0 * water_density * diameter**3) * drop_number_concentration # [g/m3 mm-1]
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
# def get_mass_flux(drop_number_concentration, diameter, velocity, diameter_bin_width, water_density=1000):
|
|
649
|
+
# """
|
|
650
|
+
# Calculate the mass flux based on drop number concentration and drop diameter and velocity.
|
|
651
|
+
|
|
652
|
+
# Parameters
|
|
653
|
+
# ----------
|
|
654
|
+
# drop_number_concentration : array-like
|
|
655
|
+
# The concentration of droplets (number of droplets per unit volume) in each diameter bin.
|
|
656
|
+
# diameter : array-like
|
|
657
|
+
# The diameters of the droplets for each bin, in meters (m).
|
|
658
|
+
# water_density : float, optional
|
|
659
|
+
# The density of water in kg/m^3. The default is 1000 kg/m3.
|
|
660
|
+
|
|
661
|
+
# Returns
|
|
662
|
+
# -------
|
|
663
|
+
# array-like
|
|
664
|
+
# The calculated mass in grams per cubic meter per second (g/m3/s).
|
|
665
|
+
|
|
666
|
+
# """
|
|
667
|
+
# if VELOCITY_DIMENSION in velocity.dims:
|
|
668
|
+
# raise ValueError("The 'velocity' DataArray must not have the {VELOCITY_DIMENSION} dimension.")
|
|
669
|
+
# # Convert water density from kg/m3 to g/m3
|
|
670
|
+
# water_density = water_density * 1000
|
|
671
|
+
|
|
672
|
+
# # Calculate the volume constant for the water droplet formula
|
|
673
|
+
# vol_constant = np.pi / 6.0 * water_density
|
|
674
|
+
|
|
675
|
+
# # Calculate the mass flux
|
|
676
|
+
# # TODO: check equal to density * R
|
|
677
|
+
# mass_flux = (vol_constant *
|
|
678
|
+
# (drop_number_concentration *
|
|
679
|
+
# (diameter**3 * velocity)).sum(dim=DIAMETER_DIMENSION, skipna=False)
|
|
680
|
+
# )
|
|
681
|
+
# return mass_flux
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
def get_liquid_water_content(drop_number_concentration, diameter, diameter_bin_width, water_density=1000):
|
|
685
|
+
"""
|
|
686
|
+
Calculate the liquid water content based on drop number concentration and drop diameter.
|
|
687
|
+
|
|
688
|
+
Parameters
|
|
689
|
+
----------
|
|
690
|
+
drop_number_concentration : array-like
|
|
691
|
+
The concentration of droplets (number of droplets per unit volume) in each diameter bin.
|
|
692
|
+
diameter : array-like
|
|
693
|
+
The diameters of the droplets for each bin, in meters (m).
|
|
694
|
+
diameter_bin_width : array-like
|
|
695
|
+
The width of each diameter bin, in millimeters (mm).
|
|
696
|
+
water_density : float, optional
|
|
697
|
+
The density of water in kg/m^3. The default is 1000 kg/m3.
|
|
698
|
+
|
|
699
|
+
Returns
|
|
700
|
+
-------
|
|
701
|
+
array-like
|
|
702
|
+
The calculated liquid water content in grams per cubic meter (g/m3).
|
|
703
|
+
|
|
704
|
+
"""
|
|
705
|
+
# Convert water density from kg/m3 to g/m3
|
|
706
|
+
water_density = water_density * 1000
|
|
707
|
+
|
|
708
|
+
# Calculate the liquid water content
|
|
709
|
+
lwc = (
|
|
710
|
+
np.pi
|
|
711
|
+
/ 6.0
|
|
712
|
+
* water_density
|
|
713
|
+
* (drop_number_concentration * (diameter**3 * diameter_bin_width)).sum(
|
|
714
|
+
dim=DIAMETER_DIMENSION,
|
|
715
|
+
skipna=False,
|
|
716
|
+
)
|
|
717
|
+
)
|
|
718
|
+
return lwc
|
|
719
|
+
|
|
720
|
+
|
|
721
|
+
def get_liquid_water_content_from_moments(moment_3, water_density=1000):
|
|
722
|
+
r"""
|
|
723
|
+
Calculate the liquid water content (LWC) from the third moment of the DSD.
|
|
724
|
+
|
|
725
|
+
LWC represents the mass of liquid water per unit volume of air.
|
|
726
|
+
|
|
727
|
+
Parameters
|
|
728
|
+
----------
|
|
729
|
+
moment_3 : float or array-like
|
|
730
|
+
The third moment of the drop size distribution, \\( M_3 \\), in units of
|
|
731
|
+
[m⁻³·mm³] (number per cubic meter times diameter cubed).
|
|
732
|
+
water_density : float, optional
|
|
733
|
+
The density of water in kilograms per cubic meter (kg/m³).
|
|
734
|
+
Default is 1000 kg/m³ (approximate density of water at 20°C).
|
|
735
|
+
|
|
736
|
+
Returns
|
|
737
|
+
-------
|
|
738
|
+
lwc : float or array-like
|
|
739
|
+
The liquid water content in grams per cubic meter (g/m³).
|
|
740
|
+
|
|
741
|
+
Notes
|
|
742
|
+
-----
|
|
743
|
+
The liquid water content is calculated using the formula:
|
|
744
|
+
|
|
745
|
+
.. math::
|
|
746
|
+
|
|
747
|
+
\text{LWC} = \frac{\\pi \rho_w}{6} \\cdot M_3
|
|
748
|
+
|
|
749
|
+
where:
|
|
750
|
+
|
|
751
|
+
- \\( \text{LWC} \\) is the liquid water content [g/m³].
|
|
752
|
+
- \\( \rho_w \\) is the density of water [g/mm³].
|
|
753
|
+
- \\( M_3 \\) is the third moment of the DSD [m⁻³·mm³].
|
|
754
|
+
|
|
755
|
+
Examples
|
|
756
|
+
--------
|
|
757
|
+
Compute the liquid water content from the third moment:
|
|
758
|
+
|
|
759
|
+
>>> moment_3 = 1e6 # Example value in [m⁻³·mm³]
|
|
760
|
+
>>> lwc = get_liquid_water_content_from_moments(moment_3)
|
|
761
|
+
>>> print(f"LWC: {lwc:.4f} g/m³")
|
|
762
|
+
LWC: 0.0005 g/m³
|
|
763
|
+
"""
|
|
764
|
+
# Convert water density from kg/m³ to g/mm³
|
|
765
|
+
water_density = water_density * 1e-6 # [kg/m³] * 1e-6 = [g/mm³]
|
|
766
|
+
# Calculate LWC [g/m3]
|
|
767
|
+
lwc = (np.pi * water_density / 6) * moment_3 # [g/mm³] * [m⁻³·mm³] = [g/m³]
|
|
768
|
+
return lwc
|
|
769
|
+
|
|
770
|
+
|
|
771
|
+
####--------------------------------------------------------------------------------------------------------
|
|
772
|
+
#### Diameter Statistics
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
def get_min_max_diameter(drop_counts):
|
|
776
|
+
"""
|
|
777
|
+
Get the minimum and maximum diameters where drop_counts is non-zero.
|
|
778
|
+
|
|
779
|
+
Parameters
|
|
780
|
+
----------
|
|
781
|
+
drop_counts : xarray.DataArray
|
|
782
|
+
Drop counts with dimensions ("time", "diameter_bin_center") and
|
|
783
|
+
coordinate "diameter_bin_center".
|
|
784
|
+
It assumes the diameter coordinate to be monotonically increasing !
|
|
785
|
+
|
|
786
|
+
Returns
|
|
787
|
+
-------
|
|
788
|
+
min_drop_diameter : xarray.DataArray
|
|
789
|
+
Minimum diameter where drop_counts is non-zero, for each time step.
|
|
790
|
+
max_drop_diameter : xarray.DataArray
|
|
791
|
+
Maximum diameter where drop_counts is non-zero, for each time step.
|
|
792
|
+
"""
|
|
793
|
+
# Create a boolean mask where drop_counts is non-zero
|
|
794
|
+
non_zero_mask = drop_counts > 0
|
|
795
|
+
|
|
796
|
+
# Find the first non-zero index along 'diameter_bin_center' for each time
|
|
797
|
+
# - Return 0 if all False, zero or NaN
|
|
798
|
+
first_non_zero_idx = non_zero_mask.argmax(dim=DIAMETER_DIMENSION)
|
|
799
|
+
|
|
800
|
+
# Calculate the last non-zero index in the original array
|
|
801
|
+
last_non_zero_idx = xr_get_last_valid_idx(da_condition=non_zero_mask, dim=DIAMETER_DIMENSION)
|
|
802
|
+
|
|
803
|
+
# Get the 'diameter_bin_center' coordinate
|
|
804
|
+
diameters = drop_counts["diameter_bin_center"]
|
|
805
|
+
|
|
806
|
+
# Retrieve the diameters corresponding to the first and last non-zero indices
|
|
807
|
+
min_drop_diameter = diameters.isel({DIAMETER_DIMENSION: first_non_zero_idx.astype(int)})
|
|
808
|
+
max_drop_diameter = diameters.isel({DIAMETER_DIMENSION: last_non_zero_idx.astype(int)})
|
|
809
|
+
|
|
810
|
+
# Identify time steps where all drop_counts are zero
|
|
811
|
+
is_all_zero_or_nan = ~non_zero_mask.any(dim=DIAMETER_DIMENSION)
|
|
812
|
+
|
|
813
|
+
# Mask with NaN where no drop or all values are NaN
|
|
814
|
+
min_drop_diameter = min_drop_diameter.where(~is_all_zero_or_nan)
|
|
815
|
+
max_drop_diameter = max_drop_diameter.where(~is_all_zero_or_nan)
|
|
816
|
+
|
|
817
|
+
# Remove diameter coordinates
|
|
818
|
+
min_drop_diameter = remove_diameter_coordinates(min_drop_diameter)
|
|
819
|
+
max_drop_diameter = remove_diameter_coordinates(max_drop_diameter)
|
|
820
|
+
|
|
821
|
+
return min_drop_diameter, max_drop_diameter
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
def get_mode_diameter(drop_number_concentration, diameter):
|
|
825
|
+
"""Get raindrop diameter with highest occurrence."""
|
|
826
|
+
# If all NaN, set to 0 otherwise argmax fail when all NaN data
|
|
827
|
+
idx_all_nan_mask = np.isnan(drop_number_concentration).all(dim=DIAMETER_DIMENSION)
|
|
828
|
+
drop_number_concentration = drop_number_concentration.where(~idx_all_nan_mask, 0)
|
|
829
|
+
# Find index where all 0
|
|
830
|
+
# --> argmax will return 0
|
|
831
|
+
idx_all_zero = (drop_number_concentration == 0).all(dim=DIAMETER_DIMENSION)
|
|
832
|
+
# Find the diameter index corresponding the "mode"
|
|
833
|
+
idx_observed_mode = drop_number_concentration.argmax(dim=DIAMETER_DIMENSION)
|
|
834
|
+
# Find the diameter corresponding to the "mode"
|
|
835
|
+
diameter_mode = diameter.isel({DIAMETER_DIMENSION: idx_observed_mode})
|
|
836
|
+
# Remove diameter coordinates
|
|
837
|
+
diameter_mode = remove_diameter_coordinates(diameter_mode)
|
|
838
|
+
# Set to np.nan where data where all NaN or all 0
|
|
839
|
+
idx_mask = np.logical_or(idx_all_nan_mask, idx_all_zero)
|
|
840
|
+
diameter_mode = diameter_mode.where(~idx_mask)
|
|
841
|
+
return diameter_mode
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
####-------------------------------------------------------------------------------------------------------------------.
|
|
845
|
+
#### Mass Distribution Diameters
|
|
846
|
+
|
|
847
|
+
|
|
848
|
+
def get_mean_volume_drop_diameter(moment_3, moment_4):
|
|
849
|
+
r"""
|
|
850
|
+
Calculate the volume-weighted mean volume diameter \\( D_m \\) from DSD moments.
|
|
851
|
+
|
|
852
|
+
The mean volume diameter of a drop size distribution (DSD) is computed using
|
|
853
|
+
the third and fourth moments.
|
|
854
|
+
|
|
855
|
+
The volume-weighted mean volume diameter is also referred as the mass mean diameter.
|
|
856
|
+
It represents the first moment of the mass spectrum.
|
|
857
|
+
|
|
858
|
+
If no drops are recorded, the output values is NaN.
|
|
859
|
+
|
|
860
|
+
Parameters
|
|
861
|
+
----------
|
|
862
|
+
moment_3 : float or array-like
|
|
863
|
+
The third moment of the drop size distribution, \\( M_3 \\), in units of
|
|
864
|
+
[m⁻³·mm³].
|
|
865
|
+
moment_4 : float or array-like
|
|
866
|
+
The fourth moment of the drop size distribution, \\( M_4 \\), in units of
|
|
867
|
+
[m⁻³·mm⁴].
|
|
868
|
+
|
|
869
|
+
Returns
|
|
870
|
+
-------
|
|
871
|
+
D_m : float or array-like
|
|
872
|
+
The mean volume diameter in millimeters (mm).
|
|
873
|
+
|
|
874
|
+
Notes
|
|
875
|
+
-----
|
|
876
|
+
The mean volume diameter is calculated using the formula:
|
|
877
|
+
|
|
878
|
+
.. math::
|
|
879
|
+
|
|
880
|
+
D_m = \frac{M_4}{M_3}
|
|
881
|
+
|
|
882
|
+
where:
|
|
883
|
+
|
|
884
|
+
- \\( D_m \\) is the mean volume diameter [mm].
|
|
885
|
+
- \\( M_3 \\) is the third moment of the DSD [m⁻³·mm³].
|
|
886
|
+
- \\( M_4 \\) is the fourth moment of the DSD [m⁻³·mm⁴].
|
|
887
|
+
|
|
888
|
+
Examples
|
|
889
|
+
--------
|
|
890
|
+
Compute the mean volume diameter from the third and fourth moments:
|
|
891
|
+
|
|
892
|
+
>>> moment_3 = 1e6 # Example value in [m⁻³·mm³]
|
|
893
|
+
>>> moment_4 = 5e6 # Example value in [m⁻³·mm⁴]
|
|
894
|
+
>>> D_m = get_mean_volume_drop_diameter(moment_3, moment_4)
|
|
895
|
+
>>> print(f"Mean Volume Diameter D_m: {D_m:.4f} mm")
|
|
896
|
+
Mean Volume Diameter D_m: 5.0000 mm
|
|
897
|
+
|
|
898
|
+
"""
|
|
899
|
+
# Note:
|
|
900
|
+
# - 0/0 return NaN
|
|
901
|
+
# - <number>/0 return Inf
|
|
902
|
+
D_m = moment_4 / moment_3 # Units: [mm⁴] / [mm³] = [mm]
|
|
903
|
+
return D_m
|
|
904
|
+
|
|
905
|
+
|
|
906
|
+
def get_std_volume_drop_diameter(moment_3, moment_4, moment_5):
|
|
907
|
+
r"""
|
|
908
|
+
Calculate the standard deviation of the mass-weighted drop diameter (σₘ).
|
|
909
|
+
|
|
910
|
+
This parameter is often also referred as the mass spectrum standard deviation.
|
|
911
|
+
It quantifies the spread or variability of DSD.
|
|
912
|
+
|
|
913
|
+
If drops are recorded in just one bin, the standard deviation of the mass-weighted drop diameter
|
|
914
|
+
is set to 0.
|
|
915
|
+
If no drops are recorded, the output values is NaN.
|
|
916
|
+
|
|
917
|
+
Parameters
|
|
918
|
+
----------
|
|
919
|
+
drop_number_concentration : xarray.DataArray
|
|
920
|
+
The drop number concentration \\( N(D) \\) for each diameter bin, typically in units of
|
|
921
|
+
number per cubic meter per millimeter (m⁻³·mm⁻¹).
|
|
922
|
+
diameter : xarray.DataArray
|
|
923
|
+
The equivalent volume diameters \\( D \\) of the drops in each bin, in meters (m).
|
|
924
|
+
diameter_bin_width : xarray.DataArray
|
|
925
|
+
The width \\( \\Delta D \\) of each diameter bin, in millimeters (mm).
|
|
926
|
+
mean_volume_diameter : xarray.DataArray
|
|
927
|
+
The mean volume diameter \\( D_m \\), in millimeters (mm). This is typically computed using the
|
|
928
|
+
third and fourth moments or directly from the DSD.
|
|
929
|
+
|
|
930
|
+
Returns
|
|
931
|
+
-------
|
|
932
|
+
sigma_m : xarray.DataArray or float
|
|
933
|
+
The standard deviation of the mass-weighted drop diameter, \\( \\sigma_m \\),
|
|
934
|
+
in millimeters (mm).
|
|
935
|
+
|
|
936
|
+
Notes
|
|
937
|
+
-----
|
|
938
|
+
The standard deviation of the mass-weighted drop diameter is calculated using the formula:
|
|
939
|
+
|
|
940
|
+
.. math::
|
|
941
|
+
|
|
942
|
+
\\sigma_m = \\sqrt{\frac{\\sum [N(D) \\cdot (D - D_m)^2 \\cdot D^3
|
|
943
|
+
\\cdot \\Delta D]}{\\sum [N(D) \\cdot D^3 \\cdot \\Delta D]}}
|
|
944
|
+
|
|
945
|
+
where:
|
|
946
|
+
|
|
947
|
+
- \\( N(D) \\) is the drop number concentration for diameter \\( D \\) [m⁻³·mm⁻¹].
|
|
948
|
+
- \\( D \\) is the drop diameter [mm].
|
|
949
|
+
- \\( D_m \\) is the mean volume diameter [mm].
|
|
950
|
+
- \\( \\Delta D \\) is the diameter bin width [mm].
|
|
951
|
+
- The numerator computes the weighted variance of diameters.
|
|
952
|
+
- The weighting factor \\( D^3 \\) accounts for mass (since mass ∝ \\( D^3 \\)).
|
|
953
|
+
|
|
954
|
+
**Physical Interpretation:**
|
|
955
|
+
|
|
956
|
+
- A smaller \\( \\sigma_m \\) indicates that the mass is concentrated around the
|
|
957
|
+
mean mass-weighted diameter, implying less variability in drop sizes.
|
|
958
|
+
- A larger \\( \\sigma_m \\) suggests a wider spread of drop sizes contributing
|
|
959
|
+
to the mass, indicating greater variability.
|
|
960
|
+
|
|
961
|
+
References
|
|
962
|
+
----------
|
|
963
|
+
- Smith, P. L., Johnson, R. W., & Kliche, D. V. (2019). On Use of the Standard
|
|
964
|
+
Deviation of the Mass Distribution as a Parameter in Raindrop Size Distribution
|
|
965
|
+
Functions. *Journal of Applied Meteorology and Climatology*, 58(4), 787-796.
|
|
966
|
+
https://doi.org/10.1175/JAMC-D-18-0086.1
|
|
967
|
+
- Williams, C. R., and Coauthors, 2014: Describing the Shape of Raindrop Size Distributions Using Uncorrelated
|
|
968
|
+
Raindrop Mass Spectrum Parameters. J. Appl. Meteor. Climatol., 53, 1282-1296, https://doi.org/10.1175/JAMC-D-13-076.1.
|
|
969
|
+
"""
|
|
970
|
+
# # Full formula
|
|
971
|
+
# const = drop_number_concentration * diameter_bin_width * diameter**3
|
|
972
|
+
# numerator = ((diameter * 1000 - mean_volume_diameter) ** 2 * const).sum(dim=DIAMETER_DIMENSION, skipna=False)
|
|
973
|
+
# variance_m = numerator / const.sum(dim=DIAMETER_DIMENSION, skipna=False))
|
|
974
|
+
|
|
975
|
+
# Compute variance using moment formula
|
|
976
|
+
variance_m = (moment_3 * moment_5 - moment_4**2) / moment_3**2
|
|
977
|
+
|
|
978
|
+
# Set to 0 when very low values (resulting from numerical errors)
|
|
979
|
+
# --> For example should return 0 when drops only recorded in 1 bin !
|
|
980
|
+
variance_m = xr.where(np.logical_and(variance_m < 1e-5, ~np.isnan(variance_m)), 0, variance_m)
|
|
981
|
+
|
|
982
|
+
# Compute standard deviation
|
|
983
|
+
sigma_m = np.sqrt(variance_m)
|
|
984
|
+
return sigma_m
|
|
985
|
+
|
|
986
|
+
|
|
987
|
+
def get_median_volume_drop_diameter(drop_number_concentration, diameter, diameter_bin_width, water_density=1000):
|
|
988
|
+
r"""
|
|
989
|
+
Compute the median volume drop diameter (D50).
|
|
990
|
+
|
|
991
|
+
The median volume drop diameter (D50) is defined as the diameter at which half of the total liquid water content
|
|
992
|
+
is contributed by drops smaller than D50, and half by drops larger than D50.
|
|
993
|
+
|
|
994
|
+
Drops smaller (respectively larger) than D50 contribute to half of the
|
|
995
|
+
total rainwater content in the sampled volume.
|
|
996
|
+
D50 is sensitive to the concentration of large drops.
|
|
997
|
+
|
|
998
|
+
Often referred also as D50 (50 for 50 percentile of the distribution).
|
|
999
|
+
|
|
1000
|
+
Parameters
|
|
1001
|
+
----------
|
|
1002
|
+
drop_number_concentration : xarray.DataArray
|
|
1003
|
+
The drop number concentration \( N(D) \) for each diameter bin, typically in units of
|
|
1004
|
+
number per cubic meter per millimeter (m⁻³·mm⁻¹).
|
|
1005
|
+
diameter : xarray.DataArray
|
|
1006
|
+
The equivalent volume diameters \( D \) of the drops in each bin, in meters (m).
|
|
1007
|
+
diameter_bin_width : xarray.DataArray
|
|
1008
|
+
The width \( \Delta D \) of each diameter bin, in millimeters (mm).
|
|
1009
|
+
water_density : float, optional
|
|
1010
|
+
The density of water in kg/m^3. The default is 1000 kg/m3.
|
|
1011
|
+
|
|
1012
|
+
Returns
|
|
1013
|
+
-------
|
|
1014
|
+
xarray.DataArray
|
|
1015
|
+
Median volume drop diameter (D50) [mm].
|
|
1016
|
+
The drop diameter that divides the volume of water contained in the sample into two equal parts.
|
|
1017
|
+
|
|
1018
|
+
"""
|
|
1019
|
+
d50 = get_quantile_volume_drop_diameter(
|
|
1020
|
+
drop_number_concentration=drop_number_concentration,
|
|
1021
|
+
diameter=diameter,
|
|
1022
|
+
diameter_bin_width=diameter_bin_width,
|
|
1023
|
+
fraction=0.5,
|
|
1024
|
+
water_density=water_density,
|
|
1025
|
+
)
|
|
1026
|
+
return d50
|
|
1027
|
+
|
|
1028
|
+
|
|
1029
|
+
def _get_quantile_volume_drop_diameter(
|
|
1030
|
+
drop_number_concentration,
|
|
1031
|
+
diameter,
|
|
1032
|
+
diameter_bin_width,
|
|
1033
|
+
fraction,
|
|
1034
|
+
water_density=1000,
|
|
1035
|
+
):
|
|
1036
|
+
# Check fraction value(s)
|
|
1037
|
+
fraction = np.atleast_1d(fraction)
|
|
1038
|
+
for value in fraction:
|
|
1039
|
+
if not (0 < value < 1):
|
|
1040
|
+
raise ValueError("Fraction values must be between 0 and 1 (exclusive)")
|
|
1041
|
+
|
|
1042
|
+
# Create fraction DataArray
|
|
1043
|
+
fraction = xr.DataArray(fraction, coords={"quantile": fraction}, dims="quantile")
|
|
1044
|
+
|
|
1045
|
+
# Convert water density from kg/m3 to g/m3
|
|
1046
|
+
water_density = water_density * 1000
|
|
1047
|
+
|
|
1048
|
+
# Compute LWC per diameter bin [g/m3]
|
|
1049
|
+
mass_spectrum = get_liquid_water_spectrum(
|
|
1050
|
+
drop_number_concentration=drop_number_concentration,
|
|
1051
|
+
diameter=diameter,
|
|
1052
|
+
water_density=water_density,
|
|
1053
|
+
)
|
|
1054
|
+
lwc_per_diameter = diameter_bin_width * mass_spectrum
|
|
1055
|
+
|
|
1056
|
+
# Compute the cumulative sum of LWC along the diameter bins
|
|
1057
|
+
cumulative_lwc = lwc_per_diameter.cumsum(dim=DIAMETER_DIMENSION, skipna=False)
|
|
1058
|
+
|
|
1059
|
+
# Check if single bin
|
|
1060
|
+
is_single_bin = (lwc_per_diameter != 0).sum(dim=DIAMETER_DIMENSION) == 1
|
|
1061
|
+
|
|
1062
|
+
# Retrieve total lwc and target lwc
|
|
1063
|
+
total_lwc = cumulative_lwc.isel({DIAMETER_DIMENSION: -1})
|
|
1064
|
+
target_lwc = total_lwc * fraction
|
|
1065
|
+
|
|
1066
|
+
# Retrieve bin indices between which the quantile of the volume is reached
|
|
1067
|
+
# --> If all NaN or False, argmax and xr_get_last_valid_idx(fill_value=0) return 0 !
|
|
1068
|
+
idx_upper = (cumulative_lwc >= target_lwc).argmax(dim=DIAMETER_DIMENSION).astype(int)
|
|
1069
|
+
idx_lower = xr_get_last_valid_idx(
|
|
1070
|
+
da_condition=(cumulative_lwc <= target_lwc),
|
|
1071
|
+
dim=DIAMETER_DIMENSION,
|
|
1072
|
+
fill_value=0,
|
|
1073
|
+
).astype(int)
|
|
1074
|
+
|
|
1075
|
+
# Retrieve cumulative LWC values at such bins and target LWC
|
|
1076
|
+
y1 = cumulative_lwc.isel({DIAMETER_DIMENSION: idx_lower})
|
|
1077
|
+
y2 = cumulative_lwc.isel({DIAMETER_DIMENSION: idx_upper})
|
|
1078
|
+
yt = target_lwc
|
|
1079
|
+
|
|
1080
|
+
# ------------------------------------------------------.
|
|
1081
|
+
## Case with multiple bins
|
|
1082
|
+
# Define interpolation slope, avoiding division by zero if y1 equals y2.
|
|
1083
|
+
# - When target LWC exactly equal to cumulative_lwc value of a bin --> Diameter is the bin center diameter !
|
|
1084
|
+
slope = xr.where(y1 == y2, 0, (yt - y1) / (y2 - y1))
|
|
1085
|
+
|
|
1086
|
+
# Define diameter increment from lower bin center
|
|
1087
|
+
d1 = diameter.isel(diameter_bin_center=idx_lower) # m
|
|
1088
|
+
d2 = diameter.isel(diameter_bin_center=idx_upper) # m
|
|
1089
|
+
d_increment = (d2 - d1) * slope
|
|
1090
|
+
|
|
1091
|
+
# Define quantile diameter
|
|
1092
|
+
quantile_diameter_multi_bin = d1 + d_increment
|
|
1093
|
+
|
|
1094
|
+
## ------------------------------------------------------.
|
|
1095
|
+
## Case with single bin
|
|
1096
|
+
# When no accumulation has yet occurred (y1==0), use the upper bin for both indices.
|
|
1097
|
+
idx_lower = xr.where(y1 == 0, idx_upper, idx_lower)
|
|
1098
|
+
# Identify the bin center diameter
|
|
1099
|
+
d = diameter.isel(diameter_bin_center=idx_lower) # m
|
|
1100
|
+
d_width = diameter_bin_width.isel({DIAMETER_DIMENSION: idx_lower}) / 1000 # m
|
|
1101
|
+
d_lower = d - d_width / 2
|
|
1102
|
+
quantile_diameter_single_bin = d_lower + d_width * fraction
|
|
1103
|
+
|
|
1104
|
+
## ------------------------------------------------------.
|
|
1105
|
+
# Define quantile diameter
|
|
1106
|
+
quantile_diameter = xr.where(is_single_bin, quantile_diameter_single_bin, quantile_diameter_multi_bin)
|
|
1107
|
+
|
|
1108
|
+
# Set NaN where total sum is 0 or all NaN
|
|
1109
|
+
mask_invalid = np.logical_or(total_lwc == 0, np.isnan(total_lwc))
|
|
1110
|
+
quantile_diameter = quantile_diameter.where(~mask_invalid)
|
|
1111
|
+
|
|
1112
|
+
# Convert diameter to mm
|
|
1113
|
+
quantile_diameter = quantile_diameter * 1000
|
|
1114
|
+
|
|
1115
|
+
# If only 1 fraction specified, squeeze and drop quantile coordinate
|
|
1116
|
+
if quantile_diameter.sizes["quantile"] == 1:
|
|
1117
|
+
quantile_diameter = quantile_diameter.drop_vars("quantile").squeeze()
|
|
1118
|
+
|
|
1119
|
+
# Drop meaningless coordinates
|
|
1120
|
+
quantile_diameter = remove_diameter_coordinates(quantile_diameter)
|
|
1121
|
+
quantile_diameter = remove_velocity_coordinates(quantile_diameter)
|
|
1122
|
+
return quantile_diameter
|
|
1123
|
+
|
|
1124
|
+
|
|
1125
|
+
def get_quantile_volume_drop_diameter(
|
|
1126
|
+
drop_number_concentration,
|
|
1127
|
+
diameter,
|
|
1128
|
+
diameter_bin_width,
|
|
1129
|
+
fraction,
|
|
1130
|
+
water_density=1000,
|
|
1131
|
+
):
|
|
1132
|
+
r"""
|
|
1133
|
+
Compute the diameter corresponding to a specified fraction of the cumulative liquid water content (LWC).
|
|
1134
|
+
|
|
1135
|
+
This function calculates the diameter \( D_f \) at which the cumulative LWC reaches
|
|
1136
|
+
a specified fraction \( f \) of the total LWC for each drop size distribution (DSD).
|
|
1137
|
+
When \( f = 0.5 \), it computes the median volume drop diameter.
|
|
1138
|
+
|
|
1139
|
+
|
|
1140
|
+
Parameters
|
|
1141
|
+
----------
|
|
1142
|
+
drop_number_concentration : xarray.DataArray
|
|
1143
|
+
The drop number concentration \( N(D) \) for each diameter bin, typically in units of
|
|
1144
|
+
number per cubic meter per millimeter (m⁻³·mm⁻¹).
|
|
1145
|
+
diameter : xarray.DataArray
|
|
1146
|
+
The equivalent volume diameters \( D \) of the drops in each bin, in meters (m).
|
|
1147
|
+
diameter_bin_width : xarray.DataArray
|
|
1148
|
+
The width \( \Delta D \) of each diameter bin, in millimeters (mm).
|
|
1149
|
+
fraction : float or numpy.ndarray
|
|
1150
|
+
The fraction \( f \) of the total liquid water content to compute the diameter for.
|
|
1151
|
+
Default is 0.5, which computes the median volume diameter (D50).
|
|
1152
|
+
For other percentiles, use 0.1 for D10, 0.9 for D90, etc.
|
|
1153
|
+
Values must be between 0 and 1 (exclusive).
|
|
1154
|
+
water_density : float, optional
|
|
1155
|
+
The density of water in kg/m^3. The default is 1000 kg/m3.
|
|
1156
|
+
|
|
1157
|
+
Returns
|
|
1158
|
+
-------
|
|
1159
|
+
D_f : xarray.DataArray
|
|
1160
|
+
The diameter \( D_f \) corresponding to the specified fraction \( f \) of cumulative LWC,
|
|
1161
|
+
in millimeters (mm). For `fraction=0.5`, this is the median volume drop diameter D50.
|
|
1162
|
+
|
|
1163
|
+
Notes
|
|
1164
|
+
-----
|
|
1165
|
+
The calculation involves computing the cumulative sum of the liquid water content
|
|
1166
|
+
contributed by each diameter bin and finding the diameter at which the cumulative
|
|
1167
|
+
sum reaches the specified fraction \( f \) of the total liquid water content.
|
|
1168
|
+
|
|
1169
|
+
Linear interpolation is used between the two diameter bins where the cumulative LWC
|
|
1170
|
+
crosses the target LWC fraction.
|
|
1171
|
+
|
|
1172
|
+
"""
|
|
1173
|
+
# Dask array backend
|
|
1174
|
+
if hasattr(drop_number_concentration.data, "chunks"):
|
|
1175
|
+
fraction = np.atleast_1d(fraction)
|
|
1176
|
+
if fraction.size > 1:
|
|
1177
|
+
dask_gufunc_kwargs = {"output_sizes": {"quantile": fraction.size}}
|
|
1178
|
+
output_core_dims = [["quantile"]]
|
|
1179
|
+
else:
|
|
1180
|
+
dask_gufunc_kwargs = None
|
|
1181
|
+
output_core_dims = ((),)
|
|
1182
|
+
quantile_diameter = xr.apply_ufunc(
|
|
1183
|
+
_get_quantile_volume_drop_diameter,
|
|
1184
|
+
drop_number_concentration,
|
|
1185
|
+
kwargs={
|
|
1186
|
+
"fraction": fraction,
|
|
1187
|
+
"diameter": diameter.compute(),
|
|
1188
|
+
"diameter_bin_width": diameter_bin_width.compute(),
|
|
1189
|
+
"water_density": water_density,
|
|
1190
|
+
},
|
|
1191
|
+
input_core_dims=[[DIAMETER_DIMENSION]],
|
|
1192
|
+
vectorize=True,
|
|
1193
|
+
dask="parallelized",
|
|
1194
|
+
output_core_dims=output_core_dims,
|
|
1195
|
+
dask_gufunc_kwargs=dask_gufunc_kwargs,
|
|
1196
|
+
output_dtypes=["float64"],
|
|
1197
|
+
)
|
|
1198
|
+
if fraction.size > 1:
|
|
1199
|
+
quantile_diameter = quantile_diameter.assign_coords({"quantile": fraction})
|
|
1200
|
+
return quantile_diameter
|
|
1201
|
+
|
|
1202
|
+
# Numpy array backed
|
|
1203
|
+
quantile_diameter = _get_quantile_volume_drop_diameter(
|
|
1204
|
+
drop_number_concentration=drop_number_concentration,
|
|
1205
|
+
diameter=diameter,
|
|
1206
|
+
diameter_bin_width=diameter_bin_width,
|
|
1207
|
+
fraction=fraction,
|
|
1208
|
+
water_density=water_density,
|
|
1209
|
+
)
|
|
1210
|
+
return quantile_diameter
|
|
1211
|
+
|
|
1212
|
+
|
|
1213
|
+
####-----------------------------------------------------------------------------------------------------
|
|
1214
|
+
#### Normalized Gamma Parameters
|
|
1215
|
+
|
|
1216
|
+
|
|
1217
|
+
def get_normalized_intercept_parameter(liquid_water_content, mean_volume_diameter, water_density=1000):
|
|
1218
|
+
r"""
|
|
1219
|
+
Calculate the normalized intercept parameter \\( N_w \\) of the drop size distribution.
|
|
1220
|
+
|
|
1221
|
+
A higher \\( N_w \\) indicates a higher concentration of smaller drops.
|
|
1222
|
+
The \\( N_w \\) is used in models to represent the DSD when assuming a normalized gamma distribution.
|
|
1223
|
+
|
|
1224
|
+
Parameters
|
|
1225
|
+
----------
|
|
1226
|
+
liquid_water_content : float or array-like
|
|
1227
|
+
Liquid water content \\( LWC \\) in grams per cubic meter (g/m³).
|
|
1228
|
+
mean_volume_diameter : float or array-like
|
|
1229
|
+
Mean volume diameter \\( D_m \\) in millimeters (mm).
|
|
1230
|
+
water_density : float, optional
|
|
1231
|
+
Density of water \\( \rho_w \\) in kilograms per cubic meter (kg/m³).
|
|
1232
|
+
The default is 1000 kg/m³.
|
|
1233
|
+
|
|
1234
|
+
Returns
|
|
1235
|
+
-------
|
|
1236
|
+
Nw : xarray.DataArray or float
|
|
1237
|
+
Normalized intercept parameter \\( N_w \\) in units of m⁻3·mm⁻¹.
|
|
1238
|
+
|
|
1239
|
+
Notes
|
|
1240
|
+
-----
|
|
1241
|
+
The normalized intercept parameter \\( N_w \\) is calculated using the formula:
|
|
1242
|
+
|
|
1243
|
+
.. math::
|
|
1244
|
+
|
|
1245
|
+
N_w = \frac{256}{\\pi \rho_w} \\cdot \frac{W}{D_m^4}
|
|
1246
|
+
|
|
1247
|
+
where:
|
|
1248
|
+
|
|
1249
|
+
- \\( N_w \\) is the normalized intercept parameter.
|
|
1250
|
+
- \\( W \\) is the liquid water content in g/m³.
|
|
1251
|
+
- \\( D_m \\) is the mean volume diameter in mm.
|
|
1252
|
+
- \\( \rho_w \\) is the density of water in kg/m³.
|
|
1253
|
+
"""
|
|
1254
|
+
# Conversion to g/m3
|
|
1255
|
+
water_density = water_density * 1000 # g/m3
|
|
1256
|
+
|
|
1257
|
+
# Compute Nw
|
|
1258
|
+
# --> 1e9 is used to convert from mm-4 to m-3 mm-1
|
|
1259
|
+
# - 256 = 4**4
|
|
1260
|
+
# - lwc = (np.pi * water_density / 6) * moment_3
|
|
1261
|
+
Nw = (256.0 / (np.pi * water_density)) * liquid_water_content / mean_volume_diameter**4 * 1e9
|
|
1262
|
+
return Nw
|
|
1263
|
+
|
|
1264
|
+
|
|
1265
|
+
def get_normalized_intercept_parameter_from_moments(moment_3, moment_4):
|
|
1266
|
+
r"""
|
|
1267
|
+
Calculate the normalized intercept parameter \\( N_w \\) of the drop size distribution.
|
|
1268
|
+
|
|
1269
|
+
moment_3 : float or array-like
|
|
1270
|
+
The third moment of the drop size distribution, \\( M_3 \\), in units of
|
|
1271
|
+
[m⁻³·mm³] (number per cubic meter times diameter cubed).
|
|
1272
|
+
|
|
1273
|
+
moment_4 : float or array-like
|
|
1274
|
+
The foruth moment of the drop size distribution, \\( M_3 \\), in units of
|
|
1275
|
+
[m⁻³·mm4].
|
|
1276
|
+
|
|
1277
|
+
Returns
|
|
1278
|
+
-------
|
|
1279
|
+
Nw : xarray.DataArray or float
|
|
1280
|
+
Normalized intercept parameter \\( N_w \\) in units of m⁻3·mm⁻¹.
|
|
1281
|
+
|
|
1282
|
+
References
|
|
1283
|
+
----------
|
|
1284
|
+
Testud, J., S. Oury, R. A. Black, P. Amayenc, and X. Dou, 2001:
|
|
1285
|
+
The Concept of “Normalized” Distribution to Describe Raindrop spectrum:
|
|
1286
|
+
A Tool for Cloud Physics and Cloud Remote Sensing.
|
|
1287
|
+
J. Appl. Meteor. Climatol., 40, 1118-1140,
|
|
1288
|
+
https://doi.org/10.1175/1520-0450(2001)040<1118:TCONDT>2.0.CO;2
|
|
1289
|
+
|
|
1290
|
+
"""
|
|
1291
|
+
Nw = 256 / 6 * moment_3**5 / moment_4**4
|
|
1292
|
+
return Nw
|
|
1293
|
+
|
|
1294
|
+
|
|
1295
|
+
####--------------------------------------------------------------------------------------------------------
|
|
1296
|
+
#### Kinetic Energy Parameters
|
|
1297
|
+
|
|
1298
|
+
|
|
1299
|
+
def get_kinetic_energy_spectrum(
|
|
1300
|
+
drop_number_concentration,
|
|
1301
|
+
velocity,
|
|
1302
|
+
diameter,
|
|
1303
|
+
sample_interval,
|
|
1304
|
+
water_density=1000,
|
|
1305
|
+
):
|
|
1306
|
+
r"""Compute the rainfall kinetic energy per diameter class.
|
|
1307
|
+
|
|
1308
|
+
To obtain the Total Kinetic Energy (TKE) one has to multiply KE(D) with the diameter
|
|
1309
|
+
bins intervals and summing over the diameter bins.
|
|
1310
|
+
|
|
1311
|
+
Parameters
|
|
1312
|
+
----------
|
|
1313
|
+
drop_number_concentration : xarray.DataArray
|
|
1314
|
+
Array of drop number concentrations \\( N(D) \\) in m⁻³·mm⁻¹.
|
|
1315
|
+
velocity : xarray.DataArray or float
|
|
1316
|
+
The fall velocities \\( v \\) of the drops, in meters per second (m/s).
|
|
1317
|
+
diameter : xarray.DataArray
|
|
1318
|
+
The equivalent volume diameters \\( D \\) of the drops in each bin, in meters (m).
|
|
1319
|
+
sample_interval : float
|
|
1320
|
+
The time over which the drops are counted \\( \\Delta t \\) in seconds (s).
|
|
1321
|
+
water_density : float, optional
|
|
1322
|
+
The density of water \\( \rho_w \\) in kilograms per cubic meter (kg/m³).
|
|
1323
|
+
Default is 1000 kg/m³.
|
|
1324
|
+
|
|
1325
|
+
Returns
|
|
1326
|
+
-------
|
|
1327
|
+
xr.DataArray
|
|
1328
|
+
Kinetic Energy Spectrum [J/m2/mm]
|
|
1329
|
+
"""
|
|
1330
|
+
KE_spectrum = (
|
|
1331
|
+
np.pi / 12 * water_density * sample_interval * (drop_number_concentration * (diameter**3 * velocity**3))
|
|
1332
|
+
)
|
|
1333
|
+
return KE_spectrum
|
|
1334
|
+
|
|
1335
|
+
|
|
1336
|
+
def get_kinetic_energy_variables(
|
|
1337
|
+
drop_number_concentration,
|
|
1338
|
+
velocity,
|
|
1339
|
+
diameter,
|
|
1340
|
+
diameter_bin_width,
|
|
1341
|
+
sample_interval,
|
|
1342
|
+
water_density=1000,
|
|
1343
|
+
):
|
|
1344
|
+
r"""Compute rainfall kinetic energy descriptors from the drop number concentration.
|
|
1345
|
+
|
|
1346
|
+
Parameters
|
|
1347
|
+
----------
|
|
1348
|
+
drop_number_concentration : xarray.DataArray
|
|
1349
|
+
Array of drop number concentrations \\( N(D) \\) in m⁻³·mm⁻¹.
|
|
1350
|
+
velocity : xarray.DataArray or float
|
|
1351
|
+
The fall velocities \\( v \\) of the drops, in meters per second (m/s).
|
|
1352
|
+
diameter : xarray.DataArray
|
|
1353
|
+
The equivalent volume diameters \\( D \\) of the drops in each bin, in meters (m).
|
|
1354
|
+
diameter_bin_width : xarray.DataArray
|
|
1355
|
+
Width of each diameter bin \\( \\Delta D \\) in millimeters (mm).
|
|
1356
|
+
sample_interval : float
|
|
1357
|
+
The time over which the drops are counted \\( \\Delta t \\) in seconds (s).
|
|
1358
|
+
water_density : float, optional
|
|
1359
|
+
The density of water \\( \rho_w \\) in kilograms per cubic meter (kg/m³).
|
|
1360
|
+
Default is 1000 kg/m³.
|
|
1361
|
+
|
|
1362
|
+
Returns
|
|
1363
|
+
-------
|
|
1364
|
+
xarray.Dataset
|
|
1365
|
+
Xarray Dataset with relevant rainfall kinetic energy variables:
|
|
1366
|
+
- TKE: Total Kinetic Energy [J/m2]
|
|
1367
|
+
- KED: Kinetic Energy per unit rainfall Depth [J·m⁻²·mm⁻¹]. Typical values range between 0 and 40 J·m⁻²·mm⁻¹.
|
|
1368
|
+
- KEF: Kinetic Energy Flux [J·m⁻²·h⁻¹]. Typical values range between 0 and 5000 J·m⁻²·h⁻¹.
|
|
1369
|
+
KEF is related to the KED by the rain rate: KED = KEF/R .
|
|
1370
|
+
"""
|
|
1371
|
+
# Check velocity DataArray does not have a velocity dimension
|
|
1372
|
+
if VELOCITY_DIMENSION in velocity.dims:
|
|
1373
|
+
raise ValueError(f"The 'velocity' DataArray must not have the '{VELOCITY_DIMENSION}' dimension.")
|
|
1374
|
+
|
|
1375
|
+
# Compute rain rate
|
|
1376
|
+
R = get_rain_rate(
|
|
1377
|
+
drop_number_concentration=drop_number_concentration,
|
|
1378
|
+
velocity=velocity,
|
|
1379
|
+
diameter=diameter,
|
|
1380
|
+
diameter_bin_width=diameter_bin_width,
|
|
1381
|
+
)
|
|
1382
|
+
|
|
1383
|
+
# Compute total kinetic energy in [J/m2]
|
|
1384
|
+
TKE = (
|
|
1385
|
+
np.pi
|
|
1386
|
+
/ 12
|
|
1387
|
+
* water_density
|
|
1388
|
+
* sample_interval
|
|
1389
|
+
* (drop_number_concentration * diameter**3 * velocity**3 * diameter_bin_width).sum(
|
|
1390
|
+
dim=DIAMETER_DIMENSION,
|
|
1391
|
+
skipna=False,
|
|
1392
|
+
)
|
|
1393
|
+
)
|
|
1394
|
+
|
|
1395
|
+
# Compute Kinetic Energy Flux (KEF) [J/m2/h]
|
|
1396
|
+
KEF = TKE / sample_interval * 3600
|
|
1397
|
+
|
|
1398
|
+
# Compute Kinetic Energy per Rainfall Depth [J/m2/mm]
|
|
1399
|
+
KED = KEF / R
|
|
1400
|
+
KED = xr.where(R == 0, 0, KED) # Ensure KED is 0 when R (and thus drop number is 0)
|
|
1401
|
+
|
|
1402
|
+
# Create dataset
|
|
1403
|
+
dict_vars = {
|
|
1404
|
+
"TKE": TKE,
|
|
1405
|
+
"KEF": KEF,
|
|
1406
|
+
"KED": KED,
|
|
1407
|
+
}
|
|
1408
|
+
ds = xr.Dataset(dict_vars)
|
|
1409
|
+
return ds
|
|
1410
|
+
|
|
1411
|
+
|
|
1412
|
+
def get_kinetic_energy_variables_from_drop_number(
|
|
1413
|
+
drop_number,
|
|
1414
|
+
velocity,
|
|
1415
|
+
sampling_area,
|
|
1416
|
+
diameter,
|
|
1417
|
+
sample_interval,
|
|
1418
|
+
water_density=1000,
|
|
1419
|
+
):
|
|
1420
|
+
r"""Compute rainfall kinetic energy descriptors from the measured drop number spectrum.
|
|
1421
|
+
|
|
1422
|
+
Parameters
|
|
1423
|
+
----------
|
|
1424
|
+
drop_number : xarray.DataArray
|
|
1425
|
+
The number of drops in each diameter (and velocity, if available) bin(s).
|
|
1426
|
+
velocity : xarray.DataArray or float
|
|
1427
|
+
The fall velocities \\( v \\) of the drops in each bin, in meters per second (m/s).
|
|
1428
|
+
Values are broadcasted to match the dimensions of `drop_number`.
|
|
1429
|
+
diameter : xarray.DataArray
|
|
1430
|
+
The equivalent volume diameters \\( D \\) of the drops in each bin, in meters (m).
|
|
1431
|
+
sampling_area : float
|
|
1432
|
+
The effective sampling area \\( A \\) of the sensor in square meters (m²).
|
|
1433
|
+
sample_interval : float
|
|
1434
|
+
The time over which the drops are counted \\( \\Delta t \\) in seconds (s).
|
|
1435
|
+
water_density : float, optional
|
|
1436
|
+
The density of water \\( \rho_w \\) in kilograms per cubic meter (kg/m³).
|
|
1437
|
+
Default is 1000 kg/m³.
|
|
1438
|
+
|
|
1439
|
+
Returns
|
|
1440
|
+
-------
|
|
1441
|
+
xarray.Dataset
|
|
1442
|
+
Xarray Dataset with relevant rainfall kinetic energy variables:
|
|
1443
|
+
- TKE: Total Kinetic Energy [J/m2]
|
|
1444
|
+
- KED: Kinetic Energy per unit rainfall Depth [J·m⁻²·mm⁻¹]. Typical values range between 0 and 40 J·m⁻²·mm⁻¹.
|
|
1445
|
+
- KEF: Kinetic Energy Flux [J·m⁻²·h⁻¹]. Typical values range between 0 and 5000 J·m⁻²·h⁻¹.
|
|
1446
|
+
KEF is related to the KED by the rain rate: KED = KEF/R .
|
|
1447
|
+
|
|
1448
|
+
Notes
|
|
1449
|
+
-----
|
|
1450
|
+
KED provides a measure of the energy associated with each unit of rainfall depth.
|
|
1451
|
+
KED is useful for analyze the potential impact of raindrop erosion as a function of
|
|
1452
|
+
the intensity of rainfall events.
|
|
1453
|
+
|
|
1454
|
+
The kinetic energy of a rain drop is defined as:
|
|
1455
|
+
|
|
1456
|
+
.. math::
|
|
1457
|
+
|
|
1458
|
+
KE(D) = \frac{1}{2} · m_{drop} · v_{drop}^2 = \frac{\\pi \rho_{w}}{12} · D^3 · v^2
|
|
1459
|
+
|
|
1460
|
+
The Total Kinetic Energy (TKE) is calculated using:
|
|
1461
|
+
|
|
1462
|
+
.. math::
|
|
1463
|
+
|
|
1464
|
+
TKE = \\sum_{i,j} \\left({n_{ij} · KE(D_{i}) \right)
|
|
1465
|
+
= \frac{\\pi \rho_{w}}{12 · A} \\sum_{i,j} \\left( {n_{ij} · D_{i}^3 · v_{j}^2}} \right)
|
|
1466
|
+
|
|
1467
|
+
The Kinetic Energy Flux (KEF) is calculated using:
|
|
1468
|
+
|
|
1469
|
+
.. math::
|
|
1470
|
+
|
|
1471
|
+
KEF = \frac{TKE}{\\Delta t } · 3600
|
|
1472
|
+
|
|
1473
|
+
KED is calculated using:
|
|
1474
|
+
|
|
1475
|
+
.. math::
|
|
1476
|
+
|
|
1477
|
+
KED = \frac{KEF}{R} \\cdot \frac{\\pi}{6} \\cdot \frac{\rho_w}{R} \\cdot \\sum_{i,j}
|
|
1478
|
+
\\left( \frac{n_{ij} \\cdot D_i^3 \\cdot v_j^2}{A} \right)
|
|
1479
|
+
|
|
1480
|
+
where:
|
|
1481
|
+
|
|
1482
|
+
- \\( n_{ij} \\) is the number of drops in diameter bin \\( i \\) and velocity bin \\( j \\).
|
|
1483
|
+
- \\( D_i \\) is the diameter of bin \\( i \\).
|
|
1484
|
+
- \\( v_j \\) is the velocity of bin \\( j \\).
|
|
1485
|
+
- \\( A \\) is the sampling area.
|
|
1486
|
+
- \\( \\Delta t \\) is the time integration period in seconds.
|
|
1487
|
+
- \\( R \\) is the rainfall rate in mm/hr.
|
|
1488
|
+
|
|
1489
|
+
"""
|
|
1490
|
+
# Get drop number core dimensions
|
|
1491
|
+
dim = get_bin_dimensions(drop_number)
|
|
1492
|
+
|
|
1493
|
+
# Ensure velocity is 2D if drop number has velocity dimension
|
|
1494
|
+
# - if measured velocity --> already 2D
|
|
1495
|
+
# - if estimated velocity --> broadcasted to 2D
|
|
1496
|
+
velocity = xr.ones_like(drop_number) * velocity
|
|
1497
|
+
|
|
1498
|
+
# Compute rain rate
|
|
1499
|
+
R = get_rain_rate_from_drop_number(
|
|
1500
|
+
drop_number=drop_number,
|
|
1501
|
+
sampling_area=sampling_area,
|
|
1502
|
+
diameter=diameter,
|
|
1503
|
+
sample_interval=sample_interval,
|
|
1504
|
+
)
|
|
1505
|
+
|
|
1506
|
+
# Compute drop size kinetic energy per diameter (and velocity bin)
|
|
1507
|
+
KE = np.pi / 12 * water_density * diameter**3 * velocity**2 # [J]
|
|
1508
|
+
|
|
1509
|
+
# Compute total kinetic energy in [J/m2]
|
|
1510
|
+
TKE = (KE * drop_number / sampling_area).sum(dim=dim, skipna=False)
|
|
1511
|
+
|
|
1512
|
+
# Compute Kinetic Energy Flux (KEF) [J/m2/h]
|
|
1513
|
+
KEF = TKE / sample_interval * 3600
|
|
1514
|
+
|
|
1515
|
+
# Compute Kinetic Energy per Rainfall Depth [J/m2/mm]
|
|
1516
|
+
KED = KEF / R
|
|
1517
|
+
KED = xr.where(R == 0, 0, KED) # Ensure KED is 0 when R (and thus drop number is 0)
|
|
1518
|
+
|
|
1519
|
+
# Create dataset
|
|
1520
|
+
dict_vars = {
|
|
1521
|
+
"TKE": TKE,
|
|
1522
|
+
"KEF": KEF,
|
|
1523
|
+
"KED": KED,
|
|
1524
|
+
}
|
|
1525
|
+
ds = xr.Dataset(dict_vars)
|
|
1526
|
+
return ds
|
|
1527
|
+
|
|
1528
|
+
|
|
1529
|
+
####-------------------------------------------------------------------------------------------------------.
|
|
1530
|
+
#### Wrapper ####
|
|
1531
|
+
|
|
1532
|
+
|
|
1533
|
+
def compute_integral_parameters(
|
|
1534
|
+
drop_number_concentration,
|
|
1535
|
+
velocity,
|
|
1536
|
+
diameter,
|
|
1537
|
+
diameter_bin_width,
|
|
1538
|
+
sample_interval,
|
|
1539
|
+
water_density,
|
|
1540
|
+
):
|
|
1541
|
+
"""
|
|
1542
|
+
Compute integral parameters of a drop size distribution (DSD).
|
|
1543
|
+
|
|
1544
|
+
Parameters
|
|
1545
|
+
----------
|
|
1546
|
+
drop_number_concentration : xr.DataArray
|
|
1547
|
+
Drop number concentration in each diameter bin [#/m3/mm].
|
|
1548
|
+
velocity : xr.DataArray
|
|
1549
|
+
Fall velocity of drops in each diameter bin [m/s].
|
|
1550
|
+
The presence of a velocity_method dimension enable to compute the parameters
|
|
1551
|
+
with different velocity estimates.
|
|
1552
|
+
diameter : array-like
|
|
1553
|
+
Diameter of drops in each bin in m.
|
|
1554
|
+
diameter_bin_width : array-like
|
|
1555
|
+
Width of each diameter bin in mm.
|
|
1556
|
+
sample_interval : float
|
|
1557
|
+
Time interval over which the samples are collected in seconds.
|
|
1558
|
+
water_density : float or array-like
|
|
1559
|
+
Density of water [kg/m3].
|
|
1560
|
+
|
|
1561
|
+
Returns
|
|
1562
|
+
-------
|
|
1563
|
+
ds : xarray.Dataset
|
|
1564
|
+
Dataset containing the computed integral parameters:
|
|
1565
|
+
- Nt : Total number concentration [#/m3]
|
|
1566
|
+
- M1 to M6 : Moments of the drop size distribution
|
|
1567
|
+
- Z : Reflectivity factor [dBZ]
|
|
1568
|
+
- W : Liquid water content [g/m3]
|
|
1569
|
+
- D10 : Diameter at the 10th quantile of the cumulative LWC distribution [mm]
|
|
1570
|
+
- D50 : Median volume drop diameter [mm]
|
|
1571
|
+
- D90 : Diameter at the 90th quantile of the cumulative LWC distribution [mm]
|
|
1572
|
+
- Dmode : Diameter at which the distribution peaks [mm]
|
|
1573
|
+
- Dm : Mean volume drop diameter [mm]
|
|
1574
|
+
- sigma_m : Standard deviation of the volume drop diameter [mm]
|
|
1575
|
+
- Nw : Normalized intercept parameter [m-3·mm⁻¹]
|
|
1576
|
+
- R : Rain rate [mm/h]
|
|
1577
|
+
- P : Rain accumulation [mm]
|
|
1578
|
+
- TKE: Total Kinetic Energy [J/m2]
|
|
1579
|
+
- KED: Kinetic Energy per unit rainfall Depth [J·m⁻²·mm⁻¹].
|
|
1580
|
+
- KEF: Kinetic Energy Flux [J·m⁻²·h⁻¹].
|
|
1581
|
+
"""
|
|
1582
|
+
# Initialize dataset
|
|
1583
|
+
ds = xr.Dataset()
|
|
1584
|
+
|
|
1585
|
+
# Compute total number concentration (Nt) [#/m3]
|
|
1586
|
+
ds["Nt"] = get_total_number_concentration(
|
|
1587
|
+
drop_number_concentration=drop_number_concentration,
|
|
1588
|
+
diameter_bin_width=diameter_bin_width,
|
|
1589
|
+
)
|
|
1590
|
+
|
|
1591
|
+
# Compute rain rate
|
|
1592
|
+
ds["R"] = get_rain_rate(
|
|
1593
|
+
drop_number_concentration=drop_number_concentration,
|
|
1594
|
+
velocity=velocity,
|
|
1595
|
+
diameter=diameter,
|
|
1596
|
+
diameter_bin_width=diameter_bin_width,
|
|
1597
|
+
)
|
|
1598
|
+
|
|
1599
|
+
# Compute rain accumulation (P) [mm]
|
|
1600
|
+
ds["P"] = get_rain_accumulation(rain_rate=ds["R"], sample_interval=sample_interval)
|
|
1601
|
+
|
|
1602
|
+
# Compute moments (m0 to m6)
|
|
1603
|
+
for moment in range(0, 7):
|
|
1604
|
+
ds[f"M{moment}"] = get_moment(
|
|
1605
|
+
drop_number_concentration=drop_number_concentration,
|
|
1606
|
+
diameter=diameter,
|
|
1607
|
+
diameter_bin_width=diameter_bin_width,
|
|
1608
|
+
moment=moment,
|
|
1609
|
+
)
|
|
1610
|
+
|
|
1611
|
+
# Compute Liquid Water Content (LWC) (W) [g/m3]
|
|
1612
|
+
# ds["W"] = get_liquid_water_content(
|
|
1613
|
+
# drop_number_concentration=drop_number_concentration,
|
|
1614
|
+
# diameter=diameter,
|
|
1615
|
+
# diameter_bin_width=diameter_bin_width,
|
|
1616
|
+
# water_density=water_density,
|
|
1617
|
+
# )
|
|
1618
|
+
|
|
1619
|
+
ds["W"] = get_liquid_water_content_from_moments(moment_3=ds["M3"], water_density=water_density)
|
|
1620
|
+
|
|
1621
|
+
# Compute reflectivity in dBZ
|
|
1622
|
+
ds["Z"] = get_equivalent_reflectivity_factor(
|
|
1623
|
+
drop_number_concentration=drop_number_concentration,
|
|
1624
|
+
diameter=diameter,
|
|
1625
|
+
diameter_bin_width=diameter_bin_width,
|
|
1626
|
+
)
|
|
1627
|
+
|
|
1628
|
+
# Compute the diameter at which the distribution peak
|
|
1629
|
+
ds["Dmode"] = get_mode_diameter(drop_number_concentration, diameter=diameter) * 1000 # Output converted to mm
|
|
1630
|
+
|
|
1631
|
+
# Compute mean_volume_diameter (Dm) [mm]
|
|
1632
|
+
ds["Dm"] = get_mean_volume_drop_diameter(moment_3=ds["M3"], moment_4=ds["M4"])
|
|
1633
|
+
|
|
1634
|
+
# Compute σₘ[mm]
|
|
1635
|
+
ds["sigma_m"] = get_std_volume_drop_diameter(
|
|
1636
|
+
moment_3=ds["M3"],
|
|
1637
|
+
moment_4=ds["M4"],
|
|
1638
|
+
moment_5=ds["M5"],
|
|
1639
|
+
)
|
|
1640
|
+
|
|
1641
|
+
# Compute normalized_intercept_parameter (Nw) [m-3·mm⁻¹]
|
|
1642
|
+
# ds["Nw"] = get_normalized_intercept_parameter(
|
|
1643
|
+
# liquid_water_content=liquid_water_content,
|
|
1644
|
+
# mean_volume_diameter=mean_volume_diameter,
|
|
1645
|
+
# water_density=water_density,
|
|
1646
|
+
# )
|
|
1647
|
+
|
|
1648
|
+
ds["Nw"] = get_normalized_intercept_parameter_from_moments(moment_3=ds["M3"], moment_4=ds["M4"])
|
|
1649
|
+
|
|
1650
|
+
# Compute median volume_drop_diameter
|
|
1651
|
+
# --> Equivalent to get_quantile_volume_drop_diameter with fraction = 0.5
|
|
1652
|
+
# ds["D50"] = get_median_volume_drop_diameter(
|
|
1653
|
+
# drop_number_concentration=drop_number_concentration,
|
|
1654
|
+
# diameter=diameter,
|
|
1655
|
+
# diameter_bin_width=diameter_bin_width,
|
|
1656
|
+
# water_density=water_density,
|
|
1657
|
+
# )
|
|
1658
|
+
|
|
1659
|
+
# Compute volume_drop_diameter for the 10th and 90th quantile of the cumulative LWC distribution
|
|
1660
|
+
fractions = [0.1, 0.5, 0.9]
|
|
1661
|
+
d_q = get_quantile_volume_drop_diameter(
|
|
1662
|
+
drop_number_concentration=drop_number_concentration,
|
|
1663
|
+
diameter=diameter,
|
|
1664
|
+
diameter_bin_width=diameter_bin_width,
|
|
1665
|
+
fraction=fractions,
|
|
1666
|
+
water_density=water_density,
|
|
1667
|
+
)
|
|
1668
|
+
for fraction in fractions:
|
|
1669
|
+
var = f"D{round(fraction*100)!s}" # D10, D50, D90
|
|
1670
|
+
ds[var] = d_q.sel(quantile=fraction).drop_vars("quantile")
|
|
1671
|
+
|
|
1672
|
+
# Compute kinetic energy variables
|
|
1673
|
+
ds_ke = get_kinetic_energy_variables(
|
|
1674
|
+
drop_number_concentration=drop_number_concentration,
|
|
1675
|
+
velocity=velocity,
|
|
1676
|
+
diameter=diameter,
|
|
1677
|
+
diameter_bin_width=diameter_bin_width,
|
|
1678
|
+
sample_interval=sample_interval,
|
|
1679
|
+
water_density=water_density,
|
|
1680
|
+
)
|
|
1681
|
+
ds.update(ds_ke)
|
|
1682
|
+
return ds
|
|
1683
|
+
|
|
1684
|
+
|
|
1685
|
+
def compute_spectrum_parameters(
|
|
1686
|
+
drop_number_concentration,
|
|
1687
|
+
velocity,
|
|
1688
|
+
diameter,
|
|
1689
|
+
sample_interval,
|
|
1690
|
+
water_density=1000,
|
|
1691
|
+
):
|
|
1692
|
+
"""
|
|
1693
|
+
Compute drop size spectrum of rain rate, kinetic energy, mass and reflectivity.
|
|
1694
|
+
|
|
1695
|
+
Parameters
|
|
1696
|
+
----------
|
|
1697
|
+
drop_number_concentration : xr.DataArray
|
|
1698
|
+
Drop number concentration in each diameter bin [#/m3/mm].
|
|
1699
|
+
velocity : xr.DataArray
|
|
1700
|
+
Fall velocity of drops in each diameter bin [m/s].
|
|
1701
|
+
The presence of a velocity_method dimension enable to compute the parameters
|
|
1702
|
+
with different velocity estimates.
|
|
1703
|
+
diameter : array-like
|
|
1704
|
+
Diameter of drops in each bin in m.
|
|
1705
|
+
sample_interval : float
|
|
1706
|
+
Time interval over which the samples are collected in seconds.
|
|
1707
|
+
water_density : float or array-like
|
|
1708
|
+
Density of water [kg/m3].
|
|
1709
|
+
|
|
1710
|
+
Returns
|
|
1711
|
+
-------
|
|
1712
|
+
ds : xarray.Dataset
|
|
1713
|
+
Dataset containing the following spectrum:
|
|
1714
|
+
- KE_spectrum : Kinetic Energy spectrum [J/m2/mm]
|
|
1715
|
+
- R_spectrum : Rain Rate spectrum [mm/h/mm]
|
|
1716
|
+
- W_spectrum : Mass spectrum [g/m3/mm]
|
|
1717
|
+
- Z_spectrum : Reflectivity spectrum [dBZ of mm6/m3/mm]
|
|
1718
|
+
"""
|
|
1719
|
+
# Initialize dataset
|
|
1720
|
+
ds = xr.Dataset()
|
|
1721
|
+
ds["KE_spectrum"] = get_kinetic_energy_spectrum(
|
|
1722
|
+
drop_number_concentration,
|
|
1723
|
+
velocity=velocity,
|
|
1724
|
+
diameter=diameter,
|
|
1725
|
+
sample_interval=sample_interval,
|
|
1726
|
+
water_density=water_density,
|
|
1727
|
+
)
|
|
1728
|
+
ds["R_spectrum"] = get_rain_rate_spectrum(drop_number_concentration, velocity=velocity, diameter=diameter)
|
|
1729
|
+
ds["W_spectrum"] = get_liquid_water_spectrum(
|
|
1730
|
+
drop_number_concentration,
|
|
1731
|
+
diameter=diameter,
|
|
1732
|
+
water_density=water_density,
|
|
1733
|
+
)
|
|
1734
|
+
ds["Z_spectrum"] = get_equivalent_reflectivity_spectrum(drop_number_concentration, diameter=diameter)
|
|
1735
|
+
return ds
|