Make papers into modules, instead of structs; add StartupCostsFormulation

bugfix/formulations
Alinson S. Xavier 4 years ago
parent ecb13dba7c
commit 8cdd88d6de

@ -18,6 +18,17 @@ Pkg.activate(".")
@everywhere using LinearAlgebra @everywhere using LinearAlgebra
@everywhere using Random @everywhere using Random
@everywhere import UnitCommitment:
ArrCon2000,
CarArr2006,
DamKucRajAta2016,
Formulation,
Gar1962,
KnuOstWat2018,
MorLatRam2013,
PanGua2016,
XavQiuWanThi2019
@everywhere UnitCommitment._setup_logger() @everywhere UnitCommitment._setup_logger()
function main() function main()
@ -59,26 +70,26 @@ function main()
"tejada19/UC_168h_199g", "tejada19/UC_168h_199g",
] ]
formulations = Dict( formulations = Dict(
"ArrCon2000" => UnitCommitment.Formulation( "ArrCon2000" => Formulation(
ramping = UnitCommitment.ArrCon2000(), ramping = ArrCon2000.Ramping(),
), ),
"CarArr2006" => UnitCommitment.Formulation( "CarArr2006" => Formulation(
pwl_costs = UnitCommitment.CarArr2006(), pwl_costs = CarArr2006.PwlCosts(),
), ),
"DamKucRajAta2016" => UnitCommitment.Formulation( "DamKucRajAta2016" => Formulation(
ramping = UnitCommitment.DamKucRajAta2016(), ramping = DamKucRajAta2016.Ramping(),
), ),
"Gar1962" => UnitCommitment.Formulation( "Gar1962" => Formulation(
pwl_costs = UnitCommitment.Gar1962(), pwl_costs = Gar1962.PwlCosts(),
), ),
"KnuOstWat2018" => UnitCommitment.Formulation( "KnuOstWat2018" => Formulation(
pwl_costs = UnitCommitment.KnuOstWat2018(), pwl_costs = KnuOstWat2018.PwlCosts(),
), ),
"MorLatRam2013" => UnitCommitment.Formulation( "MorLatRam2013" => Formulation(
ramping = UnitCommitment.MorLatRam2013(), ramping = MorLatRam2013.Ramping(),
), ),
"PanGua2016" => UnitCommitment.Formulation( "PanGua2016" => Formulation(
ramping = UnitCommitment.PanGua2016(), ramping = PanGua2016.Ramping(),
), ),
) )
trials = [i for i in 1:5] trials = [i for i in 1:5]
@ -138,7 +149,7 @@ end
BLAS.set_num_threads(1) BLAS.set_num_threads(1)
UnitCommitment.optimize!( UnitCommitment.optimize!(
model, model,
UnitCommitment.XavQiuWanThi2019( XavQiuWanThi2019.Method(
time_limit = 3600.0, time_limit = 3600.0,
gap_limit = 1e-4, gap_limit = 1e-4,
), ),

@ -32,6 +32,7 @@ include("model/formulations/DamKucRajAta2016/ramp.jl")
include("model/formulations/Gar1962/pwlcosts.jl") include("model/formulations/Gar1962/pwlcosts.jl")
include("model/formulations/KnuOstWat2018/pwlcosts.jl") include("model/formulations/KnuOstWat2018/pwlcosts.jl")
include("model/formulations/MorLatRam2013/ramp.jl") include("model/formulations/MorLatRam2013/ramp.jl")
include("model/formulations/MorLatRam2013/scosts.jl")
include("model/formulations/PanGua2016/ramp.jl") include("model/formulations/PanGua2016/ramp.jl")
include("model/jumpext.jl") include("model/jumpext.jl")
include("solution/fix.jl") include("solution/fix.jl")

@ -5,7 +5,7 @@
function _add_ramp_eqs!( function _add_ramp_eqs!(
model::JuMP.Model, model::JuMP.Model,
g::Unit, g::Unit,
formulation::ArrCon2000, formulation::ArrCon2000.Ramping,
)::Nothing )::Nothing
# TODO: Move upper case constants to model[:instance] # TODO: Move upper case constants to model[:instance]
RESERVES_WHEN_START_UP = true RESERVES_WHEN_START_UP = true

@ -9,4 +9,10 @@ Formulation described in:
to an electricity spot market. IEEE Transactions on power systems, 15(3), to an electricity spot market. IEEE Transactions on power systems, 15(3),
1098-1104. 1098-1104.
""" """
struct ArrCon2000 <: RampingFormulation end module ArrCon2000
import ..RampingFormulation
struct Ramping <: RampingFormulation end
end

@ -5,7 +5,7 @@
function _add_production_piecewise_linear_eqs!( function _add_production_piecewise_linear_eqs!(
model::JuMP.Model, model::JuMP.Model,
g::Unit, g::Unit,
formulation::CarArr2006, formulation::CarArr2006.PwlCosts,
)::Nothing )::Nothing
eq_prod_above_def = _init(model, :eq_prod_above_def) eq_prod_above_def = _init(model, :eq_prod_above_def)
eq_segprod_limit = _init(model, :eq_segprod_limit) eq_segprod_limit = _init(model, :eq_segprod_limit)

@ -9,4 +9,10 @@ Formulation described in:
mixed-integer linear formulation for the thermal unit commitment problem. mixed-integer linear formulation for the thermal unit commitment problem.
IEEE Transactions on power systems, 21(3), 1371-1378. IEEE Transactions on power systems, 21(3), 1371-1378.
""" """
struct CarArr2006 <: PiecewiseLinearCostsFormulation end module CarArr2006
import ..PiecewiseLinearCostsFormulation
struct PwlCosts <: PiecewiseLinearCostsFormulation end
end

@ -5,7 +5,7 @@
function _add_ramp_eqs!( function _add_ramp_eqs!(
model::JuMP.Model, model::JuMP.Model,
g::Unit, g::Unit,
formulation::DamKucRajAta2016, formulation::DamKucRajAta2016.Ramping,
)::Nothing )::Nothing
# TODO: Move upper case constants to model[:instance] # TODO: Move upper case constants to model[:instance]
RESERVES_WHEN_START_UP = true RESERVES_WHEN_START_UP = true

@ -8,4 +8,10 @@ Formulation described in:
Damcı-Kurt, P., Küçükyavuz, S., Rajan, D., & Atamtürk, A. (2016). A polyhedral Damcı-Kurt, P., Küçükyavuz, S., Rajan, D., & Atamtürk, A. (2016). A polyhedral
study of production ramping. Mathematical Programming, 158(1), 175-205. study of production ramping. Mathematical Programming, 158(1), 175-205.
""" """
struct DamKucRajAta2016 <: RampingFormulation end module DamKucRajAta2016
import ..RampingFormulation
struct Ramping <: RampingFormulation end
end

@ -5,7 +5,7 @@
function _add_production_piecewise_linear_eqs!( function _add_production_piecewise_linear_eqs!(
model::JuMP.Model, model::JuMP.Model,
g::Unit, g::Unit,
formulation::Gar1962, formulation::Gar1962.PwlCosts,
)::Nothing )::Nothing
eq_prod_above_def = _init(model, :eq_prod_above_def) eq_prod_above_def = _init(model, :eq_prod_above_def)
eq_segprod_limit = _init(model, :eq_segprod_limit) eq_segprod_limit = _init(model, :eq_segprod_limit)

@ -10,4 +10,10 @@ Formulation described in:
of Electrical Engineers. Part III: Power Apparatus and Systems, 81(3), 730-734. of Electrical Engineers. Part III: Power Apparatus and Systems, 81(3), 730-734.
""" """
struct Gar1962 <: PiecewiseLinearCostsFormulation end module Gar1962
import ..PiecewiseLinearCostsFormulation
struct PwlCosts <: PiecewiseLinearCostsFormulation end
end

@ -5,7 +5,7 @@
function _add_production_piecewise_linear_eqs!( function _add_production_piecewise_linear_eqs!(
model::JuMP.Model, model::JuMP.Model,
g::Unit, g::Unit,
formulation::KnuOstWat2018, formulation::KnuOstWat2018.PwlCosts,
)::Nothing )::Nothing
eq_prod_above_def = _init(model, :eq_prod_above_def) eq_prod_above_def = _init(model, :eq_prod_above_def)
eq_segprod_limit_a = _init(model, :eq_segprod_limit_a) eq_segprod_limit_a = _init(model, :eq_segprod_limit_a)

@ -9,4 +9,10 @@ Formulation described in:
generators in unit commitment. IEEE Transactions on Power Systems, 33(4), generators in unit commitment. IEEE Transactions on Power Systems, 33(4),
4496-4507. 4496-4507.
""" """
struct KnuOstWat2018 <: PiecewiseLinearCostsFormulation end module KnuOstWat2018
import ..PiecewiseLinearCostsFormulation
struct PwlCosts <: PiecewiseLinearCostsFormulation end
end

@ -5,7 +5,7 @@
function _add_ramp_eqs!( function _add_ramp_eqs!(
model::JuMP.Model, model::JuMP.Model,
g::Unit, g::Unit,
formulation::MorLatRam2013, formulation::MorLatRam2013.Ramping,
)::Nothing )::Nothing
# TODO: Move upper case constants to model[:instance] # TODO: Move upper case constants to model[:instance]
RESERVES_WHEN_START_UP = true RESERVES_WHEN_START_UP = true

@ -0,0 +1,50 @@
# UnitCommitment.jl: Optimization Package for Security-Constrained Unit Commitment
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
function _add_startup_cost_eqs!(
model::JuMP.Model,
g::Unit,
formulation::MorLatRam2013.StartupCosts,
)::Nothing
eq_startup_choose = _init(model, :eq_startup_choose)
eq_startup_restrict = _init(model, :eq_startup_restrict)
S = length(g.startup_categories)
startup = model[:startup]
for t in 1:model[:instance].time
for s in 1:S
# If unit is switching on, we must choose a startup category
eq_startup_choose[g.name, t, s] = @constraint(
model,
model[:switch_on][g.name, t] ==
sum(startup[g.name, t, s] for s in 1:S)
)
# If unit has not switched off in the last `delay` time periods, startup category is forbidden.
# The last startup category is always allowed.
if s < S
range_start = t - g.startup_categories[s+1].delay + 1
range_end = t - g.startup_categories[s].delay
range = (range_start:range_end)
initial_sum = (
g.initial_status < 0 && (g.initial_status + 1 in range) ? 1.0 : 0.0
)
eq_startup_restrict[g.name, t, s] = @constraint(
model,
startup[g.name, t, s] <=
initial_sum + sum(
model[:switch_off][g.name, i] for i in range if i >= 1
)
)
end
# Objective function terms for start-up costs
add_to_expression!(
model[:obj],
startup[g.name, t, s],
g.startup_categories[s].cost,
)
end
end
return
end

@ -9,4 +9,12 @@ Formulation described in:
MILP formulation for the thermal unit commitment problem. IEEE Transactions MILP formulation for the thermal unit commitment problem. IEEE Transactions
on Power Systems, 28(4), 4897-4908. on Power Systems, 28(4), 4897-4908.
""" """
struct MorLatRam2013 <: RampingFormulation end module MorLatRam2013
import ..RampingFormulation
import ..StartupCostsFormulation
struct Ramping <: RampingFormulation end
struct StartupCosts <: StartupCostsFormulation end
end

@ -1,7 +1,7 @@
function _add_ramp_eqs!( function _add_ramp_eqs!(
model::JuMP.Model, model::JuMP.Model,
g::Unit, g::Unit,
formulation::PanGua2016, formulation::PanGua2016.Ramping,
)::Nothing )::Nothing
# TODO: Move upper case constants to model[:instance] # TODO: Move upper case constants to model[:instance]
RESERVES_WHEN_SHUT_DOWN = true RESERVES_WHEN_SHUT_DOWN = true

@ -8,4 +8,10 @@ Formulation described in:
Pan, K., & Guan, Y. (2016). Strong formulations for multistage stochastic Pan, K., & Guan, Y. (2016). Strong formulations for multistage stochastic
self-scheduling unit commitment. Operations Research, 64(6), 1482-1498. self-scheduling unit commitment. Operations Research, 64(6), 1482-1498.
""" """
struct PanGua2016 <: RampingFormulation end module PanGua2016
import ..RampingFormulation
struct Ramping <: RampingFormulation end
end

@ -5,18 +5,21 @@
abstract type TransmissionFormulation end abstract type TransmissionFormulation end
abstract type RampingFormulation end abstract type RampingFormulation end
abstract type PiecewiseLinearCostsFormulation end abstract type PiecewiseLinearCostsFormulation end
abstract type StartupCostsFormulation end
struct Formulation struct Formulation
pwl_costs::PiecewiseLinearCostsFormulation pwl_costs::PiecewiseLinearCostsFormulation
ramping::RampingFormulation ramping::RampingFormulation
startup_costs::StartupCostsFormulation
transmission::TransmissionFormulation transmission::TransmissionFormulation
function Formulation(; function Formulation(;
pwl_costs::PiecewiseLinearCostsFormulation = Gar1962(), pwl_costs::PiecewiseLinearCostsFormulation = Gar1962.PwlCosts(),
ramping::RampingFormulation = MorLatRam2013(), ramping::RampingFormulation = MorLatRam2013.Ramping(),
startup_costs::StartupCostsFormulation = MorLatRam2013.StartupCosts(),
transmission::TransmissionFormulation = ShiftFactorsFormulation(), transmission::TransmissionFormulation = ShiftFactorsFormulation(),
) )
return new(pwl_costs, ramping, transmission) return new(pwl_costs, ramping, startup_costs, transmission)
end end
end end

@ -22,7 +22,7 @@ function _add_unit!(model::JuMP.Model, g::Unit, f::Formulation)
_add_production_limit_eqs!(model, g) _add_production_limit_eqs!(model, g)
_add_production_piecewise_linear_eqs!(model, g, f.pwl_costs) _add_production_piecewise_linear_eqs!(model, g, f.pwl_costs)
_add_ramp_eqs!(model, g, f.ramping) _add_ramp_eqs!(model, g, f.ramping)
_add_startup_shutdown_costs_eqs!(model, g) _add_startup_cost_eqs!(model, g, f.startup_costs)
_add_startup_shutdown_limit_eqs!(model, g) _add_startup_shutdown_limit_eqs!(model, g)
_add_status_eqs!(model, g) _add_status_eqs!(model, g)
return return
@ -134,49 +134,6 @@ function _add_startup_shutdown_limit_eqs!(model::JuMP.Model, g::Unit)::Nothing
return return
end end
function _add_startup_shutdown_costs_eqs!(model::JuMP.Model, g::Unit)::Nothing
eq_startup_choose = _init(model, :eq_startup_choose)
eq_startup_restrict = _init(model, :eq_startup_restrict)
S = length(g.startup_categories)
startup = model[:startup]
for t in 1:model[:instance].time
for s in 1:S
# If unit is switching on, we must choose a startup category
eq_startup_choose[g.name, t, s] = @constraint(
model,
model[:switch_on][g.name, t] ==
sum(startup[g.name, t, s] for s in 1:S)
)
# If unit has not switched off in the last `delay` time periods, startup category is forbidden.
# The last startup category is always allowed.
if s < S
range_start = t - g.startup_categories[s+1].delay + 1
range_end = t - g.startup_categories[s].delay
range = (range_start:range_end)
initial_sum = (
g.initial_status < 0 && (g.initial_status + 1 in range) ? 1.0 : 0.0
)
eq_startup_restrict[g.name, t, s] = @constraint(
model,
startup[g.name, t, s] <=
initial_sum + sum(
model[:switch_off][g.name, i] for i in range if i >= 1
)
)
end
# Objective function terms for start-up costs
add_to_expression!(
model[:obj],
startup[g.name, t, s],
g.startup_categories[s].cost,
)
end
end
return
end
function _add_status_vars!(model::JuMP.Model, g::Unit)::Nothing function _add_status_vars!(model::JuMP.Model, g::Unit)::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)

@ -2,7 +2,7 @@
# 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.
function optimize!(model::JuMP.Model, method::XavQiuWanThi2019)::Nothing function optimize!(model::JuMP.Model, method::XavQiuWanThi2019.Method)::Nothing
function set_gap(gap) function set_gap(gap)
try try
JuMP.set_optimizer_attribute(model, "MIPGap", gap) JuMP.set_optimizer_attribute(model, "MIPGap", gap)

@ -2,10 +2,18 @@
# 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.
import DataStructures: PriorityQueue
""" """
struct XavQiuWanThi2019 <: SolutionMethod Lazy constraint solution method described in:
Xavier, A. S., Qiu, F., Wang, F., & Thimmapuram, P. R. (2019). Transmission
constraint filtering in large-scale security-constrained unit commitment.
IEEE Transactions on Power Systems, 34(3), 2457-2460.
"""
module XavQiuWanThi2019
import ..SolutionMethod
"""
struct Method
time_limit::Float64 time_limit::Float64
gap_limit::Float64 gap_limit::Float64
two_phase_gap::Bool two_phase_gap::Bool
@ -13,12 +21,6 @@ import DataStructures: PriorityQueue
max_violations_per_period::Int max_violations_per_period::Int
end end
Lazy constraint solution method described in:
Xavier, A. S., Qiu, F., Wang, F., & Thimmapuram, P. R. (2019). Transmission
constraint filtering in large-scale security-constrained unit commitment.
IEEE Transactions on Power Systems, 34(3), 2457-2460.
Fields Fields
------ ------
@ -37,14 +39,14 @@ Fields
formulation per time period. formulation per time period.
""" """
struct XavQiuWanThi2019 struct Method <: SolutionMethod
time_limit::Float64 time_limit::Float64
gap_limit::Float64 gap_limit::Float64
two_phase_gap::Bool two_phase_gap::Bool
max_violations_per_line::Int max_violations_per_line::Int
max_violations_per_period::Int max_violations_per_period::Int
function XavQiuWanThi2019(; function Method(;
time_limit::Float64 = 86400.0, time_limit::Float64 = 86400.0,
gap_limit::Float64 = 1e-3, gap_limit::Float64 = 1e-3,
two_phase_gap::Bool = true, two_phase_gap::Bool = true,
@ -60,6 +62,9 @@ struct XavQiuWanThi2019
) )
end end
end end
end
import DataStructures: PriorityQueue
struct _Violation struct _Violation
time::Int time::Int

@ -10,5 +10,5 @@ advanced methods to accelerate the solution process and to enforce transmission
and N-1 security constraints. and N-1 security constraints.
""" """
function optimize!(model::JuMP.Model)::Nothing function optimize!(model::JuMP.Model)::Nothing
return UnitCommitment.optimize!(model, XavQiuWanThi2019()) return UnitCommitment.optimize!(model, XavQiuWanThi2019.Method())
end end

@ -3,7 +3,15 @@
# Released under the modified BSD license. See COPYING.md for more details. # Released under the modified BSD license. See COPYING.md for more details.
using UnitCommitment using UnitCommitment
import UnitCommitment: Formulation import UnitCommitment:
ArrCon2000,
CarArr2006,
DamKucRajAta2016,
Formulation,
Gar1962,
KnuOstWat2018,
MorLatRam2013,
PanGua2016
function _test(formulation::Formulation)::Nothing function _test(formulation::Formulation)::Nothing
instance = UnitCommitment.read_benchmark("matpower/case118/2017-02-01") instance = UnitCommitment.read_benchmark("matpower/case118/2017-02-01")
@ -12,11 +20,16 @@ function _test(formulation::Formulation)::Nothing
end end
@testset "formulations" begin @testset "formulations" begin
_test(Formulation(ramping = UnitCommitment.ArrCon2000())) _test(Formulation(ramping = ArrCon2000.Ramping()))
_test(Formulation(ramping = UnitCommitment.DamKucRajAta2016())) _test(Formulation(ramping = DamKucRajAta2016.Ramping()))
_test(Formulation(ramping = UnitCommitment.MorLatRam2013())) _test(
_test(Formulation(ramping = UnitCommitment.PanGua2016())) Formulation(
_test(Formulation(pwl_costs = UnitCommitment.Gar1962())) ramping = MorLatRam2013.Ramping(),
_test(Formulation(pwl_costs = UnitCommitment.CarArr2006())) startup_costs = MorLatRam2013.StartupCosts(),
_test(Formulation(pwl_costs = UnitCommitment.KnuOstWat2018())) ),
)
_test(Formulation(ramping = PanGua2016.Ramping()))
_test(Formulation(pwl_costs = Gar1962.PwlCosts()))
_test(Formulation(pwl_costs = CarArr2006.PwlCosts()))
_test(Formulation(pwl_costs = KnuOstWat2018.PwlCosts()))
end end
Loading…
Cancel
Save