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
hydpy/exe/xmltools.py ADDED
@@ -0,0 +1,3280 @@
1
+ """This module provides features for executing *HydPy* workflows based on XML
2
+ configuration files.
3
+
4
+ .. _HydPy release: https://github.com/hydpy-dev/hydpy/releases
5
+ .. _`OpenDA`: https://www.openda.org/
6
+
7
+ At the heart of module |xmltools| lies function |run_simulation|, thought to be applied
8
+ via a command line (see the documentation on script |hyd| for further information).
9
+ |run_simulation| expects that the *HydPy* project you want to work with is available in
10
+ your current working directory and contains an XML configuration file (as
11
+ `single_run.xml` in the example project folder `HydPy-H-Lahn`). This configuration
12
+ file must agree with the XML schema `HydPyConfigSingleRun.xsd`, which is available in
13
+ the `conf` subpackage and separately downloadable for each `HydPy release`_. If you
14
+ did implement new or changed existing models, you have to update this schema file.
15
+ *HydPy* does this automatically through its setup mechanism (see the documentation on
16
+ class |XSDWriter|).
17
+
18
+ To show how to apply |run_simulation| via a command line, we first copy the
19
+ `HydPy-H-Lahn` project into the `iotesting` folder by calling the function
20
+ |prepare_full_example_1|:
21
+
22
+ >>> from hydpy.core.testtools import prepare_full_example_1
23
+ >>> prepare_full_example_1()
24
+
25
+ Running the simulation requires defining the main script (`hyd.py`), the function
26
+ specifying the actual workflow (`run_simulation`), the name of the project of interest
27
+ (`HydPy-H-Lahn`), and the name of the relevant XML configuration file
28
+ (`single_run.xml`). We pass the required text to function |run_subprocess| of module
29
+ subprocess to simulate using the command line:
30
+
31
+ >>> from hydpy import run_subprocess, TestIO
32
+ >>> import subprocess
33
+ >>> with TestIO(): # doctest: +ELLIPSIS
34
+ ... result = run_subprocess("hyd.py run_simulation HydPy-H-Lahn single_run.xml")
35
+ Start HydPy project `HydPy-H-Lahn` (...).
36
+ Read configuration file `single_run.xml` (...).
37
+ Interpret the defined options (...).
38
+ Interpret the defined period (...).
39
+ Read all network files (...).
40
+ Activate the selected network (...).
41
+ Read the required control files (...).
42
+ Read the required condition files (...).
43
+ Read the required time series files (...).
44
+ Perform the simulation run (...).
45
+ Write the desired condition files (...).
46
+ Write the desired time series files (...).
47
+
48
+ As defined by the XML configuration file, the simulation started on the first and
49
+ ended on the sixth of January 1996. The following example shows the read initial
50
+ conditions and the written final conditions of sequence |hland_states.SM| for the
51
+ 12 hydrological response units of the subcatchment `land_dill_assl`:
52
+
53
+ >>> with TestIO():
54
+ ... filepath = "HydPy-H-Lahn/conditions/init_1996_01_01_00_00_00/land_dill_assl.py"
55
+ ... with open(filepath) as file_:
56
+ ... print("".join(file_.readlines()[8:10]))
57
+ ... filepath = "HydPy-H-Lahn/conditions/init_1996_01_06/land_dill_assl.py"
58
+ ... with open(filepath) as file_:
59
+ ... print("".join(file_.readlines()[10:12]))
60
+ sm(185.13164, 181.18755, 199.80432, 196.55888, 212.04018, 209.48859,
61
+ 222.12115, 220.12671, 230.30756, 228.70779, 236.91943, 235.64427)
62
+ <BLANKLINE>
63
+ sm(184.517818, 180.588472, 199.142925, 195.90995, 212.04018, 209.48859,
64
+ 222.12115, 220.12671, 230.30756, 228.70779, 236.91943, 235.64427)
65
+ <BLANKLINE>
66
+
67
+ The intermediate soil moisture values have been stored in a NetCDF file called
68
+ `hland_96_state_sm.nc`:
69
+
70
+ >>> import numpy
71
+ >>> from hydpy import print_vector
72
+ >>> from hydpy.core.netcdftools import netcdf4, chars2str, query_variable
73
+ >>> with TestIO():
74
+ ... ncfile = netcdf4.Dataset("HydPy-H-Lahn/series/soildata/hland_96_state_sm.nc")
75
+ ... chars2str(query_variable(ncfile, "station_id")[:].data)[:3]
76
+ ... print_vector(query_variable(ncfile, "hland_96_state_sm")[:, 0])
77
+ ['land_dill_assl_0', 'land_dill_assl_1', 'land_dill_assl_2']
78
+ 184.958475, 184.763638, 184.610776, 184.553224, 184.517818
79
+ >>> ncfile.close()
80
+
81
+ Spatially averaged time series values have been stored in files ending with the suffix
82
+ `_mean`:
83
+
84
+ >>> import time
85
+ >>> time.sleep(10)
86
+
87
+ >>> with TestIO(clear_all=True):
88
+ ... print_vector(
89
+ ... numpy.load("HydPy-H-Lahn/series/averages/lahn_marb_sim_q_mean.npy")[13:]
90
+ ... )
91
+ 9.64767, 8.513649, 7.777628, 7.343314, 7.156591
92
+ """
93
+
94
+ # import...
95
+ # ...from standard library
96
+ from __future__ import annotations
97
+ import collections
98
+ import contextlib
99
+ import copy
100
+ import itertools
101
+ import os
102
+ import warnings
103
+ from xml.etree import ElementTree
104
+
105
+ # ...from HydPy
106
+ import hydpy
107
+ from hydpy import conf
108
+ from hydpy import config
109
+ from hydpy import models
110
+ from hydpy.core import devicetools
111
+ from hydpy.core import exceptiontools
112
+ from hydpy.core import hydpytools
113
+ from hydpy.core import importtools
114
+ from hydpy.core import itemtools
115
+ from hydpy.core import objecttools
116
+ from hydpy.core import selectiontools
117
+ from hydpy.core import parametertools
118
+ from hydpy.core import sequencetools
119
+ from hydpy.exe import commandtools
120
+ from hydpy.core.typingtools import *
121
+
122
+ if TYPE_CHECKING:
123
+ import netCDF4 as netcdf4
124
+ import xmlschema
125
+ from hydpy.core import modeltools
126
+ from hydpy.core import variabletools
127
+ else:
128
+ netcdf4 = exceptiontools.OptionalImport(
129
+ "netcdf4", ["netCDF4", "h5netcdf.legacyapi"], locals()
130
+ )
131
+ xmlschema = exceptiontools.OptionalImport("xmlschema", ["xmlschema"], locals())
132
+
133
+ _TypeSetOrAddOrMultiplyItem = TypeVar(
134
+ "_TypeSetOrAddOrMultiplyItem",
135
+ itemtools.SetItem,
136
+ itemtools.AddItem,
137
+ itemtools.MultiplyItem,
138
+ )
139
+ _TypeGetOrChangeItem = TypeVar(
140
+ "_TypeGetOrChangeItem", itemtools.GetItem, itemtools.ChangeItem, itemtools.SetItem
141
+ )
142
+
143
+ namespace = (
144
+ "{https://github.com/hydpy-dev/hydpy/releases/download/your-hydpy-version/"
145
+ "HydPyConfigBase.xsd}"
146
+ )
147
+ _ITEMGROUP2ITEMCLASS = {
148
+ "setitems": itemtools.SetItem,
149
+ "additems": itemtools.AddItem,
150
+ "multiplyitems": itemtools.MultiplyItem,
151
+ "getitems": itemtools.GetItem,
152
+ }
153
+
154
+
155
+ @overload
156
+ def find(
157
+ root: ElementTree.Element, name: str, optional: Literal[True] = True
158
+ ) -> ElementTree.Element | None:
159
+ """Optional version of function |find|."""
160
+
161
+
162
+ @overload
163
+ def find(
164
+ root: ElementTree.Element, name: str, optional: Literal[False]
165
+ ) -> ElementTree.Element:
166
+ """Non-optional version of function |find|."""
167
+
168
+
169
+ def find(
170
+ root: ElementTree.Element, name: str, optional: Literal[True, False] = True
171
+ ) -> ElementTree.Element | None:
172
+ """Return the first XML element with the given name found in the given XML root.
173
+
174
+ >>> from hydpy.exe.xmltools import find, XMLInterface
175
+ >>> from hydpy.data import make_filepath
176
+ >>> interface = XMLInterface("single_run.xml", make_filepath("HydPy-H-Lahn"))
177
+ >>> find(interface.root, "timegrid").tag.endswith("timegrid")
178
+ True
179
+
180
+ By default, function |find| returns |None| in case the required element is missing:
181
+
182
+ >>> find(interface.root, "wrong")
183
+
184
+ Set the argument `optional` to |False| to let function |find| raise errors instead:
185
+
186
+ >>> find(interface.root, "wrong", optional=False)
187
+ Traceback (most recent call last):
188
+ ...
189
+ AttributeError: The actual XML element `config` does not define a XML subelement \
190
+ named `wrong`. Please make sure your XML file follows the relevant XML schema.
191
+ """
192
+ element = root.find(f"{namespace}{name}")
193
+ if element is None and not optional:
194
+ raise AttributeError(
195
+ f"The actual XML element `{root.tag.rsplit('}')[-1]}` does not define a "
196
+ f"XML subelement named `{name}`. Please make sure your XML file follows "
197
+ f"the relevant XML schema."
198
+ )
199
+ return element
200
+
201
+
202
+ def _query_selections(xmlelement: ElementTree.Element) -> selectiontools.Selections:
203
+ selections = []
204
+ text = xmlelement.text
205
+ assert text is not None
206
+ sels = hydpy.pub.selections
207
+ for name in text.split():
208
+ if name == "complete":
209
+ selections.append(
210
+ selectiontools.Selection(
211
+ "__complete__", nodes=sels.nodes, elements=sels.elements
212
+ )
213
+ )
214
+ else:
215
+ try:
216
+ selections.append(getattr(sels, name))
217
+ except AttributeError:
218
+ raise NameError(
219
+ f"The XML configuration file tries to define a selection using "
220
+ f"the text `{name}`, but the actual project does not handle such "
221
+ f"a `Selection` object."
222
+ ) from None
223
+ return selectiontools.Selections(*selections)
224
+
225
+
226
+ def strip(name: str) -> str:
227
+ """Remove the XML namespace from the given string and return it.
228
+
229
+ >>> from hydpy.exe.xmltools import strip
230
+ >>> strip("{https://github.com/something.xsd}something")
231
+ 'something'
232
+ """
233
+ return name.split("}")[-1]
234
+
235
+
236
+ class PrepareSeriesArguments(NamedTuple):
237
+ """Helper class that determines and provides the arguments for function
238
+ |IOSequence.prepare_series|."""
239
+
240
+ allocate_ram: bool
241
+ read_jit: bool
242
+ write_jit: bool
243
+
244
+ @classmethod
245
+ def from_xmldata(
246
+ cls, is_reader: bool, is_input: bool, prefer_ram: bool
247
+ ) -> PrepareSeriesArguments:
248
+ """Create a |PrepareSeriesArguments| object based on the (already prepared)
249
+ information of an XML file.
250
+
251
+ Meaning of the arguments:
252
+ * is_reader: is the current XML-Element responsible for reading (or writing)?
253
+ * is_input: serve the addressed sequences as inputs (or outputs)?
254
+ * prefer_ram: prefer to handle time series data in RAM (or read and write it
255
+ just in time)?
256
+
257
+ >>> from hydpy.exe.xmltools import PrepareSeriesArguments
258
+ >>> from_xmldata = PrepareSeriesArguments.from_xmldata
259
+
260
+ Test cases for reading input data:
261
+
262
+ >>> from_xmldata(is_reader=True, is_input=True, prefer_ram=True)
263
+ PrepareSeriesArguments(allocate_ram=True, read_jit=False, write_jit=False)
264
+
265
+ >>> from_xmldata(is_reader=True, is_input=True, prefer_ram=False)
266
+ PrepareSeriesArguments(allocate_ram=False, read_jit=True, write_jit=False)
267
+
268
+ Attempting to read output data results in an |AssertionError| (disallowed by
269
+ all available XML schema files):
270
+
271
+ >>> from_xmldata(is_reader=True, is_input=False, prefer_ram=True)
272
+ Traceback (most recent call last):
273
+ ...
274
+ AssertionError: reading output values is disallowed
275
+
276
+ >>> from_xmldata(is_reader=True, is_input=False, prefer_ram=False)
277
+ Traceback (most recent call last):
278
+ ...
279
+ AssertionError: reading output values is disallowed
280
+
281
+ Test cases for writing input data (this is for the rare case where an
282
+ external tool like `OpenDA`_ provides or modifies the input data in RAM, and we
283
+ want to write it to a file for documentation purposes):
284
+
285
+ >>> from_xmldata(is_reader=False, is_input=True, prefer_ram=True)
286
+ PrepareSeriesArguments(allocate_ram=True, read_jit=False, write_jit=False)
287
+
288
+ >>> from_xmldata(is_reader=False, is_input=True, prefer_ram=False)
289
+ PrepareSeriesArguments(allocate_ram=True, read_jit=False, write_jit=True)
290
+
291
+ Test cases for writing output data:
292
+
293
+ >>> from_xmldata(is_reader=False, is_input=False, prefer_ram=True)
294
+ PrepareSeriesArguments(allocate_ram=True, read_jit=False, write_jit=False)
295
+
296
+ >>> from_xmldata(is_reader=False, is_input=False, prefer_ram=False)
297
+ PrepareSeriesArguments(allocate_ram=False, read_jit=False, write_jit=True)
298
+
299
+ """
300
+ is_writer = not is_reader
301
+ is_output = not is_input
302
+ prefer_jit = not prefer_ram
303
+ assert not (is_reader and is_output), "reading output values is disallowed"
304
+ return PrepareSeriesArguments(
305
+ allocate_ram=prefer_ram or (is_input and is_writer),
306
+ read_jit=is_reader and prefer_jit,
307
+ write_jit=is_writer and prefer_jit,
308
+ )
309
+
310
+
311
+ def run_simulation(projectname: str, xmlfile: str) -> None:
312
+ """Perform a *HydPy* workflow according to the given XML configuration file
313
+ available in the given project's directory.
314
+
315
+ Function |run_simulation| is a "script function". We explain its normal usage
316
+ in the main documentation on module |xmltools|.
317
+ """
318
+ write = commandtools.print_textandtime
319
+ hydpy.pub.options.printprogress = False
320
+ write(f"Start HydPy project `{projectname}`")
321
+ hp = hydpytools.HydPy(projectname)
322
+ write(f"Read configuration file `{xmlfile}`")
323
+ interface = XMLInterface(xmlfile)
324
+ write("Interpret the defined options")
325
+ interface.update_options()
326
+ hydpy.pub.options.printprogress = False
327
+ write("Interpret the defined period")
328
+ interface.update_timegrids()
329
+ write("Read all network files")
330
+ interface.network_io.prepare_network()
331
+ write("Create the custom selections (if defined)")
332
+ interface.update_selections()
333
+ write("Activate the selected network")
334
+ hp.update_devices(selection=interface.fullselection, silent=True)
335
+ write("Read the required control files")
336
+ interface.control_io.prepare_models()
337
+ write("Read the required condition files")
338
+ interface.conditions_io.load_conditions()
339
+ write("Read the required time series files")
340
+ series_io = interface.series_io
341
+ series_io.prepare_series()
342
+ series_io.load_series()
343
+ write("Perform the simulation run")
344
+ with series_io.modify_inputdir(), series_io.modify_outputdir():
345
+ hp.simulate()
346
+ write("Write the desired condition files")
347
+ interface.conditions_io.save_conditions()
348
+ write("Write the desired time series files")
349
+ series_io.save_series()
350
+
351
+
352
+ class XMLBase:
353
+ """Base class for the concrete classes |XMLInterface|, |XMLConditions|,
354
+ |XMLSeries|, and |XMLSubseries|.
355
+
356
+ Subclasses of |XMLBase| support iterating XML subelements while skipping those
357
+ named `selections`:
358
+
359
+ >>> from hydpy.exe.xmltools import XMLInterface
360
+ >>> from hydpy.data import make_filepath
361
+ >>> interface = XMLInterface("multiple_runs.xml", make_filepath("HydPy-H-Lahn"))
362
+ >>> itemgroup = interface.exchange.itemgroups[1]
363
+ >>> for element in itemgroup:
364
+ ... print(strip(element.tag))
365
+ hland_96
366
+ rconc_uh
367
+ >>> for element in itemgroup.models[0].subvars[0].vars[0]:
368
+ ... print(strip(element.tag))
369
+ name
370
+ level
371
+ """
372
+
373
+ root: ElementTree.Element
374
+
375
+ @property
376
+ def name(self) -> str:
377
+ """Apply function |strip| to the root of the object of the |XMLBase| subclass.
378
+
379
+ >>> from hydpy.exe.xmltools import XMLInterface
380
+ >>> from hydpy.data import make_filepath
381
+ >>> interface = XMLInterface("single_run.xml", make_filepath("HydPy-H-Lahn"))
382
+ >>> interface.name
383
+ 'config'
384
+ >>> interface.series_io.readers[0].name
385
+ 'reader'
386
+ """
387
+ return strip(self.root.tag)
388
+
389
+ @overload
390
+ def find(
391
+ self, name: str, optional: Literal[True] = True
392
+ ) -> ElementTree.Element | None:
393
+ """Optional version of method |XMLBase.find|."""
394
+
395
+ @overload
396
+ def find(self, name: str, optional: Literal[False]) -> ElementTree.Element:
397
+ """Non-optional version of function |XMLBase.find|."""
398
+
399
+ def find(
400
+ self, name: str, optional: Literal[True, False] = True
401
+ ) -> ElementTree.Element | None:
402
+ """Apply function |find| to the root of the object of the |XMLBase| subclass.
403
+
404
+ >>> from hydpy.exe.xmltools import XMLInterface
405
+ >>> from hydpy.data import make_filepath
406
+ >>> interface = XMLInterface("single_run.xml", make_filepath("HydPy-H-Lahn"))
407
+ >>> interface.find("timegrid").tag.endswith("timegrid")
408
+ True
409
+
410
+ >>> interface.find("wrong")
411
+
412
+ >>> interface.find("wrong", optional=False)
413
+ Traceback (most recent call last):
414
+ ...
415
+ AttributeError: The actual XML element `config` does not define a XML \
416
+ subelement named `wrong`. Please make sure your XML file follows the relevant XML \
417
+ schema.
418
+ """
419
+ return find(self.root, name, optional)
420
+
421
+ def __iter__(self) -> Iterator[ElementTree.Element]:
422
+ for element in self.root:
423
+ name = strip(element.tag)
424
+ if name != "selections":
425
+ yield element
426
+
427
+
428
+ class XMLInterface(XMLBase):
429
+ """An interface to XML configuration files that are valid concerning schema file
430
+ `HydPyConfigSingleRun.xsd` or `HydPyConfigMultipleRuns.xsd`.
431
+
432
+ >>> from hydpy.exe.xmltools import XMLInterface
433
+ >>> from hydpy.data import make_filepath
434
+ >>> interface = XMLInterface("single_run.xml", make_filepath("HydPy-H-Lahn"))
435
+ >>> interface.root.tag
436
+ '{https://github.com/hydpy-dev/hydpy/releases/download/your-hydpy-version/\
437
+ HydPyConfigSingleRun.xsd}config'
438
+ >>> interface = XMLInterface('multiple_runs.xml', make_filepath('HydPy-H-Lahn'))
439
+ >>> interface.root.tag
440
+ '{https://github.com/hydpy-dev/hydpy/releases/download/your-hydpy-version/\
441
+ HydPyConfigMultipleRuns.xsd}config'
442
+ >>> XMLInterface('wrongfilepath.xml', 'wrongdir') # doctest: +ELLIPSIS
443
+ Traceback (most recent call last):
444
+ ...
445
+ FileNotFoundError: While trying to parse the XML configuration file \
446
+ ...wrongfilepath.xml, the following error occurred: \
447
+ [Errno 2] No such file or directory: '...wrongfilepath.xml'
448
+ """
449
+
450
+ def __init__(self, filename: str, directory: str | None = None) -> None:
451
+ if directory is None:
452
+ directory = hydpy.pub.projectname
453
+ self.filepath = os.path.abspath(os.path.join(directory, filename))
454
+ try:
455
+ self.root = ElementTree.parse(self.filepath).getroot()
456
+ except BaseException:
457
+ objecttools.augment_excmessage(
458
+ f"While trying to parse the XML configuration file " f"{self.filepath}"
459
+ )
460
+
461
+ def validate_xml(self) -> None:
462
+ """Raise an error if the actual XML does not agree with one of the available
463
+ schema files.
464
+
465
+ The first example relies on a distorted version of the configuration file
466
+ `single_run.xml`:
467
+
468
+ >>> from hydpy.core.testtools import prepare_full_example_1
469
+ >>> prepare_full_example_1()
470
+ >>> from hydpy import TestIO, xml_replace
471
+ >>> from hydpy.exe.xmltools import XMLInterface
472
+ >>> import os
473
+ >>> with TestIO(): # doctest: +ELLIPSIS
474
+ ... xml_replace("HydPy-H-Lahn/single_run",
475
+ ... firstdate="1996-01-32T00:00:00")
476
+ template file: HydPy-H-Lahn/single_run.xmlt
477
+ target file: HydPy-H-Lahn/single_run.xml
478
+ replacements:
479
+ firstdate --> 1996-01-32T00:00:00 (given argument)
480
+ prefix --> init (default argument)
481
+ zip_ --> false (default argument)
482
+ >>> with TestIO():
483
+ ... interface = XMLInterface("single_run.xml", "HydPy-H-Lahn")
484
+ >>> interface.validate_xml() # doctest: +ELLIPSIS
485
+ Traceback (most recent call last):
486
+ ...
487
+ xmlschema.validators.exceptions.XMLSchemaDecodeError: While trying to \
488
+ validate XML file `...single_run.xml`, the following error occurred: failed \
489
+ validating '1996-01-32T00:00:00' with XsdAtomicBuiltin(name='xs:dateTime')...
490
+ ...
491
+ Reason: day is out of range for month
492
+ ...
493
+ Schema component:
494
+ ...
495
+ Instance:
496
+ ...
497
+ <firstdate xmlns="https://github.com/hydpy-dev/hydpy/releases/\
498
+ download/your-hydpy-version/HydPyConfigBase.xsd">1996-01-32T00:00:00</firstdate>
499
+ ...
500
+ Path: /hpcsr:config/timegrid/firstdate
501
+ ...
502
+
503
+ In the second example, we examine a correct configuration file:
504
+
505
+ >>> with TestIO(): # doctest: +ELLIPSIS
506
+ ... xml_replace("HydPy-H-Lahn/single_run")
507
+ ... interface = XMLInterface("single_run.xml", "HydPy-H-Lahn")
508
+ template file: HydPy-H-Lahn/single_run.xmlt
509
+ target file: HydPy-H-Lahn/single_run.xml
510
+ replacements:
511
+ firstdate --> 1996-01-01T00:00:00 (default argument)
512
+ prefix --> init (default argument)
513
+ zip_ --> false (default argument)
514
+ >>> interface.validate_xml()
515
+
516
+ The XML configuration file must correctly refer to the corresponding schema
517
+ file:
518
+
519
+ >>> with TestIO():
520
+ ... with open("HydPy-H-Lahn/single_run.xml") as xmlfile:
521
+ ... text = xmlfile.read()
522
+ ... text = text.replace("HydPyConfigSingleRun.xsd", "wrong.xsd")
523
+ ... with open("HydPy-H-Lahn/single_run.xml", "w") as xmlfile:
524
+ ... _ = xmlfile.write(text)
525
+ ... interface = XMLInterface("single_run.xml", "HydPy-H-Lahn")
526
+ >>> interface.validate_xml() # doctest: +ELLIPSIS
527
+ Traceback (most recent call last):
528
+ ...
529
+ RuntimeError: While trying to validate XML file `...single_run.xml`, \
530
+ the following error occurred: Configuration file `single_run.xml` does not \
531
+ correctly refer to one of the available XML schema files \
532
+ (HydPyConfigSingleRun.xsd and HydPyConfigMultipleRuns.xsd).
533
+
534
+ XML files based on `HydPyConfigMultipleRuns.xsd` can be validated as well:
535
+
536
+ >>> with TestIO():
537
+ ... interface = XMLInterface("multiple_runs.xml", "HydPy-H-Lahn")
538
+ >>> interface.validate_xml()
539
+ """
540
+ try:
541
+ filenames = ("HydPyConfigSingleRun.xsd", "HydPyConfigMultipleRuns.xsd")
542
+ for name in filenames:
543
+ if name in self.root.tag:
544
+ schemafile = name
545
+ break
546
+ else:
547
+ raise RuntimeError(
548
+ f"Configuration file `{os.path.split(self.filepath)[-1]}` does "
549
+ f"not correctly refer to one of the available XML schema files "
550
+ f"({objecttools.enumeration(filenames)})."
551
+ )
552
+ confpath: str = conf.__path__[0]
553
+ schemapath = os.path.join(confpath, schemafile)
554
+ schema = xmlschema.XMLSchema(schemapath)
555
+ schema.validate(self.filepath)
556
+ except BaseException:
557
+ objecttools.augment_excmessage(
558
+ f"While trying to validate XML file `{self.filepath}`"
559
+ )
560
+
561
+ def update_options(self) -> None:
562
+ """Update the |Options| object available in the |pub| module with the values
563
+ defined in the `options` XML element.
564
+
565
+ .. testsetup::
566
+
567
+ >>> from hydpy import pub
568
+ >>> del pub.timegrids
569
+ >>> del pub.options.simulationstep
570
+
571
+ >>> from hydpy.exe.xmltools import XMLInterface
572
+ >>> from hydpy import pub
573
+ >>> from hydpy.data import make_filepath
574
+ >>> interface = XMLInterface("single_run.xml", make_filepath("HydPy-H-Lahn"))
575
+ >>> pub.options.ellipsis = 0
576
+ >>> pub.options.parameterstep = "1h"
577
+ >>> pub.options.printprogress = True
578
+ >>> pub.options.reprdigits = -1
579
+ >>> pub.options.utcoffset = -60
580
+ >>> pub.options.timestampleft = False
581
+ >>> pub.options.warnsimulationstep = 0
582
+ >>> interface.update_options()
583
+ >>> pub.options
584
+ Options(
585
+ checkprojectstructure -> TRUE
586
+ checkseries -> TRUE
587
+ ellipsis -> 0
588
+ parameterstep -> Period("1d")
589
+ printprogress -> FALSE
590
+ reprdigits -> 6
591
+ simulationstep -> Period()
592
+ timestampleft -> TRUE
593
+ trimvariables -> TRUE
594
+ usecython -> TRUE
595
+ usedefaultvalues -> FALSE
596
+ utclongitude -> 15
597
+ utcoffset -> 60
598
+ warnmissingcontrolfile -> FALSE
599
+ warnmissingobsfile -> TRUE
600
+ warnmissingsimfile -> TRUE
601
+ warnsimulationstep -> FALSE
602
+ warntrim -> TRUE
603
+ )
604
+ >>> pub.options.checkprojectstructure = False
605
+ >>> pub.options.printprogress = False
606
+ >>> pub.options.reprdigits = 6
607
+ """
608
+ options = hydpy.pub.options
609
+ for option in self.find("options", optional=False):
610
+ value = option.text
611
+ if value in ("true", "false"):
612
+ setattr(options, strip(option.tag), value == "true")
613
+ else:
614
+ setattr(options, strip(option.tag), value)
615
+ options.printprogress = False
616
+
617
+ def update_timegrids(self) -> None:
618
+ """Update the |Timegrids| object available in the |pub| module with the values
619
+ defined in the `timegrid` XML element.
620
+
621
+ >>> from hydpy.core.testtools import prepare_full_example_1
622
+ >>> prepare_full_example_1()
623
+ >>> from hydpy import HydPy, pub, TestIO
624
+ >>> from hydpy.exe.xmltools import XMLInterface
625
+ >>> hp = HydPy("HydPy-H-Lahn")
626
+ >>> with TestIO():
627
+ ... XMLInterface("single_run.xml").update_timegrids()
628
+ >>> pub.timegrids
629
+ Timegrids("1996-01-01T00:00:00",
630
+ "1996-01-06T00:00:00",
631
+ "1d")
632
+ """
633
+ timegrid_xml = self.find("timegrid", optional=False)
634
+ firstdate = timegrid_xml[0].text
635
+ assert firstdate is not None
636
+ lastdate = timegrid_xml[1].text
637
+ assert lastdate is not None
638
+ stepsize = timegrid_xml[2].text
639
+ assert stepsize is not None
640
+ hydpy.pub.timegrids = (firstdate, lastdate, stepsize)
641
+
642
+ def update_selections(self) -> None:
643
+ """Create |Selection| objects based on the `add_selections` XML element and
644
+ add them to the |Selections| object available in module |pub|.
645
+
646
+ The `Lahn` example project comes with three selections:
647
+
648
+ >>> from hydpy.core.testtools import prepare_full_example_1
649
+ >>> prepare_full_example_1()
650
+ >>> from hydpy import HydPy, pub, TestIO
651
+ >>> from hydpy.exe.xmltools import find, XMLInterface
652
+ >>> hp = HydPy("HydPy-H-Lahn")
653
+ >>> with TestIO():
654
+ ... hp.prepare_network()
655
+ ... interface = XMLInterface("single_run.xml")
656
+ >>> pub.selections
657
+ Selections("headwaters", "nonheadwaters", "streams")
658
+
659
+ Following the definitions of the `add_selections` element of the configuration
660
+ file `single_run.xml`, method |XMLInterface.update_selections| creates three
661
+ additional selections based on given device names, keywords, or selection
662
+ names (you could also combine these subelements):
663
+
664
+ >>> interface.update_selections()
665
+ >>> pub.selections
666
+ Selections("from_devices", "from_keywords", "from_selections",
667
+ "headwaters", "nonheadwaters", "streams")
668
+ >>> pub.selections.from_devices
669
+ Selection("from_devices",
670
+ nodes=(),
671
+ elements=("land_lahn_leun", "land_lahn_marb"))
672
+ >>> pub.selections.from_keywords
673
+ Selection("from_keywords",
674
+ nodes=(),
675
+ elements=("land_dill_assl", "land_lahn_kalk",
676
+ "land_lahn_leun", "land_lahn_marb"))
677
+ >>> pub.selections.from_selections
678
+ Selection("from_selections",
679
+ nodes=("dill_assl", "lahn_kalk", "lahn_leun", "lahn_marb"),
680
+ elements=("land_dill_assl", "land_lahn_kalk",
681
+ "land_lahn_leun", "land_lahn_marb",
682
+ "stream_dill_assl_lahn_leun",
683
+ "stream_lahn_leun_lahn_kalk",
684
+ "stream_lahn_marb_lahn_leun"))
685
+
686
+ Defining wrong device names, keywords, or selection names results in error
687
+ messages:
688
+
689
+ >>> add_selections = find(interface.root, "add_selections")
690
+ >>> add_selections[2][1].text = "streams no_selection"
691
+ >>> interface.update_selections()
692
+ Traceback (most recent call last):
693
+ ...
694
+ RuntimeError: The XML configuration file tried to add the devices of a \
695
+ selection named `no_selection` to the custom selection `from_selections` but none \
696
+ of the available selections has this name.
697
+
698
+ >>> add_selections[1][1].text = "catchment no_keyword"
699
+ >>> interface.update_selections()
700
+ Traceback (most recent call last):
701
+ ...
702
+ RuntimeError: The XML configuration file tried to add at least one device \
703
+ based on the keyword `no_keyword` to the custom selection `from_keywords` but none \
704
+ of the available devices has this keyword.
705
+
706
+ >>> add_selections[0][1].text = "dill_assl no_device"
707
+ >>> interface.update_selections()
708
+ Traceback (most recent call last):
709
+ ...
710
+ RuntimeError: The XML configuration file tried to add a device named \
711
+ `no_device` to the custom selection `from_devices` but none of the available \
712
+ devices has this name.
713
+ """
714
+
715
+ def _get_texts(root: ElementTree.Element, name: str) -> list[str]:
716
+ xmlelement = find(root=root, name=name, optional=True)
717
+ if xmlelement is None or xmlelement.text is None:
718
+ return []
719
+ return xmlelement.text.split()
720
+
721
+ elements = hydpy.pub.selections.complete.elements
722
+ nodes = hydpy.pub.selections.complete.nodes
723
+ add_selections = find(self.root, "add_selections", optional=True)
724
+ if add_selections is not None:
725
+ for add_selection in add_selections:
726
+ name_new_selection = find(add_selection, "name", optional=False).text
727
+ assert name_new_selection is not None
728
+ new_selection = selectiontools.Selection(name_new_selection)
729
+ for name_device in _get_texts(add_selection, "devices"):
730
+ try:
731
+ element = elements[name_device]
732
+ new_selection.elements.add_device(element)
733
+ except KeyError:
734
+ try:
735
+ node = nodes[name_device]
736
+ new_selection.nodes.add_device(node)
737
+ except KeyError:
738
+ raise RuntimeError(
739
+ f"The XML configuration file tried to add a device "
740
+ f"named `{name_device}` to the custom selection "
741
+ f"`{name_new_selection}` but none of the available "
742
+ f"devices has this name."
743
+ ) from None
744
+ for keyword in _get_texts(add_selection, "keywords"):
745
+ nmb_devices = len(new_selection)
746
+ new_selection.elements += elements.search_keywords(keyword)
747
+ new_selection.nodes += nodes.search_keywords(keyword)
748
+ if len(new_selection) == nmb_devices:
749
+ raise RuntimeError(
750
+ f"The XML configuration file tried to add at least one "
751
+ f"device based on the keyword `{keyword}` to the custom "
752
+ f"selection `{name_new_selection}` but none of the "
753
+ f"available devices has this keyword."
754
+ ) from None
755
+ for name_old_selection in _get_texts(add_selection, "selections"):
756
+ try:
757
+ new_selection += hydpy.pub.selections[name_old_selection]
758
+ except KeyError:
759
+ raise RuntimeError(
760
+ f"The XML configuration file tried to add the devices of "
761
+ f"a selection named `{name_old_selection}` to the custom "
762
+ f"selection `{name_new_selection}` but none of the "
763
+ f"available selections has this name."
764
+ ) from None
765
+ hydpy.pub.selections += new_selection
766
+
767
+ @property
768
+ def selections(self) -> selectiontools.Selections:
769
+ """The |Selections| object defined on the main level of the actual XML file.
770
+
771
+ >>> from hydpy.core.testtools import prepare_full_example_1
772
+ >>> prepare_full_example_1()
773
+
774
+ >>> from hydpy import HydPy, TestIO, XMLInterface
775
+ >>> hp = HydPy("HydPy-H-Lahn")
776
+ >>> with TestIO():
777
+ ... hp.prepare_network()
778
+ ... interface = XMLInterface("single_run.xml")
779
+ >>> interface.update_selections()
780
+ >>> interface.find("selections").text = "headwaters streams"
781
+ >>> selections = interface.selections
782
+ >>> for selection in selections:
783
+ ... print(selection.name)
784
+ headwaters
785
+ streams
786
+ >>> selections.headwaters
787
+ Selection("headwaters",
788
+ nodes=("dill_assl", "lahn_marb"),
789
+ elements=("land_dill_assl", "land_lahn_marb"))
790
+ >>> interface.find("selections").text = "head_waters"
791
+ >>> interface.selections
792
+ Traceback (most recent call last):
793
+ ...
794
+ NameError: The XML configuration file tries to define a selection using the \
795
+ text `head_waters`, but the actual project does not handle such a `Selection` object.
796
+ """
797
+ return _query_selections(self.find("selections", optional=False))
798
+
799
+ @property
800
+ def elements(self) -> Iterator[devicetools.Element]:
801
+ """Yield all |Element| objects returned by |XMLInterface.selections| without
802
+ duplicates.
803
+
804
+ >>> from hydpy.core.testtools import prepare_full_example_1
805
+ >>> prepare_full_example_1()
806
+
807
+ >>> from hydpy import HydPy, TestIO, XMLInterface
808
+ >>> hp = HydPy("HydPy-H-Lahn")
809
+ >>> with TestIO():
810
+ ... hp.prepare_network()
811
+ ... interface = XMLInterface("single_run.xml")
812
+ >>> interface.update_timegrids()
813
+ >>> interface.update_selections()
814
+ >>> interface.find("selections").text = "headwaters streams"
815
+ >>> for element in interface.elements:
816
+ ... print(element.name)
817
+ land_dill_assl
818
+ land_lahn_marb
819
+ stream_dill_assl_lahn_leun
820
+ stream_lahn_leun_lahn_kalk
821
+ stream_lahn_marb_lahn_leun
822
+ """
823
+ selections = copy.copy(self.selections)
824
+ elements: set[devicetools.Element] = set()
825
+ for selection in selections:
826
+ for element in selection.elements:
827
+ if element not in elements:
828
+ elements.add(element)
829
+ yield element
830
+
831
+ @property
832
+ def fullselection(self) -> selectiontools.Selection:
833
+ """A |Selection| object that contains all |Element| and |Node| objects defined
834
+ by |XMLInterface.selections|.
835
+
836
+ >>> from hydpy.core.testtools import prepare_full_example_1
837
+ >>> prepare_full_example_1()
838
+
839
+ >>> from hydpy import HydPy, TestIO, XMLInterface
840
+ >>> hp = HydPy("HydPy-H-Lahn")
841
+ >>> with TestIO():
842
+ ... hp.prepare_network()
843
+ ... interface = XMLInterface("single_run.xml")
844
+ >>> interface.update_selections()
845
+ >>> interface.find("selections").text = "nonheadwaters"
846
+ >>> interface.fullselection
847
+ Selection("fullselection",
848
+ nodes=("lahn_kalk", "lahn_leun"),
849
+ elements=("land_lahn_kalk", "land_lahn_leun"))
850
+ >>> interface.find("selections").text = "from_keywords"
851
+ >>> interface.fullselection
852
+ Selection("fullselection",
853
+ nodes=(),
854
+ elements=("land_dill_assl", "land_lahn_kalk",
855
+ "land_lahn_leun", "land_lahn_marb"))
856
+ """
857
+ fullselection = selectiontools.Selection("fullselection")
858
+ for selection in self.selections:
859
+ fullselection += selection
860
+ return fullselection
861
+
862
+ @property
863
+ def network_io(self) -> XMLNetworkDefault | XMLNetworkUserDefined:
864
+ """The `network_io` element defined in the actual XML file.
865
+
866
+ >>> from hydpy.exe.xmltools import XMLInterface, strip
867
+ >>> from hydpy.data import make_filepath
868
+ >>> interface = XMLInterface("single_run.xml", make_filepath("HydPy-H-Lahn"))
869
+ >>> interface.network_io.text
870
+ 'default'
871
+ >>> interface = XMLInterface("multiple_runs.xml", make_filepath("HydPy-H-Lahn"))
872
+ >>> interface.network_io.text
873
+ 'default'
874
+ """
875
+ network_io = self.find("network_io", optional=True)
876
+ if network_io is None:
877
+ return XMLNetworkDefault(self, text="default")
878
+ return XMLNetworkUserDefined(self, network_io, text=network_io.text)
879
+
880
+ @property
881
+ def control_io(self) -> XMLControlDefault | XMLControlUserDefined:
882
+ """The `control_io` element defined in the actual XML file.
883
+
884
+ >>> from hydpy.exe.xmltools import XMLInterface, strip
885
+ >>> from hydpy.data import make_filepath
886
+ >>> interface = XMLInterface("single_run.xml", make_filepath("HydPy-H-Lahn"))
887
+ >>> interface.control_io.text
888
+ 'default'
889
+ >>> interface = XMLInterface("multiple_runs.xml", make_filepath("HydPy-H-Lahn"))
890
+ >>> interface.control_io.text
891
+ 'default'
892
+ """
893
+ control_io = self.find("control_io", optional=True)
894
+ if control_io is None:
895
+ return XMLControlDefault(self, text="default")
896
+ return XMLControlUserDefined(self, control_io, text=control_io.text)
897
+
898
+ @property
899
+ def conditions_io(self) -> XMLConditions:
900
+ """The `condition_io` element defined in the actual XML file.
901
+
902
+ >>> from hydpy.exe.xmltools import XMLInterface, strip
903
+ >>> from hydpy.data import make_filepath
904
+ >>> interface = XMLInterface("single_run.xml", make_filepath("HydPy-H-Lahn"))
905
+ >>> strip(interface.series_io.root.tag)
906
+ 'series_io'
907
+ """
908
+ return XMLConditions(self, self.find("conditions_io", optional=False))
909
+
910
+ @property
911
+ def series_io(self) -> XMLSeries:
912
+ """The `series_io` element defined in the actual XML file.
913
+
914
+ >>> from hydpy.exe.xmltools import XMLInterface, strip
915
+ >>> from hydpy.data import make_filepath
916
+ >>> interface = XMLInterface("single_run.xml", make_filepath("HydPy-H-Lahn"))
917
+ >>> strip(interface.series_io.root.tag)
918
+ 'series_io'
919
+ """
920
+ return XMLSeries(self, self.find("series_io", optional=False))
921
+
922
+ @property
923
+ def exchange(self) -> XMLExchange:
924
+ """The `exchange` element defined in the actual XML file.
925
+
926
+ >>> from hydpy.exe.xmltools import XMLInterface, strip
927
+ >>> from hydpy.data import make_filepath
928
+ >>> interface = XMLInterface(
929
+ ... "multiple_runs.xml", make_filepath("HydPy-H-Lahn"))
930
+ >>> strip(interface.exchange.root.tag)
931
+ 'exchange'
932
+ """
933
+ return XMLExchange(self, self.find("exchange", optional=False))
934
+
935
+
936
+ class XMLNetworkBase:
937
+ """Base class for |XMLNetworkDefault| and |XMLNetworkUserDefined|."""
938
+
939
+ master: XMLInterface
940
+ text: str | None
941
+
942
+ def prepare_network(self) -> None:
943
+ """Prepare the |Selections| object available in the global |pub| module:
944
+
945
+ >>> from hydpy.core.testtools import prepare_full_example_1
946
+ >>> prepare_full_example_1()
947
+
948
+ >>> from hydpy import attrready, HydPy, pub, TestIO, XMLInterface
949
+ >>> hp = HydPy("HydPy-H-Lahn")
950
+ >>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
951
+ >>> with TestIO():
952
+ ... interface = XMLInterface("single_run.xml")
953
+ ... interface.find("network_io").text = "wrong"
954
+ ... interface.network_io.prepare_network() # doctest: +ELLIPSIS
955
+ Traceback (most recent call last):
956
+ ...
957
+ RuntimeError: The directory `...wrong` does not contain any network files.
958
+
959
+ >>> with TestIO():
960
+ ... interface = XMLInterface("single_run.xml")
961
+ ... interface.find("network_io").text = "default"
962
+ ... interface.network_io.prepare_network() # doctest: +ELLIPSIS
963
+ >>> pub.selections
964
+ Selections("headwaters", "nonheadwaters", "streams")
965
+ """
966
+ if self.text:
967
+ hydpy.pub.networkmanager.currentdir = str(self.text)
968
+ hydpy.pub.selections = hydpy.pub.networkmanager.load_files()
969
+
970
+
971
+ class XMLNetworkDefault(XMLNetworkBase):
972
+ """Helper class for |XMLInterface| responsible for loading devices from network
973
+ files when the XML file does not specify a network directory."""
974
+
975
+ def __init__(self, master: XMLInterface, text: str | None) -> None:
976
+ self.master: XMLInterface = master
977
+ self.text: str | None = text
978
+
979
+
980
+ class XMLNetworkUserDefined(XMLBase, XMLNetworkBase):
981
+ """Helper class for |XMLInterface| responsible for loading devices from network
982
+ files when the XML file specifies a network directory."""
983
+
984
+ def __init__(
985
+ self, master: XMLInterface, root: ElementTree.Element, text: str | None
986
+ ) -> None:
987
+ self.master: XMLInterface = master
988
+ self.root: ElementTree.Element = root
989
+ self.text: str | None = text
990
+
991
+
992
+ class XMLControlBase:
993
+ """Base class for |XMLControlDefault| and |XMLControlUserDefined|."""
994
+
995
+ master: XMLInterface
996
+ text: str | None
997
+
998
+ def prepare_models(self) -> None:
999
+ """Prepare the |Model| objects of all |Element| objects returned by
1000
+ |XMLInterface.elements|:
1001
+
1002
+ >>> from hydpy.core.testtools import prepare_full_example_1
1003
+ >>> prepare_full_example_1()
1004
+
1005
+ >>> from hydpy import attrready, HydPy, pub, TestIO, XMLInterface
1006
+ >>> hp = HydPy("HydPy-H-Lahn")
1007
+ >>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
1008
+ >>> with TestIO():
1009
+ ... hp.prepare_network()
1010
+ ... interface = XMLInterface("single_run.xml")
1011
+ ... interface.update_selections()
1012
+ ... interface.find("selections").text = "headwaters"
1013
+ ... interface.control_io.prepare_models()
1014
+ >>> interface.update_timegrids()
1015
+ >>> hp.elements.land_lahn_marb.model.parameters.control.alpha
1016
+ alpha(1.0)
1017
+ >>> attrready(hp.elements.land_lahn_leun, "model")
1018
+ False
1019
+ """
1020
+ if self.text:
1021
+ hydpy.pub.controlmanager.currentdir = str(self.text)
1022
+ for element in self.master.elements:
1023
+ element.prepare_model()
1024
+
1025
+
1026
+ class XMLControlDefault(XMLControlBase):
1027
+ """Helper class for |XMLInterface| responsible for loading models from control
1028
+ files when the XML file does not specify a control directory."""
1029
+
1030
+ def __init__(self, master: XMLInterface, text: str | None) -> None:
1031
+ self.master: XMLInterface = master
1032
+ self.text: str | None = text
1033
+
1034
+
1035
+ class XMLControlUserDefined(XMLBase, XMLControlBase):
1036
+ """Helper class for |XMLInterface| responsible for loading models from control
1037
+ files when the XML file specifies a control directory."""
1038
+
1039
+ def __init__(
1040
+ self, master: XMLInterface, root: ElementTree.Element, text: str | None
1041
+ ) -> None:
1042
+ self.master: XMLInterface = master
1043
+ self.root: ElementTree.Element = root
1044
+ self.text: str | None = text
1045
+
1046
+
1047
+ class XMLConditions(XMLBase):
1048
+ """Helper class for |XMLInterface| responsible for loading and saving initial
1049
+ conditions."""
1050
+
1051
+ def __init__(self, master: XMLInterface, root: ElementTree.Element) -> None:
1052
+ self.master: XMLInterface = master
1053
+ self.root: ElementTree.Element = root
1054
+
1055
+ def _determine_currentdir(
1056
+ self, currentdir: str | None, type_: Literal["input", "output"], /
1057
+ ) -> str:
1058
+ if currentdir is not None:
1059
+ return currentdir
1060
+ if (inputdir := self.find(f"{type_}dir")) is not None:
1061
+ return str(inputdir.text)
1062
+ conditionmanager = hydpy.pub.conditionmanager
1063
+ prefix = None if (p := self.find("prefix")) is None else str(p.text)
1064
+ with conditionmanager.prefix(prefix):
1065
+ return getattr(conditionmanager, f"{type_}path")
1066
+
1067
+ def load_conditions(self, currentdir: str | None = None) -> None:
1068
+ """Load the condition files of the |Model| objects of all |Element| objects
1069
+ returned by |XMLInterface.elements|:
1070
+
1071
+ >>> from hydpy.core.testtools import prepare_full_example_1
1072
+ >>> prepare_full_example_1()
1073
+
1074
+ >>> from hydpy import HydPy, pub, TestIO, XMLInterface
1075
+ >>> hp = HydPy("HydPy-H-Lahn")
1076
+ >>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
1077
+ >>> with TestIO():
1078
+ ... hp.prepare_network()
1079
+ ... hp.prepare_models()
1080
+ ... interface = XMLInterface("single_run.xml")
1081
+ ... interface.update_selections()
1082
+ ... interface.find("selections").text = "headwaters"
1083
+ ... interface.conditions_io.load_conditions()
1084
+ >>> hp.elements.land_lahn_marb.model.sequences.states.lz
1085
+ lz(8.18711)
1086
+ >>> hp.elements.land_lahn_leun.model.sequences.states.lz
1087
+ lz(nan)
1088
+
1089
+ >>> from hydpy import xml_replace
1090
+ >>> with TestIO():
1091
+ ... xml_replace("HydPy-H-Lahn/single_run", printflag=False, prefix="condi")
1092
+ ... interface = XMLInterface("single_run.xml")
1093
+ ... interface.update_selections()
1094
+ ... interface.conditions_io.load_conditions() # doctest: +ELLIPSIS
1095
+ Traceback (most recent call last):
1096
+ ...
1097
+ FileNotFoundError: While trying to load the initial conditions of element \
1098
+ `land_dill_assl`, the following error occurred: [Errno 2] No such file or directory: \
1099
+ '...condi_1996_01_01_00_00_00...land_dill_assl.py'
1100
+ """
1101
+ cm = hydpy.pub.conditionmanager
1102
+ try:
1103
+ cm.currentdir = self._determine_currentdir(currentdir, "input")
1104
+ for element in self.master.elements:
1105
+ element.model.load_conditions()
1106
+ finally:
1107
+ cm.currentdir = None # type: ignore[assignment]
1108
+
1109
+ def save_conditions(self, currentdir: str | None = None) -> None:
1110
+ """Save the condition files of the |Model| objects of all |Element| objects
1111
+ returned by |XMLInterface.elements|:
1112
+
1113
+ >>> from hydpy.core.testtools import prepare_full_example_1
1114
+ >>> prepare_full_example_1()
1115
+
1116
+ >>> import os
1117
+ >>> from hydpy import HydPy, pub, TestIO, XMLInterface
1118
+ >>> hp = HydPy("HydPy-H-Lahn")
1119
+ >>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
1120
+ >>> with TestIO():
1121
+ ... hp.prepare_network()
1122
+ ... hp.prepare_models()
1123
+ ... hp.elements.land_dill_assl.model.sequences.states.lz = 999.0
1124
+ ... interface = XMLInterface("single_run.xml")
1125
+ ... interface.update_selections()
1126
+ ... interface.find("selections").text = "headwaters"
1127
+ ... interface.conditions_io.save_conditions()
1128
+ ... dirpath = "HydPy-H-Lahn/conditions/init_1996_01_06"
1129
+ ... with open(os.path.join(dirpath, "land_dill_assl.py")) as file_:
1130
+ ... print(file_.readlines()[10].strip())
1131
+ ... os.path.exists(os.path.join(dirpath, "land_lahn_leun.py"))
1132
+ lz(999.0)
1133
+ False
1134
+ >>> from hydpy import xml_replace
1135
+ >>> with TestIO():
1136
+ ... xml_replace("HydPy-H-Lahn/single_run", printflag=False, zip_="true")
1137
+ ... interface = XMLInterface("single_run.xml")
1138
+ ... interface.find("selections").text = "headwaters"
1139
+ ... os.path.exists("HydPy-H-Lahn/conditions/init_1996_01_06.zip")
1140
+ ... interface.conditions_io.save_conditions()
1141
+ ... os.path.exists("HydPy-H-Lahn/conditions/init_1996_01_06.zip")
1142
+ False
1143
+ True
1144
+ """
1145
+ cm = hydpy.pub.conditionmanager
1146
+ try:
1147
+ cm.currentdir = self._determine_currentdir(currentdir, "output")
1148
+ for element in self.master.elements:
1149
+ element.model.save_conditions()
1150
+ if (zip_ := self.find("zip")) is not None:
1151
+ zip__ = str(zip_.text)
1152
+ if objecttools.value2bool(zip__, zip__):
1153
+ cm.zip_currentdir()
1154
+ finally:
1155
+ cm.currentdir = None # type: ignore[assignment]
1156
+
1157
+
1158
+ class XMLSeries(XMLBase):
1159
+ """Helper class for |XMLInterface| responsible for loading and saving time series
1160
+ data, which is further delegated to suitable instances of class |XMLSubseries|."""
1161
+
1162
+ def __init__(self, master: XMLInterface, root: ElementTree.Element) -> None:
1163
+ self.master: XMLInterface = master
1164
+ self.root: ElementTree.Element = root
1165
+
1166
+ @property
1167
+ def readers(self) -> list[XMLSubseries]:
1168
+ """The reader XML elements defined in the actual XML file.
1169
+
1170
+ >>> from hydpy.exe.xmltools import XMLInterface
1171
+ >>> from hydpy.data import make_filepath
1172
+ >>> interface = XMLInterface("single_run.xml", make_filepath("HydPy-H-Lahn"))
1173
+ >>> for reader in interface.series_io.readers:
1174
+ ... print(reader.info)
1175
+ all input data
1176
+ """
1177
+ return [XMLSubseries(self, _) for _ in self.find("readers", optional=False)]
1178
+
1179
+ @property
1180
+ def writers(self) -> list[XMLSubseries]:
1181
+ """The writer XML elements defined in the actual XML file.
1182
+
1183
+ >>> from hydpy.exe.xmltools import XMLInterface
1184
+ >>> from hydpy.data import make_filepath
1185
+ >>> interface = XMLInterface("single_run.xml", make_filepath("HydPy-H-Lahn"))
1186
+ >>> for writer in interface.series_io.writers:
1187
+ ... print(writer.info)
1188
+ precipitation
1189
+ soilmoisture
1190
+ averaged
1191
+ """
1192
+ return [XMLSubseries(self, _) for _ in self.find("writers", optional=False)]
1193
+
1194
+ def prepare_series(self) -> None:
1195
+ """Call |XMLSubseries.prepare_series| of all |XMLSubseries| objects."""
1196
+ for ioseries in itertools.chain(self.readers, self.writers):
1197
+ ioseries.prepare_series()
1198
+
1199
+ def load_series(self, currentdir: str | None = None) -> None:
1200
+ """Call |XMLSubseries.load_series| of all |XMLSubseries| objects handled as
1201
+ "readers"."""
1202
+ for reader in self.readers:
1203
+ reader.load_series(currentdir)
1204
+
1205
+ def save_series(self, currentdir: str | None = None) -> None:
1206
+ """Call |XMLSubseries.load_series| of all |XMLSubseries| objects handled as
1207
+ "writers"."""
1208
+ for writer in self.writers:
1209
+ writer.save_series(currentdir)
1210
+
1211
+ @contextlib.contextmanager
1212
+ def modify_inputdir(self, currentdir: str | None = None) -> Iterator[None]:
1213
+ """Temporarily modify the |IOSequence.dirpath| of all |IOSequence| objects
1214
+ registered for reading time series data "just in time" during a simulation
1215
+ run."""
1216
+ try:
1217
+ for reader in self.readers:
1218
+ reader.change_dirpath(currentdir)
1219
+ yield
1220
+ finally:
1221
+ for reader in self.readers:
1222
+ reader.reset_dirpath()
1223
+
1224
+ @contextlib.contextmanager
1225
+ def modify_outputdir(self, currentdir: str | None = None) -> Iterator[None]:
1226
+ """Temporarily modify the |IOSequence.dirpath| of all |IOSequence| objects
1227
+ registered for writing time series data "just in time" during a simulation
1228
+ run."""
1229
+ try:
1230
+ for writer in self.writers:
1231
+ writer.change_dirpath(currentdir)
1232
+ yield
1233
+ finally:
1234
+ for writer in self.writers:
1235
+ writer.reset_dirpath()
1236
+
1237
+
1238
+ class XMLSelector(XMLBase):
1239
+ """Base class for |XMLSubseries| and |XMLVar| responsible for querying the relevant
1240
+ |Node| and |Element| objects."""
1241
+
1242
+ master: XMLBase
1243
+ root: ElementTree.Element
1244
+
1245
+ @property
1246
+ def selections(self) -> selectiontools.Selections:
1247
+ """The |Selections| object defined for the respective respective IO series or
1248
+ exchange item elements of the actual XML file.
1249
+
1250
+ Property |XMLSelector.selections| of class |XMLSelector| falls back to the
1251
+ general property |XMLInterface.selections| of |XMLInterface| if the relevant IO
1252
+ series or exchange item element does not define a unique selection:
1253
+
1254
+ >>> from hydpy.core.testtools import prepare_full_example_1
1255
+ >>> prepare_full_example_1()
1256
+
1257
+ >>> from hydpy import HydPy, pub, TestIO, XMLInterface
1258
+ >>> hp = HydPy("HydPy-H-Lahn")
1259
+ >>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
1260
+ >>> with TestIO():
1261
+ ... hp.prepare_network()
1262
+ ... hp.prepare_models()
1263
+ ... interface = XMLInterface("single_run.xml")
1264
+ >>> interface.update_selections()
1265
+ >>> series_io = interface.series_io
1266
+ >>> for seq in (series_io.readers + series_io.writers):
1267
+ ... print(seq.info, seq.selections.names)
1268
+ all input data ('from_keywords',)
1269
+ precipitation ('headwaters', 'from_devices')
1270
+ soilmoisture ('__complete__',)
1271
+ averaged ('from_selections',)
1272
+
1273
+ If property |XMLSelector.selections| does not find any definitions, it raises
1274
+ the following error:
1275
+
1276
+ >>> interface.root.remove(interface.find("selections"))
1277
+ >>> series_io = interface.series_io
1278
+ >>> for seq in (series_io.readers + series_io.writers):
1279
+ ... print(seq.info, seq.selections.names)
1280
+ Traceback (most recent call last):
1281
+ ...
1282
+ AttributeError: Unable to find a XML element named "selections". Please make \
1283
+ sure your XML file follows the relevant XML schema.
1284
+ """
1285
+ selections = self.find("selections")
1286
+ master: XMLBase | None = self
1287
+ while selections is None:
1288
+ master = getattr(master, "master", None)
1289
+ if master is None:
1290
+ raise AttributeError(
1291
+ 'Unable to find a XML element named "selections". Please make '
1292
+ "sure your XML file follows the relevant XML schema."
1293
+ )
1294
+ selections = master.find("selections")
1295
+ return _query_selections(selections)
1296
+
1297
+ @overload
1298
+ def _get_devices(self, attr: Literal["nodes"]) -> Iterator[devicetools.Node]:
1299
+ """Extract all nodes."""
1300
+
1301
+ @overload
1302
+ def _get_devices(self, attr: Literal["elements"]) -> Iterator[devicetools.Element]:
1303
+ """Extract all elements."""
1304
+
1305
+ def _get_devices(
1306
+ self, attr: Literal["nodes", "elements"]
1307
+ ) -> Iterator[devicetools.Node] | Iterator[devicetools.Element]:
1308
+ """Extract all nodes or elements."""
1309
+ selections = copy.copy(self.selections)
1310
+ devices = set()
1311
+ for selection in selections:
1312
+ for device in getattr(selection, attr):
1313
+ if device not in devices:
1314
+ devices.add(device)
1315
+ yield device
1316
+
1317
+ @property
1318
+ def elements(self) -> Iterator[devicetools.Element]:
1319
+ """Return the |Element| objects selected by the actual IO series or exchange
1320
+ item element.
1321
+
1322
+ >>> from hydpy.core.testtools import prepare_full_example_1
1323
+ >>> prepare_full_example_1()
1324
+
1325
+ >>> from hydpy import HydPy, TestIO, XMLInterface
1326
+ >>> hp = HydPy("HydPy-H-Lahn")
1327
+ >>> with TestIO():
1328
+ ... hp.prepare_network()
1329
+ ... interface = XMLInterface("single_run.xml")
1330
+ >>> interface.update_selections()
1331
+ >>> for element in interface.series_io.writers[0].elements:
1332
+ ... print(element.name)
1333
+ land_dill_assl
1334
+ land_lahn_marb
1335
+ land_lahn_leun
1336
+ """
1337
+ return self._get_devices("elements")
1338
+
1339
+ @property
1340
+ def nodes(self) -> Iterator[devicetools.Node]:
1341
+ """Return the |Node| objects selected by the actual IO series or exchange item
1342
+ element.
1343
+
1344
+ >>> from hydpy.core.testtools import prepare_full_example_1
1345
+ >>> prepare_full_example_1()
1346
+
1347
+ >>> from hydpy import HydPy, TestIO, XMLInterface
1348
+ >>> hp = HydPy("HydPy-H-Lahn")
1349
+ >>> with TestIO():
1350
+ ... hp.prepare_network()
1351
+ ... interface = XMLInterface("single_run.xml")
1352
+ >>> interface.update_selections()
1353
+ >>> for node in interface.series_io.writers[0].nodes:
1354
+ ... print(node.name)
1355
+ dill_assl
1356
+ lahn_marb
1357
+ """
1358
+ return self._get_devices("nodes")
1359
+
1360
+
1361
+ class XMLSubseries(XMLSelector):
1362
+ """Helper class for |XMLSeries| responsible for loading and saving time series
1363
+ data."""
1364
+
1365
+ def __init__(self, master: XMLSeries, root: ElementTree.Element) -> None:
1366
+ self.master: XMLSeries = master
1367
+ self.root: ElementTree.Element = root
1368
+
1369
+ @property
1370
+ def info(self) -> str:
1371
+ """Info attribute of the actual XML `reader` or `writer` element."""
1372
+ return self.root.attrib["info"]
1373
+
1374
+ @property
1375
+ def _is_reader(self) -> bool:
1376
+ if self.name == "reader":
1377
+ return True
1378
+ if self.name == "writer":
1379
+ return False
1380
+ assert False
1381
+
1382
+ @property
1383
+ def _is_writer(self) -> bool:
1384
+ return not self._is_reader
1385
+
1386
+ def prepare_sequencemanager(self, currentdir: str | None = None) -> None:
1387
+ """Configure the |SequenceManager| object available in module |pub| following
1388
+ the definitions of the actual XML `reader` or `writer` element when available;
1389
+ if not, use those of the XML `series_io` element or fall back to the default.
1390
+
1391
+ Compare the following results with `single_run.xml` to see that the first
1392
+ `writer` element re-defines the default file type (`asc`), that the second
1393
+ `writer` element defines an alternative file type (`npy`), and that the third
1394
+ `writer` relies on the general file type. The base mechanism is the same
1395
+ for other options, e.g. the aggregation mode.
1396
+
1397
+ >>> from hydpy.core.testtools import prepare_full_example_1
1398
+ >>> prepare_full_example_1()
1399
+
1400
+ >>> from hydpy import HydPy, TestIO, XMLInterface, pub
1401
+ >>> hp = HydPy("HydPy-H-Lahn")
1402
+ >>> with TestIO():
1403
+ ... hp.prepare_network()
1404
+ ... interface = XMLInterface("single_run.xml")
1405
+ >>> series_io = interface.series_io
1406
+ >>> with TestIO():
1407
+ ... series_io.writers[0].prepare_sequencemanager()
1408
+ ... pub.sequencemanager.currentdir
1409
+ 'default'
1410
+ >>> pub.sequencemanager.filetype
1411
+ 'asc'
1412
+ >>> pub.sequencemanager.overwrite
1413
+ TRUE
1414
+ >>> pub.sequencemanager.aggregation
1415
+ 'none'
1416
+ >>> pub.sequencemanager.convention
1417
+ 'model-specific'
1418
+ >>> with TestIO():
1419
+ ... series_io.writers[1].prepare_sequencemanager()
1420
+ ... pub.sequencemanager.currentdir
1421
+ 'soildata'
1422
+ >>> pub.sequencemanager.filetype
1423
+ 'nc'
1424
+ >>> pub.sequencemanager.overwrite
1425
+ FALSE
1426
+ >>> pub.sequencemanager.aggregation
1427
+ 'none'
1428
+ >>> pub.sequencemanager.convention
1429
+ 'model-specific'
1430
+ >>> with TestIO():
1431
+ ... series_io.writers[2].prepare_sequencemanager()
1432
+ ... pub.sequencemanager.currentdir
1433
+ 'averages'
1434
+ >>> pub.sequencemanager.filetype
1435
+ 'npy'
1436
+ >>> pub.sequencemanager.aggregation
1437
+ 'mean'
1438
+ >>> pub.sequencemanager.overwrite
1439
+ TRUE
1440
+ >>> pub.sequencemanager.aggregation
1441
+ 'mean'
1442
+ >>> pub.sequencemanager.convention
1443
+ 'model-specific'
1444
+ """
1445
+ sm = hydpy.pub.sequencemanager
1446
+ if currentdir is None:
1447
+ element = self.find("directory")
1448
+ if element is None:
1449
+ element = self.master.find("directory")
1450
+ if element is None:
1451
+ sm.currentdir = "default"
1452
+ else:
1453
+ assert element.text is not None
1454
+ sm.currentdir = element.text
1455
+ else:
1456
+ sm.currentdir = currentdir
1457
+
1458
+ for option in ("filetype", "aggregation", "convention", "overwrite"):
1459
+ delattr(sm, option)
1460
+ for element in (self.find(option), self.master.find(option)):
1461
+ if element is not None:
1462
+ assert element.text is not None
1463
+ if option == "overwrite":
1464
+ sm.overwrite = objecttools.value2bool(option, element.text)
1465
+ else:
1466
+ setattr(sm, option, element.text)
1467
+ break
1468
+
1469
+ @property
1470
+ def _ramflag(self) -> bool:
1471
+ """A flag analoge to the |IOSequence.ramflag| of class |IOSequence|."""
1472
+ mode = self.find("mode")
1473
+ if mode is None:
1474
+ mode = self.master.find("mode")
1475
+ if mode is None:
1476
+ return True
1477
+ assert mode.text is not None
1478
+ return mode.text == "ram"
1479
+
1480
+ @property
1481
+ def model2subs2seqs(
1482
+ self,
1483
+ ) -> collections.defaultdict[str, collections.defaultdict[str, list[str]]]:
1484
+ """A nested |collections.defaultdict| containing the model-specific information
1485
+ provided by the XML `sequences` element.
1486
+
1487
+ >>> from hydpy.exe.xmltools import XMLInterface
1488
+ >>> from hydpy.data import make_filepath
1489
+ >>> interface = XMLInterface("single_run.xml", make_filepath("HydPy-H-Lahn"))
1490
+ >>> series_io = interface.series_io
1491
+ >>> model2subs2seqs = series_io.writers[2].model2subs2seqs
1492
+ >>> for model, subs2seqs in sorted(model2subs2seqs.items()):
1493
+ ... for subs, seq in sorted(subs2seqs.items()):
1494
+ ... print(model, subs, seq)
1495
+ hland_96 factors ['tc']
1496
+ hland_96 fluxes ['tf']
1497
+ hland_96 states ['sm']
1498
+ musk_classic states ['discharge']
1499
+ """
1500
+ model2subs2seqs: collections.defaultdict[
1501
+ str, collections.defaultdict[str, list[str]]
1502
+ ] = collections.defaultdict(lambda: collections.defaultdict(list))
1503
+ for model in self.find("sequences", optional=False):
1504
+ model_name = strip(model.tag)
1505
+ if model_name == "node":
1506
+ continue
1507
+ for group in model:
1508
+ group_name = strip(group.tag)
1509
+ for sequence in group:
1510
+ seq_name = strip(sequence.tag)
1511
+ model2subs2seqs[model_name][group_name].append(seq_name)
1512
+ return model2subs2seqs
1513
+
1514
+ @property
1515
+ def subs2seqs(self) -> dict[str, list[str]]:
1516
+ """A |collections.defaultdict| containing the node-specific information
1517
+ provided by XML `sequences` element.
1518
+
1519
+ >>> from hydpy.exe.xmltools import XMLInterface
1520
+ >>> from hydpy.data import make_filepath
1521
+ >>> interface = XMLInterface("single_run.xml", make_filepath("HydPy-H-Lahn"))
1522
+ >>> series_io = interface.series_io
1523
+ >>> subs2seqs = series_io.writers[2].subs2seqs
1524
+ >>> for subs, seq in sorted(subs2seqs.items()):
1525
+ ... print(subs, seq)
1526
+ node ['sim', 'obs']
1527
+ """
1528
+ subs2seqs: collections.defaultdict[str, list[str]]
1529
+ subs2seqs = collections.defaultdict(list)
1530
+ nodes = find(self.find("sequences", optional=False), "node")
1531
+ if nodes is not None:
1532
+ for seq in nodes:
1533
+ subs2seqs["node"].append(strip(seq.tag))
1534
+ return subs2seqs
1535
+
1536
+ def _iterate_sequences(self) -> Iterator[sequencetools.IOSequence]:
1537
+ return itertools.chain(
1538
+ self._iterate_model_sequences(), self._iterate_node_sequences()
1539
+ )
1540
+
1541
+ def _iterate_model_sequences(self) -> Iterator[sequencetools.IOSequence]:
1542
+ m2s2s = self.model2subs2seqs
1543
+ for element in self.elements:
1544
+ for model in element.model.find_submodels(include_mainmodel=True).values():
1545
+ for subseqs_name, seq_names in m2s2s.get(model.name, {}).items():
1546
+ subseqs = getattr(model.sequences, subseqs_name)
1547
+ for seq_name in seq_names:
1548
+ yield getattr(subseqs, seq_name)
1549
+
1550
+ def _iterate_node_sequences(self) -> Iterator[sequencetools.IOSequence]:
1551
+ s2s = self.subs2seqs
1552
+ for node in self.nodes:
1553
+ for seq_names in s2s.values():
1554
+ for seq_name in seq_names:
1555
+ yield getattr(node.sequences, seq_name)
1556
+
1557
+ def prepare_series(self) -> None:
1558
+ """Call method |IOSequence.prepare_series| of class |IOSequence| for all
1559
+ sequences selected by the given element of the actual XML file.
1560
+
1561
+ Method |IOSequence.prepare_series| solves a complex task, as it needs to
1562
+ determine the correct arguments for method |IOSequence.prepare_series| of class
1563
+ |IOSequence|. Those arguments depend on whether the respective |XMLSubseries|
1564
+ element is for reading or writing data, addresses input or output sequences,
1565
+ and if one prefers to handle time series data in RAM or read or write it "just
1566
+ in time" during model simulations. Method |XMLSubseries.prepare_series|
1567
+ delegates some of the related logic to the |PrepareSeriesArguments.from_xmldata|
1568
+ method of class |PrepareSeriesArguments|. The following examples demonstrate
1569
+ that method |XMLSubseries.prepare_series| implements the remaining logic
1570
+ correctly.
1571
+
1572
+ >>> from hydpy.core.testtools import prepare_full_example_1
1573
+ >>> prepare_full_example_1()
1574
+ >>> from hydpy import HydPy, pub, TestIO, XMLInterface
1575
+ >>> hp = HydPy("HydPy-H-Lahn")
1576
+ >>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
1577
+ >>> with TestIO():
1578
+ ... hp.prepare_network()
1579
+ ... hp.prepare_models()
1580
+ ... interface = XMLInterface("single_run.xml")
1581
+ >>> interface.update_timegrids()
1582
+ >>> interface.update_selections()
1583
+
1584
+ First, we check and discuss the proper setting of the properties
1585
+ |IOSequence.ramflag|, |IOSequence.diskflag_reading|, and
1586
+ |IOSequence.diskflag_writing| for the sequences defined in `single_run.xml`.
1587
+ First, we call |XMLSubseries.prepare_series| for available |XMLSubseries|
1588
+ objects:
1589
+
1590
+ >>> series_io = interface.series_io
1591
+ >>> for reader in series_io.readers:
1592
+ ... reader.prepare_series()
1593
+ >>> for writer in series_io.writers:
1594
+ ... writer.prepare_series()
1595
+
1596
+ The following test function prints the options of the specified sequence of
1597
+ element "Dill_assl":
1598
+
1599
+ >>> def print_io_options(groupname, sequencename):
1600
+ ... sequences = hp.elements.land_dill_assl.model.sequences
1601
+ ... sequence = sequences[groupname][sequencename]
1602
+ ... print(f"ramflag={sequence.ramflag}")
1603
+ ... print(f"diskflag_reading={sequence.diskflag_reading}")
1604
+ ... print(f"diskflag_writing={sequence.diskflag_writing}")
1605
+
1606
+ The XML file uses the `jit` mode for all non-aggregated time series. Reader
1607
+ elements handle input sequences and writer elements handle output sequences.
1608
+ Hence, |IOSequence.ramflag| is generally |False| while
1609
+ |IOSequence.diskflag_reading| is |True| for the input sequences and
1610
+ |IOSequence.diskflag_writing| is |True| for the output sequences:
1611
+
1612
+ >>> print_io_options("inputs", "p")
1613
+ ramflag=False
1614
+ diskflag_reading=True
1615
+ diskflag_writing=False
1616
+
1617
+ >>> print_io_options("fluxes", "pc")
1618
+ ramflag=False
1619
+ diskflag_reading=False
1620
+ diskflag_writing=True
1621
+
1622
+ Currently, aggregation only works in combination with mode `ram`:
1623
+
1624
+ >>> print_io_options("factors", "tc")
1625
+ ramflag=True
1626
+ diskflag_reading=False
1627
+ diskflag_writing=False
1628
+
1629
+ For sequence |hland_states.SM|, two writers apply. The writer "soil moisture"
1630
+ triggers writing the complete time series "just in time" during the simulation
1631
+ run. In contrast, the writer "averaged" initiates writing averaged time series
1632
+ after the simulation run. The configuration of sequence |hland_states.SM|
1633
+ reflects this, with both "ram flag" and "disk flag writing" being "True":
1634
+
1635
+ >>> print_io_options("states", "sm")
1636
+ ramflag=True
1637
+ diskflag_reading=False
1638
+ diskflag_writing=True
1639
+
1640
+ We temporarily convert the single reader element to a writer element and apply
1641
+ method |XMLSubseries.prepare_series| again. At first, this does not work, as
1642
+ the affected input sequences have previously been configured for "just in time"
1643
+ reading, which does not work in combination with "just in time" writing:
1644
+
1645
+ >>> reader = series_io.readers[0]
1646
+ >>> reader.root.tag = reader.root.tag.replace("reader", "writer")
1647
+ >>> reader.prepare_series()
1648
+ Traceback (most recent call last):
1649
+ ...
1650
+ ValueError: Reading from and writing into the same NetCDF file "just in time" \
1651
+ during a simulation run is not supported but tried for sequence `p` of element \
1652
+ `land_dill_assl`.
1653
+
1654
+ After resetting all |InputSequence| objects and re-applying
1655
+ |XMLSubseries.prepare_series|, both |IOSequence.ramflag| and
1656
+ |IOSequence.diskflag_writing| are |True|. This case is the only one where a
1657
+ single reader or writer enables two flags (see the documentation on method
1658
+ |PrepareSeriesArguments.from_xmldata| for further information):
1659
+
1660
+ >>> hp.prepare_allseries(allocate_ram=False, jit=False)
1661
+ >>> reader.prepare_series()
1662
+ >>> reader.root.tag = reader.root.tag.replace("writer", "reader")
1663
+ >>> print_io_options("inputs", "p")
1664
+ ramflag=True
1665
+ diskflag_reading=False
1666
+ diskflag_writing=True
1667
+
1668
+ If we prefer the `ram` mode, things are more straightforward. Then, option
1669
+ |IOSequence.ramflag| is |True|, and options |IOSequence.diskflag_reading| and
1670
+ |IOSequence.diskflag_writing| are |False| regardless of all other options:
1671
+
1672
+ >>> hp.prepare_allseries(allocate_ram=False, jit=False)
1673
+ >>> series_io.find("mode").text = "ram"
1674
+ >>> for reader in series_io.readers:
1675
+ ... reader.find("mode").text = "ram"
1676
+ ... reader.prepare_series()
1677
+ >>> for writer in series_io.writers:
1678
+ ... mode = writer.find("mode")
1679
+ ... if mode is not None:
1680
+ ... mode.text = "ram"
1681
+ ... writer.prepare_series()
1682
+
1683
+ >>> print_io_options("inputs", "p")
1684
+ ramflag=True
1685
+ diskflag_reading=False
1686
+ diskflag_writing=False
1687
+
1688
+ >>> print_io_options("fluxes", "pc")
1689
+ ramflag=True
1690
+ diskflag_reading=False
1691
+ diskflag_writing=False
1692
+
1693
+ >>> print_io_options("states", "sm")
1694
+ ramflag=True
1695
+ diskflag_reading=False
1696
+ diskflag_writing=False
1697
+
1698
+ >>> reader = series_io.readers[0]
1699
+ >>> reader.root.tag = reader.root.tag.replace("reader", "writer")
1700
+ >>> reader.prepare_series()
1701
+ >>> reader.root.tag = reader.root.tag.replace("writer", "reader")
1702
+ >>> print_io_options("inputs", "p")
1703
+ ramflag=True
1704
+ diskflag_reading=False
1705
+ diskflag_writing=False
1706
+ """
1707
+ input_args = PrepareSeriesArguments.from_xmldata(
1708
+ is_reader=self._is_reader, is_input=True, prefer_ram=self._ramflag
1709
+ )
1710
+ output_args = None
1711
+ if self._is_writer:
1712
+ output_args = PrepareSeriesArguments.from_xmldata(
1713
+ is_reader=False, is_input=False, prefer_ram=self._ramflag
1714
+ )
1715
+ input_types = (sequencetools.InputSequence, sequencetools.NodeSequence)
1716
+ for sequence in self._iterate_sequences():
1717
+ args = (
1718
+ input_args
1719
+ if (output_args is None) or isinstance(sequence, input_types)
1720
+ else output_args
1721
+ )
1722
+ sequence.prepare_series(
1723
+ allocate_ram=sequence.ramflag or args.allocate_ram,
1724
+ read_jit=sequence.diskflag_reading or args.read_jit,
1725
+ write_jit=sequence.diskflag_writing or args.write_jit,
1726
+ )
1727
+
1728
+ def load_series(self, currentdir: str | None) -> None:
1729
+ """Load time series data as defined by the actual XML `reader` element.
1730
+
1731
+ >>> from hydpy.core.testtools import prepare_full_example_1
1732
+ >>> prepare_full_example_1()
1733
+
1734
+ >>> from hydpy import HydPy, pub, TestIO, xml_replace, XMLInterface
1735
+ >>> hp = HydPy("HydPy-H-Lahn")
1736
+ >>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
1737
+ >>> with TestIO():
1738
+ ... hp.prepare_network()
1739
+ ... hp.prepare_models()
1740
+ ... xml_replace("HydPy-H-Lahn/single_run", printflag=False)
1741
+ ... interface = XMLInterface("single_run.xml")
1742
+ ... interface.update_options()
1743
+ ... interface.update_timegrids()
1744
+ ... interface.update_selections()
1745
+ ... series_io = interface.series_io
1746
+ ... series_io.prepare_series()
1747
+ ... series_io.load_series()
1748
+ >>> from hydpy import print_vector
1749
+ >>> print_vector(hp.elements.land_dill_assl.model.sequences.inputs.t.series[:3])
1750
+ 0.0, -0.5, -2.4
1751
+ """
1752
+ if self._is_reader:
1753
+ hydpy.pub.sequencemanager.open_netcdfreader()
1754
+ self.prepare_sequencemanager(currentdir)
1755
+ for sequence in self._iterate_sequences():
1756
+ if sequence.ramflag:
1757
+ sequence.load_series()
1758
+ hydpy.pub.sequencemanager.close_netcdfreader()
1759
+
1760
+ def save_series(self, currentdir: str | None) -> None:
1761
+ """Save time series data as defined by the actual XML `writer` element.
1762
+
1763
+ >>> from hydpy.core.testtools import prepare_full_example_1
1764
+ >>> prepare_full_example_1()
1765
+
1766
+ >>> from hydpy import HydPy, pub, round_, TestIO, xml_replace, XMLInterface
1767
+ >>> hp = HydPy("HydPy-H-Lahn")
1768
+ >>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
1769
+ >>> with TestIO():
1770
+ ... hp.prepare_network()
1771
+ ... hp.prepare_models()
1772
+ ... xml_replace("HydPy-H-Lahn/single_run", printflag=False)
1773
+ ... interface = XMLInterface("single_run.xml")
1774
+ >>> interface.update_options()
1775
+ >>> interface.update_timegrids()
1776
+ >>> interface.update_selections()
1777
+ >>> series_io = interface.series_io
1778
+ >>> series_io.prepare_series()
1779
+ >>> hp.elements.land_dill_assl.model.sequences.fluxes.pc.series[2, 3] = 9.0
1780
+ >>> hp.nodes.lahn_leun.sequences.sim.series[4] = 7.0
1781
+ >>> with TestIO():
1782
+ ... series_io.save_series()
1783
+ >>> import numpy
1784
+ >>> with TestIO():
1785
+ ... dirpath = "HydPy-H-Lahn/series/default/"
1786
+ ... os.path.exists(f"{dirpath}land_lahn_leun_hland_96_flux_pc.npy")
1787
+ ... os.path.exists(f"{dirpath}land_lahn_kalk_hland_96_flux_pc.npy")
1788
+ ... round_(numpy.load(f"{dirpath}land_dill_assl_hland_96"
1789
+ ... f"_flux_pc.npy")[13+2, 3])
1790
+ ... round_(numpy.load(f"{dirpath}lahn_leun_sim_q_mean.npy")[13+4])
1791
+ True
1792
+ False
1793
+ 9.0
1794
+ 7.0
1795
+ """
1796
+ if self.name == "writer":
1797
+ hydpy.pub.sequencemanager.open_netcdfwriter()
1798
+ self.prepare_sequencemanager(currentdir)
1799
+ for sequence in self._iterate_sequences():
1800
+ if not sequence.diskflag_writing:
1801
+ sequence.save_series()
1802
+ hydpy.pub.sequencemanager.close_netcdfwriter()
1803
+
1804
+ def change_dirpath(self, currentdir: str | None) -> None:
1805
+ """Set the |IOSequence.dirpath| of all relevant |IOSequence| objects to the
1806
+ |FileManager.currentpath| of the |SequenceManager| object available in the
1807
+ |pub| module.
1808
+
1809
+ This "information freezing" is required for those sequences selected for
1810
+ reading data from or writing data to different directories "just in time"
1811
+ during a simulation run.
1812
+ """
1813
+ if not self._ramflag:
1814
+ self.prepare_sequencemanager(currentdir)
1815
+ currentpath = hydpy.pub.sequencemanager.currentpath
1816
+ for sequence in self._iterate_sequences():
1817
+ sequence.dirpath = currentpath
1818
+
1819
+ def reset_dirpath(self) -> None:
1820
+ """Revert |XMLSubseries.change_dirpath|."""
1821
+ if not self._ramflag:
1822
+ for sequence in self._iterate_sequences():
1823
+ del sequence.dirpath
1824
+
1825
+
1826
+ class XMLExchange(XMLBase):
1827
+ """Helper class for |XMLInterface| responsible for interpreting exchange items,
1828
+ accessible via different |XMLItemgroup| instances."""
1829
+
1830
+ def __init__(self, master: XMLInterface, root: ElementTree.Element) -> None:
1831
+ self.master: XMLInterface = master
1832
+ self.root: ElementTree.Element = root
1833
+
1834
+ def _get_items_of_certain_item_types(
1835
+ self, itemgroups: Iterable[str], itemtype: type[_TypeGetOrChangeItem]
1836
+ ) -> list[_TypeGetOrChangeItem]:
1837
+ """Return either all |GetItem| or all |ChangeItem| objects."""
1838
+ items: list[_TypeGetOrChangeItem] = []
1839
+ for itemgroup in self.itemgroups:
1840
+ if (
1841
+ issubclass(itemtype, itemtools.GetItem)
1842
+ and (itemgroup.name == "getitems")
1843
+ ) or (
1844
+ issubclass(itemtype, itemtools.ChangeItem)
1845
+ and (itemgroup.name != "getitems")
1846
+ ):
1847
+ for var in (
1848
+ var
1849
+ for model in itemgroup.models
1850
+ for subvars in model.subvars
1851
+ if subvars.name in itemgroups
1852
+ for var in subvars.vars
1853
+ ):
1854
+ item = var.item
1855
+ assert isinstance(item, itemtype)
1856
+ items.append(item)
1857
+ if "nodes" in itemgroups:
1858
+ for var in (var for node in itemgroup.nodes for var in node.vars):
1859
+ item = var.item
1860
+ assert isinstance(item, itemtype)
1861
+ items.append(item)
1862
+ return items
1863
+
1864
+ @property
1865
+ def parameteritems(self) -> list[itemtools.ChangeItem]:
1866
+ """Create and return all items for changing control parameter values.
1867
+
1868
+ >>> from hydpy.core.testtools import prepare_full_example_1
1869
+ >>> prepare_full_example_1()
1870
+
1871
+ >>> from hydpy import HydPy, pub, TestIO, XMLInterface
1872
+ >>> hp = HydPy("HydPy-H-Lahn")
1873
+ >>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
1874
+ >>> with TestIO():
1875
+ ... hp.prepare_everything()
1876
+ ... interface = XMLInterface("multiple_runs.xml")
1877
+ >>> interface.update_selections()
1878
+ >>> for item in interface.exchange.parameteritems:
1879
+ ... print(item.name)
1880
+ alpha
1881
+ beta
1882
+ lag
1883
+ damp
1884
+ sfcf_1
1885
+ sfcf_2
1886
+ sfcf_3
1887
+ k4
1888
+ """
1889
+ return self._get_items_of_certain_item_types(
1890
+ itemgroups=("control",), itemtype=itemtools.ChangeItem
1891
+ )
1892
+
1893
+ @property
1894
+ def inputitems(self) -> list[itemtools.SetItem]:
1895
+ """Return all items for changing input sequence values.
1896
+
1897
+ >>> from hydpy.core.testtools import prepare_full_example_1
1898
+ >>> prepare_full_example_1()
1899
+
1900
+ >>> from hydpy import HydPy, pub, TestIO, XMLInterface
1901
+ >>> hp = HydPy("HydPy-H-Lahn")
1902
+ >>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
1903
+ >>> with TestIO():
1904
+ ... hp.prepare_everything()
1905
+ ... interface = XMLInterface("multiple_runs.xml")
1906
+ >>> interface.update_selections()
1907
+ >>> for item in interface.exchange.inputitems:
1908
+ ... print(item.name)
1909
+ t_headwaters
1910
+ """
1911
+ return self._get_items_of_certain_item_types(
1912
+ itemgroups=("inputs",), itemtype=itemtools.SetItem
1913
+ )
1914
+
1915
+ @property
1916
+ def conditionitems(self) -> list[itemtools.SetItem]:
1917
+ """Return all items for changing condition sequence values.
1918
+
1919
+ >>> from hydpy.core.testtools import prepare_full_example_1
1920
+ >>> prepare_full_example_1()
1921
+
1922
+ >>> from hydpy import HydPy, pub, TestIO, XMLInterface
1923
+ >>> hp = HydPy("HydPy-H-Lahn")
1924
+ >>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
1925
+ >>> with TestIO():
1926
+ ... hp.prepare_everything()
1927
+ ... interface = XMLInterface("multiple_runs.xml")
1928
+ >>> interface.update_selections()
1929
+ >>> for item in interface.exchange.conditionitems:
1930
+ ... print(item.name)
1931
+ ic_lahn_leun
1932
+ ic_lahn_marb
1933
+ sm_lahn_leun
1934
+ sm_lahn_marb
1935
+ quh
1936
+ """
1937
+ return self._get_items_of_certain_item_types(
1938
+ itemgroups=("states", "logs"), itemtype=itemtools.SetItem
1939
+ )
1940
+
1941
+ @property
1942
+ def outputitems(self) -> list[itemtools.SetItem]:
1943
+ """Return all items for querying the current values or the complete time
1944
+ series of sequences in the "setitem" style.
1945
+
1946
+ >>> from hydpy.core.testtools import prepare_full_example_1
1947
+ >>> prepare_full_example_1()
1948
+
1949
+ >>> from hydpy import HydPy, pub, TestIO, XMLInterface
1950
+ >>> hp = HydPy("HydPy-H-Lahn")
1951
+ >>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
1952
+ >>> with TestIO():
1953
+ ... hp.prepare_everything()
1954
+ ... interface = XMLInterface("multiple_runs.xml")
1955
+ >>> interface.update_selections()
1956
+ >>> for item in interface.exchange.outputitems:
1957
+ ... print(item.name)
1958
+ swe_headwaters
1959
+ """
1960
+ return self._get_items_of_certain_item_types(
1961
+ itemgroups=("factors", "fluxes"), itemtype=itemtools.SetItem
1962
+ )
1963
+
1964
+ @property
1965
+ def getitems(self) -> list[itemtools.GetItem]:
1966
+ """Return all items for querying the current values or the complete time
1967
+ series of sequences in the "getitem style".
1968
+
1969
+ >>> from hydpy.core.testtools import prepare_full_example_1
1970
+ >>> prepare_full_example_1()
1971
+
1972
+ >>> from hydpy import HydPy, pub, TestIO, XMLInterface
1973
+ >>> hp = HydPy("HydPy-H-Lahn")
1974
+ >>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
1975
+ >>> with TestIO():
1976
+ ... hp.prepare_everything()
1977
+ ... interface = XMLInterface("multiple_runs.xml")
1978
+ >>> interface.update_selections()
1979
+ >>> for item in interface.exchange.getitems:
1980
+ ... print(item.target)
1981
+ factors_contriarea
1982
+ fluxes_qt
1983
+ fluxes_qt_series
1984
+ states_sm
1985
+ states_sm_series
1986
+ nodes_sim_series
1987
+ """
1988
+ return self._get_items_of_certain_item_types(
1989
+ itemgroups=(
1990
+ "control",
1991
+ "inputs",
1992
+ "factors",
1993
+ "fluxes",
1994
+ "states",
1995
+ "logs",
1996
+ "nodes",
1997
+ ),
1998
+ itemtype=itemtools.GetItem,
1999
+ )
2000
+
2001
+ def prepare_series(self) -> None:
2002
+ """Prepare all required |IOSequence.series| arrays via the
2003
+ |IOSequence.prepare_series| method.
2004
+ """
2005
+ with warnings.catch_warnings():
2006
+ warnings.filterwarnings(
2007
+ "ignore", category=exceptiontools.AttributeNotReadyWarning
2008
+ )
2009
+ for item in itertools.chain(
2010
+ self.inputitems, self.conditionitems, self.outputitems, self.getitems
2011
+ ):
2012
+ for target in item.device2target.values():
2013
+ if item.targetspecs.series:
2014
+ assert isinstance(target, sequencetools.IOSequence)
2015
+ target.prepare_series(
2016
+ allocate_ram=True, read_jit=None, write_jit=None
2017
+ )
2018
+ # for base in getattr(item, "device2base", {}).values():
2019
+ # if item.basespecs.series and not base.ramflag:
2020
+ # base.prepare_series() ToDo
2021
+
2022
+ @property
2023
+ def itemgroups(self) -> list[XMLItemgroup]:
2024
+ """The relevant |XMLItemgroup| objects."""
2025
+ return [XMLItemgroup(self, element) for element in self]
2026
+
2027
+
2028
+ class XMLItemgroup(XMLBase):
2029
+ """Helper class for |XMLExchange| responsible for handling the exchange items
2030
+ related to model parameters and sequences separately from the exchange items of
2031
+ node sequences."""
2032
+
2033
+ def __init__(self, master: XMLExchange, root: ElementTree.Element) -> None:
2034
+ self.master: XMLExchange = master
2035
+ self.root: ElementTree.Element = root
2036
+
2037
+ @property
2038
+ def models(self) -> list[XMLModel]:
2039
+ """The required |XMLModel| objects."""
2040
+ return [
2041
+ XMLModel(self, element) for element in self if strip(element.tag) != "nodes"
2042
+ ]
2043
+
2044
+ @property
2045
+ def nodes(self) -> list[XMLNode]:
2046
+ """The required |XMLNode| objects."""
2047
+ return [
2048
+ XMLNode(self, element) for element in self if strip(element.tag) == "nodes"
2049
+ ]
2050
+
2051
+
2052
+ class XMLModel(XMLBase):
2053
+ """Helper class for |XMLItemgroup| responsible for handling the exchange items
2054
+ related to different parameter or sequence groups of |Model| objects."""
2055
+
2056
+ def __init__(self, master: XMLItemgroup, root: ElementTree.Element) -> None:
2057
+ self.master: XMLItemgroup = master
2058
+ self.root: ElementTree.Element = root
2059
+
2060
+ @property
2061
+ def subvars(self) -> list[XMLSubvars]:
2062
+ """The required |XMLSubVars| objects."""
2063
+ return [XMLSubvars(self, element) for element in self]
2064
+
2065
+
2066
+ class XMLSubvars(XMLBase):
2067
+ """Helper class for |XMLModel| responsible for handling the exchange items
2068
+ related to individual parameters or sequences of |Model| objects."""
2069
+
2070
+ def __init__(self, master: XMLModel, root: ElementTree.Element):
2071
+ self.master: XMLModel = master
2072
+ self.root: ElementTree.Element = root
2073
+
2074
+ @property
2075
+ def vars(self) -> list[XMLVar]:
2076
+ """The required |XMLVar| objects."""
2077
+ return [XMLVar(self, element) for element in self]
2078
+
2079
+
2080
+ class XMLNode(XMLBase):
2081
+ """Helper class for |XMLItemgroup| responsible for handling the exchange items
2082
+ related to individual parameters or sequences of |Node| objects."""
2083
+
2084
+ def __init__(self, master: XMLItemgroup, root: ElementTree.Element) -> None:
2085
+ self.master: XMLItemgroup = master
2086
+ self.root: ElementTree.Element = root
2087
+
2088
+ @property
2089
+ def vars(self) -> list[XMLVar]:
2090
+ """The required |XMLVar| objects."""
2091
+ return [XMLVar(self, element) for element in self]
2092
+
2093
+
2094
+ class XMLVar(XMLSelector):
2095
+ """Helper class for |XMLSubvars| and |XMLNode| responsible for creating a defined
2096
+ exchange item."""
2097
+
2098
+ def __init__(self, master: XMLSubvars | XMLNode, root: ElementTree.Element) -> None:
2099
+ self.master: XMLSubvars | XMLNode = master
2100
+ self.root: ElementTree.Element = root
2101
+
2102
+ @property
2103
+ def item(self) -> itemtools.ExchangeItem:
2104
+ """The defined |ExchangeItem| object.
2105
+
2106
+ We first prepare the `HydPy-H-Lahn` example project and then create the related
2107
+ |XMLInterface| object defined by the XML configuration file `multiple_runs`:
2108
+
2109
+ >>> from hydpy.core.testtools import prepare_full_example_1
2110
+ >>> prepare_full_example_1()
2111
+
2112
+ >>> from hydpy import (HydPy, round_, print_matrix, print_vector, pub, TestIO,
2113
+ ... XMLInterface)
2114
+ >>> hp = HydPy("HydPy-H-Lahn")
2115
+ >>> pub.timegrids = "1996-01-01", "1996-01-06", "1d"
2116
+ >>> with TestIO():
2117
+ ... hp.prepare_everything()
2118
+ ... interface = XMLInterface("multiple_runs.xml")
2119
+ >>> interface.update_selections()
2120
+
2121
+ One of the defined |SetItem| objects modifies the values of all
2122
+ |hland_control.Alpha| objects of application model |hland_96|. We demonstrate
2123
+ this for the control parameter object handled by the `land_dill_assl` element:
2124
+
2125
+ >>> var = interface.exchange.itemgroups[0].models[0].subvars[0].vars[0]
2126
+ >>> item = var.item
2127
+ >>> round_(item.value)
2128
+ 2.0
2129
+ >>> hp.elements.land_dill_assl.model.parameters.control.alpha
2130
+ alpha(1.0)
2131
+ >>> item.update_variables()
2132
+ >>> hp.elements.land_dill_assl.model.parameters.control.alpha
2133
+ alpha(2.0)
2134
+
2135
+ The second example is comparable but focuses on a |SetItem| modifying control
2136
+ parameter |musk_control.NmbSegments| of application model |musk_classic| via
2137
+ its keyword argument `lag`:
2138
+
2139
+ >>> var = interface.exchange.itemgroups[0].models[2].subvars[0].vars[0]
2140
+ >>> item = var.item
2141
+ >>> round_(item.value)
2142
+ 5.0
2143
+ >>> hp.elements.stream_dill_assl_lahn_leun.model.parameters.control.nmbsegments
2144
+ nmbsegments(lag=0.0)
2145
+ >>> item.update_variables()
2146
+ >>> hp.elements.stream_dill_assl_lahn_leun.model.parameters.control.nmbsegments
2147
+ nmbsegments(lag=5.0)
2148
+
2149
+ The third discussed |SetItem| assigns the same value to all entries of state
2150
+ sequence |hland_states.SM|, resulting in the same soil moisture for all
2151
+ individual hydrological response units of element `land_lahn_leun`:
2152
+
2153
+ >>> var = interface.exchange.itemgroups[1].models[0].subvars[0].vars[2]
2154
+ >>> item = var.item
2155
+ >>> item.name
2156
+ 'sm_lahn_leun'
2157
+ >>> print_vector(item.value)
2158
+ 123.0
2159
+ >>> hp.elements.land_lahn_leun.model.sequences.states.sm
2160
+ sm(138.31396, 135.71124, 147.54968, 145.47142, 154.96405, 153.32805,
2161
+ 160.91917, 159.62434, 165.65575, 164.63255)
2162
+ >>> item.update_variables()
2163
+ >>> hp.elements.land_lahn_leun.model.sequences.states.sm
2164
+ sm(123.0, 123.0, 123.0, 123.0, 123.0, 123.0, 123.0, 123.0, 123.0, 123.0)
2165
+
2166
+ In contrast to the last example, the fourth |SetItem| is 1-dimensional and thus
2167
+ allows to assign different values to the individual hydrological response units
2168
+ of element `land_lahn_marb`:
2169
+
2170
+ >>> var = interface.exchange.itemgroups[1].models[0].subvars[0].vars[3]
2171
+ >>> item = var.item
2172
+ >>> item.name
2173
+ 'sm_lahn_marb'
2174
+ >>> print_vector(item.value)
2175
+ 110.0, 120.0, 130.0, 140.0, 150.0, 160.0, 170.0, 180.0, 190.0, 200.0,
2176
+ 210.0, 220.0, 230.0
2177
+ >>> hp.elements.land_lahn_marb.model.sequences.states.sm
2178
+ sm(99.27505, 96.17726, 109.16576, 106.39745, 117.97304, 115.56252,
2179
+ 125.81523, 123.73198, 132.80035, 130.91684, 138.95523, 137.25983,
2180
+ 142.84148)
2181
+ >>> with pub.options.warntrim(False):
2182
+ ... item.update_variables()
2183
+ >>> hp.elements.land_lahn_marb.model.sequences.states.sm
2184
+ sm(110.0, 120.0, 130.0, 140.0, 150.0, 160.0, 170.0, 180.0, 190.0, 200.0,
2185
+ 206.0, 206.0, 206.0)
2186
+
2187
+ Without defining initial values in the XML file, the |ChangeItem.value|
2188
+ property of each |SetItem| starts with the averaged (see item `ic_lahn_leun`) or
2189
+ original (see item `ic_lahn_marb`) values of the corresponding sequences:
2190
+
2191
+ >>> var = interface.exchange.itemgroups[1].models[0].subvars[0].vars[0]
2192
+ >>> item = var.item
2193
+ >>> item.name
2194
+ 'ic_lahn_leun'
2195
+ >>> round_(item.value)
2196
+ 1.184948
2197
+ >>> ic_states = hp.elements.land_lahn_leun.model.sequences.states.ic
2198
+ >>> round_(ic_states.average_values())
2199
+ 1.184948
2200
+ >>> var = interface.exchange.itemgroups[1].models[0].subvars[0].vars[1]
2201
+ >>> item = var.item
2202
+ >>> item.name
2203
+ 'ic_lahn_marb'
2204
+ >>> print_vector(item.value)
2205
+ 0.96404, 1.36332, 0.96458, 1.46458, 0.96512, 1.46512, 0.96565,
2206
+ 1.46569, 0.96617, 1.46617, 0.96668, 1.46668, 1.46719
2207
+ >>> hp.elements.land_lahn_marb.model.sequences.states.ic
2208
+ ic(0.96404, 1.36332, 0.96458, 1.46458, 0.96512, 1.46512, 0.96565,
2209
+ 1.46569, 0.96617, 1.46617, 0.96668, 1.46668, 1.46719)
2210
+
2211
+ Finally, one |SetItem| addresses the time series if the input sequence
2212
+ |hland_inputs.T| of both headwater catchments. Similar to the example above,
2213
+ its initial values stem from its target sequences' initial (time series)
2214
+ values:
2215
+
2216
+ >>> var = interface.exchange.itemgroups[2].models[0].subvars[0].vars[0]
2217
+ >>> item = var.item
2218
+ >>> print_matrix(item.value)
2219
+ | 0.0, -0.5, -2.4, -6.8, -7.8 |
2220
+ | -0.7, -1.5, -4.6, -8.2, -8.7 |
2221
+ >>> print_vector(hp.elements.land_dill_assl.model.sequences.inputs.t.series)
2222
+ 0.0, -0.5, -2.4, -6.8, -7.8
2223
+ >>> print_vector(hp.elements.land_lahn_marb.model.sequences.inputs.t.series)
2224
+ -0.7, -1.5, -4.6, -8.2, -8.7
2225
+ >>> item.value = [0.0, 1.0, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0, 9.0]
2226
+ >>> item.update_variables()
2227
+ >>> print_vector(hp.elements.land_dill_assl.model.sequences.inputs.t.series)
2228
+ 0.0, 1.0, 2.0, 3.0, 4.0
2229
+ >>> print_vector(hp.elements.land_lahn_marb.model.sequences.inputs.t.series)
2230
+ 5.0, 6.0, 7.0, 8.0, 9.0
2231
+
2232
+ |AddItem| `sfcf_1`, `sfcf_2`, and `sfcf_3` serve to demonstrate how a scalar
2233
+ value (`sfcf_1` and `sfcf_2`) or a vector of values can be used to change the
2234
+ value of a "target" parameter (|hland_control.SfCF|) in relation to a "base"
2235
+ parameter (|hland_control.RfCF|):
2236
+
2237
+ >>> for element in pub.selections.headwaters.elements:
2238
+ ... element.model.parameters.control.rfcf(1.1)
2239
+ >>> for element in pub.selections.nonheadwaters.elements:
2240
+ ... element.model.parameters.control.rfcf(1.0)
2241
+
2242
+ >>> for subvars in interface.exchange.itemgroups[3].models[0].subvars:
2243
+ ... for var in subvars.vars:
2244
+ ... var.item.update_variables()
2245
+ >>> for element in hp.elements.catchment:
2246
+ ... print(element, repr(element.model.parameters.control.sfcf))
2247
+ land_dill_assl sfcf(1.4)
2248
+ land_lahn_kalk sfcf(field=1.1, forest=1.2)
2249
+ land_lahn_leun sfcf(1.2)
2250
+ land_lahn_marb sfcf(1.4)
2251
+
2252
+ |MultiplyItem| `k4` works similar to the described add items but multiplies
2253
+ the current values of the base parameter objects of type |hland_control.K|
2254
+ with 10 to gain new values for the target parameter objects of type
2255
+ |hland_control.K4|:
2256
+
2257
+ >>> for subvars in interface.exchange.itemgroups[4].models[0].subvars:
2258
+ ... for var in subvars.vars:
2259
+ ... var.item.update_variables()
2260
+ >>> for element in hp.elements.catchment:
2261
+ ... control = element.model.parameters.control
2262
+ ... print(element, repr(control.k), repr(control.k4))
2263
+ land_dill_assl k(0.005618) k4(0.056177)
2264
+ land_lahn_kalk k(0.002571) k4(0.025712)
2265
+ land_lahn_leun k(0.005948) k4(0.059481)
2266
+ land_lahn_marb k(0.005325) k4(0.053247)
2267
+
2268
+ The final three examples focus on |GetItem| objects. One |GetItem| object
2269
+ queries the actual values of the |hland_states.SM| states of all relevant
2270
+ elements:
2271
+
2272
+ >>> var = interface.exchange.itemgroups[5].models[0].subvars[2].vars[0]
2273
+ >>> hp.elements.land_dill_assl.model.sequences.states.sm = 1.0
2274
+ >>> for name, target in var.item.yield_name2value():
2275
+ ... print(name, target) # doctest: +ELLIPSIS
2276
+ land_dill_assl_states_sm [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, \
2277
+ 1.0, 1.0]
2278
+ land_lahn_kalk_states_sm [101.3124...]
2279
+ land_lahn_leun_states_sm [123.0, 123.0, 123.0, 123.0, 123.0, 123.0, 123.0, \
2280
+ 123.0, 123.0, 123.0]
2281
+ land_lahn_marb_states_sm [110.0, 120.0, 130.0, 140.0, 150.0, 160.0, 170.0, \
2282
+ 180.0, 190.0, 200.0, 206.0, 206.0, 206.0]
2283
+
2284
+ Another |GetItem| object queries the actual value of the
2285
+ |hland_factors.ContriArea| factor sequence of element `land_dill_assl`:
2286
+
2287
+ >>> hp.elements.land_dill_assl.model.sequences.factors.contriarea(1.0)
2288
+ >>> for var in interface.exchange.itemgroups[5].models[0].subvars[0].vars:
2289
+ ... for name, target in var.item.yield_name2value():
2290
+ ... print(name, target)
2291
+ land_dill_assl_factors_contriarea 1.0
2292
+
2293
+ Another |GetItem| object queries both the actual and the time series values of
2294
+ the |hland_fluxes.QT| flux sequence of element `land_dill_assl`:
2295
+
2296
+ >>> qt = hp.elements.land_dill_assl.model.sequences.fluxes.qt
2297
+ >>> qt(1.0)
2298
+ >>> qt.series = 2.0
2299
+ >>> for var in interface.exchange.itemgroups[5].models[0].subvars[1].vars:
2300
+ ... for name, target in var.item.yield_name2value():
2301
+ ... print(name, target)
2302
+ land_dill_assl_fluxes_qt 1.0
2303
+ land_dill_assl_fluxes_qt_series [2.0, 2.0, 2.0, 2.0, 2.0]
2304
+
2305
+ Last but not least, one |GetItem| queries the simulated time series values
2306
+ available through node `dill_assl`:
2307
+
2308
+ >>> var = interface.exchange.itemgroups[5].nodes[0].vars[0]
2309
+ >>> hp.nodes.dill_assl.sequences.sim.series = range(5)
2310
+ >>> for name, target in var.item.yield_name2value():
2311
+ ... print(name, target)
2312
+ dill_assl_nodes_sim_series [0.0, 1.0, 2.0, 3.0, 4.0]
2313
+ >>> for name, target in var.item.yield_name2value(2, 4):
2314
+ ... print(name, target)
2315
+ dill_assl_nodes_sim_series [2.0, 3.0]
2316
+ """
2317
+ target = f"{self.master.name}.{self.name}"
2318
+ if self.master.name == "nodes":
2319
+ master = self.master.name
2320
+ itemgroup = self.master.master.name
2321
+ else:
2322
+ master = self.master.master.name
2323
+ itemgroup = self.master.master.master.name
2324
+ itemtype = _ITEMGROUP2ITEMCLASS[itemgroup]
2325
+ if itemgroup == "getitems":
2326
+ return self._get_getitem(target, master, itemtype)
2327
+ return self._get_changeitem(target, master, itemtype)
2328
+
2329
+ def _get_getitem(
2330
+ self, target: str, master: str, itemtype: type[itemtools.GetItem]
2331
+ ) -> itemtools.GetItem:
2332
+ xmlelement = self.find("name", optional=True)
2333
+ if xmlelement is None or xmlelement.text is None:
2334
+ name = cast(Name, "?")
2335
+ else:
2336
+ name = cast(Name, xmlelement.text)
2337
+ item = itemtype(name=name, master=master, target=target)
2338
+ self._collect_variables(item)
2339
+ return item
2340
+
2341
+ def _get_changeitem(
2342
+ self, target: str, master: str, itemtype: type[_TypeSetOrAddOrMultiplyItem]
2343
+ ) -> _TypeSetOrAddOrMultiplyItem:
2344
+ name = cast(Name, self.find("name", optional=False).text)
2345
+ assert name is not None
2346
+ level = self.find("level", optional=False).text
2347
+ assert level is not None
2348
+ item: _TypeSetOrAddOrMultiplyItem
2349
+ # Simplify the following if-clauses after Mypy issue 10989 is fixed?
2350
+ if not issubclass(itemtype, itemtools.SetItem):
2351
+ item = itemtype(
2352
+ name=name,
2353
+ master=master,
2354
+ target=target,
2355
+ base=strip(list(self)[-1].tag),
2356
+ level=cast(itemtools.LevelType, level),
2357
+ )
2358
+ elif not issubclass(itemtype, (itemtools.AddItem, itemtools.MultiplyItem)):
2359
+ keyword = self.find("keyword", optional=True)
2360
+ item = itemtype(
2361
+ name=name,
2362
+ master=master,
2363
+ target=target,
2364
+ keyword=None if keyword is None else keyword.text,
2365
+ level=cast(itemtools.LevelType, level),
2366
+ )
2367
+ self._collect_variables(item)
2368
+ element = self.find("init", optional=True)
2369
+ if element is not None:
2370
+ init = element.text
2371
+ assert init is not None
2372
+ item.value = eval(",".join(init.split()))
2373
+ else:
2374
+ assert isinstance(item, itemtools.SetItem)
2375
+ item.extract_values()
2376
+ return item
2377
+
2378
+ def _collect_variables(self, item: itemtools.ExchangeItem) -> None:
2379
+ selections = self.selections
2380
+ item.collect_variables(selections)
2381
+
2382
+
2383
+ class XSDWriter:
2384
+ """A pure |classmethod| class for writing the actual XML schema file
2385
+ `HydPyConfigBase.xsd`, which makes sure that an XML configuration file is
2386
+ readable by class |XMLInterface|.
2387
+
2388
+ Unless you are interested in enhancing HydPy's XML functionalities, you should,
2389
+ if any, be interested in method |XSDWriter.write_xsd| only.
2390
+ """
2391
+
2392
+ confpath: str = conf.__path__[0]
2393
+ filepath_source: str = os.path.join(confpath, "HydPyConfigBase" + ".xsdt")
2394
+ filepath_target: str = filepath_source[:-1]
2395
+
2396
+ @classmethod
2397
+ def write_xsd(cls) -> None:
2398
+ """Write the complete base schema file `HydPyConfigBase.xsd` based on the
2399
+ template file `HydPyConfigBase.xsdt`.
2400
+
2401
+ Method |XSDWriter.write_xsd| adds model-specific information to the general
2402
+ information of template file `HydPyConfigBase.xsdt` regarding reading and
2403
+ writing of time series data and exchanging parameter and sequence values, for
2404
+ example, during calibration.
2405
+
2406
+ The following example shows that after writing a new schema file, method
2407
+ |XMLInterface.validate_xml| does not raise an error when either applied to the
2408
+ XML configuration files `single_run.xml` or `multiple_runs.xml` of the
2409
+ `HydPy-H-Lahn` example project:
2410
+
2411
+ >>> import os
2412
+ >>> from hydpy.exe.xmltools import XSDWriter, XMLInterface
2413
+ >>> if os.path.exists(XSDWriter.filepath_target):
2414
+ ... os.remove(XSDWriter.filepath_target)
2415
+ >>> os.path.exists(XSDWriter.filepath_target)
2416
+ False
2417
+ >>> XSDWriter.write_xsd()
2418
+ >>> os.path.exists(XSDWriter.filepath_target)
2419
+ True
2420
+
2421
+ >>> from hydpy.data import make_filepath
2422
+ >>> for configfile in ("single_run.xml", "multiple_runs.xml"):
2423
+ ... XMLInterface(configfile, make_filepath("HydPy-H-Lahn")).validate_xml()
2424
+ """
2425
+ with open(cls.filepath_source, encoding=config.ENCODING) as file_:
2426
+ template = file_.read()
2427
+ template = template.replace(
2428
+ "<!--include model sequence groups-->", cls.get_insertion()
2429
+ )
2430
+ template = template.replace(
2431
+ "<!--include exchange items-->", cls.get_exchangeinsertion()
2432
+ )
2433
+ with open(cls.filepath_target, "w", encoding=config.ENCODING) as file_:
2434
+ file_.write(template)
2435
+
2436
+ @staticmethod
2437
+ def get_basemodelnames() -> list[str]:
2438
+ """Return a sorted |list| containing all base model names.
2439
+
2440
+ >>> from hydpy.exe.xmltools import XSDWriter
2441
+ >>> print(XSDWriter.get_basemodelnames()) # doctest: +ELLIPSIS
2442
+ ['arma', 'conv', ..., 'wland', 'wq']
2443
+ """
2444
+ modelspath: str = models.__path__[0]
2445
+
2446
+ def _is_basemodel(dirname: str) -> bool:
2447
+ pathname = os.path.join(modelspath, dirname)
2448
+ return os.path.isdir(pathname) and ("__init__.py" in os.listdir(pathname))
2449
+
2450
+ return sorted(dn for dn in os.listdir(modelspath) if _is_basemodel(dn))
2451
+
2452
+ @staticmethod
2453
+ def get_applicationmodelnames() -> list[str]:
2454
+ """Return a sorted |list| containing all application model names.
2455
+
2456
+ >>> from hydpy.exe.xmltools import XSDWriter
2457
+ >>> print(XSDWriter.get_applicationmodelnames()) # doctest: +ELLIPSIS
2458
+ [...'dam_v001', 'dam_v002', 'dam_v003', 'dam_v004', 'dam_v005',...]
2459
+ """
2460
+ modelspath: str = models.__path__[0]
2461
+ return sorted(
2462
+ str(fn.split(".")[0])
2463
+ for fn in sorted(os.listdir(modelspath))
2464
+ if (fn.endswith(".py") and (fn != "__init__.py"))
2465
+ )
2466
+
2467
+ @classmethod
2468
+ def get_insertion(cls) -> str:
2469
+ """Return the complete string to be inserted into the string of the template
2470
+ file.
2471
+
2472
+ >>> from hydpy.exe.xmltools import XSDWriter
2473
+ >>> print(XSDWriter.get_insertion()) # doctest: +ELLIPSIS
2474
+ <complexType name="dummy_interceptedwater_readerType">
2475
+ <sequence>
2476
+ <element name="inputs"
2477
+ minOccurs="0">
2478
+ <complexType>
2479
+ <sequence>
2480
+ <element
2481
+ name="interceptedwater"
2482
+ minOccurs="0"/>
2483
+ </sequence>
2484
+ </complexType>
2485
+ </element>
2486
+ </sequence>
2487
+ </complexType>
2488
+ ...
2489
+ <complexType name="dummy_snowalbedo_readerType">
2490
+ <sequence>
2491
+ <element name="inputs"
2492
+ minOccurs="0">
2493
+ <complexType>
2494
+ <sequence>
2495
+ <element
2496
+ name="snowalbedo"
2497
+ minOccurs="0"/>
2498
+ ...
2499
+ <element name="wland_wag"
2500
+ type="hpcb:wland_wag_readerType"
2501
+ minOccurs="0"/>
2502
+ </sequence>
2503
+ </complexType>
2504
+ ...
2505
+ <complexType name="arma_rimorido_writerType">
2506
+ <sequence>
2507
+ <element name="fluxes"
2508
+ minOccurs="0">
2509
+ <complexType>
2510
+ <sequence>
2511
+ <element
2512
+ name="qin"
2513
+ ...
2514
+ <element
2515
+ name="qout"
2516
+ minOccurs="0"/>
2517
+ </sequence>
2518
+ </complexType>
2519
+ </element>
2520
+ </sequence>
2521
+ </complexType>
2522
+ ...
2523
+ <complexType name="writerType">
2524
+ <sequence>
2525
+ <element name="node"
2526
+ type="hpcb:node_writerType"
2527
+ minOccurs="0"/>
2528
+ <element name="arma_rimorido"
2529
+ type="hpcb:arma_rimorido_writerType"
2530
+ minOccurs="0"/>
2531
+ ...
2532
+ <element name="wq_trapeze_strickler"
2533
+ type="hpcb:wq_trapeze_strickler_writerType"
2534
+ minOccurs="0"/>
2535
+ </sequence>
2536
+ </complexType>
2537
+ <BLANKLINE>
2538
+ """
2539
+ indent = 1
2540
+ blanks = " " * (indent * 4)
2541
+ subs = []
2542
+ types_: tuple[Literal["reader", "writer"], ...] = ("reader", "writer")
2543
+ for type_ in types_:
2544
+ for name in cls.get_applicationmodelnames():
2545
+ model = importtools.prepare_model(name)
2546
+ modelinsertion = cls.get_modelinsertion(
2547
+ model=model, type_=type_, indent=indent + 2
2548
+ )
2549
+ if modelinsertion:
2550
+ subs.extend(
2551
+ [
2552
+ f'{blanks}<complexType name="{name}_{type_}Type">',
2553
+ f"{blanks} <sequence>",
2554
+ modelinsertion,
2555
+ f"{blanks} </sequence>",
2556
+ f"{blanks}</complexType>",
2557
+ "",
2558
+ ]
2559
+ )
2560
+ subs.append(cls.get_readerwriterinsertion(type_=type_, indent=indent))
2561
+ return "\n".join(subs)
2562
+
2563
+ @classmethod
2564
+ def get_modelinsertion(
2565
+ cls, model: modeltools.Model, type_: str, indent: int
2566
+ ) -> str | None:
2567
+ """Return the insertion string required for the given application model.
2568
+
2569
+ >>> from hydpy.exe.xmltools import XSDWriter
2570
+ >>> from hydpy import prepare_model
2571
+ >>> model = prepare_model("hland_96")
2572
+ >>> print(XSDWriter.get_modelinsertion(
2573
+ ... model=model, type_="reader", indent=1)) # doctest: +ELLIPSIS
2574
+ <element name="inputs"
2575
+ minOccurs="0">
2576
+ <complexType>
2577
+ <sequence>
2578
+ <element
2579
+ name="p"
2580
+ minOccurs="0"/>
2581
+ <element
2582
+ name="t"
2583
+ minOccurs="0"/>
2584
+ </sequence>
2585
+ </complexType>
2586
+ </element>
2587
+ >>> print(XSDWriter.get_modelinsertion(
2588
+ ... model=model, type_="writer", indent=1)) # doctest: +ELLIPSIS
2589
+ <element name="inputs"
2590
+ minOccurs="0">
2591
+ <complexType>
2592
+ <sequence>
2593
+ <element
2594
+ name="p"
2595
+ minOccurs="0"/>
2596
+ ...
2597
+ </element>
2598
+ <element name="fluxes"
2599
+ minOccurs="0">
2600
+ ...
2601
+ </element>
2602
+ <element name="states"
2603
+ minOccurs="0">
2604
+ ...
2605
+ </element>
2606
+
2607
+ >>> model = prepare_model("arma_rimorido")
2608
+ >>> XSDWriter.get_modelinsertion(
2609
+ ... model=model, type_="reader", indent=1) # doctest: +ELLIPSIS
2610
+ >>> print(XSDWriter.get_modelinsertion(
2611
+ ... model=model, type_="writer", indent=1)) # doctest: +ELLIPSIS
2612
+ <element name="fluxes"
2613
+ minOccurs="0">
2614
+ <complexType>
2615
+ <sequence>
2616
+ <element
2617
+ name="qin"
2618
+ minOccurs="0"/>
2619
+ ...
2620
+ <element
2621
+ name="qout"
2622
+ minOccurs="0"/>
2623
+ </sequence>
2624
+ </complexType>
2625
+ </element>
2626
+ """
2627
+ names: tuple[str, ...] = ("inputs",)
2628
+ if type_ == "writer":
2629
+ names += "factors", "fluxes", "states"
2630
+ texts = []
2631
+ return_none = True
2632
+ for name in names:
2633
+ subsequences = getattr(model.sequences, name, None)
2634
+ if subsequences:
2635
+ return_none = False
2636
+ texts.append(cls.get_subsequencesinsertion(subsequences, indent))
2637
+ return None if return_none else "\n".join(texts)
2638
+
2639
+ @classmethod
2640
+ def get_subsequencesinsertion(
2641
+ cls, subsequences: sequencetools.SubSequences[Any, Any, Any], indent: int
2642
+ ) -> str:
2643
+ """Return the insertion string required for the given group of sequences.
2644
+
2645
+ >>> from hydpy.exe.xmltools import XSDWriter
2646
+ >>> from hydpy import prepare_model
2647
+ >>> model = prepare_model("hland_96")
2648
+ >>> print(XSDWriter.get_subsequencesinsertion(
2649
+ ... model.sequences.factors, 1)) # doctest: +ELLIPSIS
2650
+ <element name="factors"
2651
+ minOccurs="0">
2652
+ <complexType>
2653
+ <sequence>
2654
+ <element
2655
+ name="tc"
2656
+ minOccurs="0"/>
2657
+ <element
2658
+ name="fracrain"
2659
+ minOccurs="0"/>
2660
+ ...
2661
+ <element
2662
+ name="contriarea"
2663
+ minOccurs="0"/>
2664
+ </sequence>
2665
+ </complexType>
2666
+ </element>
2667
+
2668
+ """
2669
+ blanks = " " * (indent * 4)
2670
+ lines = [
2671
+ f'{blanks}<element name="{subsequences.name}"',
2672
+ f'{blanks} minOccurs="0">',
2673
+ f"{blanks} <complexType>",
2674
+ f"{blanks} <sequence>",
2675
+ ]
2676
+ for sequence in subsequences:
2677
+ lines.append(cls.get_sequenceinsertion(sequence, indent + 3))
2678
+ lines.extend(
2679
+ [
2680
+ f"{blanks} </sequence>",
2681
+ f"{blanks} </complexType>",
2682
+ f"{blanks}</element>",
2683
+ ]
2684
+ )
2685
+ return "\n".join(lines)
2686
+
2687
+ @staticmethod
2688
+ def get_sequenceinsertion(sequence: sequencetools.Sequence_, indent: int) -> str:
2689
+ """Return the insertion string required for the given sequence.
2690
+
2691
+ >>> from hydpy.exe.xmltools import XSDWriter
2692
+ >>> from hydpy import prepare_model
2693
+ >>> model = prepare_model("hland_96")
2694
+ >>> print(XSDWriter.get_sequenceinsertion(model.sequences.fluxes.pc, 1))
2695
+ <element
2696
+ name="pc"
2697
+ minOccurs="0"/>
2698
+ """
2699
+ blanks = " " * (indent * 4)
2700
+ return (
2701
+ f"{blanks}<element\n"
2702
+ f'{blanks} name="{sequence.name}"\n'
2703
+ f'{blanks} minOccurs="0"/>'
2704
+ )
2705
+
2706
+ @classmethod
2707
+ def get_readerwriterinsertion(
2708
+ cls, type_: Literal["reader", "writer"], indent: int
2709
+ ) -> str:
2710
+ """Return the insertion all sequences relevant for reading or writing
2711
+ time series data.
2712
+
2713
+ >>> from hydpy.exe.xmltools import XSDWriter
2714
+ >>> print(XSDWriter.get_readerwriterinsertion("reader", 1)) # doctest: +ELLIPSIS
2715
+ <complexType name="readerType">
2716
+ <sequence>
2717
+ <element name="node"
2718
+ type="hpcb:node_readerType"
2719
+ minOccurs="0"/>
2720
+ <element name="dummy_interceptedwater"
2721
+ type="hpcb:dummy_interceptedwater_readerType"
2722
+ minOccurs="0"/>
2723
+ ...
2724
+ <element name="wland_wag"
2725
+ type="hpcb:wland_wag_readerType"
2726
+ minOccurs="0"/>
2727
+ </sequence>
2728
+ </complexType>
2729
+ <BLANKLINE>
2730
+ >>> print(XSDWriter.get_readerwriterinsertion("writer", 1)) # doctest: +ELLIPSIS
2731
+ <complexType name="writerType">
2732
+ <sequence>
2733
+ <element name="node"
2734
+ type="hpcb:node_writerType"
2735
+ minOccurs="0"/>
2736
+ <element name="arma_rimorido"
2737
+ type="hpcb:arma_rimorido_writerType"
2738
+ minOccurs="0"/>
2739
+ ...
2740
+ <element name="wq_trapeze_strickler"
2741
+ type="hpcb:wq_trapeze_strickler_writerType"
2742
+ minOccurs="0"/>
2743
+ </sequence>
2744
+ </complexType>
2745
+ <BLANKLINE>
2746
+ """
2747
+ blanks = " " * (indent * 4)
2748
+ subs = [
2749
+ f'{blanks}<complexType name="{type_}Type">',
2750
+ f"{blanks} <sequence>",
2751
+ f'{blanks} <element name="node"',
2752
+ f'{blanks} type="hpcb:node_{type_}Type"',
2753
+ f'{blanks} minOccurs="0"/>',
2754
+ ]
2755
+ for name in cls.get_applicationmodelnames():
2756
+ seqs = importtools.prepare_model(name).sequences
2757
+ if seqs.inputs or (
2758
+ (type_ == "writer") and (seqs.factors or seqs.fluxes or seqs.states)
2759
+ ):
2760
+ subs.extend(
2761
+ [
2762
+ f'{blanks} <element name="{name}"',
2763
+ f'{blanks} type="hpcb:{name}_{type_}Type"',
2764
+ f'{blanks} minOccurs="0"/>',
2765
+ ]
2766
+ )
2767
+ subs.extend([f"{blanks} </sequence>", f"{blanks}</complexType>", ""])
2768
+ return "\n".join(subs)
2769
+
2770
+ @classmethod
2771
+ def get_exchangeinsertion(cls) -> str:
2772
+ """Return the complete string related to the definition of exchange items to
2773
+ be inserted into the string of the template file.
2774
+
2775
+ >>> from hydpy.exe.xmltools import XSDWriter
2776
+ >>> print(XSDWriter.get_exchangeinsertion()) # doctest: +ELLIPSIS
2777
+ <complexType name="arma_rimorido_mathitemType">
2778
+ ...
2779
+ <element name="setitems">
2780
+ ...
2781
+ <complexType name="arma_rimorido_setitemsType">
2782
+ ...
2783
+ <element name="additems">
2784
+ ...
2785
+ <element name="multiplyitems">
2786
+ ...
2787
+ <element name="getitems">
2788
+ ...
2789
+ """
2790
+ indent = 1
2791
+ subs = [
2792
+ cls.get_mathitemsinsertion(indent),
2793
+ cls.get_keyworditemsinsertion(indent),
2794
+ ]
2795
+ for groupname in ("setitems", "additems", "multiplyitems", "getitems"):
2796
+ subs.append(cls.get_itemsinsertion(groupname, indent))
2797
+ subs.append(cls.get_itemtypesinsertion(groupname, indent))
2798
+ return "\n".join(subs)
2799
+
2800
+ @classmethod
2801
+ def get_mathitemsinsertion(cls, indent: int) -> str:
2802
+ """Return a string defining a model-specific XML type extending `ItemType`.
2803
+
2804
+ >>> from hydpy.exe.xmltools import XSDWriter
2805
+ >>> print(XSDWriter.get_mathitemsinsertion(1)) # doctest: +ELLIPSIS
2806
+ <complexType name="arma_rimorido_mathitemType">
2807
+ <complexContent>
2808
+ <extension base="hpcb:mathitemType">
2809
+ <choice>
2810
+ <element name="control.responses"/>
2811
+ ...
2812
+ <element name="fluxes.qout"/>
2813
+ </choice>
2814
+ </extension>
2815
+ </complexContent>
2816
+ </complexType>
2817
+ <BLANKLINE>
2818
+ <complexType name="conv_idw_mathitemType">
2819
+ ...
2820
+ """
2821
+ blanks = " " * (indent * 4)
2822
+ subs = []
2823
+ for modelname in cls.get_applicationmodelnames():
2824
+ model = importtools.prepare_model(modelname)
2825
+ subs.extend(
2826
+ [
2827
+ f'{blanks}<complexType name="{modelname}_mathitemType">',
2828
+ f"{blanks} <complexContent>",
2829
+ f'{blanks} <extension base="hpcb:mathitemType">',
2830
+ f"{blanks} <choice>",
2831
+ ]
2832
+ )
2833
+ for subvars in cls._get_subvars(model, conditions=False):
2834
+ for var in subvars:
2835
+ subs.append(
2836
+ f"{blanks} "
2837
+ f'<element name="{subvars.name}.{var.name}"/>'
2838
+ )
2839
+ subs.extend(
2840
+ [
2841
+ f"{blanks} </choice>",
2842
+ f"{blanks} </extension>",
2843
+ f"{blanks} </complexContent>",
2844
+ f"{blanks}</complexType>",
2845
+ "",
2846
+ ]
2847
+ )
2848
+ return "\n".join(subs)
2849
+
2850
+ @classmethod
2851
+ def get_keyworditemsinsertion(cls, indent: int) -> str:
2852
+ """Return a string defining additional types that support modifying parameter
2853
+ values by specific keyword arguments.
2854
+
2855
+ >>> from hydpy.exe.xmltools import XSDWriter
2856
+ >>> print(XSDWriter.get_keyworditemsinsertion(1)) # doctest: +ELLIPSIS
2857
+ <simpleType name="lland_control_kapgrenz_keywordType">
2858
+ <restriction base="string">
2859
+ <enumeration value="option"/>
2860
+ </restriction>
2861
+ </simpleType>
2862
+ <BLANKLINE>
2863
+ <complexType name="lland_control_kapgrenz_setitemType">
2864
+ <complexContent>
2865
+ <extension base="hpcb:setitemType">
2866
+ <sequence>
2867
+ <element name="keyword"
2868
+ type="hpcb:lland_control_kapgrenz_keywordType"
2869
+ minOccurs = "0"/>
2870
+ </sequence>
2871
+ </extension>
2872
+ </complexContent>
2873
+ </complexType>
2874
+ ...
2875
+ """
2876
+ blanks = " " * (indent * 4)
2877
+ subs = []
2878
+ for modelname in cls.get_basemodelnames():
2879
+ model = importtools.prepare_model(modelname)
2880
+ for subvars in cls._get_subvars(model, conditions=False):
2881
+ for var in subvars:
2882
+ if isinstance(var, parametertools.Parameter) and var.KEYWORDS:
2883
+ prefix = f"{modelname.split('_')[0]}_{subvars.name}_{var.name}_"
2884
+ subs.extend(
2885
+ [
2886
+ f'{blanks}<simpleType name="{prefix}keywordType">',
2887
+ f'{blanks} <restriction base="string">',
2888
+ ]
2889
+ )
2890
+ for keyword in var.KEYWORDS:
2891
+ subs.append(
2892
+ f'{blanks} <enumeration value="{keyword}"/>'
2893
+ )
2894
+ subs.extend(
2895
+ [
2896
+ f"{blanks} </restriction>",
2897
+ f"{blanks}</simpleType>",
2898
+ "",
2899
+ f'{blanks}<complexType name="{prefix}setitemType">',
2900
+ f"{blanks} <complexContent>",
2901
+ f'{blanks} <extension base="hpcb:setitemType">',
2902
+ f"{blanks} <sequence>",
2903
+ f'{blanks} <element name="keyword"',
2904
+ f"{blanks} "
2905
+ f'type="hpcb:{prefix}keywordType"',
2906
+ f'{blanks} minOccurs = "0"/>',
2907
+ f"{blanks} </sequence>",
2908
+ f"{blanks} </extension>",
2909
+ f"{blanks} </complexContent>",
2910
+ f"{blanks}</complexType>",
2911
+ "",
2912
+ ]
2913
+ )
2914
+ return "\n".join(subs)
2915
+
2916
+ @staticmethod
2917
+ def _get_itemstype(modelname: str, itemgroup: str) -> str:
2918
+ return f"{modelname}_{itemgroup}Type"
2919
+
2920
+ @classmethod
2921
+ def get_itemsinsertion(cls, itemgroup: str, indent: int) -> str:
2922
+ """Return a string defining the XML element for the given exchange item group.
2923
+
2924
+ >>> from hydpy.exe.xmltools import XSDWriter
2925
+ >>> print(XSDWriter.get_itemsinsertion("setitems", 1)) # doctest: +ELLIPSIS
2926
+ <element name="setitems">
2927
+ <complexType>
2928
+ <sequence>
2929
+ <element ref="hpcb:selections"
2930
+ minOccurs="0"/>
2931
+ ...
2932
+ <element name="hland_96"
2933
+ type="hpcb:hland_96_setitemsType"
2934
+ minOccurs="0"
2935
+ maxOccurs="unbounded"/>
2936
+ ...
2937
+ <element name="nodes"
2938
+ type="hpcb:nodes_setitemsType"
2939
+ minOccurs="0"
2940
+ maxOccurs="unbounded"/>
2941
+ </sequence>
2942
+ <attribute name="info" type="string"/>
2943
+ </complexType>
2944
+ </element>
2945
+ <BLANKLINE>
2946
+ """
2947
+ blanks = " " * (indent * 4)
2948
+ subs = []
2949
+ subs.extend(
2950
+ [
2951
+ f'{blanks}<element name="{itemgroup}">',
2952
+ f"{blanks} <complexType>",
2953
+ f"{blanks} <sequence>",
2954
+ f'{blanks} <element ref="hpcb:selections"',
2955
+ f'{blanks} minOccurs="0"/>',
2956
+ ]
2957
+ )
2958
+ for modelname in cls.get_applicationmodelnames():
2959
+ type_ = cls._get_itemstype(modelname, itemgroup)
2960
+ subs.append(f'{blanks} <element name="{modelname}"')
2961
+ subs.append(f'{blanks} type="hpcb:{type_}"')
2962
+ subs.append(f'{blanks} minOccurs="0"')
2963
+ subs.append(f'{blanks} maxOccurs="unbounded"/>')
2964
+ if itemgroup in ("setitems", "getitems"):
2965
+ type_ = f"nodes_{itemgroup}Type"
2966
+ subs.append(f'{blanks} <element name="nodes"')
2967
+ subs.append(f'{blanks} type="hpcb:{type_}"')
2968
+ subs.append(f'{blanks} minOccurs="0"')
2969
+ subs.append(f'{blanks} maxOccurs="unbounded"/>')
2970
+ subs.extend(
2971
+ [
2972
+ f"{blanks} </sequence>",
2973
+ f'{blanks} <attribute name="info" type="string"/>',
2974
+ f"{blanks} </complexType>",
2975
+ f"{blanks}</element>",
2976
+ "",
2977
+ ]
2978
+ )
2979
+ return "\n".join(subs)
2980
+
2981
+ @classmethod
2982
+ def get_itemtypesinsertion(cls, itemgroup: str, indent: int) -> str:
2983
+ """Return a string defining the required types for the given exchange item
2984
+ group.
2985
+
2986
+ >>> from hydpy.exe.xmltools import XSDWriter
2987
+ >>> print(XSDWriter.get_itemtypesinsertion(
2988
+ ... "setitems", 1)) # doctest: +ELLIPSIS
2989
+ <complexType name="arma_rimorido_setitemsType">
2990
+ ...
2991
+ </complexType>
2992
+ <BLANKLINE>
2993
+ <complexType name="dam_v001_setitemsType">
2994
+ ...
2995
+ <complexType name="nodes_setitemsType">
2996
+ ...
2997
+ """
2998
+ subs = []
2999
+ for modelname in cls.get_applicationmodelnames():
3000
+ subs.append(cls.get_itemtypeinsertion(itemgroup, modelname, indent))
3001
+ subs.append(cls.get_nodesitemtypeinsertion(itemgroup, indent))
3002
+ return "\n".join(subs)
3003
+
3004
+ @classmethod
3005
+ def get_itemtypeinsertion(cls, itemgroup: str, modelname: str, indent: int) -> str:
3006
+ """Return a string defining the required types for the given combination of
3007
+ an exchange item group and an application model.
3008
+
3009
+ >>> from hydpy.exe.xmltools import XSDWriter
3010
+ >>> print(XSDWriter.get_itemtypeinsertion(
3011
+ ... "setitems", "hland_96", 1)) # doctest: +ELLIPSIS
3012
+ <complexType name="hland_96_setitemsType">
3013
+ <sequence>
3014
+ <element ref="hpcb:selections"
3015
+ minOccurs="0"/>
3016
+ <element name="control"
3017
+ minOccurs="0"
3018
+ maxOccurs="unbounded">
3019
+ ...
3020
+ </sequence>
3021
+ </complexType>
3022
+ <BLANKLINE>
3023
+ """
3024
+ blanks = " " * (indent * 4)
3025
+ type_ = cls._get_itemstype(modelname, itemgroup)
3026
+ subs = [
3027
+ f'{blanks}<complexType name="{type_}">',
3028
+ f"{blanks} <sequence>",
3029
+ f'{blanks} <element ref="hpcb:selections"',
3030
+ f'{blanks} minOccurs="0"/>',
3031
+ cls.get_subgroupsiteminsertion(itemgroup, modelname, indent + 2),
3032
+ f"{blanks} </sequence>",
3033
+ f"{blanks}</complexType>",
3034
+ "",
3035
+ ]
3036
+ return "\n".join(subs)
3037
+
3038
+ @classmethod
3039
+ def get_nodesitemtypeinsertion(cls, itemgroup: str, indent: int) -> str:
3040
+ """Return a string defining the required types for the given combination of
3041
+ an exchange item group and |Node| objects.
3042
+
3043
+ >>> from hydpy.exe.xmltools import XSDWriter
3044
+ >>> print(XSDWriter.get_nodesitemtypeinsertion(
3045
+ ... "setitems", 1)) # doctest: +ELLIPSIS
3046
+ <complexType name="nodes_setitemsType">
3047
+ <sequence>
3048
+ <element ref="hpcb:selections"
3049
+ minOccurs="0"/>
3050
+ <element name="sim"
3051
+ type="hpcb:setitemType"
3052
+ minOccurs="0"
3053
+ maxOccurs="unbounded"/>
3054
+ <element name="obs"
3055
+ type="hpcb:setitemType"
3056
+ minOccurs="0"
3057
+ maxOccurs="unbounded"/>
3058
+ <element name="sim.series"
3059
+ type="hpcb:setitemType"
3060
+ minOccurs="0"
3061
+ maxOccurs="unbounded"/>
3062
+ <element name="obs.series"
3063
+ type="hpcb:setitemType"
3064
+ minOccurs="0"
3065
+ maxOccurs="unbounded"/>
3066
+ </sequence>
3067
+ </complexType>
3068
+ <BLANKLINE>
3069
+ """
3070
+ blanks = " " * (indent * 4)
3071
+ subs = [
3072
+ f'{blanks}<complexType name="nodes_{itemgroup}Type">',
3073
+ f"{blanks} <sequence>",
3074
+ f'{blanks} <element ref="hpcb:selections"',
3075
+ f'{blanks} minOccurs="0"/>',
3076
+ ]
3077
+ type_ = "getitemType" if itemgroup == "getitems" else "setitemType"
3078
+ for name in ("sim", "obs", "sim.series", "obs.series"):
3079
+ subs.extend(
3080
+ [
3081
+ f'{blanks} <element name="{name}"',
3082
+ f'{blanks} type="hpcb:{type_}"',
3083
+ f'{blanks} minOccurs="0"',
3084
+ f'{blanks} maxOccurs="unbounded"/>',
3085
+ ]
3086
+ )
3087
+ subs.extend([f"{blanks} </sequence>", f"{blanks}</complexType>", ""])
3088
+ return "\n".join(subs)
3089
+
3090
+ @classmethod
3091
+ def get_subgroupsiteminsertion(
3092
+ cls, itemgroup: str, modelname: str, indent: int
3093
+ ) -> str:
3094
+ """Return a string defining the required types for the given combination of an
3095
+ exchange item group and an application model.
3096
+
3097
+ >>> from hydpy.exe.xmltools import XSDWriter
3098
+ >>> print(XSDWriter.get_subgroupsiteminsertion(
3099
+ ... "setitems", "hland_96", 1)) # doctest: +ELLIPSIS
3100
+ <element name="control"
3101
+ minOccurs="0"
3102
+ maxOccurs="unbounded">
3103
+ ...
3104
+ </element>
3105
+ <element name="inputs"
3106
+ ...
3107
+ <element name="factors"
3108
+ ...
3109
+ <element name="fluxes"
3110
+ ...
3111
+ <element name="states"
3112
+ ...
3113
+ """
3114
+ subs = []
3115
+ model = importtools.prepare_model(modelname)
3116
+ conditions = itemgroup in ("getitems", "setitems")
3117
+ for subvars in cls._get_subvars(model, conditions=conditions):
3118
+ subs.append(
3119
+ cls.get_subgroupiteminsertion(itemgroup, model, subvars, indent)
3120
+ )
3121
+ return "\n".join(subs)
3122
+
3123
+ @classmethod
3124
+ def _get_subvars(
3125
+ cls, model: modeltools.Model, conditions: bool
3126
+ ) -> Iterator[variabletools.SubVariables[Any, Any, Any]]:
3127
+ yield model.parameters.control
3128
+ names = ["inputs", "factors", "fluxes"]
3129
+ if conditions:
3130
+ names.extend(("states", "logs"))
3131
+ for name in names:
3132
+ subseqs = getattr(model.sequences, name, None)
3133
+ if subseqs:
3134
+ yield subseqs
3135
+
3136
+ @classmethod
3137
+ def get_subgroupiteminsertion(
3138
+ cls,
3139
+ itemgroup: str,
3140
+ model: modeltools.Model,
3141
+ subgroup: variabletools.SubVariables[Any, Any, Any],
3142
+ indent: int,
3143
+ ) -> str:
3144
+ """Return a string defining the required types for the given combination of an
3145
+ exchange item group and a specific variable subgroup of an application model or
3146
+ class |Node|.
3147
+
3148
+ Note that for `setitems` and `getitems` `setitemType` and `getitemType` are
3149
+ referenced, respectively, and for all others, the model-specific `mathitemType`:
3150
+
3151
+ >>> from hydpy import prepare_model
3152
+ >>> model = prepare_model("hland_96")
3153
+ >>> from hydpy.exe.xmltools import XSDWriter
3154
+ >>> print(XSDWriter.get_subgroupiteminsertion( # doctest: +ELLIPSIS
3155
+ ... "setitems", model, model.parameters.control, 1))
3156
+ <element name="control"
3157
+ minOccurs="0"
3158
+ maxOccurs="unbounded">
3159
+ <complexType>
3160
+ <sequence>
3161
+ <element ref="hpcb:selections"
3162
+ minOccurs="0"/>
3163
+ <element name="area"
3164
+ type="hpcb:setitemType"
3165
+ minOccurs="0"
3166
+ maxOccurs="unbounded"/>
3167
+ <element name="nmbzones"
3168
+ ...
3169
+ </sequence>
3170
+ </complexType>
3171
+ </element>
3172
+
3173
+ >>> print(XSDWriter.get_subgroupiteminsertion( # doctest: +ELLIPSIS
3174
+ ... "getitems", model, model.parameters.control, 1))
3175
+ <element name="control"
3176
+ ...
3177
+ <element name="area"
3178
+ type="hpcb:getitemType"
3179
+ minOccurs="0"
3180
+ maxOccurs="unbounded"/>
3181
+ ...
3182
+
3183
+ >>> print(XSDWriter.get_subgroupiteminsertion( # doctest: +ELLIPSIS
3184
+ ... "additems", model, model.parameters.control, 1))
3185
+ <element name="control"
3186
+ ...
3187
+ <element name="area"
3188
+ type="hpcb:hland_96_mathitemType"
3189
+ minOccurs="0"
3190
+ maxOccurs="unbounded"/>
3191
+ ...
3192
+
3193
+ >>> print(XSDWriter.get_subgroupiteminsertion( # doctest: +ELLIPSIS
3194
+ ... "multiplyitems", model, model.parameters.control, 1))
3195
+ <element name="control"
3196
+ ...
3197
+ <element name="area"
3198
+ type="hpcb:hland_96_mathitemType"
3199
+ minOccurs="0"
3200
+ maxOccurs="unbounded"/>
3201
+ ...
3202
+
3203
+ For sequence classes, additional "series" elements are added:
3204
+
3205
+ >>> print(XSDWriter.get_subgroupiteminsertion( # doctest: +ELLIPSIS
3206
+ ... "setitems", model, model.sequences.factors, 1))
3207
+ <element name="factors"
3208
+ ...
3209
+ <element name="tc"
3210
+ type="hpcb:setitemType"
3211
+ minOccurs="0"
3212
+ maxOccurs="unbounded"/>
3213
+ <element name="tc.series"
3214
+ type="hpcb:setitemType"
3215
+ minOccurs="0"
3216
+ maxOccurs="unbounded"/>
3217
+ <element name="fracrain"
3218
+ ...
3219
+ </sequence>
3220
+ </complexType>
3221
+ </element>
3222
+ """
3223
+ blanks1 = " " * (indent * 4)
3224
+ blanks2 = " " * ((indent + 5) * 4 + 1)
3225
+ subs = [
3226
+ f'{blanks1}<element name="{subgroup.name}"',
3227
+ f'{blanks1} minOccurs="0"',
3228
+ f'{blanks1} maxOccurs="unbounded">',
3229
+ f"{blanks1} <complexType>",
3230
+ f"{blanks1} <sequence>",
3231
+ f'{blanks1} <element ref="hpcb:selections"',
3232
+ f'{blanks1} minOccurs="0"/>',
3233
+ ]
3234
+ seriesflags = [False] if subgroup.name == "control" else [False, True]
3235
+ for var in subgroup:
3236
+ for series in seriesflags:
3237
+ name = f"{var.name}.series" if series else var.name
3238
+ subs.append(f'{blanks1} <element name="{name}"')
3239
+ if itemgroup == "setitems":
3240
+ if isinstance(var, parametertools.Parameter) and var.KEYWORDS:
3241
+ type_ = (
3242
+ f"{model.name.split('_')[0]}_{subgroup.name}_"
3243
+ f"{var.name}_setitemType"
3244
+ )
3245
+ else:
3246
+ type_ = "setitemType"
3247
+ elif itemgroup == "getitems":
3248
+ type_ = "getitemType"
3249
+ else:
3250
+ type_ = f"{model.name}_mathitemType"
3251
+ subs.append(f'{blanks2}type="hpcb:{type_}"')
3252
+ subs.append(f'{blanks2}minOccurs="0"')
3253
+ subs.append(f'{blanks2}maxOccurs="unbounded"/>')
3254
+ subs.extend(
3255
+ [
3256
+ f"{blanks1} </sequence>",
3257
+ f"{blanks1} </complexType>",
3258
+ f"{blanks1}</element>",
3259
+ ]
3260
+ )
3261
+ return "\n".join(subs)
3262
+
3263
+
3264
+ def xml_validate(xmlpath: str) -> int:
3265
+ """Check if an XML file complies with its XSD Schema file.
3266
+
3267
+ |xml_validate| relies on method |XMLInterface.validate_xml| of class
3268
+ |XMLInterface|. It is primarily designed for command line usage, as explained in
3269
+ the :ref:`User Guide'a <user_guide>` :ref:`Simulation > XML <simulation_xml>`
3270
+ section.
3271
+ """
3272
+ dirpath, filename = os.path.split(xmlpath)
3273
+ interface = XMLInterface(filename=filename, directory=dirpath)
3274
+ try:
3275
+ interface.validate_xml()
3276
+ except BaseException as exc:
3277
+ print(str(exc).rpartition("the following error occurred:")[2].strip())
3278
+ return 999
3279
+ print(f"{xmlpath} successfully validated")
3280
+ return 0