disdrodb 0.2.0__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (315) hide show
  1. disdrodb/__init__.py +1 -1
  2. disdrodb/_config.py +1 -3
  3. disdrodb/_version.py +2 -2
  4. disdrodb/accessor/__init__.py +1 -1
  5. disdrodb/accessor/methods.py +18 -11
  6. disdrodb/api/checks.py +2 -4
  7. disdrodb/api/configs.py +1 -3
  8. disdrodb/api/create_directories.py +4 -6
  9. disdrodb/api/info.py +1 -3
  10. disdrodb/api/io.py +15 -9
  11. disdrodb/api/path.py +1 -3
  12. disdrodb/cli/disdrodb_check_metadata_archive.py +2 -2
  13. disdrodb/cli/disdrodb_check_products_options.py +44 -0
  14. disdrodb/cli/disdrodb_create_summary.py +48 -22
  15. disdrodb/cli/disdrodb_create_summary_station.py +39 -18
  16. disdrodb/cli/disdrodb_data_archive_directory.py +1 -3
  17. disdrodb/cli/disdrodb_download_archive.py +45 -24
  18. disdrodb/cli/disdrodb_download_metadata_archive.py +27 -16
  19. disdrodb/cli/disdrodb_download_station.py +56 -26
  20. disdrodb/cli/disdrodb_initialize_station.py +40 -20
  21. disdrodb/cli/disdrodb_metadata_archive_directory.py +1 -3
  22. disdrodb/cli/disdrodb_open_data_archive.py +16 -11
  23. disdrodb/cli/disdrodb_open_logs_directory.py +29 -18
  24. disdrodb/cli/disdrodb_open_metadata_archive.py +25 -11
  25. disdrodb/cli/disdrodb_open_metadata_directory.py +32 -20
  26. disdrodb/cli/disdrodb_open_product_directory.py +38 -21
  27. disdrodb/cli/disdrodb_open_readers_directory.py +1 -3
  28. disdrodb/cli/disdrodb_run.py +189 -0
  29. disdrodb/cli/disdrodb_run_l0.py +55 -64
  30. disdrodb/cli/disdrodb_run_l0_station.py +47 -52
  31. disdrodb/cli/disdrodb_run_l0a.py +47 -45
  32. disdrodb/cli/disdrodb_run_l0a_station.py +38 -37
  33. disdrodb/cli/disdrodb_run_l0b.py +45 -45
  34. disdrodb/cli/disdrodb_run_l0b_station.py +37 -36
  35. disdrodb/cli/disdrodb_run_l0c.py +50 -47
  36. disdrodb/cli/disdrodb_run_l0c_station.py +41 -38
  37. disdrodb/cli/disdrodb_run_l1.py +49 -45
  38. disdrodb/cli/disdrodb_run_l1_station.py +40 -37
  39. disdrodb/cli/disdrodb_run_l2e.py +50 -45
  40. disdrodb/cli/disdrodb_run_l2e_station.py +41 -37
  41. disdrodb/cli/disdrodb_run_l2m.py +49 -45
  42. disdrodb/cli/disdrodb_run_l2m_station.py +40 -37
  43. disdrodb/cli/disdrodb_run_station.py +184 -0
  44. disdrodb/cli/disdrodb_upload_archive.py +45 -35
  45. disdrodb/cli/disdrodb_upload_station.py +39 -32
  46. disdrodb/configs.py +13 -8
  47. disdrodb/constants.py +4 -2
  48. disdrodb/data_transfer/__init__.py +1 -3
  49. disdrodb/data_transfer/download_data.py +38 -54
  50. disdrodb/data_transfer/upload_data.py +1 -3
  51. disdrodb/data_transfer/zenodo.py +1 -3
  52. disdrodb/docs.py +1 -3
  53. disdrodb/etc/configs/attributes.yaml +52 -2
  54. disdrodb/etc/configs/encodings.yaml +45 -1
  55. disdrodb/etc/products/L0C/ODM470/global.yaml +5 -0
  56. disdrodb/etc/products/L0C/global.yaml +5 -0
  57. disdrodb/etc/products/L1/ODM470/global.yaml +6 -0
  58. disdrodb/etc/products/L1/global.yaml +1 -14
  59. disdrodb/etc/products/L2E/LPM/1MIN.yaml +1 -0
  60. disdrodb/etc/products/L2E/LPM/global.yaml +36 -0
  61. disdrodb/etc/products/L2E/LPM_V0/1MIN.yaml +1 -0
  62. disdrodb/etc/products/L2E/LPM_V0/global.yaml +36 -0
  63. disdrodb/etc/products/L2E/ODM470/1MIN.yaml +1 -0
  64. disdrodb/etc/products/L2E/ODM470/global.yaml +36 -0
  65. disdrodb/etc/products/L2E/PARSIVEL/1MIN.yaml +1 -0
  66. disdrodb/etc/products/L2E/PARSIVEL/global.yaml +36 -0
  67. disdrodb/etc/products/L2E/PARSIVEL2/1MIN.yaml +1 -0
  68. disdrodb/etc/products/L2E/PARSIVEL2/global.yaml +36 -0
  69. disdrodb/etc/products/L2E/PWS100/1MIN.yaml +1 -0
  70. disdrodb/etc/products/L2E/PWS100/global.yaml +36 -0
  71. disdrodb/etc/products/L2E/RD80/1MIN.yaml +19 -0
  72. disdrodb/etc/products/L2E/SWS250/1MIN.yaml +19 -0
  73. disdrodb/etc/products/L2E/global.yaml +17 -3
  74. disdrodb/etc/products/L2M/global.yaml +1 -1
  75. disdrodb/fall_velocity/__init__.py +46 -0
  76. disdrodb/fall_velocity/graupel.py +483 -0
  77. disdrodb/fall_velocity/hail.py +287 -0
  78. disdrodb/{l1/fall_velocity.py → fall_velocity/rain.py} +265 -50
  79. disdrodb/issue/__init__.py +1 -3
  80. disdrodb/issue/checks.py +3 -5
  81. disdrodb/issue/reader.py +1 -3
  82. disdrodb/issue/writer.py +1 -3
  83. disdrodb/l0/__init__.py +1 -1
  84. disdrodb/l0/check_configs.py +26 -17
  85. disdrodb/l0/check_standards.py +1 -3
  86. disdrodb/l0/configs/LPM/l0a_encodings.yml +0 -1
  87. disdrodb/l0/configs/LPM/l0b_cf_attrs.yml +0 -4
  88. disdrodb/l0/configs/LPM/l0b_encodings.yml +9 -9
  89. disdrodb/l0/configs/LPM/raw_data_format.yml +11 -11
  90. disdrodb/l0/configs/LPM_V0/bins_diameter.yml +103 -0
  91. disdrodb/l0/configs/LPM_V0/bins_velocity.yml +103 -0
  92. disdrodb/l0/configs/LPM_V0/l0a_encodings.yml +45 -0
  93. disdrodb/l0/configs/LPM_V0/l0b_cf_attrs.yml +180 -0
  94. disdrodb/l0/configs/LPM_V0/l0b_encodings.yml +410 -0
  95. disdrodb/l0/configs/LPM_V0/raw_data_format.yml +474 -0
  96. disdrodb/l0/configs/ODM470/bins_diameter.yml +643 -0
  97. disdrodb/l0/configs/ODM470/bins_velocity.yml +0 -0
  98. disdrodb/l0/configs/ODM470/l0a_encodings.yml +11 -0
  99. disdrodb/l0/configs/ODM470/l0b_cf_attrs.yml +46 -0
  100. disdrodb/l0/configs/ODM470/l0b_encodings.yml +106 -0
  101. disdrodb/l0/configs/ODM470/raw_data_format.yml +111 -0
  102. disdrodb/l0/configs/PARSIVEL/l0b_cf_attrs.yml +1 -1
  103. disdrodb/l0/configs/PARSIVEL/raw_data_format.yml +8 -8
  104. disdrodb/l0/configs/PARSIVEL2/raw_data_format.yml +9 -9
  105. disdrodb/l0/l0_reader.py +1 -3
  106. disdrodb/l0/l0a_processing.py +7 -5
  107. disdrodb/l0/l0b_nc_processing.py +2 -4
  108. disdrodb/l0/l0b_processing.py +27 -22
  109. disdrodb/l0/l0c_processing.py +37 -11
  110. disdrodb/l0/manuals/LPM_V0.pdf +0 -0
  111. disdrodb/l0/readers/LPM/ARM/ARM_LPM.py +1 -1
  112. disdrodb/l0/readers/LPM/AUSTRALIA/MELBOURNE_2007_LPM.py +1 -1
  113. disdrodb/l0/readers/LPM/BRAZIL/CHUVA_LPM.py +1 -1
  114. disdrodb/l0/readers/LPM/BRAZIL/GOAMAZON_LPM.py +1 -1
  115. disdrodb/l0/readers/LPM/GERMANY/DWD.py +190 -12
  116. disdrodb/l0/readers/LPM/ITALY/GID_LPM.py +63 -14
  117. disdrodb/l0/readers/LPM/ITALY/GID_LPM_PI.py +279 -0
  118. disdrodb/l0/readers/LPM/ITALY/GID_LPM_T.py +279 -0
  119. disdrodb/l0/readers/LPM/ITALY/GID_LPM_W.py +3 -5
  120. disdrodb/l0/readers/LPM/KIT/CHWALA.py +1 -3
  121. disdrodb/l0/readers/LPM/NETHERLANDS/DELFT_LPM_NC.py +1 -1
  122. disdrodb/l0/readers/LPM/NETHERLANDS/DELFT_RWANDA_LPM_NC.py +103 -0
  123. disdrodb/l0/readers/LPM/NORWAY/HAUKELISETER_LPM.py +214 -0
  124. disdrodb/l0/readers/LPM/NORWAY/NMBU_LPM.py +206 -0
  125. disdrodb/l0/readers/LPM/SLOVENIA/ARSO.py +1 -3
  126. disdrodb/l0/readers/LPM/SLOVENIA/UL.py +1 -3
  127. disdrodb/l0/readers/LPM/SWITZERLAND/INNERERIZ_LPM.py +1 -3
  128. disdrodb/l0/readers/LPM/UK/DIVEN.py +1 -1
  129. disdrodb/l0/readers/LPM/UK/WITHWORTH_LPM.py +217 -0
  130. disdrodb/l0/readers/LPM/USA/CHARLESTON.py +227 -0
  131. disdrodb/l0/readers/{LPM → LPM_V0}/BELGIUM/ULIEGE.py +34 -52
  132. disdrodb/l0/readers/LPM_V0/ITALY/GID_LPM_V0.py +240 -0
  133. disdrodb/l0/readers/ODM470/OCEAN/OCEANRAIN.py +123 -0
  134. disdrodb/l0/readers/PARSIVEL/AUSTRALIA/MELBOURNE_2007_PARSIVEL.py +1 -1
  135. disdrodb/l0/readers/PARSIVEL/BASQUECOUNTRY/EUSKALMET_OTT.py +1 -1
  136. disdrodb/l0/readers/PARSIVEL/CHINA/CHONGQING.py +1 -3
  137. disdrodb/l0/readers/PARSIVEL/EPFL/ARCTIC_2021.py +1 -1
  138. disdrodb/l0/readers/PARSIVEL/EPFL/COMMON_2011.py +1 -1
  139. disdrodb/l0/readers/PARSIVEL/EPFL/DAVOS_2009_2011.py +1 -1
  140. disdrodb/l0/readers/PARSIVEL/EPFL/EPFL_2009.py +1 -1
  141. disdrodb/l0/readers/PARSIVEL/EPFL/EPFL_ROOF_2008.py +1 -1
  142. disdrodb/l0/readers/PARSIVEL/EPFL/EPFL_ROOF_2010.py +1 -1
  143. disdrodb/l0/readers/PARSIVEL/EPFL/EPFL_ROOF_2011.py +1 -1
  144. disdrodb/l0/readers/PARSIVEL/EPFL/EPFL_ROOF_2012.py +1 -1
  145. disdrodb/l0/readers/PARSIVEL/EPFL/GENEPI_2007.py +1 -1
  146. disdrodb/l0/readers/PARSIVEL/EPFL/GRAND_ST_BERNARD_2007.py +1 -1
  147. disdrodb/l0/readers/PARSIVEL/EPFL/GRAND_ST_BERNARD_2007_2.py +1 -1
  148. disdrodb/l0/readers/PARSIVEL/EPFL/HPICONET_2010.py +1 -1
  149. disdrodb/l0/readers/PARSIVEL/EPFL/HYMEX_LTE_SOP2.py +1 -1
  150. disdrodb/l0/readers/PARSIVEL/EPFL/HYMEX_LTE_SOP3.py +1 -1
  151. disdrodb/l0/readers/PARSIVEL/EPFL/HYMEX_LTE_SOP4.py +1 -1
  152. disdrodb/l0/readers/PARSIVEL/EPFL/LOCARNO_2018.py +1 -1
  153. disdrodb/l0/readers/PARSIVEL/EPFL/LOCARNO_2019.py +1 -1
  154. disdrodb/l0/readers/PARSIVEL/EPFL/PARADISO_2014.py +1 -1
  155. disdrodb/l0/readers/PARSIVEL/EPFL/PARSIVEL_2007.py +1 -1
  156. disdrodb/l0/readers/PARSIVEL/EPFL/PLATO_2019.py +1 -1
  157. disdrodb/l0/readers/PARSIVEL/EPFL/RACLETS_2019.py +1 -1
  158. disdrodb/l0/readers/PARSIVEL/EPFL/RACLETS_2019_WJF.py +1 -1
  159. disdrodb/l0/readers/PARSIVEL/EPFL/RIETHOLZBACH_2011.py +1 -1
  160. disdrodb/l0/readers/PARSIVEL/EPFL/SAMOYLOV_2017.py +1 -1
  161. disdrodb/l0/readers/PARSIVEL/EPFL/SAMOYLOV_2019.py +1 -1
  162. disdrodb/l0/readers/PARSIVEL/EPFL/UNIL_2022.py +1 -1
  163. disdrodb/l0/readers/PARSIVEL/JAPAN/JMA.py +1 -1
  164. disdrodb/l0/readers/PARSIVEL/KOREA/ICEPOP_MSC.py +159 -0
  165. disdrodb/l0/readers/PARSIVEL/NASA/LPVEX.py +26 -14
  166. disdrodb/l0/readers/PARSIVEL/NASA/MC3E.py +2 -2
  167. disdrodb/l0/readers/PARSIVEL/NCAR/CCOPE_2015.py +1 -1
  168. disdrodb/l0/readers/PARSIVEL/NCAR/OWLES_MIPS.py +1 -1
  169. disdrodb/l0/readers/PARSIVEL/NCAR/PECAN_MOBILE.py +1 -1
  170. disdrodb/l0/readers/PARSIVEL/NCAR/PLOWS_MIPS.py +1 -1
  171. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2009.py +1 -1
  172. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010.py +1 -3
  173. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010_UF.py +1 -3
  174. disdrodb/l0/readers/PARSIVEL/SLOVENIA/UL.py +1 -1
  175. disdrodb/l0/readers/PARSIVEL2/ARM/ARM_PARSIVEL2.py +1 -1
  176. disdrodb/l0/readers/PARSIVEL2/BASQUECOUNTRY/EUSKALMET_OTT2.py +2 -2
  177. disdrodb/l0/readers/PARSIVEL2/BELGIUM/ILVO.py +1 -3
  178. disdrodb/l0/readers/PARSIVEL2/BRAZIL/CHUVA_PARSIVEL2.py +1 -1
  179. disdrodb/l0/readers/PARSIVEL2/BRAZIL/GOAMAZON_PARSIVEL2.py +1 -1
  180. disdrodb/l0/readers/PARSIVEL2/CANADA/UQAM_NC.py +1 -1
  181. disdrodb/l0/readers/PARSIVEL2/DENMARK/DTU.py +1 -1
  182. disdrodb/l0/readers/PARSIVEL2/DENMARK/EROSION_nc.py +1 -1
  183. disdrodb/l0/readers/PARSIVEL2/DENMARK/EROSION_raw.py +1 -1
  184. disdrodb/l0/readers/PARSIVEL2/FINLAND/FMI_PARSIVEL2.py +1 -1
  185. disdrodb/l0/readers/PARSIVEL2/FRANCE/ENPC_PARSIVEL2.py +1 -3
  186. disdrodb/l0/readers/PARSIVEL2/FRANCE/OSUG.py +1 -1
  187. disdrodb/l0/readers/PARSIVEL2/FRANCE/SIRTA_PARSIVEL2.py +1 -3
  188. disdrodb/l0/readers/PARSIVEL2/GREECE/NOA.py +4 -3
  189. disdrodb/l0/readers/PARSIVEL2/ITALY/GID_PARSIVEL2.py +1 -3
  190. disdrodb/l0/readers/PARSIVEL2/ITALY/HYDROX.py +5 -3
  191. disdrodb/l0/readers/PARSIVEL2/JAPAN/PRECIP.py +155 -0
  192. disdrodb/l0/readers/PARSIVEL2/KIT/BURKINA_FASO.py +1 -1
  193. disdrodb/l0/readers/PARSIVEL2/KIT/TEAMX.py +1 -1
  194. disdrodb/l0/readers/PARSIVEL2/KOREA/ICEPOP_MSC.py +161 -0
  195. disdrodb/l0/readers/PARSIVEL2/{NASA/GCPEX.py → KOREA/ICEPOP_UCLM.py} +51 -31
  196. disdrodb/l0/readers/PARSIVEL2/MEXICO/OH_IIUNAM_nc.py +1 -1
  197. disdrodb/l0/readers/PARSIVEL2/MPI/BCO_PARSIVEL2.py +15 -8
  198. disdrodb/l0/readers/PARSIVEL2/MPI/BOWTIE.py +9 -4
  199. disdrodb/l0/readers/PARSIVEL2/NASA/APU.py +31 -6
  200. disdrodb/l0/readers/PARSIVEL2/NASA/NSSTC.py +1 -1
  201. disdrodb/l0/readers/PARSIVEL2/NCAR/FARM_PARSIVEL2.py +1 -1
  202. disdrodb/l0/readers/PARSIVEL2/NCAR/PECAN_FP3.py +1 -1
  203. disdrodb/l0/readers/PARSIVEL2/NCAR/PECAN_MIPS.py +1 -1
  204. disdrodb/l0/readers/PARSIVEL2/NCAR/PERILS_MIPS.py +1 -1
  205. disdrodb/l0/readers/PARSIVEL2/NCAR/PERILS_PIPS.py +1 -1
  206. disdrodb/l0/readers/PARSIVEL2/NCAR/RELAMPAGO_PARSIVEL2.py +2 -2
  207. disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_PJ.py +1 -1
  208. disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_SB.py +1 -1
  209. disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_P1.py +1 -3
  210. disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_P2.py +1 -1
  211. disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_PIPS.py +1 -1
  212. disdrodb/l0/readers/PARSIVEL2/NETHERLANDS/DELFT_NC.py +1 -1
  213. disdrodb/l0/readers/{PARSIVEL/NASA/PIERS.py → PARSIVEL2/NORWAY/UIB.py} +65 -31
  214. disdrodb/l0/readers/PARSIVEL2/PHILIPPINES/PAGASA.py +7 -6
  215. disdrodb/l0/readers/PARSIVEL2/SPAIN/CENER.py +1 -1
  216. disdrodb/l0/readers/PARSIVEL2/SPAIN/CR1000DL.py +1 -1
  217. disdrodb/l0/readers/PARSIVEL2/SPAIN/GRANADA.py +1 -3
  218. disdrodb/l0/readers/PARSIVEL2/SPAIN/LIAISE.py +1 -1
  219. disdrodb/l0/readers/PARSIVEL2/SWEDEN/SMHI.py +1 -1
  220. disdrodb/l0/readers/PARSIVEL2/USA/CSU.py +138 -0
  221. disdrodb/l0/readers/PARSIVEL2/USA/CW3E.py +49 -22
  222. disdrodb/l0/readers/PWS100/AUSTRIA/HOAL.py +1 -3
  223. disdrodb/l0/readers/PWS100/FRANCE/ENPC_PWS100.py +1 -3
  224. disdrodb/l0/readers/PWS100/FRANCE/ENPC_PWS100_SIRTA.py +1 -1
  225. disdrodb/l0/readers/{PARSIVEL/NASA/IFLOODS.py → RD80/BRAZIL/ATTO_RD80.py} +50 -36
  226. disdrodb/l0/readers/RD80/BRAZIL/CHUVA_RD80.py +1 -3
  227. disdrodb/l0/readers/RD80/BRAZIL/GOAMAZON_RD80.py +1 -3
  228. disdrodb/l0/readers/RD80/NCAR/CINDY_2011_RD80.py +1 -3
  229. disdrodb/l0/readers/RD80/NCAR/RELAMPAGO_RD80.py +1 -3
  230. disdrodb/l0/readers/RD80/NOAA/PSL_RD80.py +1 -3
  231. disdrodb/l0/readers/{SW250 → SWS250}/BELGIUM/KMI.py +2 -4
  232. disdrodb/l0/readers/template_reader_raw_netcdf_data.py +1 -3
  233. disdrodb/l0/readers/template_reader_raw_text_data.py +1 -3
  234. disdrodb/l0/standards.py +4 -5
  235. disdrodb/l0/template_tools.py +1 -3
  236. disdrodb/l1/__init__.py +1 -1
  237. disdrodb/l1/classification.py +913 -0
  238. disdrodb/l1/processing.py +36 -106
  239. disdrodb/l1/resampling.py +8 -3
  240. disdrodb/l1_env/__init__.py +1 -1
  241. disdrodb/l1_env/routines.py +6 -6
  242. disdrodb/l2/__init__.py +1 -1
  243. disdrodb/l2/empirical_dsd.py +61 -31
  244. disdrodb/l2/processing.py +327 -62
  245. disdrodb/metadata/checks.py +1 -3
  246. disdrodb/metadata/download.py +4 -4
  247. disdrodb/metadata/geolocation.py +1 -3
  248. disdrodb/metadata/info.py +1 -3
  249. disdrodb/metadata/manipulation.py +1 -3
  250. disdrodb/metadata/reader.py +1 -3
  251. disdrodb/metadata/search.py +1 -3
  252. disdrodb/metadata/standards.py +1 -3
  253. disdrodb/metadata/writer.py +1 -3
  254. disdrodb/physics/__init__.py +17 -0
  255. disdrodb/physics/atmosphere.py +272 -0
  256. disdrodb/physics/water.py +130 -0
  257. disdrodb/physics/wrappers.py +62 -0
  258. disdrodb/psd/__init__.py +1 -1
  259. disdrodb/psd/fitting.py +22 -9
  260. disdrodb/psd/models.py +1 -1
  261. disdrodb/routines/__init__.py +5 -1
  262. disdrodb/routines/l0.py +28 -18
  263. disdrodb/routines/l1.py +8 -6
  264. disdrodb/routines/l2.py +8 -4
  265. disdrodb/routines/options.py +116 -71
  266. disdrodb/routines/options_validation.py +728 -0
  267. disdrodb/routines/wrappers.py +431 -11
  268. disdrodb/scattering/__init__.py +1 -1
  269. disdrodb/scattering/axis_ratio.py +9 -6
  270. disdrodb/scattering/permittivity.py +8 -8
  271. disdrodb/scattering/routines.py +32 -14
  272. disdrodb/summary/__init__.py +1 -1
  273. disdrodb/summary/routines.py +146 -86
  274. disdrodb/utils/__init__.py +1 -1
  275. disdrodb/utils/archiving.py +16 -9
  276. disdrodb/utils/attrs.py +4 -3
  277. disdrodb/utils/cli.py +8 -10
  278. disdrodb/utils/compression.py +13 -13
  279. disdrodb/utils/dask.py +33 -14
  280. disdrodb/utils/dataframe.py +1 -3
  281. disdrodb/utils/decorators.py +1 -3
  282. disdrodb/utils/dict.py +1 -1
  283. disdrodb/utils/directories.py +3 -5
  284. disdrodb/utils/encoding.py +2 -4
  285. disdrodb/utils/event.py +1 -1
  286. disdrodb/utils/list.py +1 -3
  287. disdrodb/utils/logger.py +1 -3
  288. disdrodb/utils/manipulations.py +182 -6
  289. disdrodb/utils/pydantic.py +80 -0
  290. disdrodb/utils/routines.py +1 -3
  291. disdrodb/utils/subsetting.py +1 -1
  292. disdrodb/utils/time.py +3 -2
  293. disdrodb/utils/warnings.py +1 -3
  294. disdrodb/utils/writer.py +1 -3
  295. disdrodb/utils/xarray.py +30 -3
  296. disdrodb/utils/yaml.py +1 -3
  297. disdrodb/viz/__init__.py +1 -1
  298. disdrodb/viz/plots.py +197 -21
  299. {disdrodb-0.2.0.dist-info → disdrodb-0.3.0.dist-info}/METADATA +2 -2
  300. disdrodb-0.3.0.dist-info/RECORD +358 -0
  301. {disdrodb-0.2.0.dist-info → disdrodb-0.3.0.dist-info}/entry_points.txt +3 -0
  302. disdrodb/etc/products/L1/1MIN.yaml +0 -13
  303. disdrodb/etc/products/L1/LPM/1MIN.yaml +0 -13
  304. disdrodb/etc/products/L1/PARSIVEL/1MIN.yaml +0 -13
  305. disdrodb/etc/products/L1/PARSIVEL2/1MIN.yaml +0 -13
  306. disdrodb/etc/products/L1/PWS100/1MIN.yaml +0 -13
  307. disdrodb/etc/products/L1/RD80/1MIN.yaml +0 -13
  308. disdrodb/etc/products/L1/SWS250/1MIN.yaml +0 -13
  309. disdrodb/etc/products/L2M/10MIN.yaml +0 -12
  310. disdrodb/l1/beard_model.py +0 -618
  311. disdrodb/l1/filters.py +0 -203
  312. disdrodb-0.2.0.dist-info/RECORD +0 -312
  313. {disdrodb-0.2.0.dist-info → disdrodb-0.3.0.dist-info}/WHEEL +0 -0
  314. {disdrodb-0.2.0.dist-info → disdrodb-0.3.0.dist-info}/licenses/LICENSE +0 -0
  315. {disdrodb-0.2.0.dist-info → disdrodb-0.3.0.dist-info}/top_level.txt +0 -0
