Usage

Ribasim is typically used as a command-line interface (CLI). It is distributed as a .zip archive, that must be downloaded and unpacked. It can be placed anywhere, however it is important that the contents of the zip file are kept together in a directory. The Ribasim CLI executable is in the bin directory.

To download ribasim_cli.zip, see the download section.

To check whether the installation was performed successfully, run ribasim with no arguments in the command line. This will give the following message:

Usage: ribasim 'path/to/model/ribasim.toml'

1 Configuration file

Ribasim has a single configuration file, which is written in the TOML format. It contains settings, as well as paths to other input and output files. Ribasim expects the GeoPackage database database.gpkg as well as optional Arrow input files to be available in the input_dir.

# start- and endtime of the simulation
# can also be set to a date-time like 1979-05-27T07:32:00
starttime = 2019-01-01 # required
endtime = 2021-01-01   # required

# Coordinate Reference System
# The accepted strings are documented here:
# https://proj.org/en/9.4/development/reference/functions.html#c.proj_create
crs = "EPSG:4326"      # required

# input files
input_dir = "."         # required
results_dir = "results" # required

ribasim_version = "2024.8.0" # required

# Specific tables can also go into Arrow files rather than the database.
# For large tables this can benefit from better compressed file sizes.
# This is optional, tables are retrieved from the database if not specified in the TOML.
[basin]
time = "basin/time.arrow"

[allocation]
timestep = 86400                   # optional (required if use_allocation = true), default 86400
use_allocation = false             # optional, default false

[solver]
algorithm = "QNDF"  # optional, default "QNDF"
saveat = 86400      # optional, default 86400, 0 saves every timestep, inf saves only at start- and endtime
dt = 60.0           # optional, remove for adaptive time stepping
dtmin = 0.0         # optional, default 0.0
dtmax = 0.0         # optional, default length of simulation
force_dtmin = false # optional, default false
abstol = 1e-6       # optional, default 1e-6
reltol = 1e-5       # optional, default 1e-5
maxiters = 1e9      # optional, default 1e9
sparse = true       # optional, default true
autodiff = true     # optional, default true

[logging]
# defines the logging level of Ribasim
verbosity = "info" # optional, default "info", can otherwise be "debug", "warn" or "error"
timing = false     # optional, whether to log debug timing statements

[results]
# These results files are always written
compression = true  # optional, default true, using zstd compression
compression_level = 6 # optional, default 6

1.1 Solver settings

The solver section in the configuration file is entirely optional, since we aim to use defaults that will generally work well. Common reasons to modify the solver settings are to adjust the calculation or result stepsizes: dt, and saveat. If your model does not converge, or your performance is lower than expected, it can help to adjust other solver settings as well.

The default solver algorithm = "QNDF", which is a multistep method similar to Matlab’s ode15s (Shampine and Reichelt 1997). It is an implicit method that supports the default adaptive timestepping. The full list of available solvers is: QNDF, Rosenbrock23, TRBDF2, Rodas5, KenCarp4, Tsit5, RK4, ImplicitEuler, Euler. Information on the solver algorithms can be found on the ODE solvers page.

By default Ribasim uses adaptive timestepping, though not all algorithms support adaptive timestepping. To use fixed timesteps, provide a timestep size in seconds; dt = 3600.0 corresponds to an hourly timestep. With adaptive timestepping, dtmin and dtmax control the minimum and maximum allowed dt. If a smaller dt than dtmin is needed to meet the set error tolerances, the simulation stops, unless force_dtmin is set to true. force_dtmin is off by default to ensure an accurate solution.

The default result stepsize, saveat = 86400 will save results after every day that passed. The calculation and result stepsize need not be the same. If you wish to save every calculation step, set saveat = 0. If you wish to not save any intermediate steps, set saveat = inf.

