disdrodb 0.0.21__py3-none-any.whl → 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. disdrodb/__init__.py +132 -15
  2. disdrodb/_config.py +4 -2
  3. disdrodb/_version.py +9 -4
  4. disdrodb/api/checks.py +264 -237
  5. disdrodb/api/configs.py +4 -8
  6. disdrodb/api/create_directories.py +235 -290
  7. disdrodb/api/info.py +217 -26
  8. disdrodb/api/io.py +295 -269
  9. disdrodb/api/path.py +597 -173
  10. disdrodb/api/search.py +486 -0
  11. disdrodb/{metadata/scripts → cli}/disdrodb_check_metadata_archive.py +12 -7
  12. disdrodb/{utils/pandas.py → cli/disdrodb_data_archive_directory.py} +9 -18
  13. disdrodb/cli/disdrodb_download_archive.py +86 -0
  14. disdrodb/cli/disdrodb_download_metadata_archive.py +53 -0
  15. disdrodb/cli/disdrodb_download_station.py +84 -0
  16. disdrodb/{api/scripts → cli}/disdrodb_initialize_station.py +22 -10
  17. disdrodb/cli/disdrodb_metadata_archive_directory.py +32 -0
  18. disdrodb/{data_transfer/scripts/disdrodb_download_station.py → cli/disdrodb_open_data_archive.py} +22 -22
  19. disdrodb/cli/disdrodb_open_logs_directory.py +69 -0
  20. disdrodb/{data_transfer/scripts/disdrodb_upload_station.py → cli/disdrodb_open_metadata_archive.py} +22 -24
  21. disdrodb/cli/disdrodb_open_metadata_directory.py +71 -0
  22. disdrodb/cli/disdrodb_open_product_directory.py +74 -0
  23. disdrodb/cli/disdrodb_open_readers_directory.py +32 -0
  24. disdrodb/{l0/scripts → cli}/disdrodb_run_l0.py +38 -31
  25. disdrodb/{l0/scripts → cli}/disdrodb_run_l0_station.py +32 -30
  26. disdrodb/{l0/scripts → cli}/disdrodb_run_l0a.py +30 -21
  27. disdrodb/{l0/scripts → cli}/disdrodb_run_l0a_station.py +24 -33
  28. disdrodb/{l0/scripts → cli}/disdrodb_run_l0b.py +30 -21
  29. disdrodb/{l0/scripts → cli}/disdrodb_run_l0b_station.py +25 -34
  30. disdrodb/cli/disdrodb_run_l0c.py +130 -0
  31. disdrodb/cli/disdrodb_run_l0c_station.py +129 -0
  32. disdrodb/cli/disdrodb_run_l1.py +122 -0
  33. disdrodb/cli/disdrodb_run_l1_station.py +121 -0
  34. disdrodb/cli/disdrodb_run_l2e.py +122 -0
  35. disdrodb/cli/disdrodb_run_l2e_station.py +122 -0
  36. disdrodb/cli/disdrodb_run_l2m.py +122 -0
  37. disdrodb/cli/disdrodb_run_l2m_station.py +122 -0
  38. disdrodb/cli/disdrodb_upload_archive.py +105 -0
  39. disdrodb/cli/disdrodb_upload_station.py +98 -0
  40. disdrodb/configs.py +90 -25
  41. disdrodb/data_transfer/__init__.py +22 -0
  42. disdrodb/data_transfer/download_data.py +87 -90
  43. disdrodb/data_transfer/upload_data.py +64 -37
  44. disdrodb/data_transfer/zenodo.py +15 -18
  45. disdrodb/docs.py +1 -1
  46. disdrodb/issue/__init__.py +17 -4
  47. disdrodb/issue/checks.py +10 -23
  48. disdrodb/issue/reader.py +9 -12
  49. disdrodb/issue/writer.py +14 -17
  50. disdrodb/l0/__init__.py +17 -26
  51. disdrodb/l0/check_configs.py +35 -23
  52. disdrodb/l0/check_standards.py +32 -42
  53. disdrodb/l0/configs/{Thies_LPM → LPM}/bins_diameter.yml +44 -44
  54. disdrodb/l0/configs/{Thies_LPM → LPM}/bins_velocity.yml +40 -40
  55. disdrodb/l0/configs/LPM/l0a_encodings.yml +80 -0
  56. disdrodb/l0/configs/{Thies_LPM → LPM}/l0b_cf_attrs.yml +62 -59
  57. disdrodb/l0/configs/{Thies_LPM → LPM}/l0b_encodings.yml +9 -9
  58. disdrodb/l0/configs/{Thies_LPM → LPM}/raw_data_format.yml +245 -245
  59. disdrodb/l0/configs/{OTT_Parsivel → PARSIVEL}/bins_diameter.yml +66 -66
  60. disdrodb/l0/configs/{OTT_Parsivel → PARSIVEL}/bins_velocity.yml +64 -64
  61. disdrodb/l0/configs/PARSIVEL/l0a_encodings.yml +32 -0
  62. disdrodb/l0/configs/{OTT_Parsivel → PARSIVEL}/l0b_cf_attrs.yml +22 -20
  63. disdrodb/l0/configs/{OTT_Parsivel → PARSIVEL}/l0b_encodings.yml +17 -17
  64. disdrodb/l0/configs/{OTT_Parsivel → PARSIVEL}/raw_data_format.yml +77 -77
  65. disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/bins_diameter.yml +64 -64
  66. disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/bins_velocity.yml +64 -64
  67. disdrodb/l0/configs/PARSIVEL2/l0a_encodings.yml +39 -0
  68. disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/l0b_cf_attrs.yml +24 -22
  69. disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/l0b_encodings.yml +20 -20
  70. disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/raw_data_format.yml +98 -98
  71. disdrodb/l0/configs/{RD_80 → RD80}/bins_diameter.yml +40 -40
  72. disdrodb/l0/configs/RD80/l0a_encodings.yml +16 -0
  73. disdrodb/l0/configs/{RD_80 → RD80}/l0b_cf_attrs.yml +3 -3
  74. disdrodb/l0/configs/RD80/l0b_encodings.yml +135 -0
  75. disdrodb/l0/configs/{RD_80 → RD80}/raw_data_format.yml +48 -48
  76. disdrodb/l0/l0_reader.py +216 -340
  77. disdrodb/l0/l0a_processing.py +237 -208
  78. disdrodb/l0/l0b_nc_processing.py +227 -80
  79. disdrodb/l0/l0b_processing.py +93 -173
  80. disdrodb/l0/l0c_processing.py +627 -0
  81. disdrodb/l0/readers/{ARM → LPM/ARM}/ARM_LPM.py +36 -58
  82. disdrodb/l0/readers/LPM/AUSTRALIA/MELBOURNE_2007_LPM.py +226 -0
  83. disdrodb/l0/readers/LPM/BRAZIL/CHUVA_LPM.py +185 -0
  84. disdrodb/l0/readers/LPM/BRAZIL/GOAMAZON_LPM.py +183 -0
  85. disdrodb/l0/readers/LPM/ITALY/GID_LPM.py +179 -0
  86. disdrodb/l0/readers/{UK → LPM/UK}/DIVEN.py +14 -35
  87. disdrodb/l0/readers/PARSIVEL/AUSTRALIA/MELBOURNE_2007_PARSIVEL.py +157 -0
  88. disdrodb/l0/readers/PARSIVEL/CHINA/CHONGQING.py +113 -0
  89. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/ARCTIC_2021.py +40 -57
  90. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/COMMON_2011.py +37 -54
  91. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/DAVOS_2009_2011.py +34 -51
  92. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/EPFL_2009.py +34 -51
  93. disdrodb/l0/readers/{EPFL/PARADISO_2014.py → PARSIVEL/EPFL/EPFL_ROOF_2008.py} +38 -50
  94. disdrodb/l0/readers/PARSIVEL/EPFL/EPFL_ROOF_2010.py +105 -0
  95. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/EPFL_ROOF_2011.py +34 -51
  96. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/EPFL_ROOF_2012.py +33 -51
  97. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/GENEPI_2007.py +25 -44
  98. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/GRAND_ST_BERNARD_2007.py +25 -44
  99. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/GRAND_ST_BERNARD_2007_2.py +25 -44
  100. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/HPICONET_2010.py +34 -51
  101. disdrodb/l0/readers/{EPFL/EPFL_ROOF_2010.py → PARSIVEL/EPFL/HYMEX_LTE_SOP2.py} +37 -50
  102. disdrodb/l0/readers/PARSIVEL/EPFL/HYMEX_LTE_SOP3.py +111 -0
  103. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/HYMEX_LTE_SOP4.py +36 -54
  104. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/LOCARNO_2018.py +34 -52
  105. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/LOCARNO_2019.py +38 -56
  106. disdrodb/l0/readers/PARSIVEL/EPFL/PARADISO_2014.py +105 -0
  107. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/PARSIVEL_2007.py +27 -45
  108. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/PLATO_2019.py +24 -44
  109. disdrodb/l0/readers/PARSIVEL/EPFL/RACLETS_2019.py +140 -0
  110. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/RACLETS_2019_WJF.py +41 -59
  111. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/RIETHOLZBACH_2011.py +34 -51
  112. disdrodb/l0/readers/PARSIVEL/EPFL/SAMOYLOV_2017.py +117 -0
  113. disdrodb/l0/readers/PARSIVEL/EPFL/SAMOYLOV_2019.py +137 -0
  114. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/UNIL_2022.py +42 -55
  115. disdrodb/l0/readers/PARSIVEL/GPM/IFLOODS.py +104 -0
  116. disdrodb/l0/readers/{GPM → PARSIVEL/GPM}/LPVEX.py +29 -48
  117. disdrodb/l0/readers/PARSIVEL/GPM/MC3E.py +184 -0
  118. disdrodb/l0/readers/PARSIVEL/NCAR/CCOPE_2015.py +113 -0
  119. disdrodb/l0/readers/{NCAR/VORTEX_SE_2016_P1.py → PARSIVEL/NCAR/OWLES_MIPS.py} +46 -72
  120. disdrodb/l0/readers/PARSIVEL/NCAR/PECAN_MOBILE.py +125 -0
  121. disdrodb/l0/readers/{NCAR/OWLES_MIPS.py → PARSIVEL/NCAR/PLOWS_MIPS.py} +45 -64
  122. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2009.py +114 -0
  123. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010.py +176 -0
  124. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010_UF.py +183 -0
  125. disdrodb/l0/readers/{ARM/ARM_LD.py → PARSIVEL2/ARM/ARM_PARSIVEL2.py} +27 -50
  126. disdrodb/l0/readers/PARSIVEL2/BRAZIL/CHUVA_PARSIVEL2.py +163 -0
  127. disdrodb/l0/readers/PARSIVEL2/BRAZIL/GOAMAZON_PARSIVEL2.py +163 -0
  128. disdrodb/l0/readers/{DENMARK → PARSIVEL2/DENMARK}/EROSION_nc.py +14 -35
  129. disdrodb/l0/readers/PARSIVEL2/FRANCE/SIRTA_PARSIVEL2.py +119 -0
  130. disdrodb/l0/readers/PARSIVEL2/GPM/GCPEX.py +104 -0
  131. disdrodb/l0/readers/PARSIVEL2/GPM/NSSTC.py +176 -0
  132. disdrodb/l0/readers/PARSIVEL2/ITALY/GID_PARSIVEL2.py +32 -0
  133. disdrodb/l0/readers/PARSIVEL2/MEXICO/OH_IIUNAM_nc.py +56 -0
  134. disdrodb/l0/readers/PARSIVEL2/NCAR/PECAN_FP3.py +120 -0
  135. disdrodb/l0/readers/{NCAR → PARSIVEL2/NCAR}/PECAN_MIPS.py +45 -64
  136. disdrodb/l0/readers/PARSIVEL2/NCAR/RELAMPAGO_PARSIVEL2.py +181 -0
  137. disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_PJ.py +160 -0
  138. disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_SB.py +160 -0
  139. disdrodb/l0/readers/{NCAR/PLOWS_MIPS.py → PARSIVEL2/NCAR/VORTEX_SE_2016_P1.py} +49 -66
  140. disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_P2.py +118 -0
  141. disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_PIPS.py +152 -0
  142. disdrodb/l0/readers/PARSIVEL2/NETHERLANDS/DELFT.py +166 -0
  143. disdrodb/l0/readers/{NCAR/RELAMPAGO_RD80.py → RD80/BRAZIL/CHUVA_RD80.py} +36 -60
  144. disdrodb/l0/readers/{BRAZIL → RD80/BRAZIL}/GOAMAZON_RD80.py +36 -55
  145. disdrodb/l0/readers/{NCAR → RD80/NCAR}/CINDY_2011_RD80.py +35 -54
  146. disdrodb/l0/readers/{BRAZIL/CHUVA_RD80.py → RD80/NCAR/RELAMPAGO_RD80.py} +40 -54
  147. disdrodb/l0/readers/template_reader_raw_netcdf_data.py +62 -0
  148. disdrodb/l0/readers/{reader_template.py → template_reader_raw_text_data.py} +20 -44
  149. disdrodb/l0/routines.py +885 -581
  150. disdrodb/l0/standards.py +72 -236
  151. disdrodb/l0/template_tools.py +104 -109
  152. disdrodb/l1/__init__.py +17 -0
  153. disdrodb/l1/beard_model.py +716 -0
  154. disdrodb/l1/encoding_attrs.py +620 -0
  155. disdrodb/l1/fall_velocity.py +260 -0
  156. disdrodb/l1/filters.py +192 -0
  157. disdrodb/l1/processing.py +200 -0
  158. disdrodb/l1/resampling.py +236 -0
  159. disdrodb/l1/routines.py +357 -0
  160. disdrodb/l1_env/__init__.py +17 -0
  161. disdrodb/l1_env/routines.py +38 -0
  162. disdrodb/l2/__init__.py +17 -0
  163. disdrodb/l2/empirical_dsd.py +1735 -0
  164. disdrodb/l2/event.py +388 -0
  165. disdrodb/l2/processing.py +519 -0
  166. disdrodb/l2/processing_options.py +213 -0
  167. disdrodb/l2/routines.py +868 -0
  168. disdrodb/metadata/__init__.py +9 -2
  169. disdrodb/metadata/checks.py +165 -118
  170. disdrodb/metadata/download.py +81 -0
  171. disdrodb/metadata/geolocation.py +146 -0
  172. disdrodb/metadata/info.py +20 -13
  173. disdrodb/metadata/manipulation.py +1 -1
  174. disdrodb/metadata/reader.py +59 -8
  175. disdrodb/metadata/search.py +77 -144
  176. disdrodb/metadata/standards.py +7 -8
  177. disdrodb/metadata/writer.py +8 -14
  178. disdrodb/psd/__init__.py +38 -0
  179. disdrodb/psd/fitting.py +2146 -0
  180. disdrodb/psd/models.py +774 -0
  181. disdrodb/routines.py +1176 -0
  182. disdrodb/scattering/__init__.py +28 -0
  183. disdrodb/scattering/axis_ratio.py +344 -0
  184. disdrodb/scattering/routines.py +456 -0
  185. disdrodb/utils/__init__.py +17 -0
  186. disdrodb/utils/attrs.py +208 -0
  187. disdrodb/utils/cli.py +269 -0
  188. disdrodb/utils/compression.py +60 -42
  189. disdrodb/utils/dask.py +62 -0
  190. disdrodb/utils/decorators.py +110 -0
  191. disdrodb/utils/directories.py +107 -46
  192. disdrodb/utils/encoding.py +127 -0
  193. disdrodb/utils/list.py +29 -0
  194. disdrodb/utils/logger.py +168 -46
  195. disdrodb/utils/time.py +657 -0
  196. disdrodb/utils/warnings.py +30 -0
  197. disdrodb/utils/writer.py +57 -0
  198. disdrodb/utils/xarray.py +138 -47
  199. disdrodb/utils/yaml.py +0 -1
  200. disdrodb/viz/__init__.py +17 -0
  201. disdrodb/viz/plots.py +17 -0
  202. disdrodb-0.1.0.dist-info/METADATA +321 -0
  203. disdrodb-0.1.0.dist-info/RECORD +216 -0
  204. {disdrodb-0.0.21.dist-info → disdrodb-0.1.0.dist-info}/WHEEL +1 -1
  205. disdrodb-0.1.0.dist-info/entry_points.txt +30 -0
  206. disdrodb/data_transfer/scripts/disdrodb_download_archive.py +0 -53
  207. disdrodb/data_transfer/scripts/disdrodb_upload_archive.py +0 -57
  208. disdrodb/l0/configs/OTT_Parsivel/l0a_encodings.yml +0 -32
  209. disdrodb/l0/configs/OTT_Parsivel2/l0a_encodings.yml +0 -39
  210. disdrodb/l0/configs/RD_80/l0a_encodings.yml +0 -16
  211. disdrodb/l0/configs/RD_80/l0b_encodings.yml +0 -135
  212. disdrodb/l0/configs/Thies_LPM/l0a_encodings.yml +0 -80
  213. disdrodb/l0/io.py +0 -257
  214. disdrodb/l0/l0_processing.py +0 -1091
  215. disdrodb/l0/readers/AUSTRALIA/MELBOURNE_2007_OTT.py +0 -178
  216. disdrodb/l0/readers/AUSTRALIA/MELBOURNE_2007_THIES.py +0 -247
  217. disdrodb/l0/readers/BRAZIL/CHUVA_LPM.py +0 -204
  218. disdrodb/l0/readers/BRAZIL/CHUVA_OTT.py +0 -183
  219. disdrodb/l0/readers/BRAZIL/GOAMAZON_LPM.py +0 -204
  220. disdrodb/l0/readers/BRAZIL/GOAMAZON_OTT.py +0 -183
  221. disdrodb/l0/readers/CHINA/CHONGQING.py +0 -131
  222. disdrodb/l0/readers/EPFL/EPFL_ROOF_2008.py +0 -128
  223. disdrodb/l0/readers/EPFL/HYMEX_LTE_SOP2.py +0 -127
  224. disdrodb/l0/readers/EPFL/HYMEX_LTE_SOP3.py +0 -129
  225. disdrodb/l0/readers/EPFL/RACLETS_2019.py +0 -158
  226. disdrodb/l0/readers/EPFL/SAMOYLOV_2017.py +0 -136
  227. disdrodb/l0/readers/EPFL/SAMOYLOV_2019.py +0 -158
  228. disdrodb/l0/readers/FRANCE/SIRTA_OTT2.py +0 -138
  229. disdrodb/l0/readers/GPM/GCPEX.py +0 -123
  230. disdrodb/l0/readers/GPM/IFLOODS.py +0 -123
  231. disdrodb/l0/readers/GPM/MC3E.py +0 -123
  232. disdrodb/l0/readers/GPM/NSSTC.py +0 -164
  233. disdrodb/l0/readers/ITALY/GID.py +0 -199
  234. disdrodb/l0/readers/MEXICO/OH_IIUNAM_nc.py +0 -92
  235. disdrodb/l0/readers/NCAR/CCOPE_2015.py +0 -133
  236. disdrodb/l0/readers/NCAR/PECAN_FP3.py +0 -137
  237. disdrodb/l0/readers/NCAR/PECAN_MOBILE.py +0 -144
  238. disdrodb/l0/readers/NCAR/RELAMPAGO_OTT.py +0 -195
  239. disdrodb/l0/readers/NCAR/SNOWIE_PJ.py +0 -172
  240. disdrodb/l0/readers/NCAR/SNOWIE_SB.py +0 -179
  241. disdrodb/l0/readers/NCAR/VORTEX2_2009.py +0 -133
  242. disdrodb/l0/readers/NCAR/VORTEX2_2010.py +0 -188
  243. disdrodb/l0/readers/NCAR/VORTEX2_2010_UF.py +0 -191
  244. disdrodb/l0/readers/NCAR/VORTEX_SE_2016_P2.py +0 -135
  245. disdrodb/l0/readers/NCAR/VORTEX_SE_2016_PIPS.py +0 -170
  246. disdrodb/l0/readers/NETHERLANDS/DELFT.py +0 -187
  247. disdrodb/l0/readers/SPAIN/SBEGUERIA.py +0 -179
  248. disdrodb/l0/scripts/disdrodb_run_l0b_concat.py +0 -93
  249. disdrodb/l0/scripts/disdrodb_run_l0b_concat_station.py +0 -85
  250. disdrodb/utils/netcdf.py +0 -452
  251. disdrodb/utils/scripts.py +0 -102
  252. disdrodb-0.0.21.dist-info/AUTHORS.md +0 -18
  253. disdrodb-0.0.21.dist-info/METADATA +0 -186
  254. disdrodb-0.0.21.dist-info/RECORD +0 -168
  255. disdrodb-0.0.21.dist-info/entry_points.txt +0 -15
  256. /disdrodb/l0/configs/{RD_80 → RD80}/bins_velocity.yml +0 -0
  257. /disdrodb/l0/manuals/{Thies_LPM.pdf → LPM.pdf} +0 -0
  258. /disdrodb/l0/manuals/{ODM_470.pdf → ODM470.pdf} +0 -0
  259. /disdrodb/l0/manuals/{OTT_Parsivel.pdf → PARSIVEL.pdf} +0 -0
  260. /disdrodb/l0/manuals/{OTT_Parsivel2.pdf → PARSIVEL2.pdf} +0 -0
  261. /disdrodb/l0/manuals/{PWS_100.pdf → PWS100.pdf} +0 -0
  262. /disdrodb/l0/manuals/{RD_80.pdf → RD80.pdf} +0 -0
  263. {disdrodb-0.0.21.dist-info → disdrodb-0.1.0.dist-info/licenses}/LICENSE +0 -0
  264. {disdrodb-0.0.21.dist-info → disdrodb-0.1.0.dist-info}/top_level.txt +0 -0
