Skip to content

Commit

Permalink
Document parsing Delwaq results. (#1845)
Browse files Browse the repository at this point in the history
And plot some, including:
- Utility to add tracers to existing models
- Adds default Initial (basin state) and UserDemand tracers
- Make some fancy plots



![fraction_example](https://github.com/user-attachments/assets/9341bcb0-3f18-4c78-9951-55c8674de1a1)



![fraction_spatial_example](https://github.com/user-attachments/assets/7f146ddd-7c8a-4440-925b-b2bc660ace1a)

---------

Co-authored-by: Martijn Visser <[email protected]>
  • Loading branch information
evetion and visr authored Oct 10, 2024
1 parent 4048a50 commit 5cff21d
Show file tree
Hide file tree
Showing 11 changed files with 353 additions and 42 deletions.
4 changes: 2 additions & 2 deletions .teamcity/Templates/IntegrationTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ open class IntegrationTest (platformOs: String) : Template() {
workingDir = "ribasim"
scriptContent = header +
"""
pixi run python utils/get_benchmark.py %MiniO_credential_token% "hws_2024_7_0/"
pixi run python utils/get_benchmark.py --secretkey %MiniO_credential_token% "hws_2024_7_0/"
pixi run model-integration-test
""".trimIndent()
}
Expand All @@ -72,4 +72,4 @@ open class IntegrationTest (platformOs: String) : Template() {
}

object IntegrationTestWindows : IntegrationTest("Windows")
object IntegrationTestLinux : IntegrationTest("Linux")
object IntegrationTestLinux : IntegrationTest("Linux")
4 changes: 2 additions & 2 deletions .teamcity/Templates/RegressionTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ open class RegressionTest (platformOs: String) : Template() {
workingDir = "ribasim"
scriptContent = header +
"""
pixi run python utils/get_benchmark.py %MiniO_credential_token% "benchmark/"
pixi run python utils/get_benchmark.py %MiniO_credential_token% "hws_migration_test/"
pixi run python utils/get_benchmark.py --secretkey %MiniO_credential_token% "benchmark/"
pixi run python utils/get_benchmark.py --secretkey %MiniO_credential_token% "hws_migration_test/"
pixi run test-ribasim-regression
""".trimIndent()
}
Expand Down
11 changes: 11 additions & 0 deletions .teamcity/Templates/TestDelwaqCoupling.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ open class TestDelwaqCoupling(platformOs: String) : Template() {
root(Ribasim, ". => ribasim")
cleanCheckout = true
}
params {
password("MiniO_credential_token", "credentialsJSON:86cbf3e5-724c-437d-9962-7a3f429b0aa2")
}

steps {
script {
Expand All @@ -33,6 +36,14 @@ open class TestDelwaqCoupling(platformOs: String) : Template() {
pixi run delwaq
""".trimIndent()
}
script {
name = "Upload delwaq model"
id = "Delwaq_upload"
workingDir = "ribasim"
scriptContent = """
pixi run python utils/upload_benchmark.py --secretkey %MiniO_credential_token% "python/ribasim/ribasim/delwaq/model/delwaq_map.nc" "doc-image/delwaq/delwaq_map.nc"
""".trimIndent()
}
}
}
}
Expand Down
126 changes: 122 additions & 4 deletions docs/guide/delwaq.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"from pathlib import Path\n",
"\n",
"toml_path = Path(\"../../generated_testmodels/basic/ribasim.toml\")\n",
"\n",
"assert toml_path.is_file()"
]
},
Expand Down Expand Up @@ -54,6 +55,38 @@
"model.plot(); # for later comparison"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"model.basin.profile"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's add another tracer to the model, to setup a fraction calculation."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from ribasim.delwaq import add_tracer\n",
"\n",
"add_tracer(model, 11, \"Foo\")\n",
"add_tracer(model, 15, \"Bar\")\n",
"display(model.flow_boundary.concentration) # flow boundaries\n",
"display(model.level_boundary.concentration) # flow boundaries\n",
"\n",
"model.write(toml_path)"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down Expand Up @@ -89,9 +122,8 @@
"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",
"output_path = Path(\"../../generated_testmodels/basic/delwaq\")\n",
"\n",
"graph, substances = generate(toml_path, output_path)"
]
},
Expand Down Expand Up @@ -182,6 +214,92 @@
"- 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."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Parsing the results\n",
"With Delwaq having run, we can now parse the results using `ribasim.delwaq.parse`. This function requires the `graph` and `substances` variables that were output by `ribasim.delwaq.generate`, as well as the path to the results folder of the Delwaq simulation."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# | include: false\n",
"# For documentation purposes, we will download the generated map file\n",
"import urllib.request\n",
"\n",
"urllib.request.urlretrieve(\n",
" \"https://s3.deltares.nl/ribasim/doc-image/delwaq/delwaq_map.nc\",\n",
" output_path / \"delwaq_map.nc\",\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from ribasim.delwaq import parse\n",
"\n",
"nmodel = parse(toml_path, graph, substances, output_folder=output_path)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The parsed model is identical to the Ribasim model, with the exception of the added concentration_external table that contains all tracer results from Delwaq."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"display(nmodel.basin.concentration_external)\n",
"print(substances)\n",
"t = nmodel.basin.concentration_external.df\n",
"t[t.time == t.time.unique()[2]]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can use this table to plot the results of the Delwaq model, both spatially as over time."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from ribasim.delwaq import plot_fraction\n",
"\n",
"plot_fraction(nmodel, 1) # default tracers, should add up to 1\n",
"plot_fraction(nmodel, 9, [\"Foo\", \"Bar\"]) # custom tracers\n",
"plot_fraction(nmodel, 9, [\"Continuity\"]) # mass balance check"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from ribasim.delwaq import plot_spatial\n",
"\n",
"plot_spatial(nmodel, \"Bar\")\n",
"plot_spatial(nmodel, \"Foo\", versus=\"Bar\") # ratio of Meuse to Rhine"
]
}
],
"metadata": {
Expand All @@ -200,7 +318,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.4"
"version": "3.12.5"
}
},
"nbformat": 4,
Expand Down
14 changes: 11 additions & 3 deletions python/ribasim/ribasim/delwaq/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
from .generate import generate
from .generate import add_tracer, generate
from .parse import parse
from .plot import plot
from .plot import plot_fraction, plot_spatial
from .util import run_delwaq

__all__ = ["generate", "parse", "run_delwaq", "plot"]
__all__ = [
"generate",
"parse",
"run_delwaq",
"plot",
"add_tracer",
"plot_fraction",
"plot_spatial",
]
40 changes: 36 additions & 4 deletions python/ribasim/ribasim/delwaq/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
from datetime import timedelta
from pathlib import Path

from ribasim.utils import MissingOptionalModule, _concat
from ribasim import nodes
from ribasim.utils import MissingOptionalModule, _concat, _pascal_to_snake

try:
import networkx as nx
Expand Down Expand Up @@ -277,16 +278,17 @@ def generate(
toml_path: Path,
output_folder=output_folder,
use_evaporation=USE_EVAP,
results_folder="results",
) -> tuple[nx.DiGraph, set[str]]:
"""Generate a Delwaq model from a Ribasim model and results."""

# Read in model and results
model = ribasim.Model.read(toml_path)
basins = pd.read_feather(
toml_path.parent / "results" / "basin.arrow", dtype_backend="pyarrow"
toml_path.parent / results_folder / "basin.arrow", dtype_backend="pyarrow"
)
flows = pd.read_feather(
toml_path.parent / "results" / "flow.arrow", dtype_backend="pyarrow"
toml_path.parent / results_folder / "flow.arrow", dtype_backend="pyarrow"
)

output_folder.mkdir(exist_ok=True)
Expand Down Expand Up @@ -414,10 +416,13 @@ def generate(
# Setup initial basin concentrations
defaults = {
"Continuity": 1.0,
"Basin": 0.0,
"Initial": 1.0,
"LevelBoundary": 0.0,
"FlowBoundary": 0.0,
"Terminal": 0.0,
"UserDemand": 0.0,
"Precipitation": 0.0,
"Drainage": 0.0,
}
substances.update(defaults.keys())

Expand Down Expand Up @@ -491,6 +496,33 @@ def generate(
return G, substances


def add_tracer(model, node_id, tracer_name):
"""Add a tracer to the Delwaq model."""
n = model.node_table().df.loc[node_id]
node_type = n.node_type
if node_type not in [
"Basin",
"LevelBoundary",
"FlowBoundary",
"UserDemand",
]:
raise ValueError("Can only trace Basins and boundaries")
snake_node_type = _pascal_to_snake(node_type)
nt = getattr(model, snake_node_type)

ct = getattr(nodes, snake_node_type)
table = ct.Concentration(
node_id=[node_id],
time=[model.starttime],
substance=[tracer_name],
concentration=[1.0],
)
if nt.concentration is None:
nt.concentration = table
else:
nt.concentration = pd.concat([nt.concentration.df, table.df], ignore_index=True)


if __name__ == "__main__":
# Generate a Delwaq model from the default Ribasim model
toml_path = Path(sys.argv[1])
Expand Down
Loading

0 comments on commit 5cff21d

Please sign in to comment.