The Jacobian matrix provides information about the local sensitivity of the model with respect to changes in the states. For implicit solvers it must be calculated often, which can be expensive to do. There are several methods to do this. By default Ribasim uses a Jacobian derived automatically using ForwardDiff.jl with memory management provided by PreallocationTools.jl. If this is not used by setting autodiff = false, the Jacobian is calculated with a finite difference method, which can be less accurate and more expensive.

By default the Jacobian matrix is a sparse matrix (sparse = true). Since each state typically only depends on a small number of other states, this is generally more efficient, especially for larger models. The sparsity structure is calculated from the network and provided as a Jacobian prototype to the solver. For small or highly connected models it could be faster to use a dense Jacobian matrix instead by setting sparse = false.

The total maximum number of iterations maxiters = 1e9, can normally stay as-is unless doing extremely long simulations.

The absolute and relative tolerance for adaptive timestepping can be set with abstol and reltol. For more information on these and other solver options, see the DifferentialEquations.jl docs.

1.2 Allocation settings

Currently there are the following allocation settings: - use_allocation: A boolean which says whether allocation should be used or not; - timestep: a float value in seconds which dictates the update interval for allocations.

1.3 Results settings

The following entries can be set in the configuration in the [results] section.

entry type description
compression Bool Whether to apply compression or not.
compression_level Int Zstandard compression level. Default is 6, higher compresses more.
subgrid Bool Compute and output more detailed water levels.

1.4 Logging settings

The following entries can be set in the configuration in the [logging] section.

entry type description
verbosity String Verbosity level: debug, info, warn, or error.
timing Bool Enable timings.

2 GeoPackage database and Arrow tables

The input and output tables described below all share that they are tabular files. The Node and Edge tables always have to be in the GeoPackage database file, and results are always written to Apache Arrow files, sometimes also known as Feather files. All other tables can either be in the database or in separate Arrow files that are listed in the TOML as described above.

For visualization, the Node and Edge tables typically have associated geometries. GeoPackage was used since it provides a standardized way to store tables with (and without) geometry columns in a SQLite database. If, like Ribasim, you can ignore the geometries, a GeoPackage is easy to read using SQLite libraries, which are commonly available. Furthermore GeoPackage can be updated in place when working on a model.

Arrow was chosen since it is standardized, fast, simple and flexible. It can be read and written by many different software packages. In Ribasim we use Arrow.jl. Results are written to Arrow, since for long runs Ribasim can produce tables with many rows. Arrow is well suited for large tabular datasets, and file size is kept small by using compression. The Arrow input files can be compressed with LZ4 or Zstd compression. Furthermore, in some of the columns, a small amount of different values are repeated many times. To reduce file sizes it may be a good idea to apply dictionary encoding to those columns. The Ribasim version that was used to create the results is written to each file in the ribasim_version schema metadata.

2.1 Table requirements

Below we give details per file, in which we describe the schema of the table using a syntax like this:

column type unit restriction
node_id Int32 - sorted
storage Float64 \(m^3\) non-negative

This means that two columns are required, one named node_id, that contained elements of type Int32, and a column named storage that contains elements of type Float64. The order of the columns does not matter. In some cases there may be restrictions on the values. This is indicated under restriction.

Tables are also allowed to have rows for timestamps that are not part of the simulation, these will be ignored. That makes it easy to prepare data for a larger period, and test models on a shorted period.

When preparing the model for simulation, input validation is performed in the Julia core. The validation rules are described in the validation section.

2.2 Custom metadata

It may be advantageous to add metadata to rows. For example, basin areas might have names and objects such as weirs might have specific identification codes. Additional columns can be freely added to tables. The column names should be prefixed with meta_. They will not be used in computations or validated by the Julia core.

3 Node

Node is a table that specifies the ID and type of each node of a model. The ID must be unique among all nodes, and the type must be one of the available node types listed below.

