diff --git a/src/UnitCommitment.jl b/src/UnitCommitment.jl index e908b1f..3a7c529 100644 --- a/src/UnitCommitment.jl +++ b/src/UnitCommitment.jl @@ -13,6 +13,7 @@ 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("model/formulations/CarArr06/structs.jl") include("solution/methods/XavQiuWanThi19/structs.jl") include("import/egret.jl") @@ -25,6 +26,7 @@ include("model/formulations/base/psload.jl") include("model/formulations/base/sensitivity.jl") include("model/formulations/base/system.jl") include("model/formulations/base/unit.jl") +include("model/formulations/CarArr06/pwlcosts.jl") include("model/formulations/DamKucRajAta16/ramp.jl") include("model/formulations/Gar62/pwlcosts.jl") include("model/formulations/MorLatRam13/ramp.jl") diff --git a/src/model/formulations/CarArr06/pwlcosts.jl b/src/model/formulations/CarArr06/pwlcosts.jl new file mode 100644 index 0000000..de7d6c7 --- /dev/null +++ b/src/model/formulations/CarArr06/pwlcosts.jl @@ -0,0 +1,46 @@ +function _add_production_piecewise_linear_eqs!( + model::JuMP.Model, + g::Unit, + formulation::CarArr06, +)::Nothing + eq_prod_above_def = _init(model, :eq_prod_above_def) + eq_segprod_limit = _init(model, :eq_segprod_limit) + prod_above = model[:prod_above] + segprod = model[:segprod] + gn = g.name + K = length(g.cost_segments) + for t in 1:model[:instance].time + gn = g.name + for k in 1:K + # Equation (45) in Kneuven et al. (2020) + # 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] + ) + + # 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]) + + # 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) + ) + + # 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 +end diff --git a/src/model/formulations/CarArr06/structs.jl b/src/model/formulations/CarArr06/structs.jl new file mode 100644 index 0000000..c4c0fe1 --- /dev/null +++ b/src/model/formulations/CarArr06/structs.jl @@ -0,0 +1,12 @@ +# 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: + + Carrión, M., & Arroyo, J. M. (2006). A computationally efficient + mixed-integer linear formulation for the thermal unit commitment problem. + IEEE Transactions on power systems, 21(3), 1371-1378. +""" +struct CarArr06 <: PiecewiseLinearCostsFormulation end diff --git a/test/model/formulations_test.jl b/test/model/formulations_test.jl index 52dc75c..1649dc6 100644 --- a/test/model/formulations_test.jl +++ b/test/model/formulations_test.jl @@ -15,4 +15,6 @@ end _test(UnitCommitment.Formulation(ramping = UnitCommitment.DamKucRajAta16())) _test(UnitCommitment.Formulation(ramping = UnitCommitment.MorLatRam13())) _test(UnitCommitment.Formulation(ramping = UnitCommitment.PanGua16())) + _test(UnitCommitment.Formulation(pwl_costs = UnitCommitment.Gar62())) + _test(UnitCommitment.Formulation(pwl_costs = UnitCommitment.CarArr06())) end