Skip to content

Support LARSIM's VSPE branching option #203

Closed

Description

@tyralla

I open this subissue to discuss optional water transfers out of (or into) some HydPy-Dam models separately from #201, which focuses more on water transfers out of (or into) streams.

We initially hoped to make the main model HydPy-Exch-Branch-Ext usable as an optional submodel for some members of the HydPy-Dam model family, but now think we need a separate model. Because most of our HydPy-Dam models are ODE-based, I'm not sure it's straightforward to add this functionality as a submodel, but I am relatively confident. At least if this as has technical implementations, we should consider adding the new submodel to HydPy-Dam instead of HydPy-Exch.

Activity

  1. tyralla commented on Sep 28, 2026

    @tyralla
    MemberAuthor

    Which interface?

    The current best fits:

    1. ExchangeModel_V1: Interface for exchanging modified scalar data of arbitrary type.
    2. DischargeModel_V2: Simple interface for calculating discharge in mm/T based on the current water depth.

    Principally, "exchange" sounds more suitable than "discharge", but we are definitely dealing with m³/s here and not with arbitrary units.

    My suggestion:

    class ExchangeModel_V2(modeltools.SubmodelInterface):
        """Interface for calculating water exchange in mm/T based on the currently
        available water volume."""
    
        typeid: ClassVar[Literal[2]] = 2
        """Type identifier for |ExchangeModel_V2| submodels."""
    
        @modeltools.abstractmodelmethod
        def determine_exchange(self, watervolume: float) -> None:
            """Determine the water exchange based on the water volume given in million
            m³."""
            assert False
    
        @modeltools.abstractmodelmethod
        def get_exchange(self) -> float:
            """Determine the water exchange in [m³/s].
    
            Positive values indicate withdrawals, and negative values indicate supplies.
            """
            assert False
  2. tyralla commented on Sep 28, 2026

    @tyralla
    MemberAuthor

    Submodel name?

    My first idea: HydPy-Exch-Water-Transfer-IO

    The difference between HydPy-Exch-Branch-IO and HydPy-Exch-Water-Transfer-IO is not very explicit. But, at least for me, the term "branching" belongs to rivers and local structures, while the term "water transfer" sounds more like regional measures involving large dams or lakes.

    Better suggestions are welcome, of course.

  3. tyralla commented on Sep 28, 2026

    @tyralla
    MemberAuthor

    Submodel configuration

    The following parameters correspond to the ideas of #201 (comment), taking into account most of the improvements suggested in #201 (comment):

    from hydpy.models.dam_llake import *
    parameterstep()
    with model.add_exchangemodel_v2("exch_water_transfer_io"):
        volumethreshold(0.0)
        volumetolerance(0.0)
        fixwaterbalance(True)
        fallbacktransfer(PPoly.from_data(xs=[0.0], ys=[1.0]))

    We again need another "rule parameter" because this time it really only works with simple PPoly instances for the individual times of the year (not with PPolys instances).

    ViolateWaterBalance sounds very harsh to me. Other alternatives to FixWaterBalance that come to mind are KeepWaterBalance, PreserveWaterBalance, or StrictWaterBalance.

    Instead of just defining MinVolume (corresponding to MinStream), I suggest defining two parameters: VolumeThreshold and VolumeTolerance. HydPy-Dam solves its differential equations numerically, and this works much better without existing discontinuities. Beyond this technical point, users may also prefer smoother transitions for practical reasons. However, the names VolumeThreshold and VolumeTolerance follow HydPy-Dam's naming style. Possible alternatives: VolumeMin and VolumeTol. Or we could define two thresholds, VolMin0 and VolMin1, or so, and interpolate linearly between them (simpler to understand than smoothing kernels based on the logistic function, but discontinuous in the first derivative).

    (It would be very cool to reactivate the discontinuity locking approach at some time to get rid of the need to configure smoothing kernels. At least for the relatively simple "scalar volume" models of the HydPy-Dam family.)

  4. tyralla commented on Sep 28, 2026

    @tyralla
    MemberAuthor

    Some updates

    I think it is better to introduce a submodel interface family specialised for the problem at hand:

    class TransferModel_V1(modeltools.SubmodelInterface):
        """Interface for calculating water transfer in mm/T based on the currently
        available water volume."""
    
        typeid: ClassVar[Literal[1]] = 1
        """Type identifier for |TransferModel_V1| submodels."""
    
        @modeltools.abstractmodelmethod
        def determine_transfer(self, watervolume: float) -> None:
            """Determine the water transfer based on the water volume given in million
            m³."""
            assert False
    
        @modeltools.abstractmodelmethod
        def get_transfer(self) -> float:
            """Determine the water transfer in [m³/s].
    
            Positive values indicate withdrawals, and negative values indicate supplies.
            """
            assert False

    I think TransferRules is better than FallbackTransfer, and if we implement VolumeMinimum and allow negative water transfer (supply), we should also implement VolumeMaximum:

    with model.add_transfermodel_v2("exch_water_transfer_io"):
        volumeminimum(0.0)
        volumemaximum(1.0)
        volumetolerance(0.2)
        keepwaterbalance(True)
        transfertrules(PPoly.from_data(xs=[0.0], ys=[1.0]))

    The loose coupling between the respective dam model and the exch_water_transfer_io model might pose numerical problems. ELSModel needs to do some special things with (temporary) values of the relevant "numerical sequences" (e.g. for estimating the numerical error) but, in this case, does not have enough access to the sequence ActualTransfer. Furthermore, how should we tell the dam model that it sometimes transfers water without affecting its own stored water volume? That behaviour is strange for a numerically solved (finite-volume) state-space model.

    We should discuss whether going this way is really worth it. As a compromise, we could keep the "numerical logic" in HydPy-Dam and use the submodel only to supply time series and control-parameter information. This is, in fact, what we do for evaporation (at least for the time series part; not the control parameter part), where the submodel only provides potential evaporation (which does not depend on the water volume), and the dam model adjusts potential to actual evaporation by itself, meaning the actual evaporation sequence is always there, and its values are always calculated, no matter whether potential evaporation data is available or not (no data availability means zero evaporation).

  5. tyralla commented on Sep 29, 2026

    @tyralla
    MemberAuthor

    My strategy for circumventing (or solving?) the "ODE problems" discussed in the last post.

    We add three flux sequences to HydPy-Exch-Water-Transfer-IO:

    • RequestedTransfer: This has either the current value of the input sequence RequestedTransfer or, if not available, the value interpolated by TransferRule.
    • ActualTransfer: The water transfer that is actually passed to the connected outlet node and thereby potentially affects other models by supplying water to them or so.
    • EffectiveTransfer: The effect of the water transfer on the lake's or dam's water balance.

    RequestedTransfer and ActualTransfer do not need to be "numerical sequences" in the sense that their values are constant within each simulation step. This is only the case for EffectiveTransfer, which depends on the currently stored water volume, which changes "continuously" within each simulation step. So, HydPy-Dam requires a corresponding sequence, which I would also name EffectiveTransfer, and it will be involved in the ODE-solving steps.

    Without further work, the EffectiveTransfer values will differ between the two models in many cases, because for HydPy-Dam it represents the integrated average flux for each simulation step, but for HydPy-Exch it is only the last function call of the applied ODE solver. To fix this, we could order the HydPy-Dam model to send its final value to the HydPy-Exch model. But maybe it is better to turn the EffectiveTransfer sequence of HydPy-Exch into an aide sequence to clarify that its values serve only internal purposes (and cannot be made available as a time series).

  6. tyralla commented on Sep 29, 2026

    @tyralla
    MemberAuthor

    Allowed water-level drop

    I forgot about this one. According to its documentation, LARSIM reduces water transfer if it would otherwise cause an exceedance of the maximum allowed water-level drop. However, it remains unclear whether this works the same way when the requested water transfer comes from an external time series rather than internal rules. The documentation only states that the transfer is reduced. However, it seems much more consistent to me that the same logic as for preventing negative storage volumes holds. So, unless there are objections, I will later try to extend the previously developed design to include a feature that prevents water-level drop violations.

  7. tyralla commented on Sep 29, 2026

    @tyralla
    MemberAuthor

    Allowed water-level drop

    It seems we need an additional decision.

    This is how HydPy-Dam-L-Lake calculates the "allowed discharge":

    Image

    And this is how HydPy-Dam-L-Reservoir does it:

    Image

    So, both models adjust their normal outflow (dam_llake directly and dam_lreservoir indirectly through its "intentional release") if necessary and possible. All other fluxes are calculated independently from the allowed water level drop. If we want the new water transfer to depend on the allowed water release, we first need to define priorities. Should we reduce the water transfer or the normal outflow first? Or should we reduce both by the same factor (which could be much simpler)?

  8. tyralla commented on Sep 30, 2026

    @tyralla
    MemberAuthor

    Allowed water-level drop

    We agreed in an offline discussion that it seems unlikely anyone would prefer to reduce water transfers first. Water transfer should seldom be intense enough to cause severe water-level drops. This is more of an issue when rapidly releasing water to increase storage capacity for predicted floods. Then it would be strange to release as much water to the riverbed as allowed and thereby stop transferring it elsewhere where it is needed. Therefore, we will calculate the EffectiveTransfer first and then include it in the above equations of the two HydPy-Dam models.

    For clarification: We still plan to restrict EffectiveTransfer to the allowed water-level drop in the same way as the two given equations. But EffectiveTransfer comes first and so gets all available water, while Outflow comes second and gets only the remaining water.

    This is how we could do it for HydPy-Dam-L-Lake (first idea, might be improvable):

    • The main model calculates AllowedDischarge as before and passes it, in combination with its current WaterVolume, to the submodel.
    • The submodel adjusts EffectiveTransfer (and eventually ActualTransfer) smoothly to the given values.
    • The main model calculates AllowedDischarge again, this time also taking EffectiveTransfer into account.

    Doesn't sound too complicated. I didn't check, but I see no reason we couldn't apply this algorithm to HydPy-Dam-L-Reservoir as well.

  9. tyralla commented on Oct 11, 2026

    @tyralla
    MemberAuthor

    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:

    >>> with model.add_transfermodel_v1("exch_water_transfer_io"):
    ...     voltransferrules(PPoly(xs=[0.0], ys=[0.0]))
    ...     minvol(-inf)
    ...     maxvol(inf)
    ...     voltol(0.0)
    ...     qtol(0.0)
    ...     keepwaterbalance(True)

    I could simplify the algorithm for respecting the allowed water-level drop by first calculating the allowed discharge without water transfer, then subtracting the effective water transfer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions