mirror of
https://github.com/ANL-CEEESA/UnitCommitment.jl.git
synced 2025-12-06 00:08:52 -06:00
Ran JuliaFormatter
This commit is contained in:
@@ -100,7 +100,7 @@ function _from_json(json; repair = true)
|
|||||||
)
|
)
|
||||||
shortfall_penalty = timeseries(
|
shortfall_penalty = timeseries(
|
||||||
json["Parameters"]["Reserve shortfall penalty (\$/MW)"],
|
json["Parameters"]["Reserve shortfall penalty (\$/MW)"],
|
||||||
default=[0. for t in 1:T]
|
default = [0.0 for t in 1:T],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Read buses
|
# Read buses
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ function _add_ramp_eqs!(
|
|||||||
RESERVES_WHEN_RAMP_DOWN = true
|
RESERVES_WHEN_RAMP_DOWN = true
|
||||||
RESERVES_WHEN_SHUT_DOWN = true
|
RESERVES_WHEN_SHUT_DOWN = true
|
||||||
is_initially_on = _is_initially_on(g)
|
is_initially_on = _is_initially_on(g)
|
||||||
|
|
||||||
# The following are the same for generator g across all time periods
|
# The following are the same for generator g across all time periods
|
||||||
SU = g.startup_limit # startup rate
|
SU = g.startup_limit # startup rate
|
||||||
SD = g.shutdown_limit # shutdown rate
|
SD = g.shutdown_limit # shutdown rate
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ function _add_status_vars!(
|
|||||||
model::JuMP.Model,
|
model::JuMP.Model,
|
||||||
g::Unit,
|
g::Unit,
|
||||||
formulation_status_vars::Gar1962.StatusVars,
|
formulation_status_vars::Gar1962.StatusVars,
|
||||||
ALWAYS_CREATE_VARS = false
|
ALWAYS_CREATE_VARS = false,
|
||||||
)::Nothing
|
)::Nothing
|
||||||
is_on = _init(model, :is_on)
|
is_on = _init(model, :is_on)
|
||||||
switch_on = _init(model, :switch_on)
|
switch_on = _init(model, :switch_on)
|
||||||
@@ -38,12 +38,16 @@ function _add_status_vars!(
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if g.must_run[t]
|
if g.must_run[t]
|
||||||
# If the generator _must_ run, then it is obviously on and cannot be switched off
|
# If the generator _must_ run, then it is obviously on and cannot be switched off
|
||||||
# In the first time period, force unit to switch on if was off before
|
# In the first time period, force unit to switch on if was off before
|
||||||
# Otherwise, unit is on, and will never turn off, so will never need to turn on
|
# Otherwise, unit is on, and will never turn off, so will never need to turn on
|
||||||
fix(is_on[g.name, t], 1.0; force = true)
|
fix(is_on[g.name, t], 1.0; force = true)
|
||||||
fix(switch_on[g.name, t], (t == 1 ? 1.0 - _is_initially_on(g) : 0.0); force = true)
|
fix(
|
||||||
|
switch_on[g.name, t],
|
||||||
|
(t == 1 ? 1.0 - _is_initially_on(g) : 0.0);
|
||||||
|
force = true,
|
||||||
|
)
|
||||||
fix(switch_off[g.name, t], 0.0; force = true)
|
fix(switch_off[g.name, t], 0.0; force = true)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@@ -57,7 +61,8 @@ function _add_status_vars!(
|
|||||||
end
|
end
|
||||||
if g.must_run[t]
|
if g.must_run[t]
|
||||||
is_on[g.name, t] = 1.0
|
is_on[g.name, t] = 1.0
|
||||||
switch_on[g.name, t] = (t == 1 ? 1.0 - _is_initially_on(g) : 0.0)
|
switch_on[g.name, t] =
|
||||||
|
(t == 1 ? 1.0 - _is_initially_on(g) : 0.0)
|
||||||
switch_off[g.name, t] = 0.0
|
switch_off[g.name, t] = 0.0
|
||||||
end
|
end
|
||||||
end # check if ALWAYS_CREATE_VARS
|
end # check if ALWAYS_CREATE_VARS
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
|
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
|
||||||
# Released under the modified BSD license. See COPYING.md for more details.
|
# Released under the modified BSD license. See COPYING.md for more details.
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
_add_startup_shutdown_limit_eqs!(model::JuMP.Model, g::Unit)::Nothing
|
_add_startup_shutdown_limit_eqs!(model::JuMP.Model, g::Unit)::Nothing
|
||||||
|
|
||||||
@@ -45,41 +44,52 @@ function _add_startup_shutdown_limit_eqs!(model::JuMP.Model, g::Unit)::Nothing
|
|||||||
|
|
||||||
if g.initial_power > g.shutdown_limit
|
if g.initial_power > g.shutdown_limit
|
||||||
#eqs.shutdown_limit[gi, 0] = @constraint(mip, vars.switch_off[gi, 1] <= 0)
|
#eqs.shutdown_limit[gi, 0] = @constraint(mip, vars.switch_off[gi, 1] <= 0)
|
||||||
fix(switch_off[gi, 1], 0.; force = true)
|
fix(switch_off[gi, 1], 0.0; force = true)
|
||||||
end
|
end
|
||||||
|
|
||||||
for t = 1:T
|
for t in 1:T
|
||||||
## 2020-10-09 amk: added eqn (20) and check of g.min_uptime
|
## 2020-10-09 amk: added eqn (20) and check of g.min_uptime
|
||||||
# Not present in (23) in Kneueven et al.
|
# Not present in (23) in Kneueven et al.
|
||||||
if g.min_uptime > 1
|
if g.min_uptime > 1
|
||||||
# Equation (20) in Kneuven et al. (2020)
|
# Equation (20) in Kneuven et al. (2020)
|
||||||
eqs.startstop_limit[gi,t] =
|
eqs.startstop_limit[gi, t] = @constraint(
|
||||||
@constraint(model,
|
model,
|
||||||
prod_above[gi, t] + reserve[gi, t]
|
prod_above[gi, t] + reserve[gi, t] <=
|
||||||
<= (g.max_power[t] - g.min_power[t]) * is_on[gi, t]
|
(g.max_power[t] - g.min_power[t]) * is_on[gi, t] -
|
||||||
- max(0, g.max_power[t] - g.startup_limit) * switch_on[gi, t]
|
max(0, g.max_power[t] - g.startup_limit) * switch_on[gi, t] - (
|
||||||
- (t < T ? max(0, g.max_power[t] - g.shutdown_limit) * switch_off[gi, t+1] : 0.)
|
t < T ?
|
||||||
)
|
max(0, g.max_power[t] - g.shutdown_limit) *
|
||||||
|
switch_off[gi, t+1] : 0.0
|
||||||
|
)
|
||||||
|
)
|
||||||
else
|
else
|
||||||
## Startup limits
|
## Startup limits
|
||||||
# Equation (23a) in Kneuven et al. (2020)
|
# Equation (23a) in Kneuven et al. (2020)
|
||||||
eqs.startup_limit[gi, t] =
|
eqs.startup_limit[gi, t] = @constraint(
|
||||||
@constraint(model,
|
model,
|
||||||
prod_above[gi, t] + reserve[gi, t]
|
prod_above[gi, t] + reserve[gi, t] <=
|
||||||
<= (g.max_power[t] - g.min_power[t]) * is_on[gi, t]
|
(g.max_power[t] - g.min_power[t]) * is_on[gi, t] -
|
||||||
- max(0, g.max_power[t] - g.startup_limit) * switch_on[gi, t]
|
max(0, g.max_power[t] - g.startup_limit) * switch_on[gi, t] - (
|
||||||
- (t < T ? max(0, g.startup_limit - g.shutdown_limit) * switch_off[gi, t+1] : 0.)
|
t < T ?
|
||||||
)
|
max(0, g.startup_limit - g.shutdown_limit) *
|
||||||
|
switch_off[gi, t+1] : 0.0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
## Shutdown limits
|
## Shutdown limits
|
||||||
if t < T
|
if t < T
|
||||||
# Equation (23b) in Kneuven et al. (2020)
|
# Equation (23b) in Kneuven et al. (2020)
|
||||||
eqs.shutdown_limit[gi, t] =
|
eqs.shutdown_limit[gi, t] = @constraint(
|
||||||
@constraint(model,
|
model,
|
||||||
prod_above[gi, t] + reserve[gi, t]
|
prod_above[gi, t] + reserve[gi, t] <=
|
||||||
<= (g.max_power[t] - g.min_power[t]) * xis_on[gi, t]
|
(g.max_power[t] - g.min_power[t]) * xis_on[gi, t] - (
|
||||||
- (t < T ? max(0, g.max_power[t] - g.shutdown_limit) * switch_off[gi, t+1] : 0.)
|
t < T ?
|
||||||
- max(0, g.shutdown_limit - g.startup_limit) * switch_on[gi, t])
|
max(0, g.max_power[t] - g.shutdown_limit) *
|
||||||
|
switch_off[gi, t+1] : 0.0
|
||||||
|
) -
|
||||||
|
max(0, g.shutdown_limit - g.startup_limit) *
|
||||||
|
switch_on[gi, t]
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end # check if g.min_uptime > 1
|
end # check if g.min_uptime > 1
|
||||||
end # loop over time
|
end # loop over time
|
||||||
|
|||||||
@@ -61,56 +61,63 @@ function _add_startup_cost_eqs!(
|
|||||||
# Equation (59) in Kneuven et al. (2020)
|
# Equation (59) in Kneuven et al. (2020)
|
||||||
# Relate downtime_arc with switch_on
|
# Relate downtime_arc with switch_on
|
||||||
# "switch_on[g,t] >= x_g(t',t) for all t' \in [t-TC+1, t-DT]"
|
# "switch_on[g,t] >= x_g(t',t) for all t' \in [t-TC+1, t-DT]"
|
||||||
eq_startup_at_t[gn, t] =
|
eq_startup_at_t[gn, t] = @constraint(
|
||||||
@constraint(model,
|
model,
|
||||||
switch_on[gn, t]
|
switch_on[gn, t] >= sum(
|
||||||
>= sum(downtime_arc[gn,tmp_t,t]
|
downtime_arc[gn, tmp_t, t] for
|
||||||
for tmp_t in t-TC+1:t-DT if tmp_t >= 1)
|
tmp_t in t-TC+1:t-DT if tmp_t >= 1
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# Equation (60) in Kneuven et al. (2020)
|
# Equation (60) in Kneuven et al. (2020)
|
||||||
# "switch_off[g,t] >= x_g(t,t') for all t' \in [t+DT, t+TC-1]"
|
# "switch_off[g,t] >= x_g(t,t') for all t' \in [t+DT, t+TC-1]"
|
||||||
eqs.shutdown_at_t[gn, t] =
|
eqs.shutdown_at_t[gn, t] = @constraint(
|
||||||
@constraint(model,
|
model,
|
||||||
switch_off[gn, t]
|
switch_off[gn, t] >= sum(
|
||||||
>= sum(downtime_arc[gn,t,tmp_t]
|
downtime_arc[gn, t, tmp_t] for
|
||||||
for tmp_t in t+DT:t+TC-1 if tmp_t <= T)
|
tmp_t in t+DT:t+TC-1 if tmp_t <= T
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# Objective function terms for start-up costs
|
# Objective function terms for start-up costs
|
||||||
# Equation (61) in Kneuven et al. (2020)
|
# Equation (61) in Kneuven et al. (2020)
|
||||||
default_category = S
|
default_category = S
|
||||||
if initial_time_shutdown > 0 && t + initial_time_shutdown - 1 < TC
|
if initial_time_shutdown > 0 && t + initial_time_shutdown - 1 < TC
|
||||||
for s in 1:S-1
|
for s in 1:S-1
|
||||||
# If off for x periods before, then belongs to category s
|
# If off for x periods before, then belongs to category s
|
||||||
# if -x+1 in [t-delay[s+1]+1,t-delay[s]]
|
# if -x+1 in [t-delay[s+1]+1,t-delay[s]]
|
||||||
# or, equivalently, if total time off in [delay[s], delay[s+1]-1]
|
# or, equivalently, if total time off in [delay[s], delay[s+1]-1]
|
||||||
# where total time off = t - 1 + initial_time_shutdown
|
# where total time off = t - 1 + initial_time_shutdown
|
||||||
# (the -1 because not off for current time period)
|
# (the -1 because not off for current time period)
|
||||||
if t + initial_time_shutdown - 1 < g.startup_categories[s+1].delay
|
if t + initial_time_shutdown - 1 <
|
||||||
default_category = s
|
g.startup_categories[s+1].delay
|
||||||
break # does not go into next category
|
default_category = s
|
||||||
end
|
break # does not go into next category
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
add_to_expression!(model[:obj],
|
add_to_expression!(
|
||||||
switch_on[gn, t],
|
model[:obj],
|
||||||
g.startup_categories[default_category].cost)
|
switch_on[gn, t],
|
||||||
|
g.startup_categories[default_category].cost,
|
||||||
|
)
|
||||||
|
|
||||||
for s in 1:S-1
|
for s in 1:S-1
|
||||||
# Objective function terms for start-up costs
|
# Objective function terms for start-up costs
|
||||||
# Equation (61) in Kneuven et al. (2020)
|
# Equation (61) in Kneuven et al. (2020)
|
||||||
# Says to replace the cost of last category with cost of category s
|
# Says to replace the cost of last category with cost of category s
|
||||||
start_range = max((t - g.startup_categories[s + 1].delay + 1),1)
|
start_range = max((t - g.startup_categories[s+1].delay + 1), 1)
|
||||||
end_range = min((t - g.startup_categories[s].delay),T-1)
|
end_range = min((t - g.startup_categories[s].delay), T - 1)
|
||||||
for tmp_t in start_range:end_range
|
for tmp_t in start_range:end_range
|
||||||
if (t < tmp_t + DT) || (t >= tmp_t + TC) # the second clause should never be true for s < S
|
if (t < tmp_t + DT) || (t >= tmp_t + TC) # the second clause should never be true for s < S
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
add_to_expression!(model[:obj],
|
add_to_expression!(
|
||||||
downtime_arc[gn,tmp_t,t],
|
model[:obj],
|
||||||
g.startup_categories[s].cost - g.startup_categories[S].cost)
|
downtime_arc[gn, tmp_t, t],
|
||||||
|
g.startup_categories[s].cost - g.startup_categories[S].cost,
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end # iterate over startup categories
|
end # iterate over startup categories
|
||||||
end # iterate over time
|
end # iterate over time
|
||||||
end # add_startup_costs_KneOstWat20
|
end # add_startup_costs_KneOstWat20
|
||||||
|
|||||||
@@ -51,8 +51,7 @@ function _add_startup_cost_eqs!(
|
|||||||
# Equation (55) in Kneuven et al. (2020)
|
# Equation (55) in Kneuven et al. (2020)
|
||||||
eq_startup_choose[gn, t] = @constraint(
|
eq_startup_choose[gn, t] = @constraint(
|
||||||
model,
|
model,
|
||||||
switch_on[gn, t] ==
|
switch_on[gn, t] == sum(startup[gn, t, s] for s in 1:S)
|
||||||
sum(startup[gn, t, s] for s in 1:S)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for s in 1:S
|
for s in 1:S
|
||||||
@@ -71,7 +70,8 @@ function _add_startup_cost_eqs!(
|
|||||||
eq_startup_restrict[gn, t, s] = @constraint(
|
eq_startup_restrict[gn, t, s] = @constraint(
|
||||||
model,
|
model,
|
||||||
startup[gn, t, s] <=
|
startup[gn, t, s] <=
|
||||||
initial_sum + sum(switch_off[gn, i] for i in range if i >= 1)
|
initial_sum +
|
||||||
|
sum(switch_off[gn, i] for i in range if i >= 1)
|
||||||
)
|
)
|
||||||
end # if s < S (not the last category)
|
end # if s < S (not the last category)
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
|
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
|
||||||
# Released under the modified BSD license. See COPYING.md for more details.
|
# Released under the modified BSD license. See COPYING.md for more details.
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
_add_startup_shutdown_limit_eqs!(model::JuMP.Model, g::Unit)::Nothing
|
_add_startup_shutdown_limit_eqs!(model::JuMP.Model, g::Unit)::Nothing
|
||||||
|
|
||||||
@@ -42,26 +41,28 @@ function _add_startup_shutdown_limit_eqs!(model::JuMP.Model, g::Unit)::Nothing
|
|||||||
|
|
||||||
T = model[:instance].time
|
T = model[:instance].time
|
||||||
gi = g.name
|
gi = g.name
|
||||||
for t = 1:T
|
for t in 1:T
|
||||||
## 2020-10-09 amk: added eqn (20) and check of g.min_uptime
|
## 2020-10-09 amk: added eqn (20) and check of g.min_uptime
|
||||||
if g.min_uptime > 1 && t < T
|
if g.min_uptime > 1 && t < T
|
||||||
# Equation (20) in Kneuven et al. (2020)
|
# Equation (20) in Kneuven et al. (2020)
|
||||||
# UT > 1 required, to guarantee that vars.switch_on[gi, t] and vars.switch_off[gi, t+1] are not both = 1 at the same time
|
# UT > 1 required, to guarantee that vars.switch_on[gi, t] and vars.switch_off[gi, t+1] are not both = 1 at the same time
|
||||||
eq_startstop_limit[gi,t] =
|
eq_startstop_limit[gi, t] = @constraint(
|
||||||
@constraint(model,
|
model,
|
||||||
prod_above[gi, t] + reserve[gi, t]
|
prod_above[gi, t] + reserve[gi, t] <=
|
||||||
<= (g.max_power[t] - g.min_power[t]) * is_on[gi, t]
|
(g.max_power[t] - g.min_power[t]) * is_on[gi, t] -
|
||||||
- max(0, g.max_power[t] - g.startup_limit) * switch_on[gi, t]
|
max(0, g.max_power[t] - g.startup_limit) * switch_on[gi, t] -
|
||||||
- max(0, g.max_power[t] - g.shutdown_limit) * switch_off[gi, t+1])
|
max(0, g.max_power[t] - g.shutdown_limit) * switch_off[gi, t+1]
|
||||||
|
)
|
||||||
else
|
else
|
||||||
## Startup limits
|
## Startup limits
|
||||||
# Equation (21a) in Kneuven et al. (2020)
|
# Equation (21a) in Kneuven et al. (2020)
|
||||||
# Proposed by Morales-España et al. (2013a)
|
# Proposed by Morales-España et al. (2013a)
|
||||||
eqs_startup_limit[gi, t] =
|
eqs_startup_limit[gi, t] = @constraint(
|
||||||
@constraint(model,
|
model,
|
||||||
prod_above[gi, t] + reserve[gi, t]
|
prod_above[gi, t] + reserve[gi, t] <=
|
||||||
<= (g.max_power[t] - g.min_power[t]) * is_on[gi, t]
|
(g.max_power[t] - g.min_power[t]) * is_on[gi, t] -
|
||||||
- max(0, g.max_power[t] - g.startup_limit) * switch_on[gi, t])
|
max(0, g.max_power[t] - g.startup_limit) * switch_on[gi, t]
|
||||||
|
)
|
||||||
|
|
||||||
## Shutdown limits
|
## Shutdown limits
|
||||||
if t < T
|
if t < T
|
||||||
@@ -74,12 +75,14 @@ function _add_startup_shutdown_limit_eqs!(model::JuMP.Model, g::Unit)::Nothing
|
|||||||
# amk: if shutdown_limit is the max prod of generator in time period before shutting down,
|
# amk: if shutdown_limit is the max prod of generator in time period before shutting down,
|
||||||
# then it makes sense to count reserves, because otherwise, if reserves ≠ 0,
|
# then it makes sense to count reserves, because otherwise, if reserves ≠ 0,
|
||||||
# then the generator will actually produce more than the limit
|
# then the generator will actually produce more than the limit
|
||||||
eqs.shutdown_limit[gi, t] =
|
eqs.shutdown_limit[gi, t] = @constraint(
|
||||||
@constraint(model,
|
model,
|
||||||
prod_above[gi, t]
|
prod_above[gi, t] +
|
||||||
+ (RESERVES_WHEN_SHUT_DOWN ? reserve[gi, t] : 0.) # amk added
|
(RESERVES_WHEN_SHUT_DOWN ? reserve[gi, t] : 0.0) <=
|
||||||
<= (g.max_power[t] - g.min_power[t]) * is_on[gi, t]
|
(g.max_power[t] - g.min_power[t]) * is_on[gi, t] -
|
||||||
- max(0, g.max_power[t] - g.shutdown_limit) * switch_off[gi, t+1])
|
max(0, g.max_power[t] - g.shutdown_limit) *
|
||||||
|
switch_off[gi, t+1]
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end # check if g.min_uptime > 1
|
end # check if g.min_uptime > 1
|
||||||
end # loop over time
|
end # loop over time
|
||||||
|
|||||||
@@ -16,5 +16,5 @@ function _add_startup_cost_eqs!(
|
|||||||
g::Unit,
|
g::Unit,
|
||||||
formulation::MorLatRam2013.StartupCosts,
|
formulation::MorLatRam2013.StartupCosts,
|
||||||
)::Nothing
|
)::Nothing
|
||||||
error("Not implemented.")
|
return error("Not implemented.")
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -53,31 +53,33 @@ function _add_ramp_eqs!(
|
|||||||
SD = g.shutdown_limit # shutdown rate
|
SD = g.shutdown_limit # shutdown rate
|
||||||
RU = g.ramp_up_limit # ramp up rate
|
RU = g.ramp_up_limit # ramp up rate
|
||||||
RD = g.ramp_down_limit # ramp down rate
|
RD = g.ramp_down_limit # ramp down rate
|
||||||
|
|
||||||
# TODO check initial conditions, but maybe okay as long as (35) and (36) are also used
|
# TODO check initial conditions, but maybe okay as long as (35) and (36) are also used
|
||||||
for t in 1:model[:instance].time
|
for t in 1:model[:instance].time
|
||||||
Pbar = g.max_power[t]
|
Pbar = g.max_power[t]
|
||||||
|
|
||||||
#TRD = floor((Pbar - SU)/RD)
|
#TRD = floor((Pbar - SU)/RD)
|
||||||
# TODO check amk changed TRD wrt Kneuven et al.
|
# TODO check amk changed TRD wrt Kneuven et al.
|
||||||
TRD = ceil((Pbar - SD) / RD) # ramp down time
|
TRD = ceil((Pbar - SD) / RD) # ramp down time
|
||||||
|
|
||||||
if Pbar < 1e-7
|
if Pbar < 1e-7
|
||||||
# Skip this time period if max power = 0
|
# Skip this time period if max power = 0
|
||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
|
||||||
if UT >= 1
|
if UT >= 1
|
||||||
# Equation (37) in Kneuven et al. (2020)
|
# Equation (37) in Kneuven et al. (2020)
|
||||||
KSD = min( TRD, UT-1, T-t-1 )
|
KSD = min(TRD, UT - 1, T - t - 1)
|
||||||
eq_str_prod_limit[gn, t] =
|
eq_str_prod_limit[gn, t] = @constraint(
|
||||||
@constraint(model,
|
model,
|
||||||
prod_above[gn, t] + g.min_power[t] * is_on[gn, t]
|
prod_above[gn, t] +
|
||||||
+ (RESERVES_WHEN_RAMP_DOWN ? reserve[gn, t] : 0.) # amk added; TODO: should this be RESERVES_WHEN_RAMP_DOWN or RESERVES_WHEN_SHUT_DOWN?
|
g.min_power[t] * is_on[gn, t] +
|
||||||
<= Pbar * is_on[gi, t]
|
(RESERVES_WHEN_RAMP_DOWN ? reserve[gn, t] : 0.0) <=
|
||||||
- sum((Pbar - (SD + i * RD)) * switch_off[gi, t+1+i]
|
Pbar * is_on[gi, t] - sum(
|
||||||
for i in 0:KSD)
|
(Pbar - (SD + i * RD)) * switch_off[gi, t+1+i] for
|
||||||
)
|
i in 0:KSD
|
||||||
|
)
|
||||||
|
)
|
||||||
end # check UT >= 1
|
end # check UT >= 1
|
||||||
end # loop over time
|
end # loop over time
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -76,19 +76,21 @@ function _add_reserve_eqs!(model::JuMP.Model)::Nothing
|
|||||||
shortfall_penalty = instance.shortfall_penalty[t]
|
shortfall_penalty = instance.shortfall_penalty[t]
|
||||||
eq_min_reserve[t] = @constraint(
|
eq_min_reserve[t] = @constraint(
|
||||||
model,
|
model,
|
||||||
sum(model[:reserve][g.name, t] for g in instance.units)
|
sum(model[:reserve][g.name, t] for g in instance.units) + (
|
||||||
+ (shortfall_penalty > 1e-7 ? model[:reserve_shortfall][t] : 0.)
|
shortfall_penalty > 1e-7 ? model[:reserve_shortfall][t] : 0.0
|
||||||
>= instance.reserves.spinning[t]
|
) >= instance.reserves.spinning[t]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Account for shortfall contribution to objective
|
# Account for shortfall contribution to objective
|
||||||
if shortfall_penalty > 1e-7
|
if shortfall_penalty > 1e-7
|
||||||
add_to_expression!(model.obj,
|
add_to_expression!(
|
||||||
shortfall_penalty,
|
model.obj,
|
||||||
model[:reserve_shortfall][t])
|
shortfall_penalty,
|
||||||
|
model[:reserve_shortfall][t],
|
||||||
|
)
|
||||||
else
|
else
|
||||||
# Not added to the model at all
|
# Not added to the model at all
|
||||||
#fix(model.vars.reserve_shortfall[t], 0.; force=true)
|
#fix(model.vars.reserve_shortfall[t], 0.; force=true)
|
||||||
end
|
end
|
||||||
end # loop over time
|
end # loop over time
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -57,7 +57,11 @@ _is_initially_on(g::Unit)::Float64 = (g.initial_status > 0 ? 1.0 : 0.0)
|
|||||||
|
|
||||||
Add `:reserve` variable to `model`, fixed to zero if no spinning reserves specified.
|
Add `:reserve` variable to `model`, fixed to zero if no spinning reserves specified.
|
||||||
"""
|
"""
|
||||||
function _add_reserve_vars!(model::JuMP.Model, g::Unit, ALWAYS_CREATE_VARS = false)::Nothing
|
function _add_reserve_vars!(
|
||||||
|
model::JuMP.Model,
|
||||||
|
g::Unit,
|
||||||
|
ALWAYS_CREATE_VARS = false,
|
||||||
|
)::Nothing
|
||||||
reserve = _init(model, :reserve)
|
reserve = _init(model, :reserve)
|
||||||
reserve_shortfall = _init(model, :reserve_shortfall) # for accounting for shortfall penalty in the objective
|
reserve_shortfall = _init(model, :reserve_shortfall) # for accounting for shortfall penalty in the objective
|
||||||
for t in 1:model[:instance].time
|
for t in 1:model[:instance].time
|
||||||
@@ -137,9 +141,9 @@ function _add_startup_shutdown_limit_eqs!(model::JuMP.Model, g::Unit)::Nothing
|
|||||||
# Generator producing too much to be turned off in the first time period
|
# Generator producing too much to be turned off in the first time period
|
||||||
# (can a binary variable have bounds x = 0?)
|
# (can a binary variable have bounds x = 0?)
|
||||||
#eqs.shutdown_limit[gi, 0] = @constraint(mip, vars.switch_off[gi, 1] <= 0)
|
#eqs.shutdown_limit[gi, 0] = @constraint(mip, vars.switch_off[gi, 1] <= 0)
|
||||||
fix(model.vars.switch_off[gi, 1], 0.; force = true)
|
fix(model.vars.switch_off[gi, 1], 0.0; force = true)
|
||||||
#eq_shutdown_limit[g.name, 0] =
|
#eq_shutdown_limit[g.name, 0] =
|
||||||
#@constraint(model, switch_off[g.name, 1] <= 0)
|
#@constraint(model, switch_off[g.name, 1] <= 0)
|
||||||
end
|
end
|
||||||
if t < T
|
if t < T
|
||||||
eq_shutdown_limit[g.name, t] = @constraint(
|
eq_shutdown_limit[g.name, t] = @constraint(
|
||||||
@@ -164,14 +168,16 @@ Variables
|
|||||||
function _add_shutdown_cost_eqs!(model::JuMP.Modle, g::Unit)::Nothing
|
function _add_shutdown_cost_eqs!(model::JuMP.Modle, g::Unit)::Nothing
|
||||||
T = model[:instance].time
|
T = model[:instance].time
|
||||||
gi = g.name
|
gi = g.name
|
||||||
for t = 1:T
|
for t in 1:T
|
||||||
shutdown_cost = 0.
|
shutdown_cost = 0.0
|
||||||
if shutdown_cost > 1e-7
|
if shutdown_cost > 1e-7
|
||||||
# Equation (62) in Kneuven et al. (2020)
|
# Equation (62) in Kneuven et al. (2020)
|
||||||
add_to_expression!(model[:obj],
|
add_to_expression!(
|
||||||
model[:switch_off][gi, t],
|
model[:obj],
|
||||||
shutdown_cost)
|
model[:switch_off][gi, t],
|
||||||
end
|
shutdown_cost,
|
||||||
|
)
|
||||||
|
end
|
||||||
end # loop over time
|
end # loop over time
|
||||||
end # _add_shutdown_cost_eqs!
|
end # _add_shutdown_cost_eqs!
|
||||||
|
|
||||||
@@ -256,18 +262,18 @@ function _add_min_uptime_downtime_eqs!(model::JuMP.Model, g::Unit)::Nothing
|
|||||||
# Equation (4) in Kneuven et al. (2020)
|
# Equation (4) in Kneuven et al. (2020)
|
||||||
eq_min_uptime[g.name, t] = @constraint(
|
eq_min_uptime[g.name, t] = @constraint(
|
||||||
model,
|
model,
|
||||||
sum(switch_on[g.name, i] for i in (t-g.min_uptime+1):t if i >= 1)
|
sum(switch_on[g.name, i] for i in (t-g.min_uptime+1):t if i >= 1) <= is_on[g.name, t]
|
||||||
<= is_on[g.name, t]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Minimum down-time
|
# Minimum down-time
|
||||||
# Equation (5) in Kneuven et al. (2020)
|
# Equation (5) in Kneuven et al. (2020)
|
||||||
eq_min_downtime[g.name, t] = @constraint(
|
eq_min_downtime[g.name, t] = @constraint(
|
||||||
model,
|
model,
|
||||||
sum(switch_off[g.name, i] for i in (t-g.min_downtime+1):t if i >= 1)
|
sum(
|
||||||
<= 1 - is_on[g.name, t]
|
switch_off[g.name, i] for i in (t-g.min_downtime+1):t if i >= 1
|
||||||
|
) <= 1 - is_on[g.name, t]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Minimum up/down-time for initial periods
|
# Minimum up/down-time for initial periods
|
||||||
# Equations (3a) and (3b) in Kneuven et al. (2020)
|
# Equations (3a) and (3b) in Kneuven et al. (2020)
|
||||||
# (using :switch_on and :switch_off instead of :is_on)
|
# (using :switch_on and :switch_off instead of :is_on)
|
||||||
|
|||||||
Reference in New Issue
Block a user