From fa1fc0fb03614928b63ca67e38bee2b65d8725dd Mon Sep 17 00:00:00 2001 From: Filippo Date: Tue, 5 Sep 2023 18:28:45 -0400 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 | 2 + src/case_runners/case_runner.jl | 2 + src/multi_stage/endogenous_retirement.jl | 95 ++++++++++++++++-------- 3 files changed, 70 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 90cdf5dbef..816eba270e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- Fix computation of cumulative minimum capacity retirements in multistage GenX (#514) + ### Added - Feature electrolysis basic (#525) Adds hydrogen electrolyzer model which enables the addition of hydrogen electrolyzer diff --git a/src/case_runners/case_runner.jl b/src/case_runners/case_runner.jl index 3388bf34fb..a1ecc05a29 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 c329b61fa1..4c915ae039 100644 --- a/src/multi_stage/endogenous_retirement.jl +++ b/src/multi_stage/endogenous_retirement.jl @@ -22,6 +22,51 @@ 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) + + if isempty(inputs_d[1]["VRE_STOR"]) + mytab =[("G","dfGen",:Min_Retired_Cap_MW), + ("STOR_ALL","dfGen",:Min_Retired_Energy_Cap_MW), + ("STOR_ASYMMETRIC","dfGen",:Min_Retired_Charge_Cap_MW) + ] + else + mytab =[("G","dfGen",:Min_Retired_Cap_MW), + ("STOR_ALL","dfGen",:Min_Retired_Energy_Cap_MW), + ("STOR_ASYMMETRIC","dfGen",:Min_Retired_Charge_Cap_MW), + ("VS_DC","dfVRE_STOR",:Min_Retired_Cap_Inverter_MW), + ("VS_SOLAR","dfVRE_STOR",:Min_Retired_Cap_Solar_MW), + ("VS_WIND","dfVRE_STOR",:Min_Retired_Cap_Wind_MW), + ("VS_STOR","dfGen",:Min_Retired_Energy_Cap_MW), + ("VS_ASYM_DC_DISCHARGE","dfVRE_STOR",:Min_Retired_Cap_Discharge_DC_MW), + ("VS_ASYM_DC_CHARGE","dfVRE_STOR",:Min_Retired_Cap_Charge_DC_MW), + ("VS_ASYM_AC_DISCHARGE","dfVRE_STOR",:Min_Retired_Cap_Discharge_AC_MW), + ("VS_ASYM_AC_CHARGE","dfVRE_STOR",:Min_Retired_Cap_Charge_AC_MW) + ] + end + + 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"] @@ -112,9 +157,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 ) @@ -166,7 +211,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 ### @@ -215,7 +260,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 ### @@ -238,11 +283,11 @@ function endogenous_retirement_vre_stor_dc!(EP::Model, inputs::Dict, num_stages: dfGen = inputs["dfGen"] + dfVRE_STOR = inputs["dfVRE_STOR"]; + NEW_CAP_DC = inputs["NEW_CAP_DC"] # Set of all resources eligible for new capacity RET_CAP_DC = inputs["RET_CAP_DC"] # Set of all resources eligible for capacity retirements - by_rid(rid, sym) = by_rid_df(rid, sym, inputs["dfVRE_STOR"]) - ### Variables ### # Keep track of all new and retired capacity from all stages @@ -264,7 +309,7 @@ function endogenous_retirement_vre_stor_dc!(EP::Model, inputs::Dict, num_stages: # Construct and add the endogenous retirement constraint expressions @expression(EP, eRetCapTrackDC[y in RET_CAP_DC], sum(EP[:vRETCAPTRACKDC][y,p] for p=1:cur_stage)) @expression(EP, eNewCapTrackDC[y in RET_CAP_DC], sum(EP[:vCAPTRACKDC][y,p] for p=1:get_retirement_stage(cur_stage, dfGen[!,:Lifetime][y], stage_lens))) - @expression(EP, eMinRetCapTrackDC[y in RET_CAP_DC], sum((by_rid(y,Symbol("Min_Retired_Cap_Inverter_MW"))) for p=1:cur_stage)) + @expression(EP, eMinRetCapTrackDC[y in RET_CAP_DC], dfVRE_STOR[y,:Cum_Min_Retired_Cap_Inverter_MW]) ### Constraints ### @@ -286,12 +331,11 @@ function endogenous_retirement_vre_stor_solar!(EP::Model, inputs::Dict, num_stag println("Endogenous Retirement (VRE-Storage Solar) Module") dfGen = inputs["dfGen"] + dfVRE_STOR = inputs["dfVRE_STOR"]; NEW_CAP_SOLAR = inputs["NEW_CAP_SOLAR"] # Set of all resources eligible for new capacity RET_CAP_SOLAR = inputs["RET_CAP_SOLAR"] # Set of all resources eligible for capacity retirements - by_rid(rid, sym) = by_rid_df(rid, sym, inputs["dfVRE_STOR"]) - ### Variables ### # Keep track of all new and retired capacity from all stages @@ -313,7 +357,7 @@ function endogenous_retirement_vre_stor_solar!(EP::Model, inputs::Dict, num_stag # Construct and add the endogenous retirement constraint expressions @expression(EP, eRetCapTrackSolar[y in RET_CAP_SOLAR], sum(EP[:vRETCAPTRACKSOLAR][y,p] for p=1:cur_stage)) @expression(EP, eNewCapTrackSolar[y in RET_CAP_SOLAR], sum(EP[:vCAPTRACKSOLAR][y,p] for p=1:get_retirement_stage(cur_stage, dfGen[!,:Lifetime][y], stage_lens))) - @expression(EP, eMinRetCapTrackSolar[y in RET_CAP_SOLAR], sum((by_rid(y,Symbol("Min_Retired_Cap_Solar_MW"))) for p=1:cur_stage)) + @expression(EP, eMinRetCapTrackSolar[y in RET_CAP_SOLAR], dfVRE_STOR[y,:Cum_Min_Retired_Cap_Solar_MW]) ### Constraints ### @@ -335,12 +379,11 @@ function endogenous_retirement_vre_stor_wind!(EP::Model, inputs::Dict, num_stage println("Endogenous Retirement (VRE-Storage Wind) Module") dfGen = inputs["dfGen"] + dfVRE_STOR = inputs["dfVRE_STOR"]; NEW_CAP_WIND = inputs["NEW_CAP_WIND"] # Set of all resources eligible for new capacity RET_CAP_WIND = inputs["RET_CAP_WIND"] # Set of all resources eligible for capacity retirements - by_rid(rid, sym) = by_rid_df(rid, sym, inputs["dfVRE_STOR"]) - ### Variables ### # Keep track of all new and retired capacity from all stages @@ -362,7 +405,7 @@ function endogenous_retirement_vre_stor_wind!(EP::Model, inputs::Dict, num_stage # Construct and add the endogenous retirement constraint expressions @expression(EP, eRetCapTrackWind[y in RET_CAP_WIND], sum(EP[:vRETCAPTRACKWIND][y,p] for p=1:cur_stage)) @expression(EP, eNewCapTrackWind[y in RET_CAP_WIND], sum(EP[:vCAPTRACKWIND][y,p] for p=1:get_retirement_stage(cur_stage, dfGen[!,:Lifetime][y], stage_lens))) - @expression(EP, eMinRetCapTrackWind[y in RET_CAP_WIND], sum((by_rid(y,Symbol("Min_Retired_Cap_Wind_MW"))) for p=1:cur_stage)) + @expression(EP, eMinRetCapTrackWind[y in RET_CAP_WIND], dfVRE_STOR[y,:Cum_Min_Retired_Cap_Wind_MW]) ### Constraints ### @@ -409,7 +452,7 @@ function endogenous_retirement_vre_stor_stor!(EP::Model, inputs::Dict, num_stage # Construct and add the endogenous retirement constraint expressions @expression(EP, eRetCapTrackEnergy_VS[y in RET_CAP_STOR], sum(EP[:vRETCAPTRACKENERGY_VS][y,p] for p=1:cur_stage)) @expression(EP, eNewCapTrackEnergy_VS[y in RET_CAP_STOR], sum(EP[:vCAPTRACKENERGY_VS][y,p] for p=1:get_retirement_stage(cur_stage, dfGen[!,:Lifetime][y], stage_lens))) - @expression(EP, eMinRetCapTrackEnergy_VS[y in RET_CAP_STOR], sum((dfGen[!,Symbol("Min_Retired_Energy_Cap_MW")][y]) for p=1:cur_stage)) + @expression(EP, eMinRetCapTrackEnergy_VS[y in RET_CAP_STOR], dfGen[y,:Cum_Min_Retired_Energy_Cap_MW]) ### Constratints ### @@ -432,11 +475,11 @@ function endogenous_retirement_vre_stor_discharge_dc!(EP::Model, inputs::Dict, n dfGen = inputs["dfGen"] + dfVRE_STOR = inputs["dfVRE_STOR"] + NEW_CAP_DISCHARGE_DC = inputs["NEW_CAP_DISCHARGE_DC"] # Set of all resources eligible for new capacity RET_CAP_DISCHARGE_DC = inputs["RET_CAP_DISCHARGE_DC"] # Set of all resources eligible for capacity retirements - by_rid(rid, sym) = by_rid_df(rid, sym, inputs["dfVRE_STOR"]) - ### Variables ### # Keep track of all new and retired capacity from all stages @@ -458,7 +501,7 @@ function endogenous_retirement_vre_stor_discharge_dc!(EP::Model, inputs::Dict, n # Construct and add the endogenous retirement constraint expressions @expression(EP, eRetCapTrackDischargeDC[y in RET_CAP_DISCHARGE_DC], sum(EP[:vRETCAPTRACKDISCHARGEDC][y,p] for p=1:cur_stage)) @expression(EP, eNewCapTrackDischargeDC[y in RET_CAP_DISCHARGE_DC], sum(EP[:vCAPTRACKDISCHARGEDC][y,p] for p=1:get_retirement_stage(cur_stage, dfGen[!,:Lifetime][y], stage_lens))) - @expression(EP, eMinRetCapTrackDischargeDC[y in RET_CAP_DISCHARGE_DC], sum((by_rid(y,Symbol("Min_Retired_Cap_Discharge_DC_MW"))) for p=1:cur_stage)) + @expression(EP, eMinRetCapTrackDischargeDC[y in RET_CAP_DISCHARGE_DC], dfVRE_STOR[y,:Cum_Min_Retired_Cap_Discharge_DC_MW]) ### Constraints ### @@ -480,12 +523,10 @@ function endogenous_retirement_vre_stor_charge_dc!(EP::Model, inputs::Dict, num_ println("Endogenous Retirement (VRE-Storage Charge DC) Module") dfGen = inputs["dfGen"] - + dfVRE_STOR = inputs["dfVRE_STOR"]; NEW_CAP_CHARGE_DC = inputs["NEW_CAP_CHARGE_DC"] # Set of all resources eligible for new capacity RET_CAP_CHARGE_DC = inputs["RET_CAP_CHARGE_DC"] # Set of all resources eligible for capacity retirements - by_rid(rid, sym) = by_rid_df(rid, sym, inputs["dfVRE_STOR"]) - ### Variables ### # Keep track of all new and retired capacity from all stages @@ -507,7 +548,7 @@ function endogenous_retirement_vre_stor_charge_dc!(EP::Model, inputs::Dict, num_ # Construct and add the endogenous retirement constraint expressions @expression(EP, eRetCapTrackChargeDC[y in RET_CAP_CHARGE_DC], sum(EP[:vRETCAPTRACKCHARGEDC][y,p] for p=1:cur_stage)) @expression(EP, eNewCapTrackChargeDC[y in RET_CAP_CHARGE_DC], sum(EP[:vCAPTRACKCHARGEDC][y,p] for p=1:get_retirement_stage(cur_stage, dfGen[!,:Lifetime][y], stage_lens))) - @expression(EP, eMinRetCapTrackChargeDC[y in RET_CAP_CHARGE_DC], sum((by_rid(y,Symbol("Min_Retired_Cap_Charge_DC_MW"))) for p=1:cur_stage)) + @expression(EP, eMinRetCapTrackChargeDC[y in RET_CAP_CHARGE_DC], dfVRE_STOR[y,:Cum_Min_Retired_Cap_Charge_DC_MW]) ### Constraints ### @@ -529,12 +570,10 @@ function endogenous_retirement_vre_stor_discharge_ac!(EP::Model, inputs::Dict, n println("Endogenous Retirement (VRE-Storage Discharge AC) Module") dfGen = inputs["dfGen"] - + dfVRE_STOR = inputs["dfVRE_STOR"]; NEW_CAP_DISCHARGE_AC = inputs["NEW_CAP_DISCHARGE_AC"] # Set of all resources eligible for new capacity RET_CAP_DISCHARGE_AC = inputs["RET_CAP_DISCHARGE_AC"] # Set of all resources eligible for capacity retirements - by_rid(rid, sym) = by_rid_df(rid, sym, inputs["dfVRE_STOR"]) - ### Variables ### # Keep track of all new and retired capacity from all stages @@ -556,7 +595,7 @@ function endogenous_retirement_vre_stor_discharge_ac!(EP::Model, inputs::Dict, n # Construct and add the endogenous retirement constraint expressions @expression(EP, eRetCapTrackDischargeAC[y in RET_CAP_DISCHARGE_AC], sum(EP[:vRETCAPTRACKDISCHARGEAC][y,p] for p=1:cur_stage)) @expression(EP, eNewCapTrackDischargeAC[y in RET_CAP_DISCHARGE_AC], sum(EP[:vCAPTRACKDISCHARGEAC][y,p] for p=1:get_retirement_stage(cur_stage, dfGen[!,:Lifetime][y], stage_lens))) - @expression(EP, eMinRetCapTrackDischargeAC[y in RET_CAP_DISCHARGE_AC], sum((by_rid(y,Symbol("Min_Retired_Cap_Discharge_AC_MW"))) for p=1:cur_stage)) + @expression(EP, eMinRetCapTrackDischargeAC[y in RET_CAP_DISCHARGE_AC], dfVRE_STOR[y,:Cum_Min_Retired_Cap_Discharge_AC_MW]) ### Constraints ### @@ -578,12 +617,10 @@ function endogenous_retirement_vre_stor_charge_ac!(EP::Model, inputs::Dict, num_ println("Endogenous Retirement (VRE-Storage Charge AC) Module") dfGen = inputs["dfGen"] - + dfVRE_STOR = inputs["dfVRE_STOR"] NEW_CAP_CHARGE_AC = inputs["NEW_CAP_CHARGE_AC"] # Set of all resources eligible for new capacity RET_CAP_CHARGE_AC = inputs["RET_CAP_CHARGE_AC"] # Set of all resources eligible for capacity retirements - by_rid(rid, sym) = by_rid_df(rid, sym, inputs["dfVRE_STOR"]) - ### Variables ### # Keep track of all new and retired capacity from all stages @@ -605,7 +642,7 @@ function endogenous_retirement_vre_stor_charge_ac!(EP::Model, inputs::Dict, num_ # Construct and add the endogenous retirement constraint expressions @expression(EP, eRetCapTrackChargeAC[y in RET_CAP_CHARGE_AC], sum(EP[:vRETCAPTRACKCHARGEAC][y,p] for p=1:cur_stage)) @expression(EP, eNewCapTrackChargeAC[y in RET_CAP_CHARGE_AC], sum(EP[:vCAPTRACKCHARGEAC][y,p] for p=1:get_retirement_stage(cur_stage, dfGen[!,:Lifetime][y], stage_lens))) - @expression(EP, eMinRetCapTrackChargeAC[y in RET_CAP_CHARGE_AC], sum((by_rid(y,Symbol("Min_Retired_Cap_Charge_AC_MW"))) for p=1:cur_stage)) + @expression(EP, eMinRetCapTrackChargeAC[y in RET_CAP_CHARGE_AC], dfVRE_STOR[y,:Cum_Min_Retired_Cap_Charge_AC_MW]) ### Constraints ###