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