Nodes are components that are connected together to form a larger system. The Basin is a central node type that stores water. The other node types influence the flow between Basins in some way. Counter intuitively, even systems you may think of as edges, such as a canal, are nodes in Ribasim. This is because edges only define direct instantaneous couplings between nodes, and never have storage of their own.

column type restriction
node_id Int32 -
node_type String known node type
geom Point (optional)
name String (optional, does not have to be unique)
subnetwork_id Int32 (optional)

The available node types as of this writing are listed as the top level bullets below. The sub-bullets indicate which tables are associated to the node type. The table name is the name it must have in the database if it is stored there.

  • Basin: stores water
    • Basin / static: default forcing values, used if no dynamic data given in the forcing table
    • Basin / profile: geometries of the basins
    • Basin / time: time series of the forcing values
    • Basin / state: used as initial condition of the basins
    • Basin / subgrid: used to interpolate basin levels to a more detailed spatial representation
  • FractionalFlow: connect two of these from a Basin to get a fixed ratio bifurcation
    • FractionalFlow / static: fractions
  • LevelBoundary: stores water at a given level unaffected by flow, like an infinitely large basin
    • LevelBoundary / static: levels
    • LevelBoundary / time: dynamic levels
  • FlowBoundary: sets a precribed flow into the model
    • FlowBoundary / static: flow rate
    • FlowBoundary / time: dynamic flow rate
  • LinearResistance: bidirectional flow based on water level difference between Basins
    • LinearResistance / static: conductances
  • ManningResistance: Flow through this connection is estimated by conservation of energy and the Manning-Gauckler formula to estimate friction losses
    • ManningResistance / static: properties
  • TabulatedRatingCurve: Basin outflow relation
    • TabulatedRatingCurve / static: rating curve
    • TabulatedRatingCurve / time: dynamic rating curve
  • Pump: pump water from a source node to a destination node
    • Pump / static: flow rate
  • Outlet: let water flow with a prescribed flux under the conditions of positive head difference and the upstream level being higher than the minimum crest level
    • Outlet / static: flow rate, minimum crest level
  • UserDemand: sets water usage demands at certain priorities
    • UserDemand / static: demands
    • UserDemand / time: dynamic demands
  • LevelDemand: Indicates minimum and maximum target level of connected basins for allocation
    • LevelDemand / static: static target levels
    • LevelDemand / time: dynamic target levels
  • FlowDemand: sets non-consuming flow demand
    • FlowDemand / static: flow demands
    • FlowDemand / time: dynamic flow demands
  • Terminal: Water sink without state or properties
    • Terminal / static: - (only node IDs)
  • DiscreteControl: Set parameters of other nodes based on model state conditions (e.g. basin level)
    • DiscreteControl / variable : Linear combinations of variables, e.g. averages or differences of levels
    • DisceteControl / condition: Conditions of the form ‘the compound variable with ID n is bigger than 2.0’
    • DisceteControl / logic: Translates the truth value of a set of conditions to parameter values for a controlled node
  • PidControl: Controls the level in a basin by continuously controlling the flow rate of a connected pump or outlet. See also Wikipedia and PID controller in equations.
    • PidControl / static: The proportional, integral, and derivative parameters, the target value and which basin should be controlled
    • PidControl / time: same as static but varying over time.

Adding a geometry to the node table can be helpful to examine models in QGIS, as it will show the location of the nodes on the map. The geometry is not used by Ribasim.

4 Edge

Edges define connections between nodes. The only thing that defines an edge is the nodes it connects, and in what direction. There are currently 2 possible edge types:

  1. “flow”: Flows between nodes are stored on edges. The effect of the edge direction depends on the node type, Node types that have a notion of an upstream and downstream side use the incoming edge as the upstream side, and the outgoing edge as the downstream side. This means that edges should generally be drawn in the main flow direction. But for instance between two LinearResistances the edge direction does not affect anything, other than the sign of the flow on the edge. The sign of the flow follows the edge direction; a positive flow flows along the edge direction, a negative flow in the opposite way.
  2. “control”: The control edges define which nodes are controlled by a particular control node. Control edges should always point away from the control node. The edges between the control node and the nodes it listens to are not present in Edge, these are defined in DiscreteControl / condition
