DiscreteControl

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
using Ribasim
using DataFrames: DataFrame
using MarkdownTables

node_names = Symbol[]
controllable_parameters = String[]

for node_type in fieldtypes(Ribasim.Parameters)
    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)
        end
        if !isempty(controllable_fields)
            push!(node_names, Ribasim.snake_case(node_name))
            push!(controllable_parameters, join(controllable_fields, ", "))
        end
    end
end

df = DataFrame(:node => node_names, :controllable_parameters => controllable_parameters)

markdown_table(df)
node controllable_parameters
linear_resistance active, resistance
manning_resistance active, manning_n
tabulated_rating_curve active, q(h) relationship (given by level, flow_rate)
pump active, flow_rate
outlet active, flow_rate
pid_control active, 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.

column type unit restriction
node_id Int32 - sorted
compound_variable_id Int32 - sorted per node_id
listen_node_type String - known node type
listen_node_id Int32 - sorted per node_id
variable String - must be “level” or “flow_rate”, sorted per listen_node_id
weight Float64 - (optional, default 1.0)
look_ahead Float64 \(\text{s}\) Only on transient boundary conditions, non-negative (optional, default 0.0).

These variables can be listened to: - 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 flow rate of a FlowBoundary (supports look ahead)

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 greater_than’. In equation form:

\[ \text{weight}_1 * \text{variable}_1 + \text{weight}_2 * \text{variable}_2 + \ldots > \text{greater\_than}. \]

Multiple conditions with different greater_than values can be defined on the same compound_variable.

Note the strict inequality ‘\(>\)’ in the equation above. This means for instance that if a simulation starts with a compound variable exactly at the greater_than value, the condition is false.

column type unit restriction
node_id Int32 - sorted
compound_variable_id Int32 - -
greater_than Float64 various sorted per variable

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 sorted order as specified in the condition schema. 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” edges in Edges / static), their parameters are set to those parameters in NodeType / static corresponding to the determined control state.

*. There is also a second truth state created in which for the last condition that changed it is specified whether it was an upcrossing (“U”) or downcrossing (“D”) of the threshold (greater than) value. If a control state is specified for a truth state that is crossing-specific, this takes precedence over the control state for the truth state that contains only “T” and “F”.

Note

When creating truth states, it is important to not use the order of the condition table as you provide it, but the order as it is written to the file. Users can provide tables in any order, but when writing the model it gets sorted in the required order as specified in the schema.

column type unit restriction
node_id Int32 - sorted
control_state String - -
truth_state String - Consists of the characters “T” (true), “F” (false), “U” (upcrossing), “D” (downcrossing) and “*” (any), sorted per node_id