disdrodb 0.0.21__py3-none-any.whl → 0.1.1__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 +306 -270
- 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 +46 -51
- 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 +84 -65
- disdrodb/l0/configs/{Thies_LPM → LPM}/l0b_encodings.yml +50 -9
- disdrodb/l0/configs/{Thies_LPM → LPM}/raw_data_format.yml +285 -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 +23 -21
- 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 +28 -26
- disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/l0b_encodings.yml +20 -20
- disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/raw_data_format.yml +107 -107
- disdrodb/l0/configs/PWS100/bins_diameter.yml +173 -0
- disdrodb/l0/configs/PWS100/bins_velocity.yml +173 -0
- disdrodb/l0/configs/PWS100/l0a_encodings.yml +19 -0
- disdrodb/l0/configs/PWS100/l0b_cf_attrs.yml +76 -0
- disdrodb/l0/configs/PWS100/l0b_encodings.yml +176 -0
- disdrodb/l0/configs/PWS100/raw_data_format.yml +182 -0
- 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 +46 -50
- 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 +96 -174
- 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 +236 -0
- disdrodb/l0/readers/LPM/BRAZIL/CHUVA_LPM.py +185 -0
- disdrodb/l0/readers/LPM/BRAZIL/GOAMAZON_LPM.py +185 -0
- disdrodb/l0/readers/LPM/ITALY/GID_LPM.py +195 -0
- disdrodb/l0/readers/LPM/ITALY/GID_LPM_W.py +210 -0
- disdrodb/l0/readers/{BRAZIL/GOAMAZON_LPM.py → LPM/KIT/CHWALA.py} +97 -76
- disdrodb/l0/readers/LPM/SLOVENIA/ARSO.py +197 -0
- disdrodb/l0/readers/LPM/SLOVENIA/CRNI_VRH.py +197 -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/KIT/BURKINA_FASO.py +133 -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/PARSIVEL/SLOVENIA/UL_FGG.py +121 -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/ENPC_PARSIVEL2.py +189 -0
- 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/PWS100/FRANCE/ENPC_PWS100.py +150 -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/RD80/NOAA/PSL_RD80.py +274 -0
- 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 +77 -238
- disdrodb/l0/template_tools.py +105 -110
- disdrodb/l1/__init__.py +17 -0
- disdrodb/l1/beard_model.py +716 -0
- disdrodb/l1/encoding_attrs.py +635 -0
- disdrodb/l1/fall_velocity.py +260 -0
- disdrodb/l1/filters.py +192 -0
- disdrodb/l1/processing.py +202 -0
- disdrodb/l1/resampling.py +236 -0
- disdrodb/l1/routines.py +358 -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 +1833 -0
- disdrodb/l2/event.py +388 -0
- disdrodb/l2/processing.py +528 -0
- disdrodb/l2/processing_options.py +213 -0
- disdrodb/l2/routines.py +868 -0
- disdrodb/metadata/__init__.py +9 -2
- disdrodb/metadata/checks.py +180 -124
- disdrodb/metadata/download.py +81 -0
- disdrodb/metadata/geolocation.py +146 -0
- disdrodb/metadata/info.py +20 -13
- disdrodb/metadata/manipulation.py +3 -3
- disdrodb/metadata/reader.py +59 -8
- disdrodb/metadata/search.py +77 -144
- disdrodb/metadata/standards.py +83 -80
- disdrodb/metadata/writer.py +10 -16
- disdrodb/psd/__init__.py +38 -0
- disdrodb/psd/fitting.py +2146 -0
- disdrodb/psd/models.py +774 -0
- disdrodb/routines.py +1412 -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/dataframe.py +342 -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.1.dist-info/METADATA +294 -0
- disdrodb-0.1.1.dist-info/RECORD +232 -0
- {disdrodb-0.0.21.dist-info → disdrodb-0.1.1.dist-info}/WHEEL +1 -1
- disdrodb-0.1.1.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_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.1.dist-info/licenses}/LICENSE +0 -0
- {disdrodb-0.0.21.dist-info → disdrodb-0.1.1.dist-info}/top_level.txt +0 -0
disdrodb/l2/event.py
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
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 event definition."""
|
|
18
|
+
import dask
|
|
19
|
+
import numpy as np
|
|
20
|
+
import pandas as pd
|
|
21
|
+
import xarray as xr
|
|
22
|
+
|
|
23
|
+
from disdrodb.api.info import get_start_end_time_from_filepaths
|
|
24
|
+
from disdrodb.utils.time import acronym_to_seconds, ensure_sorted_by_time
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dask.delayed
|
|
28
|
+
def _delayed_open_dataset(filepath):
|
|
29
|
+
with dask.config.set(scheduler="synchronous"):
|
|
30
|
+
ds = xr.open_dataset(filepath, chunks={}, autoclose=True, decode_timedelta=False, cache=False)
|
|
31
|
+
return ds
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def identify_events(
|
|
35
|
+
filepaths,
|
|
36
|
+
parallel=False,
|
|
37
|
+
min_n_drops=5,
|
|
38
|
+
neighbor_min_size=2,
|
|
39
|
+
neighbor_time_interval="5MIN",
|
|
40
|
+
intra_event_max_time_gap="6H",
|
|
41
|
+
event_min_duration="5MIN",
|
|
42
|
+
event_min_size=3,
|
|
43
|
+
):
|
|
44
|
+
"""Return a list of rainy events.
|
|
45
|
+
|
|
46
|
+
Rainy timesteps are defined when N > min_n_drops.
|
|
47
|
+
Any rainy isolated timesteps (based on neighborhood criteria) is removed.
|
|
48
|
+
Then, consecutive rainy timesteps are grouped into the same event if the time gap between them does not
|
|
49
|
+
exceed `intra_event_max_time_gap`. Finally, events that do not meet minimum size or duration
|
|
50
|
+
requirements are filtered out.
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
filepaths: list
|
|
55
|
+
List of L1C file paths.
|
|
56
|
+
parallel: bool
|
|
57
|
+
Whether to load the files in parallel.
|
|
58
|
+
Set parallel=True only in a multiprocessing environment.
|
|
59
|
+
The default is False.
|
|
60
|
+
neighbor_time_interval : str
|
|
61
|
+
The time interval around a given a timestep defining the neighborhood.
|
|
62
|
+
Only timesteps that fall within this time interval before or after a timestep are considered neighbors.
|
|
63
|
+
neighbor_min_size : int, optional
|
|
64
|
+
The minimum number of neighboring timesteps required within `neighbor_time_interval` for a
|
|
65
|
+
timestep to be considered non-isolated. Isolated timesteps are removed !
|
|
66
|
+
- If `neighbor_min_size=0, then no timestep is considered isolated and no filtering occurs.
|
|
67
|
+
- If `neighbor_min_size=1`, the timestep must have at least one neighbor within `neighbor_time_interval`.
|
|
68
|
+
- If `neighbor_min_size=2`, the timestep must have at least two timesteps within `neighbor_time_interval`.
|
|
69
|
+
Defaults to 1.
|
|
70
|
+
intra_event_max_time_gap: str
|
|
71
|
+
The maximum time interval between two timesteps to be considered part of the same event.
|
|
72
|
+
This parameters is used to group timesteps into events !
|
|
73
|
+
event_min_duration : str
|
|
74
|
+
The minimum duration an event must span. Events shorter than this duration are discarded.
|
|
75
|
+
event_min_size : int, optional
|
|
76
|
+
The minimum number of valid timesteps required for an event. Defaults to 1.
|
|
77
|
+
|
|
78
|
+
Returns
|
|
79
|
+
-------
|
|
80
|
+
list of dict
|
|
81
|
+
A list of events, where each event is represented as a dictionary with keys:
|
|
82
|
+
- "start_time": np.datetime64, start time of the event
|
|
83
|
+
- "end_time": np.datetime64, end time of the event
|
|
84
|
+
- "duration": np.timedelta64, duration of the event
|
|
85
|
+
- "n_timesteps": int, number of valid timesteps in the event
|
|
86
|
+
"""
|
|
87
|
+
# Open datasets in parallel
|
|
88
|
+
if parallel:
|
|
89
|
+
list_ds = dask.compute([_delayed_open_dataset(filepath) for filepath in filepaths])[0]
|
|
90
|
+
else:
|
|
91
|
+
list_ds = [xr.open_dataset(filepath, chunks={}, cache=False, decode_timedelta=False) for filepath in filepaths]
|
|
92
|
+
# Filter dataset for requested variables
|
|
93
|
+
variables = ["time", "N"]
|
|
94
|
+
list_ds = [ds[variables] for ds in list_ds]
|
|
95
|
+
# Concat datasets
|
|
96
|
+
ds = xr.concat(list_ds, dim="time", compat="no_conflicts", combine_attrs="override")
|
|
97
|
+
# Read in memory the variable needed
|
|
98
|
+
ds = ds.compute()
|
|
99
|
+
# Close file on disk
|
|
100
|
+
_ = [ds.close() for ds in list_ds]
|
|
101
|
+
del list_ds
|
|
102
|
+
# Sort dataset by time
|
|
103
|
+
ds = ensure_sorted_by_time(ds)
|
|
104
|
+
# Define candidate timesteps to group into events
|
|
105
|
+
idx_valid = ds["N"].data > min_n_drops
|
|
106
|
+
timesteps = ds["time"].data[idx_valid]
|
|
107
|
+
# Define event list
|
|
108
|
+
event_list = group_timesteps_into_event(
|
|
109
|
+
timesteps=timesteps,
|
|
110
|
+
neighbor_min_size=neighbor_min_size,
|
|
111
|
+
neighbor_time_interval=neighbor_time_interval,
|
|
112
|
+
intra_event_max_time_gap=intra_event_max_time_gap,
|
|
113
|
+
event_min_duration=event_min_duration,
|
|
114
|
+
event_min_size=event_min_size,
|
|
115
|
+
)
|
|
116
|
+
return event_list
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def group_timesteps_into_event(
|
|
120
|
+
timesteps,
|
|
121
|
+
intra_event_max_time_gap,
|
|
122
|
+
event_min_size=0,
|
|
123
|
+
event_min_duration="0S",
|
|
124
|
+
neighbor_min_size=0,
|
|
125
|
+
neighbor_time_interval="0S",
|
|
126
|
+
):
|
|
127
|
+
"""
|
|
128
|
+
Group candidate timesteps into events based on temporal criteria.
|
|
129
|
+
|
|
130
|
+
This function groups valid candidate timesteps into events by considering how they cluster
|
|
131
|
+
in time. Any isolated timesteps (based on neighborhood criteria) are first removed. Then,
|
|
132
|
+
consecutive timesteps are grouped into the same event if the time gap between them does not
|
|
133
|
+
exceed `intra_event_max_time_gap`. Finally, events that do not meet minimum size or duration
|
|
134
|
+
requirements are filtered out.
|
|
135
|
+
|
|
136
|
+
Please note that neighbor_min_size and neighbor_time_interval are very sensitive to the
|
|
137
|
+
actual sample interval of the data !
|
|
138
|
+
|
|
139
|
+
Parameters
|
|
140
|
+
----------
|
|
141
|
+
timesteps: np.ndarray
|
|
142
|
+
Candidate timesteps to be grouped into events.
|
|
143
|
+
neighbor_time_interval : str
|
|
144
|
+
The time interval around a given a timestep defining the neighborhood.
|
|
145
|
+
Only timesteps that fall within this time interval before or after a timestep are considered neighbors.
|
|
146
|
+
neighbor_min_size : int, optional
|
|
147
|
+
The minimum number of neighboring timesteps required within `neighbor_time_interval` for a
|
|
148
|
+
timestep to be considered non-isolated. Isolated timesteps are removed !
|
|
149
|
+
- If `neighbor_min_size=0, then no timestep is considered isolated and no filtering occurs.
|
|
150
|
+
- If `neighbor_min_size=1`, the timestep must have at least one neighbor within `neighbor_time_interval`.
|
|
151
|
+
- If `neighbor_min_size=2`, the timestep must have at least two timesteps within `neighbor_time_interval`.
|
|
152
|
+
Defaults to 1.
|
|
153
|
+
intra_event_max_time_gap: str
|
|
154
|
+
The maximum time interval between two timesteps to be considered part of the same event.
|
|
155
|
+
This parameters is used to group timesteps into events !
|
|
156
|
+
event_min_duration : str
|
|
157
|
+
The minimum duration an event must span. Events shorter than this duration are discarded.
|
|
158
|
+
event_min_size : int, optional
|
|
159
|
+
The minimum number of valid timesteps required for an event. Defaults to 1.
|
|
160
|
+
|
|
161
|
+
Returns
|
|
162
|
+
-------
|
|
163
|
+
list of dict
|
|
164
|
+
A list of events, where each event is represented as a dictionary with keys:
|
|
165
|
+
- "start_time": np.datetime64, start time of the event
|
|
166
|
+
- "end_time": np.datetime64, end time of the event
|
|
167
|
+
- "duration": np.timedelta64, duration of the event
|
|
168
|
+
- "n_timesteps": int, number of valid timesteps in the event
|
|
169
|
+
"""
|
|
170
|
+
# Retrieve datetime arguments
|
|
171
|
+
neighbor_time_interval = pd.Timedelta(acronym_to_seconds(neighbor_time_interval), unit="seconds")
|
|
172
|
+
intra_event_max_time_gap = pd.Timedelta(acronym_to_seconds(intra_event_max_time_gap), unit="seconds")
|
|
173
|
+
event_min_duration = pd.Timedelta(acronym_to_seconds(event_min_duration), unit="seconds")
|
|
174
|
+
|
|
175
|
+
# Remove isolated timesteps
|
|
176
|
+
timesteps = remove_isolated_timesteps(
|
|
177
|
+
timesteps,
|
|
178
|
+
neighbor_min_size=neighbor_min_size,
|
|
179
|
+
neighbor_time_interval=neighbor_time_interval,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# Group timesteps into events
|
|
183
|
+
# - If two timesteps are separated by less than intra_event_max_time_gap, are considered the same event
|
|
184
|
+
events = group_timesteps_into_events(timesteps, intra_event_max_time_gap)
|
|
185
|
+
|
|
186
|
+
# Define list of event
|
|
187
|
+
event_list = [
|
|
188
|
+
{
|
|
189
|
+
"start_time": event[0],
|
|
190
|
+
"end_time": event[-1],
|
|
191
|
+
"duration": (event[-1] - event[0]).astype("m8[m]"),
|
|
192
|
+
"n_timesteps": len(event),
|
|
193
|
+
}
|
|
194
|
+
for event in events
|
|
195
|
+
]
|
|
196
|
+
|
|
197
|
+
# Filter event list by duration
|
|
198
|
+
event_list = [event for event in event_list if event["duration"] >= event_min_duration]
|
|
199
|
+
|
|
200
|
+
# Filter event list by duration
|
|
201
|
+
event_list = [event for event in event_list if event["n_timesteps"] >= event_min_size]
|
|
202
|
+
|
|
203
|
+
return event_list
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def remove_isolated_timesteps(timesteps, neighbor_min_size, neighbor_time_interval):
|
|
207
|
+
"""
|
|
208
|
+
Remove isolated timesteps that do not have enough neighboring timesteps within a specified time gap.
|
|
209
|
+
|
|
210
|
+
A timestep is considered isolated (and thus removed) if it does not have at least `neighbor_min_size` other
|
|
211
|
+
timesteps within the `neighbor_time_interval` before or after it.
|
|
212
|
+
In other words, for each timestep, we look for how many other timesteps fall into the
|
|
213
|
+
time interval [t - neighbor_time_interval, t + neighbor_time_interval], excluding it itself.
|
|
214
|
+
If the count of such neighbors is less than `neighbor_min_size`, that timestep is removed.
|
|
215
|
+
|
|
216
|
+
Parameters
|
|
217
|
+
----------
|
|
218
|
+
timesteps : array-like of np.datetime64
|
|
219
|
+
Sorted or unsorted array of valid timesteps.
|
|
220
|
+
neighbor_time_interval : np.timedelta64
|
|
221
|
+
The time interval around a given a timestep defining the neighborhood.
|
|
222
|
+
Only timesteps that fall within this time interval before or after a timestep are considered neighbors.
|
|
223
|
+
neighbor_min_size : int, optional
|
|
224
|
+
The minimum number of neighboring timesteps required within `neighbor_time_interval` for a
|
|
225
|
+
timestep to be considered non-isolated.
|
|
226
|
+
- If `neighbor_min_size=0, then no timestep is considered isolated and no filtering occurs.
|
|
227
|
+
- If `neighbor_min_size=1`, the timestep must have at least one neighbor within `neighbor_time_interval`.
|
|
228
|
+
- If `neighbor_min_size=2`, the timestep must have at least two timesteps within `neighbor_time_interval`.
|
|
229
|
+
Defaults to 1.
|
|
230
|
+
|
|
231
|
+
Returns
|
|
232
|
+
-------
|
|
233
|
+
np.ndarray
|
|
234
|
+
Array of timesteps with isolated entries removed.
|
|
235
|
+
"""
|
|
236
|
+
# Sort timesteps
|
|
237
|
+
timesteps = np.array(timesteps)
|
|
238
|
+
timesteps.sort()
|
|
239
|
+
|
|
240
|
+
# Do nothing if neighbor_min_size is 0
|
|
241
|
+
if neighbor_min_size == 0:
|
|
242
|
+
return timesteps
|
|
243
|
+
|
|
244
|
+
# Compute the start and end of the interval for each timestep
|
|
245
|
+
t_starts = timesteps - neighbor_time_interval
|
|
246
|
+
t_ends = timesteps + neighbor_time_interval
|
|
247
|
+
|
|
248
|
+
# Use searchsorted to find the positions where these intervals would be inserted
|
|
249
|
+
# to keep the array sorted. This effectively gives us the bounds of timesteps
|
|
250
|
+
# within the neighbor interval.
|
|
251
|
+
left_indices = np.searchsorted(timesteps, t_starts, side="left")
|
|
252
|
+
right_indices = np.searchsorted(timesteps, t_ends, side="right")
|
|
253
|
+
|
|
254
|
+
# The number of neighbors is the difference in indices minus one (to exclude the timestep itself)
|
|
255
|
+
n_neighbors = right_indices - left_indices - 1
|
|
256
|
+
valid_mask = n_neighbors >= neighbor_min_size
|
|
257
|
+
|
|
258
|
+
non_isolated_timesteps = timesteps[valid_mask]
|
|
259
|
+
|
|
260
|
+
# NON VECTORIZED CODE
|
|
261
|
+
# non_isolated_timesteps = []
|
|
262
|
+
# n_neighbours_arr = []
|
|
263
|
+
# for i, t in enumerate(timesteps):
|
|
264
|
+
# n_neighbours = np.sum(np.logical_and(timesteps >= (t - neighbor_time_interval),
|
|
265
|
+
# timesteps <= (t + neighbor_time_interval))) - 1
|
|
266
|
+
# n_neighbours_arr.append(n_neighbours)
|
|
267
|
+
# if n_neighbours > neighbor_min_size:
|
|
268
|
+
# non_isolated_timesteps.append(t)
|
|
269
|
+
# non_isolated_timesteps = np.array(non_isolated_timesteps)
|
|
270
|
+
return non_isolated_timesteps
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def group_timesteps_into_events(timesteps, intra_event_max_time_gap):
|
|
274
|
+
"""
|
|
275
|
+
Group valid timesteps into events based on a maximum allowed dry interval.
|
|
276
|
+
|
|
277
|
+
Parameters
|
|
278
|
+
----------
|
|
279
|
+
timesteps : array-like of np.datetime64
|
|
280
|
+
Sorted array of valid timesteps.
|
|
281
|
+
intra_event_max_time_gap : np.timedelta64
|
|
282
|
+
Maximum time interval allowed between consecutive valid timesteps for them
|
|
283
|
+
to be considered part of the same event.
|
|
284
|
+
|
|
285
|
+
Returns
|
|
286
|
+
-------
|
|
287
|
+
list of np.ndarray
|
|
288
|
+
A list of events, where each event is an array of timesteps.
|
|
289
|
+
"""
|
|
290
|
+
# Deal with case with no timesteps
|
|
291
|
+
if len(timesteps) == 0:
|
|
292
|
+
return []
|
|
293
|
+
|
|
294
|
+
# Ensure timesteps are sorted
|
|
295
|
+
timesteps.sort()
|
|
296
|
+
|
|
297
|
+
# Compute differences between consecutive timesteps
|
|
298
|
+
diffs = np.diff(timesteps)
|
|
299
|
+
|
|
300
|
+
# Identify the indices where the gap is larger than intra_event_max_time_gap
|
|
301
|
+
# These indices represent boundaries between events
|
|
302
|
+
break_indices = np.where(diffs > intra_event_max_time_gap)[0] + 1
|
|
303
|
+
|
|
304
|
+
# Split the timesteps at the identified break points
|
|
305
|
+
events = np.split(timesteps, break_indices)
|
|
306
|
+
|
|
307
|
+
# NON VECTORIZED CODE
|
|
308
|
+
# events = []
|
|
309
|
+
# current_event = [timesteps[0]]
|
|
310
|
+
# for i in range(1, len(timesteps)):
|
|
311
|
+
# current_t = timesteps[i]
|
|
312
|
+
# previous_t = timesteps[i - 1]
|
|
313
|
+
|
|
314
|
+
# if current_t - previous_t <= intra_event_max_time_gap:
|
|
315
|
+
# current_event.append(current_t)
|
|
316
|
+
# else:
|
|
317
|
+
# events.append(current_event)
|
|
318
|
+
# current_event = [current_t]
|
|
319
|
+
|
|
320
|
+
# events.append(current_event)
|
|
321
|
+
return events
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
####-----------------------------------------------------------------------------------.
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def get_events_info(list_events, filepaths, accumulation_interval, rolling):
|
|
328
|
+
"""
|
|
329
|
+
Provide information about the required files for each event.
|
|
330
|
+
|
|
331
|
+
For each event in `list_events`, this function identifies the file paths from `filepaths` that
|
|
332
|
+
overlap with the event period, adjusted by the `accumulation_interval`. The event period is
|
|
333
|
+
extended backward or forward based on the `rolling` parameter.
|
|
334
|
+
|
|
335
|
+
Parameters
|
|
336
|
+
----------
|
|
337
|
+
list_events : list of dict
|
|
338
|
+
List of events, where each event is a dictionary containing at least 'start_time' and 'end_time'
|
|
339
|
+
keys with `numpy.datetime64` values.
|
|
340
|
+
filepaths : list of str
|
|
341
|
+
List of file paths corresponding to data files.
|
|
342
|
+
accumulation_interval : numpy.timedelta64 or int
|
|
343
|
+
Time interval to adjust the event period for accumulation. If an integer is provided, it is
|
|
344
|
+
assumed to be in seconds.
|
|
345
|
+
rolling : bool
|
|
346
|
+
If True, adjust the event period backward by `accumulation_interval` (rolling backward).
|
|
347
|
+
If False, adjust forward (aggregate forward).
|
|
348
|
+
|
|
349
|
+
Returns
|
|
350
|
+
-------
|
|
351
|
+
list of dict
|
|
352
|
+
A list where each element is a dictionary containing:
|
|
353
|
+
- 'start_time': Adjusted start time of the event (`numpy.datetime64`).
|
|
354
|
+
- 'end_time': Adjusted end time of the event (`numpy.datetime64`).
|
|
355
|
+
- 'filepaths': List of file paths overlapping with the adjusted event period.
|
|
356
|
+
|
|
357
|
+
"""
|
|
358
|
+
# Ensure accumulation_interval is numpy.timedelta64
|
|
359
|
+
if not isinstance(accumulation_interval, np.timedelta64):
|
|
360
|
+
accumulation_interval = np.timedelta64(accumulation_interval, "s")
|
|
361
|
+
|
|
362
|
+
# Retrieve file start_time and end_time
|
|
363
|
+
files_start_time, files_end_time = get_start_end_time_from_filepaths(filepaths)
|
|
364
|
+
|
|
365
|
+
# Retrieve information for each event
|
|
366
|
+
event_info = []
|
|
367
|
+
for event_dict in list_events:
|
|
368
|
+
# Retrieve event time period
|
|
369
|
+
event_start_time = event_dict["start_time"]
|
|
370
|
+
event_end_time = event_dict["end_time"]
|
|
371
|
+
|
|
372
|
+
# Add buffer to account for accumulation interval
|
|
373
|
+
if rolling: # backward
|
|
374
|
+
event_start_time = event_start_time - np.array(accumulation_interval, dtype="m8[s]")
|
|
375
|
+
else: # aggregate forward
|
|
376
|
+
event_end_time = event_end_time + np.array(accumulation_interval, dtype="m8[s]")
|
|
377
|
+
|
|
378
|
+
# Derive event filepaths
|
|
379
|
+
overlaps = (files_start_time <= event_end_time) & (files_end_time >= event_start_time)
|
|
380
|
+
event_filepaths = np.array(filepaths)[overlaps].tolist()
|
|
381
|
+
|
|
382
|
+
# Create dictionary
|
|
383
|
+
if len(event_filepaths) > 0:
|
|
384
|
+
event_info.append(
|
|
385
|
+
{"start_time": event_start_time, "end_time": event_end_time, "filepaths": event_filepaths},
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
return event_info
|