column type restriction
from_node_type String -
from_node_id Int32 -
to_node_type String -
to_node_id Int32 -
edge_type String must be “flow” or “control”
geom LineString or MultiLineString (optional)
name String (optional, does not have to be unique)
subnetwork_id Int32 (optional, denotes source in allocation network)

Similarly to the node table, you can use a geometry to visualize the connections between the nodes in QGIS. For instance, you can draw a line connecting the two node coordinates.

5 Basin

The Basin table can be used to set the static value of variables. The time table has a similar schema, with the time column added. A static value for a variable is only used if there is no dynamic forcing data for that variable. Specifically, if there is either no time table, it is empty, or all timestamps of that variable are missing.

column type unit restriction
node_id Int32 - sorted
precipitation Float64 \(m s^{-1}\) non-negative
potential_evaporation Float64 \(m s^{-1}\) non-negative
drainage Float64 \(m^3 s^{-1}\) non-negative
infiltration Float64 \(m^3 s^{-1}\) non-negative
urban_runoff Float64 \(m^3 s^{-1}\) non-negative

Note that if variables are not set in the static table, default values are used when possible. These are generally zero, e.g. no precipitation, no inflow. If it is not possible to have a reasonable and safe default, a value must be provided in the static table.

5.1 Basin / time

This table is the transient form of the Basin table. The only difference is that a time column is added. The table must by sorted by time, and per time it must be sorted by node_id. At the given timestamps the values are set in the simulation, such that the timeseries can be seen as forward filled.

5.2 Basin / state

The state table aims to capture the full state of the Basin, such that it can be used as an initial condition, potentially the outcome of an earlier simulation. Currently only the Basin node types have state.

column type unit restriction
node_id Int32 - sorted
level Float64 \(m\) \(\ge\) basin bottom

Each Basin ID needs to be in the table.

5.3 Basin / profile

The profile table defines the physical dimensions of the storage reservoir of each basin.

column type unit restriction
node_id Int32 - sorted
area Float64 \(m^2\) non-negative, per node_id: start positive and increasing
level Float64 \(m\) per node_id: increasing

The level is the level at the basin outlet. All levels are defined in meters above a datum that is the same for the entire model. An example of the first 5 rows of such a table is given below. The first 4 rows define the profile of ID 2. The number of rows can vary per ID. Using a very large number of rows may impact performance.

node_id area level
2 1.0 6.0
2 1000.0 7.0
2 1000.0 9.0
3 1.0 2.2

We use the symbol \(A\) for area, \(h\) for level and \(S\) for storage. The profile provides a function \(A(h)\) for each basin. Internally this get converted to two functions, \(A(S)\) and \(h(S)\), by integrating over the function, setting the storage to zero for the bottom of the profile. The minimum area cannot be zero to avoid numerical issues. The maximum area is used to convert the precipitation flux into an inflow.

5.4 Basin / area

The optional area table is not used during computation, but provides a place to associate areas in the form of polygons to Basins. Using this makes it easier to recognize which water or land surfaces are represented by Basins.

column type restriction
node_id Int32 sorted
geom Polygon or MultiPolygon (optional)

5.5 Basin / subgrid

The subgrid_level table defines a piecewise linear interpolation from a basin water level to a subgrid element water level. Many subgrid elements may be associated with a single basin, each with distinct interpolation functions. This functionality can be used to translate a single lumped basin level to a more spatially detailed representation (e.g comparable to the output of a hydrodynamic simulation).

column type unit restriction
subgrid_id Int32 - sorted
node_id Int32 - constant per subgrid_id
basin_level Float64 \(m\) sorted per subgrid_id
subgrid_level Float64 \(m\) sorted per subgrid_id

The table below shows example input for two subgrid elements:

