From 5ad8aaa996b8c07ce7e5c6ac0590a1d6ad84c79f Mon Sep 17 00:00:00 2001 From: lbonaldo Date: Fri, 15 Mar 2024 13:20:41 -0400 Subject: [PATCH] Track only cap that contribute to Min_Ret. Small cleanup. --- src/load_inputs/load_resources_data.jl | 6 +-- src/model/resources/resources.jl | 55 ++++++++++++++++++---- src/model/resources/retrofits/retrofits.jl | 14 ++++-- src/multi_stage/endogenous_retirement.jl | 34 ++++++++----- 4 files changed, 79 insertions(+), 30 deletions(-) diff --git a/src/load_inputs/load_resources_data.jl b/src/load_inputs/load_resources_data.jl index 8ef269b095..e2f5236f13 100644 --- a/src/load_inputs/load_resources_data.jl +++ b/src/load_inputs/load_resources_data.jl @@ -591,7 +591,7 @@ end """ add_attributes_to_resource!(resource::AbstractResource, new_symbols::Vector{Symbol}, new_values::T) where T <: DataFrameRow -Adds a set of new attributes (names and corresponding values) to a resource if their values are different from zero. The resource is modified in-place. +Adds a set of new attributes (names and corresponding values) to a resource. The resource is modified in-place. # Arguments - `resource::AbstractResource`: The resource to add attributes to. @@ -602,8 +602,8 @@ Adds a set of new attributes (names and corresponding values) to a resource if t function add_attributes_to_resource!(resource::AbstractResource, new_symbols::Vector{Symbol}, new_values::T) where T <: DataFrameRow # loop over new attributes for (sym, value) in zip(new_symbols, new_values) - # add attribute to resource if value is not zero - value ≠ 0 && setproperty!(resource, sym, value) + # add attribute to resource + setproperty!(resource, sym, value) end return nothing end diff --git a/src/model/resources/resources.jl b/src/model/resources/resources.jl index 4238a2bd51..e65eb7430d 100644 --- a/src/model/resources/resources.jl +++ b/src/model/resources/resources.jl @@ -548,7 +548,7 @@ end function can_contribute_min_retirement(r::AbstractResource) validate_boolean_attribute(r, :min_retirement) - return Bool(get(r, :min_retirement, false)) + return Bool(get(r, :min_retirement, true)) end const default_minmax_cap = -1. @@ -681,6 +681,7 @@ maintenance_cycle_length_years(r::AbstractResource) = get(r, :maintenance_cycle_ maintenance_begin_cadence(r::AbstractResource) = get(r, :maintenance_begin_cadence, default_zero) ids_contribute_min_retirement(rs::Vector{T}) where T <: AbstractResource = findall(r -> can_contribute_min_retirement(r) == true, rs) +ids_not_contribute_min_retirement(rs::Vector{T}) where T <: AbstractResource = findall(r -> can_contribute_min_retirement(r) == false, rs) # STORAGE interface """ @@ -931,19 +932,19 @@ function resources_in_zone_by_rid(rs::Vector{<:AbstractResource}, zone::Int) end @doc raw""" - resources_in_retrofit_pool_by_rid(rs::Vector{<:AbstractResource}, pool_id::String) + resources_in_retrofit_cluster_by_rid(rs::Vector{<:AbstractResource}, cluster_id::String) -Find R_ID's of resources with retrofit pool id `pool_id`. +Find R_ID's of resources with retrofit cluster id `cluster_id`. # Arguments - `rs::Vector{<:AbstractResource}`: The vector of resources. -- `pool_id::String`: The retrofit pool id. +- `cluster_id::String`: The retrofit cluster id. # Returns -- `Vector{Int64}`: The vector of resource ids in the retrofit pool. +- `Vector{Int64}`: The vector of resource ids in the retrofit cluster. """ -function resources_in_retrofit_pool_by_rid(rs::Vector{<:AbstractResource}, pool_id::String) - return resource_id.(rs[retrofit_id.(rs) .== pool_id]) +function resources_in_retrofit_cluster_by_rid(rs::Vector{<:AbstractResource}, cluster_id::String) + return resource_id.(rs[retrofit_id.(rs) .== cluster_id]) end """ @@ -978,6 +979,42 @@ function validate_boolean_attribute(r::AbstractResource, attr::Symbol) attr_value = get(r, attr, 0) if attr_value != 0 && attr_value != 1 error("Attribute $attr in resource $(resource_name(r)) must be boolean. - The only valid values are 0 or 1.") + The only valid values are {0,1}, not $attr_value.") end -end \ No newline at end of file +end + +""" + ids_with_not_all_options_contributing(rs::Vector{T}) where T <: AbstractResource + +Find the resource ids of the retrofit units in the vector `rs` where not all retrofit options contribute to min retirement. + +# Arguments +- `rs::Vector{T}`: The vector of resources. + +# Returns +- `Vector{Int64}`: The vector of resource ids. +""" +function ids_with_not_all_options_contributing(rs::Vector{T}) where T <: AbstractResource + # select resources that can retrofit + units_can_retrofit = ids_can_retrofit(rs) + # check if all retrofit options in the retrofit cluster of each retrofit resource contribute to min retirement + condition::Vector{Bool} = hasnot_all_options_contributing.(rs[units_can_retrofit], Ref(rs)) + return units_can_retrofit[condition] +end + +""" + hasnot_all_options_contributing(retrofit_res::AbstractResource, rs::Vector{T}) where T <: AbstractResource + +Check if all retrofit options in the retrofit cluster of the retrofit resource `retrofit_res` contribute to min retirement. + +# Arguments +- `retrofit_res::AbstractResource`: The retrofit resource. +- `rs::Vector{T}`: The vector of resources. + +# Returns +- `Bool`: True if not all retrofit options contribute to min retirement, otherwise false. +""" +function hasnot_all_options_contributing(retrofit_res::AbstractResource, rs::Vector{T}) where T <: AbstractResource + retro_id = retrofit_id(retrofit_res) + return !isempty(intersect(resources_in_retrofit_cluster_by_rid(rs, retro_id), ids_retrofit_options(rs), ids_not_contribute_min_retirement(rs))) +end \ No newline at end of file diff --git a/src/model/resources/retrofits/retrofits.jl b/src/model/resources/retrofits/retrofits.jl index 389cade130..856206d6ce 100644 --- a/src/model/resources/retrofits/retrofits.jl +++ b/src/model/resources/retrofits/retrofits.jl @@ -31,11 +31,15 @@ function retrofit(EP::Model, inputs::Dict) RETROFIT_OPTIONS = inputs["RETROFIT_OPTIONS"] # Set of all resources being created RETROFIT_IDS = inputs["RETROFIT_IDS"] # Set of unique IDs for retrofit resources - @constraint(EP, cRetrofitCapacity[id in RETROFIT_IDS], - sum(cap_size(gen[y]) * EP[:vRETROFITCAP][y] for y in intersect(RETROFIT_CAP, COMMIT, resources_in_retrofit_pool_by_rid(gen,id)); init=0) - + sum(EP[:vRETROFITCAP][y] for y in setdiff(intersect(RETROFIT_CAP, resources_in_retrofit_pool_by_rid(gen,id)), COMMIT); init=0) - == sum(cap_size(gen[y]) * EP[:vCAP][y] * (1/retrofit_efficiency(gen[y])) for y in intersect(RETROFIT_OPTIONS, COMMIT, resources_in_retrofit_pool_by_rid(gen,id)); init=0) - + sum(EP[:vCAP][y] * (1/retrofit_efficiency(gen[y])) for y in setdiff(intersect(RETROFIT_OPTIONS, resources_in_retrofit_pool_by_rid(gen,id)), COMMIT); init=0)) + @expression(EP,eRetrofittedCapByRetroId[id in RETROFIT_IDS], + sum(cap_size(gen[y]) * EP[:vRETROFITCAP][y] for y in intersect(RETROFIT_CAP, COMMIT, resources_in_retrofit_cluster_by_rid(gen,id)); init=0) + + sum(EP[:vRETROFITCAP][y] for y in setdiff(intersect(RETROFIT_CAP, resources_in_retrofit_cluster_by_rid(gen,id)), COMMIT); init=0)) + + @expression(EP,eRetrofitCapByRetroId[id in RETROFIT_IDS], + sum(cap_size(gen[y]) * EP[:vCAP][y] * (1/retrofit_efficiency(gen[y])) for y in intersect(RETROFIT_OPTIONS, COMMIT, resources_in_retrofit_cluster_by_rid(gen,id)); init=0) + + sum(EP[:vCAP][y] * (1/retrofit_efficiency(gen[y])) for y in setdiff(intersect(RETROFIT_OPTIONS, resources_in_retrofit_cluster_by_rid(gen,id)), COMMIT); init=0)) + + @constraint(EP, cRetrofitCapacity[id in RETROFIT_IDS], eRetrofittedCapByRetroId[id] == eRetrofitCapByRetroId[id]) return EP end diff --git a/src/multi_stage/endogenous_retirement.jl b/src/multi_stage/endogenous_retirement.jl index bfc6533243..a98e6deb50 100644 --- a/src/multi_stage/endogenous_retirement.jl +++ b/src/multi_stage/endogenous_retirement.jl @@ -129,7 +129,7 @@ function endogenous_retirement_discharge!(EP::Model, inputs::Dict, num_stages::I NEW_CAP = inputs["NEW_CAP"] # Set of all resources eligible for new capacity RET_CAP = inputs["RET_CAP"] # Set of all resources eligible for capacity retirements - RETROFIT_CAP = inputs["RETROFIT_CAP"] # Set of all resources being retrofitted + RETROFIT_OPTIONS = inputs["RETROFIT_OPTIONS"] # Set of all resources being retrofitted COMMIT = inputs["COMMIT"] # Set of all resources eligible for unit commitment ### Variables ### @@ -148,11 +148,14 @@ function endogenous_retirement_discharge!(EP::Model, inputs::Dict, num_stages::I end ) + @expression(EP, eRetroFittedCap[y in RET_CAP], sum(cap_size(gen[g]) * EP[:vCAP][g] * (1/retrofit_efficiency(gen[g])) for g in intersect(RETROFIT_OPTIONS, COMMIT, resources_in_retrofit_cluster_by_rid(gen,retrofit_id(gen[y])), ids_contribute_min_retirement(gen[RETROFIT_OPTIONS])); init=0) + + sum(EP[:vCAP][g] * (1/retrofit_efficiency(gen[g])) for g in setdiff(intersect(RETROFIT_OPTIONS, resources_in_retrofit_cluster_by_rid(gen,retrofit_id(gen[y])), ids_contribute_min_retirement(gen[RETROFIT_OPTIONS])), COMMIT,); init=0)) + @expression(EP, eRetCap[y in RET_CAP], - if y in ids_contribute_min_retirement(gen[RETROFIT_CAP]) - EP[:vRETCAP][y] + EP[:vRETROFITCAP][y] + if y in COMMIT + EP[:vRETCAP][y] + (1/cap_size(gen[y]))*eRetroFittedCap[y] else - EP[:vRETCAP][y] + EP[:vRETCAP][y] + eRetroFittedCap[y] end ) @@ -180,17 +183,22 @@ function endogenous_retirement_discharge!(EP::Model, inputs::Dict, num_stages::I # The RHS of this constraint will be updated in the forward pass @constraint(EP, cRetCapTrack[y in RET_CAP,p=1:(cur_stage-1)], vRETCAPTRACK[y,p] == 0) - @variable(EP,vslack_lifetime[y in RET_CAP]>=0) - - EP[:eObj]+= 2*maximum(inv_cost_per_mwyr.(gen))*sum(vslack_lifetime); - - fix.(EP[:vslack_lifetime][setdiff(RET_CAP,setdiff(RETROFIT_CAP,ids_contribute_min_retirement(gen[RETROFIT_CAP])))],0.0,force=true) - - @constraint(EP, cLifetimeRet[y in RET_CAP], eNewCapTrack[y] + eMinRetCapTrack[y] <= eRetCapTrack[y] + vslack_lifetime[y]) + RETROFIT_WITH_SLACK = ids_with_not_all_options_contributing(gen) + if !isempty(RETROFIT_WITH_SLACK) + @variable(EP, vslack_lifetime[y in RETROFIT_WITH_SLACK] >=0) + @expression(EP, vslack_term, 2*maximum(inv_cost_per_mwyr.(gen))*sum(vslack_lifetime[y] for y in RETROFIT_WITH_SLACK; init=0)) + add_to_expression!(EP[:eObj], vslack_term) + end - #@constraint(EP, cLifetimeRet[y in RET_CAP], eNewCapTrack[y] + eMinRetCapTrack[y] <= eRetCapTrack[y]) - + @expression(EP,eLifetimeRetRHS[y in RET_CAP], + if y in RETROFIT_WITH_SLACK + eRetCapTrack[y] + vslack_lifetime[y] + else + eRetCapTrack[y] + end + ) + @constraint(EP, cLifetimeRet[y in RET_CAP], eNewCapTrack[y] + eMinRetCapTrack[y] <= eLifetimeRetRHS[y]) end function endogenous_retirement_charge!(EP::Model, inputs::Dict, num_stages::Int, cur_stage::Int, stage_lens::Array{Int, 1})