HydPy 6.2.dev1__cp313-cp313-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (890) hide show
  1. hydpy/__init__.py +275 -0
  2. hydpy/aliases.py +2554 -0
  3. hydpy/auxs/__init__.py +0 -0
  4. hydpy/auxs/anntools.py +1305 -0
  5. hydpy/auxs/armatools.py +883 -0
  6. hydpy/auxs/calibtools.py +3337 -0
  7. hydpy/auxs/interptools.py +1094 -0
  8. hydpy/auxs/iuhtools.py +543 -0
  9. hydpy/auxs/networktools.py +597 -0
  10. hydpy/auxs/ppolytools.py +809 -0
  11. hydpy/auxs/quadtools.py +61 -0
  12. hydpy/auxs/roottools.py +228 -0
  13. hydpy/auxs/smoothtools.py +273 -0
  14. hydpy/auxs/statstools.py +2125 -0
  15. hydpy/auxs/validtools.py +81 -0
  16. hydpy/conf/HydPyConfigBase.xsd +68637 -0
  17. hydpy/conf/HydPyConfigBase.xsdt +358 -0
  18. hydpy/conf/HydPyConfigMultipleRuns.xsd +25 -0
  19. hydpy/conf/HydPyConfigSingleRun.xsd +24 -0
  20. hydpy/conf/__init__.py +0 -0
  21. hydpy/conf/a_coefficients_explicit_lobatto_sequence.npy +0 -0
  22. hydpy/conf/support_points_for_smoothpar_logistic2.npy +0 -0
  23. hydpy/config.py +42 -0
  24. hydpy/core/__init__.py +0 -0
  25. hydpy/core/aliastools.py +214 -0
  26. hydpy/core/autodoctools.py +1947 -0
  27. hydpy/core/auxfiletools.py +1169 -0
  28. hydpy/core/devicetools.py +3810 -0
  29. hydpy/core/exceptiontools.py +269 -0
  30. hydpy/core/filetools.py +1985 -0
  31. hydpy/core/hydpytools.py +3089 -0
  32. hydpy/core/importtools.py +1398 -0
  33. hydpy/core/indextools.py +345 -0
  34. hydpy/core/itemtools.py +1849 -0
  35. hydpy/core/masktools.py +460 -0
  36. hydpy/core/modeltools.py +4868 -0
  37. hydpy/core/netcdftools.py +2683 -0
  38. hydpy/core/objecttools.py +2023 -0
  39. hydpy/core/optiontools.py +1045 -0
  40. hydpy/core/parametertools.py +4674 -0
  41. hydpy/core/printtools.py +222 -0
  42. hydpy/core/propertytools.py +643 -0
  43. hydpy/core/pubtools.py +254 -0
  44. hydpy/core/selectiontools.py +1571 -0
  45. hydpy/core/sequencetools.py +4476 -0
  46. hydpy/core/seriestools.py +339 -0
  47. hydpy/core/testtools.py +2483 -0
  48. hydpy/core/timetools.py +3567 -0
  49. hydpy/core/typingtools.py +333 -0
  50. hydpy/core/variabletools.py +2615 -0
  51. hydpy/cythons/__init__.py +24 -0
  52. hydpy/cythons/annutils.pxd +33 -0
  53. hydpy/cythons/annutils.pyi +25 -0
  54. hydpy/cythons/annutils.pyx +120 -0
  55. hydpy/cythons/autogen/__init__.py +0 -0
  56. hydpy/cythons/autogen/annutils.cp313-win_amd64.pyd +0 -0
  57. hydpy/cythons/autogen/annutils.pxd +42 -0
  58. hydpy/cythons/autogen/annutils.pyx +129 -0
  59. hydpy/cythons/autogen/c_arma.cp313-win_amd64.pyd +0 -0
  60. hydpy/cythons/autogen/c_arma.pxd +179 -0
  61. hydpy/cythons/autogen/c_arma.pyx +356 -0
  62. hydpy/cythons/autogen/c_arma_rimorido.cp313-win_amd64.pyd +0 -0
  63. hydpy/cythons/autogen/c_arma_rimorido.pxd +179 -0
  64. hydpy/cythons/autogen/c_arma_rimorido.pyx +356 -0
  65. hydpy/cythons/autogen/c_conv.cp313-win_amd64.pyd +0 -0
  66. hydpy/cythons/autogen/c_conv.pxd +198 -0
  67. hydpy/cythons/autogen/c_conv.pyx +491 -0
  68. hydpy/cythons/autogen/c_conv_idw.cp313-win_amd64.pyd +0 -0
  69. hydpy/cythons/autogen/c_conv_idw.pxd +124 -0
  70. hydpy/cythons/autogen/c_conv_idw.pyx +264 -0
  71. hydpy/cythons/autogen/c_conv_idw_ed.cp313-win_amd64.pyd +0 -0
  72. hydpy/cythons/autogen/c_conv_idw_ed.pxd +197 -0
  73. hydpy/cythons/autogen/c_conv_idw_ed.pyx +481 -0
  74. hydpy/cythons/autogen/c_conv_nn.cp313-win_amd64.pyd +0 -0
  75. hydpy/cythons/autogen/c_conv_nn.pxd +120 -0
  76. hydpy/cythons/autogen/c_conv_nn.pyx +224 -0
  77. hydpy/cythons/autogen/c_dam.cp313-win_amd64.pyd +0 -0
  78. hydpy/cythons/autogen/c_dam.pxd +805 -0
  79. hydpy/cythons/autogen/c_dam.pyx +1477 -0
  80. hydpy/cythons/autogen/c_dam_llake.cp313-win_amd64.pyd +0 -0
  81. hydpy/cythons/autogen/c_dam_llake.pxd +364 -0
  82. hydpy/cythons/autogen/c_dam_llake.pyx +705 -0
  83. hydpy/cythons/autogen/c_dam_lreservoir.cp313-win_amd64.pyd +0 -0
  84. hydpy/cythons/autogen/c_dam_lreservoir.pxd +365 -0
  85. hydpy/cythons/autogen/c_dam_lreservoir.pyx +708 -0
  86. hydpy/cythons/autogen/c_dam_lretention.cp313-win_amd64.pyd +0 -0
  87. hydpy/cythons/autogen/c_dam_lretention.pxd +340 -0
  88. hydpy/cythons/autogen/c_dam_lretention.pyx +625 -0
  89. hydpy/cythons/autogen/c_dam_pump.cp313-win_amd64.pyd +0 -0
  90. hydpy/cythons/autogen/c_dam_pump.pxd +402 -0
  91. hydpy/cythons/autogen/c_dam_pump.pyx +724 -0
  92. hydpy/cythons/autogen/c_dam_pump_sluice.cp313-win_amd64.pyd +0 -0
  93. hydpy/cythons/autogen/c_dam_pump_sluice.pxd +452 -0
  94. hydpy/cythons/autogen/c_dam_pump_sluice.pyx +829 -0
  95. hydpy/cythons/autogen/c_dam_sluice.cp313-win_amd64.pyd +0 -0
  96. hydpy/cythons/autogen/c_dam_sluice.pxd +404 -0
  97. hydpy/cythons/autogen/c_dam_sluice.pyx +726 -0
  98. hydpy/cythons/autogen/c_dam_v001.cp313-win_amd64.pyd +0 -0
  99. hydpy/cythons/autogen/c_dam_v001.pxd +452 -0
  100. hydpy/cythons/autogen/c_dam_v001.pyx +816 -0
  101. hydpy/cythons/autogen/c_dam_v002.cp313-win_amd64.pyd +0 -0
  102. hydpy/cythons/autogen/c_dam_v002.pxd +394 -0
  103. hydpy/cythons/autogen/c_dam_v002.pyx +703 -0
  104. hydpy/cythons/autogen/c_dam_v003.cp313-win_amd64.pyd +0 -0
  105. hydpy/cythons/autogen/c_dam_v003.pxd +417 -0
  106. hydpy/cythons/autogen/c_dam_v003.pyx +744 -0
  107. hydpy/cythons/autogen/c_dam_v004.cp313-win_amd64.pyd +0 -0
  108. hydpy/cythons/autogen/c_dam_v004.pxd +486 -0
  109. hydpy/cythons/autogen/c_dam_v004.pyx +891 -0
  110. hydpy/cythons/autogen/c_dam_v005.cp313-win_amd64.pyd +0 -0
  111. hydpy/cythons/autogen/c_dam_v005.pxd +524 -0
  112. hydpy/cythons/autogen/c_dam_v005.pyx +928 -0
  113. hydpy/cythons/autogen/c_dummy.cp313-win_amd64.pyd +0 -0
  114. hydpy/cythons/autogen/c_dummy.pxd +151 -0
  115. hydpy/cythons/autogen/c_dummy.pyx +263 -0
  116. hydpy/cythons/autogen/c_dummy_interceptedwater.cp313-win_amd64.pyd +0 -0
  117. hydpy/cythons/autogen/c_dummy_interceptedwater.pxd +69 -0
  118. hydpy/cythons/autogen/c_dummy_interceptedwater.pyx +104 -0
  119. hydpy/cythons/autogen/c_dummy_node2node.cp313-win_amd64.pyd +0 -0
  120. hydpy/cythons/autogen/c_dummy_node2node.pxd +89 -0
  121. hydpy/cythons/autogen/c_dummy_node2node.pyx +148 -0
  122. hydpy/cythons/autogen/c_dummy_snowalbedo.cp313-win_amd64.pyd +0 -0
  123. hydpy/cythons/autogen/c_dummy_snowalbedo.pxd +69 -0
  124. hydpy/cythons/autogen/c_dummy_snowalbedo.pyx +104 -0
  125. hydpy/cythons/autogen/c_dummy_snowcover.cp313-win_amd64.pyd +0 -0
  126. hydpy/cythons/autogen/c_dummy_snowcover.pxd +69 -0
  127. hydpy/cythons/autogen/c_dummy_snowcover.pyx +104 -0
  128. hydpy/cythons/autogen/c_dummy_snowycanopy.cp313-win_amd64.pyd +0 -0
  129. hydpy/cythons/autogen/c_dummy_snowycanopy.pxd +69 -0
  130. hydpy/cythons/autogen/c_dummy_snowycanopy.pyx +104 -0
  131. hydpy/cythons/autogen/c_dummy_soilwater.cp313-win_amd64.pyd +0 -0
  132. hydpy/cythons/autogen/c_dummy_soilwater.pxd +69 -0
  133. hydpy/cythons/autogen/c_dummy_soilwater.pyx +104 -0
  134. hydpy/cythons/autogen/c_evap.cp313-win_amd64.pyd +0 -0
  135. hydpy/cythons/autogen/c_evap.pxd +1029 -0
  136. hydpy/cythons/autogen/c_evap.pyx +2601 -0
  137. hydpy/cythons/autogen/c_evap_aet_hbv96.cp313-win_amd64.pyd +0 -0
  138. hydpy/cythons/autogen/c_evap_aet_hbv96.pxd +227 -0
  139. hydpy/cythons/autogen/c_evap_aet_hbv96.pyx +584 -0
  140. hydpy/cythons/autogen/c_evap_aet_minhas.cp313-win_amd64.pyd +0 -0
  141. hydpy/cythons/autogen/c_evap_aet_minhas.pxd +193 -0
  142. hydpy/cythons/autogen/c_evap_aet_minhas.pyx +478 -0
  143. hydpy/cythons/autogen/c_evap_aet_morsim.cp313-win_amd64.pyd +0 -0
  144. hydpy/cythons/autogen/c_evap_aet_morsim.pxd +681 -0
  145. hydpy/cythons/autogen/c_evap_aet_morsim.pyx +1642 -0
  146. hydpy/cythons/autogen/c_evap_pet_ambav1.cp313-win_amd64.pyd +0 -0
  147. hydpy/cythons/autogen/c_evap_pet_ambav1.pxd +532 -0
  148. hydpy/cythons/autogen/c_evap_pet_ambav1.pyx +1296 -0
  149. hydpy/cythons/autogen/c_evap_pet_hbv96.cp313-win_amd64.pyd +0 -0
  150. hydpy/cythons/autogen/c_evap_pet_hbv96.pxd +179 -0
  151. hydpy/cythons/autogen/c_evap_pet_hbv96.pyx +328 -0
  152. hydpy/cythons/autogen/c_evap_pet_m.cp313-win_amd64.pyd +0 -0
  153. hydpy/cythons/autogen/c_evap_pet_m.pxd +124 -0
  154. hydpy/cythons/autogen/c_evap_pet_m.pyx +214 -0
  155. hydpy/cythons/autogen/c_evap_pet_mlc.cp313-win_amd64.pyd +0 -0
  156. hydpy/cythons/autogen/c_evap_pet_mlc.pxd +126 -0
  157. hydpy/cythons/autogen/c_evap_pet_mlc.pyx +214 -0
  158. hydpy/cythons/autogen/c_evap_ret_fao56.cp313-win_amd64.pyd +0 -0
  159. hydpy/cythons/autogen/c_evap_ret_fao56.pxd +305 -0
  160. hydpy/cythons/autogen/c_evap_ret_fao56.pyx +624 -0
  161. hydpy/cythons/autogen/c_evap_ret_io.cp313-win_amd64.pyd +0 -0
  162. hydpy/cythons/autogen/c_evap_ret_io.pxd +112 -0
  163. hydpy/cythons/autogen/c_evap_ret_io.pyx +176 -0
  164. hydpy/cythons/autogen/c_evap_ret_tw2002.cp313-win_amd64.pyd +0 -0
  165. hydpy/cythons/autogen/c_evap_ret_tw2002.pxd +139 -0
  166. hydpy/cythons/autogen/c_evap_ret_tw2002.pyx +273 -0
  167. hydpy/cythons/autogen/c_exch.cp313-win_amd64.pyd +0 -0
  168. hydpy/cythons/autogen/c_exch.pxd +230 -0
  169. hydpy/cythons/autogen/c_exch.pyx +462 -0
  170. hydpy/cythons/autogen/c_exch_branch_hbv96.cp313-win_amd64.pyd +0 -0
  171. hydpy/cythons/autogen/c_exch_branch_hbv96.pxd +134 -0
  172. hydpy/cythons/autogen/c_exch_branch_hbv96.pyx +255 -0
  173. hydpy/cythons/autogen/c_exch_waterlevel.cp313-win_amd64.pyd +0 -0
  174. hydpy/cythons/autogen/c_exch_waterlevel.pxd +54 -0
  175. hydpy/cythons/autogen/c_exch_waterlevel.pyx +78 -0
  176. hydpy/cythons/autogen/c_exch_weir_hbv96.cp313-win_amd64.pyd +0 -0
  177. hydpy/cythons/autogen/c_exch_weir_hbv96.pxd +156 -0
  178. hydpy/cythons/autogen/c_exch_weir_hbv96.pyx +282 -0
  179. hydpy/cythons/autogen/c_ga.cp313-win_amd64.pyd +0 -0
  180. hydpy/cythons/autogen/c_ga.pxd +353 -0
  181. hydpy/cythons/autogen/c_ga.pyx +1204 -0
  182. hydpy/cythons/autogen/c_ga_garto.cp313-win_amd64.pyd +0 -0
  183. hydpy/cythons/autogen/c_ga_garto.pxd +330 -0
  184. hydpy/cythons/autogen/c_ga_garto.pyx +1105 -0
  185. hydpy/cythons/autogen/c_ga_garto_submodel1.cp313-win_amd64.pyd +0 -0
  186. hydpy/cythons/autogen/c_ga_garto_submodel1.pxd +236 -0
  187. hydpy/cythons/autogen/c_ga_garto_submodel1.pyx +944 -0
  188. hydpy/cythons/autogen/c_gland.cp313-win_amd64.pyd +0 -0
  189. hydpy/cythons/autogen/c_gland.pxd +437 -0
  190. hydpy/cythons/autogen/c_gland.pyx +726 -0
  191. hydpy/cythons/autogen/c_gland_gr4.cp313-win_amd64.pyd +0 -0
  192. hydpy/cythons/autogen/c_gland_gr4.pxd +382 -0
  193. hydpy/cythons/autogen/c_gland_gr4.pyx +605 -0
  194. hydpy/cythons/autogen/c_gland_gr5.cp313-win_amd64.pyd +0 -0
  195. hydpy/cythons/autogen/c_gland_gr5.pxd +368 -0
  196. hydpy/cythons/autogen/c_gland_gr5.pyx +568 -0
  197. hydpy/cythons/autogen/c_gland_gr6.cp313-win_amd64.pyd +0 -0
  198. hydpy/cythons/autogen/c_gland_gr6.pxd +420 -0
  199. hydpy/cythons/autogen/c_gland_gr6.pyx +673 -0
  200. hydpy/cythons/autogen/c_hland.cp313-win_amd64.pyd +0 -0
  201. hydpy/cythons/autogen/c_hland.pxd +855 -0
  202. hydpy/cythons/autogen/c_hland.pyx +2486 -0
  203. hydpy/cythons/autogen/c_hland_96.cp313-win_amd64.pyd +0 -0
  204. hydpy/cythons/autogen/c_hland_96.pxd +631 -0
  205. hydpy/cythons/autogen/c_hland_96.pyx +1724 -0
  206. hydpy/cythons/autogen/c_hland_96c.cp313-win_amd64.pyd +0 -0
  207. hydpy/cythons/autogen/c_hland_96c.pxd +621 -0
  208. hydpy/cythons/autogen/c_hland_96c.pyx +1822 -0
  209. hydpy/cythons/autogen/c_hland_96p.cp313-win_amd64.pyd +0 -0
  210. hydpy/cythons/autogen/c_hland_96p.pxd +683 -0
  211. hydpy/cythons/autogen/c_hland_96p.pyx +1911 -0
  212. hydpy/cythons/autogen/c_kinw.cp313-win_amd64.pyd +0 -0
  213. hydpy/cythons/autogen/c_kinw.pxd +509 -0
  214. hydpy/cythons/autogen/c_kinw.pyx +965 -0
  215. hydpy/cythons/autogen/c_kinw_williams.cp313-win_amd64.pyd +0 -0
  216. hydpy/cythons/autogen/c_kinw_williams.pxd +409 -0
  217. hydpy/cythons/autogen/c_kinw_williams.pyx +763 -0
  218. hydpy/cythons/autogen/c_kinw_williams_ext.cp313-win_amd64.pyd +0 -0
  219. hydpy/cythons/autogen/c_kinw_williams_ext.pxd +220 -0
  220. hydpy/cythons/autogen/c_kinw_williams_ext.pyx +440 -0
  221. hydpy/cythons/autogen/c_lland.cp313-win_amd64.pyd +0 -0
  222. hydpy/cythons/autogen/c_lland.pxd +1386 -0
  223. hydpy/cythons/autogen/c_lland.pyx +3679 -0
  224. hydpy/cythons/autogen/c_lland_dd.cp313-win_amd64.pyd +0 -0
  225. hydpy/cythons/autogen/c_lland_dd.pxd +679 -0
  226. hydpy/cythons/autogen/c_lland_dd.pyx +1719 -0
  227. hydpy/cythons/autogen/c_lland_knauf.cp313-win_amd64.pyd +0 -0
  228. hydpy/cythons/autogen/c_lland_knauf.pxd +1096 -0
  229. hydpy/cythons/autogen/c_lland_knauf.pyx +2784 -0
  230. hydpy/cythons/autogen/c_lland_knauf_ic.cp313-win_amd64.pyd +0 -0
  231. hydpy/cythons/autogen/c_lland_knauf_ic.pxd +1369 -0
  232. hydpy/cythons/autogen/c_lland_knauf_ic.pyx +3625 -0
  233. hydpy/cythons/autogen/c_meteo.cp313-win_amd64.pyd +0 -0
  234. hydpy/cythons/autogen/c_meteo.pxd +469 -0
  235. hydpy/cythons/autogen/c_meteo.pyx +879 -0
  236. hydpy/cythons/autogen/c_meteo_clear_glob_io.cp313-win_amd64.pyd +0 -0
  237. hydpy/cythons/autogen/c_meteo_clear_glob_io.pxd +75 -0
  238. hydpy/cythons/autogen/c_meteo_clear_glob_io.pyx +107 -0
  239. hydpy/cythons/autogen/c_meteo_glob_fao56.cp313-win_amd64.pyd +0 -0
  240. hydpy/cythons/autogen/c_meteo_glob_fao56.pxd +209 -0
  241. hydpy/cythons/autogen/c_meteo_glob_fao56.pyx +339 -0
  242. hydpy/cythons/autogen/c_meteo_glob_io.cp313-win_amd64.pyd +0 -0
  243. hydpy/cythons/autogen/c_meteo_glob_io.pxd +63 -0
  244. hydpy/cythons/autogen/c_meteo_glob_io.pyx +91 -0
  245. hydpy/cythons/autogen/c_meteo_glob_morsim.cp313-win_amd64.pyd +0 -0
  246. hydpy/cythons/autogen/c_meteo_glob_morsim.pxd +289 -0
  247. hydpy/cythons/autogen/c_meteo_glob_morsim.pyx +527 -0
  248. hydpy/cythons/autogen/c_meteo_precip_io.cp313-win_amd64.pyd +0 -0
  249. hydpy/cythons/autogen/c_meteo_precip_io.pxd +112 -0
  250. hydpy/cythons/autogen/c_meteo_precip_io.pyx +176 -0
  251. hydpy/cythons/autogen/c_meteo_psun_sun_glob_io.cp313-win_amd64.pyd +0 -0
  252. hydpy/cythons/autogen/c_meteo_psun_sun_glob_io.pxd +87 -0
  253. hydpy/cythons/autogen/c_meteo_psun_sun_glob_io.pyx +123 -0
  254. hydpy/cythons/autogen/c_meteo_sun_fao56.cp313-win_amd64.pyd +0 -0
  255. hydpy/cythons/autogen/c_meteo_sun_fao56.pxd +209 -0
  256. hydpy/cythons/autogen/c_meteo_sun_fao56.pyx +343 -0
  257. hydpy/cythons/autogen/c_meteo_sun_morsim.cp313-win_amd64.pyd +0 -0
  258. hydpy/cythons/autogen/c_meteo_sun_morsim.pxd +286 -0
  259. hydpy/cythons/autogen/c_meteo_sun_morsim.pyx +519 -0
  260. hydpy/cythons/autogen/c_meteo_temp_io.cp313-win_amd64.pyd +0 -0
  261. hydpy/cythons/autogen/c_meteo_temp_io.pxd +112 -0
  262. hydpy/cythons/autogen/c_meteo_temp_io.pyx +176 -0
  263. hydpy/cythons/autogen/c_musk.cp313-win_amd64.pyd +0 -0
  264. hydpy/cythons/autogen/c_musk.pxd +282 -0
  265. hydpy/cythons/autogen/c_musk.pyx +605 -0
  266. hydpy/cythons/autogen/c_musk_classic.cp313-win_amd64.pyd +0 -0
  267. hydpy/cythons/autogen/c_musk_classic.pxd +138 -0
  268. hydpy/cythons/autogen/c_musk_classic.pyx +226 -0
  269. hydpy/cythons/autogen/c_musk_mct.cp313-win_amd64.pyd +0 -0
  270. hydpy/cythons/autogen/c_musk_mct.pxd +282 -0
  271. hydpy/cythons/autogen/c_musk_mct.pyx +609 -0
  272. hydpy/cythons/autogen/c_rconc.cp313-win_amd64.pyd +0 -0
  273. hydpy/cythons/autogen/c_rconc.pxd +119 -0
  274. hydpy/cythons/autogen/c_rconc.pyx +174 -0
  275. hydpy/cythons/autogen/c_rconc_nash.cp313-win_amd64.pyd +0 -0
  276. hydpy/cythons/autogen/c_rconc_nash.pxd +111 -0
  277. hydpy/cythons/autogen/c_rconc_nash.pyx +185 -0
  278. hydpy/cythons/autogen/c_rconc_uh.cp313-win_amd64.pyd +0 -0
  279. hydpy/cythons/autogen/c_rconc_uh.pxd +92 -0
  280. hydpy/cythons/autogen/c_rconc_uh.pyx +125 -0
  281. hydpy/cythons/autogen/c_sw1d.cp313-win_amd64.pyd +0 -0
  282. hydpy/cythons/autogen/c_sw1d.pxd +511 -0
  283. hydpy/cythons/autogen/c_sw1d.pyx +1263 -0
  284. hydpy/cythons/autogen/c_sw1d_channel.cp313-win_amd64.pyd +0 -0
  285. hydpy/cythons/autogen/c_sw1d_channel.pxd +119 -0
  286. hydpy/cythons/autogen/c_sw1d_channel.pyx +300 -0
  287. hydpy/cythons/autogen/c_sw1d_gate_out.cp313-win_amd64.pyd +0 -0
  288. hydpy/cythons/autogen/c_sw1d_gate_out.pxd +240 -0
  289. hydpy/cythons/autogen/c_sw1d_gate_out.pyx +476 -0
  290. hydpy/cythons/autogen/c_sw1d_lias.cp313-win_amd64.pyd +0 -0
  291. hydpy/cythons/autogen/c_sw1d_lias.pxd +320 -0
  292. hydpy/cythons/autogen/c_sw1d_lias.pyx +619 -0
  293. hydpy/cythons/autogen/c_sw1d_lias_sluice.cp313-win_amd64.pyd +0 -0
  294. hydpy/cythons/autogen/c_sw1d_lias_sluice.pxd +325 -0
  295. hydpy/cythons/autogen/c_sw1d_lias_sluice.pyx +644 -0
  296. hydpy/cythons/autogen/c_sw1d_network.cp313-win_amd64.pyd +0 -0
  297. hydpy/cythons/autogen/c_sw1d_network.pxd +90 -0
  298. hydpy/cythons/autogen/c_sw1d_network.pyx +246 -0
  299. hydpy/cythons/autogen/c_sw1d_pump.cp313-win_amd64.pyd +0 -0
  300. hydpy/cythons/autogen/c_sw1d_pump.pxd +256 -0
  301. hydpy/cythons/autogen/c_sw1d_pump.pyx +502 -0
  302. hydpy/cythons/autogen/c_sw1d_q_in.cp313-win_amd64.pyd +0 -0
  303. hydpy/cythons/autogen/c_sw1d_q_in.pxd +224 -0
  304. hydpy/cythons/autogen/c_sw1d_q_in.pyx +383 -0
  305. hydpy/cythons/autogen/c_sw1d_q_out.cp313-win_amd64.pyd +0 -0
  306. hydpy/cythons/autogen/c_sw1d_q_out.pxd +224 -0
  307. hydpy/cythons/autogen/c_sw1d_q_out.pyx +383 -0
  308. hydpy/cythons/autogen/c_sw1d_storage.cp313-win_amd64.pyd +0 -0
  309. hydpy/cythons/autogen/c_sw1d_storage.pxd +193 -0
  310. hydpy/cythons/autogen/c_sw1d_storage.pyx +349 -0
  311. hydpy/cythons/autogen/c_sw1d_weir_out.cp313-win_amd64.pyd +0 -0
  312. hydpy/cythons/autogen/c_sw1d_weir_out.pxd +212 -0
  313. hydpy/cythons/autogen/c_sw1d_weir_out.pyx +404 -0
  314. hydpy/cythons/autogen/c_test.cp313-win_amd64.pyd +0 -0
  315. hydpy/cythons/autogen/c_test.pxd +175 -0
  316. hydpy/cythons/autogen/c_test.pyx +348 -0
  317. hydpy/cythons/autogen/c_test_discontinous.cp313-win_amd64.pyd +0 -0
  318. hydpy/cythons/autogen/c_test_discontinous.pxd +146 -0
  319. hydpy/cythons/autogen/c_test_discontinous.pyx +256 -0
  320. hydpy/cythons/autogen/c_test_stiff0d.cp313-win_amd64.pyd +0 -0
  321. hydpy/cythons/autogen/c_test_stiff0d.pxd +146 -0
  322. hydpy/cythons/autogen/c_test_stiff0d.pyx +250 -0
  323. hydpy/cythons/autogen/c_test_stiff1d.cp313-win_amd64.pyd +0 -0
  324. hydpy/cythons/autogen/c_test_stiff1d.pxd +145 -0
  325. hydpy/cythons/autogen/c_test_stiff1d.pyx +294 -0
  326. hydpy/cythons/autogen/c_whmod.cp313-win_amd64.pyd +0 -0
  327. hydpy/cythons/autogen/c_whmod.pxd +482 -0
  328. hydpy/cythons/autogen/c_whmod.pyx +1156 -0
  329. hydpy/cythons/autogen/c_whmod_rural.cp313-win_amd64.pyd +0 -0
  330. hydpy/cythons/autogen/c_whmod_rural.pxd +411 -0
  331. hydpy/cythons/autogen/c_whmod_rural.pyx +982 -0
  332. hydpy/cythons/autogen/c_whmod_urban.cp313-win_amd64.pyd +0 -0
  333. hydpy/cythons/autogen/c_whmod_urban.pxd +482 -0
  334. hydpy/cythons/autogen/c_whmod_urban.pyx +1155 -0
  335. hydpy/cythons/autogen/c_wland.cp313-win_amd64.pyd +0 -0
  336. hydpy/cythons/autogen/c_wland.pxd +842 -0
  337. hydpy/cythons/autogen/c_wland.pyx +1890 -0
  338. hydpy/cythons/autogen/c_wland_gd.cp313-win_amd64.pyd +0 -0
  339. hydpy/cythons/autogen/c_wland_gd.pxd +829 -0
  340. hydpy/cythons/autogen/c_wland_gd.pyx +1847 -0
  341. hydpy/cythons/autogen/c_wland_wag.cp313-win_amd64.pyd +0 -0
  342. hydpy/cythons/autogen/c_wland_wag.pxd +810 -0
  343. hydpy/cythons/autogen/c_wland_wag.pyx +1780 -0
  344. hydpy/cythons/autogen/c_wq.cp313-win_amd64.pyd +0 -0
  345. hydpy/cythons/autogen/c_wq.pxd +275 -0
  346. hydpy/cythons/autogen/c_wq.pyx +652 -0
  347. hydpy/cythons/autogen/c_wq_trapeze.cp313-win_amd64.pyd +0 -0
  348. hydpy/cythons/autogen/c_wq_trapeze.pxd +170 -0
  349. hydpy/cythons/autogen/c_wq_trapeze.pyx +400 -0
  350. hydpy/cythons/autogen/c_wq_trapeze_strickler.cp313-win_amd64.pyd +0 -0
  351. hydpy/cythons/autogen/c_wq_trapeze_strickler.pxd +243 -0
  352. hydpy/cythons/autogen/c_wq_trapeze_strickler.pyx +578 -0
  353. hydpy/cythons/autogen/c_wq_walrus.cp313-win_amd64.pyd +0 -0
  354. hydpy/cythons/autogen/c_wq_walrus.pxd +61 -0
  355. hydpy/cythons/autogen/c_wq_walrus.pyx +82 -0
  356. hydpy/cythons/autogen/configutils.cp313-win_amd64.pyd +0 -0
  357. hydpy/cythons/autogen/configutils.pxd +17 -0
  358. hydpy/cythons/autogen/configutils.pyx +119 -0
  359. hydpy/cythons/autogen/interfaceutils.cp313-win_amd64.pyd +0 -0
  360. hydpy/cythons/autogen/interfaceutils.pxd +31 -0
  361. hydpy/cythons/autogen/interfaceutils.pyx +82 -0
  362. hydpy/cythons/autogen/interputils.cp313-win_amd64.pyd +0 -0
  363. hydpy/cythons/autogen/interputils.pxd +42 -0
  364. hydpy/cythons/autogen/interputils.pyx +118 -0
  365. hydpy/cythons/autogen/masterinterface.cp313-win_amd64.pyd +0 -0
  366. hydpy/cythons/autogen/masterinterface.pxd +153 -0
  367. hydpy/cythons/autogen/masterinterface.pyx +222 -0
  368. hydpy/cythons/autogen/pointerutils.cp313-win_amd64.pyd +0 -0
  369. hydpy/cythons/autogen/pointerutils.pxd +31 -0
  370. hydpy/cythons/autogen/pointerutils.pyx +650 -0
  371. hydpy/cythons/autogen/ppolyutils.cp313-win_amd64.pyd +0 -0
  372. hydpy/cythons/autogen/ppolyutils.pxd +35 -0
  373. hydpy/cythons/autogen/ppolyutils.pyx +59 -0
  374. hydpy/cythons/autogen/quadutils.cp313-win_amd64.pyd +0 -0
  375. hydpy/cythons/autogen/quadutils.pxd +26 -0
  376. hydpy/cythons/autogen/quadutils.pyx +973 -0
  377. hydpy/cythons/autogen/rootutils.cp313-win_amd64.pyd +0 -0
  378. hydpy/cythons/autogen/rootutils.pxd +28 -0
  379. hydpy/cythons/autogen/rootutils.pyx +109 -0
  380. hydpy/cythons/autogen/sequenceutils.cp313-win_amd64.pyd +0 -0
  381. hydpy/cythons/autogen/sequenceutils.pxd +45 -0
  382. hydpy/cythons/autogen/sequenceutils.pyx +101 -0
  383. hydpy/cythons/autogen/smoothutils.cp313-win_amd64.pyd +0 -0
  384. hydpy/cythons/autogen/smoothutils.pxd +29 -0
  385. hydpy/cythons/autogen/smoothutils.pyx +833 -0
  386. hydpy/cythons/configutils.pxd +8 -0
  387. hydpy/cythons/configutils.pyi +5 -0
  388. hydpy/cythons/configutils.pyx +110 -0
  389. hydpy/cythons/interfaceutils.pxd +22 -0
  390. hydpy/cythons/interfaceutils.pyi +15 -0
  391. hydpy/cythons/interfaceutils.pyx +73 -0
  392. hydpy/cythons/interputils.pxd +33 -0
  393. hydpy/cythons/interputils.pyi +32 -0
  394. hydpy/cythons/interputils.pyx +109 -0
  395. hydpy/cythons/modelutils.py +2990 -0
  396. hydpy/cythons/pointerutils.pxd +22 -0
  397. hydpy/cythons/pointerutils.pyi +89 -0
  398. hydpy/cythons/pointerutils.pyx +641 -0
  399. hydpy/cythons/ppolyutils.pxd +26 -0
  400. hydpy/cythons/ppolyutils.pyi +21 -0
  401. hydpy/cythons/ppolyutils.pyx +50 -0
  402. hydpy/cythons/quadutils.pxd +17 -0
  403. hydpy/cythons/quadutils.pyi +13 -0
  404. hydpy/cythons/quadutils.pyx +964 -0
  405. hydpy/cythons/rootutils.pxd +19 -0
  406. hydpy/cythons/rootutils.pyi +21 -0
  407. hydpy/cythons/rootutils.pyx +100 -0
  408. hydpy/cythons/sequenceutils.pxd +36 -0
  409. hydpy/cythons/sequenceutils.pyi +7 -0
  410. hydpy/cythons/sequenceutils.pyx +92 -0
  411. hydpy/cythons/smoothutils.pxd +20 -0
  412. hydpy/cythons/smoothutils.pyi +15 -0
  413. hydpy/cythons/smoothutils.pyx +824 -0
  414. hydpy/data/HydPy-H-Lahn/conditions/init_1996_01_01_00_00_00/land_dill_assl.py +13 -0
  415. hydpy/data/HydPy-H-Lahn/conditions/init_1996_01_01_00_00_00/land_lahn_kalk.py +13 -0
  416. hydpy/data/HydPy-H-Lahn/conditions/init_1996_01_01_00_00_00/land_lahn_leun.py +14 -0
  417. hydpy/data/HydPy-H-Lahn/conditions/init_1996_01_01_00_00_00/land_lahn_marb.py +13 -0
  418. hydpy/data/HydPy-H-Lahn/conditions/init_1996_01_01_00_00_00/stream_dill_assl_lahn_leun.py +5 -0
  419. hydpy/data/HydPy-H-Lahn/conditions/init_1996_01_01_00_00_00/stream_lahn_leun_lahn_kalk.py +5 -0
  420. hydpy/data/HydPy-H-Lahn/conditions/init_1996_01_01_00_00_00/stream_lahn_marb_lahn_leun.py +5 -0
  421. hydpy/data/HydPy-H-Lahn/control/default/land.py +9 -0
  422. hydpy/data/HydPy-H-Lahn/control/default/land_dill_assl.py +57 -0
  423. hydpy/data/HydPy-H-Lahn/control/default/land_lahn_kalk.py +57 -0
  424. hydpy/data/HydPy-H-Lahn/control/default/land_lahn_leun.py +56 -0
  425. hydpy/data/HydPy-H-Lahn/control/default/land_lahn_marb.py +57 -0
  426. hydpy/data/HydPy-H-Lahn/control/default/stream_dill_assl_lahn_leun.py +7 -0
  427. hydpy/data/HydPy-H-Lahn/control/default/stream_lahn_leun_lahn_kalk.py +7 -0
  428. hydpy/data/HydPy-H-Lahn/control/default/stream_lahn_marb_lahn_leun.py +7 -0
  429. hydpy/data/HydPy-H-Lahn/multiple_runs.xml +309 -0
  430. hydpy/data/HydPy-H-Lahn/multiple_runs_alpha.xml +71 -0
  431. hydpy/data/HydPy-H-Lahn/network/default/headwaters.py +11 -0
  432. hydpy/data/HydPy-H-Lahn/network/default/nonheadwaters.py +11 -0
  433. hydpy/data/HydPy-H-Lahn/network/default/streams.py +8 -0
  434. hydpy/data/HydPy-H-Lahn/series/default/dill_assl_obs_q.asc +11387 -0
  435. hydpy/data/HydPy-H-Lahn/series/default/evap_pet_hbv96_input_normalairtemperature.nc +0 -0
  436. hydpy/data/HydPy-H-Lahn/series/default/evap_pet_hbv96_input_normalevapotranspiration.nc +0 -0
  437. hydpy/data/HydPy-H-Lahn/series/default/hland_96_input_p.nc +0 -0
  438. hydpy/data/HydPy-H-Lahn/series/default/hland_96_input_t.nc +0 -0
  439. hydpy/data/HydPy-H-Lahn/series/default/lahn_kalk_obs_q.asc +11387 -0
  440. hydpy/data/HydPy-H-Lahn/series/default/lahn_leun_obs_q.asc +11387 -0
  441. hydpy/data/HydPy-H-Lahn/series/default/lahn_marb_obs_q.asc +11387 -0
  442. hydpy/data/HydPy-H-Lahn/series/default/land_dill_assl_evap_pet_hbv96_input_normalairtemperature.asc +11387 -0
  443. hydpy/data/HydPy-H-Lahn/series/default/land_dill_assl_evap_pet_hbv96_input_normalevapotranspiration.asc +11387 -0
  444. hydpy/data/HydPy-H-Lahn/series/default/land_dill_assl_hland_96_input_p.asc +11387 -0
  445. hydpy/data/HydPy-H-Lahn/series/default/land_dill_assl_hland_96_input_t.asc +11387 -0
  446. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_kalk_evap_pet_hbv96_input_normalairtemperature.asc +11387 -0
  447. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_kalk_evap_pet_hbv96_input_normalevapotranspiration.asc +11387 -0
  448. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_kalk_hland_96_input_p.asc +11387 -0
  449. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_kalk_hland_96_input_t.asc +11387 -0
  450. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_leun_evap_pet_hbv96_input_normalairtemperature.asc +11387 -0
  451. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_leun_evap_pet_hbv96_input_normalevapotranspiration.asc +11387 -0
  452. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_leun_hland_96_input_p.asc +11387 -0
  453. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_leun_hland_96_input_t.asc +11387 -0
  454. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_marb_evap_pet_hbv96_input_normalairtemperature.asc +11387 -0
  455. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_marb_evap_pet_hbv96_input_normalevapotranspiration.asc +11387 -0
  456. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_marb_hland_96_input_p.asc +11387 -0
  457. hydpy/data/HydPy-H-Lahn/series/default/land_lahn_marb_hland_96_input_t.asc +11387 -0
  458. hydpy/data/HydPy-H-Lahn/series/default/obs_q.nc +0 -0
  459. hydpy/data/HydPy-H-Lahn/single_run.xml +152 -0
  460. hydpy/data/HydPy-H-Lahn/single_run.xmlt +143 -0
  461. hydpy/data/__init__.py +17 -0
  462. hydpy/docs/__init__.py +0 -0
  463. hydpy/docs/autofigs/__init__.py +0 -0
  464. hydpy/docs/bib/__init__.py +0 -0
  465. hydpy/docs/bib/refs.bib +566 -0
  466. hydpy/docs/combine_docversions.py +133 -0
  467. hydpy/docs/draw_model_sketches.py +1301 -0
  468. hydpy/docs/enable_autodoc.py +7 -0
  469. hydpy/docs/figs/HydPy-G-GR4.png +0 -0
  470. hydpy/docs/figs/HydPy-G-GR5.png +0 -0
  471. hydpy/docs/figs/HydPy-G-GR6.png +0 -0
  472. hydpy/docs/figs/HydPy-H-HBV96-COSERO.png +0 -0
  473. hydpy/docs/figs/HydPy-H-HBV96-PREVAH.png +0 -0
  474. hydpy/docs/figs/HydPy-H-HBV96.png +0 -0
  475. hydpy/docs/figs/HydPy-H-Lahn.png +0 -0
  476. hydpy/docs/figs/HydPy-KinW-Williams.png +0 -0
  477. hydpy/docs/figs/HydPy-L-DD.png +0 -0
  478. hydpy/docs/figs/HydPy-W-Wag.png +0 -0
  479. hydpy/docs/figs/HydPy_Logo.png +0 -0
  480. hydpy/docs/figs/HydPy_Logo_Text.png +0 -0
  481. hydpy/docs/figs/IDLE-editor.png +0 -0
  482. hydpy/docs/figs/IDLE-shell.png +0 -0
  483. hydpy/docs/figs/LAWA_river-basin-bumbers.png +0 -0
  484. hydpy/docs/figs/__init__.py +0 -0
  485. hydpy/docs/html_/__init__.py +0 -0
  486. hydpy/docs/polish_html.py +57 -0
  487. hydpy/docs/prepare.py +224 -0
  488. hydpy/docs/publish_docs.py +53 -0
  489. hydpy/docs/rst/HydPy-ARMA.rst +27 -0
  490. hydpy/docs/rst/HydPy-Conv.rst +22 -0
  491. hydpy/docs/rst/HydPy-Dam.rst +79 -0
  492. hydpy/docs/rst/HydPy-Dummy.rst +21 -0
  493. hydpy/docs/rst/HydPy-Evap.rst +26 -0
  494. hydpy/docs/rst/HydPy-Exch.rst +36 -0
  495. hydpy/docs/rst/HydPy-G.rst +40 -0
  496. hydpy/docs/rst/HydPy-GA.rst +34 -0
  497. hydpy/docs/rst/HydPy-H.rst +24 -0
  498. hydpy/docs/rst/HydPy-KinW.rst +32 -0
  499. hydpy/docs/rst/HydPy-L.rst +42 -0
  500. hydpy/docs/rst/HydPy-Meteo.rst +28 -0
  501. hydpy/docs/rst/HydPy-Musk.rst +21 -0
  502. hydpy/docs/rst/HydPy-Rconc.rst +17 -0
  503. hydpy/docs/rst/HydPy-SW1D.rst +49 -0
  504. hydpy/docs/rst/HydPy-Test.rst +19 -0
  505. hydpy/docs/rst/HydPy-W.rst +20 -0
  506. hydpy/docs/rst/HydPy-WHMod.rst +19 -0
  507. hydpy/docs/rst/HydPy-WQ.rst +20 -0
  508. hydpy/docs/rst/__init__.py +0 -0
  509. hydpy/docs/rst/additional_repositories.rst +40 -0
  510. hydpy/docs/rst/auxiliaries.rst +31 -0
  511. hydpy/docs/rst/continuous_integration.rst +75 -0
  512. hydpy/docs/rst/core.rst +75 -0
  513. hydpy/docs/rst/cythons.rst +47 -0
  514. hydpy/docs/rst/definitions.rst +506 -0
  515. hydpy/docs/rst/developer_guide.rst +54 -0
  516. hydpy/docs/rst/example_projects.rst +40 -0
  517. hydpy/docs/rst/execution.rst +22 -0
  518. hydpy/docs/rst/framework_tools.rst +56 -0
  519. hydpy/docs/rst/how_to_read_the_reference_manual.rst +156 -0
  520. hydpy/docs/rst/hydpydependencies.rst +55 -0
  521. hydpy/docs/rst/index.rst +125 -0
  522. hydpy/docs/rst/installation.rst +155 -0
  523. hydpy/docs/rst/model_families.rst +79 -0
  524. hydpy/docs/rst/model_overview.rst +291 -0
  525. hydpy/docs/rst/modelimports.rst +10 -0
  526. hydpy/docs/rst/options.rst +119 -0
  527. hydpy/docs/rst/programming_style.rst +572 -0
  528. hydpy/docs/rst/project_structure.rst +520 -0
  529. hydpy/docs/rst/quickstart.rst +304 -0
  530. hydpy/docs/rst/reference_manual.rst +29 -0
  531. hydpy/docs/rst/required_tools.rst +50 -0
  532. hydpy/docs/rst/simulation.rst +514 -0
  533. hydpy/docs/rst/submodel_interfaces.rst +32 -0
  534. hydpy/docs/rst/tests_and_documentation.rst +85 -0
  535. hydpy/docs/rst/user_guide.rst +38 -0
  536. hydpy/docs/rst/version_control.rst +116 -0
  537. hydpy/docs/rst/zbibliography.rst +8 -0
  538. hydpy/docs/sphinx/__init__.py +0 -0
  539. hydpy/docs/sphinx/_themes/basic_hydpy/changes/frameset.html +11 -0
  540. hydpy/docs/sphinx/_themes/basic_hydpy/changes/rstsource.html +15 -0
  541. hydpy/docs/sphinx/_themes/basic_hydpy/changes/versionchanges.html +33 -0
  542. hydpy/docs/sphinx/_themes/basic_hydpy/defindex.html +35 -0
  543. hydpy/docs/sphinx/_themes/basic_hydpy/domainindex.html +56 -0
  544. hydpy/docs/sphinx/_themes/basic_hydpy/genindex-single.html +63 -0
  545. hydpy/docs/sphinx/_themes/basic_hydpy/genindex-split.html +41 -0
  546. hydpy/docs/sphinx/_themes/basic_hydpy/genindex.html +76 -0
  547. hydpy/docs/sphinx/_themes/basic_hydpy/globaltoc.html +11 -0
  548. hydpy/docs/sphinx/_themes/basic_hydpy/layout.html +221 -0
  549. hydpy/docs/sphinx/_themes/basic_hydpy/localtoc.html +15 -0
  550. hydpy/docs/sphinx/_themes/basic_hydpy/opensearch.xml +13 -0
  551. hydpy/docs/sphinx/_themes/basic_hydpy/page.html +13 -0
  552. hydpy/docs/sphinx/_themes/basic_hydpy/relations.html +23 -0
  553. hydpy/docs/sphinx/_themes/basic_hydpy/search.html +65 -0
  554. hydpy/docs/sphinx/_themes/basic_hydpy/searchbox.html +21 -0
  555. hydpy/docs/sphinx/_themes/basic_hydpy/searchfield.html +23 -0
  556. hydpy/docs/sphinx/_themes/basic_hydpy/sourcelink.html +18 -0
  557. hydpy/docs/sphinx/_themes/basic_hydpy/static/basic.css_t +925 -0
  558. hydpy/docs/sphinx/_themes/basic_hydpy/static/doctools.js +156 -0
  559. hydpy/docs/sphinx/_themes/basic_hydpy/static/documentation_options.js_t +13 -0
  560. hydpy/docs/sphinx/_themes/basic_hydpy/static/file.png +0 -0
  561. hydpy/docs/sphinx/_themes/basic_hydpy/static/language_data.js_t +26 -0
  562. hydpy/docs/sphinx/_themes/basic_hydpy/static/minus.png +0 -0
  563. hydpy/docs/sphinx/_themes/basic_hydpy/static/plus.png +0 -0
  564. hydpy/docs/sphinx/_themes/basic_hydpy/static/searchtools.js +574 -0
  565. hydpy/docs/sphinx/_themes/basic_hydpy/static/sphinx_highlight.js +154 -0
  566. hydpy/docs/sphinx/_themes/basic_hydpy/theme.conf +16 -0
  567. hydpy/docs/sphinx/_themes/classic_hydpy/layout.html +23 -0
  568. hydpy/docs/sphinx/_themes/classic_hydpy/static/classic.css_t +358 -0
  569. hydpy/docs/sphinx/_themes/classic_hydpy/static/sidebar.js_t +72 -0
  570. hydpy/docs/sphinx/_themes/classic_hydpy/theme.conf +32 -0
  571. hydpy/docs/sphinx/conf.py +398 -0
  572. hydpy/docs/sphinx/defaultlinks_extension.py +36 -0
  573. hydpy/docs/sphinx/integrationtest_extension.py +104 -0
  574. hydpy/docs/sphinx/projectstructure_extension.py +58 -0
  575. hydpy/docs/sphinx/submodelgraph_extension.py +53 -0
  576. hydpy/exe/__init__.py +0 -0
  577. hydpy/exe/commandtools.py +651 -0
  578. hydpy/exe/hyd.py +277 -0
  579. hydpy/exe/modelimports.py +41 -0
  580. hydpy/exe/replacetools.py +216 -0
  581. hydpy/exe/servertools.py +2348 -0
  582. hydpy/exe/xmltools.py +3280 -0
  583. hydpy/interfaces/__init__.py +0 -0
  584. hydpy/interfaces/aetinterfaces.py +94 -0
  585. hydpy/interfaces/dischargeinterfaces.py +45 -0
  586. hydpy/interfaces/petinterfaces.py +117 -0
  587. hydpy/interfaces/precipinterfaces.py +42 -0
  588. hydpy/interfaces/radiationinterfaces.py +79 -0
  589. hydpy/interfaces/rconcinterfaces.py +30 -0
  590. hydpy/interfaces/routinginterfaces.py +324 -0
  591. hydpy/interfaces/soilinterfaces.py +96 -0
  592. hydpy/interfaces/stateinterfaces.py +98 -0
  593. hydpy/interfaces/tempinterfaces.py +46 -0
  594. hydpy/models/__init__.py +0 -0
  595. hydpy/models/arma/__init__.py +14 -0
  596. hydpy/models/arma/arma_control.py +383 -0
  597. hydpy/models/arma/arma_derived.py +204 -0
  598. hydpy/models/arma/arma_fluxes.py +41 -0
  599. hydpy/models/arma/arma_inlets.py +11 -0
  600. hydpy/models/arma/arma_logs.py +19 -0
  601. hydpy/models/arma/arma_model.py +461 -0
  602. hydpy/models/arma/arma_outlets.py +11 -0
  603. hydpy/models/arma_rimorido.py +381 -0
  604. hydpy/models/conv/__init__.py +12 -0
  605. hydpy/models/conv/conv_control.py +303 -0
  606. hydpy/models/conv/conv_derived.py +271 -0
  607. hydpy/models/conv/conv_fluxes.py +54 -0
  608. hydpy/models/conv/conv_inlets.py +11 -0
  609. hydpy/models/conv/conv_model.py +687 -0
  610. hydpy/models/conv/conv_outlets.py +11 -0
  611. hydpy/models/conv_idw.py +120 -0
  612. hydpy/models/conv_idw_ed.py +184 -0
  613. hydpy/models/conv_nn.py +112 -0
  614. hydpy/models/dam/__init__.py +16 -0
  615. hydpy/models/dam/dam_aides.py +17 -0
  616. hydpy/models/dam/dam_control.py +346 -0
  617. hydpy/models/dam/dam_derived.py +559 -0
  618. hydpy/models/dam/dam_factors.py +46 -0
  619. hydpy/models/dam/dam_fluxes.py +179 -0
  620. hydpy/models/dam/dam_inlets.py +29 -0
  621. hydpy/models/dam/dam_logs.py +52 -0
  622. hydpy/models/dam/dam_model.py +5011 -0
  623. hydpy/models/dam/dam_outlets.py +23 -0
  624. hydpy/models/dam/dam_receivers.py +41 -0
  625. hydpy/models/dam/dam_senders.py +23 -0
  626. hydpy/models/dam/dam_solver.py +75 -0
  627. hydpy/models/dam/dam_states.py +11 -0
  628. hydpy/models/dam_llake.py +499 -0
  629. hydpy/models/dam_lreservoir.py +548 -0
  630. hydpy/models/dam_lretention.py +343 -0
  631. hydpy/models/dam_pump.py +278 -0
  632. hydpy/models/dam_pump_sluice.py +339 -0
  633. hydpy/models/dam_sluice.py +319 -0
  634. hydpy/models/dam_v001.py +1127 -0
  635. hydpy/models/dam_v002.py +381 -0
  636. hydpy/models/dam_v003.py +422 -0
  637. hydpy/models/dam_v004.py +665 -0
  638. hydpy/models/dam_v005.py +479 -0
  639. hydpy/models/dummy/__init__.py +15 -0
  640. hydpy/models/dummy/dummy_control.py +22 -0
  641. hydpy/models/dummy/dummy_fluxes.py +11 -0
  642. hydpy/models/dummy/dummy_inlets.py +11 -0
  643. hydpy/models/dummy/dummy_inputs.py +41 -0
  644. hydpy/models/dummy/dummy_model.py +196 -0
  645. hydpy/models/dummy/dummy_outlets.py +11 -0
  646. hydpy/models/dummy_interceptedwater.py +85 -0
  647. hydpy/models/dummy_node2node.py +83 -0
  648. hydpy/models/dummy_snowalbedo.py +84 -0
  649. hydpy/models/dummy_snowcover.py +84 -0
  650. hydpy/models/dummy_snowycanopy.py +86 -0
  651. hydpy/models/dummy_soilwater.py +85 -0
  652. hydpy/models/evap/__init__.py +13 -0
  653. hydpy/models/evap/evap_control.py +354 -0
  654. hydpy/models/evap/evap_derived.py +236 -0
  655. hydpy/models/evap/evap_factors.py +188 -0
  656. hydpy/models/evap/evap_fixed.py +68 -0
  657. hydpy/models/evap/evap_fluxes.py +150 -0
  658. hydpy/models/evap/evap_inputs.py +54 -0
  659. hydpy/models/evap/evap_logs.py +91 -0
  660. hydpy/models/evap/evap_masks.py +48 -0
  661. hydpy/models/evap/evap_model.py +9170 -0
  662. hydpy/models/evap/evap_parameters.py +149 -0
  663. hydpy/models/evap/evap_sequences.py +32 -0
  664. hydpy/models/evap/evap_states.py +18 -0
  665. hydpy/models/evap_aet_hbv96.py +372 -0
  666. hydpy/models/evap_aet_minhas.py +331 -0
  667. hydpy/models/evap_aet_morsim.py +627 -0
  668. hydpy/models/evap_pet_ambav1.py +483 -0
  669. hydpy/models/evap_pet_hbv96.py +147 -0
  670. hydpy/models/evap_pet_m.py +94 -0
  671. hydpy/models/evap_pet_mlc.py +107 -0
  672. hydpy/models/evap_ret_fao56.py +265 -0
  673. hydpy/models/evap_ret_io.py +74 -0
  674. hydpy/models/evap_ret_tw2002.py +165 -0
  675. hydpy/models/exch/__init__.py +14 -0
  676. hydpy/models/exch/exch_control.py +262 -0
  677. hydpy/models/exch/exch_derived.py +36 -0
  678. hydpy/models/exch/exch_factors.py +26 -0
  679. hydpy/models/exch/exch_fluxes.py +48 -0
  680. hydpy/models/exch/exch_inlets.py +11 -0
  681. hydpy/models/exch/exch_logs.py +12 -0
  682. hydpy/models/exch/exch_model.py +451 -0
  683. hydpy/models/exch/exch_outlets.py +17 -0
  684. hydpy/models/exch/exch_receivers.py +17 -0
  685. hydpy/models/exch_branch_hbv96.py +186 -0
  686. hydpy/models/exch_waterlevel.py +73 -0
  687. hydpy/models/exch_weir_hbv96.py +609 -0
  688. hydpy/models/ga/__init__.py +14 -0
  689. hydpy/models/ga/ga_aides.py +17 -0
  690. hydpy/models/ga/ga_control.py +208 -0
  691. hydpy/models/ga/ga_derived.py +77 -0
  692. hydpy/models/ga/ga_fluxes.py +83 -0
  693. hydpy/models/ga/ga_inputs.py +26 -0
  694. hydpy/models/ga/ga_logs.py +17 -0
  695. hydpy/models/ga/ga_model.py +2952 -0
  696. hydpy/models/ga/ga_states.py +87 -0
  697. hydpy/models/ga_garto.py +1001 -0
  698. hydpy/models/ga_garto_submodel1.py +79 -0
  699. hydpy/models/gland/__init__.py +14 -0
  700. hydpy/models/gland/gland_control.py +90 -0
  701. hydpy/models/gland/gland_derived.py +113 -0
  702. hydpy/models/gland/gland_fluxes.py +137 -0
  703. hydpy/models/gland/gland_inputs.py +12 -0
  704. hydpy/models/gland/gland_model.py +1439 -0
  705. hydpy/models/gland/gland_outlets.py +11 -0
  706. hydpy/models/gland/gland_states.py +90 -0
  707. hydpy/models/gland_gr4.py +501 -0
  708. hydpy/models/gland_gr5.py +463 -0
  709. hydpy/models/gland_gr6.py +487 -0
  710. hydpy/models/hland/__init__.py +20 -0
  711. hydpy/models/hland/hland_aides.py +19 -0
  712. hydpy/models/hland/hland_constants.py +37 -0
  713. hydpy/models/hland/hland_control.py +1530 -0
  714. hydpy/models/hland/hland_derived.py +683 -0
  715. hydpy/models/hland/hland_factors.py +57 -0
  716. hydpy/models/hland/hland_fixed.py +42 -0
  717. hydpy/models/hland/hland_fluxes.py +279 -0
  718. hydpy/models/hland/hland_inputs.py +19 -0
  719. hydpy/models/hland/hland_masks.py +107 -0
  720. hydpy/models/hland/hland_model.py +4664 -0
  721. hydpy/models/hland/hland_outlets.py +11 -0
  722. hydpy/models/hland/hland_parameters.py +227 -0
  723. hydpy/models/hland/hland_sequences.py +382 -0
  724. hydpy/models/hland/hland_states.py +236 -0
  725. hydpy/models/hland_96.py +1812 -0
  726. hydpy/models/hland_96c.py +1196 -0
  727. hydpy/models/hland_96p.py +1204 -0
  728. hydpy/models/kinw/__init__.py +18 -0
  729. hydpy/models/kinw/kinw_aides.py +306 -0
  730. hydpy/models/kinw/kinw_control.py +270 -0
  731. hydpy/models/kinw/kinw_derived.py +197 -0
  732. hydpy/models/kinw/kinw_fixed.py +33 -0
  733. hydpy/models/kinw/kinw_fluxes.py +37 -0
  734. hydpy/models/kinw/kinw_inlets.py +11 -0
  735. hydpy/models/kinw/kinw_model.py +3026 -0
  736. hydpy/models/kinw/kinw_outlets.py +11 -0
  737. hydpy/models/kinw/kinw_solver.py +45 -0
  738. hydpy/models/kinw/kinw_states.py +17 -0
  739. hydpy/models/kinw_williams.py +1299 -0
  740. hydpy/models/kinw_williams_ext.py +768 -0
  741. hydpy/models/lland/__init__.py +42 -0
  742. hydpy/models/lland/lland_aides.py +38 -0
  743. hydpy/models/lland/lland_constants.py +88 -0
  744. hydpy/models/lland/lland_control.py +1329 -0
  745. hydpy/models/lland/lland_derived.py +380 -0
  746. hydpy/models/lland/lland_factors.py +18 -0
  747. hydpy/models/lland/lland_fixed.py +128 -0
  748. hydpy/models/lland/lland_fluxes.py +626 -0
  749. hydpy/models/lland/lland_inlets.py +12 -0
  750. hydpy/models/lland/lland_inputs.py +33 -0
  751. hydpy/models/lland/lland_logs.py +17 -0
  752. hydpy/models/lland/lland_masks.py +212 -0
  753. hydpy/models/lland/lland_model.py +7690 -0
  754. hydpy/models/lland/lland_outlets.py +12 -0
  755. hydpy/models/lland/lland_parameters.py +195 -0
  756. hydpy/models/lland/lland_sequences.py +67 -0
  757. hydpy/models/lland/lland_states.py +280 -0
  758. hydpy/models/lland_dd.py +2270 -0
  759. hydpy/models/lland_knauf.py +2156 -0
  760. hydpy/models/lland_knauf_ic.py +1920 -0
  761. hydpy/models/meteo/__init__.py +12 -0
  762. hydpy/models/meteo/meteo_control.py +154 -0
  763. hydpy/models/meteo/meteo_derived.py +159 -0
  764. hydpy/models/meteo/meteo_factors.py +88 -0
  765. hydpy/models/meteo/meteo_fixed.py +19 -0
  766. hydpy/models/meteo/meteo_fluxes.py +46 -0
  767. hydpy/models/meteo/meteo_inputs.py +47 -0
  768. hydpy/models/meteo/meteo_logs.py +30 -0
  769. hydpy/models/meteo/meteo_model.py +2904 -0
  770. hydpy/models/meteo/meteo_parameters.py +14 -0
  771. hydpy/models/meteo/meteo_sequences.py +22 -0
  772. hydpy/models/meteo_clear_glob_io.py +77 -0
  773. hydpy/models/meteo_glob_fao56.py +217 -0
  774. hydpy/models/meteo_glob_io.py +68 -0
  775. hydpy/models/meteo_glob_morsim.py +444 -0
  776. hydpy/models/meteo_precip_io.py +76 -0
  777. hydpy/models/meteo_psun_sun_glob_io.py +83 -0
  778. hydpy/models/meteo_sun_fao56.py +188 -0
  779. hydpy/models/meteo_sun_morsim.py +466 -0
  780. hydpy/models/meteo_temp_io.py +76 -0
  781. hydpy/models/musk/__init__.py +15 -0
  782. hydpy/models/musk/musk_control.py +328 -0
  783. hydpy/models/musk/musk_derived.py +32 -0
  784. hydpy/models/musk/musk_factors.py +53 -0
  785. hydpy/models/musk/musk_fluxes.py +24 -0
  786. hydpy/models/musk/musk_inlets.py +11 -0
  787. hydpy/models/musk/musk_masks.py +15 -0
  788. hydpy/models/musk/musk_model.py +838 -0
  789. hydpy/models/musk/musk_outlets.py +11 -0
  790. hydpy/models/musk/musk_sequences.py +88 -0
  791. hydpy/models/musk/musk_solver.py +68 -0
  792. hydpy/models/musk/musk_states.py +64 -0
  793. hydpy/models/musk_classic.py +228 -0
  794. hydpy/models/musk_mct.py +1247 -0
  795. hydpy/models/rconc/__init__.py +12 -0
  796. hydpy/models/rconc/rconc_control.py +473 -0
  797. hydpy/models/rconc/rconc_derived.py +76 -0
  798. hydpy/models/rconc/rconc_fluxes.py +19 -0
  799. hydpy/models/rconc/rconc_logs.py +74 -0
  800. hydpy/models/rconc/rconc_model.py +260 -0
  801. hydpy/models/rconc/rconc_states.py +11 -0
  802. hydpy/models/rconc_nash.py +48 -0
  803. hydpy/models/rconc_uh.py +53 -0
  804. hydpy/models/sw1d/__init__.py +17 -0
  805. hydpy/models/sw1d/sw1d_control.py +356 -0
  806. hydpy/models/sw1d/sw1d_derived.py +85 -0
  807. hydpy/models/sw1d/sw1d_factors.py +78 -0
  808. hydpy/models/sw1d/sw1d_fixed.py +12 -0
  809. hydpy/models/sw1d/sw1d_fluxes.py +55 -0
  810. hydpy/models/sw1d/sw1d_inlets.py +17 -0
  811. hydpy/models/sw1d/sw1d_model.py +3385 -0
  812. hydpy/models/sw1d/sw1d_outlets.py +11 -0
  813. hydpy/models/sw1d/sw1d_receivers.py +11 -0
  814. hydpy/models/sw1d/sw1d_senders.py +11 -0
  815. hydpy/models/sw1d/sw1d_states.py +23 -0
  816. hydpy/models/sw1d_channel.py +2051 -0
  817. hydpy/models/sw1d_gate_out.py +599 -0
  818. hydpy/models/sw1d_lias.py +105 -0
  819. hydpy/models/sw1d_lias_sluice.py +531 -0
  820. hydpy/models/sw1d_network.py +1219 -0
  821. hydpy/models/sw1d_pump.py +448 -0
  822. hydpy/models/sw1d_q_in.py +79 -0
  823. hydpy/models/sw1d_q_out.py +81 -0
  824. hydpy/models/sw1d_storage.py +78 -0
  825. hydpy/models/sw1d_weir_out.py +75 -0
  826. hydpy/models/test/__init__.py +14 -0
  827. hydpy/models/test/test_control.py +28 -0
  828. hydpy/models/test/test_fluxes.py +17 -0
  829. hydpy/models/test/test_model.py +201 -0
  830. hydpy/models/test/test_solver.py +48 -0
  831. hydpy/models/test/test_states.py +17 -0
  832. hydpy/models/test_discontinous.py +46 -0
  833. hydpy/models/test_stiff0d.py +47 -0
  834. hydpy/models/test_stiff1d.py +42 -0
  835. hydpy/models/whmod/__init__.py +21 -0
  836. hydpy/models/whmod/whmod_constants.py +77 -0
  837. hydpy/models/whmod/whmod_control.py +333 -0
  838. hydpy/models/whmod/whmod_derived.py +210 -0
  839. hydpy/models/whmod/whmod_factors.py +9 -0
  840. hydpy/models/whmod/whmod_fluxes.py +105 -0
  841. hydpy/models/whmod/whmod_inputs.py +15 -0
  842. hydpy/models/whmod/whmod_masks.py +178 -0
  843. hydpy/models/whmod/whmod_model.py +2091 -0
  844. hydpy/models/whmod/whmod_parameters.py +155 -0
  845. hydpy/models/whmod/whmod_sequences.py +193 -0
  846. hydpy/models/whmod/whmod_states.py +73 -0
  847. hydpy/models/whmod_rural.py +794 -0
  848. hydpy/models/whmod_urban.py +1011 -0
  849. hydpy/models/wland/__init__.py +43 -0
  850. hydpy/models/wland/wland_aides.py +55 -0
  851. hydpy/models/wland/wland_constants.py +103 -0
  852. hydpy/models/wland/wland_control.py +508 -0
  853. hydpy/models/wland/wland_derived.py +330 -0
  854. hydpy/models/wland/wland_factors.py +11 -0
  855. hydpy/models/wland/wland_fixed.py +12 -0
  856. hydpy/models/wland/wland_fluxes.py +166 -0
  857. hydpy/models/wland/wland_inputs.py +33 -0
  858. hydpy/models/wland/wland_masks.py +54 -0
  859. hydpy/models/wland/wland_model.py +3755 -0
  860. hydpy/models/wland/wland_outlets.py +11 -0
  861. hydpy/models/wland/wland_parameters.py +214 -0
  862. hydpy/models/wland/wland_sequences.py +108 -0
  863. hydpy/models/wland/wland_solver.py +45 -0
  864. hydpy/models/wland/wland_states.py +56 -0
  865. hydpy/models/wland_gd.py +888 -0
  866. hydpy/models/wland_wag.py +1244 -0
  867. hydpy/models/wq/__init__.py +14 -0
  868. hydpy/models/wq/wq_control.py +117 -0
  869. hydpy/models/wq/wq_derived.py +182 -0
  870. hydpy/models/wq/wq_factors.py +79 -0
  871. hydpy/models/wq/wq_fluxes.py +17 -0
  872. hydpy/models/wq/wq_model.py +1889 -0
  873. hydpy/models/wq_trapeze.py +168 -0
  874. hydpy/models/wq_trapeze_strickler.py +101 -0
  875. hydpy/models/wq_walrus.py +57 -0
  876. hydpy/py.typed +0 -0
  877. hydpy/tests/.coveragerc +22 -0
  878. hydpy/tests/__init__.py +0 -0
  879. hydpy/tests/check_consistency.py +32 -0
  880. hydpy/tests/hydpydoctestcustomize.pth +1 -0
  881. hydpy/tests/hydpydoctestcustomize.py +15 -0
  882. hydpy/tests/iotesting/__init__.py +0 -0
  883. hydpy/tests/run_doctests.py +233 -0
  884. hydpy-6.2.dev1.data/scripts/hyd.py +277 -0
  885. hydpy-6.2.dev1.dist-info/LICENSE +165 -0
  886. hydpy-6.2.dev1.dist-info/METADATA +163 -0
  887. hydpy-6.2.dev1.dist-info/RECORD +890 -0
  888. hydpy-6.2.dev1.dist-info/WHEEL +5 -0
  889. hydpy-6.2.dev1.dist-info/licenses_hydpy_installer.txt +42 -0
  890. hydpy-6.2.dev1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1985 @@
