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

Source priority input #2022

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 4 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
12 changes: 6 additions & 6 deletions core/src/allocation_init.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Find the edges from the main network to a subnetwork."""
function find_subnetwork_connections!(p::Parameters)::Nothing
(; allocation, graph, allocation) = p
n_priorities = length(allocation.priorities)
n_demand_priorities = length(allocation.demand_priorities)
(; subnetwork_demands, subnetwork_allocateds) = allocation
# Find edges (node_id, outflow_id) where the source node has subnetwork id 1 and the
# destination node subnetwork id ≠1
Expand All @@ -12,10 +12,10 @@ function find_subnetwork_connections!(p::Parameters)::Nothing
get_main_network_connections(p, graph[outflow_id].subnetwork_id)
edge = (node_id, outflow_id)
push!(main_network_source_edges, edge)
# Allocate memory for the demands and priorities
# Allocate memory for the demands and demand priorities
# from the subnetwork via this edge
subnetwork_demands[edge] = zeros(n_priorities)
subnetwork_allocateds[edge] = zeros(n_priorities)
subnetwork_demands[edge] = zeros(n_demand_priorities)
subnetwork_allocateds[edge] = zeros(n_demand_priorities)
end
end
end
Expand Down Expand Up @@ -244,7 +244,7 @@ Add capacity constraints to the outflow edge of UserDemand nodes.
The constraint indices are the UserDemand node IDs.

Constraint:
flow over UserDemand edge outflow edge <= cumulative return flow from previous priorities
flow over UserDemand edge outflow edge <= cumulative return flow from previous demand priorities
"""
function add_constraints_user_source!(
problem::JuMP.Model,
Expand Down Expand Up @@ -487,7 +487,7 @@ function get_sources_in_order(
subnetwork_id::Integer,
)::OrderedDict{Tuple{NodeID, NodeID}, AllocationSource}
# NOTE: return flow has to be done before other sources, to prevent that
# return flow is directly used within the same priority
# return flow is directly used within the same source priority

(; basin, user_demand, graph, allocation) = p

Expand Down
169 changes: 96 additions & 73 deletions core/src/allocation_optim.jl

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions core/src/config.jl
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ end
@option struct Allocation <: TableOption
timestep::Float64 = 86400
use_allocation::Bool = false
default_source_priority_user_demand::Int = 1000
default_source_priority_boundary::Int = 2000
default_source_priority_level_demand::Int = 3000
default_source_priority_flow_demand::Int = 4000
default_source_priority_subnetwork_inlet::Int = 5000
end

@option struct Experimental <: TableOption
Expand Down
30 changes: 15 additions & 15 deletions core/src/parameter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ edge: The outflow edge of the source
type: The type of source (edge, basin, main_to_sub, user_return, buffer)
capacity: The initial capacity of the source as determined by the physical layer
capacity_reduced: The capacity adjusted by passed optimizations
basin_flow_rate: The total outflow rate of a basin when optimized over all sources for one priority.
basin_flow_rate: The total outflow rate of a basin when optimized over all sources for one demand priority.
Ignored when the source is not a basin.
"""
@kwdef mutable struct AllocationSource
Expand All @@ -169,7 +169,7 @@ Store information for a subnetwork used for allocation.

subnetwork_id: The ID of this allocation network
capacity: The capacity per edge of the allocation network, as constrained by nodes that have a max_flow_rate
flow: The flows over all the edges in the subnetwork for a certain priority (used for allocation_flow output)
flow: The flows over all the edges in the subnetwork for a certain demand priority (used for allocation_flow output)
sources: source data in preferred order of optimization
problem: The JuMP.jl model for solving the allocation problem
Δt_allocation: The time interval between consecutive allocation solves
Expand All @@ -189,7 +189,7 @@ subnetwork_ids: The unique sorted allocation network IDs
allocation_models: The allocation models for the main network and subnetworks corresponding to
subnetwork_ids
main_network_connections: (from_id, to_id) from the main network to the subnetwork per subnetwork
priorities: All used priority values.
demand_priorities: All used demand priority values.
subnetwork_demands: The demand of an edge from the main network to a subnetwork
subnetwork_allocateds: The allocated flow of an edge from the main network to a subnetwork
mean_input_flows: Per subnetwork, flows averaged over Δt_allocation over edges that are allocation sources
Expand All @@ -203,7 +203,7 @@ record_flow: A record of all flows computed by allocation optimization, eventual
allocation_models::Vector{AllocationModel} = AllocationModel[]
main_network_connections::Vector{Vector{Tuple{NodeID, NodeID}}} =
Vector{Tuple{NodeID, NodeID}}[]
priorities::Vector{Int32}
demand_priorities::Vector{Int32}
subnetwork_demands::Dict{Tuple{NodeID, NodeID}, Vector{Float64}} = Dict()
subnetwork_allocateds::Dict{Tuple{NodeID, NodeID}, Vector{Float64}} = Dict()
mean_input_flows::Vector{Dict{Tuple{NodeID, NodeID}, Float64}}
Expand All @@ -213,7 +213,7 @@ record_flow: A record of all flows computed by allocation optimization, eventual
subnetwork_id::Vector{Int32},
node_type::Vector{String},
node_id::Vector{Int32},
priority::Vector{Int32},
demand_priority::Vector{Int32},
demand::Vector{Float64},
allocated::Vector{Float64},
realized::Vector{Float64},
Expand All @@ -222,7 +222,7 @@ record_flow: A record of all flows computed by allocation optimization, eventual
subnetwork_id = Int32[],
node_type = String[],
node_id = Int32[],
priority = Int32[],
demand_priority = Int32[],
demand = Float64[],
allocated = Float64[],
realized = Float64[],
Expand All @@ -235,7 +235,7 @@ record_flow: A record of all flows computed by allocation optimization, eventual
to_node_type::Vector{String},
to_node_id::Vector{Int32},
subnetwork_id::Vector{Int32},
priority::Vector{Int32},
demand_priority::Vector{Int32},
flow_rate::Vector{Float64},
optimization_type::Vector{String},
} = (;
Expand All @@ -246,7 +246,7 @@ record_flow: A record of all flows computed by allocation optimization, eventual
to_node_type = String[],
to_node_id = Int32[],
subnetwork_id = Int32[],
priority = Int32[],
demand_priority = Int32[],
flow_rate = Float64[],
optimization_type = String[],
)
Expand Down Expand Up @@ -846,14 +846,14 @@ inflow_edge: incoming flow edge
outflow_edge: outgoing flow edge metadata
The ID of the source node is always the ID of the UserDemand node
active: whether this node is active and thus demands water
demand: water flux demand of UserDemand per priority (node_idx, priority_idx)
Each UserDemand has a demand for all priorities,
demand: water flux demand of UserDemand per demand priority (node_idx, demand_priority_idx)
Each UserDemand has a demand for all demand priorities,
which is 0.0 if it is not provided explicitly.
demand_reduced: the total demand reduced by allocated flows. This is used for goal programming,
and requires separate memory from `demand` since demands can come from the BMI
demand_itp: Timeseries interpolation objects for demands
demand_from_timeseries: If false the demand comes from the BMI or is fixed
allocated: water flux currently allocated to UserDemand per priority (node_idx, priority_idx)
allocated: water flux currently allocated to UserDemand per demand priority (node_idx, demand_priority_idx)
return_factor: the factor in [0,1] of how much of the abstracted water is given back to the system
min_level: The level of the source Basin below which the UserDemand does not abstract
concentration: matrix with boundary concentrations for each Basin and substance
Expand All @@ -879,26 +879,26 @@ end
node_id: node ID of the LevelDemand 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)
demand_priority: If in a shortage state, the priority of the demand of the connected basin(s)
"""
@kwdef struct LevelDemand <: AbstractDemandNode
node_id::Vector{NodeID}
min_level::Vector{ScalarInterpolation} = fill(-Inf, length(node_id))
max_level::Vector{ScalarInterpolation} = fill(Inf, length(node_id))
priority::Vector{Int32}
demand_priority::Vector{Int32}
end

"""
node_id: node ID of the FlowDemand node
demand_itp: The time interpolation of the demand of the node
demand: The current demand of the node
priority: The priority of the demand of the node
demand_priority: The priority of the demand of the node
"""
@kwdef struct FlowDemand <: AbstractDemandNode
node_id::Vector{NodeID}
demand_itp::Vector{ScalarInterpolation}
demand::Vector{Float64}
priority::Vector{Int32}
demand_priority::Vector{Int32}
end

"Subgrid linearly interpolates basin levels."
Expand Down
44 changes: 22 additions & 22 deletions core/src/read.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1055,7 +1055,7 @@ function user_demand_static!(
min_level::Vector{Float64},
static::StructVector{UserDemandStaticV1},
ids::Vector{Int32},
priorities::Vector{Int32},
demand_priorities::Vector{Int32},
)::Nothing
for group in IterTools.groupby(row -> row.node_id, static)
first_row = first(group)
Expand All @@ -1072,16 +1072,16 @@ function user_demand_static!(
min_level[user_demand_idx] = first_row.min_level

for row in group
priority_idx = findsorted(priorities, row.priority)
demand_priority_idx = findsorted(demand_priorities, row.demand_priority)
demand_row = coalesce(row.demand, 0.0)
demand_itp_old = demand_itp[user_demand_idx][priority_idx]
demand_itp[user_demand_idx][priority_idx] = LinearInterpolation(
demand_itp_old = demand_itp[user_demand_idx][demand_priority_idx]
demand_itp[user_demand_idx][demand_priority_idx] = LinearInterpolation(
fill(demand_row, 2),
demand_itp_old.t;
extrapolate = true,
cache_parameters = true,
)
demand[user_demand_idx, priority_idx] = demand_row
demand[user_demand_idx, demand_priority_idx] = demand_row
end
end
return nothing
Expand All @@ -1096,13 +1096,13 @@ function user_demand_time!(
min_level::Vector{Float64},
time::StructVector{UserDemandTimeV1},
ids::Vector{Int32},
priorities::Vector{Int32},
demand_priorities::Vector{Int32},
config::Config,
)::Bool
errors = false
t_end = seconds_since(config.endtime, config.starttime)

for group in IterTools.groupby(row -> (row.node_id, row.priority), time)
for group in IterTools.groupby(row -> (row.node_id, row.demand_priority), time)
first_row = first(group)
user_demand_idx = findsorted(ids, first_row.node_id)

Expand All @@ -1120,7 +1120,7 @@ function user_demand_time!(

min_level[user_demand_idx] = first_row.min_level

priority_idx = findsorted(priorities, first_row.priority)
demand_priority_idx = findsorted(demand_priorities, first_row.demand_priority)
demand_p_itp = get_scalar_interpolation(
config.starttime,
t_end,
Expand All @@ -1130,8 +1130,8 @@ function user_demand_time!(
default_value = 0.0,
interpolation_type = LinearInterpolation,
)
demand[user_demand_idx, priority_idx] = demand_p_itp(0.0)
demand_itp[user_demand_idx][priority_idx] = demand_p_itp
demand[user_demand_idx, demand_priority_idx] = demand_p_itp(0.0)
demand_itp[user_demand_idx][demand_priority_idx] = demand_p_itp
end
return errors
end
Expand All @@ -1149,21 +1149,21 @@ function UserDemand(db::DB, config::Config, graph::MetaGraph)::UserDemand
end

# Initialize vectors for UserDemand fields
priorities = get_all_priorities(db, config)
demand_priorities = get_all_demand_priorities(db, config)
n_user = length(node_ids)
n_priority = length(priorities)
n_demand_priority = length(demand_priorities)
active = fill(true, n_user)
demand = zeros(n_user, n_priority)
demand_reduced = zeros(n_user, n_priority)
demand = zeros(n_user, n_demand_priority)
demand_reduced = zeros(n_user, n_demand_priority)
trivial_timespan = [0.0, prevfloat(Inf)]
demand_itp = [
ScalarInterpolation[
LinearInterpolation(zeros(2), trivial_timespan; cache_parameters = true) for
i in eachindex(priorities)
i in eachindex(demand_priorities)
] for j in eachindex(node_ids)
]
demand_from_timeseries = fill(false, n_user)
allocated = fill(Inf, n_user, n_priority)
allocated = fill(Inf, n_user, n_demand_priority)
return_factor = [
LinearInterpolation(zeros(2), trivial_timespan; cache_parameters = true) for
i in eachindex(node_ids)
Expand All @@ -1179,7 +1179,7 @@ function UserDemand(db::DB, config::Config, graph::MetaGraph)::UserDemand
min_level,
static,
ids,
priorities,
demand_priorities,
)

# Process time table
Expand All @@ -1192,7 +1192,7 @@ function UserDemand(db::DB, config::Config, graph::MetaGraph)::UserDemand
min_level,
time,
ids,
priorities,
demand_priorities,
config,
)

Expand All @@ -1202,7 +1202,7 @@ function UserDemand(db::DB, config::Config, graph::MetaGraph)::UserDemand
concentration[:, Substance.UserDemand] .= 1.0
set_concentrations!(concentration, concentration_time, substances, node_ids)

if errors || !valid_demand(node_ids, demand_itp, priorities)
if errors || !valid_demand(node_ids, demand_itp, demand_priorities)
error("Errors occurred when parsing UserDemand data.")
end

Expand Down Expand Up @@ -1247,7 +1247,7 @@ function LevelDemand(db::DB, config::Config)::LevelDemand
NodeID.(NodeType.LevelDemand, node_id, eachindex(node_id)),
parsed_parameters.min_level,
parsed_parameters.max_level,
parsed_parameters.priority,
parsed_parameters.demand_priority,
)
end

Expand Down Expand Up @@ -1275,7 +1275,7 @@ function FlowDemand(db::DB, config::Config)::FlowDemand
node_id = NodeID.(NodeType.FlowDemand, node_id, eachindex(node_id)),
demand_itp = parsed_parameters.demand,
demand,
parsed_parameters.priority,
parsed_parameters.demand_priority,
)
end

Expand Down Expand Up @@ -1501,7 +1501,7 @@ function Allocation(db::DB, config::Config, graph::MetaGraph)::Allocation
end

return Allocation(;
priorities = get_all_priorities(db, config),
demand_priorities = get_all_demand_priorities(db, config),
mean_input_flows,
mean_realized_flows,
)
Expand Down
12 changes: 6 additions & 6 deletions core/src/schema.jl
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ end
demand::Union{Missing, Float64}
return_factor::Float64
min_level::Float64
priority::Union{Missing, Int32}
demand_priority::Union{Missing, Int32}
end

@version UserDemandTimeV1 begin
Expand All @@ -308,7 +308,7 @@ end
demand::Float64
return_factor::Float64
min_level::Float64
priority::Union{Missing, Int32}
demand_priority::Union{Missing, Int32}
end

@version UserDemandConcentrationV1 begin
Expand All @@ -322,26 +322,26 @@ end
node_id::Int32
min_level::Union{Missing, Float64}
max_level::Union{Missing, Float64}
priority::Union{Missing, Int32}
demand_priority::Union{Missing, Int32}
end

@version LevelDemandTimeV1 begin
node_id::Int32
time::DateTime
min_level::Union{Missing, Float64}
max_level::Union{Missing, Float64}
priority::Union{Missing, Int32}
demand_priority::Union{Missing, Int32}
end

@version FlowDemandStaticV1 begin
node_id::Int
demand::Float64
priority::Union{Missing, Int32}
demand_priority::Union{Missing, Int32}
end

@version FlowDemandTimeV1 begin
node_id::Int
time::DateTime
demand::Float64
priority::Union{Missing, Int32}
demand_priority::Union{Missing, Int32}
end
6 changes: 3 additions & 3 deletions core/src/solve.jl
Original file line number Diff line number Diff line change
Expand Up @@ -350,9 +350,9 @@ function formulate_flow!(
# and the current demand.
# If allocation is not optimized then allocated = Inf, so the result is always
# effectively allocated = demand.
for priority_idx in eachindex(allocation.priorities)
alloc_prio = allocated[priority_idx]
demand_prio = get_demand(user_demand, id, priority_idx, t)
for demand_priority_idx in eachindex(allocation.demand_priorities)
alloc_prio = allocated[demand_priority_idx]
demand_prio = get_demand(user_demand, id, demand_priority_idx, t)
alloc = min(alloc_prio, demand_prio)
q += alloc
end
Expand Down
Loading
Loading