From 68b14b4131ac460789610f2223690ef3363bb94b Mon Sep 17 00:00:00 2001 From: Yunzhi-Chen Date: Wed, 19 Aug 2026 09:56:27 -0600 Subject: [PATCH 1/5] Fix bug: match counties on FIPS code with county-name fallback --- .../process_interconnection_queues.py | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/interconnection_queues/process_interconnection_queues.py b/interconnection_queues/process_interconnection_queues.py index 80f22fe..4011332 100644 --- a/interconnection_queues/process_interconnection_queues.py +++ b/interconnection_queues/process_interconnection_queues.py @@ -73,8 +73,9 @@ def insert_rows(group): queue_data_temp = queue_data[['q_status', 'county_'+str(item), 'state', 'IA_status_clean', 'type'+str(item),'mw'+str(item)]] queue_data_temp = queue_data_temp.rename(columns={'county_'+str(item): 'county_name', 'type'+str(item): 'tech','mw'+str(item):'cap'+str(item)}) else: - queue_data_temp = queue_data[['q_status', 'county', 'state', 'IA_status_clean', 'type'+str(item),'mw'+str(item)]] - queue_data_temp = queue_data_temp.rename(columns={'county': 'county_name', 'type'+str(item): 'tech','mw'+str(item):'cap'+str(item)}) + queue_data_temp = queue_data[['q_status', 'county', 'state', 'fips_codes', 'IA_status_clean', 'type'+str(item),'mw'+str(item)]] + queue_data_temp = queue_data_temp.rename(columns={'county': 'county_name', 'fips_codes': 'FIPS', + 'type'+str(item): 'tech','mw'+str(item):'cap'+str(item)}) # Only consider queues that have active status queue_data_active_temp = queue_data_temp[queue_data_temp['q_status']=='active'] @@ -91,15 +92,28 @@ def insert_rows(group): queue_data_active_temp = queue_data_active_temp.rename(columns={'cap'+str(item):'cap'}) active_queue = pd.concat([active_queue, queue_data_active_temp], axis=0).reset_index(drop=True) -# Sum up the queue capacities by county, state, tech, and online year -active_queue['county_name'] = active_queue['county_name'].str.lower() -active_queue_agg = active_queue.groupby(['county_name', 'state','tech', 'online_year'])['cap'].sum().reset_index() - -# Merge the queue data with county2zone to assign FIPS to each county and state pair and clean up -active_queue_county = county2zone.merge(active_queue_agg, on=['county_name','state'], how='outer') -active_queue_county = active_queue_county[active_queue_county['county_name']!= '0'] -active_queue_county = active_queue_county.dropna(subset=['tech']) -active_queue_county = active_queue_county.dropna(subset=['FIPS']) +# Sum up the queue capacities by county, tech, and online year, then keep only ReEDS counties. +# Match on the FIPS code reported by LBNL rather than the county name: LBNL county names do not +# always use the ReEDS spelling (e.g. Louisiana is written "Acadia Parish" vs ReEDS "acadia"), +# which silently dropped those queues. +if 'FIPS' in active_queue.columns: + fips_reported = 'p' + pd.to_numeric(active_queue['FIPS'], errors='coerce').map( + lambda x: str(int(x)).zfill(5) if pd.notna(x) else '') + # LBNL sometimes reports a stale code (e.g. Oglala Lakota SD) or concatenates several codes for + # projects spanning multiple counties, so fall back to the county name when the code is unusable + name2fips = county2zone.set_index(county2zone['county_name']+'|'+county2zone['state'])['FIPS'] + fips_byname = (active_queue['county_name'].str.lower()+'|'+active_queue['state']).map(name2fips) + active_queue['FIPS'] = fips_reported.where(fips_reported.isin(county2zone['FIPS']), fips_byname) + active_queue_agg = active_queue.groupby(['FIPS','tech','online_year'])['cap'].sum().reset_index() + active_queue_county = county2zone.merge(active_queue_agg, on='FIPS', how='inner') +else: + # Vintages before 2024 report no FIPS code, so fall back to matching on county name and state + active_queue['county_name'] = active_queue['county_name'].str.lower() + active_queue_agg = active_queue.groupby(['county_name', 'state','tech', 'online_year'])['cap'].sum().reset_index() + active_queue_county = county2zone.merge(active_queue_agg, on=['county_name','state'], how='outer') + active_queue_county = active_queue_county[active_queue_county['county_name']!= '0'] + active_queue_county = active_queue_county.dropna(subset=['tech']) + active_queue_county = active_queue_county.dropna(subset=['FIPS']) # Assign 0 queue cap value to county-year pair with no value unique_year_FIPS = pd.DataFrame(product(active_queue_county['FIPS'].unique(),[t_1,t_2]),columns=['FIPS','online_year']) From 5f3a074a816745f2f01d7fda0116ff881ed3ca8e Mon Sep 17 00:00:00 2001 From: Yunzhi-Chen Date: Wed, 19 Aug 2026 10:13:43 -0600 Subject: [PATCH 2/5] Fix bug: t_2 capacity double-counted the IA Executed queue --- interconnection_queues/process_interconnection_queues.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interconnection_queues/process_interconnection_queues.py b/interconnection_queues/process_interconnection_queues.py index 4011332..e365ebb 100644 --- a/interconnection_queues/process_interconnection_queues.py +++ b/interconnection_queues/process_interconnection_queues.py @@ -124,9 +124,9 @@ def insert_rows(group): active_queue_county = active_queue_county[['FIPS','tech','online_year','cap']] active_queue_county['cap'] = active_queue_county['cap'].fillna(0) -# Sum queue capacity by year to get cumulative queue cap by year -active_queue_county[str(t_2)] = active_queue_county['cap'] -active_queue_county[str(t_2)] = active_queue_county.groupby(['FIPS','tech'])[str(t_2)].transform("max") +# Sum queue capacity by year to get cumulative queue cap by year. +active_queue_county[str(t_2)] = active_queue_county['cap'].where(active_queue_county['online_year']==t_2, 0) +active_queue_county[str(t_2)] = active_queue_county.groupby(['FIPS','tech'])[str(t_2)].transform("sum") active_queue_county =active_queue_county.rename(columns={'cap': str(t_1)}) active_queue_county = active_queue_county[active_queue_county['online_year']==t_1] From a0ce0b10f157f5a45d697eab1a84b20bc018714e Mon Sep 17 00:00:00 2001 From: Yunzhi-Chen Date: Wed, 26 Aug 2026 11:30:25 -0600 Subject: [PATCH 3/5] Locate county2zone under its current ReEDS path ReEDS moved the county_name/state lookup from inputs/county2zone.csv to inputs/zones/county_state.csv, so the script could no longer be run against a current ReEDS checkout. Fall back to the new location, and allow the ReEDS repo to be pointed at with REEDS_PATH instead of only the hardcoded default. Co-Authored-By: Claude Opus 5 --- interconnection_queues/process_interconnection_queues.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/interconnection_queues/process_interconnection_queues.py b/interconnection_queues/process_interconnection_queues.py index e365ebb..53574a6 100644 --- a/interconnection_queues/process_interconnection_queues.py +++ b/interconnection_queues/process_interconnection_queues.py @@ -3,7 +3,7 @@ import pandas as pd from itertools import product import altair as alt -reeds_path = os.path.expanduser('~/Documents/Github/ReEDS/ReEDS') +reeds_path = os.environ.get('REEDS_PATH', os.path.expanduser('~/Documents/Github/ReEDS/ReEDS')) sys.path.append(reeds_path) ''' @@ -47,7 +47,11 @@ queue_data.columns = queue_data.iloc[0] queue_data = queue_data[1:] -county2zone = pd.read_csv(os.path.join(reeds_path,'inputs','county2zone.csv')) +# Newer ReEDS versions moved the county_name/state lookup out of inputs/county2zone.csv +county2zone_path = os.path.join(reeds_path,'inputs','county2zone.csv') +if not os.path.exists(county2zone_path): + county2zone_path = os.path.join(reeds_path,'inputs','zones','county_state.csv') +county2zone = pd.read_csv(county2zone_path) county2zone['FIPS'] = 'p' + county2zone['FIPS'].astype(str).str.zfill(5) # Assuming zero queue for csp From dfd0292ff1e3c520cf63181526688a4510b12964 Mon Sep 17 00:00:00 2001 From: Yunzhi-Chen Date: Wed, 26 Aug 2026 11:39:50 -0600 Subject: [PATCH 4/5] Regenerate the 2024 outputs with both bug fixes applied Same input file (lbnl_ix_queue_data_file_thru2024.xlsx) as before; only the script changed. National 2030 cumulative queue moves 2,050 GW -> 1,976 GW (-3.6%) and the county count rises as parishes and other counties whose LBNL spelling did not match ReEDS are no longer silently dropped. Co-Authored-By: Claude Opus 5 --- .../figures/compare_queue_versions.html | 5 +- .../outputs/figures/queue_versions_2023.html | 5 +- .../outputs/figures/queue_versions_2024.html | 5 +- .../outputs/interconnection_queues.csv | 5712 ++++++++++------- .../outputs/interconnection_queues_2024.csv | 5712 ++++++++++------- 5 files changed, 6522 insertions(+), 4917 deletions(-) diff --git a/interconnection_queues/outputs/figures/compare_queue_versions.html b/interconnection_queues/outputs/figures/compare_queue_versions.html index d6731d5..52284c7 100644 --- a/interconnection_queues/outputs/figures/compare_queue_versions.html +++ b/interconnection_queues/outputs/figures/compare_queue_versions.html @@ -1,7 +1,6 @@ - - +
- +
- +