Skip to content

Commit

Permalink
Rename AllocationTarget to TargetLevel (#1141)
Browse files Browse the repository at this point in the history
Follow up of #1082. We discussed we need to be clear it relates to a
level, and this will be consistent with the to-be-created TargetFlow of
#78.
  • Loading branch information
visr authored Feb 16, 2024
1 parent f690380 commit b392735
Show file tree
Hide file tree
Showing 16 changed files with 79 additions and 82 deletions.
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 @@ function User(db::DB, config::Config)::User
)
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.")
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 @@ function Parameters(db::DB, config::Config)::Parameters
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 @@ function Parameters(db::DB, config::Config)::Parameters
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(nodetype::Symbol) = neighbortypes(Val(nodetype))
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{:Terminal}) = n_neighbor_bounds(1, typemax(Int), 0,
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)
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{:PidControl}) = n_neighbor_bounds(0, 1, 1, 1)
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))
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

0 comments on commit b392735

Please sign in to comment.