subgrid_id node_id basin_level subgrid_level
1 9 0.0 0.0
1 9 1.0 1.0
1 9 2.0 2.0
2 9 0.0 0.5
2 9 1.0 1.5
2 9 2.0 2.5

Both subgrid elements use the water level of the basin with node_id 9 to interpolate to their respective water levels. The first element has a one to one connection with the water level; the second also has a one to one connection, but is offset by half a meter. A basin water level of 0.3 would be translated to a water level of 0.3 for the first subgrid element, and 0.8 for the second. Water levels beyond the last basin_level are linearly extrapolated.

Note that the interpolation to subgrid water level is not constrained by any water balance within Ribasim. Generally, to create physically meaningful subgrid water levels, the subgrid table must be parametrized properly such that the spatially integrated water volume of the subgrid elements agrees with the total storage volume of the basin.

6 FractionalFlow

Lets a fraction (in [0,1]) of the incoming flow trough.

column type unit restriction
node_id Int32 - sorted
control_state String - (optional) sorted per node_id
fraction Float64 - in the interval [0,1]

7 TabulatedRatingCurve

This table is similar in structure to the Basin profile. The TabulatedRatingCurve gives a relation between the storage of a connected Basin (via the outlet level) and its outflow.

column type unit restriction
node_id Int32 - sorted
control_state String - (optional) sorted per node_id
active Bool - (optional, default true)
level Float64 \(m\) sorted per control_state
flow_rate Float64 \(m^3 s^{-1}\) non-negative
node_id flow_rate level
2 0.0 -0.105
2 0.0 0.095
2 0.00942702 0.295
2 0.942702 20.095
3 0.0 2.129

7.1 TabulatedRatingCurve / time

This table is the transient form of the TabulatedRatingCurve table. The only difference is that a time column is added. The table must by sorted by time, and per time it must be sorted by node_id. With this the rating curves can be updated over time. Note that a node_id can be either in this table or in the static one, but not both.

column type unit restriction
node_id Int32 - sorted
time DateTime - sorted per node_id
level Float64 \(m\) sorted per node_id per time
flow_rate Float64 \(m^3 s^{-1}\) non-negative

8 Pump

Pump water from a source node to a destination node. The set flow rate will be pumped unless the intake storage is less than \(10~m^3\), in which case the flow rate will be linearly reduced to \(0~m^3/s\). The intake must be either a Basin or LevelBoundary. When PID controlled, the pump must point away from the controlled basin in terms of edges.

column type unit restriction
node_id Int32 - sorted
control_state String - (optional) sorted per node_id
active Bool - (optional, default true)
flow_rate Float64 \(m^3 s^{-1}\) non-negative
min_flow_rate Float64 \(m^3 s^{-1}\) (optional, default 0.0)
max_flow_rate Float64 \(m^3 s^{-1}\) (optional)

9 Outlet

The outlet is very similar to the pump. The outlet has two additional physical constraints: water only flows trough the outlet when the head difference is positive (i.e. water flows down by gravity), and the upstream level must be above the minimum crest level if the upstream level is defined. When PID controlled, the outlet must point towards the controlled basin in terms of edges.

column type unit restriction
node_id Int32 - sorted
control_state String - (optional) sorted per node_id
active Bool - (optional, default true)
flow_rate Float64 \(m^3 s^{-1}\) non-negative
min_flow_rate Float64 \(m^3 s^{-1}\) (optional, default 0.0)
max_flow_rate Float64 \(m^3 s^{-1}\) (optional)
min_crest_level Float64 \(m\) (optional)

10 UserDemand

A UserDemand can demand a certain flow from the basin that supplies it, distributed over multiple priorities 1,2,3,… where priority 1 denotes the most important demand. Currently the UserDemand attempts to extract the complete demand from the Basin. Only if the Basin is almost empty or reaches the minimum level at which the UserDemand can extract water (min_level), will it take less than the demand. When allocation is used, water can be allocated to UserDemand based on their priority. UserDemands need an outgoing flow edge along which they can send their return flow, this can also be to the same basin from which it extracts water. The amount of return flow is always a fraction of the inflow into the UserDemand. The difference is consumed by the UserDemand.

