Set parameters of other nodes based on model state conditions (e.g. Basin level). The table below shows which parameters are controllable for a given node type.
Code
usingRibasimusingDataFrames: DataFrameusingMarkdownTablesusingPythonCall# Render matplotlib figures off-screen; they are displayed by ending a cell with the figurepyimport("matplotlib").use("Agg")include("../../../utils/gen_docs_table.jl")node_names =Symbol[]controllable_parameters =String[]for node_type infieldtypes(Ribasim.ParametersIndependent)if node_type <: Ribasim.AbstractParameterNode node_name =nameof(node_type) controllable_fields = Ribasim.controllablefields(node_name) controllable_fields =sort!(string.(controllable_fields))if node_name ==:TabulatedRatingCurve controllable_fields =map( s ->replace(s, "table"=>"q(h) relationship (given by level, flow_rate)"), controllable_fields, )endif !isempty(controllable_fields)push!(node_names, Ribasim.snake_case(node_name))push!(controllable_parameters, join(controllable_fields, ", "))endendenddf =DataFrame(:node => node_names, :controllable_parameters => controllable_parameters)markdown_table(df)
node
controllable_parameters
linear_resistance
resistance
manning_resistance
manning_n
tabulated_rating_curve
q(h) relationship (given by level, flow_rate)
pump
flow_rate
outlet
flow_rate
pid_control
derivative, integral, proportional, target
1 Tables
1.1 Variable
The compound variable schema defines linear combinations of variables which can be used in conditions. This means that this schema defines new variables with the given compound_variable_id that look like \[
\text{weight}_1 * \text{variable}_1 + \text{weight}_2 * \text{variable}_2 + \ldots,
\]
which can be for instance an average or a difference of variables. If a variable comes from a time-series, a look ahead \(\Delta t\) can be supplied.
Code
table_from_schema( Ribasim.Schema.DiscreteControl.Variable; restrictions =Dict(:listen_node_id =>"cannot be a Junction",:variable =>"must be `level`, `storage`, `flow_rate`, `concentration.<substance>` or `concentration_external.<substance>`",:look_ahead =>"only on transient boundary conditions, non-negative", ),)
column
type
unit
optional
default
restriction
node_id
Int32
-
x
-
-
compound_variable_id
Int32
-
x
-
-
listen_node_id
Int32
-
x
-
cannot be a Junction
variable
String
-
x
-
must be level, storage, flow_rate, concentration.<substance> or concentration_external.<substance>
weight
Float64
-
✓
1.0
-
look_ahead
Float64
s
✓
0.0
only on transient boundary conditions, non-negative
These variables can be listened to:
The storage of a Basin
The level of a Basin
The level of a LevelBoundary (supports look ahead)
The flow rate through one of these node types: Pump, Outlet, TabulatedRatingCurve, LinearResistance, ManningResistance
The inflow rate of a UserDemand
The flow rate of a FlowBoundary (supports look ahead)
The concentration of a substance in a Basin, with variable concentration.<substance>, e.g. concentration.Cl
The external concentration of a substance in a Basin, with variable concentration_external.<substance>, as given in the Basin / concentration_external table
1.2 Condition
The condition schema defines conditions of the form the discrete_control node with this node_id listens to whether the variable given by the node_id and compound_variable_id is greater than threshold_high (at time \(t\)) when the condition was false previously, or threshold_low when true previously, adding hysteresis. Using threshold_low is optional and defaults to threshold_high. We recommend setting threshold_low below threshold_high to create a range that avoids frequent control state changes from small fluctuations and improves model performance. In equation form:
Multiple conditions with different threshold_high and/or threshold_low values can be defined on the same compound_variable, each with their own unique condition_id.
At the start of the simulation, a condition is true if its variable exceeds the midpoint of threshold_low and threshold_high (which equals threshold_high without hysteresis).
When no (or only a single) time is supplied for a given condition_id, the threshold_high value of this condition is unchanging over time. When multiple times are supplied, in between the given times the threshold_high and threshold_low values are block interpolated (forward fill), and outside they are constant given by the nearest time value. It is possible to configure a DiscreteControl node such that it very rapidly oscillates between control states, which isn’t realistic behavior. See the example model below.
Say there is constant inflow from the FlowBoundary node and there are no vertical forcings on the Basin. Say furthermore that the Pump is active only when the Basin level is above a reference value, with a flow greater than the boundary flow. In this case the Pump will rapidly turn on and off as the Basin level oscillates around the reference value.
To make you aware of these oscillations, Ribasim will give a warning if successive control state changes of a DiscreteControl node in simulated time are closer than min_discrete_control_interval, which is configurable and defaults to 1 second. The warning is given once per DiscreteControl node, for at most 10 nodes. Set min_discrete_control_interval to 0.0 to disable these warnings.
By default the conditions are evaluated after every timestep. To evaluate them only at fixed times, for instance daily, set the solver setting discrete_control_interval, see usage.
1.3 Logic
The logic schema defines which control states are triggered based on the truth of the conditions a DiscreteControl node listens to. DiscreteControl is applied in the Julia core as follows:
During the simulation it is checked whether the truth of any of the conditions changes.
When a condition changes, the corresponding DiscreteControl node ID is retrieved (node_id in the condition schema above).
The truth value of all the conditions this DiscreteControl node listens to are retrieved, in the order of the condition IDs. This is then converted into a string of “T” for true and “F” for false. This string we call the truth state.
The table below determines for the given DiscreteControl node ID and truth state what the corresponding control state is.
For all the nodes this DiscreteControl node affects (as given by the “control” links in Links / static), their parameters are set to those parameters in NodeType / static corresponding to the determined control state.
Code
table_from_schema( Ribasim.Schema.DiscreteControl.Logic; restrictions =Dict(:truth_state =>"consists of the characters `T` (true), `F` (false) and `*` (any)", ),)
column
type
optional
restriction
node_id
Int32
x
-
truth_state
String
x
consists of the characters T (true), F (false) and * (any)