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

Fix temporal resolution connection_ratio_out_in #1147

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
Draft
17 changes: 17 additions & 0 deletions src/constraints/constraint_common.jl
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,23 @@ function t_lowest_resolution_path(m, indices, extra_indices...)
((t, path) for (t, scens) in scens_by_t for path in active_stochastic_paths(m, scens))
end

function t_highest_resolution_path(m, indices, extra_indices...)
isempty(indices) && return ()
if length(stochastic_scenario()) == 1
s = only(stochastic_scenario())
return ((t, [s]) for t in t_highest_resolution!(m, unique(x.t for x in indices)))
end
scens_by_t = t_highest_resolution_sets!(m, _scens_by_t(indices))
extra_scens_by_t = _scens_by_t(Iterators.flatten(extra_indices))
for (t, scens) in scens_by_t
for t_short in t_in_t(m; t_short=t)
union!(scens, get(extra_scens_by_t, t_short, ()))
end
end
((t, path) for (t, scens) in scens_by_t for path in active_stochastic_paths(m, scens))
end


function _popfirst!(arr, default)
try popfirst!(arr) catch default end
end
Expand Down
24 changes: 11 additions & 13 deletions src/constraints/constraint_ratio_out_in_connection_flow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -58,23 +58,22 @@ function _build_constraint_ratio_out_in_connection_flow(m::Model, conn, ng_out,
@fetch connection_flow = m.ext[:spineopt].variables
build_sense_constraint(
+ sum(
+ connection_flow[conn, n_out, d, s, t_short] * duration(t_short)
+ connection_flow[conn, n_out, d, s, t_short]
for (conn, n_out, d, s, t_short) in connection_flow_indices(
m;
connection=conn,
node=ng_out,
direction=direction(:to_node),
stochastic_scenario=s_path,
t=t_in_t(m; t_long=t),
t=t_in_t(m; t_short=t),
);
init=0,
),
sense,
+ sum(
+ connection_flow[conn, n_in, d, s_past, t_past]
* ratio_out_in(m; connection=conn, node1=ng_out, node2=ng_in, stochastic_scenario=s_past, t=t_past)
* weight
for (conn, n_in, d, s_past, t_past, weight) in _past_connection_input_flow_indices(
for (conn, n_in, d, s_past, t_past) in _past_connection_input_flow_indices(
m, conn, ng_out, ng_in, s_path, t
);
init=0,
Expand Down Expand Up @@ -111,17 +110,16 @@ end

function constraint_ratio_out_in_connection_flow_indices(m::Model, ratio_out_in)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Tasqu @DillonJ @manuelma

I want to get the highest resolution between t_out and t_in (with delays), with their associated paths.
But this code does not return the constraints I want, do you see something weird in this indices function?

(
(connection=conn, node1=ng_out, node2=ng_in, stochastic_path=path, t=t)
(connection=conn, node1=ng_out, node2=ng_in, stochastic_path=path, t=t)
for (conn, ng_out, ng_in) in indices(ratio_out_in)
if !_has_simple_fix_ratio_out_in_connection_flow(conn, ng_out, ng_in)
for (t, path_out) in t_lowest_resolution_path(
for (t_out, path_out) in t_highest_resolution_path(
m, connection_flow_indices(m; connection=conn, node=ng_out, direction=direction(:to_node))
)
for path in active_stochastic_paths(
m,
Iterators.flatten(
for (t, path) in t_highest_resolution_path(
m, Iterators.flatten(
(
((stochastic_scenario=s,) for s in path_out),
connection_flow_indices(m; connection=conn, node=ng_out, direction=direction(:to_node)),
(
ind
for s in path_out
Expand All @@ -130,20 +128,20 @@ function constraint_ratio_out_in_connection_flow_indices(m::Model, ratio_out_in)
connection=conn,
node=ng_in,
direction=direction(:from_node),
t=to_time_slice(m; t=_t_look_behind(conn, ng_out, ng_in, (s,), t)),
t=to_time_slice(m; t=_t_look_behind(conn, ng_out, ng_in, (s,), t_out)),
temporal_block=anything,
)
),
)
)
)
)
)
end

function _past_connection_input_flow_indices(m, conn, ng_out, ng_in, s_path, t)
t_look_behind = _t_look_behind(conn, ng_out, ng_in, s_path, t)
(
(; ind..., weight=overlap_duration(ind.t, t_look_behind))
(; ind...)
for ind in connection_flow_indices(
m;
connection=conn,
Expand Down
110 changes: 53 additions & 57 deletions test/constraints/constraint_connection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -862,70 +862,66 @@ function test_contraints_ptdf_lodf_duration()
end
end

function test_constraint_ratio_out_in_connection_flow()
@testset "constraint_ratio_out_in_connection_flow" begin
function test_constraint_ratio_out_in_connection_flow_highest()
@testset "constraint_ratio_out_in_connection_flow_highest" begin
flow_ratio = 0.8
model_end = Dict("type" => "date_time", "data" => "2000-01-01T04:00:00")
class = "connection__node__node"
relationship = ["connection_ab", "node_b", "node_a"]
object_parameter_values = [["model", "instance", "model_end", model_end]]
relationships = [[class, relationship]]
senses_by_prefix = Dict("min" => >=, "fix" => ==, "max" => <=)
@testset for conn_flow_minutes_delay in (150, 180, 225)
connection_flow_delay = Dict("type" => "duration", "data" => string(conn_flow_minutes_delay, "m"))
h_delay = div(conn_flow_minutes_delay, 60)
rem_minutes_delay = (conn_flow_minutes_delay % 60) / 60
@testset for p in ("min", "fix", "max")
url_in = _test_constraint_connection_setup()
sense = senses_by_prefix[p]
ratio = string(p, "_ratio_out_in_connection_flow")
relationship_parameter_values = [
[class, relationship, "connection_flow_delay", connection_flow_delay],
[class, relationship, ratio, flow_ratio],
]
SpineInterface.import_data(
url_in;
relationships=relationships,
object_parameter_values=object_parameter_values,
relationship_parameter_values=relationship_parameter_values,
h_delay = 2
connection_flow_delay = Dict("type" => "duration", "data" => string(h_delay, "h"))
@testset for p in ("min", "fix", "max")
p = "fix"
url_in = _test_constraint_connection_setup()
sense = senses_by_prefix[p]
ratio = string(p, "_ratio_out_in_connection_flow")
relationship_parameter_values = [
[class, relationship, "connection_flow_delay", connection_flow_delay],
[class, relationship, ratio, flow_ratio],
]
SpineInterface.import_data(
url_in;
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]
constraint = m.ext[:spineopt].constraints[Symbol(ratio)]

@test length(constraint) == 4
conn = connection(:connection_ab)
n_from = node(:node_a)
n_to = node(:node_b)
d_from = direction(:from_node)
d_to = direction(:to_node)
scenarios_from = [repeat([stochastic_scenario(:child)], 3); repeat([stochastic_scenario(:parent)], 3)]
time_slices_from = [
reverse(time_slice(m; temporal_block=temporal_block(:hourly)))
reverse(history_time_slice(m; temporal_block=temporal_block(:hourly)))
]
time_slices_to = reverse(time_slice(m; temporal_block=temporal_block(:two_hourly)))
s_to = stochastic_scenario(:parent)
@testset for (j, t_con) in enumerate(reverse(time_slice(m; temporal_block=temporal_block(:hourly))))
s_from = scenarios_from[h_delay+j] # get the scenario before the delay
t_from = time_slices_from[h_delay+j] # get the time slice before the delay

var_conn_flow_from = var_connection_flow[conn, n_from, d_from, s_from, t_from]
t_to = time_slices_to[(j+1)÷2]
var_conn_flow_to = var_connection_flow[conn, n_to, d_to, s_to, t_to]

expected_con = SpineOpt.build_sense_constraint(
var_conn_flow_to,
sense,
flow_ratio * var_conn_flow_from,
)
m = run_spineopt(url_in; log_level=0, optimize=false)
var_connection_flow = m.ext[:spineopt].variables[:connection_flow]
constraint = m.ext[:spineopt].constraints[Symbol(ratio)]
@test length(constraint) == 2
conn = connection(:connection_ab)
n_from = node(:node_a)
n_to = node(:node_b)
d_from = direction(:from_node)
d_to = direction(:to_node)
scenarios_from = [repeat([stochastic_scenario(:child)], 3); repeat([stochastic_scenario(:parent)], 5)]
time_slices_from = [
reverse(time_slice(m; temporal_block=temporal_block(:hourly)))
reverse(history_time_slice(m; temporal_block=temporal_block(:hourly)))
]
s_to = stochastic_scenario(:parent)
@testset for (j, t_to) in enumerate(reverse(time_slice(m; temporal_block=temporal_block(:two_hourly))))
coeffs = (1 - rem_minutes_delay, 1, rem_minutes_delay)
i = 2 * j - 1
a = i + h_delay
b = min(a + 2, length(time_slices_from))
s_set = scenarios_from[a:b]
t_set = time_slices_from[a:b]
vars_conn_flow_from = (
var_connection_flow[conn, n_from, d_from, s_from, t_from]
for (s_from, t_from) in zip(s_set, t_set)
)
var_conn_flow_to = var_connection_flow[conn, n_to, d_to, s_to, t_to]
expected_con = SpineOpt.build_sense_constraint(
2 * var_conn_flow_to,
sense,
flow_ratio * sum(c * v for (c, v) in zip(coeffs, vars_conn_flow_from)),
)
path = reverse(unique(s_set))
con_key = (conn, n_to, n_from, path, t_to)
observed_con = constraint_object(constraint[con_key...])
@test _is_constraint_equal(observed_con, expected_con)
end
path = unique([s_to; s_from])
con_key = (conn, n_to, n_from, path, t_con)
observed_con = constraint_object(constraint[con_key...])
@test _is_constraint_equal(observed_con, expected_con)
end
end
end
Expand Down Expand Up @@ -1751,7 +1747,7 @@ end
test_constraint_connection_intact_flow_ptdf()
test_constraint_connection_flow_lodf()
test_contraints_ptdf_lodf_duration()
test_constraint_ratio_out_in_connection_flow()
test_constraint_ratio_out_in_connection_flow_highest()
test_constraint_connections_invested_transition()
test_constraint_connections_invested_transition_mp()
test_constraint_connection_lifetime()
Expand Down
5 changes: 5 additions & 0 deletions test/run_examples.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ objective_function_reference_values = Dict(
"simple_system.json" => 160714.28571428574,
"unit_commitment.json" => 98637.42857142858,
"rolling_horizon.json" => 65164.8571429,
"multi-year_investment_with_econ_params_with_milestones" => 41025092.819187716,
"multi-year_investment_with_econ_params_without_milestones" => 71720404.84378022,
"multi-year_investment_without_econ_params" => 203360142.85714287,
"capacity_planning" => 352127000,
"stochastic" => 126964.2857142857,
)

@testset for path in readdir(joinpath(dirname(@__DIR__), "examples"); join=true)
Expand Down
2 changes: 1 addition & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ function _dismember_function(func)
end

@testset begin
include("data_structure/check_economic_structure.jl")
include("data_structure/migration.jl")
include("data_structure/check_data_structure.jl")
include("data_structure/check_economic_structure.jl")
include("data_structure/preprocess_data_structure.jl")
include("data_structure/temporal_structure.jl")
include("data_structure/stochastic_structure.jl")
Expand Down
Loading