column type unit restriction
node_id Int32 - sorted
active Bool - (optional, default true)
demand Float64 \(m^3 s^{-1}\) non-negative
return_factor Float64 - between [0 - 1]
min_level Float64 \(m\) -
priority Int32 - positive, sorted per node id

10.1 UserDemand / time

This table is the transient form of the UserDemand table. The only difference is that a time column is added and activity is assumed to be true. The table must by sorted by time, and per time it must be sorted by node_id. With this the demand can be updated over time. In between the given times the demand is interpolated linearly, and outside the demand is constant given by the nearest time value. Note that a node_id can be either in this table or in the static one, but not both.

column type unit restriction
node_id Int32 - sorted
priority Int32 - positive, sorted per node id
time DateTime - sorted per priority per node id
demand Float64 \(m^3 s^{-1}\) non-negative
return_factor Float64 - between [0 - 1]
min_level Float64 \(m\) -

11 LevelDemand

A LevelDemand node associates a minimum and a maximum level with connected basins to be used by the allocation algorithm. Below the minimum level the basin has a demand of the supplied priority, above the maximum level the basin has a surplus and acts as a source, used by all nodes with demands in order of priority. The same LevelDemand node can be used for Basins in different subnetworks.

Both min_level and max_level are optional, to be able to handle only the demand or surplus side. If both are missing, LevelDemand won’t have any effects on allocation.

column type unit restriction
node_id Int32 - sorted
min_level Float64 \(m\) (optional, default -Inf)
max_level Float64 \(m\) (optional, default Inf)
priority Int32 - positive

11.1 LevelDemand / time

This table is the transient form of the LevelDemand table, in which time-dependent minimum and maximum levels can be supplied. Similar to the static version, only a single priority per LevelDemand node can be provided.

column type unit restriction
node_id Int32 - sorted
time DateTime - sorted per node id
min_level Float64 \(m\) -
max_level Float64 \(m\) -
priority Int32 - positive

12 FlowDemand

A FlowDemand node associates a non-consuming flow demand to a connector node (e.g. Pump, TabulatedRatingCurve) for one single priority. FlowDemand nodes can set a flow demand only for a single connector node.

column type unit restriction
node_id Int32 - sorted
priority Int32 - positive
demand Float64 \(m^3 s^{-1}\) non-negative

12.1 FlowDemand / time

This table is the transient form of the FlowDemand table, in which a time-dependent demand can be supplied. Similar to the static version, only a single priority per FlowDemand node can be provided.

column type unit restriction
node_id Int32 - sorted
time DateTime - sorted per node id
priority Int32 - positive
demand Float64 \(m^3 s^{-1}\) non-negative

13 LevelBoundary

Acts like an infinitely large basin where the level does not change by flow. This can be connected to a basin via a LinearResistance. This boundary node will then exchange water with the basin based on the difference in water level between the two.

column type unit restriction
node_id Int32 - sorted
active Bool - (optional, default true)
level Float64 \(m\) -

13.1 LevelBoundary / time

This table is the transient form of the LevelBoundary table. The only difference is that a time column is added and activity is assumed to be true. The table must by sorted by time, and per time it must be sorted by node_id. With this the levels can be updated over time. In between the given times the level is interpolated linearly, and outside the flow rate is constant given by the nearest time value. Note that a node_id can be either in this table or in the static one, but not both.

column type unit restriction
node_id Int32 - sorted
time DateTime - sorted per node_id
level Float64 \(m\) -

14 FlowBoundary

