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

rename Basin / forcing to Basin / time #622

Merged
merged 1 commit into from
Sep 25, 2023
Merged
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: 2 additions & 2 deletions core/src/create.jl
Original file line number Diff line number Diff line change
Expand Up @@ -491,9 +491,9 @@ function Basin(db::DB, config::Config, chunk_size::Int)::Basin

area, level, storage = create_storage_tables(db, config)

# both static and forcing are optional, but we need fallback defaults
# both static and time are optional, but we need fallback defaults
static = load_structvector(db, config, BasinStaticV1)
time = load_structvector(db, config, BasinForcingV1)
time = load_structvector(db, config, BasinTimeV1)

set_static_value!(table, node_id, static)
set_current_value!(table, node_id, time, config.starttime)
Expand Down
4 changes: 2 additions & 2 deletions core/src/solve.jl
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ struct Basin{T, C} <: AbstractParameterNode
level::Vector{Vector{Float64}}
storage::Vector{Vector{Float64}}
# data source for parameter updates
time::StructVector{BasinForcingV1, C, Int}
time::StructVector{BasinTimeV1, C, Int}

function Basin(
node_id,
Expand All @@ -111,7 +111,7 @@ struct Basin{T, C} <: AbstractParameterNode
area,
level,
storage,
time::StructVector{BasinForcingV1, C, Int},
time::StructVector{BasinTimeV1, C, Int},
) where {T, C}
errors = valid_profiles(node_id, level, area)
if isempty(errors)
Expand Down
16 changes: 8 additions & 8 deletions core/src/validation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
@schema "ribasim.discretecontrol.condition" DiscreteControlCondition
@schema "ribasim.discretecontrol.logic" DiscreteControlLogic
@schema "ribasim.basin.static" BasinStatic
@schema "ribasim.basin.forcing" BasinForcing
@schema "ribasim.basin.time" BasinTime
@schema "ribasim.basin.profile" BasinProfile
@schema "ribasim.basin.state" BasinState
@schema "ribasim.terminal.static" TerminalStatic
Expand Down Expand Up @@ -36,9 +36,9 @@ From a SchemaVersion("ribasim.flowboundary.static", 1) return (:FlowBoundary, :s
"""
function nodetype(sv::SchemaVersion{T, N})::Tuple{Symbol, Symbol} where {T, N}
n, k = split(string(T), ".")[2:3]
# Names derived from a schema are in underscores (basinforcing),
# so we parse the related record Ribasim.BasinForcingV1
# to derive BasinForcing from it.
# Names derived from a schema are in underscores (basintime),
# so we parse the related record Ribasim.BasinTimeV1
# to derive BasinTime from it.
record = Legolas.record_type(sv)
node = last(split(string(Symbol(record)), "."))
return Symbol(node[begin:length(n)]), Symbol(k)
Expand Down Expand Up @@ -165,7 +165,7 @@ end
urban_runoff::Float64
end

@version BasinForcingV1 begin
@version BasinTimeV1 begin
node_id::Int
time::DateTime
drainage::Float64
Expand Down Expand Up @@ -314,7 +314,7 @@ function variable_nt(s::Any)
NamedTuple{names}((getfield(s, x) for x in names))
end

function is_consistent(node, edge, state, static, profile, forcing)
function is_consistent(node, edge, state, static, profile, time)

# Check that node ids exist
# TODO Do we need to check the reverse as well? All ids in use?
Expand All @@ -324,7 +324,7 @@ function is_consistent(node, edge, state, static, profile, forcing)
@assert state.node_id ⊆ ids "State id not in node ids"
@assert static.node_id ⊆ ids "Static id not in node ids"
@assert profile.node_id ⊆ ids "Profile id not in node ids"
@assert forcing.node_id ⊆ ids "Forcing id not in node ids"
@assert time.node_id ⊆ ids "Time id not in node ids"

# Check edges for uniqueness
@assert allunique(edge, [:from_node_id, :to_node_id]) "Duplicate edge found"
Expand All @@ -351,7 +351,7 @@ sort_by_function(table::StructVector{TabulatedRatingCurveStaticV1}) = sort_by_id
sort_by_function(table::StructVector{BasinProfileV1}) = sort_by_id_level

const TimeSchemas = Union{
BasinForcingV1,
BasinTimeV1,
FlowBoundaryTimeV1,
LevelBoundaryTimeV1,
PidControlTimeV1,
Expand Down
2 changes: 1 addition & 1 deletion core/test/docs.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ compression_level = 6 # optional, default 6
# For large tables this can benefit from better compressed file sizes.
# This is optional, tables are retrieved from the GeoPackage if not specified in the TOML.
[basin]
forcing = "forcing.arrow"
time = "basin/time.arrow"

[solver]
algorithm = "QNDF" # optional, default "QNDF"
Expand Down
2 changes: 1 addition & 1 deletion core/test/io.jl
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ end
db = SQLite.DB(gpkg_path)

# load a sorted table
table = Ribasim.load_structvector(db, config, Ribasim.BasinForcingV1)
table = Ribasim.load_structvector(db, config, Ribasim.BasinTimeV1)
by = Ribasim.sort_by_function(table)
@test by == Ribasim.sort_by_time_id
# reverse it so it needs sorting
Expand Down
2 changes: 1 addition & 1 deletion core/test/testrun.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ output_dir = "../generated_testmodels/lhm"
geopackage = "model.gpkg"

[basin]
forcing = "forcing.arrow"
time = "basin/time.arrow"

[solver]
saveat = 86400
4 changes: 2 additions & 2 deletions core/test/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ end
area,
level,
storage,
StructVector{Ribasim.BasinForcingV1}(undef, 0),
StructVector{Ribasim.BasinTimeV1}(undef, 0),
)

@test basin.level[2][1] === 4.0
Expand Down Expand Up @@ -91,7 +91,7 @@ end
[area],
[level],
[storage],
StructVector{Ribasim.BasinForcingV1}(undef, 0),
StructVector{Ribasim.BasinTimeV1}(undef, 0),
)

logger = TestLogger()
Expand Down
8 changes: 4 additions & 4 deletions docs/core/usage.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ name it must have in the GeoPackage if it is stored there.
- Basin: stores water
- `Basin / static`: default forcing values, used if no dynamic data given in the forcing table
- `Basin / profile`: geometries of the basins
- `Basin / forcing`: time series of the forcing values
- `Basin / time`: time series of the forcing values
- `Basin / state`: used as initial condition of the basins
- FractionalFlow: connect two of these from a Basin to get a fixed ratio bifurcation
- `FractionalFlow / static`: fractions
Expand Down Expand Up @@ -196,10 +196,10 @@ nodes in QGIS. For instance, you can draw a line connecting the two node coordin

# Basin

The Basin table can be used to set the static value of variables. The forcing table has a
The Basin table can be used to set the static value of variables. The time table has a
similar schema, with the time column added. A static value for a variable is only used if
there is no dynamic forcing data for that variable. Specifically, if there is either no
forcing table, it is empty, or all timestamps of that variable are missing.
time table, it is empty, or all timestamps of that variable are missing.

column | type | unit | restriction
--------- | ------- | ------------ | -----------
Expand All @@ -214,7 +214,7 @@ Note that if variables are not set in the static table, default values are used
possible. These are generally zero, e.g. no precipitation, no inflow. If it is not possible
to have a reasonable and safe default, a value must be provided in the static table.

## Basin / forcing
## Basin / time

This table is the transient form of the `Basin` table.
The only difference is that a time column is added.
Expand Down
2 changes: 1 addition & 1 deletion docs/python/examples.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@
"metadata": {},
"outputs": [],
"source": [
"model.basin.forcing = forcing\n",
"model.basin.time = forcing\n",
"model.basin.state = state"
]
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@
"precipitation",
"urban_runoff"
],
"$id": "https://deltares.github.io/Ribasim/schema/BasinForcing.schema.json",
"title": "BasinForcing",
"description": "A BasinForcing object based on Ribasim.BasinForcingV1",
"$id": "https://deltares.github.io/Ribasim/schema/BasinTime.schema.json",
"title": "BasinTime",
"description": "A BasinTime object based on Ribasim.BasinTimeV1",
"type": "object"
}
4 changes: 2 additions & 2 deletions docs/schema/Config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,10 @@
},
"basin": {
"default": {
"forcing": null,
"profile": null,
"state": null,
"static": null
"static": null,
"time": null
},
"$ref": "https://deltares.github.io/Ribasim/schema/basin.schema.json"
},
Expand Down
4 changes: 2 additions & 2 deletions docs/schema/basin.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
],
"default": null
},
"static": {
"time": {
"format": "default",
"anyOf": [
{
Expand All @@ -25,7 +25,7 @@
],
"default": null
},
"forcing": {
"static": {
"format": "default",
"anyOf": [
{
Expand Down
10 changes: 5 additions & 5 deletions docs/schema/root.schema.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"properties": {
"BasinTime": {
"$ref": "BasinTime.schema.json"
},
"DiscreteControlLogic": {
"$ref": "DiscreteControlLogic.schema.json"
},
Expand All @@ -25,15 +28,12 @@
"DiscreteControlCondition": {
"$ref": "DiscreteControlCondition.schema.json"
},
"BasinForcing": {
"$ref": "BasinForcing.schema.json"
"LinearResistanceStatic": {
"$ref": "LinearResistanceStatic.schema.json"
},
"FractionalFlowStatic": {
"$ref": "FractionalFlowStatic.schema.json"
},
"LinearResistanceStatic": {
"$ref": "LinearResistanceStatic.schema.json"
},
"PidControlStatic": {
"$ref": "PidControlStatic.schema.json"
},
Expand Down
4 changes: 2 additions & 2 deletions python/ribasim/ribasim/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ class Terminal(BaseModel):

class Basin(BaseModel):
profile: Optional[str] = None
time: Optional[str] = None
static: Optional[str] = None
forcing: Optional[str] = None
state: Optional[str] = None


Expand Down Expand Up @@ -171,7 +171,7 @@ class Config(BaseModel):
)
basin: Basin = Field(
default_factory=lambda: Basin.parse_obj(
{"forcing": None, "profile": None, "state": None, "static": None}
{"profile": None, "state": None, "static": None, "time": None}
)
)
linear_resistance: LinearResistance = Field(
Expand Down
34 changes: 17 additions & 17 deletions python/ribasim/ribasim/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@
from pydantic import BaseModel, Field


class BasinTime(BaseModel):
remarks: str = Field("", description="a hack for pandera")
time: datetime
precipitation: float
infiltration: float
urban_runoff: float
node_id: int
potential_evaporation: float
drainage: float


class DiscreteControlLogic(BaseModel):
remarks: str = Field("", description="a hack for pandera")
truth_state: str
Expand Down Expand Up @@ -77,15 +88,12 @@ class DiscreteControlCondition(BaseModel):
look_ahead: Optional[float] = None


class BasinForcing(BaseModel):
class LinearResistanceStatic(BaseModel):
remarks: str = Field("", description="a hack for pandera")
time: datetime
precipitation: float
infiltration: float
urban_runoff: float
active: Optional[bool] = None
node_id: int
potential_evaporation: float
drainage: float
resistance: float
control_state: Optional[str] = None


class FractionalFlowStatic(BaseModel):
Expand All @@ -95,14 +103,6 @@ class FractionalFlowStatic(BaseModel):
control_state: Optional[str] = None


class LinearResistanceStatic(BaseModel):
remarks: str = Field("", description="a hack for pandera")
active: Optional[bool] = None
node_id: int
resistance: float
control_state: Optional[str] = None


class PidControlStatic(BaseModel):
integral: float
remarks: str = Field("", description="a hack for pandera")
Expand Down Expand Up @@ -215,6 +215,7 @@ class BasinStatic(BaseModel):


class Root(BaseModel):
BasinTime: Optional[BasinTime] = None
DiscreteControlLogic: Optional[DiscreteControlLogic] = None
Edge: Optional[Edge] = None
FlowBoundaryTime: Optional[FlowBoundaryTime] = None
Expand All @@ -223,9 +224,8 @@ class Root(BaseModel):
LevelBoundaryStatic: Optional[LevelBoundaryStatic] = None
UserTime: Optional[UserTime] = None
DiscreteControlCondition: Optional[DiscreteControlCondition] = None
BasinForcing: Optional[BasinForcing] = None
FractionalFlowStatic: Optional[FractionalFlowStatic] = None
LinearResistanceStatic: Optional[LinearResistanceStatic] = None
FractionalFlowStatic: Optional[FractionalFlowStatic] = None
PidControlStatic: Optional[PidControlStatic] = None
PidControlTime: Optional[PidControlTime] = None
ManningResistanceStatic: Optional[ManningResistanceStatic] = None
Expand Down
12 changes: 5 additions & 7 deletions python/ribasim/ribasim/node_types/basin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

from ribasim.input_base import TableModel
from ribasim.schemas import ( # type: ignore
BasinForcingSchema,
BasinProfileSchema,
BasinStateSchema,
BasinStaticSchema,
BasinTimeSchema,
)

__all__ = ("Basin",)
Expand All @@ -23,24 +23,22 @@ class Basin(TableModel):
Table describing the geometry.
static : pandas.DataFrame, optional
Table describing the constant fluxes.
forcing : pandas.DataFrame, optional
time : pandas.DataFrame, optional
Table describing the time-varying fluxes.
state : pandas.DataFrame, optional
Table describing the initial condition.
"""

profile: DataFrame[BasinProfileSchema]
static: Optional[DataFrame[BasinStaticSchema]] = None
forcing: Optional[DataFrame[BasinForcingSchema]] = None
time: Optional[DataFrame[BasinTimeSchema]] = None
state: Optional[DataFrame[BasinStateSchema]] = None

def sort(self):
self.profile.sort_values(["node_id", "level"], ignore_index=True, inplace=True)
if self.static is not None:
self.static.sort_values("node_id", ignore_index=True, inplace=True)
if self.forcing is not None:
self.forcing.sort_values(
["time", "node_id"], ignore_index=True, inplace=True
)
if self.time is not None:
self.time.sort_values(["time", "node_id"], ignore_index=True, inplace=True)
if self.state is not None:
self.state.sort_values("node_id", ignore_index=True, inplace=True)
10 changes: 5 additions & 5 deletions python/ribasim/tests/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def test_basic(basic, tmp_path):
assert_array_equal(index_a, index_b)
assert_equal(model_orig.node.static, model_loaded.node.static)
assert_equal(model_orig.edge.static, model_loaded.edge.static)
assert model_loaded.basin.forcing is None
assert model_loaded.basin.time is None


def test_basic_transient(basic_transient, tmp_path):
Expand All @@ -47,10 +47,10 @@ def test_basic_transient(basic_transient, tmp_path):
assert_equal(model_orig.node.static, model_loaded.node.static)
assert_equal(model_orig.edge.static, model_loaded.edge.static)

forcing = model_loaded.basin.forcing
assert model_orig.basin.forcing.time[0] == forcing.time[0]
assert_equal(model_orig.basin.forcing, forcing)
assert forcing.shape == (1468, 8)
time = model_loaded.basin.time
assert model_orig.basin.time.time[0] == time.time[0]
assert_equal(model_orig.basin.time, time)
assert time.shape == (1468, 8)


def test_pydantic():
Expand Down
Loading