update capacity input database to AEO-NEMS 2026 - #13
Conversation
… available for dispatch
Co-authored-by: Wesley Cole <49044852+wesleyjcole@users.noreply.github.com>
Co-authored-by: Wesley Cole <49044852+wesleyjcole@users.noreply.github.com>
bsergi
left a comment
There was a problem hiding this comment.
A few thoughts, mostly on processing structure that aren't necessarily directly related to the update itself.
For the plots, could you structure so that the old and new capacity plots are side-by-side and use the same y-axis? I think that would facilitate comparsion. You already include the different plot which covers similar information here, but just a thought for going forward.
| * `d_additional_inputs.py`, which includes `d1_set_retire_years.py`, `d2_fix_upgrades.py`, and `d3_merge_psh_dbs.py`: This script handles updated retirement years that are outdated or missing in AEO-NEMS and EIA860M, fix upgrades, and handles other additional adjustments. Any manually adjusted retirement years should be included in `d1_set_retire_years.py`. | ||
| * `e_comparison_plotting.py`: This script generates comparison figures between previous version of NEMS and the newly updated version for validation. | ||
|
|
||
| # Input files and params to run run.sh |
There was a problem hiding this comment.
Would you add some details to this section on where you get the input files? If we download them from somewhere it would be good to include links.
There was a problem hiding this comment.
Would you add a bit more to the overview about the general process? My rough understanding is that we start from AEO file, clean it, and then supplement with the 860M.
There was a problem hiding this comment.
I added a bit more details in overview and the steps
| coal_plant_retirement='EIA860_2025ER_CoalRetirements.csv' | ||
|
|
||
| # Average lithium battery duration: | ||
| # Assumed average duration for planned storage units: |
There was a problem hiding this comment.
what is this based on on? would be good to document where this assumption comes from.
There was a problem hiding this comment.
This is now removed and storage_duration is calculated based on battery units that came online within 5 years of current year.
| return nems_eia860_final | ||
|
|
||
| def addHeatrates(nems_eia860): | ||
| # Add in heat rates for planned units (AEO inputs): |
There was a problem hiding this comment.
+1 to this comment. It also might be better to store these as an input file and read them in.
| # Main directory | ||
| dir = os.getcwd() | ||
|
|
||
| # Key parameters: |
There was a problem hiding this comment.
Right now if the script is launched using run.sh it will ignore the command line arguments and use the testing ones below. Before merging you should uncomment these and recomment the ones below.
Alternatively, you could set up the main function to take arguments and then either call directly using testing argument or pass command-line arguments to it from a if __name__ == '__main__': (this is the approach we've been using for a number of other python modules).
| # Specify scrubber or unscrubber coal units | ||
| aeo_data.loc[aeo_data['reeds_tech'].notna(), 'tech'] = aeo_data['reeds_tech'] | ||
| aeo_data.loc[(aeo_data['tech'].str.contains("coal", na=False)) & | ||
| (aeo_data['T_SYR']<=1969),'tech'] = 'coalolduns' |
There was a problem hiding this comment.
It might be overkill but could you put this in variable with a descriptive name? I think that's usually more informative than a number with no context.
There was a problem hiding this comment.
I added two variables coaluns and coalcrs with more descriptions for both now.
| # Then, collapse on T_PID and T_UID | ||
| aeo_data_mult_g = aeo_data_mult_g.groupby(['T_PID','T_UID','TVIN'], | ||
| as_index=False).agg( | ||
| {'ctt':'first','wst':'first','THRATE':'mean','TC_SUM':'sum', |
There was a problem hiding this comment.
might suggest storing this as a global dictionary or input file and then reading in here. if you do keep in the code it might be cleaner to read with one assignment per line even though it's more lines. alternatively, there might be a way to use 'first' as the default and then only specify the ones that are different.
{
'ctt':'first',
'wst':'first',
'THRATE':'mean',
'TC_SUM':'sum',
'TC_NP':'sum',
'TC_WIN':'sum',
'battery_duration':'mean',
'T_SYR':'min',
'T_RYR':'first',
'tech':'first',
'EFDcd':'first',
'ECPcd':'first',
'T_PNM':'first',
'T_PCA':'first',
'TRFURB':'first',
'T_VOM':'mean',
'T_FOM':'mean',
'T_SMO':'first',
'T_RMO':'first',
'T_CCSROV':'first',
'T_CCSF':'first',
'T_CCSV':'first',
'T_CCSHR':'first',
'T_CAPAD':'first',
'T_CCSCAPA':'first',
'T_CCSLOC':'first',
'sector':'first',
'TCOUNT':'sum',
'T_LONG':'first',
'T_LAT':'first',
'status':'first',
'nems':'first'
}
There was a problem hiding this comment.
I cleaned this up a little bit. first is now used as default and columns that are 'sum' and 'mean' are specified.
There was a problem hiding this comment.
ReEDS has switched to using EPSG:5070 instead of ESRI:102008 for the crs, so you might consider updating that here. It shouldn't change the actual lat/lon values, but it might affect how things get placed into counties.
There was a problem hiding this comment.
Thanks for catching. I updated everything to 5070 now. Previously I had some weird issue where I got point(inf,inf) with 5070 so had to use the ESRI one (but I stilled used the 5070 for nearest distance calculation). But that did not seem to be an issue with projections and can be fixed by preloading projection data by conda install -c conda-forge proj-data, which is a note I added to README.
Summary
This PR updates ReEDS capacity input database with AEO-NEMS 2026 and EIA860M version June 2026, and also overhauls the NEMS processing script to make it more clear and easier to use as well as remove redundant steps.
Technical details
Implementation notes
Changes made to to this PR include:
a_inheritance.py: This script is no longer necessary. We now directly merge AEO-NEMS to EIA860M using the final output file structure established in Clean up input plant database ReEDS#164.b_aeo_cleaning.py-->a_data_cleaning.py: Improve the process of cleaning AEO and EIA860M data to resolve issues saw in prior version of capacity database used in ReEDS (such as applying repower year as new start year).(OA) Out of service but expected to return to service in next calendar year,(OS) Out of service and NOT expected to return to service in next calendar yearand(SB) Standby/Backup: available for service but not normally usedare considered operating and available for dispatch.c_geospatial_mapping.py-->b_geospatial_mapping.py.d_hydro_classification.py-->c_hydro_classification.py: Remove any mention of state and resource regions (we only use FIPS from now on to capacity input database).e_additional_inputs.py-->d_additional_inputs.py:inputs/plant_characteristics/maxage.csvand only include one value for each plant type regardless of its size.lfill-gas,pumped-hydro,csp-ns,pvb_pvandpvb_batteryare missing frommaxage.csvin the ReEDs repo and are assigned values of 100, 100, 30, 30, 30 years respectively.Additional changes
intermediate_outputsfolder within theoutputsfolder to store outputs from each step of the process.Issues resolved
Relevant sources or documentation
Yes
No. Updated README for this input processing only.