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,4476 @@
1
+ """This module implements tools for defining and handling different kinds of
2
+ hydrological model sequences (time series)."""
3
+
4
+ # import...
5
+ # ...from standard library
6
+ from __future__ import annotations
7
+ import abc
8
+ import copy
9
+ import dataclasses
10
+ import os
11
+ import sys
12
+ import types
13
+ import warnings
14
+
15
+ # ...from site-packages
16
+ import numpy
17
+
18
+ # ...from HydPy
19
+ import hydpy
20
+ from hydpy import config
21
+ from hydpy.core import exceptiontools
22
+ from hydpy.core import objecttools
23
+ from hydpy.core import propertytools
24
+ from hydpy.core import variabletools
25
+ from hydpy.core.typingtools import *
26
+
27
+ if sys.version_info < (3, 11):
28
+ from strenum import StrEnum
29
+ else:
30
+ from enum import StrEnum
31
+
32
+ if TYPE_CHECKING:
33
+ from hydpy.core import devicetools
34
+ from hydpy.core import modeltools
35
+ from hydpy.core import timetools
36
+ from hydpy.cythons import pointerutils
37
+ from hydpy.cythons import sequenceutils
38
+ else:
39
+ # from hydpy.core import modeltools actual import below
40
+ from hydpy.cythons.autogen import pointerutils
41
+ from hydpy.cythons.autogen import sequenceutils
42
+
43
+
44
+ TypeSequences = TypeVar("TypeSequences", "Sequences", "devicetools.Node")
45
+
46
+ TypeModelSequences = TypeVar(
47
+ "TypeModelSequences",
48
+ bound="ModelSequences[ModelSequence, variabletools.FastAccess]",
49
+ )
50
+
51
+ TypeSequence_co = TypeVar("TypeSequence_co", bound="Sequence_", covariant=True)
52
+ TypeIOSequence_co = TypeVar("TypeIOSequence_co", bound="IOSequence", covariant=True)
53
+ TypeModelSequence_co = TypeVar(
54
+ "TypeModelSequence_co", bound="ModelSequence", covariant=True
55
+ )
56
+ TypeModelIOSequence_co = TypeVar(
57
+ "TypeModelIOSequence_co", bound="ModelIOSequence", covariant=True
58
+ )
59
+ TypeOutputSequence_co = TypeVar(
60
+ "TypeOutputSequence_co", bound="OutputSequence", covariant=True
61
+ )
62
+ TypeLinkSequence_co = TypeVar(
63
+ "TypeLinkSequence_co", bound="LinkSequence", covariant=True
64
+ )
65
+
66
+ TypeFastAccessIOSequence_co = TypeVar(
67
+ "TypeFastAccessIOSequence_co", bound="FastAccessIOSequence", covariant=True
68
+ )
69
+
70
+ ModelSequencesSubypes: TypeAlias = Union[
71
+ "InputSequences",
72
+ "FactorSequences",
73
+ "FluxSequences",
74
+ "StateSequences",
75
+ "LogSequences",
76
+ "AideSequences",
77
+ "InletSequences",
78
+ "OutletSequences",
79
+ "ReceiverSequences",
80
+ "SenderSequences",
81
+ ]
82
+ ModelIOSequencesSubtypes: TypeAlias = Union[
83
+ "InputSequences", "FactorSequences", "FluxSequences", "StateSequences"
84
+ ]
85
+
86
+ InOutSequence: TypeAlias = Union[
87
+ "InputSequence",
88
+ "InletSequence",
89
+ "ReceiverSequence",
90
+ "OutputSequence",
91
+ "OutletSequence",
92
+ "SenderSequence",
93
+ ]
94
+ InOutSequenceTypes: TypeAlias = Union[
95
+ type["InputSequence"],
96
+ type["InletSequence"],
97
+ type["ReceiverSequence"],
98
+ type["OutputSequence"],
99
+ type["OutletSequence"],
100
+ type["SenderSequence"],
101
+ ]
102
+
103
+ Aggregation: TypeAlias = Optional[Literal["unmodified", "mean"]]
104
+
105
+
106
+ class FastAccessIOSequence(variabletools.FastAccess):
107
+ """Provides fast access to the values of the |IOSequence| objects of a specific
108
+ subgroup and supports handling time series data during simulations.
109
+
110
+ The following details are of relevance for *HydPy* developers only.
111
+
112
+ |sequencetools.FastAccessIOSequence| is applied in Python mode only. When working
113
+ in Cython mode, it is replaced by model-specific Cython extension classes, which
114
+ are computationally more efficient. For compatibility with these extension classes,
115
+ |sequencetools.FastAccessIOSequence| objects work with dynamically set instance
116
+ members. For example, suppose there is a sequence named `seq1`, which is
117
+ 2-dimensional, then its associated attributes are:
118
+
119
+ * seq1 (|NDArrayFloat|): The actual sequence value(s).
120
+ * _seq1_ndim (|int|): The number of dimensions.
121
+ * _seq1_length_0 (|int|): Length in the first dimension.
122
+ * _seq1_length_1 (|int|): Length in the second dimension.
123
+ * _seq1_ramflag (|bool|): Handle time series data in RAM?
124
+ * _seq1_array (|NDArrayFloat|): Time-series data (when handled in RAM).
125
+ * _seq1_diskflag_reading (|bool|): Read data from a NetCDF file during simulation?
126
+ * _seq1_diskflag_writing (|bool|): Write data to a NetCDF file during simulation?
127
+ * _seq1_ncarray (|NDArrayFloat|): An array connected with the data slice of the
128
+ NetCDF file relevant for `seq1`.
129
+
130
+ Note that the respective |IOSequences| and |IOSequence| objects initialise, change,
131
+ and apply these dynamical attributes. To handle them directly is error-prone and
132
+ thus not recommended.
133
+ """
134
+
135
+ def load_data(self, idx: int) -> None:
136
+ """Load the data of certain sequences from the defined sources.
137
+
138
+ The following flags specify the data source (listed in the order of their
139
+ priority):
140
+
141
+ * inputflag (|bool|): Take the data from an "input node".
142
+ * diskflag_reading (|bool|): Read the data "on the fly" from a NetCDF file
143
+ during a simulation run.
144
+ * ramflag (|bool|): Take the data from the time series handled by the
145
+ |IOSequence.series| attribute of the respective |IOSequence| object.
146
+
147
+ If, for example, `diskflag_reading` and `ramflag` are both activated,
148
+ |FastAccessIOSequence.load_data| prefers the data available within the NetCDF
149
+ file.
150
+ """
151
+ for name in self:
152
+ ndim = self._get_attribute(name, "ndim")
153
+ inputflag = self._get_attribute(name, "inputflag", False)
154
+ diskflag = self._get_attribute(name, "diskflag_reading")
155
+ ramflag = self._get_attribute(name, "ramflag")
156
+ if inputflag or diskflag or ramflag:
157
+ if inputflag:
158
+ actual = self._get_attribute(name, "inputpointer")[0]
159
+ elif diskflag:
160
+ actual = self._get_attribute(name, "ncarray")[0]
161
+ else:
162
+ actual = self._get_attribute(name, "array")[idx]
163
+ if ndim == 0:
164
+ setattr(self, name, actual)
165
+ else:
166
+ getattr(self, name)[:] = actual
167
+
168
+ def save_data(self, idx: int) -> None:
169
+ """Save the data of certain sequences to the defined sources.
170
+
171
+ The following flags the data targets:
172
+
173
+ * diskflag_writing (|bool|): Write the data "on the fly" to a NetCDF file
174
+ during a simulation run.
175
+ * ramflag (|bool|): Give the data to the time series handled by the
176
+ |IOSequence.series| attribute of the respective |IOSequence| object.
177
+
178
+ It is possible to write data to a NetCDF file and pass it to |IOSequence.series|
179
+ simultaneously.
180
+ """
181
+ for name in self:
182
+ actual = getattr(self, name)
183
+ if self._get_attribute(name, "diskflag_writing"):
184
+ try:
185
+ self._get_attribute(name, "ncarray")[:] = actual.flatten()
186
+ except AttributeError:
187
+ self._get_attribute(name, "ncarray")[:] = actual
188
+ if self._get_attribute(name, "ramflag"):
189
+ self._get_attribute(name, "array")[idx] = actual
190
+
191
+
192
+ class FastAccessInputSequence(FastAccessIOSequence):
193
+ """|FastAccessIOSequence| subclass specialised for input sequences."""
194
+
195
+ def set_pointerinput(self, name: str, pdouble: pointerutils.PDouble) -> None:
196
+ """Use the given |PDouble| object as the pointer for the 0-dimensional
197
+ |InputSequence| object with the given name."""
198
+ setattr(self, f"_{name}_inputpointer", pdouble)
199
+
200
+
201
+ class FastAccessOutputSequence(FastAccessIOSequence):
202
+ """|FastAccessIOSequence| subclass specialised for output sequences."""
203
+
204
+ def set_pointeroutput(self, name: str, pdouble: pointerutils.PDouble) -> None:
205
+ """Use the given |PDouble| object as the pointer for the 0-dimensional
206
+ |OutputSequence| object with the given name."""
207
+ self._set_attribute(name, "outputpointer", pdouble)
208
+
209
+ def update_outputs(self) -> None:
210
+ """Pass the data of all sequences with an activated output flag."""
211
+ for name in self:
212
+ if self._get_attribute(name, "outputflag", False):
213
+ self._get_attribute(name, "outputpointer")[0] = getattr(self, name)
214
+
215
+
216
+ class FastAccessLinkSequence(variabletools.FastAccess):
217
+ """|FastAccessIOSequence| subclass specialised for link sequences."""
218
+
219
+ def alloc(self, name: str, length: int) -> None:
220
+ """Allocate enough memory for the given vector length of the |LinkSequence|
221
+ object with the given name.
222
+
223
+ Cython extension classes need to define |FastAccessLinkSequence.alloc| if the
224
+ model handles at least one 1-dimensional |LinkSequence| subclass.
225
+ """
226
+ getattr(self, name).shape = length
227
+
228
+ def dealloc(self, name: str) -> None:
229
+ """Free the previously allocated memory of the |LinkSequence| object with the
230
+ given name.
231
+
232
+ Cython extension classes need to define |FastAccessLinkSequence.dealloc| if the
233
+ model handles at least one 1-dimensional |LinkSequence| subclass.
234
+ """
235
+
236
+ def set_pointer0d(self, name: str, value: pointerutils.Double) -> None:
237
+ """Define a pointer referencing the given |Double| object for the 0-dimensional
238
+ |LinkSequence| object with the given name.
239
+
240
+ Cython extension classes need to define |FastAccessLinkSequence.set_pointer0d|
241
+ if the model handles at least one 0-dimensional |LinkSequence| subclasses.
242
+ """
243
+ setattr(self, name, pointerutils.PDouble(value))
244
+
245
+ def set_pointer1d(self, name: str, value: pointerutils.Double, idx: int) -> None:
246
+ """Define a pointer referencing the given |Double| object for the 1-dimensional
247
+ |LinkSequence| object with the given name.
248
+
249
+ The given index defines the vector position of the defined pointer.
250
+
251
+ Cython extension classes need to define |FastAccessLinkSequence.set_pointer1d|
252
+ if the model handles at least one 1-dimensional |LinkSequence| subclasses.
253
+ """
254
+ ppdouble: pointerutils.PPDouble = getattr(self, name)
255
+ ppdouble.set_pointer(value, idx)
256
+
257
+ def get_value(self, name: str) -> float | NDArrayFloat:
258
+ """Return the actual value(s) referenced by the pointer(s) of the
259
+ |LinkSequence| object with the given name."""
260
+ value = getattr(self, name)[:]
261
+ if self._get_attribute(name, "ndim"):
262
+ return numpy.asarray(value, dtype=config.NP_FLOAT)
263
+ return float(value)
264
+
265
+ def set_value(self, name: str, value: Mayberable1[float]) -> None:
266
+ """Set the actual value(s) referenced by the pointer(s) of the
267
+ |LinkSequence| object with the given name."""
268
+ getattr(self, name)[:] = value
269
+
270
+
271
+ class FastAccessNodeSequence(FastAccessIOSequence):
272
+ """|sequencetools.FastAccessIOSequence| subclass specialised for |Node| objects.
273
+
274
+ In contrast to other |FastAccessIOSequence| subclasses,
275
+ |sequencetools.FastAccessNodeSequence| only needs to handle a fixed number of
276
+ sequences, |Sim| and |Obs|. It thus can define the related attributes explicitly.
277
+ """
278
+
279
+ sim: pointerutils.Double
280
+ obs: pointerutils.Double
281
+ _sim_ramflag: bool
282
+ _obs_ramflag: bool
283
+ _sim_array: NDArrayFloat
284
+ _obs_array: NDArrayFloat
285
+ _sim_diskflag_reading: bool
286
+ _sim_diskflag_writing: bool
287
+ _obs_diskflag_reading: bool
288
+ _obs_diskflag_writing: bool
289
+ _sim_ncarray: NDArrayFloat
290
+ _obs_ncarray: NDArrayFloat
291
+ _reset_obsdata: bool
292
+
293
+ def load_simdata(self, idx: int) -> None:
294
+ """Load the next sim sequence value from a NetCDF file or, with second priority,
295
+ from the |IOSequence.series| attribute of the current |Sim| object."""
296
+ if self._sim_diskflag_reading:
297
+ self.sim[0] = self._sim_ncarray[0]
298
+ elif self._sim_ramflag:
299
+ self.sim[0] = self._sim_array[idx]
300
+
301
+ def save_simdata(self, idx: int) -> None:
302
+ """Save the next sim sequence value to a NetCDF file and/or to the
303
+ |IOSequence.series| attribute of the |Sim| object."""
304
+ if self._sim_diskflag_writing:
305
+ self._sim_ncarray[0] = self.sim[0]
306
+ if self._sim_ramflag:
307
+ self._sim_array[idx] = self.sim[0]
308
+
309
+ def load_obsdata(self, idx: int) -> None:
310
+ """Load the next sim sequence value from a NetCDF file or, with second priority,
311
+ from the |IOSequence.series| attribute of the |Obs| object."""
312
+ if self._obs_diskflag_reading:
313
+ self.obs[0] = self._obs_ncarray[0]
314
+ elif self._obs_ramflag:
315
+ self.obs[0] = self._obs_array[idx]
316
+
317
+ def save_obsdata(self, idx: int) -> None:
318
+ """Save the next sim sequence value to a NetCDF file and/or to the
319
+ |IOSequence.series| attribute of the |Obs| object."""
320
+ if self._obs_diskflag_writing:
321
+ self._obs_ncarray[0] = self.obs[0]
322
+ if self._obs_ramflag:
323
+ self._obs_array[idx] = self.obs[0]
324
+
325
+ def load_data(self, idx: int) -> None:
326
+ """Call both method |sequencetools.FastAccessNodeSequence.load_simdata| and
327
+ method |sequencetools.FastAccessNodeSequence.load_obsdata|."""
328
+ self.load_simdata(idx)
329
+ self.load_obsdata(idx)
330
+
331
+ def save_data(self, idx: int) -> None:
332
+ """Call both method |sequencetools.FastAccessNodeSequence.save_simdata| and
333
+ method |sequencetools.FastAccessNodeSequence.save_obsdata|."""
334
+ self.save_simdata(idx)
335
+ self.save_obsdata(idx)
336
+
337
+ def reset(self, idx: int = 0) -> None:
338
+ # pylint: disable=unused-argument
339
+ # required for consistincy with the other reset methods.
340
+ """Set the actual value of the simulation sequence to zero."""
341
+ self.sim[0] = 0.0
342
+
343
+ def fill_obsdata(self, idx: int = 0) -> None:
344
+ """Use the current sim value for the current `obs` value if obs is
345
+ |numpy.nan|."""
346
+ # pylint: disable=unused-argument
347
+ # required for consistincy with the other reset methods.
348
+ if numpy.isnan(self.obs[0]):
349
+ self._reset_obsdata = True
350
+ self.obs[0] = self.sim[0]
351
+
352
+ def reset_obsdata(self, idx: int = 0) -> None:
353
+ """Reset the current `obs` value to |numpy.nan| if modified beforehand by
354
+ method |FastAccessNodeSequence.fill_obsdata|."""
355
+ # pylint: disable=unused-argument
356
+ # required for consistincy with the other reset methods.
357
+ if self._reset_obsdata:
358
+ self.obs[0] = numpy.nan
359
+ self._reset_obsdata = False
360
+
361
+
362
+ class InfoArray(NDArrayFloat):
363
+ """|numpy| |numpy.ndarray| subclass with an additional attribute describing the
364
+ (potential) aggregation of the handled data.
365
+
366
+ >>> from hydpy.core.sequencetools import InfoArray
367
+ >>> array = InfoArray([1.0, 2.0], aggregation="mean")
368
+ >>> array
369
+ InfoArray([1., 2.])
370
+ >>> array.aggregation
371
+ 'mean'
372
+ >>> subarray = array[:1]
373
+ >>> subarray
374
+ InfoArray([1.])
375
+ >>> subarray.aggregation
376
+ 'mean'
377
+ """
378
+
379
+ aggregation: Aggregation
380
+
381
+ def __new__(cls, array: NDArrayFloat, aggregation: Aggregation = None) -> InfoArray:
382
+ obj = numpy.asarray(array).view(cls)
383
+ obj.aggregation = aggregation
384
+ return obj
385
+
386
+ def __array_finalize__(self, obj: NDArray | None) -> None:
387
+ if isinstance(obj, InfoArray):
388
+ self.aggregation = obj.aggregation
389
+ else:
390
+ self.aggregation = None
391
+
392
+
393
+ class StandardInputNames(StrEnum):
394
+ """Standard names for the |InputSequence| subclasses of the various models.
395
+
396
+ One can use these names instead of the model-specific sequence names for reading
397
+ input time series from or to files. For further information, see the introductory
398
+ documentation on class |HydPy|.
399
+
400
+ The suffix "_HRU" refers to 1-dimensional sequences for which the different entries
401
+ correspond to different spatial units (typically hydrological response units).
402
+ """
403
+
404
+ AIR_TEMPERATURE = "air_temperature"
405
+ """Air temperature 2 m above the ground [°C]."""
406
+ ALBEDO_HRU = "albedo_hru"
407
+ """Surface albedo [-]."""
408
+ ARTIFICIAL_GROUNDWATER_RECHARGE = "artificial_groundwater_recharge"
409
+ """Artificial/additional groundwater recharge [mm/T]."""
410
+ ARTIFICIAL_SURFACE_WATER_SUPPLY = "artificial_surface_water_supply"
411
+ """Artificial/additional surface water supply [mm/T]."""
412
+ ATMOSPHERIC_PRESSURE = "atmospheric_pressure"
413
+ """Atmospheric pressure [hPa]."""
414
+ CAPILLARY_RISE = "capillary_rise"
415
+ """Capillary rise [mm/T]."""
416
+ CLEAR_SKY_SOLAR_RADIATION = "clear_sky_solar_radiation"
417
+ """Clear sky solar radiation [W/m²]."""
418
+ EVAPOTRANSPIRATION = "evapotranspiration"
419
+ """Actual evapotranspiration [mm/T]."""
420
+ GLOBAL_RADIATION = "global_radiation"
421
+ """Global radiation [W/m²]."""
422
+ INTERCEPTED_WATER_HRU = "intercepted_water_hru"
423
+ """Amount of intercepted water [mm]."""
424
+ MAXIMUM_AIR_TEMPERATURE = "maximum_air_temperature"
425
+ """Highest air temperature 2 m above the ground within time interval [°C]."""
426
+ MINIMUM_AIR_TEMPERATURE = "minimum_air_temperature"
427
+ """Lowest air temperature 2 m above the ground within time interval [°C]."""
428
+ NORMAL_AIR_TEMPERATURE = "normal_air_temperature"
429
+ """Normal air temperature 2 m above the ground [°C]."""
430
+ NORMAL_EVAPOTRANSPIRATION = "normal_evapotranspiration"
431
+ """Normal evapotranspiration [mm/T]."""
432
+ POSSIBLE_SUNSHINE_DURATION = "possible_sunshine_duration"
433
+ """Possible sunshine duration [h]."""
434
+ POTENTIAL_EVAPOTRANSPIRATION = "potential_evapotranspiration"
435
+ """Potential evapotranspiration [mm/T]."""
436
+ PRECIPITATION = "precipitation"
437
+ """Precipitation [mm/T]."""
438
+ RELATIVE_HUMIDITY = "relative_humidity"
439
+ """Relative humidity [%]."""
440
+ SNOW_COVER_DEGREE_CANOPY_HRU = "snow_cover_degree_canopy_hru"
441
+ """Snow cover degree in the canopies of tree-like vegetation [-]."""
442
+ SNOW_COVER_DEGREE_HRU = "snow_cover_degree_hru"
443
+ """Snow cover degree [-]."""
444
+ SOIL_WATER_HRU = "soil_water_hru"
445
+ """Amount of soil water [mm]."""
446
+ SUNSHINE_DURATION = "sunshine_duration"
447
+ """Sunshine duration [h]."""
448
+ WIND_SPEED = "wind_speed"
449
+ """Wind speed [m/s]."""
450
+
451
+
452
+ class Sequences:
453
+ """Base class for handling all sequences of a specific model.
454
+
455
+ |Sequences| objects handle nine sequence subgroups as attributes such as the
456
+ `inlets` and the `receivers` subsequences:
457
+
458
+ >>> from hydpy.core.testtools import prepare_full_example_2
459
+ >>> hp, pub, TestIO = prepare_full_example_2()
460
+ >>> sequences = hp.elements.land_dill_assl.model.sequences
461
+ >>> bool(sequences.inlets)
462
+ False
463
+ >>> bool(sequences.fluxes)
464
+ True
465
+
466
+ Iteration makes only the non-empty subgroups available that handle |Sequence_|
467
+ objects:
468
+
469
+ >>> for subseqs in sequences:
470
+ ... print(subseqs.name)
471
+ inputs
472
+ factors
473
+ fluxes
474
+ states
475
+ aides
476
+ outlets
477
+ >>> len(sequences)
478
+ 6
479
+
480
+ Keyword access provides a type-safe way to query a subgroup via a string:
481
+
482
+ >>> type(sequences["inputs"]).__name__
483
+ 'InputSequences'
484
+ >>> type(sequences["wrong"])
485
+ Traceback (most recent call last):
486
+ ...
487
+ TypeError: There is no sequence subgroup named `wrong`.
488
+ >>> sequences["model"]
489
+ Traceback (most recent call last):
490
+ ...
491
+ TypeError: Attribute `model` is of type `Model`, which is not a subtype of class \
492
+ `SubSequences`.
493
+
494
+ Class |Sequences| provides some methods related to reading and writing time series
495
+ data, which (directly or indirectly) call the corresponding methods of the handled
496
+ |IOSequence| objects. In most cases, users should prefer to use the related
497
+ methods of class |HydPy|, but using the ones of class |Sequences| can be more
498
+ convenient when analysing a specific model in-depth.
499
+
500
+ To introduce these methods, we first change two IO-related settings:
501
+
502
+ >>> from hydpy import round_
503
+ >>> pub.options.checkseries = False
504
+ >>> pub.sequencemanager.overwrite = True
505
+
506
+ Method |Sequences.prepare_series| can both enable and disable the handling of
507
+ time series in rapid access memory (RAM), and both enable and disable the reading
508
+ of input data from NetCDF files and the writing of NetCDF files "on the fly"
509
+ during simulation runs:
510
+
511
+ >>> from hydpy import attrready
512
+ >>> sequences.prepare_series(allocate_ram=False, jit=False)
513
+ >>> sequences.inputs.t.ramflag
514
+ False
515
+ >>> attrready(sequences.inputs.t, "series")
516
+ False
517
+ >>> sequences.inputs.t.diskflag
518
+ False
519
+ >>> sequences.inputs.t.diskflag_reading
520
+ False
521
+ >>> sequences.states.sm.diskflag_writing
522
+ False
523
+
524
+ >>> sequences.prepare_series()
525
+ >>> sequences.inputs.t.ramflag
526
+ True
527
+ >>> attrready(sequences.inputs.t, "series")
528
+ True
529
+ >>> sequences.inputs.t.diskflag
530
+ False
531
+ >>> sequences.inputs.t.diskflag_reading
532
+ False
533
+ >>> sequences.states.sm.diskflag_writing
534
+ False
535
+
536
+ >>> sequences.prepare_series(allocate_ram=False, jit=True)
537
+ >>> sequences.inputs.t.ramflag
538
+ False
539
+ >>> attrready(sequences.inputs.t, "series")
540
+ False
541
+ >>> sequences.inputs.t.diskflag
542
+ True
543
+ >>> sequences.inputs.t.diskflag_reading
544
+ True
545
+ >>> sequences.states.sm.diskflag_writing
546
+ True
547
+
548
+ After applying |Sequences.prepare_series|, you can use the methods
549
+ |Sequences.load_series| and |Sequences.save_series| to read or write the time
550
+ series of the relevant |InputSequence|, |FactorSequence|, |FluxSequence|, and
551
+ |StateSequence| object, as the following technical test suggests. The
552
+ documentation on class |IOSequence| explains the underlying functionalities of in
553
+ more detail.
554
+
555
+ >>> from unittest.mock import patch
556
+ >>> template = "hydpy.core.sequencetools.%s.load_series"
557
+ >>> with patch(template % "InputSequences") as inputs, \
558
+ patch(template % "FactorSequences") as factors, \
559
+ patch(template % "FluxSequences") as fluxes, \
560
+ patch(template % "StateSequences") as states:
561
+ ... sequences.load_series()
562
+ ... inputs.assert_called_with()
563
+ ... factors.assert_called_with()
564
+ ... fluxes.assert_called_with()
565
+ ... states.assert_called_with()
566
+
567
+ >>> template = "hydpy.core.sequencetools.%s.save_series"
568
+ >>> with patch(template % "InputSequences") as inputs, \
569
+ patch(template % "FactorSequences") as factors, \
570
+ patch(template % "FluxSequences") as fluxes, \
571
+ patch(template % "StateSequences") as states:
572
+ ... sequences.save_series()
573
+ ... inputs.assert_called_with()
574
+ ... factors.assert_called_with()
575
+ ... fluxes.assert_called_with()
576
+ ... states.assert_called_with()
577
+
578
+ .. testsetup::
579
+
580
+ >>> from hydpy import Node, Element
581
+ >>> Node.clear_all()
582
+ >>> Element.clear_all()
583
+ >>> pub.options.checkseries = True
584
+ >>> pub.sequencemanager.overwrite = False
585
+ """
586
+
587
+ model: modeltools.Model
588
+ inlets: InletSequences
589
+ receivers: ReceiverSequences
590
+ inputs: InputSequences
591
+ factors: FactorSequences
592
+ fluxes: FluxSequences
593
+ states: StateSequences
594
+ logs: LogSequences
595
+ aides: AideSequences
596
+ outlets: OutletSequences
597
+ senders: SenderSequences
598
+
599
+ def __init__(
600
+ self,
601
+ model: modeltools.Model,
602
+ *,
603
+ cls_inlets: type[InletSequences] | None = None,
604
+ cls_receivers: type[ReceiverSequences] | None = None,
605
+ cls_inputs: type[InputSequences] | None = None,
606
+ cls_factors: type[FactorSequences] | None = None,
607
+ cls_fluxes: type[FluxSequences] | None = None,
608
+ cls_states: type[StateSequences] | None = None,
609
+ cls_logs: type[LogSequences] | None = None,
610
+ cls_aides: type[AideSequences] | None = None,
611
+ cls_outlets: type[OutletSequences] | None = None,
612
+ cls_senders: type[SenderSequences] | None = None,
613
+ cymodel: CyModelProtocol | None = None,
614
+ cythonmodule: types.ModuleType | None = None,
615
+ ) -> None:
616
+ self.model = model
617
+ self.inlets = self.__prepare_subseqs(
618
+ InletSequences, cls_inlets, cymodel, cythonmodule
619
+ )
620
+ self.receivers = self.__prepare_subseqs(
621
+ ReceiverSequences, cls_receivers, cymodel, cythonmodule
622
+ )
623
+ self.inputs = self.__prepare_subseqs(
624
+ InputSequences, cls_inputs, cymodel, cythonmodule
625
+ )
626
+ self.factors = self.__prepare_subseqs(
627
+ FactorSequences, cls_factors, cymodel, cythonmodule
628
+ )
629
+ self.fluxes = self.__prepare_subseqs(
630
+ FluxSequences, cls_fluxes, cymodel, cythonmodule
631
+ )
632
+ self.states = self.__prepare_subseqs(
633
+ StateSequences, cls_states, cymodel, cythonmodule
634
+ )
635
+ self.logs = self.__prepare_subseqs(
636
+ LogSequences, cls_logs, cymodel, cythonmodule
637
+ )
638
+ self.aides = self.__prepare_subseqs(
639
+ AideSequences, cls_aides, cymodel, cythonmodule
640
+ )
641
+ self.outlets = self.__prepare_subseqs(
642
+ OutletSequences, cls_outlets, cymodel, cythonmodule
643
+ )
644
+ self.senders = self.__prepare_subseqs(
645
+ SenderSequences, cls_senders, cymodel, cythonmodule
646
+ )
647
+
648
+ def __prepare_subseqs(
649
+ self,
650
+ default: type[TypeModelSequences],
651
+ class_: type[TypeModelSequences] | None,
652
+ cymodel,
653
+ cythonmodule,
654
+ ) -> TypeModelSequences:
655
+ name = default.__name__
656
+ if class_ is None:
657
+ class_ = copy.copy(default)
658
+ setattr(class_, "CLASSES", ())
659
+ return class_(self, getattr(cythonmodule, name, None), cymodel)
660
+
661
+ @property
662
+ def iosubsequences(self) -> Iterator[ModelIOSequencesSubtypes]:
663
+ """Yield all relevant |IOSequences| objects handled by the current |Sequences|
664
+ object.
665
+
666
+ The currently available IO-subgroups are `inputs`, `factors`, `fluxes`, and
667
+ `states`.
668
+
669
+ >>> from hydpy import prepare_model
670
+ >>> model = prepare_model("hland_96")
671
+ >>> for subseqs in model.sequences.iosubsequences:
672
+ ... print(subseqs.name)
673
+ inputs
674
+ factors
675
+ fluxes
676
+ states
677
+
678
+ However, not all models implement sequences for all these subgroups. Therefore,
679
+ the |Sequences.iosubsequences| property only yields those subgroups which are
680
+ non-empty:
681
+
682
+ >>> model = prepare_model("musk_classic")
683
+ >>> for subseqs in model.sequences.iosubsequences:
684
+ ... print(subseqs.name)
685
+ fluxes
686
+ states
687
+ """
688
+ if self.inputs:
689
+ yield self.inputs
690
+ if self.factors:
691
+ yield self.factors
692
+ if self.fluxes:
693
+ yield self.fluxes
694
+ if self.states:
695
+ yield self.states
696
+
697
+ def prepare_series(self, allocate_ram: bool = True, jit: bool = False) -> None:
698
+ """Call method |IOSequences.prepare_series| of attribute |Sequences.inputs|
699
+ with `read_jit=jit` and of attributes |Sequences.factors|, |Sequences.fluxes|,
700
+ and |Sequences.states| with `write_jit=jit`."""
701
+ self.inputs.prepare_series(allocate_ram=allocate_ram, read_jit=jit)
702
+ self.factors.prepare_series(allocate_ram=allocate_ram, write_jit=jit)
703
+ self.fluxes.prepare_series(allocate_ram=allocate_ram, write_jit=jit)
704
+ self.states.prepare_series(allocate_ram=allocate_ram, write_jit=jit)
705
+
706
+ def load_series(self):
707
+ """Call method |IOSequences.load_series| of all handled |IOSequences|
708
+ objects."""
709
+ for subseqs in self.iosubsequences:
710
+ subseqs.load_series()
711
+
712
+ def save_series(self):
713
+ """Call method |IOSequence.save_series| of all handled |IOSequences|
714
+ objects."""
715
+ for subseqs in self.iosubsequences:
716
+ subseqs.save_series()
717
+
718
+ def load_data(self, idx: int) -> None:
719
+ """Call method |ModelIOSequences.load_data| of the handled
720
+ |sequencetools.InputSequences| object."""
721
+ self.inputs.load_data(idx)
722
+
723
+ def save_data(self, idx: int) -> None:
724
+ """Call method |ModelIOSequences.save_data| of the handled
725
+ |sequencetools.InputSequences|, |sequencetools.FactorSequences|,
726
+ |sequencetools.FluxSequences|, and |sequencetools.StateSequences| objects."""
727
+ self.inputs.save_data(idx)
728
+ self.factors.save_data(idx)
729
+ self.fluxes.save_data(idx)
730
+ self.states.save_data(idx)
731
+
732
+ def update_outputs(self) -> None:
733
+ """Call the method |OutputSequences.update_outputs| of the subattributes
734
+ |Sequences.factors|, |Sequences.fluxes|, and |Sequences.states|.
735
+
736
+ When working in Cython mode, the standard model import overrides this generic
737
+ Python version with a model-specific Cython version.
738
+ """
739
+ self.factors.update_outputs()
740
+ self.fluxes.update_outputs()
741
+ self.states.update_outputs()
742
+
743
+ def reset(self) -> None:
744
+ """Call method |ConditionSequence.reset| of all handled |ConditionSequence|
745
+ objects."""
746
+ self.states.reset()
747
+ self.logs.reset()
748
+
749
+ @property
750
+ def conditionsequences(self) -> Iterator[ConditionSequence]:
751
+ """Generator object yielding all conditions (|StateSequence| and |LogSequence|
752
+ objects).
753
+ """
754
+ yield from self.states
755
+ yield from self.logs
756
+
757
+ @property
758
+ def conditions(self) -> ConditionsSubmodel:
759
+ """A nested dictionary that contains the values of all condition sequences of
760
+ a single model instance.
761
+
762
+ See the documentation on property |HydPy.conditions| for further information.
763
+ """
764
+ conditions: dict[str, dict[str, float | NDArrayFloat]] = {}
765
+ for seq in self.conditionsequences:
766
+ subconditions = conditions.get(seq.subseqs.name, {})
767
+ subconditions[seq.name] = copy.deepcopy(seq.values)
768
+ conditions[seq.subseqs.name] = subconditions
769
+ return conditions
770
+
771
+ @conditions.setter
772
+ def conditions(self, conditions: ConditionsSubmodel) -> None:
773
+ with hydpy.pub.options.trimvariables(False):
774
+ for subname, subconditions in conditions.items():
775
+ subseqs = getattr(self, subname)
776
+ for seqname, values in subconditions.items():
777
+ getattr(subseqs, seqname)(values)
778
+ for seq in reversed(tuple(self.conditionsequences)):
779
+ seq.trim()
780
+
781
+ def trim_conditions(self) -> None:
782
+ """Call method |trim| of each handled |ConditionSequence|.
783
+
784
+ |Sequences.trim_conditions| is just a convenience function for calling method
785
+ |trim| of all |StateSequence| and |LogSequence| objects returned by property
786
+ |Sequences.conditionsequences|. We demonstrate its functionality by preparing
787
+ an instance of application model |lland_dd|, using its available default
788
+ values, and defining out-of-bound values of the soil moisture state sequence
789
+ |lland_states.BoWa|:
790
+
791
+ >>> from hydpy import prepare_model, pub
792
+ >>> pub.timegrids = "2000-01-01", "2000-01-10", "1d"
793
+ >>> with pub.options.usedefaultvalues(True):
794
+ ... model = prepare_model("lland_dd")
795
+ ... model.parameters.control.nhru(2)
796
+ >>> model.sequences.states.bowa = -100.0
797
+ >>> model.sequences.trim_conditions()
798
+ >>> model.sequences.states.bowa
799
+ bowa(0.0, 0.0)
800
+ """
801
+ for seq in self.conditionsequences:
802
+ seq.trim()
803
+
804
+ def __getitem__(
805
+ self, item: str
806
+ ) -> SubSequences[TypeSequences, Sequence_, variabletools.FastAccess]:
807
+ try:
808
+ subseqs = getattr(self, item)
809
+ except AttributeError:
810
+ raise TypeError(f"There is no sequence subgroup named `{item}`.") from None
811
+ if isinstance(subseqs, SubSequences):
812
+ return subseqs
813
+ raise TypeError(
814
+ f"Attribute `{item}` is of type `{type(subseqs).__name__}`, which is not "
815
+ f"a subtype of class `SubSequences`."
816
+ )
817
+
818
+ def __iter__(self) -> Iterator[ModelSequencesSubypes]:
819
+ if self.inlets:
820
+ yield self.inlets
821
+ if self.receivers:
822
+ yield self.receivers
823
+ if self.inputs:
824
+ yield self.inputs
825
+ if self.factors:
826
+ yield self.factors
827
+ if self.fluxes:
828
+ yield self.fluxes
829
+ if self.states:
830
+ yield self.states
831
+ if self.logs:
832
+ yield self.logs
833
+ if self.aides:
834
+ yield self.aides
835
+ if self.outlets:
836
+ yield self.outlets
837
+ if self.senders:
838
+ yield self.senders
839
+
840
+ def __len__(self) -> int:
841
+ return sum(1 for _ in self)
842
+
843
+ def __bool__(self) -> bool:
844
+ return any(seqs for seqs in self)
845
+
846
+
847
+ class SubSequences(
848
+ variabletools.SubVariables[
849
+ TypeSequences, TypeSequence_co, variabletools.TypeFastAccess_co
850
+ ]
851
+ ):
852
+ """Base class for handling subgroups of sequences.
853
+
854
+ Each |SubSequences| object has a `fastaccess` attribute, which is an instance of (a
855
+ subclass of) class |FastAccess| when working in pure Python mode:
856
+
857
+ >>> from hydpy import classname, Node, prepare_model, pub
858
+ >>> with pub.options.usecython(False):
859
+ ... model = prepare_model("lland_dd")
860
+ >>> classname(model.sequences.logs.fastaccess)
861
+ 'FastAccess'
862
+ >>> classname(model.sequences.inputs.fastaccess)
863
+ 'FastAccessInputSequence'
864
+ >>> from hydpy.core.sequencetools import FastAccessNodeSequence
865
+ >>> with pub.options.usecython(False):
866
+ ... node = Node("test1")
867
+ >>> isinstance(node.sequences.fastaccess, FastAccessNodeSequence)
868
+ True
869
+
870
+ When working in Cython mode (the default and much faster than the pure Python mode),
871
+ `fastaccess` is an object of the Cython extension class `FastAccessNodeSequence` of
872
+ module `sequenceutils` or a Cython extension class specialised for the respective
873
+ model and sequence group:
874
+
875
+ >>> with pub.options.usecython(True):
876
+ ... model = prepare_model("lland_dd")
877
+ >>> classname(model.sequences.inputs.fastaccess)
878
+ 'InputSequences'
879
+ >>> from hydpy.cythons.sequenceutils import FastAccessNodeSequence
880
+ >>> with pub.options.usecython(True):
881
+ ... node = Node("test2")
882
+ >>> isinstance(Node("test2").sequences.fastaccess, FastAccessNodeSequence)
883
+ True
884
+
885
+ See the documentation of similar class |SubParameters| for further information.
886
+ However, note the difference that model developers should not subclass
887
+ |SubSequences| directly but specialised subclasses like
888
+ |sequencetools.FluxSequences| or |sequencetools.StateSequences| instead.
889
+
890
+ .. testsetup::
891
+
892
+ >>> Node.clear_all()
893
+ """
894
+
895
+ @property
896
+ def name(self) -> str:
897
+ """The class name in lowercase letters omitting the last eight characters
898
+ ("equences").
899
+
900
+ >>> from hydpy.core.sequencetools import StateSequences
901
+ >>> class StateSequences(StateSequences):
902
+ ... CLASSES = ()
903
+ >>> StateSequences(None).name
904
+ 'states'
905
+ """
906
+ return type(self).__name__[:-8].lower()
907
+
908
+
909
+ class ModelSequences(
910
+ SubSequences[Sequences, TypeModelSequence_co, variabletools.TypeFastAccess_co]
911
+ ):
912
+ """Base class for handling model-related subgroups of sequences."""
913
+
914
+ seqs: Sequences
915
+ _cymodel: CyModelProtocol | None
916
+
917
+ def __init__(
918
+ self,
919
+ master: Sequences,
920
+ cls_fastaccess: type[variabletools.TypeFastAccess_co] | None = None,
921
+ cymodel: CyModelProtocol | None = None,
922
+ ) -> None:
923
+ self.seqs = master
924
+ self._cymodel = cymodel
925
+ super().__init__(master=master, cls_fastaccess=cls_fastaccess)
926
+
927
+ def _init_fastaccess(self) -> None:
928
+ super()._init_fastaccess()
929
+ if self._cls_fastaccess and self._cymodel:
930
+ setattr(self._cymodel.sequences, self.name, self.fastaccess)
931
+
932
+
933
+ @dataclasses.dataclass
934
+ class SeriesMode:
935
+ """The type of property |IOSequence.seriesmode| of class |IOSequence|."""
936
+
937
+ ramflag: bool
938
+ """Corresponds to property |IOSequence.ramflag| of class |IOSequence|."""
939
+ diskflag_reading: bool
940
+ """Corresponds to property |IOSequence.diskflag_reading| of class |IOSequence|."""
941
+ diskflag_writing: bool
942
+ """Corresponds to property |IOSequence.diskflag_writing| of class |IOSequence|."""
943
+
944
+
945
+ class IOSequences(
946
+ SubSequences[TypeSequences, TypeIOSequence_co, TypeFastAccessIOSequence_co]
947
+ ):
948
+ """Subclass of |SubSequences|, specialised for handling |IOSequence| objects."""
949
+
950
+ seqs: Sequences
951
+
952
+ def prepare_series(
953
+ self, allocate_ram: bool = True, read_jit: bool = False, write_jit: bool = False
954
+ ) -> None:
955
+ """Call method |IOSequence.prepare_series| of all handled |IOSequence|
956
+ objects."""
957
+ for seq in self:
958
+ seq.prepare_series(
959
+ allocate_ram=allocate_ram, read_jit=read_jit, write_jit=write_jit
960
+ )
961
+
962
+ def load_series(self) -> None:
963
+ """Call method |IOSequence.load_series| of all handled |IOSequence| objects
964
+ with an activated |IOSequence.ramflag|."""
965
+ for seq in self:
966
+ if seq.ramflag:
967
+ seq.load_series()
968
+
969
+ def save_series(self) -> None:
970
+ """Call method |IOSequence.save_series| of all handled |IOSequence| objects
971
+ with an activated |IOSequence.ramflag|."""
972
+ for seq in self:
973
+ if seq.ramflag:
974
+ seq.save_series()
975
+
976
+
977
+ class ModelIOSequences(
978
+ IOSequences[Sequences, TypeModelIOSequence_co, TypeFastAccessIOSequence_co],
979
+ ModelSequences[TypeModelIOSequence_co, TypeFastAccessIOSequence_co],
980
+ ):
981
+ """Base class for handling model-related subgroups of |IOSequence| objects."""
982
+
983
+ def load_data(self, idx: int) -> None:
984
+ """Call method |FastAccessIOSequence.load_data| of the |FastAccessIOSequence|
985
+ object handled as attribute `fastaccess`."""
986
+ self.fastaccess.load_data(idx)
987
+
988
+ def save_data(self, idx: int) -> None:
989
+ """Call method |FastAccessIOSequence.save_data| of the |FastAccessIOSequence|
990
+ object handled as attribute `fastaccess`."""
991
+ self.fastaccess.save_data(idx)
992
+
993
+
994
+ class InputSequences(ModelIOSequences["InputSequence", FastAccessInputSequence]):
995
+ """Base class for handling |InputSequence| objects."""
996
+
997
+ _CLS_FASTACCESS_PYTHON = FastAccessInputSequence
998
+
999
+
1000
+ class OutputSequences(
1001
+ ModelIOSequences[TypeOutputSequence_co, FastAccessOutputSequence]
1002
+ ):
1003
+ """Base class for handling |OutputSequence| objects."""
1004
+
1005
+ _CLS_FASTACCESS_PYTHON = FastAccessOutputSequence
1006
+
1007
+ def update_outputs(self) -> None:
1008
+ """Call method |FastAccessOutputSequence.update_outputs| of the
1009
+ |FastAccessOutputSequence| object handled as attribute `fastaccess`."""
1010
+ if self:
1011
+ self.fastaccess.update_outputs()
1012
+
1013
+ @property
1014
+ def numericsequences(self) -> Iterator[TypeOutputSequence_co]:
1015
+ """Iterator for "numerical" sequences.
1016
+
1017
+ "numerical" means that the |Sequence_.NUMERIC| class attribute of the actual
1018
+ sequence is |True|:
1019
+
1020
+ >>> from hydpy import prepare_model
1021
+ >>> model = prepare_model("dam_v001")
1022
+ >>> len(model.sequences.fluxes)
1023
+ 16
1024
+ >>> for seq in model.sequences.fluxes.numericsequences:
1025
+ ... print(seq)
1026
+ adjustedprecipitation(nan)
1027
+ actualevaporation(nan)
1028
+ inflow(nan)
1029
+ actualrelease(nan)
1030
+ flooddischarge(nan)
1031
+ outflow(nan)
1032
+ """
1033
+ for flux in self:
1034
+ if flux.NUMERIC:
1035
+ yield flux
1036
+
1037
+
1038
+ class FactorSequences(OutputSequences["FactorSequence"]):
1039
+ """Base class for handling |FactorSequence| objects."""
1040
+
1041
+
1042
+ class FluxSequences(OutputSequences["FluxSequence"]):
1043
+ """Base class for handling |FluxSequence| objects."""
1044
+
1045
+ @property
1046
+ def name(self) -> str:
1047
+ """Always return the string "fluxes"."""
1048
+ return "fluxes"
1049
+
1050
+
1051
+ class StateSequences(OutputSequences["StateSequence"]):
1052
+ """Base class for handling |StateSequence| objects."""
1053
+
1054
+ fastaccess_new: FastAccessOutputSequence
1055
+ fastaccess_old: variabletools.FastAccess
1056
+
1057
+ def _init_fastaccess(self) -> None:
1058
+ super()._init_fastaccess()
1059
+ self.fastaccess_new = self.fastaccess
1060
+ if (self._cls_fastaccess is None) or (self._cymodel is None):
1061
+ self.fastaccess_old = variabletools.FastAccess()
1062
+ else:
1063
+ setattr(self._cymodel.sequences, "new_states", self.fastaccess)
1064
+ self.fastaccess_old = self._cls_fastaccess()
1065
+ setattr(self._cymodel.sequences, "old_states", self.fastaccess_old)
1066
+
1067
+ def new2old(self) -> None:
1068
+ """Call method |StateSequence.new2old| of all handled |StateSequence|
1069
+ objects."""
1070
+ for seq in self:
1071
+ seq.new2old()
1072
+
1073
+ def reset(self) -> None:
1074
+ """Call method |ConditionSequence.reset| of all handled |StateSequence|
1075
+ objects."""
1076
+ for seq in self:
1077
+ seq.reset()
1078
+
1079
+
1080
+ class LogSequences(ModelSequences["LogSequence", variabletools.FastAccess]):
1081
+ """Base class for handling |LogSequence| objects."""
1082
+
1083
+ _CLS_FASTACCESS_PYTHON = variabletools.FastAccess
1084
+
1085
+ def reset(self) -> None:
1086
+ """Call method |ConditionSequence.reset| of all handled |LogSequence|
1087
+ objects."""
1088
+ for seq in self:
1089
+ seq.reset()
1090
+
1091
+
1092
+ class AideSequences(ModelSequences["AideSequence", variabletools.FastAccess]):
1093
+ """Base class for handling |AideSequence| objects."""
1094
+
1095
+ _CLS_FASTACCESS_PYTHON = variabletools.FastAccess
1096
+
1097
+
1098
+ class LinkSequences(ModelSequences[TypeLinkSequence_co, FastAccessLinkSequence]):
1099
+ """Base class for handling |LinkSequence| objects."""
1100
+
1101
+ _CLS_FASTACCESS_PYTHON = FastAccessLinkSequence
1102
+
1103
+
1104
+ class InletSequences(LinkSequences["InletSequence"]):
1105
+ """Base class for handling "inlet" |LinkSequence| objects."""
1106
+
1107
+
1108
+ class OutletSequences(LinkSequences["OutletSequence"]):
1109
+ """Base class for handling "outlet" |LinkSequence| objects."""
1110
+
1111
+
1112
+ class ReceiverSequences(LinkSequences["ReceiverSequence"]):
1113
+ """Base class for handling "receiver" |LinkSequence| objects."""
1114
+
1115
+
1116
+ class SenderSequences(LinkSequences["SenderSequence"]):
1117
+ """Base class for handling "sender" |LinkSequence| objects."""
1118
+
1119
+
1120
+ class Sequence_(variabletools.Variable):
1121
+ """Base class for defining different kinds of sequences.
1122
+
1123
+ Note that model developers should not derive their model-specific sequence classes
1124
+ from |Sequence_| directly but from the "final" subclasses provided in module
1125
+ |sequencetools| (e.g. |FluxSequence|).
1126
+
1127
+ From the model developer perspective and especially from the user perspective,
1128
+ |Sequence_| is only a small extension of its base class |Variable|. One relevant
1129
+ extension is that (only the) 0-dimensional sequence objects come with a predefined
1130
+ shape:
1131
+
1132
+ >>> from hydpy import prepare_model
1133
+ >>> model = prepare_model("lland_dd")
1134
+ >>> model.sequences.fluxes.qa.shape
1135
+ ()
1136
+ >>> nkor = model.sequences.fluxes.nkor
1137
+ >>> nkor.shape
1138
+ Traceback (most recent call last):
1139
+ ...
1140
+ hydpy.core.exceptiontools.AttributeNotReady: Shape information for variable \
1141
+ `nkor` can only be retrieved after it has been defined.
1142
+
1143
+ For consistency with the usage of |Parameter| subclasses, |Sequence_| objects are
1144
+ also "callable" for setting their values (but in a much less and flexible manner):
1145
+
1146
+ >>> nkor.shape = 3
1147
+ >>> nkor(2.0)
1148
+ >>> nkor
1149
+ nkor(2.0, 2.0, 2.0)
1150
+
1151
+ Under the hood, class |Sequence_| also prepares some attributes of its |FastAccess|
1152
+ object, used for performing the actual simulation calculations. Framework
1153
+ developers should note that the respective `fastaccess` attributes contain both the
1154
+ name of the sequence and the name of the original attribute in lowercase letters.
1155
+ We take `NDIM` as an example:
1156
+
1157
+ >>> nkor.fastaccess._nkor_ndim
1158
+ 1
1159
+
1160
+ Some of these attributes require updating in some situations. For example, other
1161
+ sequences than |AideSequence| objects require a "length" attribute, which needs
1162
+ updating each time the sequence's shape changes:
1163
+
1164
+ >>> nkor.fastaccess._nkor_length
1165
+ 3
1166
+ """
1167
+
1168
+ TYPE: type[float] = float
1169
+ INIT: float = 0.0
1170
+ NUMERIC: bool
1171
+
1172
+ subvars: (
1173
+ SubSequences[Sequences, Sequence_, variabletools.FastAccess]
1174
+ | SubSequences[devicetools.Node, Sequence_, variabletools.FastAccess]
1175
+ )
1176
+ """The subgroup to which the sequence belongs."""
1177
+ subseqs: (
1178
+ SubSequences[Sequences, Sequence_, variabletools.FastAccess]
1179
+ | SubSequences[devicetools.Node, Sequence_, variabletools.FastAccess]
1180
+ )
1181
+ """Alias for |Sequence_.subvars|."""
1182
+ strict_valuehandling: bool = False
1183
+
1184
+ def __hydpy__connect_variable2subgroup__(self) -> None:
1185
+ super().__hydpy__connect_variable2subgroup__()
1186
+ self._set_fastaccessattribute("ndim", self.NDIM)
1187
+ self._set_fastaccessattribute("length", 0)
1188
+ for idx in range(self.NDIM):
1189
+ self._set_fastaccessattribute(f"length_{idx}", 0)
1190
+
1191
+ def _get_fastaccessattribute(self, suffix: str, default: object = None) -> Any:
1192
+ return getattr(self.fastaccess, f"_{self.name}_{suffix}", default)
1193
+
1194
+ def _set_fastaccessattribute(self, suffix: str, value: Any) -> None:
1195
+ setattr(self.fastaccess, f"_{self.name}_{suffix}", value)
1196
+
1197
+ def _finalise_connections(self) -> None:
1198
+ """A hook method, called at the end of method
1199
+ `__hydpy__connect_variable2subgroup__` for initialising values and some
1200
+ attributes."""
1201
+ if not self.NDIM:
1202
+ self.shape = ()
1203
+
1204
+ @property
1205
+ def initinfo(self) -> tuple[float | pointerutils.Double, bool]:
1206
+ """A |tuple| containing the initial value and |True| or a missing value and
1207
+ |False|, depending on the actual |Sequence_| subclass and the actual value of
1208
+ option |Options.usedefaultvalues|.
1209
+
1210
+ In the following, we do not explain property |Sequence_.initinfo| itself but
1211
+ show how it affects initialising new |Sequence_| objects. Therefore, let us
1212
+ define a sequence test class and prepare a function for initialising it and
1213
+ connecting the resulting instance to a |ModelSequences| object:
1214
+
1215
+ >>> from hydpy.core.sequencetools import Sequence_, ModelSequences
1216
+ >>> from hydpy.core.variabletools import FastAccess
1217
+ >>> class Test(Sequence_):
1218
+ ... NDIM = 0
1219
+ ... _CLS_FASTACCESS_PYTHON = FastAccess
1220
+ >>> class SubGroup(ModelSequences):
1221
+ ... CLASSES = (Test,)
1222
+ ... _CLS_FASTACCESS_PYTHON = FastAccess
1223
+ >>> def prepare():
1224
+ ... subseqs = SubGroup(None)
1225
+ ... test = Test(subseqs)
1226
+ ... test.__hydpy__connect_variable2subgroup__()
1227
+ ... return test
1228
+
1229
+ By default, making use of the `INIT` attribute is disabled:
1230
+
1231
+ >>> prepare()
1232
+ test(nan)
1233
+
1234
+ Enable it by setting |Options.usedefaultvalues| to |True|:
1235
+
1236
+ >>> from hydpy import pub
1237
+ >>> with pub.options.usedefaultvalues(True):
1238
+ ... prepare()
1239
+ test(0.0)
1240
+
1241
+ Attribute `INIT` of class |Sequence_| comes with the value `0.0` by default,
1242
+ which should be reasonable for most |Sequence_| subclasses. However,
1243
+ subclasses can define other values. Most importantly, note the possibility to
1244
+ set `INIT` to `None` for sequences that do not allow specifying a reasonabe
1245
+ initial value for all possible situations:
1246
+
1247
+ >>> Test.INIT = None
1248
+ >>> prepare()
1249
+ test(nan)
1250
+ >>> with pub.options.usedefaultvalues(True):
1251
+ ... prepare()
1252
+ test(nan)
1253
+ """
1254
+ if hydpy.pub.options.usedefaultvalues and self.INIT is not None:
1255
+ return self.INIT, True
1256
+ return numpy.nan, False
1257
+
1258
+ def __repr__(self) -> str:
1259
+ brackets = (self.NDIM == 2) and (self.shape[0] != 1)
1260
+ return variabletools.to_repr(self, self.value, brackets)
1261
+
1262
+
1263
+ class IOSequence(Sequence_):
1264
+ """Base class for sequences with input/output functionalities.
1265
+
1266
+ The documentation on modules |filetools| and |netcdftools| in some detail explains
1267
+ how to read and write time series files. However, due to efficiency, reading and
1268
+ writing time series files are disabled by default. Therefore, you must first
1269
+ prepare the |IOSequence.series| attribute of the relevant |IOSequence| objects.
1270
+ Typically, you call methods like |HydPy.prepare_inputseries| of class |HydPy|.
1271
+ Here, we instead use the related features of the |IOSequence| class itself.
1272
+
1273
+ We use the `HydPy-H-Lahn` example project and focus on the `input`, `factor`,
1274
+ `fluxes`, and `state` sequences:
1275
+
1276
+ >>> from hydpy.core.testtools import prepare_full_example_2
1277
+ >>> hp, pub, TestIO = prepare_full_example_2()
1278
+ >>> inputs = hp.elements.land_lahn_marb.model.sequences.inputs
1279
+ >>> factors = hp.elements.land_lahn_marb.model.sequences.factors
1280
+ >>> fluxes = hp.elements.land_lahn_marb.model.sequences.fluxes
1281
+ >>> states = hp.elements.land_lahn_marb.model.sequences.states
1282
+
1283
+ Each |IOSequence| object comes four flags, answering the following questions:
1284
+
1285
+ * |IOSequence.ramflag|: can its time series can be available in RAM?
1286
+ * |IOSequence.diskflag_reading|: read its values "on the fly" from a NetCDF file
1287
+ during simulation runs?
1288
+ * |IOSequence.diskflag_writing|:write its values "on the fly" to a NetCDF file
1289
+ during simulation runs?
1290
+ * |IOSequence.diskflag|: is |IOSequence.diskflag_reading| and/or
1291
+ |IOSequence.diskflag_writing| activated?
1292
+
1293
+ For input sequences as |hland_inputs.T|, it is common to store their time series
1294
+ data (required for any simulation run) in RAM, which is much faster than
1295
+ (repeatedly) reading data "on the fly" and should be preferred, as long as limited
1296
+ available RAM is not an issue. For convenience, function |prepare_full_example_2|
1297
+ prepared |hland_inputs.T| (and the other input sequences) accordingly:
1298
+
1299
+ >>> inputs.t.ramflag
1300
+ True
1301
+ >>> inputs.t.diskflag_reading
1302
+ False
1303
+ >>> inputs.t.diskflag_writing
1304
+ False
1305
+ >>> inputs.t.diskflag
1306
+ False
1307
+ >>> from hydpy import round_
1308
+ >>> round_(inputs.t.series)
1309
+ -0.7, -1.5, -4.6, -8.2
1310
+
1311
+ |prepare_full_example_2| also activated the |IOSequence.ramflag| of all factor,
1312
+ flux, and state sequences, which is unnecessary to perform a successful simulation.
1313
+ However, it is required to directly access the complete time series of simulated
1314
+ values afterwards (otherwise, only the last computed value(s) were available in
1315
+ RAM after a simulation run):
1316
+
1317
+ >>> factors.tc.ramflag
1318
+ True
1319
+ >>> factors.tc.diskflag
1320
+ False
1321
+ >>> round_(factors.tc.series[:, 0])
1322
+ nan, nan, nan, nan
1323
+
1324
+ Use |IOSequence.prepare_series| to force a sequence to handle time series data in
1325
+ RAM or to read or write it on the fly. We now activate the reading functionality
1326
+ of input sequence |hland_inputs.T| (while still keeping its time series in RAM,
1327
+ which we set to zero beforehand) and the writing feature of the factor sequences
1328
+ |hland_factors.ContriArea| and |hland_factors.TC| (without handling their data in
1329
+ RAM) and the writing feature of the state sequences |hland_states.SM| and
1330
+ |hland_states.SP| (while handling their data in RAM simultaneously):
1331
+
1332
+ >>> inputs.t.series = 0.0
1333
+ >>> inputs.t.prepare_series(allocate_ram=True, read_jit=True)
1334
+ >>> factors.contriarea.prepare_series(allocate_ram=False, write_jit=True)
1335
+ >>> factors.tc.prepare_series(allocate_ram=False, write_jit=True)
1336
+ >>> states.sm.prepare_series(allocate_ram=True, write_jit=True)
1337
+ >>> states.sp.prepare_series(allocate_ram=True, write_jit=True)
1338
+
1339
+ Use the properties |IOSequence.ramflag|, |IOSequence.diskflag_reading|,
1340
+ |IOSequence.diskflag_writing|, and |IOSequence.diskflag| for querying the current
1341
+ configuration of individual |IOSequence| objects:
1342
+
1343
+ >>> inputs.t.ramflag
1344
+ True
1345
+ >>> inputs.t.diskflag_reading
1346
+ True
1347
+ >>> inputs.t.diskflag_writing
1348
+ False
1349
+ >>> inputs.t.diskflag
1350
+ True
1351
+ >>> round_(inputs.t.series)
1352
+ 0.0, 0.0, 0.0, 0.0
1353
+
1354
+ >>> factors.contriarea.ramflag
1355
+ False
1356
+ >>> factors.contriarea.diskflag_reading
1357
+ False
1358
+ >>> factors.contriarea.diskflag_writing
1359
+ True
1360
+ >>> factors.contriarea.diskflag
1361
+ True
1362
+ >>> factors.contriarea.series
1363
+ Traceback (most recent call last):
1364
+ ...
1365
+ hydpy.core.exceptiontools.AttributeNotReady: Sequence `contriarea` of element \
1366
+ `land_lahn_marb` is not requested to make any time series data available.
1367
+
1368
+ >>> states.sm.ramflag
1369
+ True
1370
+ >>> states.sm.diskflag_reading
1371
+ False
1372
+ >>> states.sm.diskflag_writing
1373
+ True
1374
+ >>> states.sm.diskflag
1375
+ True
1376
+ >>> round_(states.sm.series[:, 0])
1377
+ nan, nan, nan, nan
1378
+
1379
+ Now we perform a simulation run. Note that we need to change the current working
1380
+ directory to the `iotesting` directory temporarily (by using class |TestIO|)
1381
+ because the relevant NetCDF files are now read and written on the fly:
1382
+
1383
+ >>> with TestIO():
1384
+ ... hp.simulate()
1385
+
1386
+ After the simulation run, the read (|hland_inputs.T|) and calculated
1387
+ (|hland_states.SM| and |hland_states.SP|) time series of the sequences with an
1388
+ activated |IOSequence.ramflag| are directly available:
1389
+
1390
+ >>> round_(inputs.t.series)
1391
+ -0.7, -1.5, -4.6, -8.2
1392
+ >>> round_(states.sm.series[:, 0])
1393
+ 99.1369, 99.01204, 98.93674, 98.91913
1394
+ >>> round_(states.sp.series[:, 0, 0])
1395
+ 0.0, 0.0, 0.0, 0.0
1396
+
1397
+ To inspect the time series of |hland_factors.ContriArea| and |hland_factors.TC|,
1398
+ you must first activate their |IOSequence.ramflag| and then load their data
1399
+ manually with method |IOSequence.load_series|. The latter requires some additional
1400
+ configuration effort (see the documentation on module |netcdftools| for further
1401
+ information):
1402
+
1403
+ >>> factors.contriarea.prepare_series()
1404
+ >>> factors.tc.prepare_series()
1405
+ >>> pub.sequencemanager.filetype = "nc"
1406
+ >>> with TestIO():
1407
+ ... pub.sequencemanager.open_netcdfreader()
1408
+ ... factors.contriarea.load_series()
1409
+ ... factors.tc.load_series()
1410
+ ... pub.sequencemanager.close_netcdfreader()
1411
+ >>> round_(factors.contriarea.series)
1412
+ 0.431311, 0.430524, 0.430049, 0.429938
1413
+ >>> round_(factors.tc.series[:, 0])
1414
+ 0.453086, -0.346914, -3.446914, -7.046914
1415
+
1416
+ We also load time series of |hland_states.SM| and |hland_states.SP| to demonstrate
1417
+ that the data written to the respective NetCDF files are identical with the data
1418
+ directly stored in RAM:
1419
+
1420
+ >>> with TestIO():
1421
+ ... pub.sequencemanager.open_netcdfreader()
1422
+ ... states.sm.load_series()
1423
+ ... states.sp.load_series()
1424
+ ... pub.sequencemanager.close_netcdfreader()
1425
+ >>> round_(states.sm.series[:, 0])
1426
+ 99.1369, 99.01204, 98.93674, 98.91913
1427
+ >>> round_(states.sp.series[:, 0, 0])
1428
+ 0.0, 0.0, 0.0, 0.0
1429
+
1430
+ Writing the time series of input sequences on the fly is supported but not
1431
+ simultaneously with reading them (at best, one would overwrite the same file with
1432
+ the same data; at worst, one could corrupt it):
1433
+
1434
+ >>> inputs.t.prepare_series(read_jit=True, write_jit=True)
1435
+ Traceback (most recent call last):
1436
+ ...
1437
+ ValueError: Reading from and writing into the same NetCDF file "just in time" \
1438
+ during a simulation run is not supported but tried for sequence `t` of element \
1439
+ `land_lahn_marb`.
1440
+
1441
+ For simplifying the following examples, we now handle all model time series in RAM:
1442
+
1443
+ >>> pub.sequencemanager.filetype = "asc"
1444
+ >>> hp.prepare_modelseries()
1445
+ >>> with TestIO():
1446
+ ... hp.load_inputseries()
1447
+
1448
+ You cannot only access the time series data of individual |IOSequence| objects, but
1449
+ you can also modify it. See, for example, the simulated time series for flux
1450
+ sequence |hland_fluxes.PC| (adjusted precipitation), which is zero because the
1451
+ values of input sequence |hland_inputs.P| (given precipitation) are also zero:
1452
+
1453
+ >>> round_(fluxes.pc.series[:, 0])
1454
+ 0.0, 0.105611, 0.0, 0.0
1455
+
1456
+ We can assign different values to attribute |IOSequence.series| of sequence
1457
+ |hland_inputs.P|, perform a new simulation run, and see that the newly calculated
1458
+ time series of sequence |hland_fluxes.PC| reflects our data modification:
1459
+
1460
+ >>> inputs.p.series = 10.0
1461
+ >>> hp.simulate()
1462
+ >>> round_(fluxes.pc.series[:, 0])
1463
+ 9.154557, 10.561131, 10.665633, 10.665633
1464
+
1465
+ Another convenience property is |IOSequence.seriesshape|, which combines the length
1466
+ of the simulation period with the shape of the individual |IOSequence| object:
1467
+
1468
+ >>> inputs.p.seriesshape
1469
+ (4,)
1470
+ >>> fluxes.pc.seriesshape
1471
+ (4, 13)
1472
+
1473
+ Note that resetting the |IOSequence.shape| of an |IOSequence| object does not
1474
+ change how it handles its internal time series data but results in a loss of
1475
+ current information:
1476
+
1477
+ >>> factors.tc.seriesshape
1478
+ (4, 13)
1479
+ >>> factors.fastaccess._tc_length
1480
+ 13
1481
+ >>> round_(factors.tc.series[:, 0], 1)
1482
+ 0.5, -0.3, -3.4, -7.0
1483
+
1484
+ >>> factors.tc.shape = 2,
1485
+ >>> factors.tc.seriesshape
1486
+ (4, 2)
1487
+ >>> factors.fastaccess._tc_length
1488
+ 2
1489
+ >>> round_(factors.tc.series[:, 0])
1490
+ nan, nan, nan, nan
1491
+
1492
+ Resetting the |IOSequence.shape| of |IOSequence| objects with a deactivated
1493
+ |IOSequence.ramflag| data works likewise:
1494
+
1495
+ >>> fluxes.pc.prepare_series(allocate_ram=False)
1496
+
1497
+ >>> fluxes.pc.seriesshape
1498
+ (4, 13)
1499
+ >>> fluxes.fastaccess._pc_length
1500
+ 13
1501
+ >>> fluxes.pc.series
1502
+ Traceback (most recent call last):
1503
+ ...
1504
+ hydpy.core.exceptiontools.AttributeNotReady: Sequence `pc` of element \
1505
+ `land_lahn_marb` is not requested to make any time series data available.
1506
+
1507
+ >>> fluxes.pc.shape = (2,)
1508
+ >>> fluxes.pc.seriesshape
1509
+ (4, 2)
1510
+ >>> fluxes.fastaccess._pc_length
1511
+ 2
1512
+ >>> fluxes.pc.series = 1.0
1513
+ Traceback (most recent call last):
1514
+ ...
1515
+ hydpy.core.exceptiontools.AttributeNotReady: Sequence `pc` of element \
1516
+ `land_lahn_marb` is not requested to make any time series data available.
1517
+
1518
+ .. testsetup::
1519
+
1520
+ >>> from hydpy import Node, Element
1521
+ >>> Node.clear_all()
1522
+ >>> Element.clear_all()
1523
+ """
1524
+
1525
+ subvars: (
1526
+ IOSequences[Sequences, IOSequence, FastAccessIOSequence]
1527
+ | IOSequences[devicetools.Node, IOSequence, FastAccessIOSequence]
1528
+ )
1529
+ """The subgroup to which the IO sequence belongs."""
1530
+ subseqs: (
1531
+ IOSequences[Sequences, IOSequence, FastAccessIOSequence]
1532
+ | IOSequences[devicetools.Node, IOSequence, FastAccessIOSequence]
1533
+ )
1534
+ """Alias for |IOSequence.subvars|."""
1535
+ fastaccess: FastAccessIOSequence
1536
+ """Object for accessing the IO sequence's data with little overhead."""
1537
+
1538
+ def _finalise_connections(self) -> None:
1539
+ self._set_fastaccessattribute("ramflag", False)
1540
+ self._set_fastaccessattribute("diskflag_reading", False)
1541
+ self._set_fastaccessattribute("diskflag_writing", False)
1542
+ super()._finalise_connections()
1543
+
1544
+ @propertytools.DefaultPropertySeriesFileType
1545
+ def filetype(self) -> SeriesFileType:
1546
+ """"Ending of the time series data file.
1547
+
1548
+ Usually, |IOSequence| objects query the current file type from the
1549
+ |SequenceManager| object available in the global |pub| module:
1550
+
1551
+ >>> from hydpy import pub
1552
+ >>> from hydpy.core.filetools import SequenceManager
1553
+ >>> pub.sequencemanager = SequenceManager()
1554
+
1555
+ >>> from hydpy.core.sequencetools import InputSequence
1556
+ >>> inputsequence = InputSequence(None)
1557
+ >>> inputsequence.filetype
1558
+ 'asc'
1559
+
1560
+ Alternatively, you can specify the file type for each |IOSequence| object
1561
+ individually:
1562
+
1563
+ >>> inputsequence.filetype = "npy"
1564
+ >>> inputsequence.filetype
1565
+ 'npy'
1566
+ >>> inputsequence.filetype = "nc"
1567
+ >>> inputsequence.filetype
1568
+ 'nc'
1569
+
1570
+ Use the `del` statement to reset the object-specific setting:
1571
+
1572
+ >>> del inputsequence.filetype
1573
+ >>> inputsequence.filetype
1574
+ 'asc'
1575
+
1576
+ If neither a specific definition nor a |SequenceManager| object is available,
1577
+ property |IOSequence.filetype| raises the following error:
1578
+
1579
+ >>> del pub.sequencemanager
1580
+ >>> inputsequence.filetype
1581
+ Traceback (most recent call last):
1582
+ ...
1583
+ hydpy.core.exceptiontools.AttributeNotReady: Sequence `inputsequence` does \
1584
+ not know its file type. Either set it manually or prepare `pub.sequencemanager` \
1585
+ correctly.
1586
+ """
1587
+ try:
1588
+ return cast(SeriesFileType, hydpy.pub.sequencemanager.filetype)
1589
+ except exceptiontools.AttributeNotReady:
1590
+ raise exceptiontools.AttributeNotReady(
1591
+ f"Sequence {objecttools.devicephrase(self)} does not know its file "
1592
+ f"type. Either set it manually or prepare `pub.sequencemanager` "
1593
+ f"correctly."
1594
+ ) from None
1595
+
1596
+ @propertytools.DefaultPropertySeriesAggregationType
1597
+ def aggregation(self) -> SeriesAggregationType:
1598
+ """Type of aggregation for writing the time series to a data file.
1599
+
1600
+ Usually, |IOSequence| objects query the current aggregation mode from the
1601
+ |SequenceManager| object available in the global |pub| module:
1602
+
1603
+ >>> from hydpy import pub
1604
+ >>> from hydpy.core.filetools import SequenceManager
1605
+ >>> pub.sequencemanager = SequenceManager()
1606
+
1607
+ >>> from hydpy.core.sequencetools import FluxSequence
1608
+ >>> fluxsequence = FluxSequence(None)
1609
+ >>> fluxsequence.aggregation
1610
+ 'none'
1611
+
1612
+ Alternatively, you can specify the aggregation for each |IOSequence| object
1613
+ individually:
1614
+
1615
+ >>> fluxsequence.aggregation = "mean"
1616
+ >>> fluxsequence.aggregation
1617
+ 'mean'
1618
+
1619
+ Use the `del` statement to reset the object-specific setting:
1620
+
1621
+ >>> del fluxsequence.aggregation
1622
+ >>> fluxsequence.aggregation
1623
+ 'none'
1624
+
1625
+ If neither a specific definition nor a |SequenceManager| object is available,
1626
+ property |IOSequence.aggregation| raises the following error:
1627
+
1628
+ >>> del pub.sequencemanager
1629
+ >>> fluxsequence.aggregation
1630
+ Traceback (most recent call last):
1631
+ ...
1632
+ hydpy.core.exceptiontools.AttributeNotReady: Sequence `fluxsequence` does not \
1633
+ know its aggregation mode. Either set it manually or prepare `pub.sequencemanager` \
1634
+ correctly.
1635
+ """
1636
+ try:
1637
+ return cast(SeriesAggregationType, hydpy.pub.sequencemanager.aggregation)
1638
+ except exceptiontools.AttributeNotReady:
1639
+ raise exceptiontools.AttributeNotReady(
1640
+ f"Sequence {objecttools.devicephrase(self)} does not know its "
1641
+ f"aggregation mode. Either set it manually or prepare "
1642
+ f"`pub.sequencemanager` correctly."
1643
+ ) from None
1644
+
1645
+ @propertytools.DefaultPropertyBool
1646
+ def overwrite(self) -> bool:
1647
+ """True/False flag indicating if overwriting an existing data file is allowed
1648
+ or not.
1649
+
1650
+ Usually, |IOSequence| objects query the current overwrite flag from the
1651
+ |SequenceManager| object available in the global |pub| module:
1652
+
1653
+ >>> from hydpy import pub
1654
+ >>> from hydpy.core.filetools import SequenceManager
1655
+ >>> pub.sequencemanager = SequenceManager()
1656
+
1657
+ >>> from hydpy.core.sequencetools import FluxSequence
1658
+ >>> fluxsequence = FluxSequence(None)
1659
+ >>> fluxsequence.overwrite
1660
+ 0
1661
+
1662
+ Alternatively, you can specify the overwrite flag for each |IOSequence| object
1663
+ individually:
1664
+
1665
+ >>> fluxsequence.overwrite = True
1666
+ >>> fluxsequence.overwrite
1667
+ 1
1668
+
1669
+ Use the `del` statement to reset the object-specific setting:
1670
+
1671
+ >>> del fluxsequence.overwrite
1672
+ >>> fluxsequence.overwrite
1673
+ 0
1674
+
1675
+ If neither a specific definition nor a |SequenceManager| object is available,
1676
+ property |IOSequence.overwrite| raises the following error:
1677
+
1678
+ >>> del pub.sequencemanager
1679
+ >>> fluxsequence.overwrite
1680
+ Traceback (most recent call last):
1681
+ ...
1682
+ hydpy.core.exceptiontools.AttributeNotReady: Sequence `fluxsequence` does not \
1683
+ know its overwrite flag. Either set it manually or prepare `pub.sequencemanager` \
1684
+ correctly.
1685
+ """
1686
+ try:
1687
+ return bool(hydpy.pub.sequencemanager.overwrite)
1688
+ except exceptiontools.AttributeNotReady:
1689
+ raise exceptiontools.AttributeNotReady(
1690
+ f"Sequence {objecttools.devicephrase(self)} does not know its "
1691
+ f"overwrite flag. Either set it manually or prepare "
1692
+ f"`pub.sequencemanager` correctly."
1693
+ ) from None
1694
+
1695
+ @propertytools.DefaultPropertyStr
1696
+ def filename(self) -> str:
1697
+ """The filename of the relevant time series file.
1698
+
1699
+ By default, the filenames of file types that store time series of single
1700
+ sequence instance consists of |IOSequence.descr_device|,
1701
+ |IOSequence.descr_sequence|, and |IOSequence.filetype|:
1702
+
1703
+ >>> from hydpy.core.sequencetools import StateSequence
1704
+ >>> class S(StateSequence):
1705
+ ... descr_device = "device"
1706
+ ... descr_sequence = "group_sequence"
1707
+ ... filetype = "npy"
1708
+ ... aggregation = "none"
1709
+ >>> s = S(None)
1710
+ >>> s.filename
1711
+ 'device_group_sequence.npy'
1712
+
1713
+ For file types that store time series of multiple sequence instances,
1714
+ |IOSequence.descr_device| is omitted:
1715
+
1716
+ >>> s.filetype = "nc"
1717
+ >>> s.filename
1718
+ 'group_sequence.nc'
1719
+
1720
+ When dealing with aggregated time series, the aggregation mode is suffixed:
1721
+
1722
+ >>> s.aggregation = "mean"
1723
+ >>> s.filename
1724
+ 'group_sequence_mean.nc'
1725
+ >>> s.filetype = "asc"
1726
+ >>> s.filename
1727
+ 'device_group_sequence_mean.asc'
1728
+ """
1729
+ if (agg := self.aggregation) == "none":
1730
+ aggregation = ""
1731
+ else:
1732
+ aggregation = f"_{agg}"
1733
+ if (filetype := self.filetype) == "nc":
1734
+ return f"{self.descr_sequence}{aggregation}.nc"
1735
+ return f"{self.descr_device}_{self.descr_sequence}{aggregation}.{filetype}"
1736
+
1737
+ @propertytools.DefaultPropertyStr
1738
+ def dirpath(self) -> str:
1739
+ """The absolute path to the time series directory.
1740
+
1741
+ As long as it is not overwritten, |IOSequence.dirpath| is identical to the
1742
+ attribute |FileManager.currentpath| of the |SequenceManager| object available
1743
+ in module |pub|:
1744
+
1745
+ >>> from hydpy import pub, repr_
1746
+ >>> from hydpy.core.filetools import SequenceManager
1747
+ >>> class SM(SequenceManager):
1748
+ ... currentpath = "temp"
1749
+ >>> pub.sequencemanager = SM()
1750
+ >>> from hydpy.core.sequencetools import StateSequence
1751
+ >>> repr_(StateSequence(None).dirpath)
1752
+ 'temp'
1753
+ """
1754
+ return hydpy.pub.sequencemanager.currentpath
1755
+
1756
+ @propertytools.DefaultPropertyStr
1757
+ def filepath(self) -> str:
1758
+ """The absolute path to the time series file.
1759
+
1760
+ The path pointing to the file consists of |IOSequence.dirpath| and
1761
+ |IOSequence.filename|:
1762
+
1763
+ >>> from hydpy.core.sequencetools import StateSequence
1764
+ >>> seq = StateSequence(None)
1765
+ >>> seq.dirpath = "path"
1766
+ >>> seq.filename = "file.npy"
1767
+ >>> from hydpy import repr_
1768
+ >>> repr_(seq.filepath)
1769
+ 'path/file.npy'
1770
+ """
1771
+ return os.path.join(self.dirpath, self.filename)
1772
+
1773
+ def update_fastaccess(self) -> None:
1774
+ """Update the |FastAccessIOSequence| object handled by the actual |IOSequence|
1775
+ object.
1776
+
1777
+ Users do not need to apply the method |IOSequence.update_fastaccess| directly.
1778
+ The following information should be relevant for framework developers only.
1779
+
1780
+ The main documentation on class |Sequence_| mentions that the
1781
+ |FastAccessIOSequence| attribute handles some information about its sequences,
1782
+ but it needs to be kept up-to-date by the sequences themselves. This updating
1783
+ is the task of method |IOSequence.update_fastaccess|, being called by some
1784
+ other methods class |IOSequence| call. We show this via the hidden attribute
1785
+ `length`, which is 0 after initialisation, and automatically set to another
1786
+ value when assigning it to property |IOSequence.shape| of |IOSequence|
1787
+ subclasses as |lland_fluxes.NKor|:
1788
+
1789
+ >>> from hydpy import prepare_model
1790
+ >>> model = prepare_model("lland_dd")
1791
+ >>> nkor = model.sequences.fluxes.nkor
1792
+ >>> nkor.fastaccess._nkor_length
1793
+ 0
1794
+ >>> nkor.shape = (3,)
1795
+ >>> nkor.fastaccess._nkor_length
1796
+ 3
1797
+ """
1798
+ length = 1
1799
+ for idx in range(self.NDIM):
1800
+ length *= self.shape[idx]
1801
+ self._set_fastaccessattribute(f"length_{idx}", self.shape[idx])
1802
+ self._set_fastaccessattribute("length", length)
1803
+
1804
+ def connect_netcdf(self, ncarray: NDArrayFloat) -> None:
1805
+ """Connect the current |IOSequence| object to the given buffer array for
1806
+ reading from or writing to a NetCDF file on the fly during a simulation run."""
1807
+ self._set_fastaccessattribute("ncarray", ncarray)
1808
+
1809
+ def prepare_series(
1810
+ self,
1811
+ allocate_ram: bool | None = True,
1812
+ read_jit: bool | None = False,
1813
+ write_jit: bool | None = False,
1814
+ ) -> None:
1815
+ """Define how to handle the time series data of the current |IOSequence| object.
1816
+
1817
+ See the main documentation on class |IOSequence| for general information on
1818
+ method |IOSequence.prepare_series|. Here, we only discuss the special case of
1819
+ passing |None| to it to preserve predefined settings.
1820
+
1821
+ When leaving out certain arguments, |IOSequence.prepare_series| takes their
1822
+ boolean defaults. That means subsequent calls overwrite previous ones:
1823
+
1824
+ >>> from hydpy.core.testtools import prepare_full_example_2
1825
+ >>> hp, pub, TestIO = prepare_full_example_2()
1826
+ >>> t = hp.elements.land_lahn_marb.model.sequences.inputs.t
1827
+ >>> t.prepare_series(allocate_ram=False, read_jit=True)
1828
+ >>> t.ramflag, t.diskflag_reading, t.diskflag_writing
1829
+ (False, True, False)
1830
+ >>> t.prepare_series(write_jit=True)
1831
+ >>> t.ramflag, t.diskflag_reading, t.diskflag_writing
1832
+ (True, False, True)
1833
+
1834
+ If you want to change one setting without modifying the others, pass |None| to
1835
+ the latter:
1836
+
1837
+ >>> t.prepare_series(allocate_ram=False, read_jit=None, write_jit=None)
1838
+ >>> t.ramflag, t.diskflag_reading, t.diskflag_writing
1839
+ (False, False, True)
1840
+ >>> t.prepare_series(allocate_ram=None, read_jit=True, write_jit=False)
1841
+ >>> t.ramflag, t.diskflag_reading, t.diskflag_writing
1842
+ (False, True, False)
1843
+ >>> t.prepare_series(allocate_ram=None, read_jit=None, write_jit=None)
1844
+ >>> t.ramflag, t.diskflag_reading, t.diskflag_writing
1845
+ (False, True, False)
1846
+
1847
+ The check for configurations attempting to both read and write "just in time"
1848
+ takes predefined flags into account:
1849
+
1850
+ >>> t.prepare_series(read_jit=None, write_jit=True)
1851
+ Traceback (most recent call last):
1852
+ ...
1853
+ ValueError: Reading from and writing into the same NetCDF file "just in time" \
1854
+ during a simulation run is not supported but tried for sequence `t` of element \
1855
+ `land_lahn_marb`.
1856
+
1857
+ >>> t.prepare_series(read_jit=False, write_jit=True)
1858
+ >>> t.prepare_series(read_jit=True, write_jit=None)
1859
+ Traceback (most recent call last):
1860
+ ...
1861
+ ValueError: Reading from and writing into the same NetCDF file "just in time" \
1862
+ during a simulation run is not supported but tried for sequence `t` of element \
1863
+ `land_lahn_marb`.
1864
+ """
1865
+ readflag = read_jit or ((read_jit is None) and self.diskflag_reading)
1866
+ writeflag = write_jit or ((write_jit is None) and self.diskflag_writing)
1867
+ if readflag and writeflag:
1868
+ raise ValueError(
1869
+ f'Reading from and writing into the same NetCDF file "just in time" '
1870
+ f"during a simulation run is not supported but tried for sequence "
1871
+ f"{objecttools.devicephrase(self)}."
1872
+ )
1873
+ if allocate_ram is not None:
1874
+ ramflag = self.ramflag
1875
+ if allocate_ram and not ramflag:
1876
+ self.__set_array(
1877
+ numpy.full(self.seriesshape, numpy.nan, dtype=config.NP_FLOAT)
1878
+ )
1879
+ if ramflag and not allocate_ram:
1880
+ del self.series
1881
+ self._set_fastaccessattribute("ramflag", allocate_ram)
1882
+ if read_jit is not None:
1883
+ inflag = self._get_fastaccessattribute("inputflag", False)
1884
+ self._set_fastaccessattribute("diskflag_reading", read_jit and not inflag)
1885
+ if write_jit is not None:
1886
+ self._set_fastaccessattribute("diskflag_writing", write_jit)
1887
+ self.update_fastaccess()
1888
+
1889
+ @property
1890
+ def ramflag(self) -> bool:
1891
+ """A flag telling if the actual |IOSequence| object makes its time series data
1892
+ directly available in RAM.
1893
+
1894
+ See the main documentation on class |IOSequence| for further information.
1895
+ """
1896
+ return self._get_fastaccessattribute("ramflag")
1897
+
1898
+ @property
1899
+ def diskflag_reading(self) -> bool:
1900
+ """A flag telling if the actual |IOSequence| reads its time series data on the
1901
+ fly from a NetCDF file during a simulation run.
1902
+
1903
+ See the main documentation on class |IOSequence| for further information.
1904
+ """
1905
+ return self._get_fastaccessattribute("diskflag_reading")
1906
+
1907
+ @property
1908
+ def diskflag_writing(self) -> bool:
1909
+ """A flag telling if the actual |IOSequence| writes its time series data on the
1910
+ fly to a NetCDF file during a simulation run.
1911
+
1912
+ See the main documentation on class |IOSequence| for further information.
1913
+ """
1914
+ return self._get_fastaccessattribute("diskflag_writing")
1915
+
1916
+ @property
1917
+ def diskflag(self) -> bool:
1918
+ """A flag telling if |IOSequence.diskflag_reading| and/or
1919
+ |IOSequence.diskflag_writing| of the current |IOSequence| object is |True|:
1920
+
1921
+ >>> from hydpy.core.sequencetools import StateSequence
1922
+ >>> for reading in (False, True):
1923
+ ... for writing in (False, True):
1924
+ ... class S(StateSequence):
1925
+ ... diskflag_reading = reading
1926
+ ... diskflag_writing = writing
1927
+ ... print(reading, writing, S(None).diskflag)
1928
+ False False False
1929
+ False True True
1930
+ True False True
1931
+ True True True
1932
+ """
1933
+ return self.diskflag_reading or self.diskflag_writing
1934
+
1935
+ @property
1936
+ def memoryflag(self) -> bool:
1937
+ """A flag telling if either |IOSequence.ramflag| and/or |IOSequence.diskflag|
1938
+ of the current |IOSequence| object is |True|:
1939
+
1940
+ >>> from hydpy.core.sequencetools import StateSequence
1941
+ >>> for ram in (False, True):
1942
+ ... for disk in (False, True):
1943
+ ... class S(StateSequence):
1944
+ ... ramflag = ram
1945
+ ... diskflag = disk
1946
+ ... print(ram, disk, S(None).memoryflag)
1947
+ False False False
1948
+ False True True
1949
+ True False True
1950
+ True True True
1951
+ """
1952
+ return self.ramflag or self.diskflag
1953
+
1954
+ @property
1955
+ def seriesmode(self) -> SeriesMode:
1956
+ """A combination of property |IOSequence.ramflag|,
1957
+ |IOSequence.diskflag_reading|, and |IOSequence.diskflag_writing|.
1958
+
1959
+ |IOSequence.seriesmode| allows querying and changing all mentioned properties in
1960
+ one step:
1961
+
1962
+ >>> from hydpy.core.testtools import prepare_full_example_2
1963
+ >>> hp, pub, TestIO = prepare_full_example_2()
1964
+ >>> t = hp.elements.land_lahn_marb.model.sequences.inputs.t
1965
+ >>> t.prepare_series(read_jit=True)
1966
+ >>> sm_t = t.seriesmode
1967
+ >>> sm_t
1968
+ SeriesMode(ramflag=True, diskflag_reading=True, diskflag_writing=False)
1969
+ >>> p = hp.elements.land_lahn_marb.model.sequences.inputs.p
1970
+ >>> p.prepare_series(allocate_ram=False, write_jit=True)
1971
+ >>> sm_p = p.seriesmode
1972
+ >>> sm_p
1973
+ SeriesMode(ramflag=False, diskflag_reading=False, diskflag_writing=True)
1974
+
1975
+ >>> t.seriesmode = sm_p
1976
+ >>> t.seriesmode
1977
+ SeriesMode(ramflag=False, diskflag_reading=False, diskflag_writing=True)
1978
+ >>> p.seriesmode = sm_t
1979
+ >>> p.seriesmode
1980
+ SeriesMode(ramflag=True, diskflag_reading=True, diskflag_writing=False)
1981
+ """
1982
+ return SeriesMode(
1983
+ ramflag=self.ramflag,
1984
+ diskflag_reading=self.diskflag_reading,
1985
+ diskflag_writing=self.diskflag_writing,
1986
+ )
1987
+
1988
+ @seriesmode.setter
1989
+ def seriesmode(self, sm: SeriesMode) -> None:
1990
+ self.prepare_series(
1991
+ allocate_ram=sm.ramflag,
1992
+ read_jit=sm.diskflag_reading,
1993
+ write_jit=sm.diskflag_writing,
1994
+ )
1995
+
1996
+ def __set_array(self, values):
1997
+ values = numpy.array(values, dtype=config.NP_FLOAT)
1998
+ self._set_fastaccessattribute("array", values)
1999
+
2000
+ def _get_shape(self) -> tuple[int, ...]:
2001
+ """A tuple containing the actual lengths of all dimensions.
2002
+
2003
+ When setting a new |IOSequence.shape| of an |IOSequence| object, one
2004
+ automatically calls method |IOSequence.update_fastaccess| and, if necessary,
2005
+ prepares the new internal |IOSequence.series| array.
2006
+
2007
+ See the main documentation on class |IOSequence| for further information.
2008
+ """
2009
+ return super()._get_shape()
2010
+
2011
+ def _set_shape(self, shape: int | tuple[int, ...]):
2012
+ super()._set_shape(shape)
2013
+ if self.ramflag:
2014
+ values = numpy.full(self.seriesshape, numpy.nan, dtype=config.NP_FLOAT)
2015
+ self.__set_array(values)
2016
+ self.update_fastaccess()
2017
+
2018
+ shape = propertytools.Property(fget=_get_shape, fset=_set_shape)
2019
+
2020
+ @property
2021
+ def seriesshape(self) -> tuple[int, ...]:
2022
+ """The shape of the whole time series (time being the first dimension)."""
2023
+ seriesshape = [len(hydpy.pub.timegrids.init)]
2024
+ seriesshape.extend(self.shape)
2025
+ return tuple(seriesshape)
2026
+
2027
+ def _get_series(self) -> InfoArray:
2028
+ """The complete time series data of the current |IOSequence| object within an
2029
+ |InfoArray| covering the whole initialisation period (defined by the
2030
+ |Timegrids.init| |Timegrid| of the global |Timegrids| object available in
2031
+ module |pub|)."""
2032
+ if self.ramflag:
2033
+ array = numpy.asarray(self._get_fastaccessattribute("array"))
2034
+ return InfoArray(array, aggregation="unmodified")
2035
+ raise exceptiontools.AttributeNotReady(
2036
+ f"Sequence {objecttools.devicephrase(self)} is not requested to make any "
2037
+ f"time series data available."
2038
+ )
2039
+
2040
+ def _set_series(self, values) -> None:
2041
+ if self.ramflag:
2042
+ self.__set_array(
2043
+ numpy.full(self.seriesshape, values, dtype=config.NP_FLOAT)
2044
+ )
2045
+ self.check_completeness()
2046
+ else:
2047
+ raise exceptiontools.AttributeNotReady(
2048
+ f"Sequence {objecttools.devicephrase(self)} is not requested to make "
2049
+ f"any time series data available."
2050
+ )
2051
+
2052
+ def _del_series(self) -> None:
2053
+ if self.ramflag:
2054
+ self._set_fastaccessattribute("array", None)
2055
+ self._set_fastaccessattribute("ramflag", False)
2056
+
2057
+ series = property(_get_series, _set_series, _del_series)
2058
+
2059
+ def _get_simseries(self) -> InfoArray:
2060
+ """Read and write access to the subset of the data of property
2061
+ |IOSequence.series| covering the actual simulation period (defined by the
2062
+ |Timegrids.sim| |Timegrid| of the global |Timegrids| object available in module
2063
+ |pub|).
2064
+
2065
+ >>> from hydpy.core.testtools import prepare_full_example_2
2066
+ >>> hp, pub, TestIO = prepare_full_example_2()
2067
+ >>> t = hp.elements.land_lahn_marb.model.sequences.inputs.t
2068
+ >>> pub.timegrids.sim.dates = "1996-01-02", "1996-01-04"
2069
+ >>> from hydpy import print_vector
2070
+ >>> print_vector(t.series)
2071
+ -0.7, -1.5, -4.6, -8.2
2072
+ >>> print_vector(t.simseries)
2073
+ -1.5, -4.6
2074
+ >>> t.simseries = 1.0, 2.0
2075
+ >>> print_vector(t.series)
2076
+ -0.7, 1.0, 2.0, -8.2
2077
+
2078
+ .. testsetup::
2079
+
2080
+ >>> from hydpy import Element, Node
2081
+ >>> Element.clear_all()
2082
+ >>> Node.clear_all()
2083
+ """
2084
+ idx0, idx1 = hydpy.pub.timegrids.simindices
2085
+ return self.series[idx0:idx1]
2086
+
2087
+ def _set_simseries(self, values) -> None:
2088
+ idx0, idx1 = hydpy.pub.timegrids.simindices
2089
+ self.series[idx0:idx1] = values
2090
+
2091
+ simseries = property(_get_simseries, _set_simseries)
2092
+
2093
+ def _get_evalseries(self) -> InfoArray:
2094
+ """Read and write access to the subset of the data of property |
2095
+ IOSequence.series| covering the actual evaluation period (defined by the
2096
+ |Timegrids.eval_| |Timegrid| of the global |Timegrids| object available in
2097
+ module |pub|).
2098
+
2099
+ >>> from hydpy.core.testtools import prepare_full_example_2
2100
+ >>> hp, pub, TestIO = prepare_full_example_2()
2101
+ >>> t = hp.elements.land_lahn_marb.model.sequences.inputs.t
2102
+ >>> pub.timegrids.eval_.dates = "1996-01-02", "1996-01-04"
2103
+ >>> from hydpy import print_vector
2104
+ >>> print_vector(t.series)
2105
+ -0.7, -1.5, -4.6, -8.2
2106
+ >>> print_vector(t.evalseries)
2107
+ -1.5, -4.6
2108
+ >>> t.evalseries = 1.0, 2.0
2109
+ >>> print_vector(t.series)
2110
+ -0.7, 1.0, 2.0, -8.2
2111
+
2112
+ .. testsetup::
2113
+
2114
+ >>> from hydpy import Element, Node
2115
+ >>> Element.clear_all()
2116
+ >>> Node.clear_all()
2117
+ """
2118
+ idx0, idx1 = hydpy.pub.timegrids.evalindices
2119
+ return self.series[idx0:idx1]
2120
+
2121
+ def _set_evalseries(self, values) -> None:
2122
+ idx0, idx1 = hydpy.pub.timegrids.evalindices
2123
+ self.series[idx0:idx1] = values
2124
+
2125
+ evalseries = property(_get_evalseries, _set_evalseries)
2126
+
2127
+ def load_series(self) -> None:
2128
+ """Read time series data from a file.
2129
+
2130
+ Method |IOSequence.load_series| only calls method |SequenceManager.load_file|
2131
+ of class |SequenceManager| passing itself as the only argument. Hence, see the
2132
+ documentation on the class |SequenceManager| for further information. The
2133
+ following example only shows the error messages when |SequenceManager.load_file|
2134
+ is missing due to incomplete project configurations:
2135
+
2136
+ .. testsetup::
2137
+
2138
+ >>> from hydpy import pub
2139
+ >>> del pub.sequencemanager
2140
+
2141
+ >>> from hydpy.core.sequencetools import StateSequence
2142
+ >>> StateSequence(None).load_series()
2143
+ Traceback (most recent call last):
2144
+ ...
2145
+ hydpy.core.exceptiontools.AttributeNotReady: While trying to load the time \
2146
+ series data of `statesequence`, the following error occurred: Attribute \
2147
+ sequencemanager of module `pub` is not defined at the moment.
2148
+ """
2149
+ try:
2150
+ sequencemanager = hydpy.pub.sequencemanager
2151
+ except BaseException:
2152
+ objecttools.augment_excmessage(
2153
+ f"While trying to load the time series data of "
2154
+ f"{objecttools.devicephrase(self)}"
2155
+ )
2156
+ sequencemanager.load_file(self)
2157
+
2158
+ def adjust_series(
2159
+ self, timegrid_data: timetools.Timegrid, values: NDArrayFloat
2160
+ ) -> NDArrayFloat:
2161
+ """Adjust a time series to the current initialisation period.
2162
+
2163
+ Note that, in most *HydPy* applications, method |IOSequence.adjust_series| is
2164
+ called by other methods related to reading data from files and does not need to
2165
+ be called by the user directly. However, if you want to call it directly for
2166
+ some reason, you need to make sure that the shape of the given |numpy|
2167
+ |numpy.ndarray| fits the given |Timegrid| object.
2168
+
2169
+ Often, time series data available in data files cover a longer period than
2170
+ required for an actual simulation run. Method |IOSequence.adjust_series|
2171
+ selects the relevant data by comparing the initialisation |Timegrid| available
2172
+ in module |pub| and the given "data" |Timegrid| object. We explain this
2173
+ behaviour by using the `HydPy-H-Lahn` example project and focussing on the
2174
+ |Obs| sequence of |Node| `dill_assl`:
2175
+
2176
+ >>> from hydpy.core.testtools import prepare_full_example_2
2177
+ >>> hp, pub, TestIO = prepare_full_example_2()
2178
+ >>> obs = hp.nodes.dill_assl.sequences.obs
2179
+
2180
+ With identical initialisation and data time grids, method
2181
+ |IOSequence.adjust_series| returns the given data completely:
2182
+
2183
+ >>> from hydpy import print_vector, Timegrid
2184
+ >>> import numpy
2185
+ >>> with TestIO(), pub.options.checkseries(False):
2186
+ ... print_vector(obs.adjust_series(
2187
+ ... Timegrid("1996-01-01", "1996-01-05", "1d"),
2188
+ ... numpy.arange(4, dtype=float)))
2189
+ 0.0, 1.0, 2.0, 3.0
2190
+
2191
+ For "too long" data, it only returns the relevant one:
2192
+
2193
+ >>> with TestIO(), pub.options.checkseries(False):
2194
+ ... print_vector(obs.adjust_series(
2195
+ ... Timegrid("1995-12-31", "1996-01-07", "1d"),
2196
+ ... numpy.arange(7, dtype=float)))
2197
+ 1.0, 2.0, 3.0, 4.0
2198
+
2199
+ For "too short" data, the behaviour differs depending on option
2200
+ |Options.checkseries|. With |Options.checkseries| being enabled, method
2201
+ |IOSequence.adjust_series| raises a |RuntimeError|. With |Options.checkseries|
2202
+ being disabled, it extends the given array with |numpy.nan| values (using
2203
+ method |IOSequence.adjust_short_series|):
2204
+
2205
+ >>> with TestIO(), pub.options.checkseries(True):
2206
+ ... obs.adjust_series(Timegrid("1996-01-02", "1996-01-04", "1d"),
2207
+ ... numpy.zeros((3,))) # doctest: +ELLIPSIS
2208
+ Traceback (most recent call last):
2209
+ ...
2210
+ RuntimeError: For sequence `obs` of node `dill_assl` the initialisation time \
2211
+ grid (Timegrid("1996-01-01 00:00:00", "1996-01-05 00:00:00", "1d")) does not define a \
2212
+ subset of the time grid of the data file `...dill_assl_obs_q.asc` \
2213
+ (Timegrid("1996-01-02 00:00:00", "1996-01-04 00:00:00", "1d")).
2214
+
2215
+ >>> with TestIO(), pub.options.checkseries(False):
2216
+ ... print_vector(obs.adjust_series(
2217
+ ... Timegrid("1996-01-02", "1996-01-04", "1d"), numpy.zeros((2,))))
2218
+ nan, 0.0, 0.0, nan
2219
+
2220
+ Additional checks raise errors in case of non-matching shapes or time
2221
+ information:
2222
+
2223
+ >>> with TestIO():
2224
+ ... obs.adjust_series(Timegrid("1996-01-01", "1996-01-05", "1d"),
2225
+ ... numpy.zeros((5, 2))) # doctest: +ELLIPSIS
2226
+ Traceback (most recent call last):
2227
+ ...
2228
+ RuntimeError: The shape of sequence `obs` of node `dill_assl` is `()` but \
2229
+ according to the data file `...dill_assl_obs_q.asc` it should be `(2,)`.
2230
+
2231
+ >>> with TestIO():
2232
+ ... obs.adjust_series(Timegrid("1996-01-01", "1996-01-05", "1h"),
2233
+ ... numpy.zeros((24*5,))) # doctest: +ELLIPSIS
2234
+ Traceback (most recent call last):
2235
+ ...
2236
+ RuntimeError: According to data file `...dill_assl_obs_q.asc`, the date time \
2237
+ step of sequence `obs` of node `dill_assl` is `1h` but the actual simulation time \
2238
+ step is `1d`.
2239
+
2240
+ .. testsetup::
2241
+
2242
+ >>> from hydpy import Node, Element
2243
+ >>> Node.clear_all()
2244
+ >>> Element.clear_all()
2245
+ """
2246
+ if self.shape != values.shape[1:]:
2247
+ raise RuntimeError(
2248
+ f"The shape of sequence {objecttools.devicephrase(self)} is "
2249
+ f"`{self.shape}` but according to the data file `{self.filepath}` it "
2250
+ f"should be `{values.shape[1:]}`."
2251
+ )
2252
+ if hydpy.pub.timegrids.init.stepsize != timegrid_data.stepsize:
2253
+ raise RuntimeError(
2254
+ f"According to data file `{self.filepath}`, the date time step of "
2255
+ f"sequence {objecttools.devicephrase(self)} is "
2256
+ f"`{timegrid_data.stepsize}` but the actual simulation time step is "
2257
+ f"`{hydpy.pub.timegrids.init.stepsize}`."
2258
+ )
2259
+ if hydpy.pub.timegrids.init not in timegrid_data:
2260
+ if hydpy.pub.options.checkseries:
2261
+ raise RuntimeError(
2262
+ f"For sequence {objecttools.devicephrase(self)} the initialisation "
2263
+ f"time grid ({hydpy.pub.timegrids.init}) does not define a subset "
2264
+ f"of the time grid of the data file `{self.filepath}` "
2265
+ f"({timegrid_data})."
2266
+ )
2267
+ return self.adjust_short_series(timegrid_data, values)
2268
+ idx1 = timegrid_data[hydpy.pub.timegrids.init.firstdate]
2269
+ idx2 = timegrid_data[hydpy.pub.timegrids.init.lastdate]
2270
+ return values[idx1:idx2]
2271
+
2272
+ def adjust_short_series(
2273
+ self, timegrid: timetools.Timegrid, values: NDArrayFloat
2274
+ ) -> NDArrayFloat:
2275
+ """Adjust a short time series to a longer time grid.
2276
+
2277
+ Mostly, time series data to be read from files should span (at least) the whole
2278
+ initialisation period of a *HydPy* project. However, incomplete time series
2279
+ might also be helpful for some variables used only for comparison (e.g.
2280
+ observed runoff used for calibration). Method |IOSequence.adjust_short_series|
2281
+ adjusts such incomplete series to the public initialisation time grid stored in
2282
+ module |pub|. It is automatically called in method |IOSequence.adjust_series|
2283
+ when necessary, provided that the option |Options.checkseries| is disabled.
2284
+
2285
+ Assume the initialisation period of a HydPy project spans five days:
2286
+
2287
+ >>> from hydpy import pub
2288
+ >>> pub.timegrids = "2000.01.10", "2000.01.15", "1d"
2289
+
2290
+ Prepare a node series object for observational data:
2291
+
2292
+ >>> from hydpy.core.sequencetools import Obs
2293
+ >>> obs = Obs(None)
2294
+
2295
+ Prepare a test function that expects the time grid of the data and the data
2296
+ itself, which returns the adjusted array through invoking the method
2297
+ |IOSequence.adjust_short_series|:
2298
+
2299
+ >>> import numpy
2300
+ >>> def test(timegrid):
2301
+ ... values = numpy.ones(len(timegrid))
2302
+ ... return obs.adjust_short_series(timegrid, values)
2303
+
2304
+ The following calls to the test function show the arrays returned for different
2305
+ kinds of misalignments:
2306
+
2307
+ >>> from hydpy import print_vector, Timegrid
2308
+ >>> print_vector(test(Timegrid("2000.01.05", "2000.01.20", "1d")))
2309
+ 1.0, 1.0, 1.0, 1.0, 1.0
2310
+ >>> print_vector(test(Timegrid("2000.01.12", "2000.01.15", "1d")))
2311
+ nan, nan, 1.0, 1.0, 1.0
2312
+ >>> print_vector(test(Timegrid("2000.01.12", "2000.01.17", "1d")))
2313
+ nan, nan, 1.0, 1.0, 1.0
2314
+ >>> print_vector(test(Timegrid("2000.01.10", "2000.01.13", "1d")))
2315
+ 1.0, 1.0, 1.0, nan, nan
2316
+ >>> print_vector(test(Timegrid("2000.01.08", "2000.01.13", "1d")))
2317
+ 1.0, 1.0, 1.0, nan, nan
2318
+ >>> print_vector(test(Timegrid("2000.01.12", "2000.01.13", "1d")))
2319
+ nan, nan, 1.0, nan, nan
2320
+ >>> print_vector(test(Timegrid("2000.01.05", "2000.01.10", "1d")))
2321
+ nan, nan, nan, nan, nan
2322
+ >>> print_vector(test(Timegrid("2000.01.05", "2000.01.08", "1d")))
2323
+ nan, nan, nan, nan, nan
2324
+ >>> print_vector(test(Timegrid("2000.01.15", "2000.01.18", "1d")))
2325
+ nan, nan, nan, nan, nan
2326
+ >>> print_vector(test(Timegrid("2000.01.16", "2000.01.18", "1d")))
2327
+ nan, nan, nan, nan, nan
2328
+
2329
+ After enabling option |Options.usedefaultvalues|, the missing values are
2330
+ initialised with zero instead of nan:
2331
+
2332
+ >>> with pub.options.usedefaultvalues(True):
2333
+ ... print_vector(test(Timegrid("2000.01.12", "2000.01.17", "1d")))
2334
+ 0.0, 0.0, 1.0, 1.0, 1.0
2335
+ """
2336
+ idxs = [
2337
+ timegrid[hydpy.pub.timegrids.init.firstdate],
2338
+ timegrid[hydpy.pub.timegrids.init.lastdate],
2339
+ ]
2340
+ valcopy = values
2341
+ values = numpy.full(self.seriesshape, float(self.initinfo[0]))
2342
+ len_ = len(valcopy)
2343
+ jdxs = []
2344
+ for idx in idxs:
2345
+ if idx < 0:
2346
+ jdxs.append(0)
2347
+ elif idx <= len_:
2348
+ jdxs.append(idx)
2349
+ else:
2350
+ jdxs.append(len_)
2351
+ valcopy = valcopy[jdxs[0] : jdxs[1]]
2352
+ zdx1 = max(-idxs[0], 0)
2353
+ zdx2 = zdx1 + jdxs[1] - jdxs[0]
2354
+ values[zdx1:zdx2] = valcopy
2355
+ return values
2356
+
2357
+ def apply_adjusted_series(
2358
+ self, timegrid_data: timetools.Timegrid, series: NDArrayFloat
2359
+ ) -> None:
2360
+ """Take the values of the given "adjusted series".
2361
+
2362
+ The "adjusted series" is usually returned by method |IOSequence.adjust_series|.
2363
+ The behaviour of method |IOSequence.apply_adjusted_series| depends on option
2364
+ |SequenceManager.reset|. By default, "resetting" is enabled, meaning that
2365
+ |numpy.nan| values due to incomplete time series files overwrite previously
2366
+ available data. We demonstrate this using the NetCDF data provided by function
2367
+ |prepare_full_example_2| but changing the initialisation period (only advised
2368
+ for testing purposes):
2369
+
2370
+ >>> from hydpy.core.testtools import prepare_full_example_2
2371
+ >>> hp, pub, TestIO = prepare_full_example_2()
2372
+ >>> pub.timegrids.init.firstdate = "1989-10-30"
2373
+ >>> pub.timegrids.init.lastdate = "1989-11-03"
2374
+ >>> t = hp.elements.land_dill_assl.model.sequences.inputs.t
2375
+ >>> t.series = -99.9
2376
+ >>> opt = pub.options
2377
+ >>> sm = pub.sequencemanager
2378
+ >>> with (
2379
+ ... TestIO(), sm.filetype("nc"), opt.checkseries(False), sm.netcdfreading()
2380
+ ... ):
2381
+ ... t.load_series()
2382
+ >>> from hydpy import round_
2383
+ >>> round_(t.series)
2384
+ nan, nan, 10.1, 10.0
2385
+
2386
+ With option |SequenceManager.reset| disabled, method
2387
+ |IOSequence.apply_adjusted_series| keeps the already available data:
2388
+
2389
+ >>> t.series = 99.9
2390
+ >>> with TestIO(), sm.reset(False), sm.filetype("nc"), opt.checkseries(False):
2391
+ ... with sm.netcdfreading():
2392
+ ... t.load_series()
2393
+ >>> from hydpy import round_
2394
+ >>> round_(t.series)
2395
+ 99.9, 99.9, 10.1, 10.0
2396
+ """
2397
+ if hydpy.pub.sequencemanager.reset:
2398
+ self.series = series
2399
+ else:
2400
+ init = hydpy.pub.timegrids.init
2401
+ i0, i1 = init[timegrid_data.firstdate], init[timegrid_data.lastdate]
2402
+ self.series[i0:i1] = series[i0:i1]
2403
+
2404
+ def check_completeness(self) -> None:
2405
+ """Raise a |RuntimeError| if the |IOSequence.series| contains at least one
2406
+ |numpy.nan| value and if the option |Options.checkseries| is enabled.
2407
+
2408
+ >>> from hydpy import pub
2409
+ >>> pub.timegrids = "2000-01-01", "2000-01-11", "1d"
2410
+ >>> from hydpy.core.sequencetools import StateSequence, StateSequences
2411
+ >>> class Seq(StateSequence):
2412
+ ... NDIM = 0
2413
+ ... NUMERIC = False
2414
+ >>> class StateSequences(StateSequences):
2415
+ ... CLASSES = (Seq,)
2416
+ >>> seq = Seq(StateSequences(None))
2417
+ >>> seq.__hydpy__connect_variable2subgroup__()
2418
+ >>> seq.prepare_series()
2419
+ >>> seq.check_completeness()
2420
+ Traceback (most recent call last):
2421
+ ...
2422
+ RuntimeError: The series array of sequence `seq` contains 10 nan values.
2423
+
2424
+ >>> seq.series = 1.0
2425
+ >>> seq.check_completeness()
2426
+
2427
+ >>> seq.series[3] = numpy.nan
2428
+ >>> seq.check_completeness()
2429
+ Traceback (most recent call last):
2430
+ ...
2431
+ RuntimeError: The series array of sequence `seq` contains 1 nan value.
2432
+
2433
+ >>> with pub.options.checkseries(False):
2434
+ ... seq.check_completeness()
2435
+ """
2436
+ if hydpy.pub.options.checkseries:
2437
+ isnan = numpy.isnan(self.series)
2438
+ if numpy.any(isnan):
2439
+ nmb = numpy.sum(isnan)
2440
+ valuestring = "value" if nmb == 1 else "values"
2441
+ raise RuntimeError(
2442
+ f"The series array of sequence {objecttools.devicephrase(self)} "
2443
+ f"contains {nmb} nan {valuestring}."
2444
+ )
2445
+
2446
+ def save_series(self) -> None:
2447
+ """Write the time series data of the current |IOSequence| object to a file.
2448
+
2449
+ Method |IOSequence.save_series| only calls method |SequenceManager.save_file|
2450
+ of class |SequenceManager|, passing itself as the only argument. Hence, see
2451
+ the documentation on class the |SequenceManager| for further information. The
2452
+ following example only shows the error messages when |SequenceManager.save_file|
2453
+ is missing due to incomplete project configurations:
2454
+
2455
+ .. testsetup::
2456
+
2457
+ >>> from hydpy import pub
2458
+ >>> del pub.sequencemanager
2459
+
2460
+ >>> from hydpy.core.sequencetools import StateSequence
2461
+ >>> StateSequence(None).save_series()
2462
+ Traceback (most recent call last):
2463
+ ...
2464
+ hydpy.core.exceptiontools.AttributeNotReady: While trying to save the time \
2465
+ series data of `statesequence`, the following error occurred: Attribute \
2466
+ sequencemanager of module `pub` is not defined at the moment.
2467
+ """
2468
+ try:
2469
+ sequencemanager = hydpy.pub.sequencemanager
2470
+ except BaseException:
2471
+ objecttools.augment_excmessage(
2472
+ f"While trying to save the time series data of "
2473
+ f"{objecttools.devicephrase(self)}"
2474
+ )
2475
+ sequencemanager.save_file(self)
2476
+
2477
+ def save_mean(self, *args, **kwargs) -> None:
2478
+ """Average the time series data with method |IOSequence.average_series| of
2479
+ class |IOSequence| and write the result to file using method
2480
+ |SequenceManager.save_file| of class |SequenceManager|.
2481
+
2482
+ The main documentation on class |SequenceManager| provides some examples.
2483
+ """
2484
+ array = InfoArray(self.average_series(*args, **kwargs), aggregation="mean")
2485
+ with hydpy.pub.sequencemanager.aggregation("mean"):
2486
+ hydpy.pub.sequencemanager.save_file(self, array=array)
2487
+
2488
+ @property
2489
+ def seriesmatrix(self) -> MatrixFloat:
2490
+ """The actual |IOSequence| object's time series, arranged in a 2-dimensional
2491
+ matrix.
2492
+
2493
+ For a 1-dimensional sequence object, property |IOSequence.seriesmatrix| returns
2494
+ the original values without modification:
2495
+
2496
+ >>> from hydpy import pub
2497
+ >>> pub.timegrids = "2000-01-01", "2000-01-04", "1d"
2498
+ >>> from hydpy.models.hland import *
2499
+ >>> parameterstep("1d")
2500
+ >>> nmbzones(2)
2501
+ >>> fluxes.pc.prepare_series()
2502
+ >>> fluxes.pc.series = [[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]
2503
+ >>> from hydpy import print_vector
2504
+ >>> for values in fluxes.pc.seriesmatrix:
2505
+ ... print_vector(values)
2506
+ 1.0, 2.0
2507
+ 3.0, 4.0
2508
+ 5.0, 6.0
2509
+
2510
+ For all other sequences, |IOSequence.seriesmatrix| raises the following error
2511
+ by default:
2512
+
2513
+ >>> inputs.p.seriesmatrix
2514
+ Traceback (most recent call last):
2515
+ ...
2516
+ NotImplementedError: Sequence `p` does not implement a method for converting \
2517
+ its series to a 2-dimensional matrix.
2518
+ """
2519
+ if self.NDIM == 1:
2520
+ return self.series
2521
+ raise NotImplementedError(
2522
+ f"Sequence {objecttools.devicephrase(self)} does not implement a method "
2523
+ f"for converting its series to a 2-dimensional matrix."
2524
+ )
2525
+
2526
+ def average_series(self, *args, **kwargs) -> InfoArray:
2527
+ """Average the actual time series of the |IOSequence| object for all time
2528
+ points.
2529
+
2530
+ Method |IOSequence.average_series| works similarly to method
2531
+ |Variable.average_values| of class |Variable|, from which we borrow some
2532
+ examples. However, we must first prepare a |Timegrids| object to define the
2533
+ |IOSequence.series| length:
2534
+
2535
+ >>> from hydpy import pub
2536
+ >>> pub.timegrids = "2000-01-01", "2000-01-04", "1d"
2537
+
2538
+ As shown for method |Variable.average_values|, for 0-dimensional |IOSequence|
2539
+ objects, the result of method |IOSequence.average_series| equals
2540
+ |IOSequence.series| itself:
2541
+
2542
+ >>> from hydpy.core.sequencetools import StateSequence, StateSequences
2543
+ >>> class SoilMoisture(StateSequence):
2544
+ ... NDIM = 0
2545
+ ... NUMERIC = False
2546
+ >>> class StateSequences(StateSequences):
2547
+ ... CLASSES = (SoilMoisture,)
2548
+ >>> sm = SoilMoisture(StateSequences(None))
2549
+ >>> sm.__hydpy__connect_variable2subgroup__()
2550
+ >>> sm.prepare_series()
2551
+ >>> import numpy
2552
+ >>> sm.series = numpy.array([190.0, 200.0, 210.0])
2553
+ >>> sm.average_series()
2554
+ InfoArray([190., 200., 210.])
2555
+
2556
+ We require a weighting parameter for |IOSequence| objects with an increased
2557
+ dimensionality:
2558
+
2559
+ >>> SoilMoisture.NDIM = 1
2560
+ >>> sm.shape = 3
2561
+ >>> sm.prepare_series()
2562
+ >>> sm.series = ([190.0, 390.0, 490.0],
2563
+ ... [200.0, 400.0, 500.0],
2564
+ ... [210.0, 410.0, 510.0])
2565
+ >>> from hydpy.core.parametertools import Parameter
2566
+ >>> class Area(Parameter):
2567
+ ... NDIM = 1
2568
+ ... shape = (3,)
2569
+ ... value = numpy.array([1.0, 1.0, 2.0])
2570
+ >>> area = Area(None)
2571
+ >>> SoilMoisture.refweights = property(lambda self: area)
2572
+ >>> sm.average_series()
2573
+ InfoArray([390., 400., 410.])
2574
+
2575
+ The documentation on method |Variable.average_values| provides many examples of
2576
+ using different masks in different ways. Here, we only show the results of
2577
+ method |IOSequence.average_series| for a mask selecting the first two entries,
2578
+ for a mask selecting no entry at all, and for an ill-defined mask:
2579
+
2580
+ >>> from hydpy.core.masktools import DefaultMask
2581
+ >>> class Soil(DefaultMask):
2582
+ ... @classmethod
2583
+ ... def new(cls, variable, **kwargs):
2584
+ ... return cls.array2mask(maskvalues)
2585
+ >>> SoilMoisture.mask = Soil()
2586
+
2587
+ >>> maskvalues = [True, True, False]
2588
+ >>> sm.average_series()
2589
+ InfoArray([290., 300., 310.])
2590
+
2591
+ >>> maskvalues = [False, False, False]
2592
+ >>> sm.average_series()
2593
+ InfoArray([nan, nan, nan])
2594
+
2595
+ >>> maskvalues = [True, True]
2596
+ >>> sm.average_series() # doctest: +ELLIPSIS
2597
+ Traceback (most recent call last):
2598
+ ...
2599
+ IndexError: While trying to calculate the mean value of the internal time \
2600
+ series of sequence `soilmoisture`, the following error occurred: While trying to \
2601
+ access the value(s) of variable `area` with key `[ True True]`, the following error \
2602
+ occurred: boolean index did not match indexed array ...
2603
+ """
2604
+ try:
2605
+ if not self.NDIM:
2606
+ array = self.series
2607
+ else:
2608
+ mask = self.get_submask(*args, **kwargs)
2609
+ if numpy.any(mask):
2610
+ weights = self.refweights[mask]
2611
+ weights /= numpy.sum(weights)
2612
+ series = self.seriesmatrix[:, mask]
2613
+ array = numpy.sum(weights * series, axis=1)
2614
+ else:
2615
+ array = numpy.full(
2616
+ len(self.series), numpy.nan, dtype=config.NP_FLOAT
2617
+ )
2618
+ return InfoArray(array, aggregation="mean")
2619
+ except BaseException:
2620
+ objecttools.augment_excmessage(
2621
+ f"While trying to calculate the mean value of the internal time "
2622
+ f"series of sequence {objecttools.devicephrase(self)}"
2623
+ )
2624
+
2625
+ def aggregate_series(self, *args, **kwargs) -> InfoArray:
2626
+ """Aggregate the time series data based on the actual |IOSequence.aggregation|
2627
+ attribute of the current |IOSequence| object.
2628
+
2629
+ We prepare some nodes and elements with the help of method
2630
+ |prepare_io_example_1| and select a 1-dimensional flux sequence of type
2631
+ |lland_fluxes.NKor|:
2632
+
2633
+ >>> from hydpy.core.testtools import prepare_io_example_1
2634
+ >>> nodes, elements = prepare_io_example_1()
2635
+ >>> seq = elements.element3.model.sequences.fluxes.nkor
2636
+
2637
+ If |IOSequence.aggregation| is `none`, the original time series values are
2638
+ returned:
2639
+
2640
+ >>> seq.aggregation
2641
+ 'none'
2642
+ >>> seq.aggregate_series()
2643
+ InfoArray([[24., 25., 26.],
2644
+ [27., 28., 29.],
2645
+ [30., 31., 32.],
2646
+ [33., 34., 35.]])
2647
+
2648
+ If |IOSequence.aggregation| is `mean`, method |IOSequence.aggregate_series| is
2649
+ called:
2650
+
2651
+ >>> seq.aggregation = "mean"
2652
+ >>> seq.aggregate_series()
2653
+ InfoArray([25., 28., 31., 34.])
2654
+
2655
+ In case the state of the sequence is invalid:
2656
+
2657
+ >>> seq.aggregation = "nonexistent"
2658
+ >>> seq.aggregate_series()
2659
+ Traceback (most recent call last):
2660
+ ...
2661
+ RuntimeError: Unknown aggregation mode `nonexistent` for sequence `nkor` of \
2662
+ element `element3`.
2663
+
2664
+ The following technical test confirms the propr passing of all potential
2665
+ positional and keyword arguments:
2666
+
2667
+ >>> seq.aggregation = "mean"
2668
+ >>> from unittest import mock
2669
+ >>> seq.average_series = mock.MagicMock()
2670
+ >>> _ = seq.aggregate_series(1, x=2)
2671
+ >>> seq.average_series.assert_called_with(1, x=2)
2672
+ """
2673
+ mode = self.aggregation
2674
+ if mode == "none": # pylint: disable=comparison-with-callable
2675
+ return self.series
2676
+ if mode == "mean": # pylint: disable=comparison-with-callable
2677
+ return self.average_series(*args, **kwargs)
2678
+ raise RuntimeError(
2679
+ f"Unknown aggregation mode `{mode}` for sequence "
2680
+ f"{objecttools.devicephrase(self)}."
2681
+ )
2682
+
2683
+ @property
2684
+ @abc.abstractmethod
2685
+ def descr_sequence(self) -> str:
2686
+ """Description of the |IOSequence| object and its context."""
2687
+
2688
+ @property
2689
+ @abc.abstractmethod
2690
+ def descr_device(self) -> str:
2691
+ """Description of the |Device| object the |IOSequence| object belongs to."""
2692
+
2693
+
2694
+ class ModelSequence(Sequence_):
2695
+ """Base class for sequences to be handled by |Model| objects."""
2696
+
2697
+ subvars: ModelSequences[ModelSequence, variabletools.FastAccess]
2698
+ """The subgroup to which the model sequence belongs."""
2699
+ subseqs: ModelSequences[ModelSequence, variabletools.FastAccess]
2700
+ """Alias for |ModelSequence.subvars|."""
2701
+
2702
+ def __init__(
2703
+ self, subvars: ModelSequences[ModelSequence, variabletools.FastAccess]
2704
+ ) -> None:
2705
+ super().__init__(subvars)
2706
+ self.subseqs = subvars
2707
+
2708
+ @property
2709
+ def descr_sequence(self) -> str:
2710
+ """Description of the |ModelSequence| object itself and the |Model| type and
2711
+ |SubSequences| group it belongs to.
2712
+
2713
+ >>> from hydpy import prepare_model
2714
+ >>> from hydpy.models import test_stiff0d
2715
+ >>> model = prepare_model(test_stiff0d)
2716
+ >>> model.sequences.fluxes.q.descr_sequence
2717
+ 'test_stiff0d_flux_q'
2718
+ """
2719
+ return (
2720
+ f"{self.subseqs.seqs.model}_"
2721
+ f"{type(self.subseqs).__name__[:-9].lower()}_"
2722
+ f"{self.name}"
2723
+ )
2724
+
2725
+ @property
2726
+ def descr_model(self) -> str:
2727
+ """Description of the |Model| the |ModelSequence| object belongs to.
2728
+
2729
+ >>> from hydpy import prepare_model
2730
+ >>> from hydpy.models import test, test_stiff0d
2731
+ >>> model = prepare_model(test)
2732
+ >>> model.sequences.fluxes.q.descr_model
2733
+ 'test'
2734
+ >>> model = prepare_model(test_stiff0d)
2735
+ >>> model.sequences.fluxes.q.descr_model
2736
+ 'test_stiff0d'
2737
+ """
2738
+ return self.subseqs.seqs.model.__module__.split(".")[2]
2739
+
2740
+ @property
2741
+ def descr_device(self) -> str:
2742
+ """Description of the |Element| object the |ModelSequence| object belongs to.
2743
+
2744
+ >>> from hydpy import prepare_model, pub, Element
2745
+ >>> element = Element("my_element", outlets="outlet")
2746
+ >>> from hydpy.models.lland_knauf import *
2747
+ >>> parameterstep()
2748
+ >>> model.sequences.inputs.windspeed.descr_device
2749
+ '?'
2750
+ >>> element.model = model
2751
+ >>> model.sequences.inputs.windspeed.descr_device
2752
+ 'my_element'
2753
+
2754
+ >>> from hydpy import pub
2755
+ >>> pub.timegrids = "2000-01-01", "2001-01-02", "1d"
2756
+ >>> nhru(1)
2757
+ >>> ft(1.0)
2758
+ >>> fhru(1.0)
2759
+ >>> lnk(ACKER)
2760
+ >>> measuringheightwindspeed(10.0)
2761
+ >>> lai(10.0)
2762
+ >>> wmax(300.0)
2763
+ >>> with model.add_aetmodel_v1("evap_aet_morsim"):
2764
+ ... pass
2765
+ >>> model.aetmodel.sequences.inputs.windspeed.descr_device
2766
+ 'my_element'
2767
+
2768
+ .. testsetup::
2769
+
2770
+ >>> from hydpy import Node
2771
+ >>> Node.clear_all()
2772
+ >>> Node.clear_all()
2773
+ """
2774
+ try:
2775
+ return self.subseqs.seqs.model.element.name
2776
+ except exceptiontools.AttributeNotReady:
2777
+ return "?"
2778
+
2779
+ @property
2780
+ def numericshape(self) -> tuple[int, ...]:
2781
+ """The shape of the array of temporary values required for the relevant
2782
+ numerical solver.
2783
+
2784
+ The class |ELSModel|, being the base of the "dam" model, uses the "Explicit
2785
+ Lobatto Sequence" for solving differential equations and therefore requires up
2786
+ to eleven array fields for storing temporary values. Hence, the
2787
+ |ModelSequence.numericshape| of the 0-dimensional sequence |dam_fluxes.Inflow|
2788
+ is eleven:
2789
+
2790
+ >>> from hydpy import prepare_model
2791
+ >>> model = prepare_model("dam")
2792
+ >>> model.sequences.fluxes.inflow.numericshape
2793
+ (11,)
2794
+
2795
+ Changing the |IOSequence.shape| through a little trick (just for demonstration
2796
+ purposes) shows that there are eleven entries for each "normal"
2797
+ |dam_fluxes.Inflow| value:
2798
+
2799
+ >>> from hydpy.models.dam.dam_fluxes import Inflow
2800
+ >>> shape = Inflow.shape
2801
+ >>> Inflow.shape = (2,)
2802
+ >>> model.sequences.fluxes.inflow.numericshape
2803
+ (11, 2)
2804
+ >>> Inflow.shape = shape
2805
+
2806
+ Erroneous configurations result in the following error:
2807
+
2808
+ >>> del model.numconsts
2809
+ >>> model.sequences.fluxes.inflow.numericshape
2810
+ Traceback (most recent call last):
2811
+ ...
2812
+ AttributeError: The `numericshape` of a sequence like `inflow` depends on the \
2813
+ configuration of the actual integration algorithm. While trying to query the \
2814
+ required configuration data `nmb_stages` of the model associated with element `?`, \
2815
+ the following error occurred: 'Model' object has no attribute 'numconsts'
2816
+ """
2817
+ from hydpy.core import modeltools # pylint: disable=import-outside-toplevel
2818
+
2819
+ try:
2820
+ model = self.subseqs.seqs.model
2821
+ assert isinstance(model, modeltools.ELSModel) # ToDo
2822
+ numericshape = [model.numconsts.nmb_stages]
2823
+ except AttributeError:
2824
+ objecttools.augment_excmessage(
2825
+ f"The `numericshape` of a sequence like `{self.name}` depends on the "
2826
+ f"configuration of the actual integration algorithm. While trying to "
2827
+ f"query the required configuration data `nmb_stages` of the model "
2828
+ f"associated with element `{objecttools.devicename(self)}`"
2829
+ )
2830
+ numericshape.extend(self.shape)
2831
+ return tuple(numericshape)
2832
+
2833
+
2834
+ class ModelIOSequence(ModelSequence, IOSequence):
2835
+ """Base class for sequences with time series functionalities to be handled by
2836
+ |Model| objects."""
2837
+
2838
+ subvars: ModelIOSequences[ModelIOSequence, FastAccessIOSequence]
2839
+ """The subgroup to which the model IO sequence belongs."""
2840
+ subseqs: ModelIOSequences[ModelIOSequence, FastAccessIOSequence]
2841
+ """Alias for |ModelIOSequence.subvars|."""
2842
+
2843
+
2844
+ class InputSequence(ModelIOSequence):
2845
+ """Base class for input sequences of |Model| objects.
2846
+
2847
+ |InputSequence| objects provide their master model with input data, which is
2848
+ possible in two ways: either by providing their individually managed data (usually
2849
+ read from a file) or data shared with an input node (usually calculated by another
2850
+ model). This flexibility allows, for example, to let application model |hland_96|
2851
+ read already preprocessed precipitation time series or to couple it with
2852
+ application models like |conv_nn|, which interpolates precipitation during the
2853
+ simulation run.
2854
+
2855
+ The second mechanism (coupling |InputSequence| objects with input nodes) is
2856
+ relatively new, and we might adjust the relevant interfaces in the future. As soon
2857
+ as we finally settle things, we will improve the following example and place it
2858
+ more prominently. In short, it shows that working with both types of input data
2859
+ sources at the same time works well and that the different |Node.deploymode|
2860
+ options are supported:
2861
+
2862
+ >>> from hydpy.core.testtools import prepare_full_example_1
2863
+ >>> prepare_full_example_1()
2864
+
2865
+ >>> from hydpy import Element, FusedVariable, HydPy, Node, print_vector, pub, TestIO
2866
+ >>> from hydpy.aliases import hland_inputs_T, hland_inputs_P
2867
+ >>> hp = HydPy("HydPy-H-Lahn")
2868
+ >>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
2869
+ >>> node_t = Node("node_t", variable=hland_inputs_T)
2870
+ >>> node_p = Node("node_p", variable=FusedVariable("Precip", hland_inputs_P))
2871
+ >>> node_q = Node("node_q")
2872
+ >>> land_dill_assl = Element("land_dill_assl", inputs=[node_t, node_p],
2873
+ ... outlets=node_q)
2874
+
2875
+ >>> import os
2876
+ >>> with TestIO():
2877
+ ... os.chdir("HydPy-H-Lahn/control/default")
2878
+ ... with open("land_dill_assl.py") as controlfile:
2879
+ ... exec(controlfile.read(), {}, locals())
2880
+ ... parameters.update()
2881
+ ... land_dill_assl.model = model
2882
+
2883
+ >>> aetmodel = model.aetmodel
2884
+ >>> petmodel = model.aetmodel.petmodel
2885
+
2886
+ >>> model.sequences.inputs.t.inputflag
2887
+ True
2888
+ >>> model.sequences.inputs.p.inputflag
2889
+ True
2890
+ >>> petmodel.sequences.inputs.normalevapotranspiration.inputflag
2891
+ False
2892
+
2893
+ >>> hp.update_devices(nodes=[node_t, node_p, node_q], elements=land_dill_assl)
2894
+ >>> hp.prepare_inputseries()
2895
+ >>> hp.prepare_factorseries()
2896
+ >>> hp.prepare_fluxseries()
2897
+ >>> with TestIO():
2898
+ ... hp.load_inputseries()
2899
+
2900
+ >>> hp.nodes.prepare_allseries()
2901
+ >>> node_t.deploymode = "oldsim"
2902
+ >>> node_t.sequences.sim.series = 1.0, 2.0, 3.0, 4.0, 5.0
2903
+ >>> node_p.deploymode = "obs"
2904
+ >>> node_p.sequences.obs.series = 0.0, 4.0, 0.0, 8.0, 0.0
2905
+
2906
+ >>> hp.simulate()
2907
+
2908
+ >>> print_vector(model.sequences.inputs.t.series)
2909
+ 1.0, 2.0, 3.0, 4.0, 5.0
2910
+ >>> print_vector(model.sequences.factors.tc.series[:, 0])
2911
+ 2.323207, 3.323207, 4.323207, 5.323207, 6.323207
2912
+ >>> print_vector(model.sequences.inputs.p.series)
2913
+ 0.0, 4.0, 0.0, 8.0, 0.0
2914
+ >>> print_vector(model.sequences.fluxes.pc.series[:, 0])
2915
+ 0.0, 3.2514, 0.0, 6.5028, 0.0
2916
+ >>> print_vector(petmodel.sequences.inputs.normalevapotranspiration.series)
2917
+ 0.3, 0.3, 0.3, 0.3, 0.3
2918
+ >>> print_vector(
2919
+ ... aetmodel.sequences.fluxes.potentialsoilevapotranspiration.series[:, 0])
2920
+ 0.309, 0.317657, 0.369, 0.352975, 0.432
2921
+
2922
+ .. testsetup::
2923
+
2924
+ >>> Element.clear_all()
2925
+ >>> Node.clear_all()
2926
+ >>> FusedVariable.clear_registry()
2927
+ """
2928
+
2929
+ subvars: InputSequences
2930
+ """The subgroup to which the input sequence belongs."""
2931
+ subseqs: InputSequences
2932
+ """Alias for |InputSequence.subvars|."""
2933
+ fastaccess: FastAccessInputSequence
2934
+ """Object for accessing the input sequence's data with little overhead."""
2935
+
2936
+ STANDARD_NAME: ClassVar[StandardInputNames]
2937
+
2938
+ _CLS_FASTACCESS_PYTHON = FastAccessInputSequence
2939
+
2940
+ def __hydpy__connect_variable2subgroup__(self) -> None:
2941
+ super().__hydpy__connect_variable2subgroup__()
2942
+ if self.NDIM == 0:
2943
+ self._set_fastaccessattribute("inputflag", False)
2944
+
2945
+ @property
2946
+ def descr_sequence(self) -> str:
2947
+ """Either a model-specific or a standard HydPy string describing the input
2948
+ sequence instance.
2949
+
2950
+ By default, the returned string equals those of other |ModelSequence|
2951
+ subclasses:
2952
+
2953
+ >>> from hydpy import pub
2954
+ >>> from hydpy.core.filetools import SequenceManager
2955
+ >>> pub.sequencemanager = SequenceManager()
2956
+
2957
+ >>> from hydpy.models.hland_96 import *
2958
+ >>> parameterstep()
2959
+ >>> inputs.t.descr_sequence
2960
+ 'hland_96_input_t'
2961
+
2962
+ When activating the standard "HydPy" convention instead of the "model-specific"
2963
+ convention, |InputSequence.descr_sequence| returns the standard name selected
2964
+ by the respective |InputSequence| subclass:
2965
+
2966
+ >>> with pub.sequencemanager.convention("HydPy"):
2967
+ ... inputs.t.descr_sequence
2968
+ <StandardInputNames.AIR_TEMPERATURE: 'air_temperature'>
2969
+ """
2970
+ if hydpy.pub.sequencemanager.convention == "model-specific":
2971
+ return super().descr_sequence
2972
+ return self.STANDARD_NAME
2973
+
2974
+ def set_pointer(self, double: pointerutils.Double) -> None:
2975
+ """Prepare a pointer referencing the given |Double| object.
2976
+
2977
+ Method |InputSequence.set_pointer| should be relevant for framework developers
2978
+ and eventually for some model developers only.
2979
+ """
2980
+ pdouble = pointerutils.PDouble(double)
2981
+ self.fastaccess.set_pointerinput(self.name, pdouble)
2982
+ self._set_fastaccessattribute("inputflag", True)
2983
+ self._set_fastaccessattribute("diskflag_reading", False)
2984
+ self._set_fastaccessattribute("diskflag_writing", False)
2985
+
2986
+ @property
2987
+ def inputflag(self) -> bool:
2988
+ """A flag telling if the actual |InputSequence| object queries its data from an
2989
+ input node (|True|) or uses individually managed data, usually read from a data
2990
+ file (|False|).
2991
+
2992
+ See the main documentation on class |InputSequence| for further information.
2993
+ """
2994
+ return self._get_fastaccessattribute("inputflag")
2995
+
2996
+
2997
+ class OutputSequence(ModelIOSequence):
2998
+ """Base class for |FactorSequence|, |FluxSequence| and |StateSequence|.
2999
+
3000
+ |OutputSequence| subclasses implement an optional output mechanism. Generally, as
3001
+ all instances of |ModelSequence| subclasses, output sequences handle values
3002
+ calculated within a simulation time step. With an activated
3003
+ |OutputSequence.outputflag|, they also pass their internal values to an output node
3004
+ (see the documentation on class |Element|), which makes them accessible to other
3005
+ models.
3006
+
3007
+ This output mechanism (coupling |OutputSequence| objects with output nodes) is
3008
+ relatively new, and we might adjust the relevant interfaces in the future.
3009
+ Additionally, it works for 0-dimensional output sequences only so far. As soon as
3010
+ we finally settle things, we will improve the following example and place it more
3011
+ prominently. In short, it shows that everything works well for the different
3012
+ |Node.deploymode| options:
3013
+
3014
+ >>> from hydpy.core.testtools import prepare_full_example_1
3015
+ >>> prepare_full_example_1()
3016
+
3017
+ >>> from hydpy import Element, HydPy, Node, print_vector, pub, Selection, TestIO
3018
+ >>> from hydpy.aliases import (
3019
+ ... hland_fluxes_Perc, hland_fluxes_Q0, hland_fluxes_Q1, hland_states_UZ)
3020
+ >>> hp = HydPy("HydPy-H-Lahn")
3021
+ >>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
3022
+ >>> node_q0 = Node("node_q0", variable=hland_fluxes_Q0)
3023
+ >>> node_q1 = Node("node_q1", variable=hland_fluxes_Q1)
3024
+ >>> node_perc = Node("node_perc", variable=hland_fluxes_Perc)
3025
+ >>> node_uz = Node("node_uz", variable=hland_states_UZ)
3026
+ >>> node_q = Node("node_q")
3027
+ >>> land_dill_assl = Element("land_dill_assl",
3028
+ ... outlets=node_q,
3029
+ ... outputs=[node_q0, node_q1, node_perc, node_uz])
3030
+
3031
+ >>> import os
3032
+ >>> with TestIO():
3033
+ ... os.chdir("HydPy-H-Lahn/control/default")
3034
+ ... with open("land_dill_assl.py") as controlfile:
3035
+ ... exec(controlfile.read(), {}, locals())
3036
+ ... parameters.update()
3037
+ ... land_dill_assl.model = model
3038
+
3039
+ >>> model.sequences.fluxes.q0.outputflag
3040
+ True
3041
+ >>> model.sequences.fluxes.q1.outputflag
3042
+ True
3043
+ >>> model.sequences.fluxes.perc.outputflag
3044
+ True
3045
+ >>> model.sequences.fluxes.qt.outputflag
3046
+ False
3047
+ >>> model.sequences.states.uz.outputflag
3048
+ True
3049
+ >>> model.sequences.states.lz.outputflag
3050
+ False
3051
+
3052
+ >>> hp.update_devices(nodes=[node_q0, node_q1, node_perc, node_uz],
3053
+ ... elements=land_dill_assl)
3054
+ >>> with TestIO():
3055
+ ... hp.load_conditions()
3056
+
3057
+ >>> hp.prepare_inputseries()
3058
+ >>> with TestIO():
3059
+ ... hp.load_inputseries()
3060
+ >>> hp.prepare_fluxseries()
3061
+ >>> hp.prepare_stateseries()
3062
+ >>> hp.nodes.prepare_allseries()
3063
+
3064
+ >>> node_q0.deploymode = "oldsim"
3065
+ >>> node_q0.sequences.sim.series = 1.0
3066
+ >>> node_q0.sequences.obs.series = 2.0
3067
+ >>> node_q1.deploymode = "obs"
3068
+ >>> node_q1.sequences.obs.series = 3.0
3069
+ >>> node_perc.deploymode = "newsim"
3070
+ >>> node_perc.sequences.obs.series = 4.0
3071
+ >>> node_uz.sequences.obs.series = 5.0
3072
+
3073
+ >>> hp.simulate()
3074
+
3075
+ >>> print_vector(node_q0.sequences.sim.series)
3076
+ 1.0, 1.0, 1.0, 1.0, 1.0
3077
+ >>> print_vector(node_q0.sequences.obs.series)
3078
+ 2.0, 2.0, 2.0, 2.0, 2.0
3079
+
3080
+ >>> print_vector(model.sequences.fluxes.q1.series)
3081
+ 0.530782, 0.539976, 0.548629, 0.556786, 0.564477
3082
+ >>> print_vector(node_q1.sequences.sim.series)
3083
+ 0.530782, 0.539976, 0.548629, 0.556786, 0.564477
3084
+ >>> print_vector(node_q1.sequences.obs.series)
3085
+ 3.0, 3.0, 3.0, 3.0, 3.0
3086
+
3087
+ >>> print_vector(model.sequences.fluxes.perc.series)
3088
+ 0.694084, 0.693611, 0.693239, 0.693098, 0.693012
3089
+ >>> print_vector(node_perc.sequences.sim.series)
3090
+ 0.694084, 0.693611, 0.693239, 0.693098, 0.693012
3091
+ >>> print_vector(node_perc.sequences.obs.series)
3092
+ 4.0, 4.0, 4.0, 4.0, 4.0
3093
+
3094
+ >>> print_vector(model.sequences.states.uz.series)
3095
+ 5.628278, 4.368269, 3.337343, 2.452946, 1.662766
3096
+ >>> print_vector(node_uz.sequences.sim.series)
3097
+ 5.628278, 4.368269, 3.337343, 2.452946, 1.662766
3098
+ >>> print_vector(node_uz.sequences.obs.series)
3099
+ 5.0, 5.0, 5.0, 5.0, 5.0
3100
+
3101
+ .. testsetup::
3102
+
3103
+ >>> Element.clear_all()
3104
+ >>> Node.clear_all()
3105
+ """
3106
+
3107
+ subvars: OutputSequences[OutputSequence]
3108
+ """The subgroup to which the output sequence belongs."""
3109
+ subseqs: OutputSequences[OutputSequence]
3110
+ """Alias for |OutputSequence.subvars|."""
3111
+ fastaccess: FastAccessOutputSequence
3112
+ """Object for accessing the output sequence's data with little overhead."""
3113
+
3114
+ _CLS_FASTACCESS_PYTHON = FastAccessOutputSequence
3115
+
3116
+ def __hydpy__connect_variable2subgroup__(self) -> None:
3117
+ super().__hydpy__connect_variable2subgroup__()
3118
+ if self.NDIM == 0:
3119
+ self._set_fastaccessattribute("outputflag", False)
3120
+
3121
+ def set_pointer(self, double: pointerutils.Double) -> None:
3122
+ """Prepare a pointer referencing the given |Double| object.
3123
+
3124
+ Method |OutputSequence.set_pointer| should be relevant for framework developers
3125
+ and eventually for some model developers only.
3126
+ """
3127
+ pdouble = pointerutils.PDouble(double)
3128
+ self.fastaccess.set_pointeroutput(self.name, pdouble)
3129
+ self._set_fastaccessattribute("outputflag", True)
3130
+
3131
+ @property
3132
+ def outputflag(self) -> bool:
3133
+ """A flag telling if the actual |OutputSequence| object passes its data to an
3134
+ output node (|True|) or not (|False|).
3135
+
3136
+ See the main documentation on class |OutputSequence| for further information.
3137
+ """
3138
+ return self._get_fastaccessattribute("outputflag")
3139
+
3140
+
3141
+ class DependentSequence(OutputSequence):
3142
+ """Base class for |FactorSequence| and |FluxSequence|."""
3143
+
3144
+ def _finalise_connections(self) -> None:
3145
+ super()._finalise_connections()
3146
+ if self.NUMERIC:
3147
+ values = None if self.NDIM else numpy.zeros(self.numericshape)
3148
+ self._set_fastaccessattribute("points", values)
3149
+ self._set_fastaccessattribute("integrals", copy.copy(values))
3150
+ self._set_fastaccessattribute("results", copy.copy(values))
3151
+ value = None if self.NDIM else 0.0
3152
+ self._set_fastaccessattribute("sum", value)
3153
+
3154
+ def _get_shape(self) -> tuple[int, ...]:
3155
+ """A tuple containing the actual lengths of all dimensions.
3156
+
3157
+ |FactorSequence| and |FluxSequence| objects come with some additional
3158
+ `fastaccess` attributes, which should only be of interest to framework
3159
+ developers. One such attribute is the `results` array, handling the
3160
+ (intermediate or final) calculation results for factor and flux sequences, as
3161
+ shown in the following example for the 0-dimensional flux sequence
3162
+ |wland_fluxes.RH| of the |wland_wag| model:
3163
+
3164
+ >>> from hydpy import prepare_model, print_vector, pub
3165
+ >>> model = prepare_model("wland_wag")
3166
+ >>> print_vector(model.sequences.fluxes.rh.fastaccess._rh_results)
3167
+ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
3168
+
3169
+ For 1-dimensional numerical factor and flux sequences, the `results` attribute
3170
+ is |None| initially, as property |ModelSequence.numericshape| is unknown.
3171
+ Setting the |DependentSequence.shape| attribute of the respective
3172
+ |FactorSequence| or |FluxSequence| object (we select |wland_fluxes.EI| as an
3173
+ example) prepares all "fastaccess attributes" automatically:
3174
+
3175
+ >>> ei = model.sequences.fluxes.ei
3176
+ >>> ei.fastaccess._ei_results
3177
+
3178
+ >>> ei.shape = (2,)
3179
+ >>> ei.shape
3180
+ (2,)
3181
+ >>> ei.fastaccess._ei_results.shape
3182
+ (11, 2)
3183
+ """
3184
+ return super()._get_shape()
3185
+
3186
+ def _set_shape(self, shape: int | tuple[int, ...]) -> None:
3187
+ super()._set_shape(shape)
3188
+ if self.NDIM and self.NUMERIC:
3189
+ self._set_fastaccessattribute("points", numpy.zeros(self.numericshape))
3190
+ self._set_fastaccessattribute("integrals", numpy.zeros(self.numericshape))
3191
+ self._set_fastaccessattribute("results", numpy.zeros(self.numericshape))
3192
+ self._set_fastaccessattribute("sum", numpy.zeros(self.shape))
3193
+
3194
+ shape = propertytools.Property(fget=_get_shape, fset=_set_shape)
3195
+
3196
+
3197
+ class FactorSequence(DependentSequence):
3198
+ """Base class for factor sequences of |Model| objects."""
3199
+
3200
+ subvars: FactorSequences
3201
+ """The subgroup to which the factor sequence belongs."""
3202
+ subseqs: FactorSequences
3203
+ """Alias for |FactorSequence.subvars|."""
3204
+
3205
+ NUMERIC = False # Changing this requires implementing the related functionalites
3206
+ # in modules `modeltools` and `modeltutils`.
3207
+
3208
+
3209
+ class FluxSequence(DependentSequence):
3210
+ """Base class for flux sequences of |Model| objects."""
3211
+
3212
+ subvars: FluxSequences
3213
+ """The subgroup to which the flux sequence belongs."""
3214
+ subseqs: FluxSequences
3215
+ """Alias for |FluxSequence.subvars|."""
3216
+
3217
+
3218
+ class ConditionSequence(ModelSequence):
3219
+ """Base class for |StateSequence| and |LogSequence|.
3220
+
3221
+ Class |ConditionSequence| should not be subclassed by model developers directly.
3222
+ Inherit from |StateSequence| or |LogSequence| instead.
3223
+ """
3224
+
3225
+ _oldargs: tuple[Any, ...] | None = None
3226
+
3227
+ def __call__(self, *args) -> None:
3228
+ """The prefered way to pass values to |Sequence_| instances within initial
3229
+ condition files."""
3230
+ super().__call__(*args)
3231
+ self.trim()
3232
+ self._oldargs = copy.deepcopy(args)
3233
+
3234
+ def trim(self, lower=None, upper=None) -> bool:
3235
+ """Apply |trim| of module |variabletools|."""
3236
+ return variabletools.trim(self, lower, upper)
3237
+
3238
+ def reset(self):
3239
+ """Reset the value of the actual |StateSequence| or |LogSequence| object to the
3240
+ last value defined by "calling" the object.
3241
+
3242
+ We use the |lland_knauf| application model, which handles sequences derived
3243
+ from |StateSequence| (taking |lland_states.Inzp| as an example) and from
3244
+ |LogSequence| (taking |lland_logs.LoggedSunshineDuration| as an example):
3245
+
3246
+ >>> from hydpy import prepare_model, pub
3247
+ >>> model = prepare_model("lland_knauf")
3248
+
3249
+ After defining their shapes, both sequences contain |numpy.nan| values:
3250
+
3251
+ >>> inzp = model.sequences.states.inzp
3252
+ >>> inzp.shape = (2,)
3253
+ >>> inzp
3254
+ inzp(nan, nan)
3255
+ >>> lsd = model.sequences.logs.loggedsunshineduration
3256
+ >>> lsd.shape = 2
3257
+ >>> lsd
3258
+ loggedsunshineduration(nan, nan)
3259
+
3260
+ Before "calling" the sequences method |ConditionSequence.reset| does nothing:
3261
+
3262
+ >>> inzp.values = 0.0
3263
+ >>> inzp.reset()
3264
+ >>> inzp
3265
+ inzp(0.0, 0.0)
3266
+ >>> lsd.values = 0.0
3267
+ >>> lsd.reset()
3268
+ >>> lsd
3269
+ loggedsunshineduration(0.0, 0.0)
3270
+
3271
+ After "calling" the sequences method |ConditionSequence.reset| reuses the
3272
+ respective arguments:
3273
+
3274
+ >>> with pub.options.warntrim(False):
3275
+ ... inzp(0.0, 1.0)
3276
+ >>> inzp.values = 0.0
3277
+ >>> inzp
3278
+ inzp(0.0, 0.0)
3279
+ >>> with pub.options.warntrim(False):
3280
+ ... inzp.reset()
3281
+ >>> inzp
3282
+ inzp(0.0, 1.0)
3283
+ >>> lsd(1.0, 2.0)
3284
+ >>> lsd.values = 3.0
3285
+ >>> lsd
3286
+ loggedsunshineduration(3.0, 3.0)
3287
+ >>> lsd.reset()
3288
+ >>> lsd
3289
+ loggedsunshineduration(1.0, 2.0)
3290
+ """
3291
+ if self._oldargs:
3292
+ self(*self._oldargs)
3293
+
3294
+
3295
+ class StateSequence(OutputSequence, ConditionSequence):
3296
+ """Base class for state sequences of |Model| objects.
3297
+
3298
+ Each |StateSequence| object can handle states at two different "time points": at
3299
+ the beginning of a simulation step via property |StateSequence.old| and the end of
3300
+ a simulation step via property |StateSequence.new|. These properties are reflected
3301
+ by two different `fastaccess` attributes. `fastaccess_new` is an alias for the
3302
+ standard `fastaccess` attribute storing the customary information. `fastaccess_old`
3303
+ is an additional feature for keeping the supplemental information.
3304
+
3305
+ We demonstrate the above explanations using state sequence |hland_states.SM| of the
3306
+ base model |hland_96| with a shape of two:
3307
+
3308
+ >>> from hydpy import prepare_model, print_vector
3309
+ >>> model = prepare_model("hland")
3310
+ >>> model.parameters.control.fc.shape = (2,)
3311
+ >>> model.parameters.control.fc = 100.0
3312
+ >>> sm = model.sequences.states.sm
3313
+ >>> sm.shape = (2,)
3314
+
3315
+ Initially, no values are available at all:
3316
+
3317
+ >>> sm
3318
+ sm(nan, nan)
3319
+ >>> print_vector(sm.values)
3320
+ nan, nan
3321
+ >>> print_vector(sm.new)
3322
+ nan, nan
3323
+ >>> print_vector(sm.old)
3324
+ nan, nan
3325
+
3326
+ The typical way to define state values, especially within condition files, is to
3327
+ "call" state sequence objects, which sets both the "old" and the "new" states to
3328
+ the given value(s):
3329
+
3330
+ >>> sm(1.0)
3331
+ >>> print_vector(sm.values)
3332
+ 1.0, 1.0
3333
+ >>> print_vector(sm.new)
3334
+ 1.0, 1.0
3335
+ >>> print_vector(sm.old)
3336
+ 1.0, 1.0
3337
+
3338
+ Alternatively, one can assign values to property |StateSequence.new| or property
3339
+ |StateSequence.old| (note that using |StateSequence.new| is identical with using
3340
+ the |Variable.value| property):
3341
+
3342
+ >>> sm.new = 2.0, 3.0
3343
+ >>> sm
3344
+ sm(2.0, 3.0)
3345
+ >>> print_vector(sm.values)
3346
+ 2.0, 3.0
3347
+ >>> print_vector(sm.new)
3348
+ 2.0, 3.0
3349
+ >>> print_vector(sm.old)
3350
+ 1.0, 1.0
3351
+
3352
+ >>> sm.old = 200.0
3353
+ >>> sm
3354
+ sm(2.0, 3.0)
3355
+ >>> print_vector(sm.values)
3356
+ 2.0, 3.0
3357
+ >>> print_vector(sm.new)
3358
+ 2.0, 3.0
3359
+ >>> print_vector(sm.old)
3360
+ 200.0, 200.0
3361
+
3362
+ If you assign problematic values to property |StateSequence.old|, it raises similar
3363
+ error messages as property |Variable.value|:
3364
+
3365
+ >>> sm.old = 1.0, 2.0, 3.0
3366
+ Traceback (most recent call last):
3367
+ ...
3368
+ ValueError: While trying to set the old value(s) of state sequence `sm`, the \
3369
+ following error occurred: While trying to convert the value(s) `(1.0, 2.0, 3.0)` to a \
3370
+ numpy ndarray with shape `(2,)` and type `float`, the following error occurred: could \
3371
+ not broadcast input array from shape (3,) into shape (2,)
3372
+
3373
+ Just for completeness: Method |StateSequence.new2old| effectively takes the new
3374
+ values as old ones, but more efficiently than using the properties
3375
+ |StateSequence.new| and |StateSequence.old| (the Python method
3376
+ |StateSequence.new2old| is usually replaced by model-specific, cythonized version
3377
+ when working in Cython mode):
3378
+
3379
+ >>> sm.new2old()
3380
+ >>> print_vector(sm.values)
3381
+ 2.0, 3.0
3382
+ >>> print_vector(sm.new)
3383
+ 2.0, 3.0
3384
+ >>> print_vector(sm.old)
3385
+ 2.0, 3.0
3386
+ """
3387
+
3388
+ subvars: StateSequences
3389
+ """The subgroup to which the state sequence belongs."""
3390
+ subseqs: StateSequences
3391
+ """Alias for |StateSequence.subvars|."""
3392
+ fastaccess_new: FastAccessOutputSequence
3393
+ fastaccess_old: variabletools.FastAccess
3394
+
3395
+ def __call__(self, *args) -> None:
3396
+ """The prefered way to pass values to |Sequence_| instances within initial
3397
+ condition files."""
3398
+ super().__call__(*args)
3399
+ self.new2old()
3400
+
3401
+ def _finalise_connections(self) -> None:
3402
+ super()._finalise_connections()
3403
+ if self.NUMERIC:
3404
+ value = None if self.NDIM else numpy.zeros(self.numericshape)
3405
+ self._set_fastaccessattribute("points", value)
3406
+ self._set_fastaccessattribute("results", copy.copy(value))
3407
+ self.fastaccess_old = self.subseqs.fastaccess_old
3408
+ self.fastaccess_new = self.subseqs.fastaccess_new
3409
+ if self.NDIM:
3410
+ setattr(self.fastaccess_old, self.name, None)
3411
+ else:
3412
+ setattr(self.fastaccess_old, self.name, 0.0)
3413
+
3414
+ def _get_shape(self) -> tuple[int, ...]:
3415
+ """A tuple containing the actual lengths of all dimensions.
3416
+
3417
+ |StateSequence| objects come with some additional `fastaccess` attributes,
3418
+ which should only be of interest to framework developers. One such attribute
3419
+ is the `results` array, handling the (intermediate or final) calculation
3420
+ results for state sequence, as shown in the following example for the
3421
+ 0-dimensional sequence |wland_states.HS| of the |wland_wag| model:
3422
+
3423
+ >>> from hydpy import prepare_model, print_vector, pub
3424
+ >>> model = prepare_model("wland_wag")
3425
+ >>> print_vector(model.sequences.states.hs.fastaccess._hs_results)
3426
+ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
3427
+
3428
+ For 1-dimensional numerical state sequences, the `results` attribute is |None|
3429
+ initially, as property |ModelSequence.numericshape| is unknown. Setting the
3430
+ |StateSequence.shape| attribute of the respective |StateSequence| object (we
3431
+ select |wland_states.IC| as an example) prepares all "fastaccess attributes"
3432
+ automatically:
3433
+
3434
+ >>> ic = model.sequences.states.ic
3435
+ >>> ic.fastaccess._ic_results
3436
+
3437
+ >>> ic.shape = (2,)
3438
+ >>> ic.shape
3439
+ (2,)
3440
+ >>> ic.fastaccess._ic_results.shape
3441
+ (11, 2)
3442
+ """
3443
+ return super()._get_shape()
3444
+
3445
+ def _set_shape(self, shape: int | tuple[int, ...]):
3446
+ super()._set_shape(shape)
3447
+ if self.NDIM:
3448
+ setattr(self.fastaccess_old, self.name, self.new.copy())
3449
+ if self.NUMERIC:
3450
+ self._set_fastaccessattribute("points", numpy.zeros(self.numericshape))
3451
+ self._set_fastaccessattribute("results", numpy.zeros(self.numericshape))
3452
+
3453
+ shape = propertytools.Property(fget=_get_shape, fset=_set_shape)
3454
+
3455
+ @property
3456
+ def new(self):
3457
+ """State(s) after calling a |Model| calculation method. (Alias for property
3458
+ |Variable.value|).
3459
+
3460
+ Property |StateSequence.new| handles, in contrast to property
3461
+ |StateSequence.old|, the newly calculated state values during each simulation
3462
+ step. It supports testing and debugging of individual |Model| methods but is
3463
+ typically irrelevant when scripting *HydPy* workflows.
3464
+ """
3465
+ return super()._get_value()
3466
+
3467
+ @new.setter
3468
+ def new(self, value):
3469
+ super()._set_value(value)
3470
+
3471
+ @property
3472
+ def old(self):
3473
+ """State(s) before calling a |Model| calculation method.
3474
+
3475
+ Note the similarity to property |StateSequence.new|. However, property
3476
+ |StateSequence.old| references the initial states of the respective simulation
3477
+ step, which should not be changed by |Model| calculation methods.
3478
+ """
3479
+ return self._prepare_getvalue(
3480
+ True, getattr(self.fastaccess_old, self.name, None)
3481
+ )
3482
+
3483
+ @old.setter
3484
+ def old(self, value):
3485
+ try:
3486
+ setattr(self.fastaccess_old, self.name, self._prepare_setvalue(value))
3487
+ except BaseException:
3488
+ objecttools.augment_excmessage(
3489
+ f"While trying to set the old value(s) of state sequence "
3490
+ f"{objecttools.devicephrase(self)}"
3491
+ )
3492
+
3493
+ def new2old(self) -> None:
3494
+ """Assign the |StateSequence.new| state values to the |StateSequence.old|
3495
+ values.
3496
+
3497
+ See the main documentation on class |StateSequence| for further information.
3498
+
3499
+ Note that method |StateSequence.new2old| is replaced by a model-specific,
3500
+ cythonized method when working in Cython mode.
3501
+ """
3502
+ if self.NDIM:
3503
+ self.old[:] = self.new[:]
3504
+ else:
3505
+ self.old = self.new
3506
+
3507
+
3508
+ class LogSequence(ConditionSequence):
3509
+ """Base class for logging values required for later calculations.
3510
+
3511
+ Class |LogSequence| serves similar purposes as |StateSequence| but is less strict
3512
+ in its assumptions. While |StateSequence| objects always handle two states (the
3513
+ |StateSequence.old| and the |StateSequence.new| one), |LogSequence| objects are
3514
+ supposed to remember an arbitrary or sequence-specific number of values, which can
3515
+ be state values but, for example, also flux values. A typical use case is to store
3516
+ "old" values of effective precipitation to calculate "new" values of direct
3517
+ discharge using the unit hydrograph concept in later simulation steps.
3518
+
3519
+ It is up to the model developer to ensure that a |LogSequence| subclass has the
3520
+ correct dimensionality and shape to store the required information. By convention,
3521
+ the "memory" of each |LogSequence| should be placed on the first axis for
3522
+ non-scalar properties.
3523
+
3524
+ As |StateSequence| objects, |LogSequence| objects store relevant information to
3525
+ start a new simulation run where another one has ended and are thus written into
3526
+ and read from condition files.
3527
+ """
3528
+
3529
+ subvars: LogSequences
3530
+ """The subgroup to which the log sequence belongs."""
3531
+ subseqs: LogSequences
3532
+ """Alias for |LogSequence.subvars|."""
3533
+
3534
+ _CLS_FASTACCESS_PYTHON = variabletools.FastAccess
3535
+
3536
+
3537
+ class LogSequenceFixed(LogSequence):
3538
+ """Base class for log sequences with a fixed shape."""
3539
+
3540
+ NDIM = 1
3541
+ SHAPE: int
3542
+
3543
+ def _finalise_connections(self):
3544
+ self.shape = (self.SHAPE,)
3545
+
3546
+ def _get_shape(self):
3547
+ """Sequences derived from |LogSequenceFixed| initialise themselves with a
3548
+ predefined shape.
3549
+
3550
+ We take parameter |dam_logs.LoggedRequiredRemoteRelease| of base model |dam| as
3551
+ an example:
3552
+
3553
+ >>> from hydpy.models.dam import *
3554
+ >>> parameterstep()
3555
+ >>> logs.loggedrequiredremoterelease.shape
3556
+ (1,)
3557
+
3558
+ Property |LogSequenceFixed.shape| results in the following exception when you
3559
+ try to set a new shape:
3560
+
3561
+ >>> logs.loggedrequiredremoterelease.shape = 2
3562
+ Traceback (most recent call last):
3563
+ ...
3564
+ AttributeError: The shape of sequence `loggedrequiredremoterelease` cannot be \
3565
+ changed, but this was attempted for element `?`.
3566
+
3567
+ See the documentation on property |Variable.shape| of class |Variable| for
3568
+ further information.
3569
+ """
3570
+ return super()._get_shape()
3571
+
3572
+ def _set_shape(self, shape):
3573
+ if exceptiontools.attrready(self, "shape"):
3574
+ raise AttributeError(
3575
+ f"The shape of sequence `{self.name}` cannot be changed, but this was "
3576
+ f"attempted for element `{objecttools.devicename(self)}`."
3577
+ )
3578
+ super()._set_shape(shape)
3579
+
3580
+ shape = propertytools.Property(fget=_get_shape, fset=_set_shape)
3581
+
3582
+
3583
+ class AideSequence(ModelSequence):
3584
+ """Base class for aide sequences of |Model| objects.
3585
+
3586
+ Aide sequences store data only relevant for calculating an individual simulation
3587
+ time step but must be shared between different calculation methods of a |Model|
3588
+ object.
3589
+ """
3590
+
3591
+ subvars: AideSequences
3592
+ """The subgroup to which the aide sequence belongs."""
3593
+ subseqs: AideSequences
3594
+ """Alias for |AideSequence.subvars|."""
3595
+
3596
+ _CLS_FASTACCESS_PYTHON = variabletools.FastAccess
3597
+
3598
+
3599
+ class LinkSequence(ModelSequence):
3600
+ """Base class for link sequences of |Model| objects.
3601
+
3602
+ |LinkSequence| objects do not handle values themselves. Instead, they point to the
3603
+ values handled by |NodeSequence| objects, using the functionalities provided by the
3604
+ Cython module |pointerutils|. Multiple |LinkSequence| objects of different
3605
+ application models can query and modify the same |NodeSequence| values, allowing
3606
+ different |Model| objects to share information and interact with each other.
3607
+
3608
+ A note for developers: |LinkSequence| subclasses must be either 0-dimensional or
3609
+ 1-dimensional.
3610
+
3611
+ Users might encounter the following exception that is a safety measure to prevent
3612
+ segmentation faults, as the error message suggests:
3613
+
3614
+ >>> from hydpy.core.sequencetools import LinkSequence
3615
+ >>> seq = LinkSequence(None)
3616
+ >>> seq
3617
+ linksequence(?)
3618
+ >>> seq.value
3619
+ Traceback (most recent call last):
3620
+ ...
3621
+ hydpy.core.exceptiontools.AttributeNotReady: While trying to query the value(s) \
3622
+ of link sequence `linksequence` of element `?`, the following error occurred: Proper \
3623
+ connections are missing (which could result in segmentation faults when using it, so \
3624
+ please be careful).
3625
+ """
3626
+
3627
+ subvars: LinkSequences[LinkSequence]
3628
+ """The subgroup to which the link sequence belongs."""
3629
+ subseqs: LinkSequences[LinkSequence]
3630
+ """Alias for |LinkSequence.subvars|."""
3631
+ fastaccess: FastAccessLinkSequence
3632
+ """Object for accessing the link sequence's data with little overhead."""
3633
+
3634
+ _CLS_FASTACCESS_PYTHON = FastAccessLinkSequence
3635
+
3636
+ __isready: bool = False
3637
+
3638
+ def set_pointer(self, double: pointerutils.Double, idx: int = 0) -> None:
3639
+ """Prepare a pointer referencing the given |Double| object.
3640
+
3641
+ For 1-dimensional sequence objects, one also needs to specify the relevant
3642
+ index position of the pointer via argument `idx`.
3643
+
3644
+ Method |LinkSequence.set_pointer| should be relevant for framework developers
3645
+ and eventually for some model developers only.
3646
+ """
3647
+ if self.NDIM == 0:
3648
+ self.fastaccess.set_pointer0d(self.name, double)
3649
+ elif self.NDIM == 1:
3650
+ self.fastaccess.set_pointer1d(self.name, double, idx)
3651
+ self.__isready = True
3652
+
3653
+ def _finalise_connections(self) -> None:
3654
+ value = pointerutils.PPDouble() if self.NDIM else None
3655
+ try:
3656
+ setattr(self.fastaccess, self.name, value)
3657
+ setattr(self.fastaccess, f"len_{self.name}", 0)
3658
+ except AttributeError:
3659
+ pass
3660
+
3661
+ def _get_value(self):
3662
+ """The actual value(s) the |LinkSequence| object is pointing at.
3663
+
3664
+ Changing a |LinkSequence.value| of a |LinkSequence| object seems very much like
3665
+ changing a |LinkSequence.value| of any other |Variable| object. However, be
3666
+ aware that you are changing a value handled by a |NodeSequence| object. We
3667
+ demonstrate this by using the `HydPy-H-Lahn` example project through invoking
3668
+ function |prepare_full_example_2|:
3669
+
3670
+ >>> from hydpy.core.testtools import prepare_full_example_2
3671
+ >>> hp, pub, TestIO = prepare_full_example_2()
3672
+
3673
+ We focus on the |musk_classic| application model `stream_lahn_marb_lahn_leun`
3674
+ routing inflow from node `lahn_marb` to node `lahn_leun`:
3675
+
3676
+ >>> model = hp.elements.stream_lahn_marb_lahn_leun.model
3677
+
3678
+ The first example shows that the 0-dimensional outlet sequence |musk_outlets.Q|
3679
+ points to the |Sim| sequence of node `lahn_leun`:
3680
+
3681
+ >>> model.sequences.outlets.q
3682
+ q(0.0)
3683
+ >>> hp.nodes.lahn_leun.sequences.sim = 1.0
3684
+ >>> model.sequences.outlets.q
3685
+ q(1.0)
3686
+ >>> model.sequences.outlets.q(2.0)
3687
+ >>> hp.nodes.lahn_leun.sequences.sim
3688
+ sim(2.0)
3689
+
3690
+ The second example shows that the 1-dimensional inlet sequence |musk_inlets.Q|
3691
+ points to the |Sim| sequence of node `lahn_marb`:
3692
+
3693
+ >>> model.sequences.inlets.q
3694
+ q(0.0)
3695
+ >>> hp.nodes.lahn_marb.sequences.sim = 1.0
3696
+ >>> model.sequences.inlets.q
3697
+ q(1.0)
3698
+ >>> model.sequences.inlets.q(2.0)
3699
+ >>> hp.nodes.lahn_marb.sequences.sim
3700
+ sim(2.0)
3701
+
3702
+ Direct querying the values of both link sequences shows that the value of the
3703
+ 0-dimensional outlet sequence is scalar, of course, and that the value of the
3704
+ 1-dimensional inlet sequence is one entry of a vector:
3705
+
3706
+ >>> from hydpy import print_vector, round_
3707
+ >>> round_(model.sequences.outlets.q.value)
3708
+ 2.0
3709
+ >>> print_vector(model.sequences.inlets.q.values)
3710
+ 2.0
3711
+
3712
+ Assigning incorrect data leads to the usual error messages:
3713
+
3714
+ >>> model.sequences.outlets.q.value = 1.0, 2.0
3715
+ Traceback (most recent call last):
3716
+ ...
3717
+ ValueError: While trying to assign the value(s) (1.0, 2.0) to link sequence \
3718
+ `q` of element `stream_lahn_marb_lahn_leun`, the following error occurred: 2 values \
3719
+ are assigned to the scalar variable `q` of element `stream_lahn_marb_lahn_leun`.
3720
+ >>> model.sequences.inlets.q.values = 1.0, 2.0
3721
+ Traceback (most recent call last):
3722
+ ...
3723
+ ValueError: While trying to assign the value(s) (1.0, 2.0) to link sequence \
3724
+ `q` of element `stream_lahn_marb_lahn_leun`, the following error occurred: While \
3725
+ trying to convert the value(s) `(1.0, 2.0)` to a numpy ndarray with shape `(1,)` and \
3726
+ type `float`, the following error occurred: could not broadcast input array from \
3727
+ shape (2,) into shape (1,)
3728
+
3729
+ In the example above, the 1-dimensional inlet sequence |musk_inlets.Q| only
3730
+ points a single |NodeSequence| value. We now prepare a |exch_branch_hbv96|
3731
+ application model instance to show what happens when connecting a 1-dimensional
3732
+ |LinkSequence| object (|exch_outlets.Branched|) with three |NodeSequence|
3733
+ objects (see the documentation of application model |exch_branch_hbv96| for
3734
+ more details):
3735
+
3736
+ >>> from hydpy import Element, Nodes, prepare_model
3737
+ >>> model = prepare_model("exch_branch_hbv96")
3738
+ >>> nodes = Nodes("input1", "input2", "output1", "output2", "output3")
3739
+ >>> branch = Element("branch",
3740
+ ... inlets=["input1", "input2"],
3741
+ ... outlets=["output1", "output2", "output3"])
3742
+ >>> model.parameters.control.xpoints(
3743
+ ... 0.0, 2.0, 4.0, 6.0)
3744
+ >>> model.parameters.control.ypoints(
3745
+ ... output1=[0.0, 1.0, 2.0, 3.0],
3746
+ ... output2=[0.0, 1.0, 0.0, 0.0],
3747
+ ... output3=[0.0, 0.0, 2.0, 6.0])
3748
+ >>> branch.model = model
3749
+
3750
+ Our third example demonstrates that each field of the values of a 1-dimensional
3751
+ |LinkSequence| objects points to another |NodeSequence| object:
3752
+
3753
+ >>> nodes.output1.sequences.sim = 1.0
3754
+ >>> nodes.output2.sequences.sim = 2.0
3755
+ >>> nodes.output3.sequences.sim = 3.0
3756
+ >>> model.sequences.outlets.branched
3757
+ branched(1.0, 2.0, 3.0)
3758
+ >>> model.sequences.outlets.branched = 4.0, 5.0, 6.0
3759
+ >>> nodes.output1.sequences.sim
3760
+ sim(4.0)
3761
+ >>> nodes.output2.sequences.sim
3762
+ sim(5.0)
3763
+ >>> nodes.output3.sequences.sim
3764
+ sim(6.0)
3765
+
3766
+ .. testsetup::
3767
+
3768
+ >>> from hydpy import Node, Element
3769
+ >>> Node.clear_all()
3770
+ >>> Element.clear_all()
3771
+ """
3772
+ try:
3773
+ if not self.__isready:
3774
+ raise exceptiontools.AttributeNotReady(
3775
+ "Proper connections are missing (which could result in "
3776
+ "segmentation faults when using it, so please be careful)."
3777
+ )
3778
+ return self.fastaccess.get_value(self.name)
3779
+ except BaseException:
3780
+ objecttools.augment_excmessage(
3781
+ f"While trying to query the value(s) of link sequence "
3782
+ f"{objecttools.elementphrase(self)}"
3783
+ )
3784
+
3785
+ def _set_value(self, value):
3786
+ try:
3787
+ self.fastaccess.set_value(self.name, self._prepare_setvalue(value))
3788
+ except BaseException:
3789
+ objecttools.augment_excmessage(
3790
+ f"While trying to assign the value(s) {value} to link sequence "
3791
+ f"{objecttools.elementphrase(self)}"
3792
+ )
3793
+
3794
+ value = property(fget=_get_value, fset=_set_value)
3795
+
3796
+ def _get_shape(self) -> tuple[int, ...]:
3797
+ """A tuple containing the actual lengths of all dimensions.
3798
+
3799
+ Property |LinkSequence.shape| of class |LinkSequence| works similarly as the
3800
+ general |Variable.shape| property of class |Variable|. Still, you need to be
3801
+ extra careful due to the pointer mechanism underlying class |LinkSequence|.
3802
+ Change the shape of a link sequence for good reasons only. Please read the
3803
+ documentation on property |LinkSequence.value| first and then see the following
3804
+ examples, which are, again, based on the `HydPy-H-Lahn` example project and
3805
+ application model |musk_classic|:
3806
+
3807
+ >>> from hydpy.core.testtools import prepare_full_example_2
3808
+ >>> hp, pub, TestIO = prepare_full_example_2()
3809
+ >>> model = hp.elements.stream_lahn_marb_lahn_leun.model
3810
+
3811
+ The default mechanisms of *HydPy* prepare both 0-dimensional and 1-dimensional
3812
+ link sequences with a proper shape (which, for inlet sequence |
3813
+ musk_inlets.Q|, depends on the number of connected |Node| objects):
3814
+
3815
+ >>> model.sequences.outlets.q.shape
3816
+ ()
3817
+ >>> model.sequences.inlets.q.shape
3818
+ (1,)
3819
+
3820
+ Attempting to set the only possible shape of 0-dimensional link sequences or
3821
+ any different shape results in the standard behaviour:
3822
+
3823
+ >>> model.sequences.outlets.q.shape = ()
3824
+ >>> model.sequences.outlets.q.shape = (1,)
3825
+ Traceback (most recent call last):
3826
+ ...
3827
+ ValueError: While trying to set the shape of link sequence`q` of element \
3828
+ `stream_lahn_marb_lahn_leun`, the following error occurred: The shape information of \
3829
+ 0-dimensional variables as `q` of element `stream_lahn_marb_lahn_leun` can only \
3830
+ be `()`, but `(1,)` is given.
3831
+
3832
+ Changing the shape of 1-dimensional link sequences is supported but destroys
3833
+ the connection to the |NodeSequence| values of the respective nodes.
3834
+ Therefore, he following exception prevents segmentation faults until proper
3835
+ connections are available:
3836
+
3837
+ >>> model.sequences.inlets.q.shape = (2,)
3838
+ >>> model.sequences.inlets.q.shape
3839
+ (2,)
3840
+ >>> model.sequences.inlets.q.shape = 1
3841
+ >>> model.sequences.inlets.q.shape
3842
+ (1,)
3843
+ >>> model.sequences.inlets.q
3844
+ Traceback (most recent call last):
3845
+ ...
3846
+ RuntimeError: While trying to query the value(s) of link sequence `q` of \
3847
+ element `stream_lahn_marb_lahn_leun`, the following error occurred: The pointer of \
3848
+ the actual `PPDouble` instance at index `0` requested, but not prepared yet via \
3849
+ `set_pointer`.
3850
+
3851
+ >>> model.sequences.inlets.q(1.0)
3852
+ Traceback (most recent call last):
3853
+ ...
3854
+ RuntimeError: While trying to assign the value(s) 1.0 to link sequence `q` of \
3855
+ element `stream_lahn_marb_lahn_leun`, the following error occurred: The pointer of \
3856
+ the actual `PPDouble` instance at index `0` requested, but not prepared yet via \
3857
+ `set_pointer`.
3858
+
3859
+ Querying the shape of a link sequence should rarely result in errors. However,
3860
+ if we enforce it by deleting the `fastaccess` attribute, we get an error
3861
+ message:
3862
+
3863
+ >>> del model.sequences.inlets.q.fastaccess
3864
+ >>> model.sequences.inlets.q.shape
3865
+ Traceback (most recent call last):
3866
+ ...
3867
+ AttributeError: While trying to query the shape of link sequence`q` of \
3868
+ element `stream_lahn_marb_lahn_leun`, the following error occurred: 'Q' object has no \
3869
+ attribute 'fastaccess'
3870
+
3871
+ .. testsetup::
3872
+
3873
+ >>> from hydpy import Node, Element
3874
+ >>> Node.clear_all()
3875
+ >>> Element.clear_all()
3876
+ """
3877
+ try:
3878
+ if self.NDIM == 0:
3879
+ return ()
3880
+ try:
3881
+ return getattr(self.fastaccess, self.name).shape
3882
+ except AttributeError:
3883
+ return (self._get_fastaccessattribute("length_0"),)
3884
+ except BaseException:
3885
+ objecttools.augment_excmessage(
3886
+ f"While trying to query the shape of link sequence"
3887
+ f"{objecttools.elementphrase(self)}"
3888
+ )
3889
+
3890
+ def _set_shape(self, shape: int | tuple[int, ...]):
3891
+ try:
3892
+ if (self.NDIM == 0) and shape:
3893
+ self._raise_wrongshape(shape)
3894
+ elif self.NDIM == 1:
3895
+ if isinstance(shape, Iterable):
3896
+ shape = list(shape)[0]
3897
+ self.fastaccess.dealloc(self.name)
3898
+ self.fastaccess.alloc(self.name, shape)
3899
+ setattr(self.fastaccess, "len_" + self.name, self.shape[0])
3900
+ except BaseException:
3901
+ objecttools.augment_excmessage(
3902
+ f"While trying to set the shape of link sequence"
3903
+ f"{objecttools.elementphrase(self)}"
3904
+ )
3905
+
3906
+ shape = propertytools.Property(fget=_get_shape, fset=_set_shape)
3907
+
3908
+ def __repr__(self):
3909
+ if self.__isready:
3910
+ return super().__repr__()
3911
+ return f"{self.name}(?)"
3912
+
3913
+
3914
+ class InletSequence(LinkSequence):
3915
+ """Base class for inlet link sequences of |Model| objects."""
3916
+
3917
+ subvars: InletSequences
3918
+ """The subgroup to which the inlet sequence belongs."""
3919
+ subseqs: InletSequences
3920
+ """Alias for |InletSequence.subvars|."""
3921
+
3922
+
3923
+ class OutletSequence(LinkSequence):
3924
+ """Base class for outlet link sequences of |Model| objects."""
3925
+
3926
+ subvars: OutletSequences
3927
+ """The subgroup to which the outlet sequence belongs."""
3928
+ subseqs: OutletSequences
3929
+ """Alias for |OutletSequence.subvars|."""
3930
+
3931
+
3932
+ class ReceiverSequence(LinkSequence):
3933
+ """Base class for receiver link sequences of |Model| objects."""
3934
+
3935
+ subvars: ReceiverSequences
3936
+ """The subgroup to which the receiver sequence belongs."""
3937
+ subseqs: ReceiverSequences
3938
+ """Alias for |ReceiverSequence.subvars|."""
3939
+
3940
+
3941
+ class SenderSequence(LinkSequence):
3942
+ """Base class for sender link sequences of |Model| objects."""
3943
+
3944
+ subvars: SenderSequences
3945
+ """The subgroup to which the sender sequence belongs."""
3946
+ subseqs: SenderSequences
3947
+ """Alias for |SenderSequence.subvars|."""
3948
+
3949
+
3950
+ class NodeSequence(IOSequence):
3951
+ """Base class for all sequences to be handled by |Node| objects."""
3952
+
3953
+ subvars: NodeSequences
3954
+ """The subgroup to which the node sequence belongs."""
3955
+ subseqs: NodeSequences
3956
+ """Alias for |NodeSequence.subvars|."""
3957
+ fastaccess: FastAccessNodeSequence
3958
+ """Object for accessing the node sequence's data with little overhead."""
3959
+
3960
+ NDIM: int = 0
3961
+ NUMERIC: bool = False
3962
+
3963
+ _CLS_FASTACCESS_PYTHON = FastAccessNodeSequence
3964
+
3965
+ def __init__(self, subvars: NodeSequences) -> None:
3966
+ super().__init__(subvars)
3967
+ self.subseqs = subvars
3968
+
3969
+ @property
3970
+ def initinfo(self) -> tuple[pointerutils.Double, bool]:
3971
+ """Return a |Double| instead of a |float| object as the first tuple entry."""
3972
+ if hydpy.pub.options.usedefaultvalues:
3973
+ return pointerutils.Double(0.0), True
3974
+ return pointerutils.Double(numpy.nan), False
3975
+
3976
+ @property
3977
+ def descr_sequence(self) -> str:
3978
+ """Description of the |NodeSequence| object, including the |Node.variable| to
3979
+ be represented.
3980
+
3981
+ >>> from hydpy import Node
3982
+ >>> Node("test_node_1", "T").sequences.sim.descr_sequence
3983
+ 'sim_t'
3984
+
3985
+ >>> from hydpy import FusedVariable
3986
+ >>> from hydpy.aliases import hland_inputs_T, lland_inputs_TemL
3987
+ >>> Temp = FusedVariable("Temp", hland_inputs_T, lland_inputs_TemL)
3988
+ >>> Node("test_node_2", Temp).sequences.sim.descr_sequence
3989
+ 'sim_temp'
3990
+
3991
+ .. testsetup::
3992
+
3993
+ >>> Node.clear_all()
3994
+ """
3995
+ return f"{self.name}_{str(self.subseqs.node.variable).lower()}"
3996
+
3997
+ @property
3998
+ def descr_device(self) -> str:
3999
+ """Description of the |Node| object the |NodeSequence| object belongs to.
4000
+
4001
+ >>> from hydpy import Node
4002
+ >>> Node("test_node_2").sequences.sim.descr_device
4003
+ 'test_node_2'
4004
+
4005
+ .. testsetup::
4006
+
4007
+ >>> Node.clear_all()
4008
+ """
4009
+ return self.subseqs.node.name
4010
+
4011
+ def _finalise_connections(self) -> None:
4012
+ super()._finalise_connections()
4013
+ setattr(self.fastaccess, self.name, pointerutils.Double(0.0))
4014
+ setattr(self.fastaccess, "_reset_obsdata", False)
4015
+
4016
+ def _get_value(self):
4017
+ """The actual sequence value.
4018
+
4019
+ For framework users, the property |NodeSequence.value| of class |NodeSequence|
4020
+ works as usual (explained in the documentation on property |Variable.shape| of
4021
+ class |Variable|). However, framework developers should note that
4022
+ |NodeSequence| objects use |Double| objects for storing their values and making
4023
+ them accessible to |PDouble| and |PPDouble| objects as explained in detail in
4024
+ the documentation on class |LinkSequence|. This mechanism is hidden for
4025
+ framework users via conversions to type |float| for safety reasons:
4026
+
4027
+ .. testsetup::
4028
+
4029
+ >>> from hydpy import Node
4030
+ >>> Node.clear_all()
4031
+
4032
+ >>> from hydpy import Node
4033
+ >>> sim = Node("node").sequences.sim
4034
+ >>> sim(1.0)
4035
+ >>> sim
4036
+ sim(1.0)
4037
+ >>> sim.value
4038
+ 1.0
4039
+ >>> sim.fastaccess.sim
4040
+ Double(1.0)
4041
+
4042
+ >>> sim.value = 2.0
4043
+ >>> sim
4044
+ sim(2.0)
4045
+
4046
+ Node sequences return errors like the following if they receive misspecified
4047
+ values or are ill-configured:
4048
+
4049
+ >>> sim.value = 1.0, 2.0 # doctest: +ELLIPSIS
4050
+ Traceback (most recent call last):
4051
+ ...
4052
+ TypeError: While trying to assign the value `(1.0, 2.0)` to sequence `sim` of \
4053
+ node `node`, the following error occurred: float() argument must be a string or a... \
4054
+ number, not 'tuple'
4055
+
4056
+ >>> sim.name = None
4057
+ >>> sim.value # doctest: +ELLIPSIS
4058
+ Traceback (most recent call last):
4059
+ ...
4060
+ TypeError: While trying to query the value of sequence `None` of node `node`, \
4061
+ the following error occurred: ...attribute name must be string...
4062
+
4063
+ .. testsetup::
4064
+
4065
+ >>> Node.clear_all()
4066
+ """
4067
+ try:
4068
+ return getattr(self.fastaccess, self.name)[0]
4069
+ except BaseException:
4070
+ objecttools.augment_excmessage(
4071
+ f"While trying to query the value of sequence "
4072
+ f"{objecttools.nodephrase(self)}"
4073
+ )
4074
+
4075
+ def _set_value(self, value):
4076
+ try:
4077
+ getattr(self.fastaccess, self.name)[0] = float(value)
4078
+ except BaseException:
4079
+ objecttools.augment_excmessage(
4080
+ f"While trying to assign the value `{value}` to sequence "
4081
+ f"{objecttools.nodephrase(self)}"
4082
+ )
4083
+
4084
+ value = property(fget=_get_value, fset=_set_value)
4085
+
4086
+ @property
4087
+ def seriescomplete(self) -> bool:
4088
+ """True/False flag indicating whether simulated or observed data is fully
4089
+ available or not.
4090
+
4091
+ We use the observation series of node `dill_assl` of the `HydPy-H-Lahn` project:
4092
+
4093
+ >>> from hydpy.core.testtools import prepare_full_example_2
4094
+ >>> hp, pub, TestIO = prepare_full_example_2()
4095
+ >>> obs = hp.nodes.dill_assl.sequences.obs
4096
+
4097
+ When the sequence does not handle any time series data,
4098
+ |NodeSequence.seriescomplete| is |False|:
4099
+
4100
+ >>> obs.prepare_series(allocate_ram=False)
4101
+ >>> obs.series
4102
+ Traceback (most recent call last):
4103
+ ...
4104
+ hydpy.core.exceptiontools.AttributeNotReady: Sequence `obs` of node \
4105
+ `dill_assl` is not requested to make any time series data available.
4106
+ >>> obs.seriescomplete
4107
+ False
4108
+
4109
+ As long as any time series data is missing, |NodeSequence.seriescomplete| is
4110
+ still |False|:
4111
+
4112
+ >>> obs.prepare_series()
4113
+ >>> obs.series[:-1] = 1.0
4114
+ >>> obs.series
4115
+ InfoArray([ 1., 1., 1., nan])
4116
+ >>> obs.seriescomplete
4117
+ False
4118
+
4119
+ Only with all data being not |numpy.nan|, |NodeSequence.seriescomplete| is
4120
+ |True|:
4121
+
4122
+ >>> obs.series[-1] = 1.0
4123
+ >>> obs.seriescomplete
4124
+ True
4125
+
4126
+ .. testsetup::
4127
+
4128
+ >>> from hydpy import Node, Element
4129
+ >>> Node.clear_all()
4130
+ >>> Element.clear_all()
4131
+ """
4132
+ return self.memoryflag and not numpy.any(numpy.isnan(self.series))
4133
+
4134
+
4135
+ class Sim(NodeSequence):
4136
+ """Class for handling those values of |Node| objects that are "simulated", meaning
4137
+ calculated by hydrological models."""
4138
+
4139
+ def load_series(self) -> None:
4140
+ """Read time series data like method |IOSequence.load_series| of class
4141
+ |IOSequence| but with special handling of missing data.
4142
+
4143
+ The method's "special handling" is to convert errors to warnings. We explain
4144
+ the reasons in the documentation on method |Obs.load_series| of class |Obs|,
4145
+ from which we borrow the following examples. The only differences are that
4146
+ method |Sim.load_series| of class |Sim| does not disable property
4147
+ |IOSequence.memoryflag| and uses the option |Options.warnmissingsimfile|
4148
+ instead of |Options.warnmissingobsfile|:
4149
+
4150
+ >>> from hydpy.core.testtools import prepare_full_example_1
4151
+ >>> prepare_full_example_1()
4152
+ >>> from hydpy import HydPy, pub, TestIO
4153
+ >>> hp = HydPy("HydPy-H-Lahn")
4154
+ >>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
4155
+ >>> with TestIO():
4156
+ ... hp.prepare_network()
4157
+ ... hp.prepare_models()
4158
+ ... hp.prepare_simseries()
4159
+ >>> sim = hp.nodes.dill_assl.sequences.sim
4160
+ >>> with TestIO():
4161
+ ... sim.load_series() # doctest: +ELLIPSIS
4162
+ Traceback (most recent call last):
4163
+ ...
4164
+ UserWarning: While trying to load the time series data of sequence `sim` of \
4165
+ node `dill_assl`, the following error occurred: [Errno 2] No such file or directory: \
4166
+ '...dill_assl_sim_q.asc'
4167
+ >>> sim.series
4168
+ InfoArray([nan, nan, nan, nan, nan])
4169
+
4170
+ >>> sim.series = 1.0
4171
+ >>> with TestIO():
4172
+ ... sim.save_series()
4173
+ >>> sim.series = 0.0
4174
+ >>> with TestIO():
4175
+ ... sim.load_series()
4176
+ >>> sim.series
4177
+ InfoArray([1., 1., 1., 1., 1.])
4178
+
4179
+ >>> import numpy
4180
+ >>> sim.series[2] = numpy.nan
4181
+ >>> with TestIO(), pub.sequencemanager.overwrite(True):
4182
+ ... sim.save_series()
4183
+ >>> with TestIO():
4184
+ ... sim.load_series()
4185
+ Traceback (most recent call last):
4186
+ ...
4187
+ UserWarning: While trying to load the time series data of sequence `sim` of \
4188
+ node `dill_assl`, the following error occurred: The series array of sequence `sim` of \
4189
+ node `dill_assl` contains 1 nan value.
4190
+ >>> sim.series
4191
+ InfoArray([ 1., 1., nan, 1., 1.])
4192
+
4193
+ >>> sim.series = 0.0
4194
+ >>> with TestIO(), pub.options.warnmissingsimfile(False):
4195
+ ... sim.load_series()
4196
+ >>> sim.series
4197
+ InfoArray([ 1., 1., nan, 1., 1.])
4198
+
4199
+ .. testsetup::
4200
+
4201
+ >>> from hydpy import Node, Element
4202
+ >>> Node.clear_all()
4203
+ >>> Element.clear_all()
4204
+ """
4205
+ try:
4206
+ super().load_series()
4207
+ except BaseException:
4208
+ if hydpy.pub.options.warnmissingsimfile:
4209
+ warnings.warn(str(sys.exc_info()[1]))
4210
+
4211
+
4212
+ class Obs(NodeSequence):
4213
+ """Class for handling those values of |Node| objects that are observed, meaning
4214
+ read from data files."""
4215
+
4216
+ def load_series(self) -> None:
4217
+ """Read time series data like method |IOSequence.load_series| of class
4218
+ |IOSequence| but with special handling of missing data.
4219
+
4220
+ When reading incomplete time series data, *HydPy* usually raises a
4221
+ |RuntimeError| to prevent from performing erroneous calculations. This
4222
+ functionality makes sense for meteorological input data that is a strict
4223
+ requirement for hydrological simulations. However, the same often does not
4224
+ hold for the time series of |Obs| sequences, e.g. representing measured
4225
+ discharge. Measured discharge is often an optional input or only used for
4226
+ comparison purposes.
4227
+
4228
+ According to this reasoning, *HydPy* raises (at most) a |UserWarning| in case
4229
+ of missing or incomplete external time series data of |Obs| sequences. The
4230
+ following examples show this based on the `HydPy-H-Lahn` project, mainly
4231
+ focussing on the |Obs| sequence of node `dill_assl`, which is ready for handling
4232
+ time series data at the end of the following steps:
4233
+
4234
+ >>> from hydpy.core.testtools import prepare_full_example_1
4235
+ >>> prepare_full_example_1()
4236
+ >>> from hydpy import HydPy, pub, TestIO
4237
+ >>> hp = HydPy("HydPy-H-Lahn")
4238
+ >>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
4239
+ >>> with TestIO():
4240
+ ... hp.prepare_network()
4241
+ ... hp.prepare_models()
4242
+ ... hp.prepare_obsseries()
4243
+ >>> obs = hp.nodes.dill_assl.sequences.obs
4244
+ >>> obs.ramflag
4245
+ True
4246
+
4247
+ Trying to read non-existing data raises the following warning and disables the
4248
+ sequence's ability to handle time series data:
4249
+
4250
+ >>> import os
4251
+ >>> with TestIO():
4252
+ ... os.remove(hp.nodes.dill_assl.sequences.obs.filepath)
4253
+ ... hp.load_obsseries() # doctest: +ELLIPSIS
4254
+ Traceback (most recent call last):
4255
+ ...
4256
+ UserWarning: The `memory flag` of sequence `obs` of node `dill_assl` had to \
4257
+ be set to `False` due to the following problem: While trying to load the time series \
4258
+ data of sequence `obs` of node `dill_assl`, the following error occurred: [Errno 2] \
4259
+ No such file or directory: '...dill_assl_obs_q.asc'
4260
+
4261
+ >>> obs.ramflag
4262
+ False
4263
+
4264
+ After writing a complete data file, everything works fine:
4265
+
4266
+ >>> obs.prepare_series()
4267
+ >>> obs.series = 1.0
4268
+ >>> with TestIO():
4269
+ ... obs.save_series()
4270
+ >>> obs.series = 0.0
4271
+ >>> with TestIO():
4272
+ ... obs.load_series()
4273
+ >>> obs.series
4274
+ InfoArray([1., 1., 1., 1., 1.])
4275
+
4276
+ Reading incomplete data also results in a warning message, but does not disable
4277
+ the |IOSequence.memoryflag|:
4278
+
4279
+ >>> import numpy
4280
+ >>> obs.series[2] = numpy.nan
4281
+ >>> with TestIO(), pub.sequencemanager.overwrite(True):
4282
+ ... obs.save_series()
4283
+ >>> with TestIO():
4284
+ ... obs.load_series()
4285
+ Traceback (most recent call last):
4286
+ ...
4287
+ UserWarning: While trying to load the time series data of sequence `obs` of \
4288
+ node `dill_assl`, the following error occurred: The series array of sequence `obs` of \
4289
+ node `dill_assl` contains 1 nan value.
4290
+ >>> obs.memoryflag
4291
+ True
4292
+
4293
+ Option |Options.warnmissingobsfile| allows disabling the warning messages
4294
+ without altering the functionalities described above:
4295
+
4296
+ >>> hp.prepare_obsseries()
4297
+ >>> with TestIO():
4298
+ ... os.remove(hp.nodes.lahn_marb.sequences.obs.filepath)
4299
+ ... with pub.options.warnmissingobsfile(False):
4300
+ ... hp.load_obsseries()
4301
+ >>> obs.series
4302
+ InfoArray([ 1., 1., nan, 1., 1.])
4303
+ >>> hp.nodes.lahn_marb.sequences.obs.memoryflag
4304
+ False
4305
+
4306
+ .. testsetup::
4307
+
4308
+ >>> from hydpy import Node, Element
4309
+ >>> Node.clear_all()
4310
+ >>> Element.clear_all()
4311
+ """
4312
+ try:
4313
+ super().load_series()
4314
+ except OSError:
4315
+ self._set_fastaccessattribute("ramflag", False)
4316
+ self._set_fastaccessattribute("diskflag_reading", False)
4317
+ if hydpy.pub.options.warnmissingobsfile:
4318
+ warnings.warn(
4319
+ f"The `memory flag` of sequence {objecttools.nodephrase(self)} had "
4320
+ f"to be set to `False` due to the following problem: "
4321
+ f"{sys.exc_info()[1]}"
4322
+ )
4323
+ except BaseException:
4324
+ if hydpy.pub.options.warnmissingobsfile:
4325
+ warnings.warn(str(sys.exc_info()[1]))
4326
+
4327
+
4328
+ class NodeSequences(
4329
+ IOSequences["devicetools.Node", NodeSequence, FastAccessNodeSequence]
4330
+ ):
4331
+ """Base class for handling |Sim| and |Obs| sequence objects.
4332
+
4333
+ Basically, |NodeSequences| works like the different |ModelSequences| subclasses
4334
+ used for handling |ModelSequence| objects. The main difference is that they do not
4335
+ reference a |Sequences| object (which is only handled by |Element| objects but not
4336
+ by |Node| objects). Instead, they directly reference their master |Node| object
4337
+ via the attribute |NodeSequences.node|:
4338
+
4339
+ >>> from hydpy import Node
4340
+ >>> node = Node("node")
4341
+ >>> node.sequences.node
4342
+ Node("node", variable="Q")
4343
+
4344
+ The implemented methods just call the same method of the underlying `fastaccess`
4345
+ attribute, which is an instance of (a Cython extension class of) the Python class
4346
+ |sequencetools.FastAccessNodeSequence|.
4347
+
4348
+ .. testsetup::
4349
+
4350
+ >>> Node.clear_all()
4351
+ """
4352
+
4353
+ CLASSES = (Sim, Obs)
4354
+
4355
+ node: devicetools.Node
4356
+ sim: Sim
4357
+ obs: Obs
4358
+ _cymodel: CyModelProtocol | None
4359
+ _CLS_FASTACCESS_PYTHON = FastAccessNodeSequence
4360
+
4361
+ def __init__(
4362
+ self,
4363
+ master: devicetools.Node,
4364
+ cls_fastaccess: type[FastAccessNodeSequence] | None = None,
4365
+ cymodel: CyModelProtocol | None = None,
4366
+ ) -> None:
4367
+ self.node = master
4368
+ self._cls_fastaccess = cls_fastaccess
4369
+ self._cymodel = cymodel
4370
+ super().__init__(master)
4371
+
4372
+ def _init_fastaccess(self) -> None:
4373
+ if hydpy.pub.options.usecython:
4374
+ self.fastaccess = sequenceutils.FastAccessNodeSequence()
4375
+ else:
4376
+ self.fastaccess = self._CLS_FASTACCESS_PYTHON()
4377
+
4378
+ def load_data(self, idx: int) -> None:
4379
+ """Call method |sequencetools.FastAccessNodeSequence.load_data| of the current
4380
+ `fastaccess` attribute.
4381
+
4382
+ >>> from hydpy import Node, pub
4383
+ >>> with pub.options.usecython(False):
4384
+ ... node = Node("node")
4385
+ >>> from unittest import mock
4386
+ >>> method = "hydpy.core.sequencetools.FastAccessNodeSequence.load_data"
4387
+ >>> with mock.patch(method) as mocked:
4388
+ ... node.sequences.load_data(5)
4389
+ >>> mocked.call_args_list
4390
+ [call(5)]
4391
+
4392
+ .. testsetup::
4393
+
4394
+ >>> Node.clear_all()
4395
+ """
4396
+ self.fastaccess.load_data(idx)
4397
+
4398
+ def load_simdata(self, idx: int) -> None:
4399
+ """Call method |sequencetools.FastAccessNodeSequence.load_simdata| of the
4400
+ current `fastaccess` attribute.
4401
+
4402
+ >>> from hydpy import Node, pub
4403
+ >>> with pub.options.usecython(False):
4404
+ ... node = Node("node")
4405
+ >>> from unittest import mock
4406
+ >>> method = "hydpy.core.sequencetools.FastAccessNodeSequence.load_simdata"
4407
+ >>> with mock.patch(method) as mocked:
4408
+ ... node.sequences.load_simdata(5)
4409
+ >>> mocked.call_args_list
4410
+ [call(5)]
4411
+
4412
+ .. testsetup::
4413
+
4414
+ >>> Node.clear_all()
4415
+ """
4416
+ self.fastaccess.load_simdata(idx)
4417
+
4418
+ def load_obsdata(self, idx: int) -> None:
4419
+ """Call method |sequencetools.FastAccessNodeSequence.load_obsdata| of the
4420
+ current `fastaccess` attribute.
4421
+
4422
+ >>> from hydpy import Node, pub
4423
+ >>> with pub.options.usecython(False):
4424
+ ... node = Node("node")
4425
+ >>> from unittest import mock
4426
+ >>> method = "hydpy.core.sequencetools.FastAccessNodeSequence.load_obsdata"
4427
+ >>> with mock.patch(method) as mocked:
4428
+ ... node.sequences.load_obsdata(5)
4429
+ >>> mocked.call_args_list
4430
+ [call(5)]
4431
+
4432
+ .. testsetup::
4433
+
4434
+ >>> Node.clear_all()
4435
+ """
4436
+ self.fastaccess.load_obsdata(idx)
4437
+
4438
+ def save_data(self, idx: int) -> None:
4439
+ """Call method |sequencetools.FastAccessNodeSequence.save_data| of the current
4440
+ `fastaccess` attribute.
4441
+
4442
+ >>> from hydpy import Node, pub
4443
+ >>> with pub.options.usecython(False):
4444
+ ... node = Node("node")
4445
+ >>> from unittest import mock
4446
+ >>> method = "hydpy.core.sequencetools.FastAccessNodeSequence.save_data"
4447
+ >>> with mock.patch(method) as mocked:
4448
+ ... node.sequences.save_data(5)
4449
+ >>> mocked.call_args_list
4450
+ [call(5)]
4451
+
4452
+ .. testsetup::
4453
+
4454
+ >>> Node.clear_all()
4455
+ """
4456
+ self.fastaccess.save_data(idx)
4457
+
4458
+ def save_simdata(self, idx: int) -> None:
4459
+ """Call method |sequencetools.FastAccessNodeSequence.save_simdata| of the
4460
+ current `fastaccess` attribute.
4461
+
4462
+ >>> from hydpy import Node, pub
4463
+ >>> with pub.options.usecython(False):
4464
+ ... node = Node('node')
4465
+ >>> from unittest import mock
4466
+ >>> method = "hydpy.core.sequencetools.FastAccessNodeSequence.save_simdata"
4467
+ >>> with mock.patch(method) as mocked:
4468
+ ... node.sequences.save_simdata(5)
4469
+ >>> mocked.call_args_list
4470
+ [call(5)]
4471
+
4472
+ .. testsetup::
4473
+
4474
+ >>> Node.clear_all()
4475
+ """
4476
+ self.fastaccess.save_simdata(idx)