Skip to content

Commit

Permalink
Add Basin / subgrid_time table
Browse files Browse the repository at this point in the history
  • Loading branch information
visr committed Dec 13, 2024
1 parent 5c08e2f commit 95c8f7c
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 14 deletions.
6 changes: 6 additions & 0 deletions core/src/read.jl
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,12 @@ function parse_static_and_time(
return out, !errors
end

"""
Validate the split of node IDs between static and time tables.
For node types that can have a part of the parameters defined statically and a part dynamically,
this checks if each ID is defined exactly once in either table.
"""
function static_and_time_node_ids(
db::DB,
static::StructVector,
Expand Down
13 changes: 13 additions & 0 deletions core/src/schema.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
@schema "ribasim.basin.profile" BasinProfile
@schema "ribasim.basin.state" BasinState
@schema "ribasim.basin.subgrid" BasinSubgrid
@schema "ribasim.basin.subgridtime" BasinSubgridTime
@schema "ribasim.basin.concentration" BasinConcentration
@schema "ribasim.basin.concentrationexternal" BasinConcentrationExternal
@schema "ribasim.basin.concentrationstate" BasinConcentrationState
Expand Down Expand Up @@ -58,9 +59,13 @@ function nodetype(
type_string = string(T)
elements = split(type_string, '.'; limit = 3)
last_element = last(elements)
# Special case last elements that need an underscore
if startswith(last_element, "concentration") && length(last_element) > 13
elements[end] = "concentration_$(last_element[14:end])"
end
if last_element == "subgridtime"
elements[end] = "subgrid_time"
end
if isnode(sv)
n = elements[2]
k = Symbol(elements[3])
Expand Down Expand Up @@ -150,6 +155,14 @@ end
subgrid_level::Float64
end

@version BasinSubgridTimeV1 begin
subgrid_id::Int32
node_id::Int32
time::DateTime
basin_level::Float64
subgrid_level::Float64
end

@version LevelBoundaryStaticV1 begin
node_id::Int32
active::Union{Missing, Bool}
Expand Down
15 changes: 12 additions & 3 deletions docs/reference/node/basin.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ ax.legend()

for converting the initial state in terms of levels to an initial state in terms of storages used in the core.

#### Interactive basin example
#### Interactive Basin example

The profile data is not detailed enough to create a full 3D picture of the basin. However, if we assume the profile data is for a stretch of canal of given length, the following plot shows a cross section of the basin.
```{python}
Expand Down Expand Up @@ -391,6 +391,15 @@ Water levels beyond the last `basin_level` are linearly extrapolated.
Note that the interpolation to subgrid water level is not constrained by any water balance within Ribasim.
Generally, to create physically meaningful subgrid water levels, the subgrid table must be parametrized properly such that the spatially integrated water volume of the subgrid elements agrees with the total storage volume of the basin.

## Subgrid time

This table is the transient form of the Subgrid table.
The only difference is that a time column is added.
The table must by sorted by time, and per time it must be sorted by `subgrid_id`.
With this the subgrid relations can be updated over time.
Note that a `node_id` can be either in this table or in the static one, but not both.
That means for each Basin all subgrid relations are either static or dynamic.

## Concentration {#sec-basin-conc}
This table defines the concentration of substances for the inflow boundaries of a Basin node.

Expand All @@ -402,7 +411,7 @@ substance | String | | can correspond to known De
drainage | Float64 | $\text{g}/\text{m}^3$ | (optional)
precipitation | Float64 | $\text{g}/\text{m}^3$ | (optional)

## ConcentrationState {#sec-basin-conc-state}
## Concentration state {#sec-basin-conc-state}
This table defines the concentration of substances in the Basin at the start of the simulation.

column | type | unit | restriction
Expand All @@ -411,7 +420,7 @@ node_id | Int32 | - | sorted
substance | String | - | can correspond to known Delwaq substances
concentration | Float64 | $\text{g}/\text{m}^3$ |

## ConcentrationExternal
## Concentration external
This table is used for (external) concentrations, that can be used for Control lookups.

column | type | unit | restriction
Expand Down
5 changes: 5 additions & 0 deletions python/ribasim/ribasim/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
BasinStateSchema,
BasinStaticSchema,
BasinSubgridSchema,
BasinSubgridTimeSchema,
BasinTimeSchema,
ContinuousControlFunctionSchema,
ContinuousControlVariableSchema,
Expand Down Expand Up @@ -405,6 +406,10 @@ class Basin(MultiNodeModel):
default_factory=TableModel[BasinSubgridSchema],
json_schema_extra={"sort_keys": ["subgrid_id", "basin_level"]},
)
subgrid_time: TableModel[BasinSubgridTimeSchema] = Field(
default_factory=TableModel[BasinSubgridTimeSchema],
json_schema_extra={"sort_keys": ["subgrid_id", "time", "basin_level"]},
)
area: SpatialTableModel[BasinAreaSchema] = Field(
default_factory=SpatialTableModel[BasinAreaSchema],
json_schema_extra={"sort_keys": ["node_id"]},
Expand Down
6 changes: 6 additions & 0 deletions python/ribasim/ribasim/nodes/basin.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
BasinStateSchema,
BasinStaticSchema,
BasinSubgridSchema,
BasinSubgridTimeSchema,
BasinTimeSchema,
)

Expand All @@ -18,6 +19,7 @@
"State",
"Static",
"Subgrid",
"SubgridTime",
"Time",
]

Expand All @@ -42,6 +44,10 @@ class Subgrid(TableModel[BasinSubgridSchema]):
pass


class SubgridTime(TableModel[BasinSubgridTimeSchema]):
pass


class Area(SpatialTableModel[BasinAreaSchema]):
pass

Expand Down
19 changes: 19 additions & 0 deletions python/ribasim/ribasim/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,25 @@ class BasinStaticSchema(_BaseSchema):
)


class BasinSubgridTimeSchema(_BaseSchema):
fid: Index[Int32] = pa.Field(default=1, check_name=True, coerce=True)
subgrid_id: Series[Annotated[pd.ArrowDtype, pyarrow.int32()]] = pa.Field(
nullable=False
)
node_id: Series[Annotated[pd.ArrowDtype, pyarrow.int32()]] = pa.Field(
nullable=False, default=0
)
time: Series[Annotated[pd.ArrowDtype, pyarrow.timestamp("ms")]] = pa.Field(
nullable=False
)
basin_level: Series[Annotated[pd.ArrowDtype, pyarrow.float64()]] = pa.Field(
nullable=False
)
subgrid_level: Series[Annotated[pd.ArrowDtype, pyarrow.float64()]] = pa.Field(
nullable=False
)


class BasinSubgridSchema(_BaseSchema):
fid: Index[Int32] = pa.Field(default=1, check_name=True, coerce=True)
subgrid_id: Series[Annotated[pd.ArrowDtype, pyarrow.int32()]] = pa.Field(
Expand Down
8 changes: 5 additions & 3 deletions python/ribasim_testmodels/ribasim_testmodels/two_basin.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,12 @@ def two_basin_model() -> Model:
Node(3, Point(750, 0)),
[
*basin_shared,
basin.Subgrid(
# Raise the subgrid levels by a meter after a month
basin.SubgridTime(
subgrid_id=2,
basin_level=[0.0, 1.0],
subgrid_level=[0.0, 1.0],
time=["2020-01-01", "2020-01-01", "2020-02-01", "2020-02-01"],
basin_level=[0.0, 1.0, 0.0, 1.0],
subgrid_level=[0.0, 1.0, 1.0, 2.0],
meta_x=750.0,
meta_y=0.0,
),
Expand Down
35 changes: 27 additions & 8 deletions ribasim_qgis/core/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,8 +348,8 @@ def geometry_type(cls) -> str:
@classmethod
def attributes(cls) -> list[QgsField]:
return [
QgsField("time", QVariant.DateTime),
QgsField("node_id", QVariant.Int),
QgsField("time", QVariant.DateTime),
QgsField("drainage", QVariant.Double),
QgsField("potential_evaporation", QVariant.Double),
QgsField("infiltration", QVariant.Double),
Expand All @@ -369,8 +369,8 @@ def geometry_type(cls) -> str:
@classmethod
def attributes(cls) -> list[QgsField]:
return [
QgsField("time", QVariant.DateTime),
QgsField("node_id", QVariant.Int),
QgsField("time", QVariant.DateTime),
QgsField("substance", QVariant.String),
QgsField("concentration", QVariant.Double),
]
Expand All @@ -388,8 +388,8 @@ def geometry_type(cls) -> str:
@classmethod
def attributes(cls) -> list[QgsField]:
return [
QgsField("time", QVariant.DateTime),
QgsField("node_id", QVariant.Int),
QgsField("time", QVariant.DateTime),
QgsField("substance", QVariant.String),
QgsField("concentration", QVariant.Double),
]
Expand All @@ -407,15 +407,15 @@ def geometry_type(cls) -> str:
@classmethod
def attributes(cls) -> list[QgsField]:
return [
QgsField("time", QVariant.DateTime),
QgsField("node_id", QVariant.Int),
QgsField("time", QVariant.DateTime),
QgsField("substance", QVariant.String),
QgsField("drainage", QVariant.Double),
QgsField("precipitation", QVariant.Double),
]


class BasinSubgridLevel(Input):
class BasinSubgrid(Input):
@classmethod
def input_type(cls) -> str:
return "Basin / subgrid"
Expand All @@ -434,6 +434,26 @@ def attributes(cls) -> list[QgsField]:
]


class BasinSubgridTime(Input):
@classmethod
def input_type(cls) -> str:
return "Basin / subgrid_time"

@classmethod
def geometry_type(cls) -> str:
return "No Geometry"

Check warning on line 444 in ribasim_qgis/core/nodes.py

View check run for this annotation

Codecov / codecov/patch

ribasim_qgis/core/nodes.py#L444

Added line #L444 was not covered by tests

@classmethod
def attributes(cls) -> list[QgsField]:
return [

Check warning on line 448 in ribasim_qgis/core/nodes.py

View check run for this annotation

Codecov / codecov/patch

ribasim_qgis/core/nodes.py#L448

Added line #L448 was not covered by tests
QgsField("subgrid_id", QVariant.Int),
QgsField("node_id", QVariant.Int),
QgsField("time", QVariant.DateTime),
QgsField("basin_level", QVariant.Double),
QgsField("subgrid_level", QVariant.Double),
]


class BasinArea(Input):
@classmethod
def input_type(cls) -> str:
Expand Down Expand Up @@ -505,8 +525,8 @@ def geometry_type(cls) -> str:
@classmethod
def attributes(cls) -> list[QgsField]:
return [
QgsField("time", QVariant.DateTime),
QgsField("node_id", QVariant.Int),
QgsField("time", QVariant.DateTime),
QgsField("level", QVariant.Double),
QgsField("flow_rate", QVariant.Double),
]
Expand Down Expand Up @@ -583,7 +603,6 @@ def geometry_type(cls) -> str:
@classmethod
def attributes(cls) -> list[QgsField]:
return [
QgsField("time", QVariant.DateTime),
QgsField("node_id", QVariant.Int),
QgsField("time", QVariant.DateTime),
QgsField("level", QVariant.Double),
Expand Down Expand Up @@ -663,8 +682,8 @@ def geometry_type(cls) -> str:
@classmethod
def attributes(cls) -> list[QgsField]:
return [
QgsField("time", QVariant.DateTime),
QgsField("node_id", QVariant.Int),
QgsField("time", QVariant.DateTime),
QgsField("flow_rate", QVariant.Double),
]

Expand Down

0 comments on commit 95c8f7c

Please sign in to comment.