mirror of
https://github.com/ANL-CEEESA/UnitCommitment.jl.git
synced 2025-12-06 08:18:51 -06:00
Add reserve shortfall penalty
This commit is contained in:
@@ -98,6 +98,10 @@ function _from_json(json; repair = true)
|
|||||||
json["Parameters"]["Power balance penalty (\$/MW)"],
|
json["Parameters"]["Power balance penalty (\$/MW)"],
|
||||||
default = [1000.0 for t in 1:T],
|
default = [1000.0 for t in 1:T],
|
||||||
)
|
)
|
||||||
|
shortfall_penalty = timeseries(
|
||||||
|
json["Parameters"]["Reserve shortfall penalty (\$/MW)"],
|
||||||
|
default = [0.0 for t in 1:T],
|
||||||
|
)
|
||||||
|
|
||||||
# Read buses
|
# Read buses
|
||||||
for (bus_name, dict) in json["Buses"]
|
for (bus_name, dict) in json["Buses"]
|
||||||
@@ -264,6 +268,7 @@ function _from_json(json; repair = true)
|
|||||||
instance = UnitCommitmentInstance(
|
instance = UnitCommitmentInstance(
|
||||||
T,
|
T,
|
||||||
power_balance_penalty,
|
power_balance_penalty,
|
||||||
|
shortfall_penalty,
|
||||||
units,
|
units,
|
||||||
buses,
|
buses,
|
||||||
lines,
|
lines,
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ end
|
|||||||
mutable struct UnitCommitmentInstance
|
mutable struct UnitCommitmentInstance
|
||||||
time::Int
|
time::Int
|
||||||
power_balance_penalty::Vector{Float64}
|
power_balance_penalty::Vector{Float64}
|
||||||
|
"Penalty for failing to meet reserve requirement."
|
||||||
|
shortfall_penalty::Vector{Float64}
|
||||||
units::Vector{Unit}
|
units::Vector{Unit}
|
||||||
buses::Vector{Bus}
|
buses::Vector{Bus}
|
||||||
lines::Vector{TransmissionLine}
|
lines::Vector{TransmissionLine}
|
||||||
|
|||||||
@@ -4,15 +4,11 @@
|
|||||||
|
|
||||||
function _add_bus!(model::JuMP.Model, b::Bus)::Nothing
|
function _add_bus!(model::JuMP.Model, b::Bus)::Nothing
|
||||||
net_injection = _init(model, :expr_net_injection)
|
net_injection = _init(model, :expr_net_injection)
|
||||||
reserve = _init(model, :expr_reserve)
|
|
||||||
curtail = _init(model, :curtail)
|
curtail = _init(model, :curtail)
|
||||||
for t in 1:model[:instance].time
|
for t in 1:model[:instance].time
|
||||||
# Fixed load
|
# Fixed load
|
||||||
net_injection[b.name, t] = AffExpr(-b.load[t])
|
net_injection[b.name, t] = AffExpr(-b.load[t])
|
||||||
|
|
||||||
# Reserves
|
|
||||||
reserve[b.name, t] = AffExpr()
|
|
||||||
|
|
||||||
# Load curtailment
|
# Load curtailment
|
||||||
curtail[b.name, t] =
|
curtail[b.name, t] =
|
||||||
@variable(model, lower_bound = 0, upper_bound = b.load[t])
|
@variable(model, lower_bound = 0, upper_bound = b.load[t])
|
||||||
|
|||||||
@@ -29,13 +29,31 @@ end
|
|||||||
|
|
||||||
function _add_reserve_eqs!(model::JuMP.Model)::Nothing
|
function _add_reserve_eqs!(model::JuMP.Model)::Nothing
|
||||||
eq_min_reserve = _init(model, :eq_min_reserve)
|
eq_min_reserve = _init(model, :eq_min_reserve)
|
||||||
for t in 1:model[:instance].time
|
instance = model[:instance]
|
||||||
|
for t in 1:instance.time
|
||||||
|
# Equation (68) in Kneuven et al. (2020)
|
||||||
|
# As in Morales-España et al. (2013a)
|
||||||
|
# Akin to the alternative formulation with max_power_avail
|
||||||
|
# from Carrión and Arroyo (2006) and Ostrowski et al. (2012)
|
||||||
|
shortfall_penalty = instance.shortfall_penalty[t]
|
||||||
eq_min_reserve[t] = @constraint(
|
eq_min_reserve[t] = @constraint(
|
||||||
model,
|
model,
|
||||||
sum(
|
sum(model[:reserve][g.name, t] for g in instance.units) + (
|
||||||
model[:expr_reserve][b.name, t] for b in model[:instance].buses
|
shortfall_penalty > 1e-7 ? model[:reserve_shortfall][t] : 0.0
|
||||||
) >= model[:instance].reserves.spinning[t]
|
) >= instance.reserves.spinning[t]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Account for shortfall contribution to objective
|
||||||
|
if shortfall_penalty > 1e-7
|
||||||
|
add_to_expression!(
|
||||||
|
model.obj,
|
||||||
|
shortfall_penalty,
|
||||||
|
model[:reserve_shortfall][t],
|
||||||
|
)
|
||||||
|
else
|
||||||
|
# Not added to the model at all
|
||||||
|
#fix(model.vars.reserve_shortfall[t], 0.; force=true)
|
||||||
end
|
end
|
||||||
|
end # loop over time
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ _is_initially_on(g::Unit)::Float64 = (g.initial_status > 0 ? 1.0 : 0.0)
|
|||||||
|
|
||||||
function _add_reserve_vars!(model::JuMP.Model, g::Unit)::Nothing
|
function _add_reserve_vars!(model::JuMP.Model, g::Unit)::Nothing
|
||||||
reserve = _init(model, :reserve)
|
reserve = _init(model, :reserve)
|
||||||
|
reserve_shortfall = _init(model, :reserve_shortfall)
|
||||||
for t in 1:model[:instance].time
|
for t in 1:model[:instance].time
|
||||||
if g.provides_spinning_reserves[t]
|
if g.provides_spinning_reserves[t]
|
||||||
reserve[g.name, t] = @variable(model, lower_bound = 0)
|
reserve[g.name, t] = @variable(model, lower_bound = 0)
|
||||||
@@ -210,11 +211,5 @@ function _add_net_injection_eqs!(model::JuMP.Model, g::Unit)::Nothing
|
|||||||
model[:is_on][g.name, t],
|
model[:is_on][g.name, t],
|
||||||
g.min_power[t],
|
g.min_power[t],
|
||||||
)
|
)
|
||||||
# Add to reserves expression
|
|
||||||
add_to_expression!(
|
|
||||||
model[:expr_reserve][g.bus.name, t],
|
|
||||||
model[:reserve][g.name, t],
|
|
||||||
1.0,
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user