disdrodb 0.2.1__py3-none-any.whl → 0.3.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 +1 -1
- disdrodb/_config.py +1 -3
- disdrodb/_version.py +2 -2
- disdrodb/accessor/__init__.py +1 -1
- disdrodb/accessor/methods.py +9 -9
- disdrodb/api/checks.py +1 -3
- disdrodb/api/configs.py +1 -3
- disdrodb/api/create_directories.py +4 -6
- disdrodb/api/info.py +1 -3
- disdrodb/api/io.py +9 -8
- disdrodb/api/path.py +1 -3
- disdrodb/cli/disdrodb_check_metadata_archive.py +2 -2
- disdrodb/cli/disdrodb_check_products_options.py +44 -0
- disdrodb/cli/disdrodb_create_summary.py +48 -22
- disdrodb/cli/disdrodb_create_summary_station.py +39 -18
- disdrodb/cli/disdrodb_data_archive_directory.py +1 -3
- disdrodb/cli/disdrodb_download_archive.py +45 -24
- disdrodb/cli/disdrodb_download_metadata_archive.py +27 -16
- disdrodb/cli/disdrodb_download_station.py +56 -26
- disdrodb/cli/disdrodb_initialize_station.py +40 -20
- disdrodb/cli/disdrodb_metadata_archive_directory.py +1 -3
- disdrodb/cli/disdrodb_open_data_archive.py +16 -11
- disdrodb/cli/disdrodb_open_logs_directory.py +29 -18
- disdrodb/cli/disdrodb_open_metadata_archive.py +25 -11
- disdrodb/cli/disdrodb_open_metadata_directory.py +32 -20
- disdrodb/cli/disdrodb_open_product_directory.py +38 -21
- disdrodb/cli/disdrodb_open_readers_directory.py +1 -3
- disdrodb/cli/disdrodb_run.py +189 -0
- disdrodb/cli/disdrodb_run_l0.py +55 -64
- disdrodb/cli/disdrodb_run_l0_station.py +47 -52
- disdrodb/cli/disdrodb_run_l0a.py +47 -45
- disdrodb/cli/disdrodb_run_l0a_station.py +38 -37
- disdrodb/cli/disdrodb_run_l0b.py +45 -45
- disdrodb/cli/disdrodb_run_l0b_station.py +37 -36
- disdrodb/cli/disdrodb_run_l0c.py +50 -47
- disdrodb/cli/disdrodb_run_l0c_station.py +41 -38
- disdrodb/cli/disdrodb_run_l1.py +49 -45
- disdrodb/cli/disdrodb_run_l1_station.py +40 -37
- disdrodb/cli/disdrodb_run_l2e.py +50 -45
- disdrodb/cli/disdrodb_run_l2e_station.py +41 -37
- disdrodb/cli/disdrodb_run_l2m.py +49 -45
- disdrodb/cli/disdrodb_run_l2m_station.py +40 -37
- disdrodb/cli/disdrodb_run_station.py +184 -0
- disdrodb/cli/disdrodb_upload_archive.py +45 -35
- disdrodb/cli/disdrodb_upload_station.py +39 -32
- disdrodb/configs.py +13 -8
- disdrodb/constants.py +4 -2
- disdrodb/data_transfer/__init__.py +1 -3
- disdrodb/data_transfer/download_data.py +38 -54
- disdrodb/data_transfer/upload_data.py +1 -3
- disdrodb/data_transfer/zenodo.py +1 -3
- disdrodb/docs.py +1 -3
- disdrodb/etc/configs/attributes.yaml +52 -2
- disdrodb/etc/configs/encodings.yaml +45 -1
- disdrodb/etc/products/L0C/ODM470/global.yaml +5 -0
- disdrodb/etc/products/L0C/global.yaml +5 -0
- disdrodb/etc/products/L1/ODM470/global.yaml +6 -0
- disdrodb/etc/products/L1/global.yaml +0 -13
- disdrodb/etc/products/L2E/LPM/1MIN.yaml +1 -0
- disdrodb/etc/products/L2E/LPM/global.yaml +36 -0
- disdrodb/etc/products/L2E/LPM_V0/1MIN.yaml +1 -0
- disdrodb/etc/products/L2E/LPM_V0/global.yaml +36 -0
- disdrodb/etc/products/L2E/ODM470/1MIN.yaml +1 -0
- disdrodb/etc/products/L2E/ODM470/global.yaml +36 -0
- disdrodb/etc/products/L2E/PARSIVEL/1MIN.yaml +1 -0
- disdrodb/etc/products/L2E/PARSIVEL/global.yaml +36 -0
- disdrodb/etc/products/L2E/PARSIVEL2/1MIN.yaml +1 -0
- disdrodb/etc/products/L2E/PARSIVEL2/global.yaml +36 -0
- disdrodb/etc/products/L2E/PWS100/1MIN.yaml +1 -0
- disdrodb/etc/products/L2E/PWS100/global.yaml +36 -0
- disdrodb/etc/products/L2E/RD80/1MIN.yaml +19 -0
- disdrodb/etc/products/L2E/SWS250/1MIN.yaml +19 -0
- disdrodb/etc/products/L2E/global.yaml +16 -2
- disdrodb/fall_velocity/__init__.py +46 -0
- disdrodb/fall_velocity/graupel.py +483 -0
- disdrodb/fall_velocity/hail.py +287 -0
- disdrodb/{l1/fall_velocity.py → fall_velocity/rain.py} +264 -44
- disdrodb/issue/__init__.py +1 -3
- disdrodb/issue/checks.py +1 -3
- disdrodb/issue/reader.py +1 -3
- disdrodb/issue/writer.py +1 -3
- disdrodb/l0/__init__.py +1 -1
- disdrodb/l0/check_configs.py +25 -16
- disdrodb/l0/check_standards.py +1 -3
- disdrodb/l0/configs/ODM470/bins_diameter.yml +643 -0
- disdrodb/l0/configs/ODM470/bins_velocity.yml +0 -0
- disdrodb/l0/configs/ODM470/l0a_encodings.yml +11 -0
- disdrodb/l0/configs/ODM470/l0b_cf_attrs.yml +46 -0
- disdrodb/l0/configs/ODM470/l0b_encodings.yml +106 -0
- disdrodb/l0/configs/ODM470/raw_data_format.yml +111 -0
- disdrodb/l0/configs/PARSIVEL/l0b_cf_attrs.yml +1 -1
- disdrodb/l0/l0_reader.py +1 -3
- disdrodb/l0/l0a_processing.py +1 -3
- disdrodb/l0/l0b_nc_processing.py +2 -4
- disdrodb/l0/l0b_processing.py +1 -3
- disdrodb/l0/l0c_processing.py +27 -11
- disdrodb/l0/readers/LPM/ARM/ARM_LPM.py +1 -1
- disdrodb/l0/readers/LPM/AUSTRALIA/MELBOURNE_2007_LPM.py +1 -1
- disdrodb/l0/readers/LPM/BRAZIL/CHUVA_LPM.py +1 -1
- disdrodb/l0/readers/LPM/BRAZIL/GOAMAZON_LPM.py +1 -1
- disdrodb/l0/readers/LPM/GERMANY/DWD.py +190 -12
- disdrodb/l0/readers/LPM/ITALY/GID_LPM.py +47 -6
- disdrodb/l0/readers/LPM/ITALY/GID_LPM_PI.py +1 -1
- disdrodb/l0/readers/LPM/ITALY/GID_LPM_T.py +5 -2
- disdrodb/l0/readers/LPM/ITALY/GID_LPM_W.py +1 -3
- disdrodb/l0/readers/LPM/KIT/CHWALA.py +1 -3
- disdrodb/l0/readers/LPM/NETHERLANDS/DELFT_LPM_NC.py +1 -1
- disdrodb/l0/readers/LPM/NETHERLANDS/DELFT_RWANDA_LPM_NC.py +1 -1
- disdrodb/l0/readers/LPM/NORWAY/HAUKELISETER_LPM.py +1 -3
- disdrodb/l0/readers/LPM/NORWAY/NMBU_LPM.py +1 -3
- disdrodb/l0/readers/LPM/SLOVENIA/ARSO.py +1 -3
- disdrodb/l0/readers/LPM/SLOVENIA/UL.py +1 -3
- disdrodb/l0/readers/LPM/SWITZERLAND/INNERERIZ_LPM.py +1 -3
- disdrodb/l0/readers/LPM/UK/DIVEN.py +1 -1
- disdrodb/l0/readers/LPM/UK/WITHWORTH_LPM.py +1 -3
- disdrodb/l0/readers/LPM/USA/CHARLESTON.py +1 -3
- disdrodb/l0/readers/LPM_V0/BELGIUM/ULIEGE.py +1 -3
- disdrodb/l0/readers/LPM_V0/ITALY/GID_LPM_V0.py +1 -1
- disdrodb/l0/readers/ODM470/OCEAN/OCEANRAIN.py +123 -0
- disdrodb/l0/readers/PARSIVEL/AUSTRALIA/MELBOURNE_2007_PARSIVEL.py +1 -1
- disdrodb/l0/readers/PARSIVEL/BASQUECOUNTRY/EUSKALMET_OTT.py +1 -1
- disdrodb/l0/readers/PARSIVEL/CHINA/CHONGQING.py +1 -3
- disdrodb/l0/readers/PARSIVEL/EPFL/ARCTIC_2021.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/COMMON_2011.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/DAVOS_2009_2011.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/EPFL_2009.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/EPFL_ROOF_2008.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/EPFL_ROOF_2010.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/EPFL_ROOF_2011.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/EPFL_ROOF_2012.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/GENEPI_2007.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/GRAND_ST_BERNARD_2007.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/GRAND_ST_BERNARD_2007_2.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/HPICONET_2010.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/HYMEX_LTE_SOP2.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/HYMEX_LTE_SOP3.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/HYMEX_LTE_SOP4.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/LOCARNO_2018.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/LOCARNO_2019.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/PARADISO_2014.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/PARSIVEL_2007.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/PLATO_2019.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/RACLETS_2019.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/RACLETS_2019_WJF.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/RIETHOLZBACH_2011.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/SAMOYLOV_2017.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/SAMOYLOV_2019.py +1 -1
- disdrodb/l0/readers/PARSIVEL/EPFL/UNIL_2022.py +1 -1
- disdrodb/l0/readers/PARSIVEL/JAPAN/JMA.py +1 -1
- disdrodb/l0/readers/PARSIVEL/KOREA/ICEPOP_MSC.py +159 -0
- disdrodb/l0/readers/PARSIVEL/NASA/LPVEX.py +1 -1
- disdrodb/l0/readers/PARSIVEL/NASA/MC3E.py +1 -1
- disdrodb/l0/readers/PARSIVEL/NCAR/CCOPE_2015.py +1 -1
- disdrodb/l0/readers/PARSIVEL/NCAR/OWLES_MIPS.py +1 -1
- disdrodb/l0/readers/PARSIVEL/NCAR/PECAN_MOBILE.py +1 -1
- disdrodb/l0/readers/PARSIVEL/NCAR/PLOWS_MIPS.py +1 -1
- disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2009.py +1 -1
- disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010.py +1 -3
- disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010_UF.py +1 -3
- disdrodb/l0/readers/PARSIVEL/SLOVENIA/UL.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/ARM/ARM_PARSIVEL2.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/BASQUECOUNTRY/EUSKALMET_OTT2.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/BELGIUM/ILVO.py +1 -3
- disdrodb/l0/readers/PARSIVEL2/BRAZIL/CHUVA_PARSIVEL2.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/BRAZIL/GOAMAZON_PARSIVEL2.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/CANADA/UQAM_NC.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/DENMARK/DTU.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/DENMARK/EROSION_nc.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/DENMARK/EROSION_raw.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/FINLAND/FMI_PARSIVEL2.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/FRANCE/ENPC_PARSIVEL2.py +1 -3
- disdrodb/l0/readers/PARSIVEL2/FRANCE/OSUG.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/FRANCE/SIRTA_PARSIVEL2.py +1 -3
- disdrodb/l0/readers/PARSIVEL2/GREECE/NOA.py +4 -3
- disdrodb/l0/readers/PARSIVEL2/ITALY/GID_PARSIVEL2.py +1 -3
- disdrodb/l0/readers/PARSIVEL2/ITALY/HYDROX.py +5 -3
- disdrodb/l0/readers/PARSIVEL2/JAPAN/PRECIP.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/KIT/BURKINA_FASO.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/KIT/TEAMX.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/KOREA/ICEPOP_MSC.py +161 -0
- disdrodb/l0/readers/PARSIVEL2/KOREA/ICEPOP_UCLM.py +126 -0
- disdrodb/l0/readers/PARSIVEL2/MEXICO/OH_IIUNAM_nc.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/MPI/BCO_PARSIVEL2.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/MPI/BOWTIE.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/NASA/APU.py +3 -1
- disdrodb/l0/readers/PARSIVEL2/NASA/NSSTC.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/NCAR/FARM_PARSIVEL2.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/NCAR/PECAN_FP3.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/NCAR/PECAN_MIPS.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/NCAR/PERILS_MIPS.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/NCAR/PERILS_PIPS.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/NCAR/RELAMPAGO_PARSIVEL2.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_PJ.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_SB.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_P1.py +1 -3
- disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_P2.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_PIPS.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/NETHERLANDS/DELFT_NC.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/NORWAY/UIB.py +10 -2
- disdrodb/l0/readers/PARSIVEL2/PHILIPPINES/PAGASA.py +1 -3
- disdrodb/l0/readers/PARSIVEL2/SPAIN/CENER.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/SPAIN/CR1000DL.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/SPAIN/GRANADA.py +1 -3
- disdrodb/l0/readers/PARSIVEL2/SPAIN/LIAISE.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/SWEDEN/SMHI.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/USA/CSU.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/USA/CW3E.py +1 -1
- disdrodb/l0/readers/PWS100/AUSTRIA/HOAL.py +1 -3
- disdrodb/l0/readers/PWS100/FRANCE/ENPC_PWS100.py +1 -3
- disdrodb/l0/readers/PWS100/FRANCE/ENPC_PWS100_SIRTA.py +1 -1
- disdrodb/l0/readers/RD80/BRAZIL/ATTO_RD80.py +1 -3
- disdrodb/l0/readers/RD80/BRAZIL/CHUVA_RD80.py +1 -3
- disdrodb/l0/readers/RD80/BRAZIL/GOAMAZON_RD80.py +1 -3
- disdrodb/l0/readers/RD80/NCAR/CINDY_2011_RD80.py +1 -3
- disdrodb/l0/readers/RD80/NCAR/RELAMPAGO_RD80.py +1 -3
- disdrodb/l0/readers/RD80/NOAA/PSL_RD80.py +1 -3
- disdrodb/l0/readers/SWS250/BELGIUM/KMI.py +1 -3
- disdrodb/l0/readers/template_reader_raw_netcdf_data.py +1 -3
- disdrodb/l0/readers/template_reader_raw_text_data.py +1 -3
- disdrodb/l0/standards.py +4 -5
- disdrodb/l0/template_tools.py +1 -3
- disdrodb/l1/__init__.py +1 -1
- disdrodb/l1/classification.py +913 -0
- disdrodb/l1/processing.py +36 -106
- disdrodb/l1/resampling.py +8 -3
- disdrodb/l1_env/__init__.py +1 -1
- disdrodb/l1_env/routines.py +6 -6
- disdrodb/l2/__init__.py +1 -1
- disdrodb/l2/empirical_dsd.py +57 -31
- disdrodb/l2/processing.py +327 -62
- disdrodb/metadata/checks.py +1 -3
- disdrodb/metadata/download.py +4 -4
- disdrodb/metadata/geolocation.py +1 -3
- disdrodb/metadata/info.py +1 -3
- disdrodb/metadata/manipulation.py +1 -3
- disdrodb/metadata/reader.py +1 -3
- disdrodb/metadata/search.py +1 -3
- disdrodb/metadata/standards.py +1 -3
- disdrodb/metadata/writer.py +1 -3
- disdrodb/physics/__init__.py +17 -0
- disdrodb/physics/atmosphere.py +272 -0
- disdrodb/physics/water.py +130 -0
- disdrodb/physics/wrappers.py +62 -0
- disdrodb/psd/__init__.py +1 -1
- disdrodb/psd/fitting.py +22 -9
- disdrodb/psd/models.py +1 -1
- disdrodb/routines/__init__.py +5 -1
- disdrodb/routines/l0.py +26 -16
- disdrodb/routines/l1.py +8 -6
- disdrodb/routines/l2.py +8 -4
- disdrodb/routines/options.py +116 -73
- disdrodb/routines/options_validation.py +728 -0
- disdrodb/routines/wrappers.py +431 -11
- disdrodb/scattering/__init__.py +1 -1
- disdrodb/scattering/axis_ratio.py +6 -6
- disdrodb/scattering/permittivity.py +8 -8
- disdrodb/scattering/routines.py +31 -13
- disdrodb/summary/__init__.py +1 -1
- disdrodb/summary/routines.py +83 -25
- disdrodb/utils/__init__.py +1 -1
- disdrodb/utils/archiving.py +16 -9
- disdrodb/utils/attrs.py +4 -3
- disdrodb/utils/cli.py +8 -10
- disdrodb/utils/compression.py +9 -11
- disdrodb/utils/dask.py +2 -3
- disdrodb/utils/dataframe.py +1 -3
- disdrodb/utils/decorators.py +1 -3
- disdrodb/utils/dict.py +1 -1
- disdrodb/utils/directories.py +3 -5
- disdrodb/utils/encoding.py +2 -4
- disdrodb/utils/event.py +1 -1
- disdrodb/utils/list.py +1 -3
- disdrodb/utils/logger.py +1 -3
- disdrodb/utils/manipulations.py +175 -5
- disdrodb/utils/pydantic.py +80 -0
- disdrodb/utils/routines.py +1 -3
- disdrodb/utils/subsetting.py +1 -1
- disdrodb/utils/time.py +3 -2
- disdrodb/utils/warnings.py +1 -3
- disdrodb/utils/writer.py +1 -3
- disdrodb/utils/xarray.py +30 -3
- disdrodb/utils/yaml.py +1 -3
- disdrodb/viz/__init__.py +1 -1
- disdrodb/viz/plots.py +192 -18
- {disdrodb-0.2.1.dist-info → disdrodb-0.3.0.dist-info}/METADATA +2 -2
- disdrodb-0.3.0.dist-info/RECORD +358 -0
- {disdrodb-0.2.1.dist-info → disdrodb-0.3.0.dist-info}/entry_points.txt +3 -0
- disdrodb/etc/products/L1/1MIN.yaml +0 -13
- disdrodb/etc/products/L1/LPM/1MIN.yaml +0 -13
- disdrodb/etc/products/L1/LPM_V0/1MIN.yaml +0 -13
- disdrodb/etc/products/L1/PARSIVEL/1MIN.yaml +0 -13
- disdrodb/etc/products/L1/PARSIVEL2/1MIN.yaml +0 -13
- disdrodb/etc/products/L1/PWS100/1MIN.yaml +0 -13
- disdrodb/etc/products/L1/RD80/1MIN.yaml +0 -13
- disdrodb/etc/products/L1/SWS250/1MIN.yaml +0 -13
- disdrodb/etc/products/L2M/10MIN.yaml +0 -12
- disdrodb/l1/beard_model.py +0 -662
- disdrodb/l1/filters.py +0 -205
- disdrodb-0.2.1.dist-info/RECORD +0 -329
- {disdrodb-0.2.1.dist-info → disdrodb-0.3.0.dist-info}/WHEEL +0 -0
- {disdrodb-0.2.1.dist-info → disdrodb-0.3.0.dist-info}/licenses/LICENSE +0 -0
- {disdrodb-0.2.1.dist-info → disdrodb-0.3.0.dist-info}/top_level.txt +0 -0
disdrodb/utils/manipulations.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
1
|
# -----------------------------------------------------------------------------.
|
|
4
|
-
# Copyright (c) 2021-
|
|
2
|
+
# Copyright (c) 2021-2026 DISDRODB developers
|
|
5
3
|
#
|
|
6
4
|
# This program is free software: you can redistribute it and/or modify
|
|
7
5
|
# it under the terms of the GNU General Public License as published by
|
|
@@ -17,13 +15,185 @@
|
|
|
17
15
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
18
16
|
# -----------------------------------------------------------------------------.
|
|
19
17
|
"""Include functions helping for DISDRODB product manipulations."""
|
|
20
|
-
|
|
21
18
|
import numpy as np
|
|
19
|
+
import xarray as xr
|
|
22
20
|
|
|
23
|
-
from disdrodb.constants import DIAMETER_DIMENSION
|
|
21
|
+
from disdrodb.constants import DIAMETER_DIMENSION, VELOCITY_DIMENSION
|
|
24
22
|
from disdrodb.utils.xarray import unstack_datarray_dimension
|
|
25
23
|
|
|
26
24
|
|
|
25
|
+
def define_diameter_datarray(bounds):
|
|
26
|
+
"""Define diameter DataArray."""
|
|
27
|
+
diameters_bin_lower = bounds[:-1]
|
|
28
|
+
diameters_bin_upper = bounds[1:]
|
|
29
|
+
diameters_bin_width = diameters_bin_upper - diameters_bin_lower
|
|
30
|
+
diameters_bin_center = diameters_bin_lower + diameters_bin_width / 2
|
|
31
|
+
da = xr.DataArray(
|
|
32
|
+
diameters_bin_center,
|
|
33
|
+
dims="diameter_bin_center",
|
|
34
|
+
coords={
|
|
35
|
+
"diameter_bin_width": ("diameter_bin_center", diameters_bin_width),
|
|
36
|
+
"diameter_bin_lower": ("diameter_bin_center", diameters_bin_lower),
|
|
37
|
+
"diameter_bin_upper": ("diameter_bin_center", diameters_bin_upper),
|
|
38
|
+
"diameter_bin_center": ("diameter_bin_center", diameters_bin_center),
|
|
39
|
+
},
|
|
40
|
+
)
|
|
41
|
+
return da
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def define_velocity_datarray(bounds):
|
|
45
|
+
"""Define velocity DataArray."""
|
|
46
|
+
velocitys_bin_lower = bounds[:-1]
|
|
47
|
+
velocitys_bin_upper = bounds[1:]
|
|
48
|
+
velocitys_bin_width = velocitys_bin_upper - velocitys_bin_lower
|
|
49
|
+
velocitys_bin_center = velocitys_bin_lower + velocitys_bin_width / 2
|
|
50
|
+
da = xr.DataArray(
|
|
51
|
+
velocitys_bin_center,
|
|
52
|
+
dims="velocity_bin_center",
|
|
53
|
+
coords={
|
|
54
|
+
"velocity_bin_width": ("velocity_bin_center", velocitys_bin_width),
|
|
55
|
+
"velocity_bin_lower": ("velocity_bin_center", velocitys_bin_lower),
|
|
56
|
+
"velocity_bin_upper": ("velocity_bin_center", velocitys_bin_upper),
|
|
57
|
+
"velocity_bin_center": ("velocity_bin_center", velocitys_bin_center),
|
|
58
|
+
},
|
|
59
|
+
)
|
|
60
|
+
return da
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def define_diameter_array(diameter_min=0, diameter_max=10, diameter_spacing=0.05):
|
|
64
|
+
"""
|
|
65
|
+
Define an array of diameters and their corresponding bin properties.
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
diameter_min : float, optional
|
|
70
|
+
The minimum diameter value. The default value is 0 mm.
|
|
71
|
+
diameter_max : float, optional
|
|
72
|
+
The maximum diameter value. The default value is 10 mm.
|
|
73
|
+
diameter_spacing : float, optional
|
|
74
|
+
The spacing between diameter values. The default value is 0.05 mm.
|
|
75
|
+
|
|
76
|
+
Returns
|
|
77
|
+
-------
|
|
78
|
+
xr.DataArray
|
|
79
|
+
A DataArray containing the center of each diameter bin, with coordinates for
|
|
80
|
+
the bin width, lower bound, upper bound, and center.
|
|
81
|
+
|
|
82
|
+
"""
|
|
83
|
+
diameters_bounds = np.arange(diameter_min, diameter_max + diameter_spacing / 2, step=diameter_spacing)
|
|
84
|
+
return define_diameter_datarray(diameters_bounds)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def define_velocity_array(velocity_min=0, velocity_max=10, velocity_spacing=0.05):
|
|
88
|
+
"""
|
|
89
|
+
Define an array of velocities and their corresponding bin properties.
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
velocity_min : float, optional
|
|
94
|
+
The minimum velocity value. The default value is 0 mm.
|
|
95
|
+
velocity_max : float, optional
|
|
96
|
+
The maximum velocity value. The default value is 10 mm.
|
|
97
|
+
velocity_spacing : float, optional
|
|
98
|
+
The spacing between velocity values. The default value is 0.05 mm.
|
|
99
|
+
|
|
100
|
+
Returns
|
|
101
|
+
-------
|
|
102
|
+
xr.DataArray
|
|
103
|
+
A DataArray containing the center of each velocity bin, with coordinates for
|
|
104
|
+
the bin width, lower bound, upper bound, and center.
|
|
105
|
+
|
|
106
|
+
"""
|
|
107
|
+
velocitys_bounds = np.arange(velocity_min, velocity_max + velocity_spacing / 2, step=velocity_spacing)
|
|
108
|
+
return define_velocity_datarray(velocitys_bounds)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def filter_diameter_bins(ds, minimum_diameter=None, maximum_diameter=None):
|
|
112
|
+
"""
|
|
113
|
+
Filter the dataset to include only diameter bins within specified bounds.
|
|
114
|
+
|
|
115
|
+
Parameters
|
|
116
|
+
----------
|
|
117
|
+
ds : xarray.Dataset
|
|
118
|
+
The dataset containing diameter bin data.
|
|
119
|
+
minimum_diameter : float, optional
|
|
120
|
+
The minimum diameter to be included, in millimeters.
|
|
121
|
+
Defaults to the minimum value in `ds["diameter_bin_lower"]`.
|
|
122
|
+
maximum_diameter : float, optional
|
|
123
|
+
The maximum diameter to be included, in millimeters.
|
|
124
|
+
Defaults to the maximum value in `ds["diameter_bin_upper"]`.
|
|
125
|
+
|
|
126
|
+
Returns
|
|
127
|
+
-------
|
|
128
|
+
xarray.Dataset
|
|
129
|
+
The filtered dataset containing only the specified diameter bins.
|
|
130
|
+
"""
|
|
131
|
+
# Put data into memory
|
|
132
|
+
ds["diameter_bin_lower"] = ds["diameter_bin_lower"].compute()
|
|
133
|
+
ds["diameter_bin_upper"] = ds["diameter_bin_upper"].compute()
|
|
134
|
+
|
|
135
|
+
# Initialize default arguments
|
|
136
|
+
if minimum_diameter is None:
|
|
137
|
+
minimum_diameter = ds["diameter_bin_lower"].min().item()
|
|
138
|
+
if maximum_diameter is None:
|
|
139
|
+
maximum_diameter = ds["diameter_bin_upper"].max().item()
|
|
140
|
+
|
|
141
|
+
# Select bins which overlap the specified diameters
|
|
142
|
+
valid_indices = np.logical_and(
|
|
143
|
+
ds["diameter_bin_upper"] > minimum_diameter,
|
|
144
|
+
ds["diameter_bin_lower"] < maximum_diameter,
|
|
145
|
+
)
|
|
146
|
+
ds = ds.isel({DIAMETER_DIMENSION: valid_indices})
|
|
147
|
+
|
|
148
|
+
if ds.sizes[DIAMETER_DIMENSION] == 0:
|
|
149
|
+
msg = f"Filtering using {minimum_diameter=} removes all diameter bins."
|
|
150
|
+
raise ValueError(msg)
|
|
151
|
+
return ds
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def filter_velocity_bins(ds, minimum_velocity=None, maximum_velocity=None):
|
|
155
|
+
"""
|
|
156
|
+
Filter the dataset to include only velocity bins within specified bounds.
|
|
157
|
+
|
|
158
|
+
Parameters
|
|
159
|
+
----------
|
|
160
|
+
ds : xarray.Dataset
|
|
161
|
+
The dataset containing velocity bin data.
|
|
162
|
+
minimum_velocity : float, optional
|
|
163
|
+
The minimum velocity to include in the filter, in meters per second.
|
|
164
|
+
Defaults to the minimum value in `ds["velocity_bin_lower"]`.
|
|
165
|
+
maximum_velocity : float, optional
|
|
166
|
+
The maximum velocity to include in the filter, in meters per second.
|
|
167
|
+
Defaults to the maximum value in `ds["velocity_bin_upper"]`.
|
|
168
|
+
|
|
169
|
+
Returns
|
|
170
|
+
-------
|
|
171
|
+
xarray.Dataset
|
|
172
|
+
The filtered dataset containing only the specified velocity bins.
|
|
173
|
+
"""
|
|
174
|
+
# Put data into memory
|
|
175
|
+
ds["velocity_bin_lower"] = ds["velocity_bin_lower"].compute()
|
|
176
|
+
ds["velocity_bin_upper"] = ds["velocity_bin_upper"].compute()
|
|
177
|
+
|
|
178
|
+
# Initialize default arguments
|
|
179
|
+
if minimum_velocity is None:
|
|
180
|
+
minimum_velocity = ds["velocity_bin_lower"].min().item()
|
|
181
|
+
if maximum_velocity is None:
|
|
182
|
+
maximum_velocity = ds["velocity_bin_upper"].max().item()
|
|
183
|
+
|
|
184
|
+
# Select bins which overlap the specified velocities
|
|
185
|
+
valid_indices = np.logical_and(
|
|
186
|
+
ds["velocity_bin_upper"] > minimum_velocity,
|
|
187
|
+
ds["velocity_bin_lower"] < maximum_velocity,
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
ds = ds.isel({VELOCITY_DIMENSION: valid_indices})
|
|
191
|
+
if ds.sizes[VELOCITY_DIMENSION] == 0:
|
|
192
|
+
msg = f"Filtering using {minimum_velocity=} removes all velocity bins."
|
|
193
|
+
raise ValueError(msg)
|
|
194
|
+
return ds
|
|
195
|
+
|
|
196
|
+
|
|
27
197
|
def get_diameter_bin_edges(ds):
|
|
28
198
|
"""Retrieve diameter bin edges."""
|
|
29
199
|
bin_edges = np.append(ds["diameter_bin_lower"].to_numpy(), ds["diameter_bin_upper"].to_numpy()[-1])
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# -----------------------------------------------------------------------------.
|
|
2
|
+
# Copyright (c) 2021-2026 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
|
+
"""Definition of pydantic validation custom class."""
|
|
18
|
+
from pydantic import BaseModel, ConfigDict, ValidationError
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def format_validation_error(validation_error: Exception) -> str:
|
|
22
|
+
"""Format a Pydantic ValidationError for better readability."""
|
|
23
|
+
if not isinstance(validation_error, ValidationError):
|
|
24
|
+
return str(validation_error)
|
|
25
|
+
|
|
26
|
+
def _shorten(value, max_len=200):
|
|
27
|
+
"""Safely truncate long inputs."""
|
|
28
|
+
text = repr(value)
|
|
29
|
+
if len(text) > max_len:
|
|
30
|
+
return text[: max_len - 5] + " ...]"
|
|
31
|
+
return text
|
|
32
|
+
|
|
33
|
+
model_name_attr = getattr(validation_error, "title", None)
|
|
34
|
+
model_name = model_name_attr() if callable(model_name_attr) else model_name_attr or "UnknownModel"
|
|
35
|
+
|
|
36
|
+
formatted_errors = [f"Validation errors in {model_name}:"]
|
|
37
|
+
|
|
38
|
+
for err in validation_error.errors():
|
|
39
|
+
path = ".".join(str(loc) for loc in err["loc"]) or "<model root>"
|
|
40
|
+
msg = err["msg"]
|
|
41
|
+
err_type = err["type"]
|
|
42
|
+
|
|
43
|
+
# Handles both "Value error, ..." and "Value error: ..."
|
|
44
|
+
if msg.lower().startswith("value error"):
|
|
45
|
+
msg = msg.split(",", 1)[-1] if "," in msg else msg.split(":", 1)[-1]
|
|
46
|
+
msg = msg.strip()
|
|
47
|
+
|
|
48
|
+
# Model-level (root) errors (raise in after or before)
|
|
49
|
+
if path == "<model root>":
|
|
50
|
+
formatted = f" • {msg}"
|
|
51
|
+
elif err_type == "missing":
|
|
52
|
+
formatted = f" • Missing field '{path}': {msg}"
|
|
53
|
+
elif "input" in err:
|
|
54
|
+
formatted = f" • Field '{path}': {msg} (got: {_shorten(err['input'])})"
|
|
55
|
+
else:
|
|
56
|
+
formatted = f" • Field '{path}': {msg}"
|
|
57
|
+
|
|
58
|
+
formatted_errors.append(formatted)
|
|
59
|
+
|
|
60
|
+
return "\n".join(formatted_errors)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class CustomBaseModel(BaseModel):
|
|
64
|
+
"""Custom pydantic BaseModel.
|
|
65
|
+
|
|
66
|
+
Forbid extra keys.
|
|
67
|
+
Hide URLs in error message.
|
|
68
|
+
Simplify error message.
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
model_config = ConfigDict(extra="forbid", hide_error_urls=True)
|
|
72
|
+
|
|
73
|
+
# Override the standard ValidationError print behavior
|
|
74
|
+
def __init__(self, **data):
|
|
75
|
+
try:
|
|
76
|
+
super().__init__(**data)
|
|
77
|
+
except ValidationError as e:
|
|
78
|
+
formatted = format_validation_error(e)
|
|
79
|
+
# Raise a new simplified exception
|
|
80
|
+
raise ValueError(formatted) from None
|
disdrodb/utils/routines.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
1
|
# -----------------------------------------------------------------------------.
|
|
4
|
-
# Copyright (c) 2021-
|
|
2
|
+
# Copyright (c) 2021-2026 DISDRODB developers
|
|
5
3
|
#
|
|
6
4
|
# This program is free software: you can redistribute it and/or modify
|
|
7
5
|
# it under the terms of the GNU General Public License as published by
|
disdrodb/utils/subsetting.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# -----------------------------------------------------------------------------.
|
|
2
|
-
# Copyright (c) 2021-
|
|
2
|
+
# Copyright (c) 2021-2026 DISDRODB developers
|
|
3
3
|
#
|
|
4
4
|
# This program is free software: you can redistribute it and/or modify
|
|
5
5
|
# it under the terms of the GNU General Public License as published by
|
disdrodb/utils/time.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# -----------------------------------------------------------------------------.
|
|
2
|
-
# Copyright (c) 2021-
|
|
2
|
+
# Copyright (c) 2021-2026 DISDRODB developers
|
|
3
3
|
#
|
|
4
4
|
# This program is free software: you can redistribute it and/or modify
|
|
5
5
|
# it under the terms of the GNU General Public License as published by
|
|
@@ -238,7 +238,7 @@ def regularize_dataset(
|
|
|
238
238
|
start_time=None,
|
|
239
239
|
end_time=None,
|
|
240
240
|
):
|
|
241
|
-
"""Regularize a
|
|
241
|
+
"""Regularize a xarray object across time dimension with uniform resolution.
|
|
242
242
|
|
|
243
243
|
Parameters
|
|
244
244
|
----------
|
|
@@ -274,6 +274,7 @@ def regularize_dataset(
|
|
|
274
274
|
start_time = start
|
|
275
275
|
if end_time is None:
|
|
276
276
|
end_time = end
|
|
277
|
+
xr_obj = xr_obj.sel({time_dim: slice(start_time, end_time)})
|
|
277
278
|
|
|
278
279
|
# Define new time index
|
|
279
280
|
new_time_index = pd.date_range(
|
disdrodb/utils/warnings.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
1
|
# -----------------------------------------------------------------------------.
|
|
4
|
-
# Copyright (c) 2021-
|
|
2
|
+
# Copyright (c) 2021-2026 DISDRODB developers
|
|
5
3
|
#
|
|
6
4
|
# This program is free software: you can redistribute it and/or modify
|
|
7
5
|
# it under the terms of the GNU General Public License as published by
|
disdrodb/utils/writer.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
1
|
# -----------------------------------------------------------------------------.
|
|
4
|
-
# Copyright (c) 2021-
|
|
2
|
+
# Copyright (c) 2021-2026 DISDRODB developers
|
|
5
3
|
#
|
|
6
4
|
# This program is free software: you can redistribute it and/or modify
|
|
7
5
|
# it under the terms of the GNU General Public License as published by
|
disdrodb/utils/xarray.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
1
|
# -----------------------------------------------------------------------------.
|
|
4
|
-
# Copyright (c) 2021-
|
|
2
|
+
# Copyright (c) 2021-2026 DISDRODB developers
|
|
5
3
|
#
|
|
6
4
|
# This program is free software: you can redistribute it and/or modify
|
|
7
5
|
# it under the terms of the GNU General Public License as published by
|
|
@@ -99,6 +97,35 @@ def xr_get_last_valid_idx(da_condition, dim, fill_value=None):
|
|
|
99
97
|
return last_idx
|
|
100
98
|
|
|
101
99
|
|
|
100
|
+
def _np_remap_numeric_array(arr, remapping_dict, fill_value=np.nan):
|
|
101
|
+
# Define conditions
|
|
102
|
+
conditions = [arr == i for i in remapping_dict]
|
|
103
|
+
# Define choices corresponding to conditions
|
|
104
|
+
choices = remapping_dict.values()
|
|
105
|
+
# Apply np.select to transform the array
|
|
106
|
+
return np.select(conditions, choices, default=fill_value)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _dask_remap_numeric_array(arr, remapping_dict, fill_value=np.nan):
|
|
110
|
+
import dask.array
|
|
111
|
+
|
|
112
|
+
return dask.array.map_blocks(_np_remap_numeric_array, arr, remapping_dict, fill_value, dtype=arr.dtype)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def remap_numeric_array(arr, remapping_dict, fill_value=np.nan):
|
|
116
|
+
"""Remap the values of a numeric array."""
|
|
117
|
+
if hasattr(arr, "chunks"):
|
|
118
|
+
return _dask_remap_numeric_array(arr, remapping_dict, fill_value=fill_value)
|
|
119
|
+
return _np_remap_numeric_array(arr, remapping_dict, fill_value=fill_value)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def xr_remap_numeric_array(da, remapping_dict, fill_value=np.nan):
|
|
123
|
+
"""Remap values of a xr.DataArray."""
|
|
124
|
+
output = da.copy()
|
|
125
|
+
output.data = remap_numeric_array(da.data, remapping_dict, fill_value=fill_value)
|
|
126
|
+
return output
|
|
127
|
+
|
|
128
|
+
|
|
102
129
|
####-------------------------------------------------------------------
|
|
103
130
|
#### Unstacking dimension
|
|
104
131
|
|
disdrodb/utils/yaml.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
1
|
# -----------------------------------------------------------------------------.
|
|
4
|
-
# Copyright (c) 2021-
|
|
2
|
+
# Copyright (c) 2021-2026 DISDRODB developers
|
|
5
3
|
#
|
|
6
4
|
# This program is free software: you can redistribute it and/or modify
|
|
7
5
|
# it under the terms of the GNU General Public License as published by
|
disdrodb/viz/__init__.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# -----------------------------------------------------------------------------.
|
|
2
|
-
# Copyright (c) 2021-
|
|
2
|
+
# Copyright (c) 2021-2026 DISDRODB developers
|
|
3
3
|
#
|
|
4
4
|
# This program is free software: you can redistribute it and/or modify
|
|
5
5
|
# it under the terms of the GNU General Public License as published by
|
disdrodb/viz/plots.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# -----------------------------------------------------------------------------.
|
|
2
|
-
# Copyright (c) 2021-
|
|
2
|
+
# Copyright (c) 2021-2026 DISDRODB developers
|
|
3
3
|
#
|
|
4
4
|
# This program is free software: you can redistribute it and/or modify
|
|
5
5
|
# it under the terms of the GNU General Public License as published by
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"""DISDRODB Plotting Tools."""
|
|
18
18
|
import matplotlib.pyplot as plt
|
|
19
19
|
import numpy as np
|
|
20
|
+
import pandas as pd
|
|
20
21
|
import psutil
|
|
21
22
|
import xarray as xr
|
|
22
23
|
from matplotlib.colors import LogNorm, Normalize
|
|
@@ -24,6 +25,7 @@ from matplotlib.gridspec import GridSpec
|
|
|
24
25
|
|
|
25
26
|
from disdrodb.constants import DIAMETER_DIMENSION, VELOCITY_DIMENSION
|
|
26
27
|
from disdrodb.l2.empirical_dsd import get_drop_average_velocity
|
|
28
|
+
from disdrodb.utils.time import ensure_sample_interval_in_seconds, regularize_dataset
|
|
27
29
|
|
|
28
30
|
####-------------------------------------------------------------------------------------------------------
|
|
29
31
|
#### N(D) visualizations
|
|
@@ -45,44 +47,216 @@ def _single_plot_nd_distribution(drop_number_concentration, diameter, diameter_b
|
|
|
45
47
|
return ax
|
|
46
48
|
|
|
47
49
|
|
|
48
|
-
def
|
|
50
|
+
def _check_has_diameter_dims(da):
|
|
51
|
+
if DIAMETER_DIMENSION not in da.dims:
|
|
52
|
+
raise ValueError(f"The DataArray must have dimension '{DIAMETER_DIMENSION}'.")
|
|
53
|
+
if "diameter_bin_width" not in da.coords:
|
|
54
|
+
raise ValueError("The DataArray must have coordinate 'diameter_bin_width'.")
|
|
55
|
+
return da
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _get_nd_variable(xr_obj, variable):
|
|
59
|
+
if not isinstance(xr_obj, (xr.Dataset, xr.DataArray)):
|
|
60
|
+
raise TypeError("Expecting xarray object as input.")
|
|
61
|
+
if isinstance(xr_obj, xr.Dataset):
|
|
62
|
+
if variable not in xr_obj:
|
|
63
|
+
raise ValueError(f"The dataset do not include {variable=}.")
|
|
64
|
+
xr_obj = xr_obj[variable]
|
|
65
|
+
if VELOCITY_DIMENSION in xr_obj.dims:
|
|
66
|
+
raise ValueError("N(D) must no have the velocity dimension.")
|
|
67
|
+
xr_obj = _check_has_diameter_dims(xr_obj)
|
|
68
|
+
return xr_obj
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def plot_nd(xr_obj, variable="drop_number_concentration", cmap=None, norm=None):
|
|
49
72
|
"""Plot drop number concentration N(D) timeseries."""
|
|
50
|
-
|
|
51
|
-
if var not in ds:
|
|
52
|
-
raise ValueError(f"{var} is not a xarray Dataset variable!")
|
|
73
|
+
da_nd = _get_nd_variable(xr_obj, variable=variable)
|
|
53
74
|
|
|
54
75
|
# Check only time and diameter dimensions are specified
|
|
55
|
-
if "time" not in
|
|
76
|
+
if "time" not in da_nd.dims:
|
|
56
77
|
ax = _single_plot_nd_distribution(
|
|
57
|
-
drop_number_concentration=
|
|
58
|
-
diameter=
|
|
59
|
-
diameter_bin_width=
|
|
78
|
+
drop_number_concentration=da_nd.isel(velocity_method=0, missing_dims="ignore"),
|
|
79
|
+
diameter=xr_obj["diameter_bin_center"],
|
|
80
|
+
diameter_bin_width=xr_obj["diameter_bin_width"],
|
|
60
81
|
)
|
|
61
82
|
return ax
|
|
62
83
|
|
|
63
|
-
# Select N(D)
|
|
64
|
-
ds_var = ds[[var]].compute()
|
|
65
|
-
|
|
66
84
|
# Regularize input
|
|
67
|
-
|
|
85
|
+
da_nd = da_nd.compute()
|
|
86
|
+
da_nd = da_nd.disdrodb.regularize()
|
|
68
87
|
|
|
69
88
|
# Set 0 values to np.nan
|
|
70
|
-
|
|
89
|
+
da_nd = da_nd.where(da_nd > 0)
|
|
71
90
|
|
|
72
91
|
# Define cmap an norm
|
|
73
92
|
if cmap is None:
|
|
74
93
|
cmap = plt.get_cmap("Spectral_r").copy()
|
|
75
94
|
|
|
76
|
-
vmin =
|
|
95
|
+
vmin = da_nd.min().item()
|
|
77
96
|
norm = LogNorm(vmin, None) if norm is None else norm
|
|
78
97
|
|
|
79
98
|
# Plot N(D)
|
|
80
|
-
|
|
81
|
-
p.
|
|
99
|
+
cbar_kwargs = {"label": "N(D) [m-3 mm-1]"}
|
|
100
|
+
p = da_nd.plot.pcolormesh(x="time", norm=norm, cmap=cmap, extend="max", cbar_kwargs=cbar_kwargs)
|
|
101
|
+
p.axes.set_title("Drop number concentration N(D)")
|
|
82
102
|
p.axes.set_ylabel("Drop diameter (mm)")
|
|
83
103
|
return p
|
|
84
104
|
|
|
85
105
|
|
|
106
|
+
def plot_nd_quicklook(
|
|
107
|
+
ds,
|
|
108
|
+
# Plot layout
|
|
109
|
+
hours_per_slice=5,
|
|
110
|
+
max_rows=6,
|
|
111
|
+
aligned=True,
|
|
112
|
+
verbose=True,
|
|
113
|
+
# Spectrum options
|
|
114
|
+
variable="drop_number_concentration",
|
|
115
|
+
cbar_label="N(D) [# m⁻³ mm⁻¹]",
|
|
116
|
+
cmap=None,
|
|
117
|
+
norm=None,
|
|
118
|
+
d_lim=(0.3, 5),
|
|
119
|
+
# R options
|
|
120
|
+
add_r=True,
|
|
121
|
+
r_lim=(0.1, 50),
|
|
122
|
+
r_scale="log",
|
|
123
|
+
r_color="tab:blue",
|
|
124
|
+
r_linewidth=1.2,
|
|
125
|
+
):
|
|
126
|
+
"""Display multi-rows quicklook of N(D)."""
|
|
127
|
+
# Colormap & normalization
|
|
128
|
+
if cmap is None:
|
|
129
|
+
cmap = plt.get_cmap("Spectral_r").copy()
|
|
130
|
+
cmap.set_under("none")
|
|
131
|
+
if norm is None:
|
|
132
|
+
norm = LogNorm(vmin=1, vmax=10_000)
|
|
133
|
+
|
|
134
|
+
# ---------------------------
|
|
135
|
+
# Define temporal slices
|
|
136
|
+
# - Align to closest <hours_per_slice> time
|
|
137
|
+
# - For hours_per_slice=3 --> 00, 03, 06, ...
|
|
138
|
+
time = ds["time"].to_index()
|
|
139
|
+
t_start = time[0]
|
|
140
|
+
t_end = time[-1]
|
|
141
|
+
if aligned:
|
|
142
|
+
aligned_start = t_start.floor(f"{hours_per_slice}h")
|
|
143
|
+
aligned_end = t_end.ceil(f"{hours_per_slice}h")
|
|
144
|
+
# Create time bins
|
|
145
|
+
time_bins = pd.date_range(
|
|
146
|
+
start=aligned_start,
|
|
147
|
+
end=aligned_end,
|
|
148
|
+
freq=f"{hours_per_slice}h",
|
|
149
|
+
)
|
|
150
|
+
else:
|
|
151
|
+
# Create time bins
|
|
152
|
+
time_bins = pd.date_range(
|
|
153
|
+
start=t_start,
|
|
154
|
+
end=t_end + pd.Timedelta(f"{hours_per_slice}h"),
|
|
155
|
+
freq=f"{hours_per_slice}h",
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
n_total_slices = len(time_bins) - 1
|
|
159
|
+
n_slices = min(n_total_slices, max_rows)
|
|
160
|
+
|
|
161
|
+
# Print info on event quicklook
|
|
162
|
+
if verbose:
|
|
163
|
+
print("=== N(D) Event Quicklook ===")
|
|
164
|
+
print(f"Dataset time span : {t_start} → {t_end}")
|
|
165
|
+
print(f"Slice length : {hours_per_slice} h")
|
|
166
|
+
print(f"Plotted slices : {n_slices}/{n_total_slices}")
|
|
167
|
+
if n_total_slices > max_rows:
|
|
168
|
+
last_plotted_end = time_bins[max_rows]
|
|
169
|
+
print(f"Unplotted period : {last_plotted_end} → {aligned_end}")
|
|
170
|
+
|
|
171
|
+
# Regularize dataset to match bin start_time and end_time
|
|
172
|
+
sample_interval = ensure_sample_interval_in_seconds(ds["sample_interval"].to_numpy()).item()
|
|
173
|
+
ds = regularize_dataset(ds, freq=f"{sample_interval}s", start_time=time_bins[0], end_time=time_bins[-1])
|
|
174
|
+
|
|
175
|
+
# Define figure
|
|
176
|
+
fig, axes = plt.subplots(
|
|
177
|
+
nrows=n_slices,
|
|
178
|
+
ncols=1,
|
|
179
|
+
figsize=(14, 2.8 * n_slices),
|
|
180
|
+
sharex=False,
|
|
181
|
+
constrained_layout=True,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
if n_slices == 1:
|
|
185
|
+
axes = [axes]
|
|
186
|
+
|
|
187
|
+
# Plot each slice
|
|
188
|
+
for i in range(n_slices):
|
|
189
|
+
# Extract dataset slice
|
|
190
|
+
t0 = time_bins[i]
|
|
191
|
+
t1 = time_bins[i + 1]
|
|
192
|
+
ds_slice = ds.sel(time=slice(t0, t1))
|
|
193
|
+
da_nd = ds_slice[variable]
|
|
194
|
+
|
|
195
|
+
# Define plot ax
|
|
196
|
+
ax = axes[i]
|
|
197
|
+
|
|
198
|
+
# Plot N(D)
|
|
199
|
+
p = da_nd.plot.pcolormesh(
|
|
200
|
+
ax=ax,
|
|
201
|
+
x="time",
|
|
202
|
+
y="diameter_bin_center",
|
|
203
|
+
norm=norm,
|
|
204
|
+
cmap=cmap,
|
|
205
|
+
shading="auto",
|
|
206
|
+
add_colorbar=False,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# Overlay Dm
|
|
210
|
+
ds_slice["Dm"].plot(
|
|
211
|
+
ax=ax,
|
|
212
|
+
x="time",
|
|
213
|
+
color="black",
|
|
214
|
+
linestyle="--",
|
|
215
|
+
linewidth=1.2,
|
|
216
|
+
label="Dm",
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
# Add axis labels and title
|
|
220
|
+
ax.set_xlabel("")
|
|
221
|
+
ax.set_ylabel("Diameter [mm]")
|
|
222
|
+
ax.set_title(f"{t0:%H:%M} - {t1:%H:%M} UTC")
|
|
223
|
+
|
|
224
|
+
if i == 0:
|
|
225
|
+
ax.legend(loc="upper right")
|
|
226
|
+
|
|
227
|
+
# Add rain rate on secondary axis
|
|
228
|
+
if add_r:
|
|
229
|
+
ax_r = ax.twinx()
|
|
230
|
+
ds_slice["R"].plot(
|
|
231
|
+
ax=ax_r,
|
|
232
|
+
x="time",
|
|
233
|
+
color=r_color,
|
|
234
|
+
linewidth=r_linewidth,
|
|
235
|
+
label="R",
|
|
236
|
+
)
|
|
237
|
+
ax_r.set_ylim(r_lim)
|
|
238
|
+
ax_r.set_yscale(r_scale)
|
|
239
|
+
ax_r.set_ylabel("Rain rate [mm h$^{-1}$]", color="tab:blue")
|
|
240
|
+
ax_r.tick_params(axis="y", labelcolor="tab:blue")
|
|
241
|
+
ax_r.set_title("")
|
|
242
|
+
|
|
243
|
+
ax.set_ylim(*d_lim)
|
|
244
|
+
|
|
245
|
+
axes[-1].set_xlabel("Time (UTC)")
|
|
246
|
+
# ---------------------------
|
|
247
|
+
# Shared colorbar
|
|
248
|
+
# ---------------------------
|
|
249
|
+
cbar = fig.colorbar(
|
|
250
|
+
p,
|
|
251
|
+
ax=axes,
|
|
252
|
+
orientation="horizontal",
|
|
253
|
+
pad=0.02,
|
|
254
|
+
fraction=0.03,
|
|
255
|
+
extend="max",
|
|
256
|
+
)
|
|
257
|
+
cbar.set_label(cbar_label)
|
|
258
|
+
|
|
259
|
+
|
|
86
260
|
####-------------------------------------------------------------------------------------------------------
|
|
87
261
|
#### Spectra visualizations
|
|
88
262
|
|
|
@@ -590,7 +764,7 @@ def compute_dense_lines(
|
|
|
590
764
|
if len(other_dims) == 1:
|
|
591
765
|
arr = da.transpose(*other_dims, x_dim).to_numpy()
|
|
592
766
|
else:
|
|
593
|
-
arr = da.stack({"sample": other_dims}).transpose("sample", x_dim).to_numpy()
|
|
767
|
+
arr = da.stack({"sample": other_dims}).transpose("sample", x_dim).to_numpy() # noqa PD013
|
|
594
768
|
|
|
595
769
|
# Define y bins center
|
|
596
770
|
y_center = (y_bins[0:-1] + y_bins[1:]) / 2
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: disdrodb
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: disdrodb provides tools to download, standardize, share and analyze global disdrometer data.
|
|
5
5
|
Author: Gionata Ghiggi
|
|
6
6
|
Project-URL: homepage, https://github.com/ltelab/disdrodb
|
|
@@ -10,7 +10,7 @@ Project-URL: tracker, https://github.com/ltelab/disdrodb/issues
|
|
|
10
10
|
Project-URL: documentation, https://disdrodb.readthedocs.io
|
|
11
11
|
Project-URL: changelog, https://github.com/ltelab/disdrodb/blob/main/CHANGELOG.md
|
|
12
12
|
Keywords: python,disdrometer,parsivel,drop size distribution
|
|
13
|
-
Classifier: Development Status ::
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
14
|
Classifier: Intended Audience :: Developers
|
|
15
15
|
Classifier: Programming Language :: Python :: 3
|
|
16
16
|
Classifier: Operating System :: Unix
|