HydPy 6.2.dev1__cp313-cp313-win_amd64.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 (890) hide show
  1. hydpy/__init__.py +275 -0
  2. hydpy/aliases.py +2554 -0
  3. hydpy/auxs/__init__.py +0 -0
  4. hydpy/auxs/anntools.py +1305 -0
  5. hydpy/auxs/armatools.py +883 -0
  6. hydpy/auxs/calibtools.py +3337 -0
  7. hydpy/auxs/interptools.py +1094 -0
  8. hydpy/auxs/iuhtools.py +543 -0
  9. hydpy/auxs/networktools.py +597 -0
  10. hydpy/auxs/ppolytools.py +809 -0
  11. hydpy/auxs/quadtools.py +61 -0
  12. hydpy/auxs/roottools.py +228 -0
  13. hydpy/auxs/smoothtools.py +273 -0
  14. hydpy/auxs/statstools.py +2125 -0
  15. hydpy/auxs/validtools.py +81 -0
  16. hydpy/conf/HydPyConfigBase.xsd +68637 -0
  17. hydpy/conf/HydPyConfigBase.xsdt +358 -0
  18. hydpy/conf/HydPyConfigMultipleRuns.xsd +25 -0
  19. hydpy/conf/HydPyConfigSingleRun.xsd +24 -0
  20. hydpy/conf/__init__.py +0 -0
  21. hydpy/conf/a_coefficients_explicit_lobatto_sequence.npy +0 -0
  22. hydpy/conf/support_points_for_smoothpar_logistic2.npy +0 -0
  23. hydpy/config.py +42 -0
  24. hydpy/core/__init__.py +0 -0
  25. hydpy/core/aliastools.py +214 -0
  26. hydpy/core/autodoctools.py +1947 -0
  27. hydpy/core/auxfiletools.py +1169 -0
  28. hydpy/core/devicetools.py +3810 -0
  29. hydpy/core/exceptiontools.py +269 -0
  30. hydpy/core/filetools.py +1985 -0
  31. hydpy/core/hydpytools.py +3089 -0
  32. hydpy/core/importtools.py +1398 -0
  33. hydpy/core/indextools.py +345 -0
  34. hydpy/core/itemtools.py +1849 -0
  35. hydpy/core/masktools.py +460 -0
  36. hydpy/core/modeltools.py +4868 -0
  37. hydpy/core/netcdftools.py +2683 -0
  38. hydpy/core/objecttools.py +2023 -0
  39. hydpy/core/optiontools.py +1045 -0
  40. hydpy/core/parametertools.py +4674 -0
  41. hydpy/core/printtools.py +222 -0
  42. hydpy/core/propertytools.py +643 -0
  43. hydpy/core/pubtools.py +254 -0
  44. hydpy/core/selectiontools.py +1571 -0
  45. hydpy/core/sequencetools.py +4476 -0
  46. hydpy/core/seriestools.py +339 -0
  47. hydpy/core/testtools.py +2483 -0
  48. hydpy/core/timetools.py +3567 -0
  49. hydpy/core/typingtools.py +333 -0
  50. hydpy/core/variabletools.py +2615 -0
  51. hydpy/cythons/__init__.py +24 -0
  52. hydpy/cythons/annutils.pxd +33 -0
  53. hydpy/cythons/annutils.pyi +25 -0
  54. hydpy/cythons/annutils.pyx +120 -0
  55. hydpy/cythons/autogen/__init__.py +0 -0
  56. hydpy/cythons/autogen/annutils.cp313-win_amd64.pyd +0 -0
  57. hydpy/cythons/autogen/annutils.pxd +42 -0
  58. hydpy/cythons/autogen/annutils.pyx +129 -0
  59. hydpy/cythons/autogen/c_arma.cp313-win_amd64.pyd +0 -0
  60. hydpy/cythons/autogen/c_arma.pxd +179 -0
  61. hydpy/cythons/autogen/c_arma.pyx +356 -0
  62. hydpy/cythons/autogen/c_arma_rimorido.cp313-win_amd64.pyd +0 -0
  63. hydpy/cythons/autogen/c_arma_rimorido.pxd +179 -0
  64. hydpy/cythons/autogen/c_arma_rimorido.pyx +356 -0
  65. hydpy/cythons/autogen/c_conv.cp313-win_amd64.pyd +0 -0
  66. hydpy/cythons/autogen/c_conv.pxd +198 -0
  67. hydpy/cythons/autogen/c_conv.pyx +491 -0
  68. hydpy/cythons/autogen/c_conv_idw.cp313-win_amd64.pyd +0 -0
  69. hydpy/cythons/autogen/c_conv_idw.pxd +124 -0
  70. hydpy/cythons/autogen/c_conv_idw.pyx +264 -0
  71. hydpy/cythons/autogen/c_conv_idw_ed.cp313-win_amd64.pyd +0 -0
  72. hydpy/cythons/autogen/c_conv_idw_ed.pxd +197 -0
  73. hydpy/cythons/autogen/c_conv_idw_ed.pyx +481 -0
  74. hydpy/cythons/autogen/c_conv_nn.cp313-win_amd64.pyd +0 -0
  75. hydpy/cythons/autogen/c_conv_nn.pxd +120 -0
  76. hydpy/cythons/autogen/c_conv_nn.pyx +224 -0
  77. hydpy/cythons/autogen/c_dam.cp313-win_amd64.pyd +0 -0
  78. hydpy/cythons/autogen/c_dam.pxd +805 -0
  79. hydpy/cythons/autogen/c_dam.pyx +1477 -0
  80. hydpy/cythons/autogen/c_dam_llake.cp313-win_amd64.pyd +0 -0
  81. hydpy/cythons/autogen/c_dam_llake.pxd +364 -0
  82. hydpy/cythons/autogen/c_dam_llake.pyx +705 -0
  83. hydpy/cythons/autogen/c_dam_lreservoir.cp313-win_amd64.pyd +0 -0
  84. hydpy/cythons/autogen/c_dam_lreservoir.pxd +365 -0
  85. hydpy/cythons/autogen/c_dam_lreservoir.pyx +708 -0
  86. hydpy/cythons/autogen/c_dam_lretention.cp313-win_amd64.pyd +0 -0
  87. hydpy/cythons/autogen/c_dam_lretention.pxd +340 -0
  88. hydpy/cythons/autogen/c_dam_lretention.pyx +625 -0
  89. hydpy/cythons/autogen/c_dam_pump.cp313-win_amd64.pyd +0 -0
  90. hydpy/cythons/autogen/c_dam_pump.pxd +402 -0
  91. hydpy/cythons/autogen/c_dam_pump.pyx +724 -0
  92. hydpy/cythons/autogen/c_dam_pump_sluice.cp313-win_amd64.pyd +0 -0
  93. hydpy/cythons/autogen/c_dam_pump_sluice.pxd +452 -0
  94. hydpy/cythons/autogen/c_dam_pump_sluice.pyx +829 -0
  95. hydpy/cythons/autogen/c_dam_sluice.cp313-win_amd64.pyd +0 -0
  96. hydpy/cythons/autogen/c_dam_sluice.pxd +404 -0
  97. hydpy/cythons/autogen/c_dam_sluice.pyx +726 -0
  98. hydpy/cythons/autogen/c_dam_v001.cp313-win_amd64.pyd +0 -0
  99. hydpy/cythons/autogen/c_dam_v001.pxd +452 -0
  100. hydpy/cythons/autogen/c_dam_v001.pyx +816 -0
  101. hydpy/cythons/autogen/c_dam_v002.cp313-win_amd64.pyd +0 -0
  102. hydpy/cythons/autogen/c_dam_v002.pxd +394 -0
  103. hydpy/cythons/autogen/c_dam_v002.pyx +703 -0
  104. hydpy/cythons/autogen/c_dam_v003.cp313-win_amd64.pyd +0 -0
  105. hydpy/cythons/autogen/c_dam_v003.pxd +417 -0
  106. hydpy/cythons/autogen/c_dam_v003.pyx +744 -0
  107. hydpy/cythons/autogen/c_dam_v004.cp313-win_amd64.pyd +0 -0
  108. hydpy/cythons/autogen/c_dam_v004.pxd +486 -0
  109. hydpy/cythons/autogen/c_dam_v004.pyx +891 -0
  110. hydpy/cythons/autogen/c_dam_v005.cp313-win_amd64.pyd +0 -0
  111. hydpy/cythons/autogen/c_dam_v005.pxd +524 -0
  112. hydpy/cythons/autogen/c_dam_v005.pyx +928 -0
  113. hydpy/cythons/autogen/c_dummy.cp313-win_amd64.pyd +0 -0
  114. hydpy/cythons/autogen/c_dummy.pxd +151 -0
  115. hydpy/cythons/autogen/c_dummy.pyx +263 -0
  116. hydpy/cythons/autogen/c_dummy_interceptedwater.cp313-win_amd64.pyd +0 -0
  117. hydpy/cythons/autogen/c_dummy_interceptedwater.pxd +69 -0
  118. hydpy/cythons/autogen/c_dummy_interceptedwater.pyx +104 -0
  119. hydpy/cythons/autogen/c_dummy_node2node.cp313-win_amd64.pyd +0 -0
  120. hydpy/cythons/autogen/c_dummy_node2node.pxd +89 -0
  121. hydpy/cythons/autogen/c_dummy_node2node.pyx +148 -0
  122. hydpy/cythons/autogen/c_dummy_snowalbedo.cp313-win_amd64.pyd +0 -0
  123. hydpy/cythons/autogen/c_dummy_snowalbedo.pxd +69 -0
  124. hydpy/cythons/autogen/c_dummy_snowalbedo.pyx +104 -0
  125. hydpy/cythons/autogen/c_dummy_snowcover.cp313-win_amd64.pyd +0 -0
  126. hydpy/cythons/autogen/c_dummy_snowcover.pxd +69 -0
  127. hydpy/cythons/autogen/c_dummy_snowcover.pyx +104 -0
  128. hydpy/cythons/autogen/c_dummy_snowycanopy.cp313-win_amd64.pyd +0 -0
  129. hydpy/cythons/autogen/c_dummy_snowycanopy.pxd +69 -0
  130. hydpy/cythons/autogen/c_dummy_snowycanopy.pyx +104 -0
  131. hydpy/cythons/autogen/c_dummy_soilwater.cp313-win_amd64.pyd +0 -0
  132. hydpy/cythons/autogen/c_dummy_soilwater.pxd +69 -0
  133. hydpy/cythons/autogen/c_dummy_soilwater.pyx +104 -0
  134. hydpy/cythons/autogen/c_evap.cp313-win_amd64.pyd +0 -0
  135. hydpy/cythons/autogen/c_evap.pxd +1029 -0
  136. hydpy/cythons/autogen/c_evap.pyx +2601 -0
  137. hydpy/cythons/autogen/c_evap_aet_hbv96.cp313-win_amd64.pyd +0 -0
  138. hydpy/cythons/autogen/c_evap_aet_hbv96.pxd +227 -0
  139. hydpy/cythons/autogen/c_evap_aet_hbv96.pyx +584 -0
  140. hydpy/cythons/autogen/c_evap_aet_minhas.cp313-win_amd64.pyd +0 -0
  141. hydpy/cythons/autogen/c_evap_aet_minhas.pxd +193 -0
  142. hydpy/cythons/autogen/c_evap_aet_minhas.pyx +478 -0
  143. hydpy/cythons/autogen/c_evap_aet_morsim.cp313-win_amd64.pyd +0 -0
  144. hydpy/cythons/autogen/c_evap_aet_morsim.pxd +681 -0
  145. hydpy/cythons/autogen/c_evap_aet_morsim.pyx +1642 -0
  146. hydpy/cythons/autogen/c_evap_pet_ambav1.cp313-win_amd64.pyd +0 -0
  147. hydpy/cythons/autogen/c_evap_pet_ambav1.pxd +532 -0
  148. hydpy/cythons/autogen/c_evap_pet_ambav1.pyx +1296 -0
  149. hydpy/cythons/autogen/c_evap_pet_hbv96.cp313-win_amd64.pyd +0 -0
  150. hydpy/cythons/autogen/c_evap_pet_hbv96.pxd +179 -0
  151. hydpy/cythons/autogen/c_evap_pet_hbv96.pyx +328 -0
  152. hydpy/cythons/autogen/c_evap_pet_m.cp313-win_amd64.pyd +0 -0
  153. hydpy/cythons/autogen/c_evap_pet_m.pxd +124 -0
  154. hydpy/cythons/autogen/c_evap_pet_m.pyx +214 -0
  155. hydpy/cythons/autogen/c_evap_pet_mlc.cp313-win_amd64.pyd +0 -0
  156. hydpy/cythons/autogen/c_evap_pet_mlc.pxd +126 -0
  157. hydpy/cythons/autogen/c_evap_pet_mlc.pyx +214 -0
  158. hydpy/cythons/autogen/c_evap_ret_fao56.cp313-win_amd64.pyd +0 -0
  159. hydpy/cythons/autogen/c_evap_ret_fao56.pxd +305 -0
  160. hydpy/cythons/autogen/c_evap_ret_fao56.pyx +624 -0
  161. hydpy/cythons/autogen/c_evap_ret_io.cp313-win_amd64.pyd +0 -0
  162. hydpy/cythons/autogen/c_evap_ret_io.pxd +112 -0
  163. hydpy/cythons/autogen/c_evap_ret_io.pyx +176 -0
  164. hydpy/cythons/autogen/c_evap_ret_tw2002.cp313-win_amd64.pyd +0 -0
  165. hydpy/cythons/autogen/c_evap_ret_tw2002.pxd +139 -0
  166. hydpy/cythons/autogen/c_evap_ret_tw2002.pyx +273 -0
  167. hydpy/cythons/autogen/c_exch.cp313-win_amd64.pyd +0 -0
  168. hydpy/cythons/autogen/c_exch.pxd +230 -0
  169. hydpy/cythons/autogen/c_exch.pyx +462 -0
  170. hydpy/cythons/autogen/c_exch_branch_hbv96.cp313-win_amd64.pyd +0 -0
  171. hydpy/cythons/autogen/c_exch_branch_hbv96.pxd +134 -0
  172. hydpy/cythons/autogen/c_exch_branch_hbv96.pyx +255 -0
  173. hydpy/cythons/autogen/c_exch_waterlevel.cp313-win_amd64.pyd +0 -0
  174. hydpy/cythons/autogen/c_exch_waterlevel.pxd +54 -0
  175. hydpy/cythons/autogen/c_exch_waterlevel.pyx +78 -0
  176. hydpy/cythons/autogen/c_exch_weir_hbv96.cp313-win_amd64.pyd +0 -0
  177. hydpy/cythons/autogen/c_exch_weir_hbv96.pxd +156 -0
  178. hydpy/cythons/autogen/c_exch_weir_hbv96.pyx +282 -0
  179. hydpy/cythons/autogen/c_ga.cp313-win_amd64.pyd +0 -0
  180. hydpy/cythons/autogen/c_ga.pxd +353 -0
  181. hydpy/cythons/autogen/c_ga.pyx +1204 -0
  182. hydpy/cythons/autogen/c_ga_garto.cp313-win_amd64.pyd +0 -0
  183. hydpy/cythons/autogen/c_ga_garto.pxd +330 -0
  184. hydpy/cythons/autogen/c_ga_garto.pyx +1105 -0
  185. hydpy/cythons/autogen/c_ga_garto_submodel1.cp313-win_amd64.pyd +0 -0
  186. hydpy/cythons/autogen/c_ga_garto_submodel1.pxd +236 -0
  187. hydpy/cythons/autogen/c_ga_garto_submodel1.pyx +944 -0
  188. hydpy/cythons/autogen/c_gland.cp313-win_amd64.pyd +0 -0
  189. hydpy/cythons/autogen/c_gland.pxd +437 -0
  190. hydpy/cythons/autogen/c_gland.pyx +726 -0
  191. hydpy/cythons/autogen/c_gland_gr4.cp313-win_amd64.pyd +0 -0
  192. hydpy/cythons/autogen/c_gland_gr4.pxd +382 -0
  193. hydpy/cythons/autogen/c_gland_gr4.pyx +605 -0
  194. hydpy/cythons/autogen/c_gland_gr5.cp313-win_amd64.pyd +0 -0
  195. hydpy/cythons/autogen/c_gland_gr5.pxd +368 -0
  196. hydpy/cythons/autogen/c_gland_gr5.pyx +568 -0
  197. hydpy/cythons/autogen/c_gland_gr6.cp313-win_amd64.pyd +0 -0
  198. hydpy/cythons/autogen/c_gland_gr6.pxd +420 -0
  199. hydpy/cythons/autogen/c_gland_gr6.pyx +673 -0
  200. hydpy/cythons/autogen/c_hland.cp313-win_amd64.pyd +0 -0
  201. hydpy/cythons/autogen/c_hland.pxd +855 -0
  202. hydpy/cythons/autogen/c_hland.pyx +2486 -0
  203. hydpy/cythons/autogen/c_hland_96.cp313-win_amd64.pyd +0 -0
  204. hydpy/cythons/autogen/c_hland_96.pxd +631 -0
  205. hydpy/cythons/autogen/c_hland_96.pyx +1724 -0
  206. hydpy/cythons/autogen/c_hland_96c.cp313-win_amd64.pyd +0 -0
  207. hydpy/cythons/autogen/c_hland_96c.pxd +621 -0
  208. hydpy/cythons/autogen/c_hland_96c.pyx +1822 -0
  209. hydpy/cythons/autogen/c_hland_96p.cp313-win_amd64.pyd +0 -0
  210. hydpy/cythons/autogen/c_hland_96p.pxd +683 -0
  211. hydpy/cythons/autogen/c_hland_96p.pyx +1911 -0
  212. hydpy/cythons/autogen/c_kinw.cp313-win_amd64.pyd +0 -0
  213. hydpy/cythons/autogen/c_kinw.pxd +509 -0
  214. hydpy/cythons/autogen/c_kinw.pyx +965 -0
  215. hydpy/cythons/autogen/c_kinw_williams.cp313-win_amd64.pyd +0 -0
  216. hydpy/cythons/autogen/c_kinw_williams.pxd +409 -0
  217. hydpy/cythons/autogen/c_kinw_williams.pyx +763 -0
  218. hydpy/cythons/autogen/c_kinw_williams_ext.cp313-win_amd64.pyd +0 -0
  219. hydpy/cythons/autogen/c_kinw_williams_ext.pxd +220 -0
  220. hydpy/cythons/autogen/c_kinw_williams_ext.pyx +440 -0
  221. hydpy/cythons/autogen/c_lland.cp313-win_amd64.pyd +0 -0
  222. hydpy/cythons/autogen/c_lland.pxd +1386 -0
  223. hydpy/cythons/autogen/c_lland.pyx +3679 -0
  224. hydpy/cythons/autogen/c_lland_dd.cp313-win_amd64.pyd +0 -0
  225. hydpy/cythons/autogen/c_lland_dd.pxd +679 -0
  226. hydpy/cythons/autogen/c_lland_dd.pyx +1719 -0
  227. hydpy/cythons/autogen/c_lland_knauf.cp313-win_amd64.pyd +0 -0
  228. hydpy/cythons/autogen/c_lland_knauf.pxd +1096 -0
  229. hydpy/cythons/autogen/c_lland_knauf.pyx +2784 -0
  230. hydpy/cythons/autogen/c_lland_knauf_ic.cp313-win_amd64.pyd +0 -0
  231. hydpy/cythons/autogen/c_lland_knauf_ic.pxd +1369 -0
  232. hydpy/cythons/autogen/c_lland_knauf_ic.pyx +3625 -0
  233. hydpy/cythons/autogen/c_meteo.cp313-win_amd64.pyd +0 -0
  234. hydpy/cythons/autogen/c_meteo.pxd +469 -0
  235. hydpy/cythons/autogen/c_meteo.pyx +879 -0
  236. hydpy/cythons/autogen/c_meteo_clear_glob_io.cp313-win_amd64.pyd +0 -0
  237. hydpy/cythons/autogen/c_meteo_clear_glob_io.pxd +75 -0
  238. hydpy/cythons/autogen/c_meteo_clear_glob_io.pyx +107 -0
  239. hydpy/cythons/autogen/c_meteo_glob_fao56.cp313-win_amd64.pyd +0 -0
  240. hydpy/cythons/autogen/c_meteo_glob_fao56.pxd +209 -0
  241. hydpy/cythons/autogen/c_meteo_glob_fao56.pyx +339 -0
  242. hydpy/cythons/autogen/c_meteo_glob_io.cp313-win_amd64.pyd +0 -0
  243. hydpy/cythons/autogen/c_meteo_glob_io.pxd +63 -0
  244. hydpy/cythons/autogen/c_meteo_glob_io.pyx +91 -0
  245. hydpy/cythons/autogen/c_meteo_glob_morsim.cp313-win_amd64.pyd +0 -0
  246. hydpy/cythons/autogen/c_meteo_glob_morsim.pxd +289 -0
  247. hydpy/cythons/autogen/c_meteo_glob_morsim.pyx +527 -0
  248. hydpy/cythons/autogen/c_meteo_precip_io.cp313-win_amd64.pyd +0 -0
  249. hydpy/cythons/autogen/c_meteo_precip_io.pxd +112 -0
  250. hydpy/cythons/autogen/c_meteo_precip_io.pyx +176 -0
  251. hydpy/cythons/autogen/c_meteo_psun_sun_glob_io.cp313-win_amd64.pyd +0 -0
  252. hydpy/cythons/autogen/c_meteo_psun_sun_glob_io.pxd +87 -0
  253. hydpy/cythons/autogen/c_meteo_psun_sun_glob_io.pyx +123 -0
  254. hydpy/cythons/autogen/c_meteo_sun_fao56.cp313-win_amd64.pyd +0 -0
  255. hydpy/cythons/autogen/c_meteo_sun_fao56.pxd +209 -0
  256. hydpy/cythons/autogen/c_meteo_sun_fao56.pyx +343 -0
  257. hydpy/cythons/autogen/c_meteo_sun_morsim.cp313-win_amd64.pyd +0 -0
  258. hydpy/cythons/autogen/c_meteo_sun_morsim.pxd +286 -0
  259. hydpy/cythons/autogen/c_meteo_sun_morsim.pyx +519 -0
  260. hydpy/cythons/autogen/c_meteo_temp_io.cp313-win_amd64.pyd +0 -0
  261. hydpy/cythons/autogen/c_meteo_temp_io.pxd +112 -0
  262. hydpy/cythons/autogen/c_meteo_temp_io.pyx +176 -0
  263. hydpy/cythons/autogen/c_musk.cp313-win_amd64.pyd +0 -0
  264. hydpy/cythons/autogen/c_musk.pxd +282 -0
  265. hydpy/cythons/autogen/c_musk.pyx +605 -0
  266. hydpy/cythons/autogen/c_musk_classic.cp313-win_amd64.pyd +0 -0
  267. hydpy/cythons/autogen/c_musk_classic.pxd +138 -0
  268. hydpy/cythons/autogen/c_musk_classic.pyx +226 -0
  269. hydpy/cythons/autogen/c_musk_mct.cp313-win_amd64.pyd +0 -0
  270. hydpy/cythons/autogen/c_musk_mct.pxd +282 -0
  271. hydpy/cythons/autogen/c_musk_mct.pyx +609 -0
  272. hydpy/cythons/autogen/c_rconc.cp313-win_amd64.pyd +0 -0
  273. hydpy/cythons/autogen/c_rconc.pxd +119 -0
  274. hydpy/cythons/autogen/c_rconc.pyx +174 -0
  275. hydpy/cythons/autogen/c_rconc_nash.cp313-win_amd64.pyd +0 -0
  276. hydpy/cythons/autogen/c_rconc_nash.pxd +111 -0
  277. hydpy/cythons/autogen/c_rconc_nash.pyx +185 -0
  278. hydpy/cythons/autogen/c_rconc_uh.cp313-win_amd64.pyd +0 -0
  279. hydpy/cythons/autogen/c_rconc_uh.pxd +92 -0
  280. hydpy/cythons/autogen/c_rconc_uh.pyx +125 -0
  281. hydpy/cythons/autogen/c_sw1d.cp313-win_amd64.pyd +0 -0
  282. hydpy/cythons/autogen/c_sw1d.pxd +511 -0
  283. hydpy/cythons/autogen/c_sw1d.pyx +1263 -0
  284. hydpy/cythons/autogen/c_sw1d_channel.cp313-win_amd64.pyd +0 -0
  285. hydpy/cythons/autogen/c_sw1d_channel.pxd +119 -0
  286. hydpy/cythons/autogen/c_sw1d_channel.pyx +300 -0
  287. hydpy/cythons/autogen/c_sw1d_gate_out.cp313-win_amd64.pyd +0 -0
  288. hydpy/cythons/autogen/c_sw1d_gate_out.pxd +240 -0
  289. hydpy/cythons/autogen/c_sw1d_gate_out.pyx +476 -0
  290. hydpy/cythons/autogen/c_sw1d_lias.cp313-win_amd64.pyd +0 -0
  291. hydpy/cythons/autogen/c_sw1d_lias.pxd +320 -0
  292. hydpy/cythons/autogen/c_sw1d_lias.pyx +619 -0
  293. hydpy/cythons/autogen/c_sw1d_lias_sluice.cp313-win_amd64.pyd +0 -0
  294. hydpy/cythons/autogen/c_sw1d_lias_sluice.pxd +325 -0
  295. hydpy/cythons/autogen/c_sw1d_lias_sluice.pyx +644 -0
  296. hydpy/cythons/autogen/c_sw1d_network.cp313-win_amd64.pyd +0 -0
  297. hydpy/cythons/autogen/c_sw1d_network.pxd +90 -0
  298. hydpy/cythons/autogen/c_sw1d_network.pyx +246 -0
  299. hydpy/cythons/autogen/c_sw1d_pump.cp313-win_amd64.pyd +0 -0
  300. hydpy/cythons/autogen/c_sw1d_pump.pxd +256 -0
  301. hydpy/cythons/autogen/c_sw1d_pump.pyx +502 -0
  302. hydpy/cythons/autogen/c_sw1d_q_in.cp313-win_amd64.pyd +0 -0
  303. hydpy/cythons/autogen/c_sw1d_q_in.pxd +224 -0
  304. hydpy/cythons/autogen/c_sw1d_q_in.pyx +383 -0
  305. hydpy/cythons/autogen/c_sw1d_q_out.cp313-win_amd64.pyd +0 -0
  306. hydpy/cythons/autogen/c_sw1d_q_out.pxd +224 -0
  307. hydpy/cythons/autogen/c_sw1d_q_out.pyx +383 -0
  308. hydpy/cythons/autogen/c_sw1d_storage.cp313-win_amd64.pyd +0 -0
  309. hydpy/cythons/autogen/c_sw1d_storage.pxd +193 -0
  310. hydpy/cythons/autogen/c_sw1d_storage.pyx +349 -0
  311. hydpy/cythons/autogen/c_sw1d_weir_out.cp313-win_amd64.pyd +0 -0
  312. hydpy/cythons/autogen/c_sw1d_weir_out.pxd +212 -0
  313. hydpy/cythons/autogen/c_sw1d_weir_out.pyx +404 -0
  314. hydpy/cythons/autogen/c_test.cp313-win_amd64.pyd +0 -0
  315. hydpy/cythons/autogen/c_test.pxd +175 -0
  316. hydpy/cythons/autogen/c_test.pyx +348 -0
  317. hydpy/cythons/autogen/c_test_discontinous.cp313-win_amd64.pyd +0 -0
  318. hydpy/cythons/autogen/c_test_discontinous.pxd +146 -0
  319. hydpy/cythons/autogen/c_test_discontinous.pyx +256 -0
  320. hydpy/cythons/autogen/c_test_stiff0d.cp313-win_amd64.pyd +0 -0
  321. hydpy/cythons/autogen/c_test_stiff0d.pxd +146 -0
  322. hydpy/cythons/autogen/c_test_stiff0d.pyx +250 -0
  323. hydpy/cythons/autogen/c_test_stiff1d.cp313-win_amd64.pyd +0 -0
  324. hydpy/cythons/autogen/c_test_stiff1d.pxd +145 -0
  325. hydpy/cythons/autogen/c_test_stiff1d.pyx +294 -0
  326. hydpy/cythons/autogen/c_whmod.cp313-win_amd64.pyd +0 -0
  327. hydpy/cythons/autogen/c_whmod.pxd +482 -0
  328. hydpy/cythons/autogen/c_whmod.pyx +1156 -0
  329. hydpy/cythons/autogen/c_whmod_rural.cp313-win_amd64.pyd +0 -0
  330. hydpy/cythons/autogen/c_whmod_rural.pxd +411 -0
  331. hydpy/cythons/autogen/c_whmod_rural.pyx +982 -0
  332. hydpy/cythons/autogen/c_whmod_urban.cp313-win_amd64.pyd +0 -0
  333. hydpy/cythons/autogen/c_whmod_urban.pxd +482 -0
  334. hydpy/cythons/autogen/c_whmod_urban.pyx +1155 -0
  335. hydpy/cythons/autogen/c_wland.cp313-win_amd64.pyd +0 -0
  336. hydpy/cythons/autogen/c_wland.pxd +842 -0
  337. hydpy/cythons/autogen/c_wland.pyx +1890 -0
  338. hydpy/cythons/autogen/c_wland_gd.cp313-win_amd64.pyd +0 -0
  339. hydpy/cythons/autogen/c_wland_gd.pxd +829 -0
  340. hydpy/cythons/autogen/c_wland_gd.pyx +1847 -0
  341. hydpy/cythons/autogen/c_wland_wag.cp313-win_amd64.pyd +0 -0
  342. hydpy/cythons/autogen/c_wland_wag.pxd +810 -0
  343. hydpy/cythons/autogen/c_wland_wag.pyx +1780 -0
  344. hydpy/cythons/autogen/c_wq.cp313-win_amd64.pyd +0 -0
  345. hydpy/cythons/autogen/c_wq.pxd +275 -0
  346. hydpy/cythons/autogen/c_wq.pyx +652 -0
  347. hydpy/cythons/autogen/c_wq_trapeze.cp313-win_amd64.pyd +0 -0
  348. hydpy/cythons/autogen/c_wq_trapeze.pxd +170 -0
  349. hydpy/cythons/autogen/c_wq_trapeze.pyx +400 -0
  350. hydpy/cythons/autogen/c_wq_trapeze_strickler.cp313-win_amd64.pyd +0 -0
  351. hydpy/cythons/autogen/c_wq_trapeze_strickler.pxd +243 -0
  352. hydpy/cythons/autogen/c_wq_trapeze_strickler.pyx +578 -0
  353. hydpy/cythons/autogen/c_wq_walrus.cp313-win_amd64.pyd +0 -0
  354. hydpy/cythons/autogen/c_wq_walrus.pxd +61 -0
  355. hydpy/cythons/autogen/c_wq_walrus.pyx +82 -0
  356. hydpy/cythons/autogen/configutils.cp313-win_amd64.pyd +0 -0
  357. hydpy/cythons/autogen/configutils.pxd +17 -0
  358. hydpy/cythons/autogen/configutils.pyx +119 -0
  359. hydpy/cythons/autogen/interfaceutils.cp313-win_amd64.pyd +0 -0
  360. hydpy/cythons/autogen/interfaceutils.pxd +31 -0
  361. hydpy/cythons/autogen/interfaceutils.pyx +82 -0
  362. hydpy/cythons/autogen/interputils.cp313-win_amd64.pyd +0 -0
  363. hydpy/cythons/autogen/interputils.pxd +42 -0
  364. hydpy/cythons/autogen/interputils.pyx +118 -0
  365. hydpy/cythons/autogen/masterinterface.cp313-win_amd64.pyd +0 -0
  366. hydpy/cythons/autogen/masterinterface.pxd +153 -0
  367. hydpy/cythons/autogen/masterinterface.pyx +222 -0
  368. hydpy/cythons/autogen/pointerutils.cp313-win_amd64.pyd +0 -0
  369. hydpy/cythons/autogen/pointerutils.pxd +31 -0
  370. hydpy/cythons/autogen/pointerutils.pyx +650 -0
  371. hydpy/cythons/autogen/ppolyutils.cp313-win_amd64.pyd +0 -0
  372. hydpy/cythons/autogen/ppolyutils.pxd +35 -0
  373. hydpy/cythons/autogen/ppolyutils.pyx +59 -0
  374. hydpy/cythons/autogen/quadutils.cp313-win_amd64.pyd +0 -0
  375. hydpy/cythons/autogen/quadutils.pxd +26 -0
  376. hydpy/cythons/autogen/quadutils.pyx +973 -0
  377. hydpy/cythons/autogen/rootutils.cp313-win_amd64.pyd +0 -0
  378. hydpy/cythons/autogen/rootutils.pxd +28 -0
  379. hydpy/cythons/autogen/rootutils.pyx +109 -0
  380. hydpy/cythons/autogen/sequenceutils.cp313-win_amd64.pyd +0 -0
  381. hydpy/cythons/autogen/sequenceutils.pxd +45 -0
  382. hydpy/cythons/autogen/sequenceutils.pyx +101 -0
  383. hydpy/cythons/autogen/smoothutils.cp313-win_amd64.pyd +0 -0
  384. hydpy/cythons/autogen/smoothutils.pxd +29 -0
  385. hydpy/cythons/autogen/smoothutils.pyx +833 -0
  386. hydpy/cythons/configutils.pxd +8 -0
  387. hydpy/cythons/configutils.pyi +5 -0
  388. hydpy/cythons/configutils.pyx +110 -0
  389. hydpy/cythons/interfaceutils.pxd +22 -0
  390. hydpy/cythons/interfaceutils.pyi +15 -0
  391. hydpy/cythons/interfaceutils.pyx +73 -0
  392. hydpy/cythons/interputils.pxd +33 -0
  393. hydpy/cythons/interputils.pyi +32 -0
  394. hydpy/cythons/interputils.pyx +109 -0
  395. hydpy/cythons/modelutils.py +2990 -0
  396. hydpy/cythons/pointerutils.pxd +22 -0
  397. hydpy/cythons/pointerutils.pyi +89 -0
  398. hydpy/cythons/pointerutils.pyx +641 -0
  399. hydpy/cythons/ppolyutils.pxd +26 -0
  400. hydpy/cythons/ppolyutils.pyi +21 -0
  401. hydpy/cythons/ppolyutils.pyx +50 -0
  402. hydpy/cythons/quadutils.pxd +17 -0
  403. hydpy/cythons/quadutils.pyi +13 -0
  404. hydpy/cythons/quadutils.pyx +964 -0
  405. hydpy/cythons/rootutils.pxd +19 -0
  406. hydpy/cythons/rootutils.pyi +21 -0
  407. hydpy/cythons/rootutils.pyx +100 -0
  408. hydpy/cythons/sequenceutils.pxd +36 -0
  409. hydpy/cythons/sequenceutils.pyi +7 -0
  410. hydpy/cythons/sequenceutils.pyx +92 -0
  411. hydpy/cythons/smoothutils.pxd +20 -0
  412. hydpy/cythons/smoothutils.pyi +15 -0
  413. hydpy/cythons/smoothutils.pyx +824 -0
  414. hydpy/data/HydPy-H-Lahn/conditions/init_1996_01_01_00_00_00/land_dill_assl.py +13 -0
  415. hydpy/data/HydPy-H-Lahn/conditions/init_1996_01_01_00_00_00/land_lahn_kalk.py +13 -0
  416. hydpy/data/HydPy-H-Lahn/conditions/init_1996_01_01_00_00_00/land_lahn_leun.py +14 -0
  417. hydpy/data/HydPy-H-Lahn/conditions/init_1996_01_01_00_00_00/land_lahn_marb.py +13 -0
  418. hydpy/data/HydPy-H-Lahn/conditions/init_1996_01_01_00_00_00/stream_dill_assl_lahn_leun.py +5 -0
  419. hydpy/data/HydPy-H-Lahn/conditions/init_1996_01_01_00_00_00/stream_lahn_leun_lahn_kalk.py +5 -0
  420. hydpy/data/HydPy-H-Lahn/conditions/init_1996_01_01_00_00_00/stream_lahn_marb_lahn_leun.py +5 -0
  421. hydpy/data/HydPy-H-Lahn/control/default/land.py +9 -0
  422. hydpy/data/HydPy-H-Lahn/control/default/land_dill_assl.py +57 -0
  423. hydpy/data/HydPy-H-Lahn/control/default/land_lahn_kalk.py +57 -0
  424. hydpy/data/HydPy-H-Lahn/control/default/land_lahn_leun.py +56 -0
  425. hydpy/data/HydPy-H-Lahn/control/default/land_lahn_marb.py +57 -0
  426. hydpy/data/HydPy-H-Lahn/control/default/stream_dill_assl_lahn_leun.py +7 -0
  427. hydpy/data/HydPy-H-Lahn/control/default/stream_lahn_leun_lahn_kalk.py +7 -0
  428. hydpy/data/HydPy-H-Lahn/control/default/stream_lahn_marb_lahn_leun.py +7 -0
  429. hydpy/data/HydPy-H-Lahn/multiple_runs.xml +309 -0
  430. hydpy/data/HydPy-H-Lahn/multiple_runs_alpha.xml +71 -0
  431. hydpy/data/HydPy-H-Lahn/network/default/headwaters.py +11 -0
  432. hydpy/data/HydPy-H-Lahn/network/default/nonheadwaters.py +11 -0
  433. hydpy/data/HydPy-H-Lahn/network/default/streams.py +8 -0
  434. hydpy/data/HydPy-H-Lahn/series/default/dill_assl_obs_q.asc +11387 -0
  435. hydpy/data/HydPy-H-Lahn/series/default/evap_pet_hbv96_input_normalairtemperature.nc +0 -0
  436. hydpy/data/HydPy-H-Lahn/series/default/evap_pet_hbv96_input_normalevapotranspiration.nc +0 -0
  437. hydpy/data/HydPy-H-Lahn/series/default/hland_96_input_p.nc +0 -0
  438. hydpy/data/HydPy-H-Lahn/series/default/hland_96_input_t.nc +0 -0
  439. hydpy/data/HydPy-H-Lahn/series/default/lahn_kalk_obs_q.asc +11387 -0
  440. hydpy/data/HydPy-H-Lahn/series/default/lahn_leun_obs_q.asc +11387 -0
  441. hydpy/data/HydPy-H-Lahn/series/default/lahn_marb_obs_q.asc +11387 -0
  442. hydpy/data/HydPy-H-Lahn/series/default/land_dill_assl_evap_pet_hbv96_input_normalairtemperature.asc +11387 -0
  443. hydpy/data/HydPy-H-Lahn/series/default/land_dill_assl_evap_pet_hbv96_input_normalevapotranspiration.asc +11387 -0
  444. hydpy/data/HydPy-H-Lahn/series/default/land_dill_assl_hland_96_input_p.asc +11387 -0
  445. hydpy/data/HydPy-H-Lahn/series/default/land_dill_assl_hland_96_input_t.asc +11387 -0
  446. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_kalk_evap_pet_hbv96_input_normalairtemperature.asc +11387 -0
  447. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_kalk_evap_pet_hbv96_input_normalevapotranspiration.asc +11387 -0
  448. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_kalk_hland_96_input_p.asc +11387 -0
  449. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_kalk_hland_96_input_t.asc +11387 -0
  450. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_leun_evap_pet_hbv96_input_normalairtemperature.asc +11387 -0
  451. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_leun_evap_pet_hbv96_input_normalevapotranspiration.asc +11387 -0
  452. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_leun_hland_96_input_p.asc +11387 -0
  453. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_leun_hland_96_input_t.asc +11387 -0
  454. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_marb_evap_pet_hbv96_input_normalairtemperature.asc +11387 -0
  455. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_marb_evap_pet_hbv96_input_normalevapotranspiration.asc +11387 -0
  456. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_marb_hland_96_input_p.asc +11387 -0
  457. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_marb_hland_96_input_t.asc +11387 -0
  458. hydpy/data/HydPy-H-Lahn/series/default/obs_q.nc +0 -0
  459. hydpy/data/HydPy-H-Lahn/single_run.xml +152 -0
  460. hydpy/data/HydPy-H-Lahn/single_run.xmlt +143 -0
  461. hydpy/data/__init__.py +17 -0
  462. hydpy/docs/__init__.py +0 -0
  463. hydpy/docs/autofigs/__init__.py +0 -0
  464. hydpy/docs/bib/__init__.py +0 -0
  465. hydpy/docs/bib/refs.bib +566 -0
  466. hydpy/docs/combine_docversions.py +133 -0
  467. hydpy/docs/draw_model_sketches.py +1301 -0
  468. hydpy/docs/enable_autodoc.py +7 -0
  469. hydpy/docs/figs/HydPy-G-GR4.png +0 -0
  470. hydpy/docs/figs/HydPy-G-GR5.png +0 -0
  471. hydpy/docs/figs/HydPy-G-GR6.png +0 -0
  472. hydpy/docs/figs/HydPy-H-HBV96-COSERO.png +0 -0
  473. hydpy/docs/figs/HydPy-H-HBV96-PREVAH.png +0 -0
  474. hydpy/docs/figs/HydPy-H-HBV96.png +0 -0
  475. hydpy/docs/figs/HydPy-H-Lahn.png +0 -0
  476. hydpy/docs/figs/HydPy-KinW-Williams.png +0 -0
  477. hydpy/docs/figs/HydPy-L-DD.png +0 -0
  478. hydpy/docs/figs/HydPy-W-Wag.png +0 -0
  479. hydpy/docs/figs/HydPy_Logo.png +0 -0
  480. hydpy/docs/figs/HydPy_Logo_Text.png +0 -0
  481. hydpy/docs/figs/IDLE-editor.png +0 -0
  482. hydpy/docs/figs/IDLE-shell.png +0 -0
  483. hydpy/docs/figs/LAWA_river-basin-bumbers.png +0 -0
  484. hydpy/docs/figs/__init__.py +0 -0
  485. hydpy/docs/html_/__init__.py +0 -0
  486. hydpy/docs/polish_html.py +57 -0
  487. hydpy/docs/prepare.py +224 -0
  488. hydpy/docs/publish_docs.py +53 -0
  489. hydpy/docs/rst/HydPy-ARMA.rst +27 -0
  490. hydpy/docs/rst/HydPy-Conv.rst +22 -0
  491. hydpy/docs/rst/HydPy-Dam.rst +79 -0
  492. hydpy/docs/rst/HydPy-Dummy.rst +21 -0
  493. hydpy/docs/rst/HydPy-Evap.rst +26 -0
  494. hydpy/docs/rst/HydPy-Exch.rst +36 -0
  495. hydpy/docs/rst/HydPy-G.rst +40 -0
  496. hydpy/docs/rst/HydPy-GA.rst +34 -0
  497. hydpy/docs/rst/HydPy-H.rst +24 -0
  498. hydpy/docs/rst/HydPy-KinW.rst +32 -0
  499. hydpy/docs/rst/HydPy-L.rst +42 -0
  500. hydpy/docs/rst/HydPy-Meteo.rst +28 -0
  501. hydpy/docs/rst/HydPy-Musk.rst +21 -0
  502. hydpy/docs/rst/HydPy-Rconc.rst +17 -0
  503. hydpy/docs/rst/HydPy-SW1D.rst +49 -0
  504. hydpy/docs/rst/HydPy-Test.rst +19 -0
  505. hydpy/docs/rst/HydPy-W.rst +20 -0
  506. hydpy/docs/rst/HydPy-WHMod.rst +19 -0
  507. hydpy/docs/rst/HydPy-WQ.rst +20 -0
  508. hydpy/docs/rst/__init__.py +0 -0
  509. hydpy/docs/rst/additional_repositories.rst +40 -0
  510. hydpy/docs/rst/auxiliaries.rst +31 -0
  511. hydpy/docs/rst/continuous_integration.rst +75 -0
  512. hydpy/docs/rst/core.rst +75 -0
  513. hydpy/docs/rst/cythons.rst +47 -0
  514. hydpy/docs/rst/definitions.rst +506 -0
  515. hydpy/docs/rst/developer_guide.rst +54 -0
  516. hydpy/docs/rst/example_projects.rst +40 -0
  517. hydpy/docs/rst/execution.rst +22 -0
  518. hydpy/docs/rst/framework_tools.rst +56 -0
  519. hydpy/docs/rst/how_to_read_the_reference_manual.rst +156 -0
  520. hydpy/docs/rst/hydpydependencies.rst +55 -0
  521. hydpy/docs/rst/index.rst +125 -0
  522. hydpy/docs/rst/installation.rst +155 -0
  523. hydpy/docs/rst/model_families.rst +79 -0
  524. hydpy/docs/rst/model_overview.rst +291 -0
  525. hydpy/docs/rst/modelimports.rst +10 -0
  526. hydpy/docs/rst/options.rst +119 -0
  527. hydpy/docs/rst/programming_style.rst +572 -0
  528. hydpy/docs/rst/project_structure.rst +520 -0
  529. hydpy/docs/rst/quickstart.rst +304 -0
  530. hydpy/docs/rst/reference_manual.rst +29 -0
  531. hydpy/docs/rst/required_tools.rst +50 -0
  532. hydpy/docs/rst/simulation.rst +514 -0
  533. hydpy/docs/rst/submodel_interfaces.rst +32 -0
  534. hydpy/docs/rst/tests_and_documentation.rst +85 -0
  535. hydpy/docs/rst/user_guide.rst +38 -0
  536. hydpy/docs/rst/version_control.rst +116 -0
  537. hydpy/docs/rst/zbibliography.rst +8 -0
  538. hydpy/docs/sphinx/__init__.py +0 -0
  539. hydpy/docs/sphinx/_themes/basic_hydpy/changes/frameset.html +11 -0
  540. hydpy/docs/sphinx/_themes/basic_hydpy/changes/rstsource.html +15 -0
  541. hydpy/docs/sphinx/_themes/basic_hydpy/changes/versionchanges.html +33 -0
  542. hydpy/docs/sphinx/_themes/basic_hydpy/defindex.html +35 -0
  543. hydpy/docs/sphinx/_themes/basic_hydpy/domainindex.html +56 -0
  544. hydpy/docs/sphinx/_themes/basic_hydpy/genindex-single.html +63 -0
  545. hydpy/docs/sphinx/_themes/basic_hydpy/genindex-split.html +41 -0
  546. hydpy/docs/sphinx/_themes/basic_hydpy/genindex.html +76 -0
  547. hydpy/docs/sphinx/_themes/basic_hydpy/globaltoc.html +11 -0
  548. hydpy/docs/sphinx/_themes/basic_hydpy/layout.html +221 -0
  549. hydpy/docs/sphinx/_themes/basic_hydpy/localtoc.html +15 -0
  550. hydpy/docs/sphinx/_themes/basic_hydpy/opensearch.xml +13 -0
  551. hydpy/docs/sphinx/_themes/basic_hydpy/page.html +13 -0
  552. hydpy/docs/sphinx/_themes/basic_hydpy/relations.html +23 -0
  553. hydpy/docs/sphinx/_themes/basic_hydpy/search.html +65 -0
  554. hydpy/docs/sphinx/_themes/basic_hydpy/searchbox.html +21 -0
  555. hydpy/docs/sphinx/_themes/basic_hydpy/searchfield.html +23 -0
  556. hydpy/docs/sphinx/_themes/basic_hydpy/sourcelink.html +18 -0
  557. hydpy/docs/sphinx/_themes/basic_hydpy/static/basic.css_t +925 -0
  558. hydpy/docs/sphinx/_themes/basic_hydpy/static/doctools.js +156 -0
  559. hydpy/docs/sphinx/_themes/basic_hydpy/static/documentation_options.js_t +13 -0
  560. hydpy/docs/sphinx/_themes/basic_hydpy/static/file.png +0 -0
  561. hydpy/docs/sphinx/_themes/basic_hydpy/static/language_data.js_t +26 -0
  562. hydpy/docs/sphinx/_themes/basic_hydpy/static/minus.png +0 -0
  563. hydpy/docs/sphinx/_themes/basic_hydpy/static/plus.png +0 -0
  564. hydpy/docs/sphinx/_themes/basic_hydpy/static/searchtools.js +574 -0
  565. hydpy/docs/sphinx/_themes/basic_hydpy/static/sphinx_highlight.js +154 -0
  566. hydpy/docs/sphinx/_themes/basic_hydpy/theme.conf +16 -0
  567. hydpy/docs/sphinx/_themes/classic_hydpy/layout.html +23 -0
  568. hydpy/docs/sphinx/_themes/classic_hydpy/static/classic.css_t +358 -0
  569. hydpy/docs/sphinx/_themes/classic_hydpy/static/sidebar.js_t +72 -0
  570. hydpy/docs/sphinx/_themes/classic_hydpy/theme.conf +32 -0
  571. hydpy/docs/sphinx/conf.py +398 -0
  572. hydpy/docs/sphinx/defaultlinks_extension.py +36 -0
  573. hydpy/docs/sphinx/integrationtest_extension.py +104 -0
  574. hydpy/docs/sphinx/projectstructure_extension.py +58 -0
  575. hydpy/docs/sphinx/submodelgraph_extension.py +53 -0
  576. hydpy/exe/__init__.py +0 -0
  577. hydpy/exe/commandtools.py +651 -0
  578. hydpy/exe/hyd.py +277 -0
  579. hydpy/exe/modelimports.py +41 -0
  580. hydpy/exe/replacetools.py +216 -0
  581. hydpy/exe/servertools.py +2348 -0
  582. hydpy/exe/xmltools.py +3280 -0
  583. hydpy/interfaces/__init__.py +0 -0
  584. hydpy/interfaces/aetinterfaces.py +94 -0
  585. hydpy/interfaces/dischargeinterfaces.py +45 -0
  586. hydpy/interfaces/petinterfaces.py +117 -0
  587. hydpy/interfaces/precipinterfaces.py +42 -0
  588. hydpy/interfaces/radiationinterfaces.py +79 -0
  589. hydpy/interfaces/rconcinterfaces.py +30 -0
  590. hydpy/interfaces/routinginterfaces.py +324 -0
  591. hydpy/interfaces/soilinterfaces.py +96 -0
  592. hydpy/interfaces/stateinterfaces.py +98 -0
  593. hydpy/interfaces/tempinterfaces.py +46 -0
  594. hydpy/models/__init__.py +0 -0
  595. hydpy/models/arma/__init__.py +14 -0
  596. hydpy/models/arma/arma_control.py +383 -0
  597. hydpy/models/arma/arma_derived.py +204 -0
  598. hydpy/models/arma/arma_fluxes.py +41 -0
  599. hydpy/models/arma/arma_inlets.py +11 -0
  600. hydpy/models/arma/arma_logs.py +19 -0
  601. hydpy/models/arma/arma_model.py +461 -0
  602. hydpy/models/arma/arma_outlets.py +11 -0
  603. hydpy/models/arma_rimorido.py +381 -0
  604. hydpy/models/conv/__init__.py +12 -0
  605. hydpy/models/conv/conv_control.py +303 -0
  606. hydpy/models/conv/conv_derived.py +271 -0
  607. hydpy/models/conv/conv_fluxes.py +54 -0
  608. hydpy/models/conv/conv_inlets.py +11 -0
  609. hydpy/models/conv/conv_model.py +687 -0
  610. hydpy/models/conv/conv_outlets.py +11 -0
  611. hydpy/models/conv_idw.py +120 -0
  612. hydpy/models/conv_idw_ed.py +184 -0
  613. hydpy/models/conv_nn.py +112 -0
  614. hydpy/models/dam/__init__.py +16 -0
  615. hydpy/models/dam/dam_aides.py +17 -0
  616. hydpy/models/dam/dam_control.py +346 -0
  617. hydpy/models/dam/dam_derived.py +559 -0
  618. hydpy/models/dam/dam_factors.py +46 -0
  619. hydpy/models/dam/dam_fluxes.py +179 -0
  620. hydpy/models/dam/dam_inlets.py +29 -0
  621. hydpy/models/dam/dam_logs.py +52 -0
  622. hydpy/models/dam/dam_model.py +5011 -0
  623. hydpy/models/dam/dam_outlets.py +23 -0
  624. hydpy/models/dam/dam_receivers.py +41 -0
  625. hydpy/models/dam/dam_senders.py +23 -0
  626. hydpy/models/dam/dam_solver.py +75 -0
  627. hydpy/models/dam/dam_states.py +11 -0
  628. hydpy/models/dam_llake.py +499 -0
  629. hydpy/models/dam_lreservoir.py +548 -0
  630. hydpy/models/dam_lretention.py +343 -0
  631. hydpy/models/dam_pump.py +278 -0
  632. hydpy/models/dam_pump_sluice.py +339 -0
  633. hydpy/models/dam_sluice.py +319 -0
  634. hydpy/models/dam_v001.py +1127 -0
  635. hydpy/models/dam_v002.py +381 -0
  636. hydpy/models/dam_v003.py +422 -0
  637. hydpy/models/dam_v004.py +665 -0
  638. hydpy/models/dam_v005.py +479 -0
  639. hydpy/models/dummy/__init__.py +15 -0
  640. hydpy/models/dummy/dummy_control.py +22 -0
  641. hydpy/models/dummy/dummy_fluxes.py +11 -0
  642. hydpy/models/dummy/dummy_inlets.py +11 -0
  643. hydpy/models/dummy/dummy_inputs.py +41 -0
  644. hydpy/models/dummy/dummy_model.py +196 -0
  645. hydpy/models/dummy/dummy_outlets.py +11 -0
  646. hydpy/models/dummy_interceptedwater.py +85 -0
  647. hydpy/models/dummy_node2node.py +83 -0
  648. hydpy/models/dummy_snowalbedo.py +84 -0
  649. hydpy/models/dummy_snowcover.py +84 -0
  650. hydpy/models/dummy_snowycanopy.py +86 -0
  651. hydpy/models/dummy_soilwater.py +85 -0
  652. hydpy/models/evap/__init__.py +13 -0
  653. hydpy/models/evap/evap_control.py +354 -0
  654. hydpy/models/evap/evap_derived.py +236 -0
  655. hydpy/models/evap/evap_factors.py +188 -0
  656. hydpy/models/evap/evap_fixed.py +68 -0
  657. hydpy/models/evap/evap_fluxes.py +150 -0
  658. hydpy/models/evap/evap_inputs.py +54 -0
  659. hydpy/models/evap/evap_logs.py +91 -0
  660. hydpy/models/evap/evap_masks.py +48 -0
  661. hydpy/models/evap/evap_model.py +9170 -0
  662. hydpy/models/evap/evap_parameters.py +149 -0
  663. hydpy/models/evap/evap_sequences.py +32 -0
  664. hydpy/models/evap/evap_states.py +18 -0
  665. hydpy/models/evap_aet_hbv96.py +372 -0
  666. hydpy/models/evap_aet_minhas.py +331 -0
  667. hydpy/models/evap_aet_morsim.py +627 -0
  668. hydpy/models/evap_pet_ambav1.py +483 -0
  669. hydpy/models/evap_pet_hbv96.py +147 -0
  670. hydpy/models/evap_pet_m.py +94 -0
  671. hydpy/models/evap_pet_mlc.py +107 -0
  672. hydpy/models/evap_ret_fao56.py +265 -0
  673. hydpy/models/evap_ret_io.py +74 -0
  674. hydpy/models/evap_ret_tw2002.py +165 -0
  675. hydpy/models/exch/__init__.py +14 -0
  676. hydpy/models/exch/exch_control.py +262 -0
  677. hydpy/models/exch/exch_derived.py +36 -0
  678. hydpy/models/exch/exch_factors.py +26 -0
  679. hydpy/models/exch/exch_fluxes.py +48 -0
  680. hydpy/models/exch/exch_inlets.py +11 -0
  681. hydpy/models/exch/exch_logs.py +12 -0
  682. hydpy/models/exch/exch_model.py +451 -0
  683. hydpy/models/exch/exch_outlets.py +17 -0
  684. hydpy/models/exch/exch_receivers.py +17 -0
  685. hydpy/models/exch_branch_hbv96.py +186 -0
  686. hydpy/models/exch_waterlevel.py +73 -0
  687. hydpy/models/exch_weir_hbv96.py +609 -0
  688. hydpy/models/ga/__init__.py +14 -0
  689. hydpy/models/ga/ga_aides.py +17 -0
  690. hydpy/models/ga/ga_control.py +208 -0
  691. hydpy/models/ga/ga_derived.py +77 -0
  692. hydpy/models/ga/ga_fluxes.py +83 -0
  693. hydpy/models/ga/ga_inputs.py +26 -0
  694. hydpy/models/ga/ga_logs.py +17 -0
  695. hydpy/models/ga/ga_model.py +2952 -0
  696. hydpy/models/ga/ga_states.py +87 -0
  697. hydpy/models/ga_garto.py +1001 -0
  698. hydpy/models/ga_garto_submodel1.py +79 -0
  699. hydpy/models/gland/__init__.py +14 -0
  700. hydpy/models/gland/gland_control.py +90 -0
  701. hydpy/models/gland/gland_derived.py +113 -0
  702. hydpy/models/gland/gland_fluxes.py +137 -0
  703. hydpy/models/gland/gland_inputs.py +12 -0
  704. hydpy/models/gland/gland_model.py +1439 -0
  705. hydpy/models/gland/gland_outlets.py +11 -0
  706. hydpy/models/gland/gland_states.py +90 -0
  707. hydpy/models/gland_gr4.py +501 -0
  708. hydpy/models/gland_gr5.py +463 -0
  709. hydpy/models/gland_gr6.py +487 -0
  710. hydpy/models/hland/__init__.py +20 -0
  711. hydpy/models/hland/hland_aides.py +19 -0
  712. hydpy/models/hland/hland_constants.py +37 -0
  713. hydpy/models/hland/hland_control.py +1530 -0
  714. hydpy/models/hland/hland_derived.py +683 -0
  715. hydpy/models/hland/hland_factors.py +57 -0
  716. hydpy/models/hland/hland_fixed.py +42 -0
  717. hydpy/models/hland/hland_fluxes.py +279 -0
  718. hydpy/models/hland/hland_inputs.py +19 -0
  719. hydpy/models/hland/hland_masks.py +107 -0
  720. hydpy/models/hland/hland_model.py +4664 -0
  721. hydpy/models/hland/hland_outlets.py +11 -0
  722. hydpy/models/hland/hland_parameters.py +227 -0
  723. hydpy/models/hland/hland_sequences.py +382 -0
  724. hydpy/models/hland/hland_states.py +236 -0
  725. hydpy/models/hland_96.py +1812 -0
  726. hydpy/models/hland_96c.py +1196 -0
  727. hydpy/models/hland_96p.py +1204 -0
  728. hydpy/models/kinw/__init__.py +18 -0
  729. hydpy/models/kinw/kinw_aides.py +306 -0
  730. hydpy/models/kinw/kinw_control.py +270 -0
  731. hydpy/models/kinw/kinw_derived.py +197 -0
  732. hydpy/models/kinw/kinw_fixed.py +33 -0
  733. hydpy/models/kinw/kinw_fluxes.py +37 -0
  734. hydpy/models/kinw/kinw_inlets.py +11 -0
  735. hydpy/models/kinw/kinw_model.py +3026 -0
  736. hydpy/models/kinw/kinw_outlets.py +11 -0
  737. hydpy/models/kinw/kinw_solver.py +45 -0
  738. hydpy/models/kinw/kinw_states.py +17 -0
  739. hydpy/models/kinw_williams.py +1299 -0
  740. hydpy/models/kinw_williams_ext.py +768 -0
  741. hydpy/models/lland/__init__.py +42 -0
  742. hydpy/models/lland/lland_aides.py +38 -0
  743. hydpy/models/lland/lland_constants.py +88 -0
  744. hydpy/models/lland/lland_control.py +1329 -0
  745. hydpy/models/lland/lland_derived.py +380 -0
  746. hydpy/models/lland/lland_factors.py +18 -0
  747. hydpy/models/lland/lland_fixed.py +128 -0
  748. hydpy/models/lland/lland_fluxes.py +626 -0
  749. hydpy/models/lland/lland_inlets.py +12 -0
  750. hydpy/models/lland/lland_inputs.py +33 -0
  751. hydpy/models/lland/lland_logs.py +17 -0
  752. hydpy/models/lland/lland_masks.py +212 -0
  753. hydpy/models/lland/lland_model.py +7690 -0
  754. hydpy/models/lland/lland_outlets.py +12 -0
  755. hydpy/models/lland/lland_parameters.py +195 -0
  756. hydpy/models/lland/lland_sequences.py +67 -0
  757. hydpy/models/lland/lland_states.py +280 -0
  758. hydpy/models/lland_dd.py +2270 -0
  759. hydpy/models/lland_knauf.py +2156 -0
  760. hydpy/models/lland_knauf_ic.py +1920 -0
  761. hydpy/models/meteo/__init__.py +12 -0
  762. hydpy/models/meteo/meteo_control.py +154 -0
  763. hydpy/models/meteo/meteo_derived.py +159 -0
  764. hydpy/models/meteo/meteo_factors.py +88 -0
  765. hydpy/models/meteo/meteo_fixed.py +19 -0
  766. hydpy/models/meteo/meteo_fluxes.py +46 -0
  767. hydpy/models/meteo/meteo_inputs.py +47 -0
  768. hydpy/models/meteo/meteo_logs.py +30 -0
  769. hydpy/models/meteo/meteo_model.py +2904 -0
  770. hydpy/models/meteo/meteo_parameters.py +14 -0
  771. hydpy/models/meteo/meteo_sequences.py +22 -0
  772. hydpy/models/meteo_clear_glob_io.py +77 -0
  773. hydpy/models/meteo_glob_fao56.py +217 -0
  774. hydpy/models/meteo_glob_io.py +68 -0
  775. hydpy/models/meteo_glob_morsim.py +444 -0
  776. hydpy/models/meteo_precip_io.py +76 -0
  777. hydpy/models/meteo_psun_sun_glob_io.py +83 -0
  778. hydpy/models/meteo_sun_fao56.py +188 -0
  779. hydpy/models/meteo_sun_morsim.py +466 -0
  780. hydpy/models/meteo_temp_io.py +76 -0
  781. hydpy/models/musk/__init__.py +15 -0
  782. hydpy/models/musk/musk_control.py +328 -0
  783. hydpy/models/musk/musk_derived.py +32 -0
  784. hydpy/models/musk/musk_factors.py +53 -0
  785. hydpy/models/musk/musk_fluxes.py +24 -0
  786. hydpy/models/musk/musk_inlets.py +11 -0
  787. hydpy/models/musk/musk_masks.py +15 -0
  788. hydpy/models/musk/musk_model.py +838 -0
  789. hydpy/models/musk/musk_outlets.py +11 -0
  790. hydpy/models/musk/musk_sequences.py +88 -0
  791. hydpy/models/musk/musk_solver.py +68 -0
  792. hydpy/models/musk/musk_states.py +64 -0
  793. hydpy/models/musk_classic.py +228 -0
  794. hydpy/models/musk_mct.py +1247 -0
  795. hydpy/models/rconc/__init__.py +12 -0
  796. hydpy/models/rconc/rconc_control.py +473 -0
  797. hydpy/models/rconc/rconc_derived.py +76 -0
  798. hydpy/models/rconc/rconc_fluxes.py +19 -0
  799. hydpy/models/rconc/rconc_logs.py +74 -0
  800. hydpy/models/rconc/rconc_model.py +260 -0
  801. hydpy/models/rconc/rconc_states.py +11 -0
  802. hydpy/models/rconc_nash.py +48 -0
  803. hydpy/models/rconc_uh.py +53 -0
  804. hydpy/models/sw1d/__init__.py +17 -0
  805. hydpy/models/sw1d/sw1d_control.py +356 -0
  806. hydpy/models/sw1d/sw1d_derived.py +85 -0
  807. hydpy/models/sw1d/sw1d_factors.py +78 -0
  808. hydpy/models/sw1d/sw1d_fixed.py +12 -0
  809. hydpy/models/sw1d/sw1d_fluxes.py +55 -0
  810. hydpy/models/sw1d/sw1d_inlets.py +17 -0
  811. hydpy/models/sw1d/sw1d_model.py +3385 -0
  812. hydpy/models/sw1d/sw1d_outlets.py +11 -0
  813. hydpy/models/sw1d/sw1d_receivers.py +11 -0
  814. hydpy/models/sw1d/sw1d_senders.py +11 -0
  815. hydpy/models/sw1d/sw1d_states.py +23 -0
  816. hydpy/models/sw1d_channel.py +2051 -0
  817. hydpy/models/sw1d_gate_out.py +599 -0
  818. hydpy/models/sw1d_lias.py +105 -0
  819. hydpy/models/sw1d_lias_sluice.py +531 -0
  820. hydpy/models/sw1d_network.py +1219 -0
  821. hydpy/models/sw1d_pump.py +448 -0
  822. hydpy/models/sw1d_q_in.py +79 -0
  823. hydpy/models/sw1d_q_out.py +81 -0
  824. hydpy/models/sw1d_storage.py +78 -0
  825. hydpy/models/sw1d_weir_out.py +75 -0
  826. hydpy/models/test/__init__.py +14 -0
  827. hydpy/models/test/test_control.py +28 -0
  828. hydpy/models/test/test_fluxes.py +17 -0
  829. hydpy/models/test/test_model.py +201 -0
  830. hydpy/models/test/test_solver.py +48 -0
  831. hydpy/models/test/test_states.py +17 -0
  832. hydpy/models/test_discontinous.py +46 -0
  833. hydpy/models/test_stiff0d.py +47 -0
  834. hydpy/models/test_stiff1d.py +42 -0
  835. hydpy/models/whmod/__init__.py +21 -0
  836. hydpy/models/whmod/whmod_constants.py +77 -0
  837. hydpy/models/whmod/whmod_control.py +333 -0
  838. hydpy/models/whmod/whmod_derived.py +210 -0
  839. hydpy/models/whmod/whmod_factors.py +9 -0
  840. hydpy/models/whmod/whmod_fluxes.py +105 -0
  841. hydpy/models/whmod/whmod_inputs.py +15 -0
  842. hydpy/models/whmod/whmod_masks.py +178 -0
  843. hydpy/models/whmod/whmod_model.py +2091 -0
  844. hydpy/models/whmod/whmod_parameters.py +155 -0
  845. hydpy/models/whmod/whmod_sequences.py +193 -0
  846. hydpy/models/whmod/whmod_states.py +73 -0
  847. hydpy/models/whmod_rural.py +794 -0
  848. hydpy/models/whmod_urban.py +1011 -0
  849. hydpy/models/wland/__init__.py +43 -0
  850. hydpy/models/wland/wland_aides.py +55 -0
  851. hydpy/models/wland/wland_constants.py +103 -0
  852. hydpy/models/wland/wland_control.py +508 -0
  853. hydpy/models/wland/wland_derived.py +330 -0
  854. hydpy/models/wland/wland_factors.py +11 -0
  855. hydpy/models/wland/wland_fixed.py +12 -0
  856. hydpy/models/wland/wland_fluxes.py +166 -0
  857. hydpy/models/wland/wland_inputs.py +33 -0
  858. hydpy/models/wland/wland_masks.py +54 -0
  859. hydpy/models/wland/wland_model.py +3755 -0
  860. hydpy/models/wland/wland_outlets.py +11 -0
  861. hydpy/models/wland/wland_parameters.py +214 -0
  862. hydpy/models/wland/wland_sequences.py +108 -0
  863. hydpy/models/wland/wland_solver.py +45 -0
  864. hydpy/models/wland/wland_states.py +56 -0
  865. hydpy/models/wland_gd.py +888 -0
  866. hydpy/models/wland_wag.py +1244 -0
  867. hydpy/models/wq/__init__.py +14 -0
  868. hydpy/models/wq/wq_control.py +117 -0
  869. hydpy/models/wq/wq_derived.py +182 -0
  870. hydpy/models/wq/wq_factors.py +79 -0
  871. hydpy/models/wq/wq_fluxes.py +17 -0
  872. hydpy/models/wq/wq_model.py +1889 -0
  873. hydpy/models/wq_trapeze.py +168 -0
  874. hydpy/models/wq_trapeze_strickler.py +101 -0
  875. hydpy/models/wq_walrus.py +57 -0
  876. hydpy/py.typed +0 -0
  877. hydpy/tests/.coveragerc +22 -0
  878. hydpy/tests/__init__.py +0 -0
  879. hydpy/tests/check_consistency.py +32 -0
  880. hydpy/tests/hydpydoctestcustomize.pth +1 -0
  881. hydpy/tests/hydpydoctestcustomize.py +15 -0
  882. hydpy/tests/iotesting/__init__.py +0 -0
  883. hydpy/tests/run_doctests.py +233 -0
  884. hydpy-6.2.dev1.data/scripts/hyd.py +277 -0
  885. hydpy-6.2.dev1.dist-info/LICENSE +165 -0
  886. hydpy-6.2.dev1.dist-info/METADATA +163 -0
  887. hydpy-6.2.dev1.dist-info/RECORD +890 -0
  888. hydpy-6.2.dev1.dist-info/WHEEL +5 -0
  889. hydpy-6.2.dev1.dist-info/licenses_hydpy_installer.txt +42 -0
  890. hydpy-6.2.dev1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,2683 @@
1
+ """
2
+ This module extends the features of module |filetools| for loading data from and
3
+ storing data to netCDF4 files, consistent with the `NetCDF Climate and Forecast (CF)
4
+ Metadata Conventions <http://cfconventions.org/Data/cf-conventions/cf-conventions-1.8/
5
+ cf-conventions.html>`_.
6
+
7
+ .. _`Delft-FEWS`: https://oss.deltares.nl/web/delft-fews
8
+
9
+ Usually, we only indirectly apply the features implemented in this module. Here, we
10
+ demonstrate the underlying functionalities, which can be subsumed by following three
11
+ steps:
12
+
13
+ 1. Call either method |SequenceManager.open_netcdfreader| or method
14
+ |SequenceManager.open_netcdfwriter| of the |SequenceManager| object available in
15
+ module |pub| to prepare a |NetCDFInterfaceReader| object for reading or a
16
+ |NetCDFInterfaceWriter| object for writing.
17
+ 2. Call either the usual reading or writing methods of other HydPy classes like
18
+ method |HydPy.load_fluxseries| of class |HydPy| or method
19
+ |Elements.save_stateseries| of class |Elements|. The prepared interface object
20
+ collects all requests of those sequences one wants to read from or write to NetCDF
21
+ files.
22
+ 3. Finalise reading or writing by calling either method
23
+ |SequenceManager.close_netcdfreader| or |SequenceManager.close_netcdfwriter|.
24
+
25
+ Step 2 is a logging process only, telling the interface object which data needs to be
26
+ read or written, while step 3 triggers the actual reading from or writing to NetCDF
27
+ files.
28
+
29
+ During step 2, the interface object and its subobjects of type
30
+ |NetCDFVariableFlatReader|, |NetCDFVariableFlatWriter|, or |NetCDFVariableAggregated|
31
+ are accessible, allowing one to inspect their current state or modify their behaviour.
32
+
33
+ The following real code examples show how to perform these three steps both for reading
34
+ and writing data, based on the example configuration defined by function
35
+ |prepare_io_example_1|:
36
+
37
+ >>> from hydpy.core.testtools import prepare_io_example_1
38
+ >>> nodes, elements = prepare_io_example_1()
39
+
40
+ We prepare a |NetCDFInterfaceWriter| object for writing data via method
41
+ |SequenceManager.open_netcdfwriter|:
42
+
43
+ >>> from hydpy import pub
44
+ >>> pub.sequencemanager.open_netcdfwriter()
45
+
46
+ We tell the |SequenceManager| object to write all the time series data to NetCDF files:
47
+
48
+ >>> pub.sequencemanager.filetype = "nc"
49
+
50
+ We store all the time series handled by the |Node| and |Element| objects of the example
51
+ dataset by calling |Nodes.save_allseries| of class |Nodes| and
52
+ |Elements.save_allseries| of class |Elements|. (In real cases, you would not write the
53
+ `with TestIO():` line. This code block makes sure we pollute the IO testing directory
54
+ instead of our current working directory):
55
+
56
+ >>> from hydpy import TestIO
57
+ >>> with TestIO():
58
+ ... nodes.save_allseries()
59
+ ... elements.save_allseries()
60
+
61
+ We again log all sequences, but after telling the |SequenceManager| object to average
62
+ each time series spatially:
63
+
64
+ ToDo: Support spatial averaging for sequences of submodels.
65
+
66
+ >>> with TestIO(), pub.sequencemanager.aggregation("mean"):
67
+ ... nodes.save_allseries()
68
+ ... elements.element3.model.aetmodel.prepare_allseries(allocate_ram=False)
69
+ ... elements.save_allseries()
70
+ ... elements.element3.model.aetmodel.prepare_allseries(allocate_ram=True)
71
+
72
+ We can now navigate into the details of the logged time series data via the
73
+ |NetCDFInterfaceWriter| object and its |NetCDFVariableFlatReader| and
74
+ |NetCDFVariableAggregated| subobjects. For example, we can query the logged flux
75
+ sequence objects of type |lland_fluxes.NKor| belonging to application model |lland_dd|
76
+ (those of elements `element1` and `element2`; the trailing numbers are the indices of
77
+ the relevant hydrological response units):
78
+
79
+ >>> writer = pub.sequencemanager.netcdfwriter
80
+ >>> writer.lland_dd_flux_nkor.subdevicenames
81
+ ('element1_0', 'element2_0', 'element2_1')
82
+
83
+ In the example discussed here, all sequences belong to the same folder (`default`).
84
+ Storing sequences in separate folders means storing them in separate NetCDF files. In
85
+ such cases, you must include the folder in the attribute name:
86
+
87
+ >>> writer.foldernames
88
+ ('default',)
89
+ >>> writer.default_lland_dd_flux_nkor.subdevicenames
90
+ ('element1_0', 'element2_0', 'element2_1')
91
+
92
+ We close the |NetCDFInterfaceWriter| object, which is the moment when the writing
93
+ process happens. After that, the interface object is no longer available:
94
+
95
+ >>> from hydpy import TestIO
96
+ >>> with TestIO():
97
+ ... pub.sequencemanager.close_netcdfwriter()
98
+ >>> pub.sequencemanager.netcdfwriter
99
+ Traceback (most recent call last):
100
+ ...
101
+ hydpy.core.exceptiontools.AttributeNotReady: The sequence file manager currently \
102
+ handles no NetCDF writer object. Consider applying the \
103
+ `pub.sequencemanager.netcdfwriting` context manager first (search in the \
104
+ documentation for help).
105
+
106
+
107
+ We set the time series values of two test sequences to zero to demonstrate that
108
+ reading the data back in actually works:
109
+
110
+ >>> nodes.node2.sequences.sim.series = 0.0
111
+ >>> elements.element2.model.sequences.fluxes.nkor.series = 0.0
112
+
113
+ We move up a gear and and prepare a |NetCDFInterfaceReader| object for reading data,
114
+ log all |NodeSequence| and |ModelSequence| objects, and read their time series data
115
+ from the created NetCDF file. We temporarily disable the |Options.checkseries| option
116
+ to prevent raising an exception when reading incomplete data from the files:
117
+
118
+ >>> with TestIO(), pub.options.checkseries(False):
119
+ ... pub.sequencemanager.open_netcdfreader()
120
+ ... nodes.load_simseries()
121
+ ... elements.load_allseries()
122
+ ... pub.sequencemanager.close_netcdfreader()
123
+
124
+ We check if the data is available via the test sequences again:
125
+
126
+ >>> nodes.node2.sequences.sim.series
127
+ InfoArray([64., 65., 66., 67.])
128
+ >>> elements.element2.model.sequences.fluxes.nkor.series
129
+ InfoArray([[16., 17.],
130
+ [18., 19.],
131
+ [20., 21.],
132
+ [22., 23.]])
133
+ >>> pub.sequencemanager.netcdfreader
134
+ Traceback (most recent call last):
135
+ ...
136
+ hydpy.core.exceptiontools.AttributeNotReady: The sequence file manager currently \
137
+ handles no NetCDF reader object. Consider applying the \
138
+ `pub.sequencemanager.netcdfreading` context manager first (search in the \
139
+ documentation for help).
140
+
141
+ We cannot invert spatial aggregation. Hence reading averaged time series is left for
142
+ postprocessing tools. To show that writing the averaged series worked, we access both
143
+ relevant NetCDF files more directly using the underlying NetCDF4 library (note that
144
+ averaging 1-dimensional time series as those of node sequence |Sim| is allowed for the
145
+ sake of consistency):
146
+
147
+ >>> from numpy import array
148
+ >>> from hydpy import print_matrix
149
+ >>> from hydpy.core.netcdftools import netcdf4
150
+ >>> filepath = "project/series/default/sim_q_mean.nc"
151
+ >>> with TestIO(), netcdf4.Dataset(filepath) as ncfile:
152
+ ... print_matrix(array(ncfile["sim_q_mean"][:]))
153
+ | 60.0 |
154
+ | 61.0 |
155
+ | 62.0 |
156
+ | 63.0 |
157
+
158
+ >>> from hydpy import print_vector
159
+ >>> filepath = "project/series/default/lland_dd_flux_nkor_mean.nc"
160
+ >>> with TestIO(), netcdf4.Dataset(filepath) as ncfile:
161
+ ... print_vector(array(ncfile["lland_dd_flux_nkor_mean"][:])[:, 1])
162
+ 16.5, 18.5, 20.5, 22.5
163
+
164
+ The previous examples relied on "model-specific" file names and variable names. The
165
+ documentation on class |HydPy| introduces the standard "HydPy" convention as an
166
+ alternative for naming input time series files and demonstrates it for file types that
167
+ store the time series of single sequence instances. Here, we take the input sequence
168
+ |lland_inputs.Nied| as an example to show that one can use its standard name
169
+ |StandardInputNames.PRECIPITATION| to read and write more generally named NetCDF files
170
+ and variables:
171
+
172
+ >>> print_vector(elements.element2.model.sequences.inputs.nied.series)
173
+ 4.0, 5.0, 6.0, 7.0
174
+
175
+ >>> pub.sequencemanager.convention = "HydPy"
176
+ >>> with TestIO():
177
+ ... elements.save_inputseries()
178
+ >>> filepath = "project/series/default/precipitation.nc"
179
+ >>> with TestIO(), netcdf4.Dataset(filepath) as ncfile:
180
+ ... print_vector(array(ncfile["precipitation"][:])[:, 1])
181
+ 4.0, 5.0, 6.0, 7.0
182
+
183
+ >>> elements.element2.model.sequences.inputs.nied.series = 0.0
184
+ >>> with TestIO(), pub.options.checkseries(False):
185
+ ... elements.load_inputseries()
186
+ >>> print_vector(elements.element2.model.sequences.inputs.nied.series)
187
+ 4.0, 5.0, 6.0, 7.0
188
+
189
+ In the last example, the methods |Elements.load_inputseries| and
190
+ |Elements.save_inputseries| of class |Elements| opened and closed the required NetCDF
191
+ reader and writer objects automatically by using the context managers
192
+ |SequenceManager.netcdfreading| and |SequenceManager.netcdfwriting|. Such comfort is
193
+ only available for these and the similar methods of the classes |HydPy|, |Elements|,
194
+ and |Nodes|. If you, for example, apply the |IOSequence.load_series| or the
195
+ |IOSequence.save_series| method of individual |IOSequence| instances, you must activate
196
+ |SequenceManager.netcdfreading| or |SequenceManager.netcdfwriting| manually. This
197
+ discomfort is intentional and should help prevent accidentally opening and closing
198
+ the same NetCDF file repeatedly, which could result in an immense waste of computation
199
+ time. The following example shows how to apply these context managers manually and
200
+ that this does not conflict with using methods that could automatically open and close
201
+ NetCDF reader and writer objects:
202
+
203
+ >>> sequences = elements.element2.model.sequences
204
+ >>> sequences.inputs.nied.series = 1.0, 3.0, 5.0, 7.0
205
+ >>> sequences.fluxes.qah.series = 2.0, 4.0, 6.0, 8.0
206
+ >>> sequences.fluxes.qa.series = 3.0, 5.0, 7.0, 9.0
207
+ >>> with TestIO(), pub.sequencemanager.netcdfwriting():
208
+ ... sequences.fluxes.qa.save_series()
209
+ ... elements.save_inputseries()
210
+ ... sequences.fluxes.qah.save_series()
211
+
212
+ >>> sequences.inputs.nied.series = 0.0
213
+ >>> sequences.fluxes.qah.series = 0.0
214
+ >>> sequences.fluxes.qa.series = 0.0
215
+ >>> with TestIO(), pub.options.checkseries(False), pub.sequencemanager.netcdfreading():
216
+ ... sequences.fluxes.qah.load_series()
217
+ ... elements.load_inputseries()
218
+ ... sequences.fluxes.qa.load_series()
219
+ >>> print_vector(sequences.inputs.nied.series)
220
+ 1.0, 3.0, 5.0, 7.0
221
+ >>> print_vector(sequences.fluxes.qah.series)
222
+ 2.0, 4.0, 6.0, 8.0
223
+ >>> print_vector(sequences.fluxes.qa.series)
224
+ 3.0, 5.0, 7.0, 9.0
225
+
226
+ Besides the testing-related specialities, the described workflow is more or less
227
+ standard but allows for different modifications. We illustrate them in the
228
+ documentation of the other features implemented in module |netcdftools| and the
229
+ documentation on class |SequenceManager| of module |filetools| and class |IOSequence|
230
+ of module |sequencetools|.
231
+
232
+ Using the NetCDF format allows reading or writing data "just in time" during simulation
233
+ runs. The documentation of class "HydPy" explains how to select and set the relevant
234
+ |IOSequence| objects for this option. See the documentation on method
235
+ |NetCDFInterfaceJIT.provide_jitaccess| of class |NetCDFInterfaceJIT| for more in-depth
236
+ information.
237
+ """
238
+
239
+ # import...
240
+ # ...from standard library
241
+ from __future__ import annotations
242
+ import abc
243
+ import collections
244
+ import functools
245
+ import contextlib
246
+ import itertools
247
+ import os
248
+ import time
249
+ import warnings
250
+
251
+ # ...from site-packages
252
+ import numpy
253
+
254
+ # ...from HydPy
255
+ import hydpy
256
+ from hydpy import config
257
+ from hydpy.core import exceptiontools
258
+ from hydpy.core import devicetools
259
+ from hydpy.core import objecttools
260
+ from hydpy.core import printtools
261
+ from hydpy.core import sequencetools
262
+ from hydpy.core import timetools
263
+ from hydpy.core.typingtools import *
264
+
265
+ if TYPE_CHECKING:
266
+ import netCDF4 as netcdf4
267
+ else:
268
+ netcdf4 = exceptiontools.OptionalImport("netcdf4", ["netCDF4"], locals())
269
+
270
+ dimmapping = {
271
+ "nmb_timepoints": "time",
272
+ "nmb_subdevices": "stations",
273
+ "nmb_characters": "char_leng_name",
274
+ }
275
+ """Dimension-related terms within NetCDF files.
276
+
277
+ You can change this mapping if it does not suit your requirements. For example, change
278
+ the value of the keyword "nmb_subdevices" if you prefer to call this dimension
279
+ "location" instead of "stations" within NetCDF files:
280
+
281
+ >>> from hydpy.core.netcdftools import dimmapping
282
+ >>> dimmapping["nmb_subdevices"] = "location"
283
+ """
284
+
285
+ varmapping = {"timepoints": "time", "subdevices": "station_id"}
286
+ """Variable-related terms within NetCDF files.
287
+
288
+ You can change this mapping if it does not suit your requirements. For example, change
289
+ the value of the keyword "timepoints" if you prefer to call this variable "period"
290
+ instead of "time" within NetCDF files:
291
+
292
+ >>> from hydpy.core.netcdftools import varmapping
293
+ >>> varmapping["timepoints"] = "period"
294
+ """
295
+
296
+ fillvalue = numpy.nan
297
+ """Default fill value for writing NetCDF files.
298
+
299
+ You can set another |float| value before writing a NetCDF file:
300
+
301
+ >>> from hydpy.core import netcdftools
302
+ >>> netcdftools.fillvalue = -777.0
303
+ """
304
+
305
+
306
+ TypeNetCDFVariable = TypeVar("TypeNetCDFVariable", bound="NetCDFVariable")
307
+
308
+ FlatUnion: TypeAlias = Union["NetCDFVariableFlatReader", "NetCDFVariableFlatWriter"]
309
+
310
+
311
+ def summarise_ncfile(ncfile: netcdf4.Dataset | str, /) -> str:
312
+ """Give a summary describing (a HydPy-compatible) NetCDF file.
313
+
314
+ You can pass the file path:
315
+
316
+ >>> import os
317
+ >>> from hydpy import data, repr_, summarise_ncfile
318
+ >>> filepath = os.path.join(
319
+ ... data.__path__[0], "HydPy-H-Lahn", "series", "default", "hland_96_input_p.nc"
320
+ ... )
321
+ >>> print(repr_(summarise_ncfile(filepath))) # doctest: +ELLIPSIS
322
+ GENERAL
323
+ file path = .../hydpy/data/HydPy-H-Lahn/series/default/hland_96_input_p.nc
324
+ file format = NETCDF4
325
+ disk format = HDF5
326
+ Attributes
327
+ hydts_timeRef = begin
328
+ title = Daily total precipitation sum HydPy-H-HBV96 model river Lahn
329
+ ...
330
+ DIMENSIONS
331
+ stations = 4
332
+ time = 11384
333
+ str_len = 40
334
+ VARIABLES
335
+ time
336
+ dimensions = time
337
+ shape = 11384
338
+ data type = float64
339
+ Attributes
340
+ units = days since 1900-01-01 00:00:00 +0100
341
+ long_name = time
342
+ axis = T
343
+ calendar = standard
344
+ hland_96_input_p
345
+ dimensions = time, stations
346
+ shape = 11384, 4
347
+ data type = float64
348
+ Attributes
349
+ units = mm
350
+ _FillValue = -9999.0
351
+ long_name = Daily Precipitation Sum
352
+ station_id
353
+ dimensions = stations, str_len
354
+ shape = 4, 40
355
+ data type = |S1
356
+ Attributes
357
+ long_name = station or node identification code
358
+ station_names
359
+ dimensions = stations, str_len
360
+ shape = 4, 40
361
+ data type = |S1
362
+ Attributes
363
+ long_name = station or node name
364
+ river_names
365
+ dimensions = stations, str_len
366
+ shape = 4, 40
367
+ data type = |S1
368
+ Attributes
369
+ long_name = river name
370
+ TIME GRID
371
+ first date = 1989-11-01 00:00:00+01:00
372
+ last date = 2021-01-01 00:00:00+01:00
373
+ step size = 1d
374
+
375
+ Alternatively, you can pass a NetCDF4 `Dataset` object:
376
+
377
+ >>> from netCDF4 import Dataset
378
+ >>> with Dataset(filepath, "r") as ncfile:
379
+ ... print(repr_(summarise_ncfile(ncfile))) # doctest: +ELLIPSIS
380
+ GENERAL
381
+ file path = ...data/HydPy-H-Lahn/series/default/hland_96_input_p.nc
382
+ ...
383
+ _FillValue = -9999.0
384
+ ...
385
+ """
386
+
387
+ def _summarize(nc: netcdf4.Dataset) -> str:
388
+
389
+ i1, i2, i3 = 4 * " ", 8 * " ", 12 * " "
390
+
391
+ lines: list[str] = []
392
+ append = lines.append
393
+
394
+ append("GENERAL")
395
+ append(f"{i1}file path = {nc.filepath()}")
396
+ append(f"{i1}file format = {nc.file_format}")
397
+ append(f"{i1}disk format = {nc.disk_format}")
398
+ if attrs_file := nc.ncattrs():
399
+ append(f"{i1}Attributes")
400
+ for attr_file in attrs_file:
401
+ append(f"{i2}{attr_file} = {nc.getncattr(attr_file)}")
402
+
403
+ if dims := nc.dimensions:
404
+ append("DIMENSIONS")
405
+ for name, dim in dims.items():
406
+ append(f"{i1}{name} = {dim.size}")
407
+
408
+ if vars_ := nc.variables:
409
+ append("VARIABLES")
410
+ for name, var in vars_.items():
411
+ append(f"{i1}{name}")
412
+ append(f"{i2}dimensions = {', '.join(var.dimensions)}")
413
+ append(f"{i2}shape = {', '.join(str(s) for s in var.shape)}")
414
+ append(f"{i2}data type = {var.datatype}")
415
+ if attrs_var := var.ncattrs():
416
+ append(f"{i2}Attributes")
417
+ for attr_var in attrs_var:
418
+ append(f"{i3}{attr_var} = {var.getncattr(attr_var)}")
419
+
420
+ timereference: str | None = getattr(nc, "timereference", None)
421
+ if timereference is not None:
422
+ append("TIME GRID")
423
+ tg = _query_timegrid(ncfile=nc, left=timereference.startswith("left"))
424
+ opts = hydpy.pub.options
425
+ firstdate = tg.firstdate.to_string(style="iso2", utcoffset=opts.utcoffset)
426
+ append(f"{i1}first date = {firstdate}")
427
+ lastdate = tg.lastdate.to_string(style="iso2", utcoffset=opts.utcoffset)
428
+ append(f"{i1}last date = {lastdate}")
429
+ append(f"{i1}step size = {tg.stepsize}")
430
+
431
+ return "\n".join(lines)
432
+
433
+ if isinstance(ncfile, str):
434
+ with netcdf4.Dataset(ncfile, "r") as ncfile_:
435
+ return _summarize(ncfile_)
436
+ return _summarize(ncfile)
437
+
438
+
439
+ def str2chars(strings: Sequence[str]) -> MatrixBytes:
440
+ """Return a |numpy.ndarray| object containing the byte characters (second axis) of
441
+ all given strings (first axis).
442
+
443
+ >>> from hydpy.core.netcdftools import str2chars
444
+ >>> str2chars(['street', 'St.', 'Straße', 'Str.'])
445
+ array([[b's', b't', b'r', b'e', b'e', b't', b''],
446
+ [b'S', b't', b'.', b'', b'', b'', b''],
447
+ [b'S', b't', b'r', b'a', b'\xc3', b'\x9f', b'e'],
448
+ [b'S', b't', b'r', b'.', b'', b'', b'']], dtype='|S1')
449
+
450
+ >>> str2chars([])
451
+ array([], shape=(0, 0), dtype='|S1')
452
+ """
453
+ if len(strings) == 0:
454
+ return numpy.full((0, 0), b"", dtype="|S1")
455
+ bytess = tuple(string.encode("utf-8") for string in strings)
456
+ max_length = max(len(bytes_) for bytes_ in bytess)
457
+ chars = numpy.full((len(strings), max_length), b"", dtype="|S1")
458
+ for idx, bytes_ in enumerate(bytess):
459
+ for jdx, int_ in enumerate(bytes_):
460
+ chars[idx, jdx] = bytes([int_])
461
+ return chars
462
+
463
+
464
+ def chars2str(chars: MatrixBytes) -> list[str]:
465
+ r"""Inversion function of |str2chars|.
466
+
467
+ >>> from hydpy.core.netcdftools import chars2str
468
+
469
+ >>> chars2str([[b"s", b"t", b"r", b"e", b"e", b"t", b""],
470
+ ... [b"S", b"t", b".", b"", b"", b"", b""],
471
+ ... [b"S", b"t", b"r", b"a", b"\xc3", b"\x9f", b"e"],
472
+ ... [b"S", b"t", b"r", b".", b"", b"", b""]])
473
+ ['street', 'St.', 'Straße', 'Str.']
474
+
475
+ >>> chars2str([])
476
+ []
477
+
478
+ >>> chars2str([[b"s", b"t", b"r", b"e", b"e", b"t"],
479
+ ... [b"S", b"t", b".", b"", b"", b""],
480
+ ... [b"S", b"t", b"r", b"a", b"\xc3", b"e"],
481
+ ... [b"S", b"t", b"r", b".", b"", b""]])
482
+ Traceback (most recent call last):
483
+ ...
484
+ ValueError: Cannot decode `b'Stra\xc3e'` (not UTF-8 compliant).
485
+ """
486
+ strings = []
487
+ for subchars in chars:
488
+ try:
489
+ bytes_ = b"".join(subchars)
490
+ strings.append(bytes_.decode("utf-8"))
491
+ except UnicodeDecodeError:
492
+ raise ValueError(
493
+ f"Cannot decode `{bytes_!r}` (not UTF-8 compliant)."
494
+ ) from None
495
+ return strings
496
+
497
+
498
+ def create_dimension(ncfile: netcdf4.Dataset, name: str, length: int) -> None:
499
+ """Add a new dimension with the given name and length to the given NetCDF file.
500
+
501
+ Essentially, |create_dimension| only calls the equally named method of the NetCDF
502
+ library but adds information to possible error messages:
503
+
504
+ >>> from hydpy import TestIO
505
+ >>> from hydpy.core.netcdftools import netcdf4
506
+ >>> with TestIO():
507
+ ... ncfile = netcdf4.Dataset("test.nc", "w")
508
+ >>> from hydpy.core.netcdftools import create_dimension
509
+ >>> create_dimension(ncfile, "dim1", 5)
510
+ >>> dim = ncfile.dimensions["dim1"]
511
+ >>> dim.size if hasattr(dim, "size") else dim
512
+ 5
513
+
514
+ >>> try:
515
+ ... create_dimension(ncfile, "dim1", 5)
516
+ ... except BaseException as exc:
517
+ ... print(exc) # doctest: +ELLIPSIS
518
+ While trying to add dimension `dim1` with length `5` to the NetCDF file \
519
+ `test.nc`, the following error occurred: ...
520
+
521
+ >>> ncfile.close()
522
+ """
523
+ try:
524
+ ncfile.createDimension(name, length)
525
+ except BaseException:
526
+ objecttools.augment_excmessage(
527
+ f"While trying to add dimension `{name}` with length `{length}` to the "
528
+ f"NetCDF file `{get_filepath(ncfile)}`"
529
+ )
530
+
531
+
532
+ def create_variable(
533
+ ncfile: netcdf4.Dataset, name: str, datatype: str, dimensions: Sequence[str]
534
+ ) -> None:
535
+ """Add a new variable with the given name, datatype, and dimensions to the given
536
+ NetCDF file.
537
+
538
+ Essentially, |create_variable| only calls the equally named method of the NetCDF
539
+ library but adds information to possible error messages:
540
+
541
+ >>> from hydpy import TestIO
542
+ >>> from hydpy.core.netcdftools import netcdf4
543
+ >>> with TestIO():
544
+ ... ncfile = netcdf4.Dataset("test.nc", "w")
545
+ >>> from hydpy.core.netcdftools import create_variable
546
+ >>> try:
547
+ ... create_variable(ncfile, "var1", "f8", ("dim1",))
548
+ ... except BaseException as exc:
549
+ ... print(str(exc).strip('"')) # doctest: +ELLIPSIS
550
+ While trying to add variable `var1` with datatype `f8` and dimensions `('dim1',)` \
551
+ to the NetCDF file `test.nc`, the following error occurred: ...
552
+
553
+ >>> from hydpy.core.netcdftools import create_dimension
554
+ >>> create_dimension(ncfile, "dim1", 5)
555
+ >>> create_variable(ncfile, "var1", "f8", ("dim1",))
556
+ >>> import numpy
557
+ >>> from hydpy import print_vector
558
+ >>> print_vector(numpy.array(ncfile["var1"][:]))
559
+ nan, nan, nan, nan, nan
560
+
561
+ >>> ncfile.close()
562
+ """
563
+ default = fillvalue if (datatype == "f8") else None
564
+ try:
565
+ ncfile.createVariable(name, datatype, dimensions=dimensions, fill_value=default)
566
+ ncfile[name].long_name = name
567
+ except BaseException:
568
+ objecttools.augment_excmessage(
569
+ f"While trying to add variable `{name}` with datatype `{datatype}` and "
570
+ f"dimensions `{dimensions}` to the NetCDF file `{get_filepath(ncfile)}`"
571
+ )
572
+
573
+
574
+ def query_variable(ncfile: netcdf4.Dataset, name: str) -> netcdf4.Variable:
575
+ """Return the variable with the given name from the given NetCDF file.
576
+
577
+ Essentially, |query_variable| only queries the variable via keyword access using
578
+ the NetCDF library but adds information to possible error messages:
579
+
580
+ >>> from hydpy.core.netcdftools import query_variable
581
+ >>> from hydpy import TestIO
582
+ >>> from hydpy.core.netcdftools import netcdf4
583
+ >>> with TestIO():
584
+ ... file_ = netcdf4.Dataset("model.nc", "w")
585
+ >>> query_variable(file_, "values")
586
+ Traceback (most recent call last):
587
+ ...
588
+ RuntimeError: NetCDF file `model.nc` does not contain variable `values`.
589
+
590
+ >>> from hydpy.core.netcdftools import create_variable
591
+ >>> create_variable(file_, "values", "f8", ())
592
+ >>> assert isinstance(query_variable(file_, "values"), netcdf4.Variable)
593
+
594
+ >>> file_.close()
595
+ """
596
+ try:
597
+ return ncfile[name]
598
+ except (IndexError, KeyError):
599
+ raise RuntimeError(
600
+ f"NetCDF file `{get_filepath(ncfile)}` does not contain variable `{name}`."
601
+ ) from None
602
+
603
+
604
+ def query_timegrid(
605
+ ncfile: netcdf4.Dataset, sequence: sequencetools.IOSequence
606
+ ) -> timetools.Timegrid:
607
+ """Return the |Timegrid| defined by the given NetCDF file.
608
+
609
+ |query_timegrid| relies on the `timereference` attribute of the given NetCDF file,
610
+ if available, and falls back to the global |Options.timestampleft| option when
611
+ necessary. The NetCDF files of the `HydPy-H-Lahn` example project (and all other
612
+ NetCDF files written by *HydPy*) include such information:
613
+
614
+ >>> from hydpy.core.testtools import prepare_full_example_2
615
+ >>> hp, pub, TestIO = prepare_full_example_2()
616
+ >>> from netCDF4 import Dataset
617
+ >>> filepath = "HydPy-H-Lahn/series/default/hland_96_input_p.nc"
618
+ >>> with TestIO(), Dataset(filepath) as ncfile:
619
+ ... ncfile.timereference
620
+ 'left interval boundary'
621
+
622
+ We start our examples considering the input sequence |hland_inputs.P|, which
623
+ handles precipitation sums. |query_timegrid| requires an instance of
624
+ |hland_inputs.P| to determine that each value of the time series of the NetCDF file
625
+ references a time interval and not a time point:
626
+
627
+ >>> p = hp.elements.land_dill_assl.model.sequences.inputs.p
628
+
629
+ If the file-specific setting does not conflict with the current value of
630
+ |Options.timestampleft|, |query_timegrid| works silently:
631
+
632
+ >>> from hydpy.core.netcdftools import query_timegrid
633
+ >>> with TestIO(), Dataset(filepath) as ncfile:
634
+ ... query_timegrid(ncfile, p)
635
+ Timegrid("1989-11-01 00:00:00",
636
+ "2021-01-01 00:00:00",
637
+ "1d")
638
+
639
+ If a file-specific setting is missing, |query_timegrid| applies the current
640
+ |Options.timestampleft| value:
641
+
642
+ >>> with TestIO(), Dataset(filepath, "r+") as ncfile:
643
+ ... del ncfile.timereference
644
+ >>> from hydpy.core.testtools import warn_later
645
+ >>> with TestIO(), Dataset(filepath) as ncfile:
646
+ ... query_timegrid(ncfile, p)
647
+ Timegrid("1989-11-01 00:00:00",
648
+ "2021-01-01 00:00:00",
649
+ "1d")
650
+
651
+ >>> with TestIO(), Dataset(filepath) as ncfile, pub.options.timestampleft(False):
652
+ ... query_timegrid(ncfile, p)
653
+ Timegrid("1989-10-31 00:00:00",
654
+ "2020-12-31 00:00:00",
655
+ "1d")
656
+
657
+ If the file-specific setting and |Options.timestampleft| conflict, |query_timegrid|
658
+ favours the file attribute and warns about this assumption:
659
+
660
+ >>> with TestIO(), Dataset(filepath, "r+") as ncfile:
661
+ ... ncfile.timereference = "right interval boundary"
662
+ >>> with TestIO(), warn_later(), Dataset(filepath) as ncfile:
663
+ ... query_timegrid(ncfile, p) # doctest: +ELLIPSIS
664
+ Timegrid("1989-10-31 00:00:00",
665
+ "2020-12-31 00:00:00",
666
+ "1d")
667
+ UserWarning: The `timereference` attribute (`right interval boundary`) of the \
668
+ NetCDF file `HydPy-H-Lahn/series/default/hland_96_input_p.nc` conflicts with the \
669
+ current value of the global `timestampleft` option (`True`). The file-specific \
670
+ information is prioritised.
671
+
672
+
673
+ State sequences like |hland_states.SM| handle data for specific time points instead
674
+ of time intervals. Their |IOSequence.series| vector contains the calculated values
675
+ for the end of each simulation step. Hence, without file-specific information,
676
+ |query_timegrid| ignores the |Options.timestampleft| option and follows the `right
677
+ interval boundary` convention:
678
+
679
+ >>> sm = hp.elements.land_dill_assl.model.sequences.states.sm
680
+ >>> with TestIO(), Dataset(filepath, "r+") as ncfile:
681
+ ... del ncfile.timereference
682
+ >>> with TestIO(), Dataset(filepath) as ncfile:
683
+ ... query_timegrid(ncfile, sm)
684
+ Timegrid("1989-10-31 00:00:00",
685
+ "2020-12-31 00:00:00",
686
+ "1d")
687
+
688
+ Add a `timereference` attribute with the value `current time` to explicitly include
689
+ this information in a NetCDF file:
690
+
691
+ >>> with TestIO(), Dataset(filepath, "r+") as ncfile:
692
+ ... ncfile.timereference = "current time"
693
+ >>> with TestIO(), Dataset(filepath) as ncfile:
694
+ ... query_timegrid(ncfile, sm)
695
+ Timegrid("1989-10-31 00:00:00",
696
+ "2020-12-31 00:00:00",
697
+ "1d")
698
+
699
+ |query_timegrid| raises special warnings when a NetCDF file's `timereference`
700
+ attribute conflicts with its judgement whether the contained data addresses time
701
+ intervals or time points:
702
+
703
+ >>> with TestIO(), warn_later(), Dataset(filepath) as ncfile:
704
+ ... query_timegrid(ncfile, p) # doctest: +ELLIPSIS
705
+ Timegrid("1989-10-31 00:00:00",
706
+ "2020-12-31 00:00:00",
707
+ "1d")
708
+ UserWarning: The `timereference` attribute (`current time`) of the NetCDF file \
709
+ `HydPy-H-Lahn/series/default/hland_96_input_p.nc` conflicts with the type of the \
710
+ relevant sequence (`P`). The file-specific information is prioritised.
711
+
712
+
713
+ >>> with TestIO(), Dataset(filepath, "r+") as ncfile:
714
+ ... ncfile.timereference = "left interval boundary"
715
+ >>> with TestIO(), warn_later(), Dataset(filepath) as ncfile:
716
+ ... query_timegrid(ncfile, sm) # doctest: +ELLIPSIS
717
+ Timegrid("1989-11-01 00:00:00",
718
+ "2021-01-01 00:00:00",
719
+ "1d")
720
+ UserWarning: The `timereference` attribute (`left interval boundary`) of the \
721
+ NetCDF file `HydPy-H-Lahn/series/default/hland_96_input_p.nc` conflicts with the type \
722
+ of the relevant sequence (`SM`). The file-specific information is prioritised.
723
+
724
+
725
+ |query_timegrid| also raises specific warnings for misstated `timereference`
726
+ attributes describing the different fallbacks for data related to time intervals
727
+ and time points:
728
+
729
+ >>> with TestIO(), Dataset(filepath, "r+") as ncfile:
730
+ ... ncfile.timereference = "wrong"
731
+ >>> with TestIO(), warn_later(), Dataset(filepath) as ncfile:
732
+ ... query_timegrid(ncfile, p) # doctest: +ELLIPSIS
733
+ Timegrid("1989-11-01 00:00:00",
734
+ "2021-01-01 00:00:00",
735
+ "1d")
736
+ UserWarning: The value of the `timereference` attribute (`wrong`) of the NetCDF \
737
+ file `HydPy-H-Lahn/series/default/hland_96_input_p.nc` is not among the accepted \
738
+ values (`left...`, `right...`, `current...`). Assuming `left interval boundary` \
739
+ according to the current value of the global `timestampleft` option.
740
+
741
+
742
+
743
+ >>> with TestIO(), warn_later(), Dataset(filepath) as ncfile:
744
+ ... query_timegrid(ncfile, sm) # doctest: +ELLIPSIS
745
+ Timegrid("1989-10-31 00:00:00",
746
+ "2020-12-31 00:00:00",
747
+ "1d")
748
+ UserWarning: The value of the `timereference` attribute (`wrong`) of the NetCDF \
749
+ file `...hland_96_input_p.nc` is not among the accepted values (`left...`, `right...`, \
750
+ `current...`). Assuming `current time` according to the type of the relevant \
751
+ sequence (`SM`).
752
+ """
753
+ currenttime = _timereference_currenttime(sequence)
754
+ opts = hydpy.pub.options
755
+ ref: str | None = getattr(ncfile, "timereference", None)
756
+ if ref is None:
757
+ left = bool(opts.timestampleft and not currenttime)
758
+ elif ref.startswith("left") or ref.startswith("right"):
759
+ left = ref.startswith("left")
760
+ if currenttime:
761
+ warnings.warn(
762
+ f"The `timereference` attribute (`{ncfile.timereference}`) of the "
763
+ f"NetCDF file `{ncfile.filepath()}` conflicts with the type of the "
764
+ f"relevant sequence (`{type(sequence).__name__}`). The file-specific "
765
+ f"information is prioritised."
766
+ )
767
+ elif left != opts.timestampleft:
768
+ warnings.warn(
769
+ f"The `timereference` attribute (`{ncfile.timereference}`) of the "
770
+ f"NetCDF file `{ncfile.filepath()}` conflicts with the current value "
771
+ f"of the global `timestampleft` option (`{bool(opts.timestampleft)}`). "
772
+ f" The file-specific information is prioritised."
773
+ )
774
+ elif ref.startswith("current"):
775
+ left = False
776
+ if not currenttime:
777
+ warnings.warn(
778
+ f"The `timereference` attribute (`{ncfile.timereference}`) of the "
779
+ f"NetCDF file `{ncfile.filepath()}` conflicts with the type of the "
780
+ f"relevant sequence (`{type(sequence).__name__}`). The file-specific "
781
+ f"information is prioritised."
782
+ )
783
+ elif currenttime:
784
+ left = False
785
+ warnings.warn(
786
+ f"The value of the `timereference` attribute (`{ncfile.timereference}`) "
787
+ f"of the NetCDF file `{ncfile.filepath()}` is not among the accepted "
788
+ f"values (`left...`, `right...`, `current...`). Assuming `current time` "
789
+ f"according to the type of the relevant sequence "
790
+ f"(`{type(sequence).__name__}`)."
791
+ )
792
+ else:
793
+ left = bool(opts.timestampleft)
794
+ text = "left" if left else "right"
795
+ warnings.warn(
796
+ f"The value of the `timereference` attribute (`{ncfile.timereference}`) "
797
+ f"of the NetCDF file `{ncfile.filepath()}` is not among the accepted "
798
+ f"values (`left...`, `right...`, `current...`). Assuming `{text} "
799
+ f"interval boundary` according to the current value of the global "
800
+ f"`timestampleft` option."
801
+ )
802
+ return _query_timegrid(ncfile=ncfile, left=left)
803
+
804
+
805
+ def _query_timegrid(ncfile: netcdf4.Dataset, left: bool) -> timetools.Timegrid:
806
+ with hydpy.pub.options.timestampleft(left):
807
+ timepoints = ncfile[varmapping["timepoints"]]
808
+ refdate = timetools.Date.from_cfunits(timepoints.units)
809
+ return timetools.Timegrid.from_timepoints(
810
+ timepoints=timepoints[:],
811
+ refdate=refdate,
812
+ unit=timepoints.units.strip().split()[0],
813
+ )
814
+
815
+
816
+ def query_array(ncfile: netcdf4.Dataset, name: str) -> NDArrayFloat:
817
+ """Return the data of the variable with the given name from the given NetCDF file.
818
+
819
+ The following example shows that |query_array| returns |numpy.nan| entries for
820
+ representing missing values even when the respective NetCDF variable defines a
821
+ different fill value:
822
+
823
+ >>> from hydpy import print_matrix, TestIO
824
+ >>> from hydpy.core import netcdftools
825
+ >>> from hydpy.core.netcdftools import netcdf4, create_dimension, create_variable
826
+ >>> import numpy
827
+ >>> with TestIO():
828
+ ... with netcdf4.Dataset("test.nc", "w") as ncfile:
829
+ ... create_dimension(ncfile, "time", 2)
830
+ ... create_dimension(ncfile, "stations", 3)
831
+ ... netcdftools.fillvalue = -999.0
832
+ ... create_variable(ncfile, "var", "f8", ("time", "stations"))
833
+ ... netcdftools.fillvalue = numpy.nan
834
+ ... ncfile = netcdf4.Dataset("test.nc", "r")
835
+ >>> from hydpy.core.netcdftools import query_variable, query_array
836
+ >>> print_matrix(query_variable(ncfile, "var")[:].data)
837
+ | -999.0, -999.0, -999.0 |
838
+ | -999.0, -999.0, -999.0 |
839
+ >>> print_matrix(query_array(ncfile, "var"))
840
+ | nan, nan, nan |
841
+ | nan, nan, nan |
842
+ >>> ncfile.close()
843
+
844
+ Usually, *HydPy* expects all data variables in NetCDF files to be 2-dimensional,
845
+ with time on the first and location on the second axis. However, |query_array|
846
+ allows for an exception for compatibility with `Delft-FEWS`_. When working with
847
+ ensembles, `Delft-FEWS`_ defines a third dimension called `realization` and puts it
848
+ between the first dimension (`time`) and the last dimension (`stations`). In our
849
+ experience, this additional dimension is always of length one, meaning we can
850
+ safely ignore it:
851
+
852
+ >>> with TestIO():
853
+ ... with netcdf4.Dataset("test.nc", "w") as ncfile:
854
+ ... create_dimension(ncfile, "time", 2)
855
+ ... create_dimension(ncfile, "realization", 1)
856
+ ... create_dimension(ncfile, "stations", 3)
857
+ ... var = create_variable(ncfile, "var", "f8",
858
+ ... ("time", "realization", "stations"))
859
+ ... ncfile["var"][:] = [[[1.1, 1.2, 1.3]], [[2.1, 2.2, 2.3]]]
860
+ ... ncfile = netcdf4.Dataset("test.nc", "r")
861
+ >>> var = query_variable(ncfile, "var")[:]
862
+ >>> var.shape
863
+ (2, 1, 3)
864
+ >>> query_array(ncfile, "var").shape
865
+ (2, 3)
866
+ >>> print_matrix(query_array(ncfile, "var"))
867
+ | 1.1, 1.2, 1.3 |
868
+ | 2.1, 2.2, 2.3 |
869
+ >>> ncfile.close()
870
+
871
+ |query_array| raises errors if dimensionality is smaller than two or larger than
872
+ three or if there are three dimensions and the length of the second dimension is
873
+ not one:
874
+
875
+ >>> with TestIO():
876
+ ... with netcdf4.Dataset("test.nc", "w") as ncfile:
877
+ ... create_dimension(ncfile, "time", 2)
878
+ ... var = create_variable(ncfile, "var", "f8", ("time",))
879
+ ... with netcdf4.Dataset("test.nc", "r") as ncfile:
880
+ ... query_array(ncfile, "var")
881
+ Traceback (most recent call last):
882
+ ...
883
+ RuntimeError: Variable `var` of NetCDF file `test.nc` must be 2-dimensional (or \
884
+ 3-dimensional with a length of one on the second axis) but has the shape `(2,)`.
885
+
886
+ >>> with TestIO():
887
+ ... with netcdf4.Dataset("test.nc", "w") as ncfile:
888
+ ... create_dimension(ncfile, "time", 2)
889
+ ... create_dimension(ncfile, "realization", 2)
890
+ ... create_dimension(ncfile, "stations", 3)
891
+ ... var = create_variable(ncfile, "var", "f8",
892
+ ... ("time", "realization", "stations"))
893
+ ... with netcdf4.Dataset("test.nc", "r") as ncfile:
894
+ ... query_array(ncfile, "var")
895
+ Traceback (most recent call last):
896
+ ...
897
+ RuntimeError: Variable `var` of NetCDF file `test.nc` must be 2-dimensional (or \
898
+ 3-dimensional with a length of one on the second axis) but has the shape `(2, 2, 3)`.
899
+
900
+ The skipping of the `realization` axis is very specific to `Delft-FEWS`_. To
901
+ prevent hiding problems when reading erroneous data from other sources,
902
+ |query_array| emits the following warning if the name of the second dimension is
903
+ not `realization`:
904
+
905
+ >>> from hydpy.core.testtools import warn_later
906
+ >>> with TestIO():
907
+ ... with netcdf4.Dataset("test.nc", "w") as ncfile:
908
+ ... create_dimension(ncfile, "time", 2)
909
+ ... create_dimension(ncfile, "realisation", 1)
910
+ ... create_dimension(ncfile, "stations", 3)
911
+ ... var = create_variable(ncfile, "var", "f8",
912
+ ... ("time", "realisation", "stations"))
913
+ ... with netcdf4.Dataset("test.nc", "r") as ncfile, warn_later():
914
+ ... print_matrix(query_array(ncfile, "var"))
915
+ | nan, nan, nan |
916
+ | nan, nan, nan |
917
+ UserWarning: Variable `var` of NetCDF file `test.nc` is 3-dimensional and the \
918
+ length of the second dimension is one, but its name is `realisation` instead of \
919
+ `realization`.
920
+ """
921
+ variable = query_variable(ncfile, name)
922
+ if _is_realisation(variable, ncfile):
923
+ maskedarray = variable[:, 0, :]
924
+ else:
925
+ maskedarray = variable[:]
926
+ fillvalue_ = getattr(variable, "_FillValue", numpy.nan)
927
+ if not numpy.isnan(fillvalue_):
928
+ maskedarray[maskedarray.mask] = numpy.nan
929
+ return cast(NDArrayFloat, maskedarray.data)
930
+
931
+
932
+ def _is_realisation(variable: netcdf4.Variable, ncfile: netcdf4.Dataset) -> bool:
933
+ if variable.ndim == 2:
934
+ return False
935
+ if (variable.ndim == 3) and (variable.shape[1] == 1):
936
+ if variable.dimensions[1] != "realization":
937
+ warnings.warn(
938
+ f"Variable `{variable.name}` of NetCDF file `{ncfile.filepath()}` is "
939
+ f"3-dimensional and the length of the second dimension is one, but "
940
+ f"its name is `{variable.dimensions[1]}` instead of `realization`."
941
+ )
942
+ return True
943
+ raise RuntimeError(
944
+ f"Variable `{variable.name}` of NetCDF file `{ncfile.filepath()}` must be "
945
+ f"2-dimensional (or 3-dimensional with a length of one on the second axis) "
946
+ f"but has the shape `{variable.shape}`."
947
+ )
948
+
949
+
950
+ def get_filepath(ncfile: netcdf4.Dataset) -> str:
951
+ """Return the path of the given NetCDF file.
952
+
953
+ >>> from hydpy import TestIO
954
+ >>> from hydpy.core.netcdftools import netcdf4
955
+ >>> from hydpy.core.netcdftools import get_filepath
956
+ >>> with TestIO():
957
+ ... with netcdf4.Dataset("test.nc", "w") as ncfile:
958
+ ... get_filepath(ncfile)
959
+ 'test.nc'
960
+ """
961
+ filepath = ncfile.filepath() if hasattr(ncfile, "filepath") else ncfile.filename
962
+ return cast(str, filepath)
963
+
964
+
965
+ def _timereference_currenttime(sequence: sequencetools.IOSequence) -> bool:
966
+ return isinstance(sequence, sequencetools.StateSequence)
967
+
968
+
969
+ class JITAccessInfo(NamedTuple):
970
+ """Helper class for structuring reading from or writing to a NetCDF file "just in
971
+ time" during a simulation run for a specific |NetCDFVariableFlat| object."""
972
+
973
+ ncvariable: netcdf4.Variable
974
+ """Variable for direct access to the relevant section of the NetCDF file."""
975
+ realisation: bool
976
+ """Flag that indicates if the relevant |JITAccessInfo.ncvariable| comes with an
977
+ additional `realization` dimension (explained in the documentation on function
978
+ |query_array|)"""
979
+ timedelta: int
980
+ """Difference between the relevant row of the NetCDF file and the current
981
+ simulation index (as defined by |Idx_Sim|)."""
982
+ columns: tuple[int, ...]
983
+ """Indices of the relevant columns of the NetCDF file correctly ordered with
984
+ respect to |JITAccessInfo.data|."""
985
+ data: NDArrayFloat
986
+ """Bridge to transfer data between the NetCDF file and the (cythonized)
987
+ hydrological models."""
988
+
989
+
990
+ class JITAccessHandler(NamedTuple):
991
+ """Handler used by the |SequenceManager| object available in module |pub| for
992
+ reading data from and/or writing data to NetCDF files at each step of a simulation
993
+ run."""
994
+
995
+ readers: tuple[JITAccessInfo, ...]
996
+ """All |JITAccessInfo| objects responsible for reading data during the simulation
997
+ run."""
998
+ writers: tuple[JITAccessInfo, ...]
999
+ """All |JITAccessInfo| objects responsible for writing data during the simulation
1000
+ run."""
1001
+
1002
+ def read_slices(self, idx: int) -> None:
1003
+ """Read the time slice of the current simulation step from each NetCDF file
1004
+ selected for reading."""
1005
+ for reader in self.readers:
1006
+ jdx = idx + reader.timedelta
1007
+ if reader.realisation:
1008
+ reader.data[:] = reader.ncvariable[jdx, 0, reader.columns]
1009
+ else:
1010
+ reader.data[:] = reader.ncvariable[jdx, reader.columns]
1011
+
1012
+ def write_slices(self, idx: int) -> None:
1013
+ """Write the time slice of the current simulation step from each NetCDF file
1014
+ selected for writing."""
1015
+ for writer in self.writers:
1016
+ jdx = idx + writer.timedelta
1017
+ if writer.realisation:
1018
+ writer.ncvariable[jdx, 0, writer.columns] = writer.data
1019
+ else:
1020
+ writer.ncvariable[jdx, writer.columns] = writer.data
1021
+
1022
+
1023
+ class Subdevice2Index:
1024
+ """Return type of method |NetCDFVariable.query_subdevice2index|."""
1025
+
1026
+ dict_: dict[str, int]
1027
+ name_sequence: str
1028
+ name_ncfile: str
1029
+
1030
+ def __init__(self, dict_: dict[str, int], name_ncfile: str) -> None:
1031
+ self.dict_ = dict_
1032
+ self.name_ncfile = name_ncfile
1033
+
1034
+ def get_index(self, name_subdevice: str) -> int:
1035
+ """Item access to the wrapped |dict| object with a specialised error message."""
1036
+ try:
1037
+ return self.dict_[name_subdevice]
1038
+ except KeyError:
1039
+ raise RuntimeError(
1040
+ f"No data for (sub)device `{name_subdevice}` is available in NetCDF "
1041
+ f"file `{self.name_ncfile}`."
1042
+ ) from None
1043
+
1044
+
1045
+ class NetCDFVariableInfo(NamedTuple):
1046
+ """Returned type of |NetCDFVariableFlatWriter| and |NetCDFVariableAggregated| when
1047
+ querying logged data via attribute access."""
1048
+
1049
+ sequence: sequencetools.IOSequence
1050
+ array: sequencetools.InfoArray | None
1051
+
1052
+
1053
+ class NetCDFVariable(abc.ABC):
1054
+ """Base class for handling single NetCDF variables of a single NetCDF file."""
1055
+
1056
+ filepath: str
1057
+ """Path to the relevant NetCDF file."""
1058
+
1059
+ def __init__(self, filepath: str) -> None:
1060
+ self.filepath = filepath
1061
+
1062
+ @property
1063
+ def name(self) -> str:
1064
+ """Name of the NetCDF file and the NetCDF variable containing the time series
1065
+ data."""
1066
+ return os.path.basename(self.filepath)[:-3]
1067
+
1068
+ @property
1069
+ @abc.abstractmethod
1070
+ def subdevicenames(self) -> tuple[str, ...]:
1071
+ """The names of all relevant (sub)devices."""
1072
+
1073
+ def query_subdevices(self, ncfile: netcdf4.Dataset) -> list[str]:
1074
+ """Query the names of the (sub)devices of the logged sequences from the given
1075
+ NetCDF file.
1076
+
1077
+ We apply method |NetCDFVariable.query_subdevices| on an empty NetCDF file. The
1078
+ error message shows that the method tries to query the (sub)device names:
1079
+
1080
+ >>> from hydpy import TestIO
1081
+ >>> from hydpy.core.netcdftools import netcdf4, NetCDFVariableFlatWriter
1082
+ >>> class Var(NetCDFVariableFlatWriter):
1083
+ ... pass
1084
+ >>> Var.subdevicenames = "element1", "element_2"
1085
+ >>> var = Var("filename.nc")
1086
+ >>> with TestIO():
1087
+ ... ncfile = netcdf4.Dataset("filename.nc", "w")
1088
+ >>> var.query_subdevices(ncfile)
1089
+ Traceback (most recent call last):
1090
+ ...
1091
+ RuntimeError: NetCDF file `filename.nc` does neither contain a variable named \
1092
+ `values_station_id` nor `station_id` for defining coordinate locations.
1093
+
1094
+ After inserting the (sub)device names, they can be queried and returned:
1095
+
1096
+ >>> var.insert_subdevices(ncfile)
1097
+ >>> Var("filename.nc").query_subdevices(ncfile)
1098
+ ['element1', 'element_2']
1099
+
1100
+ >>> ncfile.close()
1101
+ """
1102
+ tests = [f"{pref}{varmapping['subdevices']}" for pref in ("values_", "")]
1103
+ for subdevices in tests:
1104
+ try:
1105
+ chars = ncfile[subdevices][:]
1106
+ break
1107
+ except (IndexError, KeyError):
1108
+ pass
1109
+ else:
1110
+ raise RuntimeError(
1111
+ f"NetCDF file `{get_filepath(ncfile)}` does neither contain a "
1112
+ f"variable named `{tests[0]}` nor `{tests[1]}` for defining "
1113
+ f"coordinate locations."
1114
+ )
1115
+ return chars2str(chars.data)
1116
+
1117
+ def query_subdevice2index(self, ncfile: netcdf4.Dataset) -> Subdevice2Index:
1118
+ """Return a |Subdevice2Index| object that maps the (sub)device names to their
1119
+ position within the given NetCDF file.
1120
+
1121
+ Method |NetCDFVariable.query_subdevice2index| relies on
1122
+ |NetCDFVariable.query_subdevices|. The returned |Subdevice2Index| object
1123
+ remembers the NetCDF file from which the (sub)device names stem, allowing for
1124
+ clear error messages:
1125
+
1126
+ >>> from hydpy import TestIO
1127
+ >>> from hydpy.core.netcdftools import (
1128
+ ... netcdf4, NetCDFVariableFlatWriter, str2chars)
1129
+ >>> with TestIO():
1130
+ ... ncfile = netcdf4.Dataset("filename.nc", "w")
1131
+ >>> class Var(NetCDFVariableFlatWriter):
1132
+ ... pass
1133
+ >>> Var.subdevicenames = ["element3", "element1", "element1_1", "element2"]
1134
+ >>> var = Var("filename.nc")
1135
+ >>> var.insert_subdevices(ncfile)
1136
+ >>> subdevice2index = var.query_subdevice2index(ncfile)
1137
+ >>> subdevice2index.get_index("element1_1")
1138
+ 2
1139
+ >>> subdevice2index.get_index("element3")
1140
+ 0
1141
+ >>> subdevice2index.get_index("element5")
1142
+ Traceback (most recent call last):
1143
+ ...
1144
+ RuntimeError: No data for (sub)device `element5` is available in NetCDF file \
1145
+ `filename.nc`.
1146
+
1147
+ Additionally, |NetCDFVariable.query_subdevice2index| checks for duplicates:
1148
+
1149
+ >>> ncfile["station_id"][:] = str2chars(
1150
+ ... ["element3", "element1", "element1_1", "element1"])
1151
+ >>> var.query_subdevice2index(ncfile)
1152
+ Traceback (most recent call last):
1153
+ ...
1154
+ RuntimeError: The NetCDF file `filename.nc` contains duplicate (sub)device \
1155
+ names (the first found duplicate is `element1`).
1156
+
1157
+ >>> ncfile.close()
1158
+ """
1159
+ subdevices = self.query_subdevices(ncfile)
1160
+ self._test_duplicate_exists(ncfile, subdevices)
1161
+ subdev2index = {subdev: idx for (idx, subdev) in enumerate(subdevices)}
1162
+ return Subdevice2Index(subdev2index, get_filepath(ncfile))
1163
+
1164
+ @property
1165
+ @abc.abstractmethod
1166
+ def _anysequence(self) -> sequencetools.IOSequence: ...
1167
+
1168
+ @property
1169
+ @abc.abstractmethod
1170
+ def _descr2anysequence(self) -> dict[str, sequencetools.IOSequence]: ...
1171
+
1172
+ def _test_duplicate_exists(
1173
+ self, ncfile: netcdf4.Dataset, subdevices: Sequence[str]
1174
+ ) -> None:
1175
+ if len(subdevices) != len(set(subdevices)):
1176
+ for idx, name1 in enumerate(subdevices):
1177
+ for name2 in subdevices[idx + 1 :]:
1178
+ if name1 == name2:
1179
+ raise RuntimeError(
1180
+ f"The NetCDF file `{get_filepath(ncfile)}` contains "
1181
+ f"duplicate (sub)device names (the first found duplicate "
1182
+ f"is `{name1}`)."
1183
+ )
1184
+
1185
+ @staticmethod
1186
+ def _insert_timepoints(
1187
+ ncfile: netcdf4.Dataset, timepoints: NDArrayFloat, timeunit: str
1188
+ ) -> None:
1189
+ dim_name = dimmapping["nmb_timepoints"]
1190
+ var_name = varmapping["timepoints"]
1191
+ create_dimension(ncfile, dim_name, len(timepoints))
1192
+ create_variable(ncfile, var_name, "f8", (dim_name,))
1193
+ var_ = ncfile[var_name]
1194
+ var_[:] = timepoints
1195
+ var_.units = timeunit
1196
+ var_.standard_name = var_name
1197
+ var_.calendar = "standard"
1198
+ var_.delncattr("_FillValue")
1199
+
1200
+
1201
+ class NetCDFVariableFlat(NetCDFVariable, abc.ABC):
1202
+ """Base class for handling single "flat" NetCDF variables (which deal with
1203
+ unaggregated time series) of single NetCDF files.
1204
+
1205
+ The following examples describe the functioning of the subclasses
1206
+ |NetCDFVariableFlatReader| and |NetCDFVariableFlatWriter|, which serve to read and
1207
+ write data, respectively.
1208
+
1209
+ We prepare some devices handling some sequences by applying the function
1210
+ |prepare_io_example_1|. We limit our attention to the returned elements, which
1211
+ handle the more diverse sequences:
1212
+
1213
+ >>> from hydpy.core.testtools import prepare_io_example_1
1214
+ >>> nodes, (element1, element2, element3, element4) = prepare_io_example_1()
1215
+
1216
+ We define three |NetCDFVariableFlatWriter| instances with different
1217
+ dimensionalities structures and log the |lland_inputs.Nied| and |lland_fluxes.NKor|
1218
+ instances of the first two elements and the |hland_states.SP| instance of the
1219
+ fourth element:
1220
+
1221
+ >>> from hydpy.core.netcdftools import NetCDFVariableFlatWriter
1222
+ >>> var_nied = NetCDFVariableFlatWriter("nied.nc")
1223
+ >>> var_nkor = NetCDFVariableFlatWriter("nkor.nc")
1224
+ >>> var_sp = NetCDFVariableFlatWriter("sp.nc")
1225
+ >>> for element in (element1, element3):
1226
+ ... seqs = element.model.sequences
1227
+ ... var_nied.log(seqs.inputs.nied, seqs.inputs.nied.series)
1228
+ ... var_nkor.log(seqs.fluxes.nkor, seqs.fluxes.nkor.series)
1229
+ >>> sp = element4.model.sequences.states.sp
1230
+ >>> var_sp.log(sp, sp.series)
1231
+
1232
+ We further try to log the equally named "wind speed" sequences of the main model
1233
+ |lland_knauf| and the submodel |evap_aet_morsim|. As both models are handled by
1234
+ the same element, which defines the column name, their time series cannot be stored
1235
+ separately in the same NetCDF file. The |MixinVariableWriter.log| method defined
1236
+ by |MixinVariableWriter| checks for potential conflicts:
1237
+
1238
+ >>> var_windspeed = NetCDFVariableFlatWriter("windspeed.nc")
1239
+ >>> windspeed_l = element3.model.sequences.inputs.windspeed
1240
+ >>> var_windspeed.log(windspeed_l, windspeed_l.series)
1241
+ >>> windspeed_e = element3.model.aetmodel.sequences.inputs.windspeed
1242
+ >>> var_windspeed.log(windspeed_e, 2.0 * windspeed_e.series)
1243
+ Traceback (most recent call last):
1244
+ ...
1245
+ RuntimeError: When trying to log the time series of sequence `windspeed` of \
1246
+ element `element3` of model `evap_aet_morsim` for writing, the following error \
1247
+ occurred: Sequence `windspeed` of element `element3` of model `lland_knauf` is \
1248
+ already registered under the same column name(s) but with different time series data.
1249
+
1250
+ If the supplied time series are equal, there is no problem. So,
1251
+ |MixinVariableWriter.log| neither accepts the new sequence nor raises an error in
1252
+ such cases:
1253
+
1254
+ ToDo: Should we implement additional checks for just-in-time writing?
1255
+
1256
+ >>> assert var_windspeed.element3.sequence is windspeed_l
1257
+ >>> var_windspeed.log(windspeed_e, windspeed_e.series)
1258
+ >>> assert var_windspeed.element3.sequence is windspeed_l
1259
+
1260
+ We write the data of all logged sequences to separate NetCDF files:
1261
+
1262
+ >>> from hydpy import TestIO
1263
+ >>> with TestIO():
1264
+ ... var_nied.write()
1265
+ ... var_nkor.write()
1266
+ ... var_sp.write()
1267
+ ... var_windspeed.write()
1268
+
1269
+ We set all the values of the selected sequences to -777 and check that they differ
1270
+ from the original values available via the `testarray` attribute:
1271
+
1272
+ >>> nied = element1.model.sequences.inputs.nied
1273
+ >>> nkor = element3.model.sequences.fluxes.nkor
1274
+ >>> sp = element4.model.sequences.states.sp
1275
+ >>> import numpy
1276
+ >>> for seq in (nied, nkor, sp, windspeed_l, windspeed_e):
1277
+ ... seq.series = -777.0
1278
+ ... assert numpy.any(seq.series != seq.testarray)
1279
+
1280
+ Now, we prepare three |NetCDFVariableFlatReader| instances and log the same
1281
+ sequences as above, open the existing NetCDF file for reading, read its data, and
1282
+ confirm that it has been correctly passed to the test sequences:
1283
+
1284
+ >>> from hydpy.core.netcdftools import NetCDFVariableFlatReader
1285
+ >>> var_nied = NetCDFVariableFlatReader("nied.nc")
1286
+ >>> var_nkor = NetCDFVariableFlatReader("nkor.nc")
1287
+ >>> var_sp = NetCDFVariableFlatReader("sp.nc")
1288
+ >>> var_windspeed = NetCDFVariableFlatReader("windspeed.nc")
1289
+ >>> for element in (element1, element3):
1290
+ ... sequences = element.model.sequences
1291
+ ... var_nied.log(sequences.inputs.nied)
1292
+ ... var_nkor.log(sequences.fluxes.nkor)
1293
+ >>> var_sp.log(sp)
1294
+ >>> var_windspeed.log(windspeed_l)
1295
+ >>> var_windspeed.log(windspeed_e)
1296
+ >>> with TestIO():
1297
+ ... var_nied.read()
1298
+ ... var_nkor.read()
1299
+ ... var_sp.read()
1300
+ ... var_windspeed.read()
1301
+ >>> for seq in (nied, nkor, sp):
1302
+ ... assert numpy.all(seq.series == seq.testarray)
1303
+ >>> assert numpy.all(windspeed_l.series == windspeed_l.testarray)
1304
+ >>> assert numpy.all(windspeed_e.series == windspeed_l.testarray)
1305
+
1306
+ Trying to read data that is not stored properly results in error messages like
1307
+ the following:
1308
+
1309
+ >>> for element in (element1, element2, element3):
1310
+ ... element.model.sequences.inputs.nied.series = -777.0
1311
+ ... var_nied.log(element.model.sequences.inputs.nied)
1312
+ >>> with TestIO():
1313
+ ... var_nied.read()
1314
+ Traceback (most recent call last):
1315
+ ...
1316
+ RuntimeError: While trying to read data from NetCDF file `nied.nc`, the following \
1317
+ error occurred: No data for (sub)device `element2` is available in NetCDF file \
1318
+ `nied.nc`.
1319
+
1320
+ Note that method |NetCDFVariableFlatReader.read| does not abort the reading process
1321
+ when missing a time series. Instead, it sets the entries of the corresponding
1322
+ |IOSequence.series| array to |numpy.nan|, proceeds with the following sequences,
1323
+ and finally re-raises the first encountered exception:
1324
+
1325
+ >>> element1.model.sequences.inputs.nied.series
1326
+ InfoArray([0., 1., 2., 3.])
1327
+ >>> element2.model.sequences.inputs.nied.series
1328
+ InfoArray([nan, nan, nan, nan])
1329
+ >>> element3.model.sequences.inputs.nied.series
1330
+ InfoArray([ 8., 9., 10., 11.])
1331
+ """
1332
+
1333
+ @property
1334
+ def subdevicenames(self) -> tuple[str, ...]:
1335
+ """The names of the (sub)devices.
1336
+
1337
+ Property |NetCDFVariableFlat.subdevicenames| clarifies which column of NetCDF
1338
+ file contains the series of which (sub)device. For 0-dimensional series like
1339
+ |lland_inputs.Nied|, we require no subdivision. Hence, it returns the original
1340
+ device names:
1341
+
1342
+ >>> from hydpy.core.testtools import prepare_io_example_1
1343
+ >>> nodes, elements = prepare_io_example_1()
1344
+ >>> from hydpy.core.netcdftools import NetCDFVariableFlatReader
1345
+ >>> var = NetCDFVariableFlatReader("filename.nc")
1346
+ >>> for element in elements:
1347
+ ... if element.model.name.startswith("lland"):
1348
+ ... var.log(element.model.sequences.inputs.nied)
1349
+ >>> var.subdevicenames
1350
+ ('element1', 'element2', 'element3')
1351
+
1352
+ For 1-dimensional sequences like |lland_fluxes.NKor|, a suffix defines the
1353
+ index of the respective subdevice. For example, the third column of
1354
+ |NetCDFVariableFlatWriter.array| contains the series of the first hydrological
1355
+ response unit of the second element:
1356
+
1357
+ >>> var = NetCDFVariableFlatReader("filename.nc")
1358
+ >>> for element in elements:
1359
+ ... if element.model.name.startswith("lland"):
1360
+ ... var.log( element.model.sequences.fluxes.nkor)
1361
+ >>> var.subdevicenames
1362
+ ('element1_0', 'element2_0', 'element2_1', 'element3_0', 'element3_1', \
1363
+ 'element3_2')
1364
+
1365
+ 2-dimensional sequences like |hland_states.SP| require an additional suffix:
1366
+
1367
+ >>> var = NetCDFVariableFlatReader("filename.nc")
1368
+ >>> var.log(elements.element4.model.sequences.states.sp)
1369
+ >>> var.subdevicenames
1370
+ ('element4_0_0', 'element4_0_1', 'element4_0_2', 'element4_1_0', \
1371
+ 'element4_1_1', 'element4_1_2')
1372
+ """
1373
+ stats: collections.deque[str] = collections.deque()
1374
+ for devicename, seq in self._descr2anysequence.items():
1375
+ if seq.NDIM:
1376
+ temp = devicename + "_"
1377
+ for prod in self._product(seq.shape):
1378
+ stats.append(temp + "_".join(str(idx) for idx in prod))
1379
+ else:
1380
+ stats.append(devicename)
1381
+ return tuple(stats)
1382
+
1383
+ @property
1384
+ def shape(self) -> tuple[int, int]:
1385
+ """Required shape of the NetCDF variable.
1386
+
1387
+ For 0-dimensional sequences like |lland_inputs.Nied|, the first axis
1388
+ corresponds to the number of timesteps and the second axis to the number of
1389
+ devices:
1390
+
1391
+ >>> from hydpy.core.testtools import prepare_io_example_1
1392
+ >>> nodes, elements = prepare_io_example_1()
1393
+ >>> from hydpy.core.netcdftools import NetCDFVariableFlatReader
1394
+ >>> var = NetCDFVariableFlatReader("filename.nc")
1395
+ >>> for element in elements:
1396
+ ... if element.model.name.startswith("lland"):
1397
+ ... var.log(element.model.sequences.inputs.nied)
1398
+ >>> var.shape
1399
+ (4, 3)
1400
+
1401
+ For 1-dimensional sequences as |lland_fluxes.NKor|, the second axis corresponds
1402
+ to "subdevices". Here, these "subdevices" are hydrological response units of
1403
+ different elements. The model instances of the three elements define one, two,
1404
+ and three response units, respectively, making up a sum of six subdevices:
1405
+
1406
+ >>> var = NetCDFVariableFlatReader("filename.nc")
1407
+ >>> for element in elements:
1408
+ ... if element.model.name.startswith("lland"):
1409
+ ... var.log(element.model.sequences.fluxes.nkor)
1410
+ >>> var.shape
1411
+ (4, 6)
1412
+
1413
+ The above statements also hold for 2-dimensional sequences as
1414
+ |hland_states.SP|. In this specific case, each "subdevice" corresponds to a
1415
+ single snow class (one element times three zones times two snow classes makes
1416
+ six subdevices):
1417
+
1418
+ >>> var = NetCDFVariableFlatReader( "filename.nc")
1419
+ >>> var.log(elements.element4.model.sequences.states.sp)
1420
+ >>> var.shape
1421
+ (4, 6)
1422
+ """
1423
+ return (
1424
+ len(hydpy.pub.timegrids.init),
1425
+ sum(seq.numberofvalues for seq in self._descr2anysequence.values()),
1426
+ )
1427
+
1428
+ @staticmethod
1429
+ def _product(shape: Sequence[int]) -> Iterator[tuple[int, ...]]:
1430
+ """Should return all "subdevice index combinations" for sequences with
1431
+ arbitrary dimensions.
1432
+
1433
+ >>> from hydpy.core.netcdftools import NetCDFVariableFlat
1434
+ >>> _product = NetCDFVariableFlat.__dict__["_product"].__func__
1435
+ >>> for comb in _product([1, 2, 3]):
1436
+ ... print(comb)
1437
+ (0, 0, 0)
1438
+ (0, 0, 1)
1439
+ (0, 0, 2)
1440
+ (0, 1, 0)
1441
+ (0, 1, 1)
1442
+ (0, 1, 2)
1443
+ """
1444
+ return itertools.product(*(range(nmb) for nmb in shape))
1445
+
1446
+
1447
+ class NetCDFVariableFlatReader(NetCDFVariableFlat):
1448
+ """Concrete class for reading data of single "flat" NetCDF variables (which deal
1449
+ with unaggregated time series) from single NetCDF files.
1450
+
1451
+ For a general introduction to using |NetCDFVariableFlatReader|, see the
1452
+ documentation on base class |NetCDFVariableFlat|.
1453
+ """
1454
+
1455
+ _descr2sequences: dict[str, set[sequencetools.IOSequence]]
1456
+
1457
+ def __init__(self, filepath: str) -> None:
1458
+ super().__init__(filepath=filepath)
1459
+ self._descr2sequences = {}
1460
+
1461
+ @property
1462
+ def _anysequence(self) -> sequencetools.IOSequence:
1463
+ return next(iter(next(iter(self._descr2sequences.values()))))
1464
+
1465
+ @property
1466
+ def _descr2anysequence(self) -> dict[str, sequencetools.IOSequence]:
1467
+ return {d: next(iter(s)) for d, s in self._descr2sequences.items()}
1468
+
1469
+ def log(self, sequence: sequencetools.IOSequence) -> None:
1470
+ """Log the given |IOSequence| object for reading data.
1471
+
1472
+ The logged sequence is available via attribute access:
1473
+
1474
+ >>> from hydpy.core.netcdftools import NetCDFVariableFlatReader
1475
+ >>> class Var(NetCDFVariableFlatReader):
1476
+ ... pass
1477
+ >>> var = Var("filepath.nc")
1478
+ >>> from hydpy.core.testtools import prepare_io_example_1
1479
+ >>> nodes, elements = prepare_io_example_1()
1480
+ >>> nkor = elements.element1.model.sequences.fluxes.nkor
1481
+ >>> var.log(nkor)
1482
+ >>> assert "element1" in dir(var)
1483
+ >>> assert nkor in var.element1
1484
+ >>> assert "element2" not in dir(var)
1485
+ >>> var.element2 # doctest: +ELLIPSIS
1486
+ Traceback (most recent call last):
1487
+ ...
1488
+ AttributeError: The selected NetCDFVariableFlatReader object does neither \
1489
+ handle a sequence for the (sub)device `element2` nor define a member named \
1490
+ `element2`...
1491
+ """
1492
+ descr_device = sequence.descr_device
1493
+ if descr_device not in self._descr2sequences:
1494
+ self._descr2sequences[descr_device] = set()
1495
+ self._descr2sequences[descr_device].add(sequence)
1496
+
1497
+ def read(self) -> None:
1498
+ """Read the data from the relevant NetCDF file.
1499
+
1500
+ See the general documentation on class |NetCDFVariableFlat| for some examples.
1501
+ """
1502
+ try:
1503
+ with netcdf4.Dataset(self.filepath, "r") as ncfile:
1504
+ timegrid = query_timegrid(ncfile, self._anysequence)
1505
+ array = query_array(ncfile, self.name)
1506
+ idxs: tuple[Any] = (slice(None),)
1507
+ subdev2index = self.query_subdevice2index(ncfile)
1508
+ first_exception: RuntimeError | None = None
1509
+ for devicename, seqs in self._descr2sequences.items():
1510
+ for seq in seqs:
1511
+ try:
1512
+ if seq.NDIM:
1513
+ subshape = (array.shape[0],) + seq.shape
1514
+ subarray = numpy.empty(subshape)
1515
+ temp = devicename + "_"
1516
+ for prod in self._product(seq.shape):
1517
+ station = temp + "_".join(str(idx) for idx in prod)
1518
+ subarray[idxs + prod] = array[
1519
+ :, subdev2index.get_index(station)
1520
+ ]
1521
+ else:
1522
+ subarray = array[:, subdev2index.get_index(devicename)]
1523
+ series = seq.adjust_series(timegrid, subarray)
1524
+ seq.apply_adjusted_series(timegrid, series)
1525
+ except RuntimeError as current_exception:
1526
+ seq.series[:] = numpy.nan
1527
+ if first_exception is None:
1528
+ first_exception = current_exception
1529
+ if first_exception is not None:
1530
+ raise first_exception
1531
+ except BaseException:
1532
+ objecttools.augment_excmessage(
1533
+ f"While trying to read data from NetCDF file `{self.filepath}`"
1534
+ )
1535
+
1536
+ def __getattr__(self, name: str) -> set[sequencetools.IOSequence]:
1537
+ try:
1538
+ return self._descr2sequences[name]
1539
+ except KeyError:
1540
+ raise AttributeError(
1541
+ f"The selected NetCDFVariableFlatReader object does neither handle a "
1542
+ f"sequence for the (sub)device `{name}` nor define a member named "
1543
+ f"`{name}`."
1544
+ ) from None
1545
+
1546
+ def __dir__(self) -> list[str]:
1547
+ return cast(list[str], super().__dir__()) + list(self._descr2sequences.keys())
1548
+
1549
+
1550
+ class MixinVariableWriter(NetCDFVariable, abc.ABC):
1551
+ """Mixin class for |NetCDFVariableFlatWriter| and |NetCDFVariableAggregated|."""
1552
+
1553
+ _descr2sequence: dict[str, sequencetools.IOSequence]
1554
+ _descr2array: dict[str, sequencetools.InfoArray | None]
1555
+
1556
+ def __init__(self, filepath: str) -> None:
1557
+ assert isinstance(self, NetCDFVariable)
1558
+ super().__init__(filepath=filepath)
1559
+ self._descr2sequence = {}
1560
+ self._descr2array = {}
1561
+
1562
+ @property
1563
+ def _anysequence(self) -> sequencetools.IOSequence:
1564
+ return next(iter(self._descr2sequence.values()))
1565
+
1566
+ @property
1567
+ def _descr2anysequence(self) -> dict[str, sequencetools.IOSequence]:
1568
+ return self._descr2sequence
1569
+
1570
+ def insert_subdevices(self, ncfile: netcdf4.Dataset) -> None:
1571
+ """Insert a variable of the names of the (sub)devices of the logged sequences
1572
+ into the given NetCDF file.
1573
+
1574
+ We prepare a |NetCDFVariableFlatWriter| subclass with fixed (sub)device names:
1575
+
1576
+ >>> from hydpy import TestIO
1577
+ >>> from hydpy.core.netcdftools import NetCDFVariableFlatWriter, chars2str
1578
+ >>> from hydpy.core.netcdftools import netcdf4
1579
+ >>> class Var(NetCDFVariableFlatWriter):
1580
+ ... pass
1581
+ >>> Var.subdevicenames = "element1", "element_2", "element_ß"
1582
+
1583
+ The first dimension of the added variable corresponds to the number of
1584
+ (sub)devices, and the second dimension to the number of characters of the
1585
+ longest (sub)device name:
1586
+
1587
+ >>> var = Var("filename.nc")
1588
+ >>> with TestIO():
1589
+ ... ncfile = netcdf4.Dataset("filename.nc", "w")
1590
+ >>> var.insert_subdevices(ncfile)
1591
+ >>> ncfile["station_id"].dimensions
1592
+ ('stations', 'char_leng_name')
1593
+ >>> ncfile["station_id"].shape
1594
+ (3, 10)
1595
+ >>> chars2str(ncfile["station_id"][:].data)
1596
+ ['element1', 'element_2', 'element_ß']
1597
+ >>> ncfile.close()
1598
+ """
1599
+ nmb_subdevices = dimmapping["nmb_subdevices"]
1600
+ nmb_characters = dimmapping["nmb_characters"]
1601
+ subdevices = varmapping["subdevices"]
1602
+ statchars = str2chars(self.subdevicenames)
1603
+ create_dimension(ncfile, nmb_subdevices, statchars.shape[0])
1604
+ create_dimension(ncfile, nmb_characters, statchars.shape[1])
1605
+ create_variable(ncfile, subdevices, "S1", (nmb_subdevices, nmb_characters))
1606
+ ncfile[subdevices][:, :] = statchars
1607
+
1608
+ def log(
1609
+ self,
1610
+ sequence: sequencetools.IOSequence,
1611
+ infoarray: sequencetools.InfoArray | None = None,
1612
+ ) -> None:
1613
+ """Log the given |IOSequence| object for writing data.
1614
+
1615
+ When writing data "in one step", the second argument must be an |InfoArray|.
1616
+ Pass |None| when using |NetCDFVariableFlatWriter| to write data "just in time".
1617
+ The `infoarray` argument allows for passing alternative data that replaces the
1618
+ original series of the |IOSequence| object.
1619
+
1620
+ The logged time series data is available via attribute access:
1621
+
1622
+ >>> from hydpy.core.netcdftools import NetCDFVariableFlatWriter
1623
+ >>> class Var(NetCDFVariableFlatWriter):
1624
+ ... pass
1625
+ >>> var = Var("filepath.nc")
1626
+ >>> from hydpy.core.testtools import prepare_io_example_1
1627
+ >>> nodes, elements = prepare_io_example_1()
1628
+ >>> nkor = elements.element1.model.sequences.fluxes.nkor
1629
+ >>> var.log(nkor, nkor.series)
1630
+ >>> assert "element1" in dir(var)
1631
+ >>> assert var.element1.sequence is nkor
1632
+ >>> import numpy
1633
+ >>> assert numpy.all(var.element1.array == nkor.series)
1634
+ >>> assert "element2" not in dir(var)
1635
+ >>> var.element2 # doctest: +ELLIPSIS
1636
+ Traceback (most recent call last):
1637
+ ...
1638
+ AttributeError: The selected NetCDFVariable object does neither handle time \
1639
+ series data for the (sub)device `element2` nor define a member named `element2`...
1640
+ """
1641
+
1642
+ def _dp(seq: sequencetools.IOSequence) -> str:
1643
+ return (
1644
+ f"{objecttools.devicephrase(sequence)} of model "
1645
+ f"`{seq.subseqs.seqs.model.name}`"
1646
+ )
1647
+
1648
+ descr_device = sequence.descr_device
1649
+ old_sequence = self._descr2sequence.get(descr_device)
1650
+ if old_sequence is None:
1651
+ self._descr2sequence[descr_device] = sequence
1652
+ self._descr2array[descr_device] = infoarray
1653
+ elif old_sequence is not sequence:
1654
+ old_array = self._descr2array[descr_device]
1655
+ if (
1656
+ (infoarray is not None)
1657
+ and (old_array is not None)
1658
+ and not numpy.array_equal(old_array, infoarray, equal_nan=True)
1659
+ ):
1660
+ raise RuntimeError(
1661
+ f"When trying to log the time series of sequence {_dp(sequence)} "
1662
+ f"for writing, the following error occurred: Sequence "
1663
+ f"{_dp(old_sequence)} is already registered under the same column "
1664
+ f"name(s) but with different time series data."
1665
+ )
1666
+
1667
+ @property
1668
+ @abc.abstractmethod
1669
+ def array(self) -> NDArrayFloat:
1670
+ """A |numpy.ndarray| containing the values of all logged sequences."""
1671
+
1672
+ def write(self) -> None:
1673
+ """Write the logged data to a new NetCDF file.
1674
+
1675
+ See the general documentation on classes |NetCDFVariableFlatWriter| and
1676
+ |NetCDFVariableAggregated| for some examples.
1677
+ """
1678
+ with netcdf4.Dataset(self.filepath, "w") as ncfile:
1679
+ now = time.ctime(time.time())
1680
+ ncfile.history = f"Created {now} by HydPy {hydpy.__version__}"
1681
+ ncfile.sequence = (
1682
+ f"{os.path.split(self.filepath)[-1][:-3]} "
1683
+ f"(naming convention: {hydpy.pub.sequencemanager.convention})"
1684
+ )
1685
+ ncfile.Conventions = "CF-1.8"
1686
+ init = hydpy.pub.timegrids.init
1687
+ timeunit = init.firstdate.to_cfunits("hours")
1688
+ opts = hydpy.pub.options
1689
+ if _timereference_currenttime(self._anysequence):
1690
+ with opts.timestampleft(False):
1691
+ timepoints = init.to_timepoints("hours")
1692
+ ncfile.timereference = "current time"
1693
+ else:
1694
+ timepoints = init.to_timepoints("hours")
1695
+ ncfile.timereference = (
1696
+ f"{'left' if opts.timestampleft else 'right'} interval boundary"
1697
+ )
1698
+ self._insert_timepoints(ncfile, timepoints, timeunit)
1699
+ self.insert_subdevices(ncfile)
1700
+ dimensions = dimmapping["nmb_timepoints"], dimmapping["nmb_subdevices"]
1701
+ create_variable(ncfile, self.name, "f8", dimensions)
1702
+ ncfile[self.name][:] = self.array
1703
+
1704
+ def __getattr__(self, name: str) -> NetCDFVariableInfo:
1705
+ try:
1706
+ return NetCDFVariableInfo(
1707
+ self._descr2sequence[name], self._descr2array[name]
1708
+ )
1709
+ except KeyError:
1710
+ raise AttributeError(
1711
+ f"The selected NetCDFVariable object does neither handle time series "
1712
+ f"data for the (sub)device `{name}` nor define a member named "
1713
+ f"`{name}`."
1714
+ ) from None
1715
+
1716
+ def __dir__(self) -> list[str]:
1717
+ return cast(list[str], super().__dir__()) + list(self._descr2sequence.keys())
1718
+
1719
+
1720
+ class NetCDFVariableFlatWriter(MixinVariableWriter, NetCDFVariableFlat):
1721
+ """Concrete class for writing data from single "flat" NetCDF variables (which deal
1722
+ with unaggregated time series) to single NetCDF files.
1723
+
1724
+ For a general introduction to using |NetCDFVariableFlatWriter|, see the
1725
+ documentation on base class |NetCDFVariableFlat|.
1726
+ """
1727
+
1728
+ @property
1729
+ def array(self) -> NDArrayFloat:
1730
+ """The time series data of all logged |IOSequence| objects in one single
1731
+ |numpy.ndarray| object.
1732
+
1733
+ The documentation on |NetCDFVariableFlat.shape| explains the structure of
1734
+ |NetCDFVariableFlatWriter.array|. The first example confirms that the first
1735
+ axis corresponds to time while the second corresponds to the location:
1736
+
1737
+ >>> from hydpy.core.testtools import prepare_io_example_1
1738
+ >>> nodes, elements = prepare_io_example_1()
1739
+ >>> from hydpy.core.netcdftools import NetCDFVariableFlatWriter
1740
+ >>> var = NetCDFVariableFlatWriter("filename.nc")
1741
+ >>> for element in elements:
1742
+ ... if element.model.name.startswith("lland"):
1743
+ ... nied1 = element.model.sequences.inputs.nied
1744
+ ... var.log(nied1, nied1.series)
1745
+ >>> from hydpy import print_matrix
1746
+ >>> print_matrix(var.array)
1747
+ | 0.0, 4.0, 8.0 |
1748
+ | 1.0, 5.0, 9.0 |
1749
+ | 2.0, 6.0, 10.0 |
1750
+ | 3.0, 7.0, 11.0 |
1751
+
1752
+ The flattening of higher-dimensional sequences spreads the time series of
1753
+ individual "subdevices" over the array's columns. For the 1-dimensional
1754
+ sequence |lland_fluxes.NKor|, we find the time series of both zones of the
1755
+ second element in columns two and three:
1756
+
1757
+ >>> var = NetCDFVariableFlatWriter("filename.nc")
1758
+ >>> for element in elements:
1759
+ ... if element.model.name.startswith("lland"):
1760
+ ... nkor = element.model.sequences.fluxes.nkor
1761
+ ... var.log(nkor, nkor.series)
1762
+ >>> print_matrix(var.array[:, 1:3])
1763
+ | 16.0, 17.0 |
1764
+ | 18.0, 19.0 |
1765
+ | 20.0, 21.0 |
1766
+ | 22.0, 23.0 |
1767
+
1768
+ The above statements also hold for 2-dimensional sequences like
1769
+ |hland_states.SP|. In this specific case, each column contains the time series
1770
+ of a single snow class:
1771
+
1772
+ >>> var = NetCDFVariableFlatWriter("filename.nc")
1773
+ >>> sp = elements.element4.model.sequences.states.sp
1774
+ >>> var.log(sp, sp.series)
1775
+ >>> print_matrix(var.array)
1776
+ | 68.0, 69.0, 70.0, 71.0, 72.0, 73.0 |
1777
+ | 74.0, 75.0, 76.0, 77.0, 78.0, 79.0 |
1778
+ | 80.0, 81.0, 82.0, 83.0, 84.0, 85.0 |
1779
+ | 86.0, 87.0, 88.0, 89.0, 90.0, 91.0 |
1780
+ """
1781
+ array = numpy.full(self.shape, fillvalue, dtype=config.NP_FLOAT)
1782
+ idx0 = 0
1783
+ idxs: list[Any] = [slice(None)]
1784
+ for seq, subarray in zip(
1785
+ self._descr2sequence.values(), self._descr2array.values()
1786
+ ):
1787
+ for prod in self._product(seq.shape):
1788
+ if subarray is not None:
1789
+ subsubarray = subarray[tuple(idxs + list(prod))]
1790
+ array[:, idx0] = subsubarray
1791
+ idx0 += 1
1792
+ return array
1793
+
1794
+
1795
+ class NetCDFVariableAggregated(MixinVariableWriter, NetCDFVariable):
1796
+ """Concrete class for writing data from single "aggregated" NetCDF variables (which
1797
+ deal with aggregated time series) to single NetCDF files.
1798
+
1799
+ Class |NetCDFVariableAggregated| works very similarly to class
1800
+ |NetCDFVariableFlatWriter|. The following is a selection of the more thoroughly
1801
+ explained examples of the documentation on class |NetCDFVariableFlat|:
1802
+
1803
+ >>> from hydpy.core.testtools import prepare_io_example_1
1804
+ >>> nodes, (element1, element2, element3, element4) = prepare_io_example_1()
1805
+ >>> from hydpy.core.netcdftools import NetCDFVariableAggregated
1806
+ >>> var_nied = NetCDFVariableAggregated("nied.nc")
1807
+ >>> var_nkor = NetCDFVariableAggregated("nkor.nc")
1808
+ >>> var_sp = NetCDFVariableAggregated("sp.nc")
1809
+ >>> for element in (element1, element2):
1810
+ ... nied = element.model.sequences.inputs.nied
1811
+ ... var_nied.log(nied, nied.average_series())
1812
+ ... nkor = element.model.sequences.fluxes.nkor
1813
+ ... var_nkor.log(nkor, nkor.average_series())
1814
+ >>> sp = element4.model.sequences.states.sp
1815
+ >>> var_sp.log(sp, sp.average_series())
1816
+ >>> from hydpy import pub, TestIO
1817
+ >>> with TestIO():
1818
+ ... var_nied.write()
1819
+ ... var_nkor.write()
1820
+ ... var_sp.write()
1821
+
1822
+ As |NetCDFVariableAggregated| provides no reading functionality, we show that the
1823
+ aggregated values are readily available using the external NetCDF4 library:
1824
+
1825
+ >>> import numpy
1826
+ >>> from hydpy import print_matrix
1827
+ >>> with TestIO(), netcdf4.Dataset("nied.nc", "r") as ncfile:
1828
+ ... print_matrix(numpy.array(ncfile["nied"][:]))
1829
+ | 0.0, 4.0 |
1830
+ | 1.0, 5.0 |
1831
+ | 2.0, 6.0 |
1832
+ | 3.0, 7.0 |
1833
+
1834
+ >>> with TestIO(), netcdf4.Dataset("nkor.nc", "r") as ncfile:
1835
+ ... print_matrix(numpy.array(ncfile["nkor"][:]))
1836
+ | 12.0, 16.5 |
1837
+ | 13.0, 18.5 |
1838
+ | 14.0, 20.5 |
1839
+ | 15.0, 22.5 |
1840
+
1841
+ >>> with TestIO(), netcdf4.Dataset("sp.nc", "r") as ncfile:
1842
+ ... print_matrix(numpy.array(ncfile["sp"][:]))
1843
+ | 70.5 |
1844
+ | 76.5 |
1845
+ | 82.5 |
1846
+ | 88.5 |
1847
+ """
1848
+
1849
+ @property
1850
+ def shape(self) -> tuple[int, int]:
1851
+ """Required shape of |NetCDFVariableAggregated.array|.
1852
+
1853
+ The first axis corresponds to the number of timesteps and the second axis to
1854
+ the number of devices. We show this for the 1-dimensional input sequence
1855
+ |lland_fluxes.NKor|:
1856
+
1857
+ >>> from hydpy.core.testtools import prepare_io_example_1
1858
+ >>> nodes, elements = prepare_io_example_1()
1859
+ >>> from hydpy.core.netcdftools import NetCDFVariableAggregated
1860
+ >>> var = NetCDFVariableAggregated("filename.nc")
1861
+ >>> for element in elements:
1862
+ ... if element.model.name.startswith("lland"):
1863
+ ... var.log(element.model.sequences.fluxes.nkor, None)
1864
+ >>> var.shape
1865
+ (4, 3)
1866
+
1867
+ There is no difference for 2-dimensional sequences as aggregating their time
1868
+ series also results in 1-dimensional data:
1869
+
1870
+ >>> var = NetCDFVariableAggregated("filename.nc")
1871
+ >>> var.log(elements.element4.model.sequences.states.sp, None)
1872
+ >>> var.shape
1873
+ (4, 1)
1874
+ """
1875
+ return len(hydpy.pub.timegrids.init), len(self._descr2sequence)
1876
+
1877
+ @property
1878
+ def array(self) -> NDArrayFloat:
1879
+ """The aggregated time series data of all logged |IOSequence| objects in a
1880
+ single |numpy.ndarray| object.
1881
+
1882
+ The documentation on |NetCDFVariableAggregated.shape| explains the structure of
1883
+ |NetCDFVariableAggregated.array|. This first example confirms that the first
1884
+ axis corresponds to time while the second corresponds to the location:
1885
+
1886
+ >>> from hydpy.core.testtools import prepare_io_example_1
1887
+ >>> nodes, elements = prepare_io_example_1()
1888
+ >>> from hydpy.core.netcdftools import NetCDFVariableAggregated
1889
+ >>> var = NetCDFVariableAggregated("filename.nc")
1890
+ >>> for element in elements:
1891
+ ... if element.model.name.startswith("lland"):
1892
+ ... nkor = element.model.sequences.fluxes.nkor
1893
+ ... var.log(nkor, nkor.average_series())
1894
+ >>> from hydpy import print_matrix
1895
+ >>> print_matrix(var.array)
1896
+ | 12.0, 16.5, 25.0 |
1897
+ | 13.0, 18.5, 28.0 |
1898
+ | 14.0, 20.5, 31.0 |
1899
+ | 15.0, 22.5, 34.0 |
1900
+
1901
+ There is no difference for 2-dimensional sequences as aggregating their time
1902
+ series also results in 1-dimensional data:
1903
+
1904
+ >>> var = NetCDFVariableAggregated("filename.nc")
1905
+ >>> sp = elements.element4.model.sequences.states.sp
1906
+ >>> var.log(sp, sp.average_series())
1907
+ >>> print_matrix(var.array)
1908
+ | 70.5 |
1909
+ | 76.5 |
1910
+ | 82.5 |
1911
+ | 88.5 |
1912
+ """
1913
+ array = numpy.full(self.shape, fillvalue, dtype=config.NP_FLOAT)
1914
+ for idx, subarray in enumerate(self._descr2array.values()):
1915
+ if subarray is not None:
1916
+ array[:, idx] = subarray
1917
+ return array
1918
+
1919
+ @property
1920
+ def subdevicenames(self) -> tuple[str, ...]:
1921
+ """The names of all relevant devices."""
1922
+ return tuple(self._descr2sequence.keys())
1923
+
1924
+
1925
+ class NetCDFInterfaceBase(Generic[TypeNetCDFVariable]):
1926
+ """Base class for interfaces between |SequenceManager| and multiple NetCDF files.
1927
+
1928
+ The core task of all concrete |NetCDFInterfaceBase| subclasses is to distribute
1929
+ different |IOSequence| objects on multiple instances of the concrete subclasses of
1930
+ |NetCDFVariable|. The following examples describe the functioning of the
1931
+ subclasses |NetCDFInterfaceReader| and |NetCDFInterfaceWriter|, which serve to read
1932
+ and write data "in one step", respectively.
1933
+
1934
+ We prepare a |SequenceManager| object and some devices handling different sequences
1935
+ by applying function |prepare_io_example_1|:
1936
+
1937
+ >>> from hydpy.core.testtools import prepare_io_example_1
1938
+ >>> nodes, elements = prepare_io_example_1()
1939
+
1940
+ We collect all sequences used in the following examples except |lland_fluxes.NKor|
1941
+ of element `element1`, which we reserve for special tests:
1942
+
1943
+ >>> sequences = []
1944
+ >>> for node in nodes:
1945
+ ... sequences.append(node.sequences.sim)
1946
+ >>> for element in elements:
1947
+ ... if element.model.name == "hland_96":
1948
+ ... sequences.append(element.model.sequences.states.sp)
1949
+ ... else:
1950
+ ... sequences.append(element.model.sequences.inputs.nied)
1951
+ ... if element.name != "element1":
1952
+ ... sequences.append(element.model.sequences.fluxes.nkor)
1953
+
1954
+ We prepare a |NetCDFInterfaceWriter| object and log and write all test sequences
1955
+ except |lland_fluxes.NKor| of element `element1`. |NetCDFInterfaceWriter|
1956
+ initialises one |NetCDFVariableFlatWriter| and one |NetCDFVariableAggregated|
1957
+ object for each |IOSequence| subtype:
1958
+
1959
+ >>> from hydpy.core.netcdftools import NetCDFInterfaceWriter
1960
+ >>> writer = NetCDFInterfaceWriter()
1961
+ >>> len(writer)
1962
+ 0
1963
+
1964
+ >>> from hydpy import pub, TestIO
1965
+ >>> pub.sequencemanager.filetype = "nc"
1966
+ >>> with TestIO():
1967
+ ... for sequence in sequences:
1968
+ ... _ = writer.log(sequence, sequence.series)
1969
+ ... with pub.sequencemanager.aggregation("mean"):
1970
+ ... _ = writer.log(sequence, sequence.average_series())
1971
+ >>> len(writer)
1972
+ 14
1973
+
1974
+ We change the relevant directory before logging the reserved sequence.
1975
+ |NetCDFInterfaceWriter| initialises two new |NetCDFVariable| objects, despite
1976
+ other |NetCDFVariable| objects related to the same sequence type already being
1977
+ available:
1978
+
1979
+ >>> nkor = elements.element1.model.sequences.fluxes.nkor
1980
+ >>> with TestIO():
1981
+ ... pub.sequencemanager.currentdir = "test"
1982
+ ... _ = writer.log(nkor, nkor.series)
1983
+ ... with pub.sequencemanager.aggregation("mean"):
1984
+ ... _ = writer.log(nkor, nkor.average_series())
1985
+ >>> len(writer)
1986
+ 16
1987
+
1988
+ You can query all relevant folder names and filenames via properties
1989
+ |NetCDFInterfaceBase.foldernames| and |NetCDFInterfaceBase.filenames|:
1990
+
1991
+ >>> from hydpy import print_vector
1992
+ >>> print_vector(writer.foldernames)
1993
+ default, test
1994
+ >>> print_vector(writer.filenames)
1995
+ hland_96_state_sp, hland_96_state_sp_mean, lland_dd_flux_nkor,
1996
+ lland_dd_flux_nkor_mean, lland_dd_input_nied,
1997
+ lland_dd_input_nied_mean, lland_knauf_flux_nkor,
1998
+ lland_knauf_flux_nkor_mean, lland_knauf_input_nied,
1999
+ lland_knauf_input_nied_mean, sim_q, sim_q_mean, sim_t, sim_t_mean
2000
+
2001
+ |NetCDFInterfaceWriter| provides attribute access to its |NetCDFVariable|
2002
+ instances, both via their filenames and the combination of their folder names and
2003
+ filenames:
2004
+
2005
+ >>> assert writer.sim_q is writer.default_sim_q
2006
+ >>> print_vector(sorted(set(dir(writer)) - set(object.__dir__(writer))))
2007
+ default_hland_96_state_sp, default_hland_96_state_sp_mean,
2008
+ default_lland_dd_flux_nkor, default_lland_dd_flux_nkor_mean,
2009
+ default_lland_dd_input_nied, default_lland_dd_input_nied_mean,
2010
+ default_lland_knauf_flux_nkor, default_lland_knauf_flux_nkor_mean,
2011
+ default_lland_knauf_input_nied, default_lland_knauf_input_nied_mean,
2012
+ default_sim_q, default_sim_q_mean, default_sim_t, default_sim_t_mean,
2013
+ hland_96_state_sp, hland_96_state_sp_mean, lland_dd_input_nied,
2014
+ lland_dd_input_nied_mean, lland_knauf_flux_nkor,
2015
+ lland_knauf_flux_nkor_mean, lland_knauf_input_nied,
2016
+ lland_knauf_input_nied_mean, sim_q, sim_q_mean, sim_t, sim_t_mean,
2017
+ test_lland_dd_flux_nkor, test_lland_dd_flux_nkor_mean
2018
+
2019
+ If multiple NetCDF files have the same name, you must prefix the relevant folder
2020
+ name:
2021
+
2022
+ >>> writer.lland_dd_flux_nkor
2023
+ Traceback (most recent call last):
2024
+ ...
2025
+ RuntimeError: The current NetCDFInterface object handles multiple NetCDF files \
2026
+ named `lland_dd_flux_nkor`. Please be more specific.
2027
+ >>> assert hasattr(writer, "default_lland_dd_flux_nkor")
2028
+
2029
+ |NetCDFInterfaceWriter| raises the following error for completely wrong attribute
2030
+ names:
2031
+
2032
+ >>> writer.lland_dd
2033
+ Traceback (most recent call last):
2034
+ ...
2035
+ AttributeError: The current NetCDFInterface object neither handles a NetCDF file \
2036
+ named `lland_dd` nor does it define a member named `lland_dd`.
2037
+
2038
+ We write all NetCDF files into the `default` folder of the testing directory,
2039
+ defined by |prepare_io_example_1|:
2040
+
2041
+ >>> from hydpy import TestIO
2042
+ >>> with TestIO():
2043
+ ... writer.write()
2044
+
2045
+ We define a shorter initialisation period and re-activate the time series of the
2046
+ test sequences:
2047
+
2048
+ >>> from hydpy import pub
2049
+ >>> pub.timegrids = "02.01.2000", "04.01.2000", "1d"
2050
+ >>> for sequence in sequences:
2051
+ ... sequence.prepare_series(allocate_ram=False)
2052
+ ... sequence.prepare_series(allocate_ram=True)
2053
+ >>> nkor.prepare_series(allocate_ram=False)
2054
+ >>> nkor.prepare_series(allocate_ram=True)
2055
+
2056
+ We now initialise an object of class |NetCDFInterfaceReader|, log all test
2057
+ sequences, and read the test data of the defined subperiod:
2058
+
2059
+ >>> from hydpy.core.netcdftools import NetCDFInterfaceReader
2060
+ >>> reader = NetCDFInterfaceReader()
2061
+ >>> with TestIO():
2062
+ ... _ = reader.log(nkor)
2063
+ ... pub.sequencemanager.currentdir = "default"
2064
+ ... for sequence in sequences:
2065
+ ... _ = reader.log(sequence)
2066
+ ... reader.read()
2067
+ >>> nodes.node1.sequences.sim.series
2068
+ InfoArray([61., 62.])
2069
+ >>> elements.element2.model.sequences.fluxes.nkor.series
2070
+ InfoArray([[18., 19.],
2071
+ [20., 21.]])
2072
+ >>> elements.element4.model.sequences.states.sp.series
2073
+ InfoArray([[[74., 75., 76.],
2074
+ [77., 78., 79.]],
2075
+ <BLANKLINE>
2076
+ [[80., 81., 82.],
2077
+ [83., 84., 85.]]])
2078
+ """
2079
+
2080
+ _dir2file2var: dict[str, dict[str, TypeNetCDFVariable]]
2081
+
2082
+ def __init__(self) -> None:
2083
+ self._dir2file2var = {}
2084
+
2085
+ def _get_dir2file_stem(
2086
+ self, sequence: sequencetools.IOSequence
2087
+ ) -> tuple[dict[str, TypeNetCDFVariable], str]:
2088
+ dirpath = sequence.dirpath
2089
+ try:
2090
+ file2var = self._dir2file2var[dirpath]
2091
+ except KeyError:
2092
+ file2var = {}
2093
+ self._dir2file2var[dirpath] = file2var
2094
+ stem = sequence.filename.rsplit(".")[0]
2095
+ return file2var, stem
2096
+
2097
+ @staticmethod
2098
+ def _yield_disksequences(
2099
+ deviceorder: Iterable[devicetools.Node | devicetools.Element],
2100
+ ) -> Iterator[sequencetools.IOSequence]:
2101
+ for device in deviceorder:
2102
+ if isinstance(device, devicetools.Node):
2103
+ yield from device.sequences
2104
+ else:
2105
+ for model in device.model.find_submodels(
2106
+ include_mainmodel=True
2107
+ ).values():
2108
+ for subseqs in model.sequences.iosubsequences:
2109
+ yield from subseqs
2110
+
2111
+ @property
2112
+ def foldernames(self) -> tuple[str, ...]:
2113
+ """The names of all folders the sequences shall be read from or written to."""
2114
+ return tuple(os.path.split(d)[-1] for d in self._dir2file2var)
2115
+
2116
+ @property
2117
+ def filenames(self) -> tuple[str, ...]:
2118
+ """The base file names of all handled |NetCDFVariable| objects."""
2119
+ filenames = (file2var.keys() for file2var in self._dir2file2var.values())
2120
+ return tuple(sorted(set(itertools.chain(*filenames))))
2121
+
2122
+ def __getattr__(self, name: str) -> TypeNetCDFVariable:
2123
+ counter = 0
2124
+ memory = None
2125
+ for dirpath, file2var in self._dir2file2var.items():
2126
+ dirname = os.path.split(dirpath)[-1]
2127
+ for filename, variable in file2var.items():
2128
+ if name == f"{dirname}_{filename}":
2129
+ return variable
2130
+ if name == filename:
2131
+ counter += 1
2132
+ memory = variable
2133
+ if counter == 1:
2134
+ assert memory is not None
2135
+ return memory
2136
+ if counter > 1:
2137
+ raise RuntimeError(
2138
+ f"The current NetCDFInterface object handles multiple NetCDF files "
2139
+ f"named `{name}`. Please be more specific."
2140
+ )
2141
+ raise AttributeError(
2142
+ f"The current NetCDFInterface object neither handles a NetCDF file named "
2143
+ f"`{name}` nor does it define a member named `{name}`."
2144
+ )
2145
+
2146
+ __copy__ = objecttools.copy_
2147
+ __deepcopy__ = objecttools.deepcopy_
2148
+
2149
+ def __len__(self) -> int:
2150
+ return len(tuple(ncfiles for ncfiles in self))
2151
+
2152
+ def __iter__(self) -> Iterator[TypeNetCDFVariable]:
2153
+ for file2var in self._dir2file2var.values():
2154
+ yield from file2var.values()
2155
+
2156
+ def __dir__(self) -> list[str]:
2157
+ adds_long = []
2158
+ counter: collections.defaultdict[str, int] = collections.defaultdict(int)
2159
+ for dirpath, file2var in self._dir2file2var.items():
2160
+ dirname = os.path.split(dirpath)[-1]
2161
+ for filename in file2var.keys():
2162
+ adds_long.append(f"{dirname}_{filename}")
2163
+ counter[filename] += 1
2164
+ adds_short = [name for name, nmb in counter.items() if nmb == 1]
2165
+ return cast(list[str], super().__dir__()) + adds_long + adds_short
2166
+
2167
+
2168
+ class NetCDFInterfaceReader(NetCDFInterfaceBase[NetCDFVariableFlatReader]):
2169
+ """Interface between |SequenceManager| and multiple |NetCDFVariableFlatReader|
2170
+ instances for reading data in one step.
2171
+
2172
+ For a general introduction to using |NetCDFInterfaceReader|, see the
2173
+ documentation on base class |NetCDFInterfaceBase|.
2174
+ """
2175
+
2176
+ def log(self, sequence: sequencetools.IOSequence) -> NetCDFVariableFlatReader:
2177
+ """Pass the given |IOSequence| to the log method of an already existing or, if
2178
+ necessary, freshly created |NetCDFVariableFlatReader| object."""
2179
+ file2var, stem = self._get_dir2file_stem(sequence=sequence)
2180
+ try:
2181
+ variable = file2var[stem]
2182
+ except KeyError:
2183
+ variable = NetCDFVariableFlatReader(sequence.filepath)
2184
+ file2var[stem] = variable
2185
+ variable.log(sequence)
2186
+ return variable
2187
+
2188
+ @printtools.print_progress
2189
+ def read(self) -> None:
2190
+ """Call method |NetCDFVariableFlatReader.read| of all handled
2191
+ |NetCDFVariableFlatReader| objects."""
2192
+ for variable in printtools.progressbar(self):
2193
+ variable.read()
2194
+
2195
+
2196
+ class NetCDFInterfaceWriter(
2197
+ NetCDFInterfaceBase[NetCDFVariableAggregated | NetCDFVariableFlatWriter]
2198
+ ):
2199
+ """Interface between |SequenceManager| and multiple |NetCDFVariableFlatWriter| or
2200
+ |NetCDFVariableAggregated| instances for writing data in one step.
2201
+
2202
+ For a general introduction to using |NetCDFInterfaceWriter|, see the
2203
+ documentation on base class |NetCDFInterfaceBase|.
2204
+ """
2205
+
2206
+ def log(
2207
+ self,
2208
+ sequence: sequencetools.IOSequence,
2209
+ infoarray: sequencetools.InfoArray | None = None,
2210
+ ) -> NetCDFVariableAggregated | NetCDFVariableFlatWriter:
2211
+ """Pass the given |IOSequence| to the log method of an already existing or, if
2212
+ necessary, freshly created |NetCDFVariableFlatWriter| or
2213
+ |NetCDFVariableAggregated| object (depending on the currently active
2214
+ |SequenceManager.aggregation| mode)."""
2215
+ file2var, stem = self._get_dir2file_stem(sequence=sequence)
2216
+ try:
2217
+ variable = file2var[stem]
2218
+ except KeyError:
2219
+ if hydpy.pub.sequencemanager.aggregation != "none":
2220
+ variable = NetCDFVariableAggregated(sequence.filepath)
2221
+ else:
2222
+ variable = NetCDFVariableFlatWriter(sequence.filepath)
2223
+ file2var[stem] = variable
2224
+ variable.log(sequence, infoarray)
2225
+ return variable
2226
+
2227
+ @printtools.print_progress
2228
+ def write(self) -> None:
2229
+ """Call method |MixinVariableWriter.write| of all handled
2230
+ |NetCDFVariableFlatWriter| and |NetCDFVariableAggregated| objects."""
2231
+ for variable in printtools.progressbar(self):
2232
+ variable.write()
2233
+
2234
+
2235
+ class NetCDFInterfaceJIT(NetCDFInterfaceBase[FlatUnion]):
2236
+ """Interface between |SequenceManager| and multiple |NetCDFVariableFlatWriter|
2237
+ instances for reading or writing data just in time.
2238
+
2239
+ See the documentation on method |NetCDFInterfaceJIT.provide_jitaccess| for further
2240
+ information.
2241
+ """
2242
+
2243
+ def log(self, sequence: sequencetools.IOSequence) -> FlatUnion:
2244
+ """Pass the given |IOSequence| to the log method of an already existing or, if
2245
+ necessary, freshly created |NetCDFVariableFlatWriter| object."""
2246
+ file2var, stem = self._get_dir2file_stem(sequence)
2247
+ try:
2248
+ variable = file2var[stem]
2249
+ except KeyError:
2250
+ if sequence.diskflag_reading:
2251
+ variable = NetCDFVariableFlatReader(sequence.filepath)
2252
+ else:
2253
+ variable = NetCDFVariableFlatWriter(sequence.filepath)
2254
+ file2var[stem] = variable
2255
+ variable.log(sequence)
2256
+ return variable
2257
+
2258
+ @contextlib.contextmanager
2259
+ def provide_jitaccess(
2260
+ self, deviceorder: Iterable[devicetools.Node | devicetools.Element]
2261
+ ) -> Iterator[JITAccessHandler]:
2262
+ """Allow method |HydPy.simulate| of class |HydPy| to read data from or write
2263
+ data to NetCDF files "just in time" during simulation runs.
2264
+
2265
+ We consider it unlikely users need ever to call the method
2266
+ |NetCDFInterfaceJIT.provide_jitaccess| directly. See the documentation on
2267
+ class |HydPy| on applying it indirectly. However, the following explanations
2268
+ might give some additional insights into the options and limitations of the
2269
+ related functionalities.
2270
+
2271
+ You can only either read from or write to each NetCDF file. We think this
2272
+ should rarely be a limitation for the anticipated workflows. One particular
2273
+ situation where one could eventually try to read and write simultaneously is
2274
+ when trying to overwrite some of the available input data. The following
2275
+ example tries to read the input data for all "headwater" catchments from
2276
+ specific NetCDF files but defines zero input values for all "non-headwater"
2277
+ catchments and tries to write them into the same files:
2278
+
2279
+ >>> from hydpy.core.testtools import prepare_full_example_1
2280
+ >>> prepare_full_example_1()
2281
+ >>> from hydpy import HydPy, print_vector, pub, TestIO
2282
+ >>> with TestIO():
2283
+ ... hp = HydPy("HydPy-H-Lahn")
2284
+ ... pub.timegrids = "1996-01-01", "1996-01-05", "1d"
2285
+ ... hp.prepare_network()
2286
+ ... hp.prepare_models()
2287
+ ... hp.load_conditions()
2288
+ ... headwaters = pub.selections["headwaters"].elements
2289
+ ... nonheadwaters = pub.selections["nonheadwaters"].elements
2290
+ ... headwaters.prepare_inputseries(allocate_ram=False, read_jit=True)
2291
+ ... nonheadwaters.prepare_inputseries(allocate_ram=True, write_jit=True)
2292
+ ... for element in nonheadwaters:
2293
+ ... for sequence in element.model.sequences.inputs:
2294
+ ... sequence.series = 0.0
2295
+ ... hp.simulate() # doctest: +ELLIPSIS
2296
+ Traceback (most recent call last):
2297
+ ...
2298
+ RuntimeError: While trying to prepare NetCDF files for reading or writing \
2299
+ data "just in time" during the current simulation run, the following error occurred: \
2300
+ For a specific NetCDF file, you can either read or write data during a simulation run \
2301
+ but for file `...hland_96_input_p.nc` both is requested.
2302
+
2303
+ Clearly, each NetCDF file we want to read data from needs to span the current
2304
+ simulation period:
2305
+
2306
+ >>> with TestIO():
2307
+ ... pub.timegrids.init.firstdate = "1987-01-01"
2308
+ ... pub.timegrids.sim.firstdate = "1988-01-01"
2309
+ ... hp.prepare_inputseries(allocate_ram=False, read_jit=True)
2310
+ ... hp.simulate() # doctest: +ELLIPSIS
2311
+ Traceback (most recent call last):
2312
+ ...
2313
+ RuntimeError: While trying to prepare NetCDF files for reading or writing \
2314
+ data "just in time" during the current simulation run, the following error occurred: \
2315
+ The data of the NetCDF `...hland_96_input_p.nc` (Timegrid("1989-11-01 00:00:00", \
2316
+ "2021-01-01 00:00:00", "1d")) does not correctly cover the current simulation period \
2317
+ (Timegrid("1988-01-01 00:00:00", "1996-01-05 00:00:00", "1d")).
2318
+
2319
+ However, each NetCDF file selected for writing must also cover the complete
2320
+ initialisation period. If there is no adequately named NetCDF file,
2321
+ |NetCDFInterfaceJIT.provide_jitaccess| creates a new one for the current
2322
+ initialisation period. If an adequately named file exists,
2323
+ |NetCDFInterfaceJIT.provide_jitaccess| uses it without any attempt to extend it
2324
+ temporally or spatially. The following example shows the insertion of the
2325
+ output data of two subsequent simulation runs into the same NetCDF files:
2326
+
2327
+ >>> with TestIO():
2328
+ ... pub.timegrids = "1996-01-01", "1996-01-05", "1d"
2329
+ ... hp.prepare_inputseries(allocate_ram=False, read_jit=True)
2330
+ ... hp.prepare_factorseries(allocate_ram=True, write_jit=True)
2331
+ ... pub.timegrids.sim.lastdate = "1996-01-03"
2332
+ ... hp.simulate()
2333
+ ... pub.timegrids.sim.firstdate = "1996-01-03"
2334
+ ... pub.timegrids.sim.lastdate = "1996-01-05"
2335
+ ... hp.simulate()
2336
+ >>> print_vector(
2337
+ ... hp.elements["land_dill_assl"].model.sequences.factors.contriarea.series)
2338
+ 0.497067, 0.496728, 0.496461, 0.496361
2339
+ >>> from hydpy.core.netcdftools import netcdf4
2340
+ >>> filepath = "HydPy-H-Lahn/series/default/hland_96_factor_contriarea.nc"
2341
+ >>> with TestIO(), netcdf4.Dataset(filepath, "r") as ncfile:
2342
+ ... print_vector(ncfile["hland_96_factor_contriarea"][:, 0])
2343
+ 0.497067, 0.496728, 0.496461, 0.496361
2344
+
2345
+ Under particular circumstances, the data variable of a NetCDF file can be
2346
+ 3-dimensional. The documentation on function |query_array| explains this in
2347
+ detail. The following example demonstrates that reading and writing such
2348
+ 3-dimensional variables "just in time" works correctly. Therefore, we add a
2349
+ `realization` dimension to the input file `hland_96_input_t.nc` (part of the
2350
+ example project data) and the output file `hland_96_factor_contriarea.nc`
2351
+ (written in the previous example) and use them for redefining their data
2352
+ variables with this additional dimension. As expected, the results are the
2353
+ same as in the previous example:
2354
+
2355
+ >>> with TestIO():
2356
+ ... for name in ("hland_96_input_t", "hland_96_factor_contriarea"):
2357
+ ... filepath = f"HydPy-H-Lahn/series/default/{name}.nc"
2358
+ ... with netcdf4.Dataset(filepath, "r+") as ncfile:
2359
+ ... ncfile.renameVariable(name, "old")
2360
+ ... _ = ncfile.createDimension("realization", 1)
2361
+ ... var = ncfile.createVariable(name, "f8",
2362
+ ... dimensions=("time", "realization", "stations"))
2363
+ ... if name == "hland_96_input_t":
2364
+ ... var[:] = ncfile["old"][:]
2365
+ ... else:
2366
+ ... var[:] = -999.0
2367
+ ... pub.timegrids = "1996-01-01", "1996-01-05", "1d"
2368
+ ... hp.simulate()
2369
+ >>> with TestIO(), netcdf4.Dataset(filepath, "r") as ncfile:
2370
+ ... print_vector(ncfile["hland_96_factor_contriarea"][:, 0, 0])
2371
+ 0.496003, 0.495664, 0.495398, 0.495298
2372
+
2373
+ If we try to write the output of a simulation run beyond the original
2374
+ initial initialisation period into the same files,
2375
+ |NetCDFInterfaceJIT.provide_jitaccess| raises an equal error as above:
2376
+
2377
+ >>> with TestIO():
2378
+ ... pub.timegrids = "1996-01-05", "1996-01-10", "1d"
2379
+ ... hp.prepare_inputseries(allocate_ram=True, read_jit=False)
2380
+ ... hp.prepare_factorseries(allocate_ram=True, write_jit=True)
2381
+ ... hp.simulate() # doctest: +ELLIPSIS
2382
+ Traceback (most recent call last):
2383
+ ...
2384
+ RuntimeError: While trying to prepare NetCDF files for reading or writing \
2385
+ data "just in time" during the current simulation run, the following error occurred: \
2386
+ The data of the NetCDF `...hland_96_factor_tc.nc` (Timegrid("1996-01-01 00:00:00", \
2387
+ "1996-01-05 00:00:00", "1d")) does not correctly cover the current simulation period \
2388
+ (Timegrid("1996-01-05 00:00:00", "1996-01-10 00:00:00", "1d")).
2389
+
2390
+ >>> hp.prepare_factorseries(allocate_ram=False, write_jit=False)
2391
+
2392
+ Regarding the spatial dimension, things are similar. You can write data for
2393
+ different sequences in subsequent simulation runs, but you need to ensure all
2394
+ required data columns are available right from the start. Hence, relying on
2395
+ the automatic file generation of |NetCDFInterfaceJIT.provide_jitaccess| fails
2396
+ in the following example:
2397
+
2398
+ >>> with TestIO():
2399
+ ... pub.timegrids = "1996-01-01", "1996-01-05", "1d"
2400
+ ... hp.prepare_inputseries(allocate_ram=False, read_jit=True)
2401
+ ... headwaters.prepare_fluxseries(allocate_ram=True, write_jit=True)
2402
+ ... hp.simulate()
2403
+ ... nonheadwaters.prepare_fluxseries(allocate_ram=True, write_jit=True)
2404
+ ... hp.simulate() # doctest: +ELLIPSIS
2405
+ Traceback (most recent call last):
2406
+ ...
2407
+ RuntimeError: While trying to prepare NetCDF files for reading or writing \
2408
+ data "just in time" during the current simulation run, the following error occurred: \
2409
+ No data for (sub)device `land_lahn_kalk_0` is available in NetCDF \
2410
+ file `...hland_96_flux_pc.nc`.
2411
+
2412
+ Of course, one way to prepare complete HydPy-compatible NetCDF files is to let
2413
+ HydPy do it:
2414
+
2415
+ >>> with TestIO(), pub.sequencemanager.filetype("nc"):
2416
+ ... hp.prepare_fluxseries(allocate_ram=False, write_jit=False)
2417
+ ... hp.prepare_fluxseries(allocate_ram=True, write_jit=False)
2418
+ ... hp.save_fluxseries()
2419
+ ... headwaters.prepare_fluxseries(allocate_ram=True, write_jit=True)
2420
+ ... hp.load_conditions()
2421
+ ... hp.simulate()
2422
+ >>> for element in hp.elements.search_keywords("catchment"):
2423
+ ... print_vector(element.model.sequences.fluxes.qt.series)
2424
+ 11.757526, 8.865079, 7.101815, 5.994195
2425
+ 11.672862, 10.100089, 8.984317, 8.202706
2426
+ 20.588949, 8.644722, 7.265526, 6.385012
2427
+ 9.64767, 8.513649, 7.777628, 7.343314
2428
+ >>> filepath_qt = "HydPy-H-Lahn/series/default/hland_96_flux_qt.nc"
2429
+ >>> with TestIO(), netcdf4.Dataset(filepath_qt, "r") as ncfile:
2430
+ ... for jdx in range(4):
2431
+ ... print_vector(ncfile["hland_96_flux_qt"][:, jdx])
2432
+ 11.757526, 8.865079, 7.101815, 5.994195
2433
+ 0.0, 0.0, 0.0, 0.0
2434
+ 0.0, 0.0, 0.0, 0.0
2435
+ 9.64767, 8.513649, 7.777628, 7.343314
2436
+ >>> with TestIO():
2437
+ ... headwaters.prepare_fluxseries(allocate_ram=True, write_jit=False)
2438
+ ... nonheadwaters.prepare_fluxseries(allocate_ram=True, write_jit=True)
2439
+ ... hp.load_conditions()
2440
+ ... hp.simulate()
2441
+ >>> with TestIO(), netcdf4.Dataset(filepath_qt, "r") as ncfile: #
2442
+ ... for jdx in range(4):
2443
+ ... print_vector(ncfile["hland_96_flux_qt"][:, jdx])
2444
+ 11.757526, 8.865079, 7.101815, 5.994195
2445
+ 11.672862, 10.100089, 8.984317, 8.202706
2446
+ 20.588949, 8.644722, 7.265526, 6.385012
2447
+ 9.64767, 8.513649, 7.777628, 7.343314
2448
+
2449
+ >>> hp.prepare_fluxseries(allocate_ram=False, write_jit=False)
2450
+
2451
+ There should be no limitation for reading data "just in time" and using
2452
+ different |Node.deploymode| options. For demonstration, we first calculate the
2453
+ time series of the |Sim| sequences of all nodes, assign them to the
2454
+ corresponding |Obs| sequences afterwards, and then start another simulation to
2455
+ (again) write both the simulated and the observed values to NetCDF files:
2456
+
2457
+ >>> with TestIO():
2458
+ ... hp.prepare_simseries(allocate_ram=True, write_jit=True)
2459
+ ... hp.prepare_obsseries(allocate_ram=True, write_jit=True)
2460
+ ... hp.load_conditions()
2461
+ ... hp.simulate()
2462
+ ... for idx, node in enumerate(hp.nodes):
2463
+ ... node.sequences.obs.series = node.sequences.sim.series
2464
+ ... hp.load_conditions()
2465
+ ... hp.simulate()
2466
+ >>> for node in hp.nodes:
2467
+ ... print_vector(node.sequences.sim.series)
2468
+ 11.757526, 8.865079, 7.101815, 5.994195
2469
+ 54.019337, 37.257561, 31.865308, 28.359542
2470
+ 42.346475, 27.157472, 22.88099, 20.156836
2471
+ 9.64767, 8.513649, 7.777628, 7.343314
2472
+ >>> for node in hp.nodes:
2473
+ ... print_vector(node.sequences.obs.series)
2474
+ 11.757526, 8.865079, 7.101815, 5.994195
2475
+ 54.019337, 37.257561, 31.865308, 28.359542
2476
+ 42.346475, 27.157472, 22.88099, 20.156836
2477
+ 9.64767, 8.513649, 7.777628, 7.343314
2478
+ >>> filepath_sim = "HydPy-H-Lahn/series/default/sim_q.nc"
2479
+ >>> with TestIO(), netcdf4.Dataset(filepath_sim, "r") as ncfile:
2480
+ ... for jdx in range(4):
2481
+ ... print_vector(ncfile["sim_q"][:, jdx])
2482
+ 11.757526, 8.865079, 7.101815, 5.994195
2483
+ 9.64767, 8.513649, 7.777628, 7.343314
2484
+ 42.346475, 27.157472, 22.88099, 20.156836
2485
+ 54.019337, 37.257561, 31.865308, 28.359542
2486
+ >>> filepath_obs = "HydPy-H-Lahn/series/default/obs_q.nc"
2487
+ >>> from hydpy.core.netcdftools import query_timegrid
2488
+ >>> with TestIO(), netcdf4.Dataset(filepath_obs, "r") as ncfile:
2489
+ ... tg = query_timegrid(ncfile, hp.nodes.dill_assl.sequences.obs)
2490
+ ... i0 = tg[pub.timegrids.sim.firstdate]
2491
+ ... i1 = tg[pub.timegrids.sim.lastdate]
2492
+ ... for jdx in range(4):
2493
+ ... print_vector(ncfile["obs_q"][i0:i1, jdx])
2494
+ 9.64767, 8.513649, 7.777628, 7.343314
2495
+ 11.757526, 8.865079, 7.101815, 5.994195
2496
+ 42.346475, 27.157472, 22.88099, 20.156836
2497
+ 54.019337, 37.257561, 31.865308, 28.359542
2498
+
2499
+ Now we stop all sequences from writing to NetCDF files, remove the two
2500
+ headwater elements from the currently active selection, and start another
2501
+ simulation run. The time series of both headwater nodes are zero due to the
2502
+ missing inflow from their inlet headwater sub-catchments. The non-headwater
2503
+ nodes only receive inflow from the two non-headwater sub-catchments:
2504
+
2505
+ >>> with TestIO():
2506
+ ... hp.prepare_simseries(allocate_ram=True, write_jit=False)
2507
+ ... hp.prepare_obsseries(allocate_ram=True, write_jit=False)
2508
+ ... hp.update_devices(nodes=hp.nodes, elements=hp.elements - headwaters)
2509
+ ... hp.load_conditions()
2510
+ ... hp.simulate()
2511
+ >>> for node in hp.nodes:
2512
+ ... print_vector(node.sequences.sim.series)
2513
+ 0.0, 0.0, 0.0, 0.0
2514
+ 42.261811, 18.744811, 16.249844, 14.587718
2515
+ 30.588949, 8.644722, 7.265526, 6.385012
2516
+ 0.0, 0.0, 0.0, 0.0
2517
+
2518
+ Finally, we set the |Node.deploymode| of the headwater nodes `dill_assl` and
2519
+ `lahn_marb` to `oldsim` and `obs`, respectively, and read their previously
2520
+ written time series "just in time". As expected, the values of the two
2521
+ non-headwater nodes are identical to those of our initial example:
2522
+
2523
+ >>> with TestIO():
2524
+ ... hp.nodes["dill_assl"].prepare_simseries(allocate_ram=True,
2525
+ ... read_jit=True)
2526
+ ... hp.nodes["dill_assl"].deploymode = "oldsim"
2527
+ ... hp.nodes["lahn_marb"].prepare_obsseries(allocate_ram=True,
2528
+ ... read_jit=True)
2529
+ ... hp.nodes["lahn_marb"].deploymode = "obs"
2530
+ ... hp.load_conditions()
2531
+ ... hp.simulate()
2532
+ >>> for node in hp.nodes:
2533
+ ... print_vector(node.sequences.sim.series)
2534
+ 11.757526, 8.865079, 7.101815, 5.994195
2535
+ 54.019337, 37.257561, 31.865308, 28.359542
2536
+ 42.346475, 27.157472, 22.88099, 20.156836
2537
+ 0.0, 0.0, 0.0, 0.0
2538
+ """
2539
+
2540
+ readers: list[JITAccessInfo] = []
2541
+ writers: list[JITAccessInfo] = []
2542
+ variable2readmode: dict[FlatUnion, bool] = {}
2543
+ variable2ncfile: dict[FlatUnion, netcdf4.Dataset] = {}
2544
+ variable2infos: dict[FlatUnion, list[JITAccessInfo]] = {}
2545
+ variable2sequences: collections.defaultdict[
2546
+ FlatUnion, list[sequencetools.IOSequence]
2547
+ ] = collections.defaultdict(list)
2548
+ disabled: dict[sequencetools.IOSequence, sequencetools.SeriesMode] = {}
2549
+
2550
+ try: # pylint: disable=too-many-nested-blocks
2551
+
2552
+ # just-in-time calculations only work with NetCDF files
2553
+ with hydpy.pub.sequencemanager.filetype("nc"):
2554
+
2555
+ # collect the relevant sequences:
2556
+ log = self.log
2557
+ for sequence in self._yield_disksequences(deviceorder):
2558
+ if sequence.diskflag:
2559
+ variable = log(sequence)
2560
+ readmode = sequence.diskflag_reading
2561
+ variable2readmode.setdefault(variable, readmode)
2562
+ if variable2readmode[variable] != readmode:
2563
+ raise RuntimeError(
2564
+ f"For a specific NetCDF file, you can either read or "
2565
+ f"write data during a simulation run but for file "
2566
+ f"`{variable.filepath}` both is requested."
2567
+ )
2568
+ variable2infos[variable] = readers if readmode else writers
2569
+ variable2sequences[variable].append(sequence)
2570
+
2571
+ if variable2sequences:
2572
+ # prepare NetCDF files:
2573
+ variable2timedelta: dict[FlatUnion, int] = {}
2574
+ tg_init = hydpy.pub.timegrids.init
2575
+ tg_sim = hydpy.pub.timegrids.sim
2576
+ for variable in tuple(variable2readmode):
2577
+ filepath = variable.filepath
2578
+ if not os.path.exists(filepath):
2579
+ if isinstance(variable, NetCDFVariableFlatReader):
2580
+ if hydpy.pub.options.checkseries:
2581
+ raise FileNotFoundError(
2582
+ f"No file `{filepath}` available for reading."
2583
+ )
2584
+ del variable2readmode[variable]
2585
+ del variable2infos[variable]
2586
+ if sequences := variable2sequences.pop(variable):
2587
+ for sequence in sequences:
2588
+ sequence.prepare_series(read_jit=False)
2589
+ disabled[sequence] = sequence.seriesmode
2590
+ continue
2591
+ variable.write()
2592
+ ncfile = netcdf4.Dataset(filepath, "r+")
2593
+ variable2ncfile[variable] = ncfile
2594
+ sequence = variable2sequences[variable][0]
2595
+ tg_variable = query_timegrid(ncfile, sequence)
2596
+ if tg_sim not in tg_variable:
2597
+ raise RuntimeError(
2598
+ f"The data of the NetCDF `{filepath}` ({tg_variable}) "
2599
+ f"does not correctly cover the current simulation "
2600
+ f"period ({tg_sim})."
2601
+ )
2602
+ variable2timedelta[variable] = tg_variable[tg_init.firstdate]
2603
+
2604
+ # make information for reading and writing temporarily available:
2605
+ for variable, sequences in variable2sequences.items():
2606
+ ncfile = variable2ncfile[variable]
2607
+ assert ncfile is not None
2608
+ get = variable.query_subdevice2index(ncfile).get_index
2609
+ data: NDArrayFloat
2610
+ data = numpy.full(
2611
+ variable.shape[1], numpy.nan, dtype=config.NP_FLOAT
2612
+ )
2613
+ variable2infos[variable].append(
2614
+ JITAccessInfo(
2615
+ ncvariable=(ncvariable := ncfile[variable.name]),
2616
+ realisation=_is_realisation(ncvariable, ncfile),
2617
+ timedelta=variable2timedelta[variable],
2618
+ columns=tuple(get(n) for n in variable.subdevicenames),
2619
+ data=data,
2620
+ )
2621
+ )
2622
+ # the following algorithm relies on the iteration order defined
2623
+ # by method _yield_disksequences:
2624
+ i0, delta, descr_old = 0, 0, ""
2625
+ for sequence in sequences:
2626
+ if (descr_new := sequence.descr_device) != descr_old:
2627
+ descr_old = descr_new
2628
+ i0 += delta
2629
+ delta = int(numpy.prod(sequence.shape))
2630
+ sequence.connect_netcdf(ncarray=data[i0 : i0 + delta])
2631
+
2632
+ yield JITAccessHandler(
2633
+ readers=tuple(readers), writers=tuple(writers)
2634
+ )
2635
+
2636
+ else:
2637
+ # return without useless efforts:
2638
+ yield JITAccessHandler(readers=(), writers=())
2639
+
2640
+ except BaseException:
2641
+ objecttools.augment_excmessage(
2642
+ "While trying to prepare NetCDF files for reading or writing data "
2643
+ '"just in time" during the current simulation run'
2644
+ )
2645
+ finally:
2646
+ for sequence, seriesmode in disabled.items():
2647
+ sequence.seriesmode = seriesmode
2648
+ for ncfile in variable2ncfile.values():
2649
+ ncfile.close()
2650
+
2651
+
2652
+ def add_netcdfreading(wrapped: Callable[P, None]) -> Callable[P, None]:
2653
+ """Enable a function or method that can read time series from NetCDF files to
2654
+ automatically activate the |SequenceManager.netcdfreading| mode if not already
2655
+ done."""
2656
+
2657
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> None:
2658
+ sm = hydpy.pub.sequencemanager
2659
+ if sm._netcdfreader is None: # pylint: disable=protected-access
2660
+ with sm.netcdfreading():
2661
+ wrapped(*args, **kwargs)
2662
+ else:
2663
+ wrapped(*args, **kwargs)
2664
+
2665
+ functools.update_wrapper(wrapper=wrapper, wrapped=wrapped)
2666
+ return wrapper
2667
+
2668
+
2669
+ def add_netcdfwriting(wrapped: Callable[P, None]) -> Callable[P, None]:
2670
+ """Enable a function or method that can write time series to NetCDF files to
2671
+ automatically activate the |SequenceManager.netcdfwriting| mode if not already
2672
+ done."""
2673
+
2674
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> None:
2675
+ sm = hydpy.pub.sequencemanager
2676
+ if sm._netcdfwriter is None: # pylint: disable=protected-access
2677
+ with sm.netcdfwriting():
2678
+ wrapped(*args, **kwargs)
2679
+ else:
2680
+ wrapped(*args, **kwargs)
2681
+
2682
+ functools.update_wrapper(wrapper=wrapper, wrapped=wrapped)
2683
+ return wrapper