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 tableBasin / profile
: geometries of the basinsBasin / time
: time series of the forcing valuesBasin / state
: used as initial condition of the basinsBasin / 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
: levelsLevelBoundary / time
: dynamic levels
- FlowBoundary: sets a precribed flow into the model
FlowBoundary / static
: flow rateFlowBoundary / 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 curveTabulatedRatingCurve / 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
: demandsUserDemand / time
: dynamic demands
- LevelDemand: Indicates minimum and maximum target level of connected basins for allocation
LevelDemand / static
: static target levelsLevelDemand / time
: dynamic target levels
- FlowDemand: sets non-consuming flow demand
FlowDemand / static
: flow demandsFlowDemand / 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 levelsDisceteControl / condition
: Conditions of the form ‘the compound variable with IDn
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 controlledPidControl / time
: same asstatic
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:
- “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. - “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 inDiscreteControl / 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_id
listens 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”.
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
andoutflow_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
andstorage_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 thebalance_error
over the mean of thetotal_inflow
andtotal_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.
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 |