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