disdrodb 0.1.5__py3-none-any.whl → 0.2.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.
Files changed (125) hide show
  1. disdrodb/__init__.py +1 -5
  2. disdrodb/_version.py +2 -2
  3. disdrodb/accessor/methods.py +22 -4
  4. disdrodb/api/checks.py +10 -0
  5. disdrodb/api/io.py +20 -18
  6. disdrodb/api/path.py +42 -77
  7. disdrodb/api/search.py +89 -23
  8. disdrodb/cli/disdrodb_create_summary.py +1 -1
  9. disdrodb/cli/disdrodb_run_l0.py +1 -1
  10. disdrodb/cli/disdrodb_run_l0a.py +1 -1
  11. disdrodb/cli/disdrodb_run_l0b.py +1 -1
  12. disdrodb/cli/disdrodb_run_l0c.py +1 -1
  13. disdrodb/cli/disdrodb_run_l1.py +1 -1
  14. disdrodb/cli/disdrodb_run_l2e.py +1 -1
  15. disdrodb/cli/disdrodb_run_l2m.py +1 -1
  16. disdrodb/configs.py +30 -83
  17. disdrodb/constants.py +4 -3
  18. disdrodb/data_transfer/download_data.py +4 -2
  19. disdrodb/docs.py +2 -2
  20. disdrodb/etc/products/L1/1MIN.yaml +13 -0
  21. disdrodb/etc/products/L1/LPM/1MIN.yaml +13 -0
  22. disdrodb/etc/products/L1/LPM_V0/1MIN.yaml +13 -0
  23. disdrodb/etc/products/L1/PARSIVEL/1MIN.yaml +13 -0
  24. disdrodb/etc/products/L1/PARSIVEL2/1MIN.yaml +13 -0
  25. disdrodb/etc/products/L1/PWS100/1MIN.yaml +13 -0
  26. disdrodb/etc/products/L1/RD80/1MIN.yaml +13 -0
  27. disdrodb/etc/products/L1/SWS250/1MIN.yaml +13 -0
  28. disdrodb/etc/products/L1/global.yaml +6 -0
  29. disdrodb/etc/products/L2E/10MIN.yaml +1 -12
  30. disdrodb/etc/products/L2E/global.yaml +1 -1
  31. disdrodb/etc/products/L2M/MODELS/NGAMMA_GS_R_MAE.yaml +6 -0
  32. disdrodb/etc/products/L2M/global.yaml +1 -1
  33. disdrodb/issue/checks.py +2 -2
  34. disdrodb/l0/check_configs.py +1 -1
  35. disdrodb/l0/configs/LPM/l0a_encodings.yml +0 -1
  36. disdrodb/l0/configs/LPM/l0b_cf_attrs.yml +0 -4
  37. disdrodb/l0/configs/LPM/l0b_encodings.yml +9 -9
  38. disdrodb/l0/configs/LPM/raw_data_format.yml +11 -11
  39. disdrodb/l0/configs/LPM_V0/bins_diameter.yml +103 -0
  40. disdrodb/l0/configs/LPM_V0/bins_velocity.yml +103 -0
  41. disdrodb/l0/configs/LPM_V0/l0a_encodings.yml +45 -0
  42. disdrodb/l0/configs/LPM_V0/l0b_cf_attrs.yml +180 -0
  43. disdrodb/l0/configs/LPM_V0/l0b_encodings.yml +410 -0
  44. disdrodb/l0/configs/LPM_V0/raw_data_format.yml +474 -0
  45. disdrodb/l0/configs/PARSIVEL/l0b_encodings.yml +1 -1
  46. disdrodb/l0/configs/PARSIVEL/raw_data_format.yml +8 -8
  47. disdrodb/l0/configs/PARSIVEL2/raw_data_format.yml +9 -9
  48. disdrodb/l0/l0_reader.py +2 -2
  49. disdrodb/l0/l0a_processing.py +6 -2
  50. disdrodb/l0/l0b_processing.py +26 -19
  51. disdrodb/l0/l0c_processing.py +17 -3
  52. disdrodb/l0/manuals/LPM_V0.pdf +0 -0
  53. disdrodb/l0/readers/LPM/ITALY/GID_LPM.py +15 -7
  54. disdrodb/l0/readers/LPM/ITALY/GID_LPM_PI.py +279 -0
  55. disdrodb/l0/readers/LPM/ITALY/GID_LPM_T.py +276 -0
  56. disdrodb/l0/readers/LPM/ITALY/GID_LPM_W.py +2 -2
  57. disdrodb/l0/readers/LPM/NETHERLANDS/DELFT_RWANDA_LPM_NC.py +103 -0
  58. disdrodb/l0/readers/LPM/NORWAY/HAUKELISETER_LPM.py +216 -0
  59. disdrodb/l0/readers/LPM/NORWAY/NMBU_LPM.py +208 -0
  60. disdrodb/l0/readers/LPM/UK/WITHWORTH_LPM.py +219 -0
  61. disdrodb/l0/readers/LPM/USA/CHARLESTON.py +229 -0
  62. disdrodb/l0/readers/{LPM → LPM_V0}/BELGIUM/ULIEGE.py +33 -49
  63. disdrodb/l0/readers/LPM_V0/ITALY/GID_LPM_V0.py +240 -0
  64. disdrodb/l0/readers/PARSIVEL/BASQUECOUNTRY/EUSKALMET_OTT.py +227 -0
  65. disdrodb/l0/readers/{PARSIVEL2 → PARSIVEL}/NASA/LPVEX.py +16 -28
  66. disdrodb/l0/readers/PARSIVEL/{GPM → NASA}/MC3E.py +1 -1
  67. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010_UF.py +3 -3
  68. disdrodb/l0/readers/PARSIVEL2/BASQUECOUNTRY/EUSKALMET_OTT2.py +232 -0
  69. disdrodb/l0/readers/PARSIVEL2/DENMARK/EROSION_raw.py +1 -1
  70. disdrodb/l0/readers/PARSIVEL2/JAPAN/PRECIP.py +155 -0
  71. disdrodb/l0/readers/PARSIVEL2/MPI/BCO_PARSIVEL2.py +14 -7
  72. disdrodb/l0/readers/PARSIVEL2/MPI/BOWTIE.py +8 -3
  73. disdrodb/l0/readers/PARSIVEL2/NASA/APU.py +28 -5
  74. disdrodb/l0/readers/PARSIVEL2/NCAR/RELAMPAGO_PARSIVEL2.py +1 -1
  75. disdrodb/l0/readers/PARSIVEL2/{GPM/GCPEX.py → NORWAY/UIB.py} +54 -29
  76. disdrodb/l0/readers/PARSIVEL2/PHILIPPINES/{PANGASA.py → PAGASA.py} +6 -3
  77. disdrodb/l0/readers/PARSIVEL2/SPAIN/GRANADA.py +1 -1
  78. disdrodb/l0/readers/PARSIVEL2/SWEDEN/SMHI.py +189 -0
  79. disdrodb/l0/readers/{PARSIVEL/GPM/PIERS.py → PARSIVEL2/USA/CSU.py} +62 -29
  80. disdrodb/l0/readers/PARSIVEL2/USA/{C3WE.py → CW3E.py} +51 -24
  81. disdrodb/l0/readers/{PARSIVEL/GPM/IFLOODS.py → RD80/BRAZIL/ATTO_RD80.py} +50 -34
  82. disdrodb/l0/readers/{SW250 → SWS250}/BELGIUM/KMI.py +1 -1
  83. disdrodb/l1/beard_model.py +45 -1
  84. disdrodb/l1/fall_velocity.py +1 -6
  85. disdrodb/l1/filters.py +2 -0
  86. disdrodb/l1/processing.py +6 -5
  87. disdrodb/l1/resampling.py +101 -38
  88. disdrodb/l2/empirical_dsd.py +12 -8
  89. disdrodb/l2/processing.py +4 -3
  90. disdrodb/metadata/search.py +3 -4
  91. disdrodb/routines/l0.py +4 -4
  92. disdrodb/routines/l1.py +173 -60
  93. disdrodb/routines/l2.py +121 -269
  94. disdrodb/routines/options.py +347 -0
  95. disdrodb/routines/wrappers.py +9 -1
  96. disdrodb/scattering/axis_ratio.py +3 -0
  97. disdrodb/scattering/routines.py +1 -1
  98. disdrodb/summary/routines.py +765 -724
  99. disdrodb/utils/archiving.py +51 -44
  100. disdrodb/utils/attrs.py +1 -1
  101. disdrodb/utils/compression.py +4 -2
  102. disdrodb/utils/dask.py +35 -15
  103. disdrodb/utils/dict.py +33 -0
  104. disdrodb/utils/encoding.py +1 -1
  105. disdrodb/utils/manipulations.py +7 -1
  106. disdrodb/utils/routines.py +9 -8
  107. disdrodb/utils/time.py +9 -1
  108. disdrodb/viz/__init__.py +0 -13
  109. disdrodb/viz/plots.py +209 -0
  110. {disdrodb-0.1.5.dist-info → disdrodb-0.2.1.dist-info}/METADATA +1 -1
  111. {disdrodb-0.1.5.dist-info → disdrodb-0.2.1.dist-info}/RECORD +124 -95
  112. disdrodb/l0/readers/PARSIVEL/GPM/LPVEX.py +0 -85
  113. /disdrodb/etc/products/L2M/{GAMMA_GS_ND_MAE.yaml → MODELS/GAMMA_GS_ND_MAE.yaml} +0 -0
  114. /disdrodb/etc/products/L2M/{GAMMA_ML.yaml → MODELS/GAMMA_ML.yaml} +0 -0
  115. /disdrodb/etc/products/L2M/{LOGNORMAL_GS_LOG_ND_MAE.yaml → MODELS/LOGNORMAL_GS_LOG_ND_MAE.yaml} +0 -0
  116. /disdrodb/etc/products/L2M/{LOGNORMAL_GS_ND_MAE.yaml → MODELS/LOGNORMAL_GS_ND_MAE.yaml} +0 -0
  117. /disdrodb/etc/products/L2M/{LOGNORMAL_ML.yaml → MODELS/LOGNORMAL_ML.yaml} +0 -0
  118. /disdrodb/etc/products/L2M/{NGAMMA_GS_LOG_ND_MAE.yaml → MODELS/NGAMMA_GS_LOG_ND_MAE.yaml} +0 -0
  119. /disdrodb/etc/products/L2M/{NGAMMA_GS_ND_MAE.yaml → MODELS/NGAMMA_GS_ND_MAE.yaml} +0 -0
  120. /disdrodb/etc/products/L2M/{NGAMMA_GS_Z_MAE.yaml → MODELS/NGAMMA_GS_Z_MAE.yaml} +0 -0
  121. /disdrodb/l0/readers/PARSIVEL2/{GPM → NASA}/NSSTC.py +0 -0
  122. {disdrodb-0.1.5.dist-info → disdrodb-0.2.1.dist-info}/WHEEL +0 -0
  123. {disdrodb-0.1.5.dist-info → disdrodb-0.2.1.dist-info}/entry_points.txt +0 -0
  124. {disdrodb-0.1.5.dist-info → disdrodb-0.2.1.dist-info}/licenses/LICENSE +0 -0
  125. {disdrodb-0.1.5.dist-info → disdrodb-0.2.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,347 @@
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
+ # test temporal_resolutions are unique
39
+
40
+ # TODO: test return list
41
+ # get_product_temporal_resolutions(product="L1")
42
+ # get_product_temporal_resolutions(product="L2E")
43
+ # get_product_temporal_resolutions(product="L2M")
44
+
45
+
46
+ def get_product_options(product, temporal_resolution=None, sensor_name=None):
47
+ """Return DISDRODB product options.
48
+
49
+ If temporal resolution is not provided, it returns the global product option.
50
+ If temporal resolution is provided, it provides the custom product options, if specified.
51
+ If product="L1" and sensor_name is specified, it customize product options also by sensor.
52
+ """
53
+ # Retrieve products configuration directory
54
+ products_configs_dir = get_products_configs_dir()
55
+
56
+ # Validate DISDRODB products configuration
57
+ validate_product_configuration(products_configs_dir)
58
+
59
+ # Check product
60
+ check_product(product)
61
+
62
+ # Retrieve global product options (when no temporal resolution !)
63
+ global_options = read_yaml(os.path.join(products_configs_dir, product, "global.yaml"))
64
+ if temporal_resolution is None:
65
+ global_options = check_availability_radar_simulations(global_options)
66
+ return global_options
67
+
68
+ # Check temporal resolution
69
+ check_temporal_resolution(temporal_resolution)
70
+
71
+ # If temporal resolutions are specified, drop 'temporal_resolutions' key
72
+ global_options.pop("temporal_resolutions", None)
73
+
74
+ # Read custom options for specific temporal resolution
75
+ custom_options_path = os.path.join(products_configs_dir, product, f"{temporal_resolution}.yaml")
76
+ if not os.path.exists(custom_options_path):
77
+ return global_options
78
+ custom_options = read_yaml(custom_options_path)
79
+
80
+ # Define product options
81
+ options = global_options.copy()
82
+ if "product_options" in custom_options:
83
+ options["product_options"].update(custom_options.pop("product_options"))
84
+ options.update(custom_options)
85
+
86
+ # Check availability of radar simulations
87
+ options = check_availability_radar_simulations(options)
88
+
89
+ # Customize product options by sensor if L1 product
90
+ if product == "L1" and sensor_name is not None:
91
+ check_sensor_name(sensor_name)
92
+ custom_options_path = os.path.join(products_configs_dir, product, sensor_name, f"{temporal_resolution}.yaml")
93
+ if not os.path.exists(custom_options_path):
94
+ return options
95
+ custom_options = read_yaml(custom_options_path)
96
+ if "product_options" in custom_options:
97
+ options["product_options"].update(custom_options.pop("product_options"))
98
+ return options
99
+
100
+
101
+ def get_product_temporal_resolutions(product):
102
+ """Return DISDRODB products temporal resolutions."""
103
+ # Check only L2E and L2M
104
+ return get_product_options(product)["temporal_resolutions"]
105
+
106
+
107
+ def get_model_options(product, model_name):
108
+ """Return DISDRODB L2M product model options."""
109
+ # Retrieve products configuration directory
110
+ products_configs_dir = get_products_configs_dir()
111
+ model_options_path = os.path.join(products_configs_dir, product, "MODELS", f"{model_name}.yaml")
112
+ model_options = read_yaml(model_options_path)
113
+ return model_options
114
+
115
+
116
+ def check_availability_radar_simulations(options):
117
+ """Check radar simulations are possible for L2E and L2M products."""
118
+ if "radar_enabled" in options and not disdrodb.is_pytmatrix_available():
119
+ options["radar_enabled"] = False
120
+ return options
121
+
122
+
123
+ def validate_product_configuration(products_configs_dir):
124
+ """Validate the DISDRODB products configuration files."""
125
+ # TODO: Implement validation of DISDRODB products configuration files with pydantic
126
+ # TODO: Raise warning if L1 temporal resolutions does not includes all temporal resolutions of L2 products.
127
+ # TODO: Raise warning if L2E temporal resolutions does not includes all temporal resolutions of L2M products.
128
+ # if stategy_event, check neighbor_time_interval >= sample_interval !
129
+ # if temporal_resolution_to_seconds(neighbor_time_interval) < temporal_resolution_to_seconds(sample_interval):
130
+ # msg = "'neighbor_time_interval' must be at least equal to the dataset sample interval ({sample_interval})"
131
+ # raise ValueError(msg)
132
+
133
+ pass
134
+
135
+
136
+ def _define_blocks_offsets(sample_interval, temporal_resolution):
137
+ """Define blocks offset for resampling logic."""
138
+ # Retrieve accumulation_interval and rolling option
139
+ accumulation_interval, rolling = get_sampling_information(temporal_resolution)
140
+
141
+ # Ensure sample_interval and accumulation_interval is numpy.timedelta64
142
+ accumulation_interval = ensure_timedelta_seconds(accumulation_interval)
143
+ sample_interval = ensure_timedelta_seconds(sample_interval)
144
+
145
+ # Define offset to apply to time partitions blocks
146
+ block_starts_offset = 0
147
+ block_ends_offset = 0
148
+
149
+ # If rolling, need to search also in next time block ...
150
+ if rolling and sample_interval != accumulation_interval:
151
+ block_ends_offset = accumulation_interval - sample_interval
152
+ return block_starts_offset, block_ends_offset
153
+
154
+
155
+ class L1ProcessingOptions:
156
+ """Define L1 product processing options."""
157
+
158
+ def __init__(self, filepaths, parallel, sensor_name, temporal_resolutions=None):
159
+ """Define DISDRODB L1 product processing options."""
160
+ product = "L1"
161
+
162
+ # ---------------------------------------------------------------------.
163
+ # Define temporal resolutions for which to retrieve processing options
164
+ if temporal_resolutions is None:
165
+ temporal_resolutions = get_product_temporal_resolutions(product)
166
+ elif isinstance(temporal_resolutions, str):
167
+ temporal_resolutions = [temporal_resolutions]
168
+ _ = [check_temporal_resolution(temporal_resolution) for temporal_resolution in temporal_resolutions]
169
+
170
+ # ---------------------------------------------------------------------.
171
+ # Get product options at various temporal resolutions
172
+ src_dict_product_options = {
173
+ temporal_resolution: get_product_options(
174
+ product=product,
175
+ temporal_resolution=temporal_resolution,
176
+ sensor_name=sensor_name,
177
+ )
178
+ for temporal_resolution in temporal_resolutions
179
+ }
180
+
181
+ # ---------------------------------------------------------------------.
182
+ # Group filepaths by source sample intervals
183
+ # - Typically the sample interval is fixed and is just one
184
+ # - Some stations might change the sample interval along the years
185
+ # - For each sample interval, separated processing must take place
186
+ dict_filepaths = group_filepaths(filepaths, groups="sample_interval")
187
+
188
+ # ---------------------------------------------------------------------.
189
+ # Retrieve processing information for each temporal resolution
190
+ dict_product_options = {}
191
+ dict_folder_partitioning = {}
192
+ dict_files_partitions = {}
193
+ _cache_dict_temporal_partitions: dict[str, dict] = {}
194
+ # temporal_resolution = temporal_resolutions[0]
195
+ for temporal_resolution in temporal_resolutions:
196
+
197
+ # -------------------------------------------------------------------------.
198
+ # Retrieve product options
199
+ product_options = src_dict_product_options[temporal_resolution].copy()
200
+
201
+ # Extract processing options
202
+ archive_options = product_options.pop("archive_options")
203
+
204
+ dict_product_options[temporal_resolution] = product_options
205
+ # -------------------------------------------------------------------------.
206
+ # Define folder partitioning
207
+ if "folder_partitioning" not in archive_options:
208
+ dict_folder_partitioning[temporal_resolution] = disdrodb.config.get("folder_partitioning")
209
+ else:
210
+ dict_folder_partitioning[temporal_resolution] = archive_options.pop("folder_partitioning")
211
+
212
+ # -------------------------------------------------------------------------.
213
+ # Define list of temporal partitions
214
+ # - [{start_time: np.datetime64, end_time: np.datetime64}, ....]
215
+ # - Either strategy: "event" or "time_block" or save_by_time_block"
216
+ # - "event" requires loading data into memory to identify events
217
+ # --> Does some data filtering on what to process !
218
+ # - "time_block" does not require loading data into memory
219
+ # --> Does not do data filtering on what to process !
220
+ # --> Here we cache dict_temporal_partitions so that we don't need to recompute
221
+ # stuffs if processing options are the same
222
+ # --> Using time_block with e.g. freq="day" we get start/end in the format of 00:00:00-23:59:59
223
+ key = json.dumps(archive_options, sort_keys=True)
224
+ if key not in _cache_dict_temporal_partitions:
225
+ _cache_dict_temporal_partitions[key] = {
226
+ sample_interval: define_temporal_partitions(filepaths, parallel=parallel, **archive_options)
227
+ for sample_interval, filepaths in dict_filepaths.items()
228
+ }
229
+ dict_temporal_partitions = _cache_dict_temporal_partitions[key].copy() # To avoid in-place replacement
230
+
231
+ # ------------------------------------------------------------------.
232
+ # Group filepaths by temporal partitions
233
+ # - This is done separately for each possible source sample interval
234
+ # - It groups filepaths by start_time and end_time provided by temporal_partitions
235
+ # --> Output: [{'start_time': ..., 'end_time': ..., filepaths: [...]}, ...]
236
+ # - In L0C we ensure that the time reported correspond to the start of the measurement interval.
237
+ # - When aggregating/resampling/accumulating data, we need to load also some data after the
238
+ # actual end_time of the time partition to ensure that
239
+ # the resampled dataset contains the timesteps of the partition end time.
240
+ # --> Use of block_starts_offset and block_ends_offset in group_files_by_temporal_partitions
241
+ # - ATTENTION: group_files_by_temporal_partitions returns
242
+ # start_time and end_time as datetime.datetime64objects !
243
+ # - ATTENTION: Files within each files_partitions block have the same sample_interval !
244
+
245
+ # sample_interval = 30
246
+ # temporal_partitions = dict_temporal_partitions[sample_interval]
247
+
248
+ files_partitions = []
249
+ for sample_interval, temporal_partitions in dict_temporal_partitions.items():
250
+ if is_possible_product(
251
+ temporal_resolution=temporal_resolution,
252
+ sample_interval=sample_interval,
253
+ ):
254
+
255
+ block_starts_offset, block_ends_offset = _define_blocks_offsets(
256
+ sample_interval=sample_interval,
257
+ temporal_resolution=temporal_resolution,
258
+ )
259
+
260
+ files_partitions.append(
261
+ group_files_by_temporal_partitions(
262
+ temporal_partitions=temporal_partitions,
263
+ filepaths=dict_filepaths[sample_interval],
264
+ block_starts_offset=block_starts_offset,
265
+ block_ends_offset=block_ends_offset,
266
+ ),
267
+ )
268
+ files_partitions = flatten_list(files_partitions)
269
+ dict_files_partitions[temporal_resolution] = files_partitions
270
+
271
+ # ------------------------------------------------------------------.
272
+ # Keep only temporal_resolutions for which products can be defined
273
+ # - Remove e.g when not compatible accumulation_interval with source sample_interval
274
+ temporal_resolutions = [
275
+ temporal_resolution
276
+ for temporal_resolution in temporal_resolutions
277
+ if len(dict_files_partitions[temporal_resolution]) > 0
278
+ ]
279
+ # ------------------------------------------------------------------.
280
+ # Add attributes
281
+ self.temporal_resolutions = temporal_resolutions
282
+ self.dict_files_partitions = dict_files_partitions
283
+ self.dict_product_options = dict_product_options
284
+ self.dict_folder_partitioning = dict_folder_partitioning
285
+
286
+ def group_files_by_temporal_partitions(self, temporal_resolution):
287
+ """Return files partitions dictionary for a specific L2E product."""
288
+ return self.dict_files_partitions[temporal_resolution]
289
+
290
+ def get_product_options(self, temporal_resolution):
291
+ """Return product options dictionary for a specific L2E product."""
292
+ return self.dict_product_options[temporal_resolution]
293
+
294
+ def get_folder_partitioning(self, temporal_resolution):
295
+ """Return the folder partitioning for a specific L2E product."""
296
+ # to be used for logs and files !
297
+ return self.dict_folder_partitioning[temporal_resolution]
298
+
299
+
300
+ class L2ProcessingOptions:
301
+ """Define L2 products processing options."""
302
+
303
+ def __init__(self, product, filepaths, parallel, temporal_resolution):
304
+ """Define DISDRODB L2 products processing options."""
305
+ import disdrodb
306
+
307
+ # Check temporal resolution
308
+ check_temporal_resolution(temporal_resolution)
309
+
310
+ # Get product options
311
+ product_options = get_product_options(product, temporal_resolution=temporal_resolution)
312
+
313
+ # Extract processing options
314
+ archive_options = product_options.pop("archive_options")
315
+
316
+ # Define folder partitioning
317
+ if "folder_partitioning" not in archive_options:
318
+ folder_partitioning = disdrodb.config.get("folder_partitioning")
319
+ else:
320
+ folder_partitioning = archive_options.pop("folder_partitioning")
321
+
322
+ # Define files temporal partitions
323
+ # - [{start_time: np.datetime64, end_time: np.datetime64}, ....]
324
+ # - Either strategy: "event" or "time_block"
325
+ # - "strategy=event" requires loading data into memory to identify events
326
+ # --> Does some data filtering on what to process !
327
+ # - "strategy=time_block" does not require loading data into memory
328
+ # --> Does not do data filtering on what to process !
329
+ temporal_partitions = define_temporal_partitions(filepaths, parallel=parallel, **archive_options)
330
+
331
+ # ------------------------------------------------------------------.
332
+ # Group filepaths by temporal partitions
333
+ # - It groups filepaths by start_time and end_time provided by temporal_partitions
334
+ # - ATTENTION: group_files_by_temporal_partitions returns
335
+ # start_time and end_time as datetime.datetime64 objects !
336
+ files_partitions = group_files_by_temporal_partitions(
337
+ temporal_partitions=temporal_partitions,
338
+ filepaths=filepaths,
339
+ )
340
+ files_partitions = flatten_list(files_partitions)
341
+
342
+ # ------------------------------------------------------------------.
343
+ # Add attributes
344
+ # self.temporal_partitions = temporal_partitions
345
+ self.folder_partitioning = folder_partitioning
346
+ self.files_partitions = files_partitions
347
+ self.product_options = product_options
@@ -1468,6 +1468,14 @@ def create_summary(
1468
1468
  The directory path must end with ``<...>/DISDRODB``.
1469
1469
  If ``None``, it uses the ``data_archive_dir`` path specified
1470
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``.
1471
1479
  """
1472
1480
  # Get list of available stations
1473
1481
  list_info = available_stations(
@@ -1480,7 +1488,7 @@ def create_summary(
1480
1488
  station_names=station_names,
1481
1489
  # Search options
1482
1490
  product="L2E",
1483
- product_kwargs={"rolling": False, "sample_interval": 60},
1491
+ temporal_resolution=temporal_resolution,
1484
1492
  raise_error_if_empty=True,
1485
1493
  )
1486
1494
 
@@ -83,6 +83,9 @@ def get_axis_ratio_battaglia_2010(diameter):
83
83
  """
84
84
  Compute the axis ratio of raindrops using the Battaglia et al. (2010) model.
85
85
 
86
+ This axis ratio is assumed by OTT Parsivel sensors internally to compute the
87
+ reported particle size (Deq).
88
+
86
89
  Parameters
87
90
  ----------
88
91
  diameter : array-like
@@ -973,7 +973,7 @@ def get_radar_parameters(
973
973
  list_ds = [func(ds_subset, **params) for params in list_params]
974
974
 
975
975
  # Merge into a single dataset
976
- ds_radar = xr.merge(list_ds)
976
+ ds_radar = xr.merge(list_ds, compat="no_conflicts", join="outer")
977
977
 
978
978
  # Order frequency from lowest to highest
979
979
  # --> ['S', 'C', 'X', 'Ku', 'K', 'Ka', 'W']