diff --git a/docs/_quarto.yml b/docs/_quarto.yml index 9be70ea44..db0e2c94c 100644 --- a/docs/_quarto.yml +++ b/docs/_quarto.yml @@ -26,6 +26,7 @@ website: - title: "Julia core" contents: - core/index.qmd + - core/modelconcept.qmd - core/usage.qmd - core/validation.qmd - core/equations.qmd diff --git a/docs/contribute/core.qmd b/docs/contribute/core.qmd index 2bbcdf1d5..cb83dd50f 100644 --- a/docs/contribute/core.qmd +++ b/docs/contribute/core.qmd @@ -4,70 +4,15 @@ title: "Julia core development" # Julia core overview -This section is about the Julia core in `Ribasim.jl`. See the component overview [here](index.qmd) for the context of this computational core. +The computational core is one of the components of Ribasim as illustrated in the [component overview](../index.qmd#sec-components). -`Ribasim.jl` can be divided into 3 parts: +The computational process can be divided in three phases: - Model initialization - Running the simulation loop - Writing the output files -The figure below gives a more detailed description of the simulation loop in the form of a [sequence diagram](https://en.wikipedia.org/wiki/Sequence_diagram). From top to bottom, it contains the following blocks: - -- Allocation optimization; activated when the allocation timestep has been passed; -- Control actions; activated when some discrete control callback is triggered; -- Water balance; computing the flows over flow edges happens each timestep; -- Time integration step; done by the integrator from `OrdinaryDiffEq.jl`. - -```{mermaid, style="margin-top: 0"} -sequenceDiagram - autonumber - participant Int as Process: Integrator - participant Optim as Process: Allocation optimization - participant Param as Data: Parameters - participant State as Data: State - participant Sim as Process: Water balance - loop Simulation loop (OrdinaryDiffEq.jl) - activate Int - %% Allocation - rect rgb(200, 200, 200) - opt Allocation optimization, per allocation network (JuMP.jl, HiGHS) - activate Optim - Int->>Optim: Callback: allocation timestep has passed - Param-->>Optim: Input - State-->>Optim: Input - Optim->>Optim: Optimize Basin allocations if below target level - Optim->>Optim: Optimize User allocation, per priority - Optim-->>Param: Set allocated flow rates - deactivate Optim - end - end - %% Control - rect rgb(200, 200, 200) - opt Control actions - Int->>Int: DiscreteControl callback - Int-->>Param: Parameter updates by control - end - end - %% water_balance! - rect rgb(200, 200, 200) - activate Sim - State-->>Sim: Input - Param-->>Sim: Input - Sim->>Sim: Compute flows over edges per node type - Sim-->>Param: Set flows - deactivate Sim - end - %% Time integration - rect rgb(200, 200, 200) - State-->>Int: Input - Param-->>Int: Input - Int->>Int: Time integration step - Int-->>State: Update state - end - deactivate Int - end -``` +A more detailed sequence diagram of the simulation loop is available at the [core](index.qmd#sec-simulationloop) home page. # Set up the developer environment @@ -101,7 +46,7 @@ Make sure to have the correct environment when opening your IDE by running `pixi You will want to run the testsuite on a regular basis to check if your changes had unexpected side effects. It is also a good way to find out if your development environment is set up correctly. -Before the tests can run, you need to [prepare model input](./python.qmd#prepare-model-input). +Before the tests can run, you need to [prepare model input](python.qmd#prepare-model-input). With the root of the repository as your working directory you can start the REPL with activated `Ribasim` environment by running the following: diff --git a/docs/contribute/qgis.qmd b/docs/contribute/qgis.qmd index 688ee00ce..062214d42 100644 --- a/docs/contribute/qgis.qmd +++ b/docs/contribute/qgis.qmd @@ -39,7 +39,7 @@ Then simply call `pixi run test-ribasim-qgis`. After installing the plugins via `pixi run install-qgis-plugins`. Extra debugging tools are also installed in QGIS that is installed within your pixi environment. -After you have started `pixi run qgis`, you can make alterations to the python code and use the [Plugin Reloader](https://github.com/borysiasty/plugin_reloader) to reload the plugin without restarting QGIS. +After you have started `pixi run qgis`, you can make alterations to the Python code and use the [Plugin Reloader](https://github.com/borysiasty/plugin_reloader) to reload the plugin without restarting QGIS. The shortcut in QGIS is `CTRL+F5`. It is also possible to connect the debugger of Visual Studio Code. diff --git a/docs/core/index.qmd b/docs/core/index.qmd index 55a864059..82445ff6d 100644 --- a/docs/core/index.qmd +++ b/docs/core/index.qmd @@ -5,6 +5,7 @@ title: "Julia core" With the term "core", we mean the computational engine of Ribasim. As detailed in the [usage](usage.qmd) documentation, it is generally used as a command line tool. +A quick overview of the model concept is available at the [home page](../index.qmd#sec-concept), while a more in depth discussion is available on the [model concept](modelconcept.qmd) page. The theory is described on the [equations](equations.qmd) page, and more in-depth numerical considerations are described on the [numerical considerations](numerics.qmd) page. As allocation is a large and self-contained part of the Ribasim core, it is described on the separate [allocation](allocation.qmd) page. Input validation is described on the [validation](validation.qmd) page. The core is implemented in the [Julia programming language](https://julialang.org/), and @@ -12,32 +13,112 @@ can be found in the [Ribasim repository](https://github.com/Deltares/Ribasim) un `core/` folder. For developers we also advise to read the [developer documentation](../contribute/core.qmd). +An overview of all components is given on the [home page](../index.qmd#sec-components) + +# The simulation loop {#sec-simulationloop} + +The computational process can be divided in three phases: + +- Model initialization +- Running the simulation loop +- Writing the output files + +The figure below gives a more detailed description of the simulation loop in the form of a [sequence diagram](https://en.wikipedia.org/wiki/Sequence_diagram). From top to bottom, it contains the following blocks: + +- Allocation optimization; activated when the allocation timestep has been passed; +- Control actions; activated when some discrete control callback is triggered; +- Water balance; computing the flows over flow edges happens each timestep; +- Time integration step; done by the integrator from `OrdinaryDiffEq.jl`. + ```{mermaid} -%%| file: ../assets/c4_component_ribasim.mmd -%%| fig-cap: "Component overview of Ribasim" +sequenceDiagram + autonumber + participant Int as Process: Integrator + participant Optim as Process: Allocation optimization + participant Param as Data: Parameters + participant State as Data: State + participant Sim as Process: Water balance + loop Simulation loop (OrdinaryDiffEq.jl) + activate Int + %% Allocation + rect rgb(200, 200, 200) + opt Allocation optimization, per allocation network (JuMP.jl, HiGHS) + activate Optim + Int->>Optim: Callback: allocation timestep has passed + Param-->>Optim: Input + State-->>Optim: Input + Optim->>Optim: Optimize Basin allocations if below target level + Optim->>Optim: Optimize User allocation, per priority + Optim-->>Param: Set allocated flow rates + deactivate Optim + end + end + %% Control + rect rgb(200, 200, 200) + opt Control actions + Int->>Int: DiscreteControl callback + Int-->>Param: Parameter updates by control + end + end + %% water_balance! + rect rgb(200, 200, 200) + activate Sim + State-->>Sim: Input + Param-->>Sim: Input + Sim->>Sim: Compute flows over edges per node type + Sim-->>Param: Set flows + deactivate Sim + end + %% Time integration + rect rgb(200, 200, 200) + State-->>Int: Input + Param-->>Int: Input + Int->>Int: Time integration step + Int-->>State: Update state + end + deactivate Int + end ``` -# The simulation loop +# Nested allocation {#sec-nested-allocation} -The figure below shows a simple flowchart of the simulation in `Ribasim.jl`. +Since water systems may be extensive, like in the Netherlands, Ribasim models may become large networks with over ten thousand nodes. +To keep a proper functioning allocation concept under these circumstances, the modeller can decompose the network domain into a main network and multiple sub-networks. +The allocation will then be conducted in three steps: + +1. conduct an inventory of demands from the sub-networks to inlets from the main network, +2. allocate the available water in the main network to the subnetworks inlets, +3. allocate the assigned water within each subnetwork to the individual water users. + +The users then will request this updated demand from the rule-based simulation. +Whether this updated demand is indeed abstracted depends on all dry-fall control mechanism implemented in the rule-based simulation. + +The following sequence diagram illustrates this calculation process within then allocation phase. ```{mermaid} -flowchart LR -Start((Start)) -Init[Initialize model] -Con[Conditional: allocation, control] -Sim[Simulate flows over timestep] -Finished{End of simulation period?} -Done((Done)) - -Start --> Init -Init --> Con -Con --> Sim -Sim --> Finished -Finished -->|no| Con -Finished -->|yes| Done -``` +sequenceDiagram +participant boundary +participant basin +participant user +participant allocation_subNetwork +participant allocation_mainNetwork +user->>allocation_subNetwork: demand +loop + allocation_subNetwork-->>allocation_mainNetwork: demand inventory at inlets +end +user->>allocation_mainNetwork: demand +boundary->>allocation_mainNetwork: source availability +basin->>allocation_mainNetwork: source availability +allocation_mainNetwork-->>allocation_mainNetwork: allocate to inlets (and users) +allocation_mainNetwork->>user: allocated +allocation_mainNetwork->>allocation_subNetwork: allocated +loop + allocation_subNetwork-->>allocation_subNetwork: allocate to users +end +allocation_subNetwork->>user: allocated +user->>basin: abstracted +``` # Coupling diff --git a/docs/core/modelconcept.qmd b/docs/core/modelconcept.qmd new file mode 100644 index 000000000..8f13075db --- /dev/null +++ b/docs/core/modelconcept.qmd @@ -0,0 +1,126 @@ +--- +title: Model concept +--- +A brief summary of the concept is given on the documentation [home page](../index.qmd#sec-concept). +As indicated, the model concept is organized in three layers: + +- a physical layer representing water bodies and associated infrastructure, +- a rule-based control layer to manage the infrastructure, and +- a priority-based allocation layer to take centralized decisions on user abstractions. + +# Physical layer + +## Water balance equations + +The water balance equation for a drainage basin [@enwiki:1099736933] can be defined by a first-order ordinary differential equation (ODE), where the change of the storage $S$ over time is determined by the inflow fluxes minus the outflow fluxes. + +$$ +\frac{\mathrm{d}S}{\mathrm{d}t} = Q_{in} - Q_{out} +$$ + +We can split out the fluxes into separate terms, such as precipitation $P$, evapotranspiration $ET$ and runoff $R$. +For now other fluxes are combined into $Q_{rest}$. +If we define all fluxes entering our reservoir as positive, and those leaving the system as negative, all fluxes can be summed up. + +$$ +\frac{\mathrm{d}S}{\mathrm{d}t} = R + P + ET + Q_{rest} +$$ + +## Time + +The water balance equation can be applied on many timescales; years, weeks, days or hours. +Depending on the application and available data any of these can be the best choice. +In Ribasim, we make use of DifferentialEquations.jl and its [ODE solvers](https://diffeq.sciml.ai/stable/solvers/ode_solve/). +Many of these solvers are based on adaptive time stepping, which means the solver will decide how large the time steps can be depending on the state of the system. + +The forcing, like precipitation, is generally provided as a time series. +Ribasim is set up to support unevenly spaced timeseries. +The solver will stop on timestamps where new forcing values are available, so they can be loaded as the new value. + +Ribasim is essentially a continuous model, rather than daily or hourly. +If you want to use hourly forcing, you only need to make sure that your forcing data contains hourly updates. +The output frequency can be configured independently. +To be able to write a closed water balance, we accumulate the fluxes. +This way any variations in between timesteps are also included, and we can output in `m³` rather than `m³s⁻¹`. + +## Space {#sec-space} + +The water balance equation can be applied on different spatial scales. +Besides modelling a single lumped watershed, it allows you to divide the area into a network of connected representative elementary watersheds (REWs) [@REGGIANI1998367]. +At this scale global water balance laws can be formulated by means of integration of point-scale conservation equations +over control volumes. +Such an approach makes Ribasim a semi-distributed model. +In this document we typically use the term "basin" to refer to the REW. +Each basin has an associated polygon, and the set of basins is connected to each other as described by a graph, which we call the network. +Below is a representation of both on the map. + +![Mozart Local Surface Water polygons and their drainage.](https://user-images.githubusercontent.com/4471859/185932183-62c305e6-bc14-4f3c-a74c-437f831c9145.png) + +The network is described as graph. +Flow can be bi-directional, and the graph does not have to be acyclic. + +```{mermaid} +graph LR; + A["basin A"] --- B["basin B"]; + A --- C["basin C"]; + B --- D["basin D"]; + C --- D; +``` + +Internally a directed graph is used. +The direction is defined to be the positive flow direction, and is generally set in the dominant flow direction. +The basins are the nodes of the network graph. +Basin states and properties such storage volume and wetted area are associated with the nodes (A, B, C, D), as are most forcing data such as precipitation, evaporation, or water demand. +Basin connection properties and interbasin flows are associated with the edges (the +lines between A, B, C, and D) instead. + +Multiple basins may exist within the same spatial polygon, representing different aspects of the surface water system (perennial ditches, ephemeral ditches, or even surface ponding). +@fig-p, @fig-s, @fig-t show the 25.0 m rasterized primary, secondary, and tertiary surface waters as identified by BRT TOP10NL [@pdoktopnl] in the Hupsel basin. +These systems may represented in multiple ways. + +![Hupsel: primary surface water.](https://user-images.githubusercontent.com/13662783/187625163-d0a81bb6-7f55-4ad1-83e2-90ec1ee79740.PNG){#fig-p} + +![Hupsel: secondary surface water.](https://user-images.githubusercontent.com/13662783/187625170-1acdfb41-7077-443f-b140-ae18cbf21e53.PNG){#fig-s} + +![Hupsel: tertiary surface water.](https://user-images.githubusercontent.com/13662783/187625174-3eec28b5-ddbb-4870-94c3-d9e9a43f8eb4.PNG){#fig-t} + +As a single basin (A) containing all surface water, discharging to its downstream basin to the west (B): + +```{mermaid} +graph LR; + A["basin A"] --> B["basin B"]; +``` + +Such a system may be capable of representing discharge, but it cannot represent residence times or differences in solute concentrations: within a single basin, a drop of water is mixed instantaneously. +Instead, we may the group primary (P), secondary (S), and tertiary (T) surface waters. +Then T may flow into S, S into P, and P discharges to the downstream basin (B.) + +```{mermaid} +graph LR; + T["basin T"] --> S["basin S"]; + S --> P["basin P"]; + P --> B["basin B"]; +``` + +As each (sub)basin has its own volume, low throughput (high volume, low discharge, long residence time) and high throughput (low volume, high discharge, short residence time) systems +can be represented in a lumped manner; of course, more detail requires more parameters. + +## Structures in a water system +In addition to free flowing waterbodies, a watersystem typically has structures to control the flow of water. Ribasim uses connector nodes which simplify the hydraulic behaviour for the +free flowing conditions or structures. +The following type of connector nodes are available for this purpose: + +- [TabulatedRatingCurve](.\usage.qmd#sec-tabulated-rating-curve): one-directional flow based on upstream head. Node type typically used for gravity flow conditions either free flowing open water channels +or over a fixed structure. +- [LinearResistance](.\usage.qmd#sec-linear-resistance): bi-directional flow based on head difference and linear resistance. Node type typically used for bi-directional flow +situations or situations where head difference over a structure determines its actual flow capacity. +- [ManningResistance](.\usage.qmd#sec-manning-resistance): bi-directional flow based on head difference and resistance using Manning-Gauckler formula. Same usage as LinearResistance, +providing a better hydrological meaning to the resistance parameterization. +- [Pump](.\usage.qmd#sec-pump): one-directional structure with a set flow rate. Node type typically used in combination with control to force water over the edge. +- [Outlet](.\usage.qmd#sec-outlet): one-directional gravity structure with a set flow rate. Node type typically used in combination with control to force water over the edge, even if +their is a mismatch in actual hydraulic capacity. The node type has an automated mechanism to stop the flow when the head difference is zero. +- [FractionalFlow](.\usage.qmd#sec-fractional-flow): to split an outflow over multiple edges based on a flow fraction. Node type is typically used for diversions or bifurcations with a known and fixed ratio. + +The control layer can activate or deactivate nodes, set flow rates for the Pump and Outlet, or choose different parameterizations for TabulatedRatingCurve, LinearResistance, ManningResistance or FractionalFlow. + +Connector nodes are required within a Ribasim network to determine the flow exchange between basins. diff --git a/docs/core/usage.qmd b/docs/core/usage.qmd index 92a9c64fa..fe1cb5013 100644 --- a/docs/core/usage.qmd +++ b/docs/core/usage.qmd @@ -23,6 +23,7 @@ Usage: ribasim 'path/to/model/ribasim.toml' Ribasim has a single configuration file, which is written in the [TOML](https://toml.io/) format. It contains settings, as well as paths to other input and output files. +Ribasim expects the GeoPackage database [database.gpkg](usage.qmd#sec-geopackage) as well as optional Arrow input files to be available in the input_dir. ```{.toml include="../../core/test/docs.toml"} ``` @@ -89,7 +90,7 @@ entry | type | description verbosity | String | Verbosity level: debug, info, warn, or error. timing | Bool | Enable timings. -# GeoPackage database and Arrow tables +# GeoPackage database and Arrow tables {#sec-geopackage} 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](https://www.geopackage.org/) database file, and @@ -143,10 +144,10 @@ objects such as weirs might have specific identification codes. Additional colum added to tables. The column names should be prefixed with `meta_`. They will not be used in computations or validated by the Julia core. -The ribasim-python library will automatically add the `meta_` prefix to non-standard columns. +The Ribasim Python library will automatically add the `meta_` prefix to non-standard columns. Column names already prefixed with `meta_` will not be updated. -# Node +# Node {#sec-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. @@ -210,7 +211,7 @@ Adding a geometry to the node table can be helpful to examine models in [QGIS](https://qgis.org/en/site/), as it will show the location of the nodes on the map. The geometry is not used by Ribasim. -# Edge +# Edge {#sec-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: @@ -225,7 +226,7 @@ There are currently 2 possible edge types: 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 \ static`, these are defined in [Control / condition](usage.qmd#sec-condition) + The edges between the control node and the nodes it *listens* to are *not* present in `Edge`, these are defined in [`DiscreteControl / condition`](usage.qmd#sec-discrete-control) column | type | restriction --------------------- | -------- | ----------- @@ -240,7 +241,7 @@ allocation_network_id | Int | (optional, denotes source in allocation netwo 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. -# Basin +# Basin {#sec-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 @@ -372,7 +373,7 @@ The table is sorted by time, and per time the same `edge_id` order is used, thou Flows that are added to the model at a node, have a missing `edge_id`, and identical `from_node_id` and `to_node_id`. Flows out of the model always have a negative sign, and additions a positive sign. -# FractionalFlow +# FractionalFlow {#sec-fractional-flow} Lets a fraction (in [0,1]) of the incoming flow trough. @@ -382,7 +383,7 @@ node_id | Int | - | sorted control_state | String | - | (optional) sorted per node_id fraction | Float64 | - | in the interval [0,1] -# TabulatedRatingCurve +# TabulatedRatingCurve {#sec-tabulated-rating-curve} 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. @@ -418,7 +419,7 @@ time | DateTime | - | sorted per node_id level | Float64 | $m$ | - flow_rate | Float64 | $m^3 s^{-1}$ | non-negative -# Pump +# Pump {#sec-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$, @@ -435,7 +436,7 @@ 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) -# Outlet +# Outlet {#sec-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. @@ -450,7 +451,7 @@ 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) -# User +# User {#sec-user} A user 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. @@ -509,7 +510,7 @@ abstracted | Float64 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. ::: -# LevelBoundary +# LevelBoundary {#sec-level-boundary} Acts like an infinitely large basin where the level does not change by flow. This can be connected to a basin via a `LinearResistance`. @@ -538,7 +539,7 @@ node_id | Int | - | sorted time | DateTime | - | sorted per node_id level | Float64 | $m$ | - -# FlowBoundary +# FlowBoundary {#sec-flow-boundary} Pump water to a destination node. We require that the edge connecting the flow boundary to the Basin should point towards the basin, @@ -569,7 +570,7 @@ node_id | Int | - | sorted time | DateTime | - | sorted per node_id flow_rate | Float64 | $m^3 s^{-1}$ | non-negative -# LinearResistance +# LinearResistance {#sec-linear-resistance} Flow proportional to the level difference between the connected basins. @@ -580,7 +581,7 @@ control_state | String | - | (optional) sorted per node_id active | Bool | - | (optional, default true) resistance | Float64 | $sm^{-2}$ | - -# ManningResistance +# ManningResistance {#sec-manning-resistance} Flow through this connection is estimated by conservation of energy and the Manning-Gauckler formula to estimate friction losses. @@ -594,7 +595,7 @@ manning_n | Float64 | $s m^{-\frac{1}{3}}$ | positive profile_width | Float64 | $m$ | positive profile_slope | Float64 | - | - -# Terminal +# Terminal {#sec-terminal} A terminal is a water sink without state or properties. Any water that flows into a terminal node is removed from the model. @@ -605,11 +606,11 @@ column | type | unit | restriction --------- | ------- | ------------ | ----------- node_id | Int | - | sorted -# DisceteControl +# DisceteControl {#sec-discrete-control} DiscreteControl is implemented based on [VectorContinuousCallback](https://docs.sciml.ai/DiffEqDocs/stable/features/callback_functions/#VectorContinuousCallback). -## DiscreteControl / condition {#sec-condition} +## DiscreteControl / condition The condition schema defines conditions of the form 'the discrete_control node with this node id listens to whether the given variable of the node with the given listen feature id is grater than the given value'. If the condition variable comes from a time-series, a look ahead $\Delta t$ can be supplied. @@ -651,7 +652,7 @@ control_node_id | Int truth_state | String control_state | String -# PidControl +# PidControl {#sec-pid-control} 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](https://en.wikipedia.org/wiki/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. diff --git a/docs/index.qmd b/docs/index.qmd index 7cb85040d..a478c0477 100644 --- a/docs/index.qmd +++ b/docs/index.qmd @@ -1,171 +1,167 @@ --- -title: "Ribasim" +title: Ribasim quick overview --- -Ribasim is a water resources model, designed to be the replacement of the regional surface -water modules Mozart and SIMRES in the Netherlands Hydrological Instrument (NHI). Ribasim is -a work in progress, it is a prototype that demonstrates all essential functionalities. -Further development of the prototype in a software release is planned in 2022 and 2023. +# Introduction {#sec-introduction} -Ribasim is written in the [Julia programming language](https://julialang.org/) and is built -on top of the [SciML: Open Source Software for Scientific Machine Learning](https://sciml.ai/) -libraries, notably [DifferentialEquations.jl](https://docs.sciml.ai/DiffEqDocs/stable/). +Decision makers need to balance the supply and demand of water at the river basin scale, under increasing environmental pressure. +Ribasim allows users to model basins under current and changing conditions to evaluate and design and management of the water system. +It is available as free and open source software under the MIT license. +Besides a model simulation core, Ribasim also includes tooling to assist in building models from basic datasets and visualize results. +The model and its results provides insights to decision makers, enabling them to build consensus amongst water users and make informed decisions about how to manage water resources optimally. -::: {layout-ncol=2 layout-valign="bottom"} - - Deltares logo - +The model concept of Ribasim is composed of multiple layers: +- a physical layer representing water bodies and associated infrastructure as well as abstractions, +- a rule-based control layer to manage the infrastructure, and +- (optionally) a priority-based allocation layer to take centralized decisions on user abstractions. +- (optionally) a coupling layer to exchange fluxes and heads with other kernels - - NHI logo - -::: +Typically hydrological processes on land will be represented in detail by other models which can be coupled (online) to Ribasim with the help of iMOD Coupler. +Currently, an online coupling with MODFLOW 6 (groundwater) and with Metaswap + MODFLOW 6 (unsaturated zone + groundwater) is available. +The corresponding documentation can be found within the [iMOD Suite Documentation](https://deltares.github.io/iMOD-Documentation/coupler.html). -# Download {#sec-download} +This version of Ribasim is the follow up of the legacy Fortran kernel of Ribasim (version 7) applied world wide, the Fortran kernel SIMRES applied in the Netherlands, and the surface water models Distribution Model and Mozart of the Dutch National Hydrological Instrument. -- Ribasim executable - Linux: [ribasim_cli_linux.zip](https://github.com/Deltares/Ribasim/releases/latest/download/ribasim_cli_linux.zip) -- Ribasim executable - Windows: [ribasim_cli_windows.zip](https://github.com/Deltares/Ribasim/releases/latest/download/ribasim_cli_windows.zip) -- QGIS plugin: [ribasim_qgis.zip](https://github.com/Deltares/Ribasim/releases/latest/download/ribasim_qgis.zip). -- Generated testmodels: [generated_testmodels.zip](https://github.com/Deltares/Ribasim/releases/latest/download/generated_testmodels.zip) - -The nightly builds contain the latest developments and can be found below. It is important to either use the release or nightly for all components. - -- Ribasim executable: [ribasim_cli.zip](https://ribasim.s3.eu-west-3.amazonaws.com/teamcity/Ribasim_Ribasim/BuildRibasimCliWindows/latest/ribasim_cli.zip). -- QGIS plugin: [ribasim_qgis.zip](https://ribasim.s3.eu-west-3.amazonaws.com/teamcity/Ribasim_Ribasim/BuildRibasimCliWindows/latest/ribasim_qgis.zip). +# Concept {#sec-concept} -Currently only Windows builds for `ribasim_cli.zip` are available. +## Physical layer {#sec-physical} -See [Usage](core/usage.qmd) for more information. +To represent the physical characteristics of the water system in an area, Ribasim allows you to divide the area into a network of connected representative elementary watersheds ([Reggiani, Sivapalan, and Majid Hassanizadeh 1998](https://deltares.github.io/Ribasim/#ref-REGGIANI1998367)). +Within Ribasim, these elements are called basins, which are essentially buckets or reservoirs holding an aggregated volume of water bodies in an area. +Basins are chained in a graph with connector nodes determining the exchange of water between the basins. +These connector nodes can represent open water connections (e.g. bifurcations or resistance in a free flowing open water channel) or infrastructure elements such as pumps, gates or weirs. +An overview of node types and associated data inputs is provided on the [usage page](core/usage.qmd), while the associated mathematical formations are described on the [equations page](core/equations.qmd). -```{mermaid} -%%| file: assets/c4_system.mmd -%%| fig-cap: "System overview of Ribasim" -``` - -# Status +## Control layer {#sec-control} -The initial focus is on being able to -reproduce the Mozart regional surface water reservoir results. Each component is defined by -a set of symbolic equations, and can be connected to each other. From this a simplified -system of equations is generated automatically. We use solvers with adaptive time stepping -from [DifferentialEquations.jl](https://diffeq.sciml.ai/stable/) to get results. +Infrastructure elements are often controlled by humans to implement a certain water management strategy. +Ribasim allows the configuration of conditional rules to influence the exchange of water between basins, either by setting inflow or outflow, or by controlling a water level. +Control rules evaluate one or multiple conditions to change a parameter setting of an infrastructure element when the conditional criteria are met. +Conditions can be either calculated values within the network as well as boundary conditions or (todo) external observations, i.e. observation values external to the model. +An overview of node types and associated data inputs is provided on the [usage page](core/usage.qmd), while the associated mathematical formations are described on the [equations page](core/equations.qmd). -![Example timeseries of a single basin, the Hupselse Beek, with the input and output fluxes on the top, and the storage volume (the state) below.](https://user-images.githubusercontent.com/4471859/179259333-070dfe18-8f43-4ac4-bb38-013b252e2e4b.png) +## Allocation layer {#sec-allocation} -![Example bar plot of the daily waterbalance for the Hupselse Beek, comparing results of Mozart (left) and Ribasim (right).](https://user-images.githubusercontent.com/4471859/179259174-0caccd4a-c51b-449e-873c-17d48cfc8870.png) +Ribasim allows water users (water demands) to abstract water from the basins (i.e. from the physical layer) unless the water level drops below a minimum level. +Under dry conditions, water managers may want to prioritize some abstractions over other abstractions. +The Ribasim allocation layer can take care of this prioritization by reducing the abstraction rates of lower-priority demands to ensure that sufficient water remains available in the system for the higher-priority demands. +The associated mathematical formulations are described on the [allocation page](core/allocation.qmd). +In case of large networks, a subdivision in a main network with subnetworks is recommended. +For more details see the explanation of the [simulation loop](core/index.qmd#sec-nested-allocation) at the Julia core home page. +The layers and the main components and dataflows between the layers are shown in the next figure: -# Introduction -## Water balance equations - -The water balance equation for a drainage basin [@enwiki:1099736933] can be -defined by a first-order ordinary differential equation (ODE), where the change of -the storage $S$ over time is determined by the inflow fluxes minus the outflow -fluxes. - -$$ -\frac{\mathrm{d}S}{\mathrm{d}t} = Q_{in} - Q_{out} -$$ +```{mermaid} +flowchart TB +physical:::layer +rbc:::layer +allocation:::layer +user +basin +connector[basin connector] +control[control rules] +condition +alloc[global allocation] + +subgraph physical[physical layer] + user-->|abstraction| basin + basin<-->|flow| connector +end + +subgraph rbc[rule based control layer] + condition --> control +end + +subgraph allocation[allocation layer] + alloc +end + +user-->|request demand| alloc +alloc-->|assign allocation| user +basin-->|volume| alloc +basin --> |volume or level| condition +alloc --> |optional flow update| control +control --> |action| connector + +%% class definitions for C4 model +classDef layer fill:transparent,stroke-dasharray:5 5 +``` -We can split out the fluxes into separate terms, such as precipitation $P$, -evapotranspiration $ET$ and runoff $R$. For now other fluxes are combined into -$Q_{rest}$. If we define all fluxes entering our reservoir as positive, and those -leaving the system as negative, all fluxes can be summed up. -$$ -\frac{\mathrm{d}S}{\mathrm{d}t} = R + P + ET + Q_{rest} -$$ -## Time +# About the components {#sec-components} +The figure below illustrates the relation between the various components of the Ribasim software package. -The water balance equation can be applied on many timescales; years, weeks, days or hours. -Depending on the application and available data any of these can be the best choice. -In Ribasim, we make use of DifferentialEquations.jl and its [ODE solvers](https://diffeq.sciml.ai/stable/solvers/ode_solve/). -Many of these solvers are based on adaptive time stepping, which means the solver will -decide how large the time steps can be depending on the state of the system. +```{mermaid} +flowchart TB +modeler([Modeler]):::user -The forcing, like precipitation, is generally provided as a time series. Ribasim is set up -to support unevenly spaced timeseries. The solver will stop on timestamps where new forcing -values are available, so they can be loaded as the new value. +api["Ribasim Python\n[python]"] +modeler-->|prepare model|api -Ribasim is essentially a continuous model, rather than daily or hourly. If you want to use -hourly forcing, you only need to make sure that your forcing data contains hourly updates. -The output frequency can be configured independently. To be able to write a closed water -balance, we accumulate the fluxes. This way any variations in between timesteps are also -included, and we can output in `m³` rather than `m³s⁻¹`. +ribasim["Ribasim\n[julia]"] +modeler-->|start|ribasim -## Space {#sec-space} +subgraph qgisBoundary[QGIS] + QGIS[QGIS Application]:::system_ext + qgisPlugin["Ribasim QGIS plugin\n[python]"] + QGIS-->qgisPlugin +end +modeler-->|prepare model|qgisBoundary -The water balance equation can be applied on different spatial scales. Besides modelling a -single lumped watershed, it allows you to divide the area into a network of connected -representative elementary watersheds (REWs) [@REGGIANI1998367]. At this scale global water -balance laws can be formulated by means of integration of point-scale conservation equations -over control volumes. Such an approach makes Ribasim a semi-distributed model. In this document -we typically use the term "basin" to refer to the REW. (In Mozart the spatial unit was called -Local Surface Water (LSW)). Each basin has an associated polygon, and the set of basins is -connected to each other as described by a graph, which we call the network. Below is a -representation of both on the map. +model[("input model data\n[toml + geopackage + arrow]")] +qgisPlugin-->|read/write|model +api-->|read/write|model +ribasim-->|simulate|model -![Mozart Local Surface Water polygons and their drainage.](https://user-images.githubusercontent.com/4471859/185932183-62c305e6-bc14-4f3c-a74c-437f831c9145.png) +output[("simulation results\n[arrow]")] +ribasim-->|write|output -The network is described as graph. Flow can be bi-directional, and the graph does not have -to be acyclic. +class qgisBoundary boundary -```{mermaid} -graph LR; - A["basin A"] --- B["basin B"]; - A --- C["basin C"]; - B --- D["basin D"]; - C --- D; +%% class definitions for C4 model +classDef user fill:#ABD0BC +classDef system_ext fill:#D2D2D2 +classDef boundary fill:transparent,stroke-dasharray:5 5 ``` -Internally a directed graph is used. The direction is defined to be the -positive flow direction, and is generally set in the dominant flow direction. -The basins are the nodes of the network graph. Basin states and properties such -storage volume and wetted area are associated with the nodes (A, B, C, D), as are -most forcing data such as precipitation, evaporation, or water demand. Basin -connection properties and interbasin flows are associated with the edges (the -lines between A, B, C, and D) instead. +The kernel of Ribasim is written in the [Julia programming language](https://julialang.org/) and is built on top of the [SciML: Open Source Software for Scientific Machine Learning](https://sciml.ai/) libraries, notably [DifferentialEquations.jl](https://docs.sciml.ai/DiffEqDocs/stable/). -Multiple basins may exist within the same spatial polygon, representing -different aspects of the surface water system (perennial ditches, ephemeral -ditches, or even surface ponding). @fig-p, @fig-s, @fig-t show the 25.0 m -rasterized primary, secondary, and tertiary surface waters as identified by BRT -TOP10NL [@pdoktopnl] in the Hupsel basin (as defined in the Mozart LSW's). -These systems may represented in multiple ways. +The [Ribasim Python package](https://deltares.github.io/Ribasim/python/) is available to build, update and analyze Ribasim models programmatically. +For runtime data exchange and coupling with other kernels, the Julia kernel is wrapped in a Python API (`ribasim_api`) which implements the Basic Modelling Interface [BMI](https://bmi-spec.readthedocs.io/en/latest/). -![Hupsel: primary surface water.](https://user-images.githubusercontent.com/13662783/187625163-d0a81bb6-7f55-4ad1-83e2-90ec1ee79740.PNG){#fig-p} +The Ribasim QGIS plugin allows users to view or edit a model without programming. +For specific tasks, like adding long timeseries, using Python is strongly recommended. -![Hupsel: secondary surface water.](https://user-images.githubusercontent.com/13662783/187625170-1acdfb41-7077-443f-b140-ae18cbf21e53.PNG){#fig-s} +One can also use Ribasim Python to build entire models from base data, such that your model setup is fully reproducible. -![Hupsel: tertiary surface water.](https://user-images.githubusercontent.com/13662783/187625174-3eec28b5-ddbb-4870-94c3-d9e9a43f8eb4.PNG){#fig-t} +See [usage](https://deltares.github.io/Ribasim/core/usage.html) for more information. -As a single basin (A) containing all surface water, discharging to its -downstream basin to the west (B): +# Download {#sec-download} -```{mermaid} -graph LR; - A["basin A"] --> B["basin B"]; +- Ribasim executable - Linux: [ribasim_cli_linux.zip](https://github.com/Deltares/Ribasim/releases/latest/download/ribasim_cli_linux.zip) +- Ribasim executable - Windows: [ribasim_cli_windows.zip](https://github.com/Deltares/Ribasim/releases/latest/download/ribasim_cli_windows.zip) +- QGIS plugin: [ribasim_qgis.zip](https://github.com/Deltares/Ribasim/releases/latest/download/ribasim_qgis.zip). +- Generated testmodels: [generated_testmodels.zip](https://github.com/Deltares/Ribasim/releases/latest/download/generated_testmodels.zip) + +The Ribasim Python package is [registered in PyPI](https://pypi.org/project/ribasim/) and can therefore be installed with [pip](https://docs.python.org/3/installing/index.html): +``` +pip install ribasim ``` -Such a system may be capable of representing discharge, but it cannot represent -residence times or differences in solute concentrations: within a single basin, -drop of water is mixed instantaneously. Instead, we may the group primary (P), -secondary (S), and tertiary (T) surface waters. Then T may flow into S, S into -P, and P discharges to the downstream basin (B.) +# Acknowledgment +Ribasim is supported by: -```{mermaid} -graph LR; - T["basin T"] --> S["basin S"]; - S --> P["basin P"]; - P --> B["basin B"]; -``` +::: {layout-ncol=2 layout-valign="bottom"} + + Deltares logo + -As each (sub)basin has its own volume, low throughput (high volume, low -discharge, long residence time) and high throughput (low volume, high -discharge, short residence time) systems can be represented in a lumped manner; -of course, more detail requires more parameters. + + NHI logo + +:::