From f2ff6e6156091cdf1216074a372e8e66e92b27bd Mon Sep 17 00:00:00 2001 From: Jacob Schwartz Date: Wed, 29 Nov 2023 12:57:12 -0500 Subject: [PATCH 1/5] start functionizing thermal plants crm --- .../write_capacity_value.jl | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/write_outputs/capacity_reserve_margin/write_capacity_value.jl b/src/write_outputs/capacity_reserve_margin/write_capacity_value.jl index 1acae4ee7f..b2841c6e36 100644 --- a/src/write_outputs/capacity_reserve_margin/write_capacity_value.jl +++ b/src/write_outputs/capacity_reserve_margin/write_capacity_value.jl @@ -121,3 +121,23 @@ function capacity_reserve_margin_price(EP::Model, inputs::Dict, setup::Dict, cap scale_factor = setup["ParameterScale"] == 1 ? ModelScalingFactor : 1 return dual.(EP[:cCapacityResMargin][capres_zone, :]) ./ ω * scale_factor end + +function thermal_plant_effective_capacity(EP::Model, inputs::Dict, r_id::Int, capres_zone::Int)::Vector{Float} + y = r_id + T = inputs["T"] + dfGen = inputs["dfGen"] + capresfactor(y, capres) = dfGen[y, Symbol("CapRes_$capres")] + eTotalCap = value.(EP[:eTotalCap][y]) + + effective_capacity = capresfactor(y, capres_zone) * eTotalCap * ones(T) + + if has_maintenance(inputs) + resource_component(y) = dfGen[y, :Resource] + cap_size = dfGen[y, :Cap_Size] + down_var(y) = EP[Symbol(maintenance_down_name(resource_component(y)))] + vDOWN = value.(down_var(y)) + effective_capacity -= capresfactor(y, capres) * vDOWN * cap_size + end + + return effective_capacity +end From 6a4f821a826a9f8328648d8674eabd618c17e8c4 Mon Sep 17 00:00:00 2001 From: Jacob Schwartz Date: Thu, 30 Nov 2023 15:01:29 -0500 Subject: [PATCH 2/5] Use thermal_effective_capacity function --- .../effective_capacity.jl | 38 +++++++++++++++++++ .../write_capacity_value.jl | 21 +--------- .../write_reserve_margin_revenue.jl | 8 ++-- 3 files changed, 43 insertions(+), 24 deletions(-) create mode 100644 src/write_outputs/capacity_reserve_margin/effective_capacity.jl diff --git a/src/write_outputs/capacity_reserve_margin/effective_capacity.jl b/src/write_outputs/capacity_reserve_margin/effective_capacity.jl new file mode 100644 index 0000000000..4b1b27450a --- /dev/null +++ b/src/write_outputs/capacity_reserve_margin/effective_capacity.jl @@ -0,0 +1,38 @@ +@doc raw""" + thermal_plant_effective_capacity(EP::Model, + inputs::Dict, + resources::Vector{Int}, + capres_zone::Int, + timesteps::Vector{Int})::Matrix{Float64} + + Effective capacity in a capacity reserve margin zone for certain resources in the given timesteps. +""" +function thermal_plant_effective_capacity(EP, inputs, resources::Vector{Int}, capres_zone::Int, timesteps::Vector{Int})::Matrix{Float64} + eff_cap = thermal_plant_effective_capacity.(Ref(EP), Ref(inputs), resources, Ref(capres_zone), Ref(timesteps)) + return reduce(hcat, eff_cap) +end + +function thermal_plant_effective_capacity(EP::Model, inputs::Dict, y, capres_zone::Int) + T = inputs["T"] + timesteps = collect(1:T) + return thermal_plant_effective_capacity(EP, inputs, y, capres_zone, timesteps) +end + +function thermal_plant_effective_capacity(EP::Model, inputs::Dict, r_id::Int, capres_zone::Int, timesteps::Vector{Int})::Vector{Float64} + y = r_id + dfGen = inputs["dfGen"] + capresfactor(y, capres) = dfGen[y, Symbol("CapRes_$capres")] + eTotalCap = value.(EP[:eTotalCap][y]) + + effective_capacity = capresfactor(y, capres_zone) * eTotalCap * one.(timesteps) + + if has_maintenance(inputs) && y in resources_with_maintenance(dfGen) + resource_component = dfGen[y, :Resource] + cap_size = dfGen[y, :Cap_Size] + down_var = EP[Symbol(maintenance_down_name(resource_component))] + vDOWN = value.(down_var[timesteps]) + effective_capacity = effective_capacity .- capresfactor(y, capres_zone) * vDOWN * cap_size + end + + return effective_capacity +end diff --git a/src/write_outputs/capacity_reserve_margin/write_capacity_value.jl b/src/write_outputs/capacity_reserve_margin/write_capacity_value.jl index b2841c6e36..cbefad6c83 100644 --- a/src/write_outputs/capacity_reserve_margin/write_capacity_value.jl +++ b/src/write_outputs/capacity_reserve_margin/write_capacity_value.jl @@ -48,7 +48,7 @@ function write_capacity_value(path::AbstractString, inputs::Dict, setup::Dict, E power(y::Vector{Int}) = value.(EP[:vP][y, riskyhour])' - capvalue[riskyhour, THERM_ALL_EX] .= crm_derate(i, THERM_ALL_EX) + capvalue[riskyhour, THERM_ALL_EX] = thermal_plant_effective_capacity(EP, inputs, THERM_ALL_EX, i, riskyhour) ./ total_cap(THERM_ALL_EX) capvalue[riskyhour, VRE_EX] = crm_derate(i, VRE_EX) .* max_power(riskyhour, VRE_EX) @@ -122,22 +122,3 @@ function capacity_reserve_margin_price(EP::Model, inputs::Dict, setup::Dict, cap return dual.(EP[:cCapacityResMargin][capres_zone, :]) ./ ω * scale_factor end -function thermal_plant_effective_capacity(EP::Model, inputs::Dict, r_id::Int, capres_zone::Int)::Vector{Float} - y = r_id - T = inputs["T"] - dfGen = inputs["dfGen"] - capresfactor(y, capres) = dfGen[y, Symbol("CapRes_$capres")] - eTotalCap = value.(EP[:eTotalCap][y]) - - effective_capacity = capresfactor(y, capres_zone) * eTotalCap * ones(T) - - if has_maintenance(inputs) - resource_component(y) = dfGen[y, :Resource] - cap_size = dfGen[y, :Cap_Size] - down_var(y) = EP[Symbol(maintenance_down_name(resource_component(y)))] - vDOWN = value.(down_var(y)) - effective_capacity -= capresfactor(y, capres) * vDOWN * cap_size - end - - return effective_capacity -end diff --git a/src/write_outputs/capacity_reserve_margin/write_reserve_margin_revenue.jl b/src/write_outputs/capacity_reserve_margin/write_reserve_margin_revenue.jl index e2bfc70153..f40877ac78 100644 --- a/src/write_outputs/capacity_reserve_margin/write_reserve_margin_revenue.jl +++ b/src/write_outputs/capacity_reserve_margin/write_reserve_margin_revenue.jl @@ -9,6 +9,7 @@ Function for reporting the capacity revenue earned by each generator listed in t As a reminder, GenX models the capacity reserve margin (aka capacity market) at the time-dependent level, and each constraint either stands for an overall market or a locality constraint. """ function write_reserve_margin_revenue(path::AbstractString, inputs::Dict, setup::Dict, EP::Model) + scale_factor = setup["ParameterScale"] == 1 ? ModelScalingFactor : 1 dfGen = inputs["dfGen"] G = inputs["G"] # Number of resources (generators, storage, DR, and DERs) T = inputs["T"] # Number of time steps (hours) @@ -31,9 +32,10 @@ function write_reserve_margin_revenue(path::AbstractString, inputs::Dict, setup: dfResRevenue = DataFrame(Region = dfGen.region, Resource = inputs["RESOURCES"], Zone = dfGen.Zone, Cluster = dfGen.cluster) annual_sum = zeros(G) for i in 1:inputs["NCapacityReserveMargin"] + weighted_price = capacity_reserve_margin_price(EP, inputs, setup, i) .* inputs["omega"] / scale_factor sym = Symbol("CapRes_$i") tempresrev = zeros(G) - tempresrev[THERM_ALL] = dfGen[THERM_ALL, sym] .* (value.(EP[:eTotalCap][THERM_ALL])) * sum(dual.(EP[:cCapacityResMargin][i, :])) + tempresrev[THERM_ALL] = thermal_plant_effective_capacity(EP, inputs, THERM_ALL, i)' * weighted_price tempresrev[VRE] = dfGen[VRE, sym] .* (value.(EP[:eTotalCap][VRE])) .* (inputs["pP_Max"][VRE, :] * (dual.(EP[:cCapacityResMargin][i, :]))) tempresrev[MUST_RUN] = dfGen[MUST_RUN, sym] .* (value.(EP[:eTotalCap][MUST_RUN])) .* (inputs["pP_Max"][MUST_RUN, :] * (dual.(EP[:cCapacityResMargin][i, :]))) tempresrev[HYDRO_RES] = dfGen[HYDRO_RES, sym] .* (value.(EP[:vP][HYDRO_RES, :]) * (dual.(EP[:cCapacityResMargin][i, :]))) @@ -52,9 +54,7 @@ function write_reserve_margin_revenue(path::AbstractString, inputs::Dict, setup: tempresrev[DC_CHARGE] .-= dfVRE_STOR[(dfVRE_STOR.STOR_DC_CHARGE.!=0), sym_vs] .* ((value.(EP[:vCAPRES_DC_CHARGE][DC_CHARGE, :]).data ./ dfVRE_STOR[(dfVRE_STOR.STOR_DC_CHARGE.!=0), :EtaInverter]) * (dual.(EP[:cCapacityResMargin][i, :]))) tempresrev[AC_CHARGE] .-= dfVRE_STOR[(dfVRE_STOR.STOR_AC_CHARGE.!=0), sym_vs] .* ((value.(EP[:vCAPRES_AC_CHARGE][AC_CHARGE, :]).data) * (dual.(EP[:cCapacityResMargin][i, :]))) end - if setup["ParameterScale"] == 1 - tempresrev *= ModelScalingFactor^2 - end + tempresrev *= scale_factor^2 annual_sum .+= tempresrev dfResRevenue = hcat(dfResRevenue, DataFrame([tempresrev], [sym])) end From 8289bf23b74a21531d28acd1faa6321d150f1b30 Mon Sep 17 00:00:00 2001 From: Jacob Schwartz Date: Thu, 30 Nov 2023 19:15:04 -0500 Subject: [PATCH 3/5] Use `fill` instead of * one. Format. --- .../effective_capacity.jl | 56 +++++++++++++------ 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/src/write_outputs/capacity_reserve_margin/effective_capacity.jl b/src/write_outputs/capacity_reserve_margin/effective_capacity.jl index 4b1b27450a..d3e25e0a1c 100644 --- a/src/write_outputs/capacity_reserve_margin/effective_capacity.jl +++ b/src/write_outputs/capacity_reserve_margin/effective_capacity.jl @@ -7,32 +7,52 @@ Effective capacity in a capacity reserve margin zone for certain resources in the given timesteps. """ -function thermal_plant_effective_capacity(EP, inputs, resources::Vector{Int}, capres_zone::Int, timesteps::Vector{Int})::Matrix{Float64} - eff_cap = thermal_plant_effective_capacity.(Ref(EP), Ref(inputs), resources, Ref(capres_zone), Ref(timesteps)) - return reduce(hcat, eff_cap) +function thermal_plant_effective_capacity( + EP, + inputs, + resources::Vector{Int}, + capres_zone::Int, + timesteps::Vector{Int}, +)::Matrix{Float64} + eff_cap = + thermal_plant_effective_capacity.( + Ref(EP), + Ref(inputs), + resources, + Ref(capres_zone), + Ref(timesteps), + ) + return reduce(hcat, eff_cap) end function thermal_plant_effective_capacity(EP::Model, inputs::Dict, y, capres_zone::Int) - T = inputs["T"] + T = inputs["T"] timesteps = collect(1:T) - return thermal_plant_effective_capacity(EP, inputs, y, capres_zone, timesteps) + return thermal_plant_effective_capacity(EP, inputs, y, capres_zone, timesteps) end -function thermal_plant_effective_capacity(EP::Model, inputs::Dict, r_id::Int, capres_zone::Int, timesteps::Vector{Int})::Vector{Float64} - y = r_id - dfGen = inputs["dfGen"] +function thermal_plant_effective_capacity( + EP::Model, + inputs::Dict, + r_id::Int, + capres_zone::Int, + timesteps::Vector{Int}, +)::Vector{Float64} + y = r_id + dfGen = inputs["dfGen"] capresfactor(y, capres) = dfGen[y, Symbol("CapRes_$capres")] - eTotalCap = value.(EP[:eTotalCap][y]) + eTotalCap = value.(EP[:eTotalCap][y]) - effective_capacity = capresfactor(y, capres_zone) * eTotalCap * one.(timesteps) + effective_capacity = fill(capresfactor(y, capres_zone) * eTotalCap, length(timesteps)) - if has_maintenance(inputs) && y in resources_with_maintenance(dfGen) - resource_component = dfGen[y, :Resource] - cap_size = dfGen[y, :Cap_Size] - down_var = EP[Symbol(maintenance_down_name(resource_component))] - vDOWN = value.(down_var[timesteps]) - effective_capacity = effective_capacity .- capresfactor(y, capres_zone) * vDOWN * cap_size - end + if has_maintenance(inputs) && y in resources_with_maintenance(dfGen) + resource_component = dfGen[y, :Resource] + cap_size = dfGen[y, :Cap_Size] + down_var = EP[Symbol(maintenance_down_name(resource_component))] + vDOWN = value.(down_var[timesteps]) + effective_capacity = + effective_capacity .- capresfactor(y, capres_zone) * vDOWN * cap_size + end - return effective_capacity + return effective_capacity end From f8fa6da5bd1d4f2d02b7eee4f9e2c07c6a4dde1e Mon Sep 17 00:00:00 2001 From: Jacob Schwartz Date: Thu, 30 Nov 2023 20:35:45 -0500 Subject: [PATCH 4/5] Experimental use of expressions --- src/model/resources/thermal/thermal_commit.jl | 19 ++++++++++++++----- .../effective_capacity.jl | 10 +++------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/model/resources/thermal/thermal_commit.jl b/src/model/resources/thermal/thermal_commit.jl index 47949bb557..b654f098ca 100644 --- a/src/model/resources/thermal/thermal_commit.jl +++ b/src/model/resources/thermal/thermal_commit.jl @@ -361,11 +361,20 @@ function thermal_maintenance_capacity_reserve_margin_adjustment!(EP::Model, MAINT = resources_with_maintenance(dfGen) applicable_resources = intersect(MAINT, THERM_COMMIT) - resource_component(y) = dfGen[y, :Resource] - capresfactor(y, capres) = dfGen[y, Symbol("CapRes_$capres")] - cap_size(y) = dfGen[y, :Cap_Size] - down_var(y) = EP[Symbol(maintenance_down_name(resource_component(y)))] maint_adj = @expression(EP, [capres in 1:ncapres, t in 1:T], - -sum(capresfactor(y, capres) * down_var(y)[t] * cap_size(y) for y in applicable_resources)) + sum(thermal_maintenance_capacity_reserve_margin_adjustment(EP, inputs, y, capres, t) for y in applicable_resources)) add_similar_to_expression!(EP[:eCapResMarBalance], maint_adj) end + +function thermal_maintenance_capacity_reserve_margin_adjustment(EP::Model, + inputs::Dict, + y::Int, + capres::Int, + t) + dfGen = inputs["dfGen"] + resource_component = dfGen[y, :Resource] + capresfactor = dfGen[y, Symbol("CapRes_$capres")] + cap_size = dfGen[y, :Cap_Size] + down_var = EP[Symbol(maintenance_down_name(resource_component))] + return -capresfactor * down_var[t] * cap_size +end diff --git a/src/write_outputs/capacity_reserve_margin/effective_capacity.jl b/src/write_outputs/capacity_reserve_margin/effective_capacity.jl index d3e25e0a1c..76ae63a001 100644 --- a/src/write_outputs/capacity_reserve_margin/effective_capacity.jl +++ b/src/write_outputs/capacity_reserve_margin/effective_capacity.jl @@ -46,13 +46,9 @@ function thermal_plant_effective_capacity( effective_capacity = fill(capresfactor(y, capres_zone) * eTotalCap, length(timesteps)) if has_maintenance(inputs) && y in resources_with_maintenance(dfGen) - resource_component = dfGen[y, :Resource] - cap_size = dfGen[y, :Cap_Size] - down_var = EP[Symbol(maintenance_down_name(resource_component))] - vDOWN = value.(down_var[timesteps]) - effective_capacity = - effective_capacity .- capresfactor(y, capres_zone) * vDOWN * cap_size - end + adjustment = thermal_maintenance_capacity_reserve_margin_adjustment(EP, inputs, y, capres_zone, timesteps) + effective_capacity = effective_capacity .+ value.(adjustment) + end return effective_capacity end From 66b8f101b73f1302a95fbf9dc8367fe09289dde5 Mon Sep 17 00:00:00 2001 From: Jacob Schwartz Date: Thu, 30 Nov 2023 21:29:53 -0500 Subject: [PATCH 5/5] eliminate unneeded function --- .../capacity_reserve_margin/effective_capacity.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/write_outputs/capacity_reserve_margin/effective_capacity.jl b/src/write_outputs/capacity_reserve_margin/effective_capacity.jl index 76ae63a001..5d6d47246c 100644 --- a/src/write_outputs/capacity_reserve_margin/effective_capacity.jl +++ b/src/write_outputs/capacity_reserve_margin/effective_capacity.jl @@ -40,10 +40,10 @@ function thermal_plant_effective_capacity( )::Vector{Float64} y = r_id dfGen = inputs["dfGen"] - capresfactor(y, capres) = dfGen[y, Symbol("CapRes_$capres")] + capresfactor = dfGen[y, Symbol("CapRes_$capres_zone")] eTotalCap = value.(EP[:eTotalCap][y]) - effective_capacity = fill(capresfactor(y, capres_zone) * eTotalCap, length(timesteps)) + effective_capacity = fill(capresfactor * eTotalCap, length(timesteps)) if has_maintenance(inputs) && y in resources_with_maintenance(dfGen) adjustment = thermal_maintenance_capacity_reserve_margin_adjustment(EP, inputs, y, capres_zone, timesteps)