Skip to content

Commit

Permalink
Fix constraint indexing
Browse files Browse the repository at this point in the history
Fixes #763, #715
  • Loading branch information
manuelma committed Nov 6, 2023
1 parent 59f91fe commit 3c6cade
Show file tree
Hide file tree
Showing 11 changed files with 86 additions and 25 deletions.
2 changes: 1 addition & 1 deletion src/constraints/constraint_candidate_connection_flow_lb.jl
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ function constraint_candidate_connection_flow_lb_indices(m::Model)
m,
vcat(
connection_flow_indices(m; connection=conn, node=n, direction=d),
connections_invested_available_indices(m; connection=conn)
connections_invested_available_indices(m; connection=conn),
)
)
)
Expand Down
29 changes: 23 additions & 6 deletions src/constraints/constraint_common.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,36 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#############################################################################

function t_lowest_resolution_path(m, indices)
"""
t_lowest_resolution_path(m, indices...)
An iterator of tuples `(t, path)` where `t` is a `TimeSlice` and `path` is a `Vector` of stochastic scenario `Object`s
corresponding to the active stochastic paths for that `t`.
The `t`s in the result are the lowest resolution `TimeSlice`s in `indices`.
For each of these `t`s, the `path` also includes scenarios in `more_indices` where the `TimeSlice` contains the `t`.
"""
function t_lowest_resolution_path(m, indices, more_indices...)
isempty(indices) && return ()
scens_by_t = t_lowest_resolution_sets!(_scens_by_t(indices))
for (other_t, other_scens) in _scens_by_t(Iterators.flatten(more_indices))
for (t, scens) in scens_by_t
if iscontained(t, other_t)
union!(scens, other_scens)
end
end
end
((t, path) for (t, scens) in scens_by_t for path in active_stochastic_paths(m, scens))
end

function _scens_by_t(indices)
scens_by_t = Dict()
for x in indices
scens = get!(scens_by_t, x.t) do
Set{Object}()
end
push!(scens, x.stochastic_scenario)
end
(
(t, path)
for (t, scens) in t_lowest_resolution_sets!(scens_by_t)
for path in active_stochastic_paths(m, scens)
)
scens_by_t
end

function past_units_on_indices(m, u, s, t, min_time)
Expand Down
12 changes: 5 additions & 7 deletions src/constraints/constraint_connection_flow_capacity.jl
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,12 @@ end

function constraint_connection_flow_capacity_indices(m::Model)
(
(connection=c, node=ng, direction=d, stochastic_path=path, t=t)
for (c, ng, d) in indices(connection_capacity)
(connection=conn, node=ng, direction=d, stochastic_path=path, t=t)
for (conn, ng, d) in indices(connection_capacity)
for (t, path) in t_lowest_resolution_path(
m,
vcat(
connection_flow_indices(m; connection=c, node=ng, direction=d),
connections_invested_available_indices(m; connection=c)
)
m,
connection_flow_indices(m; connection=conn, node=ng, direction=d),
connections_invested_available_indices(m; connection=conn),
)
)
end
Expand Down
2 changes: 1 addition & 1 deletion src/constraints/constraint_minimum_operating_point.jl
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ function constraint_minimum_operating_point_indices(m::Model)
(unit=u, node=ng, direction=d, stochastic_path=path, t=t)
for (u, ng, d) in indices(minimum_operating_point)
for (t, path) in t_lowest_resolution_path(
m, vcat(unit_flow_indices(m; unit=u, node=ng, direction=d), units_on_indices(m; unit=u))
m, unit_flow_indices(m; unit=u, node=ng, direction=d), units_on_indices(m; unit=u)
)
)
end
Expand Down
2 changes: 1 addition & 1 deletion src/constraints/constraint_operating_point_bounds.jl
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function constraint_operating_point_bounds_indices(m::Model)
for (u, ng, d, i, _s, _t) in unit_flow_op_indices(m)
if ordered_unit_flow_op(unit=u, node=ng, direction=d, _default=false)
for (t, path) in t_lowest_resolution_path(
m, vcat(unit_flow_op_indices(m; unit=u, node=ng, direction=d, i=i), units_on_indices(m; unit=u))
m, unit_flow_op_indices(m; unit=u, node=ng, direction=d, i=i), units_on_indices(m; unit=u)
)
)
end
3 changes: 2 additions & 1 deletion src/constraints/constraint_ratio_out_in_connection_flow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ function constraint_ratio_out_in_connection_flow_indices(m::Model, ratio_out_in)
unique(
(connection=conn, node1=ng_out, node2=ng_in, stochastic_path=path, t=t)
for (conn, ng_out, ng_in) in indices(ratio_out_in)
for (t, path_out) in t_lowest_resolution_path(m,
for (t, path_out) in t_lowest_resolution_path(
m,
connection_flow_indices(m; connection=conn, node=ng_out, direction=direction(:to_node))
)
for path in active_stochastic_paths(
Expand Down
5 changes: 3 additions & 2 deletions src/constraints/constraint_ratio_unit_flow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -197,12 +197,13 @@ function constraint_ratio_unit_flow_indices(m::Model, ratio, d1, d2)
(unit=u, node1=n1, node2=n2, stochastic_path=path, t=t)
for (u, n1, n2) in indices(ratio)
for (t, path) in t_lowest_resolution_path(
m,
m,
unit_flow_indices(m; unit=u, node=[n1; n2]),
vcat(
unit_flow_indices(m; unit=u, node=n1, direction=d1),
unit_flow_indices(m; unit=u, node=n2, direction=d2),
units_on_indices(m; unit=u)
)
),
)
)
end
Expand Down
3 changes: 1 addition & 2 deletions src/constraints/constraint_storage_line_pack.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ function constraint_storage_line_pack_indices(m::Model)
(connection=conn, node1=n_stor, node2=ng, stochastic_path=path, t=t)
for (conn, n_stor, ng) in indices(connection_linepack_constant)
for (t, path) in t_lowest_resolution_path(
m,
vcat(node_state_indices(m; node=n_stor), node_pressure_indices(m; node=ng))
m, vcat(node_state_indices(m; node=n_stor), node_pressure_indices(m; node=ng))
)
)
end
Expand Down
2 changes: 1 addition & 1 deletion src/constraints/constraint_unit_flow_capacity.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ function constraint_unit_flow_capacity_indices(m::Model)
(unit=u, node=ng, direction=d, stochastic_path=path, t=t)
for (u, ng, d) in indices(unit_capacity)
for (t, path) in t_lowest_resolution_path(
m, vcat(unit_flow_indices(m; unit=u, node=ng, direction=d), units_on_indices(m; unit=u))
m, unit_flow_indices(m; unit=u, node=ng, direction=d), units_on_indices(m; unit=u)
)
)
end
Expand Down
1 change: 1 addition & 0 deletions src/constraints/constraint_unit_pw_heat_rate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ function constraint_unit_pw_heat_rate_indices(m::Model)
for (u, n_from, n_to) in indices(unit_incremental_heat_rate)
for (t, path) in t_lowest_resolution_path(
m,
unit_flow_indices(m; unit=u, node=[n_from; n_to]),
vcat(
unit_flow_indices(m; unit=u, node=n_from, direction=direction(:from_node)),
unit_flow_indices(m; unit=u, node=n_to, direction=direction(:to_node)),
Expand Down
50 changes: 47 additions & 3 deletions test/constraints/constraint_connection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,12 @@ function _test_constraint_connection_setup()
end

function test_constraint_connection_flow_capacity()
@testset "constraint_connection_flow_capacity" begin
@testset "constraint_connection_flow_capacity_basic" begin
url_in = _test_constraint_connection_setup()
connection_capacity = 200
relationship_parameter_values =
[["connection__from_node", ["connection_ab", "node_a"], "connection_capacity", connection_capacity]]
relationship_parameter_values = [
["connection__from_node", ["connection_ab", "node_a"], "connection_capacity", connection_capacity]
]
SpineInterface.import_data(url_in; relationship_parameter_values=relationship_parameter_values)
m = run_spineopt(url_in; log_level=0, optimize=false)
var_connection_flow = m.ext[:spineopt].variables[:connection_flow]
Expand All @@ -109,6 +110,49 @@ function test_constraint_connection_flow_capacity()
@test _is_constraint_equal(observed_con, expected_con)
end
end
@testset "constraint_connection_flow_capacity_with_investments" begin
url_in = _test_constraint_connection_setup()
connection_capacity = 200
objects = [["temporal_block", "investments_daily"]]
relationships = [
["model__temporal_block", ["instance", "investments_daily"]],
["connection__investment_temporal_block", ["connection_ab", "investments_daily"]],
["connection__investment_stochastic_structure", ["connection_ab", "deterministic"]],
]
object_parameter_values = [
["temporal_block", "investments_daily", "resolution", Dict("type" => "duration", "data" => "1D")],
["connection", "connection_ab", "candidate_connections", 1],
]
relationship_parameter_values = [
["connection__from_node", ["connection_ab", "node_a"], "connection_capacity", connection_capacity]
]
SpineInterface.import_data(
url_in;
objects=objects,
relationships=relationships,
object_parameter_values=object_parameter_values,
relationship_parameter_values=relationship_parameter_values,
)
m = run_spineopt(url_in; log_level=0, optimize=false)
var_connection_flow = m.ext[:spineopt].variables[:connection_flow]
var_connections_invested_available = m.ext[:spineopt].variables[:connections_invested_available]
constraint = m.ext[:spineopt].constraints[:connection_flow_capacity]
@test length(constraint) == 2
scenarios = [stochastic_scenario(:parent), stochastic_scenario(:child)]
time_slices = time_slice(m; temporal_block=temporal_block(:hourly))
daily_t = first(time_slice(m; temporal_block=temporal_block(:investments_daily)))
@testset for (k, t) in enumerate(time_slices)
s = scenarios[k]
key = (connection(:connection_ab), node(:node_a), direction(:from_node), s, t)
invest_key = (connection(:connection_ab), stochastic_scenario(:parent), daily_t)
var_conn_flow = var_connection_flow[key...]
var_conn_invest_avail = var_connections_invested_available[invest_key...]
expected_con = @build_constraint(var_conn_flow <= connection_capacity * var_conn_invest_avail)
con_key = (connection(:connection_ab), node(:node_a), direction(:from_node), scenarios[1:k], t)
observed_con = constraint_object(constraint[con_key...])
@test _is_constraint_equal(observed_con, expected_con)
end
end
end

function test_constraint_connection_flow_gas_capacity()
Expand Down

0 comments on commit 3c6cade

Please sign in to comment.