Skip to content

Commit d6b658f

Browse files
SouthEndMusicvisr
andauthored
DiscreteControl based on storage (#2573)
Fixes #2569 --------- Co-authored-by: Martijn Visser <[email protected]>
1 parent f9d74eb commit d6b658f

File tree

7 files changed

+70
-2
lines changed

7 files changed

+70
-2
lines changed

core/src/parameter.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,7 @@ const StateTimeDependentCache{T} = @NamedTuple{
786786
u_prev_call::Vector{T},
787787
} where {T}
788788

789-
@enumx CacheType flow_rate_pump flow_rate_outlet basin_level
789+
@enumx CacheType flow_rate_pump flow_rate_outlet basin_level basin_storage
790790

791791
"""
792792
A cache for intermediate results in `water_balance!` which depend only on the time `t`. A second version of this
@@ -849,6 +849,8 @@ function get_cache_vector(
849849
state_time_dependent_cache.current_flow_rate_outlet
850850
elseif type == CacheType.basin_level
851851
state_time_dependent_cache.current_level
852+
elseif type == CacheType.basin_storage
853+
state_time_dependent_cache.current_storage
852854
else
853855
error("Invalid cache type $type passed.")
854856
end

core/src/util.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,8 @@ function get_cache_ref(
509509

510510
ref = if node_id.type == NodeType.Basin && variable == "level"
511511
CacheRef(; type = CacheType.basin_level, node_id.idx)
512+
elseif node_id.type == NodeType.Basin && variable == "storage"
513+
CacheRef(; type = CacheType.basin_storage, node_id.idx)
512514
elseif variable == "flow_rate" && node_id.type != NodeType.FlowBoundary
513515
if listen
514516
if node_id.type conservative_nodetypes

core/test/control_test.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,3 +381,14 @@ end
381381
@test control.truth_state[3] == "F"
382382
@test basin6.level[findfirst(>=(t2), basin6.time)] <= 0.9 + 1e-2
383383
end
384+
385+
@testitem "Storage condition" begin
386+
toml_path =
387+
normpath(@__DIR__, "../../generated_testmodels/storage_condition/ribasim.toml")
388+
@test ispath(toml_path)
389+
model = Ribasim.run(toml_path)
390+
@test success(model)
391+
392+
storage = Ribasim.get_storages_and_levels(model).storage[1, :]
393+
@test all(storage .< 7500 + 6)
394+
end

docs/changelog.qmd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Lastly, Ribasim is now multithreaded!
3232
- Warn when reading a new model with an old Ribasim Python version. [#2410](https://github.com/Deltares/Ribasim/pull/2410)
3333
- Add instructions to install using Pixi. [#2415](https://github.com/Deltares/Ribasim/pull/2415)
3434
- Add guide on updating Ribasim models. [#2489](https://github.com/Deltares/Ribasim/pull/2489)
35+
- Support DiscreteControl listening to a Basin's storage. [#2573](https://github.com/Deltares/Ribasim/pull/2573)
3536

3637
### Changed
3738
- Allow two incoming control links for Pump and Outlet. [#2531](https://github.com/Deltares/Ribasim/pull/2531)

docs/reference/node/discrete-control.qmd

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,12 @@ column | type | unit | restriction
5757
node_id | Int32 | - |
5858
compound_variable_id | Int32 | - |
5959
listen_node_id | Int32 | - | cannot be a Junction
60-
variable | String | - | must be "level" or "flow_rate"
60+
variable | String | - | must be "level", "storage" or "flow_rate"
6161
weight | Float64 | - | (optional, default 1.0)
6262
look_ahead | Float64 | $\text{s}$ | Only on transient boundary conditions, non-negative (optional, default 0.0).
6363

6464
These variables can be listened to:
65+
- The storage of a Basin
6566
- The level of a Basin
6667
- The level of a LevelBoundary (supports look ahead)
6768
- The flow rate through one of these node types: Pump, Outlet, TabulatedRatingCurve, LinearResistance, ManningResistance

python/ribasim_testmodels/ribasim_testmodels/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
level_boundary_condition_model,
5555
level_range_model,
5656
pump_discrete_control_model,
57+
storage_condition_model,
5758
tabulated_rating_curve_control_model,
5859
transient_condition_model,
5960
)
@@ -149,6 +150,7 @@
149150
"pump_discrete_control_model",
150151
"rating_curve_model",
151152
"rating_curve_between_basins_model",
153+
"storage_condition_model",
152154
"subnetwork_model",
153155
"subnetworks_with_sources_model",
154156
"tabulated_rating_curve_control_model",

python/ribasim_testmodels/ribasim_testmodels/discrete_control.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,55 @@ def level_range_model() -> Model:
496496
return model
497497

498498

499+
def storage_condition_model() -> Model:
500+
"""Create a model with a discrete control condition based on the storage of a Basin."""
501+
model = Model(
502+
starttime="2020-01-01",
503+
endtime="2021-01-01",
504+
crs="EPSG:28992",
505+
)
506+
507+
fb = model.flow_boundary.add(
508+
Node(1, Point(0, 0)), [flow_boundary.Static(flow_rate=[1e-3])]
509+
)
510+
511+
bsn = model.basin.add(
512+
Node(2, Point(1, 0)),
513+
[basin.Profile(area=1000.0, level=[0.0, 10.0]), basin.State(level=[5.0])],
514+
)
515+
516+
pmp = model.pump.add(
517+
Node(3, Point(2, 0)),
518+
[
519+
pump.Static(
520+
control_state=["off", "on"], flow_rate="1e-3", active=[False, True]
521+
)
522+
],
523+
)
524+
525+
tmn = model.terminal.add(Node(4, Point(3, 0)))
526+
527+
dc = model.discrete_control.add(
528+
Node(5, Point(1, 1)),
529+
[
530+
discrete_control.Variable(
531+
compound_variable_id=1, listen_node_id=2, variable=["storage"]
532+
),
533+
discrete_control.Condition(
534+
compound_variable_id=1, condition_id=1, threshold_high=[7500]
535+
),
536+
discrete_control.Logic(truth_state=["F", "T"], control_state=["off", "on"]),
537+
],
538+
)
539+
540+
model.link.add(fb, bsn)
541+
model.link.add(bsn, pmp)
542+
model.link.add(pmp, tmn)
543+
model.link.add(dc, pmp)
544+
545+
return model
546+
547+
499548
def connector_node_flow_condition_model() -> Model:
500549
"""DiscreteControl with a condition on the flow through a connector node."""
501550
model = Model(

0 commit comments

Comments
 (0)