You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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.
ExchangeModel_V1: Interface for exchanging modified scalar data of arbitrary type.
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:
classExchangeModel_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.abstractmodelmethoddefdetermine_exchange(self, watervolume: float) ->None:
"""Determine the water exchange based on the water volume given in million m³."""assertFalse@modeltools.abstractmodelmethoddefget_exchange(self) ->float:
"""Determine the water exchange in [m³/s]. Positive values indicate withdrawals, and negative values indicate supplies. """assertFalse
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.
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.)
I think it is better to introduce a submodel interface family specialised for the problem at hand:
classTransferModel_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.abstractmodelmethoddefdetermine_transfer(self, watervolume: float) ->None:
"""Determine the water transfer based on the water volume given in million m³."""assertFalse@modeltools.abstractmodelmethoddefget_transfer(self) ->float:
"""Determine the water transfer in [m³/s]. Positive values indicate withdrawals, and negative values indicate supplies. """assertFalse
I think TransferRules is better than FallbackTransfer, and if we implement VolumeMinimum and allow negative water transfer (supply), we should also implement VolumeMaximum:
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).
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).
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.
This is how HydPy-Dam-L-Lake calculates the "allowed discharge":
And this is how HydPy-Dam-L-Reservoir does it:
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)?
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.
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:
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.
Activity
tyralla commentedon Sep 28, 2026
Which interface?
The current best fits:
Principally, "exchange" sounds more suitable than "discharge", but we are definitely dealing with m³/s here and not with arbitrary units.
My suggestion:
tyralla commentedon Sep 28, 2026
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.
tyralla commentedon Sep 28, 2026
Submodel configuration
The following parameters correspond to the ideas of #201 (comment), taking into account most of the improvements suggested in #201 (comment):
We again need another "rule parameter" because this time it really only works with simple
PPolyinstances for the individual times of the year (not withPPolysinstances).ViolateWaterBalancesounds very harsh to me. Other alternatives toFixWaterBalancethat come to mind areKeepWaterBalance,PreserveWaterBalance, orStrictWaterBalance.Instead of just defining
MinVolume(corresponding toMinStream), I suggest defining two parameters:VolumeThresholdandVolumeTolerance. 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 namesVolumeThresholdandVolumeTolerancefollow HydPy-Dam's naming style. Possible alternatives:VolumeMinandVolumeTol. Or we could define two thresholds,VolMin0andVolMin1, 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.)
tyralla commentedon Sep 28, 2026
Some updates
I think it is better to introduce a submodel interface family specialised for the problem at hand:
I think
TransferRulesis better thanFallbackTransfer, and if we implementVolumeMinimumand allow negative water transfer (supply), we should also implementVolumeMaximum: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).
tyralla commentedon Sep 29, 2026
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 sequenceRequestedTransferor, if not available, the value interpolated byTransferRule.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.RequestedTransferandActualTransferdo not need to be "numerical sequences" in the sense that their values are constant within each simulation step. This is only the case forEffectiveTransfer, 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 nameEffectiveTransfer, and it will be involved in the ODE-solving steps.Without further work, the
EffectiveTransfervalues 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 theEffectiveTransfersequence 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).tyralla commentedon Sep 29, 2026
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.
tyralla commentedon Sep 29, 2026
Allowed water-level drop
It seems we need an additional decision.
This is how HydPy-Dam-L-Lake calculates the "allowed discharge":
And this is how HydPy-Dam-L-Reservoir does it:
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)?
tyralla commentedon Sep 30, 2026
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
EffectiveTransferfirst and then include it in the above equations of the two HydPy-Dam models.For clarification: We still plan to restrict
EffectiveTransferto the allowed water-level drop in the same way as the two given equations. ButEffectiveTransfercomes first and so gets all available water, whileOutflowcomes second and gets only the remaining water.This is how we could do it for HydPy-Dam-L-Lake (first idea, might be improvable):
AllowedDischargeas before and passes it, in combination with its currentWaterVolume, to the submodel.EffectiveTransfer(and eventuallyActualTransfer) smoothly to the given values.AllowedDischargeagain, this time also takingEffectiveTransferinto 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.
Add the branching submodel `exch_water_transfer_io` and make it appli…
Add the branching submodel `exch_water_transfer_io` and make it appli…
Add the branching submodel `exch_water_transfer_io` and make it appli…
Add the branching submodel `exch_water_transfer_io` and make it appli…
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:
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.
Add the branching submodel `exch_water_transfer_io` and make it appli…