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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. disdrodb/__init__.py +132 -15
  2. disdrodb/_config.py +4 -2
  3. disdrodb/_version.py +9 -4
  4. disdrodb/api/checks.py +264 -237
  5. disdrodb/api/configs.py +4 -8
  6. disdrodb/api/create_directories.py +235 -290
  7. disdrodb/api/info.py +217 -26
  8. disdrodb/api/io.py +295 -269
  9. disdrodb/api/path.py +597 -173
  10. disdrodb/api/search.py +486 -0
  11. disdrodb/{metadata/scripts → cli}/disdrodb_check_metadata_archive.py +12 -7
  12. disdrodb/{utils/pandas.py → cli/disdrodb_data_archive_directory.py} +9 -18
  13. disdrodb/cli/disdrodb_download_archive.py +86 -0
  14. disdrodb/cli/disdrodb_download_metadata_archive.py +53 -0
  15. disdrodb/cli/disdrodb_download_station.py +84 -0
  16. disdrodb/{api/scripts → cli}/disdrodb_initialize_station.py +22 -10
  17. disdrodb/cli/disdrodb_metadata_archive_directory.py +32 -0
  18. disdrodb/{data_transfer/scripts/disdrodb_download_station.py → cli/disdrodb_open_data_archive.py} +22 -22
  19. disdrodb/cli/disdrodb_open_logs_directory.py +69 -0
  20. disdrodb/{data_transfer/scripts/disdrodb_upload_station.py → cli/disdrodb_open_metadata_archive.py} +22 -24
  21. disdrodb/cli/disdrodb_open_metadata_directory.py +71 -0
  22. disdrodb/cli/disdrodb_open_product_directory.py +74 -0
  23. disdrodb/cli/disdrodb_open_readers_directory.py +32 -0
  24. disdrodb/{l0/scripts → cli}/disdrodb_run_l0.py +38 -31
  25. disdrodb/{l0/scripts → cli}/disdrodb_run_l0_station.py +32 -30
  26. disdrodb/{l0/scripts → cli}/disdrodb_run_l0a.py +30 -21
  27. disdrodb/{l0/scripts → cli}/disdrodb_run_l0a_station.py +24 -33
  28. disdrodb/{l0/scripts → cli}/disdrodb_run_l0b.py +30 -21
  29. disdrodb/{l0/scripts → cli}/disdrodb_run_l0b_station.py +25 -34
  30. disdrodb/cli/disdrodb_run_l0c.py +130 -0
  31. disdrodb/cli/disdrodb_run_l0c_station.py +129 -0
  32. disdrodb/cli/disdrodb_run_l1.py +122 -0
  33. disdrodb/cli/disdrodb_run_l1_station.py +121 -0
  34. disdrodb/cli/disdrodb_run_l2e.py +122 -0
  35. disdrodb/cli/disdrodb_run_l2e_station.py +122 -0
  36. disdrodb/cli/disdrodb_run_l2m.py +122 -0
  37. disdrodb/cli/disdrodb_run_l2m_station.py +122 -0
  38. disdrodb/cli/disdrodb_upload_archive.py +105 -0
  39. disdrodb/cli/disdrodb_upload_station.py +98 -0
  40. disdrodb/configs.py +90 -25
  41. disdrodb/data_transfer/__init__.py +22 -0
  42. disdrodb/data_transfer/download_data.py +87 -90
  43. disdrodb/data_transfer/upload_data.py +64 -37
  44. disdrodb/data_transfer/zenodo.py +15 -18
  45. disdrodb/docs.py +1 -1
  46. disdrodb/issue/__init__.py +17 -4
  47. disdrodb/issue/checks.py +10 -23
  48. disdrodb/issue/reader.py +9 -12
  49. disdrodb/issue/writer.py +14 -17
  50. disdrodb/l0/__init__.py +17 -26
  51. disdrodb/l0/check_configs.py +35 -23
  52. disdrodb/l0/check_standards.py +32 -42
  53. disdrodb/l0/configs/{Thies_LPM → LPM}/bins_diameter.yml +44 -44
  54. disdrodb/l0/configs/{Thies_LPM → LPM}/bins_velocity.yml +40 -40
  55. disdrodb/l0/configs/LPM/l0a_encodings.yml +80 -0
  56. disdrodb/l0/configs/{Thies_LPM → LPM}/l0b_cf_attrs.yml +62 -59
  57. disdrodb/l0/configs/{Thies_LPM → LPM}/l0b_encodings.yml +9 -9
  58. disdrodb/l0/configs/{Thies_LPM → LPM}/raw_data_format.yml +245 -245
  59. disdrodb/l0/configs/{OTT_Parsivel → PARSIVEL}/bins_diameter.yml +66 -66
  60. disdrodb/l0/configs/{OTT_Parsivel → PARSIVEL}/bins_velocity.yml +64 -64
  61. disdrodb/l0/configs/PARSIVEL/l0a_encodings.yml +32 -0
  62. disdrodb/l0/configs/{OTT_Parsivel → PARSIVEL}/l0b_cf_attrs.yml +22 -20
  63. disdrodb/l0/configs/{OTT_Parsivel → PARSIVEL}/l0b_encodings.yml +17 -17
  64. disdrodb/l0/configs/{OTT_Parsivel → PARSIVEL}/raw_data_format.yml +77 -77
  65. disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/bins_diameter.yml +64 -64
  66. disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/bins_velocity.yml +64 -64
  67. disdrodb/l0/configs/PARSIVEL2/l0a_encodings.yml +39 -0
  68. disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/l0b_cf_attrs.yml +24 -22
  69. disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/l0b_encodings.yml +20 -20
  70. disdrodb/l0/configs/{OTT_Parsivel2 → PARSIVEL2}/raw_data_format.yml +98 -98
  71. disdrodb/l0/configs/{RD_80 → RD80}/bins_diameter.yml +40 -40
  72. disdrodb/l0/configs/RD80/l0a_encodings.yml +16 -0
  73. disdrodb/l0/configs/{RD_80 → RD80}/l0b_cf_attrs.yml +3 -3
  74. disdrodb/l0/configs/RD80/l0b_encodings.yml +135 -0
  75. disdrodb/l0/configs/{RD_80 → RD80}/raw_data_format.yml +48 -48
  76. disdrodb/l0/l0_reader.py +216 -340
  77. disdrodb/l0/l0a_processing.py +237 -208
  78. disdrodb/l0/l0b_nc_processing.py +227 -80
  79. disdrodb/l0/l0b_processing.py +93 -173
  80. disdrodb/l0/l0c_processing.py +627 -0
  81. disdrodb/l0/readers/{ARM → LPM/ARM}/ARM_LPM.py +36 -58
  82. disdrodb/l0/readers/LPM/AUSTRALIA/MELBOURNE_2007_LPM.py +226 -0
  83. disdrodb/l0/readers/LPM/BRAZIL/CHUVA_LPM.py +185 -0
  84. disdrodb/l0/readers/LPM/BRAZIL/GOAMAZON_LPM.py +183 -0
  85. disdrodb/l0/readers/LPM/ITALY/GID_LPM.py +179 -0
  86. disdrodb/l0/readers/{UK → LPM/UK}/DIVEN.py +14 -35
  87. disdrodb/l0/readers/PARSIVEL/AUSTRALIA/MELBOURNE_2007_PARSIVEL.py +157 -0
  88. disdrodb/l0/readers/PARSIVEL/CHINA/CHONGQING.py +113 -0
  89. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/ARCTIC_2021.py +40 -57
  90. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/COMMON_2011.py +37 -54
  91. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/DAVOS_2009_2011.py +34 -51
  92. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/EPFL_2009.py +34 -51
  93. disdrodb/l0/readers/{EPFL/PARADISO_2014.py → PARSIVEL/EPFL/EPFL_ROOF_2008.py} +38 -50
  94. disdrodb/l0/readers/PARSIVEL/EPFL/EPFL_ROOF_2010.py +105 -0
  95. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/EPFL_ROOF_2011.py +34 -51
  96. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/EPFL_ROOF_2012.py +33 -51
  97. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/GENEPI_2007.py +25 -44
  98. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/GRAND_ST_BERNARD_2007.py +25 -44
  99. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/GRAND_ST_BERNARD_2007_2.py +25 -44
  100. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/HPICONET_2010.py +34 -51
  101. disdrodb/l0/readers/{EPFL/EPFL_ROOF_2010.py → PARSIVEL/EPFL/HYMEX_LTE_SOP2.py} +37 -50
  102. disdrodb/l0/readers/PARSIVEL/EPFL/HYMEX_LTE_SOP3.py +111 -0
  103. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/HYMEX_LTE_SOP4.py +36 -54
  104. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/LOCARNO_2018.py +34 -52
  105. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/LOCARNO_2019.py +38 -56
  106. disdrodb/l0/readers/PARSIVEL/EPFL/PARADISO_2014.py +105 -0
  107. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/PARSIVEL_2007.py +27 -45
  108. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/PLATO_2019.py +24 -44
  109. disdrodb/l0/readers/PARSIVEL/EPFL/RACLETS_2019.py +140 -0
  110. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/RACLETS_2019_WJF.py +41 -59
  111. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/RIETHOLZBACH_2011.py +34 -51
  112. disdrodb/l0/readers/PARSIVEL/EPFL/SAMOYLOV_2017.py +117 -0
  113. disdrodb/l0/readers/PARSIVEL/EPFL/SAMOYLOV_2019.py +137 -0
  114. disdrodb/l0/readers/{EPFL → PARSIVEL/EPFL}/UNIL_2022.py +42 -55
  115. disdrodb/l0/readers/PARSIVEL/GPM/IFLOODS.py +104 -0
  116. disdrodb/l0/readers/{GPM → PARSIVEL/GPM}/LPVEX.py +29 -48
  117. disdrodb/l0/readers/PARSIVEL/GPM/MC3E.py +184 -0
  118. disdrodb/l0/readers/PARSIVEL/NCAR/CCOPE_2015.py +113 -0
  119. disdrodb/l0/readers/{NCAR/VORTEX_SE_2016_P1.py → PARSIVEL/NCAR/OWLES_MIPS.py} +46 -72
  120. disdrodb/l0/readers/PARSIVEL/NCAR/PECAN_MOBILE.py +125 -0
  121. disdrodb/l0/readers/{NCAR/OWLES_MIPS.py → PARSIVEL/NCAR/PLOWS_MIPS.py} +45 -64
  122. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2009.py +114 -0
  123. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010.py +176 -0
  124. disdrodb/l0/readers/PARSIVEL/NCAR/VORTEX2_2010_UF.py +183 -0
  125. disdrodb/l0/readers/{ARM/ARM_LD.py → PARSIVEL2/ARM/ARM_PARSIVEL2.py} +27 -50
  126. disdrodb/l0/readers/PARSIVEL2/BRAZIL/CHUVA_PARSIVEL2.py +163 -0
  127. disdrodb/l0/readers/PARSIVEL2/BRAZIL/GOAMAZON_PARSIVEL2.py +163 -0
  128. disdrodb/l0/readers/{DENMARK → PARSIVEL2/DENMARK}/EROSION_nc.py +14 -35
  129. disdrodb/l0/readers/PARSIVEL2/FRANCE/SIRTA_PARSIVEL2.py +119 -0
  130. disdrodb/l0/readers/PARSIVEL2/GPM/GCPEX.py +104 -0
  131. disdrodb/l0/readers/PARSIVEL2/GPM/NSSTC.py +176 -0
  132. disdrodb/l0/readers/PARSIVEL2/ITALY/GID_PARSIVEL2.py +32 -0
  133. disdrodb/l0/readers/PARSIVEL2/MEXICO/OH_IIUNAM_nc.py +56 -0
  134. disdrodb/l0/readers/PARSIVEL2/NCAR/PECAN_FP3.py +120 -0
  135. disdrodb/l0/readers/{NCAR → PARSIVEL2/NCAR}/PECAN_MIPS.py +45 -64
  136. disdrodb/l0/readers/PARSIVEL2/NCAR/RELAMPAGO_PARSIVEL2.py +181 -0
  137. disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_PJ.py +160 -0
  138. disdrodb/l0/readers/PARSIVEL2/NCAR/SNOWIE_SB.py +160 -0
  139. disdrodb/l0/readers/{NCAR/PLOWS_MIPS.py → PARSIVEL2/NCAR/VORTEX_SE_2016_P1.py} +49 -66
  140. disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_P2.py +118 -0
  141. disdrodb/l0/readers/PARSIVEL2/NCAR/VORTEX_SE_2016_PIPS.py +152 -0
  142. disdrodb/l0/readers/PARSIVEL2/NETHERLANDS/DELFT.py +166 -0
  143. disdrodb/l0/readers/{NCAR/RELAMPAGO_RD80.py → RD80/BRAZIL/CHUVA_RD80.py} +36 -60
  144. disdrodb/l0/readers/{BRAZIL → RD80/BRAZIL}/GOAMAZON_RD80.py +36 -55
  145. disdrodb/l0/readers/{NCAR → RD80/NCAR}/CINDY_2011_RD80.py +35 -54
  146. disdrodb/l0/readers/{BRAZIL/CHUVA_RD80.py → RD80/NCAR/RELAMPAGO_RD80.py} +40 -54
  147. disdrodb/l0/readers/template_reader_raw_netcdf_data.py +62 -0
  148. disdrodb/l0/readers/{reader_template.py → template_reader_raw_text_data.py} +20 -44
  149. disdrodb/l0/routines.py +885 -581
  150. disdrodb/l0/standards.py +72 -236
  151. disdrodb/l0/template_tools.py +104 -109
  152. disdrodb/l1/__init__.py +17 -0
  153. disdrodb/l1/beard_model.py +716 -0
  154. disdrodb/l1/encoding_attrs.py +620 -0
  155. disdrodb/l1/fall_velocity.py +260 -0
  156. disdrodb/l1/filters.py +192 -0
  157. disdrodb/l1/processing.py +200 -0
  158. disdrodb/l1/resampling.py +236 -0
  159. disdrodb/l1/routines.py +357 -0
  160. disdrodb/l1_env/__init__.py +17 -0
  161. disdrodb/l1_env/routines.py +38 -0
  162. disdrodb/l2/__init__.py +17 -0
  163. disdrodb/l2/empirical_dsd.py +1735 -0
  164. disdrodb/l2/event.py +388 -0
  165. disdrodb/l2/processing.py +519 -0
  166. disdrodb/l2/processing_options.py +213 -0
  167. disdrodb/l2/routines.py +868 -0
  168. disdrodb/metadata/__init__.py +9 -2
  169. disdrodb/metadata/checks.py +165 -118
  170. disdrodb/metadata/download.py +81 -0
  171. disdrodb/metadata/geolocation.py +146 -0
  172. disdrodb/metadata/info.py +20 -13
  173. disdrodb/metadata/manipulation.py +1 -1
  174. disdrodb/metadata/reader.py +59 -8
  175. disdrodb/metadata/search.py +77 -144
  176. disdrodb/metadata/standards.py +7 -8
  177. disdrodb/metadata/writer.py +8 -14
  178. disdrodb/psd/__init__.py +38 -0
  179. disdrodb/psd/fitting.py +2146 -0
  180. disdrodb/psd/models.py +774 -0
  181. disdrodb/routines.py +1176 -0
  182. disdrodb/scattering/__init__.py +28 -0
  183. disdrodb/scattering/axis_ratio.py +344 -0
  184. disdrodb/scattering/routines.py +456 -0
  185. disdrodb/utils/__init__.py +17 -0
  186. disdrodb/utils/attrs.py +208 -0
  187. disdrodb/utils/cli.py +269 -0
  188. disdrodb/utils/compression.py +60 -42
  189. disdrodb/utils/dask.py +62 -0
  190. disdrodb/utils/decorators.py +110 -0
  191. disdrodb/utils/directories.py +107 -46
  192. disdrodb/utils/encoding.py +127 -0
  193. disdrodb/utils/list.py +29 -0
  194. disdrodb/utils/logger.py +168 -46
  195. disdrodb/utils/time.py +657 -0
  196. disdrodb/utils/warnings.py +30 -0
  197. disdrodb/utils/writer.py +57 -0
  198. disdrodb/utils/xarray.py +138 -47
  199. disdrodb/utils/yaml.py +0 -1
  200. disdrodb/viz/__init__.py +17 -0
  201. disdrodb/viz/plots.py +17 -0
  202. disdrodb-0.1.0.dist-info/METADATA +321 -0
  203. disdrodb-0.1.0.dist-info/RECORD +216 -0
  204. {disdrodb-0.0.21.dist-info → disdrodb-0.1.0.dist-info}/WHEEL +1 -1
  205. disdrodb-0.1.0.dist-info/entry_points.txt +30 -0
  206. disdrodb/data_transfer/scripts/disdrodb_download_archive.py +0 -53
  207. disdrodb/data_transfer/scripts/disdrodb_upload_archive.py +0 -57
  208. disdrodb/l0/configs/OTT_Parsivel/l0a_encodings.yml +0 -32
  209. disdrodb/l0/configs/OTT_Parsivel2/l0a_encodings.yml +0 -39
  210. disdrodb/l0/configs/RD_80/l0a_encodings.yml +0 -16
  211. disdrodb/l0/configs/RD_80/l0b_encodings.yml +0 -135
  212. disdrodb/l0/configs/Thies_LPM/l0a_encodings.yml +0 -80
  213. disdrodb/l0/io.py +0 -257
  214. disdrodb/l0/l0_processing.py +0 -1091
  215. disdrodb/l0/readers/AUSTRALIA/MELBOURNE_2007_OTT.py +0 -178
  216. disdrodb/l0/readers/AUSTRALIA/MELBOURNE_2007_THIES.py +0 -247
  217. disdrodb/l0/readers/BRAZIL/CHUVA_LPM.py +0 -204
  218. disdrodb/l0/readers/BRAZIL/CHUVA_OTT.py +0 -183
  219. disdrodb/l0/readers/BRAZIL/GOAMAZON_LPM.py +0 -204
  220. disdrodb/l0/readers/BRAZIL/GOAMAZON_OTT.py +0 -183
  221. disdrodb/l0/readers/CHINA/CHONGQING.py +0 -131
  222. disdrodb/l0/readers/EPFL/EPFL_ROOF_2008.py +0 -128
  223. disdrodb/l0/readers/EPFL/HYMEX_LTE_SOP2.py +0 -127
  224. disdrodb/l0/readers/EPFL/HYMEX_LTE_SOP3.py +0 -129
  225. disdrodb/l0/readers/EPFL/RACLETS_2019.py +0 -158
  226. disdrodb/l0/readers/EPFL/SAMOYLOV_2017.py +0 -136
  227. disdrodb/l0/readers/EPFL/SAMOYLOV_2019.py +0 -158
  228. disdrodb/l0/readers/FRANCE/SIRTA_OTT2.py +0 -138
  229. disdrodb/l0/readers/GPM/GCPEX.py +0 -123
  230. disdrodb/l0/readers/GPM/IFLOODS.py +0 -123
  231. disdrodb/l0/readers/GPM/MC3E.py +0 -123
  232. disdrodb/l0/readers/GPM/NSSTC.py +0 -164
  233. disdrodb/l0/readers/ITALY/GID.py +0 -199
  234. disdrodb/l0/readers/MEXICO/OH_IIUNAM_nc.py +0 -92
  235. disdrodb/l0/readers/NCAR/CCOPE_2015.py +0 -133
  236. disdrodb/l0/readers/NCAR/PECAN_FP3.py +0 -137
  237. disdrodb/l0/readers/NCAR/PECAN_MOBILE.py +0 -144
  238. disdrodb/l0/readers/NCAR/RELAMPAGO_OTT.py +0 -195
  239. disdrodb/l0/readers/NCAR/SNOWIE_PJ.py +0 -172
  240. disdrodb/l0/readers/NCAR/SNOWIE_SB.py +0 -179
  241. disdrodb/l0/readers/NCAR/VORTEX2_2009.py +0 -133
  242. disdrodb/l0/readers/NCAR/VORTEX2_2010.py +0 -188
  243. disdrodb/l0/readers/NCAR/VORTEX2_2010_UF.py +0 -191
  244. disdrodb/l0/readers/NCAR/VORTEX_SE_2016_P2.py +0 -135
  245. disdrodb/l0/readers/NCAR/VORTEX_SE_2016_PIPS.py +0 -170
  246. disdrodb/l0/readers/NETHERLANDS/DELFT.py +0 -187
  247. disdrodb/l0/readers/SPAIN/SBEGUERIA.py +0 -179
  248. disdrodb/l0/scripts/disdrodb_run_l0b_concat.py +0 -93
  249. disdrodb/l0/scripts/disdrodb_run_l0b_concat_station.py +0 -85
  250. disdrodb/utils/netcdf.py +0 -452
  251. disdrodb/utils/scripts.py +0 -102
  252. disdrodb-0.0.21.dist-info/AUTHORS.md +0 -18
  253. disdrodb-0.0.21.dist-info/METADATA +0 -186
  254. disdrodb-0.0.21.dist-info/RECORD +0 -168
  255. disdrodb-0.0.21.dist-info/entry_points.txt +0 -15
  256. /disdrodb/l0/configs/{RD_80 → RD80}/bins_velocity.yml +0 -0
  257. /disdrodb/l0/manuals/{Thies_LPM.pdf → LPM.pdf} +0 -0
  258. /disdrodb/l0/manuals/{ODM_470.pdf → ODM470.pdf} +0 -0
  259. /disdrodb/l0/manuals/{OTT_Parsivel.pdf → PARSIVEL.pdf} +0 -0
  260. /disdrodb/l0/manuals/{OTT_Parsivel2.pdf → PARSIVEL2.pdf} +0 -0
  261. /disdrodb/l0/manuals/{PWS_100.pdf → PWS100.pdf} +0 -0
  262. /disdrodb/l0/manuals/{RD_80.pdf → RD80.pdf} +0 -0
  263. {disdrodb-0.0.21.dist-info → disdrodb-0.1.0.dist-info/licenses}/LICENSE +0 -0
  264. {disdrodb-0.0.21.dist-info → disdrodb-0.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,456 @@
1
+ # -----------------------------------------------------------------------------.
2
+ # Copyright (c) 2021-2023 DISDRODB developers
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+ # -----------------------------------------------------------------------------.
17
+ """Implement PSD scattering routines."""
18
+
19
+ import itertools
20
+
21
+ import dask
22
+ import numpy as np
23
+ import xarray as xr
24
+ from pytmatrix import orientation, radar, refractive, tmatrix_aux
25
+ from pytmatrix.psd import BinnedPSD, PSDIntegrator
26
+ from pytmatrix.tmatrix import Scatterer
27
+
28
+ from disdrodb.psd.models import create_psd, get_required_parameters
29
+ from disdrodb.scattering.axis_ratio import check_axis_ratio, get_axis_ratio_method
30
+ from disdrodb.utils.warnings import suppress_warnings
31
+
32
+ # Wavelengths for which the refractive index is defined in pytmatrix (in mm)
33
+ wavelength_dict = {
34
+ "S": tmatrix_aux.wl_S,
35
+ "C": tmatrix_aux.wl_C,
36
+ "X": tmatrix_aux.wl_X,
37
+ "Ku": tmatrix_aux.wl_Ku,
38
+ "Ka": tmatrix_aux.wl_Ka,
39
+ "W": tmatrix_aux.wl_W,
40
+ }
41
+
42
+
43
+ def available_radar_bands():
44
+ """Return a list of the available radar bands."""
45
+ return list(wavelength_dict)
46
+
47
+
48
+ def check_radar_band(radar_band):
49
+ """Check the validity of the specified radar band."""
50
+ available_bands = available_radar_bands()
51
+ if radar_band not in available_bands:
52
+ raise ValueError(f"{radar_band} is an invalid radar band. Valid radar bands: {available_bands}.")
53
+ return radar_band
54
+
55
+
56
+ def get_radar_wavelength(radar_band):
57
+ """Get the wavelength of a radar band."""
58
+ wavelength = wavelength_dict[radar_band]
59
+ return wavelength
60
+
61
+
62
+ def initialize_scatterer(wavelength, canting_angle_std=7, D_max=8, axis_ratio="Thurai2007"):
63
+ """Initialize T-matrix scatterer object for a given wavelength."""
64
+ # Retrieve custom axis ratio function
65
+ axis_ratio_func = get_axis_ratio_method(axis_ratio)
66
+
67
+ # Retrieve water complex refractive index
68
+ # - Here we currently assume 10 °C
69
+ # - m_w_0C and m_w_20C are also available
70
+ # TODO: should be another dimension ? Or use scatterer.psd_integrator.m_func?
71
+ water_refractive_index = refractive.m_w_10C[wavelength]
72
+
73
+ # ---------------------------------------------------------------.
74
+ # Initialize Scatterer class
75
+ scatterer = Scatterer(wavelength=wavelength, m=water_refractive_index)
76
+ # - Define particle orientation PDF for orientational averaging
77
+ # --> The standard deviation of the angle with respect to vertical orientation (the canting angle).
78
+ scatterer.or_pdf = orientation.gaussian_pdf(std=canting_angle_std)
79
+ # - Define orientation methods
80
+ # --> Alternatives: orient_averaged_fixed, orient_single
81
+ scatterer.orient = orientation.orient_averaged_fixed
82
+
83
+ # ---------------------------------------------------------------.
84
+ # Initialize PSDIntegrator
85
+ scatterer.psd_integrator = PSDIntegrator()
86
+ # - Define axis_ratio_func
87
+ # --> The Scatterer class expects horizontal to vertical
88
+ scatterer.psd_integrator.axis_ratio_func = lambda D: 1.0 / axis_ratio_func(D)
89
+ # - Define function to compute refrative index (as function of D)
90
+ # scatterer.psd_integrator.m_func = None # Use constant value of scatterer.m
91
+ # - Define number of points over which to integrate
92
+ scatterer.psd_integrator.num_points = 1024
93
+ # - Define maximum drop diameter
94
+ scatterer.psd_integrator.D_max = D_max
95
+ # - Define geometries
96
+ scatterer.psd_integrator.geometries = (tmatrix_aux.geom_horiz_back, tmatrix_aux.geom_horiz_forw)
97
+ # ---------------------------------------------------------------.
98
+ # Initialize scattering table
99
+ scatterer.psd_integrator.init_scatter_table(scatterer)
100
+ return scatterer
101
+
102
+
103
+ def compute_radar_variables(scatterer):
104
+ """Compute radar variables for a given scatter object with a specified PSD.
105
+
106
+ To speed up computations, this function should input a scatterer object with
107
+ a preinitialized scattering table.
108
+ """
109
+ # Compute radar parameters
110
+ radar_vars = {}
111
+ scatterer.set_geometry(tmatrix_aux.geom_horiz_back)
112
+ radar_vars["Zh"] = 10 * np.log10(radar.refl(scatterer, h_pol=True)) # dBZ
113
+ radar_vars["Zdr"] = 10 * np.log10(radar.Zdr(scatterer)) # dB
114
+ radar_vars["rho_hv"] = radar.rho_hv(scatterer)
115
+ radar_vars["ldr"] = radar.ldr(scatterer)
116
+ scatterer.set_geometry(tmatrix_aux.geom_horiz_forw)
117
+ radar_vars["Kdp"] = radar.Kdp(scatterer)
118
+ radar_vars["Ai"] = radar.Ai(scatterer)
119
+ return radar_vars
120
+
121
+
122
+ def _estimate_empirical_radar_parameters(
123
+ drop_number_concentration,
124
+ bin_edges,
125
+ scatterer,
126
+ output_dictionary,
127
+ ):
128
+ # Initialize bad results
129
+ if output_dictionary:
130
+ null_output = {"Zh": np.nan, "Zdr": np.nan, "rho_hv": np.nan, "ldr": np.nan, "Kdp": np.nan, "Ai": np.nan}
131
+ else:
132
+ null_output = np.array([np.nan, np.nan, np.nan, np.nan, np.nan, np.nan])
133
+
134
+ # Assign PSD model to the scatterer object
135
+ scatterer.psd = BinnedPSD(bin_edges, drop_number_concentration)
136
+
137
+ # Get radar variables
138
+ with suppress_warnings():
139
+ try:
140
+ radar_vars = compute_radar_variables(scatterer)
141
+ output = radar_vars if output_dictionary else np.array(list(radar_vars.values()))
142
+ except Exception:
143
+ output = null_output
144
+ return output
145
+
146
+
147
+ def _estimate_model_radar_parameters(
148
+ parameters,
149
+ psd_model,
150
+ psd_parameters_names,
151
+ scatterer,
152
+ output_dictionary,
153
+ ):
154
+ # Initialize bad results
155
+ if output_dictionary:
156
+ null_output = {"Zh": np.nan, "Zdr": np.nan, "rho_hv": np.nan, "ldr": np.nan, "Kdp": np.nan, "Ai": np.nan}
157
+ else:
158
+ null_output = np.array([np.nan, np.nan, np.nan, np.nan, np.nan, np.nan])
159
+
160
+ # Assign PSD model to the scatterer object
161
+ parameters = dict(zip(psd_parameters_names, parameters))
162
+ scatterer.psd = create_psd(psd_model, parameters)
163
+
164
+ # Get radar variables
165
+ with suppress_warnings():
166
+ radar_vars = compute_radar_variables(scatterer)
167
+ try:
168
+ radar_vars = compute_radar_variables(scatterer)
169
+ output = radar_vars if output_dictionary else np.array(list(radar_vars.values()))
170
+ except Exception:
171
+ output = null_output
172
+ return output
173
+
174
+
175
+ def get_psd_parameters(ds):
176
+ """Return a xr.Dataset with only the PSD parameters as variable."""
177
+ psd_model = ds.attrs["disdrodb_psd_model"]
178
+ required_parameters = get_required_parameters(psd_model)
179
+ missing_parameters = [param for param in required_parameters if param not in ds]
180
+ if len(missing_parameters) > 0:
181
+ raise ValueError(f"The {psd_model} parameters {missing_parameters} are not present in the dataset.")
182
+ return ds[required_parameters]
183
+
184
+
185
+ def get_model_radar_parameters(
186
+ ds,
187
+ radar_band,
188
+ canting_angle_std=7,
189
+ diameter_max=8,
190
+ axis_ratio="Thurai2007",
191
+ ):
192
+ """Compute radar parameters from a PSD model.
193
+
194
+ Parameters
195
+ ----------
196
+ ds : xarray.Dataset
197
+ Dataset containing the parameters of the PSD model.
198
+ The dataset attribute disdrodb_psd_model specifies the PSD model to use.
199
+ radar_band : str
200
+ Radar band to be used.
201
+ canting_angle_std : float, optional
202
+ Standard deviation of the canting angle. The default value is 7.
203
+ diameter_max : float, optional
204
+ Maximum diameter. The default value is 8 mm.
205
+ axis_ratio : str, optional
206
+ Method to compute the axis ratio. The default method is ``Thurai2007``.
207
+
208
+ Returns
209
+ -------
210
+ xarray.Dataset
211
+ Dataset containing the computed radar parameters.
212
+ """
213
+ # Retrieve psd model and parameters.
214
+ psd_model = ds.attrs["disdrodb_psd_model"]
215
+ required_parameters = get_required_parameters(psd_model)
216
+ ds_parameters = get_psd_parameters(ds)
217
+
218
+ # Check argument validity
219
+ axis_ratio = check_axis_ratio(axis_ratio)
220
+ radar_band = check_radar_band(radar_band)
221
+
222
+ # Retrieve wavelengths in mm
223
+ wavelength = get_radar_wavelength(radar_band)
224
+
225
+ # Create DataArray with PSD parameters
226
+ da_parameters = ds_parameters.to_array(dim="psd_parameters").compute()
227
+
228
+ # Initialize scattering table
229
+ scatterer = initialize_scatterer(
230
+ wavelength=wavelength,
231
+ canting_angle_std=canting_angle_std,
232
+ D_max=diameter_max,
233
+ axis_ratio=axis_ratio,
234
+ )
235
+
236
+ # Define kwargs
237
+ kwargs = {
238
+ "output_dictionary": False,
239
+ "psd_model": psd_model,
240
+ "psd_parameters_names": required_parameters,
241
+ "scatterer": scatterer,
242
+ }
243
+
244
+ # Loop over each PSD (not in parallel --> dask="forbidden")
245
+ # - It costs much more to initiate the scatterer rather than looping over timesteps !
246
+ da_radar = xr.apply_ufunc(
247
+ _estimate_model_radar_parameters,
248
+ da_parameters,
249
+ kwargs=kwargs,
250
+ input_core_dims=[["psd_parameters"]],
251
+ output_core_dims=[["radar_variables"]],
252
+ vectorize=True,
253
+ dask="forbidden",
254
+ dask_gufunc_kwargs={"output_sizes": {"radar_variables": 5}}, # lengths of the new output_core_dims dimensions.
255
+ output_dtypes=["float64"],
256
+ )
257
+
258
+ # Add parameters coordinates
259
+ da_radar = da_radar.assign_coords({"radar_variables": ["Zh", "Zdr", "rho_hv", "ldr", "Kdp", "Ai"]})
260
+
261
+ # Create parameters dataset
262
+ ds_radar = da_radar.to_dataset(dim="radar_variables")
263
+
264
+ # Expand dimensions for later merging
265
+ dims_dict = {
266
+ "radar_band": [radar_band],
267
+ "axis_ratio": [axis_ratio],
268
+ "canting_angle_std": [canting_angle_std],
269
+ "diameter_max": [diameter_max],
270
+ }
271
+ ds_radar = ds_radar.expand_dims(dim=dims_dict)
272
+ return ds_radar
273
+
274
+
275
+ def get_empirical_radar_parameters(
276
+ ds,
277
+ radar_band=None,
278
+ canting_angle_std=7,
279
+ diameter_max=8,
280
+ axis_ratio="Thurai2007",
281
+ ):
282
+ """Compute radar parameters from empirical drop number concentration.
283
+
284
+ Parameters
285
+ ----------
286
+ ds : xarray.Dataset
287
+ Dataset containing the drop number concentration variable.
288
+ radar_band : str
289
+ Radar band to be used.
290
+ canting_angle_std : float, optional
291
+ Standard deviation of the canting angle. The default value is 7.
292
+ diameter_max : float, optional
293
+ Maximum diameter. The default value is 8 mm.
294
+ axis_ratio : str, optional
295
+ Method to compute the axis ratio. The default method is ``Thurai2007``.
296
+
297
+ Returns
298
+ -------
299
+ xarray.Dataset
300
+ Dataset containing the computed radar parameters.
301
+ """
302
+ # Define inputs
303
+ da_drop_number_concentration = ds["drop_number_concentration"].compute()
304
+
305
+ # Define bin edges
306
+ bin_edges = np.append(ds["diameter_bin_lower"].compute().data, ds["diameter_bin_upper"].compute().data[-1])
307
+
308
+ # Check argument validity
309
+ axis_ratio = check_axis_ratio(axis_ratio)
310
+ radar_band = check_radar_band(radar_band)
311
+
312
+ # Retrieve wavelengths in mm
313
+ wavelength = get_radar_wavelength(radar_band)
314
+
315
+ # Initialize scattering table
316
+ scatterer = initialize_scatterer(
317
+ wavelength=wavelength,
318
+ canting_angle_std=canting_angle_std,
319
+ D_max=diameter_max,
320
+ axis_ratio=axis_ratio,
321
+ )
322
+
323
+ # Define kwargs
324
+ kwargs = {
325
+ "output_dictionary": False,
326
+ "bin_edges": bin_edges,
327
+ "scatterer": scatterer,
328
+ }
329
+
330
+ # Loop over each PSD (not in parallel --> dask="forbidden")
331
+ # - It costs much more to initiate the scatterer rather than looping over timesteps !
332
+ da_radar = xr.apply_ufunc(
333
+ _estimate_empirical_radar_parameters,
334
+ da_drop_number_concentration,
335
+ kwargs=kwargs,
336
+ input_core_dims=[["diameter_bin_center"]],
337
+ output_core_dims=[["radar_variables"]],
338
+ vectorize=True,
339
+ dask="forbidden",
340
+ dask_gufunc_kwargs={"output_sizes": {"radar_variables": 5}}, # lengths of the new output_core_dims dimensions.
341
+ output_dtypes=["float64"],
342
+ )
343
+
344
+ # Add parameters coordinates
345
+ da_radar = da_radar.assign_coords({"radar_variables": ["Zh", "Zdr", "rho_hv", "ldr", "Kdp", "Ai"]})
346
+
347
+ # Create parameters dataset
348
+ ds_radar = da_radar.to_dataset(dim="radar_variables")
349
+
350
+ # Expand dimensions for later merging
351
+ dims_dict = {
352
+ "radar_band": [radar_band],
353
+ "axis_ratio": [axis_ratio],
354
+ "canting_angle_std": [canting_angle_std],
355
+ "diameter_max": [diameter_max],
356
+ }
357
+ ds_radar = ds_radar.expand_dims(dim=dims_dict)
358
+ return ds_radar
359
+
360
+
361
+ def get_radar_parameters(
362
+ ds,
363
+ radar_band=None,
364
+ canting_angle_std=7,
365
+ diameter_max=8,
366
+ axis_ratio="Thurai2007",
367
+ parallel=True,
368
+ ):
369
+ """Compute radar parameters from empirical drop number concentration or PSD model.
370
+
371
+ Parameters
372
+ ----------
373
+ ds : xarray.Dataset
374
+ Dataset containing the drop number concentration variable.
375
+ radar_band : str or list of str, optional
376
+ Radar band(s) to be used.
377
+ If ``None`` (the default), all available radar bands are used.
378
+ canting_angle_std : float or list of float, optional
379
+ Standard deviation of the canting angle. The default value is 7.
380
+ diameter_max : float or list of float, optional
381
+ Maximum diameter. The default value is 8 mm.
382
+ axis_ratio : str or list of str, optional
383
+ Method to compute the axis ratio. The default method is ``Thurai2007``.
384
+ parallel : bool, optional
385
+ Whether to compute radar variables in parallel.
386
+ The default value is ``True``.
387
+
388
+ Returns
389
+ -------
390
+ xarray.Dataset
391
+ Dataset containing the computed radar parameters.
392
+ """
393
+ # Decide whether to simulate radar parameters based on empirical PSD or model PSD
394
+ if "disdrodb_psd_model" not in ds.attrs and "drop_number_concentration" not in ds:
395
+ raise ValueError("The input dataset is not a DISDRODB L2E or L2M product.")
396
+ # Model-based simulation
397
+ if "disdrodb_psd_model" in ds.attrs:
398
+ func = get_model_radar_parameters
399
+ ds_subset = get_psd_parameters(ds).compute()
400
+ # Empirical PSD simulation
401
+ else:
402
+ func = get_empirical_radar_parameters
403
+ ds_subset = ds[["drop_number_concentration"]].compute()
404
+
405
+ # Initialize radar band if not provided
406
+ if radar_band is None:
407
+ radar_band = available_radar_bands()
408
+
409
+ # Ensure parameters are list
410
+ diameter_max = np.atleast_1d(diameter_max)
411
+ canting_angle_std = np.atleast_1d(canting_angle_std)
412
+ axis_ratio = np.atleast_1d(axis_ratio)
413
+ radar_band = np.atleast_1d(radar_band)
414
+
415
+ # Check parameters validity
416
+ axis_ratio = [check_axis_ratio(method) for method in axis_ratio]
417
+ radar_band = [check_radar_band(band) for band in radar_band]
418
+
419
+ # Order radar band from longest to shortest wavelength
420
+ # - ["S", "C", "X", "Ku", "Ka", "W"]
421
+ radar_band = sorted(radar_band, key=lambda x: wavelength_dict[x])[::-1]
422
+
423
+ # Retrieve combination of parameters
424
+ list_params = [
425
+ {
426
+ "radar_band": rb.item(),
427
+ "canting_angle_std": cas.item(),
428
+ "axis_ratio": ar.item(),
429
+ "diameter_max": d_max.item(),
430
+ }
431
+ for rb, cas, ar, d_max in itertools.product(radar_band, canting_angle_std, axis_ratio, diameter_max)
432
+ ]
433
+
434
+ # Compute radar variables for each configuration in parallel
435
+ # - The function expects the data into memory (no dask arrays !)
436
+ if parallel:
437
+ list_ds = [dask.delayed(func)(ds_subset, **params) for params in list_params]
438
+ list_ds = dask.compute(*list_ds)
439
+ else:
440
+ list_ds = [func(ds_subset, **params) for params in list_params]
441
+
442
+ # Merge into a single dataset
443
+ # - Order radar bands from longest to shortest wavelength
444
+ ds_radar = xr.merge(list_ds)
445
+ ds_radar = ds_radar.sel(radar_band=radar_band)
446
+
447
+ # Copy global attributes from input dataset
448
+ ds_radar.attrs = ds.attrs.copy()
449
+
450
+ # Remove single dimensions (add info to attributes)
451
+ parameters = ["radar_band", "canting_angle_std", "axis_ratio", "diameter_max"]
452
+ for param in parameters:
453
+ if ds_radar.sizes[param] == 1:
454
+ ds_radar.attrs[f"disdrodb_scattering_{param}"] = ds_radar[param].item()
455
+ ds_radar = ds_radar.squeeze()
456
+ return ds_radar
@@ -0,0 +1,17 @@
1
+ # -----------------------------------------------------------------------------.
2
+ # Copyright (c) 2021-2023 DISDRODB developers
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+ # -----------------------------------------------------------------------------.
17
+ """DISDRODB Utils Module."""
@@ -0,0 +1,208 @@
1
+ #!/usr/bin/env python3
2
+
3
+ # -----------------------------------------------------------------------------.
4
+ # Copyright (c) 2021-2023 DISDRODB developers
5
+ #
6
+ # This program is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ # -----------------------------------------------------------------------------.
19
+ """DISDRODB netCDF4 attributes utilities."""
20
+ import datetime
21
+
22
+ from disdrodb import ARCHIVE_VERSION, CONVENTIONS, SOFTWARE_VERSION
23
+
24
+ ####---------------------------------------------------------------------.
25
+ #### Variable attributes
26
+
27
+
28
+ def set_attrs(ds, attrs_dict):
29
+ """Set attributes to the variables of the xr.Dataset."""
30
+ for var in attrs_dict:
31
+ if var in ds:
32
+ ds[var].attrs.update(attrs_dict[var])
33
+ return ds
34
+
35
+
36
+ ####---------------------------------------------------------------------.
37
+ #### Coordinates attributes
38
+
39
+
40
+ def get_coords_attrs_dict():
41
+ """Return dictionary with DISDRODB coordinates attributes."""
42
+ attrs_dict = {}
43
+ # Define diameter attributes
44
+ attrs_dict["diameter_bin_center"] = {
45
+ "name": "diameter_bin_center",
46
+ "standard_name": "diameter_bin_center",
47
+ "long_name": "diameter_bin_center",
48
+ "units": "mm",
49
+ "description": "Bin center drop diameter value",
50
+ }
51
+ attrs_dict["diameter_bin_width"] = {
52
+ "name": "diameter_bin_width",
53
+ "standard_name": "diameter_bin_width",
54
+ "long_name": "diameter_bin_width",
55
+ "units": "mm",
56
+ "description": "Drop diameter bin width",
57
+ }
58
+ attrs_dict["diameter_bin_upper"] = {
59
+ "name": "diameter_bin_upper",
60
+ "standard_name": "diameter_bin_upper",
61
+ "long_name": "diameter_bin_upper",
62
+ "units": "mm",
63
+ "description": "Bin upper bound drop diameter value",
64
+ }
65
+ attrs_dict["velocity_bin_lower"] = {
66
+ "name": "velocity_bin_lower",
67
+ "standard_name": "velocity_bin_lower",
68
+ "long_name": "velocity_bin_lower",
69
+ "units": "mm",
70
+ "description": "Bin lower bound drop diameter value",
71
+ }
72
+ # Define velocity attributes
73
+ attrs_dict["velocity_bin_center"] = {
74
+ "name": "velocity_bin_center",
75
+ "standard_name": "velocity_bin_center",
76
+ "long_name": "velocity_bin_center",
77
+ "units": "m/s",
78
+ "description": "Bin center drop fall velocity value",
79
+ }
80
+ attrs_dict["velocity_bin_width"] = {
81
+ "name": "velocity_bin_width",
82
+ "standard_name": "velocity_bin_width",
83
+ "long_name": "velocity_bin_width",
84
+ "units": "m/s",
85
+ "description": "Drop fall velocity bin width",
86
+ }
87
+ attrs_dict["velocity_bin_upper"] = {
88
+ "name": "velocity_bin_upper",
89
+ "standard_name": "velocity_bin_upper",
90
+ "long_name": "velocity_bin_upper",
91
+ "units": "m/s",
92
+ "description": "Bin upper bound drop fall velocity value",
93
+ }
94
+ attrs_dict["velocity_bin_lower"] = {
95
+ "name": "velocity_bin_lower",
96
+ "standard_name": "velocity_bin_lower",
97
+ "long_name": "velocity_bin_lower",
98
+ "units": "m/s",
99
+ "description": "Bin lower bound drop fall velocity value",
100
+ }
101
+ # Define geolocation attributes
102
+ attrs_dict["latitude"] = {
103
+ "name": "latitude",
104
+ "standard_name": "latitude",
105
+ "long_name": "Latitude",
106
+ "units": "degrees_north",
107
+ }
108
+ attrs_dict["longitude"] = {
109
+ "name": "longitude",
110
+ "standard_name": "longitude",
111
+ "long_name": "Longitude",
112
+ "units": "degrees_east",
113
+ }
114
+ attrs_dict["altitude"] = {
115
+ "name": "altitude",
116
+ "standard_name": "altitude",
117
+ "long_name": "Altitude",
118
+ "units": "m",
119
+ "description": "Elevation above sea level",
120
+ }
121
+ # Define time attributes
122
+ attrs_dict["time"] = {
123
+ "name": "time",
124
+ "standard_name": "time",
125
+ "long_name": "time",
126
+ "description": "UTC Time",
127
+ }
128
+
129
+ return attrs_dict
130
+
131
+
132
+ def set_coordinate_attributes(ds):
133
+ """Set coordinates attributes."""
134
+ # Get attributes dictionary
135
+ attrs_dict = get_coords_attrs_dict()
136
+ # Set attributes
137
+ ds = set_attrs(ds, attrs_dict)
138
+ return ds
139
+
140
+
141
+ ####-------------------------------------------------------------------------.
142
+ #### DISDRODB Global Attributes
143
+
144
+
145
+ def set_disdrodb_attrs(ds, product: str):
146
+ """Add DISDRODB processing information to the netCDF global attributes.
147
+
148
+ It assumes stations metadata are already added the dataset.
149
+
150
+ Parameters
151
+ ----------
152
+ ds : xarray.Dataset
153
+ Dataset
154
+ product: str
155
+ DISDRODB product.
156
+
157
+ Returns
158
+ -------
159
+ xarray dataset
160
+ Dataset.
161
+ """
162
+ # Add dataset conventions
163
+ ds.attrs["Conventions"] = CONVENTIONS
164
+
165
+ # Add featureType
166
+ if "platform_type" in ds.attrs:
167
+ platform_type = ds.attrs["platform_type"]
168
+ if platform_type == "fixed":
169
+ ds.attrs["featureType"] = "timeSeries"
170
+ else:
171
+ ds.attrs["featureType"] = "trajectory"
172
+
173
+ # Update DISDRODDB attributes
174
+ ds = update_disdrodb_attrs(ds=ds, product=product)
175
+ return ds
176
+
177
+
178
+ def update_disdrodb_attrs(ds, product: str):
179
+ """Add DISDRODB processing information to the netCDF global attributes.
180
+
181
+ It assumes stations metadata are already added the dataset.
182
+
183
+ Parameters
184
+ ----------
185
+ ds : xarray dataset.
186
+ Dataset
187
+ product: str
188
+ DISDRODB product.
189
+
190
+ Returns
191
+ -------
192
+ xarray dataset
193
+ Dataset.
194
+ """
195
+ # Add time_coverage_start and time_coverage_end
196
+ ds.attrs["time_coverage_start"] = str(ds["time"].data[0])
197
+ ds.attrs["time_coverage_end"] = str(ds["time"].data[-1])
198
+
199
+ # DISDRODDB attributes
200
+ # - Add DISDRODB processing info
201
+ now = datetime.datetime.utcnow()
202
+ current_time = now.strftime("%Y-%m-%d %H:%M:%S")
203
+ ds.attrs["disdrodb_processing_date"] = current_time
204
+ # - Add DISDRODB product and version
205
+ ds.attrs["disdrodb_product_version"] = ARCHIVE_VERSION
206
+ ds.attrs["disdrodb_software_version"] = SOFTWARE_VERSION
207
+ ds.attrs["disdrodb_product"] = product
208
+ return ds