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 time indices for representative temporal blocks #1015

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from
Draft
6 changes: 5 additions & 1 deletion src/constraints/constraint_unit_pw_heat_rate.jl
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,15 @@ function _build_constraint_unit_pw_heat_rate(m::Model, u, n_from, n_to, s_path,
* unit_idle_heat_rate(
m; unit=u, node1=n_from, node2=n_to, stochastic_scenario=s, analysis_time=t0, t=t
)
for (u, s, t1) in units_on_indices(m; unit=u, stochastic_scenario=s_path, t=t_overlaps_t(m; t=t));
init=0,
)
+ sum(
+ units_started_up[u, s, t1]
* unit_start_flow(
m; unit=u, node1=n_from, node2=n_to, stochastic_scenario=s, analysis_time=t0, t=t
)
for (u, s, t1) in units_on_indices(m; unit=u, stochastic_scenario=s_path, t=t_overlaps_t(m; t=t));
for (u, s, t1) in units_switched_indices(m; unit=u, stochastic_scenario=s_path, t=t_overlaps_t(m; t=t));
init=0,
)
)
Expand Down
9 changes: 8 additions & 1 deletion src/constraints/constraint_unit_state_transition.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,19 @@ function _build_constraint_unit_state_transition(m::Model, u, s_path, t_before,
# TODO: use :integer, :binary, :linear as parameter values -> reusable for other pruposes
@build_constraint(
sum(
+ units_on[u, s, t_after] - units_started_up[u, s, t_after] + units_shut_down[u, s, t_after]
+ units_on[u, s, t_after]
for (u, s, t_after) in units_on_indices(
m; unit=u, stochastic_scenario=s_path, t=t_after, temporal_block=anything,
);
init=0,
)
+ sum(
+ units_shut_down[u, s, t_after] - units_started_up[u, s, t_after]
for (u, s, t_after) in units_switched_indices(
m; unit=u, stochastic_scenario=s_path, t=t_after, temporal_block=anything,
);
init=0,
)
==
sum(
+ units_on[u, s, t_before]
Expand Down
28 changes: 28 additions & 0 deletions src/data_structure/preprocess_data_structure.jl
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ function preprocess_data_structure()
generate_unit_flow_capacity()
generate_connection_flow_capacity()
generate_unit_commitment_parameters()
generate_representative_temporal_block_label()
end

"""
Expand Down Expand Up @@ -991,3 +992,30 @@ function generate_unit_commitment_parameters()
export has_out_of_service_variable
end
end

function generate_representative_temporal_block_label()
_iter_explicit_repr_temporal_block = Iterators.flatten(
(
# represented temporal blocks
indices(representative_periods_mapping),
# representing temporal blocks which may be used as a group
Set(
Iterators.flatten(
groups(temporal_block.(representative_periods_mapping(temporal_block=tb).values))
for tb in indices(representative_periods_mapping)
)
),
)
)
pname = :explicit_representative_temporal_block
add_object_parameter_values!(
temporal_block,
Dict(tb => Dict(pname => parameter_value(true)) for tb in Set(_iter_explicit_repr_temporal_block))
)
add_object_parameter_defaults!(temporal_block, Dict(pname => parameter_value(false)))
explicit_representative_temporal_block = Parameter(pname, [temporal_block])
@eval begin
explicit_representative_temporal_block = $explicit_representative_temporal_block
export explicit_representative_temporal_block
end
end
10 changes: 9 additions & 1 deletion src/data_structure/temporal_structure.jl
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,15 @@ function unit_dynamic_time_indices(
(
(unit=u, t_before=tb, t_after=ta)
for (u, blk) in units_on__temporal_block(unit=unit, _compact=false)
for (tb, ta) in dynamic_time_indices(m, blk; t_before=t_before, t_after=t_after)
for (tb, ta) in dynamic_time_indices(m, blk; t_before=t_before, t_after=t_after)
# When representative temporal structure is used, the dynamic time indices of a unit must stay within the
# represented temporal blocks, including the corresponding representing blocks in case any of them leads to
# time slices outside the represented blocks. The reason is that the constraints about unit state transition
# may use variables (e.g. units_on) with time indices only inside the represented temporal blocks, otherwise
# the model can't find a mapping to representative blocks for the outside time indices.
if !(explicit_representative_temporal_block(temporal_block=blk)) || tb in time_slice(
m; temporal_block=members(blk)
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

This will damage performance.

Copy link
Member Author

Choose a reason for hiding this comment

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

Same concern. I've created issue #1018 to explain the problem. This change attempts for option 1. The latest commit ca43ceb attempts for option 2 and should be more efficient, but it couldn't pass all tests.

)
end

Expand Down
4 changes: 3 additions & 1 deletion src/variables/variable_common.jl
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ function add_variable!(
res_internal_fix_value = _resolve(internal_fix_value, m, ind, other_ind_and_factor...; reducer=_check_unique)
_finalize_variable!(vars[ind], res_bin, res_int, res_lb, res_ub, res_fix_value, res_internal_fix_value)
end
merge!(vars, Dict(ind => coeff * vars[ref_ind] for (ind, (ref_ind, coeff)) in ind_map))
# A ref_ind may not be covered by keys(vars) unless
# the ind_map is carefully designed in specific variable adding functions.
merge!(vars, Dict(ind => coeff * vars[ref_ind] for (ind, (ref_ind, coeff)) in ind_map if haskey(vars, ref_ind)))
Copy link
Collaborator

Choose a reason for hiding this comment

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

This haskey introduce an extra lookup for every iteration of the comprehension and might also damage performance.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks! The haskey check now goes outside the iteration (commit 2cefffa).

# Apply initial value, but make sure it updates itself by using a TimeSeries Call
if initial_value !== nothing
last_history_t = last(history_time_slice(m))
Expand Down
Loading