disdrodb/utils/cli.py ADDED
@@ -0,0 +1,269 @@
1
+ #!/usr/bin/env python3
2
+
3
+ # -----------------------------------------------------------------------------.
4
+ # Copyright (c) 2021-2023 DISDRODB developers
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ # -----------------------------------------------------------------------------.
19
+ """DISDRODB command-line-interface scripts utilities."""
20
+
21
+ import click
22
+
23
+
24
+ def _execute_cmd(cmd, raise_error=False):
25
+ """Execute command in the terminal, streaming output in python console."""
26
+ from subprocess import PIPE, CalledProcessError, Popen
27
+
28
+ with Popen(cmd, shell=True, stdout=PIPE, bufsize=1, universal_newlines=True) as p:
29
+ for line in p.stdout:
30
+ print(line, end="")
31
+
32
+ # Raise error if command didn't run successfully
33
+ if p.returncode != 0 and raise_error:
34
+ raise CalledProcessError(p.returncode, p.args)
35
+
36
+
37
+ def _parse_empty_string_and_none(args):
38
+ """Utility to parse argument passed from the command line.
39
+
40
+ If ``args = ''``, returns None.
41
+ If ``args = 'None'`` returns None.
42
+ Otherwise return ``args``.
43
+ """
44
+ # If '', set to 'None'
45
+ args = None if args == "" else args
46
+ # - If multiple arguments, split by space
47
+ if isinstance(args, str) and args == "None":
48
+ args = None
49
+ return args
50
+
51
+
52
+ def parse_arg_to_list(args):
53
+ """Utility to pass list to command line scripts.
54
+
55
+ If ``args = ''`` returns ``None``.
56
+ If ``args = 'None'`` returns ``None``.
57
+ If ``args = 'variable'`` returns ``[variable]``.
58
+ If ``args = 'variable1 variable2'`` returns ``[variable1, variable2]``.
59
+ """
60
+ # If '' or 'None' --> Set to None
61
+ args = _parse_empty_string_and_none(args)
62
+ # - If multiple arguments, split by space
63
+ if isinstance(args, str):
64
+ # - Split by space
65
+ list_args = args.split(" ")
66
+ # - Remove '' (deal with multi space)
67
+ args = [args for args in list_args if len(args) > 0]
68
+ return args
69
+
70
+
71
+ def parse_archive_dir(archive_dir: str):
72
+ """Utility to parse archive directories provided by command line.
73
+
74
+ If ``archive_dir = 'None'`` returns ``None``.
75
+ If ``archive_dir = ''`` returns ``None``.
76
+ """
77
+ # If '', set to 'None'
78
+ return _parse_empty_string_and_none(archive_dir)
79
+
80
+
81
+ def click_station_arguments(function: object):
82
+ """Click command line arguments for DISDRODB station processing.
83
+
84
+ Parameters
85
+ ----------
86
+ function : object
87
+ Function.
88
+ """
89
+ function = click.argument("station_name", metavar="<station>")(function)
90
+ function = click.argument("campaign_name", metavar="<CAMPAIGN_NAME>")(function)
91
+ function = click.argument("data_source", metavar="<DATA_SOURCE>")(function)
92
+ return function
93
+
94
+
95
+ def click_data_archive_dir_option(function: object):
96
+ """Click command line argument for DISDRODB ``data_archive_dir``.
97
+
98
+ Parameters
99
+ ----------
100
+ function : object
101
+ Function.
102
+ """
103
+ function = click.option(
104
+ "--data_archive_dir",
105
+ type=str,
106
+ show_default=True,
107
+ default=None,
108
+ help="DISDRODB base directory",
109
+ )(function)
110
+ return function
111
+
112
+
113
+ def click_metadata_archive_dir_option(function: object):
114
+ """Click command line argument for DISDRODB ``metadata_archive_dir``.
115
+
116
+ Parameters
117
+ ----------
118
+ function : object
119
+ Function.
120
+ """
121
+ function = click.option(
122
+ "--metadata_archive_dir",
123
+ type=str,
124
+ show_default=True,
125
+ default=None,
126
+ help="DISDRODB Metadata Archive Directory",
127
+ )(function)
128
+ return function
129
+
130
+
131
+ def click_stations_options(function: object):
132
+ """Click command line options for DISDRODB archive L0 processing.
133
+
134
+ Parameters
135
+ ----------
136
+ function : object
137
+ Function.
138
+ """
139
+ function = click.option(
140
+ "--data_sources",
141
+ type=str,
142
+ show_default=True,
143
+ default="",
144
+ help="DISDRODB data sources to process",
145
+ )(function)
146
+ function = click.option(
147
+ "--campaign_names",
148
+ type=str,
149
+ show_default=True,
150
+ default="",
151
+ help="DISDRODB campaign names to process",
152
+ )(function)
153
+ function = click.option(
154
+ "--station_names",
155
+ type=str,
156
+ show_default=True,
157
+ default="",
158
+ help="DISDRODB station names to process",
159
+ )(function)
160
+ return function
161
+
162
+
163
+ def click_processing_options(function: object):
164
+ """Click command line default parameters for L0 processing options.
165
+
166
+ Parameters
167
+ ----------
168
+ function : object
169
+ Function.
170
+ """
171
+ function = click.option(
172
+ "-p",
173
+ "--parallel",
174
+ type=bool,
175
+ show_default=True,
176
+ default=False,
177
+ help="Process files in parallel",
178
+ )(function)
179
+ function = click.option(
180
+ "-d",
181
+ "--debugging_mode",
182
+ type=bool,
183
+ show_default=True,
184
+ default=False,
185
+ help="Switch to debugging mode",
186
+ )(function)
187
+ function = click.option("-v", "--verbose", type=bool, show_default=True, default=True, help="Verbose")(function)
188
+ function = click.option(
189
+ "-f",
190
+ "--force",
191
+ type=bool,
192
+ show_default=True,
193
+ default=False,
194
+ help="Force overwriting",
195
+ )(function)
196
+ return function
197
+
198
+
199
+ def click_remove_l0a_option(function: object):
200
+ """Click command line argument for ``remove_l0a``."""
201
+ function = click.option(
202
+ "--remove_l0a",
203
+ type=bool,
204
+ show_default=True,
205
+ default=False,
206
+ help="If true, remove the L0A files once the L0B processing is terminated.",
207
+ )(function)
208
+ return function
209
+
210
+
211
+ def click_remove_l0b_option(function: object):
212
+ """Click command line argument for ``remove_l0b``."""
213
+ function = click.option(
214
+ "--remove_l0b",
215
+ type=bool,
216
+ show_default=True,
217
+ default=False,
218
+ help="If true, remove the L0B files once the L0C processing is terminated.",
219
+ )(function)
220
+ return function
221
+
222
+
223
+ def click_l0_archive_options(function: object):
224
+ """Click command line arguments for L0 processing archiving of a station.
225
+
226
+ Parameters
227
+ ----------
228
+ function : object
229
+ Function.
230
+ """
231
+ function = click.option(
232
+ "--remove_l0b",
233
+ type=bool,
234
+ show_default=True,
235
+ default=False,
236
+ help="If true, remove all source L0B files once L0B concatenation is terminated.",
237
+ )(function)
238
+ function = click.option(
239
+ "--remove_l0a",
240
+ type=bool,
241
+ show_default=True,
242
+ default=False,
243
+ help="If true, remove the L0A files once the L0B processing is terminated.",
244
+ )(function)
245
+ function = click.option(
246
+ "-l0c",
247
+ "--l0c_processing",
248
+ type=bool,
249
+ show_default=True,
250
+ default=True,
251
+ help="Perform L0C processing.",
252
+ )(function)
253
+ function = click.option(
254
+ "-l0b",
255
+ "--l0b_processing",
256
+ type=bool,
257
+ show_default=True,
258
+ default=True,
259
+ help="Perform L0B processing.",
260
+ )(function)
261
+ function = click.option(
262
+ "-l0a",
263
+ "--l0a_processing",
264
+ type=bool,
265
+ show_default=True,
266
+ default=True,
267
+ help="Perform L0A processing.",
268
+ )(function)
269
+ return function
@@ -26,9 +26,11 @@ import tempfile
26
26
  import zipfile