Pump water to a destination node. We require that the edge connecting the flow boundary to the Basin should point towards the basin, so that positive flow corresponds to water being added to the model. The set flow rate will be pumped unless the intake storage (for a negative flow rate) is less than \(10~m^3\), in which case the flow rate will be linearly reduced to \(0~m^3/s\). Note that the connected node must always be a Basin.

column type unit restriction
node_id Int32 - sorted
active Bool - (optional, default true)
flow_rate Float64 \(m^3 s^{-1}\) non-negative

14.1 FlowBoundary / time

This table is the transient form of the FlowBoundary table. The only differences are that a time column is added and the nodes are assumed to be active so this column is removed. The table must by sorted by time, and per time it must be sorted by node_id. With this the flow rates can be updated over time. In between the given times the flow rate is interpolated linearly, and outside the flow rate is constant given by the nearest time value. Note that a node_id can be either in this table or in the static one, but not both.

column type unit restriction
node_id Int32 - sorted
time DateTime - sorted per node_id
flow_rate Float64 \(m^3 s^{-1}\) non-negative

15 LinearResistance

Flow proportional to the level difference between the connected basins.

column type unit restriction
node_id Int32 - sorted
control_state String - (optional) sorted per node_id
active Bool - (optional, default true)
resistance Float64 \(sm^{-2}\) -
max_flow_rate Float64 \(m^3 s^{-1}\) non-negative

16 ManningResistance

Flow through this connection is estimated by conservation of energy and the Manning-Gauckler formula to estimate friction losses.

column type unit restriction
node_id Int32 - sorted
control_state String - (optional) sorted per node_id
active Bool - (optional, default true)
length Float64 \(m\) positive
manning_n Float64 \(s m^{-\frac{1}{3}}\) positive
profile_width Float64 \(m\) positive
profile_slope Float64 - -

17 Terminal

A terminal is a water sink without state or properties. Any water that flows into a terminal node is removed from the model. No water can come into the model from a terminal node. For example, terminal nodes can be used as a downstream boundary.

column type unit restriction
node_id Int32 - sorted

18 DisceteControl

18.1 DiscreteControl / 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 \(s\) Only on transient boundary conditions, non-negative (optional, default 0.0).

18.2 DiscreteControl / condition

The condition schema defines conditions of the form ‘the discrete_control node with this node_idlistens to whether the variable given by the node_id and compound_variable_id is greater than greater_than’. Multiple conditions with different greater_than values can be defined on the same compound_variable.

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

18.3 DiscreteControl / 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

19 PidControl

The PidControl node controls the level in a basin by continuously controlling the flow rate of a connected pump or outlet. See also PID controller. When A PidControl node is made inactive, the node under its control retains the last flow rate value, and the error integral is reset to 0.

In the future controlling the flow on a particular edge could be supported.

column type unit restriction
node_id Int32 - sorted
control_state String - (optional) sorted per node_id
active Bool - (optional, default true)
listen_node_type String - known node type
listen_node_id Int32 - -
target Float64 \(m\) -
proportional Float64 \(s^{-1}\) -
integral Float64 \(s^{-2}\) -
derivative Float64 - -

19.1 PidControl / time

This table is the transient form of the PidControl table. The differences are that a time column is added and the nodes are assumed to be active so this column is removed. The table must by sorted by time, and per time it must be sorted by node_id. With this the target level and PID coefficients can be updated over time. In between the given times the these values interpolated linearly, and outside these values area constant given by the nearest time value. Note that a node_id can be either in this table or in the static one, but not both.

column type unit restriction
node_id Int32 - sorted
time DateTime - sorted per node_id
listen_node_type Int32 - known node type
listen_node_id Int32 - -
target Float64 \(m\) -
proportional Float64 \(s^{-1}\) -
integral Float64 \(s^{-2}\) -
derivative Float64 - -

20 Results

20.1 Basin - basin.arrow

