From 6edebb4d8a7d79f2a38c0edcb42fcf8b7cf59de3 Mon Sep 17 00:00:00 2001 From: Filippo Date: Wed, 14 Feb 2024 16:09:44 -0500 Subject: [PATCH] Fixed computation of cumulative minimum retirements in multistage code. Previously, minimum capacity retirement parameters were not added up and this resulted in the minimum retirement constraints enforcing wrong lower bounds --- CHANGELOG.md | 1 + src/case_runners/case_runner.jl | 2 ++ src/multi_stage/endogenous_retirement.jl | 34 +++++++++++++++++++++--- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37fc5999ae..41deb8b17c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix summation error when a set of hours is empty (in thermal_commit.jl). - Fix access to eELOSSByZone expr before initialization (#541) - Fix modeling of hydro reservoir with long duration storage (#572). +- Fix computation of cumulative minimum capacity retirements in multistage GenX (#514) ### Changed diff --git a/src/case_runners/case_runner.jl b/src/case_runners/case_runner.jl index 5f8786db38..c50b00fcbf 100644 --- a/src/case_runners/case_runner.jl +++ b/src/case_runners/case_runner.jl @@ -130,6 +130,8 @@ function run_genx_case_multistage!(case::AbstractString, mysetup::Dict) inputs_dict[t] = load_inputs(mysetup, inpath_sub) inputs_dict[t] = configure_multi_stage_inputs(inputs_dict[t],mysetup["MultiStageSettingsDict"],mysetup["NetworkExpansion"]) + compute_cumulative_min_retirements!(inputs_dict,t) + # Step 2) Generate model model_dict[t] = generate_model(mysetup, inputs_dict[t], OPTIMIZER) end diff --git a/src/multi_stage/endogenous_retirement.jl b/src/multi_stage/endogenous_retirement.jl index f7700e80c5..20cea11220 100644 --- a/src/multi_stage/endogenous_retirement.jl +++ b/src/multi_stage/endogenous_retirement.jl @@ -22,6 +22,32 @@ function get_retirement_stage(cur_stage::Int, lifetime::Int, stage_lens::Array{I return Int(ret_stage) end +function update_cumulative_min_ret!(inputs_d::Dict,t::Int,Resource_Set::String,dfGen_Name::String,RetCap::Symbol) + + CumRetCap = Symbol("Cum_"*String(RetCap)); + + if !isempty(inputs_d[1][Resource_Set]) + if t==1 + inputs_d[t][dfGen_Name][!,CumRetCap] = inputs_d[t][dfGen_Name][!,RetCap]; + else + inputs_d[t][dfGen_Name][!,CumRetCap] = inputs_d[t-1][dfGen_Name][!,CumRetCap] + inputs_d[t][dfGen_Name][!,RetCap]; + end + end +end + +function compute_cumulative_min_retirements!(inputs_d::Dict,t::Int) + + mytab =[("G","dfGen",:Min_Retired_Cap_MW), + ("STOR_ALL","dfGen",:Min_Retired_Energy_Cap_MW), + ("STOR_ASYMMETRIC","dfGen",:Min_Retired_Charge_Cap_MW)]; + + for (Resource_Set,dfGen_Name,RetCap) in mytab + update_cumulative_min_ret!(inputs_d,t,Resource_Set,dfGen_Name,RetCap) + end + + +end + function endogenous_retirement!(EP::Model, inputs::Dict, setup::Dict) multi_stage_settings = setup["MultiStageSettingsDict"] @@ -78,9 +104,9 @@ function endogenous_retirement_discharge!(EP::Model, inputs::Dict, num_stages::I @expression(EP, eNewCapTrack[y in RET_CAP], sum(EP[:vCAPTRACK][y,p] for p=1:get_retirement_stage(cur_stage, dfGen[!,:Lifetime][y], stage_lens))) @expression(EP, eMinRetCapTrack[y in RET_CAP], if y in COMMIT - sum((dfGen[!,Symbol("Min_Retired_Cap_MW")][y]/dfGen[!,:Cap_Size][y]) for p=1:cur_stage) + dfGen[y,:Cum_Min_Retired_Cap_MW]/dfGen[y,:Cap_Size] else - sum((dfGen[!,Symbol("Min_Retired_Cap_MW")][y]) for p=1:cur_stage) + dfGen[y,:Cum_Min_Retired_Cap_MW] end ) @@ -132,7 +158,7 @@ function endogenous_retirement_charge!(EP::Model, inputs::Dict, num_stages::Int, # Construct and add the endogenous retirement constraint expressions @expression(EP, eRetCapTrackCharge[y in RET_CAP_CHARGE], sum(EP[:vRETCAPTRACKCHARGE][y,p] for p=1:cur_stage)) @expression(EP, eNewCapTrackCharge[y in RET_CAP_CHARGE], sum(EP[:vCAPTRACKCHARGE][y,p] for p=1:get_retirement_stage(cur_stage, dfGen[!,:Lifetime][y], stage_lens))) - @expression(EP, eMinRetCapTrackCharge[y in RET_CAP_CHARGE], sum((dfGen[!,Symbol("Min_Retired_Charge_Cap_MW")][y]) for p=1:cur_stage)) + @expression(EP, eMinRetCapTrackCharge[y in RET_CAP_CHARGE], dfGen[y,:Cum_Min_Retired_Charge_Cap_MW]) ### Constratints ### @@ -181,7 +207,7 @@ function endogenous_retirement_energy!(EP::Model, inputs::Dict, num_stages::Int, # Construct and add the endogenous retirement constraint expressions @expression(EP, eRetCapTrackEnergy[y in RET_CAP_ENERGY], sum(EP[:vRETCAPTRACKENERGY][y,p] for p=1:cur_stage)) @expression(EP, eNewCapTrackEnergy[y in RET_CAP_ENERGY], sum(EP[:vCAPTRACKENERGY][y,p] for p=1:get_retirement_stage(cur_stage, dfGen[!,:Lifetime][y], stage_lens))) - @expression(EP, eMinRetCapTrackEnergy[y in RET_CAP_ENERGY], sum((dfGen[!,Symbol("Min_Retired_Energy_Cap_MW")][y]) for p=1:cur_stage)) + @expression(EP, eMinRetCapTrackEnergy[y in RET_CAP_ENERGY], dfGen[y,:Cum_Min_Retired_Energy_Cap_MW]) ### Constratints ###