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,2348 @@
1
+ """This module facilitates using *HydPy* as an HTTP server application.
2
+
3
+ .. _`OpenDA`: https://www.openda.org/
4
+ .. _`curl`: https://curl.haxx.se/
5
+ .. _`HydPy-OpenDA-Black-Box-Model-Wrapper`: \
6
+ https://github.com/hydpy-dev/OpenDA/tree/master/extensions/\
7
+ HydPyOpenDABBModelWrapper
8
+ .. _`issue`: https://github.com/hydpy-dev/OpenDA/issues
9
+
10
+ *HydPy* is designed to be used interactively or by executing individual Python scripts.
11
+ Consider the typical steps of calibrating model parameters. Usually, one first
12
+ prepares an instance of class |HydPy|, then changes some parameter values and performs
13
+ a simulation, and finally inspects whether the new simulation results are better than
14
+ the ones of the original parameterisation or not. One can perform these steps
15
+ manually (in a Python console) or apply optimisation tools like those provided by
16
+ |scipy| (usually in a Python script).
17
+
18
+ Performing or implementing such procedures is relatively simple, as long as all tools
19
+ are written in Python or come with a Python interface, which is not the case for some
20
+ relevant optimisation tools. One example is `OpenDA`_, being written in Java, which
21
+ was the original reason for adding module |servertools| to the *HydPy* framework.
22
+
23
+ Module |servertools| solves such integration problems by running *HydPy* within an
24
+ HTTP server. After starting such a server, one can use any HTTP client (e.g. `curl`_)
25
+ to perform the above steps.
26
+
27
+ The server's API is relatively simple, allowing performing a "normal" calibration using
28
+ only a few server methods. However, it is also more restrictive than controlling
29
+ *HydPy* within a Python process. Within a Python process, you are free to do anything.
30
+ Using the *HydPy* server, you are much more restricted to what was anticipated by the
31
+ framework developers.
32
+
33
+ Commonly but not mandatory, one configures the initial state of a *HydPy* server with
34
+ an XML file. As an example, we prepare the `HydPy-H-Lahn` project by calling function
35
+ |prepare_full_example_1|, which contains the XML configuration file
36
+ `multiple_runs_alpha.xml`:
37
+
38
+ >>> from hydpy.core.testtools import prepare_full_example_1
39
+ >>> prepare_full_example_1()
40
+
41
+ To start the server in a new process, open a command-line tool and insert the following
42
+ command (see module |hyd| for general information on how to use *HydPy* via the command
43
+ line):
44
+
45
+ >>> command = "hyd.py start_server 8080 HydPy-H-Lahn multiple_runs_alpha.xml"
46
+ >>> from hydpy import run_subprocess, TestIO
47
+ >>> with TestIO():
48
+ ... process = run_subprocess(command, blocking=False, verbose=False)
49
+ ... result = run_subprocess("hyd.py await_server 8080 10", verbose=False)
50
+
51
+ The *HydPy* server should now be running on port 8080. You can use any HTTP client to
52
+ check it is working. For example, you can type the following URLs in your web browser
53
+ to get information on the types and initial values of the exchange items defined in
54
+ `multiple_runs_alpha.xml` (in a format required by the
55
+ `HydPy-OpenDA-Black-Box-Model-Wrapper`_):
56
+
57
+ >>> from urllib import request
58
+ >>> url = "http://127.0.0.1:8080/query_itemtypes"
59
+ >>> print(str(request.urlopen(url).read(), encoding="utf-8"))
60
+ alpha = Double0D
61
+ dill_assl_nodes_sim_series = TimeSeries0D
62
+
63
+ >>> url = "http://127.0.0.1:8080/query_initialitemvalues"
64
+ >>> print(str(request.urlopen(url).read(), encoding="utf-8"))
65
+ alpha = 2.0
66
+ dill_assl_nodes_sim_series = [nan, nan, nan, nan, nan]
67
+
68
+ It is generally possible to control the *HydPy* server via invoking each method with a
69
+ separate HTTP request. However, alternatively, one can use methods
70
+ |HydPyServer.GET_execute| and |HydPyServer.POST_execute| to execute many methods with
71
+ only one HTTP request. We now define three such metafunctions. The first one changes
72
+ the value of the parameter |hland_control.Alpha| The second one runs a simulation.
73
+ The third one prints the newly calculated discharge at the outlet of the headwater
74
+ catchment `Dill`. All of this is very similar to what the
75
+ `HydPy-OpenDA-Black-Box-Model-Wrapper`_ does.
76
+
77
+ Function `set_itemvalues` wraps the POST methods
78
+ |HydPyServer.POST_register_simulationdates|,
79
+ |HydPyServer.POST_register_parameteritemvalues|, and
80
+ |HydPyServer.POST_register_conditionitemvalues|. The *HydPy* server will execute
81
+ these methods in the given order. The arguments `firstdate_sim`, `lastdate_sim`,
82
+ and `alpha` allow changing the start and end date of the simulation period and the
83
+ value of parameter |hland_control.alpha| later:
84
+
85
+ >>> def set_itemvalues(id_, firstdate, lastdate, alpha):
86
+ ... content = (f"firstdate_sim = {firstdate}\\n"
87
+ ... f"lastdate_sim = {lastdate}\\n"
88
+ ... f"alpha = {alpha}").encode("utf-8")
89
+ ... methods = ",".join(("POST_register_simulationdates",
90
+ ... "POST_register_parameteritemvalues",
91
+ ... "POST_register_conditionitemvalues"))
92
+ ... url = f"http://127.0.0.1:8080/execute?id={id_}&methods={methods}"
93
+ ... request.urlopen(url, data=content)
94
+
95
+ Function `simulate` wraps only GET methods and triggers the next simulation run. As
96
+ for all GET and POST methods, one should pass the query parameter `id`, used by the
97
+ *HydPy* server for internal bookmarking:
98
+
99
+ >>> def simulate(id_):
100
+ ... methods = ",".join(("GET_activate_simulationdates",
101
+ ... "GET_activate_parameteritemvalues",
102
+ ... "GET_load_internalconditions",
103
+ ... "GET_activate_conditionitemvalues",
104
+ ... "GET_simulate",
105
+ ... "GET_save_internalconditions",
106
+ ... "GET_update_conditionitemvalues",
107
+ ... "GET_update_getitemvalues"))
108
+ ... url = f"http://127.0.0.1:8080/execute?id={id_}&methods={methods}"
109
+ ... request.urlopen(url)
110
+
111
+ Function `print_itemvalues` also wraps only GET methods and prints the current value of
112
+ parameter |hland_control.Alpha| as well as the lastly simulated discharge values
113
+ corresponding to the given `id` value:
114
+
115
+ >>> from hydpy import print_vector
116
+ >>> def print_itemvalues(id_):
117
+ ... methods = ",".join(("GET_query_simulationdates",
118
+ ... "GET_query_parameteritemvalues",
119
+ ... "GET_query_conditionitemvalues",
120
+ ... "GET_query_getitemvalues"))
121
+ ... url = f"http://127.0.0.1:8080/execute?id={id_}&methods={methods}"
122
+ ... data = str(request.urlopen(url).read(), encoding="utf-8")
123
+ ... for line in data.split("\\n"):
124
+ ... if line.startswith("alpha"):
125
+ ... alpha = line.split("=")[1].strip()
126
+ ... if line.startswith("dill_assl"):
127
+ ... discharge = eval(line.split("=")[1])
128
+ ... print(f"{alpha}: ", end="")
129
+ ... print_vector(discharge)
130
+
131
+ For the sake of brevity, we also define `do_everything` for calling the other functions
132
+ at once:
133
+
134
+ >>> def do_everything(id_, firstdate, lastdate, alpha):
135
+ ... set_itemvalues(id_, firstdate, lastdate, alpha)
136
+ ... simulate(id_)
137
+ ... print_itemvalues(id_)
138
+
139
+ In the simplest example, we perform a simulation throughout five days for an
140
+ |hland_control.Alpha| value of 2:
141
+
142
+ >>> do_everything("1a", "1996-01-01", "1996-01-06", 2.0)
143
+ 2.0: 35.494358, 7.730125, 5.01782, 4.508775, 4.244626
144
+
145
+ The following example shows interlocked simulation runs. The first call only triggers
146
+ a simulation run for the first initialised day:
147
+
148
+ >>> do_everything("1b", "1996-01-01", "1996-01-02", 2.0)
149
+ 2.0: 35.494358
150
+
151
+ The second call repeats the first one with a different `id` value:
152
+
153
+ >>> do_everything("2", "1996-01-01", "1996-01-02", 2.0)
154
+ 2.0: 35.494358
155
+
156
+ The third call covers the first three initialisation days:
157
+
158
+ >>> do_everything("3", "1996-01-01", "1996-01-04", 2.0)
159
+ 2.0: 35.494358, 7.730125, 5.01782
160
+
161
+ The fourth call continues the simulation of the first call, covering the last four
162
+ initialised days:
163
+
164
+ >>> do_everything("1b", "1996-01-02", "1996-01-06", 2.0)
165
+ 2.0: 7.730125, 5.01782, 4.508775, 4.244626
166
+
167
+ The results of the very first call of function `do_everything` (with`id=1`) are
168
+ identical with the pulled-together discharge values of the calls with `id=1b`, made
169
+ possible by the internal bookmarking feature of the *HydPy* server. Here we use
170
+ numbers, but any other strings are valid `id` values.
171
+
172
+ This example extends the last one by applying different parameter values:
173
+
174
+ >>> do_everything("4", "1996-01-01", "1996-01-04", 2.0)
175
+ 2.0: 35.494358, 7.730125, 5.01782
176
+ >>> do_everything("5", "1996-01-01", "1996-01-04", 1.0)
177
+ 1.0: 11.757526, 8.865079, 7.101815
178
+ >>> do_everything("4", "1996-01-04", "1996-01-06", 2.0)
179
+ 2.0: 4.508775, 4.244626
180
+ >>> do_everything("5", "1996-01-04", "1996-01-06", 1.0)
181
+ 1.0: 5.994195, 5.301584
182
+ >>> do_everything("5", "1996-01-01", "1996-01-06", 1.0)
183
+ 1.0: 11.757526, 8.865079, 7.101815, 5.994195, 5.301584
184
+
185
+ The order in which function `do_everything` calls its subfunctions seems quite natural,
186
+ but some tools might require do deviate from it. For example, `OpenDA`_ offers
187
+ ensemble-based algorithms triggering the simulation of all memberse before starting to
188
+ query any simulation results. The final example shows that the underlying atomic
189
+ methods support such an execution sequence:
190
+
191
+ >>> set_itemvalues("6", "1996-01-01", "1996-01-03", 2.0)
192
+ >>> simulate("6")
193
+ >>> set_itemvalues("7", "1996-01-01", "1996-01-03", 1.0)
194
+ >>> simulate("7")
195
+ >>> print_itemvalues("6")
196
+ 2.0: 35.494358, 7.730125
197
+ >>> print_itemvalues("7")
198
+ 1.0: 11.757526, 8.865079
199
+
200
+ When working in parallel mode, `OpenDA`_ might not always call the functions
201
+ `set_itemvalues` and `simulate` for the same `id` directly one after another, which
202
+ also causes no problem:
203
+
204
+ >>> set_itemvalues("6", "1996-01-03", "1996-01-06", 2.0)
205
+ >>> set_itemvalues("7", "1996-01-03", "1996-01-06", 1.0)
206
+ >>> simulate("6")
207
+ >>> simulate("7")
208
+ >>> print_itemvalues("6")
209
+ 2.0: 5.01782, 4.508775, 4.244626
210
+ >>> print_itemvalues("7")
211
+ 1.0: 7.101815, 5.994195, 5.301584
212
+
213
+ Finally, we close the server and kill its process (just closing your command-line tool
214
+ works likewise):
215
+
216
+ >>> _ = request.urlopen("http://127.0.0.1:8080/close_server")
217
+ >>> process.kill()
218
+ >>> _ = process.communicate()
219
+
220
+ The above description focussed on coupling *HydPy* to `OpenDA`_. However, the applied
221
+ atomic submethods of class |HydPyServer| also allow coupling *HydPy* with other
222
+ software products. See the documentation on class |HydPyServer| for further information.
223
+ """
224
+
225
+ # import...
226
+ # ...from standard library
227
+ from __future__ import annotations
228
+ import collections
229
+ import mimetypes
230
+ import os
231
+
232
+ # import http.server # moved below for efficiency reasons
233
+ import threading
234
+ import time
235
+ import urllib.error
236
+ import urllib.parse
237
+ import urllib.request
238
+ import types
239
+
240
+ # ...from site-packages
241
+ import numpy
242
+
243
+ # ...from HydPy
244
+ import hydpy
245
+ from hydpy import conf
246
+ from hydpy import config
247
+ from hydpy.core import hydpytools
248
+ from hydpy.core import itemtools
249
+ from hydpy.core import objecttools
250
+ from hydpy.core import timetools
251
+ from hydpy.exe import commandtools
252
+ from hydpy.exe import xmltools
253
+ from hydpy.core.typingtools import *
254
+
255
+
256
+ # pylint: disable=wrong-import-position, wrong-import-order
257
+ # see the documentation on method `start_server` for explanations
258
+ mimetypes.inited = True
259
+ import http.server
260
+
261
+ mimetypes.inited = False
262
+ # pylint: enable=wrong-import-position, wrong-import-order
263
+
264
+
265
+ ID = NewType("ID", str)
266
+ ID.__doc__ = """Type for strings that identify "artificial" *HydPy* instances (from a
267
+ client's point of view)."""
268
+
269
+
270
+ class ServerState:
271
+ """Singleton class handling states like the current |HydPy| instance exchange items.
272
+
273
+ The instance of class |ServerState| is available as the member `state` of class
274
+ |HydPyServer| after calling the function |start_server|. You could create other
275
+ instances (like we do in the following examples), but you most likely shouldn't.
276
+ The primary purpose of this instance is to store information between successive
277
+ initialisations of class |HydPyServer|.
278
+
279
+ We use the `HydPy-H-Lahn` project and its (complicated) XML configuration file
280
+ `multiple_runs.xml` as an example (module |xmltools| provides information on
281
+ interpreting this file):
282
+
283
+ >>> from hydpy.core.testtools import prepare_full_example_1
284
+ >>> prepare_full_example_1()
285
+ >>> from hydpy import print_vector, TestIO
286
+ >>> from hydpy.exe.servertools import ServerState
287
+ >>> with TestIO(): # doctest: +ELLIPSIS
288
+ ... state = ServerState("HydPy-H-Lahn", "multiple_runs.xml")
289
+ Start HydPy project `HydPy-H-Lahn` (...).
290
+ Read configuration file `multiple_runs.xml` (...).
291
+ Interpret the defined options (...).
292
+ Interpret the defined period (...).
293
+ Read all network files (...).
294
+ Activate the selected network (...).
295
+ Read the required control files (...).
296
+ Read the required condition files (...).
297
+ Read the required time series files (...).
298
+
299
+ After initialisation, all defined exchange items are available:
300
+
301
+ >>> for item in state.parameteritems:
302
+ ... print(item)
303
+ SetItem(name="alpha", master="hland_96", target="control.alpha", level="global")
304
+ SetItem(name="beta", master="hland_96", target="control.beta", level="global")
305
+ SetItem(name="lag", master="musk_classic", target="control.nmbsegments", \
306
+ keyword="lag", level="global")
307
+ SetItem(name="damp", master="musk_classic", target="control.coefficients", \
308
+ keyword="damp", level="global")
309
+ AddItem(name="sfcf_1", master="hland_96", target="control.sfcf", \
310
+ base="control.rfcf", level="global")
311
+ AddItem(name="sfcf_2", master="hland_96", target="control.sfcf", \
312
+ base="control.rfcf", level="global")
313
+ AddItem(name="sfcf_3", master="hland_96", target="control.sfcf", \
314
+ base="control.rfcf", level="subunit")
315
+ MultiplyItem(name="k4", master="hland_96", target="control.k4", base="control.k", \
316
+ level="global")
317
+ >>> for item in state.conditionitems:
318
+ ... print(item)
319
+ SetItem(name="ic_lahn_leun", master="hland_96", target="states.ic", level="device")
320
+ SetItem(name="ic_lahn_marb", master="hland_96", target="states.ic", level="subunit")
321
+ SetItem(name="sm_lahn_leun", master="hland_96", target="states.sm", level="device")
322
+ SetItem(name="sm_lahn_marb", master="hland_96", target="states.sm", level="subunit")
323
+ SetItem(name="quh", master="rconc_uh", target="logs.quh", level="device")
324
+ >>> for item in state.getitems:
325
+ ... print(item)
326
+ GetItem(name="?", master="hland_96", target="factors.contriarea")
327
+ GetItem(name="current_discharge", master="hland_96", target="fluxes.qt")
328
+ GetItem(name="entire_discharge_series", master="hland_96", \
329
+ target="fluxes.qt.series")
330
+ GetItem(name="?", master="hland_96", target="states.sm")
331
+ GetItem(name="?", master="hland_96", target="states.sm.series")
332
+ GetItem(name="?", master="nodes", target="nodes.sim.series")
333
+
334
+ The initialisation also memorises the initial conditions of all elements:
335
+
336
+ >>> for element in state.init_conditions:
337
+ ... print(element)
338
+ land_dill_assl
339
+ land_lahn_kalk
340
+ land_lahn_leun
341
+ land_lahn_marb
342
+ stream_dill_assl_lahn_leun
343
+ stream_lahn_leun_lahn_kalk
344
+ stream_lahn_marb_lahn_leun
345
+
346
+ The initialisation also prepares all selected series arrays and reads the
347
+ required input data:
348
+
349
+ >>> print_vector(state.hp.elements.land_dill_assl.model.sequences.inputs.t.series)
350
+ 0.0, -0.5, -2.4, -6.8, -7.8
351
+ >>> state.hp.nodes.dill_assl.sequences.sim.series
352
+ InfoArray([nan, nan, nan, nan, nan])
353
+ """
354
+
355
+ interface: xmltools.XMLInterface
356
+ hp: hydpytools.HydPy
357
+ parameteritems: list[itemtools.ChangeItem]
358
+ inputitems: list[itemtools.SetItem]
359
+ conditionitems: list[itemtools.SetItem]
360
+ outputitems: list[itemtools.SetItem]
361
+ getitems: list[itemtools.GetItem]
362
+ conditions: dict[ID, dict[int, Conditions]]
363
+ parameteritemvalues: dict[ID, dict[Name, Any]]
364
+ inputitemvalues: dict[ID, dict[Name, Any]]
365
+ conditionitemvalues: dict[ID, dict[Name, Any]]
366
+ outputitemvalues: dict[ID, dict[Name, Any]]
367
+ getitemvalues: dict[ID, dict[Name, str]]
368
+ initialparameteritemvalues: dict[Name, Any]
369
+ initialinputitemvalues: dict[Name, Any]
370
+ initialconditionitemvalues: dict[Name, Any]
371
+ initialgetitemvalues: dict[Name, Any]
372
+ timegrids: dict[ID, timetools.Timegrid]
373
+ init_conditions: Conditions
374
+ inputconditiondirs: dict[ID, str]
375
+ outputconditiondirs: dict[ID, str]
376
+ serieswriterdirs: dict[ID, str]
377
+ seriesreaderdirs: dict[ID, str]
378
+ outputcontroldirs: dict[ID, str]
379
+ idx1: int
380
+ idx2: int
381
+
382
+ def __init__(
383
+ self,
384
+ projectname: str,
385
+ xmlfile: str,
386
+ load_conditions: bool = True,
387
+ load_series: bool = True,
388
+ ) -> None:
389
+ write = commandtools.print_textandtime
390
+ write(f"Start HydPy project `{projectname}`")
391
+ hp = hydpytools.HydPy(projectname)
392
+ write(f"Read configuration file `{xmlfile}`")
393
+ self.interface = xmltools.XMLInterface(xmlfile)
394
+ write("Interpret the defined options")
395
+ self.interface.update_options()
396
+ write("Interpret the defined period")
397
+ self.interface.update_timegrids()
398
+ write("Read all network files")
399
+ self.interface.network_io.prepare_network()
400
+ write("Create the custom selections (if defined)")
401
+ self.interface.update_selections()
402
+ write("Activate the selected network")
403
+ hp.update_devices(selection=self.interface.fullselection, silent=True)
404
+ write("Read the required control files")
405
+ self.interface.control_io.prepare_models()
406
+ if load_conditions:
407
+ write("Read the required condition files")
408
+ self.interface.conditions_io.load_conditions()
409
+ if load_series:
410
+ write("Read the required time series files")
411
+ self.interface.series_io.prepare_series()
412
+ self.interface.exchange.prepare_series()
413
+ if load_series:
414
+ self.interface.series_io.load_series()
415
+ self.hp = hp
416
+ self.parameteritems = self.interface.exchange.parameteritems
417
+ self.inputitems = self.interface.exchange.inputitems
418
+ self.conditionitems = self.interface.exchange.conditionitems
419
+ self.outputitems = self.interface.exchange.outputitems
420
+ self.getitems = self.interface.exchange.getitems
421
+ self.initialparameteritemvalues = {
422
+ item.name: item.value for item in self.parameteritems
423
+ }
424
+ self.initialinputitemvalues = {
425
+ item.name: item.value for item in self.inputitems
426
+ }
427
+ self.initialconditionitemvalues = {
428
+ item.name: item.value for item in self.conditionitems
429
+ }
430
+ self.initialoutputitemvalues = {
431
+ item.name: item.value for item in self.outputitems
432
+ }
433
+ self.initialgetitemvalues = {
434
+ name: value
435
+ for item in self.getitems
436
+ for name, value in item.yield_name2value(*hydpy.pub.timegrids.simindices)
437
+ }
438
+ self.conditions = {}
439
+ self.parameteritemvalues = {}
440
+ self.inputitemvalues = {}
441
+ self.conditionitemvalues = {}
442
+ self.outputitemvalues = {}
443
+ self.getitemvalues = {}
444
+ self.init_conditions = hp.conditions
445
+ self.timegrids = {}
446
+ self.serieswriterdirs = {}
447
+ self.seriesreaderdirs = {}
448
+ self.inputconditiondirs = {}
449
+ self.outputconditiondirs = {}
450
+ self.outputcontroldirs = {}
451
+ self.idx1 = 0
452
+ self.idx2 = 0
453
+
454
+
455
+ class HydPyServer(http.server.BaseHTTPRequestHandler):
456
+ """The API of the *HydPy* server.
457
+
458
+ Technically and strictly speaking, |HydPyServer| is, only the HTTP request handler
459
+ for the real HTTP server class (from the standard library).
460
+
461
+ After initialising the *HydPy* server, each communication via a GET or POST request
462
+ is handled by a new instance of |HydPyServer|. This handling occurs in a unified
463
+ way using either method |HydPyServer.do_GET| or [HydPyServer.do_POST|, which select
464
+ and apply the actual GET or POST method. All methods provided by class
465
+ |HydPyServer| starting with "GET" or "POST" are accessible via HTTP.
466
+
467
+ In the main documentation on module |servertools|, we use the
468
+ `multiple_runs_alpha.xml` file of the `HydPy-H-Lahn` project as an example.
469
+ However, now we select the more complex XML configuration file `multiple_runs.xml`,
470
+ covering a higher number of cases:
471
+
472
+ >>> from hydpy.core.testtools import prepare_full_example_1
473
+ >>> prepare_full_example_1()
474
+ >>> from hydpy import run_subprocess, TestIO
475
+ >>> with TestIO():
476
+ ... process = run_subprocess(
477
+ ... "hyd.py start_server 8080 HydPy-H-Lahn multiple_runs.xml "
478
+ ... "debugging=enable",
479
+ ... blocking=False,
480
+ ... verbose=False,
481
+ ... )
482
+ ... result = run_subprocess("hyd.py await_server 8080 10", verbose=False)
483
+
484
+ We define a test function that simplifies sending the following requests and offers
485
+ two optional arguments. When passing a value to `id_`, `test` adds this value as
486
+ the query parameter `id` to the URL. When passing a string to `data`, `test` sends
487
+ a POST request containing the given data; otherwise, a GET request without
488
+ additional data:
489
+
490
+ >>> from urllib import request
491
+ >>> def test(name, id_=None, data=None, return_result=False):
492
+ ... url = f"http://127.0.0.1:8080/{name}"
493
+ ... if id_:
494
+ ... url = f"{url}?id={id_}"
495
+ ... if data:
496
+ ... data = bytes(data, encoding="utf-8")
497
+ ... response = request.urlopen(url, data=data)
498
+ ... result = str(response.read(), encoding="utf-8")
499
+ ... print(result)
500
+ ... return result if return_result else None
501
+
502
+ Asking for its status tells us that the server is ready (which may take a while,
503
+ depending on the project's size):
504
+
505
+ >>> test("status")
506
+ status = ready
507
+
508
+ You can query the current version number of the *HydPy* installation used to start
509
+ the server:
510
+
511
+ >>> result = test("version", return_result=True) # doctest: +ELLIPSIS
512
+ version = ...
513
+ >>> hydpy.__version__ in result
514
+ True
515
+
516
+ |HydPyServer| returns the error code `400` if it realises the URL to be wrong:
517
+
518
+ >>> test("missing")
519
+ Traceback (most recent call last):
520
+ ...
521
+ urllib.error.HTTPError: HTTP Error 400: RuntimeError: No method `GET_missing` \
522
+ available.
523
+
524
+ The error code is `500` in all other cases of error:
525
+
526
+ >>> test("register_parameteritemvalues", id_="0", data="alpha = []")
527
+ Traceback (most recent call last):
528
+ ...
529
+ urllib.error.HTTPError: HTTP Error 500: RuntimeError: While trying to execute \
530
+ method `POST_register_parameteritemvalues`, the following error occurred: A value for \
531
+ parameter item `beta` is missing.
532
+
533
+ Some methods require identity information, passed as query parameter `id`, used for
534
+ internal bookmarking:
535
+
536
+ >>> test("query_parameteritemvalues")
537
+ Traceback (most recent call last):
538
+ ...
539
+ urllib.error.HTTPError: HTTP Error 500: RuntimeError: While trying to execute \
540
+ method `GET_query_parameteritemvalues`, the following error occurred: For the GET \
541
+ method `query_parameteritemvalues` no query parameter `id` is given.
542
+
543
+ POST methods always expect an arbitrary number of lines, each one assigning some
544
+ values to some variable (in most cases, numbers to exchange items):
545
+
546
+ >>> test("parameteritemvalues",
547
+ ... id_="a",
548
+ ... data=("x = y\\n"
549
+ ... " \\n"
550
+ ... "x == y\\n"
551
+ ... "x = y"))
552
+ Traceback (most recent call last):
553
+ ...
554
+ urllib.error.HTTPError: HTTP Error 400: RuntimeError: The POST method \
555
+ `parameteritemvalues` received a wrongly formated data body. The following line has \
556
+ been extracted but cannot be further processed: `x == y`.
557
+
558
+ Before explaining the more offical methods, we introduce the method
559
+ |HydPyServer.POST_evaluate|, which evaluates arbitrary valid Python code within the
560
+ server process. Its most likely use-case is to access the (sub)attributes of the
561
+ single instance of class |ServerState|, available as a member of class
562
+ |HydPyServer|. This method can help when being puzzled about the state of the
563
+ *HydPy* server. Use it, for example, to find out which |Node| objects are
564
+ available and to see which one is the outlet node of the |Element| object
565
+ `land_dill_assl`:
566
+
567
+ >>> test("evaluate",
568
+ ... data=("nodes = HydPyServer.state.hp.nodes\\n"
569
+ ... "elements = HydPyServer.state.hp.elements.land_dill_assl"))
570
+ nodes = Nodes("dill_assl", "lahn_kalk", "lahn_leun", "lahn_marb")
571
+ elements = Element("land_dill_assl", outlets="dill_assl", keywords="catchment")
572
+
573
+ Method |HydPyServer.GET_query_itemtypes|, already described in the main
574
+ documentation of module |servertools|, returns all available exchange item types
575
+ at once. However, it is also possible to query those that are related to setting
576
+ parameter values (|HydPyServer.GET_query_parameteritemtypes|), setting condition
577
+ values (|HydPyServer.GET_query_conditionitemtypes|), setting input time series
578
+ (|HydPyServer.GET_query_inputitemtypes|), getting values or series of factors or
579
+ fluxes in the "setitem style" (|HydPyServer.GET_query_outputitemtypes|) and getting
580
+ different kinds of values or series in the "getitem style"
581
+ (|HydPyServer.GET_query_getitemtypes|) separately:
582
+
583
+ >>> test("query_parameteritemtypes")
584
+ alpha = Double0D
585
+ beta = Double0D
586
+ lag = Double0D
587
+ damp = Double0D
588
+ sfcf_1 = Double0D
589
+ sfcf_2 = Double0D
590
+ sfcf_3 = Double1D
591
+ k4 = Double0D
592
+ >>> test("query_conditionitemtypes")
593
+ ic_lahn_leun = Double1D
594
+ ic_lahn_marb = Double1D
595
+ sm_lahn_leun = Double1D
596
+ sm_lahn_marb = Double1D
597
+ quh = Double1D
598
+ >>> test("query_inputitemtypes")
599
+ t_headwaters = TimeSeries1D
600
+ >>> test("query_outputitemtypes")
601
+ swe_headwaters = TimeSeries1D
602
+ >>> test("query_getitemtypes")
603
+ land_dill_assl_factors_contriarea = Double0D
604
+ land_dill_assl_fluxes_qt = Double0D
605
+ land_dill_assl_fluxes_qt_series = TimeSeries0D
606
+ land_dill_assl_states_sm = Double1D
607
+ land_lahn_kalk_states_sm = Double1D
608
+ land_lahn_leun_states_sm = Double1D
609
+ land_lahn_marb_states_sm = Double1D
610
+ land_lahn_kalk_states_sm_series = TimeSeries1D
611
+ dill_assl_nodes_sim_series = TimeSeries0D
612
+
613
+ The same holds for the initial values of the exchange items. Method
614
+ |HydPyServer.GET_query_initialitemvalues| returns them all at once, while the
615
+ methods |HydPyServer.GET_query_initialparameteritemvalues|,
616
+ |HydPyServer.GET_query_initialconditionitemvalues|,
617
+ |HydPyServer.GET_query_initialinputitemvalues|,
618
+ |HydPyServer.GET_query_initialoutputitemvalues|, and
619
+ (|HydPyServer.GET_query_initialgetitemvalues| return the relevant subgroup only.
620
+ Note that for the exchange items related to state sequence |hland_states.SM|
621
+ (`sm_lahn_marb` and `sm_lahn_leun`), the initial values stem from the XML file.
622
+ For the items related to state sequence |hland_states.Ic| and input sequence
623
+ |hland_inputs.T|, the XML file does not provide such information. Thus, the
624
+ initial values of `ic_lahn_marb`, `ic_lahn_leun`, and `t_headwaters` stem from the
625
+ corresponding sequences themselves (and thus, indirectly, from the respective
626
+ condition and time series files):
627
+
628
+ >>> test("query_initialparameteritemvalues")
629
+ alpha = 2.0
630
+ beta = 1.0
631
+ lag = 5.0
632
+ damp = 0.5
633
+ sfcf_1 = 0.3
634
+ sfcf_2 = 0.2
635
+ sfcf_3 = [0.1, 0.2, 0.1, 0.2, 0.1, 0.2, 0.1, 0.2, 0.1, 0.2, 0.1, 0.2, 0.2, 0.2]
636
+ k4 = 10.0
637
+ >>> test("query_initialconditionitemvalues")
638
+ ic_lahn_leun = [1.184948]
639
+ ic_lahn_marb = [0.96404, 1.36332, 0.96458, 1.46458, 0.96512, 1.46512, 0.96565, \
640
+ 1.46569, 0.96617, 1.46617, 0.96668, 1.46668, 1.46719]
641
+ sm_lahn_leun = [123.0]
642
+ sm_lahn_marb = [110.0, 120.0, 130.0, 140.0, 150.0, 160.0, 170.0, 180.0, 190.0, \
643
+ 200.0, 210.0, 220.0, 230.0]
644
+ quh = [10.0]
645
+ >>> test("query_initialinputitemvalues")
646
+ t_headwaters = [[0.0, -0.5, -2.4, -6.8, -7.8], [-0.7, -1.5, -4.6, -8.2, -8.7]]
647
+ >>> test("query_initialoutputitemvalues")
648
+ swe_headwaters = [[nan, nan, nan, nan, nan], [nan, nan, nan, nan, nan]]
649
+ >>> test("query_initialgetitemvalues") # doctest: +ELLIPSIS
650
+ land_dill_assl_factors_contriarea = nan
651
+ land_dill_assl_fluxes_qt = nan
652
+ land_dill_assl_fluxes_qt_series = [nan, nan, nan, nan, nan]
653
+ land_dill_assl_states_sm = [185.13164...]
654
+ land_lahn_kalk_states_sm = [101.31248...]
655
+ land_lahn_leun_states_sm = [138.31396...]
656
+ land_lahn_marb_states_sm = [99.27505...]
657
+ land_lahn_kalk_states_sm_series = [[nan, ...], [nan, ...], ..., [nan, ...]]
658
+ dill_assl_nodes_sim_series = [nan, nan, nan, nan, nan]
659
+
660
+ Some external tools require ways to identify specific sub-values of different
661
+ exchange items. For example, they need to map those sub-values to location data
662
+ available in a separate database. Method |HydPyServer.GET_query_itemsubnames|
663
+ provides artificial sub names suitable for such a mapping. See property
664
+ |ChangeItem.subnames| of class |ChangeItem| and method |GetItem.yield_name2subnames|
665
+ of class |GetItem| for the specification of the sub names. Here, note the special
666
+ handling for change items addressing the `global` level, for which we cannot define
667
+ a meaningful sub name. Method |HydPyServer.GET_query_itemsubnames| returns the
668
+ string `*global*` in such cases:
669
+
670
+ >>> test("query_itemsubnames") # doctest: +ELLIPSIS
671
+ alpha = *global*
672
+ beta = *global*
673
+ lag = *global*
674
+ damp = *global*
675
+ sfcf_1 = *global*
676
+ sfcf_2 = *global*
677
+ sfcf_3 = [land_lahn_kalk_0, ..., land_lahn_kalk_13]
678
+ k4 = *global*
679
+ t_headwaters = [land_dill_assl, land_lahn_marb]
680
+ ic_lahn_leun = [land_lahn_leun]
681
+ ic_lahn_marb = [land_lahn_marb_0, ..., land_lahn_marb_12]
682
+ sm_lahn_leun = [land_lahn_leun]
683
+ sm_lahn_marb = [land_lahn_marb_0, ..., land_lahn_marb_12]
684
+ quh = [land_lahn_leun]
685
+ swe_headwaters = [land_dill_assl, land_lahn_marb]
686
+ land_dill_assl_factors_contriarea = land_dill_assl
687
+ land_dill_assl_fluxes_qt = land_dill_assl
688
+ land_dill_assl_fluxes_qt_series = land_dill_assl
689
+ land_dill_assl_states_sm = ('land_dill_assl_0', ..., 'land_dill_assl_11')
690
+ land_lahn_kalk_states_sm = ('land_lahn_kalk_0', ..., 'land_lahn_kalk_13')
691
+ land_lahn_leun_states_sm = ('land_lahn_leun_0', ..., 'land_lahn_leun_9')
692
+ land_lahn_marb_states_sm = ('land_lahn_marb_0', ..., 'land_lahn_marb_12')
693
+ land_lahn_kalk_states_sm_series = ('land_lahn_kalk_0', ..., 'land_lahn_kalk_13')
694
+ dill_assl_nodes_sim_series = dill_assl
695
+
696
+ The |Timegrids.init| time grid is immutable once the server is ready. Method
697
+ |HydPyServer.GET_query_initialisationtimegrid| returns the fixed first date, last
698
+ date, and stepsize of the whole initialised period:
699
+
700
+ >>> test("query_initialisationtimegrid")
701
+ firstdate_init = 1996-01-01T00:00:00+01:00
702
+ lastdate_init = 1996-01-06T00:00:00+01:00
703
+ stepsize = 1d
704
+
705
+ The dates of the |Timegrids.sim| time grid, on the other hand, are mutable and can
706
+ vary for different `id` query parameters. This flexibility makes things a little
707
+ more complicated, as the |Timegrids| object of the global |pub| module handles only
708
+ one |Timegrids.sim| object at once. Hence, we differentiate between registered
709
+ simulation dates of the respective `id` values and the current simulation dates of
710
+ the |Timegrids| object.
711
+
712
+ Method |HydPyServer.GET_query_simulationdates| asks for registered simulation dates
713
+ and thus fails at first:
714
+
715
+ >>> test("query_simulationdates", id_="0")
716
+ Traceback (most recent call last):
717
+ ...
718
+ urllib.error.HTTPError: HTTP Error 500: RuntimeError: While trying to execute \
719
+ method `GET_query_simulationdates`, the following error occurred: Nothing registered \
720
+ under the id `0`. There is nothing registered, so far.
721
+
722
+ After logging new simulation dates via the POST method
723
+ |HydPyServer.POST_register_simulationdates|, method
724
+ |HydPyServer.GET_query_simulationdates| returns them correctly:
725
+
726
+ >>> test("register_simulationdates", id_="0",
727
+ ... data=("firstdate_sim = 1996-01-01\\n"
728
+ ... "lastdate_sim = 1996-01-02"))
729
+ <BLANKLINE>
730
+ >>> test("query_simulationdates", id_="0")
731
+ firstdate_sim = 1996-01-01T00:00:00+01:00
732
+ lastdate_sim = 1996-01-02T00:00:00+01:00
733
+
734
+ Our initial call to the POST method |HydPyServer.POST_register_simulationdates| did
735
+ not affect the currently active simulation dates. We need to do this manually by
736
+ calling method |HydPyServer.GET_activate_simulationdates|:
737
+
738
+ >>> test("evaluate", data="lastdate = hydpy.pub.timegrids.sim.lastdate")
739
+ lastdate = Date("1996-01-06T00:00:00")
740
+ >>> test("activate_simulationdates", id_="0")
741
+ <BLANKLINE>
742
+ >>> test("evaluate", data="lastdate = hydpy.pub.timegrids.sim.lastdate")
743
+ lastdate = Date("1996-01-02 00:00:00")
744
+
745
+ Generally, passing a missing `id` while others are available results in error
746
+ messages like the following:
747
+
748
+ >>> test("activate_simulationdates", id_="1")
749
+ Traceback (most recent call last):
750
+ ...
751
+ urllib.error.HTTPError: HTTP Error 500: RuntimeError: While trying to execute \
752
+ method `GET_activate_simulationdates`, the following error occurred: Nothing \
753
+ registered under the id `1`. The available ids are: 0.
754
+
755
+ The logic of the parameter-related GET and POST methods is very similar to the one
756
+ of the simulation date-related methods discussed above. Method
757
+ |HydPyServer.POST_register_parameteritemvalues| registers new values of the
758
+ exchange items, and method |HydPyServer.GET_activate_parameteritemvalues| activates
759
+ them (assigns them to the relevant parameters):
760
+
761
+ >>> test("register_parameteritemvalues", id_="0",
762
+ ... data=("alpha = 3.0\\n"
763
+ ... "beta = 2.0\\n"
764
+ ... "lag = 1.0\\n"
765
+ ... "damp = 0.5\\n"
766
+ ... "sfcf_1 = 0.3\\n"
767
+ ... "sfcf_2 = 0.2\\n"
768
+ ... "sfcf_3 = 0.1\\n"
769
+ ... "k4 = 10.0\\n"))
770
+ <BLANKLINE>
771
+ >>> control = ("HydPyServer.state.hp.elements.land_dill_assl.model.parameters."
772
+ ... "control")
773
+ >>> test("evaluate",
774
+ ... data=(f"alpha = {control}.alpha\\n"
775
+ ... f"sfcf = {control}.sfcf"))
776
+ alpha = alpha(1.0)
777
+ sfcf = sfcf(1.1)
778
+
779
+ >>> test("activate_parameteritemvalues", id_="0")
780
+ <BLANKLINE>
781
+ >>> test("evaluate",
782
+ ... data=(f"alpha = {control}.alpha\\n"
783
+ ... f"sfcf = {control}.sfcf"))
784
+ alpha = alpha(3.0)
785
+ sfcf = sfcf(1.34283)
786
+
787
+ The list of exchange items must be complete:
788
+
789
+ >>> test("register_parameteritemvalues", id_="0",
790
+ ... data=("alpha = 3.0\\n"
791
+ ... "beta = 2.0"))
792
+ Traceback (most recent call last):
793
+ ...
794
+ urllib.error.HTTPError: HTTP Error 500: RuntimeError: While trying to execute \
795
+ method `POST_register_parameteritemvalues`, the following error occurred: A value for \
796
+ parameter item `lag` is missing.
797
+
798
+ Note that the related query method (|HydPyServer.GET_query_parameteritemvalues|)
799
+ returns the logged values of the |ChangeItem| objects instead of the (eventually
800
+ modified) values of the |Parameter| objects:
801
+
802
+ >>> test("query_parameteritemvalues", id_="0")
803
+ alpha = 3.0
804
+ beta = 2.0
805
+ lag = 1.0
806
+ damp = 0.5
807
+ sfcf_1 = 0.3
808
+ sfcf_2 = 0.2
809
+ sfcf_3 = 0.1
810
+ k4 = 10.0
811
+
812
+ The condition-related methods |HydPyServer.POST_register_conditionitemvalues|,
813
+ |HydPyServer.GET_activate_conditionitemvalues|, and
814
+ |HydPyServer.GET_query_conditionitemvalues| work like the parameter-related
815
+ methods described above:
816
+
817
+ >>> test("register_conditionitemvalues", id_="0",
818
+ ... data=("sm_lahn_leun = 246.0\\n"
819
+ ... "sm_lahn_marb = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]\\n"
820
+ ... "ic_lahn_leun = 642.0\\n"
821
+ ... "ic_lahn_marb = [13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]\\n"
822
+ ... "quh = 1.0\\n"))
823
+ <BLANKLINE>
824
+ >>> test("query_conditionitemvalues", id_="0")
825
+ ic_lahn_leun = 642.0
826
+ ic_lahn_marb = [13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
827
+ sm_lahn_leun = 246.0
828
+ sm_lahn_marb = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
829
+ quh = 1.0
830
+
831
+ Note the trimming of the too-high value for the state sequence |hland_states.SM| to
832
+ its highest possible value defined by control parameter |hland_control.FC|):
833
+
834
+ >>> for element in ("land_lahn_marb", "land_lahn_leun"):
835
+ ... path_element = f"HydPyServer.state.hp.elements.{element}"
836
+ ... path_sequences_model = f"{path_element}.model.sequences"
837
+ ... path_sequences_submodel = f"{path_element}.model.rconcmodel.sequences"
838
+ ... test("evaluate", # doctest: +ELLIPSIS
839
+ ... data=(f"sm = {path_sequences_model}.states.sm \\n"
840
+ ... f"quh = {path_sequences_submodel}.logs.quh"))
841
+ sm = sm(99.27505, ..., 142.84148)
842
+ quh = quh(0.0)
843
+ sm = sm(138.31396, ..., 164.63255)
844
+ quh = quh(0.7, 0.0)
845
+ >>> test("activate_conditionitemvalues", id_="0")
846
+ <BLANKLINE>
847
+ >>> for element in ("land_lahn_marb", "land_lahn_leun"):
848
+ ... path_element = f"HydPyServer.state.hp.elements.{element}"
849
+ ... path_sequences_model = f"{path_element}.model.sequences"
850
+ ... path_sequences_submodel = f"{path_element}.model.rconcmodel.sequences"
851
+ ... test("evaluate", # doctest: +ELLIPSIS
852
+ ... data=(f"sm = {path_sequences_model}.states.sm \\n"
853
+ ... f"quh = {path_sequences_submodel}.logs.quh"))
854
+ sm = sm(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0)
855
+ quh = quh(0.0)
856
+ sm = sm(197.0, 197.0, 197.0, 197.0, 197.0, 197.0, 197.0, 197.0, 197.0, 197.0)
857
+ quh = quh(1.0, 0.0)
858
+
859
+ The methods |HydPyServer.POST_register_inputitemvalues|,
860
+ |HydPyServer.GET_activate_inputitemvalues|, and
861
+ |HydPyServer.GET_query_inputitemvalues| always focus on the currently relevant
862
+ simulation time grid:
863
+
864
+ >>> test("update_inputitemvalues", id_="0")
865
+ <BLANKLINE>
866
+ >>> test("query_inputitemvalues", id_="0")
867
+ t_headwaters = [[0.0], [-0.7]]
868
+ >>> t = "HydPyServer.state.hp.elements.land_lahn_marb.model.sequences.inputs.t"
869
+ >>> test("evaluate", data=(f"t_series = {t}.series\\n"
870
+ ... f"t_simseries = {t}.simseries\\n"))
871
+ t_series = InfoArray([-0.7, -1.5, -4.6, -8.2, -8.7])
872
+ t_simseries = InfoArray([-0.7])
873
+
874
+ >>> test("register_inputitemvalues", id_="0",
875
+ ... data="t_headwaters = [[1.0], [2.0]]\\n")
876
+ <BLANKLINE>
877
+ >>> test("activate_inputitemvalues", id_="0")
878
+ <BLANKLINE>
879
+ >>> test("evaluate", data=(f"t_series = {t}.series\\n"
880
+ ... f"t_simseries = {t}.simseries\\n"))
881
+ t_series = InfoArray([ 2. , -1.5, -4.6, -8.2, -8.7])
882
+ t_simseries = InfoArray([2.])
883
+
884
+ The "official" way to gain information on modified parameters or conditions is to
885
+ use the method |HydPyServer.GET_query_getitemvalues|:
886
+
887
+ >>> test("query_getitemvalues", id_="0")
888
+ Traceback (most recent call last):
889
+ ...
890
+ urllib.error.HTTPError: HTTP Error 500: RuntimeError: While trying to execute \
891
+ method `GET_query_getitemvalues`, the following error occurred: Nothing registered \
892
+ under the id `0`. There is nothing registered, so far.
893
+
894
+ As the error message explains, we first need to fill the registry for the given
895
+ `id` parameter. Unlike the examples above, we do not do this by sending external
896
+ data via a POST request but by retrieving the server's currently active data. We
897
+ accomplish this task by calling the GET method
898
+ |HydPyServer.GET_update_getitemvalues|:
899
+
900
+ >>> test("update_getitemvalues", id_="0")
901
+ <BLANKLINE>
902
+ >>> test("query_getitemvalues", id_="0") # doctest: +ELLIPSIS
903
+ land_dill_assl_factors_contriarea = nan
904
+ land_dill_assl_fluxes_qt = nan
905
+ land_dill_assl_fluxes_qt_series = [nan]
906
+ land_dill_assl_states_sm = [185.13164, ...]
907
+ land_lahn_kalk_states_sm = [101.31248, ...]
908
+ land_lahn_leun_states_sm = [197.0, ..., 197.0]
909
+ land_lahn_marb_states_sm = [1.0, 2.0, ..., 12.0, 13.0]
910
+ land_lahn_kalk_states_sm_series = [[nan, ..., nan]]
911
+ dill_assl_nodes_sim_series = [nan]
912
+
913
+ Besides the "official" way for retrieving information (which we sometimes call the
914
+ "getitem style"), some sequences types (namely those derived from |FactorSequence|
915
+ and |FluxSequence|) also allow retrieving information in the so-called "setitem
916
+ style" via the methods |HydPyServer.GET_update_outputitemvalues| and
917
+ |HydPyServer.GET_query_outputitemvalues|:
918
+
919
+ >>> test("query_outputitemvalues", id_="0")
920
+ Traceback (most recent call last):
921
+ ...
922
+ urllib.error.HTTPError: HTTP Error 500: RuntimeError: While trying to execute \
923
+ method `GET_query_outputitemvalues`, the following error occurred: Nothing registered \
924
+ under the id `0`. There is nothing registered, so far.
925
+ >>> test("update_outputitemvalues", id_="0")
926
+ <BLANKLINE>
927
+ >>> test("query_outputitemvalues", id_="0")
928
+ swe_headwaters = [[nan], [nan]]
929
+
930
+ We now modify the parameter, condition, and input time series values again, but
931
+ this time in one step through calling |HydPyServer.POST_register_changeitemvalues|
932
+ and |HydPyServer.GET_activate_changeitemvalues|:
933
+
934
+ >>> test("register_changeitemvalues", id_="0",
935
+ ... data=("alpha = 1.0\\n"
936
+ ... "beta = 1.0\\n"
937
+ ... "lag = 0.0\\n"
938
+ ... "damp = 0.0\\n"
939
+ ... "sfcf_1 = 0.0\\n"
940
+ ... "sfcf_2 = 0.0\\n"
941
+ ... "sfcf_3 = 0.0\\n"
942
+ ... "k4 = 5.0\\n"
943
+ ... "ic_lahn_marb = 1.0\\n"
944
+ ... "ic_lahn_leun = 2.0\\n"
945
+ ... "sm_lahn_marb = 50.0\\n"
946
+ ... "sm_lahn_leun = 100.0\\n"
947
+ ... "quh = 0.0\\n"
948
+ ... "t_headwaters = [[-0.29884643], [-0.70539496]]\\n"))
949
+ <BLANKLINE>
950
+ >>> test("activate_changeitemvalues", id_="0")
951
+ <BLANKLINE>
952
+ >>> test("query_changeitemvalues", id_="0") # doctest: +ELLIPSIS
953
+ alpha = 1.0
954
+ beta = 1.0
955
+ lag = 0.0
956
+ damp = 0.0
957
+ sfcf_1 = 0.0
958
+ sfcf_2 = 0.0
959
+ sfcf_3 = 0.0
960
+ k4 = 5.0
961
+ t_headwaters = [[-0.29884...], [-0.70539...]]
962
+ ic_lahn_leun = 2.0
963
+ ic_lahn_marb = 1.0
964
+ sm_lahn_leun = 100.0
965
+ sm_lahn_marb = 50.0
966
+ quh = 0.0
967
+
968
+ Next, we trigger a simulation run by calling the GET method
969
+ |HydPyServer.GET_simulate|:
970
+
971
+ >>> test("simulate", id_="0")
972
+ <BLANKLINE>
973
+
974
+ Calling methods |HydPyServer.GET_update_getitemvalues| and
975
+ |HydPyServer.GET_query_getitemvalues| as well as methods
976
+ |HydPyServer.GET_update_outputitemvalues| and
977
+ |HydPyServer.GET_query_outputitemvalues| reveals how the simulation results:
978
+
979
+ >>> test("update_getitemvalues", id_="0")
980
+ <BLANKLINE>
981
+ >>> test("query_getitemvalues", id_="0") # doctest: +ELLIPSIS
982
+ land_dill_assl_factors_contriarea = 0.759579
983
+ land_dill_assl_fluxes_qt = 5.508952
984
+ ...
985
+ land_lahn_leun_states_sm = [100.341052, ..., 100.0]
986
+ ...
987
+ dill_assl_nodes_sim_series = [5.508952]
988
+ >>> test("update_outputitemvalues", id_="0")
989
+ <BLANKLINE>
990
+ >>> test("query_outputitemvalues", id_="0")
991
+ swe_headwaters = [[0.074231], [0.0]]
992
+
993
+ So far, we have explained how the *HydPy* server memorises different exchange item
994
+ values for different values of query parameter `id`. Complicating matters,
995
+ memorising condition values must also consider the relevant time point. You load
996
+ conditions for the simulation period's current start date with method
997
+ |HydPyServer.GET_load_internalconditions| and save them for the current end date
998
+ with method |HydPyServer.GET_save_internalconditions|. For example, we first save
999
+ the states calculated for the end time of the last simulation run (January 2):
1000
+
1001
+ >>> test("query_simulationdates", id_="0")
1002
+ firstdate_sim = 1996-01-01T00:00:00+01:00
1003
+ lastdate_sim = 1996-01-02T00:00:00+01:00
1004
+ >>> test("evaluate",
1005
+ ... data=f"sm_lahn2 = {path_sequences_model}.states.sm") # doctest: +ELLIPSIS
1006
+ sm_lahn2 = sm(100.341052, ..., 100.0)
1007
+ >>> test("save_internalconditions", id_="0")
1008
+ <BLANKLINE>
1009
+
1010
+ Calling method |HydPyServer.GET_load_internalconditions| without changing the
1011
+ simulation dates reloads the initial conditions for January 1, originally read from
1012
+ disk:
1013
+
1014
+ >>> test("load_internalconditions", id_="0")
1015
+ <BLANKLINE>
1016
+ >>> test("evaluate",
1017
+ ... data=f"sm_lahn2 = {path_sequences_model}.states.sm") # doctest: +ELLIPSIS
1018
+ sm_lahn2 = sm(138.31396, ..., 164.63255)
1019
+
1020
+ If we set the first date of the simulation period to January 2, method
1021
+ |HydPyServer.GET_load_internalconditions| loads the conditions we saved for
1022
+ January 2 previously:
1023
+
1024
+ >>> test("register_simulationdates", id_="0",
1025
+ ... data=("firstdate_sim = 1996-01-02\\n"
1026
+ ... "lastdate_sim = 1996-01-03"))
1027
+ <BLANKLINE>
1028
+ >>> test("activate_simulationdates", id_="0")
1029
+ <BLANKLINE>
1030
+ >>> test("load_internalconditions", id_="0")
1031
+ <BLANKLINE>
1032
+ >>> test("evaluate",
1033
+ ... data=f"sm_lahn2 = {path_sequences_model}.states.sm") # doctest: +ELLIPSIS
1034
+ sm_lahn2 = sm(100.341052, ..., 100.0)
1035
+
1036
+ Loading condition values for a specific time point requires saving them before:
1037
+
1038
+ >>> test("register_simulationdates", id_="0",
1039
+ ... data=("firstdate_sim = 1996-01-03\\n"
1040
+ ... "lastdate_sim = 1996-01-05"))
1041
+ <BLANKLINE>
1042
+ >>> test("activate_simulationdates", id_="0")
1043
+ <BLANKLINE>
1044
+ >>> test("load_internalconditions", id_="0")
1045
+ Traceback (most recent call last):
1046
+ ...
1047
+ urllib.error.HTTPError: HTTP Error 500: RuntimeError: While trying to execute \
1048
+ method `GET_load_internalconditions`, the following error occurred: Conditions for \
1049
+ ID `0` and time point `1996-01-03 00:00:00` are required, but have not been \
1050
+ calculated so far.
1051
+
1052
+ For example, when restarting data assimilation subsequent forecasting periods, you
1053
+ might need to get and set all internal conditions from the client side. Then, you
1054
+ have two options. The more efficient way relies on methods
1055
+ |HydPyServer.GET_query_internalconditions| and
1056
+ |HydPyServer.POST_register_internalconditions|. Method
1057
+ |HydPyServer.GET_query_internalconditions| returns the information registered for
1058
+ the end of the current simulation period. All data is within a single nested
1059
+ |dict| object (created by the |HydPy.conditions| property of class |HydPy|):
1060
+
1061
+ >>> test("register_simulationdates", id_="0",
1062
+ ... data=("firstdate_sim = 1996-01-01\\n"
1063
+ ... "lastdate_sim = 1996-01-02"))
1064
+ <BLANKLINE>
1065
+ >>> test("activate_simulationdates", id_="0")
1066
+ <BLANKLINE>
1067
+ >>> conditions = test("query_internalconditions", id_="0",
1068
+ ... return_result=True)[13:] # doctest: +ELLIPSIS
1069
+ conditions = {'land_dill_assl': {'model': {'states': {'ic': array([0.73040403, \
1070
+ 1.23040403, 0.73046025...
1071
+
1072
+ Due to the steps above, the returned dictionary agrees with the current state of
1073
+ the |HydPy| instance:
1074
+
1075
+ >>> sequences = f"HydPyServer.state.hp.elements.land_dill_assl.model.sequences"
1076
+ >>> test("evaluate",
1077
+ ... data=f"ic_dill_assl = {sequences}.states.ic") # doctest: +ELLIPSIS
1078
+ ic_dill_assl = ic(0.730404, 1.230404, 0.73046,...
1079
+
1080
+ To show that registering new internal conditions also works, we first convert the
1081
+ string representation of the data to actual Python objects by using Python's |eval|
1082
+ function. Therefore, we need to clarify that "array" means the array creation
1083
+ function |numpy.array| of |numpy|:
1084
+
1085
+ >>> import numpy
1086
+ >>> conditions = eval(conditions, {"array": numpy.array})
1087
+
1088
+ Next, we modify an arbitrary state and convert the dictionary back to a single-line
1089
+ string:
1090
+
1091
+ >>> conditions["land_dill_assl"]["model"]["states"]["ic"][:2] = 0.5, 2.0
1092
+ >>> conditions = str(conditions).replace("\\n", " ")
1093
+
1094
+ Now we can send the modified data back to the server by using the
1095
+ |HydPyServer.POST_register_internalconditions| method, which stores it for the
1096
+ start of the simulation period:
1097
+
1098
+ >>> test("register_internalconditions", id_="0", data=f"conditions = {conditions}")
1099
+ <BLANKLINE>
1100
+ >>> ic_dill_assl = ("self.state.conditions['0'][0]['land_dill_assl']['model']"
1101
+ ... "['states']['ic']")
1102
+ >>> test("evaluate",
1103
+ ... data=f"ic_dill_assl = {ic_dill_assl}") # doctest: +ELLIPSIS
1104
+ ic_dill_assl = array([0.5 , 2. , 0.73046025...
1105
+
1106
+ After calling method |HydPyServer.GET_load_internalconditions|, the freshly
1107
+ registered states are ready to be used by the next simulation run:
1108
+
1109
+ >>> test("load_internalconditions", id_="0")
1110
+ <BLANKLINE>
1111
+ >>> test("evaluate",
1112
+ ... data=f"ic_dill_assl = {sequences}.states.ic") # doctest: +ELLIPSIS
1113
+ ic_dill_assl = ic(0.5, 2.0, 0.73046,...
1114
+
1115
+ Keeping the internal conditions for multiple time points can use plenty of RAM.
1116
+ Use the GET method |HydPyServer.GET_deregister_internalconditions| to remove all
1117
+ conditions data available under the given `id` to avoid that:
1118
+
1119
+ >>> test("query_internalconditions", id_="0") # doctest: +ELLIPSIS
1120
+ conditions = {'land_dill_assl': {'model': {'states': {'ic': array([0.7304...
1121
+ >>> test("deregister_internalconditions", id_="0")
1122
+ <BLANKLINE>
1123
+ >>> test("query_internalconditions", id_="0")
1124
+ Traceback (most recent call last):
1125
+ ...
1126
+ urllib.error.HTTPError: HTTP Error 500: RuntimeError: While trying to execute \
1127
+ method `GET_query_internalconditions`, the following error occurred: No internal \
1128
+ conditions registered under the id `0` for `1996-01-02 00:00:00`.
1129
+
1130
+ Some algorithms provide new information about initial conditions and require
1131
+ information on how they evolve during a simulation. For such purposes, you can
1132
+ use method |HydPyServer.GET_update_conditionitemvalues| to store the current
1133
+ conditions under an arbitrary `id` and use method
1134
+ |HydPyServer.GET_query_conditionitemvalues| to query them later. Note that this
1135
+ approach so far only works when using |SetItem| objects that modify their target
1136
+ sequence on the `device` or `subunit` level (please tell us if you encounter other
1137
+ relevant use-cases):
1138
+
1139
+ >>> test("update_conditionitemvalues", id_="0")
1140
+ <BLANKLINE>
1141
+ >>> test("query_conditionitemvalues", id_="0") # doctest: +ELLIPSIS
1142
+ ic_lahn_leun = [0.955701]
1143
+ ic_lahn_marb = [0.7421...]
1144
+ sm_lahn_leun = [100.1983...]
1145
+ sm_lahn_marb = [49.9304...]
1146
+ quh = [0.000395]
1147
+
1148
+ The second option for handling multiple "simultaneous" initial conditions is
1149
+ telling the *HydPy* server to read them from and write them to disk, which is
1150
+ easier but often less efficient due to higher IO activity. Use methods
1151
+ |HydPyServer.GET_load_conditions| and |HydPyServer.GET_save_conditions| for this
1152
+ purpose. Reading from or writing to different directories than those defined in
1153
+ `multiple_runs.xml` requires registering them beforehand. If we, for example,
1154
+ create a new empty directory with method
1155
+ |HydPyServer.POST_register_inputconditiondir|, loading conditions from it must
1156
+ fail:
1157
+
1158
+ >>> test("register_inputconditiondir", id_="0", data="inputconditiondir = new")
1159
+ <BLANKLINE>
1160
+ >>> test("load_conditions", id_="0") # doctest: +ELLIPSIS
1161
+ Traceback (most recent call last):
1162
+ ...
1163
+ urllib.error.HTTPError: HTTP Error 500: FileNotFoundError: While trying to \
1164
+ execute method `GET_load_conditions`, the following error occurred: While trying to \
1165
+ load the initial conditions of element `land_dill_assl`, the following error occurred: \
1166
+ [Errno 2] No such file or directory: ...land_dill_assl.py'
1167
+
1168
+ >>> test("register_outputconditiondir", id_="0", data="outputconditiondir = new")
1169
+ <BLANKLINE>
1170
+ >>> test("save_conditions", id_="0")
1171
+ <BLANKLINE>
1172
+
1173
+ Hence, we better first write suitable conditions into the new directory:
1174
+
1175
+ >>> lz_dill_assl = "self.state.hp.elements.land_dill_assl.model.sequences.states.lz"
1176
+ >>> test("evaluate", data=f"lz_dill_assl = {lz_dill_assl}") # doctest: +ELLIPSIS
1177
+ lz_dill_assl = lz(9.493...)
1178
+
1179
+ To prove reading and writing conditions works, we first set the current value of
1180
+ sequence |hland_states.LZ| of catchment "Dill" to zero:
1181
+
1182
+ >>> test("evaluate", data=f"nothing = {lz_dill_assl}(0.0)")
1183
+ nothing = None
1184
+ >>> test("evaluate", data=f"lz_dill_assl = {lz_dill_assl}")
1185
+ lz_dill_assl = lz(0.0)
1186
+
1187
+ As expected, applying |HydPyServer.GET_load_conditions| on the previously written
1188
+ data resets the value of |hland_states.LZ|:
1189
+
1190
+ >>> test("load_conditions", id_="0")
1191
+ <BLANKLINE>
1192
+ >>> test("evaluate", data=f"lz_dill_assl = {lz_dill_assl}") # doctest: +ELLIPSIS
1193
+ lz_dill_assl = lz(9.493...)
1194
+
1195
+ Use the GET methods |HydPyServer.GET_query_inputconditiondir| and
1196
+ |HydPyServer.GET_deregister_inputconditiondir| to query or remove the currently
1197
+ registered input condition directory:
1198
+
1199
+ >>> test("query_inputconditiondir", id_="0")
1200
+ inputconditiondir = new
1201
+ >>> test("deregister_inputconditiondir", id_="0")
1202
+ <BLANKLINE>
1203
+ >>> test("query_inputconditiondir", id_="0")
1204
+ Traceback (most recent call last):
1205
+ ...
1206
+ urllib.error.HTTPError: HTTP Error 500: RuntimeError: While trying to execute \
1207
+ method `GET_query_inputconditiondir`, the following error occurred: Nothing \
1208
+ registered under the id `0`. There is nothing registered, so far.
1209
+
1210
+ Use the GET methods |HydPyServer.GET_query_outputconditiondir| and
1211
+ |HydPyServer.GET_deregister_outputconditiondir| to query or remove the currently
1212
+ registered output condition directory:
1213
+
1214
+ >>> test("query_outputconditiondir", id_="0")
1215
+ outputconditiondir = new
1216
+ >>> test("deregister_outputconditiondir", id_="0")
1217
+ <BLANKLINE>
1218
+ >>> test("query_outputconditiondir", id_="0")
1219
+ Traceback (most recent call last):
1220
+ ...
1221
+ urllib.error.HTTPError: HTTP Error 500: RuntimeError: While trying to execute \
1222
+ method `GET_query_outputconditiondir`, the following error occurred: Nothing \
1223
+ registered under the id `0`. There is nothing registered, so far.
1224
+
1225
+ Above, we explained the recommended way to query the initial values of all or a
1226
+ subgroup of the available exchange items. Alternatively, you can first register
1227
+ the initial values and query them later, which is a workaround for retrieving
1228
+ initial and intermediate values with the same HTTP request (an `OpenDA`_
1229
+ requirement):
1230
+
1231
+ >>> test("register_initialitemvalues", id_="1")
1232
+ <BLANKLINE>
1233
+ >>> test("query_itemvalues", id_="1") # doctest: +ELLIPSIS
1234
+ alpha = 2.0
1235
+ beta = 1.0
1236
+ lag = 5.0
1237
+ damp = 0.5
1238
+ sfcf_1 = 0.3
1239
+ sfcf_2 = 0.2
1240
+ sfcf_3 = [0.1, 0.2, 0.1, 0.2, 0.1, 0.2, 0.1, 0.2, 0.1, 0.2, 0.1, 0.2, 0.2, 0.2]
1241
+ k4 = 10.0
1242
+ t_headwaters = [[0.0, -0.5, -2.4, -6.8, -7.8], [-0.7, -1.5, -4.6, -8.2, -8.7]]
1243
+ ic_lahn_leun = [1.18494...]
1244
+ ic_lahn_marb = [0.96404...]
1245
+ sm_lahn_leun = [123.0]
1246
+ sm_lahn_marb = [110.0, 120.0, 130.0, 140.0, 150.0, 160.0, 170.0, 180.0, 190.0, \
1247
+ 200.0, 210.0, 220.0, 230.0]
1248
+ quh = [10.0]
1249
+ swe_headwaters = [[nan, nan, nan, nan, nan], [nan, nan, nan, nan, nan]]
1250
+ land_dill_assl_factors_contriarea = nan
1251
+ land_dill_assl_fluxes_qt = nan
1252
+ land_dill_assl_fluxes_qt_series = [nan, nan, nan, nan, nan]
1253
+ land_dill_assl_states_sm = [185.13164...]
1254
+ land_lahn_kalk_states_sm = [101.31248...]
1255
+ land_lahn_leun_states_sm = [138.31396...]
1256
+ land_lahn_marb_states_sm = [99.27505...]
1257
+ land_lahn_kalk_states_sm_series = [[nan, ...], [nan, ...], ..., [nan, ...]]
1258
+ dill_assl_nodes_sim_series = [nan, nan, nan, nan, nan]
1259
+
1260
+ In contrast to running a single simulation via method |run_simulation|, the *HydPy*
1261
+ server does (usually) not write calculated time series automatically. Instead, one
1262
+ must manually call method |HydPyServer.GET_save_allseries|:
1263
+
1264
+ >>> test("save_allseries", id_="0")
1265
+ <BLANKLINE>
1266
+
1267
+ According to the fixed configuration of `multiple_runs.xml`,
1268
+ |HydPyServer.GET_save_allseries| wrote averaged soil moisture values into the
1269
+ directory `mean_sm`:
1270
+
1271
+ >>> import netCDF4
1272
+ >>> from hydpy import print_vector
1273
+ >>> filepath = "HydPy-H-Lahn/series/mean_sm/hland_96_state_sm_mean.nc"
1274
+ >>> with TestIO(), netCDF4.Dataset(filepath) as ncfile:
1275
+ ... print_vector(ncfile["hland_96_state_sm_mean"][:, 0])
1276
+ 211.467386, 0.0, 0.0, 0.0, 0.0
1277
+
1278
+ To save the results of subsequent simulations without overwriting the previous
1279
+ ones, change the current series writer directory by the GET method
1280
+ |HydPyServer.POST_register_serieswriterdir|:
1281
+
1282
+ >>> test("register_serieswriterdir", id_="0", data="serieswriterdir = sm_averaged")
1283
+ <BLANKLINE>
1284
+ >>> test("save_allseries", id_="0")
1285
+ <BLANKLINE>
1286
+ >>> filepath = "HydPy-H-Lahn/series/sm_averaged/hland_96_state_sm_mean.nc"
1287
+ >>> with TestIO(), netCDF4.Dataset(filepath) as ncfile:
1288
+ ... print_vector(ncfile["hland_96_state_sm_mean"][:, 0])
1289
+ 211.467386, 0.0, 0.0, 0.0, 0.0
1290
+
1291
+ |HydPyServer.GET_deregister_serieswriterdir| removes the currently set directory
1292
+ from the registry so that the HydPy server falls back to the
1293
+ configuration of `multiple_runs.xml`:
1294
+
1295
+ >>> test("query_serieswriterdir", id_="0")
1296
+ serieswriterdir = sm_averaged
1297
+ >>> test("deregister_serieswriterdir", id_="0")
1298
+ <BLANKLINE>
1299
+ >>> test("query_serieswriterdir", id_="0")
1300
+ Traceback (most recent call last):
1301
+ ...
1302
+ urllib.error.HTTPError: HTTP Error 500: RuntimeError: While trying to execute \
1303
+ method `GET_query_serieswriterdir`, the following error occurred: Nothing registered \
1304
+ under the id `0`. There is nothing registered, so far.
1305
+
1306
+ The same holds for time series to be written "just in time" during simulation runs.
1307
+ The `temperature` writer in `multiple_runs.xml` select the `jit` mode. This
1308
+ setting triggered that the *HydPy* server wrote the time series of sequences
1309
+ |hland_inputs.T| and |evap_inputs.NormalAirTemperature| to the directory
1310
+ `temperature` during the last simulation:
1311
+
1312
+ >>> filepath = "HydPy-H-Lahn/series/temperature/hland_96_input_t.nc"
1313
+ >>> with TestIO(), netCDF4.Dataset(filepath) as ncfile:
1314
+ ... print_vector(ncfile["hland_96_input_t"][:, 0])
1315
+ -0.298846, 0.0, 0.0, 0.0, 0.0
1316
+
1317
+ The input sequences |hland_inputs.P| and |evap_inputs.NormalEvapotranspiration| are
1318
+ instead reading their time series "just in time" (reading and writing data for the
1319
+ same |IOSequence| object is not supported). We query the last read value of
1320
+ |evap_inputs.NormalEvapotranspiration| for the Dill catchment:
1321
+
1322
+ >>> submodel = ("HydPyServer.state.hp.elements.land_dill_assl.model.aetmodel."
1323
+ ... "petmodel")
1324
+ >>> net = f"{submodel}.sequences.inputs.normalevapotranspiration"
1325
+ >>> test("evaluate", data=f"net_dill_assl = {net}") # doctest: +ELLIPSIS
1326
+ net_dill_assl = normalevapotranspiration(0.3)
1327
+
1328
+ We can change the series writer directory before starting another simulation run to
1329
+ write the time series of |hland_inputs.T| and |evap_inputs.NormalAirTemperature| to
1330
+ another
1331
+ directory:
1332
+
1333
+ >>> test("register_serieswriterdir", id_="0", data="serieswriterdir = temp")
1334
+ <BLANKLINE>
1335
+ >>> test("simulate", id_="0")
1336
+ <BLANKLINE>
1337
+ >>> filepath = "HydPy-H-Lahn/series/temp/hland_96_input_t.nc"
1338
+ >>> with TestIO(), netCDF4.Dataset(filepath) as ncfile:
1339
+ ... print_vector(ncfile["hland_96_input_t"][:, 0])
1340
+ -0.298846, 0.0, 0.0, 0.0, 0.0
1341
+
1342
+ The "just in time" reading of the series of |hland_inputs.P| and
1343
+ |evap_inputs.NormalEvapotranspiration| still worked, showing the registered series
1344
+ directory "temp" only applied for writing data:
1345
+
1346
+ >>> test("evaluate", data=f"net_dill_assl = {net}") # doctest: +ELLIPSIS
1347
+ net_dill_assl = normalevapotranspiration(0.3)
1348
+
1349
+ Changing the series reader directory works as explained for the series writer
1350
+ directory. After setting it to an empty folder, |HydPyServer.GET_load_allseries|
1351
+ and |HydPyServer.GET_simulate| cannot find suitable files and report this problem:
1352
+
1353
+ >>> test("register_seriesreaderdir", id_="0", data="seriesreaderdir = no_data")
1354
+ <BLANKLINE>
1355
+ >>> test("query_seriesreaderdir", id_="0")
1356
+ seriesreaderdir = no_data
1357
+
1358
+ >>> test("load_allseries", id_="0") # doctest: +ELLIPSIS
1359
+ Traceback (most recent call last):
1360
+ ...
1361
+ urllib.error.HTTPError: HTTP Error 500: FileNotFoundError: While trying to \
1362
+ execute method `GET_load_allseries`, the following error occurred: While trying to \
1363
+ load the time series data of sequence `t` of element `land_dill_assl`, the following \
1364
+ error occurred: [Errno 2] No such file or directory: \
1365
+ ...land_dill_assl_hland_96_input_t.asc'
1366
+
1367
+ >>> test("simulate", id_="0") # doctest: +ELLIPSIS
1368
+ Traceback (most recent call last):
1369
+ ...
1370
+ urllib.error.HTTPError: HTTP Error 500: FileNotFoundError: While trying to \
1371
+ execute method `GET_simulate`, the following error occurred: While trying to prepare \
1372
+ NetCDF files for reading or writing data "just in time" during the current simulation \
1373
+ run, the following error occurred: No file `...hland_96_input_p.nc` available for \
1374
+ reading.
1375
+
1376
+ After deregistering the "no_data" directory, both methods work again:
1377
+
1378
+ >>> test("deregister_seriesreaderdir", id_="0")
1379
+ <BLANKLINE>
1380
+ >>> test("query_seriesreaderdir", id_="0")
1381
+ Traceback (most recent call last):
1382
+ ...
1383
+ urllib.error.HTTPError: HTTP Error 500: RuntimeError: While trying to execute \
1384
+ method `GET_query_seriesreaderdir`, the following error occurred: Nothing registered \
1385
+ under the id `0`. There is nothing registered, so far.
1386
+
1387
+ >>> test("load_allseries", id_="0")
1388
+ <BLANKLINE>
1389
+ >>> test("simulate", id_="0")
1390
+ <BLANKLINE>
1391
+
1392
+ As described for time series, one must explicitly pass (comparable) requests to
1393
+ the *HydPy* Server to let it write parameter control files. The control files
1394
+ reflect the current parameter values of all model instances:
1395
+
1396
+ >>> test("register_outputcontroldir", id_="0", data="outputcontroldir = calibrated")
1397
+ <BLANKLINE>
1398
+ >>> test("query_outputcontroldir", id_="0")
1399
+ outputcontroldir = calibrated
1400
+
1401
+ >>> test("save_controls", id_="0")
1402
+ <BLANKLINE>
1403
+ >>> with TestIO(), open("HydPy-H-Lahn/control/calibrated/"
1404
+ ... "land_dill_assl.py") as file_:
1405
+ ... print(file_.read()) # doctest: +ELLIPSIS
1406
+ from hydpy.models.hland_96 import *
1407
+ from hydpy.models import evap_aet_hbv96
1408
+ from hydpy.models import evap_pet_hbv96
1409
+ from hydpy.models import rconc_uh
1410
+ <BLANKLINE>
1411
+ simulationstep("1d")
1412
+ parameterstep("1d")
1413
+ ...
1414
+ beta(1.0)
1415
+ ...
1416
+
1417
+ >>> parameterstep = "hydpy.pub.options.parameterstep"
1418
+ >>> simulationstep = "hydpy.pub.options.simulationstep"
1419
+ >>> beta = ("HydPyServer.state.hp.elements.land_dill_assl.model.parameters."
1420
+ ... "control.beta")
1421
+ >>> test("evaluate", data=(f"simulationstep = {simulationstep}\\n"
1422
+ ... f"parameterstep = {parameterstep}\\n"
1423
+ ... f"beta = {beta}"))
1424
+ simulationstep = Period("1d")
1425
+ parameterstep = Period("1d")
1426
+ beta = beta(1.0)
1427
+
1428
+ >>> test("deregister_outputcontroldir", id_="0")
1429
+ <BLANKLINE>
1430
+ >>> test("query_outputcontroldir", id_="0")
1431
+ Traceback (most recent call last):
1432
+ ...
1433
+ urllib.error.HTTPError: HTTP Error 500: RuntimeError: While trying to execute \
1434
+ method `GET_query_outputcontroldir`, the following error occurred: Nothing registered \
1435
+ under the id `0`. There is nothing registered, so far.
1436
+
1437
+ To close the *HydPy* server, call |HydPyServer.GET_close_server|:
1438
+
1439
+ >>> test("close_server")
1440
+ <BLANKLINE>
1441
+ >>> process.kill()
1442
+ >>> _ = process.communicate()
1443
+ """
1444
+
1445
+ # pylint: disable=invalid-name
1446
+ # due to "GET" and "POST" method names in accordance with BaseHTTPRequestHandler
1447
+
1448
+ server: _HTTPServerBase
1449
+ state: ClassVar[ServerState]
1450
+ extensions_map: ClassVar[dict[str, str]]
1451
+ _requesttype: Literal["GET", "POST"]
1452
+ _statuscode: Literal[200, 400, 500]
1453
+ _inputs: dict[str, str]
1454
+ _outputs: dict[str, object]
1455
+
1456
+ def do_GET(self) -> None:
1457
+ """Select and apply the currently requested GET method."""
1458
+ self._requesttype = "GET"
1459
+ self._do_get_or_post()
1460
+
1461
+ def do_POST(self) -> None:
1462
+ """Select and apply the currently requested POST method."""
1463
+ self._requesttype = "POST"
1464
+ self._do_get_or_post()
1465
+
1466
+ def _do_get_or_post(self) -> None:
1467
+ self._statuscode = 200
1468
+ try:
1469
+ if self._requesttype == "POST":
1470
+ self._prepare_inputs()
1471
+ self._outputs = collections.OrderedDict()
1472
+ method = self._get_method(self._methodname)
1473
+ self._apply_method(method)
1474
+ self._write_output()
1475
+ except BaseException as exc:
1476
+ if self._statuscode not in (200, 400):
1477
+ self._statuscode = 500
1478
+ self.send_error(self._statuscode, f"{type(exc).__name__}: {exc}")
1479
+
1480
+ def _prepare_inputs(self) -> None:
1481
+ content_length = int(self.headers["Content-Length"])
1482
+ string = str(self.rfile.read(content_length), encoding="utf-8")
1483
+ self._inputs = collections.OrderedDict()
1484
+ for line in string.split("\n"):
1485
+ try:
1486
+ line = line.strip()
1487
+ if line:
1488
+ key, value = line.split("=")
1489
+ self._inputs[key.strip()] = value.strip()
1490
+ except BaseException as exc:
1491
+ self._statuscode = 400
1492
+ raise RuntimeError(
1493
+ f"The POST method `{self._externalname}` received a wrongly "
1494
+ f"formated data body. The following line has been extracted but "
1495
+ f"cannot be further processed: `{line}`."
1496
+ ) from exc
1497
+
1498
+ @property
1499
+ def _id(self) -> ID:
1500
+ return self._get_queryparameter("id")
1501
+
1502
+ def _get_queryparameter(self, name: str) -> ID:
1503
+ query = urllib.parse.urlparse(self.path).query
1504
+ try:
1505
+ return cast(ID, urllib.parse.parse_qs(query)[name][0])
1506
+ except KeyError:
1507
+ self._statuscode = 400
1508
+ raise RuntimeError(
1509
+ f"For the {self._requesttype} method `{self._externalname}` no query "
1510
+ f"parameter `{name}` is given."
1511
+ ) from None
1512
+
1513
+ @property
1514
+ def _externalname(self) -> str:
1515
+ return urllib.parse.urlparse(self.path).path[1:]
1516
+
1517
+ @property
1518
+ def _methodname(self) -> str:
1519
+ return f"{self._requesttype}_{self._externalname}"
1520
+
1521
+ def _get_method(self, name: str) -> types.MethodType:
1522
+ try:
1523
+ method = getattr(self, name)
1524
+ assert isinstance(method, types.MethodType)
1525
+ return method
1526
+ except AttributeError:
1527
+ self._statuscode = 400
1528
+ raise RuntimeError(f"No method `{name}` available.") from None
1529
+
1530
+ def _apply_method(self, method: types.MethodType) -> None:
1531
+ try:
1532
+ method()
1533
+ except BaseException:
1534
+ self._statuscode = 500
1535
+ objecttools.augment_excmessage(
1536
+ f"While trying to execute method `{method.__name__}`"
1537
+ )
1538
+
1539
+ def _write_output(self) -> None:
1540
+ string = "\n".join(f"{key} = {value}" for key, value in self._outputs.items())
1541
+ bstring = bytes(string, encoding="utf-8")
1542
+ self.send_response(self._statuscode)
1543
+ self.send_header("Content-type", "text/html")
1544
+ self.end_headers()
1545
+ self.wfile.write(bstring)
1546
+
1547
+ def GET_execute(self) -> None:
1548
+ """Execute an arbitrary number of GET methods.
1549
+
1550
+ The method names must be passed as query parameters, as explained in the main
1551
+ documentation on module |servertools|.
1552
+ """
1553
+ self._execute()
1554
+
1555
+ def POST_execute(self) -> None:
1556
+ """Execute an arbitrary number of POST and GET methods.
1557
+
1558
+ The method names must be passed as query parameters, as explained in the main
1559
+ documentation on module |servertools|.
1560
+ """
1561
+ self._execute()
1562
+
1563
+ def _execute(self) -> None:
1564
+ for name in self._get_queryparameter("methods").split(","):
1565
+ self._apply_method(self._get_method(name))
1566
+
1567
+ def POST_evaluate(self) -> None:
1568
+ """Evaluate any valid Python expression with the *HydPy* server process and get
1569
+ its result.
1570
+
1571
+ Method |HydPyServer.POST_evaluate| serves to test and debug, primarily. The
1572
+ main documentation on class |HydPyServer| explains its usage.
1573
+
1574
+ For safety purposes, method |HydPyServer.POST_evaluate| only works if you start
1575
+ the *HydPy* Server in debug mode by writing "debugging=enable", as we do in the
1576
+ examples of the main documentation on class |HydPyServer|. When not working in
1577
+ debug mode, invoking this method results in the following error message:
1578
+
1579
+ >>> from hydpy.core.testtools import prepare_full_example_1
1580
+ >>> prepare_full_example_1()
1581
+ >>> from hydpy import run_subprocess, TestIO
1582
+ >>> with TestIO():
1583
+ ... process = run_subprocess(
1584
+ ... "hyd.py start_server 8080 HydPy-H-Lahn multiple_runs_alpha.xml",
1585
+ ... blocking=False, verbose=False)
1586
+ ... _ = run_subprocess("hyd.py await_server 8080 10", verbose=False)
1587
+ >>> from urllib import request
1588
+ >>> request.urlopen("http://127.0.0.1:8080/evaluate", data=b"")
1589
+ Traceback (most recent call last):
1590
+ ...
1591
+ urllib.error.HTTPError: HTTP Error 500: RuntimeError: While trying to execute \
1592
+ method `POST_evaluate`, the following error occurred: You can only use the POST \
1593
+ method `evaluate` if you have started the `HydPy Server` in debugging mode.
1594
+
1595
+ >>> _ = request.urlopen("http://127.0.0.1:8080/close_server")
1596
+ >>> process.kill()
1597
+ >>> _ = process.communicate()
1598
+ """
1599
+ if not self.server.debugmode:
1600
+ raise RuntimeError(
1601
+ "You can only use the POST method `evaluate` if you have started the "
1602
+ "`HydPy Server` in debugging mode."
1603
+ )
1604
+ for name, value in self._inputs.items():
1605
+ result = eval(value)
1606
+ self._outputs[name] = objecttools.flatten_repr(result)
1607
+
1608
+ def GET_status(self) -> None:
1609
+ """Return "status = ready" as soon as possible."""
1610
+ self._outputs["status"] = "ready"
1611
+
1612
+ def GET_version(self) -> None:
1613
+ """Return Hydpy's version number."""
1614
+ self._outputs["version"] = hydpy.__version__
1615
+
1616
+ def GET_close_server(self) -> None:
1617
+ """Stop and close the *HydPy* server."""
1618
+
1619
+ def _close_server() -> None:
1620
+ self.server.shutdown()
1621
+ self.server.server_close()
1622
+
1623
+ shutter = threading.Thread(target=_close_server)
1624
+ shutter.start()
1625
+
1626
+ @staticmethod
1627
+ def _get_query_itemtype(item: itemtools.ExchangeItem) -> str:
1628
+ if item.targetspecs.series:
1629
+ return f"TimeSeries{item.ndim-1}D"
1630
+ return f"Double{item.ndim}D"
1631
+
1632
+ def GET_query_itemtypes(self) -> None:
1633
+ """Get the types of all current exchange items."""
1634
+ self.GET_query_changeitemtypes()
1635
+ self.GET_query_outputitemtypes()
1636
+ self.GET_query_getitemtypes()
1637
+
1638
+ def GET_query_changeitemtypes(self) -> None:
1639
+ """Get the types of all current exchange items supposed to change the values of
1640
+ |Parameter|, |StateSequence|, or |LogSequence| objects."""
1641
+ self.GET_query_parameteritemtypes()
1642
+ self.GET_query_inputitemtypes()
1643
+ self.GET_query_conditionitemtypes()
1644
+
1645
+ def GET_query_parameteritemtypes(self) -> None:
1646
+ """Get the types of all current exchange items supposed to change the values of
1647
+ |Parameter| objects."""
1648
+ for item in self.state.parameteritems:
1649
+ self._outputs[item.name] = self._get_query_itemtype(item)
1650
+
1651
+ def GET_query_inputitemtypes(self) -> None:
1652
+ """Get the types of all current exchange items supposed to change the series of
1653
+ |InputSequence| objects."""
1654
+ for item in self.state.inputitems:
1655
+ self._outputs[item.name] = self._get_query_itemtype(item)
1656
+
1657
+ def GET_query_conditionitemtypes(self) -> None:
1658
+ """Get the types of all current exchange items supposed to change the values of
1659
+ |StateSequence| or |LogSequence| objects."""
1660
+ for item in self.state.conditionitems:
1661
+ self._outputs[item.name] = self._get_query_itemtype(item)
1662
+
1663
+ def GET_query_outputitemtypes(self) -> None:
1664
+ """Get the types of all current exchange items supposed to return the values or
1665
+ series of |FactorSequence| or |FluxSequence| objects in the "setitem style"."""
1666
+ for item in self.state.outputitems:
1667
+ self._outputs[item.name] = self._get_query_itemtype(item)
1668
+
1669
+ def GET_query_getitemtypes(self) -> None:
1670
+ """Get the types of all current exchange items supposed to return the values of
1671
+ |Parameter| or |Sequence_| objects or the time series of |IOSequence|
1672
+ objects in the "getitem style"."""
1673
+ for item in self.state.getitems:
1674
+ type_ = self._get_query_itemtype(item)
1675
+ for name, _ in item.yield_name2value():
1676
+ self._outputs[name] = type_
1677
+
1678
+ def GET_query_itemsubnames(self) -> None:
1679
+ """Get names (suitable as IDs) describing the individual values of all current
1680
+ exchange objects."""
1681
+ self.GET_query_changeitemsubnames()
1682
+ self.GET_query_outputitemnames()
1683
+ self.GET_query_getitemsubnames()
1684
+
1685
+ def GET_query_changeitemsubnames(self) -> None:
1686
+ """Get names (suitable as IDs) describing the individual values of all current
1687
+ exchange objects supposed to change the values of |Parameter|, |StateSequence|,
1688
+ or |LogSequence| objects."""
1689
+ self.GET_query_parameteritemnames()
1690
+ self.GET_query_inputitemnames()
1691
+ self.GET_query_conditionitemnames()
1692
+
1693
+ def GET_query_parameteritemnames(self) -> None:
1694
+ """Get names (suitable as IDs) describing the individual values of all current
1695
+ exchange objects supposed to change the values of |Parameter| objects."""
1696
+ self._query_changeitemsubnames(self.state.parameteritems)
1697
+
1698
+ def GET_query_inputitemnames(self) -> None:
1699
+ """Get names (suitable as IDs) describing the individual values of all current
1700
+ exchange objects supposed to change the values of |InputSequence| objects."""
1701
+ self._query_changeitemsubnames(self.state.inputitems)
1702
+
1703
+ def GET_query_conditionitemnames(self) -> None:
1704
+ """Get names (suitable as IDs) describing the individual values of all current
1705
+ exchange objects supposed to change the values of |StateSequence| or
1706
+ |LogSequence| objects."""
1707
+ self._query_changeitemsubnames(self.state.conditionitems)
1708
+
1709
+ def GET_query_outputitemnames(self) -> None:
1710
+ """Get names (suitable as IDs) describing the individual values of all current
1711
+ exchange objects supposed to return the values of or series of |FactorSequence|
1712
+ or |FluxSequence| objects in the "setitem style"."""
1713
+ self._query_changeitemsubnames(self.state.outputitems)
1714
+
1715
+ def GET_query_getitemsubnames(self) -> None:
1716
+ """Get names (suitable as IDs) describing the individual values of all current
1717
+ exchange objects supposed to return the values of |Parameter| or |Sequence_|
1718
+ objects or the time series of |IOSequence| objects in the "getitem style"."""
1719
+ for item in self.state.getitems:
1720
+ for name, subnames in item.yield_name2subnames():
1721
+ self._outputs[name] = subnames
1722
+
1723
+ def _query_changeitemsubnames(self, items: Iterable[itemtools.ChangeItem]) -> None:
1724
+ for item in items:
1725
+ subnames = item.subnames
1726
+ if subnames is None:
1727
+ self._outputs[item.name] = "*global*"
1728
+ else:
1729
+ self._outputs[item.name] = f"[{', '.join(subnames)}]"
1730
+
1731
+ def GET_query_initialitemvalues(self) -> None:
1732
+ """Get the initial values of all current exchange items."""
1733
+ self.GET_query_initialchangeitemvalues()
1734
+ self.GET_query_initialoutputitemvalues()
1735
+ self.GET_query_initialgetitemvalues()
1736
+
1737
+ def GET_register_initialitemvalues(self) -> None:
1738
+ """Register the initial values of all current exchange items under the given
1739
+ `id`.
1740
+
1741
+ Implemented as a workaround to support `OpenDA`_. Better use method
1742
+ |HydPyServer.GET_query_initialitemvalues|.
1743
+ """
1744
+ self.GET_register_initialchangeitemvalues()
1745
+ self.GET_register_initialoutputitemvalues()
1746
+ self.GET_register_initialgetitemvalues()
1747
+
1748
+ def GET_query_initialchangeitemvalues(self) -> None:
1749
+ """Get the initial values of all current exchange items supposed to change the
1750
+ values of |Parameter|, |InputSequence|, |StateSequence|, or |LogSequence|
1751
+ objects."""
1752
+ self.GET_query_initialparameteritemvalues()
1753
+ self.GET_query_initialinputitemvalues()
1754
+ self.GET_query_initialconditionitemvalues()
1755
+
1756
+ def GET_register_initialchangeitemvalues(self) -> None:
1757
+ """Register the initial values of all current exchange items supposed to change
1758
+ the values of |Parameter|, |InputSequence|, |StateSequence|, or |LogSequence|
1759
+ objects under the given `id`.
1760
+
1761
+ Implemented as a workaround to support `OpenDA`_. Better use method
1762
+ |HydPyServer.GET_query_initialchangeitemvalues|.
1763
+ """
1764
+ self.GET_register_initialparameteritemvalues()
1765
+ self.GET_register_initialinputitemvalues()
1766
+ self.GET_register_initialconditionitemvalues()
1767
+
1768
+ @staticmethod
1769
+ def _array2output(values: float | VectorInputObject | MatrixInputObject) -> str:
1770
+ # duck-typing for simplicity:
1771
+ try:
1772
+ try:
1773
+ return objecttools.assignrepr_list2(
1774
+ values, prefix="" # type: ignore[arg-type]
1775
+ ).replace("\n", "")
1776
+ except TypeError:
1777
+ return objecttools.repr_list(values) # type: ignore[arg-type]
1778
+ except TypeError:
1779
+ return objecttools.repr_(values)
1780
+
1781
+ def GET_query_initialparameteritemvalues(self) -> None:
1782
+ """Get the initial values of all current exchange items supposed to change the
1783
+ values of |Parameter| objects."""
1784
+ for name, value in self.state.initialparameteritemvalues.items():
1785
+ self._outputs[name] = self._array2output(value)
1786
+
1787
+ def GET_register_initialparameteritemvalues(self) -> None:
1788
+ """Register the initial values of all current exchange items supposed to change
1789
+ the values of |Parameter| objects under the given `id`.
1790
+
1791
+ Implemented as a workaround to support `OpenDA`_. Better use method
1792
+ |HydPyServer.GET_query_initialparameteritemvalues|.
1793
+ """
1794
+ state = self.state
1795
+ state.parameteritemvalues[self._id] = state.initialparameteritemvalues.copy()
1796
+
1797
+ def GET_query_initialinputitemvalues(self) -> None:
1798
+ """Get the initial values of all current exchange items supposed to change the
1799
+ series of |InputSequence| objects."""
1800
+ for name, value in self.state.initialinputitemvalues.items():
1801
+ self._outputs[name] = self._array2output(value)
1802
+
1803
+ def GET_register_initialinputitemvalues(self) -> None:
1804
+ """Register the initial series of all current exchange items supposed to change
1805
+ the values of |InputSequence| objects under the given `id`.
1806
+
1807
+ Implemented as a workaround to support `OpenDA`_. Better use method
1808
+ |HydPyServer.GET_query_initialinputitemvalues|.
1809
+ """
1810
+ self.state.inputitemvalues[self._id] = self.state.initialinputitemvalues.copy()
1811
+
1812
+ def GET_query_initialconditionitemvalues(self) -> None:
1813
+ """Get the initial values of all current exchange items supposed to change the
1814
+ values of |StateSequence| or |LogSequence| objects."""
1815
+ for name, value in self.state.initialconditionitemvalues.items():
1816
+ self._outputs[name] = self._array2output(value)
1817
+
1818
+ def GET_register_initialconditionitemvalues(self) -> None:
1819
+ """Register the initial values of all current exchange items supposed to change
1820
+ the values of |StateSequence| or |LogSequence| objects under the given `id`.
1821
+
1822
+ Implemented as a workaround to support `OpenDA`_. Better use method
1823
+ |HydPyServer.GET_query_initialconditionitemvalues|.
1824
+ """
1825
+ state = self.state
1826
+ state.conditionitemvalues[self._id] = state.initialconditionitemvalues.copy()
1827
+
1828
+ def GET_query_initialoutputitemvalues(self) -> None:
1829
+ """Get the initial values of all current exchange items supposed to return the
1830
+ values or sequences of |FactorSequence| or |FluxSequence| objects in the
1831
+ "setitem style"."""
1832
+ for name, value in self.state.initialoutputitemvalues.items():
1833
+ self._outputs[name] = self._array2output(value)
1834
+
1835
+ def GET_register_initialoutputitemvalues(self) -> None:
1836
+ """Register the initial values of all current exchange items supposed to return
1837
+ the values or sequences of |FactorSequence| or |FluxSequence| objects in the
1838
+ "setitem style" under the given `id`.
1839
+
1840
+ Implemented as a workaround to support `OpenDA`_. Better use method
1841
+ |HydPyServer.GET_query_initialoutputitemvalues|.
1842
+ """
1843
+ state = self.state
1844
+ state.outputitemvalues[self._id] = state.initialoutputitemvalues.copy()
1845
+
1846
+ def GET_query_initialgetitemvalues(self) -> None:
1847
+ """Get the initial values of all current exchange items supposed to return the
1848
+ values of |Parameter| or |Sequence_| objects or the time series of |IOSequence|
1849
+ objects in the "getitems style"."""
1850
+ for name, value in self.state.initialgetitemvalues.items():
1851
+ self._outputs[name] = value
1852
+
1853
+ def GET_register_initialgetitemvalues(self) -> None:
1854
+ """Register the initial values of all current exchange items supposed to return
1855
+ the values of |Parameter| or |Sequence_| objects or the time series of
1856
+ |IOSequence| objects in the "getitem style" under the given `id`.
1857
+
1858
+ Implemented as a workaround to support `OpenDA`_. Better use method
1859
+ |HydPyServer.GET_query_initialgetitemvalues|.
1860
+ """
1861
+ self.state.getitemvalues[self._id] = self.state.initialgetitemvalues.copy()
1862
+
1863
+ def GET_query_initialisationtimegrid(self) -> None:
1864
+ """Return the general |Timegrids.init| time grid."""
1865
+ tg = hydpy.pub.timegrids.init
1866
+ utc = hydpy.pub.options.utcoffset
1867
+ self._outputs["firstdate_init"] = tg.firstdate.to_string("iso1", utc)
1868
+ self._outputs["lastdate_init"] = tg.lastdate.to_string("iso1", utc)
1869
+ self._outputs["stepsize"] = tg.stepsize
1870
+
1871
+ def _get_registered_content(self, dict_: dict[ID, T]) -> T:
1872
+ try:
1873
+ return dict_[self._id]
1874
+ except KeyError:
1875
+ message = f"Nothing registered under the id `{self._id}`."
1876
+ if dict_:
1877
+ message += f" The available ids are: {objecttools.enumeration(dict_)}."
1878
+ else:
1879
+ message += " There is nothing registered, so far."
1880
+ raise RuntimeError(message) from None
1881
+
1882
+ def POST_register_simulationdates(self) -> None:
1883
+ """Register the send simulation dates under the given `id`."""
1884
+ self.state.timegrids[self._id] = timetools.Timegrid(
1885
+ firstdate=self._inputs["firstdate_sim"],
1886
+ lastdate=self._inputs["lastdate_sim"],
1887
+ stepsize=hydpy.pub.timegrids.stepsize,
1888
+ )
1889
+
1890
+ def GET_activate_simulationdates(self) -> None:
1891
+ """Activate the simulation dates registered under the given `id`."""
1892
+ init = hydpy.pub.timegrids.init
1893
+ sim = hydpy.pub.timegrids.sim
1894
+ sim.dates = self._get_registered_content(self.state.timegrids).dates
1895
+ self.state.idx1 = init[sim.firstdate]
1896
+ self.state.idx2 = init[sim.lastdate]
1897
+
1898
+ def GET_query_simulationdates(self) -> None:
1899
+ """Return the simulation dates registered under the given `id`."""
1900
+ tg = self._get_registered_content(self.state.timegrids)
1901
+ utc = hydpy.pub.options.utcoffset
1902
+ self._outputs["firstdate_sim"] = tg.firstdate.to_string("iso1", utc)
1903
+ self._outputs["lastdate_sim"] = tg.lastdate.to_string("iso1", utc)
1904
+
1905
+ def GET_query_itemvalues(self) -> None:
1906
+ """Get the values of all |ExchangeItem| objects registered under the given
1907
+ `id`."""
1908
+ self.GET_query_changeitemvalues()
1909
+ self.GET_query_outputitemvalues()
1910
+ self.GET_query_getitemvalues()
1911
+
1912
+ def POST_register_changeitemvalues(self) -> None:
1913
+ """Register the send values of all |ChangeItem| objects under the given `id`."""
1914
+ self.POST_register_parameteritemvalues()
1915
+ self.POST_register_inputitemvalues()
1916
+ self.POST_register_conditionitemvalues()
1917
+
1918
+ def GET_activate_changeitemvalues(self) -> None:
1919
+ """Activate the values of the |ChangeItem| objects registered under the given
1920
+ `id`."""
1921
+ self.GET_activate_parameteritemvalues()
1922
+ self.GET_activate_inputitemvalues()
1923
+ self.GET_activate_conditionitemvalues()
1924
+
1925
+ def GET_query_changeitemvalues(self) -> None:
1926
+ """Get the values of all |ChangeItem| objects registered under the given
1927
+ `id`."""
1928
+ self.GET_query_parameteritemvalues()
1929
+ self.GET_query_inputitemvalues()
1930
+ self.GET_query_conditionitemvalues()
1931
+
1932
+ def _post_register_itemvalues(
1933
+ self,
1934
+ typename: str,
1935
+ items: Iterable[itemtools.ChangeItem],
1936
+ itemvalues: dict[ID, dict[Name, Any]],
1937
+ ) -> None:
1938
+ item2value: dict[Name, Any] = {}
1939
+ for item in items:
1940
+ try:
1941
+ value = self._inputs[item.name]
1942
+ except KeyError:
1943
+ self._statuscode = 500
1944
+ raise RuntimeError(
1945
+ f"A value for {typename} item `{item.name}` is missing."
1946
+ ) from None
1947
+ item2value[item.name] = eval(value)
1948
+ itemvalues[self._id] = item2value
1949
+
1950
+ def POST_register_parameteritemvalues(self) -> None:
1951
+ """Register the send parameter values under the given `id`."""
1952
+ self._post_register_itemvalues(
1953
+ typename="parameter",
1954
+ items=self.state.parameteritems,
1955
+ itemvalues=self.state.parameteritemvalues,
1956
+ )
1957
+
1958
+ def GET_activate_parameteritemvalues(self) -> None:
1959
+ """Activate the parameter values registered under the given `id`."""
1960
+ item2value = self._get_registered_content(self.state.parameteritemvalues)
1961
+ for item in self.state.parameteritems:
1962
+ item.value = item2value[item.name]
1963
+ item.update_variables()
1964
+
1965
+ def GET_query_parameteritemvalues(self) -> None:
1966
+ """Return the parameter values registered under the given `id`."""
1967
+ item2value = self._get_registered_content(self.state.parameteritemvalues)
1968
+ for item, value in item2value.items():
1969
+ self._outputs[item] = self._array2output(value)
1970
+
1971
+ def POST_register_inputitemvalues(self) -> None:
1972
+ """Register the send input item values under the given `id`."""
1973
+ self._post_register_itemvalues(
1974
+ typename="input",
1975
+ items=self.state.inputitems,
1976
+ itemvalues=self.state.inputitemvalues,
1977
+ )
1978
+
1979
+ def GET_activate_inputitemvalues(self) -> None:
1980
+ """Apply the input item values registered under the given `id` to modify the
1981
+ current |InputSequence| values."""
1982
+ item2value = self._get_registered_content(self.state.inputitemvalues)
1983
+ for item in self.state.inputitems:
1984
+ item.value = item2value[item.name]
1985
+ item.update_variables()
1986
+
1987
+ def GET_update_inputitemvalues(self) -> None:
1988
+ """Convert the current |InputSequence| values to input item values (when
1989
+ necessary) and register them under the given `id`."""
1990
+ item2value = {}
1991
+ for item in self.state.inputitems:
1992
+ item.extract_values()
1993
+ item2value[item.name] = item.value
1994
+ self.state.inputitemvalues[self._id] = item2value
1995
+
1996
+ def GET_query_inputitemvalues(self) -> None:
1997
+ """Return the input item values registered under the given `id`."""
1998
+ item2value = self._get_registered_content(self.state.inputitemvalues)
1999
+ for item, value in item2value.items():
2000
+ self._outputs[item] = self._array2output(value)
2001
+
2002
+ def POST_register_conditionitemvalues(self) -> None:
2003
+ """Register the send condition item values under the given `id`."""
2004
+ self._post_register_itemvalues(
2005
+ typename="condition",
2006
+ items=self.state.conditionitems,
2007
+ itemvalues=self.state.conditionitemvalues,
2008
+ )
2009
+
2010
+ def GET_activate_conditionitemvalues(self) -> None:
2011
+ """Apply the condition item values registered under the given `id` to modify
2012
+ the current |StateSequence| and |LogSequence| values."""
2013
+ item2value = self._get_registered_content(self.state.conditionitemvalues)
2014
+ for item in self.state.conditionitems:
2015
+ item.value = item2value[item.name]
2016
+ item.update_variables()
2017
+
2018
+ def GET_update_conditionitemvalues(self) -> None:
2019
+ """Convert the current |StateSequence| and |LogSequence| values to condition
2020
+ item values (when necessary) and register them under the given `id`."""
2021
+ item2value = {}
2022
+ for item in self.state.conditionitems:
2023
+ item.extract_values()
2024
+ item2value[item.name] = item.value
2025
+ self.state.conditionitemvalues[self._id] = item2value
2026
+
2027
+ def GET_query_conditionitemvalues(self) -> None:
2028
+ """Return the condition item values registered under the given `id`."""
2029
+ item2value = self._get_registered_content(self.state.conditionitemvalues)
2030
+ for item, value in item2value.items():
2031
+ self._outputs[item] = self._array2output(value)
2032
+
2033
+ def GET_update_outputitemvalues(self) -> None:
2034
+ """Convert the current |FactorSequence| and |FluxSequence| values or series to
2035
+ output item values (when necessary) and register them under the given `id`."""
2036
+ item2value = {}
2037
+ for item in self.state.outputitems:
2038
+ item.extract_values()
2039
+ item2value[item.name] = item.value
2040
+ self.state.outputitemvalues[self._id] = item2value
2041
+
2042
+ def GET_query_outputitemvalues(self) -> None:
2043
+ """Return the output item values registered under the given `id`."""
2044
+ item2value = self._get_registered_content(self.state.outputitemvalues)
2045
+ for item, value in item2value.items():
2046
+ self._outputs[item] = self._array2output(value)
2047
+
2048
+ def GET_save_internalconditions(self) -> None:
2049
+ """Register the |StateSequence| and |LogSequence| values of the |HydPy|
2050
+ instance for the current simulation endpoint under the given `id`."""
2051
+ self.state.conditions[self._id] = self.state.conditions.get(self._id, {})
2052
+ self.state.conditions[self._id][self.state.idx2] = self.state.hp.conditions
2053
+
2054
+ def GET_load_internalconditions(self) -> None:
2055
+ """Activate the |StateSequence| or |LogSequence| values registered for the
2056
+ current simulation start point under the given `id`.
2057
+
2058
+ When the simulation start point is identical with the initialisation time
2059
+ point, and you do not register alternative conditions manually, method
2060
+ |HydPyServer.GET_load_internalconditions| uses the "original" initial
2061
+ conditions of the current process (usually those of the conditions files of the
2062
+ respective *HydPy* project).
2063
+ """
2064
+ try:
2065
+ self.state.hp.conditions = self.state.conditions[self._id][self.state.idx1]
2066
+ except KeyError:
2067
+ if self.state.idx1:
2068
+ self._statuscode = 500
2069
+ raise RuntimeError(
2070
+ f"Conditions for ID `{self._id}` and time point "
2071
+ f"`{hydpy.pub.timegrids.sim.firstdate}` are required, but have "
2072
+ f"not been calculated so far."
2073
+ ) from None
2074
+ self.state.hp.conditions = self.state.init_conditions
2075
+
2076
+ def POST_register_internalconditions(self) -> None:
2077
+ """Register the send internal conditions under the given `id`."""
2078
+ conditions = eval(self._inputs["conditions"], {"array": numpy.array})
2079
+ self.state.conditions[self._id][self.state.idx1] = conditions
2080
+
2081
+ def GET_deregister_internalconditions(self) -> None:
2082
+ """Remove all internal condition directories registered under the given `id`."""
2083
+ self.state.conditions[self._id] = {}
2084
+
2085
+ def GET_query_internalconditions(self) -> None:
2086
+ """Get the internal conditions registered under the given `id`."""
2087
+ all_conditions = self._get_registered_content(self.state.conditions)
2088
+ try:
2089
+ relevant_conditons = all_conditions[self.state.idx2]
2090
+ except KeyError:
2091
+ raise RuntimeError(
2092
+ f"No internal conditions registered under the id `{self._id}` for "
2093
+ f"`{self.state.timegrids[self._id].lastdate}`."
2094
+ ) from None
2095
+ self._outputs["conditions"] = str(relevant_conditons).replace("\n", " ")
2096
+
2097
+ def POST_register_inputconditiondir(self) -> None:
2098
+ """Register the send input condition directory under the given `id`."""
2099
+ self.state.inputconditiondirs[self._id] = self._inputs["inputconditiondir"]
2100
+
2101
+ def GET_deregister_inputconditiondir(self) -> None:
2102
+ """Remove the input condition directory registered under the `id`."""
2103
+ self.state.inputconditiondirs.pop(self._id, None)
2104
+
2105
+ def GET_query_inputconditiondir(self) -> None:
2106
+ """Return the input condition directory registered under the `id`."""
2107
+ dir_ = self._get_registered_content(self.state.inputconditiondirs)
2108
+ self._outputs["inputconditiondir"] = dir_
2109
+
2110
+ def POST_register_outputconditiondir(self) -> None:
2111
+ """Register the send output condition directory under the given `id`."""
2112
+ self.state.outputconditiondirs[self._id] = self._inputs["outputconditiondir"]
2113
+
2114
+ def GET_deregister_outputconditiondir(self) -> None:
2115
+ """Remove the output condition directory registered under the `id`."""
2116
+ self.state.outputconditiondirs.pop(self._id, None)
2117
+
2118
+ def GET_query_outputconditiondir(self) -> None:
2119
+ """Return the output condition directory registered under the `id`."""
2120
+ dir_ = self._get_registered_content(self.state.outputconditiondirs)
2121
+ self._outputs["outputconditiondir"] = dir_
2122
+
2123
+ def GET_load_conditions(self) -> None:
2124
+ """Load the (initial) conditions."""
2125
+ dir_ = self.state.inputconditiondirs.get(self._id)
2126
+ self.state.interface.conditions_io.load_conditions(dir_)
2127
+
2128
+ def GET_save_conditions(self) -> None:
2129
+ """Save the (resulting) conditions."""
2130
+ dir_ = self.state.outputconditiondirs.get(self._id)
2131
+ self.state.interface.conditions_io.save_conditions(dir_)
2132
+
2133
+ def GET_update_getitemvalues(self) -> None:
2134
+ """Register the current |GetItem| values under the given `id`.
2135
+
2136
+ For |GetItem| objects observing time series, method
2137
+ |HydPyServer.GET_update_getitemvalues| registers only the values within the
2138
+ current simulation period.
2139
+ """
2140
+ item2value = {}
2141
+ for item in self.state.getitems:
2142
+ for name, value in item.yield_name2value(self.state.idx1, self.state.idx2):
2143
+ item2value[name] = value
2144
+ self.state.getitemvalues[self._id] = item2value
2145
+
2146
+ def GET_query_getitemvalues(self) -> None:
2147
+ """Get the |GetItem| values registered under the given `id`."""
2148
+ item2value = self._get_registered_content(self.state.getitemvalues)
2149
+ for name, value in item2value.items():
2150
+ self._outputs[name] = value
2151
+
2152
+ def GET_simulate(self) -> None:
2153
+ """Perform a simulation run."""
2154
+ readerdir = self.state.seriesreaderdirs.get(self._id, None)
2155
+ writerdir = self.state.serieswriterdirs.get(self._id, None)
2156
+ sio = self.state.interface.series_io
2157
+ with sio.modify_inputdir(readerdir), sio.modify_outputdir(writerdir):
2158
+ self.state.hp.simulate()
2159
+
2160
+ def POST_register_seriesreaderdir(self) -> None:
2161
+ """Register the send series reader directory under the given `id`."""
2162
+ self.state.seriesreaderdirs[self._id] = self._inputs["seriesreaderdir"]
2163
+
2164
+ def GET_deregister_seriesreaderdir(self) -> None:
2165
+ """Remove the series reader directory registered under the `id`."""
2166
+ self.state.seriesreaderdirs.pop(self._id, None)
2167
+
2168
+ def GET_query_seriesreaderdir(self) -> None:
2169
+ """Return the series reader directory registered under the `id`."""
2170
+ dir_ = self._get_registered_content(self.state.seriesreaderdirs)
2171
+ self._outputs["seriesreaderdir"] = dir_
2172
+
2173
+ def POST_register_serieswriterdir(self) -> None:
2174
+ """Register the send series writer directory under the given `id`."""
2175
+ self.state.serieswriterdirs[self._id] = self._inputs["serieswriterdir"]
2176
+
2177
+ def GET_deregister_serieswriterdir(self) -> None:
2178
+ """Remove the series writer directory registered under the `id`."""
2179
+ self.state.serieswriterdirs.pop(self._id, None)
2180
+
2181
+ def GET_query_serieswriterdir(self) -> None:
2182
+ """Return the series writer directory registered under the `id`."""
2183
+ dir_ = self._get_registered_content(self.state.serieswriterdirs)
2184
+ self._outputs["serieswriterdir"] = dir_
2185
+
2186
+ def GET_load_allseries(self) -> None:
2187
+ """Load the time series of all sequences selected for (non-jit) reading."""
2188
+ state = self.state
2189
+ state.interface.series_io.load_series(state.seriesreaderdirs.get(self._id))
2190
+
2191
+ def GET_save_allseries(self) -> None:
2192
+ """Save the time series of all sequences selected for (non-jit) writing."""
2193
+ state = self.state
2194
+ state.interface.series_io.save_series(state.serieswriterdirs.get(self._id))
2195
+
2196
+ def POST_register_outputcontroldir(self) -> None:
2197
+ """Register the send output control directory under the given `id`."""
2198
+ self.state.outputcontroldirs[self._id] = self._inputs["outputcontroldir"]
2199
+
2200
+ def GET_deregister_outputcontroldir(self) -> None:
2201
+ """Remove the output control directory registered under the `id`."""
2202
+ self.state.outputcontroldirs.pop(self._id, None)
2203
+
2204
+ def GET_query_outputcontroldir(self) -> None:
2205
+ """Return the output control directory registered under the `id`."""
2206
+ dir_ = self._get_registered_content(self.state.outputcontroldirs)
2207
+ self._outputs["outputcontroldir"] = dir_
2208
+
2209
+ def GET_save_controls(self) -> None:
2210
+ """Save the control files of all model instances."""
2211
+ state = self.state
2212
+ controldir = self._get_registered_content(state.outputcontroldirs)
2213
+ hydpy.pub.controlmanager.currentdir = controldir
2214
+ state.hp.save_controls()
2215
+
2216
+
2217
+ class _HTTPServerBase(http.server.HTTPServer):
2218
+ debugmode: bool = False
2219
+
2220
+
2221
+ def start_server(
2222
+ socket: int | str,
2223
+ projectname: str,
2224
+ xmlfilename: str,
2225
+ *,
2226
+ load_conditions: bool | str = True,
2227
+ load_series: bool | str = True,
2228
+ maxrequests: int | str = 5,
2229
+ debugging: Literal["enable", "disable"] = "disable",
2230
+ ) -> None:
2231
+ """Start the *HydPy* server using the given socket.
2232
+
2233
+ The folder with the given `projectname` must be available within the current
2234
+ working directory. The XML configuration file must be placed within the project
2235
+ folder unless `xmlfilename` is an absolute file path. The XML configuration file
2236
+ must be valid concerning the schema file `HydPyConfigMultipleRuns.xsd` (see class
2237
+ |ServerState| for further information).
2238
+
2239
+ The |HydPyServer| allows for five still unhandled requests before refusing new
2240
+ connections by default. Use the optional `maxrequests` argument to increase this
2241
+ number (which might be necessary when parallelising optimisation or data
2242
+ assimilation):
2243
+
2244
+ >>> from hydpy.core.testtools import prepare_full_example_1
2245
+ >>> prepare_full_example_1()
2246
+ >>> command = (
2247
+ ... "hyd.py start_server 8080 HydPy-H-Lahn multiple_runs_alpha.xml "
2248
+ ... "debugging=enable maxrequests=100")
2249
+ >>> from hydpy import run_subprocess, TestIO
2250
+ >>> with TestIO():
2251
+ ... process = run_subprocess(command, blocking=False, verbose=False)
2252
+ ... result = run_subprocess("hyd.py await_server 8080 10", verbose=False)
2253
+
2254
+ >>> from urllib import request
2255
+ >>> command = "maxrequests = self.server.request_queue_size"
2256
+ >>> response = request.urlopen("http://127.0.0.1:8080/evaluate",
2257
+ ... data=bytes(command, encoding="utf-8"))
2258
+ >>> print(str(response.read(), encoding="utf-8"))
2259
+ maxrequests = 100
2260
+
2261
+ >>> _ = request.urlopen("http://127.0.0.1:8080/close_server")
2262
+ >>> process.kill()
2263
+ >>> _ = process.communicate()
2264
+
2265
+ Please see the documentation on method |HydPyServer.POST_evaluate| that explains
2266
+ the "debugging" argument.
2267
+
2268
+ Note that function |start_server| tries to read the "mime types" from a dictionary
2269
+ stored in the file `mimetypes.txt` available in subpackage `conf` and passes it as
2270
+ attribute `extension_map` to class |HydPyServer|. The reason is to avoid the long
2271
+ computation time of function |mimetypes.init| of module |mimetypes|, usually called
2272
+ when defining class `BaseHTTPRequestHandler` of module `http.server`. If file
2273
+ `mimetypes.txt` does not exist or does not work for , |start_server| calls
2274
+ |mimetypes.init| as usual, (over)writes `mimetypes.txt` and tries to proceed as
2275
+ expected.
2276
+ """
2277
+ confpath: str = conf.__path__[0]
2278
+ filepath = os.path.join(confpath, "mimetypes.txt")
2279
+ try:
2280
+ with open(filepath, encoding=config.ENCODING) as file_:
2281
+ types_map: dict[str, str] = eval(str(file_.read()))
2282
+ except BaseException:
2283
+ mimetypes.init()
2284
+ types_map = mimetypes.types_map.copy()
2285
+ types_map.update(
2286
+ {
2287
+ "": "application/octet-stream",
2288
+ ".py": "text/plain",
2289
+ ".c": "text/plain",
2290
+ ".h": "text/plain",
2291
+ }
2292
+ )
2293
+ with open(filepath, "w", encoding=config.ENCODING) as file_:
2294
+ file_.write(str(types_map))
2295
+ HydPyServer.extensions_map = types_map
2296
+ HydPyServer.state = ServerState(
2297
+ projectname=projectname,
2298
+ xmlfile=xmlfilename,
2299
+ load_conditions=objecttools.value2bool("load_conditions", load_conditions),
2300
+ load_series=objecttools.value2bool("load_series", load_series),
2301
+ )
2302
+
2303
+ class _HTTPServer(_HTTPServerBase):
2304
+ debugmode = debugging == "enable"
2305
+ request_queue_size = int(maxrequests)
2306
+
2307
+ server = _HTTPServer(("", int(socket)), HydPyServer)
2308
+ server.serve_forever()
2309
+
2310
+
2311
+ def await_server(port: int | str, seconds: float | str) -> None:
2312
+ """Block the current process until either the *HydPy* server is responding on the
2313
+ given `port` or the given number of `seconds` elapsed.
2314
+
2315
+ >>> from hydpy import run_subprocess, TestIO
2316
+ >>> with TestIO(): # doctest: +ELLIPSIS
2317
+ ... result = run_subprocess("hyd.py await_server 8080 0.1")
2318
+ Invoking hyd.py with arguments `await_server, 8080, 0.1` resulted in the \
2319
+ following error:
2320
+ <urlopen error Waited for 0.1 seconds without response on port 8080.>
2321
+ ...
2322
+
2323
+ >>> from hydpy.core.testtools import prepare_full_example_1
2324
+ >>> prepare_full_example_1()
2325
+ >>> with TestIO():
2326
+ ... process = run_subprocess(
2327
+ ... "hyd.py start_server 8080 HydPy-H-Lahn multiple_runs.xml",
2328
+ ... blocking=False, verbose=False)
2329
+ ... result = run_subprocess("hyd.py await_server 8080 10", verbose=False)
2330
+
2331
+ >>> from urllib import request
2332
+ >>> _ = request.urlopen("http://127.0.0.1:8080/close_server")
2333
+ >>> process.kill()
2334
+ >>> _ = process.communicate()
2335
+ """
2336
+ now = time.perf_counter()
2337
+ end = now + float(seconds)
2338
+ while now <= end:
2339
+ try:
2340
+ with urllib.request.urlopen(f"http://127.0.0.1:{port}/status"):
2341
+ break
2342
+ except urllib.error.URLError:
2343
+ time.sleep(0.1)
2344
+ now = time.perf_counter()
2345
+ else:
2346
+ raise urllib.error.URLError(
2347
+ f"Waited for {seconds} seconds without response on port {port}."
2348
+ )