Skip to content

Commit

Permalink
Use the same network for allocation as for the physical layer (#1399)
Browse files Browse the repository at this point in the history
Fixes #1388.

This also fixes a bug I found: In allocation the `max_flow_rate` of a
node connecting the main network to a subnetwork was not taken into
account
  • Loading branch information
SouthEndMusic authored Apr 22, 2024
1 parent 46937dd commit e3bf329
Show file tree
Hide file tree
Showing 14 changed files with 442 additions and 724 deletions.
697 changes: 212 additions & 485 deletions core/src/allocation_init.jl

Large diffs are not rendered by default.

206 changes: 104 additions & 102 deletions core/src/allocation_optim.jl

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions core/src/callback.jl
Original file line number Diff line number Diff line change
Expand Up @@ -296,26 +296,26 @@ function discrete_control_affect!(integrator, compound_variable_idx)
return nothing
end

function get_allocation_model(p::Parameters, allocation_network_id::Int32)::AllocationModel
function get_allocation_model(p::Parameters, subnetwork_id::Int32)::AllocationModel
(; allocation) = p
(; allocation_network_ids, allocation_models) = allocation
idx = findsorted(allocation_network_ids, allocation_network_id)
(; subnetwork_ids, allocation_models) = allocation
idx = findsorted(subnetwork_ids, subnetwork_id)
if isnothing(idx)
error("Invalid allocation network ID $allocation_network_id.")
error("Invalid allocation network ID $subnetwork_id.")
else
return allocation_models[idx]
end
end

function get_main_network_connections(
p::Parameters,
allocation_network_id::Int32,
subnetwork_id::Int32,
)::Vector{Tuple{NodeID, NodeID}}
(; allocation) = p
(; allocation_network_ids, main_network_connections) = allocation
idx = findsorted(allocation_network_ids, allocation_network_id)
(; subnetwork_ids, main_network_connections) = allocation
idx = findsorted(subnetwork_ids, subnetwork_id)
if isnothing(idx)
error("Invalid allocation network ID $allocation_network_id.")
error("Invalid allocation network ID $subnetwork_id.")
else
return main_network_connections[idx]
end
Expand All @@ -332,9 +332,9 @@ function set_fractional_flow_in_allocation!(
)::Nothing
(; graph) = p

allocation_network_id = graph[node_id].allocation_network_id
subnetwork_id = graph[node_id].subnetwork_id
# Get the allocation model this fractional flow node is in
allocation_model = get_allocation_model(p, allocation_network_id)
allocation_model = get_allocation_model(p, subnetwork_id)
if !isnothing(allocation_model)
problem = allocation_model.problem
# The allocation edge which jumps over the fractional flow node
Expand Down
6 changes: 1 addition & 5 deletions core/src/graph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ function create_graph(db::DB, config::Config, chunk_sizes::Vector{Int})::MetaGra
)
# Node IDs per subnetwork
node_ids = Dict{Int32, Set{NodeID}}()
# Allocation edges per subnetwork
edge_ids = Dict{Int32, Set{Tuple{NodeID, NodeID}}}()
# Source edges per subnetwork
edges_source = Dict{Int32, Set{EdgeMetadata}}()
# The number of flow edges
Expand Down Expand Up @@ -68,8 +66,7 @@ function create_graph(db::DB, config::Config, chunk_sizes::Vector{Int})::MetaGra
if ismissing(subnetwork_id)
subnetwork_id = 0
end
edge_metadata =
EdgeMetadata(fid, edge_type, subnetwork_id, id_src, id_dst, false, NodeID[])
edge_metadata = EdgeMetadata(fid, edge_type, subnetwork_id, (id_src, id_dst))
if haskey(graph, id_src, id_dst)
errors = true
@error "Duplicate edge" id_src id_dst
Expand Down Expand Up @@ -102,7 +99,6 @@ function create_graph(db::DB, config::Config, chunk_sizes::Vector{Int})::MetaGra
end
graph_data = (;
node_ids,
edge_ids,
edges_source,
flow_dict,
flow,
Expand Down
22 changes: 7 additions & 15 deletions core/src/parameter.jl
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ problem: The JuMP.jl model for solving the allocation problem
Δt_allocation: The time interval between consecutive allocation solves
"""
struct AllocationModel
allocation_network_id::Int32
subnetwork_id::Int32
capacity::JuMP.Containers.SparseAxisArray{Float64, 2, Tuple{NodeID, NodeID}}
problem::JuMP.Model
Δt_allocation::Float64
Expand All @@ -68,7 +68,7 @@ record_flow: A record of all flows computed by allocation optimization, eventual
output file
"""
struct Allocation
allocation_network_ids::Vector{Int32}
subnetwork_ids::Vector{Int32}
allocation_models::Vector{AllocationModel}
main_network_connections::Vector{Vector{Tuple{NodeID, NodeID}}}
priorities::Vector{Int32}
Expand Down Expand Up @@ -107,29 +107,22 @@ allocation_network_id: Allocation network ID (0 if not in subnetwork)
"""
struct NodeMetadata
type::Symbol
allocation_network_id::Int32
subnetwork_id::Int32
end

"""
Type for storing metadata of edges in the graph:
id: ID of the edge (only used for labeling flow output)
type: type of the edge
allocation_network_id_source: ID of allocation network where this edge is a source
subnetwork_id_source: ID of subnetwork where this edge is a source
(0 if not a source)
from_id: the node ID of the source node
to_id: the node ID of the destination node
allocation_flow: whether this edge has a flow in an allocation network
node_ids: if this edge has allocation flow, these are all the
nodes from the physical layer this edge consists of
edge: (from node ID, to node ID)
"""
struct EdgeMetadata
id::Int32
type::EdgeType.T
allocation_network_id_source::Int32
from_id::NodeID
to_id::NodeID
allocation_flow::Bool
node_ids::Vector{NodeID}
subnetwork_id_source::Int32
edge::Tuple{NodeID, NodeID}
end

abstract type AbstractParameterNode end
Expand Down Expand Up @@ -592,7 +585,6 @@ struct Parameters{T, C1, C2, V1, V2, V3}
EdgeMetadata,
@NamedTuple{
node_ids::Dict{Int32, Set{NodeID}},
edge_ids::Dict{Int32, Set{Tuple{NodeID, NodeID}}},
edges_source::Dict{Int32, Set{EdgeMetadata}},
flow_dict::Dict{Tuple{NodeID, NodeID}, Int},
flow::T,
Expand Down
18 changes: 9 additions & 9 deletions core/src/read.jl
Original file line number Diff line number Diff line change
Expand Up @@ -200,31 +200,31 @@ const nonconservative_nodetypes =

function initialize_allocation!(p::Parameters, config::Config)::Nothing
(; graph, allocation) = p
(; allocation_network_ids, allocation_models, main_network_connections) = allocation
allocation_network_ids_ = sort(collect(keys(graph[].node_ids)))
(; subnetwork_ids, allocation_models, main_network_connections) = allocation
subnetwork_ids_ = sort(collect(keys(graph[].node_ids)))

if isempty(allocation_network_ids_)
if isempty(subnetwork_ids_)
return nothing
end

errors = non_positive_allocation_network_id(graph)
errors = non_positive_subnetwork_id(graph)
if errors
error("Allocation network initialization failed.")
end

for allocation_network_id in allocation_network_ids_
push!(allocation_network_ids, allocation_network_id)
for subnetwork_id in subnetwork_ids_
push!(subnetwork_ids, subnetwork_id)
push!(main_network_connections, Tuple{NodeID, NodeID}[])
end

if first(allocation_network_ids_) == 1
if first(subnetwork_ids_) == 1
find_subnetwork_connections!(p)
end

for allocation_network_id in allocation_network_ids_
for subnetwork_id in subnetwork_ids_
push!(
allocation_models,
AllocationModel(allocation_network_id, p, config.allocation.timestep),
AllocationModel(subnetwork_id, p, config.allocation.timestep),
)
end
return nothing
Expand Down
12 changes: 7 additions & 5 deletions core/src/util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -571,19 +571,21 @@ end
is_flow_constraining(node::AbstractParameterNode) = hasfield(typeof(node), :max_flow_rate)

"""Whether the given node is flow direction constraining (only in direction of edges)."""
is_flow_direction_constraining(node::AbstractParameterNode) =
(nameof(typeof(node)) [:Pump, :Outlet, :TabulatedRatingCurve, :FractionalFlow])
is_flow_direction_constraining(node::AbstractParameterNode) = (
node isa
Union{Pump, Outlet, TabulatedRatingCurve, FractionalFlow, Terminal, UserDemand}
)

function has_main_network(allocation::Allocation)::Bool
if !is_active(allocation)
false
else
first(allocation.allocation_network_ids) == 1
first(allocation.subnetwork_ids) == 1
end
end

function is_main_network(allocation_network_id::Int32)::Bool
return allocation_network_id == 1
function is_main_network(subnetwork_id::Int32)::Bool
return subnetwork_id == 1
end

function get_all_priorities(db::DB, config::Config)::Vector{Int32}
Expand Down
34 changes: 20 additions & 14 deletions core/src/validation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -406,21 +406,21 @@ end

function incomplete_subnetwork(graph::MetaGraph, node_ids::Dict{Int32, Set{NodeID}})::Bool
errors = false
for (allocation_network_id, subnetwork_node_ids) in node_ids
for (subnetwork_id, subnetwork_node_ids) in node_ids
subnetwork, _ = induced_subgraph(graph, code_for.(Ref(graph), subnetwork_node_ids))
if !is_connected(subnetwork)
@error "All nodes in subnetwork $allocation_network_id should be connected"
@error "All nodes in subnetwork $subnetwork_id should be connected"
errors = true
end
end
return errors
end

function non_positive_allocation_network_id(graph::MetaGraph)::Bool
function non_positive_subnetwork_id(graph::MetaGraph)::Bool
errors = false
for allocation_network_id in keys(graph[].node_ids)
if (allocation_network_id <= 0)
@error "Allocation network id $allocation_network_id needs to be a positive integer."
for subnetwork_id in keys(graph[].node_ids)
if (subnetwork_id <= 0)
@error "Allocation network id $subnetwork_id needs to be a positive integer."
errors = true
end
end
Expand Down Expand Up @@ -603,29 +603,35 @@ end
"""
The source nodes must only have one allocation outneighbor and no allocation inneighbors.
"""
function valid_sources(p::Parameters, allocation_network_id::Int32)::Bool
function valid_sources(
p::Parameters,
capacity::JuMP.Containers.SparseAxisArray{Float64, 2, Tuple{NodeID, NodeID}},
subnetwork_id::Int32,
)::Bool
(; graph) = p

edge_ids = graph[].edge_ids[allocation_network_id]

errors = false

for edge in edge_ids
for edge in keys(capacity.data)
if !haskey(graph, edge...)
edge = reverse(edge)
end

(id_source, id_dst) = edge
if graph[id_source, id_dst].allocation_network_id_source == allocation_network_id
if graph[edge...].subnetwork_id_source == subnetwork_id
from_source_node = id_source.type in allocation_source_nodetypes

if is_main_network(allocation_network_id)
if is_main_network(subnetwork_id)
if !from_source_node
errors = true
@error "The source node of source edge $edge in the main network must be one of $allocation_source_nodetypes."
end
else
from_main_network = is_main_network(graph[id_source].allocation_network_id)
from_main_network = is_main_network(graph[id_source].subnetwork_id)

if !from_source_node && !from_main_network
errors = true
@error "The source node of source edge $edge for subnetwork $allocation_network_id is neither a source node nor is it coming from the main network."
@error "The source node of source edge $edge for subnetwork $subnetwork_id is neither a source node nor is it coming from the main network."
end
end
end
Expand Down
Loading

0 comments on commit e3bf329

Please sign in to comment.