The Basin table contains:

  • Results of the storage and level of each Basin, which are instantaneous values;
  • Results of the fluxes on each Basin, which are mean values over the saveat intervals. In the time column the start of the period is indicated.
  • The initial condition is written to the file, but the final state is not. It will be placed in a separate output state file in the future.
  • The inflow_rate and outflow_rate are the sum of the flows from other nodes into and out of the Basin respectively. The actual flows determine in which term they are counted, not the edge direction.
  • The storage_rate is flow that adds to the storage in the Basin, increasing the water level. In the equations below this number is split out into two non-negative numbers, storage_increase and storage_decrease.
  • The balance_error is the difference of all Basin inflows (total_inflow) and outflows (total_outflow), that is (inflow_rate + precipitation + drainage - storage_increase) - (outflow_rate + evaporation + infiltration - storage_decrease). It can be used to check if the numerical error when solving the water balance is sufficiently small.
  • The relative_error is the fraction of the balance_error over the mean of the total_inflow and total_outflow.
column type unit
time DateTime -
node_id Int32 -
storage Float64 \(m^3\)
level Float64 \(m\)
inflow_rate Float64 \(m^3 s^{-1}\)
outflow_rate Float64 \(m^3 s^{-1}\)
storage_rate Float64 \(m^3 s^{-1}\)
precipitation Float64 \(m^3 s^{-1}\)
evaporation Float64 \(m^3 s^{-1}\)
drainage Float64 \(m^3 s^{-1}\)
infiltration Float64 \(m^3 s^{-1}\)
balance_error Float64 \(m^3 s^{-1}\)
relative_error Float64 -

The table is sorted by time, and per time it is sorted by node_id.

20.2 Flow - flow.arrow

The flow table contains calculated mean flows over the saveat intervals for every flow edge in the model. In the time column the start of the period is indicated.

column type unit
time DateTime -
edge_id Int32 -
from_node_type String -
from_node_id Int32 -
to_node_type String -
to_node_id Int32 -
flow_rate Float64 \(m^3 s^{-1}\)

The table is sorted by time, and per time the same edge_id order is used, though not sorted. The edge_id value is the same as the fid written to the Edge table, and can be used to directly look up the Edge geometry. Flows from the “from” to the “to” node have a positive sign, and if the flow is reversed it will be negative.

20.3 DiscreteControl - control.arrow

The control table contains a record of each change of control state: when it happened, which control node was involved, to which control state it changed and based on which truth state.

column type
time DateTime
control_node_id Int32
truth_state String
control_state String

20.4 Allocation - allocation.arrow

The allocation table contains a record of allocation results: when it happened, for which node, in which allocation network, and what the demand, allocated flow and realized flow were.

column type
time DateTime
subnetwork_id Int32
node_type String
node_id Int32
priority Int32
demand Float64
allocated Float64
realized Float64

For Basins the values demand, allocated and realized are positive if the Basin level is below the minimum level given by a LevelDemand node. The values are negative if the Basin supplies due to a surplus of water.

Note

Currently the stored demand and abstraction rate are those at the allocation timepoint (and the abstraction rate is based on the previous allocation optimization). In the future these will be an average over the previous allocation timestep.

20.5 Allocation flow - allocation_flow.arrow

The allocation flow table contains results of the optimized allocation flow on every edge in the model that is part of a subnetwork, for each time an optimization problem is solved (see also here). If in the model a main network and subnetwork(s) are specified, there are 2 different types of optimization for the subnetwork: collecting its total demand per priority (for allocating flow from the main network to the subnetwork), and allocating flow within the subnetwork. The column collect_demands provides the distinction between these two optimization types.

column type
time DateTime
edge_id Int32
from_node_type String
from_node_id Int32
to_node_type String
to_node_id Int32
subnetwork_id Int32
priority Int32
flow_rate Float64
collect_demands Bool

References

Shampine, Lawrence F, and Mark W Reichelt. 1997. “The Matlab Ode Suite.” SIAM Journal on Scientific Computing 18 (1): 1–22.