27
27
  from typing import Optional
28
28
 
29
- from disdrodb.api.checks import check_base_dir
29
+ from disdrodb.api.checks import check_data_archive_dir
30
30
  from disdrodb.api.path import define_station_dir
31
+ from disdrodb.configs import get_data_archive_dir
31
32
  from disdrodb.utils.directories import list_files
33
+ from disdrodb.utils.yaml import read_yaml
32
34
 
33
35
  COMPRESSION_OPTIONS = {
34
36
  "zip": ".zip",
@@ -38,17 +40,15 @@ COMPRESSION_OPTIONS = {
38
40
 
39
41
 
40
42
  def unzip_file(filepath: str, dest_path: str) -> None:
41
- """Unzip a file into a directory
43
+ """Unzip a file into a directory.
42
44
 
43
45
  Parameters
44
-
45
46
  ----------
46
47
  filepath : str
47
- Path of the file to unzip
48
+ Path of the file to unzip.
48
49
  dest_path : str
49
- Path of the destination directory
50
+ Path of the destination directory.
50
51
  """
51
-
52
52
  with zipfile.ZipFile(filepath, "r") as zip_ref:
53
53
  zip_ref.extractall(dest_path)
54
54
 
@@ -59,21 +59,29 @@ def _zip_dir(dir_path: str) -> str:
59
59
  Parameters
60
60
  ----------
61
61
  dir_path : str
62
- Path of the directory to zip
62
+ Path of the directory to zip.
63
63
 
64
64
  Returns
65
65
  -------
66
66
  str
67
- Path of the zip archive
67
+ Path of the zip archive.
68
68
  """
69
-
70
69
  output_path_without_extension = os.path.join(tempfile.gettempdir(), os.path.basename(dir_path))
71
70
  output_path = output_path_without_extension + ".zip"
72
71
  shutil.make_archive(output_path_without_extension, "zip", dir_path)
73
72
  return output_path
74
73
 
75
74
 
76
- def archive_station_data(metadata_filepath: str) -> str:
75
+ def check_consistent_station_name(metadata_filepath, station_name):
76
+ """Check consistent station_name between YAML file name and metadata key."""
77
+ # Check consistent station name
78
+ expected_station_name = os.path.basename(metadata_filepath).replace(".yml", "")
79
+ if station_name and str(station_name) != str(expected_station_name):
80
+ raise ValueError(f"Inconsistent station_name values in the {metadata_filepath} file. Download aborted.")
81
+ return station_name
82
+
83
+
84
+ def archive_station_data(metadata_filepath: str, data_archive_dir: str) -> str:
77
85
  """Archive station data into a zip file for subsequent data upload.
78
86
 
79
87
  It create a zip file into a temporary directory !
@@ -83,26 +91,42 @@ def archive_station_data(metadata_filepath: str) -> str:
83
91
  metadata_filepath: str
84
92
  Metadata file path.
85
93
 
86
- Returns
87
- -------
88
- station_zip_filepath
89
- Filepath of the zip file containing the station's data.
90
94
  """
91
- station_data_path = metadata_filepath.replace("metadata", "data")
92
- station_data_path = os.path.splitext(station_data_path)[0] # remove trailing ".yml"
93
- station_zip_filepath = _zip_dir(station_data_path)
95
+ # Open metadata file
96
+ metadata_dict = read_yaml(metadata_filepath)
97
+ # Retrieve station information
98
+ data_archive_dir = get_data_archive_dir(data_archive_dir)
99
+ data_source = metadata_dict["data_source"]
100
+ campaign_name = metadata_dict["campaign_name"]
101
+ station_name = metadata_dict["station_name"]
102
+ station_name = check_consistent_station_name(metadata_filepath, station_name)
103
+ # Define the destination local filepath path
104
+ station_dir = define_station_dir(
105
+ data_archive_dir=data_archive_dir,
106
+ data_source=data_source,
107
+ campaign_name=campaign_name,
108
+ station_name=station_name,
109
+ product="RAW",
110
+ )
111
+ # Zip station directory
112
+ station_zip_filepath = _zip_dir(station_dir)
94
113
  return station_zip_filepath
95
114
 
96
115
 
97
116
  def compress_station_files(
98
- base_dir: str, data_source: str, campaign_name: str, station_name: str, method: str = "gzip", skip: bool = True
117
+ data_archive_dir: str,
118
+ data_source: str,
119
+ campaign_name: str,
120
+ station_name: str,
121
+ method: str = "gzip",
122
+ skip: bool = True,
99
123
  ) -> None:
100
124
  """Compress each raw file of a station.
101
125
 
102
126
  Parameters
103
127
  ----------
104
- base_dir : str
105
- Base directory of DISDRODB
128
+ data_archive_dir : str
129
+ DISDRODB Data Archive directory
106
130
  data_source : str
107
131
  Name of data source of interest.
108
132
  campaign_name : str
@@ -110,19 +134,19 @@ def compress_station_files(
110
134
  station_name : str
111
135
  Station name of interest.
112
136
  method : str
113
- Compression method. "zip", "gzip" or "bzip2".
137
+ Compression method. ``"zip"``, ``"gzip"`` or ``"bzip2"``.
114
138
  skip : bool
115
139
  Whether to raise an error if a file is already compressed.
116
- If True, it does not raise an error and try to compress the other files.
117
- If False, it raise an error and stop the compression routine.
118
- THe default is True.
140
+ If ``True``, it does not raise an error and try to compress the other files.
141
+ If ``False``, it raise an error and stop the compression routine.
142
+ The default value is ``True``.
119
143
  """
120
144
  if method not in COMPRESSION_OPTIONS:
121
145
  raise ValueError(f"Invalid compression method {method}. Valid methods are {list(COMPRESSION_OPTIONS.keys())}")
122
146
 
123
- base_dir = check_base_dir(base_dir)
147
+ data_archive_dir = check_data_archive_dir(data_archive_dir)
124
148
  station_dir = define_station_dir(
125
- base_dir=base_dir,
149
+ data_archive_dir=data_archive_dir,
126
150
  product="RAW",
127
151
  data_source=data_source,
128
152
  campaign_name=campaign_name,
@@ -137,7 +161,7 @@ def compress_station_files(
137
161
  for filepath in filepaths:
138
162
  _ = _compress_file(filepath, method, skip=skip)
139
163
 
140
- print("All files of {data_source} {campaign_name} {station_name} have been compressed.")
164
+ print(f"All files of {data_source} {campaign_name} {station_name} have been compressed.")
141
165
  print("Please now remember to update the glob_pattern of the reader ¨!")
142
166
 
143
167
 
@@ -151,11 +175,11 @@ def _compress_file(filepath: str, method: str, skip: bool) -> str:
151
175
  filepath : str
152
176
  Path of the file to compress.
153
177
  method : str
154
- Compression method. None, "zip", "gzip" or "bzip2".
178
+ Compression method. ``None``, ``"zip"``, ``"gzip"`` or ``"bzip2"``.
155
179
  skip : bool
156
180
  Whether to raise an error if a file is already compressed.
157
- If True, it does not raise an error return the input filepath.
158
- If False, it raise an error.
181
+ If ``True``, it does not raise an error return the input filepath.
182
+ If ``False``, it raise an error.
159
183
 
160
184
  Returns
161
185
  -------
@@ -169,8 +193,7 @@ def _compress_file(filepath: str, method: str, skip: bool) -> str:
169
193
  if skip:
170
194
  print(f"File {filepath} is already compressed. Skipping.")
171
195
  return filepath
172
- else:
173
- raise ValueError(f"File {filepath} is already compressed !")
196
+ raise ValueError(f"File {filepath} is already compressed !")
174
197
 
175
198
  extension = COMPRESSION_OPTIONS[method]
176
199
  archive_name = os.path.basename(filepath) + extension
@@ -200,7 +223,7 @@ def _check_file_compression(filepath: str) -> Optional[str]:
200
223
  Returns
201
224
  -------
202
225
  Optional[str]
203
- Compression method. None, "zip", "gzip" or "bzip2".
226
+ Compression method. ``None``, ``"zip"``, ``"gzip"`` or ``"bzip2"``.
204
227
 
205
228
  """
206
229
  magic_dict = {
@@ -229,7 +252,6 @@ def _compress_file_zip(filepath: str, compressed_filepath: str) -> None:
229
252
  Path of the compressed file.
230
253
 
231
254
  """
232
-
233
255
  with zipfile.ZipFile(compressed_filepath, "w", compression=zipfile.ZIP_DEFLATED) as zipf:
234
256
  zipf.write(filepath, os.path.basename(filepath))
235
257
 
@@ -246,10 +268,8 @@ def _compress_file_gzip(filepath: str, compressed_filepath: str) -> None:
246
268
  Path of the compressed file.
247
269
 
248
270
  """
249
-
250
- with open(filepath, "rb") as f_in:
251
- with gzip.open(compressed_filepath, "wb") as f_out:
252
- f_out.writelines(f_in)
271
+ with open(filepath, "rb") as f_in, gzip.open(compressed_filepath, "wb") as f_out:
272
+ f_out.writelines(f_in)
253
273
 
254
274
 
255
275
  def _compress_file_bzip2(filepath: str, compressed_filepath: str) -> None:
@@ -264,7 +284,5 @@ def _compress_file_bzip2(filepath: str, compressed_filepath: str) -> None:
264
284
  Path of the compressed file.
265
285
 
266
286
  """
267
-
268
- with open(filepath, "rb") as f_in:
269
- with bz2.open(compressed_filepath, "wb") as f_out:
270
- f_out.writelines(f_in)
287
+ with open(filepath, "rb") as f_in, bz2.open(compressed_filepath, "wb") as f_out:
288
+ f_out.writelines(f_in)
disdrodb/utils/dask.py ADDED
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env python3
2
+
3
+ # -----------------------------------------------------------------------------.
4
+ # Copyright (c) 2021-2023 DISDRODB developers
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ # -----------------------------------------------------------------------------.
19
+ """Utilities for Dask Distributed computations."""
20
+ import logging
21
+ import os
22
+
23
+
24
+ def initialize_dask_cluster():
25
+ """Initialize Dask Cluster."""
26
+ import dask
27
+ from dask.distributed import Client, LocalCluster
28
+
29
+ # Set HDF5_USE_FILE_LOCKING to avoid going stuck with HDF
30
+ os.environ["HDF5_USE_FILE_LOCKING"] = "FALSE"
31
+ # Retrieve the number of process to run
32
+ available_workers = os.cpu_count() - 2 # if not set, all CPUs
33
+ num_workers = dask.config.get("num_workers", available_workers)
34
+ # Silence dask warnings
35
+ dask.config.set({"logging.distributed": "error"})
36
+ # dask.config.set({"distributed.admin.system-monitor.gil.enabled": False})
37
+ # Create dask.distributed local cluster
38
+ cluster = LocalCluster(
39
+ n_workers=num_workers,
40
+ threads_per_worker=1,
41
+ processes=True,
42
+ # memory_limit='8GB',
43
+ # silence_logs=False,
44
+ )
45
+ client = Client(cluster)
46
+ return cluster, client
47
+
48
+
49
+ def close_dask_cluster(cluster, client):
50
+ """Close Dask Cluster."""
51
+ logger = logging.getLogger()
52
+ # Backup current log level
53
+ original_level = logger.level
54
+ logger.setLevel(logging.CRITICAL + 1) # Set level to suppress all logs
55
+ # Close cluster
56
+ # - Avoid log 'distributed.worker - ERROR - Failed to communicate with scheduler during heartbeat.'
57
+ try:
58
+ cluster.close()
59
+ client.close()
60
+ finally:
61
+ # Restore the original log level
62
+ logger.setLevel(original_level)
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env python3
2
+
3
+ # -----------------------------------------------------------------------------.
4
+ # Copyright (c) 2021-2023 DISDRODB developers
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ # -----------------------------------------------------------------------------.
19
+ """DISDRODB decorators."""
20
+ import functools
21
+ import importlib
22
+
23
+ import dask
24
+
25
+
26
+ def delayed_if_parallel(function):
27
+ """Decorator to make the function delayed if its ``parallel`` argument is ``True``."""
28
+
29
+ @functools.wraps(function)
30
+ def wrapper(*args, **kwargs):
31
+ # Check if it must be a delayed function
32
+ parallel = kwargs.get("parallel")
33
+ # If parallel is True
34
+ if parallel:
35
+ # Enforce verbose to be False
36
+ kwargs["verbose"] = False
37
+ # Define the delayed task
38
+ result = dask.delayed(function)(*args, **kwargs)
39
+ else:
40
+ # Else run the function
41
+ result = function(*args, **kwargs)
42
+ return result
43
+
44
+ return wrapper
45
+
46
+
47
+ def single_threaded_if_parallel(function):
48
+ """Decorator to make a function use a single threadon delayed if its ``parallel`` argument is ``True``."""
49
+
50
+ @functools.wraps(function)
51
+ def wrapper(*args, **kwargs):
52
+ # Check if it must be a delayed function
53
+ parallel = kwargs.get("parallel")
54
+ # If parallel is True
55
+ if parallel:
56
+ # Call function with single thread
57
+ # with dask.config.set(scheduler='single-threaded'):
58
+ with dask.config.set(scheduler="synchronous"):
59
+ result = function(*args, **kwargs)
60
+ else:
61
+ # Else run the function as usual
62
+ result = function(*args, **kwargs)
63
+ return result
64
+
65
+ return wrapper
66
+
67
+
68
+ def check_software_availability(software, conda_package):
69
+ """A decorator to ensure that a software package is installed.
70
+
71
+ Parameters
72
+ ----------
73
+ software : str
74
+ The package name as recognized by Python's import system.
75
+ conda_package : str
76
+ The package name as recognized by conda-forge.
77
+ """
78
+
79
+ def decorator(func):
80
+ @functools.wraps(func)
81
+ def wrapper(*args, **kwargs):
82
+ if not importlib.util.find_spec(software):
83
+ raise ImportError(
84
+ f"The '{software}' package is required but not found.\n"
85
+ "Please install it using conda:\n"
86
+ f" conda install -c conda-forge {conda_package}",
87
+ )
88
+ return func(*args, **kwargs)
89
+
90
+ return wrapper
91
+
92
+ return decorator
93
+
94
+
95
+ def check_pytmatrix_availability(func):
96
+ """Decorator to ensure that the 'pytmatrix' package is installed."""
97
+
98
+ @functools.wraps(func)
99
+ def wrapper(*args, **kwargs):
100
+ if not importlib.util.find_spec("pytmatrix"):
101
+ raise ImportError(
102
+ "The 'pytmatrix' package is required but not found. \n"
103
+ "Please install the following software: \n"
104
+ " conda install conda-forge gfortran \n"
105
+ " conda install conda-forge meson \n"
106
+ " pip install git+https://github.com/ltelab/pytmatrix-lte.git@main \n",
107
+ )
108
+ return func(*args, **kwargs)
109
+
110
+ return wrapper