From 9ca3ae1e45c5ee2e4caa1a54451b4b2ffb74361d Mon Sep 17 00:00:00 2001 From: Aleksandr Kazachkov Date: Wed, 21 Jul 2021 14:52:58 -0400 Subject: [PATCH] Added comments on formulations, added start/stop constraints into MorLatRam and GenMorRam, added ability to add shortfall penalty. --- src/model/formulations/ArrCon2000/ramp.jl | 4 +- .../formulations/DamKucRajAta2016/ramp.jl | 2 +- src/model/formulations/Gar1962/status.jl | 39 +++++++++++++++++++ src/model/formulations/base/structs.jl | 20 +++++----- src/model/formulations/base/unit.jl | 10 ++--- 5 files changed, 57 insertions(+), 18 deletions(-) diff --git a/src/model/formulations/ArrCon2000/ramp.jl b/src/model/formulations/ArrCon2000/ramp.jl index b725892..9f1979c 100644 --- a/src/model/formulations/ArrCon2000/ramp.jl +++ b/src/model/formulations/ArrCon2000/ramp.jl @@ -45,7 +45,7 @@ function _add_ramp_eqs!( for t in 1:model[:instance].time # Ramp up limit if t == 1 - if is_initially_on + if _is_initially_on(g) # min power is _not_ multiplied by is_on because if !is_on, then ramp up is irrelevant eq_ramp_up[gn, t] = @constraint( model, @@ -76,7 +76,7 @@ function _add_ramp_eqs!( # Ramp down limit if t == 1 - if is_initially_on + if _is_initially_on(g) # TODO If RD < SD, or more specifically if # min_power + RD < initial_power < SD # then the generator should be able to shut down at time t = 1, diff --git a/src/model/formulations/DamKucRajAta2016/ramp.jl b/src/model/formulations/DamKucRajAta2016/ramp.jl index 497958c..0d2b8ee 100644 --- a/src/model/formulations/DamKucRajAta2016/ramp.jl +++ b/src/model/formulations/DamKucRajAta2016/ramp.jl @@ -35,7 +35,7 @@ function _add_ramp_eqs!( RESERVES_WHEN_RAMP_DOWN = true RESERVES_WHEN_SHUT_DOWN = true is_initially_on = _is_initially_on(g) - + # The following are the same for generator g across all time periods SU = g.startup_limit # startup rate SD = g.shutdown_limit # shutdown rate diff --git a/src/model/formulations/Gar1962/status.jl b/src/model/formulations/Gar1962/status.jl index be387c2..7228e42 100644 --- a/src/model/formulations/Gar1962/status.jl +++ b/src/model/formulations/Gar1962/status.jl @@ -12,6 +12,7 @@ function _add_status_vars!( model::JuMP.Model, g::Unit, formulation_status_vars::Gar1962.StatusVars, + ALWAYS_CREATE_VARS = false )::Nothing is_on = _init(model, :is_on) switch_on = _init(model, :switch_on) @@ -63,6 +64,44 @@ function _add_status_vars!( end end end + + if ALWAYS_CREATE_VARS + # If variables are created, use initial conditions to fix some values + if t == 1 + if _is_initially_on(g) + # Generator was on (for g.initial_status time periods), + # so cannot be more switched on until the period after the first time it can be turned off + fix(switch_on[g.name, 1], 0.0; force = true) + else + # Generator is initially off (for -g.initial_status time periods) + # Cannot be switched off more + fix(switch_off[g.name, 1], 0.0; force = true) + end + end + + if g.must_run[t] + # 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 + # 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(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) + end + else + # If vars are not created, then replace them by a constant + if t == 1 + if _is_initially_on(g) + switch_on[g.name, t] = 0.0 + else + switch_off[g.name, t] = 0.0 + end + end + if g.must_run[t] + is_on[g.name, t] = 1.0 + switch_on[g.name, t] = (t == 1 ? 1.0 - _is_initially_on(g) : 0.0) + switch_off[g.name, t] = 0.0 + end + end # check if ALWAYS_CREATE_VARS end return end diff --git a/src/model/formulations/base/structs.jl b/src/model/formulations/base/structs.jl index 32149d8..2887512 100644 --- a/src/model/formulations/base/structs.jl +++ b/src/model/formulations/base/structs.jl @@ -2,10 +2,10 @@ # Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. # Released under the modified BSD license. See COPYING.md for more details. -abstract type TransmissionFormulation end -abstract type RampingFormulation end +abstract type AbstractTransmissionFormulation end +abstract type AbstractRampingFormulation end abstract type PiecewiseLinearCostsFormulation end -abstract type StartupCostsFormulation end +abstract type AbstractStartupCostsFormulation end abstract type StatusVarsFormulation end abstract type ProductionVarsFormulation end @@ -21,18 +21,18 @@ Some of these components are allowed to be empty, as long as overall validity of struct Formulation prod_vars::ProductionVarsFormulation pwl_costs::PiecewiseLinearCostsFormulation - ramping::RampingFormulation - startup_costs::StartupCostsFormulation + ramping::AbstractRampingFormulation + startup_costs::AbstractStartupCostsFormulation status_vars::StatusVarsFormulation - transmission::TransmissionFormulation + transmission::AbstractTransmissionFormulation function Formulation(; prod_vars::ProductionVarsFormulation = Gar1962.ProdVars(), pwl_costs::PiecewiseLinearCostsFormulation = KnuOstWat2018.PwlCosts(), - ramping::RampingFormulation = MorLatRam2013.Ramping(), - startup_costs::StartupCostsFormulation = MorLatRam2013.StartupCosts(), + ramping::AbstractRampingFormulation = MorLatRam2013.Ramping(), + startup_costs::AbstractStartupCostsFormulation = MorLatRam2013.StartupCosts(), status_vars::StatusVarsFormulation = Gar1962.StatusVars(), - transmission::TransmissionFormulation = ShiftFactorsFormulation(), + transmission::AbstractTransmissionFormulation = ShiftFactorsFormulation(), ) return new( prod_vars, @@ -70,7 +70,7 @@ Arguments the cutoff that should be applied to the LODF matrix. Entries with magnitude smaller than this value will be set to zero. """ -struct ShiftFactorsFormulation <: TransmissionFormulation +struct ShiftFactorsFormulation <: AbstractTransmissionFormulation isf_cutoff::Float64 lodf_cutoff::Float64 precomputed_isf::Union{Nothing,Matrix{Float64}} diff --git a/src/model/formulations/base/unit.jl b/src/model/formulations/base/unit.jl index ebdee00..ca54f62 100644 --- a/src/model/formulations/base/unit.jl +++ b/src/model/formulations/base/unit.jl @@ -192,7 +192,7 @@ end function _add_ramp_eqs!( model::JuMP.Model, g::Unit, - formulation::RampingFormulation, + formulation::AbstractRampingFormulation, )::Nothing prod_above = model[:prod_above] reserve = model[:reserve] @@ -265,16 +265,16 @@ function _add_min_uptime_downtime_eqs!(model::JuMP.Model, g::Unit)::Nothing # Equation (4) in Knueven et al. (2020) eq_min_uptime[g.name, t] = @constraint( model, - sum(switch_on[g.name, i] for i in (t-g.min_uptime+1):t if i >= 1) <= is_on[g.name, t] + sum(switch_on[g.name, i] for i in (t-g.min_uptime+1):t if i >= 1) + <= is_on[g.name, t] ) # Minimum down-time # Equation (5) in Knueven et al. (2020) eq_min_downtime[g.name, t] = @constraint( model, - sum( - switch_off[g.name, i] for i in (t-g.min_downtime+1):t if i >= 1 - ) <= 1 - is_on[g.name, t] + sum(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