Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Delwaq coupling guide #1619

Merged
merged 7 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/_quarto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ website:
- guide/index.qmd
- guide/examples.ipynb
- guide/coupling.qmd
- section: "Coupling guides"
contents:
- guide/delwaq.ipynb

- title: "Concepts"
contents:
Expand Down
5 changes: 2 additions & 3 deletions docs/guide/coupling.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Ribasim can also be (online) coupled to other kernels with the help of iMOD Coup

## Water quality

Ribasim can be offline coupled to Delwaq, the Deltares Water Quality model. Note that this functionality is still in active development.
Ribasim can be offline coupled to Delwaq, the Deltares Water Quality model.

```{mermaid}
flowchart LR
Expand All @@ -21,5 +21,4 @@ flowchart LR

Delwaq can calculate the concentration of substances in [Basin](/reference/node/basin.qmd) nodes over time, based on initial concentrations, and of [FlowBoundary](/reference/node/flow-boundary.qmd) nodes. Ribasim exposes the `Basin / concentration`, `Basin / concentration_state`, `FlowBoundary / concentration`, and `LevelBoundary / concentration` tables to setup these substances and concentrations.

When a Ribasim model ran with the above tables, one can use the utilities in the `coupling/delwaq` folder to generate the input required for Delwaq to run, as well as to parse the output from Delwaq into a Ribasim compatible format.
For more information see the `README.md` in the same folder.
When a Ribasim model ran with the above tables, one can use the utilities in the `delwaq` namespace of the Ribasim Python API to generate the input required for Delwaq to run, as well as to parse the output from Delwaq into a Ribasim compatible format. For more information see the [guide](/guide/delwaq.ipynb).
208 changes: 208 additions & 0 deletions docs/guide/delwaq.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---\n",
"title: \"Ribasim Delwaq coupling\"\n",
"---"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In order to generate the Delwaq input files, we need a completed Ribasim simulation (typically one with a results folder) that ideally also includes some substances and initial concentrations. Let's take the basic test model for example, which already has set some initial concentrations.\n",
"\n",
"All testmodels can be [downloaded from here](/install.qmd#sec-download)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from pathlib import Path\n",
"\n",
"toml_path = Path(\"../../generated_testmodels/basic/ribasim.toml\")\n",
"assert toml_path.is_file()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This Ribasim model already has substance concentrations for `Cl` and `Tracer` in the input tables, and we will use these to generate the Delwaq input files."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from ribasim import Model\n",
"\n",
"model = Model.read(toml_path)\n",
"\n",
"display(model.basin.concentration_state) # basin initial state\n",
"display(model.basin.concentration) # basin boundaries\n",
"display(model.flow_boundary.concentration) # flow boundaries\n",
"display(model.level_boundary.concentration) # level boundaries\n",
"model.plot(); # for later comparison"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# | include: false\n",
"from subprocess import run\n",
"\n",
"run(\n",
" [\n",
" \"julia\",\n",
" \"--project=../../core\",\n",
" \"--eval\",\n",
" f'using Ribasim; Ribasim.main(\"{toml_path.as_posix()}\")',\n",
" ],\n",
" check=True,\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Given the path to a completed Ribasim simulation, we can call `ribasim.delwaq.generate` for generating the required input files for Delwaq from scratch."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from ribasim.delwaq import generate\n",
"\n",
"output_path = Path(\n",
" \"../../generated_testmodels/basic/delwaq\"\n",
") # set a path where we store the Delwaq input files\n",
"graph, substances = generate(toml_path, output_path)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This call produces a handful of files in the user defined folder. Let's take a look at them:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"list(output_path.iterdir())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"These files form a complete Delwaq simulation, and can be run by either pointing DIMR to the `dimr_config.xml` file or pointing Delwaq to the `delwaq.inp` file."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note that the call to `generate` produces two output variables; `graph` and `substances` that are required for parsing the results of the Delwaq model later on. Nonetheless, we can also inspect them here, and inspect the created Delwaq network."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"substances # list of substances, as will be present in the Delwaq netcdf output"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"As you can see, the complete substances list is a combination of user input (`Cl` and `Tracer` in the input tables), a `Continuity` tracer, and tracers for all nodetypes in the Ribasim model. The latter tracers allow for deeper inspection of the Ribasim model, such as debugging the mass balance by plotting fraction graphs. Let's inspect the `graph` next, which is the Delwaq network that was created from the Ribasim model:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"import networkx as nx\n",
"\n",
"# Let's draw the graph\n",
"fig, ax = plt.subplots(1, 2, figsize=(10, 5))\n",
"nx.draw(\n",
" graph,\n",
" pos={k: v[\"pos\"] for k, v in graph.nodes(data=True)},\n",
" with_labels=True,\n",
" labels={k: k for k, v in graph.nodes(data=True)},\n",
" ax=ax[0],\n",
")\n",
"ax[0].set_title(\"Delwaq node IDs\")\n",
"nx.draw(\n",
" graph,\n",
" pos={k: v[\"pos\"] for k, v in graph.nodes(data=True)},\n",
" with_labels=True,\n",
" labels={k: v[\"id\"] for k, v in graph.nodes(data=True)},\n",
" ax=ax[1],\n",
")\n",
"ax[1].set_title(\"Ribasim node IDs\")\n",
"fig.suptitle(\"Delwaq network\");"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here we plotted the Delwaq network twice, with the node IDs as used by Delwaq on the left hand side, and the corresponding Ribasim node IDs on the right hand side.\n",
"As you can see, the Delwaq network is very similar to the Ribasim network, with some notable changes:\n",
"\n",
"- All non-Basin or non-boundary types are removed (e.g. no more Pumps or TabulatedRatingCurves)\n",
"- Basin boundaries are split into separate nodes and links (drainage, precipitation, and evaporation, as indicated by the duplicated Basin IDs on the right hand side)\n",
"- All node IDs have been renumbered, with boundaries being negative, and Basins being positive."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "default",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Loading