disdrodb 0.1.4__py3-none-any.whl → 0.2.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 -5
- disdrodb/_version.py +2 -2
- disdrodb/accessor/methods.py +14 -3
- disdrodb/api/checks.py +10 -0
- disdrodb/api/create_directories.py +0 -2
- disdrodb/api/io.py +14 -17
- disdrodb/api/path.py +42 -77
- disdrodb/api/search.py +89 -23
- disdrodb/cli/disdrodb_create_summary.py +11 -1
- disdrodb/cli/disdrodb_create_summary_station.py +10 -0
- disdrodb/cli/disdrodb_run_l0.py +1 -1
- disdrodb/cli/disdrodb_run_l0a.py +1 -1
- disdrodb/cli/disdrodb_run_l0b.py +1 -1
- disdrodb/cli/disdrodb_run_l0c.py +1 -1
- disdrodb/cli/disdrodb_run_l1.py +1 -1
- disdrodb/cli/disdrodb_run_l2e.py +1 -1
- disdrodb/cli/disdrodb_run_l2m.py +1 -1
- disdrodb/configs.py +30 -83
- disdrodb/constants.py +4 -3
- disdrodb/data_transfer/download_data.py +4 -2
- disdrodb/docs.py +2 -2
- disdrodb/etc/products/L1/1MIN.yaml +13 -0
- disdrodb/etc/products/L1/LPM/1MIN.yaml +13 -0
- disdrodb/etc/products/L1/PARSIVEL/1MIN.yaml +13 -0
- disdrodb/etc/products/L1/PARSIVEL2/1MIN.yaml +13 -0
- disdrodb/etc/products/L1/PWS100/1MIN.yaml +13 -0
- disdrodb/etc/products/L1/RD80/1MIN.yaml +13 -0
- disdrodb/etc/products/L1/SWS250/1MIN.yaml +13 -0
- disdrodb/etc/products/L1/global.yaml +7 -1
- disdrodb/etc/products/L2E/10MIN.yaml +1 -12
- disdrodb/etc/products/L2E/5MIN.yaml +1 -0
- disdrodb/etc/products/L2E/global.yaml +1 -1
- disdrodb/etc/products/L2M/MODELS/GAMMA_GS_ND_MAE.yaml +6 -0
- disdrodb/etc/products/L2M/{GAMMA_ML.yaml → MODELS/GAMMA_ML.yaml} +1 -1
- disdrodb/etc/products/L2M/MODELS/LOGNORMAL_GS_LOG_ND_MAE.yaml +6 -0
- disdrodb/etc/products/L2M/MODELS/LOGNORMAL_GS_ND_MAE.yaml +6 -0
- disdrodb/etc/products/L2M/MODELS/LOGNORMAL_ML.yaml +8 -0
- disdrodb/etc/products/L2M/MODELS/NGAMMA_GS_R_MAE.yaml +6 -0
- disdrodb/etc/products/L2M/global.yaml +11 -3
- disdrodb/l0/check_configs.py +49 -16
- disdrodb/l0/configs/LPM/l0a_encodings.yml +2 -2
- disdrodb/l0/configs/LPM/l0b_cf_attrs.yml +2 -2
- disdrodb/l0/configs/LPM/l0b_encodings.yml +2 -2
- disdrodb/l0/configs/LPM/raw_data_format.yml +2 -2
- disdrodb/l0/configs/PARSIVEL/l0b_encodings.yml +1 -1
- disdrodb/l0/configs/PWS100/l0b_encodings.yml +1 -0
- disdrodb/l0/configs/SWS250/bins_diameter.yml +108 -0
- disdrodb/l0/configs/SWS250/bins_velocity.yml +83 -0
- disdrodb/l0/configs/SWS250/l0a_encodings.yml +18 -0
- disdrodb/l0/configs/SWS250/l0b_cf_attrs.yml +72 -0
- disdrodb/l0/configs/SWS250/l0b_encodings.yml +155 -0
- disdrodb/l0/configs/SWS250/raw_data_format.yml +148 -0
- disdrodb/l0/l0_reader.py +2 -2
- disdrodb/l0/l0b_processing.py +70 -15
- disdrodb/l0/l0c_processing.py +7 -3
- disdrodb/l0/readers/LPM/ARM/ARM_LPM.py +1 -1
- disdrodb/l0/readers/LPM/AUSTRALIA/MELBOURNE_2007_LPM.py +2 -2
- disdrodb/l0/readers/LPM/BELGIUM/ULIEGE.py +256 -0
- disdrodb/l0/readers/LPM/BRAZIL/CHUVA_LPM.py +2 -2
- disdrodb/l0/readers/LPM/BRAZIL/GOAMAZON_LPM.py +2 -2
- disdrodb/l0/readers/LPM/GERMANY/DWD.py +491 -0
- disdrodb/l0/readers/LPM/ITALY/GID_LPM.py +2 -2
- disdrodb/l0/readers/LPM/ITALY/GID_LPM_W.py +2 -2
- disdrodb/l0/readers/LPM/KIT/CHWALA.py +2 -2
- disdrodb/l0/readers/LPM/SLOVENIA/ARSO.py +107 -12
- disdrodb/l0/readers/LPM/SLOVENIA/UL.py +3 -3
- disdrodb/l0/readers/LPM/SWITZERLAND/INNERERIZ_LPM.py +2 -2
- disdrodb/l0/readers/PARSIVEL/BASQUECOUNTRY/EUSKALMET_OTT.py +227 -0
- disdrodb/l0/readers/PARSIVEL/{GPM → NASA}/LPVEX.py +1 -1
- disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010.py +5 -14
- disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010_UF.py +8 -17
- disdrodb/l0/readers/PARSIVEL/SLOVENIA/UL.py +117 -8
- disdrodb/l0/readers/PARSIVEL2/BASQUECOUNTRY/EUSKALMET_OTT2.py +232 -0
- disdrodb/l0/readers/PARSIVEL2/BRAZIL/CHUVA_PARSIVEL2.py +10 -14
- disdrodb/l0/readers/PARSIVEL2/BRAZIL/GOAMAZON_PARSIVEL2.py +10 -14
- disdrodb/l0/readers/PARSIVEL2/DENMARK/DTU.py +8 -14
- disdrodb/l0/readers/PARSIVEL2/DENMARK/EROSION_raw.py +382 -0
- disdrodb/l0/readers/PARSIVEL2/FINLAND/FMI_PARSIVEL2.py +4 -0
- disdrodb/l0/readers/PARSIVEL2/FRANCE/OSUG.py +1 -1
- disdrodb/l0/readers/PARSIVEL2/GREECE/NOA.py +127 -0
- disdrodb/l0/readers/PARSIVEL2/ITALY/HYDROX.py +239 -0
- disdrodb/l0/readers/PARSIVEL2/NCAR/FARM_PARSIVEL2.py +5 -11
- disdrodb/l0/readers/PARSIVEL2/NCAR/PERILS_MIPS.py +4 -17
- disdrodb/l0/readers/PARSIVEL2/NCAR/RELAMPAGO_PARSIVEL2.py +5 -14
- disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_PJ.py +10 -13
- disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_SB.py +10 -13
- disdrodb/l0/readers/PARSIVEL2/PHILIPPINES/PAGASA.py +232 -0
- disdrodb/l0/readers/PARSIVEL2/SPAIN/CENER.py +6 -18
- disdrodb/l0/readers/PARSIVEL2/{NASA/LPVEX.py → SPAIN/GRANADA.py} +46 -35
- disdrodb/l0/readers/PARSIVEL2/SWEDEN/SMHI.py +189 -0
- disdrodb/l0/readers/PARSIVEL2/USA/{C3WE.py → CW3E.py} +10 -28
- disdrodb/l0/readers/PWS100/AUSTRIA/HOAL.py +321 -0
- disdrodb/l0/readers/SW250/BELGIUM/KMI.py +239 -0
- disdrodb/l1/beard_model.py +31 -129
- disdrodb/l1/fall_velocity.py +136 -83
- disdrodb/l1/filters.py +25 -28
- disdrodb/l1/processing.py +16 -17
- disdrodb/l1/resampling.py +101 -38
- disdrodb/l1_env/routines.py +46 -17
- disdrodb/l2/empirical_dsd.py +6 -0
- disdrodb/l2/processing.py +6 -5
- disdrodb/metadata/geolocation.py +0 -2
- disdrodb/metadata/search.py +3 -4
- disdrodb/psd/fitting.py +16 -13
- disdrodb/routines/l0.py +2 -2
- disdrodb/routines/l1.py +173 -60
- disdrodb/routines/l2.py +148 -284
- disdrodb/routines/options.py +345 -0
- disdrodb/routines/wrappers.py +14 -1
- disdrodb/scattering/axis_ratio.py +90 -84
- disdrodb/scattering/permittivity.py +6 -0
- disdrodb/summary/routines.py +735 -670
- disdrodb/utils/archiving.py +51 -44
- disdrodb/utils/attrs.py +3 -1
- disdrodb/utils/dask.py +4 -4
- disdrodb/utils/dict.py +33 -0
- disdrodb/utils/encoding.py +6 -1
- disdrodb/utils/routines.py +9 -8
- disdrodb/utils/time.py +11 -3
- disdrodb/viz/__init__.py +0 -13
- disdrodb/viz/plots.py +231 -1
- {disdrodb-0.1.4.dist-info → disdrodb-0.2.0.dist-info}/METADATA +2 -1
- {disdrodb-0.1.4.dist-info → disdrodb-0.2.0.dist-info}/RECORD +135 -103
- /disdrodb/etc/products/L2M/{NGAMMA_GS_LOG_ND_MAE.yaml → MODELS/NGAMMA_GS_LOG_ND_MAE.yaml} +0 -0
- /disdrodb/etc/products/L2M/{NGAMMA_GS_ND_MAE.yaml → MODELS/NGAMMA_GS_ND_MAE.yaml} +0 -0
- /disdrodb/etc/products/L2M/{NGAMMA_GS_Z_MAE.yaml → MODELS/NGAMMA_GS_Z_MAE.yaml} +0 -0
- /disdrodb/l0/readers/PARSIVEL/{GPM → NASA}/IFLOODS.py +0 -0
- /disdrodb/l0/readers/PARSIVEL/{GPM → NASA}/MC3E.py +0 -0
- /disdrodb/l0/readers/PARSIVEL/{GPM → NASA}/PIERS.py +0 -0
- /disdrodb/l0/readers/PARSIVEL2/{GPM → NASA}/GCPEX.py +0 -0
- /disdrodb/l0/readers/PARSIVEL2/{GPM → NASA}/NSSTC.py +0 -0
- {disdrodb-0.1.4.dist-info → disdrodb-0.2.0.dist-info}/WHEEL +0 -0
- {disdrodb-0.1.4.dist-info → disdrodb-0.2.0.dist-info}/entry_points.txt +0 -0
- {disdrodb-0.1.4.dist-info → disdrodb-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {disdrodb-0.1.4.dist-info → disdrodb-0.2.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,345 @@
|
|
|
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
|
+
"""Implements ProcessingOption class for DISDRODB routines."""
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
|
|
21
|
+
import disdrodb
|
|
22
|
+
from disdrodb.api.checks import check_product, check_sensor_name, check_temporal_resolution
|
|
23
|
+
from disdrodb.api.info import group_filepaths
|
|
24
|
+
from disdrodb.configs import get_products_configs_dir
|
|
25
|
+
from disdrodb.utils.archiving import define_temporal_partitions, group_files_by_temporal_partitions
|
|
26
|
+
from disdrodb.utils.list import flatten_list
|
|
27
|
+
from disdrodb.utils.routines import is_possible_product
|
|
28
|
+
from disdrodb.utils.time import ensure_timedelta_seconds, get_sampling_information
|
|
29
|
+
from disdrodb.utils.yaml import read_yaml
|
|
30
|
+
|
|
31
|
+
# TODO: Test ensure recursive update for product_options key, do not replace just "product_options" dict !
|
|
32
|
+
# get_product_options(product="L2E", temporal_resolution="10MIN")
|
|
33
|
+
# get_product_options(product="L2M", temporal_resolution="10MIN")
|
|
34
|
+
# get_product_options(product="L1")
|
|
35
|
+
# get_product_options(product="L1", temporal_resolution="1MIN")
|
|
36
|
+
# get_product_options(product="L1", temporal_resolution="1MIN", sensor_name="PARSIVEL")
|
|
37
|
+
|
|
38
|
+
# TODO: test return list
|
|
39
|
+
# get_product_temporal_resolutions(product="L1")
|
|
40
|
+
# get_product_temporal_resolutions(product="L2E")
|
|
41
|
+
# get_product_temporal_resolutions(product="L2M")
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_product_options(product, temporal_resolution=None, sensor_name=None):
|
|
45
|
+
"""Return DISDRODB product options.
|
|
46
|
+
|
|
47
|
+
If temporal resolution is not provided, it returns the global product option.
|
|
48
|
+
If temporal resolution is provided, it provides the custom product options, if specified.
|
|
49
|
+
If product="L1" and sensor_name is specified, it customize product options also by sensor.
|
|
50
|
+
"""
|
|
51
|
+
# Retrieve products configuration directory
|
|
52
|
+
products_configs_dir = get_products_configs_dir()
|
|
53
|
+
|
|
54
|
+
# Validate DISDRODB products configuration
|
|
55
|
+
validate_product_configuration(products_configs_dir)
|
|
56
|
+
|
|
57
|
+
# Check product
|
|
58
|
+
check_product(product)
|
|
59
|
+
|
|
60
|
+
# Retrieve global product options (when no temporal resolution !)
|
|
61
|
+
global_options = read_yaml(os.path.join(products_configs_dir, product, "global.yaml"))
|
|
62
|
+
if temporal_resolution is None:
|
|
63
|
+
global_options = check_availability_radar_simulations(global_options)
|
|
64
|
+
return global_options
|
|
65
|
+
|
|
66
|
+
# Check temporal resolution
|
|
67
|
+
check_temporal_resolution(temporal_resolution)
|
|
68
|
+
|
|
69
|
+
# If temporal resolutions are specified, drop 'temporal_resolutions' key
|
|
70
|
+
global_options.pop("temporal_resolutions", None)
|
|
71
|
+
|
|
72
|
+
# Read custom options for specific temporal resolution
|
|
73
|
+
custom_options_path = os.path.join(products_configs_dir, product, f"{temporal_resolution}.yaml")
|
|
74
|
+
if not os.path.exists(custom_options_path):
|
|
75
|
+
return global_options
|
|
76
|
+
custom_options = read_yaml(custom_options_path)
|
|
77
|
+
|
|
78
|
+
# Define product options
|
|
79
|
+
options = global_options.copy()
|
|
80
|
+
if "product_options" in custom_options:
|
|
81
|
+
options["product_options"].update(custom_options.pop("product_options"))
|
|
82
|
+
options.update(custom_options)
|
|
83
|
+
|
|
84
|
+
# Check availability of radar simulations
|
|
85
|
+
options = check_availability_radar_simulations(options)
|
|
86
|
+
|
|
87
|
+
# Customize product options by sensor if L1 product
|
|
88
|
+
if product == "L1" and sensor_name is not None:
|
|
89
|
+
check_sensor_name(sensor_name)
|
|
90
|
+
custom_options_path = os.path.join(products_configs_dir, product, sensor_name, f"{temporal_resolution}.yaml")
|
|
91
|
+
if not os.path.exists(custom_options_path):
|
|
92
|
+
return options
|
|
93
|
+
custom_options = read_yaml(custom_options_path)
|
|
94
|
+
if "product_options" in custom_options:
|
|
95
|
+
options["product_options"].update(custom_options.pop("product_options"))
|
|
96
|
+
return options
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def get_product_temporal_resolutions(product):
|
|
100
|
+
"""Return DISDRODB products temporal resolutions."""
|
|
101
|
+
# Check only L2E and L2M
|
|
102
|
+
return get_product_options(product)["temporal_resolutions"]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def get_model_options(product, model_name):
|
|
106
|
+
"""Return DISDRODB L2M product model options."""
|
|
107
|
+
# Retrieve products configuration directory
|
|
108
|
+
products_configs_dir = get_products_configs_dir()
|
|
109
|
+
model_options_path = os.path.join(products_configs_dir, product, "MODELS", f"{model_name}.yaml")
|
|
110
|
+
model_options = read_yaml(model_options_path)
|
|
111
|
+
return model_options
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def check_availability_radar_simulations(options):
|
|
115
|
+
"""Check radar simulations are possible for L2E and L2M products."""
|
|
116
|
+
if "radar_enabled" in options and not disdrodb.is_pytmatrix_available():
|
|
117
|
+
options["radar_enabled"] = False
|
|
118
|
+
return options
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def validate_product_configuration(products_configs_dir):
|
|
122
|
+
"""Validate the DISDRODB products configuration files."""
|
|
123
|
+
# TODO: Implement validation of DISDRODB products configuration files with pydantic
|
|
124
|
+
# TODO: Raise warning if L1 temporal resolutions does not includes all temporal resolutions of L2 products.
|
|
125
|
+
# TODO: Raise warning if L2E temporal resolutions does not includes all temporal resolutions of L2M products.
|
|
126
|
+
# if stategy_event, check neighbor_time_interval >= sample_interval !
|
|
127
|
+
# if temporal_resolution_to_seconds(neighbor_time_interval) < temporal_resolution_to_seconds(sample_interval):
|
|
128
|
+
# msg = "'neighbor_time_interval' must be at least equal to the dataset sample interval ({sample_interval})"
|
|
129
|
+
# raise ValueError(msg)
|
|
130
|
+
|
|
131
|
+
pass
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _define_blocks_offsets(sample_interval, temporal_resolution):
|
|
135
|
+
"""Define blocks offset for resampling logic."""
|
|
136
|
+
# Retrieve accumulation_interval and rolling option
|
|
137
|
+
accumulation_interval, rolling = get_sampling_information(temporal_resolution)
|
|
138
|
+
|
|
139
|
+
# Ensure sample_interval and accumulation_interval is numpy.timedelta64
|
|
140
|
+
accumulation_interval = ensure_timedelta_seconds(accumulation_interval)
|
|
141
|
+
sample_interval = ensure_timedelta_seconds(sample_interval)
|
|
142
|
+
|
|
143
|
+
# Define offset to apply to time partitions blocks
|
|
144
|
+
block_starts_offset = 0
|
|
145
|
+
block_ends_offset = 0
|
|
146
|
+
|
|
147
|
+
# If rolling, need to search also in next time block ...
|
|
148
|
+
if rolling and sample_interval != accumulation_interval:
|
|
149
|
+
block_ends_offset = accumulation_interval - sample_interval
|
|
150
|
+
return block_starts_offset, block_ends_offset
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class L1ProcessingOptions:
|
|
154
|
+
"""Define L1 product processing options."""
|
|
155
|
+
|
|
156
|
+
def __init__(self, filepaths, parallel, sensor_name, temporal_resolutions=None):
|
|
157
|
+
"""Define DISDRODB L1 product processing options."""
|
|
158
|
+
product = "L1"
|
|
159
|
+
|
|
160
|
+
# ---------------------------------------------------------------------.
|
|
161
|
+
# Define temporal resolutions for which to retrieve processing options
|
|
162
|
+
if temporal_resolutions is None:
|
|
163
|
+
temporal_resolutions = get_product_temporal_resolutions(product)
|
|
164
|
+
elif isinstance(temporal_resolutions, str):
|
|
165
|
+
temporal_resolutions = [temporal_resolutions]
|
|
166
|
+
_ = [check_temporal_resolution(temporal_resolution) for temporal_resolution in temporal_resolutions]
|
|
167
|
+
|
|
168
|
+
# ---------------------------------------------------------------------.
|
|
169
|
+
# Get product options at various temporal resolutions
|
|
170
|
+
src_dict_product_options = {
|
|
171
|
+
temporal_resolution: get_product_options(
|
|
172
|
+
product=product,
|
|
173
|
+
temporal_resolution=temporal_resolution,
|
|
174
|
+
sensor_name=sensor_name,
|
|
175
|
+
)
|
|
176
|
+
for temporal_resolution in temporal_resolutions
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
# ---------------------------------------------------------------------.
|
|
180
|
+
# Group filepaths by source sample intervals
|
|
181
|
+
# - Typically the sample interval is fixed and is just one
|
|
182
|
+
# - Some stations might change the sample interval along the years
|
|
183
|
+
# - For each sample interval, separated processing must take place
|
|
184
|
+
dict_filepaths = group_filepaths(filepaths, groups="sample_interval")
|
|
185
|
+
|
|
186
|
+
# ---------------------------------------------------------------------.
|
|
187
|
+
# Retrieve processing information for each temporal resolution
|
|
188
|
+
dict_product_options = {}
|
|
189
|
+
dict_folder_partitioning = {}
|
|
190
|
+
dict_files_partitions = {}
|
|
191
|
+
_cache_dict_temporal_partitions: dict[str, dict] = {}
|
|
192
|
+
# temporal_resolution = temporal_resolutions[0]
|
|
193
|
+
for temporal_resolution in temporal_resolutions:
|
|
194
|
+
|
|
195
|
+
# -------------------------------------------------------------------------.
|
|
196
|
+
# Retrieve product options
|
|
197
|
+
product_options = src_dict_product_options[temporal_resolution].copy()
|
|
198
|
+
|
|
199
|
+
# Extract processing options
|
|
200
|
+
archive_options = product_options.pop("archive_options")
|
|
201
|
+
|
|
202
|
+
dict_product_options[temporal_resolution] = product_options
|
|
203
|
+
# -------------------------------------------------------------------------.
|
|
204
|
+
# Define folder partitioning
|
|
205
|
+
if "folder_partitioning" not in archive_options:
|
|
206
|
+
dict_folder_partitioning[temporal_resolution] = disdrodb.config.get("folder_partitioning")
|
|
207
|
+
else:
|
|
208
|
+
dict_folder_partitioning[temporal_resolution] = archive_options.pop("folder_partitioning")
|
|
209
|
+
|
|
210
|
+
# -------------------------------------------------------------------------.
|
|
211
|
+
# Define list of temporal partitions
|
|
212
|
+
# - [{start_time: np.datetime64, end_time: np.datetime64}, ....]
|
|
213
|
+
# - Either strategy: "event" or "time_block" or save_by_time_block"
|
|
214
|
+
# - "event" requires loading data into memory to identify events
|
|
215
|
+
# --> Does some data filtering on what to process !
|
|
216
|
+
# - "time_block" does not require loading data into memory
|
|
217
|
+
# --> Does not do data filtering on what to process !
|
|
218
|
+
# --> Here we cache dict_temporal_partitions so that we don't need to recompute
|
|
219
|
+
# stuffs if processing options are the same
|
|
220
|
+
# --> Using time_block with e.g. freq="day" we get start/end in the format of 00:00:00-23:59:59
|
|
221
|
+
key = json.dumps(archive_options, sort_keys=True)
|
|
222
|
+
if key not in _cache_dict_temporal_partitions:
|
|
223
|
+
_cache_dict_temporal_partitions[key] = {
|
|
224
|
+
sample_interval: define_temporal_partitions(filepaths, parallel=parallel, **archive_options)
|
|
225
|
+
for sample_interval, filepaths in dict_filepaths.items()
|
|
226
|
+
}
|
|
227
|
+
dict_temporal_partitions = _cache_dict_temporal_partitions[key].copy() # To avoid in-place replacement
|
|
228
|
+
|
|
229
|
+
# ------------------------------------------------------------------.
|
|
230
|
+
# Group filepaths by temporal partitions
|
|
231
|
+
# - This is done separately for each possible source sample interval
|
|
232
|
+
# - It groups filepaths by start_time and end_time provided by temporal_partitions
|
|
233
|
+
# --> Output: [{'start_time': ..., 'end_time': ..., filepaths: [...]}, ...]
|
|
234
|
+
# - In L0C we ensure that the time reported correspond to the start of the measurement interval.
|
|
235
|
+
# - When aggregating/resampling/accumulating data, we need to load also some data after the
|
|
236
|
+
# actual end_time of the time partition to ensure that
|
|
237
|
+
# the resampled dataset contains the timesteps of the partition end time.
|
|
238
|
+
# --> Use of block_starts_offset and block_ends_offset in group_files_by_temporal_partitions
|
|
239
|
+
# - ATTENTION: group_files_by_temporal_partitions returns
|
|
240
|
+
# start_time and end_time as datetime.datetime64objects !
|
|
241
|
+
# - ATTENTION: Files within each files_partitions block have the same sample_interval !
|
|
242
|
+
|
|
243
|
+
# sample_interval = 30
|
|
244
|
+
# temporal_partitions = dict_temporal_partitions[sample_interval]
|
|
245
|
+
|
|
246
|
+
files_partitions = []
|
|
247
|
+
for sample_interval, temporal_partitions in dict_temporal_partitions.items():
|
|
248
|
+
if is_possible_product(
|
|
249
|
+
temporal_resolution=temporal_resolution,
|
|
250
|
+
sample_interval=sample_interval,
|
|
251
|
+
):
|
|
252
|
+
|
|
253
|
+
block_starts_offset, block_ends_offset = _define_blocks_offsets(
|
|
254
|
+
sample_interval=sample_interval,
|
|
255
|
+
temporal_resolution=temporal_resolution,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
files_partitions.append(
|
|
259
|
+
group_files_by_temporal_partitions(
|
|
260
|
+
temporal_partitions=temporal_partitions,
|
|
261
|
+
filepaths=dict_filepaths[sample_interval],
|
|
262
|
+
block_starts_offset=block_starts_offset,
|
|
263
|
+
block_ends_offset=block_ends_offset,
|
|
264
|
+
),
|
|
265
|
+
)
|
|
266
|
+
files_partitions = flatten_list(files_partitions)
|
|
267
|
+
dict_files_partitions[temporal_resolution] = files_partitions
|
|
268
|
+
|
|
269
|
+
# ------------------------------------------------------------------.
|
|
270
|
+
# Keep only temporal_resolutions for which products can be defined
|
|
271
|
+
# - Remove e.g when not compatible accumulation_interval with source sample_interval
|
|
272
|
+
temporal_resolutions = [
|
|
273
|
+
temporal_resolution
|
|
274
|
+
for temporal_resolution in temporal_resolutions
|
|
275
|
+
if len(dict_files_partitions[temporal_resolution]) > 0
|
|
276
|
+
]
|
|
277
|
+
# ------------------------------------------------------------------.
|
|
278
|
+
# Add attributes
|
|
279
|
+
self.temporal_resolutions = temporal_resolutions
|
|
280
|
+
self.dict_files_partitions = dict_files_partitions
|
|
281
|
+
self.dict_product_options = dict_product_options
|
|
282
|
+
self.dict_folder_partitioning = dict_folder_partitioning
|
|
283
|
+
|
|
284
|
+
def group_files_by_temporal_partitions(self, temporal_resolution):
|
|
285
|
+
"""Return files partitions dictionary for a specific L2E product."""
|
|
286
|
+
return self.dict_files_partitions[temporal_resolution]
|
|
287
|
+
|
|
288
|
+
def get_product_options(self, temporal_resolution):
|
|
289
|
+
"""Return product options dictionary for a specific L2E product."""
|
|
290
|
+
return self.dict_product_options[temporal_resolution]
|
|
291
|
+
|
|
292
|
+
def get_folder_partitioning(self, temporal_resolution):
|
|
293
|
+
"""Return the folder partitioning for a specific L2E product."""
|
|
294
|
+
# to be used for logs and files !
|
|
295
|
+
return self.dict_folder_partitioning[temporal_resolution]
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class L2ProcessingOptions:
|
|
299
|
+
"""Define L2 products processing options."""
|
|
300
|
+
|
|
301
|
+
def __init__(self, product, filepaths, parallel, temporal_resolution):
|
|
302
|
+
"""Define DISDRODB L2 products processing options."""
|
|
303
|
+
import disdrodb
|
|
304
|
+
|
|
305
|
+
# Check temporal resolution
|
|
306
|
+
check_temporal_resolution(temporal_resolution)
|
|
307
|
+
|
|
308
|
+
# Get product options
|
|
309
|
+
product_options = get_product_options(product, temporal_resolution=temporal_resolution)
|
|
310
|
+
|
|
311
|
+
# Extract processing options
|
|
312
|
+
archive_options = product_options.pop("archive_options")
|
|
313
|
+
|
|
314
|
+
# Define folder partitioning
|
|
315
|
+
if "folder_partitioning" not in archive_options:
|
|
316
|
+
folder_partitioning = disdrodb.config.get("folder_partitioning")
|
|
317
|
+
else:
|
|
318
|
+
folder_partitioning = archive_options.pop("folder_partitioning")
|
|
319
|
+
|
|
320
|
+
# Define files temporal partitions
|
|
321
|
+
# - [{start_time: np.datetime64, end_time: np.datetime64}, ....]
|
|
322
|
+
# - Either strategy: "event" or "time_block"
|
|
323
|
+
# - "strategy=event" requires loading data into memory to identify events
|
|
324
|
+
# --> Does some data filtering on what to process !
|
|
325
|
+
# - "strategy=time_block" does not require loading data into memory
|
|
326
|
+
# --> Does not do data filtering on what to process !
|
|
327
|
+
temporal_partitions = define_temporal_partitions(filepaths, parallel=parallel, **archive_options)
|
|
328
|
+
|
|
329
|
+
# ------------------------------------------------------------------.
|
|
330
|
+
# Group filepaths by temporal partitions
|
|
331
|
+
# - It groups filepaths by start_time and end_time provided by temporal_partitions
|
|
332
|
+
# - ATTENTION: group_files_by_temporal_partitions returns
|
|
333
|
+
# start_time and end_time as datetime.datetime64 objects !
|
|
334
|
+
files_partitions = group_files_by_temporal_partitions(
|
|
335
|
+
temporal_partitions=temporal_partitions,
|
|
336
|
+
filepaths=filepaths,
|
|
337
|
+
)
|
|
338
|
+
files_partitions = flatten_list(files_partitions)
|
|
339
|
+
|
|
340
|
+
# ------------------------------------------------------------------.
|
|
341
|
+
# Add attributes
|
|
342
|
+
# self.temporal_partitions = temporal_partitions
|
|
343
|
+
self.folder_partitioning = folder_partitioning
|
|
344
|
+
self.files_partitions = files_partitions
|
|
345
|
+
self.product_options = product_options
|
disdrodb/routines/wrappers.py
CHANGED
|
@@ -643,6 +643,7 @@ def create_summary_station(
|
|
|
643
643
|
campaign_name,
|
|
644
644
|
station_name,
|
|
645
645
|
parallel=False,
|
|
646
|
+
temporal_resolution="1MIN",
|
|
646
647
|
data_archive_dir=None,
|
|
647
648
|
):
|
|
648
649
|
"""Create summary figures and tables for a DISDRODB station."""
|
|
@@ -658,6 +659,8 @@ def create_summary_station(
|
|
|
658
659
|
str(data_archive_dir),
|
|
659
660
|
"--parallel",
|
|
660
661
|
str(parallel),
|
|
662
|
+
"--temporal_resolution",
|
|
663
|
+
str(temporal_resolution),
|
|
661
664
|
],
|
|
662
665
|
)
|
|
663
666
|
# Execute command
|
|
@@ -1440,6 +1443,7 @@ def create_summary(
|
|
|
1440
1443
|
campaign_names=None,
|
|
1441
1444
|
station_names=None,
|
|
1442
1445
|
parallel=False,
|
|
1446
|
+
temporal_resolution="1MIN",
|
|
1443
1447
|
data_archive_dir=None,
|
|
1444
1448
|
metadata_archive_dir=None,
|
|
1445
1449
|
):
|
|
@@ -1464,6 +1468,14 @@ def create_summary(
|
|
|
1464
1468
|
The directory path must end with ``<...>/DISDRODB``.
|
|
1465
1469
|
If ``None``, it uses the ``data_archive_dir`` path specified
|
|
1466
1470
|
in the DISDRODB active configuration.
|
|
1471
|
+
metadata_archive_dir
|
|
1472
|
+
The directory path where the DISDRODB Metadata Archive is located.
|
|
1473
|
+
The directory path must end with ``<...>/DISDRODB-METADATA/DISDRODB``.
|
|
1474
|
+
If ``None``, it uses the ``metadata_archive_dir`` path specified
|
|
1475
|
+
in the DISDRODB active configuration.
|
|
1476
|
+
temporal_resolution : str
|
|
1477
|
+
Temporal resolution of the summary.
|
|
1478
|
+
The default value is ``1MIN``.
|
|
1467
1479
|
"""
|
|
1468
1480
|
# Get list of available stations
|
|
1469
1481
|
list_info = available_stations(
|
|
@@ -1476,7 +1488,7 @@ def create_summary(
|
|
|
1476
1488
|
station_names=station_names,
|
|
1477
1489
|
# Search options
|
|
1478
1490
|
product="L2E",
|
|
1479
|
-
|
|
1491
|
+
temporal_resolution=temporal_resolution,
|
|
1480
1492
|
raise_error_if_empty=True,
|
|
1481
1493
|
)
|
|
1482
1494
|
|
|
@@ -1493,6 +1505,7 @@ def create_summary(
|
|
|
1493
1505
|
station_name=station_name,
|
|
1494
1506
|
# Processing option
|
|
1495
1507
|
parallel=parallel,
|
|
1508
|
+
temporal_resolution=temporal_resolution,
|
|
1496
1509
|
)
|
|
1497
1510
|
print("Creation of station summaries has terminated.")
|
|
1498
1511
|
|
|
@@ -20,90 +20,6 @@ import numpy as np
|
|
|
20
20
|
import xarray as xr
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
def available_axis_ratio_models():
|
|
24
|
-
"""Return a list of the available drop axis ratio models."""
|
|
25
|
-
return list(AXIS_RATIO_MODELS)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def get_axis_ratio_model(model):
|
|
29
|
-
"""Return the specified drop axis ratio model.
|
|
30
|
-
|
|
31
|
-
Parameters
|
|
32
|
-
----------
|
|
33
|
-
model : str
|
|
34
|
-
The model to use for calculating the axis ratio. Available models are:
|
|
35
|
-
'Thurai2005', 'Thurai2007', 'Battaglia2010', 'Brandes2002',
|
|
36
|
-
'Pruppacher1970', 'Beard1987', 'Andsager1999'.
|
|
37
|
-
|
|
38
|
-
Returns
|
|
39
|
-
-------
|
|
40
|
-
callable
|
|
41
|
-
A function which compute the vertical-to-horizontal axis ratio given a
|
|
42
|
-
particle diameter in mm.
|
|
43
|
-
|
|
44
|
-
Notes
|
|
45
|
-
-----
|
|
46
|
-
This function serves as a wrapper to various axis ratio models for raindrops.
|
|
47
|
-
It returns the appropriate model based on the `model` parameter.
|
|
48
|
-
|
|
49
|
-
Please note that the axis ratio function to be provided to pyTmatrix expects to
|
|
50
|
-
return a horizontal-to-vertical axis ratio !
|
|
51
|
-
|
|
52
|
-
"""
|
|
53
|
-
model = check_axis_ratio_model(model)
|
|
54
|
-
return AXIS_RATIO_MODELS[model]
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def check_axis_ratio_model(model):
|
|
58
|
-
"""Check validity of the specified drop axis ratio model."""
|
|
59
|
-
available_models = available_axis_ratio_models()
|
|
60
|
-
if model not in available_models:
|
|
61
|
-
raise ValueError(f"{model} is an invalid axis-ratio model. Valid models: {available_models}.")
|
|
62
|
-
return model
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def get_axis_ratio(diameter, model):
|
|
66
|
-
"""
|
|
67
|
-
Compute the axis ratio of raindrops using the specified model.
|
|
68
|
-
|
|
69
|
-
Parameters
|
|
70
|
-
----------
|
|
71
|
-
diameter : array-like
|
|
72
|
-
Raindrops diameter in mm.
|
|
73
|
-
model : str
|
|
74
|
-
The axis ratio model to use for calculating the axis ratio. Available models are:
|
|
75
|
-
'Thurai2005', 'Thurai2007', 'Battaglia2010', 'Brandes2002',
|
|
76
|
-
'Pruppacher1970', 'Beard1987', 'Andsager1999'.
|
|
77
|
-
|
|
78
|
-
Returns
|
|
79
|
-
-------
|
|
80
|
-
axis_ratio : array-like
|
|
81
|
-
The vertical-to-horizontal drop axis ratio corresponding to the input diameters.
|
|
82
|
-
Values of 1 indicate spherical particles, while values <1 indicate oblate particles.
|
|
83
|
-
Values >1 means prolate particles.
|
|
84
|
-
|
|
85
|
-
Notes
|
|
86
|
-
-----
|
|
87
|
-
This function serves as a wrapper to various axis ratio models for raindrops.
|
|
88
|
-
It selects and applies the appropriate model based on the `model` parameter.
|
|
89
|
-
|
|
90
|
-
Examples
|
|
91
|
-
--------
|
|
92
|
-
>>> diameter = np.array([0.5, 1.0, 2.0, 3.0])
|
|
93
|
-
>>> axis_ratio = get_axis_ratio(diameter, model="Brandes2002")
|
|
94
|
-
|
|
95
|
-
"""
|
|
96
|
-
# Retrieve axis ratio function
|
|
97
|
-
axis_ratio_func = get_axis_ratio_model(model)
|
|
98
|
-
|
|
99
|
-
# Retrieve axis ratio
|
|
100
|
-
axis_ratio = axis_ratio_func(diameter)
|
|
101
|
-
|
|
102
|
-
# Clip values between 0 and 1
|
|
103
|
-
axis_ratio = np.clip(axis_ratio, 0, 1)
|
|
104
|
-
return axis_ratio
|
|
105
|
-
|
|
106
|
-
|
|
107
23
|
def get_axis_ratio_andsager_1999(diameter):
|
|
108
24
|
"""
|
|
109
25
|
Compute the axis ratio of raindrops using the Andsager et al. (1999) model.
|
|
@@ -366,3 +282,93 @@ AXIS_RATIO_MODELS = {
|
|
|
366
282
|
"Beard1987": get_axis_ratio_beard_1987,
|
|
367
283
|
"Andsager1999": get_axis_ratio_andsager_1999,
|
|
368
284
|
}
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def available_axis_ratio_models():
|
|
288
|
+
"""Return a list of the available drop axis ratio models."""
|
|
289
|
+
return list(AXIS_RATIO_MODELS)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def check_axis_ratio_model(model):
|
|
293
|
+
"""Check validity of the specified drop axis ratio model."""
|
|
294
|
+
available_models = available_axis_ratio_models()
|
|
295
|
+
if model not in available_models:
|
|
296
|
+
raise ValueError(f"{model} is an invalid axis-ratio model. Valid models: {available_models}.")
|
|
297
|
+
return model
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def get_axis_ratio_model(model):
|
|
301
|
+
"""Return the specified drop axis ratio model.
|
|
302
|
+
|
|
303
|
+
Parameters
|
|
304
|
+
----------
|
|
305
|
+
model : str
|
|
306
|
+
The model to use for calculating the axis ratio. Available models are:
|
|
307
|
+
'Thurai2005', 'Thurai2007', 'Battaglia2010', 'Brandes2002',
|
|
308
|
+
'Pruppacher1970', 'Beard1987', 'Andsager1999'.
|
|
309
|
+
|
|
310
|
+
Returns
|
|
311
|
+
-------
|
|
312
|
+
callable
|
|
313
|
+
A function which compute the vertical-to-horizontal axis ratio given a
|
|
314
|
+
particle diameter in mm.
|
|
315
|
+
|
|
316
|
+
Notes
|
|
317
|
+
-----
|
|
318
|
+
This function serves as a wrapper to various axis ratio models for raindrops.
|
|
319
|
+
It returns the appropriate model based on the `model` parameter.
|
|
320
|
+
|
|
321
|
+
Please note that the axis ratio function to be provided to pyTmatrix expects to
|
|
322
|
+
return a horizontal-to-vertical axis ratio !
|
|
323
|
+
|
|
324
|
+
"""
|
|
325
|
+
model = check_axis_ratio_model(model)
|
|
326
|
+
return AXIS_RATIO_MODELS[model]
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def get_axis_ratio(diameter, model):
|
|
330
|
+
"""
|
|
331
|
+
Compute the axis ratio of raindrops using the specified model.
|
|
332
|
+
|
|
333
|
+
Parameters
|
|
334
|
+
----------
|
|
335
|
+
diameter : array-like
|
|
336
|
+
Raindrops diameter in mm.
|
|
337
|
+
model : str
|
|
338
|
+
The axis ratio model to use for calculating the axis ratio. Available models are:
|
|
339
|
+
'Thurai2005', 'Thurai2007', 'Battaglia2010', 'Brandes2002',
|
|
340
|
+
'Pruppacher1970', 'Beard1987', 'Andsager1999'.
|
|
341
|
+
|
|
342
|
+
Returns
|
|
343
|
+
-------
|
|
344
|
+
axis_ratio : array-like
|
|
345
|
+
The vertical-to-horizontal drop axis ratio corresponding to the input diameters.
|
|
346
|
+
Values of 1 indicate spherical particles, while values <1 indicate oblate particles.
|
|
347
|
+
Values >1 means prolate particles.
|
|
348
|
+
|
|
349
|
+
Notes
|
|
350
|
+
-----
|
|
351
|
+
This function serves as a wrapper to various axis ratio models for raindrops.
|
|
352
|
+
It selects and applies the appropriate model based on the `model` parameter.
|
|
353
|
+
|
|
354
|
+
Examples
|
|
355
|
+
--------
|
|
356
|
+
>>> diameter = np.array([0.5, 1.0, 2.0, 3.0])
|
|
357
|
+
>>> axis_ratio = get_axis_ratio(diameter, model="Brandes2002")
|
|
358
|
+
|
|
359
|
+
"""
|
|
360
|
+
# Retrieve axis ratio function
|
|
361
|
+
axis_ratio_func = get_axis_ratio_model(model)
|
|
362
|
+
|
|
363
|
+
# Retrieve axis ratio
|
|
364
|
+
axis_ratio = axis_ratio_func(diameter)
|
|
365
|
+
|
|
366
|
+
# Clip values between 0 and 1
|
|
367
|
+
axis_ratio = np.clip(axis_ratio, 0, 1)
|
|
368
|
+
|
|
369
|
+
# Add attributes
|
|
370
|
+
if isinstance(axis_ratio, xr.DataArray):
|
|
371
|
+
axis_ratio.name = "axis_ratio"
|
|
372
|
+
axis_ratio.attrs["units"] = ""
|
|
373
|
+
axis_ratio.attrs["model"] = model
|
|
374
|
+
return axis_ratio
|
|
@@ -147,6 +147,12 @@ def get_refractive_index(temperature, frequency, permittivity_model):
|
|
|
147
147
|
|
|
148
148
|
# Retrieve refractive_index
|
|
149
149
|
refractive_index = func(temperature=temperature, frequency=frequency)
|
|
150
|
+
|
|
151
|
+
# Add attributes
|
|
152
|
+
if isinstance(refractive_index, xr.DataArray):
|
|
153
|
+
refractive_index.name = "refractive_index"
|
|
154
|
+
refractive_index.attrs["units"] = ""
|
|
155
|
+
refractive_index.attrs["model"] = permittivity_model
|
|
150
156
|
return refractive_index
|
|
151
157
|
|
|
152
158
|
|