disdrodb 0.0.20__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.20.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.20.dist-info/AUTHORS.md +0 -18
  253. disdrodb-0.0.20.dist-info/METADATA +0 -186
  254. disdrodb-0.0.20.dist-info/RECORD +0 -168
  255. disdrodb-0.0.20.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.20.dist-info → disdrodb-0.1.0.dist-info/licenses}/LICENSE +0 -0
  264. {disdrodb-0.0.20.dist-info → disdrodb-0.1.0.dist-info}/top_level.txt +0 -0
@@ -18,12 +18,11 @@
18
18
  # -----------------------------------------------------------------------------.
19
19
  """Functions to process DISDRODB raw netCDF files into DISDRODB L0B netCDF files."""
20
20
 
21
- import copy
22
21
  import logging
23
22
 
24
23
  import numpy as np
25
- import xarray as xr
26
24
 
25
+ from disdrodb.api.checks import check_sensor_name
27
26
  from disdrodb.l0.l0b_processing import finalize_dataset
28
27
  from disdrodb.l0.standards import (
29
28
  get_bin_coords_dict,
@@ -36,7 +35,6 @@ from disdrodb.l0.standards import (
36
35
  from disdrodb.utils.logger import (
37
36
  # log_warning,
38
37
  # log_debug,
39
- log_error,
40
38
  log_info,
41
39
  )
42
40
 
@@ -46,7 +44,7 @@ logger = logging.getLogger(__name__)
46
44
 
47
45
 
48
46
  def _check_dict_names_validity(dict_names, sensor_name):
49
- """Check dict_names dictionary values validity."""
47
+ """Check ``dict_names`` dictionary values validity."""
50
48
  valid_names = get_valid_names(sensor_name)
51
49
  keys = np.array(list(dict_names.keys()))
52
50
  values = np.array(list(dict_names.values()))
@@ -56,13 +54,11 @@ def _check_dict_names_validity(dict_names, sensor_name):
56
54
  # Report invalid keys and raise error
57
55
  invalid_dict = {k: dict_names[k] for k in invalid_keys}
58
56
  msg = f"The following dict_names values are not valid: {invalid_dict}"
59
- log_error(logger=logger, msg=msg, verbose=False)
60
57
  raise ValueError(msg)
61
- return None
62
58
 
63
59
 
64
60
  def _get_dict_names_variables(dict_names, sensor_name):
65
- """Get DISDRODB variables specified in dict_names."""
61
+ """Get DISDRODB variables specified in ``dict_names``."""
66
62
  possible_variables = get_valid_variable_names(sensor_name)
67
63
  dictionary_names = list(dict_names.values())
68
64
  variables = [name for name in dictionary_names if name in possible_variables]
@@ -78,7 +74,7 @@ def _get_missing_variables(ds, dict_names, sensor_name):
78
74
 
79
75
 
80
76
  def rename_dataset(ds, dict_names):
81
- """Rename Dataset variables, coordinates and dimensions."""
77
+ """Rename xr.Dataset variables, coordinates and dimensions."""
82
78
  # Get dataset variables, coordinates and dimensions of the dataset
83
79
  ds_vars = list(ds.data_vars)
84
80
  ds_dims = list(ds.dims)
@@ -101,24 +97,23 @@ def rename_dataset(ds, dict_names):
101
97
 
102
98
 
103
99
  def subset_dataset(ds, dict_names, sensor_name):
104
- """Subset Dataset with expected variables."""
100
+ """Subset xr.Dataset with expected variables."""
105
101
  # Get valid variable names
106
102
  possible_variables = get_valid_variable_names(sensor_name)
107
103
  # Get variables availables in the dict_names and dataset
108
104
  dataset_variables = list(ds.data_vars)
109
105
  dictionary_names = list(dict_names.values())
110
106
  # Get subset variables
111
- subset_variables = []
112
- for var in dataset_variables:
113
- if var in dictionary_names and var in possible_variables:
114
- subset_variables.append(var)
107
+ subset_variables = [var for var in dataset_variables if var in dictionary_names and var in possible_variables]
115
108
  # Subset the dataset
116
109
  ds = ds[subset_variables]
117
110
  return ds
118
111
 
119
112
 
120
113
  def add_dataset_missing_variables(ds, missing_vars, sensor_name):
121
- """Add missing Dataset variables as nan DataArrays."""
114
+ """Add missing xr.Dataset variables as ``np.nan`` xr.DataArrays."""
115
+ import xarray as xr
116
+
122
117
  from disdrodb.l0.standards import get_variables_dimension
123
118
 
124
119
  # Get dimension of each variables
@@ -128,7 +123,7 @@ def add_dataset_missing_variables(ds, missing_vars, sensor_name):
128
123
  # Get variable dimension
129
124
  dims = var_dims_dict[var]
130
125
  # Retrieve expected shape
131
- expected_shape = [ds.dims[dim] for dim in dims]
126
+ expected_shape = [ds.sizes[dim] for dim in dims]
132
127
  # Create DataArray
133
128
  arr = np.zeros(expected_shape) * np.nan
134
129
  da = xr.DataArray(arr, dims=dims)
@@ -137,15 +132,15 @@ def add_dataset_missing_variables(ds, missing_vars, sensor_name):
137
132
  return ds
138
133
 
139
134
 
140
- def preprocess_raw_netcdf(ds, dict_names, sensor_name):
135
+ def standardize_raw_dataset(ds, dict_names, sensor_name):
141
136
  """This function preprocess raw netCDF to improve compatibility with DISDRODB standards.
142
137
 
143
- This function checks validity of the dict_names, rename and subset the data accordingly.
144
- If some variables specified in the dict_names are missing, it adds a NaN DataArray !
138
+ This function checks validity of the ``dict_names``, rename and subset the data accordingly.
139
+ If some variables specified in the ``dict_names`` are missing, it adds a ``np.nan`` xr.DataArray !
145
140
 
146
141
  Parameters
147
142
  ----------
148
- ds : xr.Dataset
143
+ ds : xarray.Dataset
149
144
  Raw netCDF to be converted to DISDRODB standards.
150
145
  dict_names : dict
151
146
  Dictionary mapping raw netCDF variables/coordinates/dimension names
@@ -155,10 +150,13 @@ def preprocess_raw_netcdf(ds, dict_names, sensor_name):
155
150
 
156
151
  Returns
157
152
  -------
158
- ds : xr.Dataset
159
- xarray Dataset with DISDRODB-compliant variable naming conventions.
153
+ ds : xarray.Dataset
154
+ xarray Dataset with variables compliant with DISDRODB conventions.
160
155
 
161
156
  """
157
+ # Check if the sensor name is valid
158
+ check_sensor_name(sensor_name)
159
+
162
160
  # Check variable_dict has valid values
163
161
  # - Check valid DISDRODB variables + dimensions + coords
164
162
  _check_dict_names_validity(dict_names=dict_names, sensor_name=sensor_name)
@@ -175,32 +173,35 @@ def preprocess_raw_netcdf(ds, dict_names, sensor_name):
175
173
  ds = add_dataset_missing_variables(ds=ds, missing_vars=missing_vars, sensor_name=sensor_name)
176
174
 
177
175
  # Update the coordinates for (diameter and velocity)
178
- coords = get_bin_coords_dict(sensor_name)
179
- ds = ds.assign_coords(coords)
176
+ ds = ds.assign_coords(get_bin_coords_dict(sensor_name))
180
177
 
181
178
  # Return dataset
182
179
  return ds
183
180
 
184
181
 
185
- def replace_custom_nan_flags(ds, dict_nan_flags, verbose=False):
186
- """Set values corresponding to nan_flags to np.nan.
182
+ def replace_custom_nan_flags(ds, dict_nan_flags, logger=None, verbose=False):
183
+ """Set values corresponding to ``nan_flags`` to ``np.nan``.
187
184
 
188
185
  This function must be used in a reader, if necessary.
189
186
 
190
187
  Parameters
191
188
  ----------
192
- df : xr.Dataset
189
+ df : xarray.Dataset
193
190
  Input xarray dataset
194
191
  dict_nan_flags : dict
195
- Dictionary with nan flags value to set as np.nan
192
+ Dictionary with nan flags value to set as ``np.nan``.
193
+ verbose : bool
194
+ Whether to verbose the processing. The default value is ``False``.
196
195
 
197
196
  Returns
198
197
  -------
199
- xr.Dataset
200
- Dataset without nan_flags values.
198
+ xarray.Dataset
199
+ Dataset without ``nan_flags`` values.
201
200
  """
202
201
  # Loop over the needed variable, and replace nan_flags values with np.nan
203
202
  for var, nan_flags in dict_nan_flags.items():
203
+ # Ensure nan_flags is a list
204
+ nan_flags = [nan_flags] if not isinstance(nan_flags, list) else nan_flags
204
205
  # If the variable is in the dataframe
205
206
  if var in ds:
206
207
  # Get occurrence of nan_flags
@@ -215,12 +216,12 @@ def replace_custom_nan_flags(ds, dict_nan_flags, verbose=False):
215
216
  return ds
216
217
 
217
218
 
218
- def replace_nan_flags(ds, sensor_name, verbose):
219
- """Set values corresponding to nan_flags to np.nan.
219
+ def replace_nan_flags(ds, sensor_name, verbose, logger=None):
220
+ """Set values corresponding to ``nan_flags`` to ``np.nan``.
220
221
 
221
222
  Parameters
222
223
  ----------
223
- ds : xr.Dataset
224
+ ds : xarray.Dataset
224
225
  Input xarray dataset
225
226
  dict_nan_flags : dict
226
227
  Dictionary with nan flags value to set as np.nan
@@ -229,22 +230,22 @@ def replace_nan_flags(ds, sensor_name, verbose):
229
230
 
230
231
  Returns
231
232
  -------
232
- xr.Dataset
233
- Dataset without nan_flags values.
233
+ xarray.Dataset
234
+ Dataset without ``nan_flags`` values.
234
235
  """
235
236
  # Get dictionary of nan flags
236
237
  dict_nan_flags = get_nan_flags_dict(sensor_name)
237
238
  # Replace nan flags with nan
238
- ds = replace_custom_nan_flags(ds, dict_nan_flags, verbose=verbose)
239
+ ds = replace_custom_nan_flags(ds, dict_nan_flags=dict_nan_flags, logger=logger, verbose=verbose)
239
240
  return ds
240
241
 
241
242
 
242
- def set_nan_outside_data_range(ds, sensor_name, verbose):
243
- """Set values outside the data range as np.nan.
243
+ def set_nan_outside_data_range(ds, sensor_name, verbose, logger=None):
244
+ """Set values outside the data range as ``np.nan``.
244
245
 
245
246
  Parameters
246
247
  ----------
247
- ds : xr.Dataset
248
+ ds : xarray.Dataset
248
249
  Input xarray dataset
249
250
  sensor_name : str
250
251
  Name of the sensor.
@@ -253,7 +254,7 @@ def set_nan_outside_data_range(ds, sensor_name, verbose):
253
254
 
254
255
  Returns
255
256
  -------
256
- xr.Dataset
257
+ xarray.Dataset
257
258
  Dataset without values outside the expected data range.
258
259
  """
259
260
  # Get dictionary of data_range
@@ -277,12 +278,12 @@ def set_nan_outside_data_range(ds, sensor_name, verbose):
277
278
  return ds
278
279
 
279
280
 
280
- def set_nan_invalid_values(ds, sensor_name, verbose):
281
- """Set invalid (class) values to np.nan.
281
+ def set_nan_invalid_values(ds, sensor_name, verbose, logger=None):
282
+ """Set invalid (class) values to ``np.nan``.
282
283
 
283
284
  Parameters
284
285
  ----------
285
- ds : xr.Dataset
286
+ ds : xarray.Dataset
286
287
  Input xarray dataset
287
288
  sensor_name : str
288
289
  Name of the sensor.
@@ -291,7 +292,7 @@ def set_nan_invalid_values(ds, sensor_name, verbose):
291
292
 
292
293
  Returns
293
294
  -------
294
- xr.Dataset
295
+ xarray.Dataset
295
296
  Dataset without invalid values.
296
297
  """
297
298
  # Get dictionary of valid values
@@ -313,25 +314,146 @@ def set_nan_invalid_values(ds, sensor_name, verbose):
313
314
  return ds
314
315
 
315
316
 
316
- def create_l0b_from_raw_nc(
317
+ def drop_timesteps(ds, timesteps: list):
318
+ """
319
+ Drop specific time steps from a Dataset.
320
+
321
+ Parameters
322
+ ----------
323
+ ds : xarray.Dataset
324
+ Input dataset with a 'time' dimension.
325
+ timesteps : list
326
+ List of datetime-like values to remove.
327
+
328
+ Returns
329
+ -------
330
+ xarray.Dataset
331
+ Dataset with specified timesteps removed.
332
+
333
+ Raises
334
+ ------
335
+ ValueError
336
+ If no timesteps remain after removal.
337
+ """
338
+ # Create a boolean mask of valid timesteps
339
+ times = ds["time"].to_numpy()
340
+ mask = ~np.isin(times, np.array(timesteps, dtype=times.dtype))
341
+ ds_filtered = ds.isel(time=mask)
342
+
343
+ # Ensure there's at least one timestep left
344
+ if ds_filtered.sizes.get("time", 0) == 0:
345
+ raise ValueError(
346
+ "No timesteps left after removing problematic timesteps. " "Maybe you need to adjust the issue YAML file.",
347
+ )
348
+ return ds_filtered
349
+
350
+
351
+ def drop_time_periods(ds, time_periods: list):
352
+ """
353
+ Drop all time steps within any of the specified time intervals.
354
+
355
+ Parameters
356
+ ----------
357
+ ds : xarray.Dataset
358
+ Input dataset with a 'time' dimension.
359
+ time_periods : list of tuple
360
+ Each tuple is (start_time, end_time), datetime-like, inclusive.
361
+
362
+ Returns
363
+ -------
364
+ xarray.Dataset
365
+ Dataset with all times within the given periods removed.
366
+
367
+ Raises
368
+ ------
369
+ ValueError
370
+ If no timesteps remain after removal.
371
+ """
372
+ times = ds["time"].to_numpy()
373
+ mask = np.ones_like(times, dtype=bool)
374
+
375
+ for start, end in time_periods:
376
+ start_np = np.datetime64(start)
377
+ end_np = np.datetime64(end)
378
+ # exclude times in the inclusive interval [start, end]
379
+ mask &= ~((times >= start_np) & (times <= end_np))
380
+
381
+ ds_filtered = ds.isel(time=mask)
382
+
383
+ if ds_filtered.sizes.get("time", 0) == 0:
384
+ raise ValueError(
385
+ "No timesteps left after removing problematic time_periods. "
386
+ "Maybe you need to adjust the issue YAML file.",
387
+ )
388
+ return ds_filtered
389
+
390
+
391
+ def remove_issue_timesteps(
392
+ ds,
393
+ issue_dict: dict,
394
+ logger=None,
395
+ verbose: bool = False,
396
+ ):
397
+ """
398
+ Remove bad timesteps and time periods from an xarray Dataset according to issue definitions.
399
+
400
+ Parameters
401
+ ----------
402
+ ds : xarray.Dataset
403
+ Input dataset with a 'time' dimension.
404
+ issue_dict : dict
405
+ Dictionary with optional keys 'timesteps' (list of datetimes) and
406
+ 'time_periods' (list of (start, end) tuples).
407
+ logger : any, optional
408
+ Logger instance to record dropped steps, by default None.
409
+ verbose : bool, optional
410
+ Whether to log informational messages, by default False.
411
+
412
+ Returns
413
+ -------
414
+ xarray.Dataset
415
+ Cleaned dataset.
416
+
417
+ Raises
418
+ ------
419
+ ValueError
420
+ If after removing specified timesteps/periods no data remains.
421
+ """
422
+ n_initial = ds.sizes.get("time", 0)
423
+ timesteps = issue_dict.get("timesteps", []) or []
424
+ time_periods = issue_dict.get("time_periods", []) or []
425
+
426
+ # Drop individual timesteps
427
+ if timesteps:
428
+ ds = drop_timesteps(ds, timesteps)
429
+
430
+ # Drop intervals of time
431
+ if time_periods:
432
+ ds = drop_time_periods(ds, time_periods)
433
+
434
+ # Report number dropped
435
+ n_remaining = ds.sizes.get("time", 0)
436
+ dropped = n_initial - n_remaining
437
+ if dropped > 0:
438
+ msg = f"{dropped} timesteps were dropped according to the issue YAML file content."
439
+ log_info(logger=logger, msg=msg, verbose=verbose)
440
+ return ds
441
+
442
+
443
+ def sanitize_ds(
317
444
  ds,
318
- dict_names,
319
- ds_sanitizer_fun,
320
445
  sensor_name,
321
- verbose,
322
- attrs,
446
+ metadata,
447
+ issue_dict=None,
448
+ verbose=False,
449
+ logger=None,
323
450
  ):
324
- """Convert a raw xr.Dataset into a DISDRODB L0B netCDF.
451
+ """Convert a raw ``xr.Dataset`` into a DISDRODB L0B netCDF.
325
452
 
326
453
  Parameters
327
454
  ----------
328
- ds : xr.Dataset
329
- xr.Dataset
330
- dict_names : dict
331
- Dictionary mapping raw netCDF variables/coordinates/dimension names
332
- to DISDRODB standards.
333
- ds_sanitizer_fun : function
334
- Sanitizer function to do ad-hoc processing of the xr.Dataset.
455
+ ds : xarray.Dataset
456
+ Raw xarray dataset
335
457
  attrs: dict
336
458
  Global metadata to attach as global attributes to the xr.Dataset.
337
459
  sensor_name : str
@@ -342,39 +464,64 @@ def create_l0b_from_raw_nc(
342
464
 
343
465
  Returns
344
466
  -------
345
- xr.Dataset
467
+ xarray.Dataset
346
468
  L0B xr.Dataset
347
469
  """
348
- # Preprocess netcdf
349
- ds = preprocess_raw_netcdf(ds=ds, dict_names=dict_names, sensor_name=sensor_name)
350
-
351
- # Add CRS and geolocation information
352
- attrs = copy.deepcopy(attrs)
353
- coords = {}
354
- geolocation_vars = ["latitude", "longitude", "altitude"]
355
- for var in geolocation_vars:
356
- if var not in ds:
357
- coords[var] = attrs[var]
358
- _ = attrs.pop(var)
359
- ds = ds.assign_coords(coords)
360
-
361
- # Add global attributes
362
- ds.attrs = attrs
363
-
364
- # Apply dataset sanitizer function
365
- ds = ds_sanitizer_fun(ds)
366
-
367
470
  # Replace nan flags values with np.nans
368
- ds = replace_nan_flags(ds, sensor_name=sensor_name, verbose=verbose)
471
+ ds = replace_nan_flags(ds, sensor_name=sensor_name, logger=logger, verbose=verbose)
472
+
473
+ # Filter out problematic tiemsteps reported in the issue YAML file
474
+ ds = remove_issue_timesteps(ds, issue_dict=issue_dict, logger=logger, verbose=verbose)
369
475
 
370
476
  # Set values outside the data range to np.nan
371
- ds = set_nan_outside_data_range(ds, sensor_name=sensor_name, verbose=verbose)
477
+ ds = set_nan_outside_data_range(ds, sensor_name=sensor_name, logger=logger, verbose=verbose)
372
478
 
373
479
  # Replace invalid values with np.nan
374
- ds = set_nan_invalid_values(ds, sensor_name=sensor_name, verbose=verbose)
480
+ ds = set_nan_invalid_values(ds, sensor_name=sensor_name, logger=logger, verbose=verbose)
375
481
 
376
482
  # Finalize dataset
377
- ds = finalize_dataset(ds, sensor_name=sensor_name)
483
+ ds = finalize_dataset(ds, sensor_name=sensor_name, attrs=metadata)
378
484
 
379
485
  # Return dataset
380
486
  return ds
487
+
488
+
489
+ def open_raw_netcdf_file(
490
+ filepath,
491
+ logger=None,
492
+ engine="netcdf4",
493
+ cache=False,
494
+ chunks=None,
495
+ decode_timedelta=False,
496
+ **kwargs,
497
+ ):
498
+ """Open a raw netCDF file.
499
+
500
+ Parameters
501
+ ----------
502
+ filepath : str
503
+ Path to the raw netCDF file.
504
+
505
+ Returns
506
+ -------
507
+ xarray.Dataset
508
+ Raw netCDF file as an xarray Dataset.
509
+ """
510
+ import xarray as xr
511
+
512
+ # Note: chunks=None avoid usage of Dask
513
+
514
+ # Open the raw netCDF
515
+ with xr.open_dataset(
516
+ filepath,
517
+ decode_timedelta=decode_timedelta,
518
+ cache=cache,
519
+ engine=engine,
520
+ chunks=chunks,
521
+ **kwargs,
522
+ ) as data:
523
+ ds = data.load()
524
+
525
+ # Log information
526
+ log_info(logger=logger, msg=f"netCDF file {filepath} has been loaded successively into xarray.", verbose=False)
527
+ return ds