disdrodb/utils/cli.py CHANGED
@@ -1,7 +1,5 @@
1
- #!/usr/bin/env python3
2
-
3
1
  # -----------------------------------------------------------------------------.
4
- # Copyright (c) 2021-2023 DISDRODB developers
2
+ # Copyright (c) 2021-2026 DISDRODB developers
5
3
  #
6
4
  # This program is free software: you can redistribute it and/or modify
7
5
  # it under the terms of the GNU General Public License as published by
@@ -105,7 +103,7 @@ def click_data_archive_dir_option(function: object):
105
103
  type=str,
106
104
  show_default=True,
107
105
  default=None,
108
- help="DISDRODB base directory",
106
+ help="DISDRODB Data Archive Directory. Format: <...>/DISDRODB",
109
107
  )(function)
110
108
  return function
111
109
 
@@ -123,7 +121,7 @@ def click_metadata_archive_dir_option(function: object):
123
121
  type=str,
124
122
  show_default=True,
125
123
  default=None,
126
- help="DISDRODB Metadata Archive Directory",
124
+ help="DISDRODB Metadata Archive Directory. Format: <...>/DISDRODB-METADATA/DISDRODB",
127
125
  )(function)
128
126
  return function
129
127
 
@@ -233,14 +231,14 @@ def click_l0_archive_options(function: object):
233
231
  type=bool,
234
232
  show_default=True,
235
233
  default=False,
236
- help="If true, remove all source L0B files once L0B concatenation is terminated.",
234
+ help="If True, remove L0B files after L0C.",
237
235
  )(function)
238
236
  function = click.option(
239
237
  "--remove_l0a",
240
238
  type=bool,
241
239
  show_default=True,
242
240
  default=False,
243
- help="If true, remove the L0A files once the L0B processing is terminated.",
241
+ help="If True, remove L0A files after L0B.",
244
242
  )(function)
245
243
  function = click.option(
246
244
  "-l0c",
@@ -248,7 +246,7 @@ def click_l0_archive_options(function: object):
248
246
  type=bool,
249
247
  show_default=True,
250
248
  default=True,
251
- help="Perform L0C processing.",
249
+ help="Run L0C processing",
252
250
  )(function)
253
251
  function = click.option(
254
252
  "-l0b",
@@ -256,7 +254,7 @@ def click_l0_archive_options(function: object):
256
254
  type=bool,
257
255
  show_default=True,
258
256
  default=True,
259
- help="Perform L0B processing.",
257
+ help="Run L0B processing",
260
258
  )(function)
261
259
  function = click.option(
262
260
  "-l0a",
@@ -264,6 +262,6 @@ def click_l0_archive_options(function: object):
264
262
  type=bool,
265
263
  show_default=True,
266
264
  default=True,
267
- help="Perform L0A processing.",
265
+ help="Run L0A processing",
268
266
  )(function)
269
267
  return function
@@ -1,7 +1,5 @@
1
- #!/usr/bin/env python3
2
-
3
1
  # -----------------------------------------------------------------------------.
4
- # Copyright (c) 2021-2023 DISDRODB developers
2
+ # Copyright (c) 2021-2026 DISDRODB developers
5
3
  #
6
4
  # This program is free software: you can redistribute it and/or modify
7
5
  # it under the terms of the GNU General Public License as published by
@@ -82,7 +80,7 @@ def unzip_file_on_terminal(filepath: str, dest_path: str) -> str:
82
80
  subprocess.run(cmd, check=True)
83
81
 
84
82
 
85
- def _zip_dir(dir_path: str) -> str:
83
+ def _zip_dir(dir_path: str, dst_dir=None) -> str:
86
84
  """Zip a directory into a file located in the same directory.
87
85
 
88
86
  Parameters
@@ -95,7 +93,9 @@ def _zip_dir(dir_path: str) -> str:
95
93
  str
96
94
  Path of the zip archive.
97
95
  """
98
- output_path_without_extension = os.path.join(tempfile.gettempdir(), os.path.basename(dir_path))
96
+ if dst_dir is None:
97
+ dst_dir = tempfile.gettempdir()
98
+ output_path_without_extension = os.path.join(dst_dir, os.path.basename(dir_path))
99
99
  output_path = output_path_without_extension + ".zip"
100
100
  shutil.make_archive(output_path_without_extension, "zip", dir_path)
101
101
  return output_path
@@ -188,13 +188,13 @@ def compress_station_files(
188
188
  # Get list of files inside the station directory (in all nested directories)
189
189
  filepaths = list_files(station_dir, recursive=True)
190
190
  for filepath in filepaths:
191
- _ = _compress_file(filepath, method, skip=skip)
191
+ _ = compress_file(filepath, method, skip=skip)
192
192
 
193
193
  print(f"All files of {data_source} {campaign_name} {station_name} have been compressed.")
194
194
  print("Please now remember to update the glob_pattern of the reader ¨!")
195
195
 
196
196
 
197
- def _compress_file(filepath: str, method: str, skip: bool) -> str:
197
+ def compress_file(filepath: str, method: str, skip: bool) -> str:
198
198
  """Compress a file and delete the original.
199
199
 
200
200
  If the file is already compressed, it is not compressed again.
@@ -228,9 +228,9 @@ def _compress_file(filepath: str, method: str, skip: bool) -> str:
228
228
  archive_name = os.path.basename(filepath) + extension
229
229
  compressed_filepath = os.path.join(os.path.dirname(filepath), archive_name)
230
230
  compress_file_function = {
231
- "zip": _compress_file_zip,
232
- "gzip": _compress_file_gzip,
233
- "bzip2": _compress_file_bzip2,
231
+ "zip": compress_file_zip,
232
+ "gzip": compress_file_gzip,
233
+ "bzip2": compress_file_bzip2,
234
234
  }[method]
235
235
 
236
236
  compress_file_function(filepath, compressed_filepath)
@@ -269,7 +269,7 @@ def _check_file_compression(filepath: str) -> Optional[str]:
269
269
  return None
270
270
 
271
271
 
272
- def _compress_file_zip(filepath: str, compressed_filepath: str) -> None:
272
+ def compress_file_zip(filepath: str, compressed_filepath: str) -> None:
273
273
  """Compress a single file into a zip archive.
274
274
 
275
275
  Parameters
@@ -285,7 +285,7 @@ def _compress_file_zip(filepath: str, compressed_filepath: str) -> None:
285
285
  zipf.write(filepath, os.path.basename(filepath))
286
286
 
287
287
 
288
- def _compress_file_gzip(filepath: str, compressed_filepath: str) -> None:
288
+ def compress_file_gzip(filepath: str, compressed_filepath: str) -> None:
289
289
  """Compress a single file into a gzip archive.
290
290
 
291
291
  Parameters
@@ -301,7 +301,7 @@ def _compress_file_gzip(filepath: str, compressed_filepath: str) -> None:
301
301
  f_out.writelines(f_in)
302
302
 
303
303
 
304
- def _compress_file_bzip2(filepath: str, compressed_filepath: str) -> None:
304
+ def compress_file_bzip2(filepath: str, compressed_filepath: str) -> None:
305
305
  """Compress a single file into a bzip2 archive.
306
306
 
307
307
  Parameters
disdrodb/utils/dask.py CHANGED
@@ -1,7 +1,5 @@
1
- #!/usr/bin/env python3
2
-
3
1
  # -----------------------------------------------------------------------------.
4
- # Copyright (c) 2021-2023 DISDRODB developers
2
+ # Copyright (c) 2021-2026 DISDRODB developers
5
3
  #
6
4
  # This program is free software: you can redistribute it and/or modify
7
5
  # it under the terms of the GNU General Public License as published by
@@ -71,6 +69,7 @@ def initialize_dask_cluster(minimum_memory=None):
71
69
  os.environ["HDF5_USE_FILE_LOCKING"] = "FALSE"
72
70
 
73
71
  # Retrieve the number of processes to run
72
+ # --> If DASK_NUM_WORKERS is not set, use all CPUs minus 2
74
73
  available_workers = os.cpu_count() - 2 # if not set, all CPUs minus 2
75
74
  num_workers = dask.config.get("num_workers", available_workers)
76
75
 
@@ -113,7 +112,13 @@ def close_dask_cluster(cluster, client):
113
112
  logger.setLevel(original_level)
114
113
 
115
114
 
116
- def execute_tasks_safely(list_tasks, parallel: bool, logs_dir: str):
115
+ def _batch_iterable(iterable, n):
116
+ """Yield successive n-sized chunks from iterable."""
117
+ for i in range(0, len(iterable), n):
118
+ yield iterable[i : i + n]
119
+
120
+
121
+ def execute_tasks_safely(list_tasks, parallel: bool, logs_dir: str, max_tasks_per_batch=5_000):
117
122
  """
118
123
  Execute Dask tasks and skip failed ones.
119
124
 
@@ -125,6 +130,9 @@ def execute_tasks_safely(list_tasks, parallel: bool, logs_dir: str):
125
130
  Whether to execute in parallel with Dask or not.
126
131
  logs_dir : str
127
132
  Directory to store FAILED_TASKS.log.
133
+ max_tasks_per_batch : int or None, optional
134
+ Maximum number of tasks to submit to `client.compute()` at once.
135
+ The default is 5000. Dask struggle if more than 10_000 tasks are submitted.
128
136
 
129
137
  Returns
130
138
  -------
@@ -150,18 +158,29 @@ def execute_tasks_safely(list_tasks, parallel: bool, logs_dir: str):
150
158
  except ValueError:
151
159
  raise ValueError("No Dask Distributed Client found.")
152
160
 
153
- # Compute tasks (all concurrently)
154
- # - Runs tasks == num_workers * threads_per_worker (which is 1 for DISDRODB)
155
- # - If errors occurs in some, skip it
156
- futures = client.compute(list_tasks)
157
- results = client.gather(futures, errors="skip")
161
+ all_results = []
162
+ failed_futures = []
163
+
164
+ # Batch execution
165
+ task_batches = list(_batch_iterable(list_tasks, max_tasks_per_batch)) if max_tasks_per_batch else [list_tasks]
166
+
167
+ for batch in task_batches:
168
+ # Compute tasks (all concurrently)
169
+ # - Runs tasks == num_workers * threads_per_worker (which is 1 for DISDRODB)
170
+ # - If errors occurs in some, skip it
171
+ futures = client.compute(batch)
172
+ results = client.gather(futures, errors="skip")
173
+
174
+ # Identify and collect failed futures
175
+ batch_failed = [f for f in futures if f.status != "finished"]
176
+ failed_futures.extend(batch_failed)
158
177
 
159
- # Collect failed futures
160
- failed_futures = [f for f in futures if f.status != "finished"] # "error"
178
+ # Collect results from successful tasks
179
+ all_results.extend(results)
161
180
 
162
181
  # If no tasks failed, return results
163
182
  if not failed_futures:
164
- return results
183
+ return all_results
165
184
 
166
185
  # Otherwise define log file listing failed tasks
167
186
  with open(failed_log_path, "w") as f:
@@ -170,5 +189,5 @@ def execute_tasks_safely(list_tasks, parallel: bool, logs_dir: str):
170
189
  f.write(f"ERROR - DASK TASK FAILURE - Task {fut.key} failed: {err}\n")
171
190
 
172
191
  # Append to list of log filepaths (results) the dask failing log
173
- results.append(failed_log_path)
174
- return results
192
+ all_results.append(failed_log_path)
193
+ return all_results
@@ -1,7 +1,5 @@
1
- #!/usr/bin/env python3
2
-
3
1
  # -----------------------------------------------------------------------------.
4
- # Copyright (c) 2021-2023 DISDRODB developers
2
+ # Copyright (c) 2021-2026 DISDRODB developers
5
3
  #
6
4
  # This program is free software: you can redistribute it and/or modify
7
5
  # it under the terms of the GNU General Public License as published by
@@ -1,7 +1,5 @@
1
- #!/usr/bin/env python3
2
-
3
1
  # -----------------------------------------------------------------------------.
4
- # Copyright (c) 2021-2023 DISDRODB developers
2
+ # Copyright (c) 2021-2026 DISDRODB developers
5
3
  #
6
4
  # This program is free software: you can redistribute it and/or modify
7
5
  # it under the terms of the GNU General Public License as published by
disdrodb/utils/dict.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # -----------------------------------------------------------------------------.
2
- # Copyright (c) 2021-2023 DISDRODB developers
2
+ # Copyright (c) 2021-2026 DISDRODB developers
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify
5
5
  # it under the terms of the GNU General Public License as published by
@@ -1,7 +1,5 @@
1
- #!/usr/bin/env python3
2
-
3
1
  # -----------------------------------------------------------------------------.
4
- # Copyright (c) 2021-2023 DISDRODB developers
2
+ # Copyright (c) 2021-2026 DISDRODB developers
5
3
  #
6
4
  # This program is free software: you can redistribute it and/or modify
7
5
  # it under the terms of the GNU General Public License as published by
@@ -212,7 +210,7 @@ def is_empty_directory(path, skip_hidden=True):
212
210
  return len(paths) == 0
213
211
 
214
212
 
215
- def _remove_file_or_directories(path, logger=None):
213
+ def remove_file_or_directories(path, logger=None):
216
214
  """Return the file/directory or subdirectories tree of ``path``.
217
215
 
218
216
  Use this function with caution.
@@ -259,7 +257,7 @@ def remove_if_exists(path: str, force: bool = False, logger=None) -> None:
259
257
 
260
258
  # If force=True, remove the file/directory or subdirectories and files !
261
259
  try:
262
- _remove_file_or_directories(path, logger=logger)
260
+ remove_file_or_directories(path, logger=logger)
263
261
  except Exception as e:
264
262
  msg = f"Can not delete file(s) at {path}. The error is: {e}"
265
263
  raise ValueError(msg)
@@ -1,7 +1,5 @@
1
- #!/usr/bin/env python3
2
-
3
1
  # -----------------------------------------------------------------------------.
4
- # Copyright (c) 2021-2023 DISDRODB developers
2
+ # Copyright (c) 2021-2026 DISDRODB developers
5
3
  #
6
4
  # This program is free software: you can redistribute it and/or modify
7
5
  # it under the terms of the GNU General Public License as published by
@@ -144,7 +142,7 @@ def get_time_encoding() -> dict:
144
142
  """
145
143
  encoding = {}
146
144
  encoding["dtype"] = "int64" # if float trailing sub-seconds values
147
- encoding["fillvalue"] = np.iinfo(np.int64).max
145
+ encoding["_FillValue"] = np.iinfo(np.int64).max
148
146
  encoding["units"] = EPOCH
149
147
  encoding["calendar"] = "proleptic_gregorian"
150
148
  return encoding
disdrodb/utils/event.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # -----------------------------------------------------------------------------.
2
- # Copyright (c) 2021-2023 DISDRODB developers
2
+ # Copyright (c) 2021-2026 DISDRODB developers
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify
5
5
  # it under the terms of the GNU General Public License as published by
disdrodb/utils/list.py CHANGED
@@ -1,7 +1,5 @@
1
- #!/usr/bin/env python3
2
-
3
1
  # -----------------------------------------------------------------------------.
4
- # Copyright (c) 2021-2023 DISDRODB developers
2
+ # Copyright (c) 2021-2026 DISDRODB developers
5
3
  #
6
4
  # This program is free software: you can redistribute it and/or modify
7
5
  # it under the terms of the GNU General Public License as published by
disdrodb/utils/logger.py CHANGED
@@ -1,7 +1,5 @@
1
- #!/usr/bin/env python3
2
-
3
1
  # -----------------------------------------------------------------------------.
4
- # Copyright (c) 2021-2023 DISDRODB developers
2
+ # Copyright (c) 2021-2026 DISDRODB developers
5
3
  #
6
4
  # This program is free software: you can redistribute it and/or modify
7
5
  # it under the terms of the GNU General Public License as published by
@@ -1,7 +1,5 @@
1
- #!/usr/bin/env python3
2
-
3
1
  # -----------------------------------------------------------------------------.
4
- # Copyright (c) 2021-2023 DISDRODB developers
2
+ # Copyright (c) 2021-2026 DISDRODB developers
5
3
  #
6
4
  # This program is free software: you can redistribute it and/or modify
7
5
  # it under the terms of the GNU General Public License as published by
@@ -17,16 +15,194 @@
17
15
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
16
  # -----------------------------------------------------------------------------.
19
17
  """Include functions helping for DISDRODB product manipulations."""
20
-
21
18
  import numpy as np
19
+ import xarray as xr
22
20
 
23
- from disdrodb.constants import DIAMETER_DIMENSION
21
+ from disdrodb.constants import DIAMETER_DIMENSION, VELOCITY_DIMENSION
24
22
  from disdrodb.utils.xarray import unstack_datarray_dimension
25
23
 
26
24
 
25
+ def define_diameter_datarray(bounds):
26
+ """Define diameter DataArray."""
27
+ diameters_bin_lower = bounds[:-1]
28
+ diameters_bin_upper = bounds[1:]
29
+ diameters_bin_width = diameters_bin_upper - diameters_bin_lower
30
+ diameters_bin_center = diameters_bin_lower + diameters_bin_width / 2
31
+ da = xr.DataArray(
32
+ diameters_bin_center,
33
+ dims="diameter_bin_center",
34
+ coords={
35
+ "diameter_bin_width": ("diameter_bin_center", diameters_bin_width),
36
+ "diameter_bin_lower": ("diameter_bin_center", diameters_bin_lower),
37
+ "diameter_bin_upper": ("diameter_bin_center", diameters_bin_upper),
38
+ "diameter_bin_center": ("diameter_bin_center", diameters_bin_center),
39
+ },
40
+ )
41
+ return da
42
+
43
+
44
+ def define_velocity_datarray(bounds):
45
+ """Define velocity DataArray."""
46
+ velocitys_bin_lower = bounds[:-1]
47
+ velocitys_bin_upper = bounds[1:]
48
+ velocitys_bin_width = velocitys_bin_upper - velocitys_bin_lower
49
+ velocitys_bin_center = velocitys_bin_lower + velocitys_bin_width / 2
50
+ da = xr.DataArray(
51
+ velocitys_bin_center,
52
+ dims="velocity_bin_center",
53
+ coords={
54
+ "velocity_bin_width": ("velocity_bin_center", velocitys_bin_width),
55
+ "velocity_bin_lower": ("velocity_bin_center", velocitys_bin_lower),
56
+ "velocity_bin_upper": ("velocity_bin_center", velocitys_bin_upper),
57
+ "velocity_bin_center": ("velocity_bin_center", velocitys_bin_center),
58
+ },
59
+ )
60
+ return da
61
+
62
+
63
+ def define_diameter_array(diameter_min=0, diameter_max=10, diameter_spacing=0.05):
64
+ """
65
+ Define an array of diameters and their corresponding bin properties.
66
+
67
+ Parameters
68
+ ----------
69
+ diameter_min : float, optional
70
+ The minimum diameter value. The default value is 0 mm.
71
+ diameter_max : float, optional
72
+ The maximum diameter value. The default value is 10 mm.
73
+ diameter_spacing : float, optional
74
+ The spacing between diameter values. The default value is 0.05 mm.
75
+
76
+ Returns
77
+ -------
78
+ xr.DataArray
79
+ A DataArray containing the center of each diameter bin, with coordinates for
80
+ the bin width, lower bound, upper bound, and center.
81
+
82
+ """
83
+ diameters_bounds = np.arange(diameter_min, diameter_max + diameter_spacing / 2, step=diameter_spacing)
84
+ return define_diameter_datarray(diameters_bounds)
85
+
86
+
87
+ def define_velocity_array(velocity_min=0, velocity_max=10, velocity_spacing=0.05):
88
+ """
89
+ Define an array of velocities and their corresponding bin properties.
90
+
91
+ Parameters
92
+ ----------
93
+ velocity_min : float, optional
94
+ The minimum velocity value. The default value is 0 mm.
95
+ velocity_max : float, optional
96
+ The maximum velocity value. The default value is 10 mm.
97
+ velocity_spacing : float, optional
98
+ The spacing between velocity values. The default value is 0.05 mm.
99
+
100
+ Returns
101
+ -------
102
+ xr.DataArray
103
+ A DataArray containing the center of each velocity bin, with coordinates for
104
+ the bin width, lower bound, upper bound, and center.
105
+
106
+ """
107
+ velocitys_bounds = np.arange(velocity_min, velocity_max + velocity_spacing / 2, step=velocity_spacing)
108
+ return define_velocity_datarray(velocitys_bounds)
109
+
110
+
111
+ def filter_diameter_bins(ds, minimum_diameter=None, maximum_diameter=None):
112
+ """
113
+ Filter the dataset to include only diameter bins within specified bounds.
114
+
115
+ Parameters
116
+ ----------
117
+ ds : xarray.Dataset
118
+ The dataset containing diameter bin data.
119
+ minimum_diameter : float, optional
120
+ The minimum diameter to be included, in millimeters.
121
+ Defaults to the minimum value in `ds["diameter_bin_lower"]`.
122
+ maximum_diameter : float, optional
123
+ The maximum diameter to be included, in millimeters.
124
+ Defaults to the maximum value in `ds["diameter_bin_upper"]`.
125
+
126
+ Returns
127
+ -------
128
+ xarray.Dataset
129
+ The filtered dataset containing only the specified diameter bins.
130
+ """
131
+ # Put data into memory
132
+ ds["diameter_bin_lower"] = ds["diameter_bin_lower"].compute()
133
+ ds["diameter_bin_upper"] = ds["diameter_bin_upper"].compute()
134
+
135
+ # Initialize default arguments
136
+ if minimum_diameter is None:
137
+ minimum_diameter = ds["diameter_bin_lower"].min().item()
138
+ if maximum_diameter is None:
139
+ maximum_diameter = ds["diameter_bin_upper"].max().item()
140
+
141
+ # Select bins which overlap the specified diameters
142
+ valid_indices = np.logical_and(
143
+ ds["diameter_bin_upper"] > minimum_diameter,
144
+ ds["diameter_bin_lower"] < maximum_diameter,
145
+ )
146
+ ds = ds.isel({DIAMETER_DIMENSION: valid_indices})
147
+
148
+ if ds.sizes[DIAMETER_DIMENSION] == 0:
149
+ msg = f"Filtering using {minimum_diameter=} removes all diameter bins."
150
+ raise ValueError(msg)
151
+ return ds
152
+
153
+
154
+ def filter_velocity_bins(ds, minimum_velocity=None, maximum_velocity=None):
155
+ """
156
+ Filter the dataset to include only velocity bins within specified bounds.
157
+
158
+ Parameters
159
+ ----------
160
+ ds : xarray.Dataset
161
+ The dataset containing velocity bin data.
162
+ minimum_velocity : float, optional
163
+ The minimum velocity to include in the filter, in meters per second.
164
+ Defaults to the minimum value in `ds["velocity_bin_lower"]`.
165
+ maximum_velocity : float, optional
166
+ The maximum velocity to include in the filter, in meters per second.
167
+ Defaults to the maximum value in `ds["velocity_bin_upper"]`.
168
+
169
+ Returns
170
+ -------
171
+ xarray.Dataset
172
+ The filtered dataset containing only the specified velocity bins.
173
+ """
174
+ # Put data into memory
175
+ ds["velocity_bin_lower"] = ds["velocity_bin_lower"].compute()
176
+ ds["velocity_bin_upper"] = ds["velocity_bin_upper"].compute()
177
+
178
+ # Initialize default arguments
179
+ if minimum_velocity is None:
180
+ minimum_velocity = ds["velocity_bin_lower"].min().item()
181
+ if maximum_velocity is None:
182
+ maximum_velocity = ds["velocity_bin_upper"].max().item()
183
+
184
+ # Select bins which overlap the specified velocities
185
+ valid_indices = np.logical_and(
186
+ ds["velocity_bin_upper"] > minimum_velocity,
187
+ ds["velocity_bin_lower"] < maximum_velocity,
188
+ )
189
+
190
+ ds = ds.isel({VELOCITY_DIMENSION: valid_indices})
191
+ if ds.sizes[VELOCITY_DIMENSION] == 0:
192
+ msg = f"Filtering using {minimum_velocity=} removes all velocity bins."
193
+ raise ValueError(msg)
194
+ return ds
195
+
196
+
27
197
  def get_diameter_bin_edges(ds):
28
198
  """Retrieve diameter bin edges."""
29
- bin_edges = np.append(ds["diameter_bin_lower"].compute().data, ds["diameter_bin_upper"].compute().data[-1])
199
+ bin_edges = np.append(ds["diameter_bin_lower"].to_numpy(), ds["diameter_bin_upper"].to_numpy()[-1])
200
+ return bin_edges
201
+
202
+
203
+ def get_velocity_bin_edges(ds):
204
+ """Retrieve velocity bin edges."""
205
+ bin_edges = np.append(ds["velocity_bin_lower"].to_numpy(), ds["velocity_bin_upper"].to_numpy()[-1])
30
206
  return bin_edges
31
207
 
32
208
 
@@ -0,0 +1,80 @@
1
+ # -----------------------------------------------------------------------------.
2
+ # Copyright (c) 2021-2026 DISDRODB developers
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+ # -----------------------------------------------------------------------------.
17
+ """Definition of pydantic validation custom class."""
18
+ from pydantic import BaseModel, ConfigDict, ValidationError
19
+
20
+
21
+ def format_validation_error(validation_error: Exception) -> str:
22
+ """Format a Pydantic ValidationError for better readability."""
23
+ if not isinstance(validation_error, ValidationError):
24
+ return str(validation_error)
25
+
26
+ def _shorten(value, max_len=200):
27
+ """Safely truncate long inputs."""
28
+ text = repr(value)
29
+ if len(text) > max_len:
30
+ return text[: max_len - 5] + " ...]"
31
+ return text
32
+
33
+ model_name_attr = getattr(validation_error, "title", None)
34
+ model_name = model_name_attr() if callable(model_name_attr) else model_name_attr or "UnknownModel"
35
+
36
+ formatted_errors = [f"Validation errors in {model_name}:"]
37
+
38
+ for err in validation_error.errors():
39
+ path = ".".join(str(loc) for loc in err["loc"]) or "<model root>"
40
+ msg = err["msg"]
41
+ err_type = err["type"]
42
+
43
+ # Handles both "Value error, ..." and "Value error: ..."
44
+ if msg.lower().startswith("value error"):
45
+ msg = msg.split(",", 1)[-1] if "," in msg else msg.split(":", 1)[-1]
46
+ msg = msg.strip()
47
+
48
+ # Model-level (root) errors (raise in after or before)
49
+ if path == "<model root>":
50
+ formatted = f" • {msg}"
51
+ elif err_type == "missing":
52
+ formatted = f" • Missing field '{path}': {msg}"
53
+ elif "input" in err:
54
+ formatted = f" • Field '{path}': {msg} (got: {_shorten(err['input'])})"
55
+ else:
56
+ formatted = f" • Field '{path}': {msg}"
57
+
58
+ formatted_errors.append(formatted)
59
+
60
+ return "\n".join(formatted_errors)
61
+
62
+
63
+ class CustomBaseModel(BaseModel):
64
+ """Custom pydantic BaseModel.
65
+
66
+ Forbid extra keys.
67
+ Hide URLs in error message.
68
+ Simplify error message.
69
+ """
70
+
71
+ model_config = ConfigDict(extra="forbid", hide_error_urls=True)
72
+
73
+ # Override the standard ValidationError print behavior
74
+ def __init__(self, **data):
75
+ try:
76
+ super().__init__(**data)
77
+ except ValidationError as e:
78
+ formatted = format_validation_error(e)
79
+ # Raise a new simplified exception
80
+ raise ValueError(formatted) from None
@@ -1,7 +1,5 @@
1
- #!/usr/bin/env python3
2
-
3
1
  # -----------------------------------------------------------------------------.
4
- # Copyright (c) 2021-2023 DISDRODB developers
2
+ # Copyright (c) 2021-2026 DISDRODB developers
5
3
  #
6
4
  # This program is free software: you can redistribute it and/or modify
7
5
  # it under the terms of the GNU General Public License as published by
@@ -1,5 +1,5 @@
1
1
  # -----------------------------------------------------------------------------.
2
- # Copyright (c) 2021-2023 DISDRODB developers
2
+ # Copyright (c) 2021-2026 DISDRODB developers
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify
5
5
  # it under the terms of the GNU General Public License as published by
disdrodb/utils/time.py CHANGED
@@ -1,5 +1,5 @@
1
1
  # -----------------------------------------------------------------------------.
2
- # Copyright (c) 2021-2023 DISDRODB developers
2
+ # Copyright (c) 2021-2026 DISDRODB developers
3
3
  #
4
4
  # This program is free software: you can redistribute it and/or modify
5
5
  # it under the terms of the GNU General Public License as published by
@@ -238,7 +238,7 @@ def regularize_dataset(
238
238
  start_time=None,
239
239
  end_time=None,
240
240
  ):
241
- """Regularize a dataset across time dimension with uniform resolution.
241
+ """Regularize a xarray object across time dimension with uniform resolution.
242
242
 
243
243
  Parameters
244
244
  ----------
@@ -274,6 +274,7 @@ def regularize_dataset(
274
274
  start_time = start
275
275
  if end_time is None:
276
276
  end_time = end
277
+ xr_obj = xr_obj.sel({time_dim: slice(start_time, end_time)})
277
278
 
278
279
  # Define new time index
279
280
  new_time_index = pd.date_range(