disdrodb 0.2.1__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 (302) 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 +9 -9
  6. disdrodb/api/checks.py +1 -3
  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 +9 -8
  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 +0 -13
  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 +16 -2
  74. disdrodb/fall_velocity/__init__.py +46 -0
  75. disdrodb/fall_velocity/graupel.py +483 -0
  76. disdrodb/fall_velocity/hail.py +287 -0
  77. disdrodb/{l1/fall_velocity.py → fall_velocity/rain.py} +264 -44
  78. disdrodb/issue/__init__.py +1 -3
  79. disdrodb/issue/checks.py +1 -3
  80. disdrodb/issue/reader.py +1 -3
  81. disdrodb/issue/writer.py +1 -3
  82. disdrodb/l0/__init__.py +1 -1
  83. disdrodb/l0/check_configs.py +25 -16
  84. disdrodb/l0/check_standards.py +1 -3
  85. disdrodb/l0/configs/ODM470/bins_diameter.yml +643 -0
  86. disdrodb/l0/configs/ODM470/bins_velocity.yml +0 -0
  87. disdrodb/l0/configs/ODM470/l0a_encodings.yml +11 -0
  88. disdrodb/l0/configs/ODM470/l0b_cf_attrs.yml +46 -0
  89. disdrodb/l0/configs/ODM470/l0b_encodings.yml +106 -0
  90. disdrodb/l0/configs/ODM470/raw_data_format.yml +111 -0
  91. disdrodb/l0/configs/PARSIVEL/l0b_cf_attrs.yml +1 -1
  92. disdrodb/l0/l0_reader.py +1 -3
  93. disdrodb/l0/l0a_processing.py +1 -3
  94. disdrodb/l0/l0b_nc_processing.py +2 -4
  95. disdrodb/l0/l0b_processing.py +1 -3
  96. disdrodb/l0/l0c_processing.py +27 -11
  97. disdrodb/l0/readers/LPM/ARM/ARM_LPM.py +1 -1
  98. disdrodb/l0/readers/LPM/AUSTRALIA/MELBOURNE_2007_LPM.py +1 -1
  99. disdrodb/l0/readers/LPM/BRAZIL/CHUVA_LPM.py +1 -1
  100. disdrodb/l0/readers/LPM/BRAZIL/GOAMAZON_LPM.py +1 -1
  101. disdrodb/l0/readers/LPM/GERMANY/DWD.py +190 -12
  102. disdrodb/l0/readers/LPM/ITALY/GID_LPM.py +47 -6
  103. disdrodb/l0/readers/LPM/ITALY/GID_LPM_PI.py +1 -1
  104. disdrodb/l0/readers/LPM/ITALY/GID_LPM_T.py +5 -2
  105. disdrodb/l0/readers/LPM/ITALY/GID_LPM_W.py +1 -3
  106. disdrodb/l0/readers/LPM/KIT/CHWALA.py +1 -3
  107. disdrodb/l0/readers/LPM/NETHERLANDS/DELFT_LPM_NC.py +1 -1
  108. disdrodb/l0/readers/LPM/NETHERLANDS/DELFT_RWANDA_LPM_NC.py +1 -1
  109. disdrodb/l0/readers/LPM/NORWAY/HAUKELISETER_LPM.py +1 -3
  110. disdrodb/l0/readers/LPM/NORWAY/NMBU_LPM.py +1 -3
  111. disdrodb/l0/readers/LPM/SLOVENIA/ARSO.py +1 -3
  112. disdrodb/l0/readers/LPM/SLOVENIA/UL.py +1 -3
  113. disdrodb/l0/readers/LPM/SWITZERLAND/INNERERIZ_LPM.py +1 -3
  114. disdrodb/l0/readers/LPM/UK/DIVEN.py +1 -1
  115. disdrodb/l0/readers/LPM/UK/WITHWORTH_LPM.py +1 -3
  116. disdrodb/l0/readers/LPM/USA/CHARLESTON.py +1 -3
  117. disdrodb/l0/readers/LPM_V0/BELGIUM/ULIEGE.py +1 -3
  118. disdrodb/l0/readers/LPM_V0/ITALY/GID_LPM_V0.py +1 -1
  119. disdrodb/l0/readers/ODM470/OCEAN/OCEANRAIN.py +123 -0
  120. disdrodb/l0/readers/PARSIVEL/AUSTRALIA/MELBOURNE_2007_PARSIVEL.py +1 -1
  121. disdrodb/l0/readers/PARSIVEL/BASQUECOUNTRY/EUSKALMET_OTT.py +1 -1
  122. disdrodb/l0/readers/PARSIVEL/CHINA/CHONGQING.py +1 -3
  123. disdrodb/l0/readers/PARSIVEL/EPFL/ARCTIC_2021.py +1 -1
  124. disdrodb/l0/readers/PARSIVEL/EPFL/COMMON_2011.py +1 -1
  125. disdrodb/l0/readers/PARSIVEL/EPFL/DAVOS_2009_2011.py +1 -1
  126. disdrodb/l0/readers/PARSIVEL/EPFL/EPFL_2009.py +1 -1
  127. disdrodb/l0/readers/PARSIVEL/EPFL/EPFL_ROOF_2008.py +1 -1
  128. disdrodb/l0/readers/PARSIVEL/EPFL/EPFL_ROOF_2010.py +1 -1
  129. disdrodb/l0/readers/PARSIVEL/EPFL/EPFL_ROOF_2011.py +1 -1
  130. disdrodb/l0/readers/PARSIVEL/EPFL/EPFL_ROOF_2012.py +1 -1
  131. disdrodb/l0/readers/PARSIVEL/EPFL/GENEPI_2007.py +1 -1
  132. disdrodb/l0/readers/PARSIVEL/EPFL/GRAND_ST_BERNARD_2007.py +1 -1
  133. disdrodb/l0/readers/PARSIVEL/EPFL/GRAND_ST_BERNARD_2007_2.py +1 -1
  134. disdrodb/l0/readers/PARSIVEL/EPFL/HPICONET_2010.py +1 -1
  135. disdrodb/l0/readers/PARSIVEL/EPFL/HYMEX_LTE_SOP2.py +1 -1
  136. disdrodb/l0/readers/PARSIVEL/EPFL/HYMEX_LTE_SOP3.py +1 -1
  137. disdrodb/l0/readers/PARSIVEL/EPFL/HYMEX_LTE_SOP4.py +1 -1
  138. disdrodb/l0/readers/PARSIVEL/EPFL/LOCARNO_2018.py +1 -1
  139. disdrodb/l0/readers/PARSIVEL/EPFL/LOCARNO_2019.py +1 -1
  140. disdrodb/l0/readers/PARSIVEL/EPFL/PARADISO_2014.py +1 -1
  141. disdrodb/l0/readers/PARSIVEL/EPFL/PARSIVEL_2007.py +1 -1
  142. disdrodb/l0/readers/PARSIVEL/EPFL/PLATO_2019.py +1 -1
  143. disdrodb/l0/readers/PARSIVEL/EPFL/RACLETS_2019.py +1 -1
  144. disdrodb/l0/readers/PARSIVEL/EPFL/RACLETS_2019_WJF.py +1 -1
  145. disdrodb/l0/readers/PARSIVEL/EPFL/RIETHOLZBACH_2011.py +1 -1
  146. disdrodb/l0/readers/PARSIVEL/EPFL/SAMOYLOV_2017.py +1 -1
  147. disdrodb/l0/readers/PARSIVEL/EPFL/SAMOYLOV_2019.py +1 -1
  148. disdrodb/l0/readers/PARSIVEL/EPFL/UNIL_2022.py +1 -1
  149. disdrodb/l0/readers/PARSIVEL/JAPAN/JMA.py +1 -1
  150. disdrodb/l0/readers/PARSIVEL/KOREA/ICEPOP_MSC.py +159 -0
  151. disdrodb/l0/readers/PARSIVEL/NASA/LPVEX.py +1 -1
  152. disdrodb/l0/readers/PARSIVEL/NASA/MC3E.py +1 -1
  153. disdrodb/l0/readers/PARSIVEL/NCAR/CCOPE_2015.py +1 -1
  154. disdrodb/l0/readers/PARSIVEL/NCAR/OWLES_MIPS.py +1 -1
  155. disdrodb/l0/readers/PARSIVEL/NCAR/PECAN_MOBILE.py +1 -1
  156. disdrodb/l0/readers/PARSIVEL/NCAR/PLOWS_MIPS.py +1 -1
  157. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2009.py +1 -1
  158. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010.py +1 -3
  159. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010_UF.py +1 -3
  160. disdrodb/l0/readers/PARSIVEL/SLOVENIA/UL.py +1 -1
  161. disdrodb/l0/readers/PARSIVEL2/ARM/ARM_PARSIVEL2.py +1 -1
  162. disdrodb/l0/readers/PARSIVEL2/BASQUECOUNTRY/EUSKALMET_OTT2.py +1 -1
  163. disdrodb/l0/readers/PARSIVEL2/BELGIUM/ILVO.py +1 -3
  164. disdrodb/l0/readers/PARSIVEL2/BRAZIL/CHUVA_PARSIVEL2.py +1 -1
  165. disdrodb/l0/readers/PARSIVEL2/BRAZIL/GOAMAZON_PARSIVEL2.py +1 -1
  166. disdrodb/l0/readers/PARSIVEL2/CANADA/UQAM_NC.py +1 -1
  167. disdrodb/l0/readers/PARSIVEL2/DENMARK/DTU.py +1 -1
  168. disdrodb/l0/readers/PARSIVEL2/DENMARK/EROSION_nc.py +1 -1
  169. disdrodb/l0/readers/PARSIVEL2/DENMARK/EROSION_raw.py +1 -1
  170. disdrodb/l0/readers/PARSIVEL2/FINLAND/FMI_PARSIVEL2.py +1 -1
  171. disdrodb/l0/readers/PARSIVEL2/FRANCE/ENPC_PARSIVEL2.py +1 -3
  172. disdrodb/l0/readers/PARSIVEL2/FRANCE/OSUG.py +1 -1
  173. disdrodb/l0/readers/PARSIVEL2/FRANCE/SIRTA_PARSIVEL2.py +1 -3
  174. disdrodb/l0/readers/PARSIVEL2/GREECE/NOA.py +4 -3
  175. disdrodb/l0/readers/PARSIVEL2/ITALY/GID_PARSIVEL2.py +1 -3
  176. disdrodb/l0/readers/PARSIVEL2/ITALY/HYDROX.py +5 -3
  177. disdrodb/l0/readers/PARSIVEL2/JAPAN/PRECIP.py +1 -1
  178. disdrodb/l0/readers/PARSIVEL2/KIT/BURKINA_FASO.py +1 -1
  179. disdrodb/l0/readers/PARSIVEL2/KIT/TEAMX.py +1 -1
  180. disdrodb/l0/readers/PARSIVEL2/KOREA/ICEPOP_MSC.py +161 -0
  181. disdrodb/l0/readers/PARSIVEL2/KOREA/ICEPOP_UCLM.py +126 -0
  182. disdrodb/l0/readers/PARSIVEL2/MEXICO/OH_IIUNAM_nc.py +1 -1
  183. disdrodb/l0/readers/PARSIVEL2/MPI/BCO_PARSIVEL2.py +1 -1
  184. disdrodb/l0/readers/PARSIVEL2/MPI/BOWTIE.py +1 -1
  185. disdrodb/l0/readers/PARSIVEL2/NASA/APU.py +3 -1
  186. disdrodb/l0/readers/PARSIVEL2/NASA/NSSTC.py +1 -1
  187. disdrodb/l0/readers/PARSIVEL2/NCAR/FARM_PARSIVEL2.py +1 -1
  188. disdrodb/l0/readers/PARSIVEL2/NCAR/PECAN_FP3.py +1 -1
  189. disdrodb/l0/readers/PARSIVEL2/NCAR/PECAN_MIPS.py +1 -1
  190. disdrodb/l0/readers/PARSIVEL2/NCAR/PERILS_MIPS.py +1 -1
  191. disdrodb/l0/readers/PARSIVEL2/NCAR/PERILS_PIPS.py +1 -1
  192. disdrodb/l0/readers/PARSIVEL2/NCAR/RELAMPAGO_PARSIVEL2.py +1 -1
  193. disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_PJ.py +1 -1
  194. disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_SB.py +1 -1
  195. disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_P1.py +1 -3
  196. disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_P2.py +1 -1
  197. disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_PIPS.py +1 -1
  198. disdrodb/l0/readers/PARSIVEL2/NETHERLANDS/DELFT_NC.py +1 -1
  199. disdrodb/l0/readers/PARSIVEL2/NORWAY/UIB.py +10 -2
  200. disdrodb/l0/readers/PARSIVEL2/PHILIPPINES/PAGASA.py +1 -3
  201. disdrodb/l0/readers/PARSIVEL2/SPAIN/CENER.py +1 -1
  202. disdrodb/l0/readers/PARSIVEL2/SPAIN/CR1000DL.py +1 -1
  203. disdrodb/l0/readers/PARSIVEL2/SPAIN/GRANADA.py +1 -3
  204. disdrodb/l0/readers/PARSIVEL2/SPAIN/LIAISE.py +1 -1
  205. disdrodb/l0/readers/PARSIVEL2/SWEDEN/SMHI.py +1 -1
  206. disdrodb/l0/readers/PARSIVEL2/USA/CSU.py +1 -1
  207. disdrodb/l0/readers/PARSIVEL2/USA/CW3E.py +1 -1
  208. disdrodb/l0/readers/PWS100/AUSTRIA/HOAL.py +1 -3
  209. disdrodb/l0/readers/PWS100/FRANCE/ENPC_PWS100.py +1 -3
  210. disdrodb/l0/readers/PWS100/FRANCE/ENPC_PWS100_SIRTA.py +1 -1
  211. disdrodb/l0/readers/RD80/BRAZIL/ATTO_RD80.py +1 -3
  212. disdrodb/l0/readers/RD80/BRAZIL/CHUVA_RD80.py +1 -3
  213. disdrodb/l0/readers/RD80/BRAZIL/GOAMAZON_RD80.py +1 -3
  214. disdrodb/l0/readers/RD80/NCAR/CINDY_2011_RD80.py +1 -3
  215. disdrodb/l0/readers/RD80/NCAR/RELAMPAGO_RD80.py +1 -3
  216. disdrodb/l0/readers/RD80/NOAA/PSL_RD80.py +1 -3
  217. disdrodb/l0/readers/SWS250/BELGIUM/KMI.py +1 -3
  218. disdrodb/l0/readers/template_reader_raw_netcdf_data.py +1 -3
  219. disdrodb/l0/readers/template_reader_raw_text_data.py +1 -3
  220. disdrodb/l0/standards.py +4 -5
  221. disdrodb/l0/template_tools.py +1 -3
  222. disdrodb/l1/__init__.py +1 -1
  223. disdrodb/l1/classification.py +913 -0
  224. disdrodb/l1/processing.py +36 -106
  225. disdrodb/l1/resampling.py +8 -3
  226. disdrodb/l1_env/__init__.py +1 -1
  227. disdrodb/l1_env/routines.py +6 -6
  228. disdrodb/l2/__init__.py +1 -1
  229. disdrodb/l2/empirical_dsd.py +57 -31
  230. disdrodb/l2/processing.py +327 -62
  231. disdrodb/metadata/checks.py +1 -3
  232. disdrodb/metadata/download.py +4 -4
  233. disdrodb/metadata/geolocation.py +1 -3
  234. disdrodb/metadata/info.py +1 -3
  235. disdrodb/metadata/manipulation.py +1 -3
  236. disdrodb/metadata/reader.py +1 -3
  237. disdrodb/metadata/search.py +1 -3
  238. disdrodb/metadata/standards.py +1 -3
  239. disdrodb/metadata/writer.py +1 -3
  240. disdrodb/physics/__init__.py +17 -0
  241. disdrodb/physics/atmosphere.py +272 -0
  242. disdrodb/physics/water.py +130 -0
  243. disdrodb/physics/wrappers.py +62 -0
  244. disdrodb/psd/__init__.py +1 -1
  245. disdrodb/psd/fitting.py +22 -9
  246. disdrodb/psd/models.py +1 -1
  247. disdrodb/routines/__init__.py +5 -1
  248. disdrodb/routines/l0.py +26 -16
  249. disdrodb/routines/l1.py +8 -6
  250. disdrodb/routines/l2.py +8 -4
  251. disdrodb/routines/options.py +116 -73
  252. disdrodb/routines/options_validation.py +728 -0
  253. disdrodb/routines/wrappers.py +431 -11
  254. disdrodb/scattering/__init__.py +1 -1
  255. disdrodb/scattering/axis_ratio.py +6 -6
  256. disdrodb/scattering/permittivity.py +8 -8
  257. disdrodb/scattering/routines.py +31 -13
  258. disdrodb/summary/__init__.py +1 -1
  259. disdrodb/summary/routines.py +83 -25
  260. disdrodb/utils/__init__.py +1 -1
  261. disdrodb/utils/archiving.py +16 -9
  262. disdrodb/utils/attrs.py +4 -3
  263. disdrodb/utils/cli.py +8 -10
  264. disdrodb/utils/compression.py +9 -11
  265. disdrodb/utils/dask.py +2 -3
  266. disdrodb/utils/dataframe.py +1 -3
  267. disdrodb/utils/decorators.py +1 -3
  268. disdrodb/utils/dict.py +1 -1
  269. disdrodb/utils/directories.py +3 -5
  270. disdrodb/utils/encoding.py +2 -4
  271. disdrodb/utils/event.py +1 -1
  272. disdrodb/utils/list.py +1 -3
  273. disdrodb/utils/logger.py +1 -3
  274. disdrodb/utils/manipulations.py +175 -5
  275. disdrodb/utils/pydantic.py +80 -0
  276. disdrodb/utils/routines.py +1 -3
  277. disdrodb/utils/subsetting.py +1 -1
  278. disdrodb/utils/time.py +3 -2
  279. disdrodb/utils/warnings.py +1 -3
  280. disdrodb/utils/writer.py +1 -3
  281. disdrodb/utils/xarray.py +30 -3
  282. disdrodb/utils/yaml.py +1 -3
  283. disdrodb/viz/__init__.py +1 -1
  284. disdrodb/viz/plots.py +192 -18
  285. {disdrodb-0.2.1.dist-info → disdrodb-0.3.0.dist-info}/METADATA +2 -2
  286. disdrodb-0.3.0.dist-info/RECORD +358 -0
  287. {disdrodb-0.2.1.dist-info → disdrodb-0.3.0.dist-info}/entry_points.txt +3 -0
  288. disdrodb/etc/products/L1/1MIN.yaml +0 -13
  289. disdrodb/etc/products/L1/LPM/1MIN.yaml +0 -13
  290. disdrodb/etc/products/L1/LPM_V0/1MIN.yaml +0 -13
  291. disdrodb/etc/products/L1/PARSIVEL/1MIN.yaml +0 -13
  292. disdrodb/etc/products/L1/PARSIVEL2/1MIN.yaml +0 -13
  293. disdrodb/etc/products/L1/PWS100/1MIN.yaml +0 -13
  294. disdrodb/etc/products/L1/RD80/1MIN.yaml +0 -13
  295. disdrodb/etc/products/L1/SWS250/1MIN.yaml +0 -13
  296. disdrodb/etc/products/L2M/10MIN.yaml +0 -12
  297. disdrodb/l1/beard_model.py +0 -662
  298. disdrodb/l1/filters.py +0 -205
  299. disdrodb-0.2.1.dist-info/RECORD +0 -329
  300. {disdrodb-0.2.1.dist-info → disdrodb-0.3.0.dist-info}/WHEEL +0 -0
  301. {disdrodb-0.2.1.dist-info → disdrodb-0.3.0.dist-info}/licenses/LICENSE +0 -0
  302. {disdrodb-0.2.1.dist-info → disdrodb-0.3.0.dist-info}/top_level.txt +0 -0
@@ -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,13 +15,185 @@
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
199
  bin_edges = np.append(ds["diameter_bin_lower"].to_numpy(), ds["diameter_bin_upper"].to_numpy()[-1])
@@ -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(
@@ -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/writer.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/xarray.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
@@ -99,6 +97,35 @@ def xr_get_last_valid_idx(da_condition, dim, fill_value=None):
99
97
  return last_idx
100
98
 
101
99
 
100
+ def _np_remap_numeric_array(arr, remapping_dict, fill_value=np.nan):
101
+ # Define conditions
102
+ conditions = [arr == i for i in remapping_dict]
103
+ # Define choices corresponding to conditions
104
+ choices = remapping_dict.values()
105
+ # Apply np.select to transform the array
106
+ return np.select(conditions, choices, default=fill_value)
107
+
108
+
109
+ def _dask_remap_numeric_array(arr, remapping_dict, fill_value=np.nan):
110
+ import dask.array
111
+
112
+ return dask.array.map_blocks(_np_remap_numeric_array, arr, remapping_dict, fill_value, dtype=arr.dtype)
113
+
114
+
115
+ def remap_numeric_array(arr, remapping_dict, fill_value=np.nan):
116
+ """Remap the values of a numeric array."""
117
+ if hasattr(arr, "chunks"):
118
+ return _dask_remap_numeric_array(arr, remapping_dict, fill_value=fill_value)
119
+ return _np_remap_numeric_array(arr, remapping_dict, fill_value=fill_value)
120
+
121
+
122
+ def xr_remap_numeric_array(da, remapping_dict, fill_value=np.nan):
123
+ """Remap values of a xr.DataArray."""
124
+ output = da.copy()
125
+ output.data = remap_numeric_array(da.data, remapping_dict, fill_value=fill_value)
126
+ return output
127
+
128
+
102
129
  ####-------------------------------------------------------------------
103
130
  #### Unstacking dimension
104
131
 
disdrodb/utils/yaml.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/viz/__init__.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/viz/plots.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
@@ -17,6 +17,7 @@
17
17
  """DISDRODB Plotting Tools."""
18
18
  import matplotlib.pyplot as plt
19
19
  import numpy as np
20
+ import pandas as pd
20
21
  import psutil
21
22
  import xarray as xr
22
23
  from matplotlib.colors import LogNorm, Normalize
@@ -24,6 +25,7 @@ from matplotlib.gridspec import GridSpec
24
25
 
25
26
  from disdrodb.constants import DIAMETER_DIMENSION, VELOCITY_DIMENSION
26
27
  from disdrodb.l2.empirical_dsd import get_drop_average_velocity
28
+ from disdrodb.utils.time import ensure_sample_interval_in_seconds, regularize_dataset
27
29
 
28
30
  ####-------------------------------------------------------------------------------------------------------
29
31
  #### N(D) visualizations
@@ -45,44 +47,216 @@ def _single_plot_nd_distribution(drop_number_concentration, diameter, diameter_b
45
47
  return ax
46
48
 
47
49
 
48
- def plot_nd(ds, var="drop_number_concentration", cmap=None, norm=None):
50
+ def _check_has_diameter_dims(da):
51
+ if DIAMETER_DIMENSION not in da.dims:
52
+ raise ValueError(f"The DataArray must have dimension '{DIAMETER_DIMENSION}'.")
53
+ if "diameter_bin_width" not in da.coords:
54
+ raise ValueError("The DataArray must have coordinate 'diameter_bin_width'.")
55
+ return da
56
+
57
+
58
+ def _get_nd_variable(xr_obj, variable):
59
+ if not isinstance(xr_obj, (xr.Dataset, xr.DataArray)):
60
+ raise TypeError("Expecting xarray object as input.")
61
+ if isinstance(xr_obj, xr.Dataset):
62
+ if variable not in xr_obj:
63
+ raise ValueError(f"The dataset do not include {variable=}.")
64
+ xr_obj = xr_obj[variable]
65
+ if VELOCITY_DIMENSION in xr_obj.dims:
66
+ raise ValueError("N(D) must no have the velocity dimension.")
67
+ xr_obj = _check_has_diameter_dims(xr_obj)
68
+ return xr_obj
69
+
70
+
71
+ def plot_nd(xr_obj, variable="drop_number_concentration", cmap=None, norm=None):
49
72
  """Plot drop number concentration N(D) timeseries."""
50
- # Check inputs
51
- if var not in ds:
52
- raise ValueError(f"{var} is not a xarray Dataset variable!")
73
+ da_nd = _get_nd_variable(xr_obj, variable=variable)
53
74
 
54
75
  # Check only time and diameter dimensions are specified
55
- if "time" not in ds.dims:
76
+ if "time" not in da_nd.dims:
56
77
  ax = _single_plot_nd_distribution(
57
- drop_number_concentration=ds[var],
58
- diameter=ds["diameter_bin_center"],
59
- diameter_bin_width=ds["diameter_bin_width"],
78
+ drop_number_concentration=da_nd.isel(velocity_method=0, missing_dims="ignore"),
79
+ diameter=xr_obj["diameter_bin_center"],
80
+ diameter_bin_width=xr_obj["diameter_bin_width"],
60
81
  )
61
82
  return ax
62
83
 
63
- # Select N(D)
64
- ds_var = ds[[var]].compute()
65
-
66
84
  # Regularize input
67
- ds_var = ds_var.disdrodb.regularize()
85
+ da_nd = da_nd.compute()
86
+ da_nd = da_nd.disdrodb.regularize()
68
87
 
69
88
  # Set 0 values to np.nan
70
- ds_var = ds_var.where(ds_var[var] > 0)
89
+ da_nd = da_nd.where(da_nd > 0)
71
90
 
72
91
  # Define cmap an norm
73
92
  if cmap is None:
74
93
  cmap = plt.get_cmap("Spectral_r").copy()
75
94
 
76
- vmin = ds_var[var].min().item()
95
+ vmin = da_nd.min().item()
77
96
  norm = LogNorm(vmin, None) if norm is None else norm
78
97
 
79
98
  # Plot N(D)
80
- p = ds_var[var].plot.pcolormesh(x="time", norm=norm, cmap=cmap)
81
- p.axes.set_title("Drop number concentration (N(D))")
99
+ cbar_kwargs = {"label": "N(D) [m-3 mm-1]"}
100
+ p = da_nd.plot.pcolormesh(x="time", norm=norm, cmap=cmap, extend="max", cbar_kwargs=cbar_kwargs)
101
+ p.axes.set_title("Drop number concentration N(D)")
82
102
  p.axes.set_ylabel("Drop diameter (mm)")
83
103
  return p
84
104
 
85
105
 
106
+ def plot_nd_quicklook(
107
+ ds,
108
+ # Plot layout
109
+ hours_per_slice=5,
110
+ max_rows=6,
111
+ aligned=True,
112
+ verbose=True,
113
+ # Spectrum options
114
+ variable="drop_number_concentration",
115
+ cbar_label="N(D) [# m⁻³ mm⁻¹]",
116
+ cmap=None,
117
+ norm=None,
118
+ d_lim=(0.3, 5),
119
+ # R options
120
+ add_r=True,
121
+ r_lim=(0.1, 50),
122
+ r_scale="log",
123
+ r_color="tab:blue",
124
+ r_linewidth=1.2,
125
+ ):
126
+ """Display multi-rows quicklook of N(D)."""
127
+ # Colormap & normalization
128
+ if cmap is None:
129
+ cmap = plt.get_cmap("Spectral_r").copy()
130
+ cmap.set_under("none")
131
+ if norm is None:
132
+ norm = LogNorm(vmin=1, vmax=10_000)
133
+
134
+ # ---------------------------
135
+ # Define temporal slices
136
+ # - Align to closest <hours_per_slice> time
137
+ # - For hours_per_slice=3 --> 00, 03, 06, ...
138
+ time = ds["time"].to_index()
139
+ t_start = time[0]
140
+ t_end = time[-1]
141
+ if aligned:
142
+ aligned_start = t_start.floor(f"{hours_per_slice}h")
143
+ aligned_end = t_end.ceil(f"{hours_per_slice}h")
144
+ # Create time bins
145
+ time_bins = pd.date_range(
146
+ start=aligned_start,
147
+ end=aligned_end,
148
+ freq=f"{hours_per_slice}h",
149
+ )
150
+ else:
151
+ # Create time bins
152
+ time_bins = pd.date_range(
153
+ start=t_start,
154
+ end=t_end + pd.Timedelta(f"{hours_per_slice}h"),
155
+ freq=f"{hours_per_slice}h",
156
+ )
157
+
158
+ n_total_slices = len(time_bins) - 1
159
+ n_slices = min(n_total_slices, max_rows)
160
+
161
+ # Print info on event quicklook
162
+ if verbose:
163
+ print("=== N(D) Event Quicklook ===")
164
+ print(f"Dataset time span : {t_start} → {t_end}")
165
+ print(f"Slice length : {hours_per_slice} h")
166
+ print(f"Plotted slices : {n_slices}/{n_total_slices}")
167
+ if n_total_slices > max_rows:
168
+ last_plotted_end = time_bins[max_rows]
169
+ print(f"Unplotted period : {last_plotted_end} → {aligned_end}")
170
+
171
+ # Regularize dataset to match bin start_time and end_time
172
+ sample_interval = ensure_sample_interval_in_seconds(ds["sample_interval"].to_numpy()).item()
173
+ ds = regularize_dataset(ds, freq=f"{sample_interval}s", start_time=time_bins[0], end_time=time_bins[-1])
174
+
175
+ # Define figure
176
+ fig, axes = plt.subplots(
177
+ nrows=n_slices,
178
+ ncols=1,
179
+ figsize=(14, 2.8 * n_slices),
180
+ sharex=False,
181
+ constrained_layout=True,
182
+ )
183
+
184
+ if n_slices == 1:
185
+ axes = [axes]
186
+
187
+ # Plot each slice
188
+ for i in range(n_slices):
189
+ # Extract dataset slice
190
+ t0 = time_bins[i]
191
+ t1 = time_bins[i + 1]
192
+ ds_slice = ds.sel(time=slice(t0, t1))
193
+ da_nd = ds_slice[variable]
194
+
195
+ # Define plot ax
196
+ ax = axes[i]
197
+
198
+ # Plot N(D)
199
+ p = da_nd.plot.pcolormesh(
200
+ ax=ax,
201
+ x="time",
202
+ y="diameter_bin_center",
203
+ norm=norm,
204
+ cmap=cmap,
205
+ shading="auto",
206
+ add_colorbar=False,
207
+ )
208
+
209
+ # Overlay Dm
210
+ ds_slice["Dm"].plot(
211
+ ax=ax,
212
+ x="time",
213
+ color="black",
214
+ linestyle="--",
215
+ linewidth=1.2,
216
+ label="Dm",
217
+ )
218
+
219
+ # Add axis labels and title
220
+ ax.set_xlabel("")
221
+ ax.set_ylabel("Diameter [mm]")
222
+ ax.set_title(f"{t0:%H:%M} - {t1:%H:%M} UTC")
223
+
224
+ if i == 0:
225
+ ax.legend(loc="upper right")
226
+
227
+ # Add rain rate on secondary axis
228
+ if add_r:
229
+ ax_r = ax.twinx()
230
+ ds_slice["R"].plot(
231
+ ax=ax_r,
232
+ x="time",
233
+ color=r_color,
234
+ linewidth=r_linewidth,
235
+ label="R",
236
+ )
237
+ ax_r.set_ylim(r_lim)
238
+ ax_r.set_yscale(r_scale)
239
+ ax_r.set_ylabel("Rain rate [mm h$^{-1}$]", color="tab:blue")
240
+ ax_r.tick_params(axis="y", labelcolor="tab:blue")
241
+ ax_r.set_title("")
242
+
243
+ ax.set_ylim(*d_lim)
244
+
245
+ axes[-1].set_xlabel("Time (UTC)")
246
+ # ---------------------------
247
+ # Shared colorbar
248
+ # ---------------------------
249
+ cbar = fig.colorbar(
250
+ p,
251
+ ax=axes,
252
+ orientation="horizontal",
253
+ pad=0.02,
254
+ fraction=0.03,
255
+ extend="max",
256
+ )
257
+ cbar.set_label(cbar_label)
258
+
259
+
86
260
  ####-------------------------------------------------------------------------------------------------------
87
261
  #### Spectra visualizations
88
262
 
@@ -590,7 +764,7 @@ def compute_dense_lines(
590
764
  if len(other_dims) == 1:
591
765
  arr = da.transpose(*other_dims, x_dim).to_numpy()
592
766
  else:
593
- arr = da.stack({"sample": other_dims}).transpose("sample", x_dim).to_numpy()
767
+ arr = da.stack({"sample": other_dims}).transpose("sample", x_dim).to_numpy() # noqa PD013
594
768
 
595
769
  # Define y bins center
596
770
  y_center = (y_bins[0:-1] + y_bins[1:]) / 2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: disdrodb
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
4
  Summary: disdrodb provides tools to download, standardize, share and analyze global disdrometer data.
5
5
  Author: Gionata Ghiggi
6
6
  Project-URL: homepage, https://github.com/ltelab/disdrodb
@@ -10,7 +10,7 @@ Project-URL: tracker, https://github.com/ltelab/disdrodb/issues
10
10
  Project-URL: documentation, https://disdrodb.readthedocs.io
11
11
  Project-URL: changelog, https://github.com/ltelab/disdrodb/blob/main/CHANGELOG.md
12
12
  Keywords: python,disdrometer,parsivel,drop size distribution
13
- Classifier: Development Status :: 1 - Planning
13
+ Classifier: Development Status :: 5 - Production/Stable
14
14
  Classifier: Intended Audience :: Developers
15
15
  Classifier: Programming Language :: Python :: 3
16
16
  Classifier: Operating System :: Unix