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,2615 @@
|
|
|
1
|
+
"""This module implements general features for defining and working with model
|
|
2
|
+
parameters and sequences.
|
|
3
|
+
|
|
4
|
+
Features more specific to either parameters or sequences are implemented in modules
|
|
5
|
+
|parametertools| and |sequencetools|, respectively.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# import...
|
|
9
|
+
# ...from standard library
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
import abc
|
|
12
|
+
import contextlib
|
|
13
|
+
import copy
|
|
14
|
+
import functools
|
|
15
|
+
import inspect
|
|
16
|
+
import warnings
|
|
17
|
+
|
|
18
|
+
# ...from site-packages
|
|
19
|
+
import numpy
|
|
20
|
+
|
|
21
|
+
# ...from HydPy
|
|
22
|
+
import hydpy
|
|
23
|
+
from hydpy import config
|
|
24
|
+
from hydpy.core import exceptiontools
|
|
25
|
+
from hydpy.core import masktools
|
|
26
|
+
from hydpy.core import objecttools
|
|
27
|
+
from hydpy.core import propertytools
|
|
28
|
+
from hydpy.core.typingtools import *
|
|
29
|
+
|
|
30
|
+
if TYPE_CHECKING:
|
|
31
|
+
from hydpy.core import devicetools
|
|
32
|
+
from hydpy.core import parametertools
|
|
33
|
+
from hydpy.core import sequencetools
|
|
34
|
+
from hydpy.cythons import pointerutils
|
|
35
|
+
from hydpy.cythons import sequenceutils
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
TypeGroup_co = TypeVar(
|
|
39
|
+
"TypeGroup_co",
|
|
40
|
+
"parametertools.Parameters",
|
|
41
|
+
"sequencetools.Sequences",
|
|
42
|
+
"devicetools.Node",
|
|
43
|
+
covariant=True,
|
|
44
|
+
)
|
|
45
|
+
TypeVariable = TypeVar("TypeVariable", bound="Variable")
|
|
46
|
+
TypeVariable_co = TypeVar("TypeVariable_co", bound="Variable", covariant=True)
|
|
47
|
+
TypeFastAccess_co = TypeVar("TypeFastAccess_co", bound="FastAccess", covariant=True)
|
|
48
|
+
|
|
49
|
+
INT_NAN: int = -999999
|
|
50
|
+
"""Surrogate for `nan`, which is available for floating-point values but not for
|
|
51
|
+
integer values."""
|
|
52
|
+
|
|
53
|
+
TYPE2MISSINGVALUE = {float: numpy.nan, int: INT_NAN, bool: False}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def trim(self: Variable, lower=None, upper=None) -> bool:
|
|
57
|
+
"""Trim the value(s) of a |Variable| instance.
|
|
58
|
+
|
|
59
|
+
The returned boolean indicates whether at least one value has been trimmed.
|
|
60
|
+
|
|
61
|
+
Usually, users do not need to apply the |trim| function directly. Instead, some
|
|
62
|
+
|Variable| subclasses implement their own `trim` methods relying on function |trim|.
|
|
63
|
+
Model developers should implement individual `trim` methods for their |Parameter|
|
|
64
|
+
or |Sequence_| subclasses when their boundary values depend on the actual project
|
|
65
|
+
configuration (one example is soil moisture; its lowest possible value should
|
|
66
|
+
possibly be zero in all cases, but its highest possible value could depend on
|
|
67
|
+
another parameter defining the maximum storage capacity).
|
|
68
|
+
|
|
69
|
+
For the following examples, we prepare a simple (not fully functional) |Variable|
|
|
70
|
+
subclass, making use of function |trim| without any modifications. Function |trim|
|
|
71
|
+
works slightly different for variables handling |float|, |int|, and |bool| values.
|
|
72
|
+
We start with the most common content type, |float|:
|
|
73
|
+
|
|
74
|
+
>>> from hydpy.core.variabletools import trim, Variable
|
|
75
|
+
>>> class Var(Variable):
|
|
76
|
+
... NDIM = 0
|
|
77
|
+
... TYPE = float
|
|
78
|
+
... SPAN = 1.0, 3.0
|
|
79
|
+
... trim = trim
|
|
80
|
+
... initinfo = 2.0, False
|
|
81
|
+
... _CLS_FASTACCESS_PYTHON = FastAccess
|
|
82
|
+
|
|
83
|
+
First, we enable the printing of warning messages raised by function |trim|:
|
|
84
|
+
|
|
85
|
+
>>> from hydpy import pub
|
|
86
|
+
>>> pub.options.warntrim = True
|
|
87
|
+
|
|
88
|
+
When not passing boundary values, function |trim| extracts them from class
|
|
89
|
+
attribute `SPAN` of the given |Variable| instance, if available:
|
|
90
|
+
|
|
91
|
+
>>> var = Var(None)
|
|
92
|
+
>>> var.value = 2.0
|
|
93
|
+
>>> assert var.trim() is False
|
|
94
|
+
>>> var
|
|
95
|
+
var(2.0)
|
|
96
|
+
|
|
97
|
+
>>> var.value = 0.0
|
|
98
|
+
>>> from hydpy.core.testtools import warn_later
|
|
99
|
+
>>> with warn_later():
|
|
100
|
+
... assert var.trim() is True
|
|
101
|
+
UserWarning: For variable `var` at least one value needed to be trimmed. The old \
|
|
102
|
+
and the new value(s) are `0.0` and `1.0`, respectively.
|
|
103
|
+
>>> var
|
|
104
|
+
var(1.0)
|
|
105
|
+
|
|
106
|
+
>>> var.value = 4.0
|
|
107
|
+
>>> with warn_later():
|
|
108
|
+
... assert var.trim() is True
|
|
109
|
+
UserWarning: For variable `var` at least one value needed to be trimmed. The old \
|
|
110
|
+
and the new value(s) are `4.0` and `3.0`, respectively.
|
|
111
|
+
>>> var
|
|
112
|
+
var(3.0)
|
|
113
|
+
|
|
114
|
+
In the examples above, outlier values are set to the respective boundary value,
|
|
115
|
+
accompanied by suitable warning messages. For minimal deviations (defined by
|
|
116
|
+
function |get_tolerance|), which might be due to precision problems only, outliers
|
|
117
|
+
are trimmed but not reported:
|
|
118
|
+
|
|
119
|
+
>>> var.value = 1.0 - 1e-15
|
|
120
|
+
>>> var == 1.0
|
|
121
|
+
False
|
|
122
|
+
>>> assert trim(var) is False
|
|
123
|
+
>>> var == 1.0
|
|
124
|
+
True
|
|
125
|
+
|
|
126
|
+
>>> var.value = 3.0 + 1e-15
|
|
127
|
+
>>> var == 3.0
|
|
128
|
+
False
|
|
129
|
+
>>> assert var.trim() is False
|
|
130
|
+
>>> var == 3.0
|
|
131
|
+
True
|
|
132
|
+
|
|
133
|
+
Use arguments `lower` and `upper` to override the (eventually) available `SPAN`
|
|
134
|
+
entries:
|
|
135
|
+
|
|
136
|
+
>>> with warn_later():
|
|
137
|
+
... assert var.trim(lower=4.0) is True
|
|
138
|
+
UserWarning: For variable `var` at least one value needed to be trimmed. The old \
|
|
139
|
+
and the new value(s) are `3.0` and `4.0`, respectively.
|
|
140
|
+
|
|
141
|
+
>>> with warn_later():
|
|
142
|
+
... assert var.trim(lower=3.0) is True
|
|
143
|
+
UserWarning: For variable `var` at least one value needed to be trimmed. The old \
|
|
144
|
+
and the new value(s) are `4.0` and `3.0`, respectively.
|
|
145
|
+
|
|
146
|
+
Function |trim| interprets both |None| and |numpy.nan| values as if no boundary
|
|
147
|
+
value exists:
|
|
148
|
+
|
|
149
|
+
>>> import numpy
|
|
150
|
+
>>> var.value = 0.0
|
|
151
|
+
>>> assert var.trim(lower=numpy.nan) is False
|
|
152
|
+
>>> var.value = 5.0
|
|
153
|
+
>>> assert var.trim(upper=numpy.nan) is False
|
|
154
|
+
|
|
155
|
+
You can disable function |trim| via option |Options.trimvariables|:
|
|
156
|
+
|
|
157
|
+
>>> with pub.options.trimvariables(False):
|
|
158
|
+
... var.value = 5.0
|
|
159
|
+
... assert var.trim() is False
|
|
160
|
+
>>> var
|
|
161
|
+
var(5.0)
|
|
162
|
+
|
|
163
|
+
Alternatively, you can omit the warning messages only without modifying the return
|
|
164
|
+
value:
|
|
165
|
+
|
|
166
|
+
>>> with pub.options.warntrim(False):
|
|
167
|
+
... var.value = 5.0
|
|
168
|
+
... assert var.trim() is True
|
|
169
|
+
>>> var
|
|
170
|
+
var(3.0)
|
|
171
|
+
|
|
172
|
+
If a |Variable| subclass does not have (fixed) boundaries, give it either no `SPAN`
|
|
173
|
+
attribute or a |tuple| containing |None| values:
|
|
174
|
+
|
|
175
|
+
>>> del Var.SPAN
|
|
176
|
+
>>> var.value = 5.0
|
|
177
|
+
>>> assert var.trim() is False
|
|
178
|
+
>>> var
|
|
179
|
+
var(5.0)
|
|
180
|
+
|
|
181
|
+
>>> Var.SPAN = (None, None)
|
|
182
|
+
>>> assert var.trim() is False
|
|
183
|
+
>>> var
|
|
184
|
+
var(5.0)
|
|
185
|
+
|
|
186
|
+
The above examples deal with a 0-dimensional |Variable| subclass. The following
|
|
187
|
+
examples repeat the most relevant examples for a 2-dimensional subclass:
|
|
188
|
+
|
|
189
|
+
>>> Var.SPAN = 1.0, 3.0
|
|
190
|
+
>>> Var.NDIM = 2
|
|
191
|
+
>>> var.shape = 1, 3
|
|
192
|
+
>>> var.values = 2.0
|
|
193
|
+
>>> assert var.trim() is False
|
|
194
|
+
|
|
195
|
+
>>> var.values = 0.0, 1.0, 2.0
|
|
196
|
+
>>> with warn_later():
|
|
197
|
+
... assert var.trim() is True
|
|
198
|
+
UserWarning: For variable `var` at least one value needed to be trimmed. The old \
|
|
199
|
+
and the new value(s) are `0.0, 1.0, 2.0` and `1.0, 1.0, 2.0`, respectively.
|
|
200
|
+
>>> var
|
|
201
|
+
var(1.0, 1.0, 2.0)
|
|
202
|
+
|
|
203
|
+
>>> var.values = 2.0, 3.0, 4.0
|
|
204
|
+
>>> with warn_later():
|
|
205
|
+
... assert var.trim() is True
|
|
206
|
+
UserWarning: For variable `var` at least one value needed to be trimmed. The old \
|
|
207
|
+
and the new value(s) are `2.0, 3.0, 4.0` and `2.0, 3.0, 3.0`, respectively.
|
|
208
|
+
>>> var
|
|
209
|
+
var(2.0, 3.0, 3.0)
|
|
210
|
+
|
|
211
|
+
>>> from hydpy import print_matrix
|
|
212
|
+
>>> var.values = 1.0-1e-15, 2.0, 3.0+1e-15
|
|
213
|
+
>>> print_matrix(var.values == (1.0, 2.0, 3.0))
|
|
214
|
+
| False, True, False |
|
|
215
|
+
>>> assert var.trim() is False
|
|
216
|
+
>>> print_matrix(var.values == (1.0, 2.0, 3.0))
|
|
217
|
+
| True, True, True |
|
|
218
|
+
|
|
219
|
+
>>> var.values = 0.0, 2.0, 4.0
|
|
220
|
+
>>> assert var.trim(lower=numpy.nan, upper=numpy.nan) is False
|
|
221
|
+
>>> var
|
|
222
|
+
var(0.0, 2.0, 4.0)
|
|
223
|
+
|
|
224
|
+
>>> with warn_later():
|
|
225
|
+
... assert var.trim(lower=[numpy.nan, 3.0, 3.0]) is True
|
|
226
|
+
UserWarning: For variable `var` at least one value needed to be trimmed. The old \
|
|
227
|
+
and the new value(s) are `0.0, 2.0, 4.0` and `0.0, 3.0, 3.0`, respectively.
|
|
228
|
+
|
|
229
|
+
>>> var.values = 0.0, 2.0, 4.0
|
|
230
|
+
>>> with warn_later():
|
|
231
|
+
... assert var.trim(upper=[numpy.nan, 1.0, numpy.nan]) is True
|
|
232
|
+
UserWarning: For variable `var` at least one value needed to be trimmed. The old \
|
|
233
|
+
and the new value(s) are `0.0, 2.0, 4.0` and `1.0, 1.0, 4.0`, respectively.
|
|
234
|
+
|
|
235
|
+
For |Variable| subclasses handling |float| values, setting outliers to the
|
|
236
|
+
respective boundary value might often be an acceptable approach. However, this is
|
|
237
|
+
often not the case for subclasses handling |int| values, which often serve as
|
|
238
|
+
option flags (e.g. to enable/disable a certain hydrological process for different
|
|
239
|
+
land-use types). Hence, function |trim| raises an exception instead of a warning
|
|
240
|
+
and does not modify the wrong |int| value:
|
|
241
|
+
|
|
242
|
+
>>> Var.TYPE = int
|
|
243
|
+
>>> Var.NDIM = 0
|
|
244
|
+
>>> Var.SPAN = 1, 3
|
|
245
|
+
|
|
246
|
+
>>> var.value = 2
|
|
247
|
+
>>> assert var.trim() is False
|
|
248
|
+
>>> var
|
|
249
|
+
var(2)
|
|
250
|
+
|
|
251
|
+
>>> var.value = 0
|
|
252
|
+
>>> var.trim()
|
|
253
|
+
Traceback (most recent call last):
|
|
254
|
+
...
|
|
255
|
+
ValueError: The value `0` of parameter `var` of element `?` is not valid.
|
|
256
|
+
>>> var
|
|
257
|
+
var(0)
|
|
258
|
+
>>> var.value = 4
|
|
259
|
+
>>> var.trim()
|
|
260
|
+
Traceback (most recent call last):
|
|
261
|
+
...
|
|
262
|
+
ValueError: The value `4` of parameter `var` of element `?` is not valid.
|
|
263
|
+
>>> var
|
|
264
|
+
var(4)
|
|
265
|
+
|
|
266
|
+
>>> from hydpy import INT_NAN
|
|
267
|
+
>>> var.value = 0
|
|
268
|
+
>>> assert var.trim(lower=0) is False
|
|
269
|
+
>>> assert var.trim(lower=INT_NAN) is False
|
|
270
|
+
|
|
271
|
+
>>> var.value = 4
|
|
272
|
+
>>> assert var.trim(upper=4) is False
|
|
273
|
+
>>> assert var.trim(upper=INT_NAN) is False
|
|
274
|
+
|
|
275
|
+
>>> Var.SPAN = 1, None
|
|
276
|
+
>>> var.value = 0
|
|
277
|
+
>>> var.trim()
|
|
278
|
+
Traceback (most recent call last):
|
|
279
|
+
...
|
|
280
|
+
ValueError: The value `0` of parameter `var` of element `?` is not valid.
|
|
281
|
+
>>> var
|
|
282
|
+
var(0)
|
|
283
|
+
|
|
284
|
+
>>> Var.SPAN = None, 3
|
|
285
|
+
>>> var.value = 0
|
|
286
|
+
>>> assert var.trim() is False
|
|
287
|
+
>>> var.value = 4
|
|
288
|
+
>>> var.trim()
|
|
289
|
+
Traceback (most recent call last):
|
|
290
|
+
...
|
|
291
|
+
ValueError: The value `4` of parameter `var` of element `?` is not valid.
|
|
292
|
+
|
|
293
|
+
>>> del Var.SPAN
|
|
294
|
+
>>> var.value = 0
|
|
295
|
+
>>> assert var.trim() is False
|
|
296
|
+
>>> var.value = 4
|
|
297
|
+
>>> assert var.trim() is False
|
|
298
|
+
|
|
299
|
+
>>> Var.SPAN = 1, 3
|
|
300
|
+
>>> Var.NDIM = 2
|
|
301
|
+
>>> var.shape = (1, 3)
|
|
302
|
+
>>> var.values = 2
|
|
303
|
+
>>> assert var.trim() is False
|
|
304
|
+
|
|
305
|
+
>>> var.values = 0, 1, 2
|
|
306
|
+
>>> var.trim()
|
|
307
|
+
Traceback (most recent call last):
|
|
308
|
+
...
|
|
309
|
+
ValueError: At least one value of parameter `var` of element `?` is not valid.
|
|
310
|
+
>>> var
|
|
311
|
+
var(0, 1, 2)
|
|
312
|
+
>>> var.values = 2, 3, 4
|
|
313
|
+
>>> var.trim()
|
|
314
|
+
Traceback (most recent call last):
|
|
315
|
+
...
|
|
316
|
+
ValueError: At least one value of parameter `var` of element `?` is not valid.
|
|
317
|
+
>>> var
|
|
318
|
+
var(2, 3, 4)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
>>> var.values = 0, 0, 2
|
|
322
|
+
>>> assert var.trim(lower=[0, INT_NAN, 2]) is False
|
|
323
|
+
|
|
324
|
+
>>> var.values = 2, 4, 4
|
|
325
|
+
>>> assert var.trim(upper=[2, INT_NAN, 4]) is False
|
|
326
|
+
|
|
327
|
+
For |bool| values, defining outliers does not make much sense, which is why
|
|
328
|
+
function |trim| does nothing when applied to variables handling |bool| values:
|
|
329
|
+
|
|
330
|
+
>>> Var.TYPE = bool
|
|
331
|
+
>>> assert var.trim() is False
|
|
332
|
+
|
|
333
|
+
If function |trim| encounters an unmanageable type, it raises an exception like the
|
|
334
|
+
following:
|
|
335
|
+
|
|
336
|
+
>>> Var.TYPE = str
|
|
337
|
+
>>> var.trim()
|
|
338
|
+
Traceback (most recent call last):
|
|
339
|
+
...
|
|
340
|
+
NotImplementedError: Method `trim` can only be applied on parameters handling \
|
|
341
|
+
floating-point, integer, or boolean values, but the "value type" of parameter `var` \
|
|
342
|
+
is `str`.
|
|
343
|
+
|
|
344
|
+
>>> pub.options.warntrim = False
|
|
345
|
+
"""
|
|
346
|
+
if hydpy.pub.options.trimvariables:
|
|
347
|
+
if lower is None:
|
|
348
|
+
lower = self.SPAN[0]
|
|
349
|
+
if upper is None:
|
|
350
|
+
upper = self.SPAN[1]
|
|
351
|
+
type_ = getattr(self, "TYPE", float)
|
|
352
|
+
if type_ is float:
|
|
353
|
+
if self.NDIM == 0:
|
|
354
|
+
return _trim_float_0d(self, lower, upper)
|
|
355
|
+
return _trim_float_nd(self, lower, upper)
|
|
356
|
+
if type_ is int:
|
|
357
|
+
if self.NDIM == 0:
|
|
358
|
+
return _trim_int_0d(self, lower, upper)
|
|
359
|
+
return _trim_int_nd(self, lower, upper)
|
|
360
|
+
if type_ is bool:
|
|
361
|
+
return False
|
|
362
|
+
raise NotImplementedError(
|
|
363
|
+
f"Method `trim` can only be applied on parameters handling "
|
|
364
|
+
f'floating-point, integer, or boolean values, but the "value type" of '
|
|
365
|
+
f"parameter `{self.name}` is `{self.TYPE.__name__}`."
|
|
366
|
+
)
|
|
367
|
+
return False
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
def _trim_float_0d(self, lower, upper) -> bool:
|
|
371
|
+
if numpy.isnan(self.value):
|
|
372
|
+
return False
|
|
373
|
+
if (lower is None) or numpy.isnan(lower):
|
|
374
|
+
lower = -numpy.inf
|
|
375
|
+
if (upper is None) or numpy.isnan(upper):
|
|
376
|
+
upper = numpy.inf
|
|
377
|
+
if self < lower:
|
|
378
|
+
old = self.value
|
|
379
|
+
self.value = lower
|
|
380
|
+
if (old + get_tolerance(old)) < (lower - get_tolerance(lower)):
|
|
381
|
+
_warn_trim(self, oldvalue=old, newvalue=lower)
|
|
382
|
+
return True
|
|
383
|
+
elif self > upper:
|
|
384
|
+
old = self.value
|
|
385
|
+
self.value = upper
|
|
386
|
+
if (old - get_tolerance(old)) > (upper + get_tolerance(upper)):
|
|
387
|
+
_warn_trim(self, oldvalue=old, newvalue=upper)
|
|
388
|
+
return True
|
|
389
|
+
return False
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def _trim_float_nd(self, lower, upper) -> bool:
|
|
393
|
+
values = self.values
|
|
394
|
+
shape = values.shape
|
|
395
|
+
if lower is None:
|
|
396
|
+
lower = -numpy.inf
|
|
397
|
+
lower = numpy.full(shape, lower, dtype=config.NP_FLOAT)
|
|
398
|
+
lower[numpy.where(numpy.isnan(lower))] = -numpy.inf
|
|
399
|
+
if upper is None:
|
|
400
|
+
upper = numpy.inf
|
|
401
|
+
upper = numpy.full(shape, upper, dtype=config.NP_FLOAT)
|
|
402
|
+
upper[numpy.where(numpy.isnan(upper))] = numpy.inf
|
|
403
|
+
idxs = numpy.isnan(values)
|
|
404
|
+
try:
|
|
405
|
+
values[idxs] = lower[idxs]
|
|
406
|
+
if numpy.any(values < lower) or numpy.any(values > upper):
|
|
407
|
+
old = values.copy()
|
|
408
|
+
trimmed = numpy.clip(values, lower, upper)
|
|
409
|
+
values[:] = trimmed
|
|
410
|
+
if numpy.any(
|
|
411
|
+
(old + get_tolerance(old)) < (lower - get_tolerance(lower))
|
|
412
|
+
) or numpy.any((old - get_tolerance(old)) > (upper + get_tolerance(upper))):
|
|
413
|
+
_warn_trim(self, oldvalue=old, newvalue=trimmed)
|
|
414
|
+
return True
|
|
415
|
+
return False
|
|
416
|
+
finally:
|
|
417
|
+
values[idxs] = numpy.nan
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def _trim_int_0d(self, lower, upper) -> bool:
|
|
421
|
+
if lower is None:
|
|
422
|
+
lower = INT_NAN
|
|
423
|
+
if (upper is None) or (upper == INT_NAN):
|
|
424
|
+
upper = -INT_NAN
|
|
425
|
+
if (self != INT_NAN) and ((self < lower) or (self > upper)):
|
|
426
|
+
raise ValueError(
|
|
427
|
+
f"The value `{self.value}` of parameter "
|
|
428
|
+
f"{objecttools.elementphrase(self)} is not valid."
|
|
429
|
+
)
|
|
430
|
+
return False
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def _trim_int_nd(self, lower, upper) -> bool:
|
|
434
|
+
if lower is None:
|
|
435
|
+
lower = INT_NAN
|
|
436
|
+
lower = numpy.full(self.shape, lower, dtype=config.NP_INT)
|
|
437
|
+
if upper is None:
|
|
438
|
+
upper = -INT_NAN
|
|
439
|
+
upper = numpy.full(self.shape, upper, dtype=config.NP_INT)
|
|
440
|
+
upper[upper == INT_NAN] = -INT_NAN
|
|
441
|
+
idxs = numpy.where(self.values == INT_NAN)
|
|
442
|
+
try:
|
|
443
|
+
self[idxs] = lower[idxs]
|
|
444
|
+
if numpy.any(self.values < lower) or numpy.any(self.values > upper):
|
|
445
|
+
raise ValueError(
|
|
446
|
+
f"At least one value of parameter {objecttools.elementphrase(self)} "
|
|
447
|
+
f"is not valid."
|
|
448
|
+
)
|
|
449
|
+
return False
|
|
450
|
+
finally:
|
|
451
|
+
self[idxs] = INT_NAN
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
def get_tolerance(values):
|
|
455
|
+
"""Return some "numerical accuracy" to be expected for the given floating-point
|
|
456
|
+
value(s).
|
|
457
|
+
|
|
458
|
+
The documentation on function |trim| explains also function |get_tolerance|.
|
|
459
|
+
However, note the special case of infinite input values, for which function
|
|
460
|
+
|get_tolerance| returns zero:
|
|
461
|
+
|
|
462
|
+
>>> from hydpy.core.variabletools import get_tolerance
|
|
463
|
+
>>> import numpy
|
|
464
|
+
>>> get_tolerance(numpy.inf)
|
|
465
|
+
0.0
|
|
466
|
+
>>> from hydpy import round_
|
|
467
|
+
>>> round_(get_tolerance(
|
|
468
|
+
... numpy.array([1.0, numpy.inf, 2.0, -numpy.inf])), 16)
|
|
469
|
+
0.000000000000001, 0.0, 0.000000000000002, 0.0
|
|
470
|
+
"""
|
|
471
|
+
tolerance = numpy.abs(values * 1e-15)
|
|
472
|
+
if hasattr(tolerance, "__setitem__"):
|
|
473
|
+
tolerance[numpy.isinf(tolerance)] = 0.0
|
|
474
|
+
elif numpy.isinf(tolerance):
|
|
475
|
+
tolerance = 0.0
|
|
476
|
+
return tolerance
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def _warn_trim(self, oldvalue, newvalue):
|
|
480
|
+
if hydpy.pub.options.warntrim:
|
|
481
|
+
warnings.warn(
|
|
482
|
+
f"For variable {objecttools.devicephrase(self)} at least one value "
|
|
483
|
+
f"needed to be trimmed. The old and the new value(s) are "
|
|
484
|
+
f"`{objecttools.repr_numbers(oldvalue)}` and "
|
|
485
|
+
f"`{objecttools.repr_numbers(newvalue)}`, respectively."
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def combine_arrays_to_lower_or_upper_bound(
|
|
490
|
+
*arrays: NDArrayFloat | None, lower: bool
|
|
491
|
+
) -> NDArrayFloat | None:
|
|
492
|
+
"""Helper function for parameter-specific trimming functions that collects all
|
|
493
|
+
available lower or upper bound values.
|
|
494
|
+
|
|
495
|
+
See function |sw1d_control.BottomLowWaterThreshold.trim| of class
|
|
496
|
+
|sw1d_control.BottomLowWaterThreshold| for an example.
|
|
497
|
+
"""
|
|
498
|
+
result: NDArrayFloat | None = None
|
|
499
|
+
for array in arrays:
|
|
500
|
+
if (array is not None) and (array.ndim > 0):
|
|
501
|
+
if result is None:
|
|
502
|
+
result = array
|
|
503
|
+
elif lower:
|
|
504
|
+
result = numpy.maximum(result, array)
|
|
505
|
+
else:
|
|
506
|
+
result = numpy.minimum(result, array)
|
|
507
|
+
return result
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
class FastAccess:
|
|
511
|
+
"""Used as a surrogate for typed Cython classes handling parameters or sequences
|
|
512
|
+
when working in pure Python mode."""
|
|
513
|
+
|
|
514
|
+
def _get_attribute(self, name, suffix, default=None):
|
|
515
|
+
return getattr(self, f"_{name}_{suffix}", default)
|
|
516
|
+
|
|
517
|
+
def _set_attribute(self, name, suffix, value):
|
|
518
|
+
return setattr(self, f"_{name}_{suffix}", value)
|
|
519
|
+
|
|
520
|
+
def __iter__(self):
|
|
521
|
+
"""Iterate over all sequence names."""
|
|
522
|
+
for key in vars(self).keys():
|
|
523
|
+
if not key.startswith("_"):
|
|
524
|
+
yield key
|
|
525
|
+
|
|
526
|
+
# ToDo: Replace this hack with a Mypy plugin?
|
|
527
|
+
def __getattr__(self, item: str) -> Any:
|
|
528
|
+
assert False
|
|
529
|
+
|
|
530
|
+
del __getattr__
|
|
531
|
+
|
|
532
|
+
def __setattr__(self, key: str, value: Any) -> None:
|
|
533
|
+
assert False
|
|
534
|
+
|
|
535
|
+
del __setattr__
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
class Variable:
|
|
539
|
+
"""Base class for |Parameter| and |Sequence_|.
|
|
540
|
+
|
|
541
|
+
The subclasses are required to provide the class attributes `NDIM`
|
|
542
|
+
and `TYPE`, defining the dimensionality and the type of the values
|
|
543
|
+
to be handled by the subclass, respectively. Class attribute `INIT`
|
|
544
|
+
is optional and should provide a suitable default value.
|
|
545
|
+
|
|
546
|
+
Class |Variable| implements methods for arithmetic calculations,
|
|
547
|
+
comparisons and type conversions. See the following examples on
|
|
548
|
+
how to do math with HydPys |Parameter| and |Sequence_| objects.
|
|
549
|
+
|
|
550
|
+
We start with demonstrating the supported mathematical operations
|
|
551
|
+
on 0-dimensional |Variable| objects handling |float| values:
|
|
552
|
+
|
|
553
|
+
>>> import numpy
|
|
554
|
+
>>> from hydpy.core.variabletools import FastAccess, Variable
|
|
555
|
+
>>> class Var(Variable):
|
|
556
|
+
... NDIM = 0
|
|
557
|
+
... TYPE = float
|
|
558
|
+
... initinfo = 0.0, False
|
|
559
|
+
... _CLS_FASTACCESS_PYTHON = FastAccess
|
|
560
|
+
>>> var = Var(None)
|
|
561
|
+
|
|
562
|
+
You can perform additions both with other |Variable| objects and
|
|
563
|
+
with ordinary number objects:
|
|
564
|
+
|
|
565
|
+
>>> var.value = 2.0
|
|
566
|
+
>>> var + var
|
|
567
|
+
4.0
|
|
568
|
+
>>> var + 3.0
|
|
569
|
+
5.0
|
|
570
|
+
>>> 4.0 + var
|
|
571
|
+
6.0
|
|
572
|
+
>>> var += 1
|
|
573
|
+
>>> var
|
|
574
|
+
var(3.0)
|
|
575
|
+
>>> var += -1.0
|
|
576
|
+
>>> var
|
|
577
|
+
var(2.0)
|
|
578
|
+
|
|
579
|
+
If something goes wrong, all math operations return errors like the following:
|
|
580
|
+
|
|
581
|
+
>>> var = Var(None)
|
|
582
|
+
>>> var + 1.0
|
|
583
|
+
Traceback (most recent call last):
|
|
584
|
+
...
|
|
585
|
+
hydpy.core.exceptiontools.AttributeNotReady: While trying to add \
|
|
586
|
+
variable `var` and `float` instance `1.0`, the following error occurred: \
|
|
587
|
+
For variable `var`, no value has been defined so far.
|
|
588
|
+
|
|
589
|
+
In general, the examples above are valid for the following binary
|
|
590
|
+
operations:
|
|
591
|
+
|
|
592
|
+
>>> var.value = 3.0
|
|
593
|
+
>>> var - 1
|
|
594
|
+
2.0
|
|
595
|
+
>>> 7.0 - var
|
|
596
|
+
4.0
|
|
597
|
+
>>> var -= 2.0
|
|
598
|
+
>>> var
|
|
599
|
+
var(1.0)
|
|
600
|
+
|
|
601
|
+
>>> var.value = 2.0
|
|
602
|
+
>>> var * 3
|
|
603
|
+
6.0
|
|
604
|
+
>>> 4.0 * var
|
|
605
|
+
8.0
|
|
606
|
+
>>> var *= 0.5
|
|
607
|
+
>>> var
|
|
608
|
+
var(1.0)
|
|
609
|
+
|
|
610
|
+
>>> var.value = 3.0
|
|
611
|
+
>>> var / 2
|
|
612
|
+
1.5
|
|
613
|
+
>>> 7.5 / var
|
|
614
|
+
2.5
|
|
615
|
+
>>> var /= 6.0
|
|
616
|
+
>>> var
|
|
617
|
+
var(0.5)
|
|
618
|
+
|
|
619
|
+
>>> var.value = 3.0
|
|
620
|
+
>>> var // 2
|
|
621
|
+
1.0
|
|
622
|
+
>>> 7.5 // var
|
|
623
|
+
2.0
|
|
624
|
+
>>> var //= 0.9
|
|
625
|
+
>>> var
|
|
626
|
+
var(3.0)
|
|
627
|
+
|
|
628
|
+
>>> var.value = 5.0
|
|
629
|
+
>>> var % 2
|
|
630
|
+
1.0
|
|
631
|
+
>>> 7.5 % var
|
|
632
|
+
2.5
|
|
633
|
+
>>> var %= 3.0
|
|
634
|
+
>>> var
|
|
635
|
+
var(2.0)
|
|
636
|
+
|
|
637
|
+
>>> var.value = 2.0
|
|
638
|
+
>>> var**3
|
|
639
|
+
8.0
|
|
640
|
+
>>> 3.0**var
|
|
641
|
+
9.0
|
|
642
|
+
>>> var **= 4.0
|
|
643
|
+
>>> var
|
|
644
|
+
var(16.0)
|
|
645
|
+
|
|
646
|
+
>>> var.value = 5.0
|
|
647
|
+
>>> divmod(var, 3)
|
|
648
|
+
(1.0, 2.0)
|
|
649
|
+
>>> divmod(13.0, var)
|
|
650
|
+
(2.0, 3.0)
|
|
651
|
+
|
|
652
|
+
Additionally, we support the following unary operations:
|
|
653
|
+
|
|
654
|
+
>>> var.values = -5.0
|
|
655
|
+
>>> +var
|
|
656
|
+
-5.0
|
|
657
|
+
>>> -var
|
|
658
|
+
5.0
|
|
659
|
+
>>> abs(var)
|
|
660
|
+
5.0
|
|
661
|
+
>>> ~var
|
|
662
|
+
-0.2
|
|
663
|
+
>>> var.value = 2.5
|
|
664
|
+
>>> import math
|
|
665
|
+
>>> math.floor(var)
|
|
666
|
+
2
|
|
667
|
+
>>> math.ceil(var)
|
|
668
|
+
3
|
|
669
|
+
>>> bool(var)
|
|
670
|
+
True
|
|
671
|
+
>>> int(var)
|
|
672
|
+
2
|
|
673
|
+
>>> float(var)
|
|
674
|
+
2.5
|
|
675
|
+
>>> var.value = 1.67
|
|
676
|
+
>>> from hydpy import round_
|
|
677
|
+
>>> round_(var.value, 1)
|
|
678
|
+
1.7
|
|
679
|
+
|
|
680
|
+
You can apply all the operations discussed above (except |float| and
|
|
681
|
+
|int|) on |Variable| objects of arbitrary dimensionality:
|
|
682
|
+
|
|
683
|
+
>>> from hydpy import print_matrix, print_vector
|
|
684
|
+
>>> Var.NDIM = 1
|
|
685
|
+
>>> Var.TYPE = float
|
|
686
|
+
>>> var.shape = (2,)
|
|
687
|
+
>>> var.values = 2.0
|
|
688
|
+
>>> print_vector(var + var)
|
|
689
|
+
4.0, 4.0
|
|
690
|
+
>>> print_vector(var + 3.0)
|
|
691
|
+
5.0, 5.0
|
|
692
|
+
>>> print_vector([4.0, 0.0] + var)
|
|
693
|
+
6.0, 2.0
|
|
694
|
+
>>> var += 1
|
|
695
|
+
>>> var
|
|
696
|
+
var(3.0, 3.0)
|
|
697
|
+
|
|
698
|
+
>>> var.values = 3.0
|
|
699
|
+
>>> print_vector(var - [1.0, 0.0])
|
|
700
|
+
2.0, 3.0
|
|
701
|
+
>>> print_vector([7.0, 0.0] - var)
|
|
702
|
+
4.0, -3.0
|
|
703
|
+
>>> var -= [2.0, 0.0]
|
|
704
|
+
>>> var
|
|
705
|
+
var(1.0, 3.0)
|
|
706
|
+
|
|
707
|
+
>>> var.values = 2.0
|
|
708
|
+
>>> print_vector(var * [3.0, 1.0])
|
|
709
|
+
6.0, 2.0
|
|
710
|
+
>>> print_vector([4.0, 1.0] * var)
|
|
711
|
+
8.0, 2.0
|
|
712
|
+
>>> var *= [0.5, 1.0]
|
|
713
|
+
>>> var
|
|
714
|
+
var(1.0, 2.0)
|
|
715
|
+
|
|
716
|
+
>>> var.values = 3.0
|
|
717
|
+
>>> print_vector(var / [2.0, 1.0])
|
|
718
|
+
1.5, 3.0
|
|
719
|
+
>>> print_vector([7.5, 3.0] / var)
|
|
720
|
+
2.5, 1.0
|
|
721
|
+
>>> var /= [6.0, 1.]
|
|
722
|
+
>>> var
|
|
723
|
+
var(0.5, 3.0)
|
|
724
|
+
|
|
725
|
+
>>> var.values = 3.0
|
|
726
|
+
>>> print_vector(var // [2.0, 1.0])
|
|
727
|
+
1.0, 3.0
|
|
728
|
+
>>> print_vector([7.5, 3.0] // var)
|
|
729
|
+
2.0, 1.0
|
|
730
|
+
>>> var //= [0.9, 1.0]
|
|
731
|
+
>>> var
|
|
732
|
+
var(3.0, 3.0)
|
|
733
|
+
|
|
734
|
+
>>> var.values = 5.0
|
|
735
|
+
>>> print_vector(var % [2.0, 5.0])
|
|
736
|
+
1.0, 0.0
|
|
737
|
+
>>> print_vector([7.5, 5.0] % var)
|
|
738
|
+
2.5, 0.0
|
|
739
|
+
>>> var %= [3.0, 5.0]
|
|
740
|
+
>>> var
|
|
741
|
+
var(2.0, 0.0)
|
|
742
|
+
|
|
743
|
+
>>> var.values = 2.0
|
|
744
|
+
>>> print_vector(var**[3.0, 1.0])
|
|
745
|
+
8.0, 2.0
|
|
746
|
+
>>> print_vector([3.0, 1.0]**var)
|
|
747
|
+
9.0, 1.0
|
|
748
|
+
>>> var **= [4.0, 1.0]
|
|
749
|
+
>>> var
|
|
750
|
+
var(16.0, 2.0)
|
|
751
|
+
|
|
752
|
+
>>> var.value = 5.0
|
|
753
|
+
>>> print_matrix(divmod(var, [3.0, 5.0]))
|
|
754
|
+
| 1.0, 1.0 |
|
|
755
|
+
| 2.0, 0.0 |
|
|
756
|
+
>>> print_matrix(divmod([13.0, 5.0], var))
|
|
757
|
+
| 2.0, 1.0 |
|
|
758
|
+
| 3.0, 0.0 |
|
|
759
|
+
|
|
760
|
+
>>> var.values = -5.0
|
|
761
|
+
>>> print_vector(+var)
|
|
762
|
+
-5.0, -5.0
|
|
763
|
+
>>> print_vector(-var)
|
|
764
|
+
5.0, 5.0
|
|
765
|
+
>>> print_vector(abs(var))
|
|
766
|
+
5.0, 5.0
|
|
767
|
+
>>> print_vector(~var)
|
|
768
|
+
-0.2, -0.2
|
|
769
|
+
>>> var.value = 2.5
|
|
770
|
+
>>> import math
|
|
771
|
+
>>> print_vector(math.floor(var))
|
|
772
|
+
2, 2
|
|
773
|
+
>>> print_vector(math.ceil(var))
|
|
774
|
+
3, 3
|
|
775
|
+
>>> var.values = 1.67
|
|
776
|
+
>>> print_vector(round(var, 1))
|
|
777
|
+
1.7, 1.7
|
|
778
|
+
>>> bool(var)
|
|
779
|
+
True
|
|
780
|
+
>>> int(var)
|
|
781
|
+
Traceback (most recent call last):
|
|
782
|
+
...
|
|
783
|
+
TypeError: The variable `var` is 1-dimensional and thus cannot be \
|
|
784
|
+
converted to a scalar int value.
|
|
785
|
+
>>> float(var)
|
|
786
|
+
Traceback (most recent call last):
|
|
787
|
+
...
|
|
788
|
+
TypeError: The variable `var` is 1-dimensional and thus cannot be \
|
|
789
|
+
converted to a scalar float value.
|
|
790
|
+
|
|
791
|
+
Indexing is supported (for consistency reasons, even for
|
|
792
|
+
0-dimensional variables):
|
|
793
|
+
|
|
794
|
+
>>> Var.NDIM = 0
|
|
795
|
+
>>> var.value = 5.0
|
|
796
|
+
>>> var[0] += var[0]
|
|
797
|
+
>>> var[:]
|
|
798
|
+
10.0
|
|
799
|
+
>>> var[1]
|
|
800
|
+
Traceback (most recent call last):
|
|
801
|
+
...
|
|
802
|
+
IndexError: While trying to access the value(s) of variable `var` \
|
|
803
|
+
with key `1`, the following error occurred: The only allowed keys for \
|
|
804
|
+
0-dimensional variables are `0` and `:`.
|
|
805
|
+
|
|
806
|
+
>>> Var.NDIM = 1
|
|
807
|
+
>>> var = Var(None)
|
|
808
|
+
>>> var.shape = (5,)
|
|
809
|
+
>>> var.value = 2.0, 4.0, 6.0, 8.0, 10.0
|
|
810
|
+
>>> round_(var[0])
|
|
811
|
+
2.0
|
|
812
|
+
>>> round_(var[-1])
|
|
813
|
+
10.0
|
|
814
|
+
>>> var[1:-1:2] = 2.0 * var[1:-1:2]
|
|
815
|
+
>>> var
|
|
816
|
+
var(2.0, 8.0, 6.0, 16.0, 10.0)
|
|
817
|
+
>>> var[:] = "test"
|
|
818
|
+
Traceback (most recent call last):
|
|
819
|
+
...
|
|
820
|
+
ValueError: While trying to set the value(s) of variable `var` \
|
|
821
|
+
with key `slice(None, None, None)`, the following error occurred: \
|
|
822
|
+
could not convert string to float: 'test'
|
|
823
|
+
|
|
824
|
+
Comparisons with |Variable| objects containing multiple values return a single
|
|
825
|
+
boolean value. Two objects are equal if all of their value pairs are equal, and
|
|
826
|
+
they are unequal if at least one of their value pairs is unequal:
|
|
827
|
+
|
|
828
|
+
>>> var.shape = (2,)
|
|
829
|
+
>>> var.values = 1.0, 3.0
|
|
830
|
+
>>> var == [0.0, 2.0], var == [1.0, 2.0], var == [1.0, 3.0]
|
|
831
|
+
(False, False, True)
|
|
832
|
+
>>> var != [0.0, 2.0], var != [1.0, 2.0], var != [1.0, 3.0]
|
|
833
|
+
(True, True, False)
|
|
834
|
+
|
|
835
|
+
While either the `==` or the `!=` operator returns `True` (but not both),
|
|
836
|
+
this must not be the case for the operator pairs `<`and `>=` as well as
|
|
837
|
+
`>` and `<=`:
|
|
838
|
+
|
|
839
|
+
>>> var < 2.0, var < 3.0, var < 4.0
|
|
840
|
+
(False, False, True)
|
|
841
|
+
>>> var <= 2.0, var <= 3.0, var <= 4.0
|
|
842
|
+
(False, True, True)
|
|
843
|
+
>>> var >= 0.0, var >= 1.0, var >= 2.0
|
|
844
|
+
(True, True, False)
|
|
845
|
+
>>> var > 0.0, var > 1.0, var > 2.0
|
|
846
|
+
(True, False, False)
|
|
847
|
+
|
|
848
|
+
Comparing wrongly shaped values does work for `==` and `!=` but
|
|
849
|
+
results in errors for the other operations:
|
|
850
|
+
|
|
851
|
+
>>> var.values = 2.0
|
|
852
|
+
>>> var == [2.0], var != [2.0]
|
|
853
|
+
(True, False)
|
|
854
|
+
>>> var == [2.0, 2.0, 2.0], var != [2.0, 2.0, 2.0]
|
|
855
|
+
(False, True)
|
|
856
|
+
>>> var < [2.0], var <= [2.0], var >= [2.0], var > [2.0]
|
|
857
|
+
(False, True, True, False)
|
|
858
|
+
>>> var < [2.0, 2.0, 2.0] # doctest: +ELLIPSIS
|
|
859
|
+
Traceback (most recent call last):
|
|
860
|
+
...
|
|
861
|
+
ValueError: While trying to compare variable `var` of element `?` \
|
|
862
|
+
with object `[2.0, 2.0, 2.0]` of type `list`, the following error occurred: \
|
|
863
|
+
operands could not be broadcast together with shapes (2,) (3,)...
|
|
864
|
+
|
|
865
|
+
You can compare different |Variable| objects directly with each other:
|
|
866
|
+
|
|
867
|
+
>>> from copy import deepcopy
|
|
868
|
+
>>> var < var, var < deepcopy(var)
|
|
869
|
+
(False, False)
|
|
870
|
+
>>> var <= var, var <= deepcopy(var)
|
|
871
|
+
(True, True)
|
|
872
|
+
>>> var == var, var == deepcopy(var)
|
|
873
|
+
(True, True)
|
|
874
|
+
>>> var != var, var != deepcopy(var)
|
|
875
|
+
(False, False)
|
|
876
|
+
>>> var >= var, var >= deepcopy(var)
|
|
877
|
+
(True, True)
|
|
878
|
+
>>> var > var, var > deepcopy(var)
|
|
879
|
+
(False, False)
|
|
880
|
+
|
|
881
|
+
When asking for impossible comparisons, |trim| raises error
|
|
882
|
+
like the following:
|
|
883
|
+
|
|
884
|
+
>>> var < "text"
|
|
885
|
+
Traceback (most recent call last):
|
|
886
|
+
...
|
|
887
|
+
TypeError: While trying to compare variable `var` of element `?` with \
|
|
888
|
+
object `text` of type `str`, the following error occurred: ufunc 'isnan' \
|
|
889
|
+
not supported for the input types, and the inputs could not be safely \
|
|
890
|
+
coerced to any supported types according to the casting rule ''safe''
|
|
891
|
+
|
|
892
|
+
Note that, in contrast to the usual |numpy| array comparison, we ignore
|
|
893
|
+
all single comparison results between two |numpy.nan| values:
|
|
894
|
+
|
|
895
|
+
>>> from numpy import nan
|
|
896
|
+
>>> var.shape = (3,)
|
|
897
|
+
>>> var.values = 1.0, 2.0, nan
|
|
898
|
+
>>> var < [2.0, 3.0, nan], var < [1.0, 2.0, nan], var < [2.0, nan, nan], \
|
|
899
|
+
var < [2.0, 3.0, 4.0]
|
|
900
|
+
(True, False, False, False)
|
|
901
|
+
>>> var <= [1.0, 3.0, nan], var <= [1.0, 1.0, nan], var <= [1.0, nan, nan], \
|
|
902
|
+
var <= [1.0, 3.0, 5.0]
|
|
903
|
+
(True, False, False, False)
|
|
904
|
+
>>> var == [1.0, 2.0, nan], var == [1.0, 1.0, nan], var == [1.0, nan, nan], \
|
|
905
|
+
var == [1.0, 2.0, 3.0]
|
|
906
|
+
(True, False, False, False)
|
|
907
|
+
>>> var != [1.0, 1.0, nan], var != [1.0, 2.0, nan], var != [1.0, nan, nan], \
|
|
908
|
+
var != [1.0, 2.0, 3.0]
|
|
909
|
+
(True, False, True, True)
|
|
910
|
+
>>> var >= [1.0, 1.0, nan], var >= [1.0, 3.0, nan], var <= [1.0, nan, nan], \
|
|
911
|
+
var <= [1.0, 3.0, 5.0]
|
|
912
|
+
(True, False, False, False)
|
|
913
|
+
>>> var > [0.0, 1.0, nan], var > [0.0, 2.0, nan], var < [0.0, nan, nan], \
|
|
914
|
+
var < [0.0, 1.0, 2.0]
|
|
915
|
+
(True, False, False, False)
|
|
916
|
+
|
|
917
|
+
Hence, when all entries of two compared objects are |numpy.nan|, we
|
|
918
|
+
consider these objects equal:
|
|
919
|
+
|
|
920
|
+
>>> var.values = nan
|
|
921
|
+
>>> var < [nan, nan, nan], var <= [nan, nan, nan], var == [nan, nan, nan], \
|
|
922
|
+
var != [nan, nan, nan], var >= [nan, nan, nan], var > [nan, nan, nan]
|
|
923
|
+
(False, True, True, False, True, False)
|
|
924
|
+
>>> Var.NDIM = 0
|
|
925
|
+
>>> var = Var(None)
|
|
926
|
+
>>> var.shape = ()
|
|
927
|
+
>>> var.value = nan
|
|
928
|
+
>>> var < nan, var <= nan, var == nan, var != nan, var >= nan, var > nan
|
|
929
|
+
(False, True, True, False, True, False)
|
|
930
|
+
|
|
931
|
+
The |len| operator does not work for 0-dimensional variables:
|
|
932
|
+
|
|
933
|
+
>>> Var.NDIM = 0
|
|
934
|
+
>>> var = Var(None)
|
|
935
|
+
>>> var.shape = ()
|
|
936
|
+
>>> len(var)
|
|
937
|
+
Traceback (most recent call last):
|
|
938
|
+
...
|
|
939
|
+
TypeError: The `len` operator was applied on `var`, but this variable is \
|
|
940
|
+
0-dimensional and thus unsized. Consider using the `numberofvalues` property instead.
|
|
941
|
+
|
|
942
|
+
For higher-dimensional variables, `len` always returns the length of the first
|
|
943
|
+
dimension:
|
|
944
|
+
|
|
945
|
+
>>> Var.NDIM = 1
|
|
946
|
+
>>> var = Var(None)
|
|
947
|
+
>>> var.shape = (5,)
|
|
948
|
+
>>> len(var)
|
|
949
|
+
5
|
|
950
|
+
>>> Var.NDIM = 3
|
|
951
|
+
>>> var = Var(None)
|
|
952
|
+
>>> var.shape = (2, 1, 4)
|
|
953
|
+
>>> len(var)
|
|
954
|
+
2
|
|
955
|
+
|
|
956
|
+
|Variable| objects are hashable based on their |id| value to avoid avoiding
|
|
957
|
+
confusion when adding different but equal objects into one |set| or |dict| object.
|
|
958
|
+
The following examples show this behaviour by making deep copies of existing
|
|
959
|
+
|Variable| objects:
|
|
960
|
+
|
|
961
|
+
>>> Var.NDIM = 0
|
|
962
|
+
>>> var1 = Var(None)
|
|
963
|
+
>>> var1.value = 5.0
|
|
964
|
+
>>> varset = set([var1])
|
|
965
|
+
>>> var1 in varset
|
|
966
|
+
True
|
|
967
|
+
>>> var1.value = 7.0
|
|
968
|
+
>>> var1 in varset
|
|
969
|
+
True
|
|
970
|
+
>>> var2 = deepcopy(var1)
|
|
971
|
+
>>> var1 == var2
|
|
972
|
+
True
|
|
973
|
+
>>> var2 in varset
|
|
974
|
+
False
|
|
975
|
+
|
|
976
|
+
>>> Var.NDIM = 1
|
|
977
|
+
>>> var1 = Var(None)
|
|
978
|
+
>>> var1.shape = (2,)
|
|
979
|
+
>>> var1.value = 3.0, 5.0
|
|
980
|
+
>>> varset = set([var1])
|
|
981
|
+
>>> var1 in varset
|
|
982
|
+
True
|
|
983
|
+
>>> var1[1] = 7.0
|
|
984
|
+
>>> var1 in varset
|
|
985
|
+
True
|
|
986
|
+
>>> var2 = deepcopy(var1)
|
|
987
|
+
>>> var1 == var2
|
|
988
|
+
True
|
|
989
|
+
>>> var2 in varset
|
|
990
|
+
False
|
|
991
|
+
|
|
992
|
+
During initialisation, each |Variable| subclass tries to extract its
|
|
993
|
+
unit from its docstring:
|
|
994
|
+
|
|
995
|
+
>>> type("Var", (Variable,), {"__doc__": "Discharge [m³/s]."}).unit
|
|
996
|
+
'm³/s'
|
|
997
|
+
|
|
998
|
+
For missing or poorly written docstrings, we set `unit` to "?":
|
|
999
|
+
|
|
1000
|
+
>>> type("Var", (Variable,), {}).unit
|
|
1001
|
+
'?'
|
|
1002
|
+
>>> type("Var", (Variable,), {"__doc__": "Discharge ]m³/s[."}).unit
|
|
1003
|
+
'?'
|
|
1004
|
+
>>> type("Var", (Variable,), {"__doc__": "Discharge m³/s]."}).unit
|
|
1005
|
+
'?'
|
|
1006
|
+
"""
|
|
1007
|
+
|
|
1008
|
+
# Subclasses need to define...
|
|
1009
|
+
NDIM: int
|
|
1010
|
+
TYPE: type[float | int | bool] # ToDo: is still `str` in some cases
|
|
1011
|
+
# ...and optionally...
|
|
1012
|
+
SPAN: tuple[int | float | bool | None, int | float | bool | None] = (None, None)
|
|
1013
|
+
INIT: int | float | bool | None = None
|
|
1014
|
+
|
|
1015
|
+
_NOT_DEEPCOPYABLE_MEMBERS: Final[frozenset[str]] = frozenset(
|
|
1016
|
+
(
|
|
1017
|
+
"subvars",
|
|
1018
|
+
"subpars",
|
|
1019
|
+
"subseqs",
|
|
1020
|
+
"fastaccess",
|
|
1021
|
+
"fastaccess_old",
|
|
1022
|
+
"fastaccess_new",
|
|
1023
|
+
)
|
|
1024
|
+
)
|
|
1025
|
+
_CLS_FASTACCESS_PYTHON: ClassVar[type[FastAccess]]
|
|
1026
|
+
|
|
1027
|
+
strict_valuehandling: bool = True
|
|
1028
|
+
|
|
1029
|
+
__hydpy__subclasscounter__ = 1
|
|
1030
|
+
|
|
1031
|
+
name: str
|
|
1032
|
+
"""Name of the variable in lowercase letters."""
|
|
1033
|
+
unit: str
|
|
1034
|
+
"""Unit of the variable."""
|
|
1035
|
+
fastaccess: FastAccess
|
|
1036
|
+
"""Object for accessing the variable's data with little overhead."""
|
|
1037
|
+
subvars: SubVariables
|
|
1038
|
+
"""The subgroup to which the variable belongs."""
|
|
1039
|
+
|
|
1040
|
+
_refweights: parametertools.Parameter | VectorFloat | None = None
|
|
1041
|
+
|
|
1042
|
+
mask = masktools.DefaultMask(
|
|
1043
|
+
doc="The standard mask used by all variables (if not overwritten)."
|
|
1044
|
+
)
|
|
1045
|
+
|
|
1046
|
+
@classmethod
|
|
1047
|
+
@contextlib.contextmanager
|
|
1048
|
+
def modify_refweights(
|
|
1049
|
+
cls, refweights: parametertools.Parameter | None
|
|
1050
|
+
) -> Generator[None, None, None]:
|
|
1051
|
+
"""Eventually, set or modify the reference to a parameter defining the
|
|
1052
|
+
weighting coefficients required for aggregating values.
|
|
1053
|
+
|
|
1054
|
+
The following example demonstrates that changes affect the relevant class only
|
|
1055
|
+
temporarily, but its objects initialised within the "with" block persistently:
|
|
1056
|
+
|
|
1057
|
+
>>> from hydpy.core.variabletools import FastAccess, Variable
|
|
1058
|
+
>>> class Var1(Variable):
|
|
1059
|
+
... initinfo = 0.0, True
|
|
1060
|
+
... _CLS_FASTACCESS_PYTHON = FastAccess
|
|
1061
|
+
>>> class Var2(Variable):
|
|
1062
|
+
... NDIM = 1
|
|
1063
|
+
... TYPE = float
|
|
1064
|
+
... initinfo = 0.0, True
|
|
1065
|
+
... _CLS_FASTACCESS_PYTHON = FastAccess
|
|
1066
|
+
>>> var2 = Var2(None)
|
|
1067
|
+
>>> var2.shape = 3
|
|
1068
|
+
>>> with Var1.modify_refweights(var2):
|
|
1069
|
+
... Var1._refweights
|
|
1070
|
+
... var1 = Var1(None)
|
|
1071
|
+
... var1.refweights
|
|
1072
|
+
var2(0.0, 0.0, 0.0)
|
|
1073
|
+
var2(0.0, 0.0, 0.0)
|
|
1074
|
+
>>> Var1._refweights
|
|
1075
|
+
>>> var1.refweights
|
|
1076
|
+
var2(0.0, 0.0, 0.0)
|
|
1077
|
+
|
|
1078
|
+
Passing |None| does not overwrite previously set references:
|
|
1079
|
+
|
|
1080
|
+
>>> Var1._refweights = var2
|
|
1081
|
+
>>> with Var1.modify_refweights(None):
|
|
1082
|
+
... Var1._refweights
|
|
1083
|
+
... var1 = Var1(None)
|
|
1084
|
+
... var1.refweights
|
|
1085
|
+
var2(0.0, 0.0, 0.0)
|
|
1086
|
+
var2(0.0, 0.0, 0.0)
|
|
1087
|
+
>>> Var1._refweights
|
|
1088
|
+
var2(0.0, 0.0, 0.0)
|
|
1089
|
+
>>> var1.refweights
|
|
1090
|
+
var2(0.0, 0.0, 0.0)
|
|
1091
|
+
"""
|
|
1092
|
+
if refweights is None:
|
|
1093
|
+
yield
|
|
1094
|
+
else:
|
|
1095
|
+
old = cls._refweights
|
|
1096
|
+
try:
|
|
1097
|
+
cls._refweights = refweights
|
|
1098
|
+
yield
|
|
1099
|
+
finally:
|
|
1100
|
+
cls._refweights = old
|
|
1101
|
+
|
|
1102
|
+
def __init__(self, subvars: SubVariables) -> None:
|
|
1103
|
+
self.subvars = subvars
|
|
1104
|
+
self.fastaccess = self._CLS_FASTACCESS_PYTHON()
|
|
1105
|
+
self._valueready = False
|
|
1106
|
+
self.__shapeready = False
|
|
1107
|
+
self._refweights = type(self)._refweights
|
|
1108
|
+
|
|
1109
|
+
def __init_subclass__(cls) -> None:
|
|
1110
|
+
super().__init_subclass__()
|
|
1111
|
+
cls.name = cls.__name__.lower()
|
|
1112
|
+
cls.unit = cls._get_unit()
|
|
1113
|
+
subclasscounter = Variable.__hydpy__subclasscounter__ + 1
|
|
1114
|
+
Variable.__hydpy__subclasscounter__ = subclasscounter
|
|
1115
|
+
cls.__hydpy__subclasscounter__ = subclasscounter
|
|
1116
|
+
|
|
1117
|
+
@classmethod
|
|
1118
|
+
def _get_unit(cls) -> str:
|
|
1119
|
+
descr = objecttools.description(cls)
|
|
1120
|
+
idx1 = descr.find("[") + 1
|
|
1121
|
+
idx2 = descr.find("]")
|
|
1122
|
+
if 0 < idx1 < idx2:
|
|
1123
|
+
return descr[idx1:idx2]
|
|
1124
|
+
return "?"
|
|
1125
|
+
|
|
1126
|
+
def __hydpy__connect_variable2subgroup__(self) -> None:
|
|
1127
|
+
"""To be called by the |SubVariables| object when preparing a
|
|
1128
|
+
new |Variable| object."""
|
|
1129
|
+
self.fastaccess = self.subvars.fastaccess
|
|
1130
|
+
self._finalise_connections()
|
|
1131
|
+
|
|
1132
|
+
def _finalise_connections(self) -> None:
|
|
1133
|
+
"""A hook method, called at the end of method
|
|
1134
|
+
`__hydpy__connect_variable2subgroup__` for initialising
|
|
1135
|
+
values and some related attributes."""
|
|
1136
|
+
|
|
1137
|
+
@property
|
|
1138
|
+
@abc.abstractmethod
|
|
1139
|
+
def initinfo(self) -> tuple[float | int | bool | pointerutils.Double, bool]:
|
|
1140
|
+
"""To be overridden."""
|
|
1141
|
+
|
|
1142
|
+
def __call__(self, *args) -> None:
|
|
1143
|
+
if len(args) == 1:
|
|
1144
|
+
args = args[0]
|
|
1145
|
+
self.values = args
|
|
1146
|
+
|
|
1147
|
+
def _get_value(self):
|
|
1148
|
+
"""The actual parameter or sequence value(s).
|
|
1149
|
+
|
|
1150
|
+
First, we prepare a simple (not fully functional) |Variable| subclass:
|
|
1151
|
+
|
|
1152
|
+
>>> from hydpy.core.variabletools import Variable
|
|
1153
|
+
>>> class Var(Variable):
|
|
1154
|
+
... NDIM = 0
|
|
1155
|
+
... TYPE = float
|
|
1156
|
+
... initinfo = 3.0, True
|
|
1157
|
+
... _CLS_FASTACCESS_PYTHON = FastAccess
|
|
1158
|
+
|
|
1159
|
+
Without making use of default values (see below), trying to
|
|
1160
|
+
query the actual value of a freshly initialised |Variable|
|
|
1161
|
+
object results in the following error:
|
|
1162
|
+
|
|
1163
|
+
>>> var = Var(None)
|
|
1164
|
+
>>> var.value
|
|
1165
|
+
Traceback (most recent call last):
|
|
1166
|
+
...
|
|
1167
|
+
hydpy.core.exceptiontools.AttributeNotReady: For variable `var`, \
|
|
1168
|
+
no value has been defined so far.
|
|
1169
|
+
|
|
1170
|
+
Property |Variable.value| tries to normalise assigned values and
|
|
1171
|
+
raises an error, if not possible:
|
|
1172
|
+
|
|
1173
|
+
>>> var.value = 3
|
|
1174
|
+
>>> var.value
|
|
1175
|
+
3.0
|
|
1176
|
+
|
|
1177
|
+
>>> var.value = ["2.0"]
|
|
1178
|
+
>>> var.value
|
|
1179
|
+
2.0
|
|
1180
|
+
|
|
1181
|
+
>>> var.value = 1.0, 1.0
|
|
1182
|
+
Traceback (most recent call last):
|
|
1183
|
+
...
|
|
1184
|
+
ValueError: While trying to set the value(s) of variable `var`, the \
|
|
1185
|
+
following error occurred: 2 values are assigned to the scalar variable `var`.
|
|
1186
|
+
>>> var.value
|
|
1187
|
+
2.0
|
|
1188
|
+
|
|
1189
|
+
>>> var.value = "O"
|
|
1190
|
+
Traceback (most recent call last):
|
|
1191
|
+
...
|
|
1192
|
+
TypeError: While trying to set the value(s) of variable `var`, \
|
|
1193
|
+
the following error occurred: The given value `O` cannot be converted \
|
|
1194
|
+
to type `float`.
|
|
1195
|
+
>>> var.value
|
|
1196
|
+
2.0
|
|
1197
|
+
|
|
1198
|
+
The above examples deal with a 0-dimensional variable handling
|
|
1199
|
+
|float| values. The following examples focus on a 2-dimensional
|
|
1200
|
+
variable handling |int| values:
|
|
1201
|
+
|
|
1202
|
+
>>> from hydpy import INT_NAN
|
|
1203
|
+
>>> Var.NDIM = 2
|
|
1204
|
+
>>> Var.TYPE = int
|
|
1205
|
+
>>> Var.initinfo = INT_NAN, False
|
|
1206
|
+
|
|
1207
|
+
For multidimensional objects, assigning new values required
|
|
1208
|
+
defining their |Variable.shape| first:
|
|
1209
|
+
|
|
1210
|
+
>>> var = Var(None)
|
|
1211
|
+
>>> var.value
|
|
1212
|
+
Traceback (most recent call last):
|
|
1213
|
+
...
|
|
1214
|
+
hydpy.core.exceptiontools.AttributeNotReady: Shape information for \
|
|
1215
|
+
variable `var` can only be retrieved after it has been defined.
|
|
1216
|
+
|
|
1217
|
+
>>> var.value = 2
|
|
1218
|
+
Traceback (most recent call last):
|
|
1219
|
+
...
|
|
1220
|
+
hydpy.core.exceptiontools.AttributeNotReady: While trying to set the \
|
|
1221
|
+
value(s) of variable `var`, the following error occurred: Shape information \
|
|
1222
|
+
for variable `var` can only be retrieved after it has been defined.
|
|
1223
|
+
|
|
1224
|
+
>>> var.shape = (2, 3)
|
|
1225
|
+
>>> var.value
|
|
1226
|
+
Traceback (most recent call last):
|
|
1227
|
+
...
|
|
1228
|
+
hydpy.core.exceptiontools.AttributeNotReady: For variable `var`, \
|
|
1229
|
+
no values have been defined so far.
|
|
1230
|
+
|
|
1231
|
+
>>> from hydpy import print_matrix
|
|
1232
|
+
>>> var.value = 2
|
|
1233
|
+
>>> print_matrix(var.value)
|
|
1234
|
+
| 2, 2, 2 |
|
|
1235
|
+
| 2, 2, 2 |
|
|
1236
|
+
|
|
1237
|
+
>>> var.value = 1, 2
|
|
1238
|
+
Traceback (most recent call last):
|
|
1239
|
+
...
|
|
1240
|
+
ValueError: While trying to set the value(s) of variable `var`, \
|
|
1241
|
+
the following error occurred: While trying to convert the value(s) `(1, 2)` \
|
|
1242
|
+
to a numpy ndarray with shape `(2, 3)` and type `int`, the following error \
|
|
1243
|
+
occurred: could not broadcast input array from shape (2,) into shape (2,3)
|
|
1244
|
+
>>> print_matrix(var.value)
|
|
1245
|
+
| 2, 2, 2 |
|
|
1246
|
+
| 2, 2, 2 |
|
|
1247
|
+
|
|
1248
|
+
>>> var.shape = (0, 0)
|
|
1249
|
+
>>> var.shape
|
|
1250
|
+
(0, 0)
|
|
1251
|
+
>>> var.value # doctest: +ELLIPSIS
|
|
1252
|
+
array([], shape=(0, 0), dtype=...)
|
|
1253
|
+
"""
|
|
1254
|
+
if (self.NDIM > 0) and not self.__shapeready:
|
|
1255
|
+
self._get_shape() # raise the proper error
|
|
1256
|
+
value = self._prepare_getvalue(
|
|
1257
|
+
self._valueready or not self.strict_valuehandling,
|
|
1258
|
+
getattr(self.fastaccess, self.name, None),
|
|
1259
|
+
)
|
|
1260
|
+
if value is None:
|
|
1261
|
+
substring = "values have" if self.NDIM else "value has"
|
|
1262
|
+
raise exceptiontools.AttributeNotReady(
|
|
1263
|
+
f"For variable {objecttools.devicephrase(self)}, no {substring} been "
|
|
1264
|
+
f"defined so far."
|
|
1265
|
+
)
|
|
1266
|
+
return value
|
|
1267
|
+
|
|
1268
|
+
def _set_value(self, value) -> None:
|
|
1269
|
+
try:
|
|
1270
|
+
value = self._prepare_setvalue(value)
|
|
1271
|
+
setattr(self.fastaccess, self.name, value)
|
|
1272
|
+
self._valueready = True
|
|
1273
|
+
except BaseException:
|
|
1274
|
+
objecttools.augment_excmessage(
|
|
1275
|
+
f"While trying to set the value(s) of variable "
|
|
1276
|
+
f"{objecttools.devicephrase(self)}"
|
|
1277
|
+
)
|
|
1278
|
+
|
|
1279
|
+
def _prepare_getvalue(self, readyflag: bool, value):
|
|
1280
|
+
if readyflag:
|
|
1281
|
+
if self.NDIM:
|
|
1282
|
+
return numpy.asarray(value)
|
|
1283
|
+
return self.TYPE(value)
|
|
1284
|
+
if self.NDIM and not sum(self.shape):
|
|
1285
|
+
return numpy.asarray(value)
|
|
1286
|
+
return None
|
|
1287
|
+
|
|
1288
|
+
def _prepare_setvalue(self, value):
|
|
1289
|
+
if self.NDIM:
|
|
1290
|
+
value = getattr(value, "value", value)
|
|
1291
|
+
try:
|
|
1292
|
+
value = numpy.full(
|
|
1293
|
+
self.shape, value, dtype=config.TYPES_PY2NP[self.TYPE]
|
|
1294
|
+
)
|
|
1295
|
+
except BaseException:
|
|
1296
|
+
objecttools.augment_excmessage(
|
|
1297
|
+
f"While trying to convert the value(s) `{value}` to a numpy "
|
|
1298
|
+
f"ndarray with shape `{self.shape}` and type `{self.TYPE.__name__}`"
|
|
1299
|
+
)
|
|
1300
|
+
else:
|
|
1301
|
+
if isinstance(value, Sequence):
|
|
1302
|
+
if len(value) > 1:
|
|
1303
|
+
raise ValueError(
|
|
1304
|
+
f"{len(value)} values are assigned to the scalar variable "
|
|
1305
|
+
f"{objecttools.devicephrase(self)}."
|
|
1306
|
+
)
|
|
1307
|
+
value = value[0]
|
|
1308
|
+
try:
|
|
1309
|
+
value = self.TYPE(value)
|
|
1310
|
+
except BaseException:
|
|
1311
|
+
raise TypeError(
|
|
1312
|
+
f"The given value `{value}` cannot be converted to type "
|
|
1313
|
+
f"`{self.TYPE.__name__}`."
|
|
1314
|
+
) from None
|
|
1315
|
+
return value
|
|
1316
|
+
|
|
1317
|
+
value = property(fget=_get_value, fset=_set_value)
|
|
1318
|
+
|
|
1319
|
+
@property
|
|
1320
|
+
def values(self):
|
|
1321
|
+
"""Alias for |Variable.value|."""
|
|
1322
|
+
return self._get_value()
|
|
1323
|
+
|
|
1324
|
+
@values.setter
|
|
1325
|
+
def values(self, values):
|
|
1326
|
+
self._set_value(values)
|
|
1327
|
+
|
|
1328
|
+
def _get_shape(self) -> tuple[int, ...]:
|
|
1329
|
+
"""A tuple containing the actual lengths of all dimensions.
|
|
1330
|
+
|
|
1331
|
+
Note that setting a new |Variable.shape| results in a loss of
|
|
1332
|
+
the actual |Variable.values| of the respective |Variable| object.
|
|
1333
|
+
|
|
1334
|
+
First, we prepare a simple (not fully functional) |Variable| subclass:
|
|
1335
|
+
|
|
1336
|
+
>>> from hydpy.core.variabletools import Variable
|
|
1337
|
+
>>> class Var(Variable):
|
|
1338
|
+
... NDIM = 1
|
|
1339
|
+
... TYPE = float
|
|
1340
|
+
... initinfo = 3.0, True
|
|
1341
|
+
... _CLS_FASTACCESS_PYTHON = FastAccess
|
|
1342
|
+
|
|
1343
|
+
Initially, the shape of a new |Variable| object is unknown:
|
|
1344
|
+
|
|
1345
|
+
>>> var = Var(None)
|
|
1346
|
+
>>> var.shape
|
|
1347
|
+
Traceback (most recent call last):
|
|
1348
|
+
...
|
|
1349
|
+
hydpy.core.exceptiontools.AttributeNotReady: Shape information for \
|
|
1350
|
+
variable `var` can only be retrieved after it has been defined.
|
|
1351
|
+
|
|
1352
|
+
For multidimensional objects, assigning shape information (as a
|
|
1353
|
+
|tuple| of |int| values) prepares the required array automatically.
|
|
1354
|
+
Due to the |Variable.initinfo| surrogate of our test class,
|
|
1355
|
+
the entries of this array are `3.0`:
|
|
1356
|
+
|
|
1357
|
+
>>> from hydpy import print_vector
|
|
1358
|
+
>>> var.shape = (3,)
|
|
1359
|
+
>>> var.shape
|
|
1360
|
+
(3,)
|
|
1361
|
+
>>> print_vector(var.values)
|
|
1362
|
+
3.0, 3.0, 3.0
|
|
1363
|
+
|
|
1364
|
+
For the |Variable.initinfo| flag (second |tuple| entry) being
|
|
1365
|
+
|False|, the array is still prepared but not directly accessible
|
|
1366
|
+
to the user:
|
|
1367
|
+
|
|
1368
|
+
>>> import numpy
|
|
1369
|
+
>>> Var.initinfo = numpy.nan, False
|
|
1370
|
+
>>> var = Var(None)
|
|
1371
|
+
|
|
1372
|
+
>>> var.shape = (3,)
|
|
1373
|
+
>>> var.shape
|
|
1374
|
+
(3,)
|
|
1375
|
+
>>> var.values
|
|
1376
|
+
Traceback (most recent call last):
|
|
1377
|
+
...
|
|
1378
|
+
hydpy.core.exceptiontools.AttributeNotReady: For variable `var`, no \
|
|
1379
|
+
values have been defined so far.
|
|
1380
|
+
|
|
1381
|
+
>>> print_vector(var.fastaccess.var)
|
|
1382
|
+
nan, nan, nan
|
|
1383
|
+
|
|
1384
|
+
Property |Variable.shape| tries to normalise assigned values and
|
|
1385
|
+
raises errors like the following, if not possible:
|
|
1386
|
+
|
|
1387
|
+
>>> var.shape = "x"
|
|
1388
|
+
Traceback (most recent call last):
|
|
1389
|
+
...
|
|
1390
|
+
TypeError: While trying create a new numpy ndarray for \
|
|
1391
|
+
variable `var`, the following error occurred: 'str' object cannot \
|
|
1392
|
+
be interpreted as an integer
|
|
1393
|
+
>>> from hydpy import attrready
|
|
1394
|
+
>>> attrready(var, "shape")
|
|
1395
|
+
False
|
|
1396
|
+
>>> var.fastaccess.var
|
|
1397
|
+
|
|
1398
|
+
>>> var.shape = (1,)
|
|
1399
|
+
>>> attrready(var, "shape")
|
|
1400
|
+
True
|
|
1401
|
+
|
|
1402
|
+
>>> var.shape = (2, 3)
|
|
1403
|
+
Traceback (most recent call last):
|
|
1404
|
+
...
|
|
1405
|
+
ValueError: Variable `var` is 1-dimensional, but the given \
|
|
1406
|
+
shape indicates `2` dimensions.
|
|
1407
|
+
>>> attrready(var, "shape")
|
|
1408
|
+
False
|
|
1409
|
+
>>> var.fastaccess.var
|
|
1410
|
+
|
|
1411
|
+
|
|
1412
|
+
0-dimensional |Variable| objects inform the user about their shape
|
|
1413
|
+
but do not allow to change it for obvious reasons:
|
|
1414
|
+
|
|
1415
|
+
>>> class Var(Variable):
|
|
1416
|
+
... NDIM = 0
|
|
1417
|
+
... TYPE = int
|
|
1418
|
+
... initinfo = 3, True
|
|
1419
|
+
... _CLS_FASTACCESS_PYTHON = FastAccess
|
|
1420
|
+
|
|
1421
|
+
>>> var = Var(None)
|
|
1422
|
+
>>> var.shape
|
|
1423
|
+
()
|
|
1424
|
+
>>> var.value
|
|
1425
|
+
Traceback (most recent call last):
|
|
1426
|
+
...
|
|
1427
|
+
hydpy.core.exceptiontools.AttributeNotReady: For variable `var`, \
|
|
1428
|
+
no value has been defined so far.
|
|
1429
|
+
|
|
1430
|
+
>>> var.shape = ()
|
|
1431
|
+
>>> var.shape
|
|
1432
|
+
()
|
|
1433
|
+
>>> var.value
|
|
1434
|
+
3
|
|
1435
|
+
>>> var.shape = (2,)
|
|
1436
|
+
Traceback (most recent call last):
|
|
1437
|
+
...
|
|
1438
|
+
ValueError: The shape information of 0-dimensional variables \
|
|
1439
|
+
as `var` can only be `()`, but `(2,)` is given.
|
|
1440
|
+
|
|
1441
|
+
With a |False| |Variable.initinfo| flag, the default value is
|
|
1442
|
+
still readily prepared after initialisation but not directly
|
|
1443
|
+
accessible to the user:
|
|
1444
|
+
|
|
1445
|
+
>>> from hydpy import INT_NAN
|
|
1446
|
+
>>> Var.initinfo = INT_NAN, False
|
|
1447
|
+
>>> var = Var(None)
|
|
1448
|
+
>>> var.shape
|
|
1449
|
+
()
|
|
1450
|
+
>>> var.shape = ()
|
|
1451
|
+
>>> attrready(var, "value")
|
|
1452
|
+
False
|
|
1453
|
+
>>> var.fastaccess.var
|
|
1454
|
+
-999999
|
|
1455
|
+
|
|
1456
|
+
>>> var.value = 6
|
|
1457
|
+
>>> var.value
|
|
1458
|
+
6
|
|
1459
|
+
|
|
1460
|
+
>>> var.shape = ()
|
|
1461
|
+
>>> var.fastaccess.var
|
|
1462
|
+
-999999
|
|
1463
|
+
"""
|
|
1464
|
+
if self.NDIM:
|
|
1465
|
+
if self.__shapeready:
|
|
1466
|
+
shape = getattr(self.fastaccess, self.name).shape
|
|
1467
|
+
return tuple(int(x) for x in shape)
|
|
1468
|
+
raise exceptiontools.AttributeNotReady(
|
|
1469
|
+
f"Shape information for variable {objecttools.devicephrase(self)} can "
|
|
1470
|
+
f"only be retrieved after it has been defined."
|
|
1471
|
+
)
|
|
1472
|
+
return ()
|
|
1473
|
+
|
|
1474
|
+
def _set_shape(self, shape: int | tuple[int, ...]) -> None:
|
|
1475
|
+
self._valueready = False
|
|
1476
|
+
self.__shapeready = False
|
|
1477
|
+
initvalue, initflag = self.initinfo
|
|
1478
|
+
if self.NDIM:
|
|
1479
|
+
try:
|
|
1480
|
+
array = numpy.full(
|
|
1481
|
+
shape, initvalue, dtype=config.TYPES_PY2NP[self.TYPE]
|
|
1482
|
+
)
|
|
1483
|
+
except BaseException:
|
|
1484
|
+
setattr(self.fastaccess, self.name, None)
|
|
1485
|
+
objecttools.augment_excmessage(
|
|
1486
|
+
f"While trying create a new numpy ndarray for variable "
|
|
1487
|
+
f"{objecttools.devicephrase(self)}"
|
|
1488
|
+
)
|
|
1489
|
+
if array.ndim != self.NDIM:
|
|
1490
|
+
setattr(self.fastaccess, self.name, None)
|
|
1491
|
+
raise ValueError(
|
|
1492
|
+
f"Variable {objecttools.devicephrase(self)} is "
|
|
1493
|
+
f"{self.NDIM}-dimensional, but the given "
|
|
1494
|
+
f"shape indicates `{array.ndim}` dimensions."
|
|
1495
|
+
)
|
|
1496
|
+
setattr(self.fastaccess, self.name, array)
|
|
1497
|
+
self.__shapeready = True
|
|
1498
|
+
else:
|
|
1499
|
+
if shape:
|
|
1500
|
+
setattr(self.fastaccess, self.name, TYPE2MISSINGVALUE[self.TYPE])
|
|
1501
|
+
self._raise_wrongshape(shape)
|
|
1502
|
+
setattr(self.fastaccess, self.name, initvalue)
|
|
1503
|
+
if initflag:
|
|
1504
|
+
self._valueready = True
|
|
1505
|
+
|
|
1506
|
+
shape = propertytools.Property(fget=_get_shape, fset=_set_shape)
|
|
1507
|
+
|
|
1508
|
+
def _raise_wrongshape(self, shape):
|
|
1509
|
+
raise ValueError(
|
|
1510
|
+
f"The shape information of 0-dimensional variables "
|
|
1511
|
+
f"as {objecttools.devicephrase(self)} can only be `()`, "
|
|
1512
|
+
f"but `{shape}` is given."
|
|
1513
|
+
)
|
|
1514
|
+
|
|
1515
|
+
@property
|
|
1516
|
+
def numberofvalues(self) -> int:
|
|
1517
|
+
"""The total number of values handled by the variable according to the current
|
|
1518
|
+
shape.
|
|
1519
|
+
|
|
1520
|
+
We create an incomplete |Variable| subclass for testing:
|
|
1521
|
+
|
|
1522
|
+
>>> from hydpy.core.variabletools import FastAccess, Variable
|
|
1523
|
+
>>> class Var(Variable):
|
|
1524
|
+
... TYPE = float
|
|
1525
|
+
... initinfo = 0.0, False
|
|
1526
|
+
... _CLS_FASTACCESS_PYTHON = FastAccess
|
|
1527
|
+
>>> var = Var(None)
|
|
1528
|
+
|
|
1529
|
+
0-dimensional variables always handle precisely one value:
|
|
1530
|
+
|
|
1531
|
+
>>> Var.NDIM = 0
|
|
1532
|
+
>>> var = Var(None)
|
|
1533
|
+
>>> var.shape = ()
|
|
1534
|
+
>>> var.numberofvalues
|
|
1535
|
+
1
|
|
1536
|
+
|
|
1537
|
+
For higher-dimensional variables, |Variable.numberofvalues| is the cumulative
|
|
1538
|
+
product of the individual dimensons lengths:
|
|
1539
|
+
|
|
1540
|
+
>>> Var.NDIM = 1
|
|
1541
|
+
>>> var = Var(None)
|
|
1542
|
+
>>> var.shape = (5,)
|
|
1543
|
+
>>> var.numberofvalues
|
|
1544
|
+
5
|
|
1545
|
+
>>> Var.NDIM = 3
|
|
1546
|
+
>>> var = Var(None)
|
|
1547
|
+
>>> var.shape = (2, 1, 4)
|
|
1548
|
+
>>> var.numberofvalues
|
|
1549
|
+
8
|
|
1550
|
+
|
|
1551
|
+
As long as the shape of a higher-dimensional variable is undefined,
|
|
1552
|
+
|Variable.numberofvalues| is zero:
|
|
1553
|
+
|
|
1554
|
+
>>> var = Var(None)
|
|
1555
|
+
>>> var.numberofvalues
|
|
1556
|
+
0
|
|
1557
|
+
"""
|
|
1558
|
+
if self.NDIM == 0:
|
|
1559
|
+
return 1
|
|
1560
|
+
if (shape := exceptiontools.getattr_(self, "shape", None)) is None:
|
|
1561
|
+
return 0
|
|
1562
|
+
return int(numpy.cumprod(shape)[-1])
|
|
1563
|
+
|
|
1564
|
+
def verify(self) -> None:
|
|
1565
|
+
"""Raise a |RuntimeError| if at least one of the required values of a |Variable|
|
|
1566
|
+
object is |None| or |numpy.nan|.
|
|
1567
|
+
|
|
1568
|
+
The descriptor |Variable.mask| defines which values are considered necessary.
|
|
1569
|
+
For |Variable| subclasses defining |numpy.nan| as their |Variable.INIT| value,
|
|
1570
|
+
method |Variable.verify| assumes that |numpy.nan| are not problematic.
|
|
1571
|
+
|
|
1572
|
+
Examples on a 0-dimensional |Variable|:
|
|
1573
|
+
|
|
1574
|
+
>>> from hydpy.core.variabletools import Variable
|
|
1575
|
+
>>> class Var(Variable):
|
|
1576
|
+
... NDIM = 0
|
|
1577
|
+
... TYPE = float
|
|
1578
|
+
... initinfo = 0.0, False
|
|
1579
|
+
... _CLS_FASTACCESS_PYTHON = FastAccess
|
|
1580
|
+
>>> var = Var(None)
|
|
1581
|
+
>>> import numpy
|
|
1582
|
+
>>> var.shape = ()
|
|
1583
|
+
>>> var.value = 1.0
|
|
1584
|
+
>>> var.verify()
|
|
1585
|
+
>>> var.value = numpy.nan
|
|
1586
|
+
>>> var.verify()
|
|
1587
|
+
Traceback (most recent call last):
|
|
1588
|
+
...
|
|
1589
|
+
RuntimeError: For variable `var`, 1 required value has not been set yet: \
|
|
1590
|
+
var(nan).
|
|
1591
|
+
|
|
1592
|
+
>>> var.INIT = numpy.nan
|
|
1593
|
+
>>> var.verify()
|
|
1594
|
+
|
|
1595
|
+
Examples on a 2-dimensional |Variable|:
|
|
1596
|
+
|
|
1597
|
+
>>> Var.NDIM = 2
|
|
1598
|
+
>>> var = Var(None)
|
|
1599
|
+
>>> var.shape = (2, 3)
|
|
1600
|
+
>>> var.value = numpy.ones((2,3))
|
|
1601
|
+
>>> var.value[:, 1] = numpy.nan
|
|
1602
|
+
>>> var.verify()
|
|
1603
|
+
Traceback (most recent call last):
|
|
1604
|
+
...
|
|
1605
|
+
RuntimeError: For variable `var`, 2 required values have not been set yet: \
|
|
1606
|
+
var([[1.0, nan, 1.0], [1.0, nan, 1.0]]).
|
|
1607
|
+
|
|
1608
|
+
>>> Var.mask = var.mask
|
|
1609
|
+
>>> Var.mask[0, 1] = False
|
|
1610
|
+
>>> var.verify()
|
|
1611
|
+
Traceback (most recent call last):
|
|
1612
|
+
...
|
|
1613
|
+
RuntimeError: For variable `var`, 1 required value has not been set yet: \
|
|
1614
|
+
var([[1.0, nan, 1.0], [1.0, nan, 1.0]]).
|
|
1615
|
+
|
|
1616
|
+
>>> Var.mask[1, 1] = False
|
|
1617
|
+
>>> var.verify()
|
|
1618
|
+
"""
|
|
1619
|
+
valueready = self._valueready
|
|
1620
|
+
try:
|
|
1621
|
+
self._valueready = True
|
|
1622
|
+
nmbnan: int = numpy.sum(numpy.isnan(numpy.array(self.value)[self.mask]))
|
|
1623
|
+
finally:
|
|
1624
|
+
self._valueready = valueready
|
|
1625
|
+
if nmbnan and ((self.INIT is None) or ~numpy.isnan(self.INIT)):
|
|
1626
|
+
text = "value has" if nmbnan == 1 else "values have"
|
|
1627
|
+
raise RuntimeError(
|
|
1628
|
+
f"For variable {objecttools.devicephrase(self)}, {nmbnan} required "
|
|
1629
|
+
f"{text} not been set yet: {objecttools.flatten_repr(self)}."
|
|
1630
|
+
)
|
|
1631
|
+
|
|
1632
|
+
@property
|
|
1633
|
+
def valuevector(self) -> Vector:
|
|
1634
|
+
"""The values of the actual |Variable| object, arranged in a 1-dimensional
|
|
1635
|
+
vector.
|
|
1636
|
+
|
|
1637
|
+
For a 1-dimensional variable object, property |Variable.valuevector| returns
|
|
1638
|
+
the original values without any modification:
|
|
1639
|
+
|
|
1640
|
+
>>> from hydpy.models.hland import *
|
|
1641
|
+
>>> simulationstep("1d")
|
|
1642
|
+
>>> parameterstep("1d")
|
|
1643
|
+
>>> nmbzones(3)
|
|
1644
|
+
>>> sclass(2)
|
|
1645
|
+
>>> states.sm.values = 1.0, 2.0, 3.0
|
|
1646
|
+
>>> from hydpy import print_vector
|
|
1647
|
+
>>> print_vector(states.sm.valuevector)
|
|
1648
|
+
1.0, 2.0, 3.0
|
|
1649
|
+
|
|
1650
|
+
For all other variables, |Variable.valuevector| raises the following error by
|
|
1651
|
+
default:
|
|
1652
|
+
|
|
1653
|
+
>>> states.uz.valuevector
|
|
1654
|
+
Traceback (most recent call last):
|
|
1655
|
+
...
|
|
1656
|
+
NotImplementedError: Variable `uz` does not implement a method for converting \
|
|
1657
|
+
its values to a 1-dimensional vector.
|
|
1658
|
+
|
|
1659
|
+
If considered appropriate, model developers should override
|
|
1660
|
+
|Variable.valuevector| for individual multidimensional variables, to support
|
|
1661
|
+
methods like |Variable.average_values|, which rely on 1-dimensional data. One
|
|
1662
|
+
example is the state sequence |hland_states.SP| of base model |hland|, which
|
|
1663
|
+
handles values for individual zones (second axis) and snow classes (first axis).
|
|
1664
|
+
Here we decided to let |hland_sequences.State2DSequence.valuevector| return the
|
|
1665
|
+
sums of all snow classes for each zone so that the content of the returned
|
|
1666
|
+
vector agrees with the contents of most 1-dimensional sequences of |hland|:
|
|
1667
|
+
|
|
1668
|
+
>>> states.sp = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]
|
|
1669
|
+
>>> print_vector(states.sp.valuevector)
|
|
1670
|
+
2.5, 3.5, 4.5
|
|
1671
|
+
"""
|
|
1672
|
+
if self.NDIM == 1:
|
|
1673
|
+
return self.value
|
|
1674
|
+
raise NotImplementedError(
|
|
1675
|
+
f"Variable {objecttools.devicephrase(self)} does not implement a method "
|
|
1676
|
+
f"for converting its values to a 1-dimensional vector."
|
|
1677
|
+
)
|
|
1678
|
+
|
|
1679
|
+
@property
|
|
1680
|
+
def refweights(self) -> parametertools.Parameter | VectorFloat:
|
|
1681
|
+
"""Reference to a |Parameter| object or a simple vector that defines weighting
|
|
1682
|
+
coefficients (e.g. fractional areas) for applying function
|
|
1683
|
+
|Variable.average_values|.
|
|
1684
|
+
|
|
1685
|
+
Must be overwritten by subclasses when required."""
|
|
1686
|
+
if (refweights := self._refweights) is not None:
|
|
1687
|
+
return refweights
|
|
1688
|
+
raise AttributeError(
|
|
1689
|
+
f"Variable {objecttools.devicephrase(self)} does not define any weighting "
|
|
1690
|
+
f"coefficients."
|
|
1691
|
+
)
|
|
1692
|
+
|
|
1693
|
+
def average_values(self, *args, **kwargs) -> float:
|
|
1694
|
+
"""Average the actual values of the |Variable| object.
|
|
1695
|
+
|
|
1696
|
+
For 0-dimensional |Variable| objects, the result of method
|
|
1697
|
+
|Variable.average_values| equals |Variable.value|. The following example shows
|
|
1698
|
+
this for the poorly defined class `SoilMoisture`:
|
|
1699
|
+
|
|
1700
|
+
>>> from hydpy.core.variabletools import Variable
|
|
1701
|
+
>>> class SoilMoisture(Variable):
|
|
1702
|
+
... NDIM = 0
|
|
1703
|
+
... TYPE = float
|
|
1704
|
+
... refweigths = None
|
|
1705
|
+
... availablemasks = None
|
|
1706
|
+
... initinfo = None
|
|
1707
|
+
... _CLS_FASTACCESS_PYTHON = FastAccess
|
|
1708
|
+
... value = 200.0
|
|
1709
|
+
>>> sm = SoilMoisture(None)
|
|
1710
|
+
>>> sm.average_values()
|
|
1711
|
+
200.0
|
|
1712
|
+
|
|
1713
|
+
When the dimensionality of this class is increased to one, applying method
|
|
1714
|
+
|Variable.average_values| results in the following error:
|
|
1715
|
+
|
|
1716
|
+
>>> SoilMoisture.NDIM = 1
|
|
1717
|
+
>>> import numpy
|
|
1718
|
+
>>> SoilMoisture.shape = (3,)
|
|
1719
|
+
>>> SoilMoisture.value = numpy.array([200.0, 400.0, 500.0])
|
|
1720
|
+
>>> sm.average_values()
|
|
1721
|
+
Traceback (most recent call last):
|
|
1722
|
+
...
|
|
1723
|
+
AttributeError: While trying to calculate the mean value of variable \
|
|
1724
|
+
`soilmoisture`, the following error occurred: Variable `soilmoisture` does not define \
|
|
1725
|
+
any weighting coefficients.
|
|
1726
|
+
|
|
1727
|
+
So model developers have to define another (in this case 1-dimensional)
|
|
1728
|
+
|Variable| subclass (usually a |Parameter| subclass) and make the relevant
|
|
1729
|
+
object available via property |Variable.refweights|:
|
|
1730
|
+
|
|
1731
|
+
>>> class Area(Variable):
|
|
1732
|
+
... NDIM = 1
|
|
1733
|
+
... shape = (3,)
|
|
1734
|
+
... value = numpy.array([1.0, 1.0, 2.0])
|
|
1735
|
+
... initinfo = None
|
|
1736
|
+
... _CLS_FASTACCESS_PYTHON = FastAccess
|
|
1737
|
+
>>> area = Area(None)
|
|
1738
|
+
>>> SoilMoisture.refweights = property(lambda self: area)
|
|
1739
|
+
>>> sm.average_values()
|
|
1740
|
+
400.0
|
|
1741
|
+
|
|
1742
|
+
In the examples above, all single entries of `values` are relevant, which is
|
|
1743
|
+
the default case. However, subclasses of |Variable| can define an alternative
|
|
1744
|
+
mask, allowing to make some entries irrelevant. Assume, for example, that our
|
|
1745
|
+
`SoilMoisture` object contains three single values, each one associated with a
|
|
1746
|
+
specific hydrological response unit (hru). To indicate that soil moisture is
|
|
1747
|
+
undefined for the third unit (maybe because it is a water area), we set the
|
|
1748
|
+
third entry of the verification mask to |False|:
|
|
1749
|
+
|
|
1750
|
+
>>> from hydpy.core.masktools import DefaultMask
|
|
1751
|
+
>>> class Soil(DefaultMask):
|
|
1752
|
+
... @classmethod
|
|
1753
|
+
... def new(cls, variable, **kwargs):
|
|
1754
|
+
... return cls.array2mask([True, True, False])
|
|
1755
|
+
>>> SoilMoisture.mask = Soil()
|
|
1756
|
+
>>> sm.average_values()
|
|
1757
|
+
300.0
|
|
1758
|
+
|
|
1759
|
+
Alternatively, method |Variable.average_values| accepts additional masking
|
|
1760
|
+
information as positional or keyword arguments. Therefore, the corresponding
|
|
1761
|
+
model must implement some alternative masks, which are provided by property
|
|
1762
|
+
|Variable.availablemasks|. We mock this property with a new |Masks| object,
|
|
1763
|
+
handling one mask for flat soils (only the first hru), one mask for deep soils
|
|
1764
|
+
(only the second hru), and one mask for water areas (only the third hru):
|
|
1765
|
+
|
|
1766
|
+
>>> class FlatSoil(DefaultMask):
|
|
1767
|
+
... @classmethod
|
|
1768
|
+
... def new(cls, variable, **kwargs):
|
|
1769
|
+
... return cls.array2mask([True, False, False])
|
|
1770
|
+
>>> class DeepSoil(DefaultMask):
|
|
1771
|
+
... @classmethod
|
|
1772
|
+
... def new(cls, variable, **kwargs):
|
|
1773
|
+
... return cls.array2mask([False, True, False])
|
|
1774
|
+
>>> class Water(DefaultMask):
|
|
1775
|
+
... @classmethod
|
|
1776
|
+
... def new(cls, variable, **kwargs):
|
|
1777
|
+
... return cls.array2mask([False, False, True])
|
|
1778
|
+
>>> from hydpy.core import masktools
|
|
1779
|
+
>>> class Masks(masktools.Masks):
|
|
1780
|
+
... CLASSES = (FlatSoil,
|
|
1781
|
+
... DeepSoil,
|
|
1782
|
+
... Water)
|
|
1783
|
+
>>> SoilMoisture.availablemasks = Masks()
|
|
1784
|
+
|
|
1785
|
+
One can pass either the mask classes themselves or their names:
|
|
1786
|
+
|
|
1787
|
+
>>> sm.average_values(sm.availablemasks.flatsoil)
|
|
1788
|
+
200.0
|
|
1789
|
+
>>> sm.average_values("deepsoil")
|
|
1790
|
+
400.0
|
|
1791
|
+
|
|
1792
|
+
Both variants can be combined:
|
|
1793
|
+
|
|
1794
|
+
>>> sm.average_values(sm.availablemasks.deepsoil, "flatsoil")
|
|
1795
|
+
300.0
|
|
1796
|
+
|
|
1797
|
+
The following error happens if the general mask of the variable
|
|
1798
|
+
does not contain the given masks:
|
|
1799
|
+
|
|
1800
|
+
>>> sm.average_values("flatsoil", "water")
|
|
1801
|
+
Traceback (most recent call last):
|
|
1802
|
+
...
|
|
1803
|
+
ValueError: While trying to calculate the mean value of variable \
|
|
1804
|
+
`soilmoisture`, the following error occurred: Based on the arguments \
|
|
1805
|
+
`('flatsoil', 'water')` and `{}` the mask `CustomMask([ True, False, True])` \
|
|
1806
|
+
has been determined, which is not a submask of `Soil([ True, True, False])`.
|
|
1807
|
+
|
|
1808
|
+
Applying masks with custom options is also supported. One can change the
|
|
1809
|
+
behaviour of the following mask via the argument `complete`:
|
|
1810
|
+
|
|
1811
|
+
>>> class AllOrNothing(DefaultMask):
|
|
1812
|
+
... @classmethod
|
|
1813
|
+
... def new(cls, variable, complete):
|
|
1814
|
+
... if complete:
|
|
1815
|
+
... bools = [True, True, True]
|
|
1816
|
+
... else:
|
|
1817
|
+
... bools = [False, False, False]
|
|
1818
|
+
... return cls.array2mask(bools)
|
|
1819
|
+
>>> class Masks(Masks):
|
|
1820
|
+
... CLASSES = (FlatSoil,
|
|
1821
|
+
... DeepSoil,
|
|
1822
|
+
... Water,
|
|
1823
|
+
... AllOrNothing)
|
|
1824
|
+
>>> SoilMoisture.availablemasks = Masks()
|
|
1825
|
+
|
|
1826
|
+
Again, one can apply the mask class directly (but note that one has to pass the
|
|
1827
|
+
relevant variable as the first argument):
|
|
1828
|
+
|
|
1829
|
+
>>> sm.average_values( # doctest: +ELLIPSIS
|
|
1830
|
+
... sm.availablemasks.allornothing(sm, complete=True))
|
|
1831
|
+
Traceback (most recent call last):
|
|
1832
|
+
...
|
|
1833
|
+
ValueError: While trying to...
|
|
1834
|
+
|
|
1835
|
+
Alternatively, one can pass the mask name as a keyword and pack the mask's
|
|
1836
|
+
options into a |dict| object:
|
|
1837
|
+
|
|
1838
|
+
>>> sm.average_values(allornothing={"complete": False})
|
|
1839
|
+
nan
|
|
1840
|
+
|
|
1841
|
+
You can combine all variants explained above:
|
|
1842
|
+
|
|
1843
|
+
>>> sm.average_values("deepsoil", flatsoil={}, allornothing={"complete": False})
|
|
1844
|
+
300.0
|
|
1845
|
+
"""
|
|
1846
|
+
try:
|
|
1847
|
+
if self.NDIM == 0:
|
|
1848
|
+
return self.value
|
|
1849
|
+
mask = self.get_submask(*args, **kwargs)
|
|
1850
|
+
if numpy.any(mask):
|
|
1851
|
+
weights = self.refweights[mask]
|
|
1852
|
+
values = self.valuevector[mask]
|
|
1853
|
+
return float(numpy.sum(weights * values) / numpy.sum(weights))
|
|
1854
|
+
return numpy.nan
|
|
1855
|
+
except BaseException:
|
|
1856
|
+
objecttools.augment_excmessage(
|
|
1857
|
+
f"While trying to calculate the mean value of variable "
|
|
1858
|
+
f"{objecttools.devicephrase(self)}"
|
|
1859
|
+
)
|
|
1860
|
+
|
|
1861
|
+
@property
|
|
1862
|
+
def availablemasks(self) -> masktools.Masks:
|
|
1863
|
+
"""For |ModelSequence| objects, a |Masks| object provided by the corresponding
|
|
1864
|
+
|Model| object; for |NodeSequence| object, a suitable |DefaultMask|.
|
|
1865
|
+
|
|
1866
|
+
>>> from hydpy.core.testtools import prepare_full_example_2
|
|
1867
|
+
>>> hp, pub, TestIO = prepare_full_example_2()
|
|
1868
|
+
|
|
1869
|
+
>>> hp.elements["land_dill_assl"].model.parameters.control.fc.availablemasks
|
|
1870
|
+
complete of module hydpy.models.hland.hland_masks
|
|
1871
|
+
land of module hydpy.models.hland.hland_masks
|
|
1872
|
+
upperzone of module hydpy.models.hland.hland_masks
|
|
1873
|
+
snow of module hydpy.models.hland.hland_masks
|
|
1874
|
+
soil of module hydpy.models.hland.hland_masks
|
|
1875
|
+
field of module hydpy.models.hland.hland_masks
|
|
1876
|
+
forest of module hydpy.models.hland.hland_masks
|
|
1877
|
+
ilake of module hydpy.models.hland.hland_masks
|
|
1878
|
+
glacier of module hydpy.models.hland.hland_masks
|
|
1879
|
+
sealed of module hydpy.models.hland.hland_masks
|
|
1880
|
+
noglacier of module hydpy.models.hland.hland_masks
|
|
1881
|
+
|
|
1882
|
+
>>> hp.nodes.dill_assl.sequences.sim.availablemasks
|
|
1883
|
+
defaultmask of module hydpy.core.masktools
|
|
1884
|
+
"""
|
|
1885
|
+
model = getattr(self.subvars.vars, "model", None)
|
|
1886
|
+
if model:
|
|
1887
|
+
return model.masks
|
|
1888
|
+
return self.subvars.vars.masks
|
|
1889
|
+
|
|
1890
|
+
def get_submask(
|
|
1891
|
+
self, *args, **kwargs
|
|
1892
|
+
) -> masktools.CustomMask | masktools.DefaultMask:
|
|
1893
|
+
"""Get a sub-mask of the mask handled by the actual |Variable| object based on
|
|
1894
|
+
the given arguments.
|
|
1895
|
+
|
|
1896
|
+
See the documentation on method |Variable.average_values| for further
|
|
1897
|
+
information.
|
|
1898
|
+
"""
|
|
1899
|
+
if args or kwargs:
|
|
1900
|
+
masks = self.availablemasks
|
|
1901
|
+
mask = masktools.CustomMask(numpy.full(self.shape, False))
|
|
1902
|
+
for arg in args:
|
|
1903
|
+
mask = mask + self._prepare_mask(arg, masks)
|
|
1904
|
+
for key, value in kwargs.items():
|
|
1905
|
+
mask = mask + self._prepare_mask(key, masks, **value)
|
|
1906
|
+
if mask not in self.mask:
|
|
1907
|
+
raise ValueError(
|
|
1908
|
+
f"Based on the arguments `{args}` and `{kwargs}` the mask "
|
|
1909
|
+
f"`{repr(mask)}` has been determined, which is not a submask of "
|
|
1910
|
+
f"`{repr(self.mask)}`."
|
|
1911
|
+
)
|
|
1912
|
+
return mask
|
|
1913
|
+
return self.mask
|
|
1914
|
+
|
|
1915
|
+
def _prepare_mask(self, mask, masks, **kwargs):
|
|
1916
|
+
mask = masks[mask]
|
|
1917
|
+
if inspect.isclass(mask):
|
|
1918
|
+
return mask(self, **kwargs)
|
|
1919
|
+
return mask
|
|
1920
|
+
|
|
1921
|
+
def __deepcopy__(self, memo):
|
|
1922
|
+
new = type(self)(None)
|
|
1923
|
+
for key, value in vars(self).items():
|
|
1924
|
+
if key not in self._NOT_DEEPCOPYABLE_MEMBERS:
|
|
1925
|
+
setattr(new, key, copy.deepcopy(value, memo))
|
|
1926
|
+
if self.NDIM:
|
|
1927
|
+
new.shape = self.shape
|
|
1928
|
+
new.value = self.value
|
|
1929
|
+
return new
|
|
1930
|
+
|
|
1931
|
+
def __getitem__(self, key):
|
|
1932
|
+
try:
|
|
1933
|
+
if self.NDIM:
|
|
1934
|
+
return self.value[key]
|
|
1935
|
+
self._check_key(key)
|
|
1936
|
+
return self.value
|
|
1937
|
+
except BaseException:
|
|
1938
|
+
objecttools.augment_excmessage(
|
|
1939
|
+
f"While trying to access the value(s) of variable "
|
|
1940
|
+
f"{objecttools.devicephrase(self)} with key `{key}`"
|
|
1941
|
+
)
|
|
1942
|
+
|
|
1943
|
+
def __setitem__(self, key, value):
|
|
1944
|
+
try:
|
|
1945
|
+
if self.NDIM:
|
|
1946
|
+
self.value[key] = value
|
|
1947
|
+
else:
|
|
1948
|
+
self._check_key(key)
|
|
1949
|
+
self.value = value
|
|
1950
|
+
except BaseException:
|
|
1951
|
+
objecttools.augment_excmessage(
|
|
1952
|
+
f"While trying to set the value(s) of variable "
|
|
1953
|
+
f"{objecttools.devicephrase(self)} with key `{key}`"
|
|
1954
|
+
)
|
|
1955
|
+
|
|
1956
|
+
@staticmethod
|
|
1957
|
+
def _check_key(key):
|
|
1958
|
+
if key not in (0, slice(None, None, None)):
|
|
1959
|
+
raise IndexError(
|
|
1960
|
+
"The only allowed keys for 0-dimensional variables are `0` and `:`."
|
|
1961
|
+
)
|
|
1962
|
+
|
|
1963
|
+
def __len__(self) -> int:
|
|
1964
|
+
if self.NDIM == 0:
|
|
1965
|
+
raise TypeError(
|
|
1966
|
+
f"The `len` operator was applied on {objecttools.devicephrase(self)}, "
|
|
1967
|
+
f"but this variable is 0-dimensional and thus unsized. Consider "
|
|
1968
|
+
f"using the `numberofvalues` property instead."
|
|
1969
|
+
)
|
|
1970
|
+
return self._get_shape()[0]
|
|
1971
|
+
|
|
1972
|
+
def _do_math(self, other, methodname, description):
|
|
1973
|
+
try:
|
|
1974
|
+
if hasattr(type(other), "_get_value"):
|
|
1975
|
+
value = other.value
|
|
1976
|
+
else:
|
|
1977
|
+
value = other
|
|
1978
|
+
result = getattr(self.value, methodname)(value)
|
|
1979
|
+
if (result is NotImplemented) and (not self.NDIM) and (self.TYPE is int):
|
|
1980
|
+
result = getattr(float(self.value), methodname)(value)
|
|
1981
|
+
return result
|
|
1982
|
+
except BaseException:
|
|
1983
|
+
objecttools.augment_excmessage(
|
|
1984
|
+
f"While trying to {description} variable "
|
|
1985
|
+
f"{objecttools.devicephrase(self)} and `{type(other).__name__}` "
|
|
1986
|
+
f"instance `{objecttools.repr_(other)}`"
|
|
1987
|
+
)
|
|
1988
|
+
|
|
1989
|
+
def __add__(self, other):
|
|
1990
|
+
return self._do_math(other, "__add__", "add")
|
|
1991
|
+
|
|
1992
|
+
def __radd__(self, other):
|
|
1993
|
+
return self._do_math(other, "__radd__", "add")
|
|
1994
|
+
|
|
1995
|
+
def __iadd__(self, other):
|
|
1996
|
+
self.value = self._do_math(other, "__add__", "add")
|
|
1997
|
+
return self
|
|
1998
|
+
|
|
1999
|
+
def __sub__(self, other):
|
|
2000
|
+
return self._do_math(other, "__sub__", "subtract")
|
|
2001
|
+
|
|
2002
|
+
def __rsub__(self, other):
|
|
2003
|
+
return self._do_math(other, "__rsub__", "subtract")
|
|
2004
|
+
|
|
2005
|
+
def __isub__(self, other):
|
|
2006
|
+
self.value = self._do_math(other, "__sub__", "subtract")
|
|
2007
|
+
return self
|
|
2008
|
+
|
|
2009
|
+
def __mul__(self, other):
|
|
2010
|
+
return self._do_math(other, "__mul__", "multiply")
|
|
2011
|
+
|
|
2012
|
+
def __rmul__(self, other):
|
|
2013
|
+
return self._do_math(other, "__rmul__", "multiply")
|
|
2014
|
+
|
|
2015
|
+
def __imul__(self, other):
|
|
2016
|
+
self.value = self._do_math(other, "__mul__", "multiply")
|
|
2017
|
+
return self
|
|
2018
|
+
|
|
2019
|
+
def __truediv__(self, other):
|
|
2020
|
+
return self._do_math(other, "__truediv__", "divide")
|
|
2021
|
+
|
|
2022
|
+
def __rtruediv__(self, other):
|
|
2023
|
+
return self._do_math(other, "__rtruediv__", "divide")
|
|
2024
|
+
|
|
2025
|
+
def __itruediv__(self, other):
|
|
2026
|
+
self.value = self._do_math(other, "__truediv__", "divide")
|
|
2027
|
+
return self
|
|
2028
|
+
|
|
2029
|
+
def __floordiv__(self, other):
|
|
2030
|
+
return self._do_math(other, "__floordiv__", "floor divide")
|
|
2031
|
+
|
|
2032
|
+
def __rfloordiv__(self, other):
|
|
2033
|
+
return self._do_math(other, "__rfloordiv__", "floor divide")
|
|
2034
|
+
|
|
2035
|
+
def __ifloordiv__(self, other):
|
|
2036
|
+
self.value = self._do_math(other, "__floordiv__", "floor divide")
|
|
2037
|
+
return self
|
|
2038
|
+
|
|
2039
|
+
def __mod__(self, other):
|
|
2040
|
+
return self._do_math(other, "__mod__", "mod divide")
|
|
2041
|
+
|
|
2042
|
+
def __rmod__(self, other):
|
|
2043
|
+
return self._do_math(other, "__rmod__", "mod divide")
|
|
2044
|
+
|
|
2045
|
+
def __imod__(self, other):
|
|
2046
|
+
self.value = self._do_math(other, "__mod__", "mod divide")
|
|
2047
|
+
return self
|
|
2048
|
+
|
|
2049
|
+
def __divmod__(self, other):
|
|
2050
|
+
return self.__floordiv__(other), self.__mod__(other)
|
|
2051
|
+
|
|
2052
|
+
def __rdivmod__(self, other):
|
|
2053
|
+
return self.__rfloordiv__(other), self.__rmod__(other)
|
|
2054
|
+
|
|
2055
|
+
def __pow__(self, other):
|
|
2056
|
+
return self._do_math(other, "__pow__", "exponentiate")
|
|
2057
|
+
|
|
2058
|
+
def __rpow__(self, other):
|
|
2059
|
+
return self._do_math(other, "__rpow__", "exponentiate (reflectively)")
|
|
2060
|
+
|
|
2061
|
+
def __ipow__(self, other):
|
|
2062
|
+
self.value = self._do_math(other, "__pow__", "exponentiate")
|
|
2063
|
+
return self
|
|
2064
|
+
|
|
2065
|
+
def __pos__(self):
|
|
2066
|
+
return +self.value
|
|
2067
|
+
|
|
2068
|
+
def __neg__(self):
|
|
2069
|
+
return -self.value
|
|
2070
|
+
|
|
2071
|
+
def __abs__(self):
|
|
2072
|
+
return abs(self.value)
|
|
2073
|
+
|
|
2074
|
+
def __invert__(self):
|
|
2075
|
+
return 1.0 / self.value
|
|
2076
|
+
|
|
2077
|
+
def __floor__(self):
|
|
2078
|
+
result = self.value // 1.0
|
|
2079
|
+
try:
|
|
2080
|
+
return int(result)
|
|
2081
|
+
except TypeError:
|
|
2082
|
+
return numpy.array(result, dtype=config.NP_INT)
|
|
2083
|
+
|
|
2084
|
+
def __ceil__(self):
|
|
2085
|
+
result = numpy.ceil(self.value)
|
|
2086
|
+
try:
|
|
2087
|
+
return int(result)
|
|
2088
|
+
except TypeError:
|
|
2089
|
+
return numpy.array(result, dtype=config.NP_INT)
|
|
2090
|
+
|
|
2091
|
+
def _compare(
|
|
2092
|
+
self,
|
|
2093
|
+
other: object,
|
|
2094
|
+
comparefunc: Callable,
|
|
2095
|
+
callingfunc: Literal["lt", "le", "eq", "ne", "ge", "gt"],
|
|
2096
|
+
) -> bool:
|
|
2097
|
+
try:
|
|
2098
|
+
vs1 = self._get_value()
|
|
2099
|
+
if isinstance(other, Variable):
|
|
2100
|
+
vs2 = other._get_value() # pylint: disable=protected-access
|
|
2101
|
+
else:
|
|
2102
|
+
vs2 = numpy.asarray(other)
|
|
2103
|
+
if self.NDIM == 0:
|
|
2104
|
+
if numpy.isnan(vs1) and bool(numpy.isnan(vs2)):
|
|
2105
|
+
if callingfunc in ("le", "eq", "ge"):
|
|
2106
|
+
return True
|
|
2107
|
+
return False
|
|
2108
|
+
return comparefunc(vs1, vs2)
|
|
2109
|
+
try:
|
|
2110
|
+
idxs = ~(numpy.isnan(vs1) * numpy.isnan(vs2))
|
|
2111
|
+
except BaseException as exc:
|
|
2112
|
+
if callingfunc == "eq":
|
|
2113
|
+
return False
|
|
2114
|
+
if callingfunc == "ne":
|
|
2115
|
+
return True
|
|
2116
|
+
raise exc
|
|
2117
|
+
if numpy.sum(idxs) == 0:
|
|
2118
|
+
if callingfunc in ("le", "eq", "ge"):
|
|
2119
|
+
return True
|
|
2120
|
+
return False
|
|
2121
|
+
return comparefunc(vs1, vs2)[idxs]
|
|
2122
|
+
except BaseException:
|
|
2123
|
+
objecttools.augment_excmessage(
|
|
2124
|
+
f"While trying to compare variable {objecttools.elementphrase(self)} "
|
|
2125
|
+
f"with object `{other}` of type `{type(other).__name__}`"
|
|
2126
|
+
)
|
|
2127
|
+
|
|
2128
|
+
def __lt__(self, other: Variable | float) -> bool:
|
|
2129
|
+
return bool(
|
|
2130
|
+
numpy.all(
|
|
2131
|
+
self._compare(
|
|
2132
|
+
other=other,
|
|
2133
|
+
comparefunc=lambda vs1, vs2: vs1 < vs2,
|
|
2134
|
+
callingfunc="lt",
|
|
2135
|
+
)
|
|
2136
|
+
)
|
|
2137
|
+
)
|
|
2138
|
+
|
|
2139
|
+
def __le__(self, other: Variable | float) -> bool:
|
|
2140
|
+
return bool(
|
|
2141
|
+
numpy.all(
|
|
2142
|
+
self._compare(
|
|
2143
|
+
other=other,
|
|
2144
|
+
comparefunc=lambda vs1, vs2: vs1 <= vs2,
|
|
2145
|
+
callingfunc="le",
|
|
2146
|
+
)
|
|
2147
|
+
)
|
|
2148
|
+
)
|
|
2149
|
+
|
|
2150
|
+
def __eq__(self, other: object) -> bool:
|
|
2151
|
+
if self is other:
|
|
2152
|
+
return True
|
|
2153
|
+
return bool(
|
|
2154
|
+
numpy.all(
|
|
2155
|
+
self._compare(
|
|
2156
|
+
other=other,
|
|
2157
|
+
comparefunc=lambda vs1, vs2: vs1 == vs2,
|
|
2158
|
+
callingfunc="eq",
|
|
2159
|
+
)
|
|
2160
|
+
)
|
|
2161
|
+
)
|
|
2162
|
+
|
|
2163
|
+
def __ne__(self, other: object) -> bool:
|
|
2164
|
+
return bool(
|
|
2165
|
+
numpy.any(
|
|
2166
|
+
self._compare(
|
|
2167
|
+
other=other,
|
|
2168
|
+
comparefunc=lambda vs1, vs2: vs1 != vs2,
|
|
2169
|
+
callingfunc="ne",
|
|
2170
|
+
)
|
|
2171
|
+
)
|
|
2172
|
+
)
|
|
2173
|
+
|
|
2174
|
+
def __ge__(self, other: Variable | float) -> bool:
|
|
2175
|
+
return bool(
|
|
2176
|
+
numpy.all(
|
|
2177
|
+
self._compare(
|
|
2178
|
+
other=other,
|
|
2179
|
+
comparefunc=lambda vs1, vs2: vs1 >= vs2,
|
|
2180
|
+
callingfunc="ge",
|
|
2181
|
+
)
|
|
2182
|
+
)
|
|
2183
|
+
)
|
|
2184
|
+
|
|
2185
|
+
def __gt__(self, other: Variable | float) -> bool:
|
|
2186
|
+
return bool(
|
|
2187
|
+
numpy.all(
|
|
2188
|
+
self._compare(
|
|
2189
|
+
other=other,
|
|
2190
|
+
comparefunc=lambda vs1, vs2: vs1 > vs2,
|
|
2191
|
+
callingfunc="gt",
|
|
2192
|
+
)
|
|
2193
|
+
)
|
|
2194
|
+
)
|
|
2195
|
+
|
|
2196
|
+
def _typeconversion(self, type_):
|
|
2197
|
+
if self.NDIM:
|
|
2198
|
+
raise TypeError(
|
|
2199
|
+
f"The variable {objecttools.devicephrase(self)} is "
|
|
2200
|
+
f"{self.NDIM}-dimensional and thus cannot be converted "
|
|
2201
|
+
f"to a scalar {type_.__name__} value."
|
|
2202
|
+
)
|
|
2203
|
+
return type_(self.value)
|
|
2204
|
+
|
|
2205
|
+
def __bool__(self) -> bool:
|
|
2206
|
+
if self.NDIM == 0:
|
|
2207
|
+
return bool(self.value)
|
|
2208
|
+
return self.numberofvalues > 0
|
|
2209
|
+
|
|
2210
|
+
def __float__(self) -> float:
|
|
2211
|
+
return self._typeconversion(float)
|
|
2212
|
+
|
|
2213
|
+
def __int__(self) -> int:
|
|
2214
|
+
return self._typeconversion(int)
|
|
2215
|
+
|
|
2216
|
+
def __round__(self, ndigits: int = 0):
|
|
2217
|
+
return numpy.round(self.value, ndigits)
|
|
2218
|
+
|
|
2219
|
+
def __hash__(self) -> int:
|
|
2220
|
+
return id(self)
|
|
2221
|
+
|
|
2222
|
+
def __repr__(self) -> str:
|
|
2223
|
+
brackets = (self.NDIM == 2) and (self.shape[0] != 1)
|
|
2224
|
+
return to_repr(self, self.value, brackets)
|
|
2225
|
+
|
|
2226
|
+
|
|
2227
|
+
class MixinFixedShape:
|
|
2228
|
+
"""Mixin class for defining variables with a fixed shape."""
|
|
2229
|
+
|
|
2230
|
+
SHAPE: tuple[int, ...]
|
|
2231
|
+
name: str
|
|
2232
|
+
|
|
2233
|
+
def _finalise_connections(self) -> None:
|
|
2234
|
+
super()._finalise_connections() # type: ignore[misc]
|
|
2235
|
+
self.shape = self.SHAPE
|
|
2236
|
+
|
|
2237
|
+
def _get_shape(self) -> tuple[int, ...]:
|
|
2238
|
+
"""Variables that mix in |MixinFixedShape| are generally initialised with a
|
|
2239
|
+
fixed shape.
|
|
2240
|
+
|
|
2241
|
+
We take parameter |kinw_control.BV| of base model |kinw| and sequence
|
|
2242
|
+
|exch_factors.WaterLevels| of base model |exch| as examples:
|
|
2243
|
+
|
|
2244
|
+
>>> from hydpy import prepare_model
|
|
2245
|
+
>>> prepare_model("kinw").parameters.control.bv.shape
|
|
2246
|
+
(2,)
|
|
2247
|
+
>>> waterlevels = prepare_model("exch").sequences.factors.waterlevels
|
|
2248
|
+
>>> waterlevels.shape
|
|
2249
|
+
(2,)
|
|
2250
|
+
|
|
2251
|
+
If we try to set a new shape, |MixinFixedShape| responds with the following
|
|
2252
|
+
exceptions:
|
|
2253
|
+
|
|
2254
|
+
>>> waterlevels.shape = 2
|
|
2255
|
+
Traceback (most recent call last):
|
|
2256
|
+
...
|
|
2257
|
+
AttributeError: The shape of variable `waterlevels` cannot be changed but \
|
|
2258
|
+
this was attempted for element `?`.
|
|
2259
|
+
|
|
2260
|
+
See the documentation on property |Variable.shape| of class |Variable| for
|
|
2261
|
+
further information.
|
|
2262
|
+
"""
|
|
2263
|
+
return super()._get_shape() # type: ignore[misc]
|
|
2264
|
+
|
|
2265
|
+
def _set_shape(self, shape: int | tuple[int, ...]) -> None:
|
|
2266
|
+
oldshape = exceptiontools.getattr_(self, "shape", None)
|
|
2267
|
+
if oldshape is None:
|
|
2268
|
+
super()._set_shape(shape) # type: ignore[misc]
|
|
2269
|
+
elif shape != oldshape:
|
|
2270
|
+
raise AttributeError(
|
|
2271
|
+
f"The shape of variable `{self.name}` cannot be changed but this was "
|
|
2272
|
+
f"attempted for element `{objecttools.devicename(self)}`."
|
|
2273
|
+
)
|
|
2274
|
+
|
|
2275
|
+
shape = propertytools.Property(fget=_get_shape, fset=_set_shape)
|
|
2276
|
+
|
|
2277
|
+
|
|
2278
|
+
@overload
|
|
2279
|
+
def sort_variables(
|
|
2280
|
+
values: Iterable[type[TypeVariable_co]],
|
|
2281
|
+
) -> tuple[type[TypeVariable_co], ...]: ...
|
|
2282
|
+
|
|
2283
|
+
|
|
2284
|
+
@overload
|
|
2285
|
+
def sort_variables(
|
|
2286
|
+
values: Iterable[tuple[type[TypeVariable_co], T]],
|
|
2287
|
+
) -> tuple[tuple[type[TypeVariable_co], T], ...]: ...
|
|
2288
|
+
|
|
2289
|
+
|
|
2290
|
+
def sort_variables(
|
|
2291
|
+
values: Iterable[type[TypeVariable] | tuple[type[TypeVariable], T]],
|
|
2292
|
+
) -> tuple[type[TypeVariable] | tuple[type[TypeVariable], T], ...]:
|
|
2293
|
+
"""Sort the given |Variable| subclasses by their initialisation order.
|
|
2294
|
+
|
|
2295
|
+
When defined in one module, the initialisation order corresponds to the order
|
|
2296
|
+
within the file:
|
|
2297
|
+
|
|
2298
|
+
>>> from hydpy import classname, sort_variables
|
|
2299
|
+
>>> from hydpy.models.hland.hland_control import Area, NmbZones, ZoneType
|
|
2300
|
+
>>> from hydpy import classname
|
|
2301
|
+
>>> for var in sort_variables([NmbZones, ZoneType, Area]):
|
|
2302
|
+
... print(classname(var))
|
|
2303
|
+
Area
|
|
2304
|
+
NmbZones
|
|
2305
|
+
ZoneType
|
|
2306
|
+
|
|
2307
|
+
When defined in multiple modules, alphabetical sorting of the modules' filepaths
|
|
2308
|
+
takes priority:
|
|
2309
|
+
|
|
2310
|
+
>>> from hydpy.models.evap.evap_control import NmbHRU, ExcessReduction
|
|
2311
|
+
>>> for var in sort_variables([NmbZones, ZoneType, Area, NmbHRU, ExcessReduction]):
|
|
2312
|
+
... print(classname(var))
|
|
2313
|
+
NmbHRU
|
|
2314
|
+
ExcessReduction
|
|
2315
|
+
Area
|
|
2316
|
+
NmbZones
|
|
2317
|
+
ZoneType
|
|
2318
|
+
|
|
2319
|
+
Function |sort_variables| also supports sorting tuples. Each first entry must be
|
|
2320
|
+
a |Variable| subclass:
|
|
2321
|
+
|
|
2322
|
+
>>> for var, i in sort_variables([(NmbZones, 1), (ZoneType, 2), (Area, 3)]):
|
|
2323
|
+
... print(classname(var), i)
|
|
2324
|
+
Area 3
|
|
2325
|
+
NmbZones 1
|
|
2326
|
+
ZoneType 2
|
|
2327
|
+
|
|
2328
|
+
>>> for var, i in sort_variables([(NmbZones, 1), (ZoneType, 2), (Area, 3)]):
|
|
2329
|
+
... print(classname(var), i)
|
|
2330
|
+
Area 3
|
|
2331
|
+
NmbZones 1
|
|
2332
|
+
ZoneType 2
|
|
2333
|
+
|
|
2334
|
+
|sort_variables| does not remove duplicates:
|
|
2335
|
+
|
|
2336
|
+
>>> for var, i in sort_variables([(Area, 3), (ZoneType, 2), (Area, 1), (Area, 3)]):
|
|
2337
|
+
... print(classname(var), i)
|
|
2338
|
+
Area 1
|
|
2339
|
+
Area 3
|
|
2340
|
+
Area 3
|
|
2341
|
+
ZoneType 2
|
|
2342
|
+
"""
|
|
2343
|
+
modulepath_position_value = []
|
|
2344
|
+
for value in values:
|
|
2345
|
+
variable = value[0] if isinstance(value, tuple) else value
|
|
2346
|
+
modulepath = variable.__module__
|
|
2347
|
+
position = variable.__hydpy__subclasscounter__
|
|
2348
|
+
modulepath_position_value.append((modulepath, position, value))
|
|
2349
|
+
return tuple(value for _, _, value in sorted(modulepath_position_value))
|
|
2350
|
+
|
|
2351
|
+
|
|
2352
|
+
class SubVariables(Generic[TypeGroup_co, TypeVariable_co, TypeFastAccess_co]):
|
|
2353
|
+
"""Base class for |SubParameters| and |SubSequences|.
|
|
2354
|
+
|
|
2355
|
+
Each subclass of class |SubVariables| is thought for handling a certain group of
|
|
2356
|
+
|Parameter| or |Sequence_| objects. One specific example is subclass
|
|
2357
|
+
|sequencetools.InputSequences|, collecting all |InputSequence| objects of a
|
|
2358
|
+
specific hydrological model.
|
|
2359
|
+
|
|
2360
|
+
For the following examples, we first prepare a (not fully functional) |Variable|
|
|
2361
|
+
subclass:
|
|
2362
|
+
|
|
2363
|
+
>>> from hydpy.core.variabletools import FastAccess, SubVariables, Variable
|
|
2364
|
+
>>> class TestVar(Variable):
|
|
2365
|
+
... NDIM = 0
|
|
2366
|
+
... TYPE = float
|
|
2367
|
+
... initinfo = 0.0, False
|
|
2368
|
+
... _CLS_FASTACCESS_PYTHON = FastAccess
|
|
2369
|
+
|
|
2370
|
+
Out test |SubVariables| subclass is thought to handle only this
|
|
2371
|
+
single |Variable| subclass, indicated by putting it into the
|
|
2372
|
+
|tuple| class attribute `CLASSES`:
|
|
2373
|
+
|
|
2374
|
+
>>> class SubVars(SubVariables):
|
|
2375
|
+
... CLASSES = (TestVar,)
|
|
2376
|
+
... name = "subvars"
|
|
2377
|
+
... _CLS_FASTACCESS_PYTHON = FastAccess
|
|
2378
|
+
|
|
2379
|
+
|
|
2380
|
+
After initialisation, |SubVariables| objects reference their master object (either
|
|
2381
|
+
a |Parameters| or a |Sequences| object), passed to their constructor. However, in
|
|
2382
|
+
our simple test example, we just passed a string instead:
|
|
2383
|
+
|
|
2384
|
+
>>> subvars = SubVars("test")
|
|
2385
|
+
>>> subvars.vars
|
|
2386
|
+
'test'
|
|
2387
|
+
|
|
2388
|
+
The string representation lists all available variables and uses question marks to
|
|
2389
|
+
indicate cases where their values are not readily available:
|
|
2390
|
+
|
|
2391
|
+
>>> subvars
|
|
2392
|
+
testvar(?)
|
|
2393
|
+
|
|
2394
|
+
Class |SubVariables| provides attribute access to the handled |Variable| objects
|
|
2395
|
+
and protects |Variable| objects from accidental overwriting:
|
|
2396
|
+
|
|
2397
|
+
>>> subvars.testvar = 3.0
|
|
2398
|
+
>>> subvars.testvar
|
|
2399
|
+
testvar(3.0)
|
|
2400
|
+
|
|
2401
|
+
Trying to query not available |Variable| objects (or other attributes) results in
|
|
2402
|
+
the following error message:
|
|
2403
|
+
|
|
2404
|
+
>>> subvars.wrong
|
|
2405
|
+
Traceback (most recent call last):
|
|
2406
|
+
...
|
|
2407
|
+
AttributeError: Collection object `subvars` does neither handle a \
|
|
2408
|
+
variable nor another attribute named wrong.
|
|
2409
|
+
|
|
2410
|
+
Class |SubVariables| protects only the handled |Variable| objects from overwriting
|
|
2411
|
+
with unplausible data:
|
|
2412
|
+
|
|
2413
|
+
>>> subvars.vars = "wrong"
|
|
2414
|
+
>>> subvars.vars
|
|
2415
|
+
'wrong'
|
|
2416
|
+
|
|
2417
|
+
>>> subvars.testvar = "wrong"
|
|
2418
|
+
Traceback (most recent call last):
|
|
2419
|
+
...
|
|
2420
|
+
ValueError: While trying to set the value(s) of variable `testvar`, the following \
|
|
2421
|
+
error occurred: 5 values are assigned to the scalar variable `testvar`.
|
|
2422
|
+
|
|
2423
|
+
Alternatively, you can item-access a variable:
|
|
2424
|
+
|
|
2425
|
+
>>> subvars["testvar"]
|
|
2426
|
+
testvar(3.0)
|
|
2427
|
+
|
|
2428
|
+
>>> subvars["wrong"]
|
|
2429
|
+
Traceback (most recent call last):
|
|
2430
|
+
...
|
|
2431
|
+
AttributeError: Collection object `subvars` does not handle a variable named \
|
|
2432
|
+
`wrong`.
|
|
2433
|
+
|
|
2434
|
+
Class |SubVariables| supporte iteration and the application of the |len| operator:
|
|
2435
|
+
|
|
2436
|
+
>>> for variable in subvars:
|
|
2437
|
+
... print(variable.name)
|
|
2438
|
+
testvar
|
|
2439
|
+
>>> len(subvars)
|
|
2440
|
+
1
|
|
2441
|
+
"""
|
|
2442
|
+
|
|
2443
|
+
CLASSES: tuple[type[TypeVariable_co], ...]
|
|
2444
|
+
vars: TypeGroup_co
|
|
2445
|
+
_name2variable: dict[str, TypeVariable_co] = {}
|
|
2446
|
+
fastaccess: TypeFastAccess_co
|
|
2447
|
+
_cls_fastaccess: type[TypeFastAccess_co] | None = None
|
|
2448
|
+
_CLS_FASTACCESS_PYTHON: ClassVar[type[TypeFastAccess_co]] # type: ignore[misc]
|
|
2449
|
+
|
|
2450
|
+
def __init__(
|
|
2451
|
+
self,
|
|
2452
|
+
master: TypeGroup_co,
|
|
2453
|
+
cls_fastaccess: type[TypeFastAccess_co] | None = None,
|
|
2454
|
+
):
|
|
2455
|
+
self.vars = master
|
|
2456
|
+
if cls_fastaccess:
|
|
2457
|
+
self._cls_fastaccess = cls_fastaccess
|
|
2458
|
+
self._init_fastaccess()
|
|
2459
|
+
self._name2variable = {}
|
|
2460
|
+
for cls in self.CLASSES:
|
|
2461
|
+
variable = cls(self)
|
|
2462
|
+
self._name2variable[variable.name] = variable
|
|
2463
|
+
variable.__hydpy__connect_variable2subgroup__()
|
|
2464
|
+
|
|
2465
|
+
@property
|
|
2466
|
+
@abc.abstractmethod
|
|
2467
|
+
def name(self) -> str:
|
|
2468
|
+
"""To be overridden."""
|
|
2469
|
+
|
|
2470
|
+
@functools.cached_property
|
|
2471
|
+
def names(self) -> frozenset:
|
|
2472
|
+
"""The names of all handled variables."""
|
|
2473
|
+
return frozenset(self._name2variable)
|
|
2474
|
+
|
|
2475
|
+
def _init_fastaccess(self) -> None:
|
|
2476
|
+
"""Create a `fastaccess` attribute and build the required connections to the
|
|
2477
|
+
related cythonized model eventually."""
|
|
2478
|
+
if (self._cls_fastaccess is None) or (self._cymodel is None):
|
|
2479
|
+
self.fastaccess = self._CLS_FASTACCESS_PYTHON()
|
|
2480
|
+
else:
|
|
2481
|
+
self.fastaccess = self._cls_fastaccess()
|
|
2482
|
+
|
|
2483
|
+
def __getitem__(self, item) -> TypeVariable_co:
|
|
2484
|
+
try:
|
|
2485
|
+
return self._name2variable[item]
|
|
2486
|
+
except KeyError:
|
|
2487
|
+
raise AttributeError(
|
|
2488
|
+
f"Collection object {objecttools.devicephrase(self)} does not handle "
|
|
2489
|
+
f"a variable named `{item}`."
|
|
2490
|
+
) from None
|
|
2491
|
+
|
|
2492
|
+
def __getattr__(self, name) -> TypeVariable_co:
|
|
2493
|
+
try:
|
|
2494
|
+
return self._name2variable[name]
|
|
2495
|
+
except KeyError:
|
|
2496
|
+
raise AttributeError(
|
|
2497
|
+
f"Collection object {objecttools.devicephrase(self)} does neither "
|
|
2498
|
+
f"handle a variable nor another attribute named {name}."
|
|
2499
|
+
) from None
|
|
2500
|
+
|
|
2501
|
+
def __setattr__(self, name, value):
|
|
2502
|
+
variable = self._name2variable.get(name)
|
|
2503
|
+
if variable is None:
|
|
2504
|
+
super().__setattr__(name, value)
|
|
2505
|
+
else:
|
|
2506
|
+
variable._set_value(value)
|
|
2507
|
+
|
|
2508
|
+
def __iter__(self) -> Iterator[TypeVariable_co]:
|
|
2509
|
+
yield from self._name2variable.values()
|
|
2510
|
+
|
|
2511
|
+
def __len__(self) -> int:
|
|
2512
|
+
return len(self.CLASSES)
|
|
2513
|
+
|
|
2514
|
+
def __bool__(self) -> bool:
|
|
2515
|
+
return bool(self.CLASSES)
|
|
2516
|
+
|
|
2517
|
+
def __repr__(self) -> str:
|
|
2518
|
+
lines = []
|
|
2519
|
+
for variable in self:
|
|
2520
|
+
try:
|
|
2521
|
+
lines.append(repr(variable))
|
|
2522
|
+
except BaseException:
|
|
2523
|
+
lines.append(f"{variable.name}(?)")
|
|
2524
|
+
return "\n".join(lines)
|
|
2525
|
+
|
|
2526
|
+
def __dir__(self) -> list[str]:
|
|
2527
|
+
"""
|
|
2528
|
+
>>> from hydpy.core.variabletools import SubVariables, Variable
|
|
2529
|
+
>>> class TestVar(Variable):
|
|
2530
|
+
... NDIM = 0
|
|
2531
|
+
... TYPE = float
|
|
2532
|
+
... initinfo = 0.0, False
|
|
2533
|
+
... _CLS_FASTACCESS_PYTHON = FastAccess
|
|
2534
|
+
>>> class TestSubVars(SubVariables):
|
|
2535
|
+
... CLASSES = (TestVar,)
|
|
2536
|
+
... name = None
|
|
2537
|
+
... _CLS_FASTACCESS_PYTHON = FastAccess
|
|
2538
|
+
>>> testsubvars = TestSubVars(None)
|
|
2539
|
+
>>> sorted(set(dir(testsubvars)) - set(object.__dir__(testsubvars)))
|
|
2540
|
+
['testvar']
|
|
2541
|
+
"""
|
|
2542
|
+
return cast(list[str], super().__dir__()) + list(self._name2variable.keys())
|
|
2543
|
+
|
|
2544
|
+
|
|
2545
|
+
def to_repr(self: Variable, values, brackets: bool = False) -> str:
|
|
2546
|
+
"""Return a valid string representation for the given |Variable| object.
|
|
2547
|
+
|
|
2548
|
+
Function |to_repr| is thought for internal purposes only, more specifically for
|
|
2549
|
+
defining string representations of subclasses of class |Variable| like the
|
|
2550
|
+
following:
|
|
2551
|
+
|
|
2552
|
+
>>> from hydpy.core.variabletools import to_repr, Variable
|
|
2553
|
+
>>> class Var(Variable):
|
|
2554
|
+
... NDIM = 0
|
|
2555
|
+
... TYPE = int
|
|
2556
|
+
... initinfo = 1.0, False
|
|
2557
|
+
... _CLS_FASTACCESS_PYTHON = FastAccess
|
|
2558
|
+
>>> var = Var(None)
|
|
2559
|
+
>>> var.value = 2
|
|
2560
|
+
>>> var
|
|
2561
|
+
var(2)
|
|
2562
|
+
|
|
2563
|
+
The following examples demonstrate all covered cases. Note that option `brackets`
|
|
2564
|
+
allows choosing between a "vararg" and an "iterable" string representation for
|
|
2565
|
+
multidimensional variables:
|
|
2566
|
+
|
|
2567
|
+
>>> print(to_repr(var, 2))
|
|
2568
|
+
var(2)
|
|
2569
|
+
|
|
2570
|
+
>>> Var.NDIM = 1
|
|
2571
|
+
>>> var = Var(None)
|
|
2572
|
+
>>> var.shape = 3
|
|
2573
|
+
>>> print(to_repr(var, range(3)))
|
|
2574
|
+
var(0, 1, 2)
|
|
2575
|
+
>>> print(to_repr(var, range(3), True))
|
|
2576
|
+
var([0, 1, 2])
|
|
2577
|
+
>>> print(to_repr(var, range(30)))
|
|
2578
|
+
var(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
|
|
2579
|
+
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29)
|
|
2580
|
+
>>> print(to_repr(var, range(30), True))
|
|
2581
|
+
var([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
|
|
2582
|
+
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29])
|
|
2583
|
+
|
|
2584
|
+
>>> Var.NDIM = 2
|
|
2585
|
+
>>> var = Var(None)
|
|
2586
|
+
>>> var.shape = (2, 3)
|
|
2587
|
+
>>> print(to_repr(var, [range(3), range(3, 6)]))
|
|
2588
|
+
var(0, 1, 2,
|
|
2589
|
+
3, 4, 5)
|
|
2590
|
+
>>> print(to_repr(var, [range(3), range(3, 6)], True))
|
|
2591
|
+
var([[0, 1, 2],
|
|
2592
|
+
[3, 4, 5]])
|
|
2593
|
+
>>> print(to_repr(var, [range(30), range(30, 60)], True))
|
|
2594
|
+
var([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
|
|
2595
|
+
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
|
|
2596
|
+
[30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45,
|
|
2597
|
+
46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59]])
|
|
2598
|
+
>>> print(to_repr(var, [range(30), range(30, 60)]))
|
|
2599
|
+
var(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
|
|
2600
|
+
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
|
|
2601
|
+
30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
|
|
2602
|
+
47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59)
|
|
2603
|
+
"""
|
|
2604
|
+
prefix = f"{self.name}("
|
|
2605
|
+
if isinstance(values, str):
|
|
2606
|
+
return f"{self.name}({values})"
|
|
2607
|
+
if self.NDIM == 0:
|
|
2608
|
+
return f"{self.name}({objecttools.repr_(values)})"
|
|
2609
|
+
if self.NDIM == 1:
|
|
2610
|
+
if brackets:
|
|
2611
|
+
return objecttools.assignrepr_list(values, prefix, 72) + ")"
|
|
2612
|
+
return objecttools.assignrepr_values(values, prefix, 72) + ")"
|
|
2613
|
+
if brackets:
|
|
2614
|
+
return objecttools.assignrepr_list2(values, prefix, 72) + ")"
|
|
2615
|
+
return objecttools.assignrepr_values2(values, prefix, 72) + ")"
|