Ribasim Delwaq coupling

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.

All testmodels can be downloaded from here.

from pathlib import Path

toml_path = Path("../../generated_testmodels/basic/ribasim.toml")
assert toml_path.is_file()

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.

from ribasim import Model

model = Model.read(toml_path)

display(model.basin.concentration_state)  # basin initial state
display(model.basin.concentration)  # basin boundaries
display(model.flow_boundary.concentration)  # flow boundaries
display(model.level_boundary.concentration)  # level boundaries
model.plot();  # for later comparison
Basin / concentration_state
node_id substance concentration
fid
0 1 Cl 0.0
1 3 Cl 0.0
2 6 Cl 0.0
3 9 Cl 0.0
Basin / concentration
node_id time substance drainage precipitation
fid
0 1 2020-01-01 Cl 0.0 0.0
1 1 2020-01-02 Cl 1.0 1.0
2 1 2020-01-01 Tracer 1.0 1.0
3 3 2020-01-01 Cl 0.0 0.0
4 3 2020-01-02 Cl 1.0 1.0
5 3 2020-01-01 Tracer 1.0 1.0
6 6 2020-01-01 Cl 0.0 0.0
7 6 2020-01-02 Cl 1.0 1.0
8 6 2020-01-01 Tracer 1.0 1.0
9 9 2020-01-01 Cl 0.0 0.0
10 9 2020-01-02 Cl 1.0 1.0
11 9 2020-01-01 Tracer 1.0 1.0
FlowBoundary / concentration
node_id time substance concentration
fid
0 15 2020-01-01 Cl 0.0
1 15 2020-01-01 Tracer 1.0
2 16 2020-01-01 Cl 0.0
3 16 2020-01-01 Tracer 1.0
LevelBoundary / concentration
node_id time substance concentration
fid
0 11 2020-01-01 Cl 34.0
1 17 2020-01-01 Cl 34.0

Given the path to a completed Ribasim simulation, we can call ribasim.delwaq.generate for generating the required input files for Delwaq from scratch.

from ribasim.delwaq import generate

output_path = Path(
    "../../generated_testmodels/basic/delwaq"
)  # set a path where we store the Delwaq input files
graph, substances = generate(toml_path, output_path)

This call produces a handful of files in the user defined folder. Let’s take a look at them:

list(output_path.iterdir())
[PosixPath('../../generated_testmodels/basic/delwaq/ribasim.flo'),
 PosixPath('../../generated_testmodels/basic/delwaq/volumes.csv'),
 PosixPath('../../generated_testmodels/basic/delwaq/ribasim.are'),
 PosixPath('../../generated_testmodels/basic/delwaq/ribasim.poi'),
 PosixPath('../../generated_testmodels/basic/delwaq/dimr_config.xml'),
 PosixPath('../../generated_testmodels/basic/delwaq/ribasim.nc'),
 PosixPath('../../generated_testmodels/basic/delwaq/flows.csv'),
 PosixPath('../../generated_testmodels/basic/delwaq/network.csv'),
 PosixPath('../../generated_testmodels/basic/delwaq/ribasim.len'),
 PosixPath('../../generated_testmodels/basic/delwaq/B5_bounddata.inc'),
 PosixPath('../../generated_testmodels/basic/delwaq/ribasim.atr'),
 PosixPath('../../generated_testmodels/basic/delwaq/ribasim_bndlist.inc'),
 PosixPath('../../generated_testmodels/basic/delwaq/delwaq.inp'),
 PosixPath('../../generated_testmodels/basic/delwaq/ribasim.vel'),
 PosixPath('../../generated_testmodels/basic/delwaq/ribasim.vol')]

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.

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.

substances  # list of substances, as will be present in the Delwaq netcdf output
{'Basin',
 'Cl',
 'Continuity',
 'FlowBoundary',
 'LevelBoundary',
 'Terminal',
 'Tracer'}

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:

import matplotlib.pyplot as plt
import networkx as nx

# Let's draw the graph
fig, ax = plt.subplots(1, 2, figsize=(10, 5))
nx.draw(
    graph,
    pos={k: v["pos"] for k, v in graph.nodes(data=True)},
    with_labels=True,
    labels={k: k for k, v in graph.nodes(data=True)},
    ax=ax[0],
)
ax[0].set_title("Delwaq node IDs")
nx.draw(
    graph,
    pos={k: v["pos"] for k, v in graph.nodes(data=True)},
    with_labels=True,
    labels={k: v["id"] for k, v in graph.nodes(data=True)},
    ax=ax[1],
)
ax[1].set_title("Ribasim node IDs")
fig.suptitle("Delwaq network");

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. As you can see, the Delwaq network is very similar to the Ribasim network, with some notable changes: