Repository navigation
Description
We need to add some functionalities from the following LARSIM options to HydPy:
- VERZ: simple branching from streams
- ERWV: extended branching from streams
- VSPE: branching from reservoirs
First thoughts (only on VERZ and ERWV for now):
VERZ seems like a simplification of ERWV. I don't think we need separate HydPy models to reflect both options.
Basically, ERWV works like HydPy-Exch-Branch-HBV96. However, it has the following additional features:
- There can be different rules either for different times of the year (e.g.
VONBIS 01.06.9999 05:00 01.07.9999 05:00) or for different concrete periods (e.g.VONBIS 01.06.2010 05:00 01.06.2020 05:00). - One can define one time series for each rule. If time series values are not missing, they define the extraction amount (and override the fixed relationship).
Defining rules for concrete periods is an interesting feature, but it's seldom required and isn't currently on our list. So, we can focus on defining rules for different times of the year (seasons). This is something we usually do with the help of the TOYParameter class.
The code for configuring HydPy-Exch-Branch-HBV96 currently looks like this:
from hydpy.models.exch_branch_hbv96 import *
parameterstep()
delta(-1.0)
minimum(-1.0)
xpoints(0.0, 2.0, 4.0, 6.0)
ypoints(output1=[0.0, 1.0, 2.0, 3.0],
output2=[0.0, 1.0, 0.0, 0.0],
output3=[0.0, 0.0, 2.0, 6.0])Delta and Minimum are very special parameters that we needed for compatibility with HBV96.
HydPy-Exch-Branch-HBV96 allows branching to an arbitrary number of places; LARSIM allows branching to only one place.
Extending all these parameters (or at least XPoints and YPoints with "time of year" (toy) functionality seems suboptimal:
xpoints(toy_06=[0.0, 2.0, 4.0, 6.0], toy_07=[0.0, 2.0, 4.0, 6.0])
ypoints(
toy_06={"output1"=[0.0, 1.0, 2.0, 3.0], "output2"=[0.0, 1.0, 0.0, 0.0], "output3"=[0.0, 0.0, 2.0, 6.0]},
toy_07={"output1"=[0.0, 1.0, 2.0, 4.0], "output2"=[0.0, 1.0, 0.0, 0.0], "output3"=[0.0, 0.0, 2.0, 6.0]},
)Keeping the data for each time of year seems preferable. For example;
from hydpy.models.exch_branch_larsim import Rule
rules(
toy_06=Rule(
[0.0, 2.0, 4.0, 6.0],
output1=[0.0, 1.0, 2.0, 3.0],
output2=[0.0, 1.0, 0.0, 0.0],
output3=[0.0, 0.0, 2.0, 6.0],
),
toy_07=Rule(
[0.0, 2.0, 4.0, 6.0],
output1=[0.0, 1.0, 2.0, 4.0],
output2=[0.0, 1.0, 0.0, 0.0],
output3=[0.0, 0.0, 2.0, 6.0],
),
)My original gut feeling was to extend HydPy-Branch-HBV96 with the new features. However, because Delta and Minimum are a little special and the API we've designed so far differs significantly, I'm now considering creating a new model, HydPy-Branch-LARSIM (working title).
Regarding the externally defined time series, I would prefer to keep things simple. LARSIM lets you assign different time series to individual rules. I want to delegate such work to preprocessing, meaning users would have to compile multiple series into one. Following the mentioned LARSIM convention, nan values in the time series mean that the currently relevant fixed rule applies.
Two problems:
- In HydPy,
nanusually indicates data that is unfortunately missing. Here, it may mean the same but could also mean "just use the standard rule here". Hence, an activated checkseries flag would often cause false positives. (Didn't we have a similar problem before?) - Users would have to prepare
nantime series, even if they only want to rely on fixed rules. To avoid this, we could create two versions of HydPy-Branch-LARSIM or allow adding a submodel that reads the time series only if desired. (I have a small tendency for the latter.)
Activity
tyralla commentedon Sep 21, 2026
Other HydPy models interpret, for example,
toy_06_01=2.0andtoy_07_01=3.0so that 2.0 applies only on each June 1 and 3.0 applies only on each July 1. The values for all other days in June are linearly interpolated. I think we should stick to this practice. This would give us flexibility, but also make defining a complete LARSIM-like, discontinuous annual cycle more laborious (assuming a simulation step of one day):This could eventually be simplified later, for example by automatically reusing equal neighbouring rules in string representations:
Or maybe we could support additional keywords:
But these steps don't seem urgent.
tyralla commentedon Sep 22, 2026
I see two simple solutions:
numpy.inf(and also-numpy.inf).I think the third option is the best because we can use
numpy.isinfto identifyinf(and-inf), and normal negative values could more easily lead to misleading analysis results (for example, when calculating averages, whereasnumpy.mean([inf, 0.0]isinf).tyralla commentedon Sep 22, 2026
I am thinking about two possible ways to refine the mentioned "Rule" idea.
Rulesas a "special" parameterThen, no
Rulesinstances would be available during simulations.Ruleswould collect information from the user only. Corresponding derived parameters would convert this information to data usable during simulation runs.We already discussed officially introducing "special" parameters, which users can access directly like "normal" control parameters, but which are clearly distinguishable from them. Currently, the inheritance tree is a little misleading, giving the impression that all already available special parameters have all the features of normal control parameters, which is not the case.
I think if we implement
Rulesfrom the ground up, it would be a perfect opportunity to introduce something like aSpecialParameterclass and deriveRulesfrom it. However, subclassing the existing special parameters could introduce breaking changes, but we could postpone that (it would be a cool feature for HydPy 7).Rulessubclassed from SeasonalInterpolatorSearching through our existing base classes, I found SeasonalInterpolator. Basically, it does what we want (interpolate for specific times of the year based on some fixed x-y relationship and then interpolate the results temporarily). However, there is a limitation regarding the number of x-y relationships per time of year. We need one input (x) and an arbitrary number of outputs (y). SeasonalInterpolator itself does not seem to be limited in this regard. However, it uses instances of the base class InterpAlgorithm to represent the x-y relationships. Currently, we have only two concrete, applicable subclasses of it. PPoly allows for linear and different kinds of piecewise polynomial interpolation, but unfortunately only for univariate output (one y value). ANN allows for arbitrary relationships and an arbitrary number of outputs, but it relies on neural network equations, which is too much effort to configure in most situations.
So we would have to implement (at least) two enhancements:
PPolyinstances (or something similar).With respect to the last point, we must keep in mind that HydPy-Exch-Branch-HBV96 actively allows water violations (see the examples above), while LARSIM likely does not (at least, if I understand the documentation correctly).
tyralla commentedon Sep 24, 2026
I currently favour this option. I have a prototype of
PPolysthat handles multiple PPoly instances. Later, one should be able to pass onePPolysinstance to a parameter derived from SeasonalInterpolator for each time of year. The following examples show my current ideas:The HydPy-Exch-Branch-LARSIM submodel receives inflow from a normal inlet node and connects to five outlet nodes named main, branch1, branch2, branch3, and supplier. This complexity is likely seldom required (or never), but it gives an impression of all possible features I currently see.
For all branches, interpolation happens piecewise linearly. From 10 m³/s on, all additional discharge is routed into the branches. For branch 1, there is linear extrapolation. The routing to branch 2 has a maximum of 2 m³/s for an inflow of 16 m³/s and falls back to zero for larger inflow values. All remaining excess inflow reaches branch 3, until its maximum capacity of 12 m³/s is reached.
The supplier node works differently. First, it sends water when inflow values are small. Second, this supply decreases nonlinearly from 2 m³/s to zero.
With respect to the three branch nodes, one could configure the release to the main river by defining another x-y-based piecewise linear relationship, without violating the water balance. However, this is error-prone and very hard, or even impossible, when working with spline interpolation, as with the supplier node. Hence, we could say that the main branch should always receive the rest, but this might not always be desired (for example, we might want to "lose" water without explicitly sending it to another node). Hence, we could tell the
PPolysinstance to always calculate the rest by itself and send it to the main node by assigning the constantPPolys.REST. This would also allow more thorough verification (for example, the sum of all y values must not exceed the corresponding x value). Maybe we would want to add other options later by adding more constants.Not that the value passed to the supplier node only depends on the inflow to the HydPy-Exch-Branch-LARSIM instance. This could be problematic if the supplier node extracts water from another model instance that in fact cannot provide enough water.
Also note that the automatic string representation of the above example looks even more complicated (but this should be fixable):
tyralla commentedon Sep 24, 2026
I could get the first preliminary doctest running with relative ease (still using
1instead ofPPolys.RESTand with fixed x-y data compared to the example mentioned above) without bumping into an unexpected problem:Many things need some love (for example, the error messages raised by
verify), but nothing seems to speak against going this way.tyralla commentedon Sep 25, 2026
I tried to put everything together quickly and dirty, and in principle, everything seems to work.
The following configuration reproduces the single integration test of HydPy-Exch-Branch-HBV96:
Adding discharge and keeping the water balance automatically works:
Defining different rules for different seasons works:
At least I didn't find any inconsistencies in the show result tables.
Before tidying up, I will add the option to extract (or add) values from external time series instead of applying the
PPolysrules.tyralla commentedon Sep 25, 2026
We had a very thorough offline discussion. I hope the following list of the decisions we made is complete:
PPolys.RESTidea (behind the scenes), so configuring it will be very easy and safe.DeltaandMinimumto HydPy-Exch-Branch-Rules. (We thought about renaming them toDeltaInputandMinimumInputfor clarity, but - if I remember correctly - this was not a final decision.)tyralla commentedon Sep 26, 2026
For VERZ and ERWV, LARSIM's documentation does not clarify what happens if the external time series (QE) requests more discharge than is currently flowing through the river (QR). I would expect one of the following behaviours:
After reading the available information on the same matter for VSPE, I think LARSIM favours option one because it considers the external time series as measured and certain. (@parvumflumen: Maybe you know better?)
However, violating the water balance equation is not always desired. Hence, we could define an option for choosing the preferred behaviour:
This parameter could also control the functionality of the fallback interpolation rule (if the given piecewise polynomials lead to such inconsistencies).
tyralla commentedon Sep 26, 2026
We considered naming the input sequence for providing the external time series
Exchange. However, this name is already taken by the outlet sequence Exchange. My new suggestions:BGWKlein commentedon Sep 28, 2026
What about RequestedTransfer?, the other proposed names somehow carry a direction.
tyralla commentedon Sep 28, 2026
This was intentional because it clarifies the meaning of positive and negative values. But admittedly, negative withdrawal and negative diversion are a little unintuitive.
RequestedTransferis much better.I also made some adjustments to the parameter names:
I think
FixWaterBalanceis much clearer thanAdjustExtraction. It's current option names are:Instead of ADJUST_BRANCH, we could use ADJUST_TRANSFER, which is much clearer, especially after naming the input sequence
RequestedTransferm. However, then ADJUST_RIVER doesn't sound right anymore. Maybe ADJUST_DISCHARGE?.The name
FallbackRulesshould clarify that the given rules only serve as a fallback. I first thought about reusingRules, but a separate parameter allows simpler configuration and more thorough automatic validation.Targetsis a special parameter, that always has the integer value two. I needed it to clarify which outlet node serves which purpose. Alternatively, we could remove this parameter and use two different outlet sequence types, so this network-configuration aspect would be handled by Node.variable. This would be more consistent with how most models connect their sequences to nodes. However, models that branch their outflow to an arbitrary number of nodes definitely need such a model-internal selection mechanism. Hence, the proposed design is somewhat consistent with HydPy-Exch-Branch-Rules (and HydPy-Exch-Branch-HBV96).Based on these considerations, my slightly modified suggestion:
10 remaining items
Deprecate `exch_branch_hbv96` while adding the transition method `con…
Add the branching model `exch_branch_io` (see issue #201).
Add the branching model `exch_branch_rules` (see issue #201).
Deprecate `exch_branch_hbv96` while adding the transition method `con…
Add the branching model `exch_branch_io` (see issue #201).
Add the branching model `exch_branch_rules` (see issue #201).
Deprecate `exch_branch_hbv96` while adding the transition method `con…
Add the branching model `exch_branch_io` (see issue #201).
Add the branching model `exch_branch_rules` (see issue #201).
Deprecate `exch_branch_hbv96` while adding the transition method `con…
tyralla commentedon Oct 11, 2026
The changes discussed here are now included in the master branch and in the documentation. The following short examples should cover our final (naming) decisions:
exch_branch_hbv96 is now deprecated and can be automatically converted to exch_branch_rules with method convert_2_exch_branch_rules. We nevertheless did not add the parameters Delta and Minimum to exch_branch_rules, because they can be simply emulated by piecewise interpolation rules (as shown in the method convert_2_exch_branch_rules and the integration test input-modifying rules).
We switched from the name
FallbackRulestoFlowTransferRulesbecause exch_water_transfer_io requires a similar but not identical fallback parameter. Since we cannot use the same name twice, we chose FlowTransferRules and VolTransferRules, which clarify the different input types.Add the branching model `exch_branch_io` (see issue #201).
Add the branching model `exch_branch_rules` (see issue #201).
Deprecate `exch_branch_hbv96` while adding the transition method `con…