Validation
The tables below show the validation rules applied to the input to the Julia core before running the model.
1 Connectivity
In the table below, each column shows which node types are allowed to be downstream (or ‘down-control’) of the node type at the top of the column.
Code
using Ribasim
using DataFrames: DataFrame
using MarkdownTables
node_names_snake_case = Symbol[]
node_names_camel_case = Symbol[]
for (node_name, node_type) in
    zip(fieldnames(Ribasim.ParametersIndependent), fieldtypes(Ribasim.ParametersIndependent))
    if node_type <: Ribasim.AbstractParameterNode
        push!(node_names_snake_case, node_name)
        push!(node_names_camel_case, nameof(node_type))
    end
end
function to_symbol(b::Bool)::String
    return b ? "✓" : "x"
end
df = DataFrame()
df[!, :downstream] = node_names_snake_case
for node_name in node_names_snake_case
    df[!, node_name] = [
        (to_symbol(node_name_ in Ribasim.neighbortypes(node_name))) for
        node_name_ in node_names_snake_case
    ]
end
markdown_table(df)| downstream | basin | linear_resistance | manning_resistance | tabulated_rating_curve | level_boundary | flow_boundary | pump | outlet | terminal | junction | discrete_control | continuous_control | pid_control | user_demand | level_demand | flow_demand | 
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
basin | 
x | ✓ | ✓ | ✓ | x | ✓ | ✓ | ✓ | x | ✓ | x | x | x | ✓ | ✓ | x | 
linear_resistance | 
✓ | x | x | x | ✓ | x | x | x | x | ✓ | ✓ | x | x | x | x | ✓ | 
manning_resistance | 
✓ | x | x | x | x | x | x | x | x | ✓ | ✓ | x | x | x | x | ✓ | 
tabulated_rating_curve | 
✓ | x | x | x | ✓ | x | x | x | x | ✓ | ✓ | x | x | x | x | ✓ | 
level_boundary | 
x | ✓ | x | ✓ | x | ✓ | ✓ | ✓ | x | x | x | x | x | ✓ | x | x | 
flow_boundary | 
x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | 
pump | 
✓ | x | x | x | ✓ | x | x | x | x | ✓ | ✓ | ✓ | ✓ | x | x | ✓ | 
outlet | 
✓ | x | x | x | ✓ | x | x | x | x | ✓ | ✓ | ✓ | ✓ | x | x | ✓ | 
terminal | 
x | x | x | ✓ | x | ✓ | ✓ | ✓ | x | ✓ | x | x | x | ✓ | x | x | 
junction | 
✓ | ✓ | ✓ | ✓ | x | ✓ | ✓ | ✓ | x | ✓ | x | x | x | ✓ | x | x | 
discrete_control | 
x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | 
continuous_control | 
x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | 
pid_control | 
x | x | x | x | x | x | x | x | x | x | ✓ | x | x | x | x | x | 
user_demand | 
✓ | x | x | x | x | x | x | x | x | ✓ | x | x | x | x | x | x | 
level_demand | 
x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | 
flow_demand | 
x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | x | 
2 Neighbor amounts
The table below shows for each node type between which bounds the amount of in- and outneighbors must be, for both flow and control links.
Code
flow_in_min = Vector{String}()
flow_in_max = Vector{String}()
flow_out_min = Vector{String}()
flow_out_max = Vector{String}()
control_in_min = Vector{String}()
control_in_max = Vector{String}()
control_out_min = Vector{String}()
control_out_max = Vector{String}()
function unbounded(i::Int)::String
    return i == typemax(Int) ? "∞" : string(i)
end
for node_name in node_names_camel_case
    bounds_flow = Ribasim.n_neighbor_bounds_flow(node_name)
    push!(flow_in_min, string(bounds_flow.in_min))
    push!(flow_in_max, unbounded(bounds_flow.in_max))
    push!(flow_out_min, string(bounds_flow.out_min))
    push!(flow_out_max, unbounded(bounds_flow.out_max))
    bounds_control = Ribasim.n_neighbor_bounds_control(node_name)
    push!(control_in_min, string(bounds_control.in_min))
    push!(control_in_max, unbounded(bounds_control.in_max))
    push!(control_out_min, string(bounds_control.out_min))
    push!(control_out_max, unbounded(bounds_control.out_max))
end
df = DataFrame(;
    node_type = node_names_snake_case,
    flow_in_min,
    flow_in_max,
    flow_out_min,
    flow_out_max,
    control_in_min,
    control_in_max,
    control_out_min,
    control_out_max,
)
markdown_table(df)| node_type | flow_in_min | flow_in_max | flow_out_min | flow_out_max | control_in_min | control_in_max | control_out_min | control_out_max | 
|---|---|---|---|---|---|---|---|---|
basin | 
0 | ∞ | 0 | ∞ | 0 | 1 | 0 | 0 | 
linear_resistance | 
1 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 
manning_resistance | 
1 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 
tabulated_rating_curve | 
1 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 
level_boundary | 
0 | ∞ | 0 | ∞ | 0 | 0 | 0 | 0 | 
flow_boundary | 
0 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 
pump | 
1 | 1 | 1 | 1 | 0 | 2 | 0 | 0 | 
outlet | 
1 | 1 | 1 | 1 | 0 | 2 | 0 | 0 | 
terminal | 
1 | ∞ | 0 | 0 | 0 | 0 | 0 | 0 | 
junction | 
1 | ∞ | 1 | ∞ | 0 | 0 | 0 | 0 | 
discrete_control | 
0 | 0 | 0 | 0 | 0 | 0 | 1 | ∞ | 
continuous_control | 
0 | 0 | 0 | 0 | 0 | 0 | 1 | ∞ | 
pid_control | 
0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 
user_demand | 
1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 
level_demand | 
0 | 0 | 0 | 0 | 0 | 0 | 1 | ∞ | 
flow_demand | 
0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 
Note
Pump and Outlet can receive two control links, in which case one must be from a FlowDemand, and the other from a control node.