From 0b71370be6ffd2080a2871d7398c3508ba9cc2b3 Mon Sep 17 00:00:00 2001 From: Martijn Visser Date: Fri, 26 Jan 2024 10:00:12 +0100 Subject: [PATCH] Use flow_rate rather than discharge in TabulatedRatingCurve A breaking change, but this is more consistent. Fixes #983. --- core/src/bmi.jl | 4 ++-- core/src/solve.jl | 2 +- core/src/utils.jl | 10 +++++----- core/src/validation.jl | 4 ++-- core/test/run_models_test.jl | 4 ++-- docs/core/equations.qmd | 4 ---- docs/core/usage.qmd | 6 +++--- docs/python/examples.ipynb | 8 ++++---- docs/schema/TabulatedRatingCurveStatic.schema.json | 4 ++-- docs/schema/TabulatedRatingCurveTime.schema.json | 4 ++-- python/ribasim/ribasim/models.py | 4 ++-- .../ribasim_testmodels/allocation.py | 6 +++--- python/ribasim_testmodels/ribasim_testmodels/basic.py | 6 +++--- .../ribasim_testmodels/discrete_control.py | 4 ++-- .../ribasim_testmodels/dutch_waterways.py | 4 ++-- .../ribasim_testmodels/ribasim_testmodels/equations.py | 4 ++-- .../ribasim_testmodels/ribasim_testmodels/invalid.py | 6 +++--- .../ribasim_testmodels/pid_control.py | 2 +- .../ribasim_testmodels/ribasim_testmodels/trivial.py | 2 +- ribasim_qgis/core/nodes.py | 4 ++-- 20 files changed, 44 insertions(+), 48 deletions(-) diff --git a/core/src/bmi.jl b/core/src/bmi.jl index 9b9a7993d..24c3b763a 100644 --- a/core/src/bmi.jl +++ b/core/src/bmi.jl @@ -607,9 +607,9 @@ function update_tabulated_rating_curve!(integrator)::Nothing # update the existing LinearInterpolation id = first(group).node_id level = [row.level for row in group] - discharge = [row.discharge for row in group] + flow_rate = [row.flow_rate for row in group] i = searchsortedfirst(node_id, NodeID(id)) - tables[i] = LinearInterpolation(discharge, level; extrapolate = true) + tables[i] = LinearInterpolation(flow_rate, level; extrapolate = true) end return nothing end diff --git a/core/src/solve.jl b/core/src/solve.jl index ab37fd191..f93a76ad5 100644 --- a/core/src/solve.jl +++ b/core/src/solve.jl @@ -121,7 +121,7 @@ end """ struct TabulatedRatingCurve{C} -Rating curve from level to discharge. The rating curve is a lookup table with linear +Rating curve from level to flow rate. The rating curve is a lookup table with linear interpolation in between. Relation can be updated in time, which is done by moving data from the `time` field into the `tables`, which is done in the `update_tabulated_rating_curve` callback. diff --git a/core/src/utils.jl b/core/src/utils.jl index 20ff9f75c..5ca7608b4 100644 --- a/core/src/utils.jl +++ b/core/src/utils.jl @@ -570,14 +570,14 @@ end function qh_interpolation( level::AbstractVector, - discharge::AbstractVector, + flow_rate::AbstractVector, )::Tuple{LinearInterpolation, Bool} - return LinearInterpolation(discharge, level; extrapolate = true), allunique(level) + return LinearInterpolation(flow_rate, level; extrapolate = true), allunique(level) end """ -From a table with columns node_id, discharge (Q) and level (h), -create a LinearInterpolation from level to discharge for a given node_id. +From a table with columns node_id, flow_rate (Q) and level (h), +create a LinearInterpolation from level to flow rate for a given node_id. """ function qh_interpolation( node_id::Int, @@ -585,7 +585,7 @@ function qh_interpolation( )::Tuple{LinearInterpolation, Bool} rowrange = findlastgroup(node_id, table.node_id) @assert !isempty(rowrange) "timeseries starts after model start time" - return qh_interpolation(table.level[rowrange], table.discharge[rowrange]) + return qh_interpolation(table.level[rowrange], table.flow_rate[rowrange]) end """ diff --git a/core/src/validation.jl b/core/src/validation.jl index 922b02fe2..8ff6e59cc 100644 --- a/core/src/validation.jl +++ b/core/src/validation.jl @@ -261,7 +261,7 @@ end node_id::Int active::Union{Missing, Bool} level::Float64 - discharge::Float64 + flow_rate::Float64 control_state::Union{Missing, String} end @@ -269,7 +269,7 @@ end node_id::Int time::DateTime level::Float64 - discharge::Float64 + flow_rate::Float64 end @version TerminalStaticV1 begin diff --git a/core/test/run_models_test.jl b/core/test/run_models_test.jl index 227ec010f..109c20dba 100644 --- a/core/test/run_models_test.jl +++ b/core/test/run_models_test.jl @@ -335,12 +335,12 @@ end See: https://en.wikipedia.org/wiki/Standard_step_method - * The left boundary has a fixed discharge `Q`. + * The left boundary has a fixed flow rate `Q`. * The right boundary has a fixed level `h_right`. * Channel profile is rectangular. # Arguments - - `Q`: discharge entering in the left boundary (m3/s) + - `Q`: flow rate entering in the left boundary (m3/s) - `w`: width (m) - `n`: Manning roughness - `h_right`: water level on the right boundary diff --git a/docs/core/equations.qmd b/docs/core/equations.qmd index 0ab8aeaa5..cc238d8a6 100644 --- a/docs/core/equations.qmd +++ b/docs/core/equations.qmd @@ -260,10 +260,6 @@ discharge. It can be understood as an empirical description of a basin's properties. This can include an outlet, but also the lumped hydraulic behavior of the upstream channels. - -The Tabulated Rating Curve should indicate at which volume no discharge occurs (the dead -storage volume). - :::{.callout-note} Currently, the discharge relies only on the basin's level; it could also use the volume of both connected basins to simulate backwater effects, submersion diff --git a/docs/core/usage.qmd b/docs/core/usage.qmd index e8e2789bd..6c7e3a529 100644 --- a/docs/core/usage.qmd +++ b/docs/core/usage.qmd @@ -393,9 +393,9 @@ node_id | Int | - | sorted control_state | String | - | (optional) sorted per node_id active | Bool | - | (optional, default true) level | Float64 | $m$ | sorted per control_state -discharge | Float64 | $m^3 s^{-1}$ | non-negative +flow_rate | Float64 | $m^3 s^{-1}$ | non-negative -node_id | discharge | level +node_id | flow_rate | level ------- |----------- |------- 2 | 0.0 | -0.105 2 | 0.0 | 0.095 @@ -416,7 +416,7 @@ column | type | unit | restriction node_id | Int | - | sorted time | DateTime | - | sorted per node_id level | Float64 | $m$ | - -discharge | Float64 | $m^3 s^{-1}$ | non-negative +flow_rate | Float64 | $m^3 s^{-1}$ | non-negative # Pump diff --git a/docs/python/examples.ipynb b/docs/python/examples.ipynb index 7b27083d2..13097eada 100644 --- a/docs/python/examples.ipynb +++ b/docs/python/examples.ipynb @@ -158,7 +158,7 @@ " data={\n", " \"node_id\": [4, 4],\n", " \"level\": [0.0, 1.0],\n", - " \"discharge\": [0.0, q1000],\n", + " \"flow_rate\": [0.0, q1000],\n", " }\n", " )\n", ")" @@ -867,7 +867,7 @@ "source": [ "rating_curve = ribasim.TabulatedRatingCurve(\n", " static=pd.DataFrame(\n", - " data={\"node_id\": 2 * [5], \"level\": [2.0, 15.0], \"discharge\": [0.0, 1e-3]}\n", + " data={\"node_id\": 2 * [5], \"level\": [2.0, 15.0], \"flow_rate\": [0.0, 1e-3]}\n", " )\n", ")" ] @@ -1633,7 +1633,7 @@ " data={\n", " \"node_id\": 7,\n", " \"level\": [0.0, 0.5, 1.0],\n", - " \"discharge\": [0.0, 0.0, 2.0],\n", + " \"flow_rate\": [0.0, 0.0, 2.0],\n", " }\n", " )\n", ")" @@ -1941,7 +1941,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.6" + "version": "3.11.7" } }, "nbformat": 4, diff --git a/docs/schema/TabulatedRatingCurveStatic.schema.json b/docs/schema/TabulatedRatingCurveStatic.schema.json index 0e21b91f0..319d37d8c 100644 --- a/docs/schema/TabulatedRatingCurveStatic.schema.json +++ b/docs/schema/TabulatedRatingCurveStatic.schema.json @@ -24,7 +24,7 @@ "format": "double", "type": "number" }, - "discharge": { + "flow_rate": { "format": "double", "type": "number" }, @@ -49,6 +49,6 @@ "required": [ "node_id", "level", - "discharge" + "flow_rate" ] } diff --git a/docs/schema/TabulatedRatingCurveTime.schema.json b/docs/schema/TabulatedRatingCurveTime.schema.json index 447df4100..52a07a442 100644 --- a/docs/schema/TabulatedRatingCurveTime.schema.json +++ b/docs/schema/TabulatedRatingCurveTime.schema.json @@ -17,7 +17,7 @@ "format": "double", "type": "number" }, - "discharge": { + "flow_rate": { "format": "double", "type": "number" }, @@ -32,6 +32,6 @@ "node_id", "time", "level", - "discharge" + "flow_rate" ] } diff --git a/python/ribasim/ribasim/models.py b/python/ribasim/ribasim/models.py index 38de5e958..87605768b 100644 --- a/python/ribasim/ribasim/models.py +++ b/python/ribasim/ribasim/models.py @@ -189,7 +189,7 @@ class TabulatedRatingCurveStatic(BaseModel): node_id: int active: bool | None = None level: float - discharge: float + flow_rate: float control_state: str | None = None remarks: str = Field("", description="a hack for pandera") @@ -198,7 +198,7 @@ class TabulatedRatingCurveTime(BaseModel): node_id: int time: datetime level: float - discharge: float + flow_rate: float remarks: str = Field("", description="a hack for pandera") diff --git a/python/ribasim_testmodels/ribasim_testmodels/allocation.py b/python/ribasim_testmodels/ribasim_testmodels/allocation.py index 4a218bd98..3be17b397 100644 --- a/python/ribasim_testmodels/ribasim_testmodels/allocation.py +++ b/python/ribasim_testmodels/ribasim_testmodels/allocation.py @@ -503,7 +503,7 @@ def looped_subnetwork_model(): data={ "node_id": [13, 13, 14, 14, 19, 19], "level": 3 * [0.0, 1.0], - "discharge": 3 * [0.0, 2.0], + "flow_rate": 3 * [0.0, 2.0], } ) ) @@ -784,7 +784,7 @@ def fractional_flow_subnetwork_model(): data={ "node_id": [3, 3], "level": [0.0, 1.0], - "discharge": [0.0, 1e-4], + "flow_rate": [0.0, 1e-4], } ) ) @@ -986,7 +986,7 @@ def allocation_example_model(): data={ "node_id": 7, "level": [0.0, 0.5, 1.0], - "discharge": [0.0, 0.0, 2.0], + "flow_rate": [0.0, 0.0, 2.0], } ) ) diff --git a/python/ribasim_testmodels/ribasim_testmodels/basic.py b/python/ribasim_testmodels/ribasim_testmodels/basic.py index 8d449d8c2..179d03a65 100644 --- a/python/ribasim_testmodels/ribasim_testmodels/basic.py +++ b/python/ribasim_testmodels/ribasim_testmodels/basic.py @@ -79,7 +79,7 @@ def basic_model() -> ribasim.Model: data={ "node_id": [4, 4], "level": [0.0, 1.0], - "discharge": [0.0, q1000], + "flow_rate": [0.0, q1000], } ) ) @@ -329,7 +329,7 @@ def tabulated_rating_curve_model() -> ribasim.Model: data={ "node_id": [2, 2], "level": [0.0, 1.0], - "discharge": [0.0, q1000], + "flow_rate": [0.0, q1000], } ), time=pd.DataFrame( @@ -345,7 +345,7 @@ def tabulated_rating_curve_model() -> ribasim.Model: pd.Timestamp("2020-03"), ], "level": [0.0, 1.0, 0.0, 1.1, 0.0, 1.2], - "discharge": [0.0, q1000, 0.0, q1000, 0.0, q1000], + "flow_rate": [0.0, q1000, 0.0, q1000, 0.0, q1000], } ), ) diff --git a/python/ribasim_testmodels/ribasim_testmodels/discrete_control.py b/python/ribasim_testmodels/ribasim_testmodels/discrete_control.py index e82b606a0..a1bb7f88c 100644 --- a/python/ribasim_testmodels/ribasim_testmodels/discrete_control.py +++ b/python/ribasim_testmodels/ribasim_testmodels/discrete_control.py @@ -524,7 +524,7 @@ def tabulated_rating_curve_control_model() -> ribasim.Model: data={ "node_id": [2, 2, 2, 2], "level": [0.0, 1.2, 0.0, 1.0], - "discharge": [0.0, q100, 0.0, q100], + "flow_rate": [0.0, q100, 0.0, q100], "control_state": ["low", "low", "high", "high"], } ), @@ -665,7 +665,7 @@ def level_setpoint_with_minmax_model(): # Setup the rating curve rating_curve = ribasim.TabulatedRatingCurve( static=pd.DataFrame( - data={"node_id": 2 * [5], "level": [2.0, 15.0], "discharge": [0.0, 1e-3]} + data={"node_id": 2 * [5], "level": [2.0, 15.0], "flow_rate": [0.0, 1e-3]} ) ) diff --git a/python/ribasim_testmodels/ribasim_testmodels/dutch_waterways.py b/python/ribasim_testmodels/ribasim_testmodels/dutch_waterways.py index e82e358f1..4920369f5 100644 --- a/python/ribasim_testmodels/ribasim_testmodels/dutch_waterways.py +++ b/python/ribasim_testmodels/ribasim_testmodels/dutch_waterways.py @@ -105,8 +105,8 @@ def dutch_waterways_model(): 7.46, 4.45, 4.46, - ], # The level and discharge for "pump_low", "pump_high" are irrelevant - "discharge": [0.0, 0.0] + ], # The level and flow rate for "pump_low", "pump_high" are irrelevant + "flow_rate": [0.0, 0.0] + 2 * [418, 420.15], # since the rating curve is not active here } ) diff --git a/python/ribasim_testmodels/ribasim_testmodels/equations.py b/python/ribasim_testmodels/ribasim_testmodels/equations.py index bbb678f11..87e9330a4 100644 --- a/python/ribasim_testmodels/ribasim_testmodels/equations.py +++ b/python/ribasim_testmodels/ribasim_testmodels/equations.py @@ -175,14 +175,14 @@ def rating_curve_model(): level_min = 1.0 node_id = np.full(n_datapoints, 2) level = np.linspace(0, 12, 100) - discharge = np.square(level - level_min) / (60 * 60 * 24) + flow_rate = np.square(level - level_min) / (60 * 60 * 24) rating_curve = ribasim.TabulatedRatingCurve( static=pd.DataFrame( data={ "node_id": node_id, "level": level, - "discharge": discharge, + "flow_rate": flow_rate, } ) ) diff --git a/python/ribasim_testmodels/ribasim_testmodels/invalid.py b/python/ribasim_testmodels/ribasim_testmodels/invalid.py index 7301f7f2a..c77bde12b 100644 --- a/python/ribasim_testmodels/ribasim_testmodels/invalid.py +++ b/python/ribasim_testmodels/ribasim_testmodels/invalid.py @@ -67,7 +67,7 @@ def invalid_qh_model(): rating_curve_static = pd.DataFrame( # Invalid: levels must not be repeated - data={"node_id": [1, 1], "level": [0.0, 0.0], "discharge": [1.0, 2.0]} + data={"node_id": [1, 1], "level": [0.0, 0.0], "flow_rate": [1.0, 2.0]} ) rating_curve_time = pd.DataFrame( data={ @@ -78,7 +78,7 @@ def invalid_qh_model(): ], # Invalid: levels must not be repeated "level": [0.0, 0.0], - "discharge": [1.0, 2.0], + "flow_rate": [1.0, 2.0], } ) @@ -195,7 +195,7 @@ def invalid_fractional_flow_model(): # Setup the tabulated rating curve: rating_curve = ribasim.TabulatedRatingCurve( static=pd.DataFrame( - data={"node_id": [7, 7], "level": [0.0, 1.0], "discharge": [0.0, 50.0]} + data={"node_id": [7, 7], "level": [0.0, 1.0], "flow_rate": [0.0, 50.0]} ) ) diff --git a/python/ribasim_testmodels/ribasim_testmodels/pid_control.py b/python/ribasim_testmodels/ribasim_testmodels/pid_control.py index 865067e42..095f506dd 100644 --- a/python/ribasim_testmodels/ribasim_testmodels/pid_control.py +++ b/python/ribasim_testmodels/ribasim_testmodels/pid_control.py @@ -256,7 +256,7 @@ def discrete_control_of_pid_control_model(): data={ "node_id": [4, 4], "level": [0.0, 1.0], - "discharge": [0.0, q1000], + "flow_rate": [0.0, q1000], } ) ) diff --git a/python/ribasim_testmodels/ribasim_testmodels/trivial.py b/python/ribasim_testmodels/ribasim_testmodels/trivial.py index 99b5067cf..4cca5a8a4 100644 --- a/python/ribasim_testmodels/ribasim_testmodels/trivial.py +++ b/python/ribasim_testmodels/ribasim_testmodels/trivial.py @@ -101,7 +101,7 @@ def trivial_model() -> ribasim.Model: data={ "node_id": [0, 0], "level": [0.0, 1.0], - "discharge": [0.0, q1000], + "flow_rate": [0.0, q1000], } ) ) diff --git a/ribasim_qgis/core/nodes.py b/ribasim_qgis/core/nodes.py index 20c63c2ed..84f240139 100644 --- a/ribasim_qgis/core/nodes.py +++ b/ribasim_qgis/core/nodes.py @@ -446,7 +446,7 @@ def attributes(cls) -> list[QgsField]: QgsField("node_id", QVariant.Int), QgsField("active", QVariant.Bool), QgsField("level", QVariant.Double), - QgsField("discharge", QVariant.Double), + QgsField("flow_rate", QVariant.Double), QgsField("control_state", QVariant.String), ] @@ -466,7 +466,7 @@ def attributes(cls) -> list[QgsField]: QgsField("time", QVariant.DateTime), QgsField("node_id", QVariant.Int), QgsField("level", QVariant.Double), - QgsField("discharge", QVariant.Double), + QgsField("flow_rate", QVariant.Double), ]