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 AllocationTarget to TargetLevel #1141

Merged
merged 1 commit into from
Feb 16, 2024
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
33 changes: 16 additions & 17 deletions core/src/allocation_optim.jl
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ Get several variables associated with a basin:
- Its current storage
- The allocation update interval
- The influx (sum of instantaneous vertical fluxes of the basin)
- The index of the connected allocation_target node (0 if such a
- The index of the connected target_level node (0 if such a
node does not exist)
- The index of the basin
"""
Expand All @@ -370,28 +370,27 @@ function get_basin_data(
u::ComponentVector,
node_id::NodeID,
)
(; graph, basin, allocation_target) = p
(; graph, basin, target_level) = p
(; Δt_allocation) = allocation_model
@assert node_id.type == NodeType.Basin
influx = get_flow(graph, node_id, 0.0)
_, basin_idx = id_index(basin.node_id, node_id)
storage_basin = u.storage[basin_idx]
control_inneighbors = inneighbor_labels_type(graph, node_id, EdgeType.control)
if isempty(control_inneighbors)
allocation_target_idx = 0
target_level_idx = 0
else
allocation_target_node_id = first(control_inneighbors)
allocation_target_idx =
findsorted(allocation_target.node_id, allocation_target_node_id)
target_level_node_id = first(control_inneighbors)
target_level_idx = findsorted(target_level.node_id, target_level_node_id)
end
return storage_basin, Δt_allocation, influx, allocation_target_idx, basin_idx
return storage_basin, Δt_allocation, influx, target_level_idx, basin_idx
end

"""
Get the capacity of the basin, i.e. the maximum
flow that can be abstracted from the basin if it is in a
state of surplus storage (0 if no reference levels are provided by
a allocation_target node).
a target_level node).
Storages are converted to flows by dividing by the allocation timestep.
"""
function get_basin_capacity(
Expand All @@ -401,14 +400,14 @@ function get_basin_capacity(
t::Float64,
node_id::NodeID,
)::Float64
(; allocation_target) = p
(; target_level) = p
@assert node_id.type == NodeType.Basin
storage_basin, Δt_allocation, influx, allocation_target_idx, basin_idx =
storage_basin, Δt_allocation, influx, target_level_idx, basin_idx =
get_basin_data(allocation_model, p, u, node_id)
if iszero(allocation_target_idx)
if iszero(target_level_idx)
return 0.0
else
level_max = allocation_target.max_level[allocation_target_idx](t)
level_max = target_level.max_level[target_level_idx](t)
storage_max = get_storage_from_level(p.basin, basin_idx, level_max)
return max(0.0, (storage_basin - storage_max) / Δt_allocation + influx)
end
Expand All @@ -417,7 +416,7 @@ end
"""
Get the demand of the basin, i.e. how large a flow the
basin needs to get to its minimum target level (0 if no
reference levels are provided by a allocation_target node).
reference levels are provided by a target_level node).
Storages are converted to flows by dividing by the allocation timestep.
"""
function get_basin_demand(
Expand All @@ -427,14 +426,14 @@ function get_basin_demand(
t::Float64,
node_id::NodeID,
)::Float64
(; allocation_target) = p
(; target_level) = p
@assert node_id.type == NodeType.Basin
storage_basin, Δt_allocation, influx, allocation_target_idx, basin_idx =
storage_basin, Δt_allocation, influx, target_level_idx, basin_idx =
get_basin_data(allocation_model, p, u, node_id)
if iszero(allocation_target_idx)
if iszero(target_level_idx)
return 0.0
else
level_min = allocation_target.min_level[allocation_target_idx](t)
level_min = target_level.min_level[target_level_idx](t)
storage_min = get_storage_from_level(p.basin, basin_idx, level_min)
return max(0.0, (storage_min - storage_basin) / Δt_allocation - influx)
end
Expand Down
6 changes: 3 additions & 3 deletions core/src/parameter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -519,12 +519,12 @@ struct User <: AbstractParameterNode
end

"""
node_id: node ID of the AllocationTarget node
node_id: node ID of the TargetLevel node
min_level: The minimum target level of the connected basin(s)
max_level: The maximum target level of the connected basin(s)
priority: If in a shortage state, the priority of the demand of the connected basin(s)
"""
struct AllocationTarget
struct TargetLevel
node_id::Vector{NodeID}
min_level::Vector{LinearInterpolation}
max_level::Vector{LinearInterpolation}
Expand Down Expand Up @@ -573,6 +573,6 @@ struct Parameters{T, C1, C2}
discrete_control::DiscreteControl
pid_control::PidControl{T}
user::User
allocation_target::AllocationTarget
target_level::TargetLevel
subgrid::Subgrid
end
18 changes: 9 additions & 9 deletions core/src/read.jl
Original file line number Diff line number Diff line change
Expand Up @@ -765,25 +765,25 @@
)
end

function AllocationTarget(db::DB, config::Config)::AllocationTarget
static = load_structvector(db, config, AllocationTargetStaticV1)
time = load_structvector(db, config, AllocationTargetTimeV1)
function TargetLevel(db::DB, config::Config)::TargetLevel
static = load_structvector(db, config, TargetLevelStaticV1)
time = load_structvector(db, config, TargetLevelTimeV1)

parsed_parameters, valid = parse_static_and_time(
db,
config,
"AllocationTarget";
"TargetLevel";
static,
time,
time_interpolatables = [:min_level, :max_level],
)

if !valid
error("Errors occurred when parsing AllocationTarget data.")
error("Errors occurred when parsing TargetLevel data.")

Check warning on line 782 in core/src/read.jl

View check run for this annotation

Codecov / codecov/patch

core/src/read.jl#L782

Added line #L782 was not covered by tests
end

return AllocationTarget(
NodeID.(NodeType.AllocationTarget, parsed_parameters.node_id),
return TargetLevel(
NodeID.(NodeType.TargetLevel, parsed_parameters.node_id),
parsed_parameters.min_level,
parsed_parameters.max_level,
parsed_parameters.priority,
Expand Down Expand Up @@ -875,7 +875,7 @@
discrete_control = DiscreteControl(db, config)
pid_control = PidControl(db, config, chunk_sizes)
user = User(db, config)
allocation_target = AllocationTarget(db, config)
target_level = TargetLevel(db, config)

basin = Basin(db, config, chunk_sizes)
subgrid_level = Subgrid(db, config, basin)
Expand Down Expand Up @@ -913,7 +913,7 @@
discrete_control,
pid_control,
user,
allocation_target,
target_level,
subgrid_level,
)

Expand Down
8 changes: 4 additions & 4 deletions core/src/schema.jl
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
@schema "ribasim.outlet.static" OutletStatic
@schema "ribasim.user.static" UserStatic
@schema "ribasim.user.time" UserTime
@schema "ribasim.allocationtarget.static" AllocationTargetStatic
@schema "ribasim.allocationtarget.time" AllocationTargetTime
@schema "ribasim.targetlevel.static" TargetLevelStatic
@schema "ribasim.targetlevel.time" TargetLevelTime

const delimiter = " / "
tablename(sv::Type{SchemaVersion{T, N}}) where {T, N} = tablename(sv())
Expand Down Expand Up @@ -238,14 +238,14 @@ end
priority::Int
end

@version AllocationTargetStaticV1 begin
@version TargetLevelStaticV1 begin
node_id::Int
min_level::Float64
max_level::Float64
priority::Int
end

@version AllocationTargetTimeV1 begin
@version TargetLevelTimeV1 begin
node_id::Int
time::DateTime
min_level::Float64
Expand Down
8 changes: 4 additions & 4 deletions core/src/util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -646,20 +646,20 @@ function get_all_priorities(db::DB, config::Config)::Vector{Int}
priorities = Set{Int}()

# TODO: Is there a way to automatically grab all tables with a priority column?
for type in [UserStaticV1, UserTimeV1, AllocationTargetStaticV1, AllocationTargetTimeV1]
for type in [UserStaticV1, UserTimeV1, TargetLevelStaticV1, TargetLevelTimeV1]
union!(priorities, load_structvector(db, config, type).priority)
end
return sort(unique(priorities))
end

function get_basin_priority(p::Parameters, node_id::NodeID)::Int
(; graph, allocation_target) = p
(; graph, target_level) = p
@assert node_id.type == NodeType.Basin
inneighbors_control = inneighbor_labels_type(graph, node_id, EdgeType.control)
if isempty(inneighbors_control)
return 0
else
idx = findsorted(allocation_target.node_id, only(inneighbors_control))
return allocation_target.priority[idx]
idx = findsorted(target_level.node_id, only(inneighbors_control))
return target_level.priority[idx]
end
end
7 changes: 3 additions & 4 deletions core/src/validation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
neighbortypes(::Val{:pump}) = Set((:basin, :fractional_flow, :terminal, :level_boundary))
neighbortypes(::Val{:outlet}) = Set((:basin, :fractional_flow, :terminal, :level_boundary))
neighbortypes(::Val{:user}) = Set((:basin, :fractional_flow, :terminal, :level_boundary))
neighbortypes(::Val{:allocation_target}) = Set((:basin,))
neighbortypes(::Val{:target_level}) = Set((:basin,))
neighbortypes(::Val{:basin}) = Set((
:linear_resistance,
:tabulated_rating_curve,
Expand Down Expand Up @@ -58,7 +58,7 @@
n_neighbor_bounds_flow(::Val{:PidControl}) = n_neighbor_bounds(0, 0, 0, 0)
n_neighbor_bounds_flow(::Val{:DiscreteControl}) = n_neighbor_bounds(0, 0, 0, 0)
n_neighbor_bounds_flow(::Val{:User}) = n_neighbor_bounds(1, 1, 1, 1)
n_neighbor_bounds_flow(::Val{:AllocationTarget}) = n_neighbor_bounds(0, 0, 0, 0)
n_neighbor_bounds_flow(::Val{:TargetLevel}) = n_neighbor_bounds(0, 0, 0, 0)

Check warning on line 61 in core/src/validation.jl

View check run for this annotation

Codecov / codecov/patch

core/src/validation.jl#L61

Added line #L61 was not covered by tests
n_neighbor_bounds_flow(nodetype) =
error("'n_neighbor_bounds_flow' not defined for $nodetype.")

Expand All @@ -77,8 +77,7 @@
n_neighbor_bounds_control(::Val{:DiscreteControl}) =
n_neighbor_bounds(0, 0, 1, typemax(Int))
n_neighbor_bounds_control(::Val{:User}) = n_neighbor_bounds(0, 0, 0, 0)
n_neighbor_bounds_control(::Val{:AllocationTarget}) =
n_neighbor_bounds(0, 0, 1, typemax(Int))
n_neighbor_bounds_control(::Val{:TargetLevel}) = n_neighbor_bounds(0, 0, 1, typemax(Int))

Check warning on line 80 in core/src/validation.jl

View check run for this annotation

Codecov / codecov/patch

core/src/validation.jl#L80

Added line #L80 was not covered by tests
n_neighbor_bounds_control(nodetype) =
error("'n_neighbor_bounds_control' not defined for $nodetype.")

Expand Down
7 changes: 3 additions & 4 deletions core/test/allocation_test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -321,16 +321,15 @@ end
end

@testitem "Allocation level control" begin
toml_path =
normpath(@__DIR__, "../../generated_testmodels/allocation_target/ribasim.toml")
toml_path = normpath(@__DIR__, "../../generated_testmodels/target_level/ribasim.toml")
@test ispath(toml_path)
model = Ribasim.run(toml_path)

storage = Ribasim.get_storages_and_levels(model).storage[1, :]
t = Ribasim.timesteps(model)

p = model.integrator.p
(; user, graph, allocation, basin, allocation_target) = p
(; user, graph, allocation, basin, target_level) = p

d = user.demand_itp[1][2](0)
ϕ = 1e-3 # precipitation
Expand All @@ -341,7 +340,7 @@ end
0,
)
A = basin.area[1][1]
l_max = allocation_target.max_level[1](0)
l_max = target_level.max_level[1](0)
Δt_allocation = allocation.allocation_models[1].Δt_allocation

# Until the first allocation solve, the user abstracts fully
Expand Down
18 changes: 9 additions & 9 deletions docs/core/usage.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -198,9 +198,9 @@ name it must have in the database if it is stored there.
- User: sets water usage demands at certain priorities
- `User / static`: demands
- `User / time`: dynamic demands
- AllocationTarget: Indicates minimum and maximum target level of connected basins for allocation
- `AllocationTarget / static`: static target levels
- `AllocationTarget / time`: dynamic target levels
- TargetLevel: Indicates minimum and maximum target level of connected basins for allocation
- `TargetLevel / static`: static target levels
- `TargetLevel / time`: dynamic target levels
- Terminal: Water sink without state or properties
- `Terminal / static`: - (only node IDs)
- DiscreteControl: Set parameters of other nodes based on model state conditions (e.g. basin level)
Expand Down Expand Up @@ -464,12 +464,12 @@ demand | Float64 | $m^3 s^{-1}$ | -
return_factor | Float64 | - | between [0 - 1]
min_level | Float64 | $m$ | -

# AllocationTarget {#sec-allocation_target}
# TargetLevel {#sec-target_level}

An `AllocationTarget` node associates a minimum and a maximum level with connected basins to be used by the allocation algorithm.
An `TargetLevel` node associates a minimum and a maximum level with connected basins to be used by the allocation algorithm.
Below the minimum level the basin has a demand of the supplied priority,
above the maximum level the basin acts as a source, used by all nodes with demands in order of priority.
The same `AllocationTarget` node can be used for basins in different subnetworks.
The same `TargetLevel` node can be used for basins in different subnetworks.

column | type | unit | restriction
------------- | ------- | ------------ | -----------
Expand All @@ -478,10 +478,10 @@ min_level | Float64 | $m$ | -
max_level | Float64 | $m$ | -
priority | Int | - | positive

## AllocationTarget / time
## TargetLevel / time

This table is the transient form of the `AllocationTarget` table, in which time-dependent minimum and maximum levels can be supplied.
Similar to the static version, only a single priority per `AllocationTarget` node can be provided.
This table is the transient form of the `TargetLevel` table, in which time-dependent minimum and maximum levels can be supplied.
Similar to the static version, only a single priority per `TargetLevel` node can be provided.

column | type | unit | restriction
------------- | ------- | ------------ | -----------
Expand Down
4 changes: 2 additions & 2 deletions python/ribasim/ribasim/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from ribasim import utils
from ribasim.config import (
Allocation,
AllocationTarget,
Basin,
Compression,
DiscreteControl,
Expand All @@ -20,6 +19,7 @@
Results,
Solver,
TabulatedRatingCurve,
TargetLevel,
Terminal,
User,
Verbosity,
Expand All @@ -30,7 +30,7 @@

__all__ = [
"Allocation",
"AllocationTarget",
"TargetLevel",
"Basin",
"DiscreteControl",
"Compression",
Expand Down
14 changes: 7 additions & 7 deletions python/ribasim/ribasim/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@

# These schemas are autogenerated
from ribasim.schemas import (
AllocationTargetStaticSchema,
AllocationTargetTimeSchema,
BasinProfileSchema,
BasinStateSchema,
BasinStaticSchema,
Expand All @@ -28,6 +26,8 @@
PumpStaticSchema,
TabulatedRatingCurveStaticSchema,
TabulatedRatingCurveTimeSchema,
TargetLevelStaticSchema,
TargetLevelTimeSchema,
TerminalStaticSchema,
UserStaticSchema,
UserTimeSchema,
Expand Down Expand Up @@ -137,13 +137,13 @@ class User(NodeModel):
)


class AllocationTarget(NodeModel):
static: TableModel[AllocationTargetStaticSchema] = Field(
default_factory=TableModel[AllocationTargetStaticSchema],
class TargetLevel(NodeModel):
static: TableModel[TargetLevelStaticSchema] = Field(
default_factory=TableModel[TargetLevelStaticSchema],
json_schema_extra={"sort_keys": ["node_id", "priority"]},
)
time: TableModel[AllocationTargetTimeSchema] = Field(
default_factory=TableModel[AllocationTargetTimeSchema],
time: TableModel[TargetLevelTimeSchema] = Field(
default_factory=TableModel[TargetLevelTimeSchema],
json_schema_extra={"sort_keys": ["node_id", "priority", "time"]},
)

Expand Down
4 changes: 2 additions & 2 deletions python/ribasim/ribasim/geometry/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ def plot(self, ax=None, zorder=None) -> Any:
"DiscreteControl": "*",
"PidControl": "x",
"User": "s",
"AllocationTarget": "o",
"TargetLevel": "o",
"": "o",
}

Expand All @@ -223,7 +223,7 @@ def plot(self, ax=None, zorder=None) -> Any:
"DiscreteControl": "k",
"PidControl": "k",
"User": "g",
"AllocationTarget": "k",
"TargetLevel": "k",
"": "k",
}
assert self.df is not None
Expand Down
Loading
Loading