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 cyclic forcing support #2081

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
4 changes: 4 additions & 0 deletions python/ribasim/ribasim/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,16 @@ class Node(pydantic.BaseModel):
Optionally adds this node to a subnetwork, which is input for the allocation algorithm.
source_priority : int
Optionally overrides the source priority for this node, which is used in the allocation algorithm.
cyclic_forcing : bool
Optionally extrapolate forcing timeseries periodically. Defaults to False.
"""

node_id: NonNegativeInt | None = None
geometry: Point
name: str = ""
subnetwork_id: int | None = None
source_priority: int | None = None
cyclic_forcing: bool = False

model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow")

Expand Down Expand Up @@ -224,6 +227,7 @@ def into_geodataframe(self, node_type: str, node_id: int) -> GeoDataFrame:
"source_priority": pd.Series(
[self.source_priority], dtype=pd.Int32Dtype()
),
"cyclic_forcing": pd.Series([self.cyclic_forcing], dtype=bool),
**extra,
},
geometry=[self.geometry],
Expand Down
1 change: 1 addition & 0 deletions python/ribasim/ribasim/geometry/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class NodeSchema(_GeoBaseSchema):
source_priority: Series[pd.Int32Dtype] = pa.Field(
default=pd.NA, nullable=True, coerce=True
)
cyclic_forcing: Series[bool] = pa.Field(default=False)
geometry: GeoSeries[Point] = pa.Field(default=None, nullable=True)

@classmethod
Expand Down
2 changes: 2 additions & 0 deletions python/ribasim_testmodels/ribasim_testmodels/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
basic_arrow_model,
basic_model,
basic_transient_model,
cyclic_forcing_model,
outlet_model,
tabulated_rating_curve_model,
)
Expand Down Expand Up @@ -75,6 +76,7 @@
"concentration_condition_model",
"continuous_concentration_condition_model",
"connector_node_flow_condition_model",
"cyclic_forcing_model",
"discrete_control_of_pid_control_model",
"fair_distribution_model",
"flow_boundary_time_model",
Expand Down
59 changes: 52 additions & 7 deletions python/ribasim_testmodels/ribasim_testmodels/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import numpy as np
import pandas as pd
import ribasim
from ribasim import Model
from ribasim.config import Experimental, Node
from ribasim.input_base import TableModel
from ribasim.nodes import (
Expand All @@ -20,9 +21,9 @@
from shapely.geometry import MultiPolygon, Point


def basic_model() -> ribasim.Model:
def basic_model() -> Model:
# Setup model
model = ribasim.Model(
model = Model(
starttime="2020-01-01",
endtime="2021-01-01",
crs="EPSG:28992",
Expand Down Expand Up @@ -205,14 +206,14 @@ def basic_model() -> ribasim.Model:
return model


def basic_arrow_model() -> ribasim.Model:
def basic_arrow_model() -> Model:
model = basic_model()
model.basin.profile.set_filepath(Path("profile.arrow"))
model.input_dir = Path("input")
return model


def basic_transient_model() -> ribasim.Model:
def basic_transient_model() -> Model:
"""Update the basic model with transient forcing."""
model = basic_model()
time = pd.date_range(model.starttime, model.endtime)
Expand Down Expand Up @@ -262,7 +263,7 @@ def basic_transient_model() -> ribasim.Model:
return model


def tabulated_rating_curve_model() -> ribasim.Model:
def tabulated_rating_curve_model() -> Model:
"""
Set up a model where the upstream Basin has two TabulatedRatingCurve attached.
Expand All @@ -271,7 +272,7 @@ def tabulated_rating_curve_model() -> ribasim.Model:
Only the upstream Basin receives a (constant) precipitation.
"""
# Setup a model:
model = ribasim.Model(
model = Model(
starttime="2020-01-01",
endtime="2021-01-01",
crs="EPSG:28992",
Expand Down Expand Up @@ -350,7 +351,7 @@ def tabulated_rating_curve_model() -> ribasim.Model:

def outlet_model():
"""Set up a basic model with an outlet that encounters various physical constraints."""
model = ribasim.Model(
model = Model(
starttime="2020-01-01",
endtime="2021-01-01",
crs="EPSG:28992",
Expand Down Expand Up @@ -392,3 +393,47 @@ def outlet_model():
model.link.add(model.outlet[2], model.basin[3])

return model


def cyclic_forcing_model() -> Model:
model = Model(
starttime="2020-01-01",
endtime="2021-01-01",
crs="EPSG:28992",
)

bsn = model.basin.add(
Node(1, Point(0, 0), cyclic_forcing=True),
[
basin.Profile(level=[0.0, 1.0], area=100.0),
basin.Time(
time=["2020-01-01", "2020-05-01", "2021-09-01", "2020-01-01"],
precipitation=[1.0, 2.0, 1.0, 2.0],
),
],
)

lr = model.linear_resistance.add(
Node(2, Point(1, 0)), [linear_resistance.Static(resistance=[1.0])]
)

lb = model.level_boundary.add(
Node(3, Point(2, 0), cyclic_forcing=True),
[
level_boundary.Time(
time=["2020-01-01", "2020-05-01"],
level=[2.0, 3.0],
)
],
)

fb = model.flow_boundary.add(
Node(4, Point(0, 1), cyclic_forcing=True),
[flow_boundary.Time(time=["2020-01-01", "2020-07-01"], flow_rate=[1.0, 2.0])],
)

model.edge.add(bsn, lr)
model.edge.add(lr, lb)
model.edge.add(fb, bsn)

return model
Loading