From a3d0f2c65cc41f1e6ff18b78e942917237910c8e Mon Sep 17 00:00:00 2001 From: Alinson S Xavier Date: Tue, 1 Jun 2021 11:29:08 -0500 Subject: [PATCH] Split Gar62 into separate formulation; add PiecewiseLinearCostsFormulation --- src/UnitCommitment.jl | 5 ++- src/model/formulations/Gar62/pwlcosts.jl | 52 ++++++++++++++++++++++++ src/model/formulations/Gar62/structs.jl | 13 ++++++ src/model/formulations/base/structs.jl | 5 ++- src/model/formulations/base/unit.jl | 50 +---------------------- 5 files changed, 74 insertions(+), 51 deletions(-) create mode 100644 src/model/formulations/Gar62/pwlcosts.jl create mode 100644 src/model/formulations/Gar62/structs.jl diff --git a/src/UnitCommitment.jl b/src/UnitCommitment.jl index 8d42c7a..e908b1f 100644 --- a/src/UnitCommitment.jl +++ b/src/UnitCommitment.jl @@ -6,11 +6,13 @@ module UnitCommitment include("instance/structs.jl") include("model/formulations/base/structs.jl") +include("solution/structs.jl") + include("model/formulations/ArrCon00/structs.jl") include("model/formulations/DamKucRajAta16/structs.jl") +include("model/formulations/Gar62/structs.jl") include("model/formulations/MorLatRam13/structs.jl") include("model/formulations/PanGua16/structs.jl") -include("solution/structs.jl") include("solution/methods/XavQiuWanThi19/structs.jl") include("import/egret.jl") @@ -24,6 +26,7 @@ include("model/formulations/base/sensitivity.jl") include("model/formulations/base/system.jl") include("model/formulations/base/unit.jl") include("model/formulations/DamKucRajAta16/ramp.jl") +include("model/formulations/Gar62/pwlcosts.jl") include("model/formulations/MorLatRam13/ramp.jl") include("model/formulations/PanGua16/ramp.jl") include("model/jumpext.jl") diff --git a/src/model/formulations/Gar62/pwlcosts.jl b/src/model/formulations/Gar62/pwlcosts.jl new file mode 100644 index 0000000..adc823e --- /dev/null +++ b/src/model/formulations/Gar62/pwlcosts.jl @@ -0,0 +1,52 @@ +# 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_production_piecewise_linear_eqs!( + model::JuMP.Model, + g::Unit, + formulation::Gar62, +)::Nothing + eq_prod_above_def = _init(model, :eq_prod_above_def) + eq_segprod_limit = _init(model, :eq_segprod_limit) + is_on = model[:is_on] + prod_above = model[:prod_above] + segprod = model[:segprod] + gn = g.name + K = length(g.cost_segments) + for t in 1:model[:instance].time + # Definition of production + # Equation (43) in Kneuven et al. (2020) + eq_prod_above_def[gn, t] = @constraint( + model, + prod_above[gn, t] == sum(segprod[gn, t, k] for k in 1:K) + ) + + for k in 1:K + # Equation (42) in Kneuven et al. (2020) + # Without this, solvers will add a lot of implied bound cuts to + # have this same effect. + # NB: when reading instance, UnitCommitment.jl already calculates + # difference between max power for segments k and k-1 so the + # value of cost_segments[k].mw[t] is the max production *for + # that segment* + eq_segprod_limit[gn, t, k] = @constraint( + model, + segprod[gn, t, k] <= g.cost_segments[k].mw[t] * is_on[gn, t] + ) + + # Also add this as an explicit upper bound on segprod to make the + # solver's work a bit easier + set_upper_bound(segprod[gn, t, k], g.cost_segments[k].mw[t]) + + # Objective function + # Equation (44) in Kneuven et al. (2020) + add_to_expression!( + model[:obj], + segprod[gn, t, k], + g.cost_segments[k].cost[t], + ) + end + end + return +end diff --git a/src/model/formulations/Gar62/structs.jl b/src/model/formulations/Gar62/structs.jl new file mode 100644 index 0000000..8abc979 --- /dev/null +++ b/src/model/formulations/Gar62/structs.jl @@ -0,0 +1,13 @@ +# 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. + +""" +Formulation described in: + + Garver, L. L. (1962). Power generation scheduling by integer + programming-development of theory. Transactions of the American Institute + of Electrical Engineers. Part III: Power Apparatus and Systems, 81(3), 730-734. + +""" +struct Gar62 <: PiecewiseLinearCostsFormulation end diff --git a/src/model/formulations/base/structs.jl b/src/model/formulations/base/structs.jl index 3d92c69..a21ac0d 100644 --- a/src/model/formulations/base/structs.jl +++ b/src/model/formulations/base/structs.jl @@ -4,16 +4,19 @@ abstract type TransmissionFormulation end abstract type RampingFormulation end +abstract type PiecewiseLinearCostsFormulation end struct Formulation + pwl_costs::PiecewiseLinearCostsFormulation ramping::RampingFormulation transmission::TransmissionFormulation function Formulation(; + pwl_costs::PiecewiseLinearCostsFormulation = Gar62(), ramping::RampingFormulation = MorLatRam13(), transmission::TransmissionFormulation = ShiftFactorsFormulation(), ) - return new(ramping, transmission) + return new(pwl_costs, ramping, transmission) end end diff --git a/src/model/formulations/base/unit.jl b/src/model/formulations/base/unit.jl index a7b5d1d..ed4fa47 100644 --- a/src/model/formulations/base/unit.jl +++ b/src/model/formulations/base/unit.jl @@ -20,7 +20,7 @@ function _add_unit!(model::JuMP.Model, g::Unit, f::Formulation) _add_min_uptime_downtime_eqs!(model, g) _add_net_injection_eqs!(model, g) _add_production_limit_eqs!(model, g) - _add_production_piecewise_linear_eqs!(model, g) + _add_production_piecewise_linear_eqs!(model, g, f.pwl_costs) _add_ramp_eqs!(model, g, f.ramping) _add_startup_shutdown_costs_eqs!(model, g) _add_startup_shutdown_limit_eqs!(model, g) @@ -69,54 +69,6 @@ function _add_production_limit_eqs!(model::JuMP.Model, g::Unit)::Nothing end end -function _add_production_piecewise_linear_eqs!( - model::JuMP.Model, - g::Unit, -)::Nothing - eq_prod_above_def = _init(model, :eq_prod_above_def) - eq_segprod_limit = _init(model, :eq_segprod_limit) - is_on = model[:is_on] - prod_above = model[:prod_above] - segprod = model[:segprod] - gn = g.name - K = length(g.cost_segments) - for t in 1:model[:instance].time - # Definition of production - # Equation (43) in Kneuven et al. (2020) - eq_prod_above_def[gn, t] = @constraint( - model, - prod_above[gn, t] == sum(segprod[gn, t, k] for k in 1:K) - ) - - for k in 1:K - # Equation (42) in Kneuven et al. (2020) - # Without this, solvers will add a lot of implied bound cuts to - # have this same effect. - # NB: when reading instance, UnitCommitment.jl already calculates - # difference between max power for segments k and k-1 so the - # value of cost_segments[k].mw[t] is the max production *for - # that segment* - eq_segprod_limit[gn, t, k] = @constraint( - model, - segprod[gn, t, k] <= g.cost_segments[k].mw[t] * is_on[gn, t] - ) - - # Also add this as an explicit upper bound on segprod to make the - # solver's work a bit easier - set_upper_bound(segprod[gn, t, k], g.cost_segments[k].mw[t]) - - # Objective function - # Equation (44) in Kneuven et al. (2020) - add_to_expression!( - model[:obj], - segprod[gn, t, k], - g.cost_segments[k].cost[t], - ) - end - end - return -end - function _add_reserve_vars!(model::JuMP.Model, g::Unit)::Nothing reserve = _init(model, :reserve) for t in 1:model[:instance].time