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,3089 @@
1
+ """This module implements the main features for managing *HydPy* projects.
2
+
3
+ .. _`NetCDF Climate and Forecast (CF) Metadata Conventions`: http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html # pylint: disable=line-too-long
4
+ """
5
+
6
+ # import...
7
+ # ...from standard library
8
+ from __future__ import annotations
9
+ import collections
10
+ import contextlib
11
+ import itertools
12
+ import warnings
13
+
14
+ # ...from site-packages
15
+ import networkx
16
+
17
+ # ...from HydPy
18
+ import hydpy
19
+ from hydpy.core import devicetools
20
+ from hydpy.core import exceptiontools
21
+ from hydpy.core import filetools
22
+ from hydpy.core import modeltools
23
+ from hydpy.core import objecttools
24
+ from hydpy.core import printtools
25
+ from hydpy.core import propertytools
26
+ from hydpy.core import selectiontools
27
+ from hydpy.core import sequencetools
28
+ from hydpy.core import timetools
29
+ from hydpy.core.typingtools import *
30
+
31
+ if TYPE_CHECKING:
32
+ from hydpy.core import auxfiletools
33
+
34
+
35
+ class HydPy:
36
+ """The main class for managing *HydPy* projects.
37
+
38
+ In typical *HydPy* projects, one prepares a single instance of class |HydPy|. This
39
+ instance, which we name "hp" throughout this documentation instead of "hydpy" to
40
+ avoid a naming collision with the `hydpy` site package, provides many convenient
41
+ methods to perform tasks like reading time series data or starting simulation runs.
42
+ Additionally, it serves as a root to access most details of a *HydPy* project,
43
+ allowing for more granular control over the framework features.
44
+
45
+ We elaborate on these short explanations by using the :ref:`HydPy-H-Lahn` example
46
+ project. Calling function |prepare_full_example_1| copies the complete example
47
+ project :ref:`HydPy-H-Lahn` into the `iotesting` directory of the *HydPy* site
48
+ package (alternatively, you can copy the `HydPy-H-Lahn` example project, which can
49
+ be found in subpackage `data`, into a working directory of your choice):
50
+
51
+ >>> from hydpy.core.testtools import prepare_full_example_1
52
+ >>> prepare_full_example_1()
53
+
54
+ At first, the |HydPy| instance needs to know the name of the relevant project,
55
+ which is identical to the name of the project's root directory. Pass
56
+ `HydPy-H-Lahn` to the constructor of class |HydPy|:
57
+
58
+ >>> from hydpy import HydPy
59
+ >>> hp = HydPy("HydPy-H-Lahn")
60
+
61
+ So far, our |HydPy| instance does not know any project configurations except its
62
+ name. Most of this information would be available via properties |HydPy.nodes| and
63
+ |HydPy.elements|, but we get the following error responses if we try to access them:
64
+
65
+ >>> hp.nodes
66
+ Traceback (most recent call last):
67
+ ...
68
+ hydpy.core.exceptiontools.AttributeNotReady: The actual HydPy instance does not \
69
+ handle any nodes at the moment.
70
+
71
+ >>> hp.elements
72
+ Traceback (most recent call last):
73
+ ...
74
+ hydpy.core.exceptiontools.AttributeNotReady: The actual HydPy instance does not \
75
+ handle any elements at the moment.
76
+
77
+ One could continue rather quickly by calling the method |HydPy.prepare_everything|,
78
+ which would make our |HydPy| instance ready for its first simulation run in one go.
79
+ However, we prefer to continue step by step by calling the more specific
80
+ preparation methods, which offers more flexibility.
81
+
82
+ First, the |HydPy| instance needs to know the relevant |Node| and |Element| objects.
83
+ Method |HydPy.prepare_network| reads this information from so-called "network
84
+ files". Then, the |Node| and |Element| objects connect automatically and thereby
85
+ define the topology or the network structure of the project (see the documentation
86
+ on class |NetworkManager| and module |devicetools| for more detailed explanations):
87
+
88
+ >>> from hydpy import TestIO
89
+ >>> with TestIO():
90
+ ... hp.prepare_network()
91
+
92
+ (Using the "with" statement in combination with class |TestIO| makes sure we are
93
+ reading the network files from a subdirectory of the `iotesting` directory. Here
94
+ and in the following, you must omit such "with blocks" in case you copied the
95
+ :ref:`HydPy-H-Lahn` example project into your current working directory.)
96
+
97
+ Now, our |HydPy| instance offers access to all |Node| objects defined within the
98
+ :ref:`HydPy-H-Lahn` example project, which are grouped by a |Nodes| object:
99
+
100
+ >>> hp.nodes
101
+ Nodes("dill_assl", "lahn_kalk", "lahn_leun", "lahn_marb")
102
+
103
+ Taking the node `dill_assl` as an example, we can dive into the details and, for
104
+ example, search for those elements to which node `dill_assl` is connected (it
105
+ receives water from element `land_dill_assl` and passes it to element
106
+ `stream_dill_assl_lahn_leun`), or inspect its simulated discharge value handled by
107
+ a |Sim| sequence object (so far, zero):
108
+
109
+ >>> hp.nodes.dill_assl.entries
110
+ Elements("land_dill_assl")
111
+
112
+ >>> hp.nodes.dill_assl.exits
113
+ Elements("stream_dill_assl_lahn_leun")
114
+
115
+ >>> hp.nodes.dill_assl.sequences.sim
116
+ sim(0.0)
117
+
118
+ All |Node| objects are ready to be used. The same is only partly true for the
119
+ |Element| objects, which are also accessible (via a |Elements| instance) and
120
+ properly connected to the |Node| objects but do not handle workable |Model| objects,
121
+ which is required to perform any simulation run:
122
+
123
+ >>> hp.elements
124
+ Elements("land_dill_assl", "land_lahn_kalk", "land_lahn_leun",
125
+ "land_lahn_marb", "stream_dill_assl_lahn_leun",
126
+ "stream_lahn_leun_lahn_kalk", "stream_lahn_marb_lahn_leun")
127
+
128
+ >>> hp.elements.stream_dill_assl_lahn_leun
129
+ Element("stream_dill_assl_lahn_leun",
130
+ inlets="dill_assl",
131
+ outlets="lahn_leun",
132
+ keywords="river")
133
+
134
+ >>> hp.elements.land_dill_assl.model
135
+ Traceback (most recent call last):
136
+ ...
137
+ hydpy.core.exceptiontools.AttributeNotReady: The model object of element \
138
+ `land_dill_assl` has been requested but not been prepared so far.
139
+
140
+ Hence, we need to call method |HydPy.prepare_models|, which instructs all |Element|
141
+ objects to read the relevant parameter control files and prepare their |Model|
142
+ objects. Note that the individual |Element| object does not know the relevant
143
+ model type beforehand; both the information on the model type and the parameter
144
+ settings is encoded in individual control files, making it easy to exchange
145
+ individual models later (the documentation on method |Elements.prepare_models| of
146
+ class |Elements| is a good starting point for a deeper understanding on configuring
147
+ *HydPy* projects via control files):
148
+
149
+ >>> with TestIO():
150
+ ... hp.prepare_models()
151
+ Traceback (most recent call last):
152
+ ...
153
+ hydpy.core.exceptiontools.AttributeNotReady: While trying to initialise the model \
154
+ object of element `land_dill_assl`, the following error occurred: The initialisation \
155
+ period has not been defined via attribute `timegrids` of module `pub` yet but might be \
156
+ required to prepare the model properly.
157
+
158
+ Oops, something went wrong. We forgot to define the simulation period, which might
159
+ be relevant for some time-dependent configurations. We discuss some examples of
160
+ such configurations below but now use this little accident to discuss the typical
161
+ pattern of *HydPy* error messages. First, we usually try to add some additional
162
+ "spatial" information (in this case: the name of the related |Element| object).
163
+ Second, we try to explain in which program context an error occurs. This context
164
+ is already available in much more detail in the so-called "stack trace" (the middle
165
+ part of the printed error response we do not show). Stack trace descriptions are
166
+ great for programmers but hard to read for others, which is why we often add "While
167
+ trying to..." explanations to our error messages. In our example, one can see that
168
+ the error occurred while trying to initialise the |Model| object of element
169
+ `land_dill_assl`, which is quite evident in our example but could be less evident in
170
+ more complex *HydPy* applications.
171
+
172
+ The last sentence of the error message tells us that we need to define the
173
+ attribute `timegrids` of module `pub`. `pub` stands for "public", meaning module
174
+ `pub` handles all (or at least most of) the globally available configuration data.
175
+ One example is that module `pub` handles a |Timegrids| instance defining both the
176
+ initialisation and the simulation period, which can be done by the following
177
+ assignment (see the documentation on class |Timegrid| and on class |Timegrids| for
178
+ further information):
179
+
180
+ >>> from hydpy import pub
181
+ >>> pub.timegrids = "1996-01-01", "1996-01-05", "1d"
182
+
183
+ Now, method |HydPy.prepare_models| does not complain anymore and adds an instance
184
+ of the |hland_96| application model to element `land_dill_assl`, to which we set an
185
+ additional reference to shorten the following examples:
186
+
187
+ >>> with TestIO():
188
+ ... hp.prepare_models()
189
+
190
+ >>> model = hp.elements.land_dill_assl.model
191
+ >>> model.name
192
+ 'hland_96'
193
+
194
+ All control parameter values, defined in the corresponding control file, are
195
+ correctly set. As an example, we show the values of the control parameter
196
+ |hland_control.IcMax|, which in this case defines different values for hydrological
197
+ response units of type |hland_constants.FIELD| (1.0 mm) and of type
198
+ |hland_constants.FOREST| (1.5 mm):
199
+
200
+ >>> model.parameters.control.icmax
201
+ icmax(field=1.0, forest=1.5)
202
+
203
+ The appearance (or "string representation") of all parameters that have a unit with
204
+ a time reference (we call these parameters "time-dependent") like
205
+ |hland_control.PercMax| depends on the current setting of option
206
+ |Options.parameterstep|, which is one day by default (see the documentation on
207
+ class |Parameter| for more information on dealing with time-dependent parameters
208
+ subclasses):
209
+
210
+ >>> model.parameters.control.percmax
211
+ percmax(1.39636)
212
+ >>> pub.options.parameterstep("1h")
213
+ Period("1d")
214
+
215
+ The values of the derived parameters, which need to be calculated before starting a
216
+ simulation run based on the control parameters and eventually based on some other
217
+ settings (e.g. the initialisation period), are also ready. Here, we show the value
218
+ of the derived parameter |hland_derived.Z|, representing the average (reference)
219
+ subbasin elevation:
220
+
221
+ >>> model.parameters.derived.z
222
+ z(4.205345)
223
+
224
+ We define all class names in "CamelCase" letters (which is a Python convention) and,
225
+ whenever practical, name the related objects identically but in lowercase letters.
226
+ We hope that eases finding the relevant parts of the online documentation when in
227
+ trouble with a particular object. Three examples we already encountered are the
228
+ |Timegrids| instance `timegrids` of module `pub`, the |Nodes| instance `nodes` of
229
+ class `HydPy`, and the |hland_derived.Z| instance `z` of application model
230
+ |hland_96|:
231
+
232
+ >>> from hydpy import classname
233
+ >>> classname(pub.timegrids)
234
+ 'Timegrids'
235
+
236
+ >>> classname(hp.nodes)
237
+ 'Nodes'
238
+
239
+ >>> classname(model.parameters.derived.z)
240
+ 'Z'
241
+
242
+ As shown above, all |Parameter| objects of the model of element `land_dill_assl` are
243
+ ready to be used. However, all sequences (which handle the time variable properties)
244
+ contain |numpy| |numpy.nan| values, which we use to indicate missing data. We show
245
+ this for the 0-dimensional input sequence |hland_inputs.T|, the 1-dimensional factor
246
+ sequence |hland_factors.TC|, the 1-dimensional state sequence |hland_states.SM|,
247
+ and the 0-dimensional flux sequence |hland_fluxes.QT|:
248
+
249
+ >>> model.sequences.inputs.t
250
+ t(nan)
251
+
252
+ >>> model.sequences.factors.tc
253
+ tc(nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan)
254
+
255
+ >>> model.sequences.states.sm
256
+ sm(nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan)
257
+
258
+ >>> model.sequences.fluxes.qt
259
+ qt(nan)
260
+
261
+ There are some other sequence types (see the documentation on module |sequencetools|
262
+ for more details) but |InputSequence|, |FactorSequence| |FluxSequence|, and
263
+ |StateSequence| are the most common ones (besides the |NodeSequence| subtypes
264
+ |Obs| and especially |Sim|).
265
+
266
+ |StateSequence| objects describe many aspects of the current state of a model (or,
267
+ e.g., of a catchment). Each simulation run requires proper initial states, which
268
+ we call initial conditions in the following (also covering memory aspects
269
+ represented by |LogSequence| objects). We load all necessary initial conditions by
270
+ calling the method |HydPy.load_conditions| (see the documentation on method
271
+ |HydPy.load_conditions| for further details):
272
+
273
+ >>> with TestIO():
274
+ ... hp.load_conditions()
275
+
276
+ Now, the states of our model are also ready to be used. However, one should note
277
+ that state sequence |hland_states.SM| knows only the current soil moisture states
278
+ for the twelve hydrological response units of element `land_dill_assl` (more
279
+ specifically, we loaded the soil moisture values related to the start date of the
280
+ initialisation period, which is January 1 at zero o'clock). By default and for
281
+ reasons of memory storage efficiency, sequences generally handle the currently
282
+ relevant values only instead of complete time series:
283
+
284
+ >>> model.sequences.inputs.t
285
+ t(nan)
286
+
287
+ >>> model.sequences.factors.tc
288
+ tc(nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan)
289
+
290
+ >>> model.sequences.states.sm
291
+ sm(185.13164, 181.18755, 199.80432, 196.55888, 212.04018, 209.48859,
292
+ 222.12115, 220.12671, 230.30756, 228.70779, 236.91943, 235.64427)
293
+
294
+ >>> model.sequences.fluxes.qt
295
+ qt(nan)
296
+
297
+ For states like |hland_states.SM|, we need to know the values at the beginning of
298
+ the simulation period only. All following values are calculated subsequentially
299
+ during the simulation run. However, this is different for input sequences like
300
+ |hland_inputs.T|. Time variable properties like the air temperature are external
301
+ forcings. Hence, they must be available over the whole simulation period apriori.
302
+ Such complete time series can be made available via property |IOSequence.series| of
303
+ class |IOSequence|, which has not happened for any sequence so far:
304
+
305
+ >>> model.sequences.inputs.t.series
306
+ Traceback (most recent call last):
307
+ ...
308
+ hydpy.core.exceptiontools.AttributeNotReady: Sequence `t` of element \
309
+ `land_dill_assl` is not requested to make any time series data available.
310
+
311
+ Before loading time series data, we need to reserve the required memory storage.
312
+ We do this for all sequences at once (not only the |ModelSequence| objects but also
313
+ the |NodeSequence| objects as the |Sim| instance handled by node `dill_assl`) by
314
+ calling the method |HydPy.prepare_allseries|:
315
+
316
+ >>> hp.prepare_allseries()
317
+
318
+ Now, property |IOSequence.series| returns an |InfoArray| object, which is a slight
319
+ modification of the widely applied |numpy| |numpy.ndarray|. The first axis (or the
320
+ only axis) corresponds to the number of days of the initialisation period (a
321
+ *HydPy* convention). For the 1-dimensional sequences |hland_factors.TC| and
322
+ |hland_states.SM|, the second axis corresponds to the number of hydrological
323
+ response units (a |hland| convention):
324
+
325
+ >>> model.sequences.inputs.t.series
326
+ InfoArray([nan, nan, nan, nan])
327
+
328
+ >>> model.sequences.factors.tc.series
329
+ InfoArray([[nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan],
330
+ [nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan],
331
+ [nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan],
332
+ [nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan]])
333
+
334
+ >>> model.sequences.states.sm.series
335
+ InfoArray([[nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan],
336
+ [nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan],
337
+ [nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan],
338
+ [nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan]])
339
+
340
+ >>> model.sequences.fluxes.qt.series
341
+ InfoArray([nan, nan, nan, nan])
342
+
343
+ >>> hp.nodes.dill_assl.sequences.sim.series
344
+ InfoArray([nan, nan, nan, nan])
345
+
346
+ So far, each time series array is empty. The :ref:`HydPy-H-Lahn` example project
347
+ provides time series files for the input sequences, which is the minimum
348
+ requirement for starting a simulation run. We use method |HydPy.load_inputseries|
349
+ to load this data:
350
+
351
+ >>> with TestIO():
352
+ ... hp.load_inputseries()
353
+
354
+ >>> from hydpy import round_
355
+ >>> round_(model.sequences.inputs.t.series)
356
+ 0.0, -0.5, -2.4, -6.8
357
+
358
+ Finally, we can perform the simulation run by calling the method |HydPy.simulate|:
359
+
360
+ >>> hp.simulate()
361
+
362
+ The time series arrays of all sequences now contain calculated values --- except
363
+ those of input sequence |hland_inputs.T|, of course (for the sequences
364
+ |hland_factors.TC| and |hland_states.SM|, we show the time series of the first
365
+ hydrological response unit only):
366
+
367
+ >>> round_(model.sequences.inputs.t.series)
368
+ 0.0, -0.5, -2.4, -6.8
369
+
370
+ >>> round_(model.sequences.factors.tc.series[:, 0])
371
+ 1.323207, 0.823207, -1.076793, -5.476793
372
+
373
+ >>> round_(model.sequences.states.sm.series[:, 0])
374
+ 184.958475, 184.763638, 184.610776, 184.553224
375
+
376
+ >>> round_(model.sequences.fluxes.qt.series)
377
+ 11.757526, 8.865079, 7.101815, 5.994195
378
+
379
+ >>> round_(hp.nodes.dill_assl.sequences.sim.series)
380
+ 11.757526, 8.865079, 7.101815, 5.994195
381
+
382
+ By comparison, you see that the last calculated (or read) time series value is
383
+ the actual one for each |Sequence_| object. This mechanism allows, for example, to
384
+ write the final states of soil moisture sequence |hland_states.SM| and use them as
385
+ initial conditions later, even if its complete time series were not available:
386
+
387
+ >>> model.sequences.inputs.t
388
+ t(-6.8)
389
+
390
+ >>> model.sequences.states.sm
391
+ sm(184.553224, 180.623124, 199.181137, 195.947542, 212.04018, 209.48859,
392
+ 222.12115, 220.12671, 230.30756, 228.70779, 236.91943, 235.64427)
393
+
394
+ >>> model.sequences.fluxes.qt
395
+ qt(5.994195)
396
+
397
+ >>> hp.nodes.dill_assl.sequences.sim
398
+ sim(5.994195)
399
+
400
+ In many applications, the simulated time series is the result we are interested in.
401
+ Hence, we close our explanations with some detailed examples on this topic that
402
+ also cover the potential problem of limited rapid access storage availability.
403
+
404
+ The *HydPy* framework does not overwrite already existing time series by default
405
+ files. However, you can change this and related settings via the |SequenceManager|
406
+ object available in module |pub| (module |pub| also handles |ControlManager| and
407
+ |ConditionManager| objects for settings related to reading and writing control
408
+ files and condition files). We change the default behaviour by setting the
409
+ |SequenceManager.overwrite| attribute to |True|:
410
+
411
+ >>> pub.sequencemanager.overwrite = True
412
+
413
+ Now, we can (over)write all possible time series:
414
+
415
+ >>> with TestIO():
416
+ ... hp.save_inputseries()
417
+ ... hp.save_factorseries()
418
+ ... hp.save_fluxseries()
419
+ ... hp.save_stateseries()
420
+ ... hp.save_simseries()
421
+ ... hp.save_obsseries()
422
+
423
+ Alternatively, apply |HydPy.save_modelseries| to write the series of all the
424
+ |InputSequence|, |FactorSequence|, |FluxSequence|, and |StateSequence| objects and
425
+ |HydPy.save_nodeseries| to write the series of all |Sim| and |Obs| objects in one
426
+ step:
427
+
428
+ >>> with TestIO():
429
+ ... hp.save_modelseries()
430
+ ... hp.save_nodeseries()
431
+
432
+ Even shorter, just apply the method |HydPy.save_allseries|:
433
+
434
+ >>> with TestIO():
435
+ ... hp.save_allseries()
436
+
437
+ Next, we show how the reading of time series works. We first set the time series
438
+ values of all considered sequences to zero for this purpose:
439
+
440
+ >>> model.sequences.inputs.t.series = 0.0
441
+ >>> model.sequences.states.sm.series = 0.0
442
+ >>> model.sequences.inputs.t.series = 0.0
443
+ >>> hp.nodes.dill_assl.sequences.sim.series = 0.0
444
+
445
+ Now, we can reload the time series of all relevant sequences. However, doing so
446
+ would result in a warning due to incomplete data (for example, of the observation
447
+ data handled by the |Obs| sequence objects). To circumvent this problem, we turn
448
+ off the |Options.checkseries| option, which is one of the public options handled by
449
+ the instance of class |Options| available as another attribute of module |pub|. We
450
+ again use "with blocks", making sure the option (and the current working directory)
451
+ changes only temporarily while loading the time series:
452
+
453
+ >>> with TestIO(), pub.options.checkseries(False):
454
+ ... hp.load_inputseries()
455
+ ... hp.load_factorseries()
456
+ ... hp.load_fluxseries()
457
+ ... hp.load_stateseries()
458
+ ... hp.load_simseries()
459
+ ... hp.load_obsseries()
460
+
461
+ >>> with TestIO(), pub.options.checkseries(False):
462
+ ... hp.load_modelseries()
463
+ ... hp.load_nodeseries()
464
+
465
+ >>> with TestIO(), pub.options.checkseries(False):
466
+ ... hp.load_allseries()
467
+
468
+ The read time series data equals the previously written one:
469
+
470
+ >>> round_(model.sequences.inputs.t.series)
471
+ 0.0, -0.5, -2.4, -6.8
472
+
473
+ >>> round_(model.sequences.factors.tc.series[:, 0])
474
+ 1.323207, 0.823207, -1.076793, -5.476793
475
+
476
+ >>> round_(model.sequences.states.sm.series[:, 0])
477
+ 184.958475, 184.763638, 184.610776, 184.553224
478
+
479
+ >>> round_(model.sequences.fluxes.qt.series)
480
+ 11.757526, 8.865079, 7.101815, 5.994195
481
+
482
+ >>> round_(hp.nodes.dill_assl.sequences.sim.series)
483
+ 11.757526, 8.865079, 7.101815, 5.994195
484
+
485
+ We mentioned the possibility for more granular control of *HydPy* by using the
486
+ different objects handled by the |HydPy| object instead of using its convenience
487
+ methods. Here is an elaborate example showing how to (re)load the states of an
488
+ arbitrary simulation time step, which might be relevant for more complex workflows
489
+ implementing data assimilation techniques:
490
+
491
+ >>> model.sequences.states.load_data(1)
492
+ >>> model.sequences.states.sm
493
+ sm(184.763638, 180.829058, 199.40823, 196.170947, 212.04018, 209.48859,
494
+ 222.12115, 220.12671, 230.30756, 228.70779, 236.91943, 235.64427)
495
+
496
+ Using the node sequence |Sim| as an example, we also show the inverse functionality
497
+ of changing time series values:
498
+
499
+ >>> hp.nodes.dill_assl.sequences.sim = 0.0
500
+ >>> hp.nodes.dill_assl.sequences.save_data(2)
501
+ >>> round_(hp.nodes.dill_assl.sequences.sim.series)
502
+ 11.757526, 8.865079, 0.0, 5.994195
503
+
504
+ >>> hp.nodes.dill_assl.sequences.load_data(1)
505
+ >>> hp.nodes.dill_assl.sequences.sim
506
+ sim(8.865079)
507
+
508
+ In the examples above, we keep all data in rapid access memory, which can be
509
+ problematic when handling long time series in huge *HydPy* projects. When in
510
+ trouble, first try to prepare only those time series that are strictly required
511
+ (very often, it is sufficient to call |HydPy.prepare_inputseries|,
512
+ |HydPy.load_inputseries|, and |HydPy.prepare_simseries| only). If this does not
513
+ work in your project, you can read input data from and write output data to NetCDF
514
+ files during simulation. These follow the `NetCDF Climate and Forecast (CF)
515
+ Metadata Conventions`_. To benefit from this feature, assign |False| to the
516
+ `allocate_ram` argument of the individual "prepare series" methods (which disables
517
+ handling the time series in RAM) and assign |True| to the respective "jit"
518
+ arguments (which prepares the "just-in-time" file access). The methods
519
+ |HydPy.prepare_factorseries|, |HydPy.prepare_fluxseries|, and
520
+ |HydPy.prepare_stateseries| deal with "output sequences" for which read data would
521
+ be overwritten during the simulation and thus only support the `write_jit` argument.
522
+ The |HydPy.prepare_inputseries| method, on the other hand, supports both the
523
+ `read_jit` and the `write_jit` argument. However, in most cases, only reading
524
+ makes sense. The argument `write_jit` is thought for when other methods (for
525
+ example data assimilation approaches) modify the input data, and we need to keep
526
+ track of these modifications:
527
+
528
+ >>> hp.prepare_inputseries(allocate_ram=False, read_jit=True)
529
+ >>> hp.prepare_factorseries(allocate_ram=False, write_jit=True)
530
+ >>> hp.prepare_fluxseries(allocate_ram=False, write_jit=True)
531
+ >>> hp.prepare_stateseries(allocate_ram=False, write_jit=True)
532
+ >>> hp.prepare_simseries(allocate_ram=False, write_jit=True)
533
+ >>> hp.prepare_obsseries(allocate_ram=False, read_jit=True)
534
+
535
+ By doing so, you lose the previously available time series information. We use
536
+ function |attrready| to check this:
537
+
538
+ >>> from hydpy import attrready
539
+ >>> attrready(model.sequences.inputs.t, "series")
540
+ False
541
+
542
+ >>> attrready(model.sequences.factors.tc, "series")
543
+ False
544
+
545
+ >>> attrready(model.sequences.states.sm, "series")
546
+ False
547
+
548
+ >>> attrready(model.sequences.fluxes.qt, "series")
549
+ False
550
+
551
+ >>> attrready(hp.nodes.dill_assl.sequences.sim, "series")
552
+ False
553
+
554
+ Reloading the initial conditions and starting a new simulation run leads to the
555
+ same results as the simulation run above:
556
+
557
+ >>> with TestIO(), pub.options.checkseries(False):
558
+ ... hp.load_conditions()
559
+ ... hp.simulate()
560
+
561
+ This time, reading input data from files happened during simulation. Likewise, the
562
+ calculated output data is not directly available in RAM but in different NetCDF
563
+ files. To check all results are identical to those shown above, we must load them
564
+ into RAM. Therefore, we first need to prepare the |IOSequence.series| objects
565
+ again:
566
+
567
+ >>> hp.prepare_allseries()
568
+
569
+ By default, *HydPy* handles time series data in simple text files ("asc" files):
570
+
571
+ >>> pub.sequencemanager.filetype
572
+ 'asc'
573
+
574
+ One way to prepare to load the results from the available NetCDF files instead is
575
+ to set the |SequenceManager.filetype| attribute of the public |SequenceManager|
576
+ object to "nc":
577
+
578
+ >>> pub.sequencemanager.filetype = "nc"
579
+
580
+ We can load the previously written results into RAM (see the documentation on
581
+ module |netcdftools| for further information) and inspect the results:
582
+
583
+ >>> with TestIO():
584
+ ... hp.load_modelseries()
585
+ ... hp.load_simseries()
586
+
587
+ >>> round_(model.sequences.inputs.t.series)
588
+ 0.0, -0.5, -2.4, -6.8
589
+
590
+ >>> round_(model.sequences.factors.tc.series[:, 0])
591
+ 1.323207, 0.823207, -1.076793, -5.476793
592
+
593
+ >>> round_(model.sequences.states.sm.series[:, 0])
594
+ 184.958475, 184.763638, 184.610776, 184.553224
595
+
596
+ >>> round_(model.sequences.fluxes.qt.series)
597
+ 11.757526, 8.865079, 7.101815, 5.994195
598
+
599
+ >>> round_(hp.nodes.dill_assl.sequences.sim.series)
600
+ 11.757526, 8.865079, 7.101815, 5.994195
601
+
602
+ You can handle time series in RAM and allow just-in-time NetCDF file access at the
603
+ same time. Before showing how this works, we first disable both functionalities
604
+ for all sequences and delete all previously written NetCDF files:
605
+
606
+ >>> hp.prepare_allseries(allocate_ram=False)
607
+
608
+ >>> attrready(model.sequences.inputs.t, "series")
609
+ False
610
+
611
+ >>> attrready(model.sequences.factors.tc, "series")
612
+ False
613
+
614
+ >>> attrready(model.sequences.states.sm, "series")
615
+ False
616
+
617
+ >>> attrready(model.sequences.fluxes.qt, "series")
618
+ False
619
+
620
+ >>> attrready(hp.nodes.dill_assl.sequences.sim, "series")
621
+ False
622
+
623
+ >>> import os
624
+ >>> with TestIO():
625
+ ... for filename in os.listdir(f"HydPy-H-Lahn/series/default"):
626
+ ... if "input" not in filename:
627
+ ... os.remove(f"HydPy-H-Lahn/series/default/{filename}")
628
+
629
+ We again call method |HydPy.prepare_allseries|, but now, by assigning |True| to the
630
+ arguments `allocate_ram` and `jit`:
631
+
632
+ >>> hp.prepare_allseries(allocate_ram=True, jit=True)
633
+
634
+ After another simulation run, all input data (read during simulation) and output
635
+ data (calculated during simulation) are directly available:
636
+
637
+ >>> with TestIO(), pub.options.checkseries(False):
638
+ ... hp.load_conditions()
639
+ ... hp.simulate()
640
+
641
+ >>> round_(model.sequences.inputs.t.series)
642
+ 0.0, -0.5, -2.4, -6.8
643
+
644
+ >>> round_(model.sequences.factors.tc.series[:, 0])
645
+ 1.323207, 0.823207, -1.076793, -5.476793
646
+
647
+ >>> round_(model.sequences.states.sm.series[:, 0])
648
+ 184.958475, 184.763638, 184.610776, 184.553224
649
+
650
+ >>> round_(model.sequences.fluxes.qt.series)
651
+ 11.757526, 8.865079, 7.101815, 5.994195
652
+
653
+ >>> round_(hp.nodes.dill_assl.sequences.sim.series)
654
+ 11.757526, 8.865079, 7.101815, 5.994195
655
+
656
+ After subsequent deallocation and allocation for refreshing RAM, reading the
657
+ previously written NetCDF files makes the same data available:
658
+
659
+ >>> hp.prepare_allseries(allocate_ram=False)
660
+ >>> hp.prepare_allseries(allocate_ram=True)
661
+ >>> with TestIO():
662
+ ... hp.load_modelseries()
663
+ ... hp.load_simseries()
664
+
665
+ >>> round_(model.sequences.inputs.t.series)
666
+ 0.0, -0.5, -2.4, -6.8
667
+
668
+ >>> round_(model.sequences.factors.tc.series[:, 0])
669
+ 1.323207, 0.823207, -1.076793, -5.476793
670
+
671
+ >>> round_(model.sequences.states.sm.series[:, 0])
672
+ 184.958475, 184.763638, 184.610776, 184.553224
673
+
674
+ >>> round_(model.sequences.fluxes.qt.series)
675
+ 11.757526, 8.865079, 7.101815, 5.994195
676
+
677
+ >>> round_(hp.nodes.dill_assl.sequences.sim.series)
678
+ 11.757526, 8.865079, 7.101815, 5.994195
679
+
680
+ All filenames of meteorological input time series provided by the
681
+ :ref:`HydPy-H-Lahn` example follow a "model-specific" pattern. Each filename
682
+ contains not only the name of the corresponding |InputSequence| subclass (e.g.,
683
+ "t") in lowercase letters but also the sequence's group (e.g., "input") and the
684
+ responsible model's name (e.g., "hland_96"):
685
+
686
+ >>> old_model = hp.elements.land_dill_assl.model
687
+ >>> old_model.sequences.inputs.t.filename
688
+ 'hland_96_input_t.nc'
689
+
690
+ For file types that store the time series of single sequence instances, file names
691
+ must also contain information about the location. Therefore, the relevant
692
+ element's name prefixes the pure model-specific pattern:
693
+
694
+ >>> pub.sequencemanager.filetype = "asc"
695
+ >>> old_model.sequences.inputs.t.filename
696
+ 'land_dill_assl_hland_96_input_t.asc'
697
+
698
+ This naming pattern is exact but not always convenient. For example, assume we
699
+ want to simulate the `dill_assl` subcatchment with application model |hland_96p|
700
+ instead of |hland_96|:
701
+
702
+ >>> from hydpy import prepare_model
703
+ >>> hp.elements.land_dill_assl.model = new_model = prepare_model("hland_96p")
704
+
705
+ |hland_96p| requires the same meteorological input as |hland_96|, for example, the
706
+ air temperature:
707
+
708
+ >>> new_model.prepare_inputseries()
709
+ >>> round_(new_model.sequences.inputs.t.series)
710
+ nan, nan, nan, nan
711
+
712
+ Reading these data from the available files does not work because of the described
713
+ filename convention:
714
+
715
+ >>> with TestIO():
716
+ ... hp.load_inputseries() # doctest: +ELLIPSIS
717
+ Traceback (most recent call last):
718
+ ...
719
+ FileNotFoundError: While trying to load the time series data of sequence `p` of \
720
+ element `land_dill_assl`, the following error occurred: [Errno 2] No such file or \
721
+ directory: '...land_dill_assl_hland_96p_input_p.asc'
722
+
723
+ To circumvent this problem, one can use the standard "HydPy" convention instead of
724
+ the "model-specific" convention when reading or writing input sequences.
725
+
726
+ >>> pub.sequencemanager.convention = "HydPy"
727
+
728
+ Then, the standard names defined by the |StandardInputNames| enum replace the
729
+ model-specific filename parts. For example, the input sequence |hland_inputs.T|
730
+ selects |StandardInputNames.AIR_TEMPERATURE| as its standard name:
731
+
732
+ >>> with pub.sequencemanager.filetype("nc"):
733
+ ... old_model.sequences.inputs.t.filename
734
+ 'air_temperature.nc'
735
+ >>> old_model.sequences.inputs.t.filename
736
+ 'land_dill_assl_air_temperature.asc'
737
+
738
+ If we use the |hland_96| instance to store the meteorological time series based on
739
+ the "HydPy" convention, the |hland_96p| instance can load them without further
740
+ effort:
741
+
742
+ >>> hp.elements.land_dill_assl.model = old_model
743
+ >>> with TestIO():
744
+ ... hp.save_inputseries()
745
+ ... hp.elements.land_dill_assl.model = new_model
746
+ ... hp.load_inputseries()
747
+ >>> round_(old_model.sequences.inputs.t.series)
748
+ 0.0, -0.5, -2.4, -6.8
749
+ >>> round_(new_model.sequences.inputs.t.series)
750
+ 0.0, -0.5, -2.4, -6.8
751
+
752
+ The standard "HydPy" naming convention is compatible with reading data
753
+ "just-in-time" from NetCDF files, even if main models and submodels come with
754
+ different input sequences that require the same data. Before explaining this, we
755
+ restore the original |hland_96| submodel and write all input time series to NetCDF
756
+ files following the standard naming convention:
757
+
758
+ >>> with TestIO():
759
+ ... hp.elements.land_dill_assl.prepare_model()
760
+ ... hp.elements.land_dill_assl.model.load_conditions()
761
+ ... hp.elements.land_dill_assl.prepare_inputseries()
762
+ ... hp.elements.land_dill_assl.load_inputseries()
763
+ ... with pub.sequencemanager.filetype("nc"):
764
+ ... hp.save_inputseries()
765
+
766
+ Now, instead of letting the |evap_pet_hbv96| sub-submodel query the air temperature
767
+ from the |hland_96| main model, we add a |meteo_temp_io| sub-sub-submodel that
768
+ comes with an independent air temperature sequence
769
+
770
+ >>> hland = hp.elements.land_dill_assl.model
771
+ >>> with hland.aetmodel.petmodel.add_tempmodel_v2("meteo_temp_io"):
772
+ ... temperatureaddend(0.0)
773
+ >>> hp.prepare_inputseries(allocate_ram=True, read_jit=True)
774
+ >>> round_(hland.aetmodel.petmodel.tempmodel.sequences.inputs.temperature.series)
775
+ nan, nan, nan, nan
776
+
777
+ Reading data "just-in-time" makes the same data of the "air_temperature.nc" file
778
+ available to both sequences (and leads to the same simulation results):
779
+
780
+ >>> hland.sequences.inputs.t.series = -777.0
781
+ >>> with TestIO():
782
+ ... hp.prepare_fluxseries()
783
+ ... hp.simulate()
784
+ >>> round_(hland.sequences.inputs.t.series)
785
+ 0.0, -0.5, -2.4, -6.8
786
+ >>> round_(hland.aetmodel.petmodel.tempmodel.sequences.inputs.temperature.series)
787
+ 0.0, -0.5, -2.4, -6.8
788
+ >>> round_(hland.sequences.fluxes.qt.series)
789
+ 11.757526, 8.865079, 7.101815, 5.994195
790
+ """
791
+
792
+ _deviceorder: tuple[devicetools.Node | devicetools.Element, ...] | None
793
+
794
+ _nodes: devicetools.Nodes | None
795
+ _elements: devicetools.Elements | None
796
+ _collectives: devicetools.Elements | None
797
+
798
+ def __init__(self, projectname: str | None = None) -> None:
799
+ self._nodes = None
800
+ self._elements = None
801
+ self._collectives = None
802
+ self._deviceorder = None
803
+ if projectname is not None:
804
+ if hydpy.pub.options.checkprojectstructure:
805
+ filetools.check_projectstructure(projectname)
806
+ hydpy.pub.projectname = projectname
807
+ hydpy.pub.networkmanager = filetools.NetworkManager()
808
+ hydpy.pub.controlmanager = filetools.ControlManager()
809
+ hydpy.pub.sequencemanager = filetools.SequenceManager()
810
+ hydpy.pub.conditionmanager = filetools.ConditionManager()
811
+
812
+ nodes = propertytools.Property[devicetools.NodesConstrArg, devicetools.Nodes]()
813
+
814
+ @nodes.getter
815
+ def _get_nodes(self) -> devicetools.Nodes:
816
+ """The currently handled |Node| objects.
817
+
818
+ You are allowed to get, set and delete the currently handled nodes:
819
+
820
+ >>> from hydpy.core.testtools import prepare_full_example_2
821
+ >>> hp, pub, TestIO = prepare_full_example_2()
822
+ >>> hp.nodes
823
+ Nodes("dill_assl", "lahn_kalk", "lahn_leun", "lahn_marb")
824
+
825
+ >>> del hp.nodes
826
+ >>> hp.nodes
827
+ Traceback (most recent call last):
828
+ ...
829
+ hydpy.core.exceptiontools.AttributeNotReady: The actual HydPy instance does \
830
+ not handle any nodes at the moment.
831
+
832
+ >>> hp.nodes = "dill_assl", "lahn_marb"
833
+ >>> hp.nodes
834
+ Nodes("dill_assl", "lahn_marb")
835
+
836
+ However, note that doing so might result in erroneous networks and that you,
837
+ even in case of correctness, must most likely call method |HydPy.update_devices|
838
+ before performing the next simulation run.
839
+ """
840
+ nodes = self._nodes
841
+ if nodes is None:
842
+ raise exceptiontools.AttributeNotReady(
843
+ "The actual HydPy instance does not handle any nodes at the moment."
844
+ )
845
+ return nodes
846
+
847
+ @nodes.setter
848
+ def _set_nodes(self, values: devicetools.NodesConstrArg) -> None:
849
+ self._nodes = devicetools.Nodes(values).copy()
850
+
851
+ @nodes.deleter
852
+ def _del_nodes(self) -> None:
853
+ self._nodes = None
854
+
855
+ elements = propertytools.Property[
856
+ devicetools.ElementsConstrArg, devicetools.Elements
857
+ ]()
858
+
859
+ @elements.getter
860
+ def _get_elements(self) -> devicetools.Elements:
861
+ """The currently handled |Element| objects.
862
+
863
+ You are allowed to get, set and delete the currently handled elements:
864
+
865
+ >>> from hydpy.core.testtools import prepare_full_example_2
866
+ >>> hp, pub, TestIO = prepare_full_example_2()
867
+ >>> hp.elements
868
+ Elements("land_dill_assl", "land_lahn_kalk", "land_lahn_leun",
869
+ "land_lahn_marb", "stream_dill_assl_lahn_leun",
870
+ "stream_lahn_leun_lahn_kalk", "stream_lahn_marb_lahn_leun")
871
+
872
+ >>> del hp.elements
873
+ >>> hp.elements
874
+ Traceback (most recent call last):
875
+ ...
876
+ hydpy.core.exceptiontools.AttributeNotReady: The actual HydPy instance does \
877
+ not handle any elements at the moment.
878
+
879
+ >>> hp.elements = "land_dill_assl", "land_lahn_marb"
880
+ >>> hp.elements
881
+ Elements("land_dill_assl", "land_lahn_marb")
882
+
883
+ However, note that doing so might result in erroneous networks
884
+ and that you, even in case of correctness, must most likely call
885
+ method |HydPy.update_devices| before performing the next
886
+ simulation run.
887
+ """
888
+ elements = self._elements
889
+ if elements is None:
890
+ raise exceptiontools.AttributeNotReady(
891
+ "The actual HydPy instance does not handle any elements at the moment."
892
+ )
893
+ return elements
894
+
895
+ @elements.setter
896
+ def _set_elements(self, values: devicetools.ElementsConstrArg) -> None:
897
+ self._elements = devicetools.Elements(values).copy()
898
+
899
+ @elements.deleter
900
+ def _del_elements(self) -> None:
901
+ self._elements = None
902
+
903
+ @property
904
+ def collectives(self) -> devicetools.Elements:
905
+ """The elements relevant for simulation.
906
+
907
+ Usually, |HydPy.collectives| returns the "original" elements also available via
908
+ property |HydPy.elements|. However, if some of these elements belong to a
909
+ |Element.collective|, |HydPy.collectives| does not return them but
910
+ overarching elements suitable for simulation.
911
+
912
+ |HydPy.collectives| raises the following error if the overarching elements are
913
+ unavailable:
914
+
915
+ >>> from hydpy import HydPy
916
+ >>> HydPy().collectives
917
+ Traceback (most recent call last):
918
+ ...
919
+ RuntimeError: The currently available elements have not been analysed and \
920
+ eventually combined regarding their collectives. Please call method `update_devices` \
921
+ first.
922
+ """
923
+ if (collectives := self._collectives) is None:
924
+ raise RuntimeError(
925
+ "The currently available elements have not been analysed and "
926
+ "eventually combined regarding their collectives. Please call method "
927
+ "`update_devices` first."
928
+ )
929
+ return collectives
930
+
931
+ def prepare_everything(self) -> None:
932
+ """Convenience method to make the actual |HydPy| instance runnable.
933
+
934
+ Method |HydPy.prepare_everything| is the fastest approach to get a runnable
935
+ |HydPy| object. You only need to import class |Hydpy|, initialise it with the
936
+ project name, define the simulation period via the |Timegrids| object of module
937
+ |pub|, and call method |HydPy.prepare_everything| (in this documentation, we
938
+ first need to prepare the example project via function |prepare_full_example_1|
939
+ and change the current working directory via class |TestIO|):
940
+
941
+ >>> from hydpy.core.testtools import prepare_full_example_1
942
+ >>> prepare_full_example_1()
943
+ >>> from hydpy import HydPy, print_vector, pub, TestIO
944
+ >>> with TestIO():
945
+ ... hp = HydPy("HydPy-H-Lahn")
946
+ ... pub.timegrids = "1996-01-01", "1996-01-05", "1d"
947
+ ... hp.prepare_everything()
948
+
949
+ Now, you can start a simulation run and inspect the calculated time series of
950
+ all relevant sequences. We take the discharge values of the flux sequence
951
+ |hland_fluxes.QT| of |Element| object `land_dill_assl` and of the node sequence
952
+ |Sim| of |Node| object `dill_assl` as examples, which provide the same
953
+ information:
954
+
955
+ >>> hp.simulate()
956
+ >>> print_vector(hp.elements.land_dill_assl.model.sequences.fluxes.qt.series)
957
+ 11.757526, 8.865079, 7.101815, 5.994195
958
+ >>> print_vector(hp.nodes.dill_assl.sequences.sim.series)
959
+ 11.757526, 8.865079, 7.101815, 5.994195
960
+
961
+ The observed discharge values are also directly available via the node sequence
962
+ |Obs|:
963
+
964
+ >>> print_vector(hp.nodes.dill_assl.sequences.obs.series)
965
+ 4.84, 5.19, 4.22, 3.65
966
+ """
967
+ self.prepare_network()
968
+ self.prepare_models()
969
+ self.load_conditions()
970
+ self.prepare_nodeseries()
971
+ with hydpy.pub.options.warnmissingobsfile(False):
972
+ self.load_obsseries()
973
+ self.prepare_modelseries()
974
+ self.load_inputseries()
975
+
976
+ @printtools.print_progress
977
+ def prepare_network(self) -> None:
978
+ """Load all network files as |Selections| (stored in module |pub|) and assign
979
+ the |Selections.complete| selection to the |HydPy| object.
980
+
981
+ .. testsetup::
982
+
983
+ >>> from hydpy import pub
984
+ >>> del pub.selections
985
+
986
+ First, we call function |prepare_full_example_1| to prepare the
987
+ :ref:`HydPy-H-Lahn` example project, including its network files
988
+ `headwaters.py`, `nonheadwaters.py`, and `streams.py`:
989
+
990
+ >>> from hydpy.core.testtools import prepare_full_example_1
991
+ >>> prepare_full_example_1()
992
+
993
+ Directly after initialising class |HydPy|, neither the resulting object nor
994
+ module |pub| contains any information from the network files:
995
+
996
+ >>> from hydpy import HydPy, pub, TestIO
997
+ >>> hp = HydPy("HydPy-H-Lahn")
998
+ >>> pub.selections
999
+ Traceback (most recent call last):
1000
+ ...
1001
+ hydpy.core.exceptiontools.AttributeNotReady: Attribute selections of module \
1002
+ `pub` is not defined at the moment.
1003
+
1004
+ By calling the method |HydPy.prepare_network|, one loads all three network
1005
+ files into separate |Selection| objects, all handled by the |Selections| object
1006
+ of module |pub|:
1007
+
1008
+ >>> with TestIO():
1009
+ ... hp.prepare_network()
1010
+ >>> pub.selections
1011
+ Selections("headwaters", "nonheadwaters", "streams")
1012
+
1013
+ Additionally, a |Selection| object named "complete" that covers all |Node| and
1014
+ |Element| objects of the user-defined selections is automatically creatable
1015
+ by property |Selections.complete| of class |Selections|:
1016
+
1017
+ >>> whole = pub.selections.headwaters.copy("whole")
1018
+ >>> whole += pub.selections.nonheadwaters
1019
+ >>> whole += pub.selections.streams
1020
+ >>> whole == pub.selections.complete
1021
+ True
1022
+
1023
+ Initially, the |HydPy| object is aware of the complete set of |Node| and
1024
+ |Element| objects:
1025
+
1026
+ >>> hp.nodes == pub.selections.complete.nodes
1027
+ True
1028
+ >>> hp.elements == pub.selections.complete.elements
1029
+ True
1030
+
1031
+ See the documentation on method |HydPy.update_devices| on how to "activate|
1032
+ another selection in the safest manner.
1033
+ """
1034
+ hydpy.pub.selections = selectiontools.Selections()
1035
+ hydpy.pub.selections.add_selections(*hydpy.pub.networkmanager.load_files())
1036
+ self.update_devices(selection=hydpy.pub.selections.complete, silent=True)
1037
+
1038
+ def prepare_models(self) -> None:
1039
+ """Read all control files related to the current |Element| objects, initialise
1040
+ the defined models, and prepare their parameter values.
1041
+
1042
+ .. testsetup::
1043
+
1044
+ >>> from hydpy import pub
1045
+ >>> del pub.options.parameterstep
1046
+
1047
+ First, we call function |prepare_full_example_1| to prepare the
1048
+ :ref:`HydPy-H-Lahn` example project:
1049
+
1050
+ >>> from hydpy.core.testtools import prepare_full_example_1
1051
+ >>> prepare_full_example_1()
1052
+
1053
+ Now, we can initialise a |HydPy| instance accordingly and call its methods
1054
+ |HydPy.prepare_network| and |HydPy.prepare_models|:
1055
+
1056
+ >>> from hydpy import HydPy, pub, round_, TestIO
1057
+ >>> with TestIO():
1058
+ ... pub.timegrids = "1996-01-01", "1996-01-05", "1d"
1059
+ ... hp = HydPy("HydPy-H-Lahn")
1060
+ ... hp.prepare_network()
1061
+ ... hp.prepare_models()
1062
+
1063
+ As a result, each |Element| object handles a model of the type and with the
1064
+ parameter values defined in the relevant control file:
1065
+
1066
+ >>> hp.elements.land_dill_assl.model.name
1067
+ 'hland_96'
1068
+ >>> hp.elements.land_dill_assl.model.parameters.control.area
1069
+ area(692.3)
1070
+ >>> hp.elements.stream_lahn_marb_lahn_leun.model.name
1071
+ 'musk_classic'
1072
+ >>> hp.elements.stream_lahn_marb_lahn_leun.model.parameters.control.nmbsegments
1073
+ nmbsegments(lag=0.583)
1074
+
1075
+ The :ref:`HydPy-H-Lahn` example project comes with one auxiliary file, named
1076
+ `land.py`. This file defines general parameter values, valid for all single
1077
+ parameter objects of the different model instances referencing this file via
1078
+ the `auxfile` keyword argument. The following examples use the `land_dill_assl`
1079
+ element to show that the affected parameters are also correctly prepared:
1080
+
1081
+ >>> control = hp.elements.land_dill_assl.model.parameters.control
1082
+ >>> control.alpha
1083
+ alpha(1.0)
1084
+ >>> control.pcorr
1085
+ pcorr(1.0)
1086
+ >>> control.resparea
1087
+ resparea(True)
1088
+ >>> control.icmax
1089
+ icmax(field=1.0, forest=1.5)
1090
+
1091
+ We show that the individual |hland_control.IcMax| values for two different
1092
+ elements are different to demonstrate that parameter values defined within a
1093
+ master control file (|hland_control.ZoneType|) can affect the actual values of
1094
+ parameters defined in auxiliary control files:
1095
+
1096
+ >>> from hydpy import round_
1097
+ >>> round_(control.icmax.values)
1098
+ 1.0, 1.5, 1.0, 1.5, 1.0, 1.5, 1.0, 1.5, 1.0, 1.5, 1.0, 1.5
1099
+ >>> round_(
1100
+ ... hp.elements.land_lahn_leun.model.parameters.control.icmax.values)
1101
+ 1.0, 1.5, 1.0, 1.5, 1.0, 1.5, 1.0, 1.5, 1.0, 1.5
1102
+
1103
+ Missing parameter information in auxiliary files results in errors like the
1104
+ following:
1105
+
1106
+ >>> filepath = "HydPy-H-Lahn/control/default/land.py"
1107
+ >>> with TestIO():
1108
+ ... with open(filepath) as infile:
1109
+ ... text = infile.read().replace("alpha(1.0)", "")
1110
+ ... with open(filepath, "w") as outfile:
1111
+ ... outfile.write(text)
1112
+ ... hp.prepare_models() # doctest: +ELLIPSIS
1113
+ Traceback (most recent call last):
1114
+ ...
1115
+ RuntimeError: While trying to initialise the model object of element \
1116
+ `land_dill_assl`, the following error occurred: While trying to load the control file \
1117
+ `...land_dill_assl.py`, the following error occurred: While trying to extract \
1118
+ information for parameter `alpha` from file `land`, the following error occurred: The \
1119
+ selected auxiliary file does not define value(s) for parameter `alpha`.
1120
+
1121
+ Completely wrong control files result in the following error:
1122
+
1123
+ >>> with TestIO():
1124
+ ... with open("HydPy-H-Lahn/control/default/land_dill_assl.py", "w"):
1125
+ ... pass
1126
+ ... hp.prepare_models() # doctest: +ELLIPSIS
1127
+ Traceback (most recent call last):
1128
+ ...
1129
+ RuntimeError: While trying to initialise the model object of element \
1130
+ `land_dill_assl`, the following error occurred: Model parameters cannot be loaded from \
1131
+ control file `...land_dill_assl.py`. Please refer to the HydPy documentation on how \
1132
+ to prepare control files properly.
1133
+ """
1134
+ self.elements.prepare_models()
1135
+
1136
+ def init_models(self) -> None:
1137
+ """Deprecated! Use method |HydPy.prepare_models| instead.
1138
+
1139
+ >>> from hydpy import HydPy
1140
+ >>> from unittest import mock
1141
+ >>> from hydpy.core.testtools import warn_later
1142
+ >>> with warn_later(), mock.patch.object(HydPy, "prepare_models") as mocked:
1143
+ ... hp = HydPy("test")
1144
+ ... hp.init_models()
1145
+ HydPyDeprecationWarning: Method `init_models` of class `HydPy` is \
1146
+ deprecated. Use method `prepare_models` instead.
1147
+ >>> mocked.call_args_list
1148
+ [call()]
1149
+ """
1150
+ self.prepare_models()
1151
+ warnings.warn(
1152
+ "Method `init_models` of class `HydPy` is deprecated. Use method "
1153
+ "`prepare_models` instead.",
1154
+ exceptiontools.HydPyDeprecationWarning,
1155
+ )
1156
+
1157
+ def save_controls(
1158
+ self,
1159
+ parameterstep: timetools.PeriodConstrArg | None = None,
1160
+ simulationstep: timetools.PeriodConstrArg | None = None,
1161
+ auxfiler: auxfiletools.Auxfiler | None = None,
1162
+ ) -> None:
1163
+ """Write the control files of all current |Element| objects.
1164
+
1165
+ .. testsetup::
1166
+
1167
+ >>> from hydpy import pub
1168
+ >>> del pub.options.parameterstep
1169
+
1170
+ We use the :ref:`HydPy-H-Lahn` example project to demonstrate how to write a
1171
+ complete set of parameter control files. For convenience, we let function
1172
+ |prepare_full_example_2| prepare a fully functional |HydPy| object, handling
1173
+ seven |Element| objects controlling four |hland_96| and three |musk_classic|
1174
+ application models:
1175
+
1176
+ >>> from hydpy.core.testtools import prepare_full_example_2
1177
+ >>> hp, pub, TestIO = prepare_full_example_2()
1178
+
1179
+ At first, there is only one control subfolder named "default", containing the
1180
+ seven master control files used in the step above:
1181
+
1182
+ >>> import os
1183
+ >>> with TestIO():
1184
+ ... os.listdir("HydPy-H-Lahn/control")
1185
+ ['default']
1186
+
1187
+ Next, we use the |ControlManager| to create a new directory and write analogue
1188
+ control files into it:
1189
+
1190
+ >>> with TestIO():
1191
+ ... pub.controlmanager.currentdir = "newdir"
1192
+ ... hp.save_controls()
1193
+ ... sorted(os.listdir("HydPy-H-Lahn/control"))
1194
+ ['default', 'newdir']
1195
+
1196
+ First, we focus our examples on the (shorter) control files of the application
1197
+ model |musk_classic|. These control files define the values of the parameters
1198
+ |musk_control.NmbSegments| and |musk_control.Coefficients| via the keyword
1199
+ arguments `lag` and `damp`. For the river channel connecting the outlets of
1200
+ subcatchment `lahn_marb` and `lahn_leun`, the `lag` value is 0.583 days, and the
1201
+ `damp` value is zero:
1202
+
1203
+ >>> model = hp.elements.stream_lahn_marb_lahn_leun.model
1204
+ >>> model.parameters.control
1205
+ nmbsegments(lag=0.583)
1206
+ coefficients(damp=0.0)
1207
+
1208
+ Its control file's name equals the element's name:
1209
+
1210
+ >>> dir_ = "HydPy-H-Lahn/control/newdir/"
1211
+ >>> with TestIO():
1212
+ ... with open(dir_ + "stream_lahn_marb_lahn_leun.py") as controlfile:
1213
+ ... print(controlfile.read())
1214
+ from hydpy.models.musk_classic import *
1215
+ <BLANKLINE>
1216
+ simulationstep("1d")
1217
+ parameterstep("1d")
1218
+ <BLANKLINE>
1219
+ nmbsegments(lag=0.583)
1220
+ coefficients(damp=0.0)
1221
+ <BLANKLINE>
1222
+
1223
+ The time step information stems from the |Timegrid| object available via |pub|:
1224
+
1225
+ >>> pub.timegrids.stepsize
1226
+ Period("1d")
1227
+
1228
+ Use the |Auxfiler| class to avoid redefining the same parameter values in
1229
+ multiple control files. We prepare an |Auxfiler| object that handles the
1230
+ model's two parameters discussed above:
1231
+
1232
+ >>> from hydpy import Auxfiler
1233
+ >>> auxfiler = Auxfiler("musk_classic")
1234
+ >>> auxfiler.musk_classic.add_parameter(
1235
+ ... model.parameters.control.nmbsegments, filename="stream")
1236
+ >>> auxfiler.musk_classic.add_parameter(
1237
+ ... model.parameters.control.coefficients, filename="stream")
1238
+
1239
+ When passing the |Auxfiler| object to the method |HydPy.save_controls|, the
1240
+ control file of element `stream_lahn_marb_lahn_leun` does not define the values
1241
+ of both parameters on its own but references the auxiliary file `stream.py`
1242
+ instead:
1243
+
1244
+ >>> with TestIO():
1245
+ ... pub.controlmanager.currentdir = "newdir"
1246
+ ... hp.save_controls(auxfiler=auxfiler)
1247
+ ... with open(dir_ + "stream_lahn_marb_lahn_leun.py") as controlfile:
1248
+ ... print(controlfile.read())
1249
+ from hydpy.models.musk_classic import *
1250
+ <BLANKLINE>
1251
+ simulationstep("1d")
1252
+ parameterstep("1d")
1253
+ <BLANKLINE>
1254
+ nmbsegments(auxfile="stream")
1255
+ coefficients(auxfile="stream")
1256
+ <BLANKLINE>
1257
+
1258
+ `stream.py` contains the actual value definitions:
1259
+
1260
+ >>> with TestIO():
1261
+ ... with open(dir_ + "stream.py") as controlfile:
1262
+ ... print(controlfile.read())
1263
+ from hydpy.models.musk_classic import *
1264
+ <BLANKLINE>
1265
+ simulationstep("1d")
1266
+ parameterstep("1d")
1267
+ <BLANKLINE>
1268
+ nmbsegments(lag=0.583)
1269
+ coefficients(damp=0.0)
1270
+ <BLANKLINE>
1271
+
1272
+ The |musk_classic| model of element `stream_lahn_leun_lahn_kalk` defines the
1273
+ same value for parameter |musk_control.Coefficients| but a different one for
1274
+ parameter |musk_control.NmbSegments|. Hence, only |musk_control.Coefficients|
1275
+ can reference the control file `stream.py`:
1276
+
1277
+ >>> with TestIO():
1278
+ ... with open(dir_ + "stream_lahn_leun_lahn_kalk.py") as controlfile:
1279
+ ... print(controlfile.read())
1280
+ from hydpy.models.musk_classic import *
1281
+ <BLANKLINE>
1282
+ simulationstep("1d")
1283
+ parameterstep("1d")
1284
+ <BLANKLINE>
1285
+ nmbsegments(lag=0.417)
1286
+ coefficients(auxfile="stream")
1287
+ <BLANKLINE>
1288
+
1289
+ Another option is to pass alternative step size information. The
1290
+ `simulationstep` information, which is no integral part of control files but
1291
+ helpful in testing them, has no impact on the written data. However, passing
1292
+ an alternative `parameterstep` information changes the written values of
1293
+ time-dependent parameters both in the primary and the auxiliary control files:
1294
+
1295
+ >>> with TestIO():
1296
+ ... pub.controlmanager.currentdir = "newdir"
1297
+ ... hp.save_controls(
1298
+ ... auxfiler=auxfiler, parameterstep="2d", simulationstep="1h")
1299
+ ... with open(dir_ + "stream_lahn_marb_lahn_leun.py") as controlfile:
1300
+ ... print(controlfile.read())
1301
+ from hydpy.models.musk_classic import *
1302
+ <BLANKLINE>
1303
+ simulationstep("1h")
1304
+ parameterstep("2d")
1305
+ <BLANKLINE>
1306
+ nmbsegments(auxfile="stream")
1307
+ coefficients(auxfile="stream")
1308
+ <BLANKLINE>
1309
+
1310
+ >>> with TestIO():
1311
+ ... with open(dir_ + "stream.py") as controlfile:
1312
+ ... print(controlfile.read())
1313
+ from hydpy.models.musk_classic import *
1314
+ <BLANKLINE>
1315
+ simulationstep("1h")
1316
+ parameterstep("2d")
1317
+ <BLANKLINE>
1318
+ nmbsegments(lag=0.2915)
1319
+ coefficients(damp=0.0)
1320
+ <BLANKLINE>
1321
+
1322
+ >>> with TestIO():
1323
+ ... with open(dir_ + "stream_lahn_leun_lahn_kalk.py") as controlfile:
1324
+ ... print(controlfile.read())
1325
+ from hydpy.models.musk_classic import *
1326
+ <BLANKLINE>
1327
+ simulationstep("1h")
1328
+ parameterstep("2d")
1329
+ <BLANKLINE>
1330
+ nmbsegments(lag=0.2085)
1331
+ coefficients(auxfile="stream")
1332
+ <BLANKLINE>
1333
+
1334
+ In the :ref:`HydPy-H-Lahn` example project, all |hland_96| instances use an
1335
+ |evap_pet_hbv96| submodel for calculating potential evapotranspiration. The
1336
+ discussed writing mechanisms include such submodels automatically. The written
1337
+ files rely on the preferred "with" block syntax for adding submodels and
1338
+ defining their parameter values:
1339
+
1340
+ >>> with TestIO():
1341
+ ... with open(dir_ + "land_dill_assl.py") as controlfile:
1342
+ ... print(controlfile.read()) # doctest: +ELLIPSIS
1343
+ from hydpy.models.hland_96 import *
1344
+ from hydpy.models import evap_aet_hbv96
1345
+ from hydpy.models import evap_pet_hbv96
1346
+ from hydpy.models import rconc_uh
1347
+ <BLANKLINE>
1348
+ simulationstep("1h")
1349
+ parameterstep("2d")
1350
+ <BLANKLINE>
1351
+ area(692.3)
1352
+ ...
1353
+ gamma(0.0)
1354
+ with model.add_aetmodel_v1(evap_aet_hbv96):
1355
+ temperaturethresholdice(nan)
1356
+ soilmoisturelimit(0.9)
1357
+ excessreduction(0.0)
1358
+ with model.add_petmodel_v1(evap_pet_hbv96):
1359
+ evapotranspirationfactor(1.0)
1360
+ altitudefactor(0.0)
1361
+ precipitationfactor(0.01)
1362
+ airtemperaturefactor(0.1)
1363
+ with model.add_rconcmodel_v1(rconc_uh):
1364
+ uh("triangle", tb=0.18364)
1365
+ <BLANKLINE>
1366
+
1367
+ When delegating parameter value definitions to auxiliary files, it makes no
1368
+ difference if these parameters are members of a main model or a submodel:
1369
+
1370
+ >>> auxfiler = Auxfiler("evap_pet_hbv96")
1371
+ >>> for element in hp.elements.search_keywords("catchment"):
1372
+ ... control = element.model.aetmodel.petmodel.parameters.control
1373
+ ... control.airtemperaturefactor(field=0.2, forest=0.1)
1374
+ >>> auxfiler.evap_pet_hbv96.add_parameter(
1375
+ ... control.airtemperaturefactor, filename="evap",
1376
+ ... keywordarguments=control.airtemperaturefactor.keywordarguments)
1377
+ >>> with TestIO():
1378
+ ... hp.save_controls(
1379
+ ... auxfiler=auxfiler, parameterstep="2d", simulationstep="1h")
1380
+ ... with open(dir_ + "evap.py") as controlfile:
1381
+ ... print(controlfile.read()) # doctest: +ELLIPSIS
1382
+ from hydpy.models.evap_pet_hbv96 import *
1383
+ <BLANKLINE>
1384
+ simulationstep("1h")
1385
+ parameterstep("2d")
1386
+ <BLANKLINE>
1387
+ airtemperaturefactor(field=0.2, forest=0.1)
1388
+ <BLANKLINE>
1389
+ >>> with TestIO():
1390
+ ... with open(dir_ + "land_dill_assl.py") as controlfile:
1391
+ ... print(controlfile.read()) # doctest: +ELLIPSIS
1392
+ from hydpy.models.hland_96 import *
1393
+ ...
1394
+ gamma(0.0)
1395
+ with model.add_aetmodel_v1(evap_aet_hbv96):
1396
+ temperaturethresholdice(nan)
1397
+ soilmoisturelimit(0.9)
1398
+ excessreduction(0.0)
1399
+ with model.add_petmodel_v1(evap_pet_hbv96):
1400
+ evapotranspirationfactor(1.0)
1401
+ altitudefactor(0.0)
1402
+ precipitationfactor(0.01)
1403
+ airtemperaturefactor(auxfile="evap")
1404
+ with model.add_rconcmodel_v1(rconc_uh):
1405
+ uh("triangle", tb=0.18364)
1406
+ <BLANKLINE>
1407
+
1408
+ >>> with TestIO():
1409
+ ... hp.prepare_models()
1410
+ >>> parameters = hp.elements.land_dill_assl.model.aetmodel.petmodel.parameters
1411
+ >>> control = parameters.control
1412
+ >>> control.airtemperaturefactor
1413
+ airtemperaturefactor(field=0.2, forest=0.1)
1414
+
1415
+ The :ref:`HydPy-H-Lahn` example project relies only upon "scalar" submodels
1416
+ (handled by |SubmodelProperty| instances) and not on "vectorial" submodels
1417
+ (handled by |SubmodelsProperty| instances). Therefore, we now prepare an
1418
+ instance of model |sw1d_channel| and add, for a start, two |sw1d_storage|
1419
+ models to it, assign it to a new |Element| object, and show how method
1420
+ |HydPy.save_controls| writes the further information into a control file:
1421
+
1422
+ >>> from hydpy import prepare_model
1423
+ >>> channel = prepare_model("sw1d_channel")
1424
+ >>> channel.parameters.control.nmbsegments(2)
1425
+ >>> with channel.add_storagemodel_v1("sw1d_storage", position=0) as storage:
1426
+ ... length(1.0)
1427
+ ... with storage.add_crosssection_v2("wq_trapeze"):
1428
+ ... nmbtrapezes(1)
1429
+ ... bottomlevels(5.0)
1430
+ ... bottomwidths(10.0)
1431
+ ... sideslopes(0.0)
1432
+ >>> with channel.add_storagemodel_v1("sw1d_storage", position=1) as storage:
1433
+ ... length(2.0)
1434
+ ... with storage.add_crosssection_v2("wq_trapeze"):
1435
+ ... nmbtrapezes(1)
1436
+ ... bottomlevels(5.0)
1437
+ ... bottomwidths(10.0)
1438
+ ... sideslopes(0.0)
1439
+ >>> from hydpy import Element
1440
+ >>> my_channel = Element("my_channel")
1441
+ >>> my_channel.model = channel
1442
+ >>> hp.update_devices(elements=my_channel)
1443
+ >>> with TestIO():
1444
+ ... hp.save_controls()
1445
+ ... with open(dir_ + "my_channel.py") as controlfile:
1446
+ ... print(controlfile.read()) # doctest: +ELLIPSIS
1447
+ from hydpy.models.sw1d_channel import *
1448
+ from hydpy.models import sw1d_storage
1449
+ from hydpy.models import wq_trapeze
1450
+ <BLANKLINE>
1451
+ simulationstep("1d")
1452
+ parameterstep("1d")
1453
+ <BLANKLINE>
1454
+ nmbsegments(2)
1455
+ with model.add_storagemodel_v1(sw1d_storage, position=0):
1456
+ length(1.0)
1457
+ ...
1458
+ with model.add_storagemodel_v1(sw1d_storage, position=1):
1459
+ length(2.0)
1460
+ ...
1461
+ <BLANKLINE>
1462
+
1463
+ We now add one of three possible routing models in the second position to
1464
+ demonstrate the writing mechanism not only for completely missing (like in the
1465
+ last example) but also for incomplete submodel vectors:
1466
+
1467
+ >>> with channel.add_routingmodel_v2("sw1d_lias", position=1):
1468
+ ... lengthupstream(1.0)
1469
+ ... lengthdownstream(2.0)
1470
+ ... stricklercoefficient(30.0)
1471
+ ... timestepfactor(0.7)
1472
+ ... diffusionfactor(0.2)
1473
+ ... with storage.add_crosssection_v2("wq_trapeze"):
1474
+ ... nmbtrapezes(1)
1475
+ ... bottomlevels(5.0)
1476
+ ... bottomwidths(10.0)
1477
+ ... sideslopes(0.0)
1478
+ >>> with TestIO():
1479
+ ... hp.save_controls()
1480
+ ... with open(dir_ + "my_channel.py") as controlfile:
1481
+ ... print(controlfile.read()) # doctest: +ELLIPSIS
1482
+ from hydpy.models.sw1d_channel import *
1483
+ from hydpy.models import sw1d_lias
1484
+ from hydpy.models import sw1d_storage
1485
+ from hydpy.models import wq_trapeze
1486
+ <BLANKLINE>
1487
+ simulationstep("1d")
1488
+ parameterstep("1d")
1489
+ <BLANKLINE>
1490
+ nmbsegments(2)
1491
+ with model.add_routingmodel_v2(sw1d_lias, position=1):
1492
+ lengthupstream(1.0)
1493
+ ...
1494
+ with model.add_storagemodel_v1(sw1d_storage, position=0):
1495
+ length(1.0)
1496
+ ...
1497
+ with model.add_storagemodel_v1(sw1d_storage, position=1):
1498
+ length(2.0)
1499
+ ...
1500
+ <BLANKLINE>
1501
+
1502
+ Finally, we demonstrate the writing mechanism for the case of different
1503
+ submodel types within the same submodel vector:
1504
+
1505
+ >>> with channel.add_routingmodel_v1("sw1d_q_in", position=0):
1506
+ ... lengthdownstream(1.0)
1507
+ >>> with channel.add_routingmodel_v3("sw1d_weir_out", position=2):
1508
+ ... lengthupstream(2.0)
1509
+ >>> with TestIO():
1510
+ ... hp.save_controls()
1511
+ ... with open(dir_ + "my_channel.py") as controlfile:
1512
+ ... print(controlfile.read()) # doctest: +ELLIPSIS
1513
+ from hydpy.models.sw1d_channel import *
1514
+ from hydpy.models import sw1d_lias
1515
+ from hydpy.models import sw1d_q_in
1516
+ from hydpy.models import sw1d_storage
1517
+ from hydpy.models import sw1d_weir_out
1518
+ from hydpy.models import wq_trapeze
1519
+ <BLANKLINE>
1520
+ simulationstep("1d")
1521
+ parameterstep("1d")
1522
+ <BLANKLINE>
1523
+ nmbsegments(2)
1524
+ with model.add_routingmodel_v1(sw1d_q_in, position=0):
1525
+ lengthdownstream(1.0)
1526
+ ...
1527
+ with model.add_routingmodel_v2(sw1d_lias, position=1):
1528
+ lengthupstream(1.0)
1529
+ lengthdownstream(2.0)
1530
+ ...
1531
+ with model.add_routingmodel_v3(sw1d_weir_out, position=2):
1532
+ lengthupstream(2.0)
1533
+ ...
1534
+ with model.add_storagemodel_v1(sw1d_storage, position=0):
1535
+ length(1.0)
1536
+ ...
1537
+ with model.add_storagemodel_v1(sw1d_storage, position=1):
1538
+ length(2.0)
1539
+ ...
1540
+ <BLANKLINE>
1541
+ """
1542
+ self.elements.save_controls(
1543
+ parameterstep=parameterstep,
1544
+ simulationstep=simulationstep,
1545
+ auxfiler=auxfiler,
1546
+ )
1547
+
1548
+ def update_parameters(self) -> None:
1549
+ """Update the derived parameters of all models managed by the respective
1550
+ elements.
1551
+
1552
+ The following examples demonstrate method |HydPy.update_parameters| based on
1553
+ the :ref:`HydPy-H-Lahn` project:
1554
+
1555
+ >>> from hydpy.core.testtools import prepare_full_example_2
1556
+ >>> hp, pub, TestIO = prepare_full_example_2()
1557
+
1558
+ We focus on the Dill subcatchment represented by element `land_dill_assl` and
1559
+ an application model instance of type |hland_96|:
1560
+
1561
+ >>> parameters = hp.elements.land_dill_assl.model.parameters
1562
+
1563
+ |hland_96| has the parameter |hland_derived.QFactor|, which serves as a factor
1564
+ for converting runoff height (mm/T) to runoff volume (m³/s):
1565
+
1566
+ >>> parameters.derived.qfactor
1567
+ qfactor(8.012731)
1568
+
1569
+ Such a factor factor obviously depends on the subcatchment's area. Hence,
1570
+ |hland_derived.QFactor| is implemented as a derived parameter with an
1571
+ |hland_derived.QFactor.update| method that relies on the current value of the
1572
+ control parameter |hland_control.Area|:
1573
+
1574
+ >>> parameters.control.area
1575
+ area(692.3)
1576
+
1577
+ Assume we made an error when transferring the original subcatchment size to our
1578
+ :ref:`HydPy-H-Lahn` project and now want to fix it:
1579
+
1580
+ >>> parameters.control.area(329.6)
1581
+
1582
+ Despite fixing the value of |hland_control.Area|, the value of the
1583
+ |hland_derived.QFactor| instance stays the same:
1584
+
1585
+ >>> parameters.derived.qfactor
1586
+ qfactor(8.012731)
1587
+
1588
+ One now could call method |Model.update_parameters| of the |hland_96| instance
1589
+ to achieve an update of all its derived parameters. However, if one has
1590
+ changed the control parameter values of multiple models, one might find it more
1591
+ convenient to call the |HydPy.update_parameters| of the |HydPy| instance, which
1592
+ then invokes the |Model.update_parameters| of all handled models:
1593
+
1594
+ >>> hp.update_parameters()
1595
+ >>> parameters.derived.qfactor
1596
+ qfactor(3.814815)
1597
+
1598
+ .. warning::
1599
+
1600
+ Updating derived parameters sometimes requires further action to restore
1601
+ runnable models. For example, it might cause a reshaping of condition
1602
+ sequences, which makes defining new initial conditions necessary.
1603
+ """
1604
+ self.elements.update_parameters()
1605
+
1606
+ def load_conditions(self) -> None:
1607
+ """Load all currently relevant initial conditions.
1608
+
1609
+ .. testsetup::
1610
+
1611
+ >>> from hydpy import pub
1612
+ >>> del pub.options.parameterstep
1613
+
1614
+ The following examples demonstrate both the functionality of method
1615
+ |HydPy.load_conditions| and |HydPy.save_conditions| based on the
1616
+ :ref:`HydPy-H-Lahn` project, which we prepare via function
1617
+ |prepare_full_example_2|:
1618
+
1619
+ >>> from hydpy.core.testtools import prepare_full_example_2
1620
+ >>> hp, pub, TestIO = prepare_full_example_2()
1621
+
1622
+ Our |HydPy| instance `hp` is ready for the first simulation run, meaning the
1623
+ required initial conditions are available already. First, we start a
1624
+ simulation run covering the whole initialisation period and inspect the
1625
+ resulting soil moisture values of |Element| `land_dill_assl`, handled by a
1626
+ sequence object of type |hland_states.SM|:
1627
+
1628
+ >>> hp.simulate()
1629
+ >>> sm = hp.elements.land_dill_assl.model.sequences.states.sm
1630
+ >>> sm
1631
+ sm(184.553224, 180.623124, 199.181137, 195.947542, 212.04018, 209.48859,
1632
+ 222.12115, 220.12671, 230.30756, 228.70779, 236.91943, 235.64427)
1633
+
1634
+ By default, method |HydPy.load_conditions| always (re)loads the initial
1635
+ conditions from the directory with its name matching the start date of the
1636
+ simulation period, which we prove by also showing the related content of the
1637
+ respective condition file `land_dill_assl.py`:
1638
+
1639
+ >>> with TestIO():
1640
+ ... hp.load_conditions()
1641
+ >>> sm
1642
+ sm(185.13164, 181.18755, 199.80432, 196.55888, 212.04018, 209.48859,
1643
+ 222.12115, 220.12671, 230.30756, 228.70779, 236.91943, 235.64427)
1644
+
1645
+ >>> path = "HydPy-H-Lahn/conditions/init_1996_01_01_00_00_00/land_dill_assl.py"
1646
+ >>> with TestIO():
1647
+ ... with open(path, "r") as file_:
1648
+ ... lines = file_.read().split("\\n")
1649
+ ... print(lines[8])
1650
+ ... print(lines[9])
1651
+ sm(185.13164, 181.18755, 199.80432, 196.55888, 212.04018, 209.48859,
1652
+ 222.12115, 220.12671, 230.30756, 228.70779, 236.91943, 235.64427)
1653
+
1654
+ Now, we perform two consecutive runs, covering the first and the second half of
1655
+ the initialisation period, respectively, and write, in both cases, the
1656
+ resulting final conditions to disk:
1657
+
1658
+ >>> pub.timegrids.sim.lastdate = "1996-01-03"
1659
+ >>> hp.simulate()
1660
+ >>> sm
1661
+ sm(184.763638, 180.829058, 199.40823, 196.170947, 212.04018, 209.48859,
1662
+ 222.12115, 220.12671, 230.30756, 228.70779, 236.91943, 235.64427)
1663
+ >>> with TestIO():
1664
+ ... hp.save_conditions()
1665
+
1666
+ >>> pub.timegrids.sim.firstdate = "1996-01-03"
1667
+ >>> pub.timegrids.sim.lastdate = "1996-01-05"
1668
+ >>> hp.simulate()
1669
+ >>> with TestIO():
1670
+ ... hp.save_conditions()
1671
+ >>> sm
1672
+ sm(184.553224, 180.623124, 199.181137, 195.947542, 212.04018, 209.48859,
1673
+ 222.12115, 220.12671, 230.30756, 228.70779, 236.91943, 235.64427)
1674
+
1675
+ Analogous to method |HydPy.load_conditions|, method |HydPy.save_conditions|
1676
+ writes the resulting conditions to a directory with its name matching the end
1677
+ date of the simulation period, which we prove by reloading the conditions
1678
+ related to the middle of the initialisation period and showing the relevant
1679
+ file content:
1680
+
1681
+ >>> with TestIO():
1682
+ ... hp.load_conditions()
1683
+ >>> sm
1684
+ sm(184.763638, 180.829058, 199.40823, 196.170947, 212.04018, 209.48859,
1685
+ 222.12115, 220.12671, 230.30756, 228.70779, 236.91943, 235.64427)
1686
+
1687
+ >>> path = "HydPy-H-Lahn/conditions/init_1996_01_03_00_00_00/land_dill_assl.py"
1688
+ >>> with TestIO():
1689
+ ... with open(path, "r") as file_:
1690
+ ... lines = file_.read().split("\\n")
1691
+ ... print(lines[10])
1692
+ ... print(lines[11])
1693
+ sm(184.763638, 180.829058, 199.40823, 196.170947, 212.04018, 209.48859,
1694
+ 222.12115, 220.12671, 230.30756, 228.70779, 236.91943, 235.64427)
1695
+
1696
+ You can define another directory by assigning a different name to the attribute
1697
+ |FileManager.currentdir| of the actual |ConditionManager| instance:
1698
+
1699
+ >>> with TestIO():
1700
+ ... pub.conditionmanager.currentdir = "test"
1701
+ ... hp.save_conditions()
1702
+
1703
+ >>> path = "HydPy-H-Lahn/conditions/test/land_dill_assl.py"
1704
+ >>> with TestIO():
1705
+ ... with open(path, "r") as file_:
1706
+ ... lines = file_.read().split("\\n")
1707
+ ... print(lines[10])
1708
+ ... print(lines[11])
1709
+ sm(184.763638, 180.829058, 199.40823, 196.170947, 212.04018, 209.48859,
1710
+ 222.12115, 220.12671, 230.30756, 228.70779, 236.91943, 235.64427)
1711
+
1712
+ This change remains permanent until you undo it manually:
1713
+
1714
+ >>> sm(0.0)
1715
+ >>> pub.timegrids.sim.firstdate = "1996-01-01"
1716
+ >>> with TestIO():
1717
+ ... hp.load_conditions()
1718
+ >>> sm
1719
+ sm(184.763638, 180.829058, 199.40823, 196.170947, 212.04018, 209.48859,
1720
+ 222.12115, 220.12671, 230.30756, 228.70779, 236.91943, 235.64427)
1721
+
1722
+ >>> with TestIO():
1723
+ ... del pub.conditionmanager.currentdir
1724
+ ... hp.load_conditions()
1725
+ >>> sm
1726
+ sm(185.13164, 181.18755, 199.80432, 196.55888, 212.04018, 209.48859,
1727
+ 222.12115, 220.12671, 230.30756, 228.70779, 236.91943, 235.64427)
1728
+
1729
+ The :ref:`HydPy-H-Lahn` example project relies only upon "scalar" submodels
1730
+ (handled by |SubmodelProperty| instances) and not on "vectorial" submodels
1731
+ (handled by |SubmodelsProperty| instances). Therefore, we now prepare an
1732
+ instance of model |sw1d_channel| and add, for a start, two |sw1d_storage|
1733
+ models to it, assign it to a new |Element| object, and show how method
1734
+ |HydPy.save_conditions| writes their states into a condition file:
1735
+
1736
+ >>> from hydpy import Element, prepare_model
1737
+ >>> channel = prepare_model("sw1d_channel")
1738
+ >>> channel.parameters.control.nmbsegments(2)
1739
+ >>> with channel.add_storagemodel_v1("sw1d_storage", position=0):
1740
+ ... states.watervolume = 10.0
1741
+ >>> with channel.add_storagemodel_v1("sw1d_storage", position=1):
1742
+ ... states.watervolume = 20.0
1743
+ >>> my_channel = Element("my_channel")
1744
+ >>> my_channel.model = channel
1745
+ >>> hp.update_devices(elements=my_channel)
1746
+ >>> pub.timegrids.sim.lastdate = "1996-01-03"
1747
+ >>> path = "HydPy-H-Lahn/conditions/init_1996_01_03_00_00_00/my_channel.py"
1748
+ >>> with TestIO():
1749
+ ... hp.save_conditions()
1750
+ ... with open(path) as conditionfile:
1751
+ ... print(conditionfile.read())
1752
+ from hydpy.models.sw1d_channel import *
1753
+ from hydpy.models import sw1d_storage
1754
+ <BLANKLINE>
1755
+ controlcheck(projectdir=r"HydPy-H-Lahn", controldir="default", stepsize="1d")
1756
+ <BLANKLINE>
1757
+ with model.storagemodels[0].define_conditions(sw1d_storage):
1758
+ watervolume(10.0)
1759
+ with model.storagemodels[1].define_conditions(sw1d_storage):
1760
+ watervolume(20.0)
1761
+ <BLANKLINE>
1762
+
1763
+ For testing, we set both submodels' water volume to zero:
1764
+
1765
+ >>> channel.storagemodels[0].sequences.states.watervolume = 0.0
1766
+ >>> channel.storagemodels[1].sequences.states.watervolume = 0.0
1767
+
1768
+ Loading the previously written condition file restores the original values:
1769
+
1770
+ >>> pub.timegrids.sim.firstdate = "1996-01-03"
1771
+ >>> with TestIO():
1772
+ ... hp.load_conditions()
1773
+ >>> channel.storagemodels[0].sequences.states.watervolume
1774
+ watervolume(10.0)
1775
+ >>> channel.storagemodels[1].sequences.states.watervolume
1776
+ watervolume(20.0)
1777
+
1778
+ We now add one of three possible routing models in the second position to
1779
+ demonstrate the writing mechanism not only for completely missing (like in the
1780
+ last example) but also for incomplete submodel vectors:
1781
+
1782
+ >>> with channel.add_routingmodel_v2("sw1d_lias", position=1, update=False):
1783
+ ... states.discharge(1.1)
1784
+ >>> with TestIO():
1785
+ ... hp.save_conditions()
1786
+ ... with open(path) as conditionfile:
1787
+ ... print(conditionfile.read())
1788
+ from hydpy.models.sw1d_channel import *
1789
+ from hydpy.models import sw1d_lias
1790
+ from hydpy.models import sw1d_storage
1791
+ <BLANKLINE>
1792
+ controlcheck(projectdir=r"HydPy-H-Lahn", controldir="default", stepsize="1d")
1793
+ <BLANKLINE>
1794
+ with model.routingmodels[1].define_conditions(sw1d_lias):
1795
+ discharge(1.1)
1796
+ with model.storagemodels[0].define_conditions(sw1d_storage):
1797
+ watervolume(10.0)
1798
+ with model.storagemodels[1].define_conditions(sw1d_storage):
1799
+ watervolume(20.0)
1800
+ <BLANKLINE>
1801
+ >>> channel.storagemodels[0].sequences.states.watervolume = 0.0
1802
+ >>> channel.storagemodels[1].sequences.states.watervolume = 0.0
1803
+ >>> channel.routingmodels[1].sequences.states.discharge = 0.0
1804
+ >>> with TestIO():
1805
+ ... hp.load_conditions()
1806
+ >>> channel.storagemodels[0].sequences.states.watervolume
1807
+ watervolume(10.0)
1808
+ >>> channel.storagemodels[1].sequences.states.watervolume
1809
+ watervolume(20.0)
1810
+ >>> channel.routingmodels[1].sequences.states.discharge
1811
+ discharge(1.1)
1812
+
1813
+ Finally, we demonstrate the writing mechanism for the case of different
1814
+ submodel types within the same submodel vector:
1815
+
1816
+ >>> with channel.add_routingmodel_v1("sw1d_q_in", position=0):
1817
+ ... states.discharge = 1.0
1818
+ >>> with channel.add_routingmodel_v3("sw1d_weir_out", position=2):
1819
+ ... states.discharge = 1.2
1820
+ >>> with TestIO():
1821
+ ... hp.save_conditions()
1822
+ ... with open(path) as conditionfile:
1823
+ ... print(conditionfile.read())
1824
+ from hydpy.models.sw1d_channel import *
1825
+ from hydpy.models import sw1d_lias
1826
+ from hydpy.models import sw1d_q_in
1827
+ from hydpy.models import sw1d_storage
1828
+ from hydpy.models import sw1d_weir_out
1829
+ <BLANKLINE>
1830
+ controlcheck(projectdir=r"HydPy-H-Lahn", controldir="default", stepsize="1d")
1831
+ <BLANKLINE>
1832
+ with model.routingmodels[0].define_conditions(sw1d_q_in):
1833
+ discharge(1.0)
1834
+ with model.routingmodels[1].define_conditions(sw1d_lias):
1835
+ discharge(1.1)
1836
+ with model.routingmodels[2].define_conditions(sw1d_weir_out):
1837
+ discharge(1.2)
1838
+ with model.storagemodels[0].define_conditions(sw1d_storage):
1839
+ watervolume(10.0)
1840
+ with model.storagemodels[1].define_conditions(sw1d_storage):
1841
+ watervolume(20.0)
1842
+ <BLANKLINE>
1843
+ >>> channel.storagemodels[0].sequences.states.watervolume = 0.0
1844
+ >>> channel.storagemodels[1].sequences.states.watervolume = 0.0
1845
+ >>> channel.routingmodels[0].sequences.states.discharge = 0.0
1846
+ >>> channel.routingmodels[1].sequences.states.discharge = 0.0
1847
+ >>> channel.routingmodels[2].sequences.states.discharge = 0.0
1848
+ >>> with TestIO():
1849
+ ... hp.load_conditions()
1850
+ >>> channel.storagemodels[0].sequences.states.watervolume
1851
+ watervolume(10.0)
1852
+ >>> channel.storagemodels[1].sequences.states.watervolume
1853
+ watervolume(20.0)
1854
+ >>> channel.routingmodels[0].sequences.states.discharge
1855
+ discharge(1.0)
1856
+ >>> channel.routingmodels[1].sequences.states.discharge
1857
+ discharge(1.1)
1858
+ >>> channel.routingmodels[2].sequences.states.discharge
1859
+ discharge(1.2)
1860
+ """
1861
+ self.elements.load_conditions()
1862
+
1863
+ def save_conditions(self) -> None:
1864
+ """Save all currently relevant final conditions.
1865
+
1866
+ See the documentation on method |HydPy.load_conditions| for further information.
1867
+ """
1868
+ self.elements.save_conditions()
1869
+
1870
+ def trim_conditions(self) -> None:
1871
+ """Check all values of the condition sequences (|StateSequence| and
1872
+ |LogSequence| objects) for boundary violations and fix them if necessary.
1873
+
1874
+ We use the :ref:`HydPy-H-Lahn` example project to explain the functionality of
1875
+ the method |HydPy.trim_conditions|, which gives no response when all conditions
1876
+ are correctly set:
1877
+
1878
+ >>> from hydpy.core.testtools import prepare_full_example_2
1879
+ >>> hp, pub, TestIO = prepare_full_example_2()
1880
+ >>> with pub.options.warntrim(True):
1881
+ ... hp.trim_conditions()
1882
+
1883
+ If you try, for example, to set the snow layer's liquid water content
1884
+ (|hland_states.WC|) to a value larger than the allowed fraction of the snow
1885
+ layer's frozen water content (|hland_states.SP|), you get a direct
1886
+ response based on function |trim|:
1887
+
1888
+ >>> from hydpy.core.testtools import warn_later
1889
+ >>> with pub.options.warntrim(True), warn_later(): # doctest: +ELLIPSIS
1890
+ ... hp.elements.land_dill_assl.model.sequences.states.wc(1.0)
1891
+ UserWarning: For variable `wc` of element `land_dill_assl` at least one value \
1892
+ needed to be trimmed. The old and the new value(s) are `1.0, ..., 1.0` and `0.0, \
1893
+ ..., 0.0`, respectively.
1894
+
1895
+ However, changing the allowed fraction (|hland_control.WHC|) without adjusting
1896
+ the conditions cannot be detected automatically. Whenever in doubt, call
1897
+ method |HydPy.trim_conditions| explicitly:
1898
+
1899
+ >>> hp.elements.land_dill_assl.model.sequences.states.sp(10.0)
1900
+ >>> hp.elements.land_dill_assl.model.sequences.states.wc(1.0)
1901
+ >>> hp.elements.land_dill_assl.model.parameters.control.whc(0.0)
1902
+ >>> with pub.options.warntrim(True), warn_later():
1903
+ ... hp.trim_conditions() # doctest: +ELLIPSIS
1904
+ UserWarning: For variable `wc` of element `land_dill_assl` at least one value \
1905
+ needed to be trimmed. The old and the new value(s) are `1.0, ..., 1.0` and `0.0, \
1906
+ ..., 0.0`, respectively.
1907
+ """
1908
+ self.elements.trim_conditions()
1909
+
1910
+ def reset_conditions(self) -> None:
1911
+ """Reset all currently relevant condition sequences.
1912
+
1913
+ Method |HydPy.reset_conditions| is the most convenient way to perform
1914
+ simulations repeatedly for the same period, each time starting from the same
1915
+ initial conditions, e.g. for parameter calibration. Each |StateSequence| and
1916
+ |LogSequence| object remembers the last assigned values and can reactivate them
1917
+ for the mentioned purpose.
1918
+
1919
+ For demonstration, we perform a simulation for the :ref:`HydPy-H-Lahn` example
1920
+ project spanning four days:
1921
+
1922
+ >>> from hydpy.core.testtools import prepare_full_example_2
1923
+ >>> hp, pub, TestIO = prepare_full_example_2()
1924
+ >>> hp.simulate()
1925
+ >>> from hydpy import print_vector
1926
+ >>> print_vector(hp.nodes.lahn_kalk.sequences.sim.series)
1927
+ 54.019337, 37.257561, 31.865308, 28.359542
1928
+
1929
+ Just repeating the simulation gives different results due to applying the final
1930
+ states of the first simulation run as the initial states of the second run:
1931
+
1932
+ >>> hp.simulate()
1933
+ >>> print_vector(hp.nodes.lahn_kalk.sequences.sim.series)
1934
+ 26.196165, 25.047469, 24.227865, 23.307609
1935
+
1936
+ Calling |HydPy.reset_conditions| first allows repeating the first simulation
1937
+ run exactly multiple times:
1938
+
1939
+ >>> hp.reset_conditions()
1940
+ >>> hp.simulate()
1941
+ >>> print_vector(hp.nodes.lahn_kalk.sequences.sim.series)
1942
+ 54.019337, 37.257561, 31.865308, 28.359542
1943
+ >>> hp.reset_conditions()
1944
+ >>> hp.simulate()
1945
+ >>> print_vector(hp.nodes.lahn_kalk.sequences.sim.series)
1946
+ 54.019337, 37.257561, 31.865308, 28.359542
1947
+ """
1948
+ self.elements.reset_conditions()
1949
+
1950
+ @property
1951
+ def conditions(self) -> Conditions:
1952
+ """A nested dictionary that contains the values of all condition sequences of
1953
+ all currently handled models.
1954
+
1955
+ The primary purpose of property |HydPy.conditions| is, similar to method
1956
+ |HydPy.reset_conditions|, to allow repeated calculations starting from the same
1957
+ initial conditions. Nevertheless, |HydPy.conditions| is more flexible when
1958
+ handling multiple conditions, which can, for example, help apply ensemble-based
1959
+ assimilation algorithms.
1960
+
1961
+ For demonstration, we perform simulations for the :ref:`HydPy-H-Lahn` example
1962
+ project spanning the first three months of 1996. We begin with a preparation
1963
+ run beginning on January 1 and ending on February 20:
1964
+
1965
+ >>> from hydpy.core.testtools import prepare_full_example_1
1966
+ >>> prepare_full_example_1()
1967
+ >>> from hydpy import HydPy, pub, TestIO, print_vector
1968
+ >>> with TestIO():
1969
+ ... hp = HydPy("HydPy-H-Lahn")
1970
+ ... pub.timegrids = "1996-01-01", "1996-04-01", "1d"
1971
+ ... hp.prepare_everything()
1972
+ >>> pub.timegrids.sim.lastdate = "1996-02-20"
1973
+ >>> hp.simulate()
1974
+ >>> print_vector(hp.nodes.lahn_kalk.sequences.sim.series[48:52])
1975
+ 74.80616, 97.932993, nan, nan
1976
+
1977
+ At the end of the preparation run, a snow layer is covering the Lahn catchment.
1978
+ In the `lahn_marb` subcatchment, this snow layer contains 13.7 mm of frozen
1979
+ water and 1.3 mm of liquid water:
1980
+
1981
+ >>> lahn1_states = hp.elements.land_lahn_marb.model.sequences.states
1982
+ >>> print_vector([lahn1_states.sp.average_values()])
1983
+ 13.727031
1984
+ >>> print_vector([lahn1_states.wc.average_values()])
1985
+ 1.250427
1986
+
1987
+ Now, we save the current conditions and perform the first simulation run from
1988
+ the 20th day of February until the end of March:
1989
+
1990
+ >>> conditions = hp.conditions
1991
+ >>> hp.nodes.lahn_kalk.sequences.sim.series = 0.0
1992
+ >>> pub.timegrids.sim.firstdate = "1996-02-20"
1993
+ >>> pub.timegrids.sim.lastdate = "1996-04-01"
1994
+ >>> hp.simulate()
1995
+ >>> first = hp.nodes.lahn_kalk.sequences.sim.series.copy()
1996
+ >>> print_vector(first[48:52])
1997
+ 0.0, 0.0, 88.226976, 66.667346
1998
+
1999
+ To exactly repeat the last simulation run, we assign the memorised conditions
2000
+ to property |HydPy.conditions|:
2001
+
2002
+ >>> hp.conditions = conditions
2003
+ >>> print_vector([lahn1_states.sp.average_values()])
2004
+ 13.727031
2005
+ >>> print_vector([lahn1_states.wc.average_values()])
2006
+ 1.250427
2007
+
2008
+ All discharge values of the second simulation run are identical to the ones of
2009
+ the first simulation run:
2010
+
2011
+ >>> hp.nodes.lahn_kalk.sequences.sim.series = 0.0
2012
+ >>> pub.timegrids.sim.firstdate = "1996-02-20"
2013
+ >>> pub.timegrids.sim.lastdate = "1996-04-01"
2014
+ >>> hp.simulate()
2015
+ >>> second = hp.nodes.lahn_kalk.sequences.sim.series.copy()
2016
+ >>> print_vector(second[48:52])
2017
+ 0.0, 0.0, 88.226976, 66.667346
2018
+ >>> from numpy import isclose
2019
+ >>> assert all(isclose(first, second, rtol=0.0, atol=1e-12))
2020
+
2021
+ We selected the snow period as an example due to potential problems with the
2022
+ limited water-holding capacity of the snow layer, which depends on the ice
2023
+ content of the snow layer (|hland_states.SP|) and the relative water-holding
2024
+ capacity (|hland_control.WHC|). Due to this restriction, problems can occur.
2025
+ To give an example, we set |hland_control.WHC| to zero temporarily, apply the
2026
+ memorised conditions, and finally reset the original values of |
2027
+ hland_control.WHC|:
2028
+
2029
+ >>> for element in hp.elements.catchment:
2030
+ ... element.whc = element.model.parameters.control.whc.values
2031
+ ... element.model.parameters.control.whc = 0.0
2032
+ >>> with pub.options.warntrim(False):
2033
+ ... hp.conditions = conditions
2034
+ >>> for element in hp.elements.catchment:
2035
+ ... element.model.parameters.control.whc = element.whc
2036
+
2037
+ Without any water-holding capacity of the snow layer, its water content is zero
2038
+ despite the actual memorised value of 1.1 mm:
2039
+
2040
+ >>> print_vector([lahn1_states.sp.average_values()])
2041
+ 13.727031
2042
+ >>> print_vector([lahn1_states.wc.average_values()])
2043
+ 0.0
2044
+
2045
+ What happens in such conflicts depends on the implementation of the respective
2046
+ application model. For safety, we suggest setting the option
2047
+ |Options.warntrim| to |True| before resetting conditions.
2048
+ """
2049
+ return self.elements.conditions
2050
+
2051
+ @conditions.setter
2052
+ def conditions(self, conditions: Conditions) -> None:
2053
+ self.elements.conditions = conditions
2054
+
2055
+ @property
2056
+ def networkproperties(
2057
+ self,
2058
+ ) -> dict[str, int | dict[str, int] | dict[devicetools.NodeVariableType, int]]:
2059
+ """Some properties of the network defined by the currently relevant |Node| and
2060
+ |Element| objects.
2061
+
2062
+ See the documentation on method |HydPy.print_networkproperties| for further
2063
+ information.
2064
+ """
2065
+ return {
2066
+ "Number of nodes": len(self.nodes),
2067
+ "Number of elements": len(self.elements),
2068
+ "Number of end nodes": len(self.endnodes),
2069
+ "Number of distinct networks": len(self.segregatednetworks),
2070
+ "Applied node variables": self.variables,
2071
+ "Applied model types": self.modeltypes,
2072
+ }
2073
+
2074
+ def print_networkproperties(self) -> None:
2075
+ """Print some properties of the network defined by the currently relevant
2076
+ |Node| and |Element| objects.
2077
+
2078
+ |HydPy.print_networkproperties| is for convenience to summarise specific
2079
+ network measures like |HydPy.segregatednetworks|.
2080
+
2081
+ The :ref:`HydPy-H-Lahn` example project defines a small, single network, with
2082
+ all catchments ultimately discharging to node `lahn_kalk`:
2083
+
2084
+ >>> from hydpy.core.testtools import prepare_full_example_1
2085
+ >>> prepare_full_example_1()
2086
+ >>> from hydpy import HydPy, pub, TestIO
2087
+ >>> pub.timegrids = "1996-01-01", "1996-01-05", "1d"
2088
+ >>> with TestIO():
2089
+ ... hp = HydPy("HydPy-H-Lahn")
2090
+ ... hp.prepare_network()
2091
+ ... hp.prepare_models()
2092
+ >>> hp.print_networkproperties()
2093
+ Number of nodes: 4
2094
+ Number of elements: 7
2095
+ Number of end nodes: 1
2096
+ Number of distinct networks: 1
2097
+ Applied node variables: Q (4)
2098
+ Applied model types: hland_96 (4) and musk_classic (3)
2099
+ """
2100
+ value: str | int | dict[str, int] | dict[devicetools.NodeVariableType, int]
2101
+ for key, value in self.networkproperties.items():
2102
+ if isinstance(value, dict):
2103
+ value = objecttools.enumeration(
2104
+ f"{name} ({nmb})" for name, nmb in value.items()
2105
+ )
2106
+ print(f"{key}: {value}")
2107
+
2108
+ @property
2109
+ def endnodes(self) -> devicetools.Nodes:
2110
+ """All currently relevant |Node| objects that define a downstream endpoint of
2111
+ the network.
2112
+
2113
+ The :ref:`HydPy-H-Lahn` example project defines a small, single network, with
2114
+ all catchments ultimately discharging to node `lahn_kalk`:
2115
+
2116
+ >>> from hydpy.core.testtools import prepare_full_example_1
2117
+ >>> prepare_full_example_1()
2118
+ >>> from hydpy import HydPy, TestIO
2119
+ >>> with TestIO():
2120
+ ... hp = HydPy("HydPy-H-Lahn")
2121
+ ... hp.prepare_network()
2122
+ >>> hp.endnodes
2123
+ Nodes("lahn_kalk")
2124
+
2125
+ After breaking the connection between node `lahn_marb` and its downstream river
2126
+ channel element `stream_lahn_marb_lahn2`, `lahn_marb` also becomes an end node:
2127
+
2128
+ >>> hp.nodes.lahn_marb.exits.remove_device("stream_lahn_marb_lahn_leun",
2129
+ ... force=True)
2130
+ >>> hp.elements.stream_lahn_marb_lahn_leun.inlets.remove_device("lahn_marb",
2131
+ ... force=True)
2132
+ >>> hp.endnodes
2133
+ Nodes("lahn_kalk", "lahn_marb")
2134
+
2135
+ Even with a proper connection to a downstream element, a node counts as an end
2136
+ node as long as these elements are not part of the currently relevant network
2137
+ (meaning, currently handled by the |HydPy| object):
2138
+
2139
+ >>> del hp.elements.stream_dill_assl_lahn_leun
2140
+ >>> hp.nodes.dill_assl.exits
2141
+ Elements("stream_dill_assl_lahn_leun")
2142
+ >>> hp.endnodes
2143
+ Nodes("dill_assl", "lahn_kalk", "lahn_marb")
2144
+
2145
+ Connections with "remote" elements are considered irrelevant:
2146
+
2147
+ >>> stream = hp.elements.stream_lahn_leun_lahn_kalk
2148
+ >>> stream.receivers.add_device(stream.inlets.lahn_leun, force=True)
2149
+ >>> stream.inlets.remove_device("lahn_leun", force=True)
2150
+ >>> hp.endnodes
2151
+ Nodes("dill_assl", "lahn_kalk", "lahn_leun", "lahn_marb")
2152
+ """
2153
+ endnodes = devicetools.Nodes()
2154
+ for node in self.nodes:
2155
+ for element in node.exits:
2156
+ if (element in self.elements) and (node not in element.receivers):
2157
+ break
2158
+ else:
2159
+ endnodes += node
2160
+ return endnodes
2161
+
2162
+ @property
2163
+ def segregatednetworks(self) -> selectiontools.Selections:
2164
+ """The number of segregated networks, as defined by the currently relevant
2165
+ |Node| and |Element| objects.
2166
+
2167
+ Each end node (as defined by property |HydPy.endnodes|) eventually defines a
2168
+ single network, segregated from the networks of other end nodes. Due to the
2169
+ :ref:`HydPy-H-Lahn` example project defining only a single end node, there can
2170
+ be only one segregated network, accordingly:
2171
+
2172
+ >>> from hydpy.core.testtools import prepare_full_example_1
2173
+ >>> prepare_full_example_1()
2174
+ >>> from hydpy import HydPy, TestIO
2175
+ >>> with TestIO():
2176
+ ... hp = HydPy("HydPy-H-Lahn")
2177
+ ... hp.prepare_network()
2178
+ >>> hp.segregatednetworks
2179
+ Selections("lahn_kalk")
2180
+ >>> hp.segregatednetworks.lahn_kalk
2181
+ Selection("lahn_kalk",
2182
+ nodes=("dill_assl", "lahn_kalk", "lahn_leun", "lahn_marb"),
2183
+ elements=("land_dill_assl", "land_lahn_kalk",
2184
+ "land_lahn_leun", "land_lahn_marb",
2185
+ "stream_dill_assl_lahn_leun",
2186
+ "stream_lahn_leun_lahn_kalk",
2187
+ "stream_lahn_marb_lahn_leun"))
2188
+
2189
+ Revisiting the examples of the documentation on property |HydPy.endnodes|, we
2190
+ get the similar results. Note that the segregated networks are always
2191
+ |Selection| objects that do not overlap each other (meaning, no |Node| or
2192
+ |Element| object occurs more than one time):
2193
+
2194
+ >>> hp.nodes.lahn_marb.exits.remove_device("stream_lahn_marb_lahn_leun",
2195
+ ... force=True)
2196
+ >>> hp.elements.stream_lahn_marb_lahn_leun.inlets.remove_device("lahn_marb",
2197
+ ... force=True)
2198
+ >>> hp.segregatednetworks
2199
+ Selections("lahn_kalk", "lahn_marb")
2200
+ >>> hp.segregatednetworks.lahn_marb
2201
+ Selection("lahn_marb",
2202
+ nodes="lahn_marb",
2203
+ elements="land_lahn_marb")
2204
+ >>> hp.segregatednetworks.lahn_kalk
2205
+ Selection("lahn_kalk",
2206
+ nodes=("dill_assl", "lahn_kalk", "lahn_leun"),
2207
+ elements=("land_dill_assl", "land_lahn_kalk",
2208
+ "land_lahn_leun", "stream_dill_assl_lahn_leun",
2209
+ "stream_lahn_leun_lahn_kalk",
2210
+ "stream_lahn_marb_lahn_leun"))
2211
+
2212
+ >>> del hp.elements.stream_dill_assl_lahn_leun
2213
+ >>> hp.nodes.dill_assl.exits
2214
+ Elements("stream_dill_assl_lahn_leun")
2215
+ >>> hp.segregatednetworks
2216
+ Selections("dill_assl", "lahn_kalk", "lahn_marb")
2217
+ >>> hp.segregatednetworks.dill_assl
2218
+ Selection("dill_assl",
2219
+ nodes="dill_assl",
2220
+ elements="land_dill_assl")
2221
+ >>> hp.segregatednetworks.lahn_marb
2222
+ Selection("lahn_marb",
2223
+ nodes="lahn_marb",
2224
+ elements="land_lahn_marb")
2225
+ >>> hp.segregatednetworks.lahn_kalk
2226
+ Selection("lahn_kalk",
2227
+ nodes=("lahn_kalk", "lahn_leun"),
2228
+ elements=("land_lahn_kalk", "land_lahn_leun",
2229
+ "stream_lahn_leun_lahn_kalk",
2230
+ "stream_lahn_marb_lahn_leun"))
2231
+
2232
+
2233
+ >>> stream = hp.elements.stream_lahn_leun_lahn_kalk
2234
+ >>> stream.receivers.add_device(stream.inlets.lahn_leun, force=True)
2235
+ >>> stream.inlets.remove_device("lahn_leun", force=True)
2236
+ >>> hp.segregatednetworks
2237
+ Selections("dill_assl", "lahn_kalk", "lahn_leun", "lahn_marb")
2238
+ >>> hp.segregatednetworks.dill_assl
2239
+ Selection("dill_assl",
2240
+ nodes="dill_assl",
2241
+ elements="land_dill_assl")
2242
+ >>> hp.segregatednetworks.lahn_marb
2243
+ Selection("lahn_marb",
2244
+ nodes="lahn_marb",
2245
+ elements="land_lahn_marb")
2246
+ >>> hp.segregatednetworks.lahn_leun
2247
+ Selection("lahn_leun",
2248
+ nodes="lahn_leun",
2249
+ elements=("land_lahn_leun", "stream_lahn_marb_lahn_leun"))
2250
+ >>> hp.segregatednetworks.lahn_kalk
2251
+ Selection("lahn_kalk",
2252
+ nodes="lahn_kalk",
2253
+ elements=("land_lahn_kalk", "stream_lahn_leun_lahn_kalk"))
2254
+
2255
+ In all the examples above, the number of the end nodes and the number of the
2256
+ segregated networks are identical, which is not the case when two or more
2257
+ networks share the same network. We restore our original network and add two
2258
+ additional end nodes, `nowhere` and `somewhere`, linking the first one with
2259
+ element `stream_lahn_leun_lahn_kalk` and the second one with the additional
2260
+ element `stream_lahn_marb_nowhere`, which we connect to node `lahn_marb`:
2261
+
2262
+ >>> prepare_full_example_1()
2263
+ >>> with TestIO():
2264
+ ... hp = HydPy("HydPy-H-Lahn")
2265
+ ... hp.prepare_network()
2266
+ >>> from hydpy import Element
2267
+ >>> _ = Element("stream_lahn_leun_lahn_kalk", outlets="nowhere")
2268
+ >>> hp.nodes += "nowhere"
2269
+ >>> hp.elements += Element("stream_lahn_marb_nowhere",
2270
+ ... inlets="lahn_marb",
2271
+ ... outlets="somewhere")
2272
+ >>> hp.nodes += "somewhere"
2273
+
2274
+ Now, there are three end nodes but only two segregated networks, as node
2275
+ `nowhere` does not reference any upstream devices, which is not also referenced
2276
+ by node `lahn_kalk`. The unique feature of the elements `lahn_kalk` and
2277
+ `stream_lahn_marb_nowhere` is that they drain to either node `lahn_kalk` or
2278
+ `somewhere` but not both, which is why they are the only members of selection
2279
+ `lahn_kalk` and `somewhere`, respectively:
2280
+
2281
+ >>> hp.endnodes
2282
+ Nodes("lahn_kalk", "nowhere", "somewhere")
2283
+ >>> hp.segregatednetworks
2284
+ Selections("lahn_kalk", "somewhere")
2285
+ >>> hp.segregatednetworks.lahn_kalk
2286
+ Selection("lahn_kalk",
2287
+ nodes="lahn_kalk",
2288
+ elements="land_lahn_kalk")
2289
+ >>> hp.segregatednetworks.somewhere
2290
+ Selection("somewhere",
2291
+ nodes="somewhere",
2292
+ elements="stream_lahn_marb_nowhere")
2293
+ """
2294
+ sels1, sels2 = selectiontools.Selections(), selectiontools.Selections()
2295
+ whole = selectiontools.Selection("whole", self.nodes, self.elements)
2296
+ for node in self.endnodes:
2297
+ sel = whole.search_upstream(device=node, name=node.name, inclusive=False)
2298
+ sels1.add_selections(sel)
2299
+ sels2.add_selections(sel.copy(node.name))
2300
+ for sel1, sel2 in itertools.product(sels1, sels2):
2301
+ if sel1.name != sel2.name:
2302
+ sel1 -= sel2
2303
+ for name in tuple(sels1.names):
2304
+ if not sels1[name].elements:
2305
+ del sels1[name]
2306
+ return sels1
2307
+
2308
+ @property
2309
+ def variables(self) -> dict[devicetools.NodeVariableType, int]:
2310
+ """Summary of all |Node.variable| properties of the currently relevant |Node|
2311
+ objects.
2312
+
2313
+ >>> from hydpy.core.testtools import prepare_full_example_1
2314
+ >>> prepare_full_example_1()
2315
+ >>> from hydpy import HydPy, TestIO
2316
+ >>> with TestIO():
2317
+ ... hp = HydPy("HydPy-H-Lahn")
2318
+ ... hp.prepare_network()
2319
+ >>> hp.variables
2320
+ {'Q': 4}
2321
+
2322
+ >>> from hydpy import FusedVariable, Node
2323
+ >>> from hydpy.aliases import hland_inputs_T
2324
+ >>> hp.nodes += Node("test", variable=FusedVariable("T", hland_inputs_T))
2325
+ >>> hp.variables
2326
+ {'Q': 4, FusedVariable("T", hland_inputs_T): 1}
2327
+ """
2328
+ variables: collections.defaultdict[
2329
+ (
2330
+ str
2331
+ | type[sequencetools.InputSequence]
2332
+ | type[sequencetools.InletSequence]
2333
+ | type[sequencetools.ReceiverSequence]
2334
+ | type[sequencetools.OutputSequence]
2335
+ | type[sequencetools.OutletSequence]
2336
+ | type[sequencetools.SenderSequence]
2337
+ | devicetools.FusedVariable
2338
+ ),
2339
+ int,
2340
+ ] = collections.defaultdict(int)
2341
+ for node in self.nodes:
2342
+ variables[node.variable] += 1
2343
+ return dict(
2344
+ sorted(
2345
+ variables.items(),
2346
+ key=lambda tuple_: f"{(tuple_[0])}{str(tuple_[1]).rjust(9)}",
2347
+ )
2348
+ )
2349
+
2350
+ @property
2351
+ def modeltypes(self) -> dict[str, int]:
2352
+ """Summary of all |Model| subclasses of the currently relevant |Element|
2353
+ objects.
2354
+
2355
+ >>> from hydpy.core.testtools import prepare_full_example_1
2356
+ >>> prepare_full_example_1()
2357
+ >>> from hydpy import HydPy, pub, TestIO
2358
+ >>> with TestIO():
2359
+ ... hp = HydPy("HydPy-H-Lahn")
2360
+ ... hp.prepare_network()
2361
+ >>> hp.modeltypes
2362
+ {'unprepared': 7}
2363
+
2364
+ >>> pub.timegrids = "1996-01-01", "1996-01-05", "1d"
2365
+ >>> with TestIO():
2366
+ ... hp.prepare_models()
2367
+ >>> hp.modeltypes
2368
+ {'hland_96': 4, 'musk_classic': 3}
2369
+ """
2370
+ modeltypes: collections.defaultdict[str, int]
2371
+ modeltypes = collections.defaultdict(int)
2372
+ for element in self.elements:
2373
+ model = exceptiontools.getattr_(
2374
+ element,
2375
+ "model",
2376
+ "unprepared",
2377
+ modeltools.Model, # type: ignore[type-abstract]
2378
+ )
2379
+ modeltypes[str(model)] += 1
2380
+ return dict(sorted(modeltypes.items()))
2381
+
2382
+ @overload
2383
+ def update_devices(
2384
+ self, *, selection: selectiontools.Selection, silent: bool = False
2385
+ ) -> None: ...
2386
+
2387
+ @overload
2388
+ def update_devices(
2389
+ self,
2390
+ *,
2391
+ nodes: devicetools.NodesConstrArg | None = None,
2392
+ elements: devicetools.ElementsConstrArg | None = None,
2393
+ silent: bool = False,
2394
+ ) -> None: ...
2395
+
2396
+ def update_devices(
2397
+ self,
2398
+ *,
2399
+ selection: selectiontools.Selection | None = None,
2400
+ nodes: devicetools.NodesConstrArg | None = None,
2401
+ elements: devicetools.ElementsConstrArg | None = None,
2402
+ silent: bool = False,
2403
+ ) -> None:
2404
+ """Determine the order in which method |HydPy.simulate| processes the currently
2405
+ relevant |Node| and |Element| objects.
2406
+
2407
+ Passed |Node| and |Element| objects (for example, contained within a
2408
+ |Selection| object) replace existing ones.
2409
+
2410
+ As described in the documentation on the method |HydPy.prepare_network|, a
2411
+ |HydPy| object usually starts with the complete network of the considered
2412
+ project:
2413
+
2414
+ >>> from hydpy.core.testtools import prepare_full_example_2
2415
+ >>> hp, pub, TestIO = prepare_full_example_2()
2416
+
2417
+ The safest approach to "activate" another selection is to use the method
2418
+ |HydPy.update_devices|. The first option is to pass a |Selection| object:
2419
+
2420
+ >>> pub.selections.headwaters
2421
+ Selection("headwaters",
2422
+ nodes=("dill_assl", "lahn_marb"),
2423
+ elements=("land_dill_assl", "land_lahn_marb"))
2424
+
2425
+ >>> hp.update_devices(selection=pub.selections.headwaters)
2426
+ >>> hp.nodes
2427
+ Nodes("dill_assl", "lahn_marb")
2428
+ >>> hp.elements
2429
+ Elements("land_dill_assl", "land_lahn_marb")
2430
+
2431
+ Method |HydPy.update_devices| automatically updates the `deviceorder`, assuring
2432
+ method |HydPy.simulate| processes "upstream" model instances before it
2433
+ processes their "downstream" neighbours:
2434
+
2435
+ >>> for device in hp.deviceorder:
2436
+ ... print(device)
2437
+ land_dill_assl
2438
+ land_lahn_marb
2439
+ dill_assl
2440
+ lahn_marb
2441
+
2442
+ Second, you can pass some nodes only, which, by the way, removes the old
2443
+ elements:
2444
+
2445
+ >>> hp.update_devices(nodes="dill_assl")
2446
+ >>> hp.nodes
2447
+ Nodes("dill_assl")
2448
+ >>> hp.elements
2449
+ Elements()
2450
+ >>> for device in hp.deviceorder:
2451
+ ... print(device)
2452
+ dill_assl
2453
+
2454
+ Third, you can pass some elements only, which, by the way, removes the old
2455
+ nodes:
2456
+
2457
+ >>> hp.update_devices(elements=["land_lahn_marb", "land_dill_assl"])
2458
+ >>> hp.nodes
2459
+ Nodes()
2460
+ >>> hp.elements
2461
+ Elements("land_dill_assl", "land_lahn_marb")
2462
+ >>> for device in hp.deviceorder:
2463
+ ... print(device)
2464
+ land_dill_assl
2465
+ land_lahn_marb
2466
+
2467
+ Fourth, you can pass nodes and elements at the same time:
2468
+
2469
+ >>> hp.update_devices(nodes="dill_assl", elements=["land_lahn_marb",
2470
+ ... "land_dill_assl"])
2471
+ >>> hp.nodes
2472
+ Nodes("dill_assl")
2473
+ >>> hp.elements
2474
+ Elements("land_dill_assl", "land_lahn_marb")
2475
+ >>> for device in hp.deviceorder:
2476
+ ... print(device)
2477
+ land_dill_assl
2478
+ land_lahn_marb
2479
+ dill_assl
2480
+
2481
+ Fifth, you can pass no argument at all, which only updates the device order:
2482
+
2483
+ >>> del hp.nodes.dill_assl
2484
+ >>> for device in hp.deviceorder:
2485
+ ... print(device)
2486
+ land_dill_assl
2487
+ land_lahn_marb
2488
+ dill_assl
2489
+ >>> hp.update_devices()
2490
+ >>> for device in hp.deviceorder:
2491
+ ... print(device)
2492
+ land_dill_assl
2493
+ land_lahn_marb
2494
+
2495
+ Method |HydPy.update_devices| does not allow using the `selections` argument
2496
+ and the `nodes` or `elements` argument simultaneously:
2497
+
2498
+ >>> hp.update_devices(selection=pub.selections.headwaters, nodes="dill_assl")
2499
+ Traceback (most recent call last):
2500
+ ...
2501
+ ValueError: Method `update_devices` of class `HydPy` does not allow to use \
2502
+ both the `selection` argument and the `nodes` or the `elements` argument at the same \
2503
+ time.
2504
+
2505
+ >>> hp.update_devices(selection=pub.selections.headwaters,
2506
+ ... elements=["land_lahn_marb", "land_dill_assl"])
2507
+ Traceback (most recent call last):
2508
+ ...
2509
+ ValueError: Method `update_devices` of class `HydPy` does not allow to use \
2510
+ both the `selection` argument and the `nodes` or the `elements` argument at the same \
2511
+ time.
2512
+
2513
+ Elements of the same |Element.collective| require special attention, as they
2514
+ and their models must be combined (at the latest before simulation). Method
2515
+ |HydPy.update_devices| raises the following error if any model is unavailable:
2516
+
2517
+ >>> from hydpy import Element, Node
2518
+ >>> junction = Node("junction")
2519
+ >>> reach1 = Element("reach1", collective="network", inlets=junction)
2520
+ >>> reach2 = Element("reach2", collective="network", outlets=junction)
2521
+ >>> hp.update_devices(elements=(reach1, reach2), nodes=junction)
2522
+ Traceback (most recent call last):
2523
+ ...
2524
+ hydpy.core.exceptiontools.AttributeNotReady: While trying to unite the \
2525
+ elements belonging to collective `network`, the following error occurred: The model \
2526
+ object of element `reach1` has been requested but not been prepared so far.
2527
+
2528
+ After such an error, at least the passed devices are accessible:
2529
+
2530
+ >>> hp.nodes
2531
+ Nodes("junction")
2532
+ >>> hp.elements
2533
+ Elements("reach1", "reach2")
2534
+
2535
+ Use the `silent` argument if you are aware of the incompleteness of the current
2536
+ project configuration and want to avoid capturing the error:
2537
+
2538
+ >>> hp.update_devices(selection=pub.selections.headwaters)
2539
+ >>> hp.update_devices(elements=(reach1, reach2), nodes=junction, silent=True)
2540
+ >>> hp.nodes
2541
+ Nodes("junction")
2542
+ >>> hp.elements
2543
+ Elements("reach1", "reach2")
2544
+
2545
+ Of course, silencing the error still does not mean |HydPy.update_devices|
2546
+ can do the complete work, which one might realise later when trying to access
2547
+ one of the unprepared properties:
2548
+
2549
+ >>> hp.deviceorder
2550
+ Traceback (most recent call last):
2551
+ ...
2552
+ hydpy.core.exceptiontools.AttributeNotReady: While trying to access the \
2553
+ simulation order of the currently relevant devices, the following error occurred: \
2554
+ While trying to unite the elements belonging to collective `network`, the following \
2555
+ error occurred: The model object of element `reach1` has been requested but not been \
2556
+ prepared so far.
2557
+ """
2558
+ if (nodes is not None) or (elements is not None):
2559
+ if selection is not None:
2560
+ raise ValueError(
2561
+ "Method `update_devices` of class `HydPy` does not allow to use "
2562
+ "both the `selection` argument and the `nodes` or the `elements` "
2563
+ "argument at the same time."
2564
+ )
2565
+ del self.nodes
2566
+ if nodes is None:
2567
+ nodes = devicetools.Nodes()
2568
+ self.nodes = nodes
2569
+ del self.elements
2570
+ if elements is None:
2571
+ elements = devicetools.Elements()
2572
+ self.elements = elements
2573
+ if selection is not None:
2574
+ self.nodes = selection.nodes
2575
+ self.elements = selection.elements
2576
+ self._update_collectives_and_deviceorder(silent=silent)
2577
+
2578
+ def _update_collectives_and_deviceorder(self, silent: bool):
2579
+ try:
2580
+ self._collectives = self.elements.unite_collectives()
2581
+ except exceptiontools.AttributeNotReady as exc:
2582
+ self._collectives = None
2583
+ self._deviceorder = None
2584
+ if not silent:
2585
+ raise exc
2586
+ else:
2587
+ graph = create_directedgraph(self.nodes, self._collectives)
2588
+ devices = tuple(networkx.topological_sort(graph))
2589
+ names = set(self.nodes.names)
2590
+ names.update(self._collectives.names)
2591
+ self._deviceorder = tuple(d for d in devices if d.name in names)
2592
+
2593
+ @property
2594
+ def deviceorder(self) -> tuple[devicetools.Node | devicetools.Element, ...]:
2595
+ """The simulation order of the currently selected devices.
2596
+
2597
+ |HydPy| needs to know the devices before determining their order:
2598
+
2599
+ >>> from hydpy import HydPy
2600
+ >>> HydPy().deviceorder
2601
+ Traceback (most recent call last):
2602
+ ...
2603
+ hydpy.core.exceptiontools.AttributeNotReady: While trying to access the \
2604
+ simulation order of the currently relevant devices, the following error occurred: The \
2605
+ actual HydPy instance does not handle any elements at the moment.
2606
+
2607
+ Usually, |HydPy.deviceorder| is ready after calling |HydPy.prepare_network|.
2608
+ |HydPy.update_devices| updates the given devices' order, too.
2609
+ """
2610
+ try:
2611
+ if self._deviceorder is None:
2612
+ self._update_collectives_and_deviceorder(silent=False)
2613
+ assert (deviceorder := self._deviceorder) is not None
2614
+ return deviceorder
2615
+ except BaseException:
2616
+ objecttools.augment_excmessage(
2617
+ "While trying to access the simulation order of the currently "
2618
+ "relevant devices"
2619
+ )
2620
+
2621
+ @property
2622
+ def methodorder(self) -> list[Callable[[int], None]]:
2623
+ """All methods of the currently relevant |Node| and |Element| objects, which
2624
+ are to be processed by method |HydPy.simulate| during a simulation time step,
2625
+ in a working execution order.
2626
+
2627
+ Property |HydPy.methodorder| should be of interest to framework developers only.
2628
+ """
2629
+ # due to https://github.com/python/mypy/issues/9718:
2630
+ # pylint: disable=consider-using-in
2631
+
2632
+ funcs: list[Callable[[int], None]] = []
2633
+ if exceptiontools.attrready(hydpy.pub, "sequencemanager"):
2634
+ funcs.append(hydpy.pub.sequencemanager.read_netcdfslices)
2635
+ for node in self.nodes:
2636
+ if (
2637
+ (dm := node.deploymode) == "oldsim"
2638
+ or dm == "obs_oldsim"
2639
+ or dm == "oldsim_bi"
2640
+ or dm == "obs_oldsim_bi"
2641
+ ):
2642
+ funcs.append(node.sequences.fastaccess.load_simdata)
2643
+ elif dm == "newsim" or dm == "obs" or dm == "obs_newsim" or dm == "obs_bi":
2644
+ funcs.append(node.sequences.fastaccess.reset)
2645
+ else:
2646
+ assert_never(dm)
2647
+ funcs.append(node.sequences.fastaccess.load_obsdata)
2648
+ for device in self.deviceorder:
2649
+ if isinstance(device, devicetools.Element):
2650
+ funcs.append(device.model.simulate)
2651
+ elif (
2652
+ (dm := device.deploymode) == "obs_newsim"
2653
+ or dm == "obs_oldsim"
2654
+ or dm == "obs_oldsim_bi"
2655
+ ):
2656
+ funcs.append(device.sequences.fastaccess.fill_obsdata)
2657
+ elif not (
2658
+ dm == "newsim"
2659
+ or dm == "oldsim"
2660
+ or dm == "obs"
2661
+ or dm == "oldsim_bi"
2662
+ or dm == "obs_bi"
2663
+ ):
2664
+ assert_never(dm)
2665
+ elements = self.collectives
2666
+ for element in elements:
2667
+ funcs.append(element.model.update_senders)
2668
+ for element in elements:
2669
+ funcs.append(element.model.update_receivers)
2670
+ for element in elements:
2671
+ funcs.append(element.model.save_data)
2672
+ for node in self.nodes:
2673
+ if (
2674
+ (dm := node.deploymode) == "obs_newsim"
2675
+ or dm == "obs_oldsim"
2676
+ or dm == "obs_oldsim_bi"
2677
+ ):
2678
+ funcs.append(node.sequences.fastaccess.reset_obsdata)
2679
+ elif not (
2680
+ dm == "newsim"
2681
+ or dm == "oldsim"
2682
+ or dm == "obs"
2683
+ or dm == "oldsim_bi"
2684
+ or dm == "obs_bi"
2685
+ ):
2686
+ assert_never(dm)
2687
+ funcs.append(node.sequences.fastaccess.save_simdata)
2688
+ funcs.append(node.sequences.fastaccess.save_obsdata)
2689
+ if exceptiontools.attrready(hydpy.pub, "sequencemanager"):
2690
+ funcs.append(hydpy.pub.sequencemanager.write_netcdfslices)
2691
+ return funcs
2692
+
2693
+ @printtools.print_progress
2694
+ def simulate(self) -> None:
2695
+ """Perform a simulation run over the actual simulation period defined by the
2696
+ |Timegrids| object stored in module |pub|.
2697
+
2698
+ We let function |prepare_full_example_2| prepare a runnable |HydPy| object
2699
+ related to the :ref:`HydPy-H-Lahn` example project:
2700
+
2701
+ >>> from hydpy.core.testtools import prepare_full_example_2
2702
+ >>> hp, pub, TestIO = prepare_full_example_2()
2703
+
2704
+ First, we execute a default simulation run covering the whole simulation period
2705
+ and inspect the discharge series simulated at the outlet of the river basin,
2706
+ represented by node `lahn_kalk`:
2707
+
2708
+ >>> hp.simulate()
2709
+ >>> from hydpy import round_
2710
+ >>> round_(hp.nodes.lahn_kalk.sequences.sim.series)
2711
+ 54.019337, 37.257561, 31.865308, 28.359542
2712
+
2713
+ After resetting the initial conditions via method |HydPy.reset_conditions|, we
2714
+ repeat the simulation run and get the same results:
2715
+
2716
+ >>> hp.reset_conditions()
2717
+ >>> hp.simulate()
2718
+ >>> round_(hp.nodes.lahn_kalk.sequences.sim.series)
2719
+ 54.019337, 37.257561, 31.865308, 28.359542
2720
+
2721
+ Simulation runs do not need to cover the whole initialisation period at once.
2722
+ After setting the |Timegrid.lastdate| property of the `sim` |Timegrid| of the
2723
+ |Timegrids| objects stored within module |pub| to the middle of the
2724
+ initialisation period, method |HydPy.simulate| calculates the first two
2725
+ discharge values only:
2726
+
2727
+ >>> hp.reset_conditions()
2728
+ >>> hp.nodes.lahn_kalk.sequences.sim.series = 0.0
2729
+ >>> pub.timegrids.sim.lastdate = "1996-01-03"
2730
+ >>> hp.simulate()
2731
+ >>> round_(hp.nodes.lahn_kalk.sequences.sim.series)
2732
+ 54.019337, 37.257561, 0.0, 0.0
2733
+
2734
+ After adjusting both the |Timegrid.firstdate| and |Timegrid.lastdate| of the
2735
+ `sim` |Timegrid| to the second half of the initialisation period,
2736
+ |HydPy.simulate| completes the time series:
2737
+
2738
+ >>> pub.timegrids.sim.firstdate = "1996-01-03"
2739
+ >>> pub.timegrids.sim.lastdate = "1996-01-05"
2740
+ >>> hp.simulate()
2741
+ >>> round_(hp.nodes.lahn_kalk.sequences.sim.series)
2742
+ 54.019337, 37.257561, 31.865308, 28.359542
2743
+
2744
+ In the above examples, each |Model| object (handled by an |Element| object)
2745
+ passes its simulated values via a |Node| object to its downstream |Model|
2746
+ object. There are four ways to deviate from this default behaviour that can be
2747
+ selected for each node individually via the property |Node.deploymode|. We
2748
+ focus on node `lahn_leun` as the upstream neighbour of node `lahn_kalk`. So
2749
+ far, its deploy mode is `newsim`, meaning that the node passes newly calculated
2750
+ simulation values to the downstream element `stream_lahn_leun_lahn_kalk`:
2751
+
2752
+ >>> hp.nodes.lahn_leun.deploymode
2753
+ 'newsim'
2754
+
2755
+ Under the second option, `oldsim`, node `lahn_leun` does not pass the discharge
2756
+ values simulated in the next simulation run, but the "old" discharge values
2757
+ already available by the |IOSequence.series| array of the |Sim| sequence. This
2758
+ behaviour can, for example, be useful when calibrating subsequent subareas of a
2759
+ river basin sequentially, beginning with the headwaters and continuing with
2760
+ their downstream neighbours. For the clarity of this example, we decrease all
2761
+ values of the "old" simulated series of node `lahn_leun` by 10 m³/s:
2762
+
2763
+ >>> round_(hp.nodes.lahn_leun.sequences.sim.series)
2764
+ 42.346475, 27.157472, 22.88099, 20.156836
2765
+ >>> hp.nodes.lahn_leun.deploymode = "oldsim"
2766
+ >>> hp.nodes.lahn_leun.sequences.sim.series -= 10.0
2767
+
2768
+ After performing another simulation run (over the whole initialisation period,
2769
+ again), the modified discharge values of node `lahn_leun` are unchanged. The
2770
+ simulated values of node `lahn_kalk` are, compared to the `newsim` runs,
2771
+ decreased by 10 m³/s (there is no time delay or dampening of the discharge
2772
+ values between both nodes due to the lag time of application model
2773
+ |musk_classic| being shorter than the simulation time step):
2774
+
2775
+ >>> hp.reset_conditions()
2776
+ >>> pub.timegrids.sim.firstdate = "1996-01-01"
2777
+ >>> pub.timegrids.sim.lastdate = "1996-01-05"
2778
+ >>> hp.simulate()
2779
+ >>> round_(hp.nodes.lahn_leun.sequences.sim.series)
2780
+ 32.346475, 17.157472, 12.88099, 10.156836
2781
+ >>> round_(hp.nodes.lahn_kalk.sequences.sim.series)
2782
+ 44.019337, 27.257561, 21.865308, 18.359542
2783
+
2784
+ The third option is `obs`, where node `lahn_leun` receives and stores the values
2785
+ from its upstream models but passes other, observed values, handled by sequence
2786
+ |Obs|, which we, for simplicity, set to zero for the complete initialisation
2787
+ and simulation period (more often, one would read measured data from files via
2788
+ methods as |HydPy.load_obsseries|):
2789
+
2790
+ >>> hp.nodes.lahn_leun.deploymode = "obs"
2791
+ >>> hp.nodes.lahn_leun.sequences.obs.series = 0.0
2792
+
2793
+ Now, the simulated values of node `lahn_leun` are identical with the ones of the
2794
+ `newsim` example, but the simulated values of node `lahn_kalk` are lower due to
2795
+ receiving the observed instead of the simulated values from upstream:
2796
+
2797
+ >>> hp.reset_conditions()
2798
+ >>> hp.nodes.lahn_kalk.sequences.sim.series = 0.0
2799
+ >>> hp.simulate()
2800
+ >>> round_(hp.nodes.lahn_leun.sequences.obs.series)
2801
+ 0.0, 0.0, 0.0, 0.0
2802
+ >>> round_(hp.nodes.lahn_leun.sequences.sim.series)
2803
+ 42.346475, 27.157472, 22.88099, 20.156836
2804
+ >>> round_(hp.nodes.lahn_kalk.sequences.sim.series)
2805
+ 11.672862, 10.100089, 8.984317, 8.202706
2806
+
2807
+ Unfortunately, observation time series are often incomplete. *HydPy* generally
2808
+ uses |numpy| |numpy.nan| to represent missing values. Passing |numpy.nan|
2809
+ inputs to a model usually results in |numpy.nan| outputs. Hence, after
2810
+ assigning |numpy.nan| to some entries of the observation series of node
2811
+ `lahn_leun`, the simulation series of node `lahn_kalk` also contains |numpy.nan|
2812
+ values:
2813
+
2814
+ >>> from numpy import nan
2815
+ >>> with pub.options.checkseries(False):
2816
+ ... hp.nodes.lahn_leun.sequences.obs.series= 0.0, nan, 0.0, nan
2817
+ >>> hp.reset_conditions()
2818
+ >>> hp.nodes.lahn_kalk.sequences.sim.series = 0.0
2819
+ >>> hp.simulate()
2820
+ >>> round_(hp.nodes.lahn_leun.sequences.obs.series)
2821
+ 0.0, nan, 0.0, nan
2822
+ >>> round_(hp.nodes.lahn_leun.sequences.sim.series)
2823
+ 42.346475, 27.157472, 22.88099, 20.156836
2824
+ >>> round_(hp.nodes.lahn_kalk.sequences.sim.series)
2825
+ 11.672862, nan, 8.984317, nan
2826
+
2827
+ To avoid calculating |numpy.nan| values, one can select the fourth option,
2828
+ `obs_newsim`. Now, the priority for node `lahn_leun` is to deploy its observed
2829
+ values. However, for each missing observation, it deploys its newly simulated
2830
+ value instead:
2831
+
2832
+ >>> hp.nodes.lahn_leun.deploymode = "obs_newsim"
2833
+ >>> hp.reset_conditions()
2834
+ >>> hp.simulate()
2835
+ >>> round_(hp.nodes.lahn_leun.sequences.obs.series)
2836
+ 0.0, nan, 0.0, nan
2837
+ >>> round_(hp.nodes.lahn_leun.sequences.sim.series)
2838
+ 42.346475, 27.157472, 22.88099, 20.156836
2839
+ >>> round_(hp.nodes.lahn_kalk.sequences.sim.series)
2840
+ 11.672862, 37.257561, 8.984317, 28.359542
2841
+
2842
+ The fifth option, `obs_oldsim`, serves the same purpose as option `obs_newsim`
2843
+ but uses already available "old" simulation results as substitutes:
2844
+
2845
+ >>> hp.nodes.lahn_leun.deploymode = "obs_oldsim"
2846
+ >>> hp.reset_conditions()
2847
+ >>> hp.nodes.lahn_leun.sequences.sim.series = (
2848
+ ... 32.3697, 17.210443, 12.930066, 10.20133)
2849
+ >>> hp.simulate()
2850
+ >>> round_(hp.nodes.lahn_leun.sequences.obs.series)
2851
+ 0.0, nan, 0.0, nan
2852
+ >>> round_(hp.nodes.lahn_leun.sequences.sim.series)
2853
+ 32.3697, 17.210443, 12.930066, 10.20133
2854
+ >>> round_(hp.nodes.lahn_kalk.sequences.sim.series)
2855
+ 11.672862, 27.310532, 8.984317, 18.404036
2856
+
2857
+ The last example shows that resetting option |Node.deploymode| to `newsim`
2858
+ results in the default behaviour of the method |HydPy.simulate| again:
2859
+
2860
+ >>> hp.nodes.lahn_leun.deploymode = "newsim"
2861
+ >>> hp.reset_conditions()
2862
+ >>> hp.simulate()
2863
+ >>> round_(hp.nodes.lahn_leun.sequences.sim.series)
2864
+ 42.346475, 27.157472, 22.88099, 20.156836
2865
+ >>> round_(hp.nodes.lahn_kalk.sequences.sim.series)
2866
+ 54.019337, 37.257561, 31.865308, 28.359542
2867
+ """
2868
+ idx_start, idx_end = hydpy.pub.timegrids.simindices
2869
+ methodorder = self.methodorder
2870
+ cm: AbstractContextManager[None] = contextlib.nullcontext()
2871
+ if exceptiontools.attrready(hydpy.pub, "sequencemanager"):
2872
+ cm = hydpy.pub.sequencemanager.provide_netcdfjitaccess(self.deviceorder)
2873
+ with cm:
2874
+ for idx in printtools.progressbar(range(idx_start, idx_end)):
2875
+ for func in methodorder:
2876
+ func(idx)
2877
+
2878
+ def doit(self) -> None:
2879
+ """Deprecated! Use method |HydPy.simulate| instead.
2880
+
2881
+ >>> from hydpy import HydPy
2882
+ >>> from hydpy.core.testtools import warn_later
2883
+ >>> from unittest import mock
2884
+ >>> with warn_later(), mock.patch.object(HydPy, "simulate") as mocked:
2885
+ ... hp = HydPy("test")
2886
+ ... hp.doit()
2887
+ HydPyDeprecationWarning: Method `doit` of class `HydPy` is deprecated. Use \
2888
+ method `simulate` instead.
2889
+ >>> mocked.call_args_list
2890
+ [call()]
2891
+ """
2892
+ self.simulate()
2893
+ warnings.warn(
2894
+ "Method `doit` of class `HydPy` is deprecated. Use method "
2895
+ "`simulate` instead.",
2896
+ exceptiontools.HydPyDeprecationWarning,
2897
+ )
2898
+
2899
+ def prepare_allseries(self, allocate_ram: bool = True, jit: bool = False) -> None:
2900
+ """Tell all current |IOSequence| objects how to handle time series data.
2901
+
2902
+ Assign |True| to the `allocate_ram` argument (default) to activate the
2903
+ |IOSequence.series| property of all sequences so that their time series data
2904
+ can become available in RAM.
2905
+
2906
+ Assign |True| to the `jit` argument to activate the "just-in-time" reading from
2907
+ NetCDF files for all |InputSequence| and |Obs| objects and to activate the
2908
+ "just-in-time" writing of NetCDF files for all |FactorSequence|, |FluxSequence|,
2909
+ |StateSequence| and |Sim| objects.
2910
+
2911
+ See the main documentation on class |HydPy| for further information.
2912
+ """
2913
+ self.prepare_modelseries(allocate_ram=allocate_ram, jit=jit)
2914
+ self.prepare_nodeseries(allocate_ram=allocate_ram, jit=jit)
2915
+
2916
+ def prepare_modelseries(self, allocate_ram: bool = True, jit: bool = False) -> None:
2917
+ """An alternative method for |HydPy.prepare_allseries| specialised for model
2918
+ sequences."""
2919
+ self.elements.prepare_allseries(allocate_ram=allocate_ram, jit=jit)
2920
+
2921
+ def prepare_inputseries(
2922
+ self, allocate_ram: bool = True, read_jit: bool = False, write_jit: bool = False
2923
+ ) -> None:
2924
+ """An alternative method for |HydPy.prepare_allseries| specialised for model
2925
+ input sequences."""
2926
+ self.elements.prepare_inputseries(
2927
+ allocate_ram=allocate_ram, read_jit=read_jit, write_jit=write_jit
2928
+ )
2929
+
2930
+ def prepare_factorseries(
2931
+ self, allocate_ram: bool = True, write_jit: bool = False
2932
+ ) -> None:
2933
+ """An alternative method for |HydPy.prepare_allseries| specialised for model
2934
+ factor sequences."""
2935
+ self.elements.prepare_factorseries(
2936
+ allocate_ram=allocate_ram, write_jit=write_jit
2937
+ )
2938
+
2939
+ def prepare_fluxseries(
2940
+ self, allocate_ram: bool = True, write_jit: bool = False
2941
+ ) -> None:
2942
+ """An alternative method for |HydPy.prepare_allseries| specialised for model
2943
+ flux sequences."""
2944
+ self.elements.prepare_fluxseries(allocate_ram=allocate_ram, write_jit=write_jit)
2945
+
2946
+ def prepare_stateseries(
2947
+ self, allocate_ram: bool = True, write_jit: bool = False
2948
+ ) -> None:
2949
+ """An alternative method for |HydPy.prepare_allseries| specialised for model
2950
+ state sequences."""
2951
+ self.elements.prepare_stateseries(
2952
+ allocate_ram=allocate_ram, write_jit=write_jit
2953
+ )
2954
+
2955
+ def prepare_nodeseries(self, allocate_ram: bool = True, jit: bool = False) -> None:
2956
+ """An alternative method for |HydPy.prepare_allseries| specialised for node
2957
+ sequences."""
2958
+ self.nodes.prepare_allseries(allocate_ram=allocate_ram, jit=jit)
2959
+
2960
+ def prepare_simseries(
2961
+ self, allocate_ram: bool = True, read_jit: bool = False, write_jit: bool = False
2962
+ ) -> None:
2963
+ """An alternative method for |HydPy.prepare_allseries| specialised for
2964
+ simulation sequences of nodes."""
2965
+ self.nodes.prepare_simseries(
2966
+ allocate_ram=allocate_ram, read_jit=read_jit, write_jit=write_jit
2967
+ )
2968
+
2969
+ def prepare_obsseries(
2970
+ self, allocate_ram: bool = True, read_jit: bool = False, write_jit: bool = False
2971
+ ) -> None:
2972
+ """An alternative method for |HydPy.prepare_allseries| specialised for
2973
+ observation sequences of nodes."""
2974
+ self.nodes.prepare_obsseries(
2975
+ allocate_ram=allocate_ram, read_jit=read_jit, write_jit=write_jit
2976
+ )
2977
+
2978
+ def save_allseries(self) -> None:
2979
+ """Write the time series data of all current |IOSequence| objects at once to
2980
+ data file(s).
2981
+
2982
+ See the main documentation on class |HydPy| for further information.
2983
+ """
2984
+ self.save_modelseries()
2985
+ self.save_nodeseries()
2986
+
2987
+ def save_modelseries(self) -> None:
2988
+ """An alternative method for |HydPy.save_modelseries| specialised for model
2989
+ sequences."""
2990
+ self.elements.save_allseries()
2991
+
2992
+ def save_inputseries(self) -> None:
2993
+ """An alternative method for |HydPy.save_modelseries| specialised for model
2994
+ input sequences."""
2995
+ self.elements.save_inputseries()
2996
+
2997
+ def save_factorseries(self) -> None:
2998
+ """An alternative method for |HydPy.save_modelseries| specialised for model
2999
+ factor sequences."""
3000
+ self.elements.save_factorseries()
3001
+
3002
+ def save_fluxseries(self) -> None:
3003
+ """An alternative method for |HydPy.save_modelseries| specialised for model
3004
+ flux sequences."""
3005
+ self.elements.save_fluxseries()
3006
+
3007
+ def save_stateseries(self) -> None:
3008
+ """An alternative method for |HydPy.save_modelseries| specialised for model
3009
+ state sequences."""
3010
+ self.elements.save_stateseries()
3011
+
3012
+ def save_nodeseries(self) -> None:
3013
+ """An alternative method for |HydPy.save_modelseries| specialised for node
3014
+ sequences."""
3015
+ self.nodes.save_allseries()
3016
+
3017
+ def save_simseries(self) -> None:
3018
+ """An alternative method for |HydPy.save_modelseries| specialised for
3019
+ simulation sequences of nodes."""
3020
+ self.nodes.save_simseries()
3021
+
3022
+ def save_obsseries(self) -> None:
3023
+ """An alternative method for |HydPy.save_modelseries| specialised for
3024
+ observation sequences of nodes."""
3025
+ self.nodes.save_obsseries()
3026
+
3027
+ def load_allseries(self) -> None:
3028
+ """Read the time series data of all current |IOSequence| objects at once from
3029
+ data file(s).
3030
+
3031
+ See the main documentation on class |HydPy| for further information.
3032
+ """
3033
+ self.load_modelseries()
3034
+ self.load_nodeseries()
3035
+
3036
+ def load_modelseries(self) -> None:
3037
+ """An alternative method for |HydPy.load_modelseries| specialised for model
3038
+ sequences."""
3039
+ self.elements.load_allseries()
3040
+
3041
+ def load_inputseries(self) -> None:
3042
+ """An alternative method for |HydPy.load_modelseries| specialised for model
3043
+ input sequences."""
3044
+ self.elements.load_inputseries()
3045
+
3046
+ def load_factorseries(self) -> None:
3047
+ """An alternative method for |HydPy.load_modelseries| specialised for model
3048
+ factor sequences."""
3049
+ self.elements.load_factorseries()
3050
+
3051
+ def load_fluxseries(self) -> None:
3052
+ """An alternative method for |HydPy.load_modelseries| specialised for model
3053
+ flux sequences."""
3054
+ self.elements.load_fluxseries()
3055
+
3056
+ def load_stateseries(self) -> None:
3057
+ """An alternative method for |HydPy.load_modelseries| specialised for model
3058
+ state sequences."""
3059
+ self.elements.load_stateseries()
3060
+
3061
+ def load_nodeseries(self) -> None:
3062
+ """An alternative method for |HydPy.load_modelseries| specialised for node
3063
+ sequences."""
3064
+ self.nodes.load_allseries()
3065
+
3066
+ def load_simseries(self) -> None:
3067
+ """An alternative method for |HydPy.load_modelseries| specialised for
3068
+ simulation sequences of nodes."""
3069
+ self.nodes.load_simseries()
3070
+
3071
+ def load_obsseries(self) -> None:
3072
+ """An alternative method for |HydPy.load_modelseries| specialised for
3073
+ observation sequences of nodes."""
3074
+ self.nodes.load_obsseries()
3075
+
3076
+
3077
+ def create_directedgraph(
3078
+ nodes: devicetools.Nodes, elements: devicetools.Elements
3079
+ ) -> networkx.DiGraph:
3080
+ """Create a directed graph based on the given devices."""
3081
+ digraph = networkx.DiGraph()
3082
+ digraph.add_nodes_from(elements)
3083
+ digraph.add_nodes_from(nodes)
3084
+ for element in elements:
3085
+ for node in itertools.chain(element.inlets, element.inputs):
3086
+ digraph.add_edge(node, element)
3087
+ for node in itertools.chain(element.outlets, element.outputs):
3088
+ digraph.add_edge(element, node)
3089
+ return digraph