1
+ """This module provides features for handling the folder structure of *HydPy* projects
2
+ as well as loading data from and storing data to files."""
3
+
4
+ # import...
5
+ # ...from standard library
6
+ from __future__ import annotations
7
+ import contextlib
8
+ import os
9
+ import runpy
10
+ import shutil
11
+ import types
12
+ import warnings
13
+ import zipfile
14
+
15
+ # ...from site-packages
16
+ import numpy
17
+
18
+ # ...from HydPy
19
+ import hydpy
20
+ from hydpy import config
21
+ from hydpy.core import exceptiontools
22
+ from hydpy.core import devicetools
23
+ from hydpy.core import netcdftools
24
+ from hydpy.core import objecttools
25
+ from hydpy.core import optiontools
26
+ from hydpy.core import selectiontools
27
+ from hydpy.core import sequencetools
28
+ from hydpy.core import timetools
29
+ from hydpy.core.typingtools import *
30
+
31
+
32
+ class Folder2Path:
33
+ """Map folder names to their pathnames.
34
+
35
+ You can pass positional or keyword arguments when initialising |Folder2Path|. For
36
+ positional arguments, the folder and its path are assumed to be identical. For
37
+ keyword arguments, the keyword corresponds to the folder name, and its value is the
38
+ pathname:
39
+
40
+ >>> from hydpy.core.filetools import Folder2Path
41
+ >>> Folder2Path()
42
+ Folder2Path()
43
+ >>> f2p = Folder2Path(
44
+ ... "folder1", "folder2", folder3="folder3", folder4="path4")
45
+ >>> f2p
46
+ Folder2Path(folder1,
47
+ folder2,
48
+ folder3,
49
+ folder4=path4)
50
+ >>> print(f2p)
51
+ Folder2Path(folder1, folder2, folder3, folder4=path4)
52
+
53
+ To add folders after initialisation is supported:
54
+
55
+ >>> f2p.add("folder5")
56
+ >>> f2p.add("folder6", "path6")
57
+ >>> f2p
58
+ Folder2Path(folder1,
59
+ folder2,
60
+ folder3,
61
+ folder5,
62
+ folder4=path4,
63
+ folder6=path6)
64
+
65
+ Folder names are required to be valid Python identifiers:
66
+
67
+ >>> f2p.add("folder 7")
68
+ Traceback (most recent call last):
69
+ ...
70
+ ValueError: The given name string `folder 7` does not define a valid variable \
71
+ identifier. Valid identifiers do not contain characters like `-` or empty spaces, do \
72
+ not start with numbers, cannot be mistaken with Python built-ins like `for`...)
73
+
74
+ You can query the folder and attribute names:
75
+
76
+ >>> f2p.folders
77
+ ['folder1', 'folder2', 'folder3', 'folder4', 'folder5', 'folder6']
78
+ >>> f2p.paths
79
+ ['folder1', 'folder2', 'folder3', 'path4', 'folder5', 'path6']
80
+
81
+ Attribute access and iteration are also supported:
82
+
83
+ >>> "folder1" in dir(f2p)
84
+ True
85
+ >>> f2p.folder1
86
+ 'folder1'
87
+ >>> f2p.folder4
88
+ 'path4'
89
+
90
+ >>> for folder, path in f2p:
91
+ ... print(folder, path)
92
+ folder1 folder1
93
+ folder2 folder2
94
+ folder3 folder3
95
+ folder4 path4
96
+ folder5 folder5
97
+ folder6 path6
98
+
99
+ >>> len(f2p)
100
+ 6
101
+ >>> bool(f2p)
102
+ True
103
+ >>> bool(Folder2Path())
104
+ False
105
+ """
106
+
107
+ def __init__(self, *args: str, **kwargs: str) -> None:
108
+ for arg in args:
109
+ self.add(arg)
110
+ for key, value in kwargs.items():
111
+ self.add(key, value)
112
+
113
+ def add(self, directory: str, path: str | None = None) -> None:
114
+ """Add a directory and, optionally, its path."""
115
+ objecttools.valid_variable_identifier(directory)
116
+ if path is None:
117
+ path = directory
118
+ setattr(self, directory, path)
119
+
120
+ @property
121
+ def folders(self) -> list[str]:
122
+ """The currently handled folder names."""
123
+ return [folder for folder, path in self]
124
+
125
+ @property
126
+ def paths(self) -> list[str]:
127
+ """The currently handled path names."""
128
+ return [path for folder, path in self]
129
+
130
+ def __iter__(self) -> Iterator[tuple[str, str]]:
131
+ yield from sorted(vars(self).items())
132
+
133
+ def __len__(self) -> int:
134
+ return len(vars(self))
135
+
136
+ def __str__(self) -> str:
137
+ return " ".join(repr(self).split())
138
+
139
+ def __repr__(self) -> str:
140
+ if self:
141
+ args, kwargs = [], []
142
+ for key, value in self:
143
+ if key == value:
144
+ args.append(key)
145
+ else:
146
+ kwargs.append(f"{key}={objecttools.repr_(value)}")
147
+ lines = [f" {arg}," for arg in args + kwargs]
148
+ lines[0] = "Folder2Path(" + lines[0][12:]
149
+ lines[-1] = lines[-1][:-1] + ")"
150
+ return "\n".join(lines)
151
+ return "Folder2Path()"
152
+
153
+
154
+ class FileManager:
155
+ """Base class for |NetworkManager|, |ControlManager|, |ConditionManager|, and
156
+ |SequenceManager|."""
157
+
158
+ BASEDIR: str
159
+ DEFAULTDIR: str | None
160
+
161
+ _projectdir: str | None
162
+ _currentdir: str | None
163
+
164
+ def __init__(self) -> None:
165
+ self._projectdir = None
166
+ self._currentdir = None
167
+
168
+ @property
169
+ def projectdir(self) -> str:
170
+ """The folder name of a project's root directory.
171
+
172
+ For the :ref:`HydPy-H-Lahn` example project, |FileManager.projectdir| is (not
173
+ surprisingly) `HydPy-H-Lahn` and is queried from the |pub| module. However,
174
+ you can define or change |FileManager.projectdir| interactively, which can be
175
+ useful for more complex tasks like copying (parts of) projects:
176
+
177
+ >>> from hydpy.core.filetools import FileManager
178
+ >>> from hydpy import pub
179
+ >>> pub.projectname = "project_A"
180
+ >>> filemanager = FileManager()
181
+ >>> filemanager.projectdir
182
+ 'project_A'
183
+
184
+ >>> filemanager.projectdir = "project_B"
185
+ >>> filemanager.projectdir
186
+ 'project_B'
187
+
188
+ >>> pub.projectname = "project_C"
189
+ >>> filemanager.projectdir
190
+ 'project_B'
191
+
192
+ >>> del filemanager.projectdir
193
+ >>> filemanager.projectdir
194
+ 'project_C'
195
+
196
+ >>> del pub.projectname
197
+ >>> filemanager.projectdir
198
+ Traceback (most recent call last):
199
+ ...
200
+ hydpy.core.exceptiontools.AttributeNotReady: While trying to automatically \
201
+ determine the file manager's project root directory, the following error occurred: \
202
+ Attribute projectname of module `pub` is not defined at the moment.
203
+ """
204
+ if (projectdir := self._projectdir) is None:
205
+ try:
206
+ return hydpy.pub.projectname
207
+ except BaseException:
208
+ objecttools.augment_excmessage(
209
+ f"While trying to automatically determine the {self._docname}'s "
210
+ f"project root directory"
211
+ )
212
+ return projectdir
213
+
214
+ @projectdir.setter
215
+ def projectdir(self, name: str) -> None:
216
+ self._projectdir = name
217
+
218
+ @projectdir.deleter
219
+ def projectdir(self) -> None:
220
+ self._projectdir = None
221
+
222
+ @property
223
+ def basepath(self) -> str:
224
+ """The absolute path pointing to the available working directories.
225
+
226
+ >>> from hydpy.core.filetools import FileManager
227
+ >>> filemanager = FileManager()
228
+ >>> filemanager.BASEDIR = "basename"
229
+ >>> filemanager.projectdir = "projectname"
230
+ >>> from hydpy import repr_, TestIO
231
+ >>> with TestIO():
232
+ ... repr_(filemanager.basepath) # doctest: +ELLIPSIS
233
+ '...hydpy/tests/iotesting/projectname/basename'
234
+ """
235
+ return os.path.abspath(os.path.join(self.projectdir, self.BASEDIR))
236
+
237
+ @property
238
+ def availabledirs(self) -> Folder2Path:
239
+ """The names and paths of the available working directories.
240
+
241
+ All possible working directories must be availablein the base directory of the
242
+ respective |FileManager| subclass. Folders with names starting with an
243
+ underscore do not count (use this for directories handling additional data
244
+ files, if you like), while zipped directories do count as available directories:
245
+
246
+ >>> from hydpy.core.filetools import FileManager
247
+ >>> filemanager = FileManager()
248
+ >>> filemanager.BASEDIR = "basename"
249
+ >>> filemanager.projectdir = "projectname"
250
+ >>> import os
251
+ >>> from hydpy import repr_, TestIO
252
+ >>> TestIO.clear()
253
+ >>> with TestIO():
254
+ ... os.makedirs("projectname/basename/folder1")
255
+ ... os.makedirs("projectname/basename/folder2")
256
+ ... open("projectname/basename/folder3.zip", "w").close()
257
+ ... os.makedirs("projectname/basename/_folder4")
258
+ ... open("projectname/basename/folder5.tar", "w").close()
259
+ ... filemanager.availabledirs # doctest: +ELLIPSIS
260
+ Folder2Path(folder1=.../projectname/basename/folder1,
261
+ folder2=.../projectname/basename/folder2,
262
+ folder3=.../projectname/basename/folder3.zip)
263
+ """
264
+ directories = Folder2Path()
265
+ for directory in sorted(os.listdir(self.basepath)):
266
+ if not directory.startswith("_"):
267
+ path = os.path.join(self.basepath, directory)
268
+ if os.path.isdir(path):
269
+ directories.add(directory, path)
270
+ elif directory.endswith(".zip"):
271
+ directories.add(directory[:-4], path)
272
+ return directories
273
+
274
+ @property
275
+ def currentdir(self) -> str:
276
+ """The name of the current working directory containing the relevant files.
277
+
278
+ To show most of the functionality of |property| |FileManager.currentdir| (we
279
+ explain unpacking zipped files on the fly in the documentation on function
280
+ |FileManager.zip_currentdir|), we first prepare a |FileManager| object with the
281
+ default |FileManager.basepath| `projectname/basename` and no
282
+ |FileManager.DEFAULTDIR| defined:
283
+
284
+ >>> from hydpy.core.filetools import FileManager
285
+ >>> filemanager = FileManager()
286
+ >>> filemanager.BASEDIR = "basename"
287
+ >>> filemanager.DEFAULTDIR = None
288
+ >>> filemanager.projectdir = "projectname"
289
+ >>> import os
290
+ >>> from hydpy import pub, repr_, TestIO
291
+ >>> TestIO.clear()
292
+ >>> with TestIO():
293
+ ... os.makedirs("projectname/basename")
294
+ ... repr_(filemanager.basepath) # doctest: +ELLIPSIS
295
+ '...hydpy/tests/iotesting/projectname/basename'
296
+
297
+ At first, the base directory is empty and asking for the current working
298
+ directory results in the following error:
299
+
300
+ >>> with TestIO():
301
+ ... filemanager.currentdir # doctest: +ELLIPSIS
302
+ Traceback (most recent call last):
303
+ ...
304
+ RuntimeError: The current working directory of the file manager has not been \
305
+ defined manually and cannot be determined automatically: `.../projectname/basename` \
306
+ does not contain any available directories.
307
+
308
+ If only one directory exists, it is considered the current working directory
309
+ automatically:
310
+
311
+ >>> with TestIO(), pub.options.printprogress(True):
312
+ ... os.mkdir("projectname/basename/dir1")
313
+ ... assert filemanager.currentdir == "dir1"
314
+ The name of the file manager's current working directory has not been \
315
+ previously defined and is hence set to `dir1`.
316
+
317
+ |property| |FileManager.currentdir| memorises the name of the current working
318
+ directory, even if another directory is added later to the base path:
319
+
320
+ >>> with TestIO():
321
+ ... os.mkdir("projectname/basename/dir2")
322
+ ... assert filemanager.currentdir == "dir1"
323
+
324
+ Set the value of |FileManager.currentdir| to |None| to let it forget the
325
+ memorised directory. After that, trying to query the current working directory
326
+ results in another error, as it is unclear which directory to select:
327
+
328
+ >>> with TestIO():
329
+ ... filemanager.currentdir = None
330
+ ... filemanager.currentdir # doctest: +ELLIPSIS
331
+ Traceback (most recent call last):
332
+ ...
333
+ RuntimeError: The current working directory of the file manager has not been \
334
+ defined manually and cannot be determined automatically: `.../projectname/basename` \
335
+ does contain multiple available directories (dir1 and dir2).
336
+
337
+ Setting |FileManager.currentdir| manually solves the problem:
338
+
339
+ >>> with TestIO():
340
+ ... filemanager.currentdir = "dir1"
341
+ ... assert filemanager.currentdir == "dir1"
342
+
343
+ Remove the current working directory `dir1` with the `del` statement:
344
+
345
+ >>> with TestIO(), pub.options.printprogress(True): # doctest: +ELLIPSIS
346
+ ... del filemanager.currentdir
347
+ ... assert not os.path.exists("projectname/basename/dir1")
348
+ Directory ...dir1 has been removed.
349
+
350
+ |FileManager| subclasses can define a default directory name. When many
351
+ directories exist, and none is selected manually, the default directory is
352
+ chosen automatically. The following example shows an error message due to
353
+ multiple directories without any default name:
354
+
355
+ >>> with TestIO():
356
+ ... os.mkdir("projectname/basename/dir1")
357
+ ... filemanager.DEFAULTDIR = "dir3"
358
+ ... del filemanager.currentdir
359
+ ... filemanager.currentdir # doctest: +ELLIPSIS
360
+ Traceback (most recent call last):
361
+ ...
362
+ RuntimeError: The current working directory of the file manager has not been \
363
+ defined manually and cannot be determined automatically: The default directory (dir3) \
364
+ is not among the available directories (dir1 and dir2).
365
+
366
+ We can fix this by manually adding the required default directory:
367
+
368
+ >>> with TestIO(), pub.options.printprogress(True):
369
+ ... os.mkdir("projectname/basename/dir3")
370
+ ... assert filemanager.currentdir == "dir3"
371
+ The name of the file manager's current working directory has not been \
372
+ previously defined and is hence set to `dir3`.
373
+
374
+ Setting the |FileManager.currentdir| to `dir4` not only overwrites the default
375
+ name but also creates the required folder:
376
+
377
+ >>> with TestIO(), pub.options.printprogress(True):
378
+ ... filemanager.currentdir = "dir4"
379
+ ... assert filemanager.currentdir == "dir4" # doctest: +ELLIPSIS
380
+ Directory ...dir4 has been created.
381
+ >>> with TestIO():
382
+ ... dirs = os.listdir("projectname/basename")
383
+ ... assert sorted(dirs) == ["dir1", "dir2", "dir3", "dir4"]
384
+
385
+ Failed attempts to remove directories result in error messages like the
386
+ following one:
387
+
388
+ >>> import shutil
389
+ >>> from unittest.mock import patch
390
+ >>> with TestIO(), patch.object(shutil, "rmtree", side_effect=AttributeError):
391
+ ... del filemanager.currentdir # doctest: +ELLIPSIS
392
+ Traceback (most recent call last):
393
+ ...
394
+ AttributeError: While trying to delete the current working directory \
395
+ `.../projectname/basename/dir4` of the file manager, the following error occurred: ...
396
+
397
+ Then, the current working directory still exists and is remembered by property
398
+ |FileManager.currentdir|:
399
+
400
+ >>> with TestIO():
401
+ ... assert filemanager.currentdir == "dir4"
402
+ >>> with TestIO():
403
+ ... dirs =os.listdir("projectname/basename")
404
+ ... assert sorted(dirs) == ["dir1", "dir2", "dir3", "dir4"]
405
+
406
+ Assign the folder's absolute path if you need to work outside the current
407
+ project directory (for example, to archive simulated data):
408
+
409
+ >>> with TestIO(): # doctest: +ELLIPSIS
410
+ ... os.mkdir("differentproject")
411
+ ... filemanager.currentdir = os.path.abspath("differentproject/dir1")
412
+ ... path = repr_(filemanager.currentpath)
413
+ ... assert path.endswith("hydpy/tests/iotesting/differentproject/dir1")
414
+ ... assert os.listdir("differentproject") == ["dir1"]
415
+
416
+ If a |FileManager| subclass defines its |FileManager.DEFAULTDIR| class
417
+ attribute, the above behaviour differs in the case of an initially empty base
418
+ directory. Then, |FileManager.currentdir| activates and creates an accordingly
419
+ named directory automatically:
420
+
421
+ >>> filemanager.currentdir = None
422
+ >>> filemanager.DEFAULTDIR = "default"
423
+ >>> TestIO.clear()
424
+ >>> with TestIO(), pub.options.printprogress(True): # doctest: +ELLIPSIS
425
+ ... os.makedirs("projectname/basename")
426
+ ... assert filemanager.currentdir == "default"
427
+ ... assert os.path.exists("projectname/basename/default")
428
+ The name of the file manager's current working directory has not been \
429
+ previously defined and is hence set to `default`.
430
+ Directory ...default has been created.
431
+ """
432
+
433
+ def _print_info(dirname: str, /) -> None:
434
+ if hydpy.pub.options.printprogress:
435
+ print(
436
+ f"The name of the {self._docname}'s current working directory "
437
+ f"has not been previously defined and is hence set to "
438
+ f"`{dirname}`."
439
+ )
440
+
441
+ currentdir = self._currentdir
442
+ if currentdir is None:
443
+ dirs = self.availabledirs.folders
444
+ if len(dirs) == 1:
445
+ _print_info(dirs[0])
446
+ currentdir = dirs[0]
447
+ elif (default := self.DEFAULTDIR) and (default in dirs or not dirs):
448
+ _print_info(default)
449
+ currentdir = default
450
+ else:
451
+ prefix = (
452
+ f"The current working directory of the {self._docname} has not "
453
+ f"been defined manually and cannot be determined automatically:"
454
+ )
455
+ if not dirs:
456
+ raise RuntimeError(
457
+ f"{prefix} `{objecttools.repr_(self.basepath)}` does not "
458
+ f"contain any available directories."
459
+ )
460
+ if default is None:
461
+ raise RuntimeError(
462
+ f"{prefix} `{objecttools.repr_(self.basepath)}` does contain "
463
+ f"multiple available directories "
464
+ f"({objecttools.enumeration(dirs)})."
465
+ )
466
+ raise RuntimeError(
467
+ f"{prefix} The default directory ({default}) is not among the "
468
+ f"available directories ({objecttools.enumeration(dirs)})."
469
+ )
470
+ self.currentdir = currentdir
471
+ return currentdir
472
+
473
+ @currentdir.setter
474
+ def currentdir(self, directory: str | None) -> None:
475
+ if directory is None:
476
+ self._currentdir = None
477
+ else:
478
+ dirpath = os.path.join(self.basepath, directory)
479
+ zippath = f"{dirpath}.zip"
480
+ if os.path.exists(zippath):
481
+ shutil.unpack_archive(
482
+ filename=zippath, extract_dir=dirpath, format="zip"
483
+ )
484
+ os.remove(zippath)
485
+ if hydpy.pub.options.printprogress:
486
+ print(
487
+ f"The zip file {zippath} has been extracted to directory "
488
+ f"{dirpath} and removed."
489
+ )
490
+ elif not os.path.exists(dirpath):
491
+ os.makedirs(dirpath)
492
+ if hydpy.pub.options.printprogress:
493
+ print(f"Directory {dirpath} has been created.")
494
+ self._currentdir = str(directory)
495
+
496
+ @currentdir.deleter
497
+ def currentdir(self) -> None:
498
+ path = os.path.join(self.basepath, self.currentdir)
499
+ if os.path.exists(path):
500
+ try:
501
+ shutil.rmtree(path)
502
+ if hydpy.pub.options.printprogress:
503
+ print(f"Directory {path} has been removed.")
504
+ except BaseException:
505
+ objecttools.augment_excmessage(
506
+ f"While trying to delete the current working directory "
507
+ f"`{objecttools.repr_(path)}` of the {self._docname}"
508
+ )
509
+ self._currentdir = None
510
+
511
+ @property
512
+ def currentpath(self) -> str:
513
+ """The absolute path of the current working directory.
514
+
515
+ >>> from hydpy.core.filetools import FileManager
516
+ >>> filemanager = FileManager()
517
+ >>> filemanager.BASEDIR = "basename"
518
+ >>> filemanager.projectdir = "projectname"
519
+ >>> from hydpy import repr_, TestIO
520
+ >>> with TestIO():
521
+ ... filemanager.currentdir = "testdir"
522
+ ... repr_(filemanager.currentpath) # doctest: +ELLIPSIS
523
+ '...hydpy/tests/iotesting/projectname/basename/testdir'
524
+ """
525
+ return os.path.join(self.basepath, self.currentdir)
526
+
527
+ @property
528
+ def filenames(self) -> list[str]:
529
+ """The names of the files in the current working directory, except those
530
+ starting with an underscore.
531
+
532
+ >>> from hydpy.core.filetools import FileManager
533
+ >>> filemanager = FileManager()
534
+ >>> filemanager.BASEDIR = "basename"
535
+ >>> filemanager.projectdir = "projectname"
536
+ >>> from hydpy import TestIO
537
+ >>> with TestIO():
538
+ ... filemanager.currentdir = "testdir"
539
+ ... open("projectname/basename/testdir/file1.txt", "w").close()
540
+ ... open("projectname/basename/testdir/file2.npy", "w").close()
541
+ ... open("projectname/basename/testdir/_file1.nc", "w").close()
542
+ ... filemanager.filenames
543
+ ['file1.txt', 'file2.npy']
544
+ """
545
+ return sorted(
546
+ fn for fn in os.listdir(self.currentpath) if not fn.startswith("_")
547
+ )
548
+
549
+ @property
550
+ def filepaths(self) -> list[str]:
551
+ """The absolute path names of the files returned by property
552
+ |FileManager.filenames|.
553
+
554
+ >>> from hydpy.core.filetools import FileManager
555
+ >>> filemanager = FileManager()
556
+ >>> filemanager.BASEDIR = "basename"
557
+ >>> filemanager.projectdir = "projectname"
558
+ >>> from hydpy import repr_, TestIO
559
+ >>> with TestIO():
560
+ ... filemanager.currentdir = "testdir"
561
+ ... open("projectname/basename/testdir/file1.txt", "w").close()
562
+ ... open("projectname/basename/testdir/file2.npy", "w").close()
563
+ ... open("projectname/basename/testdir/_file1.nc", "w").close()
564
+ ... for filepath in filemanager.filepaths:
565
+ ... repr_(filepath) # doctest: +ELLIPSIS
566
+ '...hydpy/tests/iotesting/projectname/basename/testdir/file1.txt'
567
+ '...hydpy/tests/iotesting/projectname/basename/testdir/file2.npy'
568
+ """
569
+ path = self.currentpath
570
+ return [os.path.join(path, name) for name in self.filenames]
571
+
572
+ def zip_currentdir(self) -> None:
573
+ """Pack the current working directory in a `zip` file.
574
+
575
+ |FileManager| subclasses allow for manual packing and automatic unpacking of
576
+ working directories. The only supported format is "zip". The original
577
+ directories and zip files are removed after packing or unpacking to avoid
578
+ possible inconsistencies.
579
+
580
+ As an example scenario, we prepare a |FileManager| object with the current
581
+ working directory `folder` containing the files `test1.txt` and `text2.txt`:
582
+
583
+ >>> from hydpy.core.filetools import FileManager
584
+ >>> filemanager = FileManager()
585
+ >>> filemanager.BASEDIR = "basename"
586
+ >>> filemanager.DEFAULTDIR = None
587
+ >>> filemanager.projectdir = "projectname"
588
+ >>> import os
589
+ >>> from hydpy import pub, repr_, TestIO
590
+ >>> TestIO.clear()
591
+ >>> basepath = "projectname/basename"
592
+ >>> with TestIO():
593
+ ... os.makedirs(basepath)
594
+ ... filemanager.currentdir = "folder"
595
+ ... open(f"{basepath}/folder/file1.txt", "w").close()
596
+ ... open(f"{basepath}/folder/file2.txt", "w").close()
597
+ ... filemanager.filenames
598
+ ['file1.txt', 'file2.txt']
599
+
600
+ The directories under the base path are identical to the ones returned by
601
+ property |FileManager.availabledirs|:
602
+
603
+ >>> with TestIO():
604
+ ... assert os.listdir(basepath) == ["folder"]
605
+ ... filemanager.availabledirs # doctest: +ELLIPSIS
606
+ Folder2Path(folder=.../projectname/basename/folder)
607
+
608
+ After manually packing the current working directory, it still counts as an
609
+ available directory:
610
+
611
+ >>> with TestIO(), pub.options.printprogress(True):
612
+ ... filemanager.zip_currentdir()
613
+ ... assert os.listdir(basepath) == ["folder.zip"]
614
+ ... filemanager.availabledirs # doctest: +ELLIPSIS
615
+ Directory ...folder has been removed.
616
+ Folder2Path(folder=.../projectname/basename/folder.zip)
617
+
618
+ Instead of the complete directory, only its files are packed:
619
+
620
+ >>> from zipfile import ZipFile
621
+ >>> with TestIO():
622
+ ... with ZipFile("projectname/basename/folder.zip", "r") as zp:
623
+ ... assert sorted(zp.namelist()) == ["file1.txt", "file2.txt"]
624
+
625
+ The zip file is unpacked again when `folder` becomes the current working
626
+ directory:
627
+
628
+ >>> with TestIO(), pub.options.printprogress(True): # doctest: +ELLIPSIS
629
+ ... filemanager.currentdir = "folder"
630
+ ... assert os.listdir(basepath) == ["folder"]
631
+ ... assert sorted(filemanager.filenames) == ["file1.txt", "file2.txt"]
632
+ ... filemanager.availabledirs
633
+ The zip file ...folder.zip has been extracted to directory ...folder and \
634
+ removed.
635
+ Folder2Path(folder=.../projectname/basename/folder)
636
+ """
637
+ with zipfile.ZipFile(f"{self.currentpath}.zip", "w") as zipfile_:
638
+ for filepath, filename in zip(self.filepaths, self.filenames):
639
+ zipfile_.write(filename=filepath, arcname=filename)
640
+ del self.currentdir
641
+
642
+ @property
643
+ def _docname(self) -> str:
644
+ return f"{type(self).__name__[:-7].lower()} manager"
645
+
646
+
647
+ class NetworkManager(FileManager):
648
+ """Manager for network files.
649
+
650
+ The base and default folder names of class |NetworkManager| are:
651
+
652
+ >>> from hydpy.core.filetools import NetworkManager
653
+ >>> NetworkManager.BASEDIR
654
+ 'network'
655
+ >>> NetworkManager.DEFAULTDIR
656
+ 'default'
657
+
658
+ The documentation of base class |FileManager| explains most aspects of using
659
+ |NetworkManager| objects. The following examples deal with the extended features
660
+ of class |NetworkManager|: reading, writing, and removing network files. For this
661
+ purpose, we prepare the example project `HydPy-H-Lahn` in the `iotesting` directory
662
+ by calling function |prepare_full_example_1|:
663
+
664
+ >>> from hydpy.core.testtools import prepare_full_example_1
665
+ >>> prepare_full_example_1()
666
+
667
+ You can define the complete network structure of an `HydPy` project by an arbitrary
668
+ number of "network files". These valid Python files define |Node| and |Element|
669
+ objects and their connections. Network files are allowed to overlap, meaning two
670
+ or more files can define the same objects (in a consistent manner only, of course).
671
+ The primary purpose of class |NetworkManager| is to execute each network file
672
+ individually and pass its content to a |Selection| object, which is done by method
673
+ |NetworkManager.load_files|:
674
+
675
+ >>> networkmanager = NetworkManager()
676
+ >>> from hydpy import TestIO
677
+ >>> with TestIO():
678
+ ... networkmanager.projectdir = "HydPy-H-Lahn"
679
+ ... selections = networkmanager.load_files()
680
+
681
+ Method |NetworkManager.load_files| takes file names as selection names (without
682
+ file endings):
683
+
684
+ >>> selections
685
+ Selections("headwaters", "nonheadwaters", "streams")
686
+ >>> selections.headwaters
687
+ Selection("headwaters",
688
+ nodes=("dill_assl", "lahn_marb"),
689
+ elements=("land_dill_assl", "land_lahn_marb"))
690
+
691
+ The whole set of |Node| and |Element| objects is accessible via the property
692
+ |Selections.complete|:
693
+
694
+ >>> selections.complete
695
+ Selection("complete",
696
+ nodes=("dill_assl", "lahn_kalk", "lahn_leun", "lahn_marb"),
697
+ elements=("land_dill_assl", "land_lahn_kalk",
698
+ "land_lahn_leun", "land_lahn_marb",
699
+ "stream_dill_assl_lahn_leun",
700
+ "stream_lahn_leun_lahn_kalk",
701
+ "stream_lahn_marb_lahn_leun"))
702
+
703
+ Method |NetworkManager.save_files| writes all user-defined selections into separate
704
+ files. First, we change the current working directory to ensure we do not
705
+ overwrite already existing files:
706
+
707
+ >>> import os
708
+ >>> with TestIO():
709
+ ... networkmanager.currentdir = "testdir"
710
+ ... networkmanager.save_files(selections)
711
+ ... sorted(os.listdir("HydPy-H-Lahn/network/testdir"))
712
+ ['headwaters.py', 'nonheadwaters.py', 'streams.py']
713
+
714
+ Reloading and comparing with the still available |Selection| objects proves that
715
+ the contents of the original and the new network files are equivalent:
716
+
717
+ >>> with TestIO():
718
+ ... selections == networkmanager.load_files()
719
+ True
720
+
721
+ Method |NetworkManager.delete_files| removes the network files of the given
722
+ |Selection| objects:
723
+
724
+ >>> selections -= selections.streams
725
+ >>> with TestIO():
726
+ ... networkmanager.delete_files(selections)
727
+ ... sorted(os.listdir("HydPy-H-Lahn/network/testdir"))
728
+ ['streams.py']
729
+
730
+ When defining network files, many things can go wrong. In the following, we list
731
+ all specialised error messages of what we hope to be concrete enough to aid in
732
+ finding the relevant problems:
733
+
734
+ >>> with TestIO():
735
+ ... networkmanager.delete_files(["headwaters"]) # doctest: +ELLIPSIS
736
+ Traceback (most recent call last):
737
+ ...
738
+ FileNotFoundError: While trying to remove the network files of the selection(s) \
739
+ `headwaters`, the following error occurred: ...
740
+
741
+ >>> with TestIO():
742
+ ... with open("HydPy-H-Lahn/network/testdir/streams.py", "w") as wrongfile:
743
+ ... _ = wrongfile.write("x = y")
744
+ ... networkmanager.load_files() # doctest: +ELLIPSIS
745
+ Traceback (most recent call last):
746
+ ...
747
+ NameError: While trying to load the network file `...streams.py`, the following \
748
+ error occurred: name 'y' is not defined
749
+
750
+ >>> with TestIO():
751
+ ... with open("HydPy-H-Lahn/network/testdir/streams.py", "w") as wrongfile:
752
+ ... _ = wrongfile.write("from hydpy import Node")
753
+ ... networkmanager.load_files() # doctest: +ELLIPSIS
754
+ Traceback (most recent call last):
755
+ ...
756
+ RuntimeError: The class Element cannot be loaded from the network file \
757
+ `...streams.py`.
758
+
759
+ >>> with TestIO():
760
+ ... with open("HydPy-H-Lahn/network/testdir/streams.py", "w") as wrongfile:
761
+ ... _ = wrongfile.write("from hydpy import Element")
762
+ ... networkmanager.load_files() # doctest: +ELLIPSIS
763
+ Traceback (most recent call last):
764
+ ...
765
+ RuntimeError: The class Node cannot be loaded from the network file \
766
+ `...streams.py`.
767
+
768
+ >>> import shutil
769
+ >>> with TestIO():
770
+ ... shutil.rmtree("HydPy-H-Lahn/network/testdir")
771
+ ... networkmanager.save_files(selections) # doctest: +ELLIPSIS
772
+ Traceback (most recent call last):
773
+ ...
774
+ FileNotFoundError: While trying to save the selection(s) `headwaters and \
775
+ nonheadwaters` into network files, the following error occurred: ...
776
+ """
777
+
778
+ BASEDIR = "network"
779
+ DEFAULTDIR = "default"
780
+
781
+ def load_files(self) -> selectiontools.Selections:
782
+ """Read all network files of the current working directory, structure their
783
+ contents in a |selectiontools.Selections| object, and return it.
784
+
785
+ See the main documentation of class |NetworkManager| for further information.
786
+ """
787
+ selections = selectiontools.Selections()
788
+ if not self.filenames:
789
+ raise RuntimeError(
790
+ f"The directory `{self.currentpath}` does not contain any network "
791
+ f"files."
792
+ )
793
+ for filename, path in zip(self.filenames, self.filepaths):
794
+ # Ensure both `Node` and `Element`start with a `fresh` memory.
795
+ devicetools.Node.extract_new()
796
+ devicetools.Element.extract_new()
797
+ try:
798
+ info = runpy.run_path(path)
799
+ except BaseException:
800
+ objecttools.augment_excmessage(
801
+ f"While trying to load the network file `{path}`"
802
+ )
803
+ try:
804
+ node: devicetools.Node = info["Node"]
805
+ element: devicetools.Element = info["Element"]
806
+ selections += selectiontools.Selection(
807
+ filename.split(".")[0], node.extract_new(), element.extract_new()
808
+ )
809
+ except KeyError as exc:
810
+ raise RuntimeError(
811
+ f"The class {exc.args[0]} cannot be loaded from the network file "
812
+ f"`{path}`."
813
+ ) from None
814
+ return selections
815
+
816
+ def save_files(self, selections: Iterable[selectiontools.Selection]) -> None:
817
+ """Save the |Selection| objects contained in the given |Selections| instance to
818
+ separate network files.
819
+
820
+ See the main documentation on class |NetworkManager| for further information.
821
+ """
822
+ selections = tuple(selections)
823
+ try:
824
+ currentpath = self.currentpath
825
+ for selection in selections:
826
+ path = os.path.join(currentpath, selection.name + ".py")
827
+ selection.save_networkfile(filepath=path)
828
+ except BaseException:
829
+ objecttools.augment_excmessage(
830
+ f"While trying to save the selection(s) "
831
+ f"`{objecttools.enumeration(selections)}` into network files"
832
+ )
833
+
834
+ def delete_files(self, selections: Iterable[selectiontools.Selection]) -> None:
835
+ """Delete the network files corresponding to the given selections (e.g. a
836
+ |list| of |str| objects or a |Selections| object).
837
+
838
+ See the main documentation on class |NetworkManager| for further information.
839
+ """
840
+ selections = tuple(selections)
841
+ try:
842
+ currentpath = self.currentpath
843
+ for selection in selections:
844
+ name = str(selection)
845
+ if not name.endswith(".py"):
846
+ name += ".py"
847
+ path = os.path.join(currentpath, name)
848
+ os.remove(path)
849
+ except BaseException:
850
+ objecttools.augment_excmessage(
851
+ f"While trying to remove the network files of the selection(s) "
852
+ f"`{objecttools.enumeration(selections)}`"
853
+ )
854
+
855
+
856
+ class ControlManager(FileManager):
857
+ """Manager for control parameter files.
858
+
859
+ The base and default folder names of class |ControlManager| are:
860
+
861
+ >>> from hydpy.core.filetools import ControlManager
862
+ >>> ControlManager.BASEDIR
863
+ 'control'
864
+ >>> ControlManager.DEFAULTDIR
865
+ 'default'
866
+
867
+ Class |ControlManager| extends the functionalities of class |FileManager| only
868
+ slightly, which is why the documentation on class |FileManager| should serve as a
869
+ good starting point for understanding class |ControlManager|. Also, see the
870
+ documentation on method |HydPy.prepare_models| of class |HydPy|, which relies on
871
+ the functionalities of class |ControlManager|.
872
+ """
873
+
874
+ # The following file path to content mapping is used to circumvent reading
875
+ # the same auxiliary control parameter file from disk multiple times.
876
+ _registry: dict[str, types.CodeType] = {}
877
+ _workingpath: str = "."
878
+ BASEDIR = "control"
879
+ DEFAULTDIR = "default"
880
+
881
+ def load_file(
882
+ self,
883
+ element: devicetools.Element | None = None,
884
+ filename: str | None = None,
885
+ clear_registry: bool = True,
886
+ ) -> dict[str, Any]:
887
+ """Return the namespace of the given file (and eventually of its corresponding
888
+ auxiliary subfiles).
889
+
890
+ By default, |ControlManager| clears the internal registry after loading a
891
+ control file and all its corresponding auxiliary files. You can change this
892
+ behaviour by passing `False` to the `clear_registry` argument, which might
893
+ decrease model initialisation times significantly. However, then it is your
894
+ own responsibility to call the method |ControlManager.clear_registry| when
895
+ necessary (usually before reloading a changed control file).
896
+
897
+ One advantage of using method |ControlManager.load_file| directly is that it
898
+ supports reading control files that are yet not correctly integrated into a
899
+ complete *HydPy* project by passing its name:
900
+
901
+ >>> from hydpy.core.testtools import prepare_full_example_1
902
+ >>> prepare_full_example_1()
903
+
904
+ >>> from hydpy.core.filetools import ControlManager
905
+ >>> controlmanager = ControlManager()
906
+ >>> from hydpy import pub, round_, TestIO
907
+ >>> pub.timegrids = "2000-01-01", "2001-01-01", "12h"
908
+ >>> with TestIO():
909
+ ... controlmanager.projectdir = "HydPy-H-Lahn"
910
+ ... results = controlmanager.load_file(filename="land_dill_assl")
911
+
912
+
913
+ >>> results["control"]
914
+ area(692.3)
915
+ nmbzones(12)
916
+ sclass(1)
917
+ zonetype(FIELD, FOREST, FIELD, FOREST, FIELD, FOREST, FIELD, FOREST,
918
+ FIELD, FOREST, FIELD, FOREST)
919
+ zonearea(14.41, 7.06, 70.83, 84.36, 70.97, 198.0, 27.75, 130.0, 27.28,
920
+ 56.94, 1.09, 3.61)
921
+ psi(1.0)
922
+ zonez(2.0, 2.0, 3.0, 3.0, 4.0, 4.0, 5.0, 5.0, 6.0, 6.0, 7.0, 7.0)
923
+ pcorr(1.0)
924
+ pcalt(0.1)
925
+ rfcf(1.04283)
926
+ sfcf(1.1)
927
+ tcorr(0.0)
928
+ tcalt(0.6)
929
+ icmax(field=1.0, forest=1.5)
930
+ sfdist(1.0)
931
+ smax(inf)
932
+ sred(0.0)
933
+ tt(0.55824)
934
+ ttint(2.0)
935
+ dttm(0.0)
936
+ cfmax(field=4.55853, forest=2.735118)
937
+ cfvar(0.0)
938
+ gmelt(nan)
939
+ gvar(nan)
940
+ cfr(0.05)
941
+ whc(0.1)
942
+ fc(278.0)
943
+ beta(2.54011)
944
+ percmax(1.39636)
945
+ cflux(0.0)
946
+ resparea(True)
947
+ recstep(1200.0)
948
+ alpha(1.0)
949
+ k(0.005618)
950
+ k4(0.05646)
951
+ gamma(0.0)
952
+
953
+ >>> results["percmax"].values
954
+ 0.69818
955
+
956
+ Passing neither a filename nor an |Element| object raises the following error:
957
+
958
+ >>> controlmanager.load_file()
959
+ Traceback (most recent call last):
960
+ ...
961
+ RuntimeError: When trying to load a control file you must either pass its \
962
+ name or the responsible Element object.
963
+ """
964
+ if not filename:
965
+ if element:
966
+ filename = element.name
967
+ else:
968
+ raise RuntimeError(
969
+ "When trying to load a control file you must either pass its name "
970
+ "or the responsible Element object."
971
+ )
972
+ type(self)._workingpath = self.currentpath
973
+ info = {}
974
+ if element:
975
+ info["element"] = element
976
+ try:
977
+ self.read2dict(filename, info)
978
+ finally:
979
+ type(self)._workingpath = "."
980
+ if clear_registry:
981
+ self._registry.clear()
982
+ return info
983
+
984
+ @classmethod
985
+ def read2dict(cls, filename: str, info: dict[str, Any]) -> None:
986
+ """Read the control parameters from the given path (and its auxiliary paths,
987
+ where appropriate) and store them in the given |dict| object `info`.
988
+
989
+ Note that`info` can be used to feed information into the execution of control
990
+ files. Use this method only if you are entirely sure of how the control
991
+ parameter import of *HydPy* works. Otherwise, you should most probably prefer
992
+ to use the method |ControlManager.load_file|.
993
+ """
994
+ if not filename.endswith(".py"):
995
+ filename += ".py"
996
+ filepath = os.path.abspath(os.path.join(cls._workingpath, filename))
997
+ with hydpy.pub.options.parameterstep(None):
998
+ try:
999
+ if filepath not in cls._registry:
1000
+ with open(filepath, encoding=config.ENCODING) as file_:
1001
+ cls._registry[filepath] = compile(
1002
+ source=file_.read(), filename=filepath, mode="exec"
1003
+ )
1004
+ exec(cls._registry[filepath], {}, info)
1005
+ except BaseException:
1006
+ objecttools.augment_excmessage(
1007
+ f"While trying to load the control file `{filepath}`"
1008
+ )
1009
+ if "model" not in info:
1010
+ raise RuntimeError(
1011
+ f"Model parameters cannot be loaded from control file `{filepath}`. "
1012
+ f"Please refer to the HydPy documentation on how to prepare control "
1013
+ f"files properly."
1014
+ )
1015
+
1016
+ @classmethod
1017
+ def clear_registry(cls) -> None:
1018
+ """Clear the internal registry from control file information."""
1019
+ cls._registry.clear()
1020
+
1021
+ def save_file(self, filename: str, text: str) -> None:
1022
+ """Save the given text under the given control filename and the current path."""
1023
+ if not filename.endswith(".py"):
1024
+ filename += ".py"
1025
+ path = os.path.join(self.currentpath, filename)
1026
+ with open(path, "w", encoding="utf-8") as file_:
1027
+ file_.write(text)
1028
+
1029
+
1030
+ class ConditionManager(FileManager):
1031
+ """Manager for condition files.
1032
+
1033
+ The base folder name of class |ConditionManager| is:
1034
+
1035
+ >>> from hydpy.core.filetools import ConditionManager
1036
+ >>> ConditionManager.BASEDIR
1037
+ 'conditions'
1038
+
1039
+ Class |ConditionManager| generally works like class |FileManager|. The following
1040
+ examples, based on the `HydPy-H-Lahn` example project, explain the additional
1041
+ functionalities of the |ConditionManager| specific properties
1042
+ |ConditionManager.inputpath| and |ConditionManager.outputpath|:
1043
+
1044
+ >>> from hydpy.core.testtools import prepare_full_example_2
1045
+ >>> hp, pub, TestIO = prepare_full_example_2()
1046
+
1047
+ If the current directory named is not defined explicitly, both properties construct
1048
+ it following the actual simulation start or end date, respectively:
1049
+
1050
+ >>> from hydpy import repr_
1051
+ >>> with TestIO(), pub.options.printprogress(True): # doctest: +ELLIPSIS
1052
+ ... repr_(pub.conditionmanager.inputpath)
1053
+ ... repr_(pub.conditionmanager.outputpath)
1054
+ The condition manager's current working directory is not defined explicitly. \
1055
+ Hence, the condition manager reads its data from a directory named \
1056
+ `init_1996_01_01_00_00_00`.
1057
+ '.../hydpy/tests/iotesting/HydPy-H-Lahn/conditions/init_1996_01_01_00_00_00'
1058
+ The condition manager's current working directory is not defined explicitly. \
1059
+ Hence, the condition manager writes its data to a directory named \
1060
+ `init_1996_01_05_00_00_00`.
1061
+ Directory ...init_1996_01_05_00_00_00 has been created.
1062
+ '.../hydpy/tests/iotesting/HydPy-H-Lahn/conditions/init_1996_01_05_00_00_00'
1063
+
1064
+ >>> pub.timegrids.sim.firstdate += "1d"
1065
+ >>> pub.timegrids.sim.lastdate -= "1d"
1066
+ >>> pub.timegrids
1067
+ Timegrids(init=Timegrid("1996-01-01 00:00:00",
1068
+ "1996-01-05 00:00:00",
1069
+ "1d"),
1070
+ sim=Timegrid("1996-01-02 00:00:00",
1071
+ "1996-01-04 00:00:00",
1072
+ "1d"),
1073
+ eval_=Timegrid("1996-01-01 00:00:00",
1074
+ "1996-01-05 00:00:00",
1075
+ "1d"))
1076
+
1077
+ >>> with TestIO(): # doctest: +ELLIPSIS
1078
+ ... repr_(pub.conditionmanager.inputpath)
1079
+ ... repr_(pub.conditionmanager.outputpath)
1080
+ '.../hydpy/tests/iotesting/HydPy-H-Lahn/conditions/init_1996_01_02_00_00_00'
1081
+ '.../hydpy/tests/iotesting/HydPy-H-Lahn/conditions/init_1996_01_04_00_00_00'
1082
+
1083
+ Use the property |FileManager.currentdir| to change the values of both properties:
1084
+
1085
+ >>> with TestIO(): # doctest: +ELLIPSIS
1086
+ ... pub.conditionmanager.currentdir = "test"
1087
+ ... repr_(pub.conditionmanager.inputpath)
1088
+ ... repr_(pub.conditionmanager.outputpath)
1089
+ '.../hydpy/tests/iotesting/HydPy-H-Lahn/conditions/test'
1090
+ '.../hydpy/tests/iotesting/HydPy-H-Lahn/conditions/test'
1091
+
1092
+ After deleting the custom value of property |FileManager.currentdir|, the
1093
+ properties |ConditionManager.inputpath| and |ConditionManager.outputpath| work as
1094
+ before:
1095
+
1096
+ >>> with TestIO(): # doctest: +ELLIPSIS
1097
+ ... del pub.conditionmanager.currentdir
1098
+ ... repr_(pub.conditionmanager.inputpath)
1099
+ ... repr_(pub.conditionmanager.outputpath)
1100
+ '.../hydpy/tests/iotesting/HydPy-H-Lahn/conditions/init_1996_01_02_00_00_00'
1101
+ '.../hydpy/tests/iotesting/HydPy-H-Lahn/conditions/init_1996_01_04_00_00_00'
1102
+
1103
+ Use the |ConditionManager.prefix| option to configure the automatically determined
1104
+ folder names:
1105
+
1106
+ >>> with TestIO(), pub.conditionmanager.prefix("condi"): # doctest: +ELLIPSIS
1107
+ ... repr_(pub.conditionmanager.inputpath)
1108
+ ... repr_(pub.conditionmanager.outputpath)
1109
+ '.../hydpy/tests/iotesting/HydPy-H-Lahn/conditions/condi_1996_01_02_00_00_00'
1110
+ '.../hydpy/tests/iotesting/HydPy-H-Lahn/conditions/condi_1996_01_04_00_00_00'
1111
+
1112
+ The date-based construction of directory names requires a |Timegrids| object
1113
+ available in module |pub|:
1114
+
1115
+ >>> del pub.timegrids
1116
+ >>> with TestIO(): # doctest: +ELLIPSIS
1117
+ ... repr_(pub.conditionmanager.inputpath)
1118
+ Traceback (most recent call last):
1119
+ ...
1120
+ hydpy.core.exceptiontools.AttributeNotReady: While trying to determine the \
1121
+ currently relevant input path for loading conditions file, the following error \
1122
+ occurred: Attribute timegrids of module `pub` is not defined at the moment.
1123
+
1124
+ >>> del pub.timegrids
1125
+ >>> with TestIO(): # doctest: +ELLIPSIS
1126
+ ... repr_(pub.conditionmanager.outputpath)
1127
+ Traceback (most recent call last):
1128
+ ...
1129
+ hydpy.core.exceptiontools.AttributeNotReady: While trying to determine the \
1130
+ currently relevant output path for saving conditions file, the following error \
1131
+ occurred: Attribute timegrids of module `pub` is not defined at the moment.
1132
+ """
1133
+
1134
+ BASEDIR = "conditions"
1135
+ DEFAULTDIR = None
1136
+
1137
+ prefix = optiontools.OptionPropertyStr(
1138
+ "init",
1139
+ """The prefix of the automatically determined, time-dependent condition
1140
+ directory names.
1141
+
1142
+ The default prefix is `init`:
1143
+
1144
+ >>> from hydpy.core.testtools import prepare_full_example_2
1145
+ >>> hp, pub, TestIO = prepare_full_example_2()
1146
+ >>> cm = pub.conditionmanager
1147
+ >>> with TestIO():
1148
+ ... assert cm.inputpath.endswith("init_1996_01_01_00_00_00")
1149
+ ... assert cm.outputpath.endswith("init_1996_01_05_00_00_00")
1150
+
1151
+ For example, you can vary the prefix to store the conditions of different
1152
+ ensemble members in separate directories:
1153
+
1154
+ >>> with TestIO(), cm.prefix("member_01"):
1155
+ ... assert cm.inputpath.endswith("member_01_1996_01_01_00_00_00")
1156
+ ... assert cm.outputpath.endswith("member_01_1996_01_05_00_00_00")
1157
+ """,
1158
+ )
1159
+
1160
+ def _print_info(self, dirname, task) -> None:
1161
+ if hydpy.pub.options.printprogress:
1162
+ print(
1163
+ f"The {self._docname}'s current working directory is not defined "
1164
+ f"explicitly. Hence, the {self._docname} {task} a directory named "
1165
+ f"`{dirname}`."
1166
+ )
1167
+
1168
+ @property
1169
+ def inputpath(self) -> str:
1170
+ """The directory path for loading initial conditions.
1171
+
1172
+ See the main documentation on class |ConditionManager| and its option
1173
+ |ConditionManager.prefix| for further information.
1174
+ """
1175
+ currentdir = self._currentdir
1176
+ try:
1177
+ if not currentdir:
1178
+ to_string = hydpy.pub.timegrids.sim.firstdate.to_string
1179
+ autodir = f"{self.prefix}_{to_string('os')}"
1180
+ self._print_info(dirname=autodir, task="reads its data from")
1181
+ self.currentdir = autodir
1182
+ return self.currentpath
1183
+ except BaseException:
1184
+ objecttools.augment_excmessage(
1185
+ "While trying to determine the currently relevant input path for "
1186
+ "loading conditions file"
1187
+ )
1188
+ finally:
1189
+ self._currentdir = currentdir
1190
+
1191
+ @property
1192
+ def outputpath(self) -> str:
1193
+ """The directory path for saving (final) conditions.
1194
+
1195
+ See the main documentation on class |ConditionManager| and its option
1196
+ |ConditionManager.prefix| for further information.
1197
+ """
1198
+ currentdir = self._currentdir
1199
+ try:
1200
+ if not currentdir:
1201
+ to_string = hydpy.pub.timegrids.sim.lastdate.to_string
1202
+ autodir = f"{self.prefix}_{to_string('os')}"
1203
+ self._print_info(dirname=autodir, task="writes its data to")
1204
+ self.currentdir = autodir
1205
+ return self.currentpath
1206
+ except BaseException:
1207
+ objecttools.augment_excmessage(
1208
+ "While trying to determine the currently relevant output path for "
1209
+ "saving conditions file"
1210
+ )
1211
+ finally:
1212
+ self._currentdir = currentdir
1213
+
1214
+
1215
+ class SequenceManager(FileManager):
1216
+ """Manager for sequence files.
1217
+
1218
+ Usually, there is only one |SequenceManager| used within each *HydPy* project,
1219
+ stored in module |pub|. This object is responsible for the actual I/O tasks
1220
+ related to |IOSequence| objects.
1221
+
1222
+ Working with a complete *HydPy* project, one often does not use the
1223
+ |SequenceManager| directly, except one wishes to load or save time series data in
1224
+ a way different from the default settings. The following examples show the
1225
+ essential features of class |SequenceManager| based on the example project
1226
+ configuration defined by function |prepare_io_example_1|.
1227
+
1228
+ We prepare the project and select one 0-dimensional sequence of type |Sim| and one
1229
+ 1-dimensional sequence of type |lland_fluxes.NKor| for the following examples:
1230
+
1231
+ >>> from hydpy.core.testtools import prepare_io_example_1
1232
+ >>> nodes, elements = prepare_io_example_1()
1233
+ >>> sim = nodes.node2.sequences.sim
1234
+ >>> nkor = elements.element2.model.sequences.fluxes.nkor
1235
+
1236
+ We store the time series data of both sequences in ASCII files (methods
1237
+ |SequenceManager.save_file| and |IOSequence.save_series| are interchangeable here.
1238
+ The last one is only a convenience function for the first one):
1239
+
1240
+ >>> from hydpy import pub
1241
+ >>> pub.sequencemanager.filetype = "asc"
1242
+ >>> from hydpy import TestIO
1243
+ >>> with TestIO():
1244
+ ... pub.sequencemanager.save_file(sim)
1245
+ ... nkor.save_series()
1246
+
1247
+ We can load the file content from the output directory defined by
1248
+ |prepare_io_example_1| and print it to check this was successful:
1249
+
1250
+ >>> import os
1251
+ >>> from hydpy import round_
1252
+ >>> def print_file(filename):
1253
+ ... path = os.path.join("project", "series", "default", filename)
1254
+ ... with TestIO():
1255
+ ... with open(path) as file_:
1256
+ ... lines = file_.readlines()
1257
+ ... print("".join(lines[:3]), end="")
1258
+ ... for line in lines[3:]:
1259
+ ... round_([float(x) for x in line.split()])
1260
+
1261
+ >>> print_file("node2_sim_t.asc")
1262
+ Timegrid("2000-01-01 00:00:00+01:00",
1263
+ "2000-01-05 00:00:00+01:00",
1264
+ "1d")
1265
+ 64.0
1266
+ 65.0
1267
+ 66.0
1268
+ 67.0
1269
+ >>> print_file("element2_lland_dd_flux_nkor.asc")
1270
+ Timegrid("2000-01-01 00:00:00+01:00",
1271
+ "2000-01-05 00:00:00+01:00",
1272
+ "1d")
1273
+ 16.0, 17.0
1274
+ 18.0, 19.0
1275
+ 20.0, 21.0
1276
+ 22.0, 23.0
1277
+
1278
+ To show that reloading the data works, we set the values of the time series of both
1279
+ objects to zero and recover the original values afterwards:
1280
+
1281
+ >>> sim.series = 0.0
1282
+ >>> sim.series
1283
+ InfoArray([0., 0., 0., 0.])
1284
+ >>> nkor.series = 0.0
1285
+ >>> nkor.series
1286
+ InfoArray([[0., 0.],
1287
+ [0., 0.],
1288
+ [0., 0.],
1289
+ [0., 0.]])
1290
+ >>> with TestIO():
1291
+ ... pub.sequencemanager.load_file(sim)
1292
+ ... nkor.load_series()
1293
+ >>> sim.series
1294
+ InfoArray([64., 65., 66., 67.])
1295
+ >>> nkor.series
1296
+ InfoArray([[16., 17.],
1297
+ [18., 19.],
1298
+ [20., 21.],
1299
+ [22., 23.]])
1300
+
1301
+ We now write two files that do not span the initialisation period.
1302
+
1303
+ >>> with TestIO():
1304
+ ... for filename in ("incomplete_1.asc", "incomplete_2.asc"):
1305
+ ... path = os.path.join("project", "series", "default", filename)
1306
+ ... with open(path, "w") as file_:
1307
+ ... _ = file_.write('Timegrid("2000-01-02 00:00:00+01:00",\\n'
1308
+ ... ' "2000-01-04 00:00:00+01:00",\\n'
1309
+ ... ' "1d")\\n')
1310
+ ... for value in (1.0, 2.0):
1311
+ ... if filename == "incomplete_1.asc":
1312
+ ... _ = file_.write(f"{value}\\n")
1313
+ ... else:
1314
+ ... _ = file_.write(f"{value} {value + 1.0}\\n")
1315
+
1316
+ >>> print_file("incomplete_1.asc")
1317
+ Timegrid("2000-01-02 00:00:00+01:00",
1318
+ "2000-01-04 00:00:00+01:00",
1319
+ "1d")
1320
+ 1.0
1321
+ 2.0
1322
+
1323
+ >>> print_file("incomplete_2.asc")
1324
+ Timegrid("2000-01-02 00:00:00+01:00",
1325
+ "2000-01-04 00:00:00+01:00",
1326
+ "1d")
1327
+ 1.0, 2.0
1328
+ 2.0, 3.0
1329
+
1330
+ By default, trying to read such incomplete files results in an error:
1331
+
1332
+ >>> sim.filename = "incomplete_1.asc"
1333
+ >>> nkor.filename = "incomplete_2.asc"
1334
+ >>> with TestIO(): # doctest: +ELLIPSIS
1335
+ ... pub.sequencemanager.load_file(sim)
1336
+ Traceback (most recent call last):
1337
+ ...
1338
+ RuntimeError: While trying to load the time series data of sequence `sim` of node \
1339
+ `node2`, the following error occurred: For sequence `sim` of node `node2` the \
1340
+ initialisation time grid (Timegrid("2000-01-01 00:00:00", "2000-01-05 00:00:00", \
1341
+ "1d")) does not define a subset of the time grid of the data file \
1342
+ `...incomplete_1.asc` (Timegrid("2000-01-02 00:00:00", "2000-01-04 00:00:00", "1d")).
1343
+
1344
+ Setting option |Options.checkseries| to |False| turns this safety mechanism off:
1345
+
1346
+ >>> with TestIO(), pub.options.checkseries(False):
1347
+ ... pub.sequencemanager.load_file(sim)
1348
+ ... nkor.load_series()
1349
+ >>> sim.series
1350
+ InfoArray([nan, 1., 2., nan])
1351
+ >>> nkor.series
1352
+ InfoArray([[nan, nan],
1353
+ [ 1., 2.],
1354
+ [ 2., 3.],
1355
+ [nan, nan]])
1356
+
1357
+ Note that all previously available data outside the period supported by the read
1358
+ files has been set to |numpy.nan|, another safety mechanism to avoid accidentally
1359
+ mixing data. If you instead want to mix data from different sources, set option
1360
+ |SequenceManager.reset| to |True|:
1361
+
1362
+ >>> sim.series = 5.0, 6.0, 7.0, 8.0
1363
+ >>> nkor.series = [[5.0, 6.0], [6.0, 7.0], [7.0, 8.0], [8.0, 9.0]]
1364
+ >>> with TestIO(), pub.options.checkseries(False), pub.sequencemanager.reset(False):
1365
+ ... pub.sequencemanager.load_file(sim)
1366
+ ... nkor.load_series()
1367
+ >>> sim.series
1368
+ InfoArray([5., 1., 2., 8.])
1369
+ >>> nkor.series
1370
+ InfoArray([[5., 6.],
1371
+ [1., 2.],
1372
+ [2., 3.],
1373
+ [8., 9.]])
1374
+
1375
+ We reset the file names and data for the remaining tests:
1376
+
1377
+ >>> del sim.filename
1378
+ >>> del nkor.filename
1379
+ >>> with TestIO():
1380
+ ... pub.sequencemanager.load_file(sim)
1381
+ ... nkor.load_series()
1382
+
1383
+ Wrongly formatted ASCII files and incomplete data should result in understandable
1384
+ error messages:
1385
+
1386
+ >>> path = os.path.join("project", "series", "default", "node2_sim_t.asc")
1387
+ >>> with TestIO():
1388
+ ... with open(path) as file_:
1389
+ ... right = file_.read()
1390
+ ... wrong = right.replace("Timegrid", "timegrid")
1391
+ ... with open(path, "w") as file_:
1392
+ ... _ = file_.write(wrong)
1393
+ >>> with TestIO():
1394
+ ... pub.sequencemanager.load_file(sim)
1395
+ Traceback (most recent call last):
1396
+ ...
1397
+ NameError: While trying to load the time series data of sequence `sim` of node \
1398
+ `node2`, the following error occurred: name 'timegrid' is not defined
1399
+
1400
+ >>> sim_series = sim.series.copy()
1401
+ >>> with TestIO():
1402
+ ... lines = right.split("\\n")
1403
+ ... lines[5] = "nan"
1404
+ ... wrong = "\\n".join(lines)
1405
+ ... with open(path, "w") as file_:
1406
+ ... _ = file_.write(wrong)
1407
+ >>> with TestIO():
1408
+ ... pub.sequencemanager.load_file(sim)
1409
+ Traceback (most recent call last):
1410
+ ...
1411
+ RuntimeError: While trying to load the time series data of sequence `sim` of node \
1412
+ `node2`, the following error occurred: The series array of sequence `sim` of node \
1413
+ `node2` contains 1 nan value.
1414
+ >>> sim.series = sim_series
1415
+
1416
+ By default, overwriting existing time series files is disabled:
1417
+
1418
+ >>> with TestIO():
1419
+ ... sim.save_series() # doctest: +ELLIPSIS
1420
+ Traceback (most recent call last):
1421
+ ...
1422
+ OSError: While trying to save the time series data of sequence `sim` of \
1423
+ node `node2`, the following error occurred: Sequence `sim` of node `node2` is \
1424
+ not allowed to overwrite the existing file `...`.
1425
+ >>> pub.sequencemanager.overwrite = True
1426
+ >>> with TestIO():
1427
+ ... sim.save_series()
1428
+
1429
+ When a sequence comes with a weighting parameter referenced by |property|
1430
+ |Variable.refweights|, one can save the averaged time series by using the method
1431
+ |IOSequence.save_mean|:
1432
+
1433
+ >>> with TestIO():
1434
+ ... nkor.save_mean()
1435
+ >>> print_file("element2_lland_dd_flux_nkor_mean.asc")
1436
+ Timegrid("2000-01-01 00:00:00+01:00",
1437
+ "2000-01-05 00:00:00+01:00",
1438
+ "1d")
1439
+ 16.5
1440
+ 18.5
1441
+ 20.5
1442
+ 22.5
1443
+
1444
+ Method |IOSequence.save_mean| is strongly related to method
1445
+ |IOSequence.average_series|, meaning one can pass the same arguments. We show this
1446
+ by changing the land use classes of `element2` (parameter |lland_control.Lnk|) to
1447
+ field (|lland_constants.ACKER|) and water (|lland_constants.WASSER|) and averaging
1448
+ the values of sequence |lland_fluxes.NKor| for the single field area only:
1449
+
1450
+ >>> from hydpy.models.lland_dd import ACKER, WASSER
1451
+ >>> nkor.subseqs.seqs.model.parameters.control.lnk = ACKER, WASSER
1452
+ >>> with TestIO():
1453
+ ... nkor.save_mean("acker")
1454
+ >>> print_file("element2_lland_dd_flux_nkor_mean.asc")
1455
+ Timegrid("2000-01-01 00:00:00+01:00",
1456
+ "2000-01-05 00:00:00+01:00",
1457
+ "1d")
1458
+ 16.0
1459
+ 18.0
1460
+ 20.0
1461
+ 22.0
1462
+
1463
+ All numbers are written in scientific notation under the default setting of option
1464
+ |Options.reprdigits| (-1):
1465
+
1466
+ >>> nodes.node1.sequences.sim.series = 0.12345678
1467
+ >>> with TestIO(), pub.options.reprdigits(-1):
1468
+ ... nodes.node1.sequences.sim.save_series()
1469
+ >>> print_file("node1_sim_q.asc")
1470
+ Timegrid("2000-01-01 00:00:00+01:00",
1471
+ "2000-01-05 00:00:00+01:00",
1472
+ "1d")
1473
+ 0.123457
1474
+ 0.123457
1475
+ 0.123457
1476
+ 0.123457
1477
+
1478
+ If you set this option to two, for example, all numbers are written in the decimal
1479
+ form with at most two decimal places:
1480
+
1481
+ >>> with TestIO(), pub.options.reprdigits(2):
1482
+ ... nodes.node1.sequences.sim.save_series()
1483
+ >>> print_file("node1_sim_q.asc")
1484
+ Timegrid("2000-01-01 00:00:00+01:00",
1485
+ "2000-01-05 00:00:00+01:00",
1486
+ "1d")
1487
+ 0.12
1488
+ 0.12
1489
+ 0.12
1490
+ 0.12
1491
+
1492
+ Another option is storing data using |numpy| binary files, which is good for saving
1493
+ computation times but possibly problematic for sharing data with colleagues:
1494
+
1495
+ >>> pub.sequencemanager.filetype = "npy"
1496
+ >>> with TestIO():
1497
+ ... sim.save_series()
1498
+ ... nkor.save_series()
1499
+
1500
+ The time information (without time zone information) is available within the first
1501
+ thirteen entries:
1502
+
1503
+ >>> path = os.path.join("project", "series", "default", "node2_sim_t.npy")
1504
+ >>> import numpy
1505
+ >>> from hydpy import print_vector, print_matrix
1506
+ >>> with TestIO():
1507
+ ... print_vector(numpy.load(path))
1508
+ 2000.0, 1.0, 1.0, 0.0, 0.0, 0.0, 2000.0, 1.0, 5.0, 0.0, 0.0, 0.0,
1509
+ 86400.0, 64.0, 65.0, 66.0, 67.0
1510
+
1511
+ Reloading the data works as expected:
1512
+
1513
+ >>> sim.series = 0.0
1514
+ >>> nkor.series = 0.0
1515
+ >>> with TestIO():
1516
+ ... sim.load_series()
1517
+ ... nkor.load_series()
1518
+ >>> print_vector(sim.series)
1519
+ 64.0, 65.0, 66.0, 67.0
1520
+ >>> print_matrix(nkor.series)
1521
+ | 16.0, 17.0 |
1522
+ | 18.0, 19.0 |
1523
+ | 20.0, 21.0 |
1524
+ | 22.0, 23.0 |
1525
+
1526
+ Writing mean values into |numpy| binary files is also supported:
1527
+
1528
+ >>> import numpy
1529
+ >>> from hydpy import print_vector
1530
+ >>> path = os.path.join(
1531
+ ... "project", "series", "default", "element2_lland_dd_flux_nkor_mean.npy")
1532
+ >>> with TestIO():
1533
+ ... nkor.save_mean("wasser")
1534
+ ... print_vector(numpy.load(path)[-4:])
1535
+ 17.0, 19.0, 21.0, 23.0
1536
+
1537
+ Generally, trying to load data for "deactivated" sequences results in the following
1538
+ error message:
1539
+
1540
+ >>> nkor.prepare_series(allocate_ram=False)
1541
+ >>> with TestIO(clear_all=True):
1542
+ ... pub.sequencemanager.save_file(nkor)
1543
+ Traceback (most recent call last):
1544
+ ...
1545
+ hydpy.core.exceptiontools.AttributeNotReady: Sequence `nkor` of element \
1546
+ `element2` is not requested to make any time series data available.
1547
+
1548
+ The third option is to store data in NetCDF files, which is explained separately in
1549
+ the documentation on module |netcdftools|.
1550
+ """
1551
+
1552
+ SUPPORTED_MODES = "npy", "asc", "nc"
1553
+ BASEDIR = "series"
1554
+ DEFAULTDIR = "default"
1555
+
1556
+ filetype = optiontools.OptionPropertySeriesFileType(
1557
+ "asc",
1558
+ """Currently active time series file type.
1559
+
1560
+ |SequenceManager.filetype| is an option based on |OptionPropertySeriesFileType|.
1561
+ See its documentation for further information.
1562
+ """,
1563
+ )
1564
+ reset = optiontools.OptionPropertyBool(
1565
+ True,
1566
+ """A flag that indicates whether to reset already available time series data
1567
+ before reading incomplete time series files.
1568
+
1569
+ |SequenceManager.reset| is an option based on |OptionPropertyBool|. See its
1570
+ documentation for further information.
1571
+ """,
1572
+ )
1573
+ overwrite = optiontools.OptionPropertyBool(
1574
+ False,
1575
+ """Currently active overwrite flag for time series files.
1576
+
1577
+ |SequenceManager.overwrite| is an option based on |OptionPropertyBool|. See
1578
+ its documentation for further information.
1579
+ """,
1580
+ )
1581
+ aggregation = optiontools.OptionPropertySeriesAggregation(
1582
+ "none",
1583
+ """Currently active aggregation mode for writing time series files.
1584
+
1585
+ |SequenceManager.aggregation| is an option based on
1586
+ |OptionPropertySeriesAggregation|. See its documentation for further
1587
+ information.
1588
+ """,
1589
+ )
1590
+ convention = optiontools.OptionPropertySeriesConvention(
1591
+ "model-specific",
1592
+ """Currently selected naming convention for reading and writing input time
1593
+ series files.
1594
+
1595
+ |SequenceManager.convention| is an option based on
1596
+ |OptionPropertySeriesConvention|. See its documentation for further
1597
+ information.
1598
+ """,
1599
+ )
1600
+
1601
+ _netcdfreader: netcdftools.NetCDFInterfaceReader | None = None
1602
+ _netcdfwriter: netcdftools.NetCDFInterfaceWriter | None = None
1603
+ _jitaccesshandler: netcdftools.JITAccessHandler | None = None
1604
+
1605
+ def load_file(self, sequence: sequencetools.IOSequence) -> None:
1606
+ """Load data from a data file and pass it to the given |IOSequence|."""
1607
+ try:
1608
+ if sequence.filetype == "nc":
1609
+ self._load_nc(sequence)
1610
+ else:
1611
+ if sequence.filetype == "npy":
1612
+ timegrid, series = self._load_npy(sequence)
1613
+ elif sequence.filetype == "asc":
1614
+ timegrid, series = self._load_asc(sequence)
1615
+ else:
1616
+ assert_never(sequence.filetype)
1617
+ series = sequence.adjust_series(timegrid, series)
1618
+ sequence.apply_adjusted_series(timegrid, series)
1619
+ except BaseException:
1620
+ objecttools.augment_excmessage(
1621
+ f"While trying to load the time series data of sequence "
1622
+ f"{objecttools.devicephrase(sequence)}"
1623
+ )
1624
+
1625
+ @staticmethod
1626
+ def _load_npy(
1627
+ sequence: sequencetools.IOSequence,
1628
+ ) -> tuple[timetools.Timegrid, NDArrayFloat]:
1629
+ data = numpy.load(sequence.filepath)
1630
+ timegrid_data = timetools.Timegrid.from_array(data)
1631
+ return timegrid_data, data[13:]
1632
+
1633
+ @staticmethod
1634
+ def _load_asc(
1635
+ sequence: sequencetools.IOSequence,
1636
+ ) -> tuple[timetools.Timegrid, NDArrayFloat]:
1637
+ filepath = sequence.filepath
1638
+ with open(filepath, encoding=config.ENCODING) as file_:
1639
+ header = "\n".join([file_.readline() for _ in range(3)])
1640
+ timegrid_data = eval(header, {}, {"Timegrid": timetools.Timegrid})
1641
+ values = numpy.loadtxt( # type: ignore[call-overload]
1642
+ filepath, skiprows=3, ndmin=min(sequence.NDIM + 1, 2)
1643
+ )
1644
+ if sequence.NDIM == 2:
1645
+ values = values.reshape(*sequence.seriesshape)
1646
+ return timegrid_data, values
1647
+
1648
+ def _load_nc(self, sequence: sequencetools.IOSequence) -> None:
1649
+ self.netcdfreader.log(sequence)
1650
+
1651
+ def save_file(
1652
+ self,
1653
+ sequence: sequencetools.IOSequence,
1654
+ array: sequencetools.InfoArray | None = None,
1655
+ ) -> None:
1656
+ """Write the data stored in the |IOSequence.series| property of the given
1657
+ |IOSequence| into a data file."""
1658
+ if array is None:
1659
+ array = sequence.aggregate_series()
1660
+ try:
1661
+ if sequence.filetype == "nc":
1662
+ self._save_nc(sequence, array)
1663
+ else:
1664
+ filepath = sequence.filepath
1665
+ if not sequence.overwrite and os.path.exists(filepath):
1666
+ raise OSError(
1667
+ f"Sequence {objecttools.devicephrase(sequence)} is not allowed "
1668
+ f"to overwrite the existing file `{filepath}`."
1669
+ )
1670
+ if sequence.filetype == "npy":
1671
+ self._save_npy(array, filepath)
1672
+ elif sequence.filetype == "asc":
1673
+ self._save_asc(array, filepath)
1674
+ except BaseException:
1675
+ objecttools.augment_excmessage(
1676
+ f"While trying to save the time series data of sequence "
1677
+ f"{objecttools.devicephrase(sequence)}"
1678
+ )
1679
+
1680
+ @staticmethod
1681
+ def _save_npy(array: NDArrayFloat, filepath: str) -> None:
1682
+ numpy.save(filepath, hydpy.pub.timegrids.init.array2series(array))
1683
+
1684
+ @staticmethod
1685
+ def _save_asc(array: NDArrayFloat, filepath: str) -> None:
1686
+ with open(filepath, "w", encoding=config.ENCODING) as file_:
1687
+ file_.write(
1688
+ hydpy.pub.timegrids.init.assignrepr(
1689
+ prefix="", style="iso2", utcoffset=hydpy.pub.options.utcoffset
1690
+ )
1691
+ + "\n"
1692
+ )
1693
+ if array.ndim == 3:
1694
+ array = array.reshape(array.shape[0], -1)
1695
+ with open(filepath, "a", encoding=config.ENCODING) as file_:
1696
+ digits = hydpy.pub.options.reprdigits
1697
+ format_ = "%.14e" if digits == -1 else f"%.{digits}f"
1698
+ numpy.savetxt(file_, array, fmt=format_, delimiter="\t")
1699
+
1700
+ def _save_nc(
1701
+ self, sequence: sequencetools.IOSequence, array: sequencetools.InfoArray
1702
+ ) -> None:
1703
+ self.netcdfwriter.log(sequence, array)
1704
+
1705
+ @property
1706
+ def netcdfreader(self) -> netcdftools.NetCDFInterfaceReader:
1707
+ """A |NetCDFInterfaceReader| object prepared by method
1708
+ |SequenceManager.open_netcdfreader| and to be finalised by method
1709
+ |SequenceManager.close_netcdfreader|.
1710
+
1711
+ >>> from hydpy.core.filetools import SequenceManager
1712
+ >>> sm = SequenceManager()
1713
+ >>> sm.netcdfreader
1714
+ Traceback (most recent call last):
1715
+ ...
1716
+ hydpy.core.exceptiontools.AttributeNotReady: The sequence file manager \
1717
+ currently handles no NetCDF reader object. Consider applying the \
1718
+ `pub.sequencemanager.netcdfreading` context manager first (search in the \
1719
+ documentation for help).
1720
+
1721
+ >>> sm.open_netcdfreader()
1722
+ >>> from hydpy import classname
1723
+ >>> classname(sm.netcdfreader)
1724
+ 'NetCDFInterfaceReader'
1725
+
1726
+ >>> sm.close_netcdfreader()
1727
+ >>> sm.netcdfreader
1728
+ Traceback (most recent call last):
1729
+ ...
1730
+ hydpy.core.exceptiontools.AttributeNotReady: The sequence file manager \
1731
+ currently handles no NetCDF reader object. Consider applying the \
1732
+ `pub.sequencemanager.netcdfreading` context manager first (search in the \
1733
+ documentation for help).
1734
+ """
1735
+ if self._netcdfreader is None:
1736
+ raise exceptiontools.AttributeNotReady(
1737
+ "The sequence file manager currently handles no NetCDF reader object. "
1738
+ "Consider applying the `pub.sequencemanager.netcdfreading` context "
1739
+ "manager first (search in the documentation for help)."
1740
+ )
1741
+ return self._netcdfreader
1742
+
1743
+ def open_netcdfreader(self) -> None:
1744
+ """Prepare a new |NetCDFInterfaceReader| object for reading data."""
1745
+ self._netcdfreader = netcdftools.NetCDFInterfaceReader()
1746
+
1747
+ def close_netcdfreader(self) -> None:
1748
+ """Read data with a prepared |NetCDFInterfaceReader| object and delete it
1749
+ afterwards."""
1750
+ self.netcdfreader.read()
1751
+ self._netcdfreader = None
1752
+
1753
+ @contextlib.contextmanager
1754
+ def netcdfreading(self) -> Iterator[None]:
1755
+ """Prepare a new |NetCDFInterfaceReader| object for collecting data at the
1756
+ beginning of a with-block and read the data and delete the object at the end of
1757
+ the same with-block."""
1758
+ self.open_netcdfreader()
1759
+ yield
1760
+ self.close_netcdfreader()
1761
+
1762
+ @property
1763
+ def netcdfwriter(self) -> netcdftools.NetCDFInterfaceWriter:
1764
+ """A |NetCDFInterfaceWriter| object prepared by method
1765
+ |SequenceManager.open_netcdfwriter| and to be finalised by method
1766
+ |SequenceManager.close_netcdfwriter|.
1767
+
1768
+ >>> from hydpy.core.filetools import SequenceManager
1769
+ >>> sm = SequenceManager()
1770
+ >>> sm.netcdfwriter
1771
+ Traceback (most recent call last):
1772
+ ...
1773
+ hydpy.core.exceptiontools.AttributeNotReady: The sequence file manager \
1774
+ currently handles no NetCDF writer object. Consider applying the \
1775
+ `pub.sequencemanager.netcdfwriting` context manager first (search in the \
1776
+ documentation for help).
1777
+
1778
+ >>> sm.open_netcdfwriter()
1779
+ >>> from hydpy import classname
1780
+ >>> classname(sm.netcdfwriter)
1781
+ 'NetCDFInterfaceWriter'
1782
+
1783
+ >>> sm.close_netcdfwriter()
1784
+ >>> sm.netcdfwriter
1785
+ Traceback (most recent call last):
1786
+ ...
1787
+ hydpy.core.exceptiontools.AttributeNotReady: The sequence file manager \
1788
+ currently handles no NetCDF writer object. Consider applying the \
1789
+ `pub.sequencemanager.netcdfwriting` context manager first (search in the \
1790
+ documentation for help).
1791
+ """
1792
+ if self._netcdfwriter is None:
1793
+ raise exceptiontools.AttributeNotReady(
1794
+ "The sequence file manager currently handles no NetCDF writer object. "
1795
+ "Consider applying the `pub.sequencemanager.netcdfwriting` context "
1796
+ "manager first (search in the documentation for help)."
1797
+ )
1798
+ return self._netcdfwriter
1799
+
1800
+ def open_netcdfwriter(self) -> None:
1801
+ """Prepare a new |NetCDFInterfaceWriter| object for writing data."""
1802
+ self._netcdfwriter = netcdftools.NetCDFInterfaceWriter()
1803
+
1804
+ def close_netcdfwriter(self) -> None:
1805
+ """Write data with a prepared |NetCDFInterfaceWriter| object and delete it
1806
+ afterwards."""
1807
+ self.netcdfwriter.write()
1808
+ self._netcdfwriter = None
1809
+
1810
+ @contextlib.contextmanager
1811
+ def netcdfwriting(self) -> Iterator[None]:
1812
+ """Prepare a new |NetCDFInterfaceWriter| object for collecting data at the
1813
+ beginning of a with-block and write the data and delete the object at the end
1814
+ of the same with-block."""
1815
+ self.open_netcdfwriter()
1816
+ yield
1817
+ self.close_netcdfwriter()
1818
+
1819
+ @contextlib.contextmanager
1820
+ def provide_netcdfjitaccess(
1821
+ self, deviceorder: Iterable[devicetools.Node | devicetools.Element]
1822
+ ) -> Iterator[None]:
1823
+ """Open all required internal NetCDF time series files.
1824
+
1825
+ This method is only relevant for reading data from or writing data to NetCDF
1826
+ files "just in time" during simulation runs. See the main documentation on
1827
+ class |HydPy| for further information.
1828
+ """
1829
+ try:
1830
+ interface = netcdftools.NetCDFInterfaceJIT()
1831
+ with interface.provide_jitaccess(deviceorder) as jitaccesshandler:
1832
+ self._jitaccesshandler = jitaccesshandler
1833
+ yield
1834
+ finally:
1835
+ self._jitaccesshandler = None
1836
+
1837
+ def read_netcdfslices(self, idx: int) -> None:
1838
+ """Read the time slice relevant to the current simulation step.
1839
+
1840
+ This method is only relevant for reading data from or writing data to NetCDF
1841
+ files "just in time" during simulation runs. See the main documentation on
1842
+ class |HydPy| for further information.
1843
+ """
1844
+ handler = self._jitaccesshandler
1845
+ if handler is not None:
1846
+ handler.read_slices(idx)
1847
+
1848
+ def write_netcdfslices(self, idx: int) -> None:
1849
+ """Write the time slice relevant to the current simulation step.
1850
+
1851
+ This method is only relevant for reading data from or writing data to NetCDF
1852
+ files "just in time" during simulation runs. See the main documentation on
1853
+ class |HydPy| for further information.
1854
+ """
1855
+ handler = self._jitaccesshandler
1856
+ if handler is not None:
1857
+ handler.write_slices(idx)
1858
+
1859
+
1860
+ _FILEMANAGERS = (NetworkManager, ControlManager, ConditionManager, SequenceManager)
1861
+
1862
+
1863
+ def check_projectstructure(projectpath: str) -> None:
1864
+ """Raise a warning if the given project root directory does not exist or does not
1865
+ contain all relevant base directories.
1866
+
1867
+ First, |check_projectstructure| checks if the root directory exists:
1868
+
1869
+ >>> from hydpy import check_projectstructure, HydPy, pub, TestIO
1870
+ >>> TestIO.clear()
1871
+ >>> with TestIO():
1872
+ ... check_projectstructure("my_project") # doctest: +ELLIPSIS
1873
+ Traceback (most recent call last):
1874
+ ...
1875
+ UserWarning: The project root directory `...my_project` does not exists.
1876
+
1877
+ Second, it lists all missing base directories:
1878
+
1879
+ >>> import os
1880
+ >>> from hydpy.core.testtools import warn_later
1881
+ >>> with TestIO(), warn_later(), pub.options.checkprojectstructure(True):
1882
+ ... os.makedirs(os.path.join("my_project", "control"))
1883
+ ... hp = HydPy("my_project") # doctest: +ELLIPSIS
1884
+ UserWarning: The project root directory ...my_project has no base directory \
1885
+ named `network` as required by the network manager.
1886
+ UserWarning: The project root directory ...my_project has no base directory \
1887
+ named `conditions` as required by the condition manager.
1888
+ UserWarning: The project root directory ...my_project has no base directory \
1889
+ named `series` as required by the sequence manager.
1890
+
1891
+ Note that class |HydPy| calls function |check_projectstructure| automatically if
1892
+ option |Options.checkprojectstructure| is enabled:
1893
+
1894
+ >>> TestIO.clear()
1895
+ >>> with TestIO(), pub.options.checkprojectstructure(False):
1896
+ ... hp = HydPy("my_project")
1897
+
1898
+ >>> with TestIO(), pub.options.checkprojectstructure(True):
1899
+ ... hp = HydPy("my_project") # doctest: +ELLIPSIS
1900
+ Traceback (most recent call last):
1901
+ ...
1902
+ UserWarning: The project root directory `...my_project` does not exists.
1903
+ """
1904
+
1905
+ projectpath = os.path.abspath(projectpath)
1906
+ if os.path.exists(projectpath):
1907
+ for filemanager in _FILEMANAGERS:
1908
+ basepath = os.path.join(projectpath, filemanager.BASEDIR)
1909
+ if not os.path.exists(basepath):
1910
+ warnings.warn(
1911
+ f"The project root directory {projectpath} has no base directory "
1912
+ f"named `{filemanager.BASEDIR}` as required by the "
1913
+ f"{filemanager.__name__[:-7].lower()} manager."
1914
+ )
1915
+ else:
1916
+ warnings.warn(f"The project root directory `{projectpath}` does not exists.")
1917
+
1918
+
1919
+ def create_projectstructure(projectpath: str, overwrite: bool = False) -> None:
1920
+ """Make the given project root directory and its base directories.
1921
+
1922
+ If everything works well, function |create_projectstructure| creates the required
1923
+ directories silently:
1924
+
1925
+ >>> from hydpy import create_projectstructure, TestIO
1926
+ >>> from hydpy.core.testtools import print_filestructure
1927
+ >>> TestIO.clear()
1928
+ >>> with TestIO():
1929
+ ... create_projectstructure("my_project")
1930
+ ... print_filestructure("my_project") # doctest: +ELLIPSIS
1931
+ * ...my_project
1932
+ - conditions
1933
+ - control
1934
+ - network
1935
+ - series
1936
+
1937
+ If the root directory already exists, it does not make any changes and instead
1938
+ raises the following error by default:
1939
+
1940
+ >>> with TestIO():
1941
+ ... os.makedirs(os.path.join("my_project", "zap"))
1942
+ ... create_projectstructure("my_project") # doctest: +ELLIPSIS
1943
+ Traceback (most recent call last):
1944
+ ...
1945
+ FileExistsError: While trying to create the basic directory structure for project \
1946
+ `my_project`the directory ...iotesting, the following error occurred: The root \
1947
+ directory already exists and overwriting is not allowed.
1948
+ >>> with TestIO():
1949
+ ... print_filestructure("my_project") # doctest: +ELLIPSIS
1950
+ * ...my_project
1951
+ - conditions
1952
+ - control
1953
+ - network
1954
+ - series
1955
+ - zap
1956
+
1957
+ Use the `overwrite` flag to let function |create_projectstructure| remove the
1958
+ existing directory and make a new one:
1959
+
1960
+ >>> with TestIO():
1961
+ ... create_projectstructure("my_project", overwrite=True)
1962
+ ... print_filestructure("my_project") # doctest: +ELLIPSIS
1963
+ * ...my_project
1964
+ - conditions
1965
+ - control
1966
+ - network
1967
+ - series
1968
+ """
1969
+ projectpath = os.path.abspath(projectpath)
1970
+ try:
1971
+ if os.path.exists(projectpath):
1972
+ if overwrite:
1973
+ shutil.rmtree(projectpath)
1974
+ else:
1975
+ raise FileExistsError(
1976
+ "The root directory already exists and overwriting is not allowed."
1977
+ )
1978
+ for filenmanager in _FILEMANAGERS:
1979
+ os.makedirs(os.path.join(projectpath, filenmanager.BASEDIR))
1980
+ except BaseException:
1981
+ dirpath, projectname = os.path.split(projectpath)
1982
+ objecttools.augment_excmessage(
1983
+ f"While trying to create the basic directory structure for project "
1984
+ f"`{projectname}`the directory {dirpath}"
1985
+ )