From 640e164daa616cbc281a42df904502ba173398f8 Mon Sep 17 00:00:00 2001 From: wcole Date: Thu, 3 Sep 2026 11:51:21 -0600 Subject: [PATCH 01/30] Replace instances of CAP with CAP_CLASS in outputs. --- reeds/core/terminus/report.gms | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/reeds/core/terminus/report.gms b/reeds/core/terminus/report.gms index a11b561bd..53c451623 100644 --- a/reeds/core/terminus/report.gms +++ b/reeds/core/terminus/report.gms @@ -680,7 +680,7 @@ gen_ann_nat(i,t)$tmodel_new(t) = sum{r, gen_ann(i,r,t) } ; * Report generation without the charging and production included as above gen_ivrt(i,v,r,t)$valgen(i,v,r,t) = sum{h, GEN.l(i,v,r,h,t) * hours(h) } ; gen_ivrt_uncurt(i,v,r,t)$[(vre(i) or storage_hybrid(i)$(not csp(i)))$valgen(i,v,r,t)] = - sum{(h,c)$i_c(i,c), m_cf(i,c,v,r,h,t) * CAP.l(i,v,r,t) * hours(h) } ; + sum{(h,c)$valcap_class(i,c,v,r,t), m_cf(i,c,v,r,h,t) * CAP_CLASS.l(i,c,v,r,t) * hours(h) } ; * Report generation that will be used as a denominator in outputs, where VRE uses uncurtailed gen and storage uses GEN gen_uncurtailed(i,r,t)$[valgen_irt(i,r,t)$(not vre(i))] = sum{v, gen_ivrt(i,v,r,t) } ; @@ -763,8 +763,8 @@ gen_new_uncurt(i,r,h,t)$[(vre(i) or storage_hybrid(i)$(not csp(i)))$valcap_irt(i * curtailment = (availability - generation - operating reserves) curt_h(r,h,t)$tmodel_new(t) = - sum{(i,v,c)$[valcap(i,v,r,t)$(vre(i) or storage_hybrid(i)$(not csp(i)))$i_c(i,c)], - m_cf(i,c,v,r,h,t) * CAP.l(i,v,r,t) } + sum{(i,v,c)$[valcap_class(i,c,v,r,t)$(vre(i) or storage_hybrid(i)$(not csp(i)))], + m_cf(i,c,v,r,h,t) * CAP_CLASS.l(i,c,v,r,t) } - sum{(i,v)$[valgen(i,v,r,t)$vre(i)], GEN.l(i,v,r,h,t) } - sum{(i,v)$[valgen(i,v,r,t)$storage_hybrid(i)$(not csp(i))], GEN_PLANT.l(i,v,r,h,t) }$Sw_HybridPlant - sum{(ortype,i,v)$[Sw_OpRes$opres_h(h)$reserve_frac(i,ortype)$valgen(i,v,r,t)$vre(i)], @@ -774,8 +774,8 @@ curt_h(r,h,t)$tmodel_new(t) = curt_ann(r,t)$tmodel_new(t) = sum{h, curt_h(r,h,t) * hours(h) } ; curt_tech(i,r,t)$[tmodel_new(t)$vre(i)] = - sum{(v,h,c)$[valcap(i,v,r,t)$i_c(i,c)], - m_cf(i,c,v,r,h,t) * CAP.l(i,v,r,t) * hours(h) } + sum{(v,h,c)$[valcap_class(i,c,v,r,t)], + m_cf(i,c,v,r,h,t) * CAP_CLASS.l(i,c,v,r,t) * hours(h) } - sum{(v,h)$valgen(i,v,r,t), GEN.l(i,v,r,h,t) * hours(h) } - sum{(ortype,v,h)$[Sw_OpRes$opres_h(h)$reserve_frac(i,ortype)$valgen(i,v,r,t)], @@ -1013,10 +1013,10 @@ revenue_en(rev_cat,i,r,t) revenue_en(rev_cat,i,r,t) $[tmodel_new(t) - $sum{(v,h,c)$[valcap(i,v,r,t)$i_c(i,c)], m_cf(i,c,v,r,h,t) * CAP.l(i,v,r,t) } + $sum{(v,h,c)$valcap_class(i,c,v,r,t), m_cf(i,c,v,r,h,t) * CAP_CLASS.l(i,c,v,r,t) } $vre(i)] = - revenue(rev_cat,i,r,t) / sum{(v,h,c)$[valcap(i,v,r,t)$i_c(i,c)], - m_cf(i,c,v,r,h,t) * CAP.l(i,v,r,t) * hours(h) } ; + revenue(rev_cat,i,r,t) / sum{(v,h,c)$valcap_class(i,c,v,r,t), + m_cf(i,c,v,r,h,t) * CAP_CLASS.l(i,c,v,r,t) * hours(h) } ; revenue_en_nat(rev_cat,i,t) $[tmodel_new(t) @@ -1026,10 +1026,10 @@ revenue_en_nat(rev_cat,i,t) revenue_en_nat(rev_cat,i,t) $[tmodel_new(t) - $sum{(v,r,h,c)$[valcap(i,v,r,t)$i_c(i,c)], m_cf(i,c,v,r,h,t) * CAP.l(i,v,r,t) } + $sum{(v,r,h,c)$valcap_class(i,c,v,r,t), m_cf(i,c,v,r,h,t) * CAP_CLASS.l(i,c,v,r,t) } $vre(i)] = - revenue_nat(rev_cat,i,t) / sum{(v,r,h,c)$[valcap(i,v,r,t)$i_c(i,c)], - m_cf(i,c,v,r,h,t) * CAP.l(i,v,r,t) * hours(h) } ; + revenue_nat(rev_cat,i,t) / sum{(v,r,h,c)$valcap_class(i,c,v,r,t), + m_cf(i,c,v,r,h,t) * CAP_CLASS.l(i,c,v,r,t) * hours(h) } ; revenue_cap(rev_cat,i,r,t)$[tmodel_new(t)$cap_out(i,r,t)] = revenue(rev_cat,i,r,t) / cap_out(i,r,t) ; From 92e9cc45750d6d6d1fb8496b3718349bbd3d8108 Mon Sep 17 00:00:00 2001 From: wcole Date: Thu, 3 Sep 2026 12:50:04 -0600 Subject: [PATCH 02/30] Add class to INV_REFURB and m_cf_filt. --- reeds/core/setup/c_model.gms | 20 ++++++++++---------- reeds/core/setup/d_objective.gms | 8 ++++---- reeds/core/solve/5_varfix.gms | 3 ++- reeds/core/solve/6_data_dump.gms | 8 ++++---- reeds/core/terminus/report.gms | 32 ++++++++++++++++---------------- 5 files changed, 36 insertions(+), 35 deletions(-) diff --git a/reeds/core/setup/c_model.gms b/reeds/core/setup/c_model.gms index f79022d53..042bd131d 100644 --- a/reeds/core/setup/c_model.gms +++ b/reeds/core/setup/c_model.gms @@ -37,7 +37,7 @@ positive variables EXTRA_PRESCRIP_ENERGY(i,v,r,t) "--MWh-- builds beyond those prescribed battery energy capacity once allowed in firstyear(i)" INV_CAP_UP(i,v,r,rscbin,t) "--MW-- upsized generation capacity addition in year t" INV_ENER_UP(i,v,r,rscbin,t) "--MW-- upsized energy addition in year t using capacity factor to convert to capacity units" - INV_REFURB(i,v,r,t) "--MW-- investment in refurbishments of technologies that use a resource supply curve" + INV_REFURB(i,c,v,r,t) "--MW-- investment in refurbishments of technologies that use a resource supply curve" INV_RSC(i,c,v,r,rscbin,t) "--MW-- investment in technologies that use a resource supply curve" UPGRADES(i,v,r,t) "--MW-- investments in upgraded capacity from ii to i" UPGRADES_RETIRE(i,v,r,t) "--MW-- upgrades that have been retired - used as a free slack variable in eq_cap_upgrade" @@ -694,7 +694,7 @@ eq_cap_new_noret(i,v,r,t)$[valcap(i,v,r,t)$tmodel(t)$newv(v)$(not upgrade(i)) $(not retiretech(i,v,r,t))$(not Sw_PCM)].. sum{tt$[inv_cond(i,v,r,t,tt)$(tmodel(tt) or tfix(tt))$valcap(i,v,r,tt)], - degrade(i,tt,t) * (INV(i,v,r,tt) + INV_REFURB(i,v,r,tt)$[refurbtech(i)$Sw_Refurb]) + degrade(i,tt,t) * (INV(i,v,r,tt) + sum{c$i_c(i,c), INV_REFURB(i,c,v,r,tt) }$[refurbtech(i)$Sw_Refurb]) } - sum{(tt,ttt)$[inv_cond(i,v,r,tt,ttt)$(tmodel(tt) or tfix(tt))$valcap(i,v,r,ttt)$(tt.val>=ttt.val)$(t.val>=tt.val)], @@ -751,7 +751,7 @@ eq_cap_new_retub(i,v,r,t)$[valcap(i,v,r,t)$tmodel(t)$newv(v)$(not upgrade(i)) $retiretech(i,v,r,t)$(not Sw_PCM)].. sum{tt$[inv_cond(i,v,r,t,tt)$(tmodel(tt) or tfix(tt))$valcap(i,v,r,tt)], - degrade(i,tt,t) * (INV(i,v,r,tt) + INV_REFURB(i,v,r,tt)$[refurbtech(i)$Sw_Refurb]) + degrade(i,tt,t) * (INV(i,v,r,tt) + sum{c$i_c(i,c), INV_REFURB(i,c,v,r,tt) }$[refurbtech(i)$Sw_Refurb]) } - sum{(tt,ttt)$[inv_cond(i,v,r,tt,ttt)$(tmodel(tt) or tfix(tt))$valcap(i,v,r,ttt)$(tt.val>=ttt.val)$(t.val>=tt.val)], @@ -802,7 +802,7 @@ eq_cap_new_retmo(i,v,r,t)$[valcap(i,v,r,t)$tmodel(t)$newv(v)$(not upgrade(i)) + INV(i,v,r,t)$valinv(i,v,r,t) - + INV_REFURB(i,v,r,t)$[valinv(i,v,r,t)$refurbtech(i)$Sw_Refurb] + + sum{c$i_c(i,c), INV_REFURB(i,c,v,r,t) }$[valinv(i,v,r,t)$refurbtech(i)$Sw_Refurb] * Account for capacity upsizing within new vintages + sum{rscbin$allow_cap_up(i,v,r,rscbin,t), INV_CAP_UP(i,v,r,rscbin,t) } @@ -948,7 +948,7 @@ eq_forceprescription_power(i,newv,r,t) *capacity built in the current period or prior - INV(i,newv,r,t) + INV_REFURB(i,newv,r,t)$[refurbtech(i)$Sw_Refurb] + INV(i,newv,r,t) + sum{c$i_c(i,c), INV_REFURB(i,c,newv,r,t) }$[refurbtech(i)$Sw_Refurb] =e= @@ -1012,8 +1012,8 @@ eq_refurblim(i,r,t)$[tmodel(t)$refurbtech(i)$Sw_Refurb$(not Sw_PCM)].. *must exceed the total sum of investments in refurbishments *that have yet to expire - implying an investment can be refurbished more than once *if the first refurbishment has exceed its age limit - sum{(vv,tt)$[inv_cond(i,vv,r,t,tt)$(tmodel(tt) or tfix(tt))$valinv(i,vv,r,tt)], - INV_REFURB(i,vv,r,tt) + sum{(c,vv,tt)$[i_c(i,c)$inv_cond(i,vv,r,t,tt)$(tmodel(tt) or tfix(tt))$valinv(i,vv,r,tt)], + INV_REFURB(i,c,vv,r,tt) } ; @@ -1400,7 +1400,7 @@ eq_interconnection_queues(tg,r,t) sum{(i,newv,tt)$[valinv(i,newv,r,tt)$tg_i(tg,i) $(yeart(tt)>=interconnection_start) $(tmodel(tt) or tfix(tt))], - INV(i,newv,r,tt) + INV_REFURB(i,newv,r,tt)$[refurbtech(i)$Sw_Refurb] } + INV(i,newv,r,tt) + sum{c$i_c(i,c), INV_REFURB(i,c,newv,r,tt) }$[refurbtech(i)$Sw_Refurb] } ; *=============================== @@ -1732,7 +1732,7 @@ eq_reserve_margin(r,ccseason,t) *only used in sequential solve case (otherwise m_cc_mar = 0) *Note: new distpv is included with cc_old + sum{(i,v)$[(vre(i) or csp(i) or pvb(i))$valinv(i,v,r,t)$(not forced_retire(i,r,t))], - m_cc_mar(i,r,ccseason,t) * (INV(i,v,r,t) + INV_REFURB(i,v,r,t)$[refurbtech(i)$Sw_Refurb]) + m_cc_mar(i,r,ccseason,t) * (INV(i,v,r,t) + sum{c$i_c(i,c), INV_REFURB(i,c,v,r,t) }$[refurbtech(i)$Sw_Refurb]) } *[plus] firm capacity contribution from all binned storage capacity @@ -2819,7 +2819,7 @@ eq_RPS_OFSWind(st,t)$[tmodel(t)$stfeas(st)$offshore_cap_req(st,t)$Sw_StateRPS * investments over time + sum{(i,v,r,tt)$[r_st(r,st)$ofswind(i)$inv_cond(i,v,r,t,tt)$(tmodel(tt) or tfix(tt))], - INV(i,v,r,tt) + INV_REFURB(i,v,r,tt)$[refurbtech(i)$Sw_Refurb] } + INV(i,v,r,tt) + sum{c$i_c(i,c), INV_REFURB(i,c,v,r,tt) }$[refurbtech(i)$Sw_Refurb] } =g= diff --git a/reeds/core/setup/d_objective.gms b/reeds/core/setup/d_objective.gms index 1298021be..51cb27c6d 100644 --- a/reeds/core/setup/d_objective.gms +++ b/reeds/core/setup/d_objective.gms @@ -77,7 +77,7 @@ eq_ObjFn_inv(t)$tmodel(t).. * --- cost of water access--- + [ (8760/1E6) * sum{ (i,v,w,r)$[i_w(i,w)$valinv(i,v,r,t)], sum{wst$i_wst(i,wst), m_watsc_dat(wst,"cost",r,t) } * water_rate(i,w) * - ( INV(i,v,r,t) + INV_REFURB(i,v,r,t)$[refurbtech(i)$Sw_Refurb] ) } + ( INV(i,v,r,t) + sum{c$i_c(i,c), INV_REFURB(i,c,v,r,t) }$[refurbtech(i)$Sw_Refurb] ) } + sum{(rscbin,i,c,v,r)$[i_c(i,c)$m_rscfeas(r,i,c,rscbin)$psh(i)], sum{wst$i_wst(i,wst), m_watsc_dat(wst,"cost",r,t) } * INV_RSC(i,c,v,r,rscbin,t) * water_req_psh(r,rscbin) }$Sw_PSHwatercon @@ -88,8 +88,8 @@ eq_ObjFn_inv(t)$tmodel(t).. + sum{(wst,r), 1E6 * WATER_CAPACITY_LIMIT_SLACK(wst,r,t) }$[Sw_WaterMain$Sw_WaterCapacity] * --- cost of refurbishments of RSC tech--- - + sum{(i,v,r)$[Sw_Refurb$valinv(i,v,r,t)$refurbtech(i)], - cost_cap_fin_mult(i,r,t) * cost_cap(i,t) * INV_REFURB(i,v,r,t) + + sum{(i,c,v,r)$[i_c(i,c)$Sw_Refurb$valinv(i,v,r,t)$refurbtech(i)], + cost_cap_fin_mult(i,r,t) * cost_cap(i,t) * INV_REFURB(i,c,v,r,t) } * --- cost of interzonal AC transmission--- @@ -213,7 +213,7 @@ eq_Objfn_op(t)$tmodel(t).. cost_fom(i,v,r,t) * retire_penalty(t) * (CAP(i,v,r,t) - INV(i,v,r,t)$valinv(i,v,r,t) - - INV_REFURB(i,v,r,t)$[valinv(i,v,r,t)$refurbtech(i)$Sw_Refurb] + - sum{c$i_c(i,c), INV_REFURB(i,c,v,r,t) }$[valinv(i,v,r,t)$refurbtech(i)$Sw_Refurb] - UPGRADES(i,v,r,t)$[upgrade(i)$Sw_Upgrades] ) } diff --git a/reeds/core/solve/5_varfix.gms b/reeds/core/solve/5_varfix.gms index 63da328cc..29998ad9a 100644 --- a/reeds/core/solve/5_varfix.gms +++ b/reeds/core/solve/5_varfix.gms @@ -8,6 +8,7 @@ if(Sw_RemoveSmallNumbers = 1, CAP_ABOVE_LIM.l(tg,r,tfix)$[abs(CAP_ABOVE_LIM.l(tg,r,tfix)) < rhs_tolerance] = 0 ; INV.l(i,v,r,tfix)$[abs(INV.l(i,v,r,tfix)) < rhs_tolerance] = 0 ; INV_ENERGY.l(i,v,r,tfix)$[abs(INV_ENERGY.l(i,v,r,tfix)) < rhs_tolerance] = 0 ; + INV_REFURB.l(i,c,v,r,tfix)$[abs(INV_REFURB.l(i,c,v,r,tfix)) < rhs_tolerance] = 0 ; INV_RSC.l(i,c,v,r,rscbin,tfix)$[abs(INV_RSC.l(i,c,v,r,rscbin,tfix)) < rhs_tolerance] = 0 ; INV_POI.l(r,tfix)$[abs(INV_POI.l(r,tfix)) < rhs_tolerance] = 0 ; H2_STOR_INV.l(h2_stor,r,tfix)$[abs(H2_STOR_INV.l(h2_stor,r,tfix)) < rhs_tolerance] = 0 ; @@ -36,7 +37,7 @@ CAP_SDBIN_ENERGY.fx(i,v,r,ccseason,sdbin,tfix)$[valcap(i,v,r,tfix)$battery(i)$Sw GROWTH_BIN.fx(gbin,i,st,tfix)$[sum{r$[r_st(r,st)], valinv_irt(i,r,tfix) }$stfeas(st)$Sw_GrowthPenalties$(yeart(tfix)<=Sw_GrowthPenLastYear)] = GROWTH_BIN.l(gbin,i,st,tfix) ; INV.fx(i,v,r,tfix)$[valinv(i,v,r,tfix)] = INV.l(i,v,r,tfix) ; INV_ENERGY.fx(i,v,r,tfix)$[valinv(i,v,r,tfix)$battery(i)] = INV_ENERGY.l(i,v,r,tfix) ; -INV_REFURB.fx(i,v,r,tfix)$[valinv(i,v,r,tfix)$refurbtech(i)] = INV_REFURB.l(i,v,r,tfix) ; +INV_REFURB.fx(i,c,v,r,tfix)$[i_c(i,c)$valinv(i,v,r,tfix)$refurbtech(i)] = INV_REFURB.l(i,c,v,r,tfix) ; INV_RSC.fx(i,c,v,r,rscbin,tfix)$[i_c(i,c)$valinv(i,v,r,tfix)$rsc_i(i)$m_rscfeas(r,i,c,rscbin)] = INV_RSC.l(i,c,v,r,rscbin,tfix) ; CAP_RSC.fx(i,c,v,r,rscbin,tfix)$[i_c(i,c)$valcap(i,v,r,tfix)$rsc_i(i)$m_rscfeas(r,i,c,rscbin)] = CAP_RSC.l(i,c,v,r,rscbin,tfix) ; INV_CAP_UP.fx(i,v,r,rscbin,tfix)$[allow_cap_up(i,v,r,rscbin,tfix)] = INV_CAP_UP.l(i,v,r,rscbin,tfix) ; diff --git a/reeds/core/solve/6_data_dump.gms b/reeds/core/solve/6_data_dump.gms index 3dfb9f515..9f8239ea7 100644 --- a/reeds/core/solve/6_data_dump.gms +++ b/reeds/core/solve/6_data_dump.gms @@ -62,7 +62,7 @@ h2_usage_regional(r,allh,t) "--metric tons-- H2 usage by region" inv_cond_filt(i,v,t) "--set-- vintage-year mapping for investments by technology" inv_ivrt(i,v,r,t) "--MW-- investments in power generation capacity" inv_energy_ivrt(i,v,r,t) "--MWh-- investments in energy generation capacity" -m_cf_filt(i,v,r,allh) "--fraction-- capacity factor used in the model" +m_cf_filt(i,c,v,r,allh) "--fraction-- capacity factor used in the model" m_cf_szn_filt(i,v,r,allszn) "--fraction-- modelled capacity factors filtered for hydro resources to set seasonal energy constraints" minloadfrac_filt(r,i,allszn) "--fraction-- modelled mingen fraction filtered for hydro resources to set mingen constraints" prod_filt(i,v,r,allh) "--MW-- power consumed for PRODUCE.l" @@ -123,10 +123,10 @@ cap_energy_ivrt(i,v,r,t)$[valcap(i,v,r,t)$trange(t)$battery(i)] = CAP_ENERGY.l(i cap_ivrt(i,v,r,t)$([upv(i) or wind(i)]$valcap(i,v,r,t)) = m_capacity_exog(i,v,r,t)$trange(t) + sum{tt$[inv_cond(i,v,r,t,tt)$trange(tt)], - INV.l(i,v,r,tt) + INV_REFURB.l(i,v,r,tt)$[refurbtech(i)$Sw_Refurb]} ; + INV.l(i,v,r,tt) + sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,tt) }$[refurbtech(i)$Sw_Refurb]} ; cap_init(i,v,r)$([not distpv(i)]$valcap_ivr(i,v,r)) = sum{t$tcur(t), cap_ivrt(i,v,r,t)$initv(v) } ; cap_init(i,v,r)$(distpv(i)$valcap_ivr(i,v,r)) = sum{t$tfirst(t), cap_ivrt(i,v,r,t) } ; -inv_ivrt(i,v,r,t)$[valcap(i,v,r,t)$trange(t)] = [INV.l(i,v,r,t) + INV_REFURB.l(i,v,r,t)]$valinv(i,v,r,t) + UPGRADES.l(i,v,r,t)$[upgrade(i)$valcap(i,v,r,t)$Sw_Upgrades] ; +inv_ivrt(i,v,r,t)$[valcap(i,v,r,t)$trange(t)] = [INV.l(i,v,r,t) + sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) }]$valinv(i,v,r,t) + UPGRADES.l(i,v,r,t)$[upgrade(i)$valcap(i,v,r,t)$Sw_Upgrades] ; inv_energy_ivrt(i,v,r,t)$[valcap(i,v,r,t)$trange(t)$battery(i)] = INV_ENERGY.l(i,v,r,t); inv_ivrt("distpv",v,r,t)$([trange(t)$(not tfirst(t))]$valcap("distpv",v,r,t)) = cap_ivrt("distpv",v,r,t) - sum{tt$tprev(t,tt), cap_ivrt("distpv",v,r,tt) } ; inv_ivrt("distpv","init-1",r,"%next_year%") = inv_distpv(r,"%next_year%") ; @@ -228,7 +228,7 @@ heat_rate_filt(i,v,r)$cap_exist(i,v,r) = sum{t$tcur(t), heat_rate(i,v,r,t) } ; inv_cond_filt(i,v,t)$[(vre(i) or pvb(i))$tnext(t)] = sum{(tt,r), inv_cond(i,v,r,tt,t) } ; -m_cf_filt(i,v,r,h)$[(vre(i) or pvb(i))$cap_exist(i,v,r)] = sum{(t,c)$[tnext(t)$i_c(i,c)], m_cf(i,c,v,r,h,t) } ; +m_cf_filt(i,c,v,r,h)$[i_c(i,c)$(vre(i) or pvb(i))$cap_exist(i,v,r)] = sum{t$tnext(t), m_cf(i,c,v,r,h,t) } ; m_cf_szn_filt(i,v,r,szn)$[hydro(i)$cap_exist(i,v,r)] = sum{t$tcur(t), m_cf_szn(i,v,r,szn,t) } ; diff --git a/reeds/core/terminus/report.gms b/reeds/core/terminus/report.gms index 53c451623..ab51b76ec 100644 --- a/reeds/core/terminus/report.gms +++ b/reeds/core/terminus/report.gms @@ -709,7 +709,7 @@ watcap_new_out(i,r,t)$[valcap_irt(i,r,t)$i_water_cooling(i)] = hours(h) * sum{w$[i_w(i,w)], water_rate(i,w) } - * ( sum{v$valinv(i,v,r,t), INV.l(i,v,r,t) + INV_REFURB.l(i,v,r,t)} + * ( sum{v$valinv(i,v,r,t), INV.l(i,v,r,t) + sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) }} + sum{v$valcap(i,v,r,t), (1-upgrade_derate(i,v,r,t)) * (UPGRADES.l(i,v,r,t) - UPGRADES_RETIRE.l(i,v,r,t))}$[upgrade(i)$Sw_Upgrades] ) * (1 + sum{(v,szn), h_szn(h,szn) * seas_cap_frac_delta(i,v,r,szn,t)}) @@ -721,7 +721,7 @@ watcap_new_ivrt(i,v,r,t)$[valcap(i,v,r,t)$i_water_cooling(i)] = hours(h) * sum{w$[i_w(i,w)], water_rate(i,w) } - * ( [INV.l(i,v,r,t) + INV_REFURB.l(i,v,r,t)]$valinv(i,v,r,t) + * ( [INV.l(i,v,r,t) + sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) }]$valinv(i,v,r,t) + [(1-upgrade_derate(i,v,r,t)) * (UPGRADES.l(i,v,r,t) - UPGRADES_RETIRE.l(i,v,r,t))]$[upgrade(i)$valcap(i,v,r,t)$Sw_Upgrades] ) * (1 + sum{szn, h_szn(h,szn) * seas_cap_frac_delta(i,v,r,szn,t)}) } / 1E6 @@ -758,7 +758,7 @@ opres_trade(ortype,r,rr,t)$[opres_routes(r,rr,t)$tmodel_new(t)] = *========================= gen_new_uncurt(i,r,h,t)$[(vre(i) or storage_hybrid(i)$(not csp(i)))$valcap_irt(i,r,t)] = - sum{(v,c)$[valinv(i,v,r,t)$i_c(i,c)], (INV.l(i,v,r,t) + INV_REFURB.l(i,v,r,t)) * m_cf(i,c,v,r,h,t) * hours(h) } + sum{(v,c)$[i_c(i,c)$valinv(i,v,r,t)], (INV.l(i,v,r,t) + INV_REFURB.l(i,c,v,r,t)) * m_cf(i,c,v,r,h,t) * hours(h) } ; * curtailment = (availability - generation - operating reserves) @@ -820,7 +820,7 @@ cap_ivrt(i,v,r,t)$[(not (upv(i) or wind(i)))$valcap(i,v,r,t)] = cap_deg_ivrt(i,v cap_ivrt(i,v,r,t)$[(upv(i) or wind(i))$valcap(i,v,r,t)] = ( m_capacity_exog(i,v,r,t)$tmodel_new(t) + sum{tt$[inv_cond(i,v,r,t,tt)$[tmodel(tt) or tfix(tt)]], - INV.l(i,v,r,tt) + INV_REFURB.l(i,v,r,tt)$[refurbtech(i)$Sw_Refurb]}) / ilr(i) ; + INV.l(i,v,r,tt) + sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,tt) }$[refurbtech(i)$Sw_Refurb]}) / ilr(i) ; cap_out(i,r,t)$[valcap_irt(i,r,t)$tmodel_new(t)] = sum{v$valcap(i,v,r,t), cap_ivrt(i,v,r,t) } ; * A small amount of upv capacity is actually csp-ns, so convert it back now. @@ -843,7 +843,7 @@ cap_exog(i,v,r,t)$tmodel_new(t) = m_capacity_exog(i,v,r,t) ; cap_new_out(i,r,t)$[valcap_irt(i,r,t)] = [ sum{v$valinv(i,v,r,t), - INV.l(i,v,r,t) + INV_REFURB.l(i,v,r,t) } + INV.l(i,v,r,t) + sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) } } + sum{v$valcap(i,v,r,t), (1 - upgrade_derate(i,v,r,t)) * (UPGRADES.l(i,v,r,t) - UPGRADES_RETIRE.l(i,v,r,t))}$[upgrade(i)$Sw_Upgrades] ] / ilr(i) ; @@ -857,12 +857,12 @@ cap_new_bin_out(i,v,r,t,rscbin)$[rsc_i(i)$valinv(i,v,r,t)] = sum{c$i_c(i,c), INV_RSC.l(i,c,v,r,rscbin,t) } / ilr(i) ; cap_new_bin_out(i,v,r,t,"bin1")$[(not rsc_i(i))$valinv(i,v,r,t)] = INV.l(i,v,r,t) / ilr(i) ; cap_new_ivrt(i,v,r,t)$[valcap(i,v,r,t)] = [ - [INV.l(i,v,r,t) + INV_REFURB.l(i,v,r,t)]$valinv(i,v,r,t) + [INV.l(i,v,r,t) + sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) }]$valinv(i,v,r,t) + [(1-upgrade_derate(i,v,r,t)) * (UPGRADES.l(i,v,r,t) - UPGRADES_RETIRE.l(i,v,r,t))]$[upgrade(i)$valcap(i,v,r,t)$Sw_Upgrades] ] / ilr(i) ; cap_new_ivrt("distpv",v,r,t)$[tfirst(t)$valcap("distpv",v,r,t)] = cap_ivrt("distpv",v,r,t) ; cap_new_ivrt("distpv",v,r,t)$[(not tfirst(t))$valcap("distpv",v,r,t)] = cap_ivrt("distpv",v,r,t) - sum{tt$tprev(t,tt), cap_ivrt("distpv",v,r,tt) } ; -cap_new_ivrt_refurb(i,v,r,t)$valinv(i,v,r,t) = INV_REFURB.l(i,v,r,t) / ilr(i) ; +cap_new_ivrt_refurb(i,v,r,t)$valinv(i,v,r,t) = sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) } / ilr(i) ; * Capacity by reV site site_spurinv(x,t)$[tmodel_new(t)$xfeas(x)] = INV_SPUR.l(x,t) ; @@ -965,7 +965,7 @@ cap_firm(i,r,ccseason,t)$[valcap_irt(i,r,t)$[not consume(i)]$tmodel_new(t)$Sw_PR } + cc_old(i,r,ccseason,t) + sum{v$[(vre(i) or csp(i) or storage_hybrid(i)$(not csp(i)))$valinv(i,v,r,t)], - m_cc_mar(i,r,ccseason,t) * (INV.l(i,v,r,t) + INV_REFURB.l(i,v,r,t)$[refurbtech(i)$Sw_Refurb]) } + m_cc_mar(i,r,ccseason,t) * (INV.l(i,v,r,t) + sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) }$[refurbtech(i)$Sw_Refurb]) } + sum{v$[(vre(i) or csp(i) or storage_hybrid(i)$(not csp(i)))$valcap(i,v,r,t)], cc_int(i,v,r,ccseason,t) * CAP.l(i,v,r,t) } + cc_excess(i,r,ccseason,t)$[(vre(i) or csp(i) or storage_hybrid(i)$(not csp(i)))] @@ -1042,9 +1042,9 @@ revenue_cap_nat(rev_cat,i,t)$[tmodel_new(t)$sum{r$valcap_irt(i,r,t), cap_out(i,r *======================================== valnew('MW',i,r,t)$[(not tfirst(t))$valcap_irt(i,r,t)] = - sum{v$valinv(i,v,r,t), INV.l(i,v,r,t) + INV_REFURB.l(i,v,r,t) } / ilr(i) ; + sum{v$valinv(i,v,r,t), INV.l(i,v,r,t) + sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) } } / ilr(i) ; valnew('inv_cap_ratio',i,r,t)$[valnew('MW',i,r,t)] = - sum{v$[valinv(i,v,r,t)$CAP.l(i,v,r,t)], (INV.l(i,v,r,t) + INV_REFURB.l(i,v,r,t)) + sum{v$[valinv(i,v,r,t)$CAP.l(i,v,r,t)], (INV.l(i,v,r,t) + sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) }) / CAP.l(i,v,r,t) } ; valnew('MWh',i,r,t)$[valnew('MW',i,r,t)] = sum{v$valinv(i,v,r,t), gen_ivrt(i,v,r,t)} * valnew('inv_cap_ratio',i,r,t) ; @@ -1223,7 +1223,7 @@ capex_ivrt(i,v,r,t)$valcap(i,v,r,t) = INV.l(i,v,r,t) * (cost_cap_fin_mult_no_credits(i,r,t) * cost_cap(i,t) ) + INV_ENERGY.l(i,v,r,t) * (cost_cap_fin_mult_no_credits(i,r,t) * cost_cap_energy(i,t) ) + sum{(c,rscbin)$[i_c(i,c)$m_rscfeas(r,i,c,rscbin)],INV_RSC.l(i,c,v,r,rscbin,t) * m_rsc_dat(r,i,c,rscbin,"cost") * cost_cap_fin_mult_no_credits(i,r,t) } - + (INV_REFURB.l(i,v,r,t) * (cost_cap_fin_mult_no_credits(i,r,t) * cost_cap(i,t)))$[refurbtech(i)$Sw_Refurb] + + (sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) } * (cost_cap_fin_mult_no_credits(i,r,t) * cost_cap(i,t)))$[refurbtech(i)$Sw_Refurb] + UPGRADES.l(i,v,r,t) * (cost_upgrade(i,v,r,t) * cost_cap_fin_mult_no_credits(i,r,t))$[upgrade(i)$Sw_Upgrades] ; *========================= @@ -1315,13 +1315,13 @@ systemcost_techba("inv_investment_capacity_costs",i,r,t)$[tmodel_new(t)$consume( systemcost_techba("inv_investment_refurbishment_capacity",i,r,t)$tmodel_new(t) = *costs of refurbishments of RSC tech (without the subtraction of any ITC/PTC value) + sum{v$[Sw_Refurb$valinv(i,v,r,t)$refurbtech(i)], - (cost_cap_fin_mult_noITC(i,r,t) * cost_cap(i,t)) * INV_REFURB.l(i,v,r,t) } + (cost_cap_fin_mult_noITC(i,r,t) * cost_cap(i,t)) * sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) } } ; systemcost_techba("inv_itc_payments_negative_refurbishments",i,r,t)$tmodel_new(t) = *costs of refurbishments of RSC tech (including reduction from ITC) + sum{v$[Sw_Refurb$valinv(i,v,r,t)$refurbtech(i)], - (cost_cap_fin_mult_out(i,r,t) * cost_cap(i,t)) * INV_REFURB.l(i,v,r,t) } + (cost_cap_fin_mult_out(i,r,t) * cost_cap(i,t)) * sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) } } *minus capacity costs without ITC - systemcost_techba("inv_investment_refurbishment_capacity",i,r,t) ; @@ -1329,7 +1329,7 @@ systemcost_techba("inv_itc_payments_negative_refurbishments",i,r,t)$tmodel_new(t systemcost_techba("inv_investment_water_access",i,r,t)$tmodel_new(t) = *cost of water access + (8760/1E6) * sum{ (v,w)$[i_w(i,w)$valinv(i,v,r,t)], sum{wst$i_wst(i,wst), m_watsc_dat(wst,"cost",r,t)} * water_rate(i,w) * - ( INV.l(i,v,r,t) + INV_REFURB.l(i,v,r,t)$[refurbtech(i)$Sw_Refurb] ) } + ( INV.l(i,v,r,t) + sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) }$[refurbtech(i)$Sw_Refurb] ) } + sum{(rscbin,c,v)$[i_c(i,c)$m_rscfeas(r,i,c,rscbin)$psh(i)], sum{wst$i_wst(i,wst), m_watsc_dat(wst,"cost",r,t) } * ( INV_RSC.l(i,c,v,r,rscbin,t) * water_req_psh(r,rscbin) ) } ; @@ -1708,7 +1708,7 @@ error_check('z') = ( * Retirement penalty - pvf_onm(t) * sum{(i,v,r)$[valcap(i,v,r,t)$retiretech(i,v,r,t)$Sw_RetirePenalty], cost_fom(i,v,r,t) * retire_penalty(t) - * (CAP.l(i,v,r,t) - INV.l(i,v,r,t) - INV_REFURB.l(i,v,r,t)$[refurbtech(i)$Sw_Refurb] - UPGRADES.l(i,v,r,t)$[upgrade(i)$Sw_Upgrades]) } + * (CAP.l(i,v,r,t) - INV.l(i,v,r,t) - sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) }$[refurbtech(i)$Sw_Refurb] - UPGRADES.l(i,v,r,t)$[upgrade(i)$Sw_Upgrades]) } * Hurdle costs + pvf_onm(t) * sum{(r,rr,trtype)$cost_hurdle(r,rr,t), tran_hurdle_cost_ann(r,rr,trtype,t) } * Penalty cost for dropped/excess load before Sw_StartMarkets @@ -1762,7 +1762,7 @@ error_check('z') = ( * (cost_cap_fin_mult(i,r,t) - cost_cap_fin_mult_out(i,r,t)) } + sum{(i,v,r)$[Sw_Refurb$valinv(i,v,r,t)$refurbtech(i)], - cost_cap(i,t) * INV_REFURB.l(i,v,r,t) + cost_cap(i,t) * sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) } * (cost_cap_fin_mult(i,r,t) - cost_cap_fin_mult_out(i,r,t)) } ) * account for penalty paid to deploy capacity beyond interconnection queue limits From 5c1a5e56700528b4c8cd0038c4775aaeb5904856 Mon Sep 17 00:00:00 2001 From: wcole Date: Thu, 3 Sep 2026 14:11:29 -0600 Subject: [PATCH 03/30] Propagate "c" through 6_data_dump.gms --- reeds/core/solve/6_data_dump.gms | 36 +++++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/reeds/core/solve/6_data_dump.gms b/reeds/core/solve/6_data_dump.gms index 9f8239ea7..c90f4b815 100644 --- a/reeds/core/solve/6_data_dump.gms +++ b/reeds/core/solve/6_data_dump.gms @@ -39,12 +39,12 @@ cap_exist(i,v,r) "--MW-- capacity that exists in the current s cap_exog_filt(i,v,r) "--MW-- exogenous capacity" cap_hyd_szn_adj_filt(i,allszn,r) "--fraction-- seasonal hydro capacity adjustment filtered for the previous solve year" cap_init(i,v,r) "--MW-- initial capacity" -cap_ivrt(i,v,r,t) "--MW-- generation power capacity" +cap_ivrt(i,c,v,r,t) "--MW-- generation power capacity" cap_energy_ivrt(i,v,r,t) "--MWh-- generation energy capacity" cap_pvb(i,v,r) "--MW-- Hybrid PV+battery capacity (PV)" cap_trans_energy(r,rr,trtype) "--MW-- transmission capacity for energy trading" cap_trans_prm(r,rr,trtype) "--MW-- transmission capacity for PRM trading" -cf_adj_t_filt(i,v,t) "--fraction-- capacity factor adjustment for wind" +cf_adj_t_filt(i,c,v,t) "--fraction-- capacity factor adjustment for wind" cost_cap_filt(i,t) "--2004$/MW-- technology capital costs" cost_cap_fin_mult_filt(i,r,t) "--unitless-- capital cost financial multipliers" cost_vom_filt(i,v,r) "--$/MWh-- VO&M costs filtered for the previous solve year and existing capacity" @@ -60,7 +60,7 @@ gen_h_stress_filt(i,r,allh,t) "--MW-- generation by stress timeslice with c heat_rate_filt(i,v,r) "--MMBtu/MWh-- heat rate" h2_usage_regional(r,allh,t) "--metric tons-- H2 usage by region" inv_cond_filt(i,v,t) "--set-- vintage-year mapping for investments by technology" -inv_ivrt(i,v,r,t) "--MW-- investments in power generation capacity" +inv_ivrt(i,c,v,r,t) "--MW-- investments in power generation capacity" inv_energy_ivrt(i,v,r,t) "--MWh-- investments in energy generation capacity" m_cf_filt(i,c,v,r,allh) "--fraction-- capacity factor used in the model" m_cf_szn_filt(i,v,r,allszn) "--fraction-- modelled capacity factors filtered for hydro resources to set seasonal energy constraints" @@ -118,20 +118,26 @@ cap_exist_ir(i,r)$valcap_ir_filt(i,r) = sum{v, cap_exist(i,v,r) } ; cap_exist_iv(i,v)$valcap_iv_filt(i,v) = sum{r, cap_exist(i,v,r) } ; cap_exist_i(i)$valcap_i_filt(i) = sum{(r,v), cap_exist(i,v,r) } ; -cap_ivrt(i,v,r,t)$([not (upv(i) or wind(i))]$valcap(i,v,r,t)$trange(t)) = CAP.l(i,v,r,t) ; +cap_ivrt(i,c,v,r,t)$[i_c(i,c)$(not (upv(i) or wind(i)))$valcap(i,v,r,t)$trange(t)] = CAP.l(i,v,r,t) ; +cap_ivrt(i,c,v,r,t)$[valcap_class(i,c,v,r,t)$(not (upv(i) or wind(i)))$trange(t)] = CAP_CLASS.l(i,c,v,r,t) ; cap_energy_ivrt(i,v,r,t)$[valcap(i,v,r,t)$trange(t)$battery(i)] = CAP_ENERGY.l(i,v,r,t) ; -cap_ivrt(i,v,r,t)$([upv(i) or wind(i)]$valcap(i,v,r,t)) = - m_capacity_exog(i,v,r,t)$trange(t) +cap_ivrt(i,c,v,r,t)$[i_c(i,c)$(upv(i) or wind(i))$valcap(i,v,r,t)] = + sum{rscbin, capacity_exog_rsc(i,c,v,r,rscbin,t) }$trange(t) + sum{tt$[inv_cond(i,v,r,t,tt)$trange(tt)], - INV.l(i,v,r,tt) + sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,tt) }$[refurbtech(i)$Sw_Refurb]} ; -cap_init(i,v,r)$([not distpv(i)]$valcap_ivr(i,v,r)) = sum{t$tcur(t), cap_ivrt(i,v,r,t)$initv(v) } ; -cap_init(i,v,r)$(distpv(i)$valcap_ivr(i,v,r)) = sum{t$tfirst(t), cap_ivrt(i,v,r,t) } ; -inv_ivrt(i,v,r,t)$[valcap(i,v,r,t)$trange(t)] = [INV.l(i,v,r,t) + sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) }]$valinv(i,v,r,t) + UPGRADES.l(i,v,r,t)$[upgrade(i)$valcap(i,v,r,t)$Sw_Upgrades] ; + sum{rscbin$m_rscfeas(r,i,c,rscbin), INV_RSC.l(i,c,v,r,rscbin,tt) } + + INV_REFURB.l(i,c,v,r,tt)$[refurbtech(i)$Sw_Refurb]} ; +cap_init(i,v,r)$([not distpv(i)]$valcap_ivr(i,v,r)) = sum{(c,t)$[i_c(i,c)$tcur(t)], cap_ivrt(i,c,v,r,t)$initv(v) } ; +cap_init(i,v,r)$(distpv(i)$valcap_ivr(i,v,r)) = sum{(c,t)$[i_c(i,c)$tfirst(t)], cap_ivrt(i,c,v,r,t) } ; +inv_ivrt(i,c,v,r,t)$[i_c(i,c)$valcap(i,v,r,t)$trange(t)] = + [INV.l(i,v,r,t)$(not rsc_i(i)) + + sum{rscbin$m_rscfeas(r,i,c,rscbin), INV_RSC.l(i,c,v,r,rscbin,t) }$rsc_i(i) + + INV_REFURB.l(i,c,v,r,t)]$valinv(i,v,r,t) + + UPGRADES.l(i,v,r,t)$[upgrade(i)$valcap(i,v,r,t)$Sw_Upgrades] ; inv_energy_ivrt(i,v,r,t)$[valcap(i,v,r,t)$trange(t)$battery(i)] = INV_ENERGY.l(i,v,r,t); -inv_ivrt("distpv",v,r,t)$([trange(t)$(not tfirst(t))]$valcap("distpv",v,r,t)) = cap_ivrt("distpv",v,r,t) - sum{tt$tprev(t,tt), cap_ivrt("distpv",v,r,tt) } ; -inv_ivrt("distpv","init-1",r,"%next_year%") = inv_distpv(r,"%next_year%") ; +inv_ivrt("distpv",c,v,r,t)$[i_c("distpv",c)$trange(t)$(not tfirst(t))$valcap("distpv",v,r,t)] = cap_ivrt("distpv",c,v,r,t) - sum{tt$tprev(t,tt), cap_ivrt("distpv",c,v,r,tt) } ; +inv_ivrt("distpv",c,"init-1",r,"%next_year%")$i_c("distpv",c) = inv_distpv(r,"%next_year%") ; -ret_ivrt(i,v,r,t)$([trange(t)$(not tfirst(t))$newv(v)]$valcap(i,v,r,t)) = sum{tt$tprev(t,tt), cap_ivrt(i,v,r,tt)} - cap_ivrt(i,v,r,t) + inv_ivrt(i,v,r,t) ; +ret_ivrt(i,v,r,t)$([trange(t)$(not tfirst(t))$newv(v)]$valcap(i,v,r,t)) = sum{(c,tt)$[i_c(i,c)$tprev(t,tt)], cap_ivrt(i,c,v,r,tt)} - sum{c$i_c(i,c), cap_ivrt(i,c,v,r,t)} + sum{c$i_c(i,c), inv_ivrt(i,c,v,r,t)} ; ret_ivrt(i,v,r,t)$([abs(ret_ivrt(i,v,r,t) < 1e-6)]$valcap(i,v,r,t)) = 0 ; ret(i,v,r)$valcap_ivr(i,v,r) = sum{t, ret_ivrt(i,v,r,t) } ; @@ -215,8 +221,8 @@ cost_cap_fin_mult_filt(i,r,t)$([storage_standalone(i)]) = cost_cap_fin_mult(i,r, cost_vom_filt(i,v,r)$cap_exist(i,v,r) = sum{t$tcur(t), cost_vom(i,v,r,t) } ; -cf_adj_t_filt(i,v,t)$[cap_exist_iv(i,v)$trange(t)] = sum{c$i_c(i,c), cf_adj_t(i,c,v,t) } ; -cf_adj_t_filt(i,v,"%next_year%") = sum{c$i_c(i,c), cf_adj_t(i,c,v,"%next_year%") }$(vre(i) or pvb(i)) ; +cf_adj_t_filt(i,c,v,t)$[i_c(i,c)$cap_exist_iv(i,v)$trange(t)] = cf_adj_t(i,c,v,t) ; +cf_adj_t_filt(i,c,v,"%next_year%")$i_c(i,c) = cf_adj_t(i,c,v,"%next_year%")$(vre(i) or pvb(i)) ; ctt_i_ii_filt(i,ii) = ctt_i_ii(i,ii)$cap_exist_i(i) ; From a7c08a55991c3350598836b88720c97a2cfe771e Mon Sep 17 00:00:00 2001 From: wcole Date: Thu, 3 Sep 2026 14:13:10 -0600 Subject: [PATCH 04/30] Add class to resources input. --- reeds/input_processing/recf.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/reeds/input_processing/recf.py b/reeds/input_processing/recf.py index 0d0fdbdaa..c556a5075 100644 --- a/reeds/input_processing/recf.py +++ b/reeds/input_processing/recf.py @@ -454,7 +454,9 @@ def main(reeds_path, inputs_case): # Set the column names for resources to match ReEDS resources['ccreg'] = resources.area.map(r2ccreg) resources.rename(columns={'area':'r','tech':'i'}, inplace=True) - resources = resources[['r','i','ccreg','resource']] + i2c = reeds.techs.get_class_map(inputs_case) + resources['c'] = resources['i'].map(i2c) + resources = resources[['r','i','c','ccreg','resource']] #%%### Concentrated solar thermal power (CSP) @@ -475,8 +477,9 @@ def main(reeds_path, inputs_case): .assign(i=csp_resources['tech'] + '_' + csp_resources['class'].astype(str)) .assign(resource=csp_resources['tech'] + '_' + csp_resources['resource']) .assign(ccreg=csp_resources.r.map(r2ccreg)) - [['i','r','resource','ccreg']] - ) + ) + csp_resources['c'] = csp_resources['i'].map(i2c) + csp_resources = csp_resources[['i','r','c','resource','ccreg']] ###### Simulate CSP dispatch for each design ### Get solar multiples sms = {tech: scalars[f'csp_sm_{tech.strip("csp")}'] for tech in csptechs} From 250e101da15e5a1c0a93a4d3c800befae5421644 Mon Sep 17 00:00:00 2001 From: wcole Date: Thu, 3 Sep 2026 14:13:49 -0600 Subject: [PATCH 05/30] Add class to relevant resource_adequacy parameters. --- reeds/resource_adequacy/prep_data.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/reeds/resource_adequacy/prep_data.py b/reeds/resource_adequacy/prep_data.py index fd63e86f1..f4b915873 100644 --- a/reeds/resource_adequacy/prep_data.py +++ b/reeds/resource_adequacy/prep_data.py @@ -196,14 +196,14 @@ def main(t, casedir, iteration=0): #%%### Nameplate capacity cap_ivr_realvint = ( gdxreeds['cap_ivrt'].loc[gdxreeds['cap_ivrt'].t==t].drop('t', axis=1) - .groupby(['i','v','r'], as_index=False).Value.sum() + .groupby(['i','c','v','r'], as_index=False).Value.sum() ) ### Reset the vintages of all storage units to 'new1' to reduce model size cap_storage_devint = cap_ivr_realvint.loc[ cap_ivr_realvint.i.isin(gdxreeds['storage_standalone'].i)].copy() cap_storage_devint['v'] = 'new1' cap_storage_devint = ( - cap_storage_devint.groupby(['i','v','r'], as_index=False).Value.sum()) + cap_storage_devint.groupby(['i','c','v','r'], as_index=False).Value.sum()) def _devint_storage(dfin): dfout = pd.concat([ @@ -237,7 +237,7 @@ def _devint_storage(dfin): vretechs_i = resources.i.str.lower().unique() cap_vre = ( cap_ivr.loc[cap_ivr.i.str.lower().isin(vretechs_i)] - .set_index(['i','v','r']).Value.copy() + .set_index(['i','c','v','r']).Value.copy() ) #%%### VRE generation, accounting for generation @@ -247,7 +247,7 @@ def _devint_storage(dfin): ### so fill missing values with 1, then drop rows with missing region (indicating no capacity) cf_adj_iv = ( gdxreeds['cf_adj_t_filt'].loc[gdxreeds['cf_adj_t_filt'].t==t].drop('t', axis=1) - .set_index(['i','v']).Value + .set_index(['i','c','v']).Value ) cap_vre_derated = cap_vre.multiply(cf_adj_iv, fill_value=1).reset_index().dropna() if len(cap_vre) != len(cap_vre_derated): @@ -440,7 +440,7 @@ def intify(v): ]) ### Nameplate capacity - max_cap = cap_nonloadtechs.set_index(['i','v','r']).Value.rename('MW') + max_cap = cap_nonloadtechs.drop(columns='c').set_index(['i','v','r']).Value.rename('MW') ## Drop VRE since it is handled through pras_vre_gen max_cap = max_cap.loc[ ~max_cap.index.get_level_values('i').str.startswith( From b97a64c62850fc44a2fcb264caed3312667f8384 Mon Sep 17 00:00:00 2001 From: wcole Date: Thu, 3 Sep 2026 14:23:24 -0600 Subject: [PATCH 06/30] Add class to reeds_to_rev.py inputs. --- hourlize/reeds_to_rev.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hourlize/reeds_to_rev.py b/hourlize/reeds_to_rev.py index 455d5a306..59c1ecb07 100644 --- a/hourlize/reeds_to_rev.py +++ b/hourlize/reeds_to_rev.py @@ -370,8 +370,8 @@ def get_new_investments(run_folder, tech): df_inv_rsc = pd.read_csv( inv_rsc, low_memory=False, - names=["tech", "vintage", "region", "year", "bin", "MW"], - usecols=["tech", "region", "year", "bin", "MW"], + names=["tech", "class", "vintage", "region", "year", "bin", "MW"], + usecols=["tech", "class", "region", "year", "bin", "MW"], header=0, ) df_inv_rsc = df_inv_rsc[df_inv_rsc["tech"].str.startswith(tech)].copy() @@ -593,9 +593,9 @@ def get_exogenous_capacity(run_folder, tech): df_cap_exog = pd.read_csv( cap_exog, low_memory=False, - names=["tech", "vintage", "region", "year", "MW"], + names=["tech", "class", "vintage", "region", "year", "MW"], header=0, - usecols=["tech", "region", "year", "MW"], + usecols=["tech", "class", "region", "year", "MW"], ) df_cap_exog = df_cap_exog[df_cap_exog["tech"].str.startswith(tech)].copy() From 8f3f31e3e4feddb0eabd053be89ddd3f3c19b9bd Mon Sep 17 00:00:00 2001 From: wcole Date: Thu, 3 Sep 2026 14:34:40 -0600 Subject: [PATCH 07/30] Add class to more outputs --- reeds/core/terminus/report.gms | 14 ++++++++------ reeds/core/terminus/report_params.csv | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/reeds/core/terminus/report.gms b/reeds/core/terminus/report.gms index ab51b76ec..9424ce2f9 100644 --- a/reeds/core/terminus/report.gms +++ b/reeds/core/terminus/report.gms @@ -835,7 +835,9 @@ cap_out("upv_5",r,t)$[cap_cspns(r,t)$tmodel_new(t)$(cap_out("upv_5",r,t) < 0)] = cap_nat(i,t)$tmodel_new(t) = sum{r, cap_out(i,r,t) } ; * Exogenous capacity (used by reeds_to_rev) -cap_exog(i,v,r,t)$tmodel_new(t) = m_capacity_exog(i,v,r,t) ; +cap_exog(i,c,v,r,t)$[i_c(i,c)$tmodel_new(t)] = m_capacity_exog(i,v,r,t) ; +cap_exog(i,c,v,r,t)$[i_c(i,c)$tmodel_new(t)$exog_rsc(i)] = + sum{rscbin, capacity_exog_rsc(i,c,v,r,rscbin,t) } ; *========================= * NEW CAPACITY @@ -853,9 +855,9 @@ cap_new_out("distpv",r,t)$[tfirst(t)$valcap_irt("distpv",r,t)] = cap_out("distpv cap_new_out("distpv",r,t)$[(not tfirst(t))$valcap_irt("distpv",r,t)] = cap_out("distpv",r,t) - sum{tt$tprev(t,tt), cap_out("distpv",r,tt) } ; cap_new_ann(i,r,t)$cap_new_out(i,r,t) = cap_new_out(i,r,t) / (yeart(t) - sum{tt$tprev(t,tt), yeart(tt) }) ; cap_new_ann_nat(i,t)$tmodel_new(t) = sum{r, cap_new_ann(i,r,t) } ; -cap_new_bin_out(i,v,r,t,rscbin)$[rsc_i(i)$valinv(i,v,r,t)] = - sum{c$i_c(i,c), INV_RSC.l(i,c,v,r,rscbin,t) } / ilr(i) ; -cap_new_bin_out(i,v,r,t,"bin1")$[(not rsc_i(i))$valinv(i,v,r,t)] = INV.l(i,v,r,t) / ilr(i) ; +cap_new_bin_out(i,c,v,r,t,rscbin)$[i_c(i,c)$rsc_i(i)$valinv(i,v,r,t)] = + INV_RSC.l(i,c,v,r,rscbin,t) / ilr(i) ; +cap_new_bin_out(i,c,v,r,t,"bin1")$[i_c(i,c)$(not rsc_i(i))$valinv(i,v,r,t)] = INV.l(i,v,r,t) / ilr(i) ; cap_new_ivrt(i,v,r,t)$[valcap(i,v,r,t)] = [ [INV.l(i,v,r,t) + sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) }]$valinv(i,v,r,t) + [(1-upgrade_derate(i,v,r,t)) * (UPGRADES.l(i,v,r,t) - UPGRADES_RETIRE.l(i,v,r,t))]$[upgrade(i)$valcap(i,v,r,t)$Sw_Upgrades] @@ -871,10 +873,10 @@ site_spurcap(x,t)$[tmodel_new(t)$xfeas(x)] = CAP_SPUR.l(x,t) ; site_cap(i,x,t)$[tmodel_new(t)$sum{(c,r,rscbin), spurline_sitemap(i,c,r,rscbin,x)}] = sum{(c,v,r,rscbin,tt) $[spurline_sitemap(i,c,r,rscbin,x) - $cap_new_bin_out(i,v,r,tt,rscbin) + $cap_new_bin_out(i,c,v,r,tt,rscbin) $(yeart(tt) <= yeart(t))], * Multiply by ILR to get DC capacity for PV - cap_new_bin_out(i,v,r,tt,rscbin) * ilr(i) + cap_new_bin_out(i,c,v,r,tt,rscbin) * ilr(i) } ; site_gir(i,x,t)$[site_cap(i,x,t)$site_spurcap(x,t)] = site_cap(i,x,t) / site_spurcap(x,t) ; diff --git a/reeds/core/terminus/report_params.csv b/reeds/core/terminus/report_params.csv index e30647243..1be9bcc10 100644 --- a/reeds/core/terminus/report_params.csv +++ b/reeds/core/terminus/report_params.csv @@ -10,7 +10,7 @@ param,units,comment,reeds2x,output_rename,input "state_cap_and_trade_quant(st,t)",metric tons,state annual CO2 cap constraints,,, "cap_above_limit(tg,r,t)",MW,Near-term capacity deployed above interconnection queue limit,,, "cap_avail(i,r,t,rscbin)",MW,Available capacity at beginning of model year for rsc techs,,, -"cap_exog(i,v,r,t)",MW,Exogenous capacity from m_capacity_exog; used by reeds_to_rev,,, +"cap_exog(i,c,v,r,t)",MW,Exogenous capacity from m_capacity_exog; used by reeds_to_rev,,, "cap_firm(i,r,ccseason,t)",MW,firm capacity that counts toward the reserve margin constraint by BA and season,,, "cap_nat(i,t)",MW,national capacity,,, "cap_new_cc(i,r,ccseason,t)",MW,"new capacity that is VRE, for new capacity credit calculation",,, @@ -22,7 +22,7 @@ param,units,comment,reeds2x,output_rename,input "cap_deg_ivrt(i,v,r,t)",MW,"Degraded capacity, equal to CAP.l",,, "cap_new_ann(i,r,t)",MW/yr,new annual capacity by region,,, "cap_new_ann_nat(i,t)",MW/yr,new annual capacity national,,, -"cap_new_bin_out(i,v,r,t,rscbin)",MW,capacity of built techs,,, +"cap_new_bin_out(i,c,v,r,t,rscbin)",MW,capacity of built techs,,, "cap_new_ivrt(i,v,r,t)",MW,new capacity,1,, "cap_new_ivrt_refurb(i,v,r,t)",MW,new refurbished capacity,,, "cap_new_out(i,r,t)",MW,"new capacity by region, which are investments and upgrades from one solve year to the next",,, From 65735b2c8f6826a6101a1ca44e080c478126be08 Mon Sep 17 00:00:00 2001 From: wcole Date: Thu, 3 Sep 2026 15:30:39 -0600 Subject: [PATCH 08/30] Add class to cap_ivrt. --- reeds/core/terminus/report.gms | 24 +++++++++++++----------- reeds/core/terminus/report_params.csv | 2 +- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/reeds/core/terminus/report.gms b/reeds/core/terminus/report.gms index 9424ce2f9..4a397f62a 100644 --- a/reeds/core/terminus/report.gms +++ b/reeds/core/terminus/report.gms @@ -815,14 +815,16 @@ losses_tran_h(rr,r,h,trtype,t)$[routes(r,rr,trtype,t)$tmodel_new(t)] cap_deg_ivrt(i,v,r,t)$valcap(i,v,r,t) = CAP.l(i,v,r,t) / ilr(i) ; -cap_ivrt(i,v,r,t)$[(not (upv(i) or wind(i)))$valcap(i,v,r,t)] = cap_deg_ivrt(i,v,r,t) ; +cap_ivrt(i,c,v,r,t)$[i_c(i,c)$(not (upv(i) or wind(i)))$valcap(i,v,r,t)] = cap_deg_ivrt(i,v,r,t) ; +cap_ivrt(i,c,v,r,t)$[valcap_class(i,c,v,r,t)$(not (upv(i) or wind(i)))] = CAP_CLASS.l(i,c,v,r,t) / ilr(i) ; *upv, and wind have degradation, so use INV rather than CAP to get the reported capacity -cap_ivrt(i,v,r,t)$[(upv(i) or wind(i))$valcap(i,v,r,t)] = ( - m_capacity_exog(i,v,r,t)$tmodel_new(t) +cap_ivrt(i,c,v,r,t)$[i_c(i,c)$(upv(i) or wind(i))$valcap(i,v,r,t)] = ( + sum{rscbin, capacity_exog_rsc(i,c,v,r,rscbin,t) }$tmodel_new(t) + sum{tt$[inv_cond(i,v,r,t,tt)$[tmodel(tt) or tfix(tt)]], - INV.l(i,v,r,tt) + sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,tt) }$[refurbtech(i)$Sw_Refurb]}) / ilr(i) ; + sum{rscbin$m_rscfeas(r,i,c,rscbin), INV_RSC.l(i,c,v,r,rscbin,tt) } + + INV_REFURB.l(i,c,v,r,tt)$[refurbtech(i)$Sw_Refurb]}) / ilr(i) ; -cap_out(i,r,t)$[valcap_irt(i,r,t)$tmodel_new(t)] = sum{v$valcap(i,v,r,t), cap_ivrt(i,v,r,t) } ; +cap_out(i,r,t)$[valcap_irt(i,r,t)$tmodel_new(t)] = sum{(c,v)$[i_c(i,c)$valcap(i,v,r,t)], cap_ivrt(i,c,v,r,t) } ; * A small amount of upv capacity is actually csp-ns, so convert it back now. * UPV capacity is already in MWac at this point (matching csp-ns), * so don't need to account for ILR @@ -862,8 +864,8 @@ cap_new_ivrt(i,v,r,t)$[valcap(i,v,r,t)] = [ [INV.l(i,v,r,t) + sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) }]$valinv(i,v,r,t) + [(1-upgrade_derate(i,v,r,t)) * (UPGRADES.l(i,v,r,t) - UPGRADES_RETIRE.l(i,v,r,t))]$[upgrade(i)$valcap(i,v,r,t)$Sw_Upgrades] ] / ilr(i) ; -cap_new_ivrt("distpv",v,r,t)$[tfirst(t)$valcap("distpv",v,r,t)] = cap_ivrt("distpv",v,r,t) ; -cap_new_ivrt("distpv",v,r,t)$[(not tfirst(t))$valcap("distpv",v,r,t)] = cap_ivrt("distpv",v,r,t) - sum{tt$tprev(t,tt), cap_ivrt("distpv",v,r,tt) } ; +cap_new_ivrt("distpv",v,r,t)$[tfirst(t)$valcap("distpv",v,r,t)] = sum{c$i_c("distpv",c), cap_ivrt("distpv",c,v,r,t) } ; +cap_new_ivrt("distpv",v,r,t)$[(not tfirst(t))$valcap("distpv",v,r,t)] = sum{c$i_c("distpv",c), cap_ivrt("distpv",c,v,r,t) - sum{tt$tprev(t,tt), cap_ivrt("distpv",c,v,r,tt) } } ; cap_new_ivrt_refurb(i,v,r,t)$valinv(i,v,r,t) = sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) } / ilr(i) ; * Capacity by reV site @@ -920,7 +922,7 @@ cap_upgrade_ivrt(i,v,r,t)$[valcap(i,v,r,t)$upgrade(i)$Sw_Upgrades] = (1-upgrade_ *========================= ret_ivrt(i,v,r,t)$[(not tfirst(t))] = - sum{tt$tprev(t,tt), cap_ivrt(i,v,r,tt) } - cap_ivrt(i,v,r,t) + cap_new_ivrt(i,v,r,t) + sum{(c,tt)$[i_c(i,c)$tprev(t,tt)], cap_ivrt(i,c,v,r,tt) } - sum{c$i_c(i,c), cap_ivrt(i,c,v,r,t) } + cap_new_ivrt(i,v,r,t) - sum{ii$upgrade_from(ii,i), UPGRADES.l(ii,v,r,t) } ; ret_ivrt(i,v,r,t)$[abs(ret_ivrt(i,v,r,t)) < 1e-6] = 0 ; @@ -1358,7 +1360,7 @@ systemcost_techba("op_vom_costs",i,r,t)$[tmodel_new(t)$consume(i)] = 0 ; systemcost_techba("op_fom_costs",i,r,t)$tmodel_new(t) = *fixed O&M costs for generation capacity + sum{v$[valcap(i,v,r,t)$((not one_newv(i)) or retiretech(i,v,r,t))], - cost_fom(i,v,r,t) * cap_ivrt(i,v,r,t) * ilr(i) } + cost_fom(i,v,r,t) * sum{c$i_c(i,c), cap_ivrt(i,c,v,r,t) } * ilr(i) } *for technologies with only one newv that are not allowed to retire, *use the investments rather than the capacity to calculate FOM costs + sum{(v,tt)$[inv_cond(i,v,r,t,tt)$one_newv(i)$(not retiretech(i,v,r,tt))], @@ -1719,9 +1721,9 @@ error_check('z') = ( + pvf_onm(t) * sum{(p,i,v,r,h)$[valcap(i,v,r,t)$i_p(i,p)$h_rep(h)$Sw_RetailAdder$Sw_Prod], hours(h) * Sw_RetailAdder * PRODUCE.l(p,i,v,r,h,t) / prod_conversion_rate(i,v,r,t) } * Account for difference in fixed O&M between model (CAP.l(i,v,r,t)) -* and outputs (cap_ivrt(i,v,r,t) * ilr(i)) for techs with more than one newv +* and outputs (cap_ivrt summed over class * ilr(i)) for techs with more than one newv + pvf_onm(t) * sum{(i,v,r)$[valcap(i,v,r,t)$((not one_newv(i)) or retiretech(i,v,r,t))], - cost_fom(i,v,r,t) * (CAP.l(i,v,r,t) - cap_ivrt(i,v,r,t) * ilr(i)) } + cost_fom(i,v,r,t) * (CAP.l(i,v,r,t) - sum{c$i_c(i,c), cap_ivrt(i,c,v,r,t) } * ilr(i)) } * Account for difference in fixed O&M between model (CAP.l(i,v,r,t)) * and outputs (based on INV.l) for techs with more only one newv that cannot retire + pvf_onm(t) * sum{(i,v,r)$[valcap(i,v,r,t)$(one_newv(i))$(not retiretech(i,v,r,t))], diff --git a/reeds/core/terminus/report_params.csv b/reeds/core/terminus/report_params.csv index 1be9bcc10..c6a86a165 100644 --- a/reeds/core/terminus/report_params.csv +++ b/reeds/core/terminus/report_params.csv @@ -16,7 +16,7 @@ param,units,comment,reeds2x,output_rename,input "cap_new_cc(i,r,ccseason,t)",MW,"new capacity that is VRE, for new capacity credit calculation",,, "cc_new(i,r,ccseason,t)",frac,capacity credit for new VRE techs,,, "cap_converter_out(r,t)",MW,AC/DC converter capacity,1,, -"cap_ivrt(i,v,r,t)",MW,"undegraded power capacity by tech, year, region, and class",1,, +"cap_ivrt(i,c,v,r,t)",MW,"undegraded power capacity by tech, year, region, and class",1,, "cap_energy_ivrt(i,v,r,t)",MWh,"undegraded energy capacity by tech, year, region, and class",1,, "cap_sdbin_out(i,r,ccseason,sdbin,t)",MW,binned storage capacity by year,,, "cap_deg_ivrt(i,v,r,t)",MW,"Degraded capacity, equal to CAP.l",,, From d19892f8c1f7b3a2ff0f5c1514a665420b914b08 Mon Sep 17 00:00:00 2001 From: wcole Date: Thu, 3 Sep 2026 15:30:55 -0600 Subject: [PATCH 09/30] Add class to Bokeh. --- postprocessing/bokehpivot/reeds2.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/postprocessing/bokehpivot/reeds2.py b/postprocessing/bokehpivot/reeds2.py index ee1413e50..311c15275 100644 --- a/postprocessing/bokehpivot/reeds2.py +++ b/postprocessing/bokehpivot/reeds2.py @@ -1407,7 +1407,7 @@ def pre_spur(dfs, **kw): ('Capacity ivrt (GW)', {'file':'cap_ivrt', - 'columns': ['tech', 'vintage', 'rb', 'year','Capacity (GW)'], + 'columns': ['tech', 'class', 'vintage', 'rb', 'year','Capacity (GW)'], 'preprocess': [ {'func': scale_column, 'args': {'scale_factor': .001, 'column':'Capacity (GW)'}}, ], @@ -1540,7 +1540,7 @@ def pre_spur(dfs, **kw): ('Exogenous capacity (GW)', {'file':'cap_exog', - 'columns': ['tech', 'vintage', 'region', 'year', 'Capacity (GW)'], + 'columns': ['tech', 'class', 'vintage', 'region', 'year', 'Capacity (GW)'], 'preprocess': [ {'func': scale_column, 'args': {'scale_factor': .001, 'column':'Capacity (GW)'}}, ], @@ -1893,7 +1893,7 @@ def pre_spur(dfs, **kw): ('Capacity Factor ivrt', {'sources': [ {'name': 'gen', 'file': 'gen_ivrt', 'columns': ['tech', 'vintage', 'rb', 'year','MWh']}, - {'name': 'cap', 'file': 'cap_ivrt', 'columns': ['tech', 'vintage', 'rb', 'year','MW']}, + {'name': 'cap', 'file': 'cap_ivrt', 'columns': ['tech', 'class', 'vintage', 'rb', 'year','MW']}, ], 'preprocess': [ {'func': pre_cf, 'args': {}}, @@ -2680,7 +2680,7 @@ def pre_spur(dfs, **kw): ('LCOE ($/MWh)', {'sources': [ {'name': 'lcoe', 'file': 'lcoe', 'columns': ['tech', 'vintage', 'rb', 'year', 'bin','$/MWh']}, - {'name': 'inv', 'file': 'cap_new_bin_out', 'columns': ['tech', 'vintage', 'rb', 'year', 'bin','chosen MW']}, + {'name': 'inv', 'file': 'cap_new_bin_out', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'bin','chosen MW']}, {'name': 'avail', 'file': 'cap_avail', 'columns': ['tech', 'rb', 'year', 'bin','available MW']}, ], 'preprocess': [ @@ -2698,7 +2698,7 @@ def pre_spur(dfs, **kw): ('LCOE cf_act ($/MWh)', {'sources': [ {'name': 'lcoe', 'file': 'lcoe_cf_act', 'columns': ['tech', 'vintage', 'rb', 'year', 'bin','$/MWh']}, - {'name': 'inv', 'file': 'cap_new_bin_out', 'columns': ['tech', 'vintage', 'rb', 'year', 'bin','chosen MW']}, + {'name': 'inv', 'file': 'cap_new_bin_out', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'bin','chosen MW']}, {'name': 'avail', 'file': 'cap_avail', 'columns': ['tech', 'rb', 'year', 'bin','available MW']}, ], 'preprocess': [ @@ -2716,7 +2716,7 @@ def pre_spur(dfs, **kw): ('LCOE nopol ($/MWh)', {'sources': [ {'name': 'lcoe', 'file': 'lcoe_nopol', 'columns': ['tech', 'vintage', 'rb', 'year', 'bin','$/MWh']}, - {'name': 'inv', 'file': 'cap_new_bin_out', 'columns': ['tech', 'vintage', 'rb', 'year', 'bin','chosen MW']}, + {'name': 'inv', 'file': 'cap_new_bin_out', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'bin','chosen MW']}, {'name': 'avail', 'file': 'cap_avail', 'columns': ['tech', 'rb', 'year', 'bin','available MW']}, ], 'preprocess': [ @@ -2734,7 +2734,7 @@ def pre_spur(dfs, **kw): ('LCOE fullpol ($/MWh)', {'sources': [ {'name': 'lcoe', 'file': 'lcoe_fullpol', 'columns': ['tech', 'vintage', 'rb', 'year', 'bin','$/MWh']}, - {'name': 'inv', 'file': 'cap_new_bin_out', 'columns': ['tech', 'vintage', 'rb', 'year', 'bin','chosen MW']}, + {'name': 'inv', 'file': 'cap_new_bin_out', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'bin','chosen MW']}, {'name': 'avail', 'file': 'cap_avail', 'columns': ['tech', 'rb', 'year', 'bin','available MW']}, ], 'preprocess': [ @@ -2837,7 +2837,7 @@ def pre_spur(dfs, **kw): {'name':'tran_mi_out', 'file':'tran_mi_out', 'columns':['trtype', 'year', 'Amount (GW-mi)']}, {'name':'tran_prm_mi_out', 'file':'tran_prm_mi_out', 'columns':['trtype', 'year', 'Trans cap, PRM (GW-mi)']}, {'name':'spur_parameters', 'file':'../inputs_case/spur_parameters.csv'}, - {'name':'cap_new_bin_out', 'file':'cap_new_bin_out', 'columns':['i','v','r','year','rscbin','MW']}, + {'name':'cap_new_bin_out', 'file':'cap_new_bin_out', 'columns':['i','c','v','r','year','rscbin','MW']}, {'name': 'scalars', 'file': '../inputs_case/scalars.csv', 'header':None, 'columns': ['scalar', 'value', 'comment']}, ], 'preprocess': [ From a31ff0d2163ba5be3bc9d4fdb6378d569edbd856 Mon Sep 17 00:00:00 2001 From: wcole Date: Thu, 3 Sep 2026 15:40:46 -0600 Subject: [PATCH 10/30] Add class to cap_out. --- hourlize/reeds_to_rev.py | 4 ++-- postprocessing/bokehpivot/reeds2.py | 16 ++++++++-------- reeds/core/terminus/report.gms | 26 +++++++++++++------------- reeds/core/terminus/report_params.csv | 2 +- reeds/results.py | 2 +- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/hourlize/reeds_to_rev.py b/hourlize/reeds_to_rev.py index 59c1ecb07..d1232e1e9 100644 --- a/hourlize/reeds_to_rev.py +++ b/hourlize/reeds_to_rev.py @@ -333,7 +333,7 @@ class that can be used to check capacity values at the end of disaggregation. # Get check for capacity cap_chk = os.path.join(run_folder, "outputs", "cap.csv") df_cap_chk = pd.read_csv( - cap_chk, low_memory=False, names=["tech", "region", "year", "MW"], header=0 + cap_chk, low_memory=False, names=["tech", "class", "region", "year", "MW"], header=0 ) df_cap_chk[["tech_cat", "class"]] = df_cap_chk["tech"].str.rsplit( "_", n=1, expand=True @@ -2016,7 +2016,7 @@ def check_tech(run_folder, tech): """ cap_chk = os.path.join(run_folder, "outputs", "cap.csv") df_cap = pd.read_csv( - cap_chk, low_memory=False, names=["tech", "region", "year", "MW"], header=0 + cap_chk, low_memory=False, names=["tech", "class", "region", "year", "MW"], header=0 ) tech_included = df_cap["tech"].str.startswith(tech).any() diff --git a/postprocessing/bokehpivot/reeds2.py b/postprocessing/bokehpivot/reeds2.py index 311c15275..d958f2a0b 100644 --- a/postprocessing/bokehpivot/reeds2.py +++ b/postprocessing/bokehpivot/reeds2.py @@ -1375,7 +1375,7 @@ def pre_spur(dfs, **kw): ('Capacity BA (GW)', {'file':'cap', - 'columns': ['tech', 'rb', 'year', 'Capacity (GW)'], + 'columns': ['tech', 'class', 'rb', 'year', 'Capacity (GW)'], 'preprocess': [ {'func': scale_column, 'args': {'scale_factor': .001, 'column':'Capacity (GW)'}}, ], @@ -1395,7 +1395,7 @@ def pre_spur(dfs, **kw): ('Capacity BA by class (GW)', {'file':'cap', - 'columns': ['tech', 'rb', 'year', 'Capacity (GW)'], + 'columns': ['tech', 'class', 'rb', 'year', 'Capacity (GW)'], 'preprocess': [ {'func': add_class, 'args': {}}, {'func': scale_column, 'args': {'scale_factor': .001, 'column':'Capacity (GW)'}}, @@ -1456,7 +1456,7 @@ def pre_spur(dfs, **kw): ('Storage Capacity (GW or GWh)', {'sources': [ - {'name': 'cap', 'file': 'cap', 'columns': ['tech', 'rb', 'year', 'Capacity (GW)']}, + {'name': 'cap', 'file': 'cap', 'columns': ['tech', 'class', 'rb', 'year', 'Capacity (GW)']}, {'name': 'energy', 'file': 'stor_energy_cap', 'columns': ['tech', 'vintage', 'rb', 'year', 'Energy (GWh)']}, {'name': 'bcr', 'file': '../inputs_case/bcr.csv', 'columns': ['tech', 'bcr']}, ], @@ -1488,7 +1488,7 @@ def pre_spur(dfs, **kw): ('New Annual Capacity BA (GW)', {'file':'cap_new_ann', - 'columns': ['tech', 'rb', 'year', 'Capacity (GW)'], + 'columns': ['tech', 'class', 'rb', 'year', 'Capacity (GW)'], 'preprocess': [ {'func': scale_column, 'args': {'scale_factor': .001, 'column':'Capacity (GW)'}}, ], @@ -1522,7 +1522,7 @@ def pre_spur(dfs, **kw): ('Annual Retirements BA (GW)', {'file':'ret_ann', - 'columns': ['tech', 'rb', 'year', 'Capacity (GW)'], + 'columns': ['tech', 'class', 'rb', 'year', 'Capacity (GW)'], 'preprocess': [ {'func': scale_column, 'args': {'scale_factor': .001, 'column':'Capacity (GW)'}}, ], @@ -1979,7 +1979,7 @@ def pre_spur(dfs, **kw): ('Firm Capacity National (GW)', {'sources': [ {'name': 'firmcap', 'file': 'cap_firm', 'columns': ['tech', 'rb', 'season', 'year', 'Firm Capacity (GW)']}, - {'name': 'cap', 'file': 'cap', 'columns': ['tech', 'rb', 'year', 'Capacity (GW)']}, + {'name': 'cap', 'file': 'cap', 'columns': ['tech', 'class', 'rb', 'year', 'Capacity (GW)']}, ], 'index': ['tech', 'season', 'year'], 'preprocess': [ @@ -1997,7 +1997,7 @@ def pre_spur(dfs, **kw): ('Firm Capacity BA (GW)', {'sources': [ {'name': 'firmcap', 'file': 'cap_firm', 'columns': ['tech', 'rb', 'season', 'year', 'Firm Capacity (GW)']}, - {'name': 'cap', 'file': 'cap', 'columns': ['tech', 'rb', 'year', 'Capacity (GW)']}, + {'name': 'cap', 'file': 'cap', 'columns': ['tech', 'class', 'rb', 'year', 'Capacity (GW)']}, ], 'index': ['tech', 'rb', 'season', 'year'], 'preprocess': [ @@ -3065,7 +3065,7 @@ def pre_spur(dfs, **kw): ), ('Upgraded Capacity (GW)', {'file':'cap_upgrade', - 'columns': ['tech', 'rb', 'year', 'Capacity (GW)'], + 'columns': ['tech', 'class', 'rb', 'year', 'Capacity (GW)'], 'preprocess': [ # {'func': sum_over_cols, 'args': {'drop_cols': ['rb'], 'group_cols': ['tech', 'year']}}, {'func': scale_column, 'args': {'scale_factor': .001, 'column':'Capacity (GW)'}}, diff --git a/reeds/core/terminus/report.gms b/reeds/core/terminus/report.gms index 4a397f62a..b452f05bc 100644 --- a/reeds/core/terminus/report.gms +++ b/reeds/core/terminus/report.gms @@ -824,17 +824,17 @@ cap_ivrt(i,c,v,r,t)$[i_c(i,c)$(upv(i) or wind(i))$valcap(i,v,r,t)] = ( sum{rscbin$m_rscfeas(r,i,c,rscbin), INV_RSC.l(i,c,v,r,rscbin,tt) } + INV_REFURB.l(i,c,v,r,tt)$[refurbtech(i)$Sw_Refurb]}) / ilr(i) ; -cap_out(i,r,t)$[valcap_irt(i,r,t)$tmodel_new(t)] = sum{(c,v)$[i_c(i,c)$valcap(i,v,r,t)], cap_ivrt(i,c,v,r,t) } ; +cap_out(i,c,r,t)$[i_c(i,c)$valcap_irt(i,r,t)$tmodel_new(t)] = sum{v$valcap(i,v,r,t), cap_ivrt(i,c,v,r,t) } ; * A small amount of upv capacity is actually csp-ns, so convert it back now. * UPV capacity is already in MWac at this point (matching csp-ns), * so don't need to account for ILR -cap_out("csp-ns",r,t)$[cap_cspns(r,t)$tmodel_new(t)] = cap_cspns(r,t) ; -* We have to take csp-ns capacity from somewhere, so take it from upv_6 (which all the -* csp-ns-containing regions have) -cap_out("upv_5",r,t)$[cap_cspns(r,t)$tmodel_new(t)] = cap_out("upv_5",r,t) - cap_cspns(r,t) ; +cap_out("csp-ns",c,r,t)$[i_c("csp-ns",c)$cap_cspns(r,t)$tmodel_new(t)] = cap_cspns(r,t) ; +* We have to take csp-ns capacity from somewhere, so take it from the upv class that all the +* csp-ns-containing regions have +cap_out("upv_5",c,r,t)$[i_c("upv_5",c)$cap_cspns(r,t)$tmodel_new(t)] = cap_out("upv_5",c,r,t) - cap_cspns(r,t) ; * Make sure it doesn't go negative, just in case -cap_out("upv_5",r,t)$[cap_cspns(r,t)$tmodel_new(t)$(cap_out("upv_5",r,t) < 0)] = 0 ; -cap_nat(i,t)$tmodel_new(t) = sum{r, cap_out(i,r,t) } ; +cap_out("upv_5",c,r,t)$[i_c("upv_5",c)$cap_cspns(r,t)$tmodel_new(t)$(cap_out("upv_5",c,r,t) < 0)] = 0 ; +cap_nat(i,t)$tmodel_new(t) = sum{(c,r)$i_c(i,c), cap_out(i,c,r,t) } ; * Exogenous capacity (used by reeds_to_rev) cap_exog(i,c,v,r,t)$[i_c(i,c)$tmodel_new(t)] = m_capacity_exog(i,v,r,t) ; @@ -853,8 +853,8 @@ cap_new_out(i,r,t)$[valcap_irt(i,r,t)] = [ ] / ilr(i) ; * Capacity of distpv is not tracked in INV because it is an exogenous input, so use the change in cap_out to calculate new capacity * (except for the first year, in which all distpv capacity is counted as new) -cap_new_out("distpv",r,t)$[tfirst(t)$valcap_irt("distpv",r,t)] = cap_out("distpv",r,t) ; -cap_new_out("distpv",r,t)$[(not tfirst(t))$valcap_irt("distpv",r,t)] = cap_out("distpv",r,t) - sum{tt$tprev(t,tt), cap_out("distpv",r,tt) } ; +cap_new_out("distpv",r,t)$[tfirst(t)$valcap_irt("distpv",r,t)] = sum{c$i_c("distpv",c), cap_out("distpv",c,r,t) } ; +cap_new_out("distpv",r,t)$[(not tfirst(t))$valcap_irt("distpv",r,t)] = sum{c$i_c("distpv",c), cap_out("distpv",c,r,t) - sum{tt$tprev(t,tt), cap_out("distpv",c,r,tt) } } ; cap_new_ann(i,r,t)$cap_new_out(i,r,t) = cap_new_out(i,r,t) / (yeart(t) - sum{tt$tprev(t,tt), yeart(tt) }) ; cap_new_ann_nat(i,t)$tmodel_new(t) = sum{r, cap_new_ann(i,r,t) } ; cap_new_bin_out(i,c,v,r,t,rscbin)$[i_c(i,c)$rsc_i(i)$valinv(i,v,r,t)] = @@ -905,7 +905,7 @@ cap_avail(i,r,t,rscbin)$[tmodel_new(t)$rsc_i(i)$sum{c, m_rscfeas(r,i,c,rscbin) } capacity_offline(i,r,allh,t) $[valcap_irt(i,r,t)$tmodel_new(t)$(h_stress_t(allh,t) or h_rep(allh))] = - cap_out(i,r,t) * (1 - avail(i,r,allh)) ; + sum{c$i_c(i,c), cap_out(i,c,r,t) } * (1 - avail(i,r,allh)) ; forced_outage(i) = sum{(r,h), outage_forced_h(i,r,h) * hours(h) } / sum{(r,h), hours(h) } ; planned_outage(i) = sum{h, outage_scheduled_h(i,h) * hours(h) } / sum{h, hours(h) } ; @@ -1035,10 +1035,10 @@ revenue_en_nat(rev_cat,i,t) revenue_nat(rev_cat,i,t) / sum{(v,r,h,c)$valcap_class(i,c,v,r,t), m_cf(i,c,v,r,h,t) * CAP_CLASS.l(i,c,v,r,t) * hours(h) } ; -revenue_cap(rev_cat,i,r,t)$[tmodel_new(t)$cap_out(i,r,t)] = - revenue(rev_cat,i,r,t) / cap_out(i,r,t) ; +revenue_cap(rev_cat,i,r,t)$[tmodel_new(t)$sum{c$i_c(i,c), cap_out(i,c,r,t) }] = + revenue(rev_cat,i,r,t) / sum{c$i_c(i,c), cap_out(i,c,r,t) } ; -revenue_cap_nat(rev_cat,i,t)$[tmodel_new(t)$sum{r$valcap_irt(i,r,t), cap_out(i,r,t) }] = +revenue_cap_nat(rev_cat,i,t)$[tmodel_new(t)$sum{(c,r)$[i_c(i,c)$valcap_irt(i,r,t)], cap_out(i,c,r,t) }] = revenue_nat(rev_cat,i,t) / cap_nat(i,t) ; *======================================== diff --git a/reeds/core/terminus/report_params.csv b/reeds/core/terminus/report_params.csv index c6a86a165..1d736dcbe 100644 --- a/reeds/core/terminus/report_params.csv +++ b/reeds/core/terminus/report_params.csv @@ -27,7 +27,7 @@ param,units,comment,reeds2x,output_rename,input "cap_new_ivrt_refurb(i,v,r,t)",MW,new refurbished capacity,,, "cap_new_out(i,r,t)",MW,"new capacity by region, which are investments and upgrades from one solve year to the next",,, "cap_energy_new_out(i,v,r,t)",MWh,"new energy capacity by region, which are investments from one solve year to the next",,, -"cap_out(i,r,t)",MW,capacity by region,1,cap, +"cap_out(i,c,r,t)",MW,capacity by region,1,cap, "cap_out_ivrt(i,v,r,t)",MW,capacity by region and vintage,,, "capacity_offline(i,r,allh,t)",MW,capacity offline due to forced or scheduled outages,,, "capex_ivrt(i,v,r,t)",$,"capital expenditure for new capacity, no ITC/depreciation/PTC reductions",,, diff --git a/reeds/results.py b/reeds/results.py index 22743d651..741142da0 100644 --- a/reeds/results.py +++ b/reeds/results.py @@ -48,7 +48,7 @@ def calc_cap(case): Value (float): Capacity (GW) """ # Get data - df = reeds.io.read_output(case, 'cap') + df = reeds.io.read_output(case, 'cap').drop(columns='c', errors='ignore') # simplify technology names based on bokehpivot mapping df['i'] = reeds.reedsplots.simplify_techs(df['i']) From 701deecc1d058265dc40e45ba140fe1bdd6f7cb6 Mon Sep 17 00:00:00 2001 From: wcole Date: Thu, 3 Sep 2026 16:29:39 -0600 Subject: [PATCH 11/30] Remove class splitting from reeds_to_rev.py --- hourlize/reeds_to_rev.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/hourlize/reeds_to_rev.py b/hourlize/reeds_to_rev.py index d1232e1e9..00a68e4cf 100644 --- a/hourlize/reeds_to_rev.py +++ b/hourlize/reeds_to_rev.py @@ -279,7 +279,7 @@ def get_preexisting_capacity(df_sc_in, tech, first_model_year=2009): # Find existing capacity by bin with raw supply curve. # Consider existing capacity as investment in 2009 to use the # same logic as inv_rsc when assigning to gid. - exist_columns = ["tech", "region", "year", "bin", "MW"] + exist_columns = ["tech", "class", "region", "year", "bin", "MW"] if "existing_capacity" in df_sc_in: if "online_year" not in df_sc_in: raise KeyError( @@ -401,8 +401,6 @@ def combine_preexisting_and_new_investments(df_bin_exist, df_inv_rsc): """ # Concatenate existing and inv_rsc df_inv = pd.concat([df_bin_exist, df_inv_rsc], sort=False, ignore_index=True) - # Split tech from class - df_inv[["tech_cat", "class"]] = df_inv["tech"].str.rsplit("_", n=1, expand=True) df_inv = df_inv[["year", "region", "class", "bin", "MW"]] df_inv["class"] = df_inv["class"].astype("int") df_inv["bin"] = df_inv["bin"].str.replace("bin", "", regex=False).astype("int") @@ -435,9 +433,9 @@ def get_input_refurbishments(run_folder, tech): df_inv_refurb_in = pd.read_csv( inv_refurb, low_memory=False, - names=["tech", "vintage", "region", "year", "MW"], + names=["tech", "class", "vintage", "region", "year", "MW"], header=0, - usecols=["tech", "region", "year", "MW"], + usecols=["tech", "class", "region", "year", "MW"], ) df_inv_refurb_in = df_inv_refurb_in[ df_inv_refurb_in["tech"].str.startswith(tech) @@ -462,14 +460,7 @@ def amend_refurbishments(df_inv_refurb_in): Returns refurbishments for the given technology, by year, region, and class. Output columns include: ["year", "region", "class", "MW"] """ - # Split tech from class df_inv_refurb = df_inv_refurb_in.copy() - if df_inv_refurb.empty: - df_inv_refurb[["tech_cat", "class"]] = "" - else: - df_inv_refurb[["tech_cat", "class"]] = df_inv_refurb["tech"].str.split( - "_", n=1, expand=True - ) df_inv_refurb = df_inv_refurb[["year", "region", "class", "MW"]] df_inv_refurb["class"] = df_inv_refurb["class"].astype("int") df_inv_refurb = df_inv_refurb.sort_values(by=["year", "region", "class"]) From d0cdb47e8ca0e8a3557af470a4ef3a3514c401ca Mon Sep 17 00:00:00 2001 From: wcole Date: Thu, 3 Sep 2026 16:30:02 -0600 Subject: [PATCH 12/30] Add class to cap_new_ivrt_refurb. --- reeds/core/terminus/report.gms | 2 +- reeds/core/terminus/report_params.csv | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/reeds/core/terminus/report.gms b/reeds/core/terminus/report.gms index b452f05bc..350e33292 100644 --- a/reeds/core/terminus/report.gms +++ b/reeds/core/terminus/report.gms @@ -866,7 +866,7 @@ cap_new_ivrt(i,v,r,t)$[valcap(i,v,r,t)] = [ ] / ilr(i) ; cap_new_ivrt("distpv",v,r,t)$[tfirst(t)$valcap("distpv",v,r,t)] = sum{c$i_c("distpv",c), cap_ivrt("distpv",c,v,r,t) } ; cap_new_ivrt("distpv",v,r,t)$[(not tfirst(t))$valcap("distpv",v,r,t)] = sum{c$i_c("distpv",c), cap_ivrt("distpv",c,v,r,t) - sum{tt$tprev(t,tt), cap_ivrt("distpv",c,v,r,tt) } } ; -cap_new_ivrt_refurb(i,v,r,t)$valinv(i,v,r,t) = sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) } / ilr(i) ; +cap_new_ivrt_refurb(i,c,v,r,t)$[i_c(i,c)$valinv(i,v,r,t)] = INV_REFURB.l(i,c,v,r,t) / ilr(i) ; * Capacity by reV site site_spurinv(x,t)$[tmodel_new(t)$xfeas(x)] = INV_SPUR.l(x,t) ; diff --git a/reeds/core/terminus/report_params.csv b/reeds/core/terminus/report_params.csv index 1d736dcbe..0ca77daf1 100644 --- a/reeds/core/terminus/report_params.csv +++ b/reeds/core/terminus/report_params.csv @@ -24,7 +24,7 @@ param,units,comment,reeds2x,output_rename,input "cap_new_ann_nat(i,t)",MW/yr,new annual capacity national,,, "cap_new_bin_out(i,c,v,r,t,rscbin)",MW,capacity of built techs,,, "cap_new_ivrt(i,v,r,t)",MW,new capacity,1,, -"cap_new_ivrt_refurb(i,v,r,t)",MW,new refurbished capacity,,, +"cap_new_ivrt_refurb(i,c,v,r,t)",MW,new refurbished capacity,,, "cap_new_out(i,r,t)",MW,"new capacity by region, which are investments and upgrades from one solve year to the next",,, "cap_energy_new_out(i,v,r,t)",MWh,"new energy capacity by region, which are investments from one solve year to the next",,, "cap_out(i,c,r,t)",MW,capacity by region,1,cap, From 89f7e252274da972fe06ff75576c79a9eec16f0b Mon Sep 17 00:00:00 2001 From: wcole Date: Thu, 3 Sep 2026 16:44:43 -0600 Subject: [PATCH 13/30] Add class to LCOE outputs. --- postprocessing/bokehpivot/reeds2.py | 8 ++++---- reeds/core/terminus/report_params.csv | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/postprocessing/bokehpivot/reeds2.py b/postprocessing/bokehpivot/reeds2.py index d958f2a0b..8ef474c36 100644 --- a/postprocessing/bokehpivot/reeds2.py +++ b/postprocessing/bokehpivot/reeds2.py @@ -2679,7 +2679,7 @@ def pre_spur(dfs, **kw): ('LCOE ($/MWh)', {'sources': [ - {'name': 'lcoe', 'file': 'lcoe', 'columns': ['tech', 'vintage', 'rb', 'year', 'bin','$/MWh']}, + {'name': 'lcoe', 'file': 'lcoe', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'bin','$/MWh']}, {'name': 'inv', 'file': 'cap_new_bin_out', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'bin','chosen MW']}, {'name': 'avail', 'file': 'cap_avail', 'columns': ['tech', 'rb', 'year', 'bin','available MW']}, ], @@ -2697,7 +2697,7 @@ def pre_spur(dfs, **kw): ('LCOE cf_act ($/MWh)', {'sources': [ - {'name': 'lcoe', 'file': 'lcoe_cf_act', 'columns': ['tech', 'vintage', 'rb', 'year', 'bin','$/MWh']}, + {'name': 'lcoe', 'file': 'lcoe_cf_act', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'bin','$/MWh']}, {'name': 'inv', 'file': 'cap_new_bin_out', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'bin','chosen MW']}, {'name': 'avail', 'file': 'cap_avail', 'columns': ['tech', 'rb', 'year', 'bin','available MW']}, ], @@ -2715,7 +2715,7 @@ def pre_spur(dfs, **kw): ('LCOE nopol ($/MWh)', {'sources': [ - {'name': 'lcoe', 'file': 'lcoe_nopol', 'columns': ['tech', 'vintage', 'rb', 'year', 'bin','$/MWh']}, + {'name': 'lcoe', 'file': 'lcoe_nopol', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'bin','$/MWh']}, {'name': 'inv', 'file': 'cap_new_bin_out', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'bin','chosen MW']}, {'name': 'avail', 'file': 'cap_avail', 'columns': ['tech', 'rb', 'year', 'bin','available MW']}, ], @@ -2733,7 +2733,7 @@ def pre_spur(dfs, **kw): ('LCOE fullpol ($/MWh)', {'sources': [ - {'name': 'lcoe', 'file': 'lcoe_fullpol', 'columns': ['tech', 'vintage', 'rb', 'year', 'bin','$/MWh']}, + {'name': 'lcoe', 'file': 'lcoe_fullpol', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'bin','$/MWh']}, {'name': 'inv', 'file': 'cap_new_bin_out', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'bin','chosen MW']}, {'name': 'avail', 'file': 'cap_avail', 'columns': ['tech', 'rb', 'year', 'bin','available MW']}, ], diff --git a/reeds/core/terminus/report_params.csv b/reeds/core/terminus/report_params.csv index 0ca77daf1..17d3d7d64 100644 --- a/reeds/core/terminus/report_params.csv +++ b/reeds/core/terminus/report_params.csv @@ -2,7 +2,7 @@ # This parameter list is in alphabetical order - please add new entries that way,,,,, param,units,comment,reeds2x,output_rename,input "acp_purchases_out(rpscat,st,t)",MWh,Annual alternative compliance credits from the variable ACP_PURCHASES,,, -"avg_cf(i,v,r,t)",frac,Annual average capacity factor for rsc technologies,,, +"avg_cf(i,v,r,t)",frac,Annual average capacity factor for non-rsc technologies from realized generation,,, "avg_avail(i,v,r)",frac,Annual average avail factor,,, "bioused_out(bioclass,r,t)",dry tons (imperial),biomass used by class in each model region (-> bioused.csv),,, "bioused_usda(bioclass,usda_region,t)",dry tons (imperial),biomass used by class in each USDA region,,, @@ -90,7 +90,7 @@ planned_outage(i),fraction,average scheduled outage rate (h-weighted) by technol "gen_ann_nat(i,t)",MWh,"annual generation with charge and production load as negative generation, nationwide",,, "gen_uncurtailed(i,r,t)",MWh,annual generation where VRE uses uncurtailed gen and storage uses GEN,,, "gen_uncurtailed_nat(i,t)",MWh,national annual generation where VRE uses uncurtailed gen and storage uses GEN,,, -"gen_rsc(i,v,r,t)",MWh/MW,Annual generation per MW from rsc techs,,, +"gen_rsc(i,c,v,r,t)",MWh/MW,Annual generation per MW from rsc techs,,, "h2_demand_by_sector(h2_demand_type,t)",metric tons,national H2 demand by use,,, "h2_inout(h2_stor,r,allh,t,*)",metric tons/hour,H2 moving in/out of storage,,, "h2_price_h(r,allh,t)",$2004/kg,Marginal cost of H2 by region and timeslice,,, @@ -112,12 +112,12 @@ planned_outage(i),fraction,average scheduled outage rate (h-weighted) by technol "loadsite_cap(r,t)",MW,Capacity of flexibly sited load,,, "loadsite_op(r,allh,t)",MW,Operations of flexibly sited load,,, "invtran_out(r,rr,trtype,t)",MW,new transmission capacity,1,, -"lcoe(i,v,r,t,rscbin)",$/MWh,levelized cost of electricity for all tech options,,, +"lcoe(i,c,v,r,t,rscbin)",$/MWh,levelized cost of electricity for all tech options,,, "lcoe_built(i,r,t)",$/MWh,levelized cost of electricity for technologies that were built,,, "lcoe_built_nat(i,t)",$/MWh,national average levelized cost of electricity for technologies that were built,,, -"lcoe_cf_act(i,v,r,t,rscbin)",$/MWh,LCOE using actual (instead of max) capacity factors,,, -"lcoe_fullpol(i,v,r,t,rscbin)",$/MWh,"LCOE considering full ITC and PTC value, whereas the LCOE parameter considers the annualized objective function",,, -"lcoe_nopol(i,v,r,t,rscbin)",$/MWh,LCOE without considering ITC and PTC adjustments,,, +"lcoe_cf_act(i,c,v,r,t,rscbin)",$/MWh,LCOE using actual (instead of max) capacity factors,,, +"lcoe_fullpol(i,c,v,r,t,rscbin)",$/MWh,"LCOE considering full ITC and PTC value, whereas the LCOE parameter considers the annualized objective function",,, +"lcoe_nopol(i,c,v,r,t,rscbin)",$/MWh,LCOE without considering ITC and PTC adjustments,,, "lcoe_pieces(lcoe_cat,i,r,t)",varies,levelized cost of electricity elements for technologies that were built,,, "lcoe_pieces_nat(lcoe_cat,i,t)",varies,national average levelized cost of electricity elements for technologies that were built,,, "losses_ann(*,t)",MWh,annual losses by category,1,, From 485daefab699ba2f24afecf327879bf3e0a2e951 Mon Sep 17 00:00:00 2001 From: wcole Date: Thu, 3 Sep 2026 16:45:02 -0600 Subject: [PATCH 14/30] (forgot to include this file with the last commit...). --- reeds/core/terminus/report.gms | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/reeds/core/terminus/report.gms b/reeds/core/terminus/report.gms index 350e33292..25b4d7333 100644 --- a/reeds/core/terminus/report.gms +++ b/reeds/core/terminus/report.gms @@ -189,7 +189,7 @@ avg_cf(i,v,r,t)$[CAP.l(i,v,r,t)$(not rsc_i(i))] = *LCOE calculation is appropriate for sequential solve mode only where annual energy production is the same in every year. *In inter-temporal modes this isn't the case and energy production should be discounted appropriately. -lcoe(i,v,r,t,"bin1")$[(not rsc_i(i))$valcap_init(i,v,r,t)$ivt(i,v,t)$avg_avail(i,v,r)] = +lcoe(i,c,v,r,t,"bin1")$[i_c(i,c)$(not rsc_i(i))$valcap_init(i,v,r,t)$ivt(i,v,t)$avg_avail(i,v,r)] = * cost of capacity divided by generation ((crf(t) * cost_cap_fin_mult(i,r,t) * cost_cap(i,t)$newv(v) + cost_fom(i,v,r,t) @@ -200,26 +200,26 @@ lcoe(i,v,r,t,"bin1")$[(not rsc_i(i))$valcap_init(i,v,r,t)$ivt(i,v,t)$avg_avail(i + heat_rate(i,v,r,t) * fuel_price(i,r,t) ; -gen_rsc(i,v,r,t)$[valcap_init(i,v,r,t)$ivt(i,v,t)$rsc_i(i)] = - sum{(h,c)$i_c(i,c), m_cf(i,c,v,r,h,t) * hours(h) } ; +gen_rsc(i,c,v,r,t)$[i_c(i,c)$valcap_init(i,v,r,t)$ivt(i,v,t)$rsc_i(i)] = + sum{h, m_cf(i,c,v,r,h,t) * hours(h) } ; -lcoe(i,v,r,t,rscbin)$[valcap_init(i,v,r,t)$ivt(i,v,t)$rsc_i(i)$sum{c, m_rscfeas(r,i,c,rscbin) }$gen_rsc(i,v,r,t)] = +lcoe(i,c,v,r,t,rscbin)$[i_c(i,c)$valcap_init(i,v,r,t)$ivt(i,v,t)$rsc_i(i)$m_rscfeas(r,i,c,rscbin)$gen_rsc(i,c,v,r,t)] = * cost of capacity divided by generation (crf(t) * (cost_cap_fin_mult(i,r,t) * cost_cap(i,t) * Spur-line costs embedded in supply curve for techs without explicitly-modeled spurlines - + sum{c, m_rsc_dat(r,i,c,rscbin,"cost") }$[newv(v)$(not spur_techs(i))] + + m_rsc_dat(r,i,c,rscbin,"cost")$[newv(v)$(not spur_techs(i))] * Spur-line costs assuming 1:1 ratio between gen cap and spur cap (i.e. no overbuilding) + sum{x$[xfeas(x)$x_r(x,r)$spur_techs(i)], spurline_cost(x) * Sw_SpurCostMult} ) + cost_fom(i,v,r,t) - ) / gen_rsc(i,v,r,t) + ) / gen_rsc(i,c,v,r,t) *plus VOM costs + cost_vom(i,v,r,t) ; -lcoe_cf_act(i,v,r,t,rscbin)$[valcap_init(i,v,r,t)$rsc_i(i)] = lcoe(i,v,r,t,rscbin) ; -lcoe_cf_act(i,v,r,t,"bin1")$[(not rsc_i(i))$valcap_init(i,v,r,t)$ivt(i,v,t)$avg_cf(i,v,r,t)] = +lcoe_cf_act(i,c,v,r,t,rscbin)$[i_c(i,c)$valcap_init(i,v,r,t)$rsc_i(i)] = lcoe(i,c,v,r,t,rscbin) ; +lcoe_cf_act(i,c,v,r,t,"bin1")$[i_c(i,c)$(not rsc_i(i))$valcap_init(i,v,r,t)$ivt(i,v,t)$avg_cf(i,v,r,t)] = * cost of capacity divided by generation ((crf(t) * cost_cap_fin_mult(i,r,t) * cost_cap(i,t)$newv(v) + cost_fom(i,v,r,t) @@ -231,32 +231,32 @@ lcoe_cf_act(i,v,r,t,"bin1")$[(not rsc_i(i))$valcap_init(i,v,r,t)$ivt(i,v,t)$avg_ + heat_rate(i,v,r,t) * fuel_price(i,r,t) ; -lcoe_nopol(i,v,r,t,rscbin)$valcap_init(i,v,r,t) = lcoe(i,v,r,t,rscbin) ; -lcoe_nopol(i,v,r,t,rscbin)$[valcap_init(i,v,r,t)$ivt(i,v,t)$rsc_i(i)$sum{c, m_rscfeas(r,i,c,rscbin) }$gen_rsc(i,v,r,t)] = +lcoe_nopol(i,c,v,r,t,rscbin)$[i_c(i,c)$valcap_init(i,v,r,t)] = lcoe(i,c,v,r,t,rscbin) ; +lcoe_nopol(i,c,v,r,t,rscbin)$[i_c(i,c)$valcap_init(i,v,r,t)$ivt(i,v,t)$rsc_i(i)$m_rscfeas(r,i,c,rscbin)$gen_rsc(i,c,v,r,t)] = * cost of capacity divided by generation (crf(t) * (cost_cap_fin_mult_noITC(i,r,t) * cost_cap(i,t) * Spur-line costs embedded in supply curve for techs without explicitly-modeled spurlines - + sum{c, m_rsc_dat(r,i,c,rscbin,"cost") }$newv(v)$(not spur_techs(i))) + + m_rsc_dat(r,i,c,rscbin,"cost")$newv(v)$(not spur_techs(i))) * Spur-line costs assuming 1:1 ratio between gen cap and spur cap (i.e. no overbuilding) + sum{x$[xfeas(x)$x_r(x,r)$spur_techs(i)], spurline_cost(x) * Sw_SpurCostMult} + cost_fom(i,v,r,t) - ) / gen_rsc(i,v,r,t) + ) / gen_rsc(i,c,v,r,t) *plus VOM costs + cost_vom(i,v,r,t) ; -lcoe_fullpol(i,v,r,t,rscbin)$valcap_init(i,v,r,t) = lcoe(i,v,r,t,rscbin) ; -lcoe_fullpol(i,v,r,t,rscbin)$[valcap_init(i,v,r,t)$ivt(i,v,t)$rsc_i(i)$sum{c, m_rscfeas(r,i,c,rscbin) }$gen_rsc(i,v,r,t)] = +lcoe_fullpol(i,c,v,r,t,rscbin)$[i_c(i,c)$valcap_init(i,v,r,t)] = lcoe(i,c,v,r,t,rscbin) ; +lcoe_fullpol(i,c,v,r,t,rscbin)$[i_c(i,c)$valcap_init(i,v,r,t)$ivt(i,v,t)$rsc_i(i)$m_rscfeas(r,i,c,rscbin)$gen_rsc(i,c,v,r,t)] = * cost of capacity divided by generation (crf(t) * (cost_cap_fin_mult(i,r,t) * cost_cap(i,t) * Spur-line costs embedded in supply curve for techs without explicitly-modeled spurlines - + sum{c, m_rsc_dat(r,i,c,rscbin,"cost") }$newv(v)$(not spur_techs(i))) + + m_rsc_dat(r,i,c,rscbin,"cost")$newv(v)$(not spur_techs(i))) * Spur-line costs assuming 1:1 ratio between gen cap and spur cap (i.e. no overbuilding) + sum{x$[xfeas(x)$x_r(x,r)$spur_techs(i)], spurline_cost(x) * Sw_SpurCostMult} + cost_fom(i,v,r,t)) - / gen_rsc(i,v,r,t) + / gen_rsc(i,c,v,r,t) *plus VOM costs + cost_vom(i,v,r,t) ; From 2f948abf63518f23e883b10712656b96dd7a60bf Mon Sep 17 00:00:00 2001 From: wcole Date: Thu, 3 Sep 2026 17:04:53 -0600 Subject: [PATCH 15/30] Add class to additional capacity by ivrt outputs --- postprocessing/bokehpivot/reeds2.py | 8 ++++---- reeds/core/solve/6_data_dump.gms | 8 ++++---- reeds/core/terminus/report.gms | 28 ++++++++++++++------------- reeds/core/terminus/report_params.csv | 6 +++--- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/postprocessing/bokehpivot/reeds2.py b/postprocessing/bokehpivot/reeds2.py index 8ef474c36..92c651577 100644 --- a/postprocessing/bokehpivot/reeds2.py +++ b/postprocessing/bokehpivot/reeds2.py @@ -2464,7 +2464,7 @@ def pre_spur(dfs, **kw): ('Value Streams Sequential New Techs', {'sources': [ {'name': 'vs', 'file': 'valuestreams_chosen.csv', 'columns': ['tech', 'vintage', 'rb', 'year', 'var_name', 'con_name', '$']}, - {'name': 'cap', 'file': 'cap_new_ivrt', 'columns': ['tech', 'vintage', 'rb', 'year', 'MW']}, + {'name': 'cap', 'file': 'cap_new_ivrt', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'MW']}, {'name': 'gen', 'file': 'gen_ivrt', 'columns': ['tech', 'vintage', 'rb', 'year', 'MWh']}, {'name': 'pvf_cap', 'file': 'pvf_capital', 'columns': ['year', 'pvfcap']}, {'name': 'pvf_onm', 'file': 'pvf_onm', 'columns': ['year', 'pvfonm']}, @@ -2494,7 +2494,7 @@ def pre_spur(dfs, **kw): ('Competitiveness Sequential New Techs', {'sources': [ {'name': 'vs', 'file': 'valuestreams_chosen.csv', 'columns': ['tech', 'vintage', 'rb', 'year', 'var_name', 'con_name', '$']}, - {'name': 'cap', 'file': 'cap_new_ivrt', 'columns': ['tech', 'vintage', 'rb', 'year', 'MW']}, + {'name': 'cap', 'file': 'cap_new_ivrt', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'MW']}, {'name': 'gen', 'file': 'gen_ivrt', 'columns': ['tech', 'vintage', 'rb', 'year', 'MWh']}, {'name': 'pvf_cap', 'file': 'pvf_capital', 'columns': ['year', 'pvfcap']}, {'name': 'pvf_onm', 'file': 'pvf_onm', 'columns': ['year', 'pvfonm']}, @@ -2530,7 +2530,7 @@ def pre_spur(dfs, **kw): ('LCOE ($/MWh) Sequential New Techs (uncurt MWh)', {'sources': [ {'name': 'vs', 'file': 'valuestreams_chosen.csv', 'columns': ['tech', 'vintage', 'rb', 'year', 'var_name', 'con_name', '$']}, - {'name': 'cap', 'file': 'cap_new_ivrt', 'columns': ['tech', 'vintage', 'rb', 'year', 'MW']}, + {'name': 'cap', 'file': 'cap_new_ivrt', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'MW']}, {'name': 'gen', 'file': 'gen_ivrt', 'columns': ['tech', 'vintage', 'rb', 'year', 'MWh']}, {'name': 'gen_uncurt', 'file': 'gen_ivrt_uncurt', 'columns': ['tech', 'vintage', 'rb', 'year', 'MWh']}, {'name': 'pvf_cap', 'file': 'pvf_capital', 'columns': ['year', 'pvfcap']}, @@ -2573,7 +2573,7 @@ def pre_spur(dfs, **kw): ('Value Streams Intertemporal', {'sources': [ {'name': 'vs', 'file': 'valuestreams_chosen.csv', 'columns': ['tech', 'vintage', 'rb', 'year', 'var_name', 'con_name', '$']}, - {'name': 'cap', 'file': 'cap_new_ivrt', 'columns': ['tech', 'vintage', 'rb', 'year', 'MW']}, + {'name': 'cap', 'file': 'cap_new_ivrt', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'MW']}, {'name': 'gen', 'file': 'gen_ivrt', 'columns': ['tech', 'vintage', 'rb', 'year', 'MWh']}, {'name': 'pvf_cap', 'file': 'pvf_capital', 'columns': ['year', 'pvfcap']}, {'name': 'pvf_onm', 'file': 'pvf_onm', 'columns': ['year', 'pvfonm']}, diff --git a/reeds/core/solve/6_data_dump.gms b/reeds/core/solve/6_data_dump.gms index c90f4b815..732a4d7e2 100644 --- a/reeds/core/solve/6_data_dump.gms +++ b/reeds/core/solve/6_data_dump.gms @@ -69,7 +69,7 @@ prod_filt(i,v,r,allh) "--MW-- power consumed for PRODUCE.l" ra_cap_loadsite(r,t) "--MW-- capacity of flexibly sited load" repbioprice_filt(r) "--2004$/MWh-- marginal price for biofuel in region where biofuel was used" repgasquant(cendiv,t) "--mmBTU-- NG fuel usage in ReEDS - used to determine NG price" -ret_ivrt(i,v,r,t) "--MW-- retirements of generation capacity" +ret_ivrt(i,c,v,r,t) "--MW-- retirements of generation capacity" ret(i,v,r) "--MW-- retirements of generation capacity" rsc_dat_filt(i,r,sc_cat,rscbin) "--$/MW-- capital costs filtered for pumped-hydro so arbitrage value doesn't exceed capital costs" storage_eff_filt(i) "--fraction-- storage efficiency filtered for the next solve year" @@ -137,10 +137,10 @@ inv_energy_ivrt(i,v,r,t)$[valcap(i,v,r,t)$trange(t)$battery(i)] = INV_ENERGY.l(i inv_ivrt("distpv",c,v,r,t)$[i_c("distpv",c)$trange(t)$(not tfirst(t))$valcap("distpv",v,r,t)] = cap_ivrt("distpv",c,v,r,t) - sum{tt$tprev(t,tt), cap_ivrt("distpv",c,v,r,tt) } ; inv_ivrt("distpv",c,"init-1",r,"%next_year%")$i_c("distpv",c) = inv_distpv(r,"%next_year%") ; -ret_ivrt(i,v,r,t)$([trange(t)$(not tfirst(t))$newv(v)]$valcap(i,v,r,t)) = sum{(c,tt)$[i_c(i,c)$tprev(t,tt)], cap_ivrt(i,c,v,r,tt)} - sum{c$i_c(i,c), cap_ivrt(i,c,v,r,t)} + sum{c$i_c(i,c), inv_ivrt(i,c,v,r,t)} ; -ret_ivrt(i,v,r,t)$([abs(ret_ivrt(i,v,r,t) < 1e-6)]$valcap(i,v,r,t)) = 0 ; +ret_ivrt(i,c,v,r,t)$[i_c(i,c)$trange(t)$(not tfirst(t))$newv(v)$valcap(i,v,r,t)] = sum{tt$tprev(t,tt), cap_ivrt(i,c,v,r,tt)} - cap_ivrt(i,c,v,r,t) + inv_ivrt(i,c,v,r,t) ; +ret_ivrt(i,c,v,r,t)$([abs(ret_ivrt(i,c,v,r,t) < 1e-6)]$valcap(i,v,r,t)) = 0 ; -ret(i,v,r)$valcap_ivr(i,v,r) = sum{t, ret_ivrt(i,v,r,t) } ; +ret(i,v,r)$valcap_ivr(i,v,r) = sum{(c,t)$i_c(i,c), ret_ivrt(i,c,v,r,t) } ; cap_exog_filt(i,v,r)$([not canada(i)]$valcap_ivr(i,v,r)) = sum{t$tnext(t), m_capacity_exog(i,v,r,t) } ; diff --git a/reeds/core/terminus/report.gms b/reeds/core/terminus/report.gms index 25b4d7333..1201420dc 100644 --- a/reeds/core/terminus/report.gms +++ b/reeds/core/terminus/report.gms @@ -813,10 +813,10 @@ losses_tran_h(rr,r,h,trtype,t)$[routes(r,rr,trtype,t)$tmodel_new(t)] * CAPACITY *========================= -cap_deg_ivrt(i,v,r,t)$valcap(i,v,r,t) = CAP.l(i,v,r,t) / ilr(i) ; +cap_deg_ivrt(i,c,v,r,t)$[i_c(i,c)$valcap(i,v,r,t)] = CAP.l(i,v,r,t) / ilr(i) ; +cap_deg_ivrt(i,c,v,r,t)$valcap_class(i,c,v,r,t) = CAP_CLASS.l(i,c,v,r,t) / ilr(i) ; -cap_ivrt(i,c,v,r,t)$[i_c(i,c)$(not (upv(i) or wind(i)))$valcap(i,v,r,t)] = cap_deg_ivrt(i,v,r,t) ; -cap_ivrt(i,c,v,r,t)$[valcap_class(i,c,v,r,t)$(not (upv(i) or wind(i)))] = CAP_CLASS.l(i,c,v,r,t) / ilr(i) ; +cap_ivrt(i,c,v,r,t)$[i_c(i,c)$(not (upv(i) or wind(i)))$valcap(i,v,r,t)] = cap_deg_ivrt(i,c,v,r,t) ; *upv, and wind have degradation, so use INV rather than CAP to get the reported capacity cap_ivrt(i,c,v,r,t)$[i_c(i,c)$(upv(i) or wind(i))$valcap(i,v,r,t)] = ( sum{rscbin, capacity_exog_rsc(i,c,v,r,rscbin,t) }$tmodel_new(t) @@ -860,12 +860,14 @@ cap_new_ann_nat(i,t)$tmodel_new(t) = sum{r, cap_new_ann(i,r,t) } ; cap_new_bin_out(i,c,v,r,t,rscbin)$[i_c(i,c)$rsc_i(i)$valinv(i,v,r,t)] = INV_RSC.l(i,c,v,r,rscbin,t) / ilr(i) ; cap_new_bin_out(i,c,v,r,t,"bin1")$[i_c(i,c)$(not rsc_i(i))$valinv(i,v,r,t)] = INV.l(i,v,r,t) / ilr(i) ; -cap_new_ivrt(i,v,r,t)$[valcap(i,v,r,t)] = [ - [INV.l(i,v,r,t) + sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) }]$valinv(i,v,r,t) - + [(1-upgrade_derate(i,v,r,t)) * (UPGRADES.l(i,v,r,t) - UPGRADES_RETIRE.l(i,v,r,t))]$[upgrade(i)$valcap(i,v,r,t)$Sw_Upgrades] +cap_new_ivrt(i,c,v,r,t)$[i_c(i,c)$valcap(i,v,r,t)] = [ + [INV.l(i,v,r,t)$(not rsc_i(i)) + + sum{rscbin$m_rscfeas(r,i,c,rscbin), INV_RSC.l(i,c,v,r,rscbin,t) }$rsc_i(i) + + INV_REFURB.l(i,c,v,r,t)]$valinv(i,v,r,t) + + [(1-upgrade_derate(i,v,r,t)) * (UPGRADES.l(i,v,r,t) - UPGRADES_RETIRE.l(i,v,r,t))]$[upgrade(i)$valcap(i,v,r,t)$Sw_Upgrades] ] / ilr(i) ; -cap_new_ivrt("distpv",v,r,t)$[tfirst(t)$valcap("distpv",v,r,t)] = sum{c$i_c("distpv",c), cap_ivrt("distpv",c,v,r,t) } ; -cap_new_ivrt("distpv",v,r,t)$[(not tfirst(t))$valcap("distpv",v,r,t)] = sum{c$i_c("distpv",c), cap_ivrt("distpv",c,v,r,t) - sum{tt$tprev(t,tt), cap_ivrt("distpv",c,v,r,tt) } } ; +cap_new_ivrt("distpv",c,v,r,t)$[i_c("distpv",c)$tfirst(t)$valcap("distpv",v,r,t)] = cap_ivrt("distpv",c,v,r,t) ; +cap_new_ivrt("distpv",c,v,r,t)$[i_c("distpv",c)$(not tfirst(t))$valcap("distpv",v,r,t)] = cap_ivrt("distpv",c,v,r,t) - sum{tt$tprev(t,tt), cap_ivrt("distpv",c,v,r,tt) } ; cap_new_ivrt_refurb(i,c,v,r,t)$[i_c(i,c)$valinv(i,v,r,t)] = INV_REFURB.l(i,c,v,r,t) / ilr(i) ; * Capacity by reV site @@ -921,12 +923,12 @@ cap_upgrade_ivrt(i,v,r,t)$[valcap(i,v,r,t)$upgrade(i)$Sw_Upgrades] = (1-upgrade_ * RETIRED CAPACITY *========================= -ret_ivrt(i,v,r,t)$[(not tfirst(t))] = - sum{(c,tt)$[i_c(i,c)$tprev(t,tt)], cap_ivrt(i,c,v,r,tt) } - sum{c$i_c(i,c), cap_ivrt(i,c,v,r,t) } + cap_new_ivrt(i,v,r,t) +ret_ivrt(i,c,v,r,t)$[i_c(i,c)$(not tfirst(t))] = + sum{tt$tprev(t,tt), cap_ivrt(i,c,v,r,tt) } - cap_ivrt(i,c,v,r,t) + cap_new_ivrt(i,c,v,r,t) - sum{ii$upgrade_from(ii,i), UPGRADES.l(ii,v,r,t) } ; -ret_ivrt(i,v,r,t)$[abs(ret_ivrt(i,v,r,t)) < 1e-6] = 0 ; +ret_ivrt(i,c,v,r,t)$[abs(ret_ivrt(i,c,v,r,t)) < 1e-6] = 0 ; -ret_out(i,r,t)$[(not tfirst(t))] = sum{v, ret_ivrt(i,v,r,t) } ; +ret_out(i,r,t)$[(not tfirst(t))] = sum{(c,v)$i_c(i,c), ret_ivrt(i,c,v,r,t) } ; ret_out(i,r,t)$[abs(ret_out(i,r,t)) < 1e-6] = 0 ; ret_ann(i,r,t)$ret_out(i,r,t) = ret_out(i,r,t) / (yeart(t) - sum{tt$tprev(t,tt), yeart(tt) }) ; ret_ann_nat(i,t)$tmodel_new(t) = sum{r, ret_ann(i,r,t) } ; @@ -957,7 +959,7 @@ cc_all_out(i,v,r,ccseason,t)$tmodel_new(t) = m_cc_mar(i,r,ccseason,t)$[(vre(i) or csp(i) or storage(i) or storage_hybrid(i)$(not csp(i)))$valinv_init(i,v,r,t)] ; -cap_new_cc(i,r,ccseason,t)$[(vre(i) or storage(i) or storage_hybrid(i)$(not csp(i)))$valcap_irt(i,r,t)] = sum{v$ivt(i,v,t),cap_new_ivrt(i,v,r,t) } ; +cap_new_cc(i,r,ccseason,t)$[(vre(i) or storage(i) or storage_hybrid(i)$(not csp(i)))$valcap_irt(i,r,t)] = sum{(c,v)$[i_c(i,c)$ivt(i,v,t)], cap_new_ivrt(i,c,v,r,t) } ; cc_new(i,r,ccseason,t)$[valcap_irt(i,r,t)$cap_new_cc(i,r,ccseason,t)] = sum{v$ivt(i,v,t), cc_all_out(i,v,r,ccseason,t) } ; diff --git a/reeds/core/terminus/report_params.csv b/reeds/core/terminus/report_params.csv index 17d3d7d64..d3d5f5a33 100644 --- a/reeds/core/terminus/report_params.csv +++ b/reeds/core/terminus/report_params.csv @@ -19,11 +19,11 @@ param,units,comment,reeds2x,output_rename,input "cap_ivrt(i,c,v,r,t)",MW,"undegraded power capacity by tech, year, region, and class",1,, "cap_energy_ivrt(i,v,r,t)",MWh,"undegraded energy capacity by tech, year, region, and class",1,, "cap_sdbin_out(i,r,ccseason,sdbin,t)",MW,binned storage capacity by year,,, -"cap_deg_ivrt(i,v,r,t)",MW,"Degraded capacity, equal to CAP.l",,, +"cap_deg_ivrt(i,c,v,r,t)",MW,"Degraded capacity, equal to CAP.l",,, "cap_new_ann(i,r,t)",MW/yr,new annual capacity by region,,, "cap_new_ann_nat(i,t)",MW/yr,new annual capacity national,,, "cap_new_bin_out(i,c,v,r,t,rscbin)",MW,capacity of built techs,,, -"cap_new_ivrt(i,v,r,t)",MW,new capacity,1,, +"cap_new_ivrt(i,c,v,r,t)",MW,new capacity,1,, "cap_new_ivrt_refurb(i,c,v,r,t)",MW,new refurbished capacity,,, "cap_new_out(i,r,t)",MW,"new capacity by region, which are investments and upgrades from one solve year to the next",,, "cap_energy_new_out(i,v,r,t)",MWh,"new energy capacity by region, which are investments from one solve year to the next",,, @@ -157,7 +157,7 @@ repgasquant_nat(t),Quads,national consumption of natural gas,,, "reqt_quant_sys(*,*,*,t)",varies,System-wide requirement quantity,,, "ret_ann(i,r,t)",MW/yr,annual retired capacity by region,,, "ret_ann_nat(i,t)",MW/yr,annual retired capacity national,,, -"ret_ivrt(i,v,r,t)",MW,retired capacity by region and vintage,1,, +"ret_ivrt(i,c,v,r,t)",MW,retired capacity by region and vintage,1,, "ret_out(i,r,t)",MW,retired capacity by region,,, "revenue(rev_cat,i,r,t)",2004$,sum of revenues,,, "revenue_nat(rev_cat,i,t)",2004$,sum of revenues,,, From 1bfc477c827fe937fa88c628432280e9d2f14dc7 Mon Sep 17 00:00:00 2001 From: wcole Date: Thu, 3 Sep 2026 17:22:00 -0600 Subject: [PATCH 16/30] Add class to cap_avail and capex_ivrt --- postprocessing/bokehpivot/reeds2.py | 8 ++++---- reeds/core/terminus/report.gms | 18 ++++++++++-------- reeds/core/terminus/report_params.csv | 4 ++-- reeds/reedsplots.py | 4 ++-- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/postprocessing/bokehpivot/reeds2.py b/postprocessing/bokehpivot/reeds2.py index 92c651577..5386056e5 100644 --- a/postprocessing/bokehpivot/reeds2.py +++ b/postprocessing/bokehpivot/reeds2.py @@ -2681,7 +2681,7 @@ def pre_spur(dfs, **kw): {'sources': [ {'name': 'lcoe', 'file': 'lcoe', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'bin','$/MWh']}, {'name': 'inv', 'file': 'cap_new_bin_out', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'bin','chosen MW']}, - {'name': 'avail', 'file': 'cap_avail', 'columns': ['tech', 'rb', 'year', 'bin','available MW']}, + {'name': 'avail', 'file': 'cap_avail', 'columns': ['tech', 'class', 'rb', 'year', 'bin','available MW']}, ], 'preprocess': [ {'func': pre_lcoe, 'args': {}}, @@ -2699,7 +2699,7 @@ def pre_spur(dfs, **kw): {'sources': [ {'name': 'lcoe', 'file': 'lcoe_cf_act', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'bin','$/MWh']}, {'name': 'inv', 'file': 'cap_new_bin_out', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'bin','chosen MW']}, - {'name': 'avail', 'file': 'cap_avail', 'columns': ['tech', 'rb', 'year', 'bin','available MW']}, + {'name': 'avail', 'file': 'cap_avail', 'columns': ['tech', 'class', 'rb', 'year', 'bin','available MW']}, ], 'preprocess': [ {'func': pre_lcoe, 'args': {}}, @@ -2717,7 +2717,7 @@ def pre_spur(dfs, **kw): {'sources': [ {'name': 'lcoe', 'file': 'lcoe_nopol', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'bin','$/MWh']}, {'name': 'inv', 'file': 'cap_new_bin_out', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'bin','chosen MW']}, - {'name': 'avail', 'file': 'cap_avail', 'columns': ['tech', 'rb', 'year', 'bin','available MW']}, + {'name': 'avail', 'file': 'cap_avail', 'columns': ['tech', 'class', 'rb', 'year', 'bin','available MW']}, ], 'preprocess': [ {'func': pre_lcoe, 'args': {}}, @@ -2735,7 +2735,7 @@ def pre_spur(dfs, **kw): {'sources': [ {'name': 'lcoe', 'file': 'lcoe_fullpol', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'bin','$/MWh']}, {'name': 'inv', 'file': 'cap_new_bin_out', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'bin','chosen MW']}, - {'name': 'avail', 'file': 'cap_avail', 'columns': ['tech', 'rb', 'year', 'bin','available MW']}, + {'name': 'avail', 'file': 'cap_avail', 'columns': ['tech', 'class', 'rb', 'year', 'bin','available MW']}, ], 'preprocess': [ {'func': pre_lcoe, 'args': {}}, diff --git a/reeds/core/terminus/report.gms b/reeds/core/terminus/report.gms index 1201420dc..ef09b1e7b 100644 --- a/reeds/core/terminus/report.gms +++ b/reeds/core/terminus/report.gms @@ -893,15 +893,15 @@ site_hybridization(x,t)$site_pv_fraction(x,t) = abs(1 - 2 * abs(site_pv_fraction *========================= * AVAILABLE CAPACITY *========================= -cap_avail(i,r,t,rscbin)$[tmodel_new(t)$rsc_i(i)$sum{c, m_rscfeas(r,i,c,rscbin) }$sum{c, m_rsc_con(r,i,c) }] = - sum{c, m_rsc_dat(r,i,c,rscbin,"cap") } +cap_avail(i,c,r,t,rscbin)$[i_c(i,c)$tmodel_new(t)$rsc_i(i)$m_rscfeas(r,i,c,rscbin)$m_rsc_con(r,i,c)] = + m_rsc_dat(r,i,c,rscbin,"cap") + hyd_add_upg_cap(r,i,rscbin,t)$(Sw_HydroCapEnerUpgradeType=1) - ( - sum{(ii,c,v,tt)$[rsc_agg(i,ii)$i_c(ii,c)$valinv(ii,v,r,tt)$(yeart(tt) < yeart(t))], + sum{(ii,v,tt)$[rsc_agg(i,ii)$i_c(ii,c)$valinv(ii,v,r,tt)$(yeart(tt) < yeart(t))], INV_RSC.l(ii,c,v,r,rscbin,tt) * resourcescaler(ii) } - + sum{(ii,c,v,tt)$[rsc_agg(i,ii)$i_c(ii,c)$tfirst(tt)$exog_rsc(i)], + + sum{(ii,v,tt)$[rsc_agg(i,ii)$i_c(ii,c)$tfirst(tt)$exog_rsc(i)], capacity_exog_rsc(ii,c,v,r,rscbin,tt) } ); @@ -1225,11 +1225,13 @@ RE_gen_price_nat(t)$tmodel_new(t) = (1/cost_scale) * crf(t) * eq_national_gen.m( * [i,v,r,t]-level capital expenditures (for retail rate calculations) *========================= -capex_ivrt(i,v,r,t)$valcap(i,v,r,t) = - INV.l(i,v,r,t) * (cost_cap_fin_mult_no_credits(i,r,t) * cost_cap(i,t) ) +capex_ivrt(i,c,v,r,t)$[i_c(i,c)$valcap(i,v,r,t)] = + [INV.l(i,v,r,t)$(not rsc_i(i)) + + sum{rscbin$m_rscfeas(r,i,c,rscbin), INV_RSC.l(i,c,v,r,rscbin,t) }$rsc_i(i)] + * (cost_cap_fin_mult_no_credits(i,r,t) * cost_cap(i,t) ) + INV_ENERGY.l(i,v,r,t) * (cost_cap_fin_mult_no_credits(i,r,t) * cost_cap_energy(i,t) ) - + sum{(c,rscbin)$[i_c(i,c)$m_rscfeas(r,i,c,rscbin)],INV_RSC.l(i,c,v,r,rscbin,t) * m_rsc_dat(r,i,c,rscbin,"cost") * cost_cap_fin_mult_no_credits(i,r,t) } - + (sum{c$i_c(i,c), INV_REFURB.l(i,c,v,r,t) } * (cost_cap_fin_mult_no_credits(i,r,t) * cost_cap(i,t)))$[refurbtech(i)$Sw_Refurb] + + sum{rscbin$m_rscfeas(r,i,c,rscbin), INV_RSC.l(i,c,v,r,rscbin,t) * m_rsc_dat(r,i,c,rscbin,"cost") * cost_cap_fin_mult_no_credits(i,r,t) } + + (INV_REFURB.l(i,c,v,r,t) * (cost_cap_fin_mult_no_credits(i,r,t) * cost_cap(i,t)))$[refurbtech(i)$Sw_Refurb] + UPGRADES.l(i,v,r,t) * (cost_upgrade(i,v,r,t) * cost_cap_fin_mult_no_credits(i,r,t))$[upgrade(i)$Sw_Upgrades] ; *========================= diff --git a/reeds/core/terminus/report_params.csv b/reeds/core/terminus/report_params.csv index d3d5f5a33..203bb735e 100644 --- a/reeds/core/terminus/report_params.csv +++ b/reeds/core/terminus/report_params.csv @@ -9,7 +9,7 @@ param,units,comment,reeds2x,output_rename,input "state_cap_and_trade_price(st,t)",$/metric ton,marginal from state annual CO2 cap constraints,,, "state_cap_and_trade_quant(st,t)",metric tons,state annual CO2 cap constraints,,, "cap_above_limit(tg,r,t)",MW,Near-term capacity deployed above interconnection queue limit,,, -"cap_avail(i,r,t,rscbin)",MW,Available capacity at beginning of model year for rsc techs,,, +"cap_avail(i,c,r,t,rscbin)",MW,Available capacity at beginning of model year for rsc techs,,, "cap_exog(i,c,v,r,t)",MW,Exogenous capacity from m_capacity_exog; used by reeds_to_rev,,, "cap_firm(i,r,ccseason,t)",MW,firm capacity that counts toward the reserve margin constraint by BA and season,,, "cap_nat(i,t)",MW,national capacity,,, @@ -30,7 +30,7 @@ param,units,comment,reeds2x,output_rename,input "cap_out(i,c,r,t)",MW,capacity by region,1,cap, "cap_out_ivrt(i,v,r,t)",MW,capacity by region and vintage,,, "capacity_offline(i,r,allh,t)",MW,capacity offline due to forced or scheduled outages,,, -"capex_ivrt(i,v,r,t)",$,"capital expenditure for new capacity, no ITC/depreciation/PTC reductions",,, +"capex_ivrt(i,c,v,r,t)",$,"capital expenditure for new capacity, no ITC/depreciation/PTC reductions",,, "cap_upgrade(i,r,t)",MW,upgraded capacity by region,,, "cap_upgrade_ivrt(i,v,r,t)",MW,upgraded capacity by region and vintage,,, "CO2_CAPTURED_out(r,allh,t)",metric tons/hour,amount of CO2 captured_out from DAC and CCS technologies,,, diff --git a/reeds/reedsplots.py b/reeds/reedsplots.py index c1771be29..5b367b561 100644 --- a/reeds/reedsplots.py +++ b/reeds/reedsplots.py @@ -972,8 +972,8 @@ def plot_diff_maps( dfbase.i = simplify_techs(dfbase.i) dfcomp.i = simplify_techs(dfcomp.i) - dfbase = dfbase.groupby(['i','r','t']).sum().reset_index().copy() - dfcomp = dfcomp.groupby(['i','r','t']).sum().reset_index().copy() + dfbase = dfbase.groupby(['i','r','t'])[valcol].sum().reset_index().copy() + dfcomp = dfcomp.groupby(['i','r','t'])[valcol].sum().reset_index().copy() # print(dfbase.i.unique()) # print(dfcomp.i.unique()) From 005e4790a6443b1e8a85616e5043218167864a4b Mon Sep 17 00:00:00 2001 From: wcole Date: Thu, 3 Sep 2026 17:55:21 -0600 Subject: [PATCH 17/30] Fix issue in Bokeh --- postprocessing/bokehpivot/reeds2.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/postprocessing/bokehpivot/reeds2.py b/postprocessing/bokehpivot/reeds2.py index 5386056e5..4bfd3a39f 100644 --- a/postprocessing/bokehpivot/reeds2.py +++ b/postprocessing/bokehpivot/reeds2.py @@ -476,9 +476,10 @@ def pre_abatement_cost(dfs, **kw): return df def add_class(df, **kw): - cond = df['tech'].str.contains('_', regex=False) - #Assume class is at the end, after the final underscore: - df.loc[cond, 'class']='class_' + df.loc[cond, 'tech'].str.split('_').str[-1] + #Class is read from the class column; '0' marks techs with no resource class + cond = df['class'].astype(str) != '0' + df.loc[cond, 'class'] = 'class_' + df.loc[cond, 'class'].astype(str) + df.loc[~cond, 'class'] = None return df def sort_timeslices(df, **kw): From 098e2a46289309a1097f26e4d3fe6a6e29a21151 Mon Sep 17 00:00:00 2001 From: wcole Date: Thu, 3 Sep 2026 18:52:47 -0600 Subject: [PATCH 18/30] Adjust csp-ns allocation --- reeds/core/setup/b_inputs.gms | 2 +- reeds/core/terminus/report.gms | 43 +++++++++++++++++---------- reeds/input_processing/writecapdat.py | 41 ++++++++++++++----------- 3 files changed, 51 insertions(+), 35 deletions(-) diff --git a/reeds/core/setup/b_inputs.gms b/reeds/core/setup/b_inputs.gms index 676aeb03d..93d1a4b9e 100644 --- a/reeds/core/setup/b_inputs.gms +++ b/reeds/core/setup/b_inputs.gms @@ -1644,7 +1644,7 @@ m_capacity_exog(i,"init-1",r,t)$geo(i) = geo_cap_exog(i,r) ; * We assign the ~1.3 GW of exising csp-ns to upv throughout the model, but then * convert 1.3 GW of upv back to csp-ns in the output processing. $onempty -parameter cap_cspns(r,allt) "--MW-- csp-ns capacity" +parameter cap_cspns(r,c,allt) "--MW-- csp-ns capacity by the upv resource class it is modeled as" / $offlisting $ondelim diff --git a/reeds/core/terminus/report.gms b/reeds/core/terminus/report.gms index ef09b1e7b..708b63abe 100644 --- a/reeds/core/terminus/report.gms +++ b/reeds/core/terminus/report.gms @@ -651,16 +651,27 @@ gen_h(i,r,h,t)$[tmodel_new(t)$valgen_irt(i,r,t)] = - sum{(v,p)$[consume(i)$valcap(i,v,r,t)$i_p(i,p)], PRODUCE.l(p,i,v,r,h,t) / prod_conversion_rate(i,v,r,t)}$Sw_Prod ; * A small amount of upv capacity is actually csp-ns, so convert it back now. -* UPV capacity is already in MWac at this point (matching csp-ns), -* so don't need to account for ILR. -gen_h("csp-ns",r,h,t)$[cap_cspns(r,t)$tmodel_new(t)] - = cap_cspns(r,t) * sum{c$i_c("upv_5",c), m_cf("upv_5",c,"new1",r,h,t) } ; -* We have to take csp-ns generation from somewhere, so take it from upv_5 (which all the -* csp-ns-containing regions have) -gen_h("upv_5",r,h,t)$[cap_cspns(r,t)$tmodel_new(t)] - = gen_h("upv_5",r,h,t) - gen_h("csp-ns",r,h,t) ; -* Make sure it doesn't go negative, just in case -gen_h("upv_5",r,h,t)$[cap_cspns(r,t)$tmodel_new(t)$(gen_h("upv_5",r,h,t) < 0)] = 0 ; +* writecapdat.py bins csp-ns into the upv resource classes it is modeled as, so take it +* back out of those same classes. +parameter cap_upv_class(c,r,t) "--MWac-- upv capacity by resource class" ; +parameter cap_cspns_short(c,r,t) "--MWac-- csp-ns capacity with no upv capacity to come out of" ; + +cap_upv_class(c,r,t)$tmodel_new(t) = + sum{(i,v)$[upv(i)$valcap_class(i,c,v,r,t)], CAP_CLASS.l(i,c,v,r,t) / ilr(i) } ; + +cap_cspns_short(c,r,t)$[cap_cspns(r,c,t)$tmodel_new(t)] = + max(0, cap_cspns(r,c,t) - cap_upv_class(c,r,t)) ; + +* Move the generation that goes with the reassigned capacity, class by class +gen_h("csp-ns",r,h,t)$[sum{c, cap_cspns(r,c,t) }$tmodel_new(t)] = + sum{(i,c)$[upv(i)$i_c(i,c)$cap_upv_class(c,r,t)], + gen_h(i,r,h,t) * min(cap_cspns(r,c,t), cap_upv_class(c,r,t)) / cap_upv_class(c,r,t) } ; + +gen_h(i,r,h,t)$[upv(i)$tmodel_new(t)$sum{c$i_c(i,c), cap_cspns(r,c,t) } + $sum{c$i_c(i,c), cap_upv_class(c,r,t) }] = + gen_h(i,r,h,t) + * (1 - sum{c$i_c(i,c), + min(cap_cspns(r,c,t), cap_upv_class(c,r,t)) / cap_upv_class(c,r,t) }) ; gen_h_nat(i,h,t)$tmodel_new(t) = sum{r, gen_h(i,r,h,t) } ; * Do it again for stress periods @@ -828,12 +839,10 @@ cap_out(i,c,r,t)$[i_c(i,c)$valcap_irt(i,r,t)$tmodel_new(t)] = sum{v$valcap(i,v,r * A small amount of upv capacity is actually csp-ns, so convert it back now. * UPV capacity is already in MWac at this point (matching csp-ns), * so don't need to account for ILR -cap_out("csp-ns",c,r,t)$[i_c("csp-ns",c)$cap_cspns(r,t)$tmodel_new(t)] = cap_cspns(r,t) ; -* We have to take csp-ns capacity from somewhere, so take it from the upv class that all the -* csp-ns-containing regions have -cap_out("upv_5",c,r,t)$[i_c("upv_5",c)$cap_cspns(r,t)$tmodel_new(t)] = cap_out("upv_5",c,r,t) - cap_cspns(r,t) ; -* Make sure it doesn't go negative, just in case -cap_out("upv_5",c,r,t)$[i_c("upv_5",c)$cap_cspns(r,t)$tmodel_new(t)$(cap_out("upv_5",c,r,t) < 0)] = 0 ; +cap_out("csp-ns",c,r,t)$[i_c("csp-ns",c)$tmodel_new(t)] = sum{cc, cap_cspns(r,cc,t) } ; +* Take it back out of the same upv classes it was binned into +cap_out(i,c,r,t)$[upv(i)$i_c(i,c)$cap_cspns(r,c,t)$tmodel_new(t)] = + max(0, cap_out(i,c,r,t) - cap_cspns(r,c,t)) ; cap_nat(i,t)$tmodel_new(t) = sum{(c,r)$i_c(i,c), cap_out(i,c,r,t) } ; * Exogenous capacity (used by reeds_to_rev) @@ -1788,6 +1797,8 @@ error_check("cap") = sum{(i,v,r,t)$[not valcap(i,v,r,t)], CAP.l(i,v,r,t) } ; error_check("RPS") = sum{(RPSCat,i,st,ast,t)$[(not RecMap(i,RPSCat,st,ast,t))$[(not stfeas(ast)) or not sameas(ast,"voluntary")]], RECS.l(RPSCat,i,st,ast,t) } ; error_check("OpRes") = sum{(ortype,i,v,r,h,t)$[not valgen(i,v,r,t)], OPRES.l(ortype,i,v,r,h,t) } ; error_check("m_rsc_dat") = sum{(r,i,c,rscbin)$m_rsc_dat(r,i,c,rscbin,"cap"), m_rsc_dat_init(r,i,c,rscbin) - m_rsc_dat(r,i,c,rscbin,"cap") } ; +* csp-ns capacity is reported by moving it out of upv; flag any that had no upv to come out of +error_check("cspns") = sum{(c,r,t), cap_cspns_short(c,r,t) } ; * Check to make sure there's no dropped/excess load in or after Sw_StartMarkets error_check("dropped") = sum{(r,h,t)$[yeart(t)>=Sw_StartMarkets], DROPPED.l(r,h,t) } ; diff --git a/reeds/input_processing/writecapdat.py b/reeds/input_processing/writecapdat.py index 1b53264dd..d8abf9326 100644 --- a/reeds/input_processing/writecapdat.py +++ b/reeds/input_processing/writecapdat.py @@ -437,28 +437,33 @@ def main(reeds_path, inputs_case): # We model csp-ns (CSP No Storage) as upv throughout ReEDS, but switch it back for reporting. # So save the csp-ns capacity separately, then rename it. - csp_units = ( - gdb_use.loc[(gdb_use['tech']=='csp-ns') & (gdb_use['RetireYear'] > startyear)] - .groupby(['r','StartYear','RetireYear']).summer_power_capacity_MW.sum() - .reset_index() - ) + csp_units = gdb_use.loc[ + (gdb_use['tech']=='csp-ns') & (gdb_use['RetireYear'] > startyear) + ].copy() if len(csp_units): - cap_cspns = ( - pd.concat( - {i: pd.Series( - [row.summer_power_capacity_MW]*(row.RetireYear - row.StartYear + 2), - index=range(row.StartYear, row.RetireYear + 2) - ) for (i,row) in csp_units.iterrows()}, - axis=1) - .rename(columns=csp_units['r']).fillna(0) - .T.groupby(level=0).sum().T - .stack().replace(0,np.nan).dropna() - .rename_axis(['t','r']).reorder_levels(['r','t']).rename('MWac') - ) + # csp-ns is modeled as upv, so bin it into the same resource classes that the + # upv capacity it becomes will be binned into. Reporting takes it back out of + # those classes. + upv_class = get_class_cf_bounds( + reeds_path, tech='upv', access_case=sw.GSw_SitingUPV, subtech='') + csp_units['c'] = csp_units['reV_capacity_factor_ac'].apply( + lambda x: assign_class(x, 'upv', upv_class)) + cap_cspns = pd.concat( + [ + pd.DataFrame({ + 'r': row.r, + 'c': row.c, + 't': list(range(row.StartYear, row.RetireYear + 2)), + 'MWac': row.summer_power_capacity_MW, + }) + for (_, row) in csp_units.iterrows() + ], + ignore_index=True, + ).groupby(['r','c','t']).MWac.sum() cap_cspns = ( cap_cspns.loc[cap_cspns.index.get_level_values('t') >= startyear].copy()) else: - cap_cspns = pd.DataFrame(columns=['r','t','MWac']).set_index(['r','t']) + cap_cspns = pd.DataFrame(columns=['r','c','t','MWac']).set_index(['r','c','t']) # Rename csp-ns to upv gdb_use.loc[gdb_use['tech']=='csp-ns','coolingwatertech'] = ( gdb_use.loc[gdb_use['tech']=='csp-ns','coolingwatertech'] From e5eff1196fe3dc3addc8c2d6a0604f469ed1bda0 Mon Sep 17 00:00:00 2001 From: wcole Date: Wed, 9 Sep 2026 08:29:03 -0600 Subject: [PATCH 19/30] Update bokeh class assignments. --- postprocessing/bokehpivot/reeds2.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/postprocessing/bokehpivot/reeds2.py b/postprocessing/bokehpivot/reeds2.py index 4bfd3a39f..f86e927e0 100644 --- a/postprocessing/bokehpivot/reeds2.py +++ b/postprocessing/bokehpivot/reeds2.py @@ -1489,7 +1489,7 @@ def pre_spur(dfs, **kw): ('New Annual Capacity BA (GW)', {'file':'cap_new_ann', - 'columns': ['tech', 'class', 'rb', 'year', 'Capacity (GW)'], + 'columns': ['tech', 'rb', 'year', 'Capacity (GW)'], 'preprocess': [ {'func': scale_column, 'args': {'scale_factor': .001, 'column':'Capacity (GW)'}}, ], @@ -1523,7 +1523,7 @@ def pre_spur(dfs, **kw): ('Annual Retirements BA (GW)', {'file':'ret_ann', - 'columns': ['tech', 'class', 'rb', 'year', 'Capacity (GW)'], + 'columns': ['tech', 'rb', 'year', 'Capacity (GW)'], 'preprocess': [ {'func': scale_column, 'args': {'scale_factor': .001, 'column':'Capacity (GW)'}}, ], @@ -2551,7 +2551,7 @@ def pre_spur(dfs, **kw): ('Value Streams Sequential Existing Techs', {'sources': [ {'name': 'vs', 'file': 'valuestreams_chosen.csv', 'columns': ['tech', 'vintage', 'rb', 'year', 'var_name', 'con_name', '$']}, - {'name': 'cap', 'file': 'cap_ivrt', 'columns': ['tech', 'vintage', 'rb', 'year', 'MW']}, + {'name': 'cap', 'file': 'cap_ivrt', 'columns': ['tech', 'class', 'vintage', 'rb', 'year', 'MW']}, {'name': 'gen', 'file': 'gen_ivrt', 'columns': ['tech', 'vintage', 'rb', 'year', 'MWh']}, {'name': 'pvf_cap', 'file': 'pvf_capital', 'columns': ['year', 'pvfcap']}, {'name': 'pvf_onm', 'file': 'pvf_onm', 'columns': ['year', 'pvfonm']}, @@ -3066,7 +3066,7 @@ def pre_spur(dfs, **kw): ), ('Upgraded Capacity (GW)', {'file':'cap_upgrade', - 'columns': ['tech', 'class', 'rb', 'year', 'Capacity (GW)'], + 'columns': ['tech', 'rb', 'year', 'Capacity (GW)'], 'preprocess': [ # {'func': sum_over_cols, 'args': {'drop_cols': ['rb'], 'group_cols': ['tech', 'year']}}, {'func': scale_column, 'args': {'scale_factor': .001, 'column':'Capacity (GW)'}}, From ea86ae1066a178b537ea9a1d00b15551fca1dd8b Mon Sep 17 00:00:00 2001 From: wcole Date: Wed, 9 Sep 2026 08:36:12 -0600 Subject: [PATCH 20/30] Add "excess" and and unallocated csp-ns to compare_cases.py --- postprocessing/compare_cases.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/postprocessing/compare_cases.py b/postprocessing/compare_cases.py index c03d8f868..e6ab82f8d 100644 --- a/postprocessing/compare_cases.py +++ b/postprocessing/compare_cases.py @@ -661,7 +661,9 @@ def plot_bars_abs_stacked( 'RPS': {'title': 'Non-RecMap\nRECS\n[GWh]', 'scale':1e-3}, 'OpRes': {'title': 'Non-valgen\nopres\n[MWh]', 'scale':1}, 'm_rsc_dat': {'title': 'Supply curve\ntweaks [GW]', 'scale':1e-3}, + 'cspns': {'title': 'Unallocated\ncsp-ns [MW]', 'scale':1}, 'dropped': {'title': 'Dropped load\n[GWh]', 'scale':1e-3}, + 'excess': {'title': 'Excess load\n[GWh]', 'scale':1e-3}, } data = {k:v for k,v in data.items() if k in dfplot.index} From 550fbb02bd8ea83d13fcad856bdc1e37326f4a1f Mon Sep 17 00:00:00 2001 From: wcole Date: Wed, 9 Sep 2026 08:40:57 -0600 Subject: [PATCH 21/30] Move csp-ns reassignment parameters to report_params.csv --- postprocessing/compare_cases.py | 2 +- reeds/core/terminus/report.gms | 3 --- reeds/core/terminus/report_params.csv | 2 ++ 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/postprocessing/compare_cases.py b/postprocessing/compare_cases.py index e6ab82f8d..c71ade555 100644 --- a/postprocessing/compare_cases.py +++ b/postprocessing/compare_cases.py @@ -661,7 +661,7 @@ def plot_bars_abs_stacked( 'RPS': {'title': 'Non-RecMap\nRECS\n[GWh]', 'scale':1e-3}, 'OpRes': {'title': 'Non-valgen\nopres\n[MWh]', 'scale':1}, 'm_rsc_dat': {'title': 'Supply curve\ntweaks [GW]', 'scale':1e-3}, - 'cspns': {'title': 'Unallocated\ncsp-ns [MW]', 'scale':1}, + 'cspns': {'title': 'Unreassigned\ncsp-ns [MW]', 'scale':1}, 'dropped': {'title': 'Dropped load\n[GWh]', 'scale':1e-3}, 'excess': {'title': 'Excess load\n[GWh]', 'scale':1e-3}, } diff --git a/reeds/core/terminus/report.gms b/reeds/core/terminus/report.gms index 708b63abe..35f03c446 100644 --- a/reeds/core/terminus/report.gms +++ b/reeds/core/terminus/report.gms @@ -653,9 +653,6 @@ gen_h(i,r,h,t)$[tmodel_new(t)$valgen_irt(i,r,t)] = * A small amount of upv capacity is actually csp-ns, so convert it back now. * writecapdat.py bins csp-ns into the upv resource classes it is modeled as, so take it * back out of those same classes. -parameter cap_upv_class(c,r,t) "--MWac-- upv capacity by resource class" ; -parameter cap_cspns_short(c,r,t) "--MWac-- csp-ns capacity with no upv capacity to come out of" ; - cap_upv_class(c,r,t)$tmodel_new(t) = sum{(i,v)$[upv(i)$valcap_class(i,c,v,r,t)], CAP_CLASS.l(i,c,v,r,t) / ilr(i) } ; diff --git a/reeds/core/terminus/report_params.csv b/reeds/core/terminus/report_params.csv index 203bb735e..361e5a0e7 100644 --- a/reeds/core/terminus/report_params.csv +++ b/reeds/core/terminus/report_params.csv @@ -10,6 +10,7 @@ param,units,comment,reeds2x,output_rename,input "state_cap_and_trade_quant(st,t)",metric tons,state annual CO2 cap constraints,,, "cap_above_limit(tg,r,t)",MW,Near-term capacity deployed above interconnection queue limit,,, "cap_avail(i,c,r,t,rscbin)",MW,Available capacity at beginning of model year for rsc techs,,, +"cap_cspns_short(c,r,t)",MWac,csp-ns capacity that could not be reassigned out of upv,,, "cap_exog(i,c,v,r,t)",MW,Exogenous capacity from m_capacity_exog; used by reeds_to_rev,,, "cap_firm(i,r,ccseason,t)",MW,firm capacity that counts toward the reserve margin constraint by BA and season,,, "cap_nat(i,t)",MW,national capacity,,, @@ -20,6 +21,7 @@ param,units,comment,reeds2x,output_rename,input "cap_energy_ivrt(i,v,r,t)",MWh,"undegraded energy capacity by tech, year, region, and class",1,, "cap_sdbin_out(i,r,ccseason,sdbin,t)",MW,binned storage capacity by year,,, "cap_deg_ivrt(i,c,v,r,t)",MW,"Degraded capacity, equal to CAP.l",,, +"cap_upv_class(c,r,t)",MWac,upv capacity by resource class,,, "cap_new_ann(i,r,t)",MW/yr,new annual capacity by region,,, "cap_new_ann_nat(i,t)",MW/yr,new annual capacity national,,, "cap_new_bin_out(i,c,v,r,t,rscbin)",MW,capacity of built techs,,, From 56e65c27981795897e0fdf781ffad86178e95930 Mon Sep 17 00:00:00 2001 From: wcole Date: Wed, 9 Sep 2026 10:05:36 -0600 Subject: [PATCH 22/30] Fix csp-ns accounting error. --- reeds/core/setup/b_inputs.gms | 5 +++-- reeds/core/terminus/report.gms | 4 +++- reeds/input_processing/writecapdat.py | 7 ++++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/reeds/core/setup/b_inputs.gms b/reeds/core/setup/b_inputs.gms index 90de8569c..0cfd79c0f 100644 --- a/reeds/core/setup/b_inputs.gms +++ b/reeds/core/setup/b_inputs.gms @@ -1641,8 +1641,9 @@ m_capacity_exog(i,v,r,t)$capacity_exog(i,v,r,t) = capacity_exog(i,v,r,t) ; m_capacity_exog_energy(i,v,r,t)$capacity_exog_energy(i,v,r,t) = capacity_exog_energy(i,v,r,t) ; m_capacity_exog(i,"init-1",r,t)$geo(i) = geo_cap_exog(i,r) ; -* We assign the ~1.3 GW of exising csp-ns to upv throughout the model, but then -* convert 1.3 GW of upv back to csp-ns in the output processing. +* We assign the ~1.3 GW of existing csp-ns to upv throughout the model, both in the +* exogenous and the prescribed capacity, and convert it back to csp-ns when reporting. +* Written by writecapdat.py $onempty parameter cap_cspns(r,c,allt) "--MW-- csp-ns capacity by the upv resource class it is modeled as" / diff --git a/reeds/core/terminus/report.gms b/reeds/core/terminus/report.gms index 35f03c446..d23f93feb 100644 --- a/reeds/core/terminus/report.gms +++ b/reeds/core/terminus/report.gms @@ -652,7 +652,9 @@ gen_h(i,r,h,t)$[tmodel_new(t)$valgen_irt(i,r,t)] = ; * A small amount of upv capacity is actually csp-ns, so convert it back now. * writecapdat.py bins csp-ns into the upv resource classes it is modeled as, so take it -* back out of those same classes. +* back out of those same classes. cap_cspns_short catches any csp-ns left with no upv to +* come out of, which means the capacity written by writecapdat.py and the capacity carried +* by the model have diverged. cap_upv_class(c,r,t)$tmodel_new(t) = sum{(i,v)$[upv(i)$valcap_class(i,c,v,r,t)], CAP_CLASS.l(i,c,v,r,t) / ilr(i) } ; diff --git a/reeds/input_processing/writecapdat.py b/reeds/input_processing/writecapdat.py index d8abf9326..b6cc4588a 100644 --- a/reeds/input_processing/writecapdat.py +++ b/reeds/input_processing/writecapdat.py @@ -647,6 +647,11 @@ def main(reeds_path, inputs_case): (cap_exog, rsc_class) = create_exog_rsc(reeds_path, inputs_case, gdb_use_cap_exog, TECH, COLNAMES, sw, startyear) + # csp-ns is modeled as upv, so its pre-startyear capacity belongs with the exogenous + # upv capacity. + exog_cap_upv = pd.concat( + [cap_exog['upv'], cap_exog['csp-ns']], ignore_index=True) + #%%#################################### # -- RSC Prescribed Capacity -- # @@ -1039,7 +1044,7 @@ def main(reeds_path, inputs_case): 'can_imports_capacity' : can_imports_capacity.reset_index(), 'geoexist' : geoexist, 'h2_ba_share': h2_ba_share_out, - 'exog_cap_upv':cap_exog['upv'], + 'exog_cap_upv':exog_cap_upv, 'exog_cap_wind-ons':cap_exog['wind-ons'], 'exog_cap_wind-ofs':cap_exog['wind-ofs'], 'exog_cap_geohydro':cap_exog['geohydro_allkm'] From a17e8bb2230aa9b668eda98bd871f13509531625 Mon Sep 17 00:00:00 2001 From: wcole Date: Wed, 9 Sep 2026 10:36:15 -0600 Subject: [PATCH 23/30] Update comment for cap_cspns --- reeds/core/setup/b_inputs.gms | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reeds/core/setup/b_inputs.gms b/reeds/core/setup/b_inputs.gms index 0cfd79c0f..8fdf70faa 100644 --- a/reeds/core/setup/b_inputs.gms +++ b/reeds/core/setup/b_inputs.gms @@ -1645,7 +1645,7 @@ m_capacity_exog(i,"init-1",r,t)$geo(i) = geo_cap_exog(i,r) ; * exogenous and the prescribed capacity, and convert it back to csp-ns when reporting. * Written by writecapdat.py $onempty -parameter cap_cspns(r,c,allt) "--MW-- csp-ns capacity by the upv resource class it is modeled as" +parameter cap_cspns(r,c,allt) "--MW-- csp-ns capacity modeled as upv, by resource class" / $offlisting $ondelim From 3036306ffe02ee882485ab4f78090ec61ed2efc6 Mon Sep 17 00:00:00 2001 From: wcole Date: Wed, 9 Sep 2026 11:27:38 -0600 Subject: [PATCH 24/30] Add class to merges in bokeh. --- postprocessing/bokehpivot/reeds2.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/postprocessing/bokehpivot/reeds2.py b/postprocessing/bokehpivot/reeds2.py index f86e927e0..81cd092fd 100644 --- a/postprocessing/bokehpivot/reeds2.py +++ b/postprocessing/bokehpivot/reeds2.py @@ -818,12 +818,12 @@ def pre_lcoe(dfs, **kw): #Apply inflation dfs['lcoe']['$/MWh'] = inflate_series(dfs['lcoe']['$/MWh']) #Merge with available capacity - df = pd.merge(left=dfs['lcoe'], right=dfs['avail'], how='left', on=['tech', 'rb', 'year', 'bin'], sort=False) + df = pd.merge(left=dfs['lcoe'], right=dfs['avail'], how='left', on=['tech', 'class', 'rb', 'year', 'bin'], sort=False) df['available MW'] = df['available MW'].fillna(0) df['available'] = 'no' df.loc[df['available MW'] > 0.001, 'available'] = 'yes' #Merge with chosen capacity - df = pd.merge(left=df, right=dfs['inv'], how='left', on=['tech', 'vintage', 'rb', 'year', 'bin'], sort=False) + df = pd.merge(left=df, right=dfs['inv'], how='left', on=['tech', 'class', 'vintage', 'rb', 'year', 'bin'], sort=False) df['chosen MW'] = df['chosen MW'].fillna(0) df['chosen'] = 'no' df.loc[df['chosen MW'] != 0, 'chosen'] = 'yes' From caa4fff15915ec8ce10ad28684b148ee845b8323 Mon Sep 17 00:00:00 2001 From: wcole Date: Wed, 9 Sep 2026 11:28:38 -0600 Subject: [PATCH 25/30] Select numeric value columns in uncertainty_plots.py --- postprocessing/uncertainty_plots.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/postprocessing/uncertainty_plots.py b/postprocessing/uncertainty_plots.py index 8e8f9f4a9..2685dc2c6 100644 --- a/postprocessing/uncertainty_plots.py +++ b/postprocessing/uncertainty_plots.py @@ -973,7 +973,8 @@ def apply_transformations( items_map = Conventions.items_color_map[items_map_name][0] if items_column == 'tech': df['tech'] = df['tech'].str.lower().map(lambda x: items_map.get(x, x)) - df = df.groupby(['tech', 'r', 'year']).sum().reset_index() + valcols = df.select_dtypes('number').columns.difference(['year']) + df = df.groupby(['tech', 'r', 'year'])[valcols].sum().reset_index() # Fill missing techs with 0 idx = pd.MultiIndex.from_product( From 9860c76c94d352302474fa4a4a0b33a6068a0c8f Mon Sep 17 00:00:00 2001 From: wcole Date: Wed, 9 Sep 2026 11:30:38 -0600 Subject: [PATCH 26/30] Reorder indices of cap_cspns to be consistent. Also adjust reporting to use undegradated capacity. --- reeds/core/setup/b_inputs.gms | 2 +- reeds/core/terminus/report.gms | 45 ++++++++++++++------------- reeds/input_processing/writecapdat.py | 6 ++-- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/reeds/core/setup/b_inputs.gms b/reeds/core/setup/b_inputs.gms index 8fdf70faa..d1ba57cf6 100644 --- a/reeds/core/setup/b_inputs.gms +++ b/reeds/core/setup/b_inputs.gms @@ -1645,7 +1645,7 @@ m_capacity_exog(i,"init-1",r,t)$geo(i) = geo_cap_exog(i,r) ; * exogenous and the prescribed capacity, and convert it back to csp-ns when reporting. * Written by writecapdat.py $onempty -parameter cap_cspns(r,c,allt) "--MW-- csp-ns capacity modeled as upv, by resource class" +parameter cap_cspns(c,r,allt) "--MW-- csp-ns capacity modeled as upv, by resource class" / $offlisting $ondelim diff --git a/reeds/core/terminus/report.gms b/reeds/core/terminus/report.gms index d23f93feb..c661e5f60 100644 --- a/reeds/core/terminus/report.gms +++ b/reeds/core/terminus/report.gms @@ -650,27 +650,41 @@ gen_h(i,r,h,t)$[tmodel_new(t)$valgen_irt(i,r,t)] = * less load from hydrogen production - sum{(v,p)$[consume(i)$valcap(i,v,r,t)$i_p(i,p)], PRODUCE.l(p,i,v,r,h,t) / prod_conversion_rate(i,v,r,t)}$Sw_Prod ; +* Capacity is needed here to reassign csp-ns, so calculate it before generation. +cap_deg_ivrt(i,c,v,r,t)$[i_c(i,c)$valcap(i,v,r,t)] = CAP.l(i,v,r,t) / ilr(i) ; +cap_deg_ivrt(i,c,v,r,t)$valcap_class(i,c,v,r,t) = CAP_CLASS.l(i,c,v,r,t) / ilr(i) ; + +cap_ivrt(i,c,v,r,t)$[i_c(i,c)$(not (upv(i) or wind(i)))$valcap(i,v,r,t)] = cap_deg_ivrt(i,c,v,r,t) ; +*upv, and wind have degradation, so use INV rather than CAP to get the reported capacity +cap_ivrt(i,c,v,r,t)$[i_c(i,c)$(upv(i) or wind(i))$valcap(i,v,r,t)] = ( + sum{rscbin, capacity_exog_rsc(i,c,v,r,rscbin,t) }$tmodel_new(t) + + sum{tt$[inv_cond(i,v,r,t,tt)$[tmodel(tt) or tfix(tt)]], + sum{rscbin$m_rscfeas(r,i,c,rscbin), INV_RSC.l(i,c,v,r,rscbin,tt) } + + INV_REFURB.l(i,c,v,r,tt)$[refurbtech(i)$Sw_Refurb]}) / ilr(i) ; + * A small amount of upv capacity is actually csp-ns, so convert it back now. * writecapdat.py bins csp-ns into the upv resource classes it is modeled as, so take it * back out of those same classes. cap_cspns_short catches any csp-ns left with no upv to * come out of, which means the capacity written by writecapdat.py and the capacity carried * by the model have diverged. +* cap_upv_class uses the same undegraded basis as cap_ivrt so that the capacity moved out +* of upv and the generation that goes with it are taken from the same denominator. cap_upv_class(c,r,t)$tmodel_new(t) = - sum{(i,v)$[upv(i)$valcap_class(i,c,v,r,t)], CAP_CLASS.l(i,c,v,r,t) / ilr(i) } ; + sum{(i,v)$[upv(i)$i_c(i,c)$valcap(i,v,r,t)], cap_ivrt(i,c,v,r,t) } ; -cap_cspns_short(c,r,t)$[cap_cspns(r,c,t)$tmodel_new(t)] = - max(0, cap_cspns(r,c,t) - cap_upv_class(c,r,t)) ; +cap_cspns_short(c,r,t)$[cap_cspns(c,r,t)$tmodel_new(t)] = + max(0, cap_cspns(c,r,t) - cap_upv_class(c,r,t)) ; * Move the generation that goes with the reassigned capacity, class by class -gen_h("csp-ns",r,h,t)$[sum{c, cap_cspns(r,c,t) }$tmodel_new(t)] = +gen_h("csp-ns",r,h,t)$[sum{c, cap_cspns(c,r,t) }$tmodel_new(t)] = sum{(i,c)$[upv(i)$i_c(i,c)$cap_upv_class(c,r,t)], - gen_h(i,r,h,t) * min(cap_cspns(r,c,t), cap_upv_class(c,r,t)) / cap_upv_class(c,r,t) } ; + gen_h(i,r,h,t) * min(cap_cspns(c,r,t), cap_upv_class(c,r,t)) / cap_upv_class(c,r,t) } ; -gen_h(i,r,h,t)$[upv(i)$tmodel_new(t)$sum{c$i_c(i,c), cap_cspns(r,c,t) } +gen_h(i,r,h,t)$[upv(i)$tmodel_new(t)$sum{c$i_c(i,c), cap_cspns(c,r,t) } $sum{c$i_c(i,c), cap_upv_class(c,r,t) }] = gen_h(i,r,h,t) * (1 - sum{c$i_c(i,c), - min(cap_cspns(r,c,t), cap_upv_class(c,r,t)) / cap_upv_class(c,r,t) }) ; + min(cap_cspns(c,r,t), cap_upv_class(c,r,t)) / cap_upv_class(c,r,t) }) ; gen_h_nat(i,h,t)$tmodel_new(t) = sum{r, gen_h(i,r,h,t) } ; * Do it again for stress periods @@ -823,25 +837,14 @@ losses_tran_h(rr,r,h,trtype,t)$[routes(r,rr,trtype,t)$tmodel_new(t)] * CAPACITY *========================= -cap_deg_ivrt(i,c,v,r,t)$[i_c(i,c)$valcap(i,v,r,t)] = CAP.l(i,v,r,t) / ilr(i) ; -cap_deg_ivrt(i,c,v,r,t)$valcap_class(i,c,v,r,t) = CAP_CLASS.l(i,c,v,r,t) / ilr(i) ; - -cap_ivrt(i,c,v,r,t)$[i_c(i,c)$(not (upv(i) or wind(i)))$valcap(i,v,r,t)] = cap_deg_ivrt(i,c,v,r,t) ; -*upv, and wind have degradation, so use INV rather than CAP to get the reported capacity -cap_ivrt(i,c,v,r,t)$[i_c(i,c)$(upv(i) or wind(i))$valcap(i,v,r,t)] = ( - sum{rscbin, capacity_exog_rsc(i,c,v,r,rscbin,t) }$tmodel_new(t) - + sum{tt$[inv_cond(i,v,r,t,tt)$[tmodel(tt) or tfix(tt)]], - sum{rscbin$m_rscfeas(r,i,c,rscbin), INV_RSC.l(i,c,v,r,rscbin,tt) } - + INV_REFURB.l(i,c,v,r,tt)$[refurbtech(i)$Sw_Refurb]}) / ilr(i) ; - cap_out(i,c,r,t)$[i_c(i,c)$valcap_irt(i,r,t)$tmodel_new(t)] = sum{v$valcap(i,v,r,t), cap_ivrt(i,c,v,r,t) } ; * A small amount of upv capacity is actually csp-ns, so convert it back now. * UPV capacity is already in MWac at this point (matching csp-ns), * so don't need to account for ILR -cap_out("csp-ns",c,r,t)$[i_c("csp-ns",c)$tmodel_new(t)] = sum{cc, cap_cspns(r,cc,t) } ; +cap_out("csp-ns",c,r,t)$[i_c("csp-ns",c)$tmodel_new(t)] = sum{cc, cap_cspns(cc,r,t) } ; * Take it back out of the same upv classes it was binned into -cap_out(i,c,r,t)$[upv(i)$i_c(i,c)$cap_cspns(r,c,t)$tmodel_new(t)] = - max(0, cap_out(i,c,r,t) - cap_cspns(r,c,t)) ; +cap_out(i,c,r,t)$[upv(i)$i_c(i,c)$cap_cspns(c,r,t)$tmodel_new(t)] = + max(0, cap_out(i,c,r,t) - cap_cspns(c,r,t)) ; cap_nat(i,t)$tmodel_new(t) = sum{(c,r)$i_c(i,c), cap_out(i,c,r,t) } ; * Exogenous capacity (used by reeds_to_rev) diff --git a/reeds/input_processing/writecapdat.py b/reeds/input_processing/writecapdat.py index b6cc4588a..522c5bd91 100644 --- a/reeds/input_processing/writecapdat.py +++ b/reeds/input_processing/writecapdat.py @@ -451,19 +451,19 @@ def main(reeds_path, inputs_case): cap_cspns = pd.concat( [ pd.DataFrame({ - 'r': row.r, 'c': row.c, + 'r': row.r, 't': list(range(row.StartYear, row.RetireYear + 2)), 'MWac': row.summer_power_capacity_MW, }) for (_, row) in csp_units.iterrows() ], ignore_index=True, - ).groupby(['r','c','t']).MWac.sum() + ).groupby(['c','r','t']).MWac.sum() cap_cspns = ( cap_cspns.loc[cap_cspns.index.get_level_values('t') >= startyear].copy()) else: - cap_cspns = pd.DataFrame(columns=['r','c','t','MWac']).set_index(['r','c','t']) + cap_cspns = pd.DataFrame(columns=['c','r','t','MWac']).set_index(['c','r','t']) # Rename csp-ns to upv gdb_use.loc[gdb_use['tech']=='csp-ns','coolingwatertech'] = ( gdb_use.loc[gdb_use['tech']=='csp-ns','coolingwatertech'] From d1d3c790451d11737dc7efa28712425ee69ce5ea Mon Sep 17 00:00:00 2001 From: wcole Date: Wed, 9 Sep 2026 11:30:59 -0600 Subject: [PATCH 27/30] Add groupby rather than drop class column --- reeds/resource_adequacy/prep_data.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/reeds/resource_adequacy/prep_data.py b/reeds/resource_adequacy/prep_data.py index f4b915873..74161428e 100644 --- a/reeds/resource_adequacy/prep_data.py +++ b/reeds/resource_adequacy/prep_data.py @@ -440,7 +440,8 @@ def intify(v): ]) ### Nameplate capacity - max_cap = cap_nonloadtechs.drop(columns='c').set_index(['i','v','r']).Value.rename('MW') + max_cap = ( + cap_nonloadtechs.groupby(['i','v','r']).Value.sum().rename('MW')) ## Drop VRE since it is handled through pras_vre_gen max_cap = max_cap.loc[ ~max_cap.index.get_level_values('i').str.startswith( From 7ff162849aef4b09c7101e07ef47e40edd2b4046 Mon Sep 17 00:00:00 2001 From: wcole Date: Fri, 11 Sep 2026 09:12:35 -0600 Subject: [PATCH 28/30] Change description to unassigned. --- postprocessing/compare_cases.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/postprocessing/compare_cases.py b/postprocessing/compare_cases.py index c71ade555..4391a3d05 100644 --- a/postprocessing/compare_cases.py +++ b/postprocessing/compare_cases.py @@ -661,7 +661,7 @@ def plot_bars_abs_stacked( 'RPS': {'title': 'Non-RecMap\nRECS\n[GWh]', 'scale':1e-3}, 'OpRes': {'title': 'Non-valgen\nopres\n[MWh]', 'scale':1}, 'm_rsc_dat': {'title': 'Supply curve\ntweaks [GW]', 'scale':1e-3}, - 'cspns': {'title': 'Unreassigned\ncsp-ns [MW]', 'scale':1}, + 'cspns': {'title': 'Unassigned\ncsp-ns [MW]', 'scale':1}, 'dropped': {'title': 'Dropped load\n[GWh]', 'scale':1e-3}, 'excess': {'title': 'Excess load\n[GWh]', 'scale':1e-3}, } From e92fd37d944822ba67aa02b0ef62901985dd4358 Mon Sep 17 00:00:00 2001 From: wcole Date: Fri, 11 Sep 2026 09:13:01 -0600 Subject: [PATCH 29/30] Fixes for reeds_to_rev.py --- hourlize/reeds_to_rev.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/hourlize/reeds_to_rev.py b/hourlize/reeds_to_rev.py index 00a68e4cf..c879c1286 100644 --- a/hourlize/reeds_to_rev.py +++ b/hourlize/reeds_to_rev.py @@ -335,9 +335,7 @@ class that can be used to check capacity values at the end of disaggregation. df_cap_chk = pd.read_csv( cap_chk, low_memory=False, names=["tech", "class", "region", "year", "MW"], header=0 ) - df_cap_chk[["tech_cat", "class"]] = df_cap_chk["tech"].str.rsplit( - "_", n=1, expand=True - ) + df_cap_chk["tech_cat"] = df_cap_chk["tech"].str.rsplit("_", n=1).str[0] df_cap_chk = df_cap_chk[df_cap_chk["tech_cat"] == tech].copy() df_cap_chk = df_cap_chk[["year", "region", "class", "MW"]].dropna(subset=["class"]) df_cap_chk["class"] = df_cap_chk["class"].astype("int") @@ -1531,7 +1529,7 @@ def simultaneous_fill( break # check to make sure inv_left isn't negative if ret_left < 0: - print(f"ERROR at rcby={rcby}: ret_left is negative: {ret_left}") + print(f"ERROR at rcy={rcy}: ret_left is negative: {ret_left}") if np.floor(ret_left * 100) / 100 != 0: print( @@ -1598,7 +1596,7 @@ def simultaneous_fill( # check to make sure inv_left isn't negative if refurb_left < 0: print( - f"ERROR at rcby={rcby}: refurb_left is negative: {refurb_left}" + f"ERROR at rcy={rcy}: refurb_left is negative: {refurb_left}" ) if round(refurb_left, 2) != 0: From 8a60ebff688738c20d41ded658448542f58b66a8 Mon Sep 17 00:00:00 2001 From: wcole Date: Fri, 11 Sep 2026 09:13:22 -0600 Subject: [PATCH 30/30] Add additional groupby for uncertainty_plots.py --- postprocessing/uncertainty_plots.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/postprocessing/uncertainty_plots.py b/postprocessing/uncertainty_plots.py index 2685dc2c6..3f61a7dde 100644 --- a/postprocessing/uncertainty_plots.py +++ b/postprocessing/uncertainty_plots.py @@ -588,6 +588,10 @@ def _fetch_cap_out(self, case: str) -> pd.DataFrame: columns={'Value': 'Capacity (GW)', 't': 'year', 'i': 'tech'}, inplace=True, ) + + # Sum over resource class + df = df.groupby(['tech', 'r', 'year'], as_index=False)['Capacity (GW)'].sum() + return df