Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/sources.csv
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ RelativeFilePath,RelativeFolderPath,FileName_new,FileExtension,Description_new,I
/inputs/state_policies/ces_fraction.csv,/inputs/state_policies,ces_fraction,.csv,Annual compliance for states with a CES policy,,,,,
/inputs/state_policies/forced_retirements.csv,/inputs/state_policies,forced_retirements,.csv,List of regions with mandatory retirement policies for certain technologies,,,,,
/inputs/state_policies/hydrofrac_policy.csv,/inputs/state_policies,hydrofrac_policy,.csv,,,,,,
/inputs/state_policies/ng_crf_penalty_st.csv,/inputs/state_policies,ng_crf_penalty_st,.csv,Cost adjustment for NG techs in states where all NG techs must be retired by a certain year,"allt,st",N/A,https://github.nrel.gov/ReEDS/ReEDS-2.0/pull/1220,Inputs,rate (unitless)
/inputs/state_policies/ng_st_forced.csv,/inputs/state_policies,ng_st_forced,.csv,List of states where natural gas plants must be retired by a certain year,"t,st",N/A,https://github.nrel.gov/ReEDS/ReEDS-2.0/pull/1220,Inputs,year
/inputs/state_policies/nuclear_subsidies.csv,/inputs/state_policies,nuclear_subsidies,.csv,,,,,,
/inputs/state_policies/offshore_req_default.csv,/inputs/state_policies,offshore_req_default,.csv,"default state mandates of offshore wind capacity, updated in November 2025","st,allt",,,Inputs,MW
/inputs/state_policies/oosfrac.csv,/inputs/state_policies,oosfrac,.csv,Defines the fraction of renewable and clean energy credits can be purchased from out of state (oos). Applied for RPS and CES,,,,,
Expand Down
8 changes: 4 additions & 4 deletions docs/sources_documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -2849,13 +2849,13 @@
- [hydrofrac_policy.csv](/inputs/state_policies/hydrofrac_policy.csv)
---

- [ng_crf_penalty_st.csv](/inputs/state_policies/ng_crf_penalty_st.csv)
- [ng_st_forced.csv](/inputs/state_policies/ng_st_forced.csv)
- **File Type:** Inputs
- **Description:** Cost adjustment for NG techs in states where all NG techs must be retired by a certain year
- **Indices:** allt,st
- **Description:** The certain year for specific states that NG techs must be retired by a certain year
- **Indices:** t,st
- **Dollar year:** N/A
- **Citation:** [https://github.nrel.gov/ReEDS/ReEDS-2.0/pull/1220](https://github.nrel.gov/ReEDS/ReEDS-2.0/pull/1220)
- **Units:** rate (unitless)
- **Units:** year

---

Expand Down
5 changes: 5 additions & 0 deletions inputs/state_policies/ng_st_forced.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
*t,st,tech
2045,DE,ng
2045,IL,ng
2045,VA,ng
2040,NY,ng
71 changes: 71 additions & 0 deletions reeds/financials.py
Original file line number Diff line number Diff line change
Expand Up @@ -788,3 +788,74 @@ def param_exporter(df, parameter, file_name, output_dir):
### Add '*' to first column name so GAMS reads it as a comment
df = df.rename(columns={c: (f'*{c}' if not i else c) for i, c in enumerate(df.columns)})
df.round(5).to_csv(os.path.join(output_dir, f'{file_name}.csv'), index=False, header=True)


def calc_ng_crf_penalty_st(inputs_case, years, financials_sys, sw):
"""
Compute the CRF-based cost penalty for natural gas plants in states with
forced retirement policies.

For each (state, forced-retirement year) pair in ng_st_forced.csv, this
function calculates how much of a plant's remaining economic life is cut
short by the policy. The penalty is the ratio of the CRF over the
truncated remaining life to the CRF over the full evaluation period:
- value = 100 if the plant comes online after the forced year
- value > 1 if the plant is forced to retire before its full life

States not present in hierarchy.csv (i.e. outside the modeled region) are
dropped before any computation. If no forced-retirement states remain,
returns an empty DataFrame with columns ['t', 'st', 'value'].
"""
# Load state-level forced retirement years for NG plants
ng_st_forced = pd.read_csv(os.path.join(inputs_case, 'ng_st_forced.csv'))

# Keep only states that exist in this run's modeled region
hierarchy = pd.read_csv(os.path.join(inputs_case, 'hierarchy.csv'))
valid_states = hierarchy['st'].unique()
ng_st_forced = ng_st_forced[ng_st_forced['st'].isin(valid_states)]

# No forced retirements in this region — return empty result
if ng_st_forced.empty:
ng_crf_penalty_st=pd.DataFrame(columns=['*t', 'st', 'value'])
ng_crf_penalty_st.to_csv(os.path.join(inputs_case, 'ng_crf_penalty_st.csv'),
index=False)
return ng_crf_penalty_st

# Years when retirements are forced, and the model's active year range
forced_years = ng_st_forced['*t'].astype(int).unique()
years_arr = np.asarray([y for y in years if int(sw['startyear']) <= y <= int(sw['endyear'])])

# Real discount rate and full-life CRF for each year (the baseline)
d_real_arr = financials_sys.set_index('t')['d_real'].reindex(years_arr).values
base_crf_arr = np.array([calc_crf(d, sw['sys_eval_years']) for d in d_real_arr])

# How many years remain between each build year t and each forced retirement year
# shape: (len(years_arr), len(forced_years))
diff = forced_years[None, :] - years_arr[:, None]
# Effective remaining life: at least 1 year, at most the full evaluation period
n_eff = np.clip(diff, 1, sw['sys_eval_years'])

# CRF over the truncated life, then normalize by the full-life baseline
crf_vals = np.vectorize(calc_crf)(d_real_arr[:, None], n_eff)
value_matrix = np.where(diff <= 0, 100.0, crf_vals / base_crf_arr[:, None])

# Wrap results in a DataFrame indexed by build year, columns = forced years
crf_table = pd.DataFrame(value_matrix, index=years_arr,
columns=forced_years, dtype=float)
crf_table.index.name = 't'

# Reshape to long format: one row per (build year, forced year)
crf_long = (
crf_table.stack().rename('value')
.rename_axis(['t', '*t']).reset_index()
)

# Attach state labels by merging on the forced retirement year
ng_crf_penalty_st = (
ng_st_forced[['*t', 'st']].merge(crf_long, on='*t', how='left')
[['t', 'st', 'value']]
.rename(columns={'t': '*t'}))

ng_crf_penalty_st.to_csv(os.path.join(inputs_case, 'ng_crf_penalty_st.csv'),
index=False)
return ng_crf_penalty_st
4 changes: 4 additions & 0 deletions reeds/input_processing/calc_financial_inputs.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ def calc_financial_inputs(inputs_case):
sw['financials_sys_suffix'], inflation_df, modeled_years,
years, year_map, sw['sys_eval_years'], scen_settings, scalars['co2_capture_incentive_length'],scalars['h2_ptc_length'])
financials_sys.to_csv(os.path.join(inputs_case,'financials_sys.csv'),index=False)

# Calculate the natural gas CRF penalty for states
reeds.financials.calc_ng_crf_penalty_st(inputs_case, years, financials_sys, sw)

df_ivt = df_ivt.merge(
financials_sys[['t', 'pvf_capital', 'crf', 'crf_co2_incentive','crf_h2_incentive','d_real', 'd_nom', 'interest_rate_nom',
'tax_rate', 'debt_fraction', 'rroe_nom']],
Expand Down
2 changes: 1 addition & 1 deletion reeds/input_processing/runfiles.csv
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ natgas_price_cendiv.csv,inputs/fuelprices/ng_{ngscen}.csv,1,ignore,ignore,wide_c
national_rps_frac_allScen.csv,inputs/national_generation/national_rps_frac_allScen.csv,int(sw.GSw_StateRPS) != 0,ignore,ignore,,,,1,0,,,,,,
net_gen_existing_hydro.csv,inputs/hydro/net_gen_existing_hydro.csv,1,ignore,ignore,,"t,month",,0,0,,,,,,
peak_net_imports.csv,inputs/reserves/peak_net_imports.csv,1,ignore,ignore,nercr,t,,0,0,,,,,,
ng_crf_penalty_st.csv,inputs/state_policies/ng_crf_penalty_st.csv,1,ignore,ignore,st,*t,,0,0,,,,,,
ng_st_forced.csv,inputs/state_policies/ng_st_forced.csv,1,ignore,ignore,st,*t,,0,0,,,,,,
ng_demand_elec.csv,inputs/fuelprices/ng_demand_{ngscen}.csv,1,ignore,ignore,wide_cendiv,year,,1,0,,,,,,
ng_demand_tot.csv,inputs/fuelprices/ng_tot_demand_{ngscen}.csv,1,ignore,ignore,wide_cendiv,year,,1,0,,,,,,
noretire.csv,inputs/sets/noretire.csv,1,ignore,ignore,,,,,0,,,set,noretire,technologies that will never be retired,
Expand Down
Loading