Skip to content
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
3a173f9
Toward implementing secondary network demand analogously to UserDemand
SouthEndMusic Sep 2, 2025
64f85a8
Toward adding `has_demand_priority` to `AllocationModel`
SouthEndMusic Sep 2, 2025
902d6ec
Finalize getting demand_priority for secondary networks
SouthEndMusic Sep 2, 2025
6ef764f
Ground work
SouthEndMusic Sep 3, 2025
80a6d9b
Merge branch 'main' into primary_to_secondary_communication
SouthEndMusic Sep 3, 2025
aeccef8
Merge branch 'main' into primary_to_secondary_communication
SouthEndMusic Sep 8, 2025
d7db802
replace native multi objective with manual multi objective optimization
simulutions Sep 18, 2025
93fb449
fix mistake
simulutions Sep 18, 2025
b4d1614
setup demand collection and test driven case
simulutions Sep 19, 2025
6b9d665
intermediate commit
simulutions Sep 26, 2025
0162194
set demands from subnetwork in primary network
simulutions Sep 29, 2025
ce770fe
subnet communication works 2 timesteps
simulutions Sep 30, 2025
0abf7aa
fixed missing set_simulation_data! for primary network
simulutions Sep 30, 2025
444ee01
succesfull test
simulutions Oct 1, 2025
a9bcc94
Add subnetwork demand fairness constraints only for primary network
simulutions Oct 2, 2025
76f7140
it works!!
simulutions Oct 3, 2025
251a3a8
save flows only once
simulutions Oct 3, 2025
45928ba
small fixes
simulutions Oct 6, 2025
e43974c
this seems to work. Added unit test
simulutions Oct 6, 2025
5ba9a9e
Merge branch 'main' into primary_to_secondary_communication
simulutions Oct 6, 2025
0730a01
better test
simulutions Oct 6, 2025
07e044f
Merge branch 'primary_to_secondary_communication' of https://github.c…
simulutions Oct 6, 2025
a63cafe
fix naming conflict
simulutions Oct 6, 2025
a73e9b3
change order
simulutions Oct 6, 2025
5614722
update allocation problems
simulutions Oct 6, 2025
e9739a0
reduce code duplication in optimize_multi_objective!
simulutions Oct 6, 2025
315b2ea
remove MOA
simulutions Oct 6, 2025
81cc022
remove MOA
simulutions Oct 7, 2025
fb7bd66
rename
simulutions Oct 7, 2025
83ba2ca
rename errors
simulutions Oct 7, 2025
884a912
remove pump/outlet
simulutions Oct 7, 2025
74bfade
continue after heisenbug infeasibility
simulutions Oct 7, 2025
44a5ab1
allocate owned links in init
simulutions Oct 7, 2025
c801121
update allocation problems
simulutions Oct 7, 2025
1bc5586
fix bug in owned links
simulutions Oct 7, 2025
eccfdd2
Revert "fix bug in owned links"
simulutions Oct 7, 2025
ae3cf87
Revert "allocate owned links in init"
simulutions Oct 7, 2025
3e7f904
update allocation problems
simulutions Oct 7, 2025
76824a0
update project files
simulutions Oct 8, 2025
00868d5
Revert "update project files"
simulutions Oct 8, 2025
ff17dc2
medium sized test model runs without errors
simulutions Oct 9, 2025
97a176c
add proper test for medium sized multi subnet allocation model
simulutions Oct 9, 2025
1a8f93d
improve model structure
simulutions Oct 9, 2025
1135fb4
temp plot
simulutions Oct 9, 2025
4ecb0c5
Merge branch 'main' into primary_to_secondary_communication
simulutions Oct 9, 2025
edc7aaa
update version
simulutions Oct 9, 2025
a72e6b0
update alloc probs
simulutions Oct 9, 2025
5c56926
delete plots
simulutions Oct 10, 2025
dba20fa
attempt fix for infeasibility test
simulutions Oct 10, 2025
18a3718
only run tests
simulutions Oct 10, 2025
e841fbd
revert docs
simulutions Oct 10, 2025
3a7ddf7
mark broken
simulutions Oct 10, 2025
ad96a38
better brokenm
simulutions Oct 10, 2025
5a658cc
Merge branch 'main' into primary_to_secondary_communication
simulutions Oct 10, 2025
a2185d3
fix typo
simulutions Oct 10, 2025
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
182 changes: 135 additions & 47 deletions core/src/allocation_init.jl
Original file line number Diff line number Diff line change
Expand Up @@ -131,17 +131,13 @@ function add_conservation!(
flow = problem[:flow]
inflow_sum = Dict(
basin_id => sum(
flow[(other_id, basin_id)] for
other_id in basin.inflow_ids[basin_id.idx] if
graph[other_id].subnetwork_id == subnetwork_id;
flow[(other_id, basin_id)] for other_id in basin.inflow_ids[basin_id.idx];
init = 0,
) for basin_id in basin_ids_subnetwork
)
outflow_sum = Dict(
basin_id => sum(
flow[(basin_id, other_id)] for
other_id in basin.outflow_ids[basin_id.idx] if
graph[other_id].subnetwork_id == subnetwork_id;
flow[(basin_id, other_id)] for other_id in basin.outflow_ids[basin_id.idx];
init = 0,
) for basin_id in basin_ids_subnetwork
)
Expand Down Expand Up @@ -197,7 +193,8 @@ function add_user_demand!(
flow[inflow_link[node_id.idx].link] == sum(
user_demand_allocated[node_id, demand_priority] for
demand_priority in DemandPriorityIterator(node_id, p_independent)
)
);
base_name = "user_demand_allocated_sum_constraint"
)

# Define decision variables: Per UserDemand node the allocation error per priority for
Expand Down Expand Up @@ -556,7 +553,7 @@ function add_outlet!(
return nothing
end

function add_subnetwork_demand!(
function add_secondary_network_demand!(
allocation_model::AllocationModel,
p_independent::ParametersIndependent,
)::Nothing
Expand All @@ -568,34 +565,55 @@ function add_subnetwork_demand!(
connecting_links =
vcat(sort!(collect(values(allocation.primary_network_connections)))...)

# Define parameters: flow allocated to user subnetworks (scaling.flow * m^3/s, values to be filled in before optimizing)
subnetwork_allocated =
problem[:subnetwork_allocated] =
JuMP.@variable(problem, subnetwork[connecting_links] == 0)

# Define decision variables: lower and upper user demand error (unitless)
relative_subnetwork_error_lower =
problem[:relative_subnetwork_error_lower] =
JuMP.@variable(problem, relative_subnetwork_error_lower[connecting_links] ≥ 0)
relative_subnetwork_error_upper =
problem[:relative_subnetwork_error_upper] =
JuMP.@variable(problem, relative_subnetwork_error_upper[connecting_links] ≥ 0)

# Define constraints: error terms
# Define decision variables: flow allocated to secondary networks
# (scaling.flow * m^3/s, values to be filled in before optimizing)
d = 2.0 # Example demand (scaling.flow * m^3/s, values to be filled in before optimizing)
problem[:subnetwork_constraint_lower] = JuMP.@constraint(
secondary_network_allocated =
problem[:secondary_network_allocated] = JuMP.@variable(
problem,
0 ≤
secondary_network_allocated[
link = connecting_links,
DemandPriorityIterator(link, p_independent),
] ≤
d
)

# Define constraints: The sum of the flows allocated to the secondary network is equal to the total flow into the secondary network
problem[:secondary_network_allocated_sum_constraint] = JuMP.@constraint(
problem,
[link = connecting_links],
d * (relative_subnetwork_error_lower[link]) ≥
-(flow[link] - subnetwork_allocated[link]),
base_name = "subnetwork_constraint_lower"
flow[link] == sum(
secondary_network_allocated[link, demand_priority] for
demand_priority in DemandPriorityIterator(link, p_independent)
);
base_name = "secondary_network_allocated_sum_constraint",
)
problem[:subnetwork_constraint_upper] = JuMP.@constraint(

# Define decision variables: Per secondary network connection, the allocation error per priority for
# which the secondary network has a demand, for both the first and second objective (unitless)
secondary_network_error =
problem[:secondary_network_error] = JuMP.@variable(
problem,
0 ≤
secondary_network_error[
link = connecting_links,
DemandPriorityIterator(link, p_independent),
[:first, :second],
] ≤
1
)

# Define constraints: error terms
problem[:secondary_network_relative_error_constraint] = JuMP.@constraint(
problem,
[link = connecting_links],
d * (relative_subnetwork_error_upper[link]) ≥
flow[link] - subnetwork_allocated[link],
base_name = "subnetwork_constraint_upper"
[
link = connecting_links,
demand_priority = DemandPriorityIterator(link, p_independent),
],
d * secondary_network_error[link, demand_priority, :first] ≥
d - secondary_network_allocated[link, demand_priority];
base_name = "secondary_network_relative_error_constraint"
)

return nothing
Expand All @@ -609,7 +627,7 @@ function add_demand_objectives!(
allocation_model::AllocationModel,
p_independent::ParametersIndependent,
)::Nothing
(; objectives, problem, node_ids_in_subnetwork) = allocation_model
(; objectives, problem, node_ids_in_subnetwork, subnetwork_id) = allocation_model
(;
user_demand_ids_subnetwork,
node_ids_subnetwork_with_flow_demand,
Expand Down Expand Up @@ -640,19 +658,27 @@ function add_demand_objectives!(
# Objective for a fair distribution of what was allocated with the previous objective
second_objective_expression = JuMP.AffExpr()

for error_collection in [user_demand_error, flow_demand_error]
for (node_id, demand_priority_, objective_ord) in keys(error_collection.data)
# Add UserDemand, FlowDemand and secondary network errors
error_collections = [user_demand_error, flow_demand_error]
if is_primary_network(subnetwork_id)
subnetwork_demand_error = problem[:secondary_network_error]
push!(error_collections, subnetwork_demand_error)
end

for error_collection in error_collections
for (identifier, demand_priority_, objective_ord) in keys(error_collection.data)
if demand_priority == demand_priority_
JuMP.add_to_expression!(
(objective_ord == :first) ? first_objective_expression :
second_objective_expression,
error_collection[node_id, demand_priority, objective_ord],
error_collection[identifier, demand_priority, objective_ord],
)
has_flow_unit_demands = true
end
end
end

# Add LevelDemand errors
for (node_id, demand_priority_, side, objective_ord) in
keys(level_demand_error.data)
if demand_priority == demand_priority_
Expand Down Expand Up @@ -738,9 +764,30 @@ function add_demand_objectives!(
],
flow_demand_error[node_id, demand_priority, :second] ≥
flow_demand_error[node_id, demand_priority, :first] -
average_flow_unit_error[demand_priority]
average_flow_unit_error[demand_priority],
base_name = "flow_demand_fairness_error_constraint"
)

# If this is the primary network, also add fairness constraints for secondary network demands
if is_primary_network(subnetwork_id)
subnetwork_demand_error = problem[:secondary_network_error]
# Sort connections for deterministic problem generation
connecting_links =
vcat(sort!(collect(values(allocation.primary_network_connections)))...)

problem[:subnetwork_demand_fairness_error_constraint] = JuMP.@constraint(
problem,
[
link = connecting_links,
demand_priority = DemandPriorityIterator(link, p_independent),
],
subnetwork_demand_error[link, demand_priority, :second] ≥
subnetwork_demand_error[link, demand_priority, :first] -
average_flow_unit_error[demand_priority],
base_name = "subnetwork_demand_fairness_error_constraint"
)
end

# Define variables: average level error for storage unit demands (LevelDemand) per demand priority (m)
average_storage_unit_error =
problem[:average_storage_unit_error] = JuMP.@variable(
Expand Down Expand Up @@ -894,22 +941,65 @@ function NodeIDsInSubnetwork(
return node_ids_in_subnetwork
end

function has_demand_priority_subnetwork(
p_independent::ParametersIndependent,
node_ids_in_subnetwork::NodeIDsInSubnetwork,
)::Vector{Bool}
(; allocation, graph, user_demand, flow_demand, level_demand) = p_independent
(; demand_priorities_all) = allocation
(;
user_demand_ids_subnetwork,
node_ids_subnetwork_with_flow_demand,
level_demand_ids_subnetwork,
) = node_ids_in_subnetwork

has_demand_priority = zeros(Bool, length(demand_priorities_all))

for node_id in user_demand_ids_subnetwork
has_demand_priority .|= view(user_demand.has_demand_priority, node_id.idx, :)
end

for node_id in node_ids_subnetwork_with_flow_demand
has_demand_priority .|= view(flow_demand.has_demand_priority, node_id.idx, :)
end

for node_id in level_demand_ids_subnetwork
has_demand_priority .|= view(level_demand.has_demand_priority, node_id.idx, :)
end

return has_demand_priority
end

function AllocationModel(
subnetwork_id::Int32,
p_independent::ParametersIndependent,
allocation_config::config.Allocation,
)
Δt_allocation = allocation_config.timestep
problem = JuMP.Model(() -> MOA.Optimizer(get_optimizer()))
set_multi_objective_attributes!(problem)
problem = JuMP.Model()
JuMP.set_optimizer(problem, get_optimizer())
node_ids_in_subnetwork = NodeIDsInSubnetwork(p_independent, subnetwork_id)
scaling = ScalingFactors(p_independent, subnetwork_id, Δt_allocation)
has_demand_priority =
has_demand_priority_subnetwork(p_independent, node_ids_in_subnetwork)

# Initialize secondary_network_demand before constructing AllocationModel
secondary_network_demand = Dict{Tuple{NodeID, NodeID}, Vector{Float64}}()
if !is_primary_network(subnetwork_id)
n_priorities = length(p_independent.allocation.demand_priorities_all)
for link in p_independent.allocation.primary_network_connections[subnetwork_id]
secondary_network_demand[link] = zeros(n_priorities)
end
end

allocation_model = AllocationModel(;
subnetwork_id,
node_ids_in_subnetwork,
problem,
Δt_allocation,
scaling,
has_demand_priority,
secondary_network_demand,
)

# Volume and flow
Expand All @@ -928,21 +1018,19 @@ function AllocationModel(
add_pump!(allocation_model, p_independent)
add_outlet!(allocation_model, p_independent)

# Demand nodes and subnetworks as demand nodes
# Demand nodes
add_user_demand!(allocation_model, p_independent)
add_flow_demand!(allocation_model, p_independent)
add_level_demand!(allocation_model, p_independent)

# Primary to secondary subnetwork connections
if is_primary_network(subnetwork_id)
add_subnetwork_demand!(allocation_model, p_independent)
else
# Initialize subnetwork demands
n_demands = length(p_independent.allocation.demand_priorities_all)
if !is_primary_network(subnetwork_id)
for link in p_independent.allocation.primary_network_connections[subnetwork_id]
allocation_model.subnetwork_demand[link] = zeros(n_demands)
end
add_secondary_network_demand!(allocation_model, p_independent)
else # Initialize subnetwork demands
n_priorities = length(p_independent.allocation.demand_priorities_all)

for link in p_independent.allocation.primary_network_connections[subnetwork_id]
allocation_model.secondary_network_demand[link] = zeros(n_priorities)
end
end

Expand Down
Loading
Loading