diff --git a/.gitignore b/.gitignore index cd8b1e0..26eec7d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,30 @@ gen_database_updated_test.csv *.lst gens_fixme.csv ATB/inputs/ +atb/output/ +atb/inputs/ +# Raw ATB figures remain local. The smoothing comparisons are intentionally +# versioned so reviewers can see the effect without rerunning the workflow. +atb/figures/* +!atb/figures/smoothing_comparison/ +atb/figures/smoothing_comparison/* +!atb/figures/smoothing_comparison/*.png +# Keep the local ATB comparison utility, but never version generated plots or +# legacy tabular reports. +atb/comparison/* +!atb/comparison/README.md +!atb/comparison/compare_atb_outputs.py +atb/_dev/ +# Raw ATB downloads; reproduced by scripts/scrape_atb_inputs.py from the URLs +# pinned in atb/config.yaml, so they are never committed (see the File Size +# Guidelines in the top-level README). +atb/scraped_input/* +!atb/scraped_input/README.md +# Unpublished ATB 2025 manual inputs; keep local, never commit +atb/manual_input/battery_costs_2025.csv +atb/manual_input/csp_cost_ratios_2025.csv +# Legacy CF source; final historical CF multipliers now live in historical/. +atb/manual_input/historic_capacity_factors.csv /NEMS_database_processing/Outputs/ /AEO_Updates/outputs/ /AEO_Updates/output/AEO_HIGHMACRO_2021_electricity_consumption.csv @@ -12,8 +36,6 @@ ATB/inputs/ /AEO_Updates/output/AEO_LOWMACRO_2021_electricity_consumption.csv /AEO_Updates/output/AEO_REF2021_2021_electricity_consumption.csv /AEO_Updates/output/AEO_REF2021_2021_ng_prices.csv -/ATB/figures/ - # pyc files *.pyc *__pycache__ @@ -44,7 +66,7 @@ Manifest.toml # IDE files *.vscode -# NREL HPC files +# NLR HPC files slurm*.out # local downloaded files diff --git a/README.md b/README.md index 0e017f2..8e1b054 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,16 @@ -# ReEDS-2.0 Input Processing +# ReEDS Input Processing -This repository is a collection of data and preprocessing scripts that generate input files for [ReEDS-2.0](https://github.com/NatLabRockies/ReEDS-2.0), the Regional Energy Deployment System. +This repository is a collection of data and preprocessing scripts that generate input files for [ReEDS](https://github.com/ReEDS-Model/ReEDS), the Regional Energy Deployment System. This repository is organized into subfolders, each corresponding to a category of inputs for ReEDS. Each subfolder is mostly self-contained and should include its own README. ## Getting Started **Prerequisites:** -- ReEDS: This repository has a dependency on ReEDS. Make sure you have the [ReEDS-2.0 repository](https://github.com/natlabrockies/ReEDS-2.0) installed. +- ReEDS: This repository has a dependency on ReEDS. Make sure you have the [ReEDS](https://github.com/ReEDS-Model/ReEDS) installed. - **note:** most scripts are developed assuming the use of the `reeds2` environment from the ReEDS repository - Python: Most scripts require Python with various packages (see individual subfolder READMEs) -1. **Clone this repository** and the ReEDS-2.0 repository +1. **Clone this repository** and the ReEDS repository 2. **Navigate to the subfolder** relevant to the ReEDS input(s) you're working with 3. **Follow the instructions** in that subfolder's README for setup and execution @@ -22,7 +22,7 @@ This repository is organized into subfolders, each corresponding to a category o - **Naming convention**: Use lowercase with underscores for file and folder names - **Documentation**: Every subfolder should include a comprehensive README (see requirements below) -- **Code style**: Follow the [ReEDS Developer Guide](https://natlabrockies.github.io/ReEDS-2.0/developer_best_practices.html#coding-standards-and-conventions) standards +- **Code style**: Follow the [ReEDS Developer Guide](https://reeds-model.github.io/ReEDS/developer_best_practices.html#coding-standards-and-conventions) standards ### Subfolder README Requirements @@ -48,5 +48,5 @@ Git performs best with small files, but data processing sometimes requires large For questions or issues: 1. Check the relevant subfolder README first -2. Review existing GitHub issues +2. Review existing [GitHub issues](https://github.com/ReEDS-Model/ReEDS_Input_Processing/issues) and discussions 3. Open a new issue with detailed information about your problem and environment \ No newline at end of file diff --git a/aeo_updates/AEO2025_bldgs_pv_gen_ref_high_low_economic_growth_2025-04-17.xlsx b/aeo_updates/AEO2025_bldgs_pv_gen_ref_high_low_economic_growth_2025-04-17.xlsx deleted file mode 100644 index aa2652c..0000000 Binary files a/aeo_updates/AEO2025_bldgs_pv_gen_ref_high_low_economic_growth_2025-04-17.xlsx and /dev/null differ diff --git a/aeo_updates/AEO2026_bldgs_pv_gen_cb_high_low_economic_growth_2026-04-27.xlsx b/aeo_updates/AEO2026_bldgs_pv_gen_cb_high_low_economic_growth_2026-04-27.xlsx new file mode 100644 index 0000000..07a6729 Binary files /dev/null and b/aeo_updates/AEO2026_bldgs_pv_gen_cb_high_low_economic_growth_2026-04-27.xlsx differ diff --git a/aeo_updates/AEO_Load_Projections.py b/aeo_updates/AEO_Load_Projections.py index f3d4277..83a93ea 100644 --- a/aeo_updates/AEO_Load_Projections.py +++ b/aeo_updates/AEO_Load_Projections.py @@ -1,11 +1,14 @@ -# This script creates the demand projection files for AEO scenarios -# It uses historical retail sales and behind-the-meter PV generation -# data from EIA to calibrate historical state-level demand, and then -# carries that forward using demand ratios calculated from the -# AEO scenarios. - -# The original capability was created by Anna Schleifer in 2023, and -# then was adapted by Wesley Cole in 2025. +# This script creates state-level demand multiplier files for AEO scenarios. +# +# For historical years (2010-lastyear), state-level demand is derived from +# EIA retail electricity sales and behind-the-meter PV generation (via EIA API). +# +# For projected years (lastyear+1 through 2050), demand growth is computed from: +# - AEO electricity consumption CSVs (outputs/ folder, by census division, in quads) +# - Regional rooftop PV generation from an EIA-provided Excel file (residential + +# commercial, by census division, in trillion Btu) +# These two are added to reconstruct gross electricity demand, then normalized to the +# first AEO projected year so the historical and projected series join cleanly. import os import pandas as pd @@ -15,8 +18,103 @@ ### Set up information # lastyear is the last year that historical data are available -lastyear = 2023 -AEO_year = 2025 +lastyear = 2024 +AEO_year = 2026 +# First projected year in AEO data (2025 for AEO2026) +aeo_first_year = AEO_year - 1 + +### Helper functions + +# Census division number to name mapping (from "census division mapping" tab +# in AEO2026_bldgs_pv_gen_cb_high_low_economic_growth_2026-04-27.xlsx) +_DIV_TO_CENDIV = { + 1: 'NewEngland', 2: 'MiddleAtlantic', 3: 'EastNorthCentral', + 4: 'WestNorthCentral', 5: 'SouthAtlantic', 6: 'EastSouthCentral', + 7: 'WestSouthCentral', 8: 'Mountain', 9: 'Pacific', +} + +# Mapping from AEO CSV column-name format to no-spaces cendiv format +_CENDIV_NAME_MAP = { + 'East North Central': 'EastNorthCentral', + 'East South Central': 'EastSouthCentral', + 'Middle Atlantic': 'MiddleAtlantic', + 'Mountain': 'Mountain', + 'New England': 'NewEngland', + 'Pacific': 'Pacific', + 'South Atlantic': 'SouthAtlantic', + 'West North Central': 'WestNorthCentral', + 'West South Central': 'WestSouthCentral', +} + + +def read_dgpv_from_excel(xl_file, sheet_name): + """Read census-division DGPV (all sectors, residential + commercial) from + an EIA AEO2026 Excel sheet. Returns long-format DataFrame with + columns [cendiv, year, dgpv] where dgpv is in quads.""" + df = pd.read_excel(xl_file, sheet_name=sheet_name, header=None) + + # Section headers are rows where column 1 == 'Division' + header_rows = df[df.iloc[:, 1].astype(str).str.strip() == 'Division'].index.tolist() + + records = [] + for header_row in header_rows: + # Extract (year, column_index) pairs from this header row + year_cols = [] + for col in range(2, df.shape[1]): + raw = df.iloc[header_row, col] + if pd.isna(raw): + continue + try: + year_cols.append((int(float(raw)), col)) + except (ValueError, TypeError): + pass + + # Read the 9 division rows immediately following the header + for row_idx in range(header_row + 1, header_row + 11): + if row_idx >= len(df): + break + div_raw = df.iloc[row_idx, 1] + if pd.isna(div_raw) or str(div_raw).strip() == 'Grand Total': + break + try: + div = int(float(div_raw)) + except (ValueError, TypeError): + break + cendiv = _DIV_TO_CENDIV[div] + for yr, col in year_cols: + val = df.iloc[row_idx, col] + if val == '-' or pd.isna(val): + val = 0.0 + records.append({'cendiv': cendiv, 'year': yr, 'dgpv': float(val)}) + + df_dgpv = pd.DataFrame(records) + df_dgpv = df_dgpv.groupby(['cendiv', 'year'])['dgpv'].sum().reset_index() + # Convert trillion Btu to quads + df_dgpv['dgpv'] = df_dgpv['dgpv'] / 1000 + return df_dgpv + + +def read_aeo_electricity(csv_path): + """Read an AEO electricity consumption CSV and return long-format + DataFrame with columns [year, cendiv, aeo_electricity] in quads.""" + df = pd.read_csv(csv_path) + rename_map = {} + for col in df.columns: + if col in ('year', 'units'): + continue + for long_name, short_name in _CENDIV_NAME_MAP.items(): + if long_name in col: + rename_map[col] = short_name + break + df = df.rename(columns=rename_map) + cendiv_cols = [c for c in df.columns if c in _CENDIV_NAME_MAP.values()] + df_long = df.melt(id_vars=['year'], value_vars=cendiv_cols, + var_name='cendiv', value_name='aeo_electricity') + df_long['year'] = df_long['year'].astype(int) + df_long['aeo_electricity'] = pd.to_numeric(df_long['aeo_electricity'], + errors='coerce').fillna(0) + return df_long + ### Load and process data @@ -25,29 +123,29 @@ # Rename columns to match EIA data st_cendiv.rename(columns={'st':'stateid'}, inplace=True) -# Load the "Demand Ratios" sheet from the Excel file -demand_ratios = pd.read_excel( - 'Electricity Demand Preprocessing for AEO Inputs.xlsx', - sheet_name='Demand Ratios' -) -# Melt years into long format -demand_ratios = demand_ratios.melt(id_vars=['scenario','cendiv'], var_name='year', value_name='ratio') -demand_ratios['year'] = demand_ratios['year'].astype(int) - -# Normalize ratios to 1 in lastyear -demand_ratios = demand_ratios.merge( - demand_ratios[demand_ratios['year'] == lastyear][['scenario', 'cendiv', 'ratio']], - on=['scenario', 'cendiv'], - suffixes=('', '_lastyear') -) -demand_ratios['ratio'] = demand_ratios['ratio'] / demand_ratios['ratio_lastyear'] -demand_ratios.drop(columns=['ratio_lastyear'], inplace=True) - -# Set values before lastyear to 1.0 -demand_ratios.loc[demand_ratios['year'] < lastyear, 'ratio'] = 1.0 +# Scenario configuration: maps scenario names to input files +dgpv_file = 'AEO2026_bldgs_pv_gen_cb_high_low_economic_growth_2026-04-27.xlsx' + +scenario_config = [ + { + 'name': 'Counterfactual Baseline', + 'elec_csv': os.path.join('outputs', f'AEO_CB2026_{AEO_year}_electricity_consumption.csv'), + 'dgpv_sheet': 'AEO2026 Counterfactual Baseline', + }, + { + 'name': 'High Economic Growth', + 'elec_csv': os.path.join('outputs', f'AEO_HM2026_{AEO_year}_electricity_consumption.csv'), + 'dgpv_sheet': 'AEO2026 High Economic Growth', + }, + { + 'name': 'Low Economic Growth', + 'elec_csv': os.path.join('outputs', f'AEO_LM2026_{AEO_year}_electricity_consumption.csv'), + 'dgpv_sheet': 'AEO2026 Low Economic Growth', + }, +] # Collect state-level retail sales data from EIA API -url_retail = create_EIA_url(api_key, 'retail-sales', ['sales'], +url_retail = create_EIA_url(api_key, 'retail-sales', ['sales'], {'sectorid':['ALL']}, freq='annual', start=2010) df_retail = retrieve_EIA_data(url_retail) df_retail = df_retail[['year', 'stateid', 'sales']].copy() @@ -77,7 +175,7 @@ # Rename value column to generation df_pv3.rename(columns={'value':'pvgen'}, inplace=True) -# Combine retail sales and residential PV generation +# Combine retail sales and behind-the-meter PV generation df_combined = df_retail.merge(df_pv3, on=['year', 'stateid'], how='left').fillna(0) # Add load column (sales + pv generation) @@ -85,6 +183,44 @@ # Drop sales and pvgen columns df_combined.drop(columns=['sales', 'pvgen'], inplace=True) +# Build demand ratios from AEO electricity consumption + DGPV for each scenario. +# Ratios are normalized so that the first AEO projected year = 1.0 for each cendiv. +# This avoids unit-mismatch between historical EIA data (million kWh) and AEO data (quads). +# Historical years (2010-lastyear) use ratio = 1.0 so the state-level loadmult drives +# the multiplier for those years; AEO growth trajectory applies from lastyear onward. +all_cendivs = list(_DIV_TO_CENDIV.values()) +hist_years = list(range(2010, lastyear + 1)) # 2010 through lastyear inclusive +all_ratios = [] + +# Add historical rows with ratio = 1.0 for all scenarios +for cfg in scenario_config: + hist_rows = pd.DataFrame([ + {'scenario': cfg['name'], 'cendiv': cd, 'year': yr, 'ratio': 1.0} + for cd in all_cendivs + for yr in hist_years + ]) + all_ratios.append(hist_rows) + +# Add projected rows (2025-2050) normalized to ratio = 1.0 in aeo_first_year +for cfg in scenario_config: + df_elec = read_aeo_electricity(cfg['elec_csv']) + df_dgpv = read_dgpv_from_excel(dgpv_file, cfg['dgpv_sheet']) + + df_future = df_elec.merge(df_dgpv, on=['year', 'cendiv'], how='left').fillna(0) + df_future['total_demand'] = df_future['aeo_electricity'] + df_future['dgpv'] + + # Normalize so ratio = 1.0 in aeo_first_year for each cendiv + base = (df_future[df_future['year'] == aeo_first_year][['cendiv', 'total_demand']] + .rename(columns={'total_demand': 'demand_base'})) + df_future = df_future.merge(base, on='cendiv') + df_future['ratio'] = df_future['total_demand'] / df_future['demand_base'] + df_future['scenario'] = cfg['name'] + all_ratios.append(df_future[['scenario', 'cendiv', 'year', 'ratio']]) + +demand_ratios = pd.concat(all_ratios, ignore_index=True) +# Drop duplicate lastyear rows (historical block already added lastyear = 1.0) +demand_ratios = demand_ratios.drop_duplicates(subset=['scenario', 'cendiv', 'year'], keep='last') + # Normalize load by 2010 load df_load_2010 = df_combined[df_combined['year'] == 2010][['stateid', 'load']].rename(columns={'load':'load_2010'}) df_load = df_combined.merge(df_load_2010, on='stateid', how='left') @@ -93,28 +229,28 @@ # Fill in future years using a value of 1.0 for loadmult future_years = pd.DataFrame({ - 'year': range(2024, 2051) + 'year': range(2025, 2051) }).assign(key=1) unique_states = df_load[['stateid']].drop_duplicates().assign(key=1) -df_future = pd.merge(future_years, unique_states, on='key').drop(columns=['key']) +df_future_yrs = pd.merge(future_years, unique_states, on='key').drop(columns=['key']) -# Fill loadmult with with the value from lastyear +# Fill loadmult with the value from lastyear lastyear_loadmult = df_load[df_load['year'] == lastyear][['stateid', 'loadmult']] -df_future = df_future.merge(lastyear_loadmult, on='stateid', how='left') +df_future_yrs = df_future_yrs.merge(lastyear_loadmult, on='stateid', how='left') -df_load2 = pd.concat([df_load, df_future], ignore_index=True) +df_load2 = pd.concat([df_load, df_future_yrs], ignore_index=True) # Map states to census divisions df_load3 = df_load2.merge(st_cendiv, on='stateid', how='left').dropna() # Remove spaces from cendiv names and make lower case for merging -df_load3['cendiv'] = df_load3['cendiv'].str.strip().str.lower() -demand_ratios['cendiv'] = demand_ratios['cendiv'].str.strip().str.lower() +df_load3['cendiv'] = df_load3['cendiv'].str.strip().str.replace(' ', '').str.lower() +demand_ratios['cendiv'] = demand_ratios['cendiv'].str.lower() # Merge with demand ratios -df_loadtot = df_load3.merge(demand_ratios, left_on=['year', 'cendiv'], right_on=['year', 'cendiv'], how='left') +df_loadtot = df_load3.merge(demand_ratios, on=['year', 'cendiv'], how='left') # Check stateid = "ND" for verification df_check = df_loadtot[df_loadtot['stateid'] == 'ND'].copy() @@ -130,16 +266,16 @@ # Split out into different scenarios df_low = df_loadtot[df_loadtot['scenario']=='Low Economic Growth'].copy() -df_ref = df_loadtot[df_loadtot['scenario']=='Reference Case'].copy() +df_baseline = df_loadtot[df_loadtot['scenario']=='Counterfactual Baseline'].copy() df_high = df_loadtot[df_loadtot['scenario']=='High Economic Growth'].copy() # Drop scenario column df_low.drop(columns=['scenario'], inplace=True) -df_ref.drop(columns=['scenario'], inplace=True) +df_baseline.drop(columns=['scenario'], inplace=True) df_high.drop(columns=['scenario'], inplace=True) # Plot the multipliers by r for each scenario (optional) -for scenario, df in zip(['Low', 'Reference', 'High'], [df_low, df_ref, df_high]): +for scenario, df in zip(['Low', 'Baseline', 'High'], [df_low, df_baseline, df_high]): plt.figure(figsize=(10,6)) for r in df['r'].unique(): df_r = df[df['r'] == r] @@ -159,6 +295,6 @@ if not os.path.exists(Output_folder): os.makedirs(Output_folder) -df_low.to_csv(os.path.join(Output_folder, 'demand_AEO_{}_low.csv').format(AEO_year), index=False) -df_ref.to_csv(os.path.join(Output_folder, 'demand_AEO_{}_reference.csv').format(AEO_year), index=False) -df_high.to_csv(os.path.join(Output_folder, 'demand_AEO_{}_high.csv').format(AEO_year), index=False) \ No newline at end of file +df_low.to_csv(os.path.join(Output_folder, 'demand_AEO_{}_low.csv'.format(AEO_year)), index=False) +df_baseline.to_csv(os.path.join(Output_folder, 'demand_AEO_{}_baseline.csv'.format(AEO_year)), index=False) +df_high.to_csv(os.path.join(Output_folder, 'demand_AEO_{}_high.csv'.format(AEO_year)), index=False) diff --git a/aeo_updates/AEO_scraper.ipynb b/aeo_updates/AEO_scraper.ipynb index 99c4137..3daf326 100644 --- a/aeo_updates/AEO_scraper.ipynb +++ b/aeo_updates/AEO_scraper.ipynb @@ -7,19 +7,20 @@ "metadata": {}, "outputs": [], "source": [ - "# Script to pull AEO 2023 data using the API\n", + "# Script to pull AEO 2026 data using the API\n", "\n", "import requests\n", - "import json\n", "import pandas as pd\n", "from time import sleep\n", "import os\n", "\n", "# Set AEO_year\n", - "AEO_year = 2025\n", + "AEO_year = 2026\n", "\n", "# Get EIA API key\n", - "api_key = os.getenv('EIA_API_KEY')" + "api_key = os.getenv('EIA_API_KEY')\n", + "if not api_key:\n", + " raise ValueError(\"Missing EIA_API_KEY environment variable. Set EIA_API_KEY before running this notebook.\")" ] }, { @@ -52,16 +53,16 @@ " 'Energy Prices : Electric Power : Steam Coal',\n", " 'Energy Prices : Electric Power : Natural Gas',\n", "# 'Energy Prices : Nominal : Electric Power : Natural Gas',\n", - " 'Energy Prices : Electric Power : Uranium',\n", + " 'Energy Prices : Electric Power : Nuclear Fuel',\n", "# 'Energy Prices : Nominal : Electric Power : Uranium',\n", " 'Energy Use : Electric Power : Natural Gas',\n", " 'Energy Use : Total : Natural Gas',\n", " 'Energy Use : Delivered : All Sectors : Electricity'\n", - "]\n", + " ]\n", "\n", "# This API call will get us a list of dictionaries that associate each seriesId to a series name.\n", "path = f'https://api.eia.gov/v2/aeo/{AEO_year}/facet/seriesId?api_key={api_key}'\n", - "resp = json.loads(requests.get(path).text)\n", + "resp = requests.get(path).json()\n", "series_maps = resp['response']['facets']\n", "\n", "# Next we construct the series_lists dictionary, filtered to the desired series in series_names.\n", @@ -95,9 +96,9 @@ " 'Mountain',\n", " 'Pacific',\n", " 'United States'\n", - "]\n", + " ]\n", "path = f'https://api.eia.gov/v2/aeo/{AEO_year}/facet/regionId?api_key={api_key}'\n", - "resp = json.loads(requests.get(path).text)\n", + "resp = requests.get(path).json()\n", "region_maps = resp['response']['facets']\n", "regionIds = {item['id'] for item in region_maps if str(item['name']) in region_names}\n", "#n = [(item['id'], item['name']) for item in x if item['name'] in region_names]\n", @@ -105,6 +106,22 @@ "regionIds" ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "c0b3db81", + "metadata": {}, + "outputs": [], + "source": [ + "# Inspect available scenario IDs before building the scenario filter in Cell 4\n", + "path = f'https://api.eia.gov/v2/aeo/{AEO_year}/facet/scenario?api_key={api_key}'\n", + "resp = requests.get(path).json()\n", + "scenario_maps = resp['response']['facets']\n", + "scenario_df = pd.DataFrame(scenario_maps)[['id', 'name']]\n", + "scenario_df = scenario_df.sort_values('id').reset_index(drop=True)\n", + "scenario_df" + ] + }, { "cell_type": "code", "execution_count": null, @@ -113,8 +130,18 @@ "outputs": [], "source": [ "# Scenario filter\n", - "# hm2025 = high economic growth, lm2025 = low economic growth, nocaa111 = alternative electricity scenario\n", - "scenarios = [f'ref{AEO_year}', 'highogs', 'lowogs', 'hm2025', 'lm2025', 'nocaa111']\n", + "# Use dynamic IDs for high/low economic growth so this updates with AEO_year\n", + "scenarios = [f'cb{AEO_year}', 'highogs', 'lowogs', f'hm{AEO_year}', f'lm{AEO_year}', 'altelec']\n", + "\n", + "# Validate scenario IDs so naming changes are caught early\n", + "path = f'https://api.eia.gov/v2/aeo/{AEO_year}/facet/scenario?api_key={api_key}'\n", + "resp = requests.get(path).json()\n", + "available_scenarios = {item['id'] for item in resp['response']['facets']}\n", + "missing_scenarios = [s for s in scenarios if s not in available_scenarios]\n", + "if missing_scenarios:\n", + " print('Scenario IDs not found for this AEO year:', missing_scenarios)\n", + " print('Run the scenario lookup cell above and update scenarios.')\n", + " raise ValueError('Invalid scenario IDs in scenarios list')\n", "f1 = '&'.join(['facets[scenario][]=' + scenario for scenario in scenarios])\n", "\n", "# RegionId filter\n", @@ -129,7 +156,7 @@ "\n", " # Construct API call\n", " path = f'https://api.eia.gov/v2/aeo/{AEO_year}/data?api_key={api_key}&data[]=value&{f1}&{f2}&{f3}'\n", - " resp = json.loads(requests.get(path).text)\n", + " resp = requests.get(path).json()\n", "\n", " # The API throttling is very aggressive. We have to slow things way down or our api_key will\n", " # be temporarily locked. This makes it important for us to do as few api calls as possible, or\n", @@ -154,9 +181,7 @@ "# Additional processing\n", "df = df.drop_duplicates()\n", "df = df.drop(columns=['history'])\n", - "df = df.rename(columns={'period': 'year', 'unit': 'units'})\n", - "\n", - "\n" + "df = df.rename(columns={'period': 'year', 'unit': 'units'})" ] }, { @@ -203,7 +228,7 @@ "file_name_suffix_list = {\n", " 'Energy Prices : Electric Power : Steam Coal': 'coal_prices',\n", " 'Energy Prices : Electric Power : Natural Gas': 'ng_prices',\n", - " 'Energy Prices : Electric Power : Uranium': 'uranium_prices',\n", + " 'Energy Prices : Electric Power : Nuclear Fuel': 'uranium_prices',\n", " 'Energy Use : Electric Power : Natural Gas': 'ng_demand_electricity',\n", " 'Energy Use : Total : Natural Gas': 'ng_tot_demand',\n", " 'Energy Use : Delivered : All Sectors : Electricity': 'electricity_consumption'\n", @@ -241,6 +266,10 @@ "\n", "#df_new = {}\n", "\n", + "# Specify outputs location\n", + "output_dir = 'outputs'\n", + "os.makedirs(output_dir, exist_ok=True)\n", + "\n", "for x in series_names:\n", " for j in scenario_filter:\n", " df_filter = pd.DataFrame(df2.loc[(df2['seriesName'] == x) & (df2['scenario'] == j)])\n", @@ -265,9 +294,8 @@ " \n", " fileseries_suffix = file_name_suffix_list.get(x, 'Unknown')\n", " \n", - " #Please alter file path if needed for the generated files to go\n", - " \n", - " filename = f\"AEO_{j}_{AEO_year}_{fileseries_suffix}.csv\"\n", + " # Output files\n", + " filename = os.path.join(output_dir, f\"AEO_{j}_{AEO_year}_{fileseries_suffix}.csv\")\n", " \n", " # Only output coal and uranium prices for REF scenario\n", " # if x == 'Energy Prices : Electric Power : Steam Coal' or x == 'Energy Prices : Electric Power : Uranium':\n", @@ -279,24 +307,8 @@ " df_pivot_filtered.to_csv(filename, index=False)\n", " \n", "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", "print('Scrape complete! Files generated!')" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "925f73c4", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { @@ -315,7 +327,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.14" + "version": "3.11.15" } }, "nbformat": 4, diff --git a/aeo_updates/Compare AEO 2025 to AEO 2023.xlsx b/aeo_updates/Compare AEO 2025 to AEO 2023.xlsx deleted file mode 100644 index c5ab839..0000000 Binary files a/aeo_updates/Compare AEO 2025 to AEO 2023.xlsx and /dev/null differ diff --git a/aeo_updates/Compare AEO 2026 to AEO 2025.xlsx b/aeo_updates/Compare AEO 2026 to AEO 2025.xlsx new file mode 100644 index 0000000..1aae3ec Binary files /dev/null and b/aeo_updates/Compare AEO 2026 to AEO 2025.xlsx differ diff --git a/aeo_updates/Electricity Demand Preprocessing for AEO Inputs.xlsx b/aeo_updates/Electricity Demand Preprocessing for AEO Inputs.xlsx deleted file mode 100644 index f4880fb..0000000 Binary files a/aeo_updates/Electricity Demand Preprocessing for AEO Inputs.xlsx and /dev/null differ diff --git a/aeo_updates/NG Prices Preprocessing for AEO Inputs.xlsx b/aeo_updates/NG Prices Preprocessing for AEO Inputs.xlsx deleted file mode 100644 index 3a1b3af..0000000 Binary files a/aeo_updates/NG Prices Preprocessing for AEO Inputs.xlsx and /dev/null differ diff --git a/aeo_updates/README.md b/aeo_updates/README.md index f02f09f..d89d9e4 100644 --- a/aeo_updates/README.md +++ b/aeo_updates/README.md @@ -5,23 +5,22 @@ This module processes data from the Annual Energy Outlook (AEO) published by the ## Key Scripts ### AEO_Load_Projections.py -This script creates the demand projection files for AEO scenarios. It uses historical retail sales and behind-the-meter PV generation data from EIA to calibrate historical state-level demand, and then carries that forward using demand ratios calculated from the AEO scenarios. +This script creates state-level demand multiplier files for AEO scenarios. For historical years (2010–lastyear), state-level demand is derived from EIA retail electricity sales and behind-the-meter PV generation via the EIA API. For projected years (lastyear+1 through 2050), demand growth is computed from AEO electricity consumption CSVs (outputs/ folder, by census division, in quads) combined with regional rooftop PV generation from an EIA-provided Excel file (residential + commercial, by census division). These are added to reconstruct gross electricity demand, then normalized to the first AEO projected year so the historical and projected series join cleanly. ### AEO_scraper.ipynb The scraper will grab the following data from EIA's API: -* Electricity growth by region for high, low, and reference economic growth. +* Electricity growth by region for high, low, and Counterfactual Baseline economic growth. * Natural gas prices by region for high, low, and reference oil and gas growth. * Total natural gas use by region for high, low, and reference oil and gas resource * Natural gas use for electricity by region for high, low, and reference oil and gas resource. -* Coal prices by region for the reference scenario. -* Uranium prices for the US for the reference scenario. +* Coal prices by region for the Counterfactual Baseline scenario. +* Uranium/Nuclear Fuel prices for the US for the Counterfactual Baseline scenario. This can be run as is. You will want to adjust the default AEO year. Sometimes the scenario names change from one year to the next, so if a scenario is not populated, its name has likely changed. +In the AEO 2026, "Uranium" became "Nuclear Fuel", but only for real prices (nominal prices still use Uranium)--so you can check for similar name changes if you run into an error. -These results will be written out as csv files. -Note that the write directory is not specified and hence the files will be output to the directory from where the file is run. -The output directory may be altered as required. +These results will be written out as csv files to the outputs directory (it will be created if it does not already exist). If more data sets are desired, search for the API url here: https://www.eia.gov/opendata/qb.php?category=371 @@ -42,22 +41,8 @@ Once you have obtained your api key, create a new environment variable to store ### Input Data Changes When Updating AEO Data #### Natural Gas Prices and Demand -Natural gas prices and demand can be pulled using the EIA AEO data grabber. The spreadsheet "NG Prices Preprocessing for AEO Inputs.xlsx" is used to calculate the alphas using the preset betas. You need to paste in the NG prices and NG electricity sector demand into the relevant tabs. Historical data needs to be updated to the current dollar year. The deflator to convert the alphas back to 2004$ also need to be updated. The alphas are then put into a csv file to be added to the inputs/fuelprices folder of the ReEDS model repo. - -The prices and demand (both for the electricity sector and for all sectors) are also put into the relevant csv files in the inputs/fuelprices folder of the ReEDS model repo. Here are the NG input files that should be updated: - -* ng_tot_demand_AEO_{year}_HOG.csv -* ng_tot_demand_AEO_{year}_LOG.csv -* ng_tot_demand_AEO_{year}_reference.csv -* ng_demand_AEO_{year}_LOG.csv -* ng_demand_AEO_{year}_HOG.csv -* ng_demand_AEO_{year}_reference.csv -* ng_AEO_{year}_LOG.csv -* ng_AEO_{year}_HOG.csv -* ng_AEO_{year}_reference.csv -* alpha_AEO_{year}_LOG.csv -* alpha_AEO_{year}_HOG.csv -* alpha_AEO_{year}_reference.csv +Natural gas prices and demand are updated using the files in natural_gas_price_regression. +See the README.md in that folder for more information. #### Coal Prices Pulled using the EIA data grabber. Coal data are input into the coal_AEO_{year}_reference.csv. @@ -66,19 +51,15 @@ Pulled using the EIA data grabber. Coal data are input into the coal_AEO_{year}_ Pulled using the EIA data grabber. Uranium prices are input into uranium_AEO_{year}_reference.csv. #### Demand Growth -Pulled using the EIA AEO data grabber. The demand growth data reports electricity consumption after rooftop PV has supplied a portion of the load, so we need to add the rooftop PV consumption back into this demand. Total rooftop PV consumption is in Table 17, "Renewable Energy Consumption by Sector and Source." We received regional (census division) numbers from EIA by asking Kevin Jarzomski (Kevin.Jarzomski@eia.gov), who sent us the file "AEO2025_bldgs_pv_gen_ref_high_low_economic_growth_2025-04-17.xlsx."[^1] -Electricity demand and rooftop PV consumption were put into the -"Electricity Demand Preprocessing for AEO Inputs.xlsx" spreadsheet, -"Adding DGPV to Demand" tab, and added together to create the demand -growth projections. +Pulled using the EIA AEO data grabber. The demand growth data reports electricity consumption after rooftop PV has supplied a portion of the load, so we need to add the rooftop PV consumption back into this demand. We received regional (census division) rooftop PV numbers (residential + commercial) from EIA by asking Kevin Jarzomski (Kevin.Jarzomski@eia.gov), who sent us the file "AEO2026_bldgs_pv_gen_cb_high_low_economic_growth_2026-04-27.xlsx."[^1] -The growth numbers are ratios of the specified year to 2010, i.e. -Demand~year~ / Demand~2010~. +The projections through 2050 are created using AEO_Load_Projections.py, which reads the AEO electricity consumption CSVs from the outputs/ folder and the EIA-provided DGPV Excel file directly. The script calibrates historical years (2010–lastyear) to EIA retail sales data via the EIA API, then applies census-division demand growth ratios from the AEO scenarios for projected years. -The projections through 2050 are created using AEO_Load_Projections.py, -which calibrates historical years to sales, and then carries the -census-division projection forward through 2050. It is set up to pull -the most recent EIA data using the EIA API. +The output multipliers are ratios of demand in the specified year to demand in 2010, i.e. Demandyear / Demand2010. Output files are written to the Outputs/ folder: + +* demand_AEO_{year}_baseline.csv (Counterfactual Baseline scenario) +* demand_AEO_{year}_low.csv (Low Economic Growth scenario) +* demand_AEO_{year}_high.csv (High Economic Growth scenario) #### Capital Costs You can get the Table 123 data from table 55 at @@ -113,4 +94,4 @@ historical values. using the ratio of the rooftop PV generation to the total electricity sector generation. See "Electricity Demand met by Rooftop PV - AEO 2016 - Not Used.xlsx" in - \\\\nrelnas01\\ReEDS\\\_ReEDS Documentation\\AEO 2016 Update. \ No newline at end of file + \\\\nrelnas01\\ReEDS\\\_ReEDS Documentation\\AEO 2016 Update. diff --git a/aeo_updates/natural_gas_price_regression/aeo_pipeline_config.json b/aeo_updates/natural_gas_price_regression/aeo_pipeline_config.json index c9ecc72..951062d 100644 --- a/aeo_updates/natural_gas_price_regression/aeo_pipeline_config.json +++ b/aeo_updates/natural_gas_price_regression/aeo_pipeline_config.json @@ -23,8 +23,6 @@ ], "exclude_aliases": [ "highogs", - "highprice", - "lowprice", "lowogs" ] }, @@ -57,18 +55,17 @@ "backoff_seconds": 1 }, "ng": { - "regions": [ - "New England", - "Middle Atlantic", - "East North Central", - "West North Central", - "South Atlantic", - "East South Central", - "West South Central", - "Mountain", - "Pacific" - ], - "price_deflator_to_2004": 0.586750, - "regional_beta_path": "inputs for alpha regression/cd_beta0.csv" + "cendiv_and_label": { + "New England": "New_England", + "Middle Atlantic": "Mid_Atlantic", + "East North Central": "East_North_Central", + "West North Central": "West_North_Central", + "South Atlantic": "South_Atlantic", + "East South Central": "East_South_Central", + "West South Central": "West_South_Central", + "Mountain": "Mountain", + "Pacific": "Pacific" + }, + "price_deflator_to_2004": 0.586750 } } diff --git a/aeo_updates/natural_gas_price_regression/inputs for alpha regression/README.md b/aeo_updates/natural_gas_price_regression/inputs for alpha regression/README.md index 0913706..acdcbe5 100644 --- a/aeo_updates/natural_gas_price_regression/inputs for alpha regression/README.md +++ b/aeo_updates/natural_gas_price_regression/inputs for alpha regression/README.md @@ -11,6 +11,8 @@ These files provide historical data to backfill years (2010 – most recent year - `ng_tot_demand_AEO_historical.csv` — Historical total (all-sector) NG demand by census division (Quads). - `st_cendiv.csv` — State to Census Division mapping. +Each pipeline run automatically appends the current AEO's calibration year (e.g., AEO 2026 appends 2025) to these CSVs. For prices, the appended value is deflated back to 2024$ so the file remains in a consistent dollar year. + ## Auto-generated files During the pipeline run, `sync_beta_to_alpha_inputs.py` copies beta regression results (`cd_beta0.csv`, `national_beta.csv`) into this directory. These are then read by `aeo_alpha_regression.py`. diff --git a/aeo_updates/natural_gas_price_regression/inputs for alpha regression/cd_beta0.csv b/aeo_updates/natural_gas_price_regression/inputs for alpha regression/cd_beta0.csv deleted file mode 100644 index 755bebb..0000000 --- a/aeo_updates/natural_gas_price_regression/inputs for alpha regression/cd_beta0.csv +++ /dev/null @@ -1,10 +0,0 @@ -*cendiv,value -East_North_Central,0.033492 -East_South_Central,0.139571 -Mid_Atlantic,0.151497 -Mountain,0.670353 -New_England,3.875323 -Pacific,1.341567 -South_Atlantic,0.175498 -West_North_Central,0.856915 -West_South_Central,-0.054900 diff --git a/aeo_updates/natural_gas_price_regression/inputs for alpha regression/national_beta.csv b/aeo_updates/natural_gas_price_regression/inputs for alpha regression/national_beta.csv deleted file mode 100644 index abd889b..0000000 --- a/aeo_updates/natural_gas_price_regression/inputs for alpha regression/national_beta.csv +++ /dev/null @@ -1,2 +0,0 @@ -beta -0.080017 diff --git a/aeo_updates/natural_gas_price_regression/inputs for alpha regression/ng_AEO_historical.csv b/aeo_updates/natural_gas_price_regression/inputs for alpha regression/ng_AEO_historical.csv index 21c2a70..6d416fe 100644 --- a/aeo_updates/natural_gas_price_regression/inputs for alpha regression/ng_AEO_historical.csv +++ b/aeo_updates/natural_gas_price_regression/inputs for alpha regression/ng_AEO_historical.csv @@ -14,3 +14,4 @@ year,East_North_Central,East_South_Central,Mid_Atlantic,Mountain,New_England,Pac 2022,6.73681,7.91093,6.17517,7.81644,8.92891,8.04040,8.54369,7.62799,7.36052 2023,2.57670,2.93427,3.09409,3.30029,5.18236,3.49938,3.74350,3.02199,2.51248 2024,2.28508,2.64533,2.26953,2.84119,3.91474,3.17877,3.58168,2.60507,2.19948 +2025,3.18162,3.67831,3.28914,3.58087,5.18377,3.81538,4.65433,3.44724,3.20723 diff --git a/aeo_updates/natural_gas_price_regression/inputs for alpha regression/ng_demand_AEO_historical.csv b/aeo_updates/natural_gas_price_regression/inputs for alpha regression/ng_demand_AEO_historical.csv index 906ac82..de8133d 100644 --- a/aeo_updates/natural_gas_price_regression/inputs for alpha regression/ng_demand_AEO_historical.csv +++ b/aeo_updates/natural_gas_price_regression/inputs for alpha regression/ng_demand_AEO_historical.csv @@ -14,3 +14,4 @@ year,East_North_Central,East_South_Central,Mid_Atlantic,Mountain,New_England,Pac 2022,1.47769,0.90048,1.99829,0.94774,0.36677,0.85018,2.60534,0.25751,2.79701 2023,1.33703,1.10878,1.78325,1.01090,0.38207,0.89752,2.93080,0.26185,3.11328 2024,1.80377,1.12075,1.94787,1.20213,0.45162,1.02698,2.77112,0.32823,3.27809 +2025,1.65962,1.02549,1.82135,1.21019,0.41596,0.85889,2.87384,0.32285,3.06582 diff --git a/aeo_updates/natural_gas_price_regression/inputs for alpha regression/ng_tot_demand_AEO_historical.csv b/aeo_updates/natural_gas_price_regression/inputs for alpha regression/ng_tot_demand_AEO_historical.csv index 12fc1e0..13dbe98 100644 --- a/aeo_updates/natural_gas_price_regression/inputs for alpha regression/ng_tot_demand_AEO_historical.csv +++ b/aeo_updates/natural_gas_price_regression/inputs for alpha regression/ng_tot_demand_AEO_historical.csv @@ -14,3 +14,4 @@ year,East_North_Central,East_South_Central,Mid_Atlantic,Mountain,New_England,Pac 2022,4.94008,1.89274,4.04472,1.91471,0.92315,2.85715,4.15986,1.99868,7.15639 2023,4.64873,1.86139,3.70918,1.73492,0.91437,2.86945,4.15805,1.87774,6.91027 2024,4.96334,2.09747,3.90312,2.15953,0.96217,2.98253,4.37537,2.04819,7.42142 +2025,5.01978,2.01659,3.82598,2.18927,0.94249,2.78771,4.54999,2.08700,7.43280 diff --git a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/alpha_AEO_2025_HOG.csv b/aeo_updates/natural_gas_price_regression/outputs of alpha regression/alpha_AEO_2025_HOG.csv deleted file mode 100644 index 6f26bdf..0000000 --- a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/alpha_AEO_2025_HOG.csv +++ /dev/null @@ -1,42 +0,0 @@ -t,East_North_Central,East_South_Central,Mid_Atlantic,Mountain,New_England,Pacific,South_Atlantic,West_North_Central,West_South_Central -2010,4.274321,4.137875,4.711561,4.348849,4.683875,4.172505,5.211406,4.662091,3.986710 -2011,3.232510,2.927834,3.561499,3.072886,1.816976,2.283109,3.674277,3.611184,3.128780 -2012,1.835805,1.657892,2.046154,1.711281,0.608251,0.855820,2.358643,2.023828,1.846135 -2013,2.717594,2.513758,2.919933,2.527450,2.752755,1.493476,2.911481,2.958827,2.646791 -2014,3.473377,2.970544,3.226643,2.832534,3.290931,1.899157,3.347307,3.696555,3.142849 -2015,1.425834,1.389863,1.335855,1.280899,1.075644,0.350386,1.933619,1.745417,1.571988 -2016,1.317182,1.059534,1.178246,0.798265,0.754709,0.154124,1.462783,1.120631,1.171322 -2017,1.772829,1.557646,1.563513,1.303398,1.026074,0.634368,1.739925,1.783923,1.649825 -2018,1.570148,1.430502,1.494355,1.231903,1.135924,0.575436,1.526521,1.592755,1.569901 -2019,0.871148,0.898527,0.989434,0.538674,1.046551,0.198684,1.038924,0.883068,0.937050 -2020,0.455373,0.555079,0.537067,0.269587,0.582678,-0.114265,0.676152,0.539248,0.622535 -2021,2.207225,2.555236,2.060217,2.261829,2.557165,1.958033,2.666629,2.345259,2.531617 -2022,3.035049,3.666597,2.443258,3.100001,2.984546,2.729747,3.716462,3.401063,3.614057 -2023,0.482152,0.587715,0.568648,0.285439,0.616932,-0.120976,0.715908,0.570962,0.659140 -2024,0.210737,0.345082,-0.031321,-0.191626,-0.468010,-0.546024,0.497425,0.184579,0.398380 -2025,0.344876,0.410939,0.112763,-0.100039,-0.454252,-0.445625,0.499549,0.320929,0.468171 -2026,0.197131,0.224597,0.019707,-0.232231,-0.252073,-0.439900,0.316351,0.206801,0.318176 -2027,0.157925,0.175124,-0.033279,-0.229435,-0.258877,-0.439578,0.353443,0.160226,0.277408 -2028,0.207416,0.262914,-0.037449,-0.125625,-0.289135,-0.368992,0.437009,0.252221,0.367318 -2029,0.242622,0.314815,-0.032144,-0.066422,-0.322310,-0.365759,0.477042,0.276503,0.415802 -2030,0.269754,0.378667,-0.033219,0.001634,-0.251269,-0.312151,0.533248,0.334502,0.493271 -2031,0.268865,0.424748,-0.023406,0.069528,-0.182167,-0.296495,0.481149,0.299335,0.550534 -2032,0.332092,0.490504,-0.010662,0.153234,-0.176144,-0.183365,0.630270,0.340568,0.644607 -2033,0.371831,0.539430,0.064029,0.282865,-0.082352,-0.174894,0.590180,0.450130,0.707330 -2034,0.403914,0.565403,0.122283,0.370414,-0.011003,-0.123494,0.619309,0.500722,0.733782 -2035,0.439723,0.607326,0.171979,0.464788,0.056341,-0.103677,0.668940,0.566309,0.777152 -2036,0.495611,0.667947,0.217097,0.464172,0.093461,0.082731,0.737113,0.691647,0.818150 -2037,0.497352,0.683358,0.236787,0.502848,0.092945,0.041974,0.757634,0.689038,0.805579 -2038,0.536568,0.734539,0.280015,0.692031,0.198916,0.112107,0.813723,0.770227,0.833305 -2039,0.576221,0.788379,0.334648,0.836469,0.222122,0.184849,0.867698,0.812897,0.877299 -2040,0.575600,0.801046,0.307851,0.861927,0.164746,0.224821,0.862221,0.855858,0.902691 -2041,0.575753,0.810443,0.307528,0.924016,0.221233,0.204696,0.870508,0.895945,0.930069 -2042,0.557631,0.813421,0.317525,0.923621,0.198786,0.243462,0.867713,0.881745,0.943432 -2043,0.489477,0.749603,0.235768,0.831754,0.097936,0.237156,0.819954,0.812074,0.912820 -2044,0.424122,0.713009,0.142980,0.730459,0.027747,0.171369,0.782351,0.753046,0.886083 -2045,0.370421,0.681674,0.076273,0.676002,0.070819,0.217207,0.758601,0.713242,0.876443 -2046,0.349860,0.669778,0.029397,0.695452,0.043637,0.199327,0.743219,0.715050,0.883537 -2047,0.310455,0.649055,-0.008888,0.660313,-0.012972,0.230160,0.833784,0.696182,0.856814 -2048,0.296592,0.657421,-0.029444,0.662000,-0.033075,0.294965,0.847091,0.695373,0.868570 -2049,0.245406,0.617461,-0.064672,0.593020,-0.062187,0.265435,0.791758,0.700461,0.841883 -2050,0.225173,0.585727,-0.095693,0.525706,-0.061527,0.281430,0.756995,0.701374,0.823963 diff --git a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/alpha_AEO_2025_LOG.csv b/aeo_updates/natural_gas_price_regression/outputs of alpha regression/alpha_AEO_2025_LOG.csv deleted file mode 100644 index aed84e9..0000000 --- a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/alpha_AEO_2025_LOG.csv +++ /dev/null @@ -1,42 +0,0 @@ -t,East_North_Central,East_South_Central,Mid_Atlantic,Mountain,New_England,Pacific,South_Atlantic,West_North_Central,West_South_Central -2010,4.274321,4.137875,4.711561,4.348849,4.683875,4.172505,5.211406,4.662091,3.986710 -2011,3.232510,2.927834,3.561499,3.072886,1.816976,2.283109,3.674277,3.611184,3.128780 -2012,1.835805,1.657892,2.046154,1.711281,0.608251,0.855820,2.358643,2.023828,1.846135 -2013,2.717594,2.513758,2.919933,2.527450,2.752755,1.493476,2.911481,2.958827,2.646791 -2014,3.473377,2.970544,3.226643,2.832534,3.290931,1.899157,3.347307,3.696555,3.142849 -2015,1.425834,1.389863,1.335855,1.280899,1.075644,0.350386,1.933619,1.745417,1.571988 -2016,1.317182,1.059534,1.178246,0.798265,0.754709,0.154124,1.462783,1.120631,1.171322 -2017,1.772829,1.557646,1.563513,1.303398,1.026074,0.634368,1.739925,1.783923,1.649825 -2018,1.570148,1.430502,1.494355,1.231903,1.135924,0.575436,1.526521,1.592755,1.569901 -2019,0.871148,0.898527,0.989434,0.538674,1.046551,0.198684,1.038924,0.883068,0.937050 -2020,0.455373,0.555079,0.537067,0.269587,0.582678,-0.114265,0.676152,0.539248,0.622535 -2021,2.207225,2.555236,2.060217,2.261829,2.557165,1.958033,2.666629,2.345259,2.531617 -2022,3.035049,3.666597,2.443258,3.100001,2.984546,2.729747,3.716462,3.401063,3.614057 -2023,0.482152,0.587715,0.568648,0.285439,0.616932,-0.120976,0.715908,0.570962,0.659140 -2024,0.215051,0.339058,-0.029711,-0.191824,-0.484707,-0.544057,0.490094,0.187075,0.400083 -2025,1.169469,1.322634,0.889994,0.743120,0.320668,0.468185,1.370276,1.126793,1.365049 -2026,1.435401,1.591261,1.277484,1.117014,0.993648,0.752048,1.674898,1.432441,1.515472 -2027,1.502568,1.668365,1.273043,1.214643,0.980905,0.838834,1.757396,1.511037,1.596232 -2028,1.681291,1.917707,1.426261,1.497429,1.115336,1.103785,1.990057,1.729195,1.845684 -2029,1.977326,2.224647,1.682775,1.860526,1.380942,1.387974,2.297298,2.076482,2.187165 -2030,2.498073,2.814701,2.120230,2.476454,1.840662,1.950263,2.855960,2.597319,2.806373 -2031,2.922170,3.231337,2.540952,2.981482,2.351311,2.424394,3.339785,3.044480,3.234114 -2032,3.379454,3.625742,2.992064,3.420201,2.669670,2.877220,3.697091,3.481538,3.664041 -2033,3.661245,3.901682,3.288055,3.630120,3.059481,3.098685,3.982265,3.739282,3.892631 -2034,3.835762,4.056546,3.470360,3.855987,3.327007,3.313662,4.168600,3.868858,4.057258 -2035,3.940490,4.191552,3.607970,4.031437,3.407640,3.491140,4.302966,3.954335,4.110478 -2036,4.100178,4.374171,3.658682,4.314109,3.491305,3.745742,4.517574,4.105921,4.338926 -2037,4.073614,4.422630,3.640128,4.387943,3.563082,3.863371,4.676863,4.119122,4.451877 -2038,4.013947,4.282977,3.579627,4.456922,3.496389,3.936299,4.663879,4.087316,4.440391 -2039,3.965983,4.428681,3.570535,4.655245,3.442832,4.161951,4.789079,3.972211,4.607058 -2040,4.031987,4.429668,3.551047,4.705194,3.426911,4.199913,4.760876,4.088288,4.625246 -2041,4.022913,4.621356,3.562317,4.565469,3.355756,4.239417,4.802329,4.099254,4.622140 -2042,3.963444,4.518745,3.460866,4.480272,3.316872,4.109517,4.750108,4.055829,4.571450 -2043,3.895821,4.415660,3.392686,4.357078,3.212400,4.036913,4.675000,4.008246,4.488237 -2044,3.905402,4.547160,3.403920,4.514365,3.171646,4.149064,4.676325,4.001124,4.638910 -2045,3.930714,4.517120,3.423385,4.607649,3.150600,4.239064,4.583925,4.048498,4.686094 -2046,4.060075,4.547413,3.472359,4.655728,3.230419,4.276129,4.623913,4.183568,4.733054 -2047,4.195563,4.762990,3.606305,4.865448,3.330979,4.463472,4.773827,4.287413,4.918477 -2048,4.353308,4.887999,3.740855,5.097561,3.450908,4.618085,4.956059,4.443471,5.152434 -2049,4.524340,5.046874,3.934228,5.257141,3.627606,4.741983,5.109198,4.607130,5.350720 -2050,4.696611,5.230470,4.124450,5.452070,3.805277,4.890810,5.306124,4.764436,5.537398 diff --git a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/alpha_AEO_2025_reference.csv b/aeo_updates/natural_gas_price_regression/outputs of alpha regression/alpha_AEO_2025_reference.csv deleted file mode 100644 index b4f06e4..0000000 --- a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/alpha_AEO_2025_reference.csv +++ /dev/null @@ -1,42 +0,0 @@ -t,East_North_Central,East_South_Central,Mid_Atlantic,Mountain,New_England,Pacific,South_Atlantic,West_North_Central,West_South_Central -2010,4.274321,4.137875,4.711561,4.348849,4.683875,4.172505,5.211406,4.662091,3.986710 -2011,3.232510,2.927834,3.561499,3.072886,1.816976,2.283109,3.674277,3.611184,3.128780 -2012,1.835805,1.657892,2.046154,1.711281,0.608251,0.855820,2.358643,2.023828,1.846135 -2013,2.717594,2.513758,2.919933,2.527450,2.752755,1.493476,2.911481,2.958827,2.646791 -2014,3.473377,2.970544,3.226643,2.832534,3.290931,1.899157,3.347307,3.696555,3.142849 -2015,1.425834,1.389863,1.335855,1.280899,1.075644,0.350386,1.933619,1.745417,1.571988 -2016,1.317182,1.059534,1.178246,0.798265,0.754709,0.154124,1.462783,1.120631,1.171322 -2017,1.772829,1.557646,1.563513,1.303398,1.026074,0.634368,1.739925,1.783923,1.649825 -2018,1.570148,1.430502,1.494355,1.231903,1.135924,0.575436,1.526521,1.592755,1.569901 -2019,0.871148,0.898527,0.989434,0.538674,1.046551,0.198684,1.038924,0.883068,0.937050 -2020,0.455373,0.555079,0.537067,0.269587,0.582678,-0.114265,0.676152,0.539248,0.622535 -2021,2.207225,2.555236,2.060217,2.261829,2.557165,1.958033,2.666629,2.345259,2.531617 -2022,3.035049,3.666597,2.443258,3.100001,2.984546,2.729747,3.716462,3.401063,3.614057 -2023,0.482152,0.587715,0.568648,0.285439,0.616932,-0.120976,0.715908,0.570962,0.659140 -2024,0.202311,0.323449,-0.041746,-0.207917,-0.505133,-0.576337,0.557962,0.174344,0.391092 -2025,0.602813,0.692976,0.347671,0.205100,-0.229539,-0.115501,0.772238,0.572750,0.749541 -2026,0.613034,0.694058,0.431180,0.230798,0.116280,-0.054895,0.782696,0.607796,0.728853 -2027,0.540627,0.615471,0.312819,0.178473,0.025300,-0.086562,0.799770,0.519140,0.672565 -2028,0.581150,0.707458,0.295013,0.304707,-0.005586,-0.041584,0.796321,0.597140,0.769714 -2029,0.669987,0.822394,0.342341,0.459622,0.067221,0.070685,1.001846,0.689225,0.892482 -2030,0.746032,0.956262,0.407551,0.616757,0.201912,0.206840,1.023289,0.790169,1.043240 -2031,0.851821,1.056561,0.511255,0.763157,0.270865,0.321341,1.103114,0.911957,1.155028 -2032,1.019251,1.236409,0.615433,1.014594,0.383170,0.526482,1.263653,1.080553,1.392116 -2033,1.250259,1.509990,0.857661,1.402480,0.667851,0.878486,1.550552,1.359765,1.683922 -2034,1.397943,1.689997,1.009215,1.640171,0.851366,1.045899,1.725658,1.597190,1.869690 -2035,1.492374,1.788715,1.079863,1.722907,0.910406,1.135310,1.819346,1.688441,1.911618 -2036,1.541326,1.811399,1.117161,1.746874,0.978942,1.165524,1.874950,1.757318,1.912304 -2037,1.536159,1.834123,1.155582,1.796241,0.991961,1.212068,1.942654,1.773991,1.941018 -2038,1.487182,1.834754,1.140687,1.836029,0.941332,1.255656,1.946024,1.777185,1.955180 -2039,1.483093,1.813399,1.106120,1.852664,0.903317,1.292380,1.942899,1.738510,1.955097 -2040,1.465823,1.816640,1.076208,1.929209,0.902075,1.386383,1.951433,1.783399,1.997464 -2041,1.485430,1.837854,1.079738,2.034826,0.940136,1.503686,1.952438,1.852105,2.062283 -2042,1.518709,1.858898,1.120315,2.110740,0.974734,1.655205,1.987258,1.936311,2.136794 -2043,1.529437,1.936040,1.132037,2.199057,1.000147,1.697789,2.038861,1.962805,2.223861 -2044,1.519776,1.964234,1.123951,2.219031,1.041327,1.774132,2.050695,1.987807,2.287435 -2045,1.520266,1.974527,1.099421,2.234710,1.086377,1.797817,2.027370,1.967755,2.312392 -2046,1.528193,2.019305,1.143505,2.302125,1.034779,1.933259,2.053118,2.025646,2.355159 -2047,1.514791,2.018503,1.127624,2.347354,1.059170,1.939243,2.015636,1.996091,2.409184 -2048,1.495751,1.965800,1.098686,2.275422,0.948902,1.943456,1.953655,1.948797,2.405515 -2049,1.466790,1.856363,1.069420,2.194083,0.957797,1.949716,1.896208,1.903059,2.375852 -2050,1.448063,1.769984,1.008986,2.211597,0.950400,1.936905,1.828244,1.910854,2.347436 diff --git a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/cd_beta0.csv b/aeo_updates/natural_gas_price_regression/outputs of alpha regression/cd_beta0.csv deleted file mode 100644 index 755bebb..0000000 --- a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/cd_beta0.csv +++ /dev/null @@ -1,10 +0,0 @@ -*cendiv,value -East_North_Central,0.033492 -East_South_Central,0.139571 -Mid_Atlantic,0.151497 -Mountain,0.670353 -New_England,3.875323 -Pacific,1.341567 -South_Atlantic,0.175498 -West_North_Central,0.856915 -West_South_Central,-0.054900 diff --git a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/national_beta.csv b/aeo_updates/natural_gas_price_regression/outputs of alpha regression/national_beta.csv deleted file mode 100644 index abd889b..0000000 --- a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/national_beta.csv +++ /dev/null @@ -1,2 +0,0 @@ -beta -0.080017 diff --git a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_AEO_2025_HOG.csv b/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_AEO_2025_HOG.csv deleted file mode 100644 index 36ad6dc..0000000 --- a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_AEO_2025_HOG.csv +++ /dev/null @@ -1,42 +0,0 @@ -year,East_North_Central,East_South_Central,Mid_Atlantic,Mountain,New_England,Pacific,South_Atlantic,West_North_Central,West_South_Central -2010,7.090990,6.864630,7.816360,7.214630,7.770430,6.922080,8.645590,7.734290,6.613850 -2011,6.415240,6.035800,7.181610,6.759040,6.976610,6.523090,7.612680,7.187830,6.021930 -2012,4.318400,4.171340,4.920950,4.812690,5.125690,4.981960,5.744140,4.836970,4.086240 -2013,5.644570,5.425660,6.221770,6.026710,8.068120,5.966590,6.488060,6.217400,5.311730 -2014,6.901570,6.196130,6.749790,6.533250,8.769200,6.652870,7.217680,7.398620,6.141910 -2015,3.728470,3.831860,3.850320,4.283490,5.664140,4.318230,5.209640,4.428220,3.709610 -2016,3.604330,3.348940,3.661070,3.544380,5.148300,3.667090,4.514280,3.500410,3.097470 -2017,4.251330,4.060240,4.159690,4.195250,5.355280,4.189160,4.872570,4.469500,3.814340 -2018,4.083060,4.025980,4.228360,4.311700,5.881250,4.317320,4.693190,4.334410,3.784560 -2019,3.074150,3.292140,3.569320,3.555690,5.476460,3.748840,4.098980,3.352610,2.850900 -2020,2.433600,2.771320,2.922260,3.117010,4.894560,3.305040,3.535610,2.854160,2.372950 -2021,5.212050,5.940870,5.323930,6.295940,7.947850,6.712710,6.632820,5.658210,5.441230 -2022,6.736810,7.910930,6.175170,7.816440,8.928910,8.040400,8.543690,7.627990,7.360520 -2023,2.576700,2.934270,3.094090,3.300290,5.182360,3.499380,3.743500,3.021990,2.512480 -2024,2.298708,2.680677,2.287270,2.863694,3.942886,3.208913,3.481087,2.621773,2.210615 -2025,2.394202,2.663157,2.313296,2.777358,3.545907,2.887983,3.371045,2.708847,2.218614 -2026,2.279395,2.498695,2.298251,2.712186,3.424357,2.803907,3.222955,2.664633,2.059296 -2027,2.267412,2.484879,2.255369,2.717369,3.179948,2.824805,3.352517,2.702573,2.033904 -2028,2.342423,2.607332,2.219732,2.860942,2.963628,2.993797,3.471617,2.822133,2.178029 -2029,2.422638,2.709477,2.237025,2.964342,2.897579,3.117639,3.536583,2.905383,2.279984 -2030,2.509715,2.835151,2.309547,3.228207,2.761189,3.405689,3.637365,3.080562,2.440423 -2031,2.525936,2.905147,2.337410,3.393098,2.747252,3.586969,3.510499,3.122771,2.544189 -2032,2.816504,3.177044,2.531026,3.814344,3.035183,4.013958,3.964278,3.501460,2.843096 -2033,2.860132,3.242102,2.633990,3.950235,3.119033,4.208856,3.848393,3.516799,2.926303 -2034,2.896689,3.271004,2.714677,4.049577,3.235024,4.334771,3.874689,3.576314,2.959214 -2035,2.911631,3.285653,2.718911,4.106940,3.332014,4.363559,3.886527,3.632553,2.990757 -2036,2.902459,3.277782,2.679176,4.154849,3.345162,4.288165,3.871522,3.651698,2.995825 -2037,2.849265,3.248419,2.655793,4.159271,3.331612,4.251697,3.826073,3.584962,2.955194 -2038,2.787631,3.233404,2.610942,4.197432,3.345557,4.234464,3.806650,3.559825,2.903788 -2039,2.754783,3.219203,2.571926,4.173839,3.217742,4.281111,3.787173,3.507277,2.895023 -2040,2.765788,3.249156,2.542018,4.291714,3.172366,4.376058,3.826042,3.599415,2.954316 -2041,2.809392,3.310269,2.589555,4.404143,3.339386,4.470148,3.902796,3.717389,3.031373 -2042,2.803964,3.326160,2.603526,4.446998,3.391913,4.516488,3.909537,3.731760,3.068110 -2043,2.752379,3.289931,2.536290,4.391260,3.330580,4.504367,3.898544,3.703559,3.063311 -2044,2.683260,3.263479,2.437677,4.328261,3.113896,4.424861,3.864220,3.646818,3.049417 -2045,2.627810,3.248250,2.366950,4.364906,2.985338,4.511312,3.847422,3.651798,3.060245 -2046,2.628173,3.277446,2.338613,4.417610,2.984034,4.539052,3.857837,3.720915,3.101784 -2047,2.562113,3.260696,2.304000,4.407011,2.934076,4.557307,4.006942,3.699291,3.061580 -2048,2.512380,3.256391,2.251554,4.434324,2.892148,4.586201,4.003499,3.689516,3.065140 -2049,2.461685,3.213834,2.203854,4.389663,2.865312,4.565211,3.966560,3.655248,3.041644 -2050,2.421813,3.162359,2.150464,4.376517,2.790408,4.556719,3.907777,3.591890,3.010422 diff --git a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_AEO_2025_LOG.csv b/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_AEO_2025_LOG.csv deleted file mode 100644 index 0e670cb..0000000 --- a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_AEO_2025_LOG.csv +++ /dev/null @@ -1,42 +0,0 @@ -year,East_North_Central,East_South_Central,Mid_Atlantic,Mountain,New_England,Pacific,South_Atlantic,West_North_Central,West_South_Central -2010,7.090990,6.864630,7.816360,7.214630,7.770430,6.922080,8.645590,7.734290,6.613850 -2011,6.415240,6.035800,7.181610,6.759040,6.976610,6.523090,7.612680,7.187830,6.021930 -2012,4.318400,4.171340,4.920950,4.812690,5.125690,4.981960,5.744140,4.836970,4.086240 -2013,5.644570,5.425660,6.221770,6.026710,8.068120,5.966590,6.488060,6.217400,5.311730 -2014,6.901570,6.196130,6.749790,6.533250,8.769200,6.652870,7.217680,7.398620,6.141910 -2015,3.728470,3.831860,3.850320,4.283490,5.664140,4.318230,5.209640,4.428220,3.709610 -2016,3.604330,3.348940,3.661070,3.544380,5.148300,3.667090,4.514280,3.500410,3.097470 -2017,4.251330,4.060240,4.159690,4.195250,5.355280,4.189160,4.872570,4.469500,3.814340 -2018,4.083060,4.025980,4.228360,4.311700,5.881250,4.317320,4.693190,4.334410,3.784560 -2019,3.074150,3.292140,3.569320,3.555690,5.476460,3.748840,4.098980,3.352610,2.850900 -2020,2.433600,2.771320,2.922260,3.117010,4.894560,3.305040,3.535610,2.854160,2.372950 -2021,5.212050,5.940870,5.323930,6.295940,7.947850,6.712710,6.632820,5.658210,5.441230 -2022,6.736810,7.910930,6.175170,7.816440,8.928910,8.040400,8.543690,7.627990,7.360520 -2023,2.576700,2.934270,3.094090,3.300290,5.182360,3.499380,3.743500,3.021990,2.512480 -2024,2.306225,2.671443,2.291188,2.867855,3.951021,3.213850,3.470091,2.628691,2.214949 -2025,3.757886,4.158859,3.629599,4.146041,4.825856,4.247904,4.788136,4.032818,3.699224 -2026,3.800100,4.182667,3.834864,4.116380,4.605783,4.119196,4.790429,4.000161,3.667104 -2027,3.930441,4.337811,3.835330,4.269623,4.343827,4.225954,4.924905,4.200141,3.808350 -2028,4.168205,4.686580,4.009411,4.650997,4.395217,4.694008,5.188810,4.513605,4.175693 -2029,4.620580,5.149542,4.403663,5.207363,4.783993,5.210474,5.605813,5.094242,4.716477 -2030,5.483608,6.110940,5.116742,6.219917,5.429995,6.208527,6.492614,6.184416,5.740700 -2031,6.032155,6.622880,5.623803,6.823698,6.023727,6.887148,7.070742,6.668324,6.323299 -2032,6.900975,7.392770,6.455877,7.823699,6.769484,7.795189,7.802534,7.624558,7.123114 -2033,7.243104,7.728008,6.816369,8.029579,7.239462,8.010993,8.118889,7.822190,7.409045 -2034,7.448060,7.892791,7.007079,8.258191,7.573184,8.196181,8.326830,7.872682,7.609113 -2035,7.551923,8.054188,7.163031,8.444173,7.670228,8.278070,8.486915,7.904687,7.651300 -2036,7.728963,8.253443,7.137417,8.753573,7.708939,8.491802,8.726404,8.052999,7.956063 -2037,7.568828,8.212115,6.949639,8.739521,7.558604,8.407489,8.848077,7.982419,8.047225 -2038,7.392748,7.899144,6.762915,8.673278,7.343988,8.322899,8.740216,7.834982,7.963167 -2039,7.297922,8.110153,6.737899,9.006240,7.251116,8.591912,8.910620,7.645207,8.226623 -2040,7.382362,8.071926,6.678354,9.026676,7.218706,8.661890,8.836534,7.822319,8.232173 -2041,7.322939,8.334088,6.644736,8.673353,7.065958,8.644938,8.862980,7.732145,8.189538 -2042,7.199334,8.141396,6.447856,8.462022,6.963392,8.381509,8.753935,7.611486,8.084666 -2043,7.084161,7.972393,6.334739,8.241702,6.808531,8.252924,8.612486,7.540718,7.942930 -2044,7.090244,8.179617,6.344906,8.507860,6.503371,8.446995,8.590377,7.552183,8.183352 -2045,7.115554,8.105153,6.351938,8.658021,6.349429,8.574844,8.392526,7.610549,8.243489 -2046,7.327775,8.147901,6.429235,8.684699,6.509525,8.631374,8.438897,7.833263,8.316871 -2047,7.564465,8.513703,6.661896,9.064015,6.689818,8.964581,8.687522,8.053114,8.630199 -2048,7.853740,8.746612,6.915947,9.510916,6.918985,9.287422,9.020991,8.339838,9.039744 -2049,8.156782,9.029250,7.254817,9.833487,7.237922,9.528601,9.287102,8.634782,9.384081 -2050,8.458136,9.348893,7.582260,10.207071,7.553951,9.821965,9.627280,8.921481,9.705161 diff --git a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_AEO_2025_reference.csv b/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_AEO_2025_reference.csv deleted file mode 100644 index de97406..0000000 --- a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_AEO_2025_reference.csv +++ /dev/null @@ -1,42 +0,0 @@ -year,East_North_Central,East_South_Central,Mid_Atlantic,Mountain,New_England,Pacific,South_Atlantic,West_North_Central,West_South_Central -2010,7.090990,6.864630,7.816360,7.214630,7.770430,6.922080,8.645590,7.734290,6.613850 -2011,6.415240,6.035800,7.181610,6.759040,6.976610,6.523090,7.612680,7.187830,6.021930 -2012,4.318400,4.171340,4.920950,4.812690,5.125690,4.981960,5.744140,4.836970,4.086240 -2013,5.644570,5.425660,6.221770,6.026710,8.068120,5.966590,6.488060,6.217400,5.311730 -2014,6.901570,6.196130,6.749790,6.533250,8.769200,6.652870,7.217680,7.398620,6.141910 -2015,3.728470,3.831860,3.850320,4.283490,5.664140,4.318230,5.209640,4.428220,3.709610 -2016,3.604330,3.348940,3.661070,3.544380,5.148300,3.667090,4.514280,3.500410,3.097470 -2017,4.251330,4.060240,4.159690,4.195250,5.355280,4.189160,4.872570,4.469500,3.814340 -2018,4.083060,4.025980,4.228360,4.311700,5.881250,4.317320,4.693190,4.334410,3.784560 -2019,3.074150,3.292140,3.569320,3.555690,5.476460,3.748840,4.098980,3.352610,2.850900 -2020,2.433600,2.771320,2.922260,3.117010,4.894560,3.305040,3.535610,2.854160,2.372950 -2021,5.212050,5.940870,5.323930,6.295940,7.947850,6.712710,6.632820,5.658210,5.441230 -2022,6.736810,7.910930,6.175170,7.816440,8.928910,8.040400,8.543690,7.627990,7.360520 -2023,2.576700,2.934270,3.094090,3.300290,5.182360,3.499380,3.743500,3.021990,2.512480 -2024,2.285080,2.645327,2.269530,2.841191,3.914745,3.178771,3.581676,2.605072,2.199480 -2025,2.837277,3.142428,2.724665,3.296411,3.945919,3.449765,3.846201,3.162053,2.702884 -2026,2.763278,3.055122,2.780215,3.156343,3.819109,3.232441,3.765639,3.076137,2.602545 -2027,2.703812,2.988694,2.636923,3.122164,3.435360,3.154894,3.837702,3.054238,2.549224 -2028,2.762565,3.127218,2.576812,3.269873,3.240752,3.344125,3.785440,3.195382,2.701754 -2029,2.918250,3.306478,2.643674,3.513918,3.229812,3.634970,4.085795,3.398271,2.910596 -2030,3.049576,3.512392,2.773706,3.861304,3.129285,4.019860,4.087484,3.646155,3.163967 -2031,3.170081,3.613755,2.866469,4.079747,3.326129,4.274564,4.101537,3.762861,3.303848 -2032,3.635774,4.085257,3.222987,4.733683,3.779099,4.891140,4.559186,4.423059,3.843493 -2033,3.881344,4.388517,3.483385,5.040173,4.071227,5.191451,4.842482,4.704285,4.214373 -2034,3.987692,4.544195,3.598001,5.230925,4.168971,5.384827,4.962396,4.772688,4.409548 -2035,4.045470,4.609845,3.631619,5.303466,4.199443,5.412060,5.016607,4.784186,4.425838 -2036,4.036805,4.556694,3.610814,5.251937,4.265835,5.282920,5.044314,4.780307,4.374448 -2037,3.899876,4.464017,3.518157,5.176685,4.120316,5.104523,4.979167,4.672058,4.317363 -2038,3.733063,4.376263,3.376865,5.154803,3.993610,5.010477,4.909653,4.566430,4.273121 -2039,3.681409,4.286094,3.256926,5.160571,3.816716,4.923649,4.850672,4.408439,4.233821 -2040,3.662889,4.299133,3.215503,5.306404,3.872509,5.055543,4.854046,4.491251,4.305593 -2041,3.723782,4.361627,3.252421,5.505489,3.966687,5.287903,4.880734,4.649086,4.432408 -2042,3.773970,4.390012,3.298153,5.683669,4.091134,5.408764,4.925858,4.751801,4.548378 -2043,3.805754,4.522407,3.344024,5.836872,4.169929,5.563621,5.008918,4.807343,4.704851 -2044,3.800041,4.578125,3.347215,5.905303,4.087124,5.645613,5.012309,4.817755,4.813431 -2045,3.798050,4.596054,3.329523,5.945200,3.891919,5.636967,4.970164,4.839172,4.858135 -2046,3.810532,4.665256,3.393556,6.013248,3.902629,5.758965,4.994895,4.945189,4.923351 -2047,3.770669,4.652761,3.383668,6.069484,3.945969,5.808951,4.917217,4.909540,5.001916 -2048,3.740844,4.571362,3.349662,6.010077,3.787596,5.813332,4.802450,4.859097,5.000763 -2049,3.685088,4.388964,3.274235,5.912456,3.870886,5.796697,4.690213,4.774216,4.946911 -2050,3.642668,4.235358,3.161351,5.972120,3.788044,5.745820,4.578797,4.791852,4.904408 diff --git a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_demand_AEO_2025_HOG.csv b/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_demand_AEO_2025_HOG.csv deleted file mode 100644 index a8c4379..0000000 --- a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_demand_AEO_2025_HOG.csv +++ /dev/null @@ -1,42 +0,0 @@ -year,East_North_Central,East_South_Central,Mid_Atlantic,Mountain,New_England,Pacific,South_Atlantic,West_North_Central,West_South_Central -2010,0.326000,0.567000,0.890000,0.641000,0.420000,0.989000,1.544000,0.124000,2.049000 -2011,0.395000,0.639000,0.965000,0.567000,0.456000,0.766000,1.671000,0.117000,2.188000 -2012,0.656000,0.797000,1.154000,0.663000,0.448000,1.045000,2.043000,0.171000,2.337000 -2013,0.475000,0.629000,1.066000,0.651000,0.372000,1.069000,1.883000,0.140000,2.075000 -2014,0.482860,0.671910,1.131480,0.648910,0.341750,1.073730,1.896250,0.108070,2.025670 -2015,0.713310,0.875420,1.236450,0.750920,0.397610,1.084430,2.330100,0.147160,2.434110 -2016,0.912140,0.961900,1.344500,0.765760,0.393180,0.917910,2.469830,0.191920,2.351840 -2017,0.810260,0.910900,1.196160,0.690330,0.371410,0.840900,2.475890,0.172190,2.063170 -2018,1.002570,0.994600,1.300060,0.760260,0.400410,0.871740,2.535530,0.189610,2.661310 -2019,1.185720,1.029820,1.451550,0.988210,0.338650,0.834000,2.790260,0.228310,2.928260 -2020,1.262780,1.047200,1.684220,0.954760,0.360850,0.847680,2.768040,0.247310,2.940390 -2021,1.082250,0.913910,1.654780,0.947250,0.344590,0.887030,2.468730,0.195060,2.732240 -2022,1.477690,0.900480,1.998290,0.947740,0.366770,0.850180,2.605340,0.257510,2.797010 -2023,1.337030,1.108780,1.783250,1.010900,0.382070,0.897520,2.930800,0.261850,3.113280 -2024,1.807217,1.120767,1.951789,1.198553,0.446506,1.018170,2.772431,0.328414,3.282638 -2025,1.721987,1.101454,1.590906,1.094271,0.400232,0.854088,2.802444,0.316580,3.123186 -2026,2.042994,1.240576,1.697816,1.131739,0.311662,0.761514,2.951403,0.339554,3.375084 -2027,2.206338,1.345413,1.701938,1.092678,0.268560,0.750901,3.033981,0.389654,3.394222 -2028,2.334784,1.306726,1.644293,1.079716,0.244934,0.780610,3.015770,0.376410,3.293242 -2029,2.512054,1.324362,1.630497,1.073624,0.241361,0.828471,2.969723,0.398223,3.188257 -2030,2.602928,1.249770,1.778863,1.176084,0.196057,0.901304,2.868471,0.427727,3.244220 -2031,2.751464,1.181122,1.787377,1.214582,0.174589,0.966842,2.697150,0.491814,3.251625 -2032,3.253971,1.202948,1.845946,1.326644,0.193283,1.003485,2.864202,0.599109,3.415374 -2033,3.239930,1.226310,1.848220,1.274809,0.185470,1.094409,2.768513,0.497179,3.408022 -2034,3.205562,1.228781,1.843454,1.246807,0.187396,1.119302,2.743542,0.490385,3.366460 -2035,3.212188,1.185304,1.710656,1.197920,0.192078,1.137609,2.655394,0.484944,3.317858 -2036,3.132051,1.137773,1.642419,1.329532,0.199700,1.008565,2.550056,0.420684,2.939176 -2037,3.140409,1.144755,1.644400,1.326660,0.206521,1.047966,2.471244,0.416561,2.535467 -2038,2.716094,1.158549,1.590891,1.171483,0.197384,1.034278,2.439109,0.376668,2.472733 -2039,2.581804,1.105213,1.437783,1.016782,0.185696,1.041979,2.377795,0.354041,2.369297 -2040,2.550396,1.084304,1.440836,1.072407,0.191300,1.048653,2.495180,0.359027,2.332093 -2041,2.603838,1.106517,1.471445,1.044572,0.196422,1.087787,2.572892,0.366828,2.428086 -2042,2.774909,1.088461,1.400841,1.070091,0.208031,1.072913,2.560002,0.382864,2.434259 -2043,2.887747,1.150788,1.453301,1.107367,0.215927,1.047360,2.604740,0.405494,2.535568 -2044,2.989638,1.153446,1.539554,1.171576,0.195102,1.045559,2.585567,0.410800,2.570477 -2045,3.061960,1.184258,1.580606,1.259128,0.159384,1.036926,2.561460,0.439917,2.601221 -2046,3.099662,1.255747,1.648451,1.248391,0.161159,1.048170,2.573675,0.463651,2.629858 -2047,2.979110,1.305926,1.739527,1.285874,0.167061,1.030689,2.549114,0.466230,2.650514 -2048,2.855375,1.313169,1.745558,1.325776,0.168816,1.004289,2.529676,0.474269,2.607504 -2049,2.984837,1.298944,1.680766,1.364214,0.167950,1.004726,2.625262,0.425213,2.676126 -2050,2.927017,1.317363,1.685399,1.455590,0.156610,0.990377,2.632061,0.381755,2.658570 diff --git a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_demand_AEO_2025_LOG.csv b/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_demand_AEO_2025_LOG.csv deleted file mode 100644 index 6fb1bc5..0000000 --- a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_demand_AEO_2025_LOG.csv +++ /dev/null @@ -1,42 +0,0 @@ -year,East_North_Central,East_South_Central,Mid_Atlantic,Mountain,New_England,Pacific,South_Atlantic,West_North_Central,West_South_Central -2010,0.326000,0.567000,0.890000,0.641000,0.420000,0.989000,1.544000,0.124000,2.049000 -2011,0.395000,0.639000,0.965000,0.567000,0.456000,0.766000,1.671000,0.117000,2.188000 -2012,0.656000,0.797000,1.154000,0.663000,0.448000,1.045000,2.043000,0.171000,2.337000 -2013,0.475000,0.629000,1.066000,0.651000,0.372000,1.069000,1.883000,0.140000,2.075000 -2014,0.482860,0.671910,1.131480,0.648910,0.341750,1.073730,1.896250,0.108070,2.025670 -2015,0.713310,0.875420,1.236450,0.750920,0.397610,1.084430,2.330100,0.147160,2.434110 -2016,0.912140,0.961900,1.344500,0.765760,0.393180,0.917910,2.469830,0.191920,2.351840 -2017,0.810260,0.910900,1.196160,0.690330,0.371410,0.840900,2.475890,0.172190,2.063170 -2018,1.002570,0.994600,1.300060,0.760260,0.400410,0.871740,2.535530,0.189610,2.661310 -2019,1.185720,1.029820,1.451550,0.988210,0.338650,0.834000,2.790260,0.228310,2.928260 -2020,1.262780,1.047200,1.684220,0.954760,0.360850,0.847680,2.768040,0.247310,2.940390 -2021,1.082250,0.913910,1.654780,0.947250,0.344590,0.887030,2.468730,0.195060,2.732240 -2022,1.477690,0.900480,1.998290,0.947740,0.366770,0.850180,2.605340,0.257510,2.797010 -2023,1.337030,1.108780,1.783250,1.010900,0.382070,0.897520,2.930800,0.261850,3.113280 -2024,1.804559,1.121850,1.954733,1.202133,0.452001,1.018694,2.774692,0.330010,3.271640 -2025,1.748320,1.053865,1.720835,1.072385,0.400253,0.786553,2.728033,0.311531,3.139995 -2026,1.402841,0.872166,1.490830,0.829442,0.251434,0.687765,2.304525,0.199013,2.062922 -2027,1.498055,0.930938,1.467800,0.809352,0.211858,0.664922,2.249545,0.238384,2.132700 -2028,1.509668,0.907154,1.385326,0.783828,0.194398,0.704409,2.034196,0.246067,1.991213 -2029,1.496857,0.871673,1.412108,0.776658,0.192247,0.741716,1.846410,0.275980,1.856138 -2030,1.534067,0.808875,1.373499,0.770984,0.174559,0.772348,1.719123,0.437111,1.857033 -2031,1.384653,0.668381,1.197684,0.692458,0.157962,0.789767,1.451879,0.358888,1.640671 -2032,1.565061,0.734924,1.132102,0.847123,0.176228,0.815216,1.585336,0.451031,1.789956 -2033,1.333007,0.691406,1.060135,0.820230,0.166235,0.797631,1.433254,0.368381,1.579497 -2034,1.233128,0.634744,0.929961,0.759908,0.161400,0.756096,1.357102,0.308266,1.513954 -2035,1.111427,0.637080,0.893260,0.722170,0.165505,0.688957,1.358099,0.275428,1.327169 -2036,1.031229,0.549481,0.788534,0.653720,0.162913,0.632692,1.244350,0.261540,1.226203 -2037,0.854965,0.482770,0.586714,0.626503,0.137538,0.554879,1.119637,0.271243,1.115833 -2038,0.732474,0.435245,0.522783,0.527240,0.132298,0.494092,1.064564,0.254090,1.057815 -2039,0.758101,0.374653,0.549630,0.545790,0.134267,0.454257,0.993744,0.266654,1.017851 -2040,0.698537,0.296413,0.527879,0.509211,0.136717,0.467174,0.974679,0.271079,1.049425 -2041,0.664124,0.238602,0.488661,0.438115,0.137923,0.449188,0.975145,0.224717,0.994796 -2042,0.615075,0.237566,0.463385,0.395162,0.135460,0.437638,0.974492,0.206150,0.978935 -2043,0.606697,0.257154,0.473387,0.383094,0.138723,0.435116,0.925296,0.213672,0.991719 -2044,0.587082,0.247615,0.474389,0.395633,0.103130,0.442636,0.871767,0.236184,1.000719 -2045,0.607441,0.218179,0.444759,0.407519,0.087387,0.440998,0.779892,0.234486,1.004305 -2046,0.663438,0.209494,0.450912,0.364727,0.092547,0.441239,0.730188,0.237393,0.993623 -2047,0.687064,0.198955,0.450285,0.383422,0.092992,0.446543,0.693493,0.263399,1.047300 -2048,0.734405,0.201429,0.473709,0.416587,0.093810,0.465141,0.714790,0.265423,1.086109 -2049,0.789578,0.213645,0.480987,0.453987,0.095297,0.473856,0.700422,0.270487,1.095479 -2050,0.816287,0.217900,0.472205,0.486470,0.096417,0.488408,0.698386,0.278686,1.125022 diff --git a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_demand_AEO_2025_reference.csv b/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_demand_AEO_2025_reference.csv deleted file mode 100644 index 39fc701..0000000 --- a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_demand_AEO_2025_reference.csv +++ /dev/null @@ -1,42 +0,0 @@ -year,East_North_Central,East_South_Central,Mid_Atlantic,Mountain,New_England,Pacific,South_Atlantic,West_North_Central,West_South_Central -2010,0.326000,0.567000,0.890000,0.641000,0.420000,0.989000,1.544000,0.124000,2.049000 -2011,0.395000,0.639000,0.965000,0.567000,0.456000,0.766000,1.671000,0.117000,2.188000 -2012,0.656000,0.797000,1.154000,0.663000,0.448000,1.045000,2.043000,0.171000,2.337000 -2013,0.475000,0.629000,1.066000,0.651000,0.372000,1.069000,1.883000,0.140000,2.075000 -2014,0.482860,0.671910,1.131480,0.648910,0.341750,1.073730,1.896250,0.108070,2.025670 -2015,0.713310,0.875420,1.236450,0.750920,0.397610,1.084430,2.330100,0.147160,2.434110 -2016,0.912140,0.961900,1.344500,0.765760,0.393180,0.917910,2.469830,0.191920,2.351840 -2017,0.810260,0.910900,1.196160,0.690330,0.371410,0.840900,2.475890,0.172190,2.063170 -2018,1.002570,0.994600,1.300060,0.760260,0.400410,0.871740,2.535530,0.189610,2.661310 -2019,1.185720,1.029820,1.451550,0.988210,0.338650,0.834000,2.790260,0.228310,2.928260 -2020,1.262780,1.047200,1.684220,0.954760,0.360850,0.847680,2.768040,0.247310,2.940390 -2021,1.082250,0.913910,1.654780,0.947250,0.344590,0.887030,2.468730,0.195060,2.732240 -2022,1.477690,0.900480,1.998290,0.947740,0.366770,0.850180,2.605340,0.257510,2.797010 -2023,1.337030,1.108780,1.783250,1.010900,0.382070,0.897520,2.930800,0.261850,3.113280 -2024,1.803768,1.120752,1.947869,1.202134,0.451624,1.026979,2.771119,0.328229,3.278091 -2025,1.779150,1.098819,1.629397,1.095031,0.402601,0.855043,2.839481,0.333078,3.062861 -2026,1.709301,1.090066,1.645651,1.009045,0.307184,0.751345,2.802256,0.292994,2.831672 -2027,1.930932,1.157464,1.664455,1.012900,0.263452,0.718387,2.786452,0.347046,2.922580 -2028,2.042475,1.160019,1.601191,0.970603,0.243434,0.776476,2.676992,0.365627,2.856328 -2029,2.250298,1.124709,1.567711,0.961866,0.223449,0.824927,2.548651,0.403164,2.763967 -2030,2.271644,1.037678,1.638802,1.036249,0.172437,0.894584,2.418587,0.456933,2.771827 -2031,2.249849,0.989105,1.537363,1.062644,0.193628,0.947843,2.196743,0.434738,2.681658 -2032,2.753094,1.045986,1.631743,1.131738,0.210223,1.000090,2.304419,0.589867,2.831487 -2033,2.610210,0.955824,1.585095,0.945356,0.202380,0.930929,2.088257,0.553112,2.650223 -2034,2.315766,0.866626,1.527352,0.872272,0.189251,0.947975,1.922410,0.410186,2.548273 -2035,2.128180,0.824893,1.546738,0.893621,0.192515,0.933309,1.878592,0.374010,2.161885 -2036,1.923508,0.771852,1.530112,0.882123,0.197367,0.888036,1.926551,0.346125,1.876698 -2037,1.718799,0.714015,1.373293,0.846004,0.189569,0.825748,1.718812,0.332811,1.742069 -2038,1.530699,0.654886,1.208284,0.834513,0.194608,0.784750,1.718832,0.307617,1.661109 -2039,1.492246,0.603034,1.129269,0.853311,0.183550,0.737564,1.680835,0.271675,1.621889 -2040,1.538328,0.603428,1.131776,0.863450,0.191371,0.723354,1.617794,0.272217,1.688687 -2041,1.606427,0.615171,1.157560,0.862811,0.192375,0.729275,1.619307,0.285775,1.746875 -2042,1.636847,0.615970,1.098378,0.915820,0.203848,0.673652,1.598931,0.264481,1.757097 -2043,1.703603,0.590668,1.162631,0.912592,0.207948,0.706872,1.554882,0.265405,1.737841 -2044,1.783943,0.604031,1.205421,0.939069,0.183532,0.684177,1.479005,0.239437,1.767898 -2045,1.743539,0.610125,1.299167,0.952057,0.141631,0.662889,1.469072,0.278295,1.725534 -2046,1.815236,0.608258,1.281461,0.916862,0.157335,0.618836,1.423282,0.288586,1.737410 -2047,1.637457,0.593526,1.377790,0.906929,0.158988,0.640318,1.396680,0.303452,1.773730 -2048,1.609448,0.605255,1.420297,0.957831,0.162292,0.637655,1.344266,0.320825,1.755996 -2049,1.616478,0.636592,1.345600,0.998673,0.174212,0.629154,1.313927,0.320191,1.718013 -2050,1.626209,0.643452,1.342685,1.036890,0.165085,0.621188,1.359366,0.331866,1.536503 diff --git a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_tot_demand_AEO_2025_HOG.csv b/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_tot_demand_AEO_2025_HOG.csv deleted file mode 100644 index cf918c8..0000000 --- a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_tot_demand_AEO_2025_HOG.csv +++ /dev/null @@ -1,42 +0,0 @@ -year,East_North_Central,East_South_Central,Mid_Atlantic,Mountain,New_England,Pacific,South_Atlantic,West_North_Central,West_South_Central -2010,3.358270,1.354120,2.642740,4.756300,0.858180,2.707130,2.864660,1.549810,5.379280 -2011,3.525400,1.416260,2.719850,4.793510,0.915350,2.548560,2.934920,1.555520,5.529350 -2012,3.547680,1.540450,2.770950,4.903690,0.879080,2.803610,3.280120,1.491080,5.730930 -2013,3.818100,1.455900,2.891800,4.972180,0.859500,2.896840,3.261050,1.645920,5.577360 -2014,4.045160,1.544660,3.120450,5.000560,0.857730,2.784840,3.326570,1.680470,5.646740 -2015,3.886720,1.696390,3.099140,5.255470,0.905920,2.763970,3.663950,1.574980,5.951670 -2016,3.920960,1.746690,3.156140,1.581650,0.856640,2.775400,3.735140,1.638640,5.803450 -2017,3.997110,1.767730,3.113560,1.576460,0.879540,2.827700,3.888780,1.729600,5.922210 -2018,4.395240,1.912480,3.316360,1.676030,0.930980,2.952350,4.067650,1.846240,6.697610 -2019,4.665440,1.962090,3.507680,1.985240,0.884970,2.949310,4.325760,1.972420,7.134520 -2020,4.578500,1.967890,3.615310,1.900720,0.849800,2.847720,4.221530,1.915620,7.066120 -2021,4.418460,1.844550,3.665610,1.921410,0.878470,2.947450,3.971680,1.893200,6.865970 -2022,4.940080,1.892740,4.044720,1.914710,0.923150,2.857150,4.159860,1.998680,7.156390 -2023,4.648730,1.861390,3.709180,1.734920,0.914370,2.869450,4.158050,1.877740,6.910270 -2024,4.966578,2.097415,3.906965,2.155910,0.957036,2.973608,4.376607,2.048244,7.426676 -2025,5.179120,2.111273,3.654048,2.100823,0.937966,2.843258,4.469378,2.077749,7.503117 -2026,5.488719,2.230853,3.759808,2.131084,0.860033,2.739021,4.646142,2.125013,7.636079 -2027,5.671466,2.335734,3.774266,2.100113,0.818995,2.732933,4.738679,2.190878,7.659956 -2028,5.789507,2.291502,3.716908,2.087887,0.795159,2.772114,4.717777,2.173376,7.557886 -2029,5.954311,2.306812,3.712479,2.086488,0.790301,2.829799,4.672873,2.195643,7.466064 -2030,6.039167,2.233391,3.873317,2.194306,0.744455,2.921344,4.578413,2.226819,7.538048 -2031,6.186546,2.170599,3.884992,2.241075,0.722228,2.999857,4.417633,2.296895,7.592106 -2032,6.693543,2.200978,3.944881,2.365347,0.741138,3.048926,4.601673,2.414316,7.769639 -2033,6.668369,2.200232,3.943127,2.325531,0.733747,3.145908,4.486273,2.318362,7.632854 -2034,6.625295,2.237109,3.933705,2.308635,0.735347,3.176948,4.505866,2.316933,7.758178 -2035,6.627602,2.198426,3.800751,2.271489,0.739885,3.205086,4.430108,2.316072,7.729087 -2036,6.545062,2.156050,3.739552,2.397209,0.747582,3.013469,4.338734,2.257230,7.367331 -2037,6.556759,2.167767,3.743082,2.404191,0.757997,3.038831,4.274085,2.263292,6.954051 -2038,6.133348,2.189104,3.694221,2.272732,0.749867,3.078229,4.256666,2.231797,6.922351 -2039,6.001094,2.141802,3.544951,2.137090,0.738831,3.124984,4.208876,2.216016,6.825901 -2040,5.973637,2.126414,3.550549,2.205422,0.745195,3.147804,4.339911,2.230641,6.780476 -2041,6.025984,2.154529,3.586475,2.189955,0.750859,3.201372,4.432344,2.242017,6.878246 -2042,6.201727,2.144847,3.519784,2.228513,0.762990,3.202829,4.433652,2.267177,6.934600 -2043,6.316656,2.215720,3.577761,2.279858,0.771564,3.196146,4.492250,2.304414,7.085598 -2044,6.423592,2.223858,3.673051,2.359413,0.751563,3.216992,4.486453,2.318251,7.117551 -2045,6.500850,2.262689,3.723683,2.461398,0.716803,3.226556,4.477860,2.359171,7.170787 -2046,6.540277,2.342593,3.795890,2.465548,0.718965,3.255445,4.501936,2.392810,7.244801 -2047,6.419639,2.399555,3.895167,2.517160,0.724840,3.255843,4.490632,2.406723,7.288305 -2048,6.308069,2.390973,3.907527,2.572379,0.726701,3.249705,4.457742,2.422065,7.159330 -2049,6.439577,2.408647,3.848761,2.626165,0.725793,3.269576,4.593865,2.387223,7.386389 -2050,6.387714,2.433508,3.860977,2.731677,0.713332,3.273041,4.613367,2.353932,7.393847 diff --git a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_tot_demand_AEO_2025_LOG.csv b/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_tot_demand_AEO_2025_LOG.csv deleted file mode 100644 index a91f2c5..0000000 --- a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_tot_demand_AEO_2025_LOG.csv +++ /dev/null @@ -1,42 +0,0 @@ -year,East_North_Central,East_South_Central,Mid_Atlantic,Mountain,New_England,Pacific,South_Atlantic,West_North_Central,West_South_Central -2010,3.358270,1.354120,2.642740,4.756300,0.858180,2.707130,2.864660,1.549810,5.379280 -2011,3.525400,1.416260,2.719850,4.793510,0.915350,2.548560,2.934920,1.555520,5.529350 -2012,3.547680,1.540450,2.770950,4.903690,0.879080,2.803610,3.280120,1.491080,5.730930 -2013,3.818100,1.455900,2.891800,4.972180,0.859500,2.896840,3.261050,1.645920,5.577360 -2014,4.045160,1.544660,3.120450,5.000560,0.857730,2.784840,3.326570,1.680470,5.646740 -2015,3.886720,1.696390,3.099140,5.255470,0.905920,2.763970,3.663950,1.574980,5.951670 -2016,3.920960,1.746690,3.156140,1.581650,0.856640,2.775400,3.735140,1.638640,5.803450 -2017,3.997110,1.767730,3.113560,1.576460,0.879540,2.827700,3.888780,1.729600,5.922210 -2018,4.395240,1.912480,3.316360,1.676030,0.930980,2.952350,4.067650,1.846240,6.697610 -2019,4.665440,1.962090,3.507680,1.985240,0.884970,2.949310,4.325760,1.972420,7.134520 -2020,4.578500,1.967890,3.615310,1.900720,0.849800,2.847720,4.221530,1.915620,7.066120 -2021,4.418460,1.844550,3.665610,1.921410,0.878470,2.947450,3.971680,1.893200,6.865970 -2022,4.940080,1.892740,4.044720,1.914710,0.923150,2.857150,4.159860,1.998680,7.156390 -2023,4.648730,1.861390,3.709180,1.734920,0.914370,2.869450,4.158050,1.877740,6.910270 -2024,4.964130,2.098575,3.909995,2.159559,0.962553,2.974449,4.378995,2.049968,7.414999 -2025,5.087374,2.031063,3.749795,2.058202,0.930546,2.728772,4.343349,2.005418,7.370770 -2026,4.620926,1.784934,3.475330,1.781902,0.781939,2.557914,3.891988,1.842845,5.958117 -2027,4.707423,1.835414,3.444869,1.754856,0.742323,2.526092,3.828328,1.880801,6.003426 -2028,4.696098,1.783213,3.364414,1.713990,0.725009,2.556307,3.591180,1.881701,5.845942 -2029,4.647316,1.719558,3.386134,1.696235,0.720008,2.585802,3.361863,1.897199,5.699160 -2030,4.641392,1.629336,3.335698,1.677637,0.699351,2.614153,3.190711,2.038857,5.668550 -2031,4.456717,1.470770,3.142804,1.608598,0.679255,2.628891,2.885270,1.944253,5.419774 -2032,4.594091,1.521027,3.056186,1.763594,0.694852,2.637849,2.987373,2.025953,5.548148 -2033,4.325788,1.469214,2.965106,1.746244,0.682768,2.634555,2.804255,1.940535,5.332105 -2034,4.196589,1.399418,2.821101,1.690408,0.676451,2.577221,2.729338,1.874597,5.265339 -2035,4.056654,1.387416,2.774165,1.656034,0.679203,2.517057,2.722006,1.840279,5.066918 -2036,3.955245,1.295392,2.661531,1.584508,0.675569,2.446003,2.598980,1.820973,4.959162 -2037,3.762507,1.225727,2.454186,1.567165,0.649520,2.364758,2.459701,1.831730,4.837954 -2038,3.630200,1.169368,2.387821,1.473797,0.645287,2.291434,2.406118,1.815282,4.794869 -2039,3.649924,1.127985,2.414410,1.487857,0.653401,2.252511,2.401801,1.826088,4.765383 -2040,3.580663,1.060108,2.393730,1.457779,0.655786,2.281605,2.404084,1.829875,4.802187 -2041,3.536101,1.008874,2.350435,1.393410,0.656713,2.238770,2.420143,1.785332,4.750147 -2042,3.480375,1.021078,2.321419,1.361234,0.654128,2.244678,2.433650,1.763587,4.739210 -2043,3.467469,1.043947,2.332030,1.362778,0.657176,2.247817,2.402768,1.775419,4.753344 -2044,3.439527,1.045289,2.330870,1.392945,0.621498,2.278843,2.371214,1.802037,4.766046 -2045,3.448897,1.024428,2.309155,1.404418,0.605406,2.285449,2.302510,1.802026,4.775317 -2046,3.494394,1.023384,2.310539,1.375992,0.609633,2.290656,2.268174,1.805467,4.763526 -2047,3.507108,1.011038,2.304937,1.401801,0.609062,2.294402,2.242798,1.829551,4.806602 -2048,3.543561,1.015026,2.319615,1.456912,0.608704,2.322879,2.267759,1.830128,4.840192 -2049,3.585941,1.036841,2.317298,1.501403,0.608873,2.333663,2.260479,1.837048,4.846149 -2050,3.602013,1.030486,2.297522,1.540658,0.608951,2.361397,2.244589,1.850036,4.884940 diff --git a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_tot_demand_AEO_2025_reference.csv b/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_tot_demand_AEO_2025_reference.csv deleted file mode 100644 index 98711b3..0000000 --- a/aeo_updates/natural_gas_price_regression/outputs of alpha regression/ng_tot_demand_AEO_2025_reference.csv +++ /dev/null @@ -1,42 +0,0 @@ -year,East_North_Central,East_South_Central,Mid_Atlantic,Mountain,New_England,Pacific,South_Atlantic,West_North_Central,West_South_Central -2010,3.358270,1.354120,2.642740,4.756300,0.858180,2.707130,2.864660,1.549810,5.379280 -2011,3.525400,1.416260,2.719850,4.793510,0.915350,2.548560,2.934920,1.555520,5.529350 -2012,3.547680,1.540450,2.770950,4.903690,0.879080,2.803610,3.280120,1.491080,5.730930 -2013,3.818100,1.455900,2.891800,4.972180,0.859500,2.896840,3.261050,1.645920,5.577360 -2014,4.045160,1.544660,3.120450,5.000560,0.857730,2.784840,3.326570,1.680470,5.646740 -2015,3.886720,1.696390,3.099140,5.255470,0.905920,2.763970,3.663950,1.574980,5.951670 -2016,3.920960,1.746690,3.156140,1.581650,0.856640,2.775400,3.735140,1.638640,5.803450 -2017,3.997110,1.767730,3.113560,1.576460,0.879540,2.827700,3.888780,1.729600,5.922210 -2018,4.395240,1.912480,3.316360,1.676030,0.930980,2.952350,4.067650,1.846240,6.697610 -2019,4.665440,1.962090,3.507680,1.985240,0.884970,2.949310,4.325760,1.972420,7.134520 -2020,4.578500,1.967890,3.615310,1.900720,0.849800,2.847720,4.221530,1.915620,7.066120 -2021,4.418460,1.844550,3.665610,1.921410,0.878470,2.947450,3.971680,1.893200,6.865970 -2022,4.940080,1.892740,4.044720,1.914710,0.923150,2.857150,4.159860,1.998680,7.156390 -2023,4.648730,1.861390,3.709180,1.734920,0.914370,2.869450,4.158050,1.877740,6.910270 -2024,4.963340,2.097466,3.903117,2.159530,0.962173,2.982530,4.375371,2.048189,7.421416 -2025,5.156418,2.089903,3.661196,2.086673,0.935287,2.807245,4.476222,2.129834,7.237640 -2026,5.073037,2.052864,3.692177,1.988881,0.848885,2.694449,4.465045,2.030126,6.971332 -2027,5.310193,2.121335,3.721632,1.999269,0.807334,2.667008,4.458486,2.096073,7.088854 -2028,5.422043,2.120489,3.668039,1.959295,0.788003,2.730486,4.351885,2.115744,7.020803 -2029,5.623929,2.085303,3.644482,1.954141,0.767319,2.789246,4.227018,2.153275,6.950130 -2030,5.640369,2.000149,3.723123,2.035256,0.715819,2.874931,4.102344,2.207593,6.981818 -2031,5.612779,1.956317,3.624680,2.067181,0.736064,2.935515,3.890681,2.187774,6.930852 -2032,6.106751,2.020668,3.716164,2.145240,0.752671,2.998962,4.012863,2.346345,7.100438 -2033,5.944864,1.933828,3.659340,1.967468,0.746767,2.933176,3.806603,2.309086,6.931971 -2034,5.637051,1.848481,3.596563,1.903266,0.733941,2.945720,3.651626,2.168169,6.835082 -2035,5.440754,1.810854,3.614042,1.936649,0.737544,2.938173,3.610039,2.135197,6.454259 -2036,5.228507,1.762085,3.597095,1.936809,0.742872,2.895602,3.659100,2.114645,6.180867 -2037,5.027087,1.709159,3.445315,1.914083,0.736183,2.844030,3.464379,2.105235,6.054377 -2038,4.835952,1.657892,3.288879,1.916980,0.742570,2.813577,3.479662,2.094151,6.006368 -2039,4.804575,1.614115,3.218215,1.950361,0.732698,2.782817,3.457128,2.066591,5.997215 -2040,4.854189,1.621953,3.226690,1.974375,0.741344,2.780822,3.408876,2.076052,6.087286 -2041,4.917473,1.639145,3.255468,1.985551,0.742691,2.793409,3.422421,2.094103,6.161439 -2042,4.942109,1.644759,3.196219,2.049288,0.754417,2.748004,3.412856,2.077646,6.188160 -2043,5.006926,1.624161,3.258941,2.057952,0.758587,2.793068,3.378010,2.081888,6.185266 -2044,5.082698,1.641373,3.302742,2.095899,0.734295,2.783741,3.311311,2.062417,6.232446 -2045,5.035407,1.651322,3.395932,2.119609,0.692461,2.772924,3.316263,2.105197,6.209975 -2046,5.099153,1.651759,3.377838,2.093664,0.707880,2.733654,3.285728,2.118033,6.231880 -2047,4.917622,1.640283,3.474578,2.094466,0.709487,2.770767,3.267011,2.137938,6.275684 -2048,4.895315,1.657869,3.520399,2.157986,0.713195,2.782060,3.225357,2.163854,6.278858 -2049,4.902303,1.695295,3.449674,2.213768,0.725422,2.792679,3.207612,2.172264,6.269968 -2050,4.926717,1.705988,3.453480,2.264314,0.716690,2.801707,3.263909,2.197461,6.099909 diff --git a/aeo_updates/natural_gas_price_regression/outputs of beta regression/beta_regression_summary.csv b/aeo_updates/natural_gas_price_regression/outputs of beta regression/beta_regression_summary.csv deleted file mode 100644 index 6870ff7..0000000 --- a/aeo_updates/natural_gas_price_regression/outputs of beta regression/beta_regression_summary.csv +++ /dev/null @@ -1,11 +0,0 @@ -scope,region,beta,r2,r2_full,n_obs -national,ALL,0.080017,0.727887,0.847220,1701 -regional,East_North_Central,0.033492,0.037937,0.842476,189 -regional,East_South_Central,0.139571,0.135091,0.853755,189 -regional,Mid_Atlantic,0.151497,0.191732,0.850741,189 -regional,Mountain,0.670353,0.575211,0.854568,189 -regional,New_England,3.875323,0.587849,0.867459,189 -regional,Pacific,1.341567,0.669742,0.855878,189 -regional,South_Atlantic,0.175498,0.407799,0.840697,189 -regional,West_North_Central,0.856915,0.452614,0.852631,189 -regional,West_South_Central,-0.054900,0.048903,0.721250,189 diff --git a/aeo_updates/natural_gas_price_regression/outputs of beta regression/cd_beta0.csv b/aeo_updates/natural_gas_price_regression/outputs of beta regression/cd_beta0.csv deleted file mode 100644 index 755bebb..0000000 --- a/aeo_updates/natural_gas_price_regression/outputs of beta regression/cd_beta0.csv +++ /dev/null @@ -1,10 +0,0 @@ -*cendiv,value -East_North_Central,0.033492 -East_South_Central,0.139571 -Mid_Atlantic,0.151497 -Mountain,0.670353 -New_England,3.875323 -Pacific,1.341567 -South_Atlantic,0.175498 -West_North_Central,0.856915 -West_South_Central,-0.054900 diff --git a/aeo_updates/natural_gas_price_regression/outputs of beta regression/national_beta.csv b/aeo_updates/natural_gas_price_regression/outputs of beta regression/national_beta.csv deleted file mode 100644 index abd889b..0000000 --- a/aeo_updates/natural_gas_price_regression/outputs of beta regression/national_beta.csv +++ /dev/null @@ -1,2 +0,0 @@ -beta -0.080017 diff --git a/aeo_updates/nrel_request_AEO2025.xlsx b/aeo_updates/nrel_request_AEO2026.xlsx similarity index 54% rename from aeo_updates/nrel_request_AEO2025.xlsx rename to aeo_updates/nrel_request_AEO2026.xlsx index 1d2ccc5..67c5a32 100644 Binary files a/aeo_updates/nrel_request_AEO2025.xlsx and b/aeo_updates/nrel_request_AEO2026.xlsx differ diff --git a/aeo_updates/temperature_gas_price_adj_regression/.gitignore b/aeo_updates/temperature_gas_price_adj_regression/.gitignore new file mode 100644 index 0000000..e4cf393 --- /dev/null +++ b/aeo_updates/temperature_gas_price_adj_regression/.gitignore @@ -0,0 +1,10 @@ +# Python cache +__pycache__/ +*.pyc + +# Notebook checkpoints +.ipynb_checkpoints/ + +# Outputs and intermediate inputs (reproducible) +outputs/ +inputs/gasreg_regression_data.csv \ No newline at end of file diff --git a/aeo_updates/temperature_gas_price_adj_regression/README.md b/aeo_updates/temperature_gas_price_adj_regression/README.md new file mode 100644 index 0000000..2abd5de --- /dev/null +++ b/aeo_updates/temperature_gas_price_adj_regression/README.md @@ -0,0 +1,14 @@ +# Overview +The notebooks in this folder create the temperature-based gas price adjustment regression parameters used in ReEDS. Specifically, for each gas price region, or gasreg (described below), we apply ordinary least squares regression with monthly fixed effects to regress daily heating and cooling degree days on the log of multiplicative differences between daily natural gas spot prices and their annual average. The regional regression models are trained on data spanning 2014 to 2023 and tested on 2024 data. Gasreg degree days are calculated by first calculating state-level degree days using [hourly average state temperatures](https://zenodo.org/records/19390491) and then aggregating them via population-weighted average. + +Gasregs are specific to the ReEDS model and are based on a mix of [census divisions](https://www2.census.gov/geo/pdfs/maps-data/maps/reference/us_regdiv.pdf), EIA-NEMS natural gas regions used to report regional flows and capacity ([EIA Natural Gas Market Module of the National Energy Modeling System: Model Documentation 2025, Figure 2.5](https://www.eia.gov/outlooks/aeo/nems/documentation/ngmm/pdf/NGMM_AEO2025.pdf)), and EIA-NEMS Natural Gas-Electricity Market Module regions ([EIA Natural Gas Market Module of the National Energy Modeling System: Model Documentation 2025, Figure 2.7](https://www.eia.gov/outlooks/aeo/nems/documentation/ngmm/pdf/NGMM_AEO2025.pdf)). + +# Workflow +To reproduce the ReEDS inputs, the following notebooks should be run in the order listed here: +- `write_regression_input_data.ipynb` writes the daily heating/cooling degree day and spot price data used to fit the regression models. Creates the intermediate input file `inputs/gasreg_regression_data.csv`. Note that the file containing the spot price data used in this notebook is not accessible to people external to the lab. +- `calculate_regression_params.ipynb` fits the regression models and exports the corresponding parameters (HDD/CDD coefficients, intercepts, and monthly fixed effects). Creates the output (ReEDS input) file `outputs/gasreg_price_adj_regression_params.csv`. + +Optionally, the `inspect_hub_locations.ipynb` can also be used to identify the gasreg that each natural gas hub overlaps with geographically. This notebook is not required to reproduce the ReEDS inputs, but was used initially to associate gasregs with daily gas prices and is here for documentation purposes. Note that the file containing the hub locations used in this notebook is not accessible to people external to the lab. + +# Outputs +The output generated by these notebooks is `outputs/gasreg_price_adj_regression_params.csv`. To be used in ReEDS, this file should be copied to the `inputs/fuelprices` folder in the ReEDS directory (with the file name unchanged). diff --git a/aeo_updates/temperature_gas_price_adj_regression/calculate_regression_params.ipynb b/aeo_updates/temperature_gas_price_adj_regression/calculate_regression_params.ipynb new file mode 100644 index 0000000..f2c46cb --- /dev/null +++ b/aeo_updates/temperature_gas_price_adj_regression/calculate_regression_params.ipynb @@ -0,0 +1,360 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "d70f4c36-7194-4615-bf6a-672108e3cba6", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "import statsmodels.api as sm\n", + "from pathlib import Path\n", + "from sklearn.metrics import mean_squared_error, r2_score" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "8a8f5f3b-63c4-4d27-901d-3837caa4e83a", + "metadata": {}, + "outputs": [], + "source": [ + "def run_ols_regression(gasreg, data):\n", + " \"\"\"\n", + " Fit an OLS regression model using daily HDD/CDDs for a given gasreg\n", + " as independent variables and daily multiplicative price differences\n", + " from the annual average price for that gasreg.\n", + " \"\"\"\n", + " data['month'] = data.index.get_level_values('month')\n", + " month_dummies = (\n", + " pd.get_dummies(data['month'], prefix='month')\n", + " .drop(columns='month_5')\n", + " .astype(float)\n", + " )\n", + " \n", + " degree_day_columns = [f'{gasreg}_{dd_var}' for dd_var in ['cdd', 'hdd']]\n", + " temperature_variable = data[degree_day_columns].astype(float)\n", + " \n", + " X = pd.concat([temperature_variable, month_dummies], axis=1)\n", + " X = sm.add_constant(X)\n", + " y = data[f\"{gasreg}_price_diff\"].values.astype(float)\n", + " \n", + " # The lines below represent an ordinary least-squares regression using a\n", + " # heteroskedasticity- and autocorrelation-consistent (HAC) estimator,\n", + " # which ensures that the standard errors calculated in the regression\n", + " # are robust to heteroskedastic and autocorrelated residuals (both\n", + " # common when working with time series data).\n", + " # The maxlags value represents the maximum number of timesteps\n", + " # (in this case days) across which the estimator adjusts for auto-\n", + " # correlation. The value is calculated using the Stock and Watson rule-of-thumb:\n", + " # number of lags = 0.75 * (number of observations)**(1/3)\n", + " # (see https://www.econometrics-with-r.org/15.4-hac-standard-errors.html)\n", + " maxlags = int(round(0.75 * len(data)**(1/3)))\n", + " model = sm.OLS(y, X).fit(cov_type='HAC', cov_kwds={'maxlags': maxlags})\n", + "\n", + " return model\n", + "\n", + "def apply_regression_model(gasreg, model, data):\n", + " \"\"\"\n", + " Apply an OLS regression model to predict daily multiplicative price\n", + " differences from daily HDD/CDDs for a given gasreg.\n", + " \"\"\"\n", + " data['month'] = data.index.get_level_values('month')\n", + " month_dummies = (\n", + " pd.get_dummies(data['month'], prefix='month')\n", + " .drop(columns='month_5')\n", + " .astype(float)\n", + " )\n", + " \n", + " degree_day_columns = [f'{gasreg}_{dd_var}' for dd_var in ['cdd', 'hdd']]\n", + " temperature_variable = data[degree_day_columns].astype(float)\n", + "\n", + " X_test = pd.concat([temperature_variable, month_dummies], axis=1)\n", + " X_test = sm.add_constant(X_test)\n", + " y_pred = model.predict(X_test)\n", + "\n", + " return y_pred" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "541c9d4f-8d35-4ed5-bfa7-16783de4d23d", + "metadata": {}, + "outputs": [], + "source": [ + "# Get daily HDD/CDDs and prices for each gasreg\n", + "gasreg_data = pd.read_csv(\n", + " Path('inputs', 'gasreg_regression_data.csv'),\n", + " index_col=['year', 'month', 'day']\n", + ")\n", + "gasreg_degree_days = (\n", + " gasreg_data[[col for col in gasreg_data.columns if 'dd' in col]]\n", + " .copy()\n", + ")\n", + "gasreg_prices = (\n", + " gasreg_data[[col for col in gasreg_data.columns if 'price' in col]]\n", + " .copy()\n", + ")\n", + "gasreg_prices.columns = [col.replace('_price', '') for col in gasreg_prices.columns]\n", + "\n", + "# Calculate average annual prices for each gasreg\n", + "# and the daily deviations (log of the multiplicative difference)\n", + "# from the annual average price\n", + "gasreg_annual_average_prices = (\n", + " gasreg_prices.groupby(gasreg_prices.index.get_level_values('year'))\n", + " .transform('mean')\n", + ")\n", + "gasreg_log_mult_diffs = np.log(gasreg_prices) - np.log(gasreg_annual_average_prices)\n", + "\n", + "# Replace prices with multiplicative price differences\n", + "gasreg_data = pd.concat([\n", + " gasreg_data.loc[:, ~gasreg_data.columns.str.contains('_price')],\n", + " gasreg_log_mult_diffs.add_suffix('_price_diff')\n", + "], axis=1)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "1d27da30-c971-4789-bced-dd04b43a79cb", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "California\n", + "Train R2: 0.185\n", + "Train Adj R2: 0.183\n", + "Test R2: 0.277\n", + "Test RMSE: 0.321\n", + "Standard deviation of test data: 0.377\n", + "Coefficients and p-values:\n", + "{'CDD': '0.048 (p ≈ 0.0)', 'HDD': '0.032 (p ≈ 0.0)'}\n", + "\n", + "East_North_Central\n", + "Train R2: 0.132\n", + "Train Adj R2: 0.129\n", + "Test R2: 0.21\n", + "Test RMSE: 0.323\n", + "Standard deviation of test data: 0.363\n", + "Coefficients and p-values:\n", + "{'CDD': '0.031 (p ≈ 0.0)', 'HDD': '0.022 (p ≈ 0.0)'}\n", + "\n", + "East_South_Central\n", + "Train R2: 0.147\n", + "Train Adj R2: 0.144\n", + "Test R2: 0.197\n", + "Test RMSE: 0.299\n", + "Standard deviation of test data: 0.334\n", + "Coefficients and p-values:\n", + "{'CDD': '0.014 (p ≈ 0.028)', 'HDD': '0.018 (p ≈ 0.0)'}\n", + "\n", + "Mid_Atlantic\n", + "Train R2: 0.419\n", + "Train Adj R2: 0.417\n", + "Test R2: 0.495\n", + "Test RMSE: 0.316\n", + "Standard deviation of test data: 0.445\n", + "Coefficients and p-values:\n", + "{'CDD': '0.096 (p ≈ 0.0)', 'HDD': '0.052 (p ≈ 0.0)'}\n", + "\n", + "Mountain\n", + "Train R2: 0.183\n", + "Train Adj R2: 0.18\n", + "Test R2: 0.305\n", + "Test RMSE: 0.373\n", + "Standard deviation of test data: 0.447\n", + "Coefficients and p-values:\n", + "{'CDD': '0.042 (p ≈ 0.001)', 'HDD': '0.027 (p ≈ 0.0)'}\n", + "\n", + "New_England\n", + "Train R2: 0.59\n", + "Train Adj R2: 0.588\n", + "Test R2: 0.635\n", + "Test RMSE: 0.38\n", + "Standard deviation of test data: 0.628\n", + "Coefficients and p-values:\n", + "{'CDD': '0.129 (p ≈ 0.0)', 'HDD': '0.071 (p ≈ 0.0)'}\n", + "\n", + "Northwest\n", + "Train R2: 0.232\n", + "Train Adj R2: 0.229\n", + "Test R2: 0.33\n", + "Test RMSE: 0.408\n", + "Standard deviation of test data: 0.499\n", + "Coefficients and p-values:\n", + "{'CDD': '0.054 (p ≈ 0.0)', 'HDD': '0.041 (p ≈ 0.0)'}\n", + "\n", + "South_Atlantic\n", + "Train R2: 0.266\n", + "Train Adj R2: 0.263\n", + "Test R2: 0.297\n", + "Test RMSE: 0.318\n", + "Standard deviation of test data: 0.38\n", + "Coefficients and p-values:\n", + "{'CDD': '0.051 (p ≈ 0.0)', 'HDD': '0.045 (p ≈ 0.0)'}\n", + "\n", + "Southwest\n", + "Train R2: 0.171\n", + "Train Adj R2: 0.168\n", + "Test R2: 0.286\n", + "Test RMSE: 0.387\n", + "Standard deviation of test data: 0.459\n", + "Coefficients and p-values:\n", + "{'CDD': '0.03 (p ≈ 0.001)', 'HDD': '0.028 (p ≈ 0.0)'}\n", + "\n", + "West_North_Central\n", + "Train R2: 0.177\n", + "Train Adj R2: 0.174\n", + "Test R2: 0.3\n", + "Test RMSE: 0.334\n", + "Standard deviation of test data: 0.399\n", + "Coefficients and p-values:\n", + "{'CDD': '0.028 (p ≈ 0.0)', 'HDD': '0.03 (p ≈ 0.0)'}\n", + "\n", + "West_South_Central\n", + "Train R2: 0.116\n", + "Train Adj R2: 0.113\n", + "Test R2: 0.226\n", + "Test RMSE: 0.289\n", + "Standard deviation of test data: 0.328\n", + "Coefficients and p-values:\n", + "{'CDD': '0.025 (p ≈ 0.0)', 'HDD': '0.029 (p ≈ 0.002)'}\n", + "\n" + ] + } + ], + "source": [ + "# Fit the regression model using data from 2014-2023 and test on 2024\n", + "train_years = [2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023]\n", + "test_years = [2024]\n", + "\n", + "gasreg_model_params = {}\n", + "for gasreg in gasreg_prices.columns:\n", + " data = gasreg_data.filter(like=gasreg)\n", + " train_data = data.loc[data.index.get_level_values('year').isin(train_years)].copy()\n", + " model = run_ols_regression(gasreg, train_data)\n", + "\n", + " # Save model parameters (coefficients, intercepts, and fixed effects)\n", + " gasreg_model_params[gasreg] = (\n", + " model.params\n", + " .rename({\n", + " f\"{gasreg}_cdd\": 'cdd',\n", + " f\"{gasreg}_hdd\": 'hdd'\n", + " })\n", + " )\n", + " \n", + " # Calculate and print relevant statistics to assess model effectiveness\n", + " test_data = data.loc[~data.index.get_level_values('year').isin(train_years)].copy()\n", + " y_pred = apply_regression_model(gasreg, model, test_data)\n", + " y_true = test_data[f\"{gasreg}_price_diff\"]\n", + " \n", + " test_r2 = r2_score(y_true, y_pred)\n", + " test_rmse = np.sqrt(mean_squared_error(y_true, y_pred)) \n", + "\n", + " print(f\"{gasreg}\")\n", + " print(f\"Train R2: {model.rsquared.round(3)}\")\n", + " print(f\"Train Adj R2: {model.rsquared_adj.round(3)}\")\n", + " print(f\"Test R2: {test_r2.round(3)}\")\n", + " print(f\"Test RMSE: {test_rmse.round(3)}\")\n", + " print(f\"Standard deviation of test data: {round(np.std(y_true), 3)}\")\n", + "\n", + " dd_coef_dict = {}\n", + " dd_coef_dict['CDD'] = f\"{model.params.iloc[1].round(3)} (p ≈ {model.pvalues.iloc[1].round(3)})\"\n", + " dd_coef_dict['HDD'] = f\"{model.params.iloc[2].round(3)} (p ≈ {model.pvalues.iloc[2].round(3)})\"\n", + " print(f\"Coefficients and p-values:\\n{dd_coef_dict}\")\n", + " print('') " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "d61d11ac-641d-4bae-b642-ff176bc97238", + "metadata": {}, + "outputs": [], + "source": [ + "# Reformat regression parameters for ReEDS\n", + "regression_params = (\n", + " pd.concat(gasreg_model_params, axis=1)\n", + " .rename({\n", + " 'const': 'alpha',\n", + " 'cdd': 'beta_CDD',\n", + " 'hdd': 'beta_HDD',\n", + " 'month_1': 'alpha_JAN',\n", + " 'month_2': 'alpha_FEB',\n", + " 'month_3': 'alpha_MAR',\n", + " 'month_4': 'alpha_APR',\n", + " 'month_6': 'alpha_JUN',\n", + " 'month_7': 'alpha_JUL',\n", + " 'month_8': 'alpha_AUG',\n", + " 'month_9': 'alpha_SEP',\n", + " 'month_10': 'alpha_OCT',\n", + " 'month_11': 'alpha_NOV',\n", + " 'month_12': 'alpha_DEC'\n", + " })\n", + ")\n", + "regression_params.loc['alpha_MAY'] = 0" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "0290ae67-a2ac-4126-b576-026e23c55039", + "metadata": {}, + "outputs": [], + "source": [ + "# Export\n", + "outpath = Path('outputs', 'gasreg_price_adj_regression_params.csv')\n", + "outpath.parent.mkdir(parents=True, exist_ok=True)\n", + "(\n", + " regression_params.loc[[\n", + " 'beta_CDD',\n", + " 'beta_HDD',\n", + " 'alpha',\n", + " 'alpha_JAN',\n", + " 'alpha_FEB',\n", + " 'alpha_MAR',\n", + " 'alpha_APR',\n", + " 'alpha_MAY',\n", + " 'alpha_JUN',\n", + " 'alpha_JUL',\n", + " 'alpha_AUG',\n", + " 'alpha_SEP',\n", + " 'alpha_OCT',\n", + " 'alpha_NOV',\n", + " 'alpha_DEC'\n", + " ]]\n", + " .rename_axis('param')\n", + " .round(3)\n", + " .sort_index(axis=1)\n", + " .to_csv(outpath)\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/aeo_updates/temperature_gas_price_adj_regression/inspect_hub_locations.ipynb b/aeo_updates/temperature_gas_price_adj_regression/inspect_hub_locations.ipynb new file mode 100644 index 0000000..ff1b0ca --- /dev/null +++ b/aeo_updates/temperature_gas_price_adj_regression/inspect_hub_locations.ipynb @@ -0,0 +1,207 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "2b4f813c-9374-4cd4-962b-3a70d4b267f1", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Especially on Linux, gdxpds should be imported before pandas to avoid a library conflict. Also make sure your GAMS directory is listed in LD_LIBRARY_PATH.\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "import geopandas as gpd\n", + "import sys\n", + "\n", + "reeds_path = '' # User should specify path to ReEDS repository here\n", + "sys.path.append(reeds_path)\n", + "import reeds" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "7e7c9b42-1b34-4b13-a497-5151dc98aa08", + "metadata": {}, + "outputs": [], + "source": [ + "state_fips_to_abbrev_map = {\n", + " '01': 'AL',\n", + " '02': 'AK',\n", + " '04': 'AZ',\n", + " '05':'AR',\n", + " '06':'CA',\n", + " '08':'CO',\n", + " '09':'CT',\n", + " '10':'DE',\n", + " '11':'DC',\n", + " '12':'FL',\n", + " '13':'GA',\n", + " '15':'HI',\n", + " '16':'ID',\n", + " '17':'IL',\n", + " '18':'IN',\n", + " '19':'IA',\n", + " '20':'KS',\n", + " '21':'KY',\n", + " '22':'LA',\n", + " '23':'ME',\n", + " '24':'MD',\n", + " '25':'MA',\n", + " '26':'MI',\n", + " '27':'MN',\n", + " '28':'MS',\n", + " '29':'MO',\n", + " '30':'MT',\n", + " '31':'NE',\n", + " '32':'NV',\n", + " '33':'NH',\n", + " '34':'NJ',\n", + " '35':'NM',\n", + " '36':'NY',\n", + " '37':'NC',\n", + " '38':'ND',\n", + " '39':'OH',\n", + " '40':'OK',\n", + " '41':'OR',\n", + " '42':'PA',\n", + " '44':'RI',\n", + " '45':'SC',\n", + " '46':'SD',\n", + " '47':'TN',\n", + " '48':'TX',\n", + " '49':'UT',\n", + " '50':'VT',\n", + " '51':'VA',\n", + " '53':'WA',\n", + " '54':'WV',\n", + " '55':'WI',\n", + " '56':'WY',\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "f321f065-2cf0-43c6-9e7a-e32a9df8c70e", + "metadata": {}, + "outputs": [], + "source": [ + "gasreg_state_map = {\n", + " 'Northwest': ['OR', 'WA'],\n", + " 'California': ['CA'],\n", + " 'New England': ['ME', 'NH', 'VT', 'MA', 'CT', 'RI'],\n", + " 'Mid-Atlantic': ['NY', 'NJ', 'PA'],\n", + " 'South Atlantic': ['DC', 'DE', 'MD', 'NC', 'SC', 'GA', 'FL', 'WV', 'VA'],\n", + " 'East North Central': ['WI', 'IL', 'IN', 'MI', 'OH'],\n", + " 'Mountain': ['MT', 'ID', 'WY', 'CO', 'UT', 'NV'],\n", + " 'Southwest': ['AZ', 'NM'],\n", + " 'West South Central': ['TX', 'OK', 'AR', 'LA'],\n", + " 'East South Central': ['KY', 'TN', 'MS', 'AL'],\n", + " 'West North Central': ['ND', 'MN', 'SD', 'NE', 'IA', 'KS', 'MO']\n", + "}\n", + "\n", + "state_region_map = {}\n", + "for region, state_list in gasreg_state_map.items():\n", + " for state in state_list:\n", + " state_region_map[state] = region" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "69c2873c-e224-4ba5-ba68-34030bb8862b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'Hitachi Energy Barnett': 'West South Central',\n", + " 'Hitachi Energy California - North': 'California',\n", + " 'Hitachi Energy California - South': 'California',\n", + " 'Hitachi Energy Chicago Metro': 'East North Central',\n", + " 'Hitachi Energy Green River': 'Mountain',\n", + " 'Hitachi Energy Gulf Coast ELA': 'West South Central',\n", + " 'Hitachi Energy Gulf Coast ETX': 'West South Central',\n", + " 'Hitachi Energy Gulf Coast STX': 'West South Central',\n", + " 'Hitachi Energy Gulf Coast WLA': 'West South Central',\n", + " 'Hitachi Energy Haynesville': 'West South Central',\n", + " 'Hitachi Energy Henry Hub': 'West South Central',\n", + " 'Hitachi Energy Marcellus - Central': 'Mid-Atlantic',\n", + " 'Hitachi Energy Marcellus - Lower': 'South Atlantic',\n", + " 'Hitachi Energy Marcellus - Upper': 'Mid-Atlantic',\n", + " 'Hitachi Energy Michigan': 'East North Central',\n", + " 'Hitachi Energy Michigan/Ontario': 'East North Central',\n", + " 'Hitachi Energy Mid-Atlantic': 'South Atlantic',\n", + " 'Hitachi Energy Midcontinent - Central': 'West North Central',\n", + " 'Hitachi Energy Midcontinent - East': 'East North Central',\n", + " 'Hitachi Energy Midcontinent - Lower': 'West South Central',\n", + " 'Hitachi Energy Midcontinent - Upper': 'West North Central',\n", + " 'Hitachi Energy Mojave': 'Southwest',\n", + " 'Hitachi Energy New England': 'New England',\n", + " 'Hitachi Energy New York - Upstate': 'Mid-Atlantic',\n", + " 'Hitachi Energy New York/Long Island': 'Mid-Atlantic',\n", + " 'Hitachi Energy New York/Niagara': 'Mid-Atlantic',\n", + " 'Hitachi Energy Niobrara DJ': 'Mountain',\n", + " 'Hitachi Energy Northwest': 'Northwest',\n", + " 'Hitachi Energy Permian': 'West South Central',\n", + " 'Hitachi Energy Piceance/Uinta': 'Mountain',\n", + " 'Hitachi Energy San Juan': 'Southwest',\n", + " 'Hitachi Energy South Atlantic': 'South Atlantic',\n", + " 'Hitachi Energy TexOK': 'West South Central'}" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "county_geo = reeds.spatial.get_map('county', source='tiger')\n", + "\n", + "hub_locations = pd.read_excel('//nrelnas01/ReEDS/FY26_NatGas_KO/HE_VelSuite_GasIndices.xlsx')\n", + "hub_locations = gpd.GeoDataFrame(\n", + " hub_locations,\n", + " geometry=gpd.points_from_xy(hub_locations.Longitude, hub_locations.Latitude),\n", + " crs='EPSG:4326'\n", + ")\n", + "hub_locations = hub_locations.to_crs(county_geo.crs)\n", + "\n", + "county_geo['STCODE'] = county_geo['STATEFP'].map(state_fips_to_abbrev_map)\n", + "county_geo['gasreg'] = county_geo.STCODE.map(state_region_map)\n", + "gasreg_geo = county_geo.dissolve('gasreg')\n", + "hub_locations = gpd.sjoin(hub_locations, gasreg_geo[['geometry']])\n", + "hub_gasreg_map = dict(zip(hub_locations['Price_Hub'], hub_locations['gasreg']))\n", + "\n", + "hub_gasreg_map" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/aeo_updates/temperature_gas_price_adj_regression/write_regression_input_data.ipynb b/aeo_updates/temperature_gas_price_adj_regression/write_regression_input_data.ipynb new file mode 100644 index 0000000..346b333 --- /dev/null +++ b/aeo_updates/temperature_gas_price_adj_regression/write_regression_input_data.ipynb @@ -0,0 +1,414 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "1ec83972-df2c-490a-be18-1b0cb633030f", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Especially on Linux, gdxpds should be imported before pandas to avoid a library conflict. Also make sure your GAMS directory is listed in LD_LIBRARY_PATH.\n" + ] + } + ], + "source": [ + "import pandas as pd\n", + "from pathlib import Path\n", + "import h5py\n", + "import sys\n", + "\n", + "reeds_path = '' # User should specify path to ReEDS repository here\n", + "sys.path.append(reeds_path)\n", + "import reeds" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "fa9c9283-1bfd-4546-885f-6d196391498f", + "metadata": {}, + "outputs": [], + "source": [ + "def get_temperatures(reeds_path, tz_in='UTC', tz_out='Etc/GMT+6'):\n", + " h5path = Path(\n", + " reeds_path, 'inputs', 'profiles_temperature', 'temperature_state.h5',\n", + " )\n", + " _temperatures = {}\n", + " with h5py.File(h5path, 'r') as f:\n", + " years = [int(i) for i in list(f) if i.isdigit()]\n", + " for year in years:\n", + " timeindex = pd.to_datetime(\n", + " pd.Series(f[f\"index_{year}\"][:])\n", + " .str.decode('utf-8')\n", + " )\n", + " _temperatures[year] = pd.DataFrame(\n", + " index=timeindex,\n", + " columns=pd.Series(f['columns']).map(lambda x: x.decode()),\n", + " data=f[str(year)],\n", + " )\n", + "\n", + " temperatures = (\n", + " pd.concat(_temperatures, names=('year','timestamp')).rename_axis(columns='r')\n", + " .reset_index('year', drop=True)\n", + " .tz_localize(tz_in)\n", + " .tz_convert(tz_out)\n", + " )\n", + "\n", + " return temperatures" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "f7c899c6-ce3b-4ebf-a6b9-3f08d3a6a940", + "metadata": {}, + "outputs": [], + "source": [ + "def calculate_daily_state_degree_days(temperatures):\n", + " # Get baseline temperature for calculating degree days\n", + " scalars = reeds.io.get_scalars()\n", + " base_temp = scalars['degree_days_base_temperature']\n", + "\n", + " # Calculate degree-hours (hourly deviations from baseline)\n", + " # and then take the daily averages of degree-hours to\n", + " # get degree days. This is different from the traditional\n", + " # approach for calculating degree days\n", + " # (https://www.eia.gov/energyexplained/units-and-calculators/degree-days.php),\n", + " # but we found that this approach generally gives better\n", + " # regression model results (higher r-squared values and smaller errors). \n", + " hdd_hourly = (base_temp - temperatures).clip(lower=0)\n", + " hdd_daily = hdd_hourly.resample('D').mean()\n", + "\n", + " cdd_hourly = (temperatures - base_temp).clip(lower=0)\n", + " cdd_daily = cdd_hourly.resample('D').mean()\n", + "\n", + " return hdd_daily, cdd_daily" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "1f45f334-81d1-47f2-b46e-0c44dd542bee", + "metadata": {}, + "outputs": [], + "source": [ + "def aggregate_by_weighted_average(\n", + " regional_data: pd.DataFrame,\n", + " region_aggregion_weights: pd.Series,\n", + " region2aggregion: dict[str, str]\n", + ") -> pd.DataFrame:\n", + " \"\"\"\n", + " Aggregate region-level data to the aggregated region\n", + " (\"aggregion\") level via weighted average.\n", + "\n", + " Args:\n", + " regional_data: Region-level data.\n", + " region_aggregion_weights: The \"weight\" of each region\n", + " corresponding to its aggregion to use in weighted\n", + " average calculation.\n", + " region2aggregion: Mapping between regions and aggregions.\n", + "\n", + " Returns:\n", + " pd.DataFrame\n", + " \"\"\"\n", + " aggregional_data = (\n", + " regional_data.mul(region_aggregion_weights)\n", + " .transpose()\n", + " .rename(region2aggregion)\n", + " .groupby(level=0)\n", + " .sum()\n", + " .transpose()\n", + " )\n", + " return aggregional_data" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "f26e208f-b68b-4b0b-b1a1-ef33e1959da8", + "metadata": {}, + "outputs": [], + "source": [ + "def calculate_region_aggregion_population_weights(\n", + " region_level: str,\n", + " aggregion_level: str,\n", + ") -> pd.Series:\n", + " \"\"\"\n", + " For a given region level and aggregated region (aggregion)\n", + " level, calculate each region's share of its corresponding\n", + " aggregion's total population.\n", + " \n", + " Args:\n", + " inputs_case: Path to the inputs case directory.\n", + " region_level: Region level (example: 'state')\n", + " aggregion_level: Aggregated region level\n", + " (example: 'cendiv')\n", + "\n", + " Returns:\n", + " pd.Series\n", + " \"\"\"\n", + " # Get county populations\n", + " county_populations = reeds.inputs.get_county_populations()\n", + " county_populations = county_populations.rename(\n", + " columns={'value': 'population'}\n", + " )\n", + "\n", + " # Get county-to-region mapping\n", + " county2zone = reeds.io.get_county2zone(\n", + " as_map=False\n", + " )\n", + " county2zone['FIPS'] = (\n", + " 'p' + county2zone['FIPS'].astype(str).str.zfill(5)\n", + " )\n", + " state_groups = reeds.inputs.get_state_groups()\n", + " county2zone = county2zone.merge(\n", + " state_groups,\n", + " left_on='state',\n", + " right_on='st'\n", + " )\n", + " county_region_map = county2zone.set_index('FIPS')[region_level]\n", + "\n", + " # Calculate regional populations\n", + " county_populations[region_level] = (\n", + " county_populations['FIPS'].map(county_region_map)\n", + " )\n", + " region_populations = (\n", + " county_populations.groupby(region_level, as_index=False)\n", + " ['population']\n", + " .sum()\n", + " )\n", + "\n", + " # Calculate each region's percentage of aggregion population\n", + " region2aggregion = dict(zip(\n", + " county2zone[region_level],\n", + " county2zone[aggregion_level]\n", + " ))\n", + " region_populations[aggregion_level] = (\n", + " region_populations[region_level].map(region2aggregion)\n", + " )\n", + " region_populations['weight'] = (\n", + " region_populations['population']\n", + " / (\n", + " region_populations.groupby(aggregion_level)\n", + " ['population']\n", + " .transform('sum')\n", + " )\n", + " )\n", + " region_aggregion_weights = (\n", + " region_populations.set_index(region_level)['weight']\n", + " )\n", + "\n", + " return region_aggregion_weights" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "2d57656c-fd8f-474b-9614-339d20b6b2fe", + "metadata": {}, + "outputs": [], + "source": [ + "# Get state-level HDD/CDDs\n", + "temperatures = get_temperatures(reeds_path)\n", + "hdd_daily_st, cdd_daily_st = calculate_daily_state_degree_days(temperatures)\n", + "\n", + "# Get state-to-gasreg mapping\n", + "state_groups = reeds.inputs.get_state_groups()\n", + "st2gasreg = state_groups.set_index('st')['gasreg']\n", + "\n", + "# Calculate population-based state-to-gasreg weights\n", + "state_gasreg_weights = calculate_region_aggregion_population_weights(\n", + " region_level='state',\n", + " aggregion_level='gasreg'\n", + ")\n", + "\n", + "# Aggregate state-level HDD/CDDs via population-weighted\n", + "# average to get gasreg-level HDD/CDDs\n", + "hdd_daily_gasreg = aggregate_by_weighted_average(\n", + " hdd_daily_st,\n", + " state_gasreg_weights,\n", + " st2gasreg\n", + ")\n", + "hdd_daily_gasreg = hdd_daily_gasreg.rename_axis(index='datetime')\n", + "cdd_daily_gasreg = aggregate_by_weighted_average(\n", + " cdd_daily_st,\n", + " state_gasreg_weights,\n", + " st2gasreg\n", + ")\n", + "cdd_daily_gasreg = cdd_daily_gasreg.rename_axis(index='datetime')" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "3dce3d8d-6b9d-4335-9013-ff8e272f4c4b", + "metadata": {}, + "outputs": [], + "source": [ + "# Get natural gas hub prices.\n", + "# Note this data is not accessible to people external to the lab.\n", + "hub_prices = pd.read_excel(\n", + " '//nrelnas01/ReEDS/FY26_NatGas_KO/Natural Gas Daily Hub Prices - 07-17-2025 - Internal NREL only.xlsx'\n", + ")\n", + "hub_prices = hub_prices.loc[(\n", + " hub_prices['Delivery Date'].dt.year.isin(range(2014, 2025))\n", + ")].copy()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "dce0f4ab-48bf-48a0-b1a8-a661946faa07", + "metadata": {}, + "outputs": [], + "source": [ + "# Assign hubs to gasregs. These are largely based on finding the geographic\n", + "# overlap between hub locations and gasregs in inspect_hub_locations.ipynb.\n", + "# In some cases, (e.g., East North Central), only a subset of the identified\n", + "# hub locations is chosen because it results in a better correlation (i.e.,\n", + "# higher HDD/CDD coefficients in the regression)\n", + "region_hub_map = {\n", + " 'California': ['California - North', 'California - South'],\n", + " 'East_North_Central': ['Chicago Metro'],\n", + " 'East_South_Central': ['Marcellus - Lower'],\n", + " 'Mid_Atlantic': ['Mid-Atlantic'],\n", + " 'Mountain': ['Green River'],\n", + " 'New_England': ['New England'],\n", + " 'Northwest': ['Northwest'],\n", + " 'South_Atlantic': ['South Atlantic', 'Mid-Atlantic'],\n", + " 'Southwest': ['Mojave'],\n", + " 'West_North_Central': ['Midcontinent - Upper', 'Midcontinent - Central'],\n", + " 'West_South_Central': ['Gulf Coast ELA', 'Gulf Coast ETX', 'TexOK']\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "a8895922-e070-4d65-bd73-4bace9bd245d", + "metadata": {}, + "outputs": [], + "source": [ + "# For each gasreg, calculate the volume-weighted average price of all hubs\n", + "def get_regional_volume_weighted_price(hub_prices, region, hub_names):\n", + " hub_names = [f\"Hitachi Energy {hub_name}\" for hub_name in hub_names]\n", + " select_hub_prices = (\n", + " hub_prices\n", + " .loc[hub_prices['Price Hub'].isin(hub_names)]\n", + " .copy()\n", + " .set_index('Delivery Date')\n", + " )\n", + "\n", + " if len(hub_names) > 1:\n", + " select_hub_prices['Total $'] = (\n", + " select_hub_prices['Wtd Avg Index $'] * select_hub_prices['Daily Volume']\n", + " )\n", + " select_hub_prices = (\n", + " select_hub_prices.groupby(level=0)\n", + " .sum(numeric_only=True)\n", + " )\n", + " select_hub_prices['volume_weighted_price'] = (\n", + " select_hub_prices['Total $'] / select_hub_prices['Daily Volume']\n", + " )\n", + " regional_volume_weighted_price = select_hub_prices['volume_weighted_price']\n", + " else:\n", + " regional_volume_weighted_price = select_hub_prices['Wtd Avg Index $']\n", + "\n", + " return regional_volume_weighted_price\n", + "\n", + "regional_prices = {}\n", + "for region, hub_names in region_hub_map.items():\n", + " regional_prices[region] = get_regional_volume_weighted_price(\n", + " hub_prices,\n", + " region,\n", + " hub_names\n", + " )\n", + "\n", + "regional_prices = pd.concat(regional_prices, axis=1)\n", + "regional_prices = regional_prices.set_index([\n", + " regional_prices.index.year,\n", + " regional_prices.index.month,\n", + " regional_prices.index.day\n", + "])\n", + "regional_prices = regional_prices.rename_axis(index=['year', 'month', 'day'])" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "3756ca41-4b8d-4bce-9b88-b27fb7dde3e3", + "metadata": {}, + "outputs": [], + "source": [ + "# Combine daily HDD/CDD and gas price data\n", + "gasreg_cdd = cdd_daily_gasreg.set_index([\n", + " cdd_daily_gasreg.index.year,\n", + " cdd_daily_gasreg.index.month,\n", + " cdd_daily_gasreg.index.day\n", + "])\n", + "gasreg_hdd = hdd_daily_gasreg.set_index([\n", + " hdd_daily_gasreg.index.year,\n", + " hdd_daily_gasreg.index.month,\n", + " hdd_daily_gasreg.index.day\n", + "])\n", + "\n", + "cdd_data = gasreg_cdd.loc[regional_prices.index].copy()\n", + "hdd_data = gasreg_hdd.loc[regional_prices.index].copy()\n", + "\n", + "cdd_data.columns = [f\"{col}_cdd\" for col in cdd_data.columns]\n", + "hdd_data.columns = [f\"{col}_hdd\" for col in hdd_data.columns]\n", + "\n", + "data = pd.concat([cdd_data, hdd_data], axis=1).fillna(0)\n", + "for region in region_hub_map.keys():\n", + " data[f\"{region}_price\"] = regional_prices[region]" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "75b48874-f4fc-422d-aa5b-d19330efe7d2", + "metadata": {}, + "outputs": [], + "source": [ + "data = data.rename_axis(index=['year', 'month', 'day'])" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "792a80fe-b042-4dc9-8343-281cf62c04f8", + "metadata": {}, + "outputs": [], + "source": [ + "# Export\n", + "outpath = Path('inputs', 'gasreg_regression_data.csv')\n", + "outpath.parent.mkdir(parents=True, exist_ok=True)\n", + "data.to_csv(outpath)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/atb/README.md b/atb/README.md index ff7e80f..b7c0244 100644 --- a/atb/README.md +++ b/atb/README.md @@ -1,10 +1,232 @@ -# Overview -This repo includes scripts to plot CAPEX, FOM, and VOM of different versions of the ATB. +# ATB input pipeline -# Required inputs -Users are required to provide Users have the options to provide the ATBe.csv file for the specific ATB version they want to plot. THere are two options to provide ATBe.csv file: -- Save the file in the ATB/inputs folder -- Provide an URL to the ATBe.csv file. If choose URL option, users are prompted to provide the URL. +This directory turns raw NLR Annual Technology Baseline (ATB) data into ReEDS +input files and plots. The workflow has three explicit stages: -# Note -This script was tested on ATB 2024 and 2025 versions only, so they may not work for earlier versions. +1. download and inspect raw data; +2. format the local raw data for ReEDS; +3. plot metrics from the same local raw data. + +## Configure the run + +[`config.yaml`](config.yaml) is the user-facing control file. It shows which +stages will run, the ATB release and source URLs, local filenames, ReEDS path, +technologies, processing choices, and plotting choices. + +Review that file first, especially: + +- `atb.year`, `atb.release_version`, and `atb.dollar_year`; +- the two `raw_data` URLs and filenames; +- `processing.reeds_repo` and selected technologies; +- `historical_data.directory` and its fixed dollar year; +- the metrics and technologies under `plotting`. + +## Run one step at a time + +Run the following commands from the `atb/` directory. + +### Step 1: scrape raw inputs + +```bash +python scripts/scrape_atb_inputs.py +``` + +This downloads or reuses both independent raw inputs: + +- `scraped_input/atb__flat_file.csv`; +- `scraped_input/atb__workbook.xlsx`. + +It then displays a summary of the flat file and the workbook sheets. Existing +files are reused unless `--force` is supplied: + +```bash +python scripts/scrape_atb_inputs.py --force +``` + +Neither raw file is committed, so this step is required after a fresh clone. +See [`scraped_input/README.md`](scraped_input/README.md) for why, and how the +pinned URLs keep a run reproducible without storing the data in Git. + +Downloads normally verify HTTPS certificates. If certificate verification +fails because the active conda environment does not trust an NLR network +inspection certificate, `raw_data.allow_insecure_ssl_fallback: true` permits a +clearly labeled `verify=False` retry. Set it to `false` to prohibit that retry. + +### Step 2: format ReEDS inputs + +```bash +python scripts/generate_atb_files.py +``` + +This reads the local raw files plus the versioned history under +`manual_input/historical/`, then writes ReEDS-formatted CSVs to `output/`. It +does **not** download raw data. If a required raw file is missing, run Step 1 +first. + +A missing history file can be initialized from the matching file in +`processing.reeds_repo` by temporarily setting +`historical_data.seed_missing_from_reeds: true`. Current scraped ATB rows +replace history from the first available projection year onward. That boundary +year is then appended to the fixed-dollar history file, ready for the next +annual ATB release. + +Whether this step generates cost files and financial files, which technologies +it processes, and whether it copies results into ReEDS are all controlled under +`processing:` in `config.yaml`. + +### Selective smoothing logic + +The optional `processing.smooth_cost_curves` block removes short-lived dips, +bumps, and rounded stair steps without replacing each ATB trajectory with one +fully smoothed curve. The `selective` method follows these rules: + +1. **Use 2022 as the projection anchor.** Moving backward from 2022, a + contiguous historical tail below the anchor value is raised to the anchor. + The scan stops at the first earlier value that already meets or exceeds it. +2. **Remove movement in the wrong direction.** For a series that declines from + the anchor to its endpoint, temporary future increases are removed. For an + improving series, such as a capacity-factor multiplier, temporary decreases + are removed instead. +3. **Bridge near-equal plateaus.** Consecutive future values within the relative + or absolute tolerance are treated as one group. The last year of each group + is retained as a change point, and the years between change points are + linearly interpolated. This removes small rounded stair steps. +4. **Bridge compact dips and bumps.** A cluster of nearby abrupt slope changes + is interpolated between the years immediately outside the disturbance. This + removes patterns such as the coal-CCS dip around 2033--2035. +5. **Preserve major transitions.** A year-to-year change at or above the major + step threshold is not bridged. For example, the large fuel-cell capital-cost + transition in 2035 remains explicit. A single isolated slope change is also + retained as a normal ATB milestone. +6. **Limit the affected metrics.** With the current configuration, smoothing + covers every `capcost*`, `fom*`, and `vom` column, including battery energy + costs, plus `cf_improvement`/`CF_mult`. Heat rate and efficiency are not + changed. + +The current defaults in `config.yaml` are: + +| Setting | Default | Meaning | +| --- | ---: | --- | +| `projection_start_year` | `2022` | Historical/projection anchor | +| `similar_value_relative_tolerance` | `0.001` | Values within 0.1% can form one plateau | +| `similar_value_absolute_tolerance` | `1e-9` | Numerical tolerance near zero | +| `slope_change_threshold` | `0.4` | Detect a normalized slope change of 40% or more | +| `max_kink_years` | `4` | Maximum span of a compact slope-change cluster | +| `major_step_relative_threshold` | `0.1` | Preserve year-to-year changes of 10% or more | +| `include_capacity_factor_multiplier` | `true` | Apply the same rules to capacity-factor multipliers | + +The older flat-history/anchor-to-target behavior remains available as +`method: linear_bridge`. + +### File names and column schemas expected by ReEDS + +Generated files must match what ReEDS expects exactly, because ReEDS resolves +these inputs by file name and, for some technologies, reads their columns by +position rather than by name. Two settings in +[`scripts/settings.yaml`](scripts/settings.yaml) handle the cases where the +internal representation and the ReEDS representation differ: + +- `reeds_name` — the file prefix ReEDS uses when it differs from the internal + technology key. Onshore and offshore wind are `wind-ons`/`wind-ofs` + internally but `ons-wind`/`ofs-wind` in ReEDS, so their outputs are written + as `ons-wind_ATB__.csv` and `ofs-wind_ATB__.csv`. + The output file name doubles as the `Scenario` key in the ReEDS + `dollaryear.csv`, so a mismatch here silently leaves ReEDS reading its + previous inputs. +- `output_cols` — an ordered mapping of internal column name to ReEDS header, + applied as the last step before writing. ReEDS reads the two wind files + positionally in `reeds/input_processing/plantcostprep.py`, and detects the + ATB 2024 offshore format by the presence of a `Turbine` column, so those + files must keep the legacy headers and this exact column order: + `Turbine, Year, CF_mult, Overnight Cap Cost $/kW, Fixed O&M $/(kW-yr), + Var O&M $/MWh` (plus `rsc_mult` for offshore). Writing the internal names or + order instead makes ReEDS assign capital cost to the capacity-factor + multiplier without raising an error. + +Technologies without these settings are written using the internal column names +listed under `cols`, which already match their ReEDS files. Everything upstream +of the write step — history files in `manual_input/historical/`, scenario +comparisons, transformations — uses the internal names throughout. + +### Step 3: plot raw ATB data + +```bash +python scripts/atb_plotting.py +``` + +This reads the same local flat file used in Step 2 and saves the configured +figures to `figures/`. It does not scrape data or plot the formatted files from +`output/`. + +## Run the configured pipeline + +To run the enabled stages in order, set the switches under `workflow:` in +`config.yaml`, then run: + +```bash +python scripts/run_pipeline.py +``` + +The runner prints which stages will run before doing any work. A stage can also +be selected explicitly: + +```bash +python scripts/run_pipeline.py --only scrape +python scripts/run_pipeline.py --only format +python scripts/run_pipeline.py --only plot +``` + +## Data flow + +```text +config.yaml + | + +--> scrape_atb_inputs.py + | +--> scraped_input/atb__flat_file.csv + | +--> scraped_input/atb__workbook.xlsx + | +--> terminal summaries/previews of both raw files + | + +--> generate_atb_files.py + | +--> raw flat file (primary ATB data) + | +--> raw workbook (battery power/energy cost split) + | +--> manual_input/historical/ (versioned ReEDS history) + | +--> manual_input/ (CSP ratios and pre-release fallbacks) + | +--> ReEDS deflator and dollaryear tables + | +--> output/*_ATB__.csv + | + +--> atb_plotting.py + +--> the same raw flat file + +--> figures/ +``` + +The flat file and workbook are independent upstream downloads. Neither is +generated from the other. The formatter and plotter do not download data; they +only consume the raw files created by the scraper. + +The normal pipeline validates the formatted pre-smoothing data against the +configured ReEDS repository when `workflow.make_comparison_plots` is enabled. +It holds that data in a temporary directory, prints the validation summary to +the terminal, writes local validation plots under `comparison/plots/`, and +writes versioned before/after plots under `figures/smoothing_comparison/`. The +temporary CSVs are deleted when the pipeline exits; no row-level or summary CSV +reports are created. + +For ATB 2024, the URLs are intentionally pinned to corrected release v3. +Changing the release can change technology trajectories; update both source +URLs together when adopting a newer release. + +## Directory layout + +| Path | Purpose | +| --- | --- | +| `config.yaml` | User-facing workflow configuration | +| `scraped_input/` | Visible, unmodified raw ATB downloads (local only; not committed) | +| `manual_input/` | Versioned history and inputs unavailable in ATB downloads | +| `scripts/settings.yaml` | Internal per-technology ReEDS formatting rules | +| `scripts/scrape_atb_inputs.py` | Raw-data download and inspection | +| `scripts/generate_atb_files.py` | ReEDS input formatter | +| `scripts/atb_plotting.py` | Raw ATB plotting | +| `output/` | Generated ReEDS-formatted CSVs | +| `figures/` | Generated ATB plots | + +See [`scripts/README.md`](scripts/README.md) for the scripts folder structure. diff --git a/atb/atb_plotting.py b/atb/atb_plotting.py deleted file mode 100644 index c3beceb..0000000 --- a/atb/atb_plotting.py +++ /dev/null @@ -1,313 +0,0 @@ -#%% Imports -import pandas as pd -import matplotlib as mpl -import matplotlib.pyplot as plt -import os -import sys -import math -import json -import argparse - -# Get reedsplots from ReEDS-2.0 repo -reeds_path = os.path.expanduser('~/Documents/Github/ReEDS/ReEDS-2.0') -sys.path.append(reeds_path) -from reeds import plots -plots.plotparams() - -############################################################################################################# -def main(): - - ########################################## USER-DEFINED INPUTS ########################################## - parser = argparse.ArgumentParser(description="Plotting ATB") - parser.add_argument('--version', '-v', type=int, required=True, - help='version of ATB to plot.') - parser.add_argument('--metric', '-m', type=str, default='CAPEX', - help='metric to plot, default is CAPEX, other options are Fixed O&M and Variable O&M') - parser.add_argument('--crpyears', '-y', type=int, default=20, - help='crpyears, default is 20') - parser.add_argument('--inputs', '-i', type=str, default='url', - help='where to look for the ATBe csv input file to plot, options are url and local') - parser.add_argument('--format', '-f', type=str, default='png', - help='format of output plots, options are .png and .pdf') - parser.add_argument('--save', '-s', action="store_true", - help='save cleaned ATB data used for plotting to csv.') - args = parser.parse_args() - - atb_version = args.version # ATB version - core_metric_parameter = args.metric # Metric to plot: 'CAPEX', 'Fixed O&M','Variable O&M' - crpyears = args.crpyears # crpyears - atb_inputs = args.inputs # Read in ATBe.csv input file from 'url' or 'local' inputs folder - # (if a version of ATB is not yet available online, read from 'inputs') - # Link to past ATBe versions: https://data.openei.org/s3_viewer?bucket=oedi-data-lake&prefix=ATB%2Felectricity%2Fcsv%2F&limit=50 - # 2024 atb_path = 'https://oedi-data-lake.s3.amazonaws.com/ATB/electricity/csv/2024/v3.0.0/ATBe.csv' - # 2025 atb is not yet available online, so only option is reading it from inputs folder. - figure_format = args.format # 'png' or 'pdf' - save_data = args.save # Save the version of ATBe that has been cleaned and ready for plotting to csv - - ######################################### FOR TESTING/DEBUGGING ######################################### - # atb_version = 2024 # ATB version - # core_metric_parameter = 'CAPEX' # Metric to plot: 'CAPEX', 'Fixed O&M','Variable O&M' - # crpyears = 20 - # atb_inputs = 'url' # Read in ATBe.csv input file from 'url' or 'local' inputs folder - # (if a version of ATB is not yet available online, read from 'inputs') - # Link to past ATBe versions: https://data.openei.org/s3_viewer?bucket=oedi-data-lake&prefix=ATB%2Felectricity%2Fcsv%2F&limit=50 - # 2024 atb_path = 'https://oedi-data-lake.s3.amazonaws.com/ATB/electricity/csv/2024/v3.0.0/ATBe.csv' - # 2025 atb is not yet available online, so only option is reading it from inputs folder. - # figure_format = 'png' # 'png' or 'pdf' - # save_data = False # Print the version of ATBe that has been cleaned and ready for plotting to csv - ######################################################################################################### - - # Specify dollar year: - if atb_version == 2024: - dollar_year = 2022 - elif atb_version == 2025: - dollar_year = 2023 - - # Get the path for ATB data and clean the data: - (inputs_path, atb_path, figures_path) = read_path(atb_inputs, atb_version) - (technologies, dfplot) = clean_atb_data(atb_inputs, atb_path, atb_version, core_metric_parameter, crpyears) - if save_data: - print("Saving the version of ATBe that has been cleaned and ready for plotting to csv file...") - dfplot.to_csv(os.path.join(inputs_path, "ATB_"+str(atb_version)+"_cleaned.csv")) - - # Define plot attributes: - (traces, colors, tracelabels, legendtitle, plottitle) = plot_attributes(inputs_path, atb_version) - - # Plot ATB: - plot_atb(figures_path, atb_version, technologies, dfplot, traces, colors, legendtitle, plottitle, - tracelabels, core_metric_parameter, figure_format, dollar_year) - -###################################################################################################### -#%% FUNCTIONS ### -def read_path(atb_inputs, atb_version): - # Iputs path: - inputs_path = os.path.join(os.getcwd(),'inputs') - if not os.path.exists(inputs_path): - os.makedirs(inputs_path) - - # ATB path: - if atb_inputs == 'url': - print('\nYou choose to read ATBe '+str(atb_version) + ' file from an URL.') - user_input = input('\nPlease enter the URL to the ATBe '+str(atb_version) + ' file here to continue: ') - atb_path = user_input - elif atb_inputs == 'local': - print('\nYou choose to read ATBe_'+str(atb_version)+ '.csv file from local "inputs" folder.') - atb_path = os.path.join(inputs_path,'ATBe_'+str(atb_version)+'.csv') - - # Figures path: - figures_path = os.path.join(os.getcwd(),'figures') - if not os.path.exists(figures_path): - os.makedirs(figures_path) - - return (inputs_path, atb_path, figures_path) - -def clean_atb_data(atb_inputs, atb_path, atb_version, core_metric_parameter, crpyears): - # Read ATB data - try: - atb = pd.read_csv(atb_path, index_col=0, low_memory=False) - except Exception: - if atb_inputs == 'url': - raise FileNotFoundError('URL is incorrect') - elif atb_inputs == 'local': - raise FileNotFoundError('ATBe file is missing or incorrect. Please provide data file with name ATBe_' + str(atb_version) + '.csv in the "inputs" folder.') - atb.technology.unique() - - # Use 'Market' case for 2024 version, which is equivalent to 'Exp + TC' in 2025 version - if atb_version == 2024: - core_metric_case = 'Market' - elif atb_version == 2025: - core_metric_case = 'Exp + TC' - - # Technology to plot - technologies = [ - 'Biopower', - 'Coal_FE', - 'CSP', - 'Geothermal', - 'LandbasedWind', - 'NaturalGas_FE', - 'Nuclear', - 'OffShoreWind', - 'Utility-Scale Battery Storage', - 'UtilityPV', - # 'CommPV', - # 'Hydropower', - # 'Pumped Storage Hydropower', - # 'ResPV', - # 'Utility-Scale PV-Plus-Battery', - # 'Commercial Battery Storage', - # 'Residential Battery Storage', - ] - - # Clean the raw ATB data in the right format for plotting - dictplot = {} - keepcols = ['technology','techdetail','scenario','core_metric_variable','value'] - for technology in technologies: - # In ATB 2025 version, some technologies do not have 'Exp + TC' case, so use 'R&D' for them - if atb_version == 2025: - if technology in [ - 'Biopower', - 'Coal_FE', - 'CSP', - 'Geothermal', - 'NaturalGas_FE', - 'Nuclear', - 'OffShoreWind', - ]: - core_metric_case = 'R&D' - else: - core_metric_case = 'Exp + TC' - - dictplot[technology] = atb.loc[ - (atb.technology==technology) - & (atb.core_metric_parameter==core_metric_parameter) - & (atb.core_metric_case==core_metric_case) - & (atb.crpyears==crpyears) - # & (atb.techdetail==techdetail) - # & (atb.core_metric_variable==2050) - , keepcols - ] - dfplot = pd.concat(dictplot, ignore_index=True).set_index('technology') - - return (technologies, dfplot) - -def plot_attributes(inputs_path, atb_version): - cm = plt.cm.coolwarm - cm = plt.cm.RdBu - d = 0.06 - # traces setting - with open(os.path.join(inputs_path,"traces_"+str(atb_version)+".json")) as file: - traces = json.load(file) - # tracelabels setting - with open(os.path.join(inputs_path,"tracelabels.json")) as file: - tracelabels = json.load(file) - - # colors setting - with open(os.path.join(inputs_path,"colors.json")) as file: - colors = json.load(file) - # add colors for offshore wind classes - colors["OffShoreWind"] = { - 'Class1': cm(0.), - 'Class2': cm(d), - 'Class3': cm(2*d), - 'Class4': cm(3*d), - 'Class5': cm(4*d), - 'Class6': cm(5*d), - 'Class7': cm(6*d), - 'Class8': cm(1.), - 'Class9': cm(1-d), - 'Class10': cm(1-d*2), - 'Class11': cm(1-d*3), - 'Class12': cm(1-d*4), - 'Class13': cm(1-d*5), - 'Class14': cm(1-d*6), - } - - legendtitle = { - 'UtilityPV':None, - 'Nuclear':None, - 'LandbasedWind':None, - 'Biopower':None, - 'CSP':None, - 'Utility-Scale Battery Storage': 'Duration\n[hours]', - 'OffShoreWind': 'Class', - 'NaturalGas_FE': None, - 'Coal_FE': None, - 'Geothermal': None, - } - - plottitle = { - 'UtilityPV':'Utility PV', - 'Nuclear':'Nuclear', - 'LandbasedWind':'Land-based wind', - 'Biopower':'Biopower', - 'CSP':'CSP', - 'Utility-Scale Battery Storage': 'Utility battery', - 'OffShoreWind': 'Offshore wind', - 'NaturalGas_FE': 'Natural gas', - 'Coal_FE': 'Coal', - 'Geothermal': 'Geothermal', - } - return (traces, colors, tracelabels, legendtitle, plottitle) - -def plot_atb(figures_path, atb_version, technologies, dfplot, traces, colors, legendtitle, plottitle, - tracelabels, core_metric_parameter, figure_format, dollar_year): - - # Set up number of panels for figure - alpha = 0.4 - nrows, ncols = 2, 5 - coords = dict(zip( - technologies, - [(row, col) for row in range(nrows) for col in range(ncols)] - )) - - # Plot the figure - plt.close() - f,ax = plt.subplots( - nrows, ncols, figsize=(13,8), sharex=True, sharey=True, - # gridspec_kw={'wspace':1.0, 'hspace':0.2}, - gridspec_kw={'hspace':0.25}, - ) - - for technology in technologies: - df = {} - #scenarios = ['Moderate'] if technology == 'Nuclear' else ['Moderate', 'Advanced', 'Conservative'] - scenarios = ['Moderate', 'Advanced', 'Conservative'] - for scenario in scenarios: - df[scenario] = ( - dfplot - .loc[dfplot.scenario==scenario] - .loc[technology] - .pivot(columns='techdetail',index='core_metric_variable',values='value') - )[traces[technology]] - ### Central - for trace in df['Moderate']: - ax[coords[technology]].plot( - df['Moderate'].index, df['Moderate'][trace].values, - color=colors[technology][trace], label=tracelabels[technology][trace], - ) - if len(traces[technology]) != 1: - ax[coords[technology]].legend( - fontsize=8, #'small', - loc = ('upper right'), - #loc=('center left' if coords[technology][1]==2 else 'lower left'), - #bbox_to_anchor=((1,0.5) if coords[technology][1]==2 else (0.,0.)), - ncol=(2 if technology == 'OffShoreWind' else 1), - handlelength=0.7, handletextpad=0.3, columnspacing=0.5, - title=legendtitle[technology], - frameon=False, - ) - ### Formatting - ax[coords[technology]].set_title(plottitle[technology], weight='bold') - ax[coords[technology]].grid(which='major',axis='y',ls=':',lw=0.5,c='0.5') - ### Range - #if technology == 'Nuclear': - # continue - for trace in df['Advanced']: - ax[coords[technology]].fill_between( - df['Advanced'].index, df['Conservative'][trace].values, df['Advanced'][trace].values, - color=colors[technology][trace], alpha=alpha, label='_nolabel_', lw=0, - ) - - ### Format the figure - for col in range(ncols): - ax[-1,col].set_xlabel(None) - ax[0,0].xaxis.set_major_locator(mpl.ticker.MultipleLocator(10)) - ax[0,0].xaxis.set_minor_locator(mpl.ticker.AutoMinorLocator(2)) - if core_metric_parameter == 'CAPEX': - ax[0,0].yaxis.set_major_locator(mpl.ticker.MultipleLocator(2000)) - else: - ax[0,0].yaxis.set_major_locator(mpl.ticker.MultipleLocator(40)) - ax[0,0].yaxis.set_minor_locator(mpl.ticker.AutoMinorLocator(2)) - ax[0,0].set_ylim(0,math.ceil(dfplot.value.max()/100)*100) - ax[1,0].set_ylabel(core_metric_parameter + ' cost [' + str(dollar_year) + '$/kW]') - ax[0,0].set_ylabel(core_metric_parameter + ' cost [' + str(dollar_year) + '$/kW]') - plots.despine(ax) - - # Save the figure - if figure_format == 'png': - plt.savefig(os.path.join(figures_path,'ATB'+str(atb_version)+'-{}.png'.format(core_metric_parameter))) - elif figure_format == 'pdf': - plt.savefig(os.path.join(figures_path,'ATB'+str(atb_version)+'-{}.pdf'.format(core_metric_parameter))) - -main() diff --git a/atb/comparison/README.md b/atb/comparison/README.md new file mode 100644 index 0000000..2a23550 --- /dev/null +++ b/atb/comparison/README.md @@ -0,0 +1,43 @@ +# Local ATB output comparison + +The comparison utility is versioned; its generated plots are local artifacts +and remain ignored by Git. + +For validation plus before/after smoothing figures, run formatting and +comparison together from `atb/`: + +```bash +python scripts/run_pipeline.py --only format compare +``` + +When the full pipeline runs formatting and comparison together, it captures +fully processed files just before smoothing in a temporary directory. The +comparison script validates those files against the matching source files in +the ReEDS repository configured in `../config.yaml`, then the temporary files +are deleted automatically. Thus smoothing differences do not obscure scraper +and formatting validation. The script normalizes legacy wind filenames and +column names, then checks: + +- missing files and columns; +- duplicate keys; +- rows present on only one side; +- changed values using configurable numeric tolerances. + +Only plots are written, under `comparison/plots/`: + +- `comparison_overview.png` summarizes file-level validation status; +- one time-series plot is written for each generated/ReEDS file pair. + +Detailed comparison statuses and reverse-coverage gaps are printed to the +terminal instead of being written as CSV reports. In these local plots, solid +lines are the unsmoothed generated baseline and dashed lines are ReEDS. + +Running `python comparison/compare_atb_outputs.py` by itself compares the final +files already in `output/` with ReEDS. It cannot regenerate before/after plots +because the temporary pre-smoothing data is intentionally not retained. + +The full pipeline also compares the temporary unsmoothed data with the final +smoothed outputs. Those plots are written to +`../figures/smoothing_comparison/` and are versioned so branch users can review +the smoothing effect without retaining duplicate CSV outputs. Solid lines are +after smoothing; point markers are the unsmoothed input values. diff --git a/atb/comparison/compare_atb_outputs.py b/atb/comparison/compare_atb_outputs.py new file mode 100644 index 0000000..4d06f52 --- /dev/null +++ b/atb/comparison/compare_atb_outputs.py @@ -0,0 +1,606 @@ +"""Compare generated ATB 2024 CSVs with the current ReEDS input files.""" + +from __future__ import annotations + +import argparse +import math +from pathlib import Path +import sys + +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + + +ATB_DIR = Path(__file__).resolve().parents[1] +SCRIPT_DIR = ATB_DIR / "scripts" +sys.path.insert(0, str(SCRIPT_DIR)) + +from atb_config import DEFAULT_CONFIG_PATH, load_processing_settings + + +DEFAULT_PLOT_DIR = Path(__file__).resolve().parent / "plots" + +FILENAME_PREFIX_MAP = { + "wind-ons_": "ons-wind_", + "wind-ofs_": "ofs-wind_", +} + +REEDS_COLUMN_MAP = { + "Turbine": "turbine", + "Year": "t", + "CF_mult": "cf_improvement", + "Overnight Cap Cost $/kW": "capcost", + "Fixed O&M $/(kW-yr)": "fom", + "Var O&M $/MWh": "vom", +} + +PREFERRED_KEYS = ["i", "type", "turbine", "t", "rsc_mult"] + +METRIC_LABELS = { + "capcost": "Capital cost ($/kW)", + "capcost_energy": "Energy capital cost ($/kWh)", + "fom": "Fixed O&M ($/kW-year)", + "fom_energy": "Energy fixed O&M ($/kWh-year)", + "vom": "Variable O&M ($/MWh)", + "heatrate": "Heat rate (MMBtu/MWh)", + "cf_improvement": "Capacity-factor multiplier", + "rte": "Round-trip efficiency", + "rsc_mult": "Resource-supply-curve multiplier", +} + + +def resolve_atb_config_path(value: str | Path) -> Path: + """Resolve a configured path relative to the ATB workflow directory.""" + path = Path(value) + return path.resolve() if path.is_absolute() else (ATB_DIR / path).resolve() + + +def reeds_filename(generated_name: str) -> str: + """Map a generated filename to its ReEDS filename.""" + for generated_prefix, reeds_prefix in FILENAME_PREFIX_MAP.items(): + if generated_name.startswith(generated_prefix): + return reeds_prefix + generated_name[len(generated_prefix):] + return generated_name + + +def normalize_frame(frame: pd.DataFrame) -> pd.DataFrame: + """Normalize known legacy ReEDS column names and cell dtypes.""" + frame = frame.rename(columns=REEDS_COLUMN_MAP).copy() + for column in frame.columns: + numeric = pd.to_numeric(frame[column], errors="coerce") + nonempty = frame[column].notna().sum() + if nonempty and numeric.notna().sum() == nonempty: + frame[column] = numeric + elif frame[column].dtype == object: + frame[column] = frame[column].astype("string").str.strip() + return frame + + +def choose_keys(generated: pd.DataFrame, reeds: pd.DataFrame) -> list[str]: + """Choose stable identifier columns shared by both frames.""" + shared = set(generated.columns) & set(reeds.columns) + keys = [column for column in PREFERRED_KEYS if column in shared] + if "t" not in keys and "t" in shared: + keys.append("t") + return keys + + +def compare_file( + generated_path: Path, + reeds_path: Path, + absolute_tolerance: float, + relative_tolerance: float, +) -> dict: + """Compare one generated/ReEDS file pair in memory.""" + result = { + "generated_file": generated_path.name, + "reeds_file": reeds_path.name, + "generated_rows": 0, + "reeds_rows": 0, + "key_columns": "", + "columns_only_generated": "", + "columns_only_reeds": "", + "duplicate_keys_generated": 0, + "duplicate_keys_reeds": 0, + "rows_only_generated": 0, + "rows_only_reeds": 0, + "shared_rows": 0, + "changed_cells": 0, + "max_absolute_difference": 0.0, + "max_relative_difference": 0.0, + "status": "", + } + + if not reeds_path.exists(): + result["status"] = "MISSING_REEDS_FILE" + return result + + generated = normalize_frame(pd.read_csv(generated_path)) + reeds = normalize_frame(pd.read_csv(reeds_path)) + result["generated_rows"] = len(generated) + result["reeds_rows"] = len(reeds) + + generated_columns = set(generated.columns) + reeds_columns = set(reeds.columns) + only_generated = sorted(generated_columns - reeds_columns) + only_reeds = sorted(reeds_columns - generated_columns) + result["columns_only_generated"] = ";".join(only_generated) + result["columns_only_reeds"] = ";".join(only_reeds) + + keys = choose_keys(generated, reeds) + result["key_columns"] = ";".join(keys) + if not keys: + result["status"] = "NO_SHARED_KEY" + return result + + result["duplicate_keys_generated"] = int(generated.duplicated(keys).sum()) + result["duplicate_keys_reeds"] = int(reeds.duplicated(keys).sum()) + if result["duplicate_keys_generated"] or result["duplicate_keys_reeds"]: + result["status"] = "DUPLICATE_KEYS" + return result + + value_columns = sorted((generated_columns & reeds_columns) - set(keys)) + merged = generated.merge( + reeds, + on=keys, + how="outer", + suffixes=("__generated", "__reeds"), + indicator=True, + ) + only_generated_rows = merged.loc[merged["_merge"] == "left_only", keys] + only_reeds_rows = merged.loc[merged["_merge"] == "right_only", keys] + shared = merged.loc[merged["_merge"] == "both"].copy() + result["rows_only_generated"] = len(only_generated_rows) + result["rows_only_reeds"] = len(only_reeds_rows) + result["shared_rows"] = len(shared) + + differences = [] + max_abs = 0.0 + max_rel = 0.0 + for column in value_columns: + generated_column = f"{column}__generated" + reeds_column = f"{column}__reeds" + generated_values = shared[generated_column] + reeds_values = shared[reeds_column] + if pd.api.types.is_numeric_dtype(generated_values) and pd.api.types.is_numeric_dtype(reeds_values): + generated_numeric = pd.to_numeric(generated_values, errors="coerce") + reeds_numeric = pd.to_numeric(reeds_values, errors="coerce") + equal = np.isclose( + generated_numeric, + reeds_numeric, + atol=absolute_tolerance, + rtol=relative_tolerance, + equal_nan=True, + ) + changed = shared.loc[~equal, keys].copy() + if not changed.empty: + changed["column"] = column + changed["generated_value"] = generated_numeric.loc[~equal].values + changed["reeds_value"] = reeds_numeric.loc[~equal].values + changed["absolute_difference"] = np.abs( + changed["generated_value"] - changed["reeds_value"] + ) + denominator = np.maximum(np.abs(changed["reeds_value"]), absolute_tolerance) + changed["relative_difference"] = changed["absolute_difference"] / denominator + max_abs = max(max_abs, float(changed["absolute_difference"].max())) + max_rel = max(max_rel, float(changed["relative_difference"].max())) + differences.append(changed) + else: + equal = generated_values.fillna("").eq(reeds_values.fillna("")) + changed = shared.loc[~equal, keys].copy() + if not changed.empty: + changed["column"] = column + changed["generated_value"] = generated_values.loc[~equal].values + changed["reeds_value"] = reeds_values.loc[~equal].values + changed["absolute_difference"] = np.nan + changed["relative_difference"] = np.nan + differences.append(changed) + + if differences: + difference_frame = pd.concat(differences, ignore_index=True) + result["changed_cells"] = len(difference_frame) + result["max_absolute_difference"] = max_abs + result["max_relative_difference"] = max_rel + + has_structure_difference = bool( + only_generated + or only_reeds + or result["rows_only_generated"] + or result["rows_only_reeds"] + ) + if has_structure_difference: + result["status"] = "STRUCTURE_DIFFERENCE" + elif result["changed_cells"]: + result["status"] = "VALUE_DIFFERENCE" + else: + result["status"] = "MATCH" + return result + + +def series_columns(frame: pd.DataFrame) -> list[str]: + """Return categorical columns that identify separate plotted series.""" + excluded = {"t", "rsc_mult"} + return [ + column + for column in frame.columns + if column not in excluded and not pd.api.types.is_numeric_dtype(frame[column]) + ] + + +def metric_columns(generated: pd.DataFrame, reeds: pd.DataFrame) -> list[str]: + """Return shared numeric output columns, with familiar metrics first.""" + excluded = {"t"} + shared = set(generated.columns) & set(reeds.columns) + numeric = [ + column + for column in shared + if column not in excluded + and pd.api.types.is_numeric_dtype(generated[column]) + and pd.api.types.is_numeric_dtype(reeds[column]) + ] + preferred = [column for column in METRIC_LABELS if column in numeric] + return preferred + sorted(set(numeric) - set(preferred)) + + +def series_label(group_values: tuple, columns: list[str]) -> str: + """Build a compact label for a plotted technology series.""" + if not columns: + return "all" + return ", ".join( + f"{column}={value}" for column, value in zip(columns, group_values) + ) + + +def grouped_series(frame: pd.DataFrame, columns: list[str]): + """Split a frame into time-series groups and labels.""" + if not columns: + return [("all", frame.sort_values("t"))] + groups = [] + grouper = columns[0] if len(columns) == 1 else columns + for values, group in frame.groupby(grouper, dropna=False, sort=True): + values_tuple = values if isinstance(values, tuple) else (values,) + groups.append((series_label(values_tuple, columns), group.sort_values("t"))) + return groups + + +def plot_file( + generated_path: Path, + reeds_path: Path, + plot_dir: Path, + solid_label: str = "Generated", + dashed_label: str = "ReEDS", + reference_linestyle: str = "--", + reference_marker: str | None = None, +) -> bool: + """Plot all shared numeric metrics for one generated/ReEDS file pair.""" + generated = normalize_frame(pd.read_csv(generated_path)) + reeds = normalize_frame(pd.read_csv(reeds_path)) + metrics = metric_columns(generated, reeds) + if not metrics or "t" not in generated.columns or "t" not in reeds.columns: + return False + + identifiers = sorted(set(series_columns(generated)) | set(series_columns(reeds))) + generated_groups = grouped_series(generated, identifiers) + reeds_groups = grouped_series(reeds, identifiers) + labels = sorted({label for label, _ in generated_groups + reeds_groups}) + colors = {label: plt.cm.tab10(index % 10) for index, label in enumerate(labels)} + + columns = min(3, len(metrics)) + rows = math.ceil(len(metrics) / columns) + figure, axes = plt.subplots( + rows, + columns, + figsize=(5.4 * columns, 3.7 * rows), + squeeze=False, + constrained_layout=True, + ) + figure.suptitle( + generated_path.stem, + y=1.03, + fontsize=12, + fontweight="bold", + ) + + for axis, metric in zip(axes.flat, metrics): + for label, group in reeds_groups: + axis.plot( + group["t"], + group[metric], + color=colors[label], + linestyle=reference_linestyle, + marker=reference_marker, + markersize=3.2 if reference_marker else None, + linewidth=2.0, + alpha=0.9, + ) + for label, group in generated_groups: + axis.plot( + group["t"], + group[metric], + color=colors[label], + linestyle="-", + linewidth=1.6, + alpha=0.9, + ) + axis.set_title(METRIC_LABELS.get(metric, metric)) + axis.set_xlabel("Year") + axis.set_ylabel(METRIC_LABELS.get(metric, metric)) + axis.grid(True, alpha=0.25) + + for axis in axes.flat[len(metrics):]: + axis.remove() + + source_handles = [ + plt.Line2D( + [0], [0], color="0.25", linewidth=2, linestyle="-", label=solid_label + ), + plt.Line2D( + [0], + [0], + color="0.25", + linewidth=2, + linestyle=reference_linestyle, + marker=reference_marker, + markersize=4 if reference_marker else None, + label=dashed_label, + ), + ] + series_handles = [ + plt.Line2D([0], [0], color=colors[label], linewidth=2, label=label) + for label in labels + ] + figure.legend( + handles=source_handles + series_handles, + loc="outside lower center", + ncol=min(4, len(source_handles + series_handles)), + fontsize=8, + frameon=False, + ) + figure.savefig( + plot_dir / f"{generated_path.stem}.png", + dpi=160, + bbox_inches="tight", + ) + plt.close(figure) + return True + + +def plot_overview(summary: pd.DataFrame, plot_dir: Path, atb_year: int) -> None: + """Plot file-level validation status and difference counts.""" + plot_data = summary.sort_values( + ["status", "changed_cells"], ascending=[True, True] + ).reset_index(drop=True) + labels = plot_data["generated_file"].str.replace( + "_ATB_2024_", " | ", regex=False + ) + y = np.arange(len(plot_data)) + + figure, (axis_status, axis_counts) = plt.subplots( + 1, + 2, + figsize=(18, 14), + gridspec_kw={"width_ratios": [0.8, 2.2]}, + constrained_layout=True, + ) + figure.suptitle( + f"ATB {atb_year} output comparison overview", + fontsize=16, + fontweight="bold", + ) + status_colors = { + "MATCH": "#2a9d8f", + "VALUE_DIFFERENCE": "#e9c46a", + "STRUCTURE_DIFFERENCE": "#e76f51", + "MISSING_REEDS_FILE": "#9b2226", + } + for index, row in plot_data.iterrows(): + axis_status.barh( + index, 1, color=status_colors.get(row["status"], "0.6") + ) + axis_status.text( + 0.5, + index, + row["status"].replace("_", " "), + ha="center", + va="center", + fontsize=8, + ) + axis_status.set_yticks(y, labels) + axis_status.set_xlim(0, 1) + axis_status.set_xticks([]) + axis_status.set_title("Validation status") + axis_status.invert_yaxis() + + axis_counts.barh( + y, + plot_data["changed_cells"], + label="Changed cells", + color="#457b9d", + ) + axis_counts.barh( + y, + plot_data["rows_only_reeds"], + left=plot_data["changed_cells"], + label="Rows only in ReEDS", + color="#e76f51", + ) + axis_counts.set_yticks([]) + axis_counts.set_xlabel("Difference count") + axis_counts.set_title("Changed cells and missing generated rows") + axis_counts.grid(True, axis="x", alpha=0.25) + axis_counts.invert_yaxis() + axis_counts.legend(frameon=False, loc="lower right") + figure.savefig(plot_dir / "comparison_overview.png", dpi=160) + plt.close(figure) + + +def write_plots( + summary: pd.DataFrame | None, + generated_files: list[Path], + reeds_dir: Path, + plot_dir: Path, + atb_year: int, + solid_label: str = "Generated", + dashed_label: str = "ReEDS", + include_overview: bool = True, + reference_linestyle: str = "--", + reference_marker: str | None = None, +) -> int: + """Generate the overview and all available file-level comparison plots.""" + plot_dir.mkdir(parents=True, exist_ok=True) + for old_plot in plot_dir.glob("*.png"): + old_plot.unlink() + if include_overview: + if summary is None: + raise ValueError("A comparison summary is required for the overview plot.") + plot_overview(summary, plot_dir, atb_year) + + plotted = 0 + skipped = [] + for generated_path in generated_files: + reeds_path = reeds_dir / reeds_filename(generated_path.name) + if reeds_path.exists(): + wrote_plot = plot_file( + generated_path, + reeds_path, + plot_dir, + solid_label=solid_label, + dashed_label=dashed_label, + reference_linestyle=reference_linestyle, + reference_marker=reference_marker, + ) + if wrote_plot: + plotted += 1 + else: + skipped.append(generated_path.name) + if skipped: + raise RuntimeError( + "Could not plot generated/ReEDS file pairs because they have no " + f"shared year-based numeric metrics: {skipped}" + ) + return plotted + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Compare generated ATB outputs with current ReEDS inputs." + ) + parser.add_argument("--config", type=Path, default=DEFAULT_CONFIG_PATH) + parser.add_argument("--generated-dir", type=Path) + parser.add_argument( + "--unsmoothed-dir", + type=Path, + help="temporary pre-smoothing outputs supplied by the full pipeline", + ) + parser.add_argument("--reeds-dir", type=Path) + parser.add_argument("--plot-dir", type=Path, default=DEFAULT_PLOT_DIR) + parser.add_argument( + "--absolute-tolerance", + type=float, + default=2e-4, + help=( + "Absolute numeric tolerance (default: 2e-4, reflecting the " + "four-decimal precision of some raw ATB values)" + ), + ) + parser.add_argument("--relative-tolerance", type=float, default=1e-9) + args = parser.parse_args() + + settings = load_processing_settings(args.config) + smoothing = settings['config']['processing'].get('smooth_cost_curves', {}) + baseline_dir = args.unsmoothed_dir.resolve() if args.unsmoothed_dir else None + if args.generated_dir: + generated_dir = args.generated_dir.resolve() + elif baseline_dir is not None: + generated_dir = baseline_dir + else: + generated_dir = Path(settings['output_dir']).resolve() + reeds_dir = ( + args.reeds_dir.resolve() + if args.reeds_dir + else Path(settings['reedspath']).resolve() / "inputs" / "plant_characteristics" + ) + plot_dir = args.plot_dir.resolve() + atb_year = int(settings['atbyear']) + + generated_files = sorted(generated_dir.glob(f"*_ATB_{atb_year}_*.csv")) + if not generated_files: + raise FileNotFoundError( + f"No ATB {atb_year} outputs found in {generated_dir}" + ) + if not reeds_dir.is_dir(): + raise FileNotFoundError(f"ReEDS plant-characteristics directory not found: {reeds_dir}") + + results = [] + for generated_path in generated_files: + reeds_path = reeds_dir / reeds_filename(generated_path.name) + result = compare_file( + generated_path, + reeds_path, + args.absolute_tolerance, + args.relative_tolerance, + ) + results.append(result) + print( + f"{result['status']:>20} {generated_path.name} -> {reeds_path.name}" + ) + + summary = pd.DataFrame(results) + expected_reeds_names = {reeds_filename(path.name) for path in generated_files} + reeds_only_files = sorted( + path.name + for path in reeds_dir.glob(f"*_ATB_{atb_year}_*.csv") + if path.name not in expected_reeds_names + ) + plotted = write_plots( + summary, + generated_files, + reeds_dir, + plot_dir, + atb_year, + solid_label=( + "Before smoothing (generated)" + if baseline_dir is not None + else "Generated" + ), + dashed_label="ReEDS", + ) + print(f"\nWrote overview and {plotted} file-level plots to {plot_dir}") + print(summary["status"].value_counts().to_string()) + print( + f"ReEDS files without a generated counterpart: {len(reeds_only_files)}" + ) + for filename in reeds_only_files: + print(f" {filename}") + + if smoothing.get('enabled', False) and baseline_dir is not None: + smoothed_dir = Path(settings['output_dir']).resolve() + smoothed_files = sorted( + smoothed_dir.glob(f"*_ATB_{atb_year}_*.csv") + ) + smoothing_plot_setting = settings['config']['plotting'].get( + 'smoothing_comparison_directory', + 'figures/smoothing_comparison', + ) + smoothing_plot_dir = resolve_atb_config_path(smoothing_plot_setting) + smoothing_plotted = write_plots( + None, + smoothed_files, + baseline_dir, + smoothing_plot_dir, + atb_year, + solid_label="After smoothing", + dashed_label="Before smoothing", + include_overview=False, + reference_linestyle="None", + reference_marker="o", + ) + print( + f"Wrote {smoothing_plotted} before/after smoothing plots to " + f"{smoothing_plot_dir}" + ) + + +if __name__ == "__main__": + main() diff --git a/atb/config.yaml b/atb/config.yaml new file mode 100644 index 0000000..d9615c3 --- /dev/null +++ b/atb/config.yaml @@ -0,0 +1,98 @@ +# User-facing configuration for the ATB workflow. +# Set the workflow switches, review the paths/URLs, then run: +# python scripts/run_pipeline.py + +workflow: + scrape_raw_data: true + format_reeds_inputs: true + make_plots: true + make_comparison_plots: true + +atb: + year: 2024 + release_version: 3 + dollar_year: 2022 + +raw_data: + directory: scraped_input + # Some NLR networks inspect HTTPS with an internal certificate that may not + # be installed in the active conda environment. Normal certificate checking + # is always attempted first; this permits a warning + verify=False retry only + # after that check fails. + allow_insecure_ssl_fallback: true + flat_file: + filename: atb_2024_flat_file.csv + url: https://oedi-data-lake.s3.amazonaws.com/ATB/electricity/csv/2024/v3.0.0/ATBe.csv + workbook: + filename: atb_2024_workbook.xlsx + url: https://data.openei.org/files/6006/2024_v3_Workbook.xlsx + +processing: + reeds_repo: C:/ReEDS/ReEDS + output_directory: output + copy_to_reeds: false + technologies: all + update_costs: true + update_financials: false + sensitivity_name: null + reeds_start_year: 2010 + decimals: 6 + # Optionally remove the transition discontinuity between the historical + # series and the current ATB projection. Selective smoothing uses 2022 as an + # anchor: a contiguous historical tail below that value is raised to 2022, + # future increases are removed, near-equal plateaus are bridged from their + # last points, and short clusters of slope changes are smoothed. Higher + # historical costs and isolated ATB milestones (such as CSP in 2030) are + # preserved. Other performance inputs are intentionally not changed. + # Keep disabled for the published ReEDS-compatible outputs. + smooth_cost_curves: + enabled: true + method: selective + technologies: all + # "all" selects capcost*, fom*, and vom, including battery energy costs. + columns: all + # Apply the same anchor-aware rule to CF_mult/cf_improvement when present. + include_capacity_factor_multiplier: true + projection_start_year: 2022 + # A relative change in adjacent annual slopes at or above this value is a + # possible kink. Only clusters of 2+ changes are smoothed. + slope_change_threshold: 0.4 + max_kink_years: 4 + # Consecutive future values within 0.1% are treated as one rounded plateau. + # Its last year becomes the interpolation point, removing small stair steps + # and short flat dips. Transitions of 10% or more remain explicit steps. + similar_value_relative_tolerance: 0.001 + similar_value_absolute_tolerance: 1.0e-9 + major_step_relative_threshold: 0.1 + +# Versioned ReEDS history used before each ATB projection series begins. +# Set seed_missing_from_reeds to true only when initializing a missing file from +# processing.reeds_repo. Each run appends the first available projection year, +# so a later ATB release can move that year into history without a gap. +historical_data: + directory: manual_input/historical + dollar_year: 2022 + seed_missing_from_reeds: false + +plotting: + metrics: + - CAPEX + - Fixed O&M + - Variable O&M + case: Market + crp_years: 20 + figure_format: png + output_directory: figures + # Before/after plots are versioned; their unsmoothed inputs are held only in + # a temporary directory while the full pipeline is running. + smoothing_comparison_directory: figures/smoothing_comparison + technologies: + - Biopower + - Coal_FE + - CSP + - LandbasedWind + - NaturalGas_FE + - Nuclear + - OffShoreWind + - Utility-Scale Battery Storage + - UtilityPV diff --git a/atb/figures/smoothing_comparison/battery_ATB_2024_advanced.png b/atb/figures/smoothing_comparison/battery_ATB_2024_advanced.png new file mode 100644 index 0000000..6d75d09 Binary files /dev/null and b/atb/figures/smoothing_comparison/battery_ATB_2024_advanced.png differ diff --git a/atb/figures/smoothing_comparison/battery_ATB_2024_conservative.png b/atb/figures/smoothing_comparison/battery_ATB_2024_conservative.png new file mode 100644 index 0000000..9418179 Binary files /dev/null and b/atb/figures/smoothing_comparison/battery_ATB_2024_conservative.png differ diff --git a/atb/figures/smoothing_comparison/battery_ATB_2024_moderate.png b/atb/figures/smoothing_comparison/battery_ATB_2024_moderate.png new file mode 100644 index 0000000..5697f98 Binary files /dev/null and b/atb/figures/smoothing_comparison/battery_ATB_2024_moderate.png differ diff --git a/atb/figures/smoothing_comparison/biopower_ATB_2024_moderate.png b/atb/figures/smoothing_comparison/biopower_ATB_2024_moderate.png new file mode 100644 index 0000000..44e4ad5 Binary files /dev/null and b/atb/figures/smoothing_comparison/biopower_ATB_2024_moderate.png differ diff --git a/atb/figures/smoothing_comparison/coal-ccs_ATB_2024_advanced.png b/atb/figures/smoothing_comparison/coal-ccs_ATB_2024_advanced.png new file mode 100644 index 0000000..551b260 Binary files /dev/null and b/atb/figures/smoothing_comparison/coal-ccs_ATB_2024_advanced.png differ diff --git a/atb/figures/smoothing_comparison/coal-ccs_ATB_2024_conservative.png b/atb/figures/smoothing_comparison/coal-ccs_ATB_2024_conservative.png new file mode 100644 index 0000000..1cc7bf4 Binary files /dev/null and b/atb/figures/smoothing_comparison/coal-ccs_ATB_2024_conservative.png differ diff --git a/atb/figures/smoothing_comparison/coal-ccs_ATB_2024_moderate.png b/atb/figures/smoothing_comparison/coal-ccs_ATB_2024_moderate.png new file mode 100644 index 0000000..94f0852 Binary files /dev/null and b/atb/figures/smoothing_comparison/coal-ccs_ATB_2024_moderate.png differ diff --git a/atb/figures/smoothing_comparison/coal_ATB_2024_moderate.png b/atb/figures/smoothing_comparison/coal_ATB_2024_moderate.png new file mode 100644 index 0000000..3e4746b Binary files /dev/null and b/atb/figures/smoothing_comparison/coal_ATB_2024_moderate.png differ diff --git a/atb/figures/smoothing_comparison/csp_ATB_2024_advanced.png b/atb/figures/smoothing_comparison/csp_ATB_2024_advanced.png new file mode 100644 index 0000000..2a5188e Binary files /dev/null and b/atb/figures/smoothing_comparison/csp_ATB_2024_advanced.png differ diff --git a/atb/figures/smoothing_comparison/csp_ATB_2024_conservative.png b/atb/figures/smoothing_comparison/csp_ATB_2024_conservative.png new file mode 100644 index 0000000..a62aa43 Binary files /dev/null and b/atb/figures/smoothing_comparison/csp_ATB_2024_conservative.png differ diff --git a/atb/figures/smoothing_comparison/csp_ATB_2024_moderate.png b/atb/figures/smoothing_comparison/csp_ATB_2024_moderate.png new file mode 100644 index 0000000..fba911a Binary files /dev/null and b/atb/figures/smoothing_comparison/csp_ATB_2024_moderate.png differ diff --git a/atb/figures/smoothing_comparison/fuelcell_ATB_2024_advanced.png b/atb/figures/smoothing_comparison/fuelcell_ATB_2024_advanced.png new file mode 100644 index 0000000..95b41e6 Binary files /dev/null and b/atb/figures/smoothing_comparison/fuelcell_ATB_2024_advanced.png differ diff --git a/atb/figures/smoothing_comparison/fuelcell_ATB_2024_moderate.png b/atb/figures/smoothing_comparison/fuelcell_ATB_2024_moderate.png new file mode 100644 index 0000000..c014c0a Binary files /dev/null and b/atb/figures/smoothing_comparison/fuelcell_ATB_2024_moderate.png differ diff --git a/atb/figures/smoothing_comparison/gas-ccs_ATB_2024_advanced.png b/atb/figures/smoothing_comparison/gas-ccs_ATB_2024_advanced.png new file mode 100644 index 0000000..a70d315 Binary files /dev/null and b/atb/figures/smoothing_comparison/gas-ccs_ATB_2024_advanced.png differ diff --git a/atb/figures/smoothing_comparison/gas-ccs_ATB_2024_conservative.png b/atb/figures/smoothing_comparison/gas-ccs_ATB_2024_conservative.png new file mode 100644 index 0000000..e908450 Binary files /dev/null and b/atb/figures/smoothing_comparison/gas-ccs_ATB_2024_conservative.png differ diff --git a/atb/figures/smoothing_comparison/gas-ccs_ATB_2024_moderate.png b/atb/figures/smoothing_comparison/gas-ccs_ATB_2024_moderate.png new file mode 100644 index 0000000..573e9a1 Binary files /dev/null and b/atb/figures/smoothing_comparison/gas-ccs_ATB_2024_moderate.png differ diff --git a/atb/figures/smoothing_comparison/gas_ATB_2024_moderate.png b/atb/figures/smoothing_comparison/gas_ATB_2024_moderate.png new file mode 100644 index 0000000..2826680 Binary files /dev/null and b/atb/figures/smoothing_comparison/gas_ATB_2024_moderate.png differ diff --git a/atb/figures/smoothing_comparison/nuclear-smr_ATB_2024_advanced.png b/atb/figures/smoothing_comparison/nuclear-smr_ATB_2024_advanced.png new file mode 100644 index 0000000..721589c Binary files /dev/null and b/atb/figures/smoothing_comparison/nuclear-smr_ATB_2024_advanced.png differ diff --git a/atb/figures/smoothing_comparison/nuclear-smr_ATB_2024_conservative.png b/atb/figures/smoothing_comparison/nuclear-smr_ATB_2024_conservative.png new file mode 100644 index 0000000..8a5c1e1 Binary files /dev/null and b/atb/figures/smoothing_comparison/nuclear-smr_ATB_2024_conservative.png differ diff --git a/atb/figures/smoothing_comparison/nuclear-smr_ATB_2024_moderate.png b/atb/figures/smoothing_comparison/nuclear-smr_ATB_2024_moderate.png new file mode 100644 index 0000000..06ec48f Binary files /dev/null and b/atb/figures/smoothing_comparison/nuclear-smr_ATB_2024_moderate.png differ diff --git a/atb/figures/smoothing_comparison/nuclear_ATB_2024_advanced.png b/atb/figures/smoothing_comparison/nuclear_ATB_2024_advanced.png new file mode 100644 index 0000000..4bfbd1c Binary files /dev/null and b/atb/figures/smoothing_comparison/nuclear_ATB_2024_advanced.png differ diff --git a/atb/figures/smoothing_comparison/nuclear_ATB_2024_conservative.png b/atb/figures/smoothing_comparison/nuclear_ATB_2024_conservative.png new file mode 100644 index 0000000..639eb3f Binary files /dev/null and b/atb/figures/smoothing_comparison/nuclear_ATB_2024_conservative.png differ diff --git a/atb/figures/smoothing_comparison/nuclear_ATB_2024_moderate.png b/atb/figures/smoothing_comparison/nuclear_ATB_2024_moderate.png new file mode 100644 index 0000000..12d4260 Binary files /dev/null and b/atb/figures/smoothing_comparison/nuclear_ATB_2024_moderate.png differ diff --git a/atb/figures/smoothing_comparison/ofs-wind_ATB_2024_advanced.png b/atb/figures/smoothing_comparison/ofs-wind_ATB_2024_advanced.png new file mode 100644 index 0000000..90bbe70 Binary files /dev/null and b/atb/figures/smoothing_comparison/ofs-wind_ATB_2024_advanced.png differ diff --git a/atb/figures/smoothing_comparison/ofs-wind_ATB_2024_conservative.png b/atb/figures/smoothing_comparison/ofs-wind_ATB_2024_conservative.png new file mode 100644 index 0000000..7cff4f5 Binary files /dev/null and b/atb/figures/smoothing_comparison/ofs-wind_ATB_2024_conservative.png differ diff --git a/atb/figures/smoothing_comparison/ofs-wind_ATB_2024_moderate.png b/atb/figures/smoothing_comparison/ofs-wind_ATB_2024_moderate.png new file mode 100644 index 0000000..748a9c9 Binary files /dev/null and b/atb/figures/smoothing_comparison/ofs-wind_ATB_2024_moderate.png differ diff --git a/atb/figures/smoothing_comparison/ons-wind_ATB_2024_advanced.png b/atb/figures/smoothing_comparison/ons-wind_ATB_2024_advanced.png new file mode 100644 index 0000000..8634577 Binary files /dev/null and b/atb/figures/smoothing_comparison/ons-wind_ATB_2024_advanced.png differ diff --git a/atb/figures/smoothing_comparison/ons-wind_ATB_2024_conservative.png b/atb/figures/smoothing_comparison/ons-wind_ATB_2024_conservative.png new file mode 100644 index 0000000..a19194e Binary files /dev/null and b/atb/figures/smoothing_comparison/ons-wind_ATB_2024_conservative.png differ diff --git a/atb/figures/smoothing_comparison/ons-wind_ATB_2024_moderate.png b/atb/figures/smoothing_comparison/ons-wind_ATB_2024_moderate.png new file mode 100644 index 0000000..fbb2000 Binary files /dev/null and b/atb/figures/smoothing_comparison/ons-wind_ATB_2024_moderate.png differ diff --git a/atb/figures/smoothing_comparison/upv_ATB_2024_advanced.png b/atb/figures/smoothing_comparison/upv_ATB_2024_advanced.png new file mode 100644 index 0000000..bceff03 Binary files /dev/null and b/atb/figures/smoothing_comparison/upv_ATB_2024_advanced.png differ diff --git a/atb/figures/smoothing_comparison/upv_ATB_2024_conservative.png b/atb/figures/smoothing_comparison/upv_ATB_2024_conservative.png new file mode 100644 index 0000000..713cdee Binary files /dev/null and b/atb/figures/smoothing_comparison/upv_ATB_2024_conservative.png differ diff --git a/atb/figures/smoothing_comparison/upv_ATB_2024_moderate.png b/atb/figures/smoothing_comparison/upv_ATB_2024_moderate.png new file mode 100644 index 0000000..ad01767 Binary files /dev/null and b/atb/figures/smoothing_comparison/upv_ATB_2024_moderate.png differ diff --git a/atb/manual_input/README.md b/atb/manual_input/README.md new file mode 100644 index 0000000..3f6a113 --- /dev/null +++ b/atb/manual_input/README.md @@ -0,0 +1,25 @@ +# manual_input/ + +Inputs that are not available from the raw ATB downloads. They are consumed by +`generate_atb_files.py`. + +- `historical/` — versioned ReEDS rows that precede each scraped ATB projection + series. The formatter rolls the first current projection year into these + files automatically after every run. +- `csp_cost_ratios_.csv` — CSP configuration cost multipliers (csp1–csp4) + relative to the base configuration (csp2). They come from the separate ReEDS + CSP thermal-storage sizing/SAM analysis rather than either ATB download. The + 2024 multipliers reproduce the published ReEDS ATB 2024 configurations. +- `offshore_cost_multipliers_.csv` — ReEDS fixed-bottom and floating + configuration adjustments applied to the corresponding ATB offshore class + proxies. These account for ReEDS configuration assumptions outside the raw + ATB flat file. +- `coal_projection_overrides_.csv` — published ReEDS coal projection + values retained where a later ATB errata release changed intermediate years. + For 2024 this pins only 2033–2034; all other coal years come from ATB v3. +- `battery_costs_.csv` — power/energy capital-cost split for an ATB year + whose Excel workbook has not yet been published. It is a temporary fallback + for `format_continuous_battery` and can be removed after publication. + +When a new ATB year is released, review the year-specific CSP or battery inputs. +The files under `historical/` update automatically. diff --git a/atb/manual_input/coal_projection_overrides_2024.csv b/atb/manual_input/coal_projection_overrides_2024.csv new file mode 100644 index 0000000..8f5083f --- /dev/null +++ b/atb/manual_input/coal_projection_overrides_2024.csv @@ -0,0 +1,11 @@ +tech,Scenario,i,t,capcost,fom,vom,heatrate +coal,Moderate,Coal-IGCC,2033,5349.3,160.4,15.79,8.308 +coal,Moderate,Coal-IGCC,2034,5328.1,159.8,15.74,8.308 +coal,Moderate,Coal-new,2033,2991.2,81.7,8.83,8.205 +coal,Moderate,Coal-new,2034,2973.7,81.2,8.79,8.182 +coal-ccs,Advanced,coal-CCS_mod,2033,4399.4,116.3,13.23,9.588 +coal-ccs,Advanced,coal-CCS_mod,2034,4323.3,114.5,12.99,9.476 +coal-ccs,Conservative,coal-CCS_mod,2033,4723.8,123.6,15.49,10.938 +coal-ccs,Conservative,coal-CCS_mod,2034,4674.7,122.4,15.44,10.938 +coal-ccs,Moderate,coal-CCS_mod,2033,4561.6,119.9,14.36,10.319 +coal-ccs,Moderate,coal-CCS_mod,2034,4499.0,118.5,14.22,10.263 diff --git a/atb/manual_input/csp_cost_ratios_2024.csv b/atb/manual_input/csp_cost_ratios_2024.csv new file mode 100644 index 0000000..0efeb29 --- /dev/null +++ b/atb/manual_input/csp_cost_ratios_2024.csv @@ -0,0 +1,5 @@ +type,duration,solar_multiple,ratio,base_tech +csp1,14,2.7,1.120642878353,0 +csp2,10,2.4,1,1 +csp3,8,1.3,0.709285122138,0 +csp4,6,1,0.612585771590,0 diff --git a/atb/manual_input/historical/README.md b/atb/manual_input/historical/README.md new file mode 100644 index 0000000..132f62b --- /dev/null +++ b/atb/manual_input/historical/README.md @@ -0,0 +1,26 @@ +# ATB historical baseline + +These CSVs provide the ReEDS years that are not present in the current scraped +ATB release. They were initially seeded from +`C:/ReEDS/ReEDS/inputs/plant_characteristics/*_ATB_2024_*.csv` and are stored in +constant 2022 dollars, as configured by `historical_data.dollar_year`. + +For each technology series and scenario, `generate_atb_files.py`: + +1. reads these rows before the first year available in the current raw ATB; +2. concatenates the current scraped ATB projection from that year onward; +3. preserves complete historical series that are no longer published by ATB; +4. appends the first current projection year to this baseline if it is missing. + +Step 4 makes the update idempotent and prepares continuity for the next annual +release. For example, ATB 2024 uses history through 2021 and projections from +2022. After the run, 2022 is stored here; if the next release begins in 2023, +the formatter uses history through 2022 and the new projection from 2023. + +The formatter validates every technology/scenario series from +`processing.reeds_start_year` through its final year and stops with an error if +any year is missing. + +Missing files may be initialized from the configured ReEDS repository when +`historical_data.seed_missing_from_reeds` is `true`. Once seeded, these files +are the versioned source of truth and should be committed with the pipeline. diff --git a/atb/manual_input/historical/battery_ATB_historical_advanced.csv b/atb/manual_input/historical/battery_ATB_historical_advanced.csv new file mode 100644 index 0000000..e422391 --- /dev/null +++ b/atb/manual_input/historical/battery_ATB_historical_advanced.csv @@ -0,0 +1,14 @@ +i,t,capcost,capcost_energy,fom,fom_energy,vom,rte +battery_li,2010,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2011,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2012,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2013,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2014,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2015,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2016,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2017,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2018,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2019,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2020,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2021,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2022,363.01,390.93,9.08,9.77,0.0,0.85 diff --git a/atb/manual_input/historical/battery_ATB_historical_conservative.csv b/atb/manual_input/historical/battery_ATB_historical_conservative.csv new file mode 100644 index 0000000..e422391 --- /dev/null +++ b/atb/manual_input/historical/battery_ATB_historical_conservative.csv @@ -0,0 +1,14 @@ +i,t,capcost,capcost_energy,fom,fom_energy,vom,rte +battery_li,2010,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2011,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2012,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2013,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2014,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2015,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2016,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2017,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2018,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2019,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2020,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2021,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2022,363.01,390.93,9.08,9.77,0.0,0.85 diff --git a/atb/manual_input/historical/battery_ATB_historical_moderate.csv b/atb/manual_input/historical/battery_ATB_historical_moderate.csv new file mode 100644 index 0000000..e422391 --- /dev/null +++ b/atb/manual_input/historical/battery_ATB_historical_moderate.csv @@ -0,0 +1,14 @@ +i,t,capcost,capcost_energy,fom,fom_energy,vom,rte +battery_li,2010,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2011,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2012,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2013,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2014,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2015,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2016,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2017,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2018,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2019,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2020,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2021,363.01,390.93,9.08,9.77,0.0,0.85 +battery_li,2022,363.01,390.93,9.08,9.77,0.0,0.85 diff --git a/atb/manual_input/historical/biopower_ATB_historical_advanced.csv b/atb/manual_input/historical/biopower_ATB_historical_advanced.csv new file mode 100644 index 0000000..6d0a43f --- /dev/null +++ b/atb/manual_input/historical/biopower_ATB_historical_advanced.csv @@ -0,0 +1,14 @@ +i,t,capcost,fom,vom,heatrate +biopower,2010,4726.476766,142.9276928,5.485907804,13.5 +biopower,2011,4712.415325,142.9276928,5.485907804,13.5 +biopower,2012,4698.353884,142.9276928,5.485907804,13.5 +biopower,2013,4684.292442,142.9276928,5.485907804,13.5 +biopower,2014,4670.231002,142.9276928,5.485907804,13.5 +biopower,2015,4656.169559,142.9276928,5.485907804,13.5 +biopower,2016,4642.108118,142.9276928,5.485907804,13.5 +biopower,2017,4628.046676,142.9276928,5.485907804,13.5 +biopower,2018,4613.985236,142.9276928,5.485907804,13.5 +biopower,2019,4599.923794,142.9276928,5.485907804,13.5 +biopower,2020,4617.272559,170.5719249,5.419053793,13.5 +biopower,2021,5183.685345,169.7976,5.4432,13.5 +biopower,2022,4829.860171,163.66602,5.24664,13.5 diff --git a/atb/manual_input/historical/biopower_ATB_historical_conservative.csv b/atb/manual_input/historical/biopower_ATB_historical_conservative.csv new file mode 100644 index 0000000..6d0a43f --- /dev/null +++ b/atb/manual_input/historical/biopower_ATB_historical_conservative.csv @@ -0,0 +1,14 @@ +i,t,capcost,fom,vom,heatrate +biopower,2010,4726.476766,142.9276928,5.485907804,13.5 +biopower,2011,4712.415325,142.9276928,5.485907804,13.5 +biopower,2012,4698.353884,142.9276928,5.485907804,13.5 +biopower,2013,4684.292442,142.9276928,5.485907804,13.5 +biopower,2014,4670.231002,142.9276928,5.485907804,13.5 +biopower,2015,4656.169559,142.9276928,5.485907804,13.5 +biopower,2016,4642.108118,142.9276928,5.485907804,13.5 +biopower,2017,4628.046676,142.9276928,5.485907804,13.5 +biopower,2018,4613.985236,142.9276928,5.485907804,13.5 +biopower,2019,4599.923794,142.9276928,5.485907804,13.5 +biopower,2020,4617.272559,170.5719249,5.419053793,13.5 +biopower,2021,5183.685345,169.7976,5.4432,13.5 +biopower,2022,4829.860171,163.66602,5.24664,13.5 diff --git a/atb/manual_input/historical/biopower_ATB_historical_moderate.csv b/atb/manual_input/historical/biopower_ATB_historical_moderate.csv new file mode 100644 index 0000000..6d0a43f --- /dev/null +++ b/atb/manual_input/historical/biopower_ATB_historical_moderate.csv @@ -0,0 +1,14 @@ +i,t,capcost,fom,vom,heatrate +biopower,2010,4726.476766,142.9276928,5.485907804,13.5 +biopower,2011,4712.415325,142.9276928,5.485907804,13.5 +biopower,2012,4698.353884,142.9276928,5.485907804,13.5 +biopower,2013,4684.292442,142.9276928,5.485907804,13.5 +biopower,2014,4670.231002,142.9276928,5.485907804,13.5 +biopower,2015,4656.169559,142.9276928,5.485907804,13.5 +biopower,2016,4642.108118,142.9276928,5.485907804,13.5 +biopower,2017,4628.046676,142.9276928,5.485907804,13.5 +biopower,2018,4613.985236,142.9276928,5.485907804,13.5 +biopower,2019,4599.923794,142.9276928,5.485907804,13.5 +biopower,2020,4617.272559,170.5719249,5.419053793,13.5 +biopower,2021,5183.685345,169.7976,5.4432,13.5 +biopower,2022,4829.860171,163.66602,5.24664,13.5 diff --git a/atb/manual_input/historical/coal-ccs_ATB_historical_advanced.csv b/atb/manual_input/historical/coal-ccs_ATB_historical_advanced.csv new file mode 100644 index 0000000..312e56b --- /dev/null +++ b/atb/manual_input/historical/coal-ccs_ATB_historical_advanced.csv @@ -0,0 +1,14 @@ +i,t,capcost,fom,vom,heatrate +coal-CCS_mod,2010,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2011,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2012,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2013,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2014,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2015,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2016,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2017,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2018,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2019,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2020,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2021,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2022,5312.5,137.2,16.12,10.938 diff --git a/atb/manual_input/historical/coal-ccs_ATB_historical_conservative.csv b/atb/manual_input/historical/coal-ccs_ATB_historical_conservative.csv new file mode 100644 index 0000000..312e56b --- /dev/null +++ b/atb/manual_input/historical/coal-ccs_ATB_historical_conservative.csv @@ -0,0 +1,14 @@ +i,t,capcost,fom,vom,heatrate +coal-CCS_mod,2010,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2011,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2012,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2013,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2014,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2015,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2016,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2017,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2018,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2019,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2020,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2021,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2022,5312.5,137.2,16.12,10.938 diff --git a/atb/manual_input/historical/coal-ccs_ATB_historical_moderate.csv b/atb/manual_input/historical/coal-ccs_ATB_historical_moderate.csv new file mode 100644 index 0000000..312e56b --- /dev/null +++ b/atb/manual_input/historical/coal-ccs_ATB_historical_moderate.csv @@ -0,0 +1,14 @@ +i,t,capcost,fom,vom,heatrate +coal-CCS_mod,2010,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2011,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2012,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2013,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2014,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2015,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2016,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2017,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2018,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2019,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2020,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2021,5125.032,131.868,15.8652,10.938 +coal-CCS_mod,2022,5312.5,137.2,16.12,10.938 diff --git a/atb/manual_input/historical/coal_ATB_historical_moderate.csv b/atb/manual_input/historical/coal_ATB_historical_moderate.csv new file mode 100644 index 0000000..8e77235 --- /dev/null +++ b/atb/manual_input/historical/coal_ATB_historical_moderate.csv @@ -0,0 +1,247 @@ +i,t,capcost,fom,vom,heatrate +Coal-IGCC,2010,5851.647055,163.5759711,16.43772529,9.325 +Coal-IGCC,2011,5803.570143,163.5759711,16.43772529,9.239333333 +Coal-IGCC,2012,5755.493231,163.5759711,16.43772529,9.153666667 +Coal-IGCC,2013,5707.416318,163.5759711,16.43772529,9.068 +Coal-IGCC,2014,5659.339405,163.5759711,16.43772529,8.982333333 +Coal-IGCC,2015,5611.262494,163.5759711,16.43772529,8.896666667 +Coal-IGCC,2016,5563.185581,163.5759711,16.43772529,8.811 +Coal-IGCC,2017,5515.108668,163.5759711,16.43772529,8.725333333 +Coal-IGCC,2018,5467.031756,163.5759711,16.43772529,8.639666667 +Coal-IGCC,2019,5418.954843,163.5759711,16.43772529,8.554 +Coal-IGCC,2020,5030.656241,158.9140095,16.23740698,8.45 +Coal-IGCC,2021,5402.592,162.108,15.9516,8.308 +Coal-IGCC,2022,5603.2,167.5,16.44,8.308 +Coal-IGCC,2023,5582.1,166.9,16.39,8.308 +Coal-IGCC,2024,5560.9,166.3,16.33,8.308 +Coal-IGCC,2025,5539.7,165.7,16.28,8.308 +Coal-IGCC,2026,5518.6,165.2,16.22,8.308 +Coal-IGCC,2027,5497.4,164.6,16.17,8.308 +Coal-IGCC,2028,5476.3,164.0,16.12,8.308 +Coal-IGCC,2029,5455.1,163.4,16.06,8.308 +Coal-IGCC,2030,5433.9,162.8,16.01,8.308 +Coal-IGCC,2031,5412.8,162.2,15.96,8.308 +Coal-IGCC,2032,5391.6,161.6,15.9,8.308 +Coal-IGCC,2033,5349.3,160.4,15.79,8.308 +Coal-IGCC,2034,5328.1,159.8,15.74,8.308 +Coal-IGCC,2035,5328.1,159.8,15.74,8.308 +Coal-IGCC,2036,5306.9,159.2,15.69,8.308 +Coal-IGCC,2037,5285.8,158.7,15.63,8.308 +Coal-IGCC,2038,5264.6,158.1,15.58,8.308 +Coal-IGCC,2039,5243.5,157.5,15.53,8.308 +Coal-IGCC,2040,5222.3,156.9,15.47,8.308 +Coal-IGCC,2041,5201.1,156.3,15.42,8.308 +Coal-IGCC,2042,5180.0,155.7,15.36,8.308 +Coal-IGCC,2043,5158.8,155.1,15.31,8.308 +Coal-IGCC,2044,5137.6,154.5,15.26,8.308 +Coal-IGCC,2045,5116.5,153.9,15.2,8.308 +Coal-IGCC,2046,5095.3,153.3,15.15,8.308 +Coal-IGCC,2047,5074.1,152.7,15.1,8.308 +Coal-IGCC,2048,5053.0,152.2,15.04,8.308 +Coal-IGCC,2049,5031.8,151.6,14.99,8.308 +Coal-IGCC,2050,5010.7,151.0,14.93,8.308 +Coal-new,2010,3066.6202,82.18862648,8.997250754,8.83 +Coal-new,2011,3057.462693,82.18862648,8.997250754,8.790333333 +Coal-new,2012,3048.305185,82.18862648,8.997250754,8.750666667 +Coal-new,2013,3039.147678,82.18862648,8.997250754,8.711 +Coal-new,2014,3029.990171,82.18862648,8.997250754,8.671333333 +Coal-new,2015,3020.832665,82.18862648,8.997250754,8.631666667 +Coal-new,2016,3011.675157,82.18862648,8.997250754,8.592 +Coal-new,2017,3002.51765,82.18862648,8.997250754,8.552333333 +Coal-new,2018,2993.360143,82.18862648,8.997250754,8.512666667 +Coal-new,2019,2984.202635,82.18862648,8.997250754,8.473 +Coal-new,2020,2903.736846,83.67465988,8.887605766,8.473 +Coal-new,2021,3085.236,83.808,9.1368,8.49 +Coal-new,2022,3201.2,86.5,9.3,8.49 +Coal-new,2023,3183.7,86.1,9.26,8.466 +Coal-new,2024,3166.2,85.7,9.22,8.442 +Coal-new,2025,3148.7,85.3,9.18,8.419 +Coal-new,2026,3131.2,84.9,9.14,8.395 +Coal-new,2027,3113.7,84.5,9.1,8.371 +Coal-new,2028,3096.2,84.1,9.06,8.347 +Coal-new,2029,3078.7,83.7,9.03,8.324 +Coal-new,2030,3061.2,83.3,8.99,8.3 +Coal-new,2031,3043.7,82.9,8.95,8.276 +Coal-new,2032,3026.2,82.5,8.91,8.253 +Coal-new,2033,2991.2,81.7,8.83,8.205 +Coal-new,2034,2973.7,81.2,8.79,8.182 +Coal-new,2035,2973.7,81.2,8.79,8.182 +Coal-new,2036,2945.2,80.6,8.76,8.182 +Coal-new,2037,2916.6,79.9,8.73,8.182 +Coal-new,2038,2888.1,79.3,8.7,8.182 +Coal-new,2039,2859.5,78.6,8.67,8.182 +Coal-new,2040,2830.9,77.9,8.64,8.182 +Coal-new,2041,2802.4,77.3,8.61,8.182 +Coal-new,2042,2773.8,76.6,8.58,8.182 +Coal-new,2043,2745.3,75.9,8.55,8.182 +Coal-new,2044,2716.7,75.3,8.52,8.182 +Coal-new,2045,2688.1,74.6,8.49,8.182 +Coal-new,2046,2659.6,74.0,8.46,8.182 +Coal-new,2047,2631.0,73.3,8.43,8.182 +Coal-new,2048,2602.5,72.6,8.4,8.182 +Coal-new,2049,2573.9,72.0,8.37,8.182 +Coal-new,2050,2545.4,71.3,8.34,8.182 +CoalOldScr,2010,3029.248836,41.49039589,7.345591952,9.984242986 +CoalOldScr,2011,3020.202926,41.49039589,7.498498976,9.984242986 +CoalOldScr,2012,3011.157017,41.49039589,7.651405997,9.984242986 +CoalOldScr,2013,3002.111109,41.49039589,7.804313019,9.984242986 +CoalOldScr,2014,2993.065199,41.49039589,7.957220041,9.984242986 +CoalOldScr,2015,2984.01929,41.49039589,8.110127063,9.984242986 +CoalOldScr,2016,2974.973381,41.49039589,8.27894877,9.984242986 +CoalOldScr,2017,2965.927471,41.49039589,8.447770478,9.984242986 +CoalOldScr,2018,2956.881562,41.49039589,8.616592186,9.984242986 +CoalOldScr,2019,2947.835654,41.49039589,8.785413895,9.984242986 +CoalOldScr,2020,2938.789744,41.49039589,8.954235602,9.984242986 +CoalOldScr,2021,2929.743835,41.49039589,9.140628409,9.984242986 +CoalOldScr,2022,2920.697925,41.49039589,9.327021215,9.984242986 +CoalOldScr,2023,2901.475368,41.49039589,9.513414022,9.984242986 +CoalOldScr,2024,2901.475368,41.49039589,9.699806829,9.984242986 +CoalOldScr,2025,2884.514289,41.49039589,9.886199635,9.984242986 +CoalOldScr,2026,2853.984346,41.49039589,10.09199235,9.984242986 +CoalOldScr,2027,2823.454401,41.49039589,10.29778508,9.984242986 +CoalOldScr,2028,2791.79372,41.49039589,10.50357779,9.984242986 +CoalOldScr,2029,2760.133038,41.49039589,10.70937051,9.984242986 +CoalOldScr,2030,2725.080139,41.49039589,10.91516323,9.984242986 +CoalOldScr,2031,2694.550196,41.49039589,11.14237502,9.984242986 +CoalOldScr,2032,2658.366559,41.49039589,11.36958682,9.984242986 +CoalOldScr,2033,2621.052184,41.49039589,11.59679861,9.984242986 +CoalOldScr,2034,2602.960366,41.49039589,11.8240104,9.984242986 +CoalOldScr,2035,2584.868547,41.49039589,12.05122218,9.984242986 +CoalOldScr,2036,2565.64599,41.49039589,12.30208237,9.984242986 +CoalOldScr,2037,2547.554172,41.49039589,12.55294255,9.984242986 +CoalOldScr,2038,2529.462354,41.49039589,12.80380272,9.984242986 +CoalOldScr,2039,2513.632013,41.49039589,13.0546629,9.984242986 +CoalOldScr,2040,2495.540194,41.49039589,13.30552308,9.984242986 +CoalOldScr,2041,2475.186899,41.49039589,13.58249298,9.984242986 +CoalOldScr,2042,2458.225818,41.49039589,13.85946288,9.984242986 +CoalOldScr,2043,2441.264739,41.49039589,14.13643279,9.984242986 +CoalOldScr,2044,2424.30366,41.49039589,14.4134027,9.984242986 +CoalOldScr,2045,2409.604057,41.49039589,14.6903726,9.984242986 +CoalOldScr,2046,2392.642977,41.49039589,14.99616976,9.984242986 +CoalOldScr,2047,2373.42042,41.49039589,15.30196691,9.984242986 +CoalOldScr,2048,2354.197863,41.49039589,15.60776407,9.984242986 +CoalOldScr,2049,2336.106045,41.49039589,15.91356123,9.984242986 +CoalOldScr,2050,2295.399453,41.49039589,16.21935838,9.984242986 +CoalOldUns,2010,2361.107915,41.49039589,7.345591952,10.26026846 +CoalOldUns,2011,2352.062005,41.49039589,7.498498976,10.26026846 +CoalOldUns,2012,2343.016096,41.49039589,7.651405997,10.26026846 +CoalOldUns,2013,2333.970188,41.49039589,7.804313019,10.26026846 +CoalOldUns,2014,2324.924278,41.49039589,7.957220041,10.26026846 +CoalOldUns,2015,2315.878369,41.49039589,8.110127063,10.26026846 +CoalOldUns,2016,2306.832459,41.49039589,8.27894877,10.26026846 +CoalOldUns,2017,2297.78655,41.49039589,8.447770478,10.26026846 +CoalOldUns,2018,2288.740641,41.49039589,8.616592186,10.26026846 +CoalOldUns,2019,2279.694732,41.49039589,8.785413895,10.26026846 +CoalOldUns,2020,2270.648823,41.49039589,8.954235602,10.26026846 +CoalOldUns,2021,2261.602914,41.49039589,9.140628409,10.26026846 +CoalOldUns,2022,2252.557004,41.49039589,9.327021215,10.26026846 +CoalOldUns,2023,2233.334447,41.49039589,9.513414022,10.26026846 +CoalOldUns,2024,2233.334447,41.49039589,9.699806829,10.26026846 +CoalOldUns,2025,2216.373368,41.49039589,9.886199635,10.26026846 +CoalOldUns,2026,2185.843425,41.49039589,10.09199235,10.26026846 +CoalOldUns,2027,2155.31348,41.49039589,10.29778508,10.26026846 +CoalOldUns,2028,2123.652799,41.49039589,10.50357779,10.26026846 +CoalOldUns,2029,2091.992117,41.49039589,10.70937051,10.26026846 +CoalOldUns,2030,2056.939218,41.49039589,10.91516323,10.26026846 +CoalOldUns,2031,2026.409275,41.49039589,11.14237502,10.26026846 +CoalOldUns,2032,1990.225638,41.49039589,11.36958682,10.26026846 +CoalOldUns,2033,1952.911263,41.49039589,11.59679861,10.26026846 +CoalOldUns,2034,1934.819445,41.49039589,11.8240104,10.26026846 +CoalOldUns,2035,1916.727626,41.49039589,12.05122218,10.26026846 +CoalOldUns,2036,1897.505069,41.49039589,12.30208237,10.26026846 +CoalOldUns,2037,1879.413251,41.49039589,12.55294255,10.26026846 +CoalOldUns,2038,1861.321433,41.49039589,12.80380272,10.26026846 +CoalOldUns,2039,1845.491092,41.49039589,13.0546629,10.26026846 +CoalOldUns,2040,1827.399273,41.49039589,13.30552308,10.26026846 +CoalOldUns,2041,1807.045978,41.49039589,13.58249298,10.26026846 +CoalOldUns,2042,1790.084897,41.49039589,13.85946288,10.26026846 +CoalOldUns,2043,1773.123818,41.49039589,14.13643279,10.26026846 +CoalOldUns,2044,1756.162739,41.49039589,14.4134027,10.26026846 +CoalOldUns,2045,1741.463136,41.49039589,14.6903726,10.26026846 +CoalOldUns,2046,1724.502056,41.49039589,14.99616976,10.26026846 +CoalOldUns,2047,1705.279499,41.49039589,15.30196691,10.26026846 +CoalOldUns,2048,1686.056942,41.49039589,15.60776407,10.26026846 +CoalOldUns,2049,1667.965124,41.49039589,15.91356123,10.26026846 +CoalOldUns,2050,1627.258533,41.49039589,16.21935838,10.26026846 +CofireNew,2010,3389.016969,81.18703486,8.887605766,8.83 +CofireNew,2011,3379.97106,81.18703486,8.887605766,8.790333333 +CofireNew,2012,3370.92515,81.18703486,8.887605766,8.750666667 +CofireNew,2013,3361.879241,81.18703486,8.887605766,8.711 +CofireNew,2014,3352.833332,81.18703486,8.887605766,8.671333333 +CofireNew,2015,3343.787423,81.18703486,8.887605766,8.631666667 +CofireNew,2016,3334.741514,81.18703486,8.887605766,8.592 +CofireNew,2017,3325.695605,81.18703486,8.887605766,8.552333333 +CofireNew,2018,3316.649695,81.18703486,8.887605766,8.512666667 +CofireNew,2019,3307.603786,81.18703486,8.887605766,8.473 +CofireNew,2020,3298.557876,81.18703486,8.887605766,8.473 +CofireNew,2021,3289.511968,81.18703486,8.887605766,8.473 +CofireNew,2022,3280.466059,81.18703486,8.887605766,8.473 +CofireNew,2023,3261.243502,81.18703486,8.887605766,8.473 +CofireNew,2024,3261.243502,81.18703486,8.887605766,8.473 +CofireNew,2025,3244.282421,80.73473941,8.819761448,8.473 +CofireNew,2026,3213.752478,80.28244395,8.763224515,8.386 +CofireNew,2027,3183.222535,79.83014848,8.695380197,8.3 +CofireNew,2028,3151.561853,79.37785302,8.627535878,8.213 +CofireNew,2029,3119.90117,78.92555757,8.570998945,8.126 +CofireNew,2030,3084.848272,78.47326211,8.503154627,8.04 +CofireNew,2031,3054.318329,78.02096665,8.435310308,7.953 +CofireNew,2032,3018.134693,77.5686712,8.378773376,7.866 +CofireNew,2033,2980.820317,77.11637574,8.310929057,7.78 +CofireNew,2034,2962.728498,77.11637574,8.310929057,7.78 +CofireNew,2035,2944.636681,77.11637574,8.310929057,7.78 +CofireNew,2036,2925.414123,77.11637574,8.310929057,7.78 +CofireNew,2037,2907.322305,77.11637574,8.310929057,7.78 +CofireNew,2038,2889.230487,77.11637574,8.310929057,7.78 +CofireNew,2039,2873.400145,77.11637574,8.310929057,7.78 +CofireNew,2040,2855.308328,77.11637574,8.310929057,7.78 +CofireNew,2041,2834.955032,77.11637574,8.310929057,7.78 +CofireNew,2042,2817.993952,77.11637574,8.310929057,7.78 +CofireNew,2043,2801.032872,77.11637574,8.310929057,7.78 +CofireNew,2044,2784.071792,77.11637574,8.310929057,7.78 +CofireNew,2045,2769.37219,77.11637574,8.310929057,7.78 +CofireNew,2046,2752.41111,77.11637574,8.310929057,7.78 +CofireNew,2047,2733.188553,77.11637574,8.310929057,7.78 +CofireNew,2048,2713.965996,77.11637574,8.310929057,7.78 +CofireNew,2049,2695.874178,77.11637574,8.310929057,7.78 +CofireNew,2050,2655.167586,77.11637574,8.310929057,7.78 +CofireOld,2010,3389.016969,41.49039589,7.345591952,10.74020434 +CofireOld,2011,3379.97106,41.49039589,7.498498976,10.74020434 +CofireOld,2012,3370.92515,41.49039589,7.651405997,10.74020434 +CofireOld,2013,3361.879241,41.49039589,7.804313019,10.74020434 +CofireOld,2014,3352.833332,41.49039589,7.957220041,10.74020434 +CofireOld,2015,3343.787423,41.49039589,8.110127063,10.74020434 +CofireOld,2016,3334.741514,41.49039589,8.27894877,10.74020434 +CofireOld,2017,3325.695605,41.49039589,8.447770478,10.74020434 +CofireOld,2018,3316.649695,41.49039589,8.616592186,10.74020434 +CofireOld,2019,3307.603786,41.49039589,8.785413895,10.74020434 +CofireOld,2020,3298.557876,41.49039589,8.954235602,10.74020434 +CofireOld,2021,3289.511968,41.49039589,9.140628409,10.74020434 +CofireOld,2022,3280.466059,41.49039589,9.327021215,10.74020434 +CofireOld,2023,3261.243502,41.49039589,9.513414022,10.74020434 +CofireOld,2024,3261.243502,41.49039589,9.699806829,10.74020434 +CofireOld,2025,3244.282421,41.49039589,9.886199635,10.74020434 +CofireOld,2026,3213.752478,41.49039589,10.09199235,10.74020434 +CofireOld,2027,3183.222535,41.49039589,10.29778508,10.74020434 +CofireOld,2028,3151.561853,41.49039589,10.50357779,10.74020434 +CofireOld,2029,3119.90117,41.49039589,10.70937051,10.74020434 +CofireOld,2030,3084.848272,41.49039589,10.91516323,10.74020434 +CofireOld,2031,3054.318329,41.49039589,11.14237502,10.74020434 +CofireOld,2032,3018.134693,41.49039589,11.36958682,10.74020434 +CofireOld,2033,2980.820317,41.49039589,11.59679861,10.74020434 +CofireOld,2034,2962.728498,41.49039589,11.8240104,10.74020434 +CofireOld,2035,2944.636681,41.49039589,12.05122218,10.74020434 +CofireOld,2036,2925.414123,41.49039589,12.30208237,10.74020434 +CofireOld,2037,2907.322305,41.49039589,12.55294255,10.74020434 +CofireOld,2038,2889.230487,41.49039589,12.80380272,10.74020434 +CofireOld,2039,2873.400145,41.49039589,13.0546629,10.74020434 +CofireOld,2040,2855.308328,41.49039589,13.30552308,10.74020434 +CofireOld,2041,2834.955032,41.49039589,13.58249298,10.74020434 +CofireOld,2042,2817.993952,41.49039589,13.85946288,10.74020434 +CofireOld,2043,2801.032872,41.49039589,14.13643279,10.74020434 +CofireOld,2044,2784.071792,41.49039589,14.4134027,10.74020434 +CofireOld,2045,2769.37219,41.49039589,14.6903726,10.74020434 +CofireOld,2046,2752.41111,41.49039589,14.99616976,10.74020434 +CofireOld,2047,2733.188553,41.49039589,15.30196691,10.74020434 +CofireOld,2048,2713.965996,41.49039589,15.60776407,10.74020434 +CofireOld,2049,2695.874178,41.49039589,15.91356123,10.74020434 +CofireOld,2050,2655.167586,41.49039589,16.21935838,10.74020434 diff --git a/atb/manual_input/historical/csp_ATB_historical_advanced.csv b/atb/manual_input/historical/csp_ATB_historical_advanced.csv new file mode 100644 index 0000000..fe14d08 --- /dev/null +++ b/atb/manual_input/historical/csp_ATB_historical_advanced.csv @@ -0,0 +1,53 @@ +type,t,capcost,fom,vom +csp1,2010,10118.39247,83.63396315,4.892457604 +csp1,2011,10118.39247,83.63396315,4.892457604 +csp1,2012,10118.39247,83.63396315,4.892457604 +csp1,2013,10118.39247,83.63396315,4.892457604 +csp1,2014,10118.39247,83.63396315,4.892457604 +csp1,2015,10118.39247,83.63396315,4.892457604 +csp1,2016,10118.39247,83.63396315,4.892457604 +csp1,2017,10118.39247,83.63396315,4.892457604 +csp1,2018,9655.056558,83.63396315,4.807945152 +csp1,2019,9205.528937,83.63396315,4.807945152 +csp1,2020,8731.09044,83.63396315,4.621539781 +csp1,2021,8256.651943,83.63396315,4.43513441 +csp1,2022,8233.260436,83.633757,4.435123 +csp2,2010,9022.519272,78.75663459,4.892457604 +csp2,2011,9022.519272,78.75663459,4.892457604 +csp2,2012,9022.519272,78.75663459,4.892457604 +csp2,2013,9022.519272,78.75663459,4.892457604 +csp2,2014,9022.519272,78.75663459,4.892457604 +csp2,2015,9022.519272,78.75663459,4.892457604 +csp2,2016,9022.519272,78.75663459,4.892457604 +csp2,2017,9022.519272,78.75663459,4.892457604 +csp2,2018,8605.929835,77.39619026,4.807945152 +csp2,2019,7448.628019,75.10677024,4.006648147 +csp2,2020,7408.195726,74.86846512,3.982154074 +csp2,2021,7367.763433,74.63016,3.95766 +csp2,2022,7346.908275,74.63016,3.95766 +csp3,2010,6378.213448,78.75663459,4.892457604 +csp3,2011,6378.213448,78.75663459,4.892457604 +csp3,2012,6378.213448,78.75663459,4.892457604 +csp3,2013,6378.213448,78.75663459,4.892457604 +csp3,2014,6378.213448,78.75663459,4.892457604 +csp3,2015,6378.213448,78.75663459,4.892457604 +csp3,2016,6378.213448,78.75663459,4.892457604 +csp3,2017,6378.213448,78.75663459,4.892457604 +csp3,2018,6290.932719,78.78932168,4.894488165 +csp3,2019,6036.47607,78.78932168,4.894488165 +csp3,2020,5631.600376,65.86614726,3.851035029 +csp3,2021,5226.724683,52.94297284,2.807581893 +csp3,2022,5211.052733,52.934062,2.807109 +csp4,2010,5499.241675,78.75663459,4.892457604 +csp4,2011,5499.241675,78.75663459,4.892457604 +csp4,2012,5499.241675,78.75663459,4.892457604 +csp4,2013,5499.241675,78.75663459,4.892457604 +csp4,2014,5499.241675,78.75663459,4.892457604 +csp4,2015,5499.241675,78.75663459,4.892457604 +csp4,2016,5499.241675,78.75663459,4.892457604 +csp4,2017,5499.241675,78.75663459,4.892457604 +csp4,2018,5411.623189,78.78932168,4.894488165 +csp4,2019,5195.786083,78.78932168,4.894488165 +csp4,2020,4855.091586,62.2584634,3.65971846 +csp4,2021,4514.397087,45.72760513,2.424948757 +csp4,2022,4500.611474,45.717374,2.424406 diff --git a/atb/manual_input/historical/csp_ATB_historical_conservative.csv b/atb/manual_input/historical/csp_ATB_historical_conservative.csv new file mode 100644 index 0000000..fe14d08 --- /dev/null +++ b/atb/manual_input/historical/csp_ATB_historical_conservative.csv @@ -0,0 +1,53 @@ +type,t,capcost,fom,vom +csp1,2010,10118.39247,83.63396315,4.892457604 +csp1,2011,10118.39247,83.63396315,4.892457604 +csp1,2012,10118.39247,83.63396315,4.892457604 +csp1,2013,10118.39247,83.63396315,4.892457604 +csp1,2014,10118.39247,83.63396315,4.892457604 +csp1,2015,10118.39247,83.63396315,4.892457604 +csp1,2016,10118.39247,83.63396315,4.892457604 +csp1,2017,10118.39247,83.63396315,4.892457604 +csp1,2018,9655.056558,83.63396315,4.807945152 +csp1,2019,9205.528937,83.63396315,4.807945152 +csp1,2020,8731.09044,83.63396315,4.621539781 +csp1,2021,8256.651943,83.63396315,4.43513441 +csp1,2022,8233.260436,83.633757,4.435123 +csp2,2010,9022.519272,78.75663459,4.892457604 +csp2,2011,9022.519272,78.75663459,4.892457604 +csp2,2012,9022.519272,78.75663459,4.892457604 +csp2,2013,9022.519272,78.75663459,4.892457604 +csp2,2014,9022.519272,78.75663459,4.892457604 +csp2,2015,9022.519272,78.75663459,4.892457604 +csp2,2016,9022.519272,78.75663459,4.892457604 +csp2,2017,9022.519272,78.75663459,4.892457604 +csp2,2018,8605.929835,77.39619026,4.807945152 +csp2,2019,7448.628019,75.10677024,4.006648147 +csp2,2020,7408.195726,74.86846512,3.982154074 +csp2,2021,7367.763433,74.63016,3.95766 +csp2,2022,7346.908275,74.63016,3.95766 +csp3,2010,6378.213448,78.75663459,4.892457604 +csp3,2011,6378.213448,78.75663459,4.892457604 +csp3,2012,6378.213448,78.75663459,4.892457604 +csp3,2013,6378.213448,78.75663459,4.892457604 +csp3,2014,6378.213448,78.75663459,4.892457604 +csp3,2015,6378.213448,78.75663459,4.892457604 +csp3,2016,6378.213448,78.75663459,4.892457604 +csp3,2017,6378.213448,78.75663459,4.892457604 +csp3,2018,6290.932719,78.78932168,4.894488165 +csp3,2019,6036.47607,78.78932168,4.894488165 +csp3,2020,5631.600376,65.86614726,3.851035029 +csp3,2021,5226.724683,52.94297284,2.807581893 +csp3,2022,5211.052733,52.934062,2.807109 +csp4,2010,5499.241675,78.75663459,4.892457604 +csp4,2011,5499.241675,78.75663459,4.892457604 +csp4,2012,5499.241675,78.75663459,4.892457604 +csp4,2013,5499.241675,78.75663459,4.892457604 +csp4,2014,5499.241675,78.75663459,4.892457604 +csp4,2015,5499.241675,78.75663459,4.892457604 +csp4,2016,5499.241675,78.75663459,4.892457604 +csp4,2017,5499.241675,78.75663459,4.892457604 +csp4,2018,5411.623189,78.78932168,4.894488165 +csp4,2019,5195.786083,78.78932168,4.894488165 +csp4,2020,4855.091586,62.2584634,3.65971846 +csp4,2021,4514.397087,45.72760513,2.424948757 +csp4,2022,4500.611474,45.717374,2.424406 diff --git a/atb/manual_input/historical/csp_ATB_historical_moderate.csv b/atb/manual_input/historical/csp_ATB_historical_moderate.csv new file mode 100644 index 0000000..fe14d08 --- /dev/null +++ b/atb/manual_input/historical/csp_ATB_historical_moderate.csv @@ -0,0 +1,53 @@ +type,t,capcost,fom,vom +csp1,2010,10118.39247,83.63396315,4.892457604 +csp1,2011,10118.39247,83.63396315,4.892457604 +csp1,2012,10118.39247,83.63396315,4.892457604 +csp1,2013,10118.39247,83.63396315,4.892457604 +csp1,2014,10118.39247,83.63396315,4.892457604 +csp1,2015,10118.39247,83.63396315,4.892457604 +csp1,2016,10118.39247,83.63396315,4.892457604 +csp1,2017,10118.39247,83.63396315,4.892457604 +csp1,2018,9655.056558,83.63396315,4.807945152 +csp1,2019,9205.528937,83.63396315,4.807945152 +csp1,2020,8731.09044,83.63396315,4.621539781 +csp1,2021,8256.651943,83.63396315,4.43513441 +csp1,2022,8233.260436,83.633757,4.435123 +csp2,2010,9022.519272,78.75663459,4.892457604 +csp2,2011,9022.519272,78.75663459,4.892457604 +csp2,2012,9022.519272,78.75663459,4.892457604 +csp2,2013,9022.519272,78.75663459,4.892457604 +csp2,2014,9022.519272,78.75663459,4.892457604 +csp2,2015,9022.519272,78.75663459,4.892457604 +csp2,2016,9022.519272,78.75663459,4.892457604 +csp2,2017,9022.519272,78.75663459,4.892457604 +csp2,2018,8605.929835,77.39619026,4.807945152 +csp2,2019,7448.628019,75.10677024,4.006648147 +csp2,2020,7408.195726,74.86846512,3.982154074 +csp2,2021,7367.763433,74.63016,3.95766 +csp2,2022,7346.908275,74.63016,3.95766 +csp3,2010,6378.213448,78.75663459,4.892457604 +csp3,2011,6378.213448,78.75663459,4.892457604 +csp3,2012,6378.213448,78.75663459,4.892457604 +csp3,2013,6378.213448,78.75663459,4.892457604 +csp3,2014,6378.213448,78.75663459,4.892457604 +csp3,2015,6378.213448,78.75663459,4.892457604 +csp3,2016,6378.213448,78.75663459,4.892457604 +csp3,2017,6378.213448,78.75663459,4.892457604 +csp3,2018,6290.932719,78.78932168,4.894488165 +csp3,2019,6036.47607,78.78932168,4.894488165 +csp3,2020,5631.600376,65.86614726,3.851035029 +csp3,2021,5226.724683,52.94297284,2.807581893 +csp3,2022,5211.052733,52.934062,2.807109 +csp4,2010,5499.241675,78.75663459,4.892457604 +csp4,2011,5499.241675,78.75663459,4.892457604 +csp4,2012,5499.241675,78.75663459,4.892457604 +csp4,2013,5499.241675,78.75663459,4.892457604 +csp4,2014,5499.241675,78.75663459,4.892457604 +csp4,2015,5499.241675,78.75663459,4.892457604 +csp4,2016,5499.241675,78.75663459,4.892457604 +csp4,2017,5499.241675,78.75663459,4.892457604 +csp4,2018,5411.623189,78.78932168,4.894488165 +csp4,2019,5195.786083,78.78932168,4.894488165 +csp4,2020,4855.091586,62.2584634,3.65971846 +csp4,2021,4514.397087,45.72760513,2.424948757 +csp4,2022,4500.611474,45.717374,2.424406 diff --git a/atb/manual_input/historical/fuelcell_ATB_historical_advanced.csv b/atb/manual_input/historical/fuelcell_ATB_historical_advanced.csv new file mode 100644 index 0000000..ddcabcb --- /dev/null +++ b/atb/manual_input/historical/fuelcell_ATB_historical_advanced.csv @@ -0,0 +1,27 @@ +i,t,capcost,fom,vom,heatrate +ng-fuel-cell,2010,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2011,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2012,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2013,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2014,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2015,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2016,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2017,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2018,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2019,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2020,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2021,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2022,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2023,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2024,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2025,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2026,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2027,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2028,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2029,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2030,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2031,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2032,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2033,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2034,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2035,1444.6,40.7,7.79,5.543 diff --git a/atb/manual_input/historical/fuelcell_ATB_historical_moderate.csv b/atb/manual_input/historical/fuelcell_ATB_historical_moderate.csv new file mode 100644 index 0000000..ddcabcb --- /dev/null +++ b/atb/manual_input/historical/fuelcell_ATB_historical_moderate.csv @@ -0,0 +1,27 @@ +i,t,capcost,fom,vom,heatrate +ng-fuel-cell,2010,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2011,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2012,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2013,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2014,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2015,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2016,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2017,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2018,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2019,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2020,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2021,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2022,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2023,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2024,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2025,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2026,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2027,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2028,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2029,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2030,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2031,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2032,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2033,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2034,9999.0,40.7,7.79,5.543 +ng-fuel-cell,2035,1444.6,40.7,7.79,5.543 diff --git a/atb/manual_input/historical/gas-ccs_ATB_historical_advanced.csv b/atb/manual_input/historical/gas-ccs_ATB_historical_advanced.csv new file mode 100644 index 0000000..f67ffa8 --- /dev/null +++ b/atb/manual_input/historical/gas-ccs_ATB_historical_advanced.csv @@ -0,0 +1,40 @@ +i,t,capcost,fom,vom,heatrate +Gas-CC-CCS_mod,2010,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2011,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2012,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2013,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2014,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2015,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2016,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2017,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2018,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2019,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2020,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2021,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2022,2656.6,70.7,5.11,7.22 +Gas-CC_H_1x1-CCS_mod,2010,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2011,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2012,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2013,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2014,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2015,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2016,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2017,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2018,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2019,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2020,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2021,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2022,2968.7,78.0,5.3,6.848 +Gas-CC_H_2x1-CCS_mod,2010,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2011,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2012,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2013,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2014,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2015,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2016,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2017,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2018,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2019,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2020,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2021,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2022,2524.1,65.8,4.86,7.007 diff --git a/atb/manual_input/historical/gas-ccs_ATB_historical_conservative.csv b/atb/manual_input/historical/gas-ccs_ATB_historical_conservative.csv new file mode 100644 index 0000000..f67ffa8 --- /dev/null +++ b/atb/manual_input/historical/gas-ccs_ATB_historical_conservative.csv @@ -0,0 +1,40 @@ +i,t,capcost,fom,vom,heatrate +Gas-CC-CCS_mod,2010,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2011,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2012,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2013,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2014,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2015,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2016,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2017,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2018,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2019,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2020,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2021,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2022,2656.6,70.7,5.11,7.22 +Gas-CC_H_1x1-CCS_mod,2010,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2011,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2012,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2013,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2014,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2015,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2016,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2017,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2018,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2019,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2020,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2021,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2022,2968.7,78.0,5.3,6.848 +Gas-CC_H_2x1-CCS_mod,2010,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2011,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2012,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2013,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2014,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2015,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2016,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2017,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2018,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2019,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2020,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2021,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2022,2524.1,65.8,4.86,7.007 diff --git a/atb/manual_input/historical/gas-ccs_ATB_historical_moderate.csv b/atb/manual_input/historical/gas-ccs_ATB_historical_moderate.csv new file mode 100644 index 0000000..f67ffa8 --- /dev/null +++ b/atb/manual_input/historical/gas-ccs_ATB_historical_moderate.csv @@ -0,0 +1,40 @@ +i,t,capcost,fom,vom,heatrate +Gas-CC-CCS_mod,2010,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2011,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2012,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2013,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2014,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2015,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2016,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2017,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2018,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2019,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2020,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2021,2572.02,68.796,5.0004,7.22 +Gas-CC-CCS_mod,2022,2656.6,70.7,5.11,7.22 +Gas-CC_H_1x1-CCS_mod,2010,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2011,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2012,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2013,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2014,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2015,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2016,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2017,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2018,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2019,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2020,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2021,2874.2,75.9,5.19,6.848 +Gas-CC_H_1x1-CCS_mod,2022,2968.7,78.0,5.3,6.848 +Gas-CC_H_2x1-CCS_mod,2010,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2011,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2012,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2013,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2014,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2015,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2016,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2017,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2018,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2019,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2020,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2021,2443.7,64.0,4.76,7.007 +Gas-CC_H_2x1-CCS_mod,2022,2524.1,65.8,4.86,7.007 diff --git a/atb/manual_input/historical/gas_ATB_historical_moderate.csv b/atb/manual_input/historical/gas_ATB_historical_moderate.csv new file mode 100644 index 0000000..76e6de1 --- /dev/null +++ b/atb/manual_input/historical/gas_ATB_historical_moderate.csv @@ -0,0 +1,94 @@ +i,t,capcost,fom,vom,heatrate +Gas-CC,2010,1141.254325,31.24999307,1.991757801,6.5375 +Gas-CC,2011,1136.675573,31.24999307,1.991757801,6.518111111 +Gas-CC,2012,1132.096819,31.24999307,1.991757801,6.498722222 +Gas-CC,2013,1127.518066,31.24999307,1.991757801,6.479333333 +Gas-CC,2014,1122.939312,31.24999307,1.991757801,6.459944444 +Gas-CC,2015,1118.360558,31.24999307,1.991757801,6.440555556 +Gas-CC,2016,1113.781805,31.24999307,1.991757801,6.421166667 +Gas-CC,2017,1109.203051,31.24999307,1.991757801,6.401777778 +Gas-CC,2018,1104.624297,31.24999307,1.991757801,6.382388889 +Gas-CC,2019,1100.045544,31.24999307,1.991757801,6.363 +Gas-CC,2020,1070.809499,31.66068212,1.967485246,6.363 +Gas-CC,2021,1205.712,33.588,2.1168,6.363 +Gas-CC,2022,1245.4,34.5,2.17,6.363 +Gas-CC_H_1x1,2010,1482.1,39.8,2.53,6.068 +Gas-CC_H_1x1,2011,1482.1,39.8,2.53,6.068 +Gas-CC_H_1x1,2012,1482.1,39.8,2.53,6.068 +Gas-CC_H_1x1,2013,1482.1,39.8,2.53,6.068 +Gas-CC_H_1x1,2014,1482.1,39.8,2.53,6.068 +Gas-CC_H_1x1,2015,1482.1,39.8,2.53,6.068 +Gas-CC_H_1x1,2016,1482.1,39.8,2.53,6.068 +Gas-CC_H_1x1,2017,1482.1,39.8,2.53,6.068 +Gas-CC_H_1x1,2018,1482.1,39.8,2.53,6.068 +Gas-CC_H_1x1,2019,1482.1,39.8,2.53,6.068 +Gas-CC_H_1x1,2020,1482.1,39.8,2.53,6.068 +Gas-CC_H_1x1,2021,1482.1,39.8,2.53,6.068 +Gas-CC_H_1x1,2022,1482.1,39.8,2.53,6.068 +Gas-CC_H_2x1,2010,1278.4,34.1,2.16,6.196 +Gas-CC_H_2x1,2011,1278.4,34.1,2.16,6.196 +Gas-CC_H_2x1,2012,1278.4,34.1,2.16,6.196 +Gas-CC_H_2x1,2013,1278.4,34.1,2.16,6.196 +Gas-CC_H_2x1,2014,1278.4,34.1,2.16,6.196 +Gas-CC_H_2x1,2015,1278.4,34.1,2.16,6.196 +Gas-CC_H_2x1,2016,1278.4,34.1,2.16,6.196 +Gas-CC_H_2x1,2017,1278.4,34.1,2.16,6.196 +Gas-CC_H_2x1,2018,1278.4,34.1,2.16,6.196 +Gas-CC_H_2x1,2019,1278.4,34.1,2.16,6.196 +Gas-CC_H_2x1,2020,1278.4,34.1,2.16,6.196 +Gas-CC_H_2x1,2021,1278.4,34.1,2.16,6.196 +Gas-CC_H_2x1,2022,1278.4,34.1,2.16,6.196 +Gas-CT,2010,1059.98145,23.92398737,5.654760652,10.2225 +Gas-CT,2011,1050.823943,23.92398737,5.654760652,10.16633333 +Gas-CT,2012,1041.666436,23.92398737,5.654760652,10.11016667 +Gas-CT,2013,1032.508929,23.92398737,5.654760652,10.054 +Gas-CT,2014,1023.351422,23.92398737,5.654760652,9.997833333 +Gas-CT,2015,1014.193915,23.92398737,5.654760652,9.941666667 +Gas-CT,2016,1005.036407,23.92398737,5.654760652,9.8855 +Gas-CT,2017,995.8789002,23.92398737,5.654760652,9.829333333 +Gas-CT,2018,986.7213932,23.92398737,5.654760652,9.773166667 +Gas-CT,2019,977.563886,23.92398737,5.654760652,9.717 +Gas-CT,2020,950.9512022,23.74551159,5.585848916,9.717 +Gas-CT,2021,1082.808,25.92,6.9552,9.717 +Gas-CT,2022,1110.4,26.1,6.94,9.717 +Gas-CT_aero,2010,1540.246,11.02,5.476,9.447 +Gas-CT_aero,2011,1540.246,11.02,5.476,9.447 +Gas-CT_aero,2012,1540.246,11.02,5.476,9.447 +Gas-CT_aero,2013,1540.246,11.02,5.476,9.447 +Gas-CT_aero,2014,1540.246,11.02,5.476,9.447 +Gas-CT_aero,2015,1540.246,11.02,5.476,9.447 +Gas-CT_aero,2016,1540.246,11.02,5.476,9.447 +Gas-CT_aero,2017,1540.246,11.02,5.476,9.447 +Gas-CT_aero,2018,1540.246,11.02,5.476,9.447 +Gas-CT_aero,2019,1540.246,11.02,5.476,9.447 +Gas-CT_aero,2020,1540.246,11.02,5.476,9.447 +Gas-CT_aero,2021,1540.246,11.02,5.476,9.447 +Gas-CT_aero,2022,1540.246,11.02,5.476,9.447 +Gas-CT_aero,2023,1540.246,11.02,5.476,9.447 +Gas-CT_aero,2024,1540.246,11.02,5.476,9.447 +Gas-CT_aero,2025,1540.246,11.02,5.476,9.447 +Gas-CT_aero,2026,1521.614,10.977,5.476,9.447 +Gas-CT_aero,2027,1506.757,10.891,5.476,9.447 +Gas-CT_aero,2028,1478.9,10.848,5.476,9.447 +Gas-CT_aero,2029,1455.704,10.763,5.476,9.447 +Gas-CT_aero,2030,1436.601,10.72,5.476,9.447 +Gas-CT_aero,2031,1421.585,10.634,5.476,9.447 +Gas-CT_aero,2032,1408.486,10.591,5.476,9.447 +Gas-CT_aero,2033,1395.738,10.505,5.476,9.447 +Gas-CT_aero,2034,1368.85,10.463,5.476,9.447 +Gas-CT_aero,2035,1359.531,10.377,5.476,9.447 +Gas-CT_aero,2036,1349.629,10.334,5.476,9.447 +Gas-CT_aero,2037,1341.603,10.248,5.476,9.447 +Gas-CT_aero,2038,1337.263,10.205,5.476,9.447 +Gas-CT_aero,2039,1332.502,10.162,5.476,9.447 +Gas-CT_aero,2040,1326.853,10.077,5.476,9.447 +Gas-CT_aero,2041,1318.519,10.034,5.476,9.447 +Gas-CT_aero,2042,1314.485,9.948,5.476,9.447 +Gas-CT_aero,2043,1310.826,9.905,5.476,9.447 +Gas-CT_aero,2044,1306.131,9.819,5.476,9.447 +Gas-CT_aero,2045,1302.451,9.776,5.476,9.447 +Gas-CT_aero,2046,1297.241,9.691,5.476,9.447 +Gas-CT_aero,2047,1291.484,9.648,5.476,9.447 +Gas-CT_aero,2048,1287.003,9.562,5.476,9.447 +Gas-CT_aero,2049,1283.086,9.519,5.476,9.447 +Gas-CT_aero,2050,1275.174,9.433,5.476,9.447 diff --git a/atb/manual_input/historical/nuclear-smr_ATB_historical_advanced.csv b/atb/manual_input/historical/nuclear-smr_ATB_historical_advanced.csv new file mode 100644 index 0000000..b05ab7b --- /dev/null +++ b/atb/manual_input/historical/nuclear-smr_ATB_historical_advanced.csv @@ -0,0 +1,22 @@ +i,t,capcost,fom,vom,heatrate +Nuclear-SMR,2010,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2011,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2012,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2013,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2014,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2015,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2016,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2017,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2018,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2019,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2020,7699.675484,128.9042058,3.37433865,10.455 +Nuclear-SMR,2021,8658.389123,128.304,3.3804,10.455 +Nuclear-SMR,2022,5500.0,118.0,2.2,9.18 +Nuclear-SMR,2023,5500.0,118.0,2.2,9.18 +Nuclear-SMR,2024,5500.0,118.0,2.2,9.18 +Nuclear-SMR,2025,5500.0,118.0,2.2,9.18 +Nuclear-SMR,2026,5500.0,118.0,2.2,9.18 +Nuclear-SMR,2027,5500.0,118.0,2.2,9.18 +Nuclear-SMR,2028,5500.0,118.0,2.2,9.18 +Nuclear-SMR,2029,5500.0,118.0,2.2,9.18 +Nuclear-SMR,2030,5500.0,118.0,2.2,9.18 diff --git a/atb/manual_input/historical/nuclear-smr_ATB_historical_conservative.csv b/atb/manual_input/historical/nuclear-smr_ATB_historical_conservative.csv new file mode 100644 index 0000000..9845331 --- /dev/null +++ b/atb/manual_input/historical/nuclear-smr_ATB_historical_conservative.csv @@ -0,0 +1,22 @@ +i,t,capcost,fom,vom,heatrate +Nuclear-SMR,2010,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2011,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2012,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2013,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2014,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2015,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2016,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2017,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2018,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2019,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2020,7699.675484,128.9042058,3.37433865,10.455 +Nuclear-SMR,2021,8658.389123,128.304,3.3804,10.455 +Nuclear-SMR,2022,10000.0,216.0,2.8,9.18 +Nuclear-SMR,2023,10000.0,216.0,2.8,9.18 +Nuclear-SMR,2024,10000.0,216.0,2.8,9.18 +Nuclear-SMR,2025,10000.0,216.0,2.8,9.18 +Nuclear-SMR,2026,10000.0,216.0,2.8,9.18 +Nuclear-SMR,2027,10000.0,216.0,2.8,9.18 +Nuclear-SMR,2028,10000.0,216.0,2.8,9.18 +Nuclear-SMR,2029,10000.0,216.0,2.8,9.18 +Nuclear-SMR,2030,10000.0,216.0,2.8,9.18 diff --git a/atb/manual_input/historical/nuclear-smr_ATB_historical_moderate.csv b/atb/manual_input/historical/nuclear-smr_ATB_historical_moderate.csv new file mode 100644 index 0000000..2b970df --- /dev/null +++ b/atb/manual_input/historical/nuclear-smr_ATB_historical_moderate.csv @@ -0,0 +1,22 @@ +i,t,capcost,fom,vom,heatrate +Nuclear-SMR,2010,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2011,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2012,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2013,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2014,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2015,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2016,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2017,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2018,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2019,7624.567702,129.5986335,3.415967332,10.455 +Nuclear-SMR,2020,7699.675484,128.9042058,3.37433865,10.455 +Nuclear-SMR,2021,8658.389123,128.304,3.3804,10.455 +Nuclear-SMR,2022,8000.0,136.0,2.6,9.18 +Nuclear-SMR,2023,8000.0,136.0,2.6,9.18 +Nuclear-SMR,2024,8000.0,136.0,2.6,9.18 +Nuclear-SMR,2025,8000.0,136.0,2.6,9.18 +Nuclear-SMR,2026,8000.0,136.0,2.6,9.18 +Nuclear-SMR,2027,8000.0,136.0,2.6,9.18 +Nuclear-SMR,2028,8000.0,136.0,2.6,9.18 +Nuclear-SMR,2029,8000.0,136.0,2.6,9.18 +Nuclear-SMR,2030,8000.0,136.0,2.6,9.18 diff --git a/atb/manual_input/historical/nuclear_ATB_historical_advanced.csv b/atb/manual_input/historical/nuclear_ATB_historical_advanced.csv new file mode 100644 index 0000000..8731f56 --- /dev/null +++ b/atb/manual_input/historical/nuclear_ATB_historical_advanced.csv @@ -0,0 +1,22 @@ +i,t,capcost,fom,vom,heatrate +Nuclear,2010,7686.711275,165.9481454,2.692053726,10.455 +Nuclear,2011,7633.565731,165.9481454,2.692053726,10.455 +Nuclear,2012,7580.420186,165.9481454,2.692053726,10.455 +Nuclear,2013,7527.27464,165.9481454,2.692053726,10.455 +Nuclear,2014,7474.129095,165.9481454,2.692053726,10.455 +Nuclear,2015,7420.983549,165.9481454,2.692053726,10.455 +Nuclear,2016,7367.838005,165.9481454,2.692053726,10.455 +Nuclear,2017,7314.692459,165.9481454,2.692053726,10.455 +Nuclear,2018,7261.546913,165.9481454,2.692053726,10.455 +Nuclear,2019,7208.401368,165.9481454,2.692053726,10.455 +Nuclear,2020,7172.069171,165.0426129,2.659247015,10.455 +Nuclear,2021,8065.093644,164.2896,2.6676,10.455 +Nuclear,2022,5250.0,126.0,1.9,10.497 +Nuclear,2023,5250.0,126.0,1.9,10.497 +Nuclear,2024,5250.0,126.0,1.9,10.497 +Nuclear,2025,5250.0,126.0,1.9,10.497 +Nuclear,2026,5250.0,126.0,1.9,10.497 +Nuclear,2027,5250.0,126.0,1.9,10.497 +Nuclear,2028,5250.0,126.0,1.9,10.497 +Nuclear,2029,5250.0,126.0,1.9,10.497 +Nuclear,2030,5250.0,126.0,1.9,10.497 diff --git a/atb/manual_input/historical/nuclear_ATB_historical_conservative.csv b/atb/manual_input/historical/nuclear_ATB_historical_conservative.csv new file mode 100644 index 0000000..f44626f --- /dev/null +++ b/atb/manual_input/historical/nuclear_ATB_historical_conservative.csv @@ -0,0 +1,22 @@ +i,t,capcost,fom,vom,heatrate +Nuclear,2010,7686.711275,165.9481454,2.692053726,10.455 +Nuclear,2011,7633.565731,165.9481454,2.692053726,10.455 +Nuclear,2012,7580.420186,165.9481454,2.692053726,10.455 +Nuclear,2013,7527.27464,165.9481454,2.692053726,10.455 +Nuclear,2014,7474.129095,165.9481454,2.692053726,10.455 +Nuclear,2015,7420.983549,165.9481454,2.692053726,10.455 +Nuclear,2016,7367.838005,165.9481454,2.692053726,10.455 +Nuclear,2017,7314.692459,165.9481454,2.692053726,10.455 +Nuclear,2018,7261.546913,165.9481454,2.692053726,10.455 +Nuclear,2019,7208.401368,165.9481454,2.692053726,10.455 +Nuclear,2020,7172.069171,165.0426129,2.659247015,10.455 +Nuclear,2021,8065.093644,164.2896,2.6676,10.455 +Nuclear,2022,7750.0,204.0,3.4,10.497 +Nuclear,2023,7750.0,204.0,3.4,10.497 +Nuclear,2024,7750.0,204.0,3.4,10.497 +Nuclear,2025,7750.0,204.0,3.4,10.497 +Nuclear,2026,7750.0,204.0,3.4,10.497 +Nuclear,2027,7750.0,204.0,3.4,10.497 +Nuclear,2028,7750.0,204.0,3.4,10.497 +Nuclear,2029,7750.0,204.0,3.4,10.497 +Nuclear,2030,7750.0,204.0,3.4,10.497 diff --git a/atb/manual_input/historical/nuclear_ATB_historical_moderate.csv b/atb/manual_input/historical/nuclear_ATB_historical_moderate.csv new file mode 100644 index 0000000..088942b --- /dev/null +++ b/atb/manual_input/historical/nuclear_ATB_historical_moderate.csv @@ -0,0 +1,22 @@ +i,t,capcost,fom,vom,heatrate +Nuclear,2010,7686.711275,165.9481454,2.692053726,10.455 +Nuclear,2011,7633.565731,165.9481454,2.692053726,10.455 +Nuclear,2012,7580.420186,165.9481454,2.692053726,10.455 +Nuclear,2013,7527.27464,165.9481454,2.692053726,10.455 +Nuclear,2014,7474.129095,165.9481454,2.692053726,10.455 +Nuclear,2015,7420.983549,165.9481454,2.692053726,10.455 +Nuclear,2016,7367.838005,165.9481454,2.692053726,10.455 +Nuclear,2017,7314.692459,165.9481454,2.692053726,10.455 +Nuclear,2018,7261.546913,165.9481454,2.692053726,10.455 +Nuclear,2019,7208.401368,165.9481454,2.692053726,10.455 +Nuclear,2020,7172.069171,165.0426129,2.659247015,10.455 +Nuclear,2021,8065.093644,164.2896,2.6676,10.455 +Nuclear,2022,5750.0,175.0,2.8,10.497 +Nuclear,2023,5750.0,175.0,2.8,10.497 +Nuclear,2024,5750.0,175.0,2.8,10.497 +Nuclear,2025,5750.0,175.0,2.8,10.497 +Nuclear,2026,5750.0,175.0,2.8,10.497 +Nuclear,2027,5750.0,175.0,2.8,10.497 +Nuclear,2028,5750.0,175.0,2.8,10.497 +Nuclear,2029,5750.0,175.0,2.8,10.497 +Nuclear,2030,5750.0,175.0,2.8,10.497 diff --git a/atb/manual_input/historical/upv_ATB_historical_advanced.csv b/atb/manual_input/historical/upv_ATB_historical_advanced.csv new file mode 100644 index 0000000..621f0e1 --- /dev/null +++ b/atb/manual_input/historical/upv_ATB_historical_advanced.csv @@ -0,0 +1,14 @@ +t,capcost,fom,vom,cf_improvement +2010,7134.47322,35.6723661,0.0,0.930346357 +2011,6626.226717,35.06566801,0.0,0.930346357 +2012,6117.98021,34.45896991,0.0,0.930346357 +2013,4246.109494,33.85227179,0.0,0.930346357 +2014,3195.163027,33.2455737,0.0,0.930346357 +2015,3190.13065,32.6388756,0.0,0.930346357 +2016,2777.886445,32.0321775,0.0,0.930346357 +2017,1699.682398,30.81878129,0.0,0.930346357 +2018,1629.132468,28.50901493,0.0,0.930346357 +2019,1558.582537,26.19924857,0.0,0.930346357 +2020,1473.096512,25.58148089,0.0,0.930346357 +2021,1345.480289,24.33122164,0.0,0.930346357 +2022,1366.598204,23.765603,0.0,0.930346 diff --git a/atb/manual_input/historical/upv_ATB_historical_conservative.csv b/atb/manual_input/historical/upv_ATB_historical_conservative.csv new file mode 100644 index 0000000..2742518 --- /dev/null +++ b/atb/manual_input/historical/upv_ATB_historical_conservative.csv @@ -0,0 +1,14 @@ +t,capcost,fom,vom,cf_improvement +2010,7134.47322,35.6723661,0.0,0.920465813 +2011,6626.226717,35.06566801,0.0,0.920465813 +2012,6117.98021,34.45896991,0.0,0.920465813 +2013,4246.109494,33.85227179,0.0,0.920465813 +2014,3195.163027,33.2455737,0.0,0.920465813 +2015,3190.13065,32.6388756,0.0,0.920465813 +2016,2777.886445,32.0321775,0.0,0.920465813 +2017,1699.682398,30.81878129,0.0,0.920465813 +2018,1629.132468,28.50901493,0.0,0.920465813 +2019,1558.582537,26.19924857,0.0,0.920465813 +2020,1473.096512,25.58148089,0.0,0.920465813 +2021,1345.480289,24.33122164,0.0,0.920465813 +2022,1366.598204,23.765603,0.0,0.920466 diff --git a/atb/manual_input/historical/upv_ATB_historical_moderate.csv b/atb/manual_input/historical/upv_ATB_historical_moderate.csv new file mode 100644 index 0000000..201561f --- /dev/null +++ b/atb/manual_input/historical/upv_ATB_historical_moderate.csv @@ -0,0 +1,14 @@ +t,capcost,fom,vom,cf_improvement +2010,7134.47322,35.6723661,0.0,0.926146827 +2011,6626.226717,35.06566801,0.0,0.926146827 +2012,6117.98021,34.45896991,0.0,0.926146827 +2013,4246.109494,33.85227179,0.0,0.926146827 +2014,3195.163027,33.2455737,0.0,0.926146827 +2015,3190.13065,32.6388756,0.0,0.926146827 +2016,2777.886445,32.0321775,0.0,0.926146827 +2017,1699.682398,30.81878129,0.0,0.926146827 +2018,1629.132468,28.50901493,0.0,0.926146827 +2019,1558.582537,26.19924857,0.0,0.926146827 +2020,1473.096512,25.58148089,0.0,0.926146827 +2021,1345.480289,24.33122164,0.0,0.926146827 +2022,1366.598204,23.765603,0.0,0.926147 diff --git a/atb/manual_input/historical/wind-ofs_ATB_historical_advanced.csv b/atb/manual_input/historical/wind-ofs_ATB_historical_advanced.csv new file mode 100644 index 0000000..81842cd --- /dev/null +++ b/atb/manual_input/historical/wind-ofs_ATB_historical_advanced.csv @@ -0,0 +1,35 @@ +turbine,t,capcost,fom,vom,cf_improvement,rsc_mult +fixed,2010,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2011,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2012,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2013,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2014,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2015,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2016,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2017,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2018,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2019,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2020,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2021,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2022,4601.48313,90.91066,0.0,0.94615,1.0 +floating,2010,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2011,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2012,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2013,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2014,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2015,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2016,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2017,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2018,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2019,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2020,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2021,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2022,14229.47981,81.5295,0.0,0.94619,1.0 +floating,2023,9577.6067,68.07725,0.0,0.97388,1.0 +floating,2024,8500.13714,64.96144,0.0,0.99021,1.0 +floating,2025,7910.7477,63.25705,0.0,1.00162,1.0 +floating,2026,7114.39406,62.11205,0.0,1.01029,1.0 +floating,2027,6421.40359,61.26249,0.0,1.01724,1.0 +floating,2028,5790.91499,60.59367,0.0,1.02301,1.0 +floating,2029,5601.50773,60.04594,0.0,1.02793,1.0 +floating,2030,5441.95175,59.58449,0.0,1.03202,1.0 diff --git a/atb/manual_input/historical/wind-ofs_ATB_historical_conservative.csv b/atb/manual_input/historical/wind-ofs_ATB_historical_conservative.csv new file mode 100644 index 0000000..c107e60 --- /dev/null +++ b/atb/manual_input/historical/wind-ofs_ATB_historical_conservative.csv @@ -0,0 +1,35 @@ +turbine,t,capcost,fom,vom,cf_improvement,rsc_mult +fixed,2010,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2011,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2012,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2013,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2014,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2015,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2016,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2017,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2018,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2019,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2020,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2021,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2022,6359.77349,90.91066,0.0,0.94615,1.0 +floating,2010,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2011,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2012,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2013,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2014,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2015,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2016,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2017,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2018,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2019,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2020,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2021,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2022,16679.24373,81.5295,0.0,0.94619,1.0 +floating,2023,15201.41551,77.74446,0.0,0.94742,1.0 +floating,2024,14509.81355,75.97312,0.0,0.94854,1.0 +floating,2025,14064.92972,74.83367,0.0,0.94957,1.0 +floating,2026,13740.20065,74.00197,0.0,0.95052,1.0 +floating,2027,13486.1424,73.35127,0.0,0.9514,1.0 +floating,2028,13278.40941,72.81922,0.0,0.95223,1.0 +floating,2029,13103.27806,72.37067,0.0,0.953,1.0 +floating,2030,12344.1771,71.9839,0.0,0.95372,1.0 diff --git a/atb/manual_input/historical/wind-ofs_ATB_historical_moderate.csv b/atb/manual_input/historical/wind-ofs_ATB_historical_moderate.csv new file mode 100644 index 0000000..ebaa24e --- /dev/null +++ b/atb/manual_input/historical/wind-ofs_ATB_historical_moderate.csv @@ -0,0 +1,35 @@ +turbine,t,capcost,fom,vom,cf_improvement,rsc_mult +fixed,2010,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2011,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2012,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2013,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2014,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2015,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2016,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2017,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2018,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2019,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2020,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2021,5237.4605,90.9107,0.0,0.94619,1.0 +fixed,2022,5237.46054,90.91066,0.0,0.94615,1.0 +floating,2010,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2011,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2012,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2013,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2014,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2015,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2016,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2017,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2018,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2019,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2020,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2021,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2022,15115.56463,81.5295,0.0,0.94619,1.0 +floating,2023,11918.12957,72.69266,0.0,0.95632,1.0 +floating,2024,10952.52993,70.02401,0.0,0.96351,1.0 +floating,2025,10396.79349,68.4881,0.0,0.96904,1.0 +floating,2026,10013.32394,67.4283,0.0,0.97351,1.0 +floating,2027,9723.70334,66.62786,0.0,0.97726,1.0 +floating,2028,9075.70416,65.98939,0.0,0.98046,1.0 +floating,2029,8467.56141,65.46107,0.0,0.98327,1.0 +floating,2030,7888.16784,65.01223,0.0,0.98574,1.0 diff --git a/atb/manual_input/historical/wind-ons_ATB_historical_advanced.csv b/atb/manual_input/historical/wind-ons_ATB_historical_advanced.csv new file mode 100644 index 0000000..7cabef1 --- /dev/null +++ b/atb/manual_input/historical/wind-ons_ATB_historical_advanced.csv @@ -0,0 +1,14 @@ +turbine,t,capcost,fom,vom,cf_improvement +115hh_170rd,2010,2639.12325,47.01292,0.0,0.61277 +115hh_170rd,2011,2466.78385,47.01292,0.0,0.60304 +115hh_170rd,2012,2294.44446,47.01292,0.0,0.69848 +115hh_170rd,2013,2157.98645,47.01292,0.0,0.79294 +115hh_170rd,2014,2021.52845,47.01292,0.0,0.87144 +115hh_170rd,2015,1909.53503,47.01292,0.0,0.82324 +115hh_170rd,2016,1797.54161,47.01292,0.0,0.89312 +115hh_170rd,2017,1795.05224,47.01292,0.0,0.85843 +115hh_170rd,2018,1649.67472,47.01292,0.0,0.92017 +115hh_170rd,2019,1504.29721,47.01292,0.0,0.85963 +115hh_170rd,2020,1517.57908,46.44,0.0,0.83469 +115hh_170rd,2021,1386.59368,32.724,0.0,0.76975 +115hh_170rd,2022,1471.55594,32.44305,0.0,0.94599 diff --git a/atb/manual_input/historical/wind-ons_ATB_historical_conservative.csv b/atb/manual_input/historical/wind-ons_ATB_historical_conservative.csv new file mode 100644 index 0000000..c6f1967 --- /dev/null +++ b/atb/manual_input/historical/wind-ons_ATB_historical_conservative.csv @@ -0,0 +1,14 @@ +turbine,t,capcost,fom,vom,cf_improvement +115hh_170rd,2010,2639.12325,47.01292,0.0,0.61277 +115hh_170rd,2011,2466.78385,47.01292,0.0,0.60304 +115hh_170rd,2012,2294.44446,47.01292,0.0,0.69848 +115hh_170rd,2013,2157.98645,47.01292,0.0,0.79294 +115hh_170rd,2014,2021.52845,47.01292,0.0,0.87144 +115hh_170rd,2015,1909.53503,47.01292,0.0,0.82324 +115hh_170rd,2016,1797.54161,47.01292,0.0,0.89312 +115hh_170rd,2017,1795.05224,47.01292,0.0,0.85843 +115hh_170rd,2018,1649.67472,47.01292,0.0,0.92017 +115hh_170rd,2019,1504.29721,47.01292,0.0,0.85963 +115hh_170rd,2020,1517.57908,46.44,0.0,0.83469 +115hh_170rd,2021,1386.59368,32.724,0.0,0.76975 +115hh_170rd,2022,1471.55594,32.44305,0.0,0.93285 diff --git a/atb/manual_input/historical/wind-ons_ATB_historical_moderate.csv b/atb/manual_input/historical/wind-ons_ATB_historical_moderate.csv new file mode 100644 index 0000000..21ba64c --- /dev/null +++ b/atb/manual_input/historical/wind-ons_ATB_historical_moderate.csv @@ -0,0 +1,14 @@ +turbine,t,capcost,fom,vom,cf_improvement +115hh_170rd,2010,2639.12325,47.01292,0.0,0.61277 +115hh_170rd,2011,2466.78385,47.01292,0.0,0.60304 +115hh_170rd,2012,2294.44446,47.01292,0.0,0.69848 +115hh_170rd,2013,2157.98645,47.01292,0.0,0.79294 +115hh_170rd,2014,2021.52845,47.01292,0.0,0.87144 +115hh_170rd,2015,1909.53503,47.01292,0.0,0.82324 +115hh_170rd,2016,1797.54161,47.01292,0.0,0.89312 +115hh_170rd,2017,1795.05224,47.01292,0.0,0.85843 +115hh_170rd,2018,1649.67472,47.01292,0.0,0.92017 +115hh_170rd,2019,1504.29721,47.01292,0.0,0.85963 +115hh_170rd,2020,1517.57908,46.44,0.0,0.83469 +115hh_170rd,2021,1386.59368,32.724,0.0,0.76975 +115hh_170rd,2022,1471.55594,32.44305,0.0,0.93873 diff --git a/atb/manual_input/offshore_cost_multipliers_2024.csv b/atb/manual_input/offshore_cost_multipliers_2024.csv new file mode 100644 index 0000000..af02848 --- /dev/null +++ b/atb/manual_input/offshore_cost_multipliers_2024.csv @@ -0,0 +1,3 @@ +turbine,capcost,fom +fixed,1.190671038860,1.002655373133 +floating,1.372107839750,0.978659140911 diff --git a/atb/scraped_input/README.md b/atb/scraped_input/README.md new file mode 100644 index 0000000..a253695 --- /dev/null +++ b/atb/scraped_input/README.md @@ -0,0 +1,41 @@ +# scraped_input/ + +This directory exposes the two independent raw downloads used by the NLR ATB +workflow. Neither file is generated from the other. + +| File | Upstream source | Used for | +| --- | --- | --- | +| `atb__flat_file.csv` | ATB summary flat file (`ATBe.csv`) on OEDI | Primary cost, performance, and financial data | +| `atb__workbook.xlsx` | ATB Excel data workbook | Battery power ($/kW) and energy ($/kWh) capital-cost components | + +The URLs and local filenames are declared under `raw_data:` in +[`../config.yaml`](../config.yaml). Download and inspect both files with: + +```bash +python ../scripts/scrape_atb_inputs.py +``` + +Useful variants: + +```bash +python ../scripts/scrape_atb_inputs.py --only flat +python ../scripts/scrape_atb_inputs.py --only workbook +python ../scripts/scrape_atb_inputs.py --force +``` + +Without `--force`, existing files are reused and displayed. These files remain +raw and unmodified: derived ReEDS inputs go in `../output/`, and plots go in +`../figures/`. + +HTTPS certificates are checked first. The +`raw_data.allow_insecure_ssl_fallback` setting controls whether a failed check +may be retried with `verify=False`, which can be needed when the active conda +environment does not trust an NLR network inspection certificate. + +Both raw files are local only and are never committed. The flat file is far +above the 50 MB limit in the top-level [`README.md`](../../README.md) File Size +Guidelines, and both files can be pulled directly from their upstream source, so +this directory is ignored except for this README. Reproducibility comes from the +URLs instead: they are pinned to a specific ATB release in +[`../config.yaml`](../config.yaml) (ATB 2024 is pinned to corrected release v3), +so re-running the scraper restores the exact inputs used for a run. diff --git a/atb/scripts/README.md b/atb/scripts/README.md new file mode 100644 index 0000000..d1602b8 --- /dev/null +++ b/atb/scripts/README.md @@ -0,0 +1,36 @@ +# scripts/ + +All scripts share the user configuration in [`../config.yaml`](../config.yaml). +User-facing run instructions are in the main [`../README.md`](../README.md). + +| File | Role | +| --- | --- | +| `run_pipeline.py` | Runs the config-enabled scrape, format, and plot stages in order | +| `scrape_atb_inputs.py` | Downloads and summarizes both raw ATB inputs | +| `generate_atb_files.py` | Converts local raw and manual inputs into ReEDS-formatted CSVs | +| `atb_plotting.py` | Plots configured metrics from the local raw flat file | +| `atb_config.py` | Shared config and path-loading functions | +| `settings.yaml` | Internal per-technology mappings and transformations | +| `battery_workbook.py` | Extracts battery cost components from the downloaded workbook; imported by the formatter | + +Normal workflow choices belong in `../config.yaml`; `settings.yaml` is only for +technology-specific formatting rules. + +## settings.yaml keys that control the ReEDS output format + +Most keys describe how to pull a technology out of the ATB flat file. These two +instead describe how the result must be written so ReEDS can read it, and are +only needed where the ReEDS representation differs from the internal one: + +| Key | Effect | +| --- | --- | +| `reeds_name` | File prefix used in ReEDS when it differs from the technology key (`wind-ons` -> `ons-wind`, `wind-ofs` -> `ofs-wind`). Also becomes the `Scenario` key written to the ReEDS `dollaryear.csv`. | +| `output_cols` | Ordered mapping of internal column name -> ReEDS header, applied as the last step before writing. Required for the wind files because ReEDS assigns their columns by position, not by name. | + +Both are optional. A technology without them is written as +`_ATB__.csv` using the internal column names listed under +`cols`. `output_cols` must cover every column in `cols`; the formatter raises an +error rather than silently dropping or reordering one. + +See "File names and column schemas expected by ReEDS" in the main +[`../README.md`](../README.md) for why the wind files need this. diff --git a/atb/scripts/atb_config.py b/atb/scripts/atb_config.py new file mode 100644 index 0000000..cc80688 --- /dev/null +++ b/atb/scripts/atb_config.py @@ -0,0 +1,77 @@ +"""Shared configuration and path helpers for the ATB workflow.""" + +from pathlib import Path + +import yaml + + +SCRIPT_DIR = Path(__file__).resolve().parent +ATB_DIR = SCRIPT_DIR.parent +DEFAULT_CONFIG_PATH = ATB_DIR / "config.yaml" +TECH_SETTINGS_PATH = SCRIPT_DIR / "settings.yaml" + +ATBE_COLUMN_MAPPING = { + "core_metric_parameter": "Parameter", + "core_metric_case": "Case", + "tax_credit_case": "TaxCreditCase", + "crpyears": "CRPYears", + "technology": "Technology", + "technology_alias": "TechnologyAlias", + "techdetail": "TechDetail", + "techdetail2": "TechDetail2", + "resourcedetail": "ResourceDetail", + "display_name": "DisplayName", + "scenario": "Scenario", + "core_metric_variable": "variable", +} + + +def load_config(config_path=None): + """Load the user-facing workflow configuration.""" + path = Path(config_path).resolve() if config_path else DEFAULT_CONFIG_PATH + with path.open(encoding="utf-8") as stream: + config = yaml.safe_load(stream) + config["_config_path"] = str(path) + return config + + +def resolve_atb_path(value): + """Resolve an absolute path or a path relative to the atb directory.""" + path = Path(value) + return path if path.is_absolute() else ATB_DIR / path + + +def raw_file_path(config, kind): + """Return the configured local path for ``flat_file`` or ``workbook``.""" + raw = config["raw_data"] + return resolve_atb_path(raw["directory"]) / raw[kind]["filename"] + + +def load_processing_settings(config_path=None): + """Merge user config with the internal per-technology settings.""" + config = load_config(config_path) + with TECH_SETTINGS_PATH.open(encoding="utf-8") as stream: + settings = yaml.safe_load(stream) + + processing = config["processing"] + history = config["historical_data"] + settings.update( + { + "atbyear": config["atb"]["year"], + "dollaryear": config["atb"]["dollar_year"], + "reedspath": str(Path(processing["reeds_repo"])), + "copy_to_reeds": bool(processing["copy_to_reeds"]), + "reeds_start_year": int(processing["reeds_start_year"]), + "decimals": int(processing["decimals"]), + "output_dir": str(resolve_atb_path(processing["output_directory"])), + "flat_file_path": str(raw_file_path(config, "flat_file")), + "workbook_path": str(raw_file_path(config, "workbook")), + "history_dir": str(resolve_atb_path(history["directory"])), + "history_dollar_year": int(history["dollar_year"]), + "seed_missing_history": bool( + history.get("seed_missing_from_reeds", False) + ), + "config": config, + } + ) + return settings diff --git a/atb/scripts/atb_plotting.py b/atb/scripts/atb_plotting.py new file mode 100644 index 0000000..3f0ca0c --- /dev/null +++ b/atb/scripts/atb_plotting.py @@ -0,0 +1,201 @@ +"""Plot raw NLR ATB metrics using the shared workflow configuration.""" + +import argparse +import math +from pathlib import Path + +import matplotlib.pyplot as plt +import pandas as pd + +from atb_config import ATBE_COLUMN_MAPPING, load_config, raw_file_path, resolve_atb_path + + +SCENARIOS = ("Moderate", "Advanced", "Conservative") +UNITS = { + "CAPEX": "USD/kW", + "OCC": "USD/kW", + "Fixed O&M": "USD/kW-yr", + "Variable O&M": "USD/MWh", +} + + +def select_year_ticks(years, max_ticks=5): + """Return at most ``max_ticks`` evenly spaced years present in the data.""" + years = sorted({int(year) for year in years if pd.notna(year)}) + if len(years) <= max_ticks: + return years + indices = { + round(position * (len(years) - 1) / (max_ticks - 1)) + for position in range(max_ticks) + } + return [years[index] for index in sorted(indices)] + + +def load_raw_atb(config): + """Load the same local flat file used by the ReEDS formatter.""" + path = raw_file_path(config, "flat_file") + if not path.is_file(): + raise FileNotFoundError( + f"Raw ATB flat file not found: {path}\n" + "Run 'python scrape_atb_inputs.py --only flat' first." + ) + print(f"Loading raw ATB flat file: {path}") + return pd.read_csv(path, low_memory=False).rename(columns=ATBE_COLUMN_MAPPING) + + +def clean_metric_data(raw, metric, case, crp_years, technologies): + """Select and aggregate one metric into a plotting-friendly table.""" + required = { + "Technology", "DisplayName", "Scenario", "Parameter", "Case", + "CRPYears", "variable", "value", + } + missing = sorted(required - set(raw.columns)) + if missing: + raise ValueError(f"Raw ATB flat file is missing columns: {missing}") + + data = raw.loc[ + (raw["Parameter"] == metric) + & (raw["Case"] == case) + & (raw["CRPYears"] == crp_years) + & (raw["Technology"].isin(technologies)) + & (raw["Scenario"].isin(SCENARIOS)), + ["Technology", "DisplayName", "Scenario", "variable", "value"], + ].copy() + data["variable"] = pd.to_numeric(data["variable"], errors="coerce") + data["value"] = pd.to_numeric(data["value"], errors="coerce") + data = data.dropna(subset=["variable", "value"]) + # Some raw ATBe rows differ in metadata not needed for plotting. Average + # exact display/scenario/year duplicates into one visible series. + return ( + data.groupby( + ["Technology", "DisplayName", "Scenario", "variable"], + as_index=False, + )["value"] + .mean() + .sort_values(["Technology", "DisplayName", "Scenario", "variable"]) + ) + + +def plot_metric(data, metric, config, figure_format, output_dir): + """Write one multi-panel figure and return its path.""" + technologies = config["plotting"]["technologies"] + technologies = [tech for tech in technologies if tech in set(data["Technology"])] + if not technologies: + raise ValueError( + f"No data found for metric={metric!r}, case={config['plotting']['case']!r}, " + f"CRPYears={config['plotting']['crp_years']}." + ) + + ncols = min(3, len(technologies)) + nrows = math.ceil(len(technologies) / ncols) + figure, axes = plt.subplots( + nrows, ncols, figsize=(5.2 * ncols, 3.6 * nrows), squeeze=False + ) + colors = plt.get_cmap("tab20") + + for panel, technology in enumerate(technologies): + axis = axes.flat[panel] + tech_data = data.loc[data["Technology"] == technology] + display_names = sorted(tech_data["DisplayName"].dropna().unique()) + for index, display_name in enumerate(display_names): + series = tech_data.loc[tech_data["DisplayName"] == display_name] + pivot = series.pivot_table( + index="variable", columns="Scenario", values="value", aggfunc="mean" + ).sort_index() + if "Moderate" not in pivot: + continue + color = colors(index % colors.N) + axis.plot(pivot.index, pivot["Moderate"], color=color, label=display_name) + if {"Advanced", "Conservative"}.issubset(pivot.columns): + band = pivot[["Advanced", "Conservative"]].dropna() + axis.fill_between( + band.index, + band.min(axis=1), + band.max(axis=1), + color=color, + alpha=0.18, + linewidth=0, + ) + axis.set_title(technology) + axis.set_xlabel("Projection year") + year_ticks = select_year_ticks(tech_data["variable"]) + axis.set_xticks(year_ticks, labels=[str(year) for year in year_ticks]) + axis.tick_params(axis="x", labelsize=8, pad=3) + axis.margins(x=0.03) + unit = UNITS.get(metric, "value") + axis.set_ylabel(f"{metric} ({config['atb']['dollar_year']} {unit})") + axis.grid(axis="y", linestyle=":", alpha=0.5) + if len(display_names) > 1: + axis.legend(fontsize=7, frameon=False) + + for panel in range(len(technologies), nrows * ncols): + axes.flat[panel].set_visible(False) + + figure.suptitle( + f"NLR ATB {config['atb']['year']} — {metric} " + f"({config['plotting']['case']}, CRP {config['plotting']['crp_years']})" + ) + figure.tight_layout() + output_dir.mkdir(parents=True, exist_ok=True) + safe_metric = metric.lower().replace(" ", "_").replace("&", "and") + output = output_dir / f"atb_{config['atb']['year']}_{safe_metric}.{figure_format}" + figure.savefig(output, dpi=200, bbox_inches="tight") + plt.close(figure) + print(f"Saved plot: {output}") + return output + + +def make_plots(config, metrics=None, show=False, save_cleaned=False): + """Create all configured plots from the local raw flat file.""" + plot_config = config["plotting"] + metrics = metrics or plot_config["metrics"] + raw = load_raw_atb(config) + output_dir = resolve_atb_path(plot_config["output_directory"]) + outputs = [] + for metric in metrics: + data = clean_metric_data( + raw, + metric, + plot_config["case"], + plot_config["crp_years"], + plot_config["technologies"], + ) + if save_cleaned: + output_dir.mkdir(parents=True, exist_ok=True) + clean_path = output_dir / ( + f"atb_{config['atb']['year']}_{metric.lower().replace(' ', '_')}_cleaned.csv" + ) + # LF line endings on every platform, matching the formatter's output + data.to_csv(clean_path, index=False, lineterminator="\n") + print(f"Saved cleaned plotting data: {clean_path}") + outputs.append( + plot_metric(data, metric, config, plot_config["figure_format"], output_dir) + ) + if show: + print("Plots were saved; open the files listed above to view them.") + return outputs + + +def main(): + parser = argparse.ArgumentParser(description="Plot configured raw NLR ATB metrics.") + parser.add_argument("--config", help="Path to config.yaml (default: ../config.yaml).") + parser.add_argument( + "--metric", "-m", nargs="+", + help="One or more ATB parameters; defaults to plotting.metrics in config.yaml.", + ) + parser.add_argument( + "--save-cleaned", action="store_true", + help="Also save the cleaned data behind each plot.", + ) + parser.add_argument("--show", action="store_true", help="Print a viewing reminder.") + args = parser.parse_args() + make_plots( + load_config(args.config), + metrics=args.metric, + show=args.show, + save_cleaned=args.save_cleaned, + ) + + +if __name__ == "__main__": + main() diff --git a/atb/scripts/battery_workbook.py b/atb/scripts/battery_workbook.py new file mode 100644 index 0000000..d697a1c --- /dev/null +++ b/atb/scripts/battery_workbook.py @@ -0,0 +1,86 @@ +"""Extract ReEDS battery cost components from a downloaded ATB workbook.""" + +from pathlib import Path + +import openpyxl +import pandas as pd + + +BATTERY_SHEET = "Utility-Scale Battery Storage" +BLOCKS = { + "Capital Cost ($/kWh)": "capcost_energy", + "Battery Power Capital Cost ($/kW)": "capcost", +} +SCENARIOS = ["Advanced", "Moderate", "Conservative"] + + +def extract_battery_costs(xlsx_path): + """Return battery power/energy capital costs by scenario and year.""" + workbook_path = Path(xlsx_path) + workbook = openpyxl.load_workbook( + workbook_path, read_only=True, data_only=True + ) + try: + if BATTERY_SHEET not in workbook.sheetnames: + raise ValueError( + f"Sheet {BATTERY_SHEET!r} not found in {workbook_path}." + ) + rows = list(workbook[BATTERY_SHEET].iter_rows(values_only=True)) + finally: + workbook.close() + + def find_label_row(label): + for index, row in enumerate(rows): + if any( + isinstance(value, str) and value.strip() == label + for value in row[:6] + ): + return index + raise ValueError(f"Label {label!r} not found in {BATTERY_SHEET}.") + + records = [] + for label, cost_name in BLOCKS.items(): + label_row = find_label_row(label) + year_header = rows[label_row + 1] + year_columns = [ + (column, int(value)) + for column, value in enumerate(year_header) + if isinstance(value, (int, float)) and 2018 <= value <= 2061 + ] + if not year_columns: + raise ValueError(f"No year header found below {label!r}.") + + for offset, scenario in enumerate(SCENARIOS): + data_row = rows[label_row + 2 + offset] + labels = [ + str(value).strip() + for value in data_row[:5] + if isinstance(value, str) + ] + if scenario not in labels: + raise ValueError( + f"Expected scenario {scenario!r} below {label!r}, " + f"got {labels!r}." + ) + record = {"cost": cost_name, "Scenario": scenario} + record.update( + {year: data_row[column] for column, year in year_columns} + ) + records.append(record) + + frame = pd.DataFrame(records) + year_columns = sorted( + column for column in frame.columns if isinstance(column, int) + ) + cost_order = {"capcost": 0, "capcost_energy": 1} + scenario_order = {scenario: index for index, scenario in enumerate(SCENARIOS)} + frame = ( + frame.assign( + _cost_order=frame["cost"].map(cost_order), + _scenario_order=frame["Scenario"].map(scenario_order), + ) + .sort_values(["_cost_order", "_scenario_order"]) + .drop(columns=["_cost_order", "_scenario_order"]) + .reset_index(drop=True) + ) + return frame[["cost", "Scenario", *year_columns]] diff --git a/atb/scripts/generate_atb_files.py b/atb/scripts/generate_atb_files.py new file mode 100644 index 0000000..924a8ea --- /dev/null +++ b/atb/scripts/generate_atb_files.py @@ -0,0 +1,1566 @@ +""" +This script processes and produces ATB costs input files for ReEDS. +Workflow choices are defined in ../config.yaml; technology-specific formatting +rules are defined in settings.yaml. This formatter reads local raw inputs only. +The script runs in the standard ReEDS environment (reeds2). +""" + +#%% =========================================================================== +### --- IMPORTS --- +### =========================================================================== +import argparse +import os +import numpy as np +import pandas as pd +import shutil +from pandas.errors import EmptyDataError +import sys + +from atb_config import ATBE_COLUMN_MAPPING, load_processing_settings + +# directory containing this script (atb/scripts); settings.yaml lives here. +THISDIR = os.path.dirname(os.path.abspath(__file__)) +# parent atb/ directory; used to build paths to input/, manual_input/, output/. +ATBDIR = os.path.dirname(THISDIR) + +# define zero marginal cost technologies (to assign zero 'vom' if input data is missing) +zero_vom_techs = {'upv', 'wind-ons', 'wind-ofs', 'battery'} + +# write LF line endings on every platform so repeated runs on Windows do not +# rewrite every versioned CSV with different line endings than the stored ones +CSV_LINE_TERMINATOR = "\n" + +#%% =========================================================================== +### --- FUNCTIONS --- +### =========================================================================== + +def subset_atb_rows(subset_rows, atb_data_in): + """ + Subset the ATB flat file data to desired Case | CRPYears rows + + Parameters + ---------- + subset_rows: dict + Default {Case: "Exp + TC", CRPYears: 30} for most techs; some techs can have other values for Case, e.g., Market, R&D etc.) + atb_data_in: pd.DataFrame + ATB flat file dataframe to subset + """ + atb_data = atb_data_in.copy() + atb_col_vals = {} + # subset to relevant cases (typically Market case and 30-year CRP, but specified in settings.yaml) + for sr in subset_rows: + print(f"Subsetting to {sr}={subset_rows[sr]}") + if sr not in atb_data.columns: + raise ValueError(f"Column {sr} not in ATB file.") + else: + atb_col_vals[sr] = atb_data_in[sr].unique().tolist() + atb_data = atb_data.loc[atb_data[sr] == subset_rows[sr]] + return atb_data, atb_col_vals + +def load_atb_flat_file(settings, args, techs_to_run): + """ + load ATB flat file with all inputs for specified techs + + Parameters + ---------- + settings: dict + Parameter settings from settings.yaml + args: argparse.Namespace + Used for debug mode + techs_to_run: list[str] + Ordered list of tech keys (matching keys in settings['techs']) + """ + filepath = settings['flat_file_path'] + if not os.path.isfile(filepath): + raise FileNotFoundError( + f"Raw ATB flat file not found: {filepath}\n" + "Run 'python scrape_atb_inputs.py' before formatting ReEDS inputs." + ) + print(f"Loading raw ATB flat file: {filepath}") + atb_data_in = pd.read_csv(filepath, low_memory=False) + # Apply the same mapping for downloaded ATBe files and canonical flat files. + atb_data_in = atb_data_in.rename(columns=ATBE_COLUMN_MAPPING) + + if args.debug: + breakpoint() + + # store validated/subsetted data for all requested techs here + validated_data_list = [] + + # iterate over requested techs to validate and subset immediately + for tech in techs_to_run: + tech_settings = settings['techs'][tech] + + # 1. Get tech-specific subset settings + if 'subset_rows' not in tech_settings: + raise ValueError(f"Missing 'subset_rows' for {tech} in settings.yaml") + + # 2. Filter by Technology + tech_rows_full = atb_data_in[atb_data_in['Technology'] == tech_settings['Technology']] + + if tech_rows_full.empty: + print(f"Warning: No data found for Technology '{tech_settings['Technology']}' ({tech}).") + continue + + # 3. Apply specific subset_rows (Case, CRPYears, etc.) + # subset_atb_rows returns (subset_df, col_vals) + tech_subset, _ = subset_atb_rows(tech_settings['subset_rows'], tech_rows_full) + + # 4. Filter for specific DisplayName(s) if applicable + if 'DisplayName' in tech_settings: + atb_techs = tech_settings['DisplayName'] + atb_techs_list = list(atb_techs.keys()) if isinstance(atb_techs, dict) else [atb_techs] + + # append all subtechs present + found_data = tech_subset[tech_subset['DisplayName'].isin(atb_techs_list)] + missing_subtechs = [t for t in atb_techs_list if t not in found_data['DisplayName'].values] + + if found_data is not None and not found_data.empty: + validated_data_list.append(found_data) + + # fallback: handle any subtechs that were missing from the subset + for atb_tech_name in missing_subtechs: + # Fallback logic: check if it exists in the wider tech data (e.g., for a different Case) + temp_df = tech_rows_full[tech_rows_full['DisplayName'] == atb_tech_name] + + if temp_df.empty: + print(f"Warning: DisplayName '{atb_tech_name}' not found in source data for {tech}.") + continue + print(f"Could not subset {atb_tech_name} data to {tech_settings['subset_rows']}.") + print(f"Available Case options: {temp_df.Case.unique()}") + + new_case = str(input("Select an alternative Case to proceed, or (q) to quit: ")) + + if new_case.lower() == "q": + sys.exit("Exiting now.") + elif new_case not in temp_df.Case.unique(): + print(f"'{new_case}' not found in available Case options. Exiting.") + sys.exit(1) + else: + # Grab the data for the alternative case + new_subset_rows = tech_settings['subset_rows'].copy() + new_subset_rows['Case'] = new_case + + # We only need this specific subtech with the new case + subtech_data, _ = subset_atb_rows(new_subset_rows, temp_df) + validated_data_list.append(subtech_data) + else: + # If no DisplayName filtering is needed, just keep the successful subset + if not tech_subset.empty: + validated_data_list.append(tech_subset) + + # Combine all valid chunks + if not validated_data_list: + raise EmptyDataError(f"\n\nNo valid data found for any of the requested technologies: {techs_to_run}") + + atb_data_final = pd.concat(validated_data_list, ignore_index=True) + return atb_data_final + +def get_atb_file_path(tech, atbyear, scenario, settings): + """ + function to construct full filepath and per-tech ATB filename + + Parameters + ---------- + tech: str + Tech identifier used as the file prefix (e.g., 'gas', 'upv' etc.) + atbyear: int + ATB year (e.g., 2025) to process + scenario: str + ATB scenario (Moderate/Advanced) from settings.yaml + settings: dict + Loaded settings from settings.yaml + """ + # allow per-tech override of the filename in existing ReEDS input files + # e.g. settings['techs']['wind-ons']['reeds_name'] = "ons-wind" + filename_root = settings['techs'].get(tech, {}).get('reeds_name', tech) + filename = f"{filename_root}_ATB_{atbyear}_{scenario.lower()}" + filepath = os.path.join( + settings['reedspath'], 'inputs', 'plant_characteristics', filename + ".csv" + ) + return filename, filepath + +def check_columns(df, col_list, setting, tech): + """ + function to check that all columns specified in settings.yml exist in the data + + Parameters + ---------- + col_list: list[str] + Sequence of required column names based on tech settings + setting: str + Settings key being validated (used in the error message, e.g. 'indexcols' or 'cols') + tech: str + Technology identifier + """ + missing_cols = [c for c in col_list if c not in df.columns] + if len(missing_cols) > 0: + raise ValueError( + f"The following columns are specified as '{setting}' but are not in the data: {missing_cols}. " + f"Please update your 'settings.yaml' for {tech}." + ) + +def _history_file_path(tech, scenario, settings): + """Return the stable, year-independent history path for one scenario.""" + return os.path.join( + settings['history_dir'], + f"{tech}_ATB_historical_{str(scenario).lower()}.csv", + ) + + +def _cost_columns(df): + """Return every monetary output column, including battery energy costs.""" + return [ + col for col in df.columns + if col == 'vom' or col.startswith('capcost') or col.startswith('fom') + ] + + +def _select_pre_projection_history(history, current, idcols): + """Keep history before each current series starts and all retired series.""" + if not idcols: + return history.loc[history['t'] < current['t'].min()].copy() + + starts = ( + current.groupby(idcols, dropna=False, as_index=False)['t'] + .min() + .rename(columns={'t': '_projection_start'}) + ) + selected = history.merge(starts, on=idcols, how='left') + selected = selected.loc[ + selected['_projection_start'].isna() + | (selected['t'] < selected['_projection_start']) + ] + return selected.drop(columns='_projection_start') + + +def _projection_boundary_rows(current, idcols): + """Select the first current-ATB year for every active technology series.""" + if not idcols: + return current.loc[current['t'] == current['t'].min()].copy() + starts = current.groupby(idcols, dropna=False)['t'].transform('min') + return current.loc[current['t'] == starts].copy() + + +def _validate_year_continuity(frame, tech, settings): + """Fail if any output series has a missing year or starts after ReEDS.""" + groupcols = [ + col for col in settings['techs'][tech]['indexcols'] if col != 't' + ] + failures = [] + grouper = groupcols[0] if len(groupcols) == 1 else groupcols + for group_values, group in frame.groupby(grouper, dropna=False, sort=False): + years = sorted(pd.to_numeric(group['t'], errors='raise').astype(int).unique()) + expected = set(range(settings['reeds_start_year'], max(years) + 1)) + missing = sorted(expected - set(years)) + if years[0] != settings['reeds_start_year'] or missing: + values = group_values if isinstance(group_values, tuple) else (group_values,) + label = dict(zip(groupcols, values)) + failures.append( + f"{label}: starts {years[0]}, missing {missing[:10]}" + ) + if failures: + raise ValueError( + f"Historical continuity check failed for {tech}: " + + "; ".join(failures[:10]) + ) + + +def _normalize_reeds_history(frame, tech, settings): + """Normalize a ReEDS file to the current technology's output schema.""" + tech_settings = settings['techs'][tech] + frame = frame.rename(columns=tech_settings.get('renamecols', {})).copy() + required = tech_settings['cols'] + missing = [col for col in required if col not in frame.columns] + if missing: + raise KeyError( + f"Historical ReEDS file for {tech} is missing columns {missing}. " + f"Available columns: {list(frame.columns)}" + ) + frame = frame[required] + frame['t'] = pd.to_numeric(frame['t'], errors='raise').astype(int) + return frame + + +def _seed_history_from_reeds( + tech, scenario, current, history_path, settings, dollaryear, deflator +): + """Create a local historical baseline from the current ReEDS ATB file.""" + reeds_key, reeds_path = get_atb_file_path( + tech, settings['atbyear'], scenario, settings + ) + if not os.path.isfile(reeds_path): + moderate_key, moderate_path = get_atb_file_path( + tech, settings['atbyear'], 'moderate', settings + ) + if os.path.isfile(moderate_path): + print( + f"...no {str(scenario).lower()} ReEDS history for {tech}; " + "using moderate history" + ) + reeds_key, reeds_path = moderate_key, moderate_path + else: + raise FileNotFoundError( + f"Historical baseline is missing: {history_path}\n" + f"Could not seed it because neither the matching nor moderate " + f"ReEDS file exists: {reeds_path}" + ) + + history = _normalize_reeds_history(pd.read_csv(reeds_path), tech, settings) + idcols = [ + col for col in settings['techs'][tech]['indexcols'] + if col not in ['Scenario', 't'] + ] + history = _select_pre_projection_history(history, current, idcols) + + source_dollar_year = int(dollaryear[reeds_key]) + history_dollar_year = settings['history_dollar_year'] + adjustment = ( + deflator[source_dollar_year] / deflator[history_dollar_year] + ) + costcols = _cost_columns(history) + history[costcols] = history[costcols] * adjustment + history = history.sort_values(settings['techs'][tech]['cols']).reset_index(drop=True) + os.makedirs(settings['history_dir'], exist_ok=True) + history.to_csv(history_path, index=False, lineterminator=CSV_LINE_TERMINATOR) + print( + f"...seeded {os.path.basename(history_path)} from " + f"{os.path.basename(reeds_path)}" + ) + return history + + +def merge_historical_atb_data( + tech_data, tech, settings, dollaryear, deflator +): + """Combine versioned local history with current scraped ATB projections. + + The local history is stored in a fixed dollar year. For each active series, + only rows before the first current scraped year are used. Series no longer + published by ATB are retained from history. The first current projection + year is appended to the stored history for the next annual update. + """ + tech_settings = settings['techs'][tech] + idcols = [ + col for col in tech_settings['indexcols'] + if col not in ['Scenario', 't'] + ] + combined = [] + current_dollar_year = settings['dollaryear'] + history_dollar_year = settings['history_dollar_year'] + + for scenario in tech_data['Scenario'].unique(): + current = tech_data.loc[tech_data['Scenario'] == scenario].copy() + history_path = _history_file_path(tech, scenario, settings) + if os.path.isfile(history_path): + history_stored = _normalize_reeds_history( + pd.read_csv(history_path), tech, settings + ) + elif settings['seed_missing_history']: + history_stored = _seed_history_from_reeds( + tech, scenario, current, history_path, settings, dollaryear, deflator + ) + else: + raise FileNotFoundError( + f"Historical baseline is missing: {history_path}. " + "Enable historical_data.seed_missing_from_reeds or add the file." + ) + + history_for_output = _select_pre_projection_history( + history_stored, current, idcols + ) + costcols = _cost_columns(history_for_output) + adjustment = ( + deflator[history_dollar_year] / deflator[current_dollar_year] + ) + history_for_output[costcols] = history_for_output[costcols] * adjustment + history_for_output['Scenario'] = scenario + combined.extend([history_for_output, current]) + + boundary = _projection_boundary_rows(current, idcols)[tech_settings['cols']] + boundary = boundary.copy().round( + tech_settings.get('decimals', settings['decimals']) + ) + boundary_costcols = _cost_columns(boundary) + boundary[boundary_costcols] = boundary[boundary_costcols] / adjustment + updated_history = pd.concat( + [history_stored, boundary], ignore_index=True + ).drop_duplicates(subset=[*idcols, 't'], keep='last') + updated_history = updated_history.sort_values( + tech_settings['cols'] + ).reset_index(drop=True) + updated_history.to_csv( + history_path, index=False, lineterminator=CSV_LINE_TERMINATOR + ) + + output = pd.concat(combined, ignore_index=True) + output = output.drop_duplicates( + subset=tech_settings['indexcols'], keep='last' + ) + output = output.reset_index(drop=True) + _validate_year_continuity(output, tech, settings) + return output + +def normalize_cf(tech, settings, df): + """ + function to normalize capacity factor to relevant base year and scenario (specified by 'cfbase') + + Parameters + ---------- + tech: str + Technology key in settings['techs'] for which cf_improvement values will be normalized + settings: dict + Parsed settings.yaml values. Expects settings['techs'][tech]['cfbase'] to define the base + selection. Example cfbase entries: + - keys with exact match values to select the base row(s) + - optional 'keepcols' list to indicate merge keys when multiple columns define the base + - 't' may be used to indicate the base year + """ + print("Normalizing cf_improvement") + tech_settings = settings['techs'][tech] + cf_base = df.copy() + for k,v in tech_settings['cfbase'].items(): + if k in df.columns: + print(f"...base {k}={v}") + cf_base = cf_base.loc[(cf_base[k] == v)] + if 'keepcols' in tech_settings['cfbase']: + cf_base = cf_base[tech_settings['cfbase']['keepcols'] + ['cf_improvement']].rename(columns={"cf_improvement":"cf_base"}) + df = df.merge(cf_base, on=tech_settings['cfbase']['keepcols'], how='left') + df['cf_improvement'] = df['cf_improvement'] / df['cf_base'] + df = df.drop(columns='cf_base') + else: + if len(cf_base) > 1: + raise ValueError("Error: more than one value found for cf_improvement without specifying which columns to keep.") + else: + df['cf_improvement'] = df['cf_improvement'] / cf_base['cf_improvement'].values[0] + + # check that base level is properly normalized to 1 + assert (df.loc[cf_base.index, 'cf_improvement'] == 1).all(), "Check cf_improvement normalization." + + return df + +def apply_offshore_cost_multipliers(tech, settings, df): + """Apply ReEDS offshore configuration adjustments to ATB class proxies.""" + multiplier_path = os.path.join( + ATBDIR, + "manual_input", + f"offshore_cost_multipliers_{settings['atbyear']}.csv", + ) + multipliers = pd.read_csv(multiplier_path).set_index("turbine") + missing = set(df["turbine"].unique()) - set(multipliers.index) + if missing: + raise ValueError( + f"Missing offshore cost multiplier(s) for turbine(s): {sorted(missing)}" + ) + for metric in ("capcost", "fom"): + df[metric] *= df["turbine"].map(multipliers[metric]) + return df + + +def apply_coal_projection_overrides(tech, settings, df): + """Preserve the published ReEDS 2024 coal values changed by ATB v3.""" + override_path = os.path.join( + ATBDIR, + "manual_input", + f"coal_projection_overrides_{settings['atbyear']}.csv", + ) + overrides = pd.read_csv(override_path) + overrides = overrides.loc[overrides["tech"] == tech].drop(columns="tech") + keys = ["Scenario", "i", "t"] + values = [column for column in overrides if column not in keys] + indexed = df.set_index(keys) + override_indexed = overrides.set_index(keys) + missing = override_indexed.index.difference(indexed.index) + if not missing.empty: + raise ValueError(f"Coal override rows not found in ATB data: {missing.tolist()}") + indexed.update(override_indexed[values]) + return indexed.reset_index() + +def smooth_hist_cf(tech, settings, df): + """ + function to interpolate historical capacity factor values through 2035 + + Parameters + ---------- + tech: str + Technology key in settings['techs'] to use when looking up cfbase/base year + settings: dict + Expects settings['techs'][tech]['cfbase']['t'] to specify the base year for interpolation + df: pd.DataFrame + Input dataframe containing ['Scenario', 't', 'cf_improvement'] columns + """ + # sort by scenario and year + df = df.sort_values(by=['Scenario', 't']) + # remove values between 2022 (ATB starts in 2023) and atb base year (usually 2035) + tech_settings = settings['techs'][tech] + baseyear = tech_settings['cfbase']['t'] + df['cf_improvement'] = np.where((df['t'] > 2022) & (df['t'] < baseyear), np.nan, df['cf_improvement']) + # interpolate to fill dropped values + df['cf_improvement'] = df.groupby('Scenario')['cf_improvement'].transform(lambda x: x.interpolate(method='linear')) + + return df + + +def _relative_change(left, right, epsilon): + """Return a scale-independent change between two finite values.""" + return abs(right - left) / max(abs(left), abs(right), epsilon) + + +def _bridge_similar_value_runs( + years, + values, + start, + relative_tolerance, + absolute_tolerance, + major_step_threshold, +): + """Interpolate between the last points of near-equal value runs. + + Rounded ATB trajectories can appear as small stairs or as a short flat dip + in an otherwise sloped curve. This follows the change-point idea used by + ``state_policies`` but retains the *last* year in each similar-value run. + Large transitions are retained on both sides so real technology milestones + are not spread over several years. + """ + bridged = np.asarray(values, dtype=float).copy() + if len(bridged) - start < 3: + return bridged + + epsilon = max(np.max(np.abs(bridged)) * 1e-12, 1e-12) + knots = {start, len(bridged) - 1} + run_reference = bridged[start] + + for position in range(start + 1, len(bridged)): + if np.isclose( + bridged[position], + run_reference, + rtol=relative_tolerance, + atol=absolute_tolerance, + ): + continue + + run_end = position - 1 + knots.add(run_end) + if ( + _relative_change( + bridged[run_end], bridged[position], epsilon + ) + >= major_step_threshold + ): + knots.add(position) + run_reference = bridged[position] + + knot_positions = np.asarray(sorted(knots), dtype=int) + projection_positions = np.arange(start, len(bridged)) + bridged[projection_positions] = np.interp( + years[projection_positions], + years[knot_positions], + bridged[knot_positions], + ) + return bridged + + +def _selective_smooth_cost_values( + years, + values, + projection_start_year, + slope_change_threshold, + max_kink_years, + similar_value_relative_tolerance, + similar_value_absolute_tolerance, + major_step_threshold, +): + """Remove boundary dips and short kinks while retaining long-term steps.""" + years = np.asarray(years, dtype=float) + smoothed = np.asarray(values, dtype=float).copy() + if len(smoothed) < 3: + return smoothed + + year_span = years[-1] - years[0] + if year_span <= 0: + return smoothed + + anchor_candidates = np.flatnonzero(years == projection_start_year) + if len(anchor_candidates) != 1: + raise ValueError( + "Selective cost smoothing requires exactly one row for " + f"projection_start_year={projection_start_year}." + ) + anchor = int(anchor_candidates[0]) + + # Determine direction from the anchor and the projection endpoint, so a + # low historical dip cannot become the ceiling for the future trajectory. + decreasing = smoothed[-1] <= smoothed[anchor] + if decreasing: + # Remove isolated upward historical spikes locally. This avoids using + # one abnormal year (for example, nuclear 2021) as a new plateau. + for position in range(1, anchor): + if ( + smoothed[position] > smoothed[position - 1] + and smoothed[position] > smoothed[position + 1] + ): + smoothed[position] = np.interp( + years[position], + [years[position - 1], years[position + 1]], + [smoothed[position - 1], smoothed[position + 1]], + ) + smoothed[anchor:] = np.minimum.accumulate(smoothed[anchor:]) + else: + # For an improving multiplier, remove isolated historical dips rather + # than upward spikes. + for position in range(1, anchor): + if ( + smoothed[position] < smoothed[position - 1] + and smoothed[position] < smoothed[position + 1] + ): + smoothed[position] = np.interp( + years[position], + [years[position - 1], years[position + 1]], + [smoothed[position - 1], smoothed[position + 1]], + ) + smoothed[anchor:] = np.maximum.accumulate(smoothed[anchor:]) + + # Raise only the historical tail below the anchor value. Stop as soon as + # an earlier value already meets or exceeds the anchor. This removes gas + # and coal-CCS cost dips and levels low capacity-factor history without + # lowering technologies whose historical values are already higher. + position = anchor - 1 + while position >= 0 and smoothed[position] < smoothed[anchor]: + smoothed[position] = smoothed[anchor] + position -= 1 + + # Replace rounded stair steps and short flat spots with transitions between + # the last points of their near-equal runs before evaluating residual + # slope kinks. Large published steps remain explicit milestones. + smoothed = _bridge_similar_value_runs( + years, + smoothed, + anchor, + similar_value_relative_tolerance, + similar_value_absolute_tolerance, + major_step_threshold, + ) + + year_deltas = np.diff(years) + if np.any(year_deltas <= 0): + raise ValueError("Cost smoothing requires unique, increasing years.") + slopes = np.diff(smoothed) / year_deltas + baseline_slope = abs((smoothed[-1] - smoothed[0]) / year_span) + epsilon = max(np.max(np.abs(smoothed)) * 1e-12, 1e-12) + + candidate_knots = [] + for knot in range(1, len(smoothed) - 1): + if knot < anchor: + continue + left_slope = slopes[knot - 1] + right_slope = slopes[knot] + scale = max(abs(left_slope), abs(right_slope), baseline_slope, epsilon) + if abs(right_slope - left_slope) / scale >= slope_change_threshold: + candidate_knots.append(knot) + + # A single slope change is a normal ATB milestone (for example, CSP in + # 2030). Several adjacent changes indicate a short-lived kink. Interpolate + # between the outside shoulders of those compact clusters; using the kink + # points themselves as endpoints can leave the steep side of a dip intact. + clusters = [] + for knot in candidate_knots: + if clusters and knot == clusters[-1][-1] + 1: + clusters[-1].append(knot) + else: + clusters.append([knot]) + for cluster in clusters: + first, last = cluster[0], cluster[-1] + if len(cluster) < 2 or years[last] - years[first] > max_kink_years: + continue + left = max(anchor, first - 1) + # Include the following segment as well as the immediate shoulder. A + # short plateau can otherwise hide the exit side of a dip after the + # near-equal-run pass has already bridged its flat bottom. + right = min(len(smoothed) - 1, last + 2) + step_changes = [ + _relative_change(smoothed[position], smoothed[position + 1], epsilon) + for position in range(left, right) + ] + if any(change >= major_step_threshold for change in step_changes): + continue + interpolation_index = np.arange(left, right + 1) + smoothed[interpolation_index] = np.interp( + years[interpolation_index], + [years[left], years[right]], + [smoothed[left], smoothed[right]], + ) + + return smoothed + + +def smooth_cost_curve(tech, settings, df): + """Smooth monetary costs without erasing normal ATB trajectory changes. + + ``selective`` mode removes temporary direction reversals, bridges the last + points of near-equal value runs, and bridges clusters of adjacent slope + changes. A single slope change and any configured major step are retained + as published milestones. ``linear_bridge`` preserves the earlier behavior + for users who explicitly want a flat history and one anchor-to-target line. + """ + smoothing = settings['config']['processing'].get('smooth_cost_curves', {}) + if not smoothing.get('enabled', False): + return df + configured_techs = smoothing.get('technologies', []) + smooth_all_techs = configured_techs == 'all' or configured_techs == ['all'] + if not smooth_all_techs and tech not in configured_techs: + return df + + configured_columns = smoothing.get('columns', 'all') + if configured_columns == 'all' or configured_columns == ['all']: + columns = _cost_columns(df) + elif isinstance(configured_columns, list): + columns = list(configured_columns) + else: + raise TypeError( + "processing.smooth_cost_curves.columns must be 'all' or a list." + ) + if ( + smoothing.get('include_capacity_factor_multiplier', False) + and 'cf_improvement' in df.columns + and 'cf_improvement' not in columns + ): + columns.append('cf_improvement') + if not columns: + raise ValueError( + f"Cannot smooth {tech}; no monetary cost columns were selected." + ) + missing = [column for column in columns if column not in df.columns] + if missing: + raise ValueError( + f"Cannot smooth {tech}; configured cost columns are missing: {missing}." + ) + + method = smoothing.get('method', 'selective') + if method not in ['selective', 'linear_bridge']: + raise ValueError( + "processing.smooth_cost_curves.method must be 'selective' " + "or 'linear_bridge'." + ) + + anchor_year = int(smoothing.get('anchor_year', 2022)) + target_year = int(smoothing.get('target_year', 2035)) + if method == 'linear_bridge' and target_year <= anchor_year: + raise ValueError( + "processing.smooth_cost_curves.target_year must be later than anchor_year." + ) + projection_start_year = int( + smoothing.get('projection_start_year', anchor_year) + ) + slope_change_threshold = float( + smoothing.get('slope_change_threshold', 0.5) + ) + max_kink_years = int(smoothing.get('max_kink_years', 4)) + similar_value_relative_tolerance = float( + smoothing.get('similar_value_relative_tolerance', 0.001) + ) + similar_value_absolute_tolerance = float( + smoothing.get('similar_value_absolute_tolerance', 1e-9) + ) + major_step_threshold = float( + smoothing.get('major_step_relative_threshold', 0.1) + ) + if slope_change_threshold <= 0: + raise ValueError("slope_change_threshold must be greater than zero.") + if max_kink_years < 1: + raise ValueError("max_kink_years must be at least one.") + if similar_value_relative_tolerance < 0: + raise ValueError( + "similar_value_relative_tolerance must be nonnegative." + ) + if similar_value_absolute_tolerance < 0: + raise ValueError( + "similar_value_absolute_tolerance must be nonnegative." + ) + if major_step_threshold <= similar_value_relative_tolerance: + raise ValueError( + "major_step_relative_threshold must be greater than " + "similar_value_relative_tolerance." + ) + + tech_settings = settings['techs'][tech] + idcols = [ + column for column in tech_settings['indexcols'] + if column not in ['Scenario', 't'] + ] + groupcols = ['Scenario', *idcols] + output = df.sort_values([*groupcols, 't']).copy() + # Interpolation generally creates fractional values even when an input + # frame happened to infer an integer dtype. + output[columns] = output[columns].astype(float) + groups = output.groupby(groupcols, dropna=False, sort=False).groups + + print(f"Smoothing {tech} cost curves: method={method}, columns={columns}") + for group_values, index in groups.items(): + group = output.loc[index].sort_values('t') + if method == 'selective': + for column in columns: + output.loc[group.index, column] = _selective_smooth_cost_values( + group['t'], + group[column], + projection_start_year, + slope_change_threshold, + max_kink_years, + similar_value_relative_tolerance, + similar_value_absolute_tolerance, + major_step_threshold, + ) + continue + + anchor_rows = group.loc[group['t'] == anchor_year] + target_rows = group.loc[group['t'] == target_year] + label_values = ( + group_values if isinstance(group_values, tuple) else (group_values,) + ) + label = dict(zip(groupcols, label_values)) + if len(anchor_rows) != 1 or len(target_rows) != 1: + raise ValueError( + f"Cannot smooth {tech} series {label}: expected exactly one row " + f"for anchor year {anchor_year} and target year {target_year}." + ) + + anchor_index = anchor_rows.index[0] + target_index = target_rows.index[0] + if smoothing.get('flatten_historical', True): + historical_index = group.index[group['t'] <= anchor_year] + output.loc[historical_index, columns] = output.loc[ + anchor_index, columns + ].to_numpy() + + bridge_index = group.index[ + (group['t'] > anchor_year) & (group['t'] < target_year) + ] + for column in columns: + anchor_value = float(output.at[anchor_index, column]) + target_value = float(output.at[target_index, column]) + bridge_years = output.loc[bridge_index, 't'].astype(float) + output.loc[bridge_index, column] = anchor_value + ( + (target_value - anchor_value) + * (bridge_years - anchor_year) + / (target_year - anchor_year) + ) + + return output.sort_index() + +def add_beccs_techs(tech, settings, df, techcol='i'): + """ + function to copy costs for beccs_mod to beccs_max + + Parameters + ---------- + tech: str + Technology key in settings['techs'] + df: pd.DataFrame + Must include column `techcol` for beccs technologies + techcol: str, optional + Column name identifying the technology label in `df` (default 'i') + """ + # copy beccs_max from beccs_mod + df_add = df.copy() + df_add['i'] = "beccs_max" + df_out = pd.concat([df, df_add]) + + return df_out + +def format_continuous_battery(tech, settings, df): + """ + function to format battery technology data + + Parameters + ---------- + tech: str + Technology key from settings (used for logging) + settings: dict + Parsed settings.yaml values (for expected column names) + df: pd.DataFrame + Input dataframe with columns that will be replaced by the battery cost fields (the function drops ['capcost', 'fom'] before merging) + """ + # Battery power/energy capital costs are extracted from the raw workbook + # downloaded by scrape_atb_inputs.py. The formatter itself never downloads + # raw data. A manual CSV remains available for pre-release ATB years. + atbyear = settings['atbyear'] + from battery_workbook import extract_battery_costs + workbook = settings['workbook_path'] + if os.path.isfile(workbook): + battery_costs = extract_battery_costs(workbook) + # round to 2 decimals to match the historical battery_costs_.csv + yearcols = [c for c in battery_costs.columns if c not in ('cost', 'Scenario')] + battery_costs[yearcols] = battery_costs[yearcols].round(2) + battery_costs.columns = [str(c) for c in battery_costs.columns] + else: + fallback = os.path.join(ATBDIR, 'manual_input', f"battery_costs_{atbyear}.csv") + print(f"Raw ATB workbook not found for {atbyear}; using manual fallback {fallback}") + if not os.path.isfile(fallback): + raise FileNotFoundError( + f"Neither raw workbook nor manual battery fallback exists.\n" + f"Expected workbook: {workbook}\nExpected fallback: {fallback}\n" + "Run 'python scrape_atb_inputs.py --only workbook'." + ) + battery_costs = pd.read_csv(fallback) + # reshape and format + battery_costs = pd.melt(battery_costs, id_vars=['cost','Scenario'], var_name='t') + battery_costs = battery_costs.pivot(index=['Scenario', 't'], columns='cost', values='value').reset_index().rename_axis(None, axis=1) + battery_costs['t'] = pd.to_numeric(battery_costs['t'], errors='coerce').astype('Int64') + # assign FOM (assumed to be 2.5% of capital costs) + fom_mult = 0.025 + battery_costs['fom'] = battery_costs['capcost'] * fom_mult + battery_costs['fom_energy'] = battery_costs['capcost_energy'] * fom_mult + battery_costs[['fom', 'fom_energy']] = battery_costs[ + ['fom', 'fom_energy'] + ].round(2) + # merge with placeholder data and return + df = df.drop(['capcost', 'fom'], axis=1).merge(battery_costs) + # if rte is missing from the ATB data, assign 0.85 as the default value + if 'rte' not in df.columns: + print("Warning: 'Round-Trip Efficiency' not found in ATB data for battery. Assigning default rte = 0.85.") + df['rte'] = 0.85 + return df + +def add_csp_techs(tech, settings, df, techcol='i'): + """ + function to create and append CSP tech types by scaling base tech values + + Parameters + ---------- + tech: str + Technology key in settings['techs'] + settings: dict + Parsed settings.yaml values to determine index columns and cost column names + df: pd.DataFrame + Must include `techcol` and cost columns listed in settings['cost_cols']) + techcol: str, optional + Column name identifying the technology label in `df` (default 'i'). + """ + # load cost ratios for csp techs + csp_ratios = pd.read_csv( + os.path.join(ATBDIR, "manual_input", f"csp_cost_ratios_{settings['atbyear']}.csv") + ).dropna(subset=['type', 'ratio', 'base_tech']) + print("updating csp tech costs using the following ratios:") + print(csp_ratios[['type','ratio']]) + + # merge with data + base_csp_tech = csp_ratios.loc[csp_ratios.base_tech==1, "type"].squeeze() + if isinstance(base_csp_tech, pd.Series): + raise ValueError("Multiple base csp techs specified in 'csp_cost_ratios.csv") + + # loop through non-base csp techs + df_add_all = [] + for _,row in csp_ratios.loc[csp_ratios.base_tech==0].iterrows(): + print(f"...updating cost data for {row['type']} using multiplier of {row['ratio']}") + # get data for baseline csp tech + df_add = df.loc[df.type == base_csp_tech].copy() + # update tech name + df_add['type'] = row['type'] + # apply multiplier to cost fields + df_add[settings['cost_cols']] *= row['ratio'] + # add to list + df_add_all.append(df_add) + + # combine entries and merge with original data + df_add_all = pd.concat(df_add_all) + df_out = pd.concat([df, df_add_all]) + # drop duplicates. keep values added here since previous ones + # come from the historic files and we want to preserve the newer ATB when available. + tech_settings = settings['techs'][tech] + df_out = df_out.drop_duplicates(subset=tech_settings['indexcols'], keep="last") + + return df_out + +def format_reeds_output(df, tech_settings): + """ + function to apply the ReEDS output schema to a formatted scenario table + + ReEDS reads some plant characteristic files by column position rather than by + column name, so those technologies declare an 'output_cols' mapping of + internal column name -> ReEDS header in settings.yaml. Technologies without + that mapping are written using the internal names in 'cols'. + + Parameters + ---------- + df: pd.DataFrame + Formatted single-scenario data using the internal column names + tech_settings: dict + Settings for the technology being written (settings['techs'][tech]) + """ + output_cols = tech_settings.get('output_cols') + if not output_cols: + return df + missing_cols = [c for c in output_cols if c not in df.columns] + if missing_cols: + raise ValueError( + f"The following columns are specified as 'output_cols' but are not in " + f"the data: {missing_cols}. Please update your 'settings.yaml'." + ) + dropped_cols = [c for c in df.columns if c not in output_cols] + if dropped_cols: + raise ValueError( + f"'output_cols' does not cover every output column; missing " + f"{dropped_cols}. Please update your 'settings.yaml'." + ) + return df[list(output_cols)].rename(columns=output_cols) + + +def write_technology_outputs( + tech_data, + tech, + settings, + tech_settings, + sensitivity_name, + outfolder, + filenames=None, + output_label="", +): + """Write one technology's scenario files using the ReEDS output schema.""" + tech_data = tech_data.copy() + tech_data['Scenario'] = tech_data['Scenario'].str.lower() + scenarios = list(tech_data['Scenario'].unique()) + if 'moderate' in scenarios: + scenarios.insert(0, scenarios.pop(scenarios.index('moderate'))) + + filename_root = tech_settings.get('reeds_name', tech) + baseline = None + written = [] + os.makedirs(outfolder, exist_ok=True) + + for scenario in scenarios: + if sensitivity_name is not None: + filename = ( + f"{filename_root}_ATB_{settings['atbyear']}_{scenario}_" + f"{sensitivity_name}.csv" + ) + else: + filename = f"{filename_root}_ATB_{settings['atbyear']}_{scenario}.csv" + + check_columns(tech_data, tech_settings['cols'], 'cols', tech) + scendata = tech_data.loc[ + tech_data.Scenario == scenario, tech_settings['cols'] + ] + scendata = scendata.round( + tech_settings.get('decimals', settings['decimals']) + ) + scendata = scendata.sort_values( + by=scendata.columns.to_list() + ).reset_index(drop=True) + + if baseline is None: + baseline = scendata.copy() + elif scendata.equals(baseline): + print( + f"...{scenario} is identical to the {scenarios[0]} scenario, " + "skipping." + ) + continue + + scendata_out = format_reeds_output(scendata, tech_settings) + label = f" {output_label}" if output_label else "" + print(f"Saving{label} {filename}") + scendata_out.to_csv( + os.path.join(outfolder, filename), + index=False, + lineterminator=CSV_LINE_TERMINATOR, + ) + written.append(filename) + if filenames is not None: + filenames.append(filename) + + return written + + +def process_tech_file(atb_data, tech, settings, filenames, dollaryear, deflator, sensitivity_name, outfolder, args): + """ + function to format per-technology output files + + Parameters + ---------- + atb_data: pd.DataFrame + Technology-level data from ATB flat-file + tech: str + Technology-specific key in settings['techs'] to process + settings: dict + Parsed settings.yaml values + filenames: list + List to which produced filenames will be appended. + dollaryear: pd.Series + Mapping of scenario -> dollar year + deflator: pd.Series + Mapping of dollar year -> deflator multiplier + sensitivity_name: str or None + Optional suffix added to output filenames for sensitivity runs + outfolder: str + Directory path where output CSVs will be written + args: argparse.Namespace + CLI arguments for debug mode and run options + """ + # subset to relevant technology + print(f"Processing {tech} file.") + tech_settings = settings['techs'][tech] + tech_data = atb_data.loc[atb_data['Technology'] == tech_settings['Technology']] + # apply tech-specific subset_rows (Case, CRPYears) + if 'subset_rows' in tech_settings: + tech_data, _ = subset_atb_rows(tech_settings['subset_rows'], tech_data) + else: + raise ValueError(f"Missing 'subset_rows' for {tech} in settings.yaml") + # get subtech(s), specified either as a dictionary or a single string + if 'DisplayName' in tech_settings: + if isinstance(tech_settings['DisplayName'], dict): + tech_data = tech_data.loc[tech_data['DisplayName'].isin(tech_settings['DisplayName'].keys())] + missing_subtechs = ( + set(tech_settings['DisplayName']) - set(tech_data['DisplayName'].unique()) + ) + tech_data = tech_data.replace({'DisplayName': tech_settings['DisplayName']}) + # verify all expected subtechs were found in the data + if missing_subtechs: + print(f"Warning: the following DisplayName(s) for {tech} were not found in the ATB subset: {sorted(missing_subtechs)}") + else: + print(f"Subsetting to {tech_settings['DisplayName']}") + tech_data = tech_data.loc[tech_data['DisplayName'] == tech_settings['DisplayName']] + if tech_data.empty: + if args.debug: + breakpoint() + else: + raise EmptyDataError(f"\n\nNo data found for {tech} in ATB data. Check file for values for specified subset_rows.") + + # add in columns as needed + if 'addcols' in tech_settings: + print(f"Adding columns {tech_settings['addcols']}") + for col in tech_settings['addcols']: + tech_data[col] = tech_settings['addcols'][col] + + # rename columns as needed + if 'renamecols' in tech_settings: + print(f"Renaming columns {tech_settings['renamecols']}") + tech_data = tech_data.rename(columns=tech_settings['renamecols']) + + # grab columns needed for this particular tech + atb_params = [k for k, v in settings['param_names'].items() if v in tech_settings['cols']] + tech_data_out = tech_data.loc[tech_data.Parameter.isin(atb_params)].copy() + # check if empty + if tech_data_out.empty: + raise EmptyDataError("\n\n" + "Dataframe is empty after subsetting; check 'param_names' in settings.yaml." + f"Current values: {settings['param_names']}" + ) + # check columns and then pivot to wide format + check_columns(tech_data_out, tech_settings['indexcols'], 'indexcols', tech) + tech_data_out = tech_data_out.pivot(index=tech_settings['indexcols'], columns='Parameter', values='value') + tech_data_out = tech_data_out.rename_axis(None, axis=1) + # map parameters to new column names and format + tech_data_out = tech_data_out.rename(columns=settings['param_names']) + # convert numeric columns + tech_data_out = tech_data_out.apply(pd.to_numeric).reset_index() + + # if vom is missing in the input ATB data, assign zero + if tech in zero_vom_techs and 'vom' not in tech_data_out.columns: + print(f"Warning: 'Variable O&M' missing in ATB data for {tech}. Assigning vom = 0.0") + tech_data_out['vom'] = 0.0 + + # if processing continuous battery techs update energy cost data + if 'battery' in tech: + tech_data_out = format_continuous_battery(tech, settings, tech_data_out) + + # Apply technology transformations to the current scraped projections first. + # History is merged afterward so derived historic series are not overwritten. + if 'functions' in tech_settings: + for function_name in tech_settings['functions']: + selected_function = FUNCTION_MAPPING.get(function_name.lower()) + if selected_function: + print(f"Running {function_name}") + tech_data_out = selected_function(tech, settings, tech_data_out) + else: + raise NameError(f"'{function_name}' is not a supported function. " + "Define and add to FUNCTION_MAPPING.") + + # Combine the current projections with the stable local historical baseline. + tech_data_out = merge_historical_atb_data( + tech_data_out, tech, settings, dollaryear, deflator + ) + tech_data_unsmoothed = tech_data_out.copy() + + # This user-facing option is intentionally applied after history is merged, + # so it can bridge the history/current-ATB boundary without changing the + # versioned historical source files. It is disabled by default. + tech_data_out = smooth_cost_curve(tech, settings, tech_data_out) + + # for fuel cells, backfill capcost values as 9999 for all pre-2035 years + if tech == 'fuelcell' and 'capcost' in tech_data_out.columns: + print("Assigning capcost = 9999 for fuelcell years before 2035") + tech_data_out.loc[tech_data_out['t'] < 2035, 'capcost'] = 9999 + tech_data_unsmoothed.loc[ + tech_data_unsmoothed['t'] < 2035, 'capcost' + ] = 9999 + + smoothing = settings['config']['processing'].get('smooth_cost_curves', {}) + baseline_directory = settings.get('unsmoothed_output_dir') + if smoothing.get('enabled', False) and baseline_directory: + baseline_folder = ( + baseline_directory + if os.path.isabs(baseline_directory) + else os.path.join(ATBDIR, baseline_directory) + ) + write_technology_outputs( + tech_data_unsmoothed, + tech, + settings, + tech_settings, + sensitivity_name, + baseline_folder, + output_label="temporary pre-smoothing", + ) + + write_technology_outputs( + tech_data_out, + tech, + settings, + tech_settings, + sensitivity_name, + outfolder, + filenames=filenames, + ) + +def update_dollaryear(settings, filenames, dollaryear): + """ + function to create dollar year entries for new tech files + + Parameters + ---------- + settings: dict + Parsed settings['dollaryear'] value as the dollar year to assign to the new files + filenames: list[str] + Output filename produced by this run; strips the ".csv" suffix to derive Scenario names + dollaryear: pd.Series + Scenario -> Dollar.Year mapping (index=Scenario, name='Dollar.Year') + """ + # strip .csv from filenames + scennames = [f[:-4] for f in filenames] + # create new dollar year entries + newdollaryear = pd.Series(data=[settings['dollaryear']] * len(scennames), index=scennames) + # combine old and new and format for ReEDS + newdollaryear = pd.concat([dollaryear, newdollaryear]).sort_index() + # drop duplicates + newdollaryear = newdollaryear[~newdollaryear.index.duplicated(keep='first')] + newdollaryear.name = "Dollar.Year" + newdollaryear.index.name = "Scenario" + + return newdollaryear + +def get_financials(atb_data, settings, tech, financial_params, scenarios=["Moderate","*"]): + """ + function to get full financial parameters for a specific technology + + Parameters + ---------- + atb_data: pd.DataFrame + ATB flat file data + settings: dict + Parsed 'decimals' and 'reeds_start_year' values + tech: str + ATB technology name to filter to + financial_params: dict + Mapping of ATB parameter name -> target column name for pivoting + scenarios: list[str], optional + List of scenario names to include (default ["Moderate","*"]) + """ + techfinancial = atb_data.loc[(atb_data.Technology == tech) + & (atb_data.Scenario.isin(scenarios)) + & (atb_data.Parameter.isin(financial_params)) + ].copy() + if techfinancial.empty: + avail_params = atb_data.loc[(atb_data.Technology == tech) + & (atb_data.Scenario.isin(scenarios)), "Parameter" + ].unique() + if args.debug: + breakpoint() + else: + + raise EmptyDataError(f"Looking for the following financial parameters for {tech}:\n{list(financial_params.keys())}.\n" + f"Found the following parameters:\n{list(avail_params)}" + ) + techfinancial = techfinancial.rename(columns={"variable":"t"}) + # reorder columns + techfinancial = techfinancial.pivot(index="t", columns='Parameter', values='value').rename(columns=financial_params) + colorder = [v for k,v in financial_params.items()] + # convert to numeric and round + techfinancial = techfinancial[colorder].apply(pd.to_numeric).round(settings['decimals']) + # reindindex to include years before atb + atb_end_year = techfinancial.index.values.max() + techfinancial = techfinancial.reindex(index=list(range(settings['reeds_start_year'],atb_end_year+1))).bfill().reset_index() + + return techfinancial + +def update_financials(settings, atb_data, outfolder): + """ + function to write updated system and tech financial outputs + + Parameters + ---------- + settings: dict + Expects keys such as 'atbyear', 'reedspath', 'wacc_base_tech', 'decimals', and tech-specific configs under settings['techs']) + atb_data: pd.DataFrame + ATB flat file data used to extract financial parameters and WACC + outfolder: str + Output directory path + """ + print("Processing system financials.") + + financial_params = { + 'Interest Rate Nominal':'interest_rate_nom', + 'Rate of Return on Equity Nominal':'rroe_nom', + 'Debt Fraction':'debt_fraction', + 'Tax Rate (Federal and State)':'tax_rate' + } + + ## system financial file + # uses base tech specified in settings (typically natural gas) + sysfinancial = get_financials(atb_data, settings, settings['wacc_base_tech'], financial_params) + + ## tech financials + # get old tech financials file + print("Processing tech financials.") + prevyear = settings['atbyear']-1 + filename_old = f"financials_tech_ATB{prevyear}.csv" + financials_old = pd.read_csv(os.path.join(settings['reedspath'], 'inputs', 'financials', filename_old)) + + wacc = atb_data.loc[(atb_data['Parameter'] == "WACC Real") & (atb_data['Scenario'] == "Moderate"), ["Technology", "variable", "value"]] + wacc = wacc.rename(columns={"variable": "t", "value":"wacc_real"}) + wacc['wacc_real'] = pd.to_numeric(wacc['wacc_real']) + # use natural gas as baseline + wacc_baseline = wacc.loc[wacc.Technology == settings['wacc_base_tech']] + wacc_baseline = wacc_baseline.rename(columns={"wacc_real":"wacc_real_base"}).drop("Technology", axis=1) + + wacc = wacc.merge(wacc_baseline, on=['t']) + # get financing difference relative to base + wacc['finance_diff_real_update'] = wacc['wacc_real'] - wacc['wacc_real_base'] + + # get WACC values for techs that were processed + financials_update = [] + for tech in settings['techs']: + print(f"Loading financials for {tech}") + + # for batteries we need to calculate the WACC Real value + if tech == "battery": + # batteries are missing WACC, so need to get individual components and recalculate + # since we don't have debt fraction for batteries we take the value from PV for now + battery_params = financial_params.copy() + battery_params.pop('Debt Fraction') + battery_params.update({'Inflation Rate':'inflation_rate'}) + pv_param = {'Debt Fraction': financial_params['Debt Fraction']} + battfinancial = get_financials(atb_data, settings, settings['techs'][tech]['Technology'], battery_params) + pvfinancial = get_financials(atb_data, settings, settings['techs']['upv']['Technology'], pv_param) + # merge and calculate WACC in two steps: + # 1. WACC Nominal = Debt Fraction * Interest Rate Nominal * (1 - Tax Rate) + (1 - Debt Fraction) * Rate of Return + # 2. WACC Real = (1 + WACC Nominal) / (1 + Inflation Rate) - 1 + wacc_tech = battfinancial.merge(pvfinancial) + wacc_tech['wacc_nom'] = (wacc_tech['debt_fraction'] + * wacc_tech['interest_rate_nom'] + * (1 - wacc_tech['tax_rate']) + + (1 - wacc_tech['debt_fraction']) + * wacc_tech['rroe_nom'] + ) + wacc_tech['wacc_real'] = ( + (1 + wacc_tech['wacc_nom']) / (1 + wacc_tech['inflation_rate']) - 1 + ) + wacc_tech = wacc_tech.merge(wacc_baseline, on=['t']) + wacc_tech['finance_diff_real_update'] = wacc_tech['wacc_real'] - wacc_tech['wacc_real_base'] + + else: + wacc_tech = wacc.loc[wacc.Technology == settings['techs'][tech]['Technology']].copy() + + if wacc_tech.empty: + print(f"...no wacc found for {tech}, skipping.") + else: + # overwrite tech with new financial name if specified + if 'financialname' in settings['techs'][tech]: + wacc_tech['i'] = settings['techs'][tech]['financialname'] + wacc_tech = wacc_tech.drop_duplicates() + # otherwise use exist tech name + else: + wacc_tech = wacc_tech.rename(columns={'Technology':'i'}) + # add to list of new financials to update + financials_update.append(wacc_tech[['i', 't', 'finance_diff_real_update']]) + + financials_update = pd.concat(financials_update) + + # merge with old file and rewrite new year values upto the max available ATB year + max_atb_year = int(financials_update['t'].max()) + max_old_year = int(financials_old['t'].max()) + if max_atb_year > max_old_year: + new_years = list(range(max_old_year + 1, max_atb_year + 1)) + # get unique (i, country) combinations from old file + id_combos = financials_old[['i', 'country']].drop_duplicates() + # create new rows for each (i, country) × new year + new_rows = id_combos.merge(pd.DataFrame({'t': new_years}), how='cross') + # merge old file's non-year columns onto new rows (forward-fill from last known year) + last_year_vals = financials_old.loc[financials_old['t'] == max_old_year].drop('t', axis=1) + new_rows = new_rows.merge(last_year_vals, on=['i', 'country'], how='left') + financials_old = pd.concat([financials_old, new_rows], ignore_index=True) + financials_out = financials_old.merge(financials_update, on=['i', 't'], how='left') + # fill forward by country and tech to create values for pre-ATB years + financials_out['finance_diff_real_update'] = financials_out.groupby(['i','country'])['finance_diff_real_update'].bfill() + + # update relevant techs while leaving others in place + financials_out['finance_diff_real'] = np.where(financials_out['finance_diff_real_update'].isna(), + financials_out['finance_diff_real'], + financials_out['finance_diff_real_update'].round(settings['decimals']) + ) + + # write system and tech financial files + sysfile_new = f"financials_sys_ATB{settings['atbyear']}.csv" + sysfinancial.to_csv( + os.path.join(outfolder, sysfile_new), index=False, + lineterminator=CSV_LINE_TERMINATOR, + ) + + techfile_new = f"financials_tech_ATB{settings['atbyear']}.csv" + financials_out = financials_out[financials_old.columns] + financials_out.to_csv( + os.path.join(outfolder, techfile_new), index=False, + lineterminator=CSV_LINE_TERMINATOR, + ) + + return sysfile_new, techfile_new + +#%% =========================================================================== +### --- Main --- +### =========================================================================== +def main(args): + settings = load_processing_settings(args.config) + settings['unsmoothed_output_dir'] = getattr( + args, 'unsmoothed_output_dir', None + ) + processing = settings['config']['processing'] + outfolder = settings['output_dir'] + args.skip_costs = args.skip_costs or not processing.get('update_costs', True) + should_update_financials = processing.get('update_financials', True) + if args.sensitivity_name is None: + args.sensitivity_name = processing.get('sensitivity_name') + + smoothing = processing.get('smooth_cost_curves', {}) + if smoothing.get('enabled', False): + smoothing_techs = smoothing.get('technologies', []) + smooth_all_techs = smoothing_techs == 'all' or smoothing_techs == ['all'] + if not smooth_all_techs and not isinstance(smoothing_techs, list): + raise TypeError( + "processing.smooth_cost_curves.technologies must be 'all' " + "or a list." + ) + unknown_smoothing_techs = [] if smooth_all_techs else [ + tech for tech in smoothing_techs + if tech not in settings['techs'] + ] + if unknown_smoothing_techs: + raise ValueError( + "Unknown technologies in processing.smooth_cost_curves: " + f"{unknown_smoothing_techs}" + ) + baseline_directory = settings.get('unsmoothed_output_dir') + if baseline_directory: + baseline_folder = ( + baseline_directory + if os.path.isabs(baseline_directory) + else os.path.join(ATBDIR, baseline_directory) + ) + os.makedirs(baseline_folder, exist_ok=True) + year_marker = f"_ATB_{settings['atbyear']}_" + for filename in os.listdir(baseline_folder): + if year_marker in filename and filename.endswith('.csv'): + os.remove(os.path.join(baseline_folder, filename)) + + # if output folder does not exist, create it + os.makedirs(outfolder, exist_ok=True) + + # get list of techs to process + configured_techs = processing.get('technologies', 'all') + requested_techs = args.techs if args.techs is not None else configured_techs + if requested_techs == 'all' or requested_techs == ['all']: + techs_to_run = list(settings['techs'].keys()) + else: + techs_to_run = requested_techs if isinstance(requested_techs, list) else [requested_techs] + missing_techs = [t for t in techs_to_run if t not in settings['techs']] + if missing_techs: + raise ValueError(f"The following technologies are not in settings['techs']: {missing_techs}") + + # check if ReEDS repo is correctly specified + if not os.path.isdir(settings['reedspath']): + raise FileNotFoundError(f"Could not find '{settings['reedspath']}'; check config.yaml.") + + # load dollaryear file in ReEDS + dollaryear = pd.read_csv(os.path.join(settings['reedspath'], 'inputs', 'plant_characteristics', 'dollaryear.csv'), + index_col='Scenario').squeeze() + deflator = pd.read_csv(os.path.join(settings['reedspath'], 'inputs', 'financials', 'deflator.csv'), + index_col='*Dollar.Year').squeeze() + # load ATB flat file + atb_data = load_atb_flat_file(settings, args, techs_to_run) + + ## process technology files + filenames = [] + if args.skip_costs: + print("Skipping cost files.") + else: + print("---------------------") + for tech in techs_to_run: + process_tech_file(atb_data, tech, settings, filenames, + dollaryear, deflator, args.sensitivity_name, outfolder, args) + print("---------------------") + # update dollaryear file + newdollaryear = update_dollaryear(settings, filenames, dollaryear) + + ## update financials file + if should_update_financials: + sysfinancialfile, techfinancialfile = update_financials(settings, atb_data, outfolder) + else: + print("Skipping financials (processing.update_financials is false).") + + ## copy new files to ReEDS + if settings['copy_to_reeds']: + + if not args.skip_costs: + # copy tech files + for f in filenames: + shutil.copy(os.path.join(outfolder,f), os.path.join(settings['reedspath'],'inputs','plant_characteristics',f)) + # update dollaryear file for tech files + newdollaryear.to_csv( + os.path.join(settings['reedspath'],'inputs','plant_characteristics','dollaryear.csv'), + index=True, lineterminator=CSV_LINE_TERMINATOR, + ) + + # copy financial files + if should_update_financials: + shutil.copy(os.path.join(outfolder,sysfinancialfile), os.path.join(settings['reedspath'],'inputs','financials',sysfinancialfile)) + shutil.copy(os.path.join(outfolder,techfinancialfile), os.path.join(settings['reedspath'],'inputs','financials',techfinancialfile)) + + +if __name__ == "__main__": + print("Processing ATB files") + parser = argparse.ArgumentParser(description="Generate ATB files.") + parser.add_argument('--config', help='path to config.yaml (default: ../config.yaml)') + parser.add_argument('--techs', '-t', nargs='+', default=None, + help='one or more techs; defaults to processing.technologies in config.yaml') + parser.add_argument('--sensitivity_name', '-s', type=str, + help='suffix to append to file name for sensitivities') + parser.add_argument('--skip_costs', '-c', action="store_true", + help='skip updating cost files for this run') + parser.add_argument('--debug', '-d', action="store_true", + help='option to run in debug mode') + parser.add_argument( + '--unsmoothed-output-dir', + help=( + 'temporary directory for pre-smoothing outputs used by the ' + 'comparison stage' + ), + ) + args = parser.parse_args() + + # list of supported custom functions to call from settings.yml + FUNCTION_MAPPING = { + 'normalize_cf': normalize_cf, + 'apply_offshore_cost_multipliers': apply_offshore_cost_multipliers, + 'apply_coal_projection_overrides': apply_coal_projection_overrides, + 'smooth_hist_cf': smooth_hist_cf, + 'add_csp_techs': add_csp_techs, + 'add_beccs_techs': add_beccs_techs, + } + main(args) diff --git a/atb/scripts/run_pipeline.py b/atb/scripts/run_pipeline.py new file mode 100644 index 0000000..c12fffb --- /dev/null +++ b/atb/scripts/run_pipeline.py @@ -0,0 +1,98 @@ +"""Run the config-selected NLR ATB workflow stages in order.""" + +import argparse +from contextlib import nullcontext +import subprocess +import sys +import tempfile +from pathlib import Path + +from atb_config import DEFAULT_CONFIG_PATH, load_config + + +SCRIPT_DIR = Path(__file__).resolve().parent +STAGES = { + "scrape": ("scrape_raw_data", "scrape_atb_inputs.py"), + "format": ("format_reeds_inputs", "generate_atb_files.py"), + "plot": ("make_plots", "atb_plotting.py"), + "compare": ( + "make_comparison_plots", + "../comparison/compare_atb_outputs.py", + ), +} + + +def run_stage( + name, + script, + config_path, + force_download=False, + unsmoothed_dir=None, +): + command = [sys.executable, str(SCRIPT_DIR / script), "--config", str(config_path)] + if name == "scrape" and force_download: + command.append("--force") + if unsmoothed_dir is not None and name == "format": + command.extend(["--unsmoothed-output-dir", str(unsmoothed_dir)]) + if unsmoothed_dir is not None and name == "compare": + command.extend(["--unsmoothed-dir", str(unsmoothed_dir)]) + print(f"\n=== {name.upper()} ===", flush=True) + subprocess.run(command, check=True) + + +def main(): + parser = argparse.ArgumentParser( + description="Run scrape, ReEDS formatting, and plotting from config.yaml." + ) + parser.add_argument("--config", help="Path to config.yaml (default: ../config.yaml).") + parser.add_argument( + "--only", nargs="+", choices=list(STAGES), + help="Run only the selected stage(s), ignoring workflow switches.", + ) + parser.add_argument( + "--force-download", action="store_true", + help="Replace existing raw files during the scrape stage.", + ) + args = parser.parse_args() + + config_path = Path(args.config).resolve() if args.config else DEFAULT_CONFIG_PATH + config = load_config(config_path) + selected = set(args.only) if args.only else None + + plan = [] + for stage, (switch, script) in STAGES.items(): + enabled = stage in selected if selected is not None else config["workflow"][switch] + plan.append((stage, script, enabled)) + + print(f"Config: {config_path}") + print(f"ATB year: {config['atb']['year']}") + print("Workflow:") + for stage, _, enabled in plan: + print(f" {'RUN ' if enabled else 'SKIP'} {stage}") + + enabled_stages = {stage for stage, _, enabled in plan if enabled} + needs_unsmoothed_data = ( + config['processing'].get('smooth_cost_curves', {}).get('enabled', False) + and {'format', 'compare'}.issubset(enabled_stages) + ) + temporary_output = ( + tempfile.TemporaryDirectory(prefix="atb_unsmoothed_") + if needs_unsmoothed_data + else nullcontext(None) + ) + with temporary_output as unsmoothed_dir: + for stage, script, enabled in plan: + if enabled: + run_stage( + stage, + script, + config_path, + args.force_download, + unsmoothed_dir, + ) + + print("\nPipeline complete.") + + +if __name__ == "__main__": + main() diff --git a/atb/scripts/scrape_atb_inputs.py b/atb/scripts/scrape_atb_inputs.py new file mode 100644 index 0000000..c4334aa --- /dev/null +++ b/atb/scripts/scrape_atb_inputs.py @@ -0,0 +1,134 @@ +"""Download and summarize the raw NLR ATB inputs configured in config.yaml.""" + +import argparse +from pathlib import Path + +import openpyxl +import pandas as pd +import requests + +from atb_config import load_config, raw_file_path + + +def _open_download(url, allow_insecure_ssl_fallback): + """Open a verified download, optionally retrying after a TLS inspection error.""" + request_options = {"stream": True, "timeout": 300} + try: + return requests.get(url, **request_options) + except requests.exceptions.SSLError as error: + if not allow_insecure_ssl_fallback: + raise RuntimeError( + "TLS certificate verification failed. Install the required CA " + "certificate in this environment or set " + "raw_data.allow_insecure_ssl_fallback: true in config.yaml." + ) from error + + import urllib3 + + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + print("WARNING: TLS certificate verification failed.") + print(" Retrying this public raw-data download with verify=False.") + return requests.get(url, verify=False, **request_options) + + +def download_file( + url, destination, force=False, allow_insecure_ssl_fallback=False +): + """Download one file atomically, or reuse the existing local copy.""" + destination = Path(destination) + destination.parent.mkdir(parents=True, exist_ok=True) + if destination.exists() and not force: + print(f"Using existing raw file: {destination}") + return destination + + temporary = destination.with_suffix(destination.suffix + ".part") + print(f"Downloading {url}") + print(f" into {destination}") + try: + with _open_download(url, allow_insecure_ssl_fallback) as response: + response.raise_for_status() + with temporary.open("wb") as stream: + for chunk in response.iter_content(chunk_size=1024 * 1024): + if chunk: + stream.write(chunk) + except Exception: + temporary.unlink(missing_ok=True) + raise + temporary.replace(destination) + return destination + + +def summarize_flat_file(path): + """Print a compact, user-visible overview of the downloaded ATBe CSV.""" + path = Path(path) + preview = pd.read_csv(path, nrows=5, low_memory=False) + header = list(preview.columns) + technology_col = "technology" if "technology" in header else "Technology" + year_col = "atb_year" if "atb_year" in header else None + selected = [technology_col] + ([year_col] if year_col else []) + summary = pd.read_csv(path, usecols=selected, low_memory=False) + + print("\nRaw flat file") + print(f" path: {path}") + print(f" size: {path.stat().st_size / 1024**2:,.1f} MiB") + print(f" rows: {len(summary):,}") + if year_col: + years = sorted(summary[year_col].dropna().unique().tolist()) + print(f" ATB years: {years}") + technologies = sorted(summary[technology_col].dropna().astype(str).unique()) + print(f" technologies ({len(technologies)}): {', '.join(technologies)}") + print(" first five rows:") + print(preview.to_string(index=False, max_cols=8)) + + +def summarize_workbook(path): + """Print the path, size, and sheet names for the raw ATB workbook.""" + path = Path(path) + workbook = openpyxl.load_workbook(path, read_only=True, data_only=True) + print("\nRaw workbook") + print(f" path: {path}") + print(f" size: {path.stat().st_size / 1024**2:,.1f} MiB") + print(f" sheets ({len(workbook.sheetnames)}): {', '.join(workbook.sheetnames)}") + workbook.close() + + +def scrape(config, selected="all", force=False): + """Download configured raw inputs and show what is now available.""" + raw = config["raw_data"] + allow_insecure = raw.get("allow_insecure_ssl_fallback", False) + if selected in ("all", "flat"): + flat = download_file( + raw["flat_file"]["url"], + raw_file_path(config, "flat_file"), + force=force, + allow_insecure_ssl_fallback=allow_insecure, + ) + summarize_flat_file(flat) + if selected in ("all", "workbook"): + workbook = download_file( + raw["workbook"]["url"], + raw_file_path(config, "workbook"), + force=force, + allow_insecure_ssl_fallback=allow_insecure, + ) + summarize_workbook(workbook) + + +def main(): + parser = argparse.ArgumentParser( + description="Download and summarize the raw NLR ATB flat file and workbook." + ) + parser.add_argument("--config", help="Path to config.yaml (default: ../config.yaml).") + parser.add_argument( + "--only", choices=["all", "flat", "workbook"], default="all", + help="Download only one raw input (default: all).", + ) + parser.add_argument( + "--force", action="store_true", help="Replace raw files that already exist." + ) + args = parser.parse_args() + scrape(load_config(args.config), selected=args.only, force=args.force) + + +if __name__ == "__main__": + main() diff --git a/atb/scripts/settings.yaml b/atb/scripts/settings.yaml new file mode 100644 index 0000000..ec46f4e --- /dev/null +++ b/atb/scripts/settings.yaml @@ -0,0 +1,435 @@ +# Internal formatting rules for converting raw ATB data into ReEDS inputs. +# Edit ../config.yaml for workflow, year, paths, downloads, and output choices. + +# paired entries mapping ATB parameter names into the names used in ReEDS +param_names: + OCC: capcost + Fixed O&M: fom + Variable O&M: vom + CF: cf_improvement + Heat Rate: heatrate + Round-Trip Efficiency: rte +# units for ATB parameters; these are carried over to ReEDS +units: + capcost: $/kW + fom: $/kW/yr + vom: $/MWh + heatrate: MMBtu/MWh +# specifies any columns related to cost (used to determine columns to inflate from historic data) +cost_cols: + - capcost + - fom + - vom +# the ATB technology used as the base tech for computing differences in the weighted average cost of capital +wacc_base_tech: "NaturalGas_FE" + +## Technology format ## + +## see comments below for details on entries +## note the indent when adding new techs + +techs: + # name of the technology in ReEDS; used in the filename and to rename the tech column unless a mapping for 'DisplayName' is present + upv: + # technology group in the ATB data that corresponds to this ReEDS tech + Technology: UtilityPV + # technology sub-class in the ATB data that corresponds to this ReEDS tech; can also be multiple paired entries + DisplayName: Utility PV - Class 4 + # columns in the ATB file to rename; each pairing should be "ATB col name: ReEDS col name" + subset_rows: + Case: Market + CRPYears: 30 + renamecols: + variable: t + # columns to use as index when pivoting the data + indexcols: + - Scenario + - t + # list of columns (in order) to output in the final data for ReEDS + cols: + - t + - capcost + - fom + - vom + - cf_improvement + # option to normalize capacity factor data to a reference point + # specify year (t) and scenario for normalization + cfbase: + t: 2035 + Scenario: Moderate + # name of technology group used in ReEDS for financial parameters + financialname: PV + # any custom functions used to process the data + functions: + - normalize_cf + + wind-ons: + Technology: LandbasedWind + DisplayName: Land-Based Wind - Class 4 - Technology 1 + reeds_name: ons-wind + subset_rows: + Case: Market + CRPYears: 30 + renamecols: + variable: t + "Overnight Cap Cost $/kW": capcost + "Fixed O&M $/(kW-yr)": fom + "Var O&M $/MWh": vom + "Turbine": turbine + "Year": t + "CF_mult": cf_improvement + # adds a column with data to the ReEDS output; paired entry with "col name: value" + addcols: + turbine: 115hh_170rd + indexcols: + - Scenario + - t + - turbine + cols: + - turbine + - t + - capcost + - fom + - vom + - cf_improvement + # ReEDS assigns the onshore wind columns by position rather than by name + # (plantcostprep.py), so the written file must keep this exact order and + # these exact headers. Maps internal column name -> ReEDS header. + output_cols: + turbine: Turbine + t: Year + cf_improvement: CF_mult + capcost: Overnight Cap Cost $/kW + fom: Fixed O&M $/(kW-yr) + vom: Var O&M $/MWh + cfbase: + t: 2035 + Scenario: Moderate + financialname: ONSWIND + decimals: 5 + functions: + - normalize_cf + + wind-ofs: + Technology: OffShoreWind + DisplayName: + Offshore Wind - Class 1: fixed + Offshore Wind - Class 8: floating + reeds_name: ofs-wind + subset_rows: + Case: Market + CRPYears: 30 + addcols: + rsc_mult: 1 + renamecols: + DisplayName: turbine + variable: t + "Overnight Cap Cost $/kW": capcost + "Fixed O&M $/(kW-yr)": fom + "Var O&M $/MWh": vom + "Turbine": turbine + "Wind class": turbine + "Year": t + "CF_mult": cf_improvement + indexcols: + - Scenario + - t + - turbine + - rsc_mult + cols: + - turbine + - t + - capcost + - fom + - vom + - cf_improvement + - rsc_mult + # ReEDS detects the ATB 2024 offshore format by the presence of a 'Turbine' + # column and then assigns the remaining columns by position + # (plantcostprep.py), so both the headers and this order are required. + output_cols: + turbine: Turbine + t: Year + cf_improvement: CF_mult + capcost: Overnight Cap Cost $/kW + fom: Fixed O&M $/(kW-yr) + vom: Var O&M $/MWh + rsc_mult: rsc_mult + cfbase: + t: 2035 + Scenario: Moderate + # preserves additional columns when normalizing capacity factors + keepcols: + - turbine + financialname: OFSWIND + decimals: 5 + functions: + - apply_offshore_cost_multipliers + - normalize_cf + + battery: + Technology: Utility-Scale Battery Storage + DisplayName: + Utility-Scale Battery Storage - 2Hr: battery_li + # note: 2-hour battery is a placeholder for calculating the power and energy costs + # see the 'format_continuous_battery' function + subset_rows: + Case: Market + CRPYears: 30 + renamecols: + DisplayName: i + variable: t + indexcols: + - Scenario + - t + - i + cols: + - i + - t + - capcost + - capcost_energy + - fom + - fom_energy + - vom + - rte + financialname: BATTERY + + csp: + Technology: CSP + DisplayName: + # mapping to csp2 since that corresponds to ATB storage + solar multiple assumption + # 'add_csp_techs' expands this base series to the other CSP configurations + # using the year-specific ratios in manual_input/csp_cost_ratios_.csv. + CSP - Class 2: csp2 + subset_rows: + Case: Market + CRPYears: 30 + renamecols: + variable: t + DisplayName: type + indexcols: + - Scenario + - t + - type + cols: + - type + - t + - capcost + - fom + - vom + financialname: CSP + functions: + - add_csp_techs + + coal: + Technology: Coal_FE + DisplayName: + Coal-new: Coal-new + Coal-IGCC: Coal-IGCC + subset_rows: + Case: Market + CRPYears: 30 + Scenario: Moderate + renamecols: + DisplayName: i + variable: t + indexcols: + - Scenario + - i + - t + cols: + - i + - t + - capcost + - fom + - vom + - heatrate + functions: + - apply_coal_projection_overrides + + coal-ccs: + Technology: Coal_FE + DisplayName: + Coal-95%-CCS: coal-CCS_mod + subset_rows: + Case: Market + CRPYears: 30 + renamecols: + DisplayName: i + variable: t + indexcols: + - Scenario + - i + - t + cols: + - i + - t + - capcost + - fom + - vom + - heatrate + functions: + - apply_coal_projection_overrides + + gas: + Technology: NaturalGas_FE + DisplayName: + NG 2-on-1 Combined Cycle (F-Frame): Gas-CC + NG Combustion Turbine (F-Frame): Gas-CT + NG 1-on-1 Combined Cycle (H-Frame): Gas-CC_H_1x1 + NG 2-on-1 Combined Cycle (H-Frame): Gas-CC_H_2x1 + subset_rows: + Case: Market + CRPYears: 30 + Scenario: Moderate + renamecols: + DisplayName: i + variable: t + indexcols: + - Scenario + - i + - t + cols: + - i + - t + - capcost + - fom + - vom + - heatrate + + gas-ccs: + Technology: NaturalGas_FE + DisplayName: + NG 2-on-1 Combined Cycle (F-Frame) 95% CCS: Gas-CC-CCS_mod + NG 1-on-1 Combined Cycle (H-Frame) 95% CCS: Gas-CC_H_1x1-CCS_mod + NG 2-on-1 Combined Cycle (H-Frame) 95% CCS: Gas-CC_H_2x1-CCS_mod + subset_rows: + Case: Market + CRPYears: 30 + renamecols: + DisplayName: i + variable: t + indexcols: + - Scenario + - i + - t + cols: + - i + - t + - capcost + - fom + - vom + - heatrate + + fuelcell: + Technology: NaturalGas_FE + DisplayName: + NG Fuel Cell: ng-fuel-cell + subset_rows: + Case: Market + CRPYears: 30 + renamecols: + DisplayName: i + variable: t + indexcols: + - Scenario + - i + - t + cols: + - i + - t + - capcost + - fom + - vom + - heatrate + + nuclear: + Technology: Nuclear + DisplayName: + Nuclear - Large: Nuclear + subset_rows: + Case: Market + CRPYears: 30 + renamecols: + DisplayName: i + variable: t + indexcols: + - Scenario + - i + - t + cols: + - i + - t + - capcost + - fom + - vom + - heatrate + + nuclear-smr: + Technology: Nuclear + DisplayName: + Nuclear - Small: Nuclear-SMR + subset_rows: + Case: Market + CRPYears: 30 + renamecols: + DisplayName: i + variable: t + indexcols: + - Scenario + - i + - t + cols: + - i + - t + - capcost + - fom + - vom + - heatrate + + biopower: + Technology: Biopower + DisplayName: + Biopower - Dedicated: biopower + subset_rows: + Case: Market + CRPYears: 30 + renamecols: + DisplayName: i + variable: t + indexcols: + - Scenario + - i + - t + cols: + - i + - t + - capcost + - fom + - vom + - heatrate + + # NOTE: beccs is not available in ATB 2024 (only 'Biopower - Dedicated' exists, + # and there is no 'R&D + TC' case). Disabled for 2024 runs; re-enable for ATB 2025. + # beccs: + # Technology: Biopower + # DisplayName: + # Biopower - 95% CCS: beccs_mod + # subset_rows: + # Case: R&D + TC + # CRPYears: 30 + # renamecols: + # DisplayName: i + # variable: t + # indexcols: + # - Scenario + # - i + # - t + # cols: + # - i + # - t + # - capcost + # - fom + # - vom + # - heatrate + # filename_historical: "beccs_reference.csv" diff --git a/csp/CSP-supplycurve-profiles.ipynb b/csp/CSP-supplycurve-profiles.ipynb index 6698669..67ab379 100644 --- a/csp/CSP-supplycurve-profiles.ipynb +++ b/csp/CSP-supplycurve-profiles.ipynb @@ -5,12 +5,12 @@ "metadata": {}, "source": [ "This notebook is meant to be run on the HPC via Europa. Instructions are here:\n", - "* https://github.com/NREL/HPC/tree/master/general/Jupyterhub/jupyter" + "* https://github.com/NatLabRockies/HPC/tree/master/general/Jupyterhub/jupyter" ] }, { "cell_type": "code", - "execution_count": 50, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -27,8 +27,8 @@ "import h5py\n", "\n", "# ### ReEDS: SA_NTPS branch\n", - "# reedspath = os.path.expanduser('~/github/ReEDS-2.0/')\n", - "# reedspath2 = os.path.expanduser('~/github2/ReEDS-2.0/')\n", + "# reedspath = os.path.expanduser('~/github/ReEDS/')\n", + "# reedspath2 = os.path.expanduser('~/github2/ReEDS/')\n", "# remotepath = '/Volumes/ReEDS/'\n", "# projpath = os.path.expanduser('~/Projects/SpatialTemporal/')\n", "\n", @@ -53,22 +53,9 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "cf_mean\n", - "cf_profile\n", - "dni_mean\n", - "lcoe_real\n", - "meta\n", - "time_index\n" - ] - } - ], + "outputs": [], "source": [ "inpath = '/shared-projects/rev/projects/csp_conus/csp/'\n", "year = 2012\n", @@ -94,7 +81,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -239,7 +226,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -253,7 +240,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -265,83 +252,29 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "sc_point_gid\n", - "5372 1\n", - "5373 1\n", - "5750 1\n", - "5751 2\n", - "5752 3\n", - " ..\n", - "97459 3\n", - "97460 2\n", - "97461 3\n", - "97462 4\n", - "97463 2\n", - "Name: profile_index, Length: 24722, dtype: int64" - ] - }, - "execution_count": 54, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "dfsc.groupby('sc_point_gid').profile_index.count()" ] }, { "cell_type": "code", - "execution_count": 55, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "sc_point_gid\n", - "5372 1\n", - "64386 1\n", - "64385 1\n", - "64384 1\n", - "64383 1\n", - " ..\n", - "42608 1\n", - "42606 1\n", - "42605 1\n", - "42367 1\n", - "97463 1\n", - "Name: profile_index, Length: 24722, dtype: int64" - ] - }, - "execution_count": 55, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "dfsc.groupby('sc_point_gid').profile_index.unique().map(lambda x: len(x)).sort_values()" ] }, { "cell_type": "code", - "execution_count": 56, + "execution_count": null, "metadata": { "scrolled": true }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "24722\n" - ] - } - ], + "outputs": [], "source": [ "sc_point_gids = sorted(dfsc.sc_point_gid.unique())\n", "print(len(sc_point_gids))" @@ -349,31 +282,9 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "DatetimeIndex(['2007-01-01 00:00:00-05:00', '2007-01-01 01:00:00-05:00',\n", - " '2007-01-01 02:00:00-05:00', '2007-01-01 03:00:00-05:00',\n", - " '2007-01-01 04:00:00-05:00', '2007-01-01 05:00:00-05:00',\n", - " '2007-01-01 06:00:00-05:00', '2007-01-01 07:00:00-05:00',\n", - " '2007-01-01 08:00:00-05:00', '2007-01-01 09:00:00-05:00',\n", - " ...\n", - " '2013-12-31 14:00:00-05:00', '2013-12-31 15:00:00-05:00',\n", - " '2013-12-31 16:00:00-05:00', '2013-12-31 17:00:00-05:00',\n", - " '2013-12-31 18:00:00-05:00', '2013-12-31 19:00:00-05:00',\n", - " '2013-12-31 20:00:00-05:00', '2013-12-31 21:00:00-05:00',\n", - " '2013-12-31 22:00:00-05:00', '2013-12-31 23:00:00-05:00'],\n", - " dtype='datetime64[ns, EST]', length=61320, freq=None)" - ] - }, - "execution_count": 57, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "timeindex = {\n", " y: pd.date_range(f'{y}-01-01', f'{y+1}-01-01', closed='left', freq='H', tz='EST')[:8760]\n", @@ -386,69 +297,25 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "DatetimeIndex(['2012-01-01 00:00:00-05:00', '2012-01-01 01:00:00-05:00',\n", - " '2012-01-01 02:00:00-05:00', '2012-01-01 03:00:00-05:00',\n", - " '2012-01-01 04:00:00-05:00', '2012-01-01 05:00:00-05:00',\n", - " '2012-01-01 06:00:00-05:00', '2012-01-01 07:00:00-05:00',\n", - " '2012-01-01 08:00:00-05:00', '2012-01-01 09:00:00-05:00',\n", - " ...\n", - " '2012-12-30 14:00:00-05:00', '2012-12-30 15:00:00-05:00',\n", - " '2012-12-30 16:00:00-05:00', '2012-12-30 17:00:00-05:00',\n", - " '2012-12-30 18:00:00-05:00', '2012-12-30 19:00:00-05:00',\n", - " '2012-12-30 20:00:00-05:00', '2012-12-30 21:00:00-05:00',\n", - " '2012-12-30 22:00:00-05:00', '2012-12-30 23:00:00-05:00'],\n", - " dtype='datetime64[ns, EST]', length=8760, freq='H')" - ] - }, - "execution_count": 58, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "timeindex[2012]" ] }, { "cell_type": "code", - "execution_count": 59, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "DatetimeIndex(['2012-01-01 00:00:00-05:00', '2012-01-01 01:00:00-05:00',\n", - " '2012-01-01 02:00:00-05:00', '2012-01-01 03:00:00-05:00',\n", - " '2012-01-01 04:00:00-05:00', '2012-01-01 05:00:00-05:00',\n", - " '2012-01-01 06:00:00-05:00', '2012-01-01 07:00:00-05:00',\n", - " '2012-01-01 08:00:00-05:00', '2012-01-01 09:00:00-05:00',\n", - " ...\n", - " '2012-12-30 14:00:00-05:00', '2012-12-30 15:00:00-05:00',\n", - " '2012-12-30 16:00:00-05:00', '2012-12-30 17:00:00-05:00',\n", - " '2012-12-30 18:00:00-05:00', '2012-12-30 19:00:00-05:00',\n", - " '2012-12-30 20:00:00-05:00', '2012-12-30 21:00:00-05:00',\n", - " '2012-12-30 22:00:00-05:00', '2012-12-30 23:00:00-05:00'],\n", - " dtype='datetime64[ns, EST]', length=8760, freq='H')" - ] - }, - "execution_count": 59, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "timeindex[2012].tz_convert('EST')" ] }, { "cell_type": "code", - "execution_count": 60, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -457,17 +324,9 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|██████████| 24722/24722 [2:49:59<00:00, 2.42it/s] \n" - ] - } - ], + "outputs": [], "source": [ "cfout = {}\n", "# for sc_point_gid in [97462]:\n", @@ -512,7 +371,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -522,7 +381,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -533,30 +392,9 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 38, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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3XfD4Hk/kb7ed9Pvfvc7IzMz02htbTUMg+f9ZJy674Zhz/OelpWfI5kltcGDB1vvJRIJzZ8/X9/97nd15plnHvU9lZWVam1t1ezZs633gsGgJk6cqHXr1kmS1q9fr0AgoKlTp1rXXHDBBQoEAtY16cyp+2568MEHNWTIEJ1zzjn68Y9/rFgs5tAn6V96c9+7g+f9xJy67yae92Oz675HIhEVFBTI52s/D5zn/cScuu+mvj7vA/ZU9+4yDEN33HGHLr74Yk2cONF6/8EHH5TP59Ptt99+zO+rra1VZmamioqKurxfUlKi2tpa65ri4uKjvre4uNi6Jl05ed8l6dvf/ramTJmioqIivfXWW1q0aJF27dqlX/3qV858oH6it/e9O3jej8/J+y7xvB+PXff9wIED+pd/+RctWLDAeo/n/ficvO+SPc972oef2267Te+++67Wrl1rvVdZWamf/exn2rRpkzweT49+nmEYXb7nWN9/5DXpyOn7/p3vfMf6/2eddZaKior05S9/2fqvhXRl930/Es/7sTl933nej82O+15fX6+rr75aZ5xxhu67774uX+N5Pzan77stz3ufJs36udtuu80YMWKEsXPnzi7vP/LII4bH4zG8Xq/1kmRkZGQYo0aNMgzDMF555RVDknHw4MEu33vWWWcZP/zhDw3DMIxnnnnGCAQCR/25gUDA+Ld/+zdHPlN/4PR9P5bq6uqj5ufTTV/u++GOt/aE5/3YnL7vx8Lzbs99r6+vNy688EJj5syZRjQa7fI1nvdjc/q+H0tvnve0DD+JRMK49dZbjWAwaHz44YdHfX3//v3G1q1bu7yCwaDxve99z1rIZi68feGFF6zv27dv3zEXPL/55pvWNRs2bEjbBXHJuu/H8qc//cmQZOzevdv+D5bi7LjvhzvZgmee93bJuu/HwvPe9/seiUSMCy64wJg+fbrR2Nh41M/hee8qWff9WHrzvKdl+PnHf/xHIxAIGP/7v/9r1NTUWK+mpqbjfs+xVpfffPPNxogRI4yXX37Z2LRpk3HppZcaZ599ttHW1mZdc8UVVxhnnXWWsX79emP9+vXGpEmTjDlz5jj10VJasu77unXrjKVLlxqbN282du7cabzwwgtGMBg05s6d6+THS1l23feamhpj8+bNxtNPP21IMl5//XVj8+bNxoEDB6xreN47Jeu+87x3Zcd9r6+vN6ZOnWpMmjTJ+Pjjj7v8HH6/H1uy7rtdz3tahh9Jx3w9++yzx/2eY/1Sikajxm233WYMHjzYyMnJMebMmWPs2bOnyzUHDhwwbrjhBiM/P9/Iz883brjhhm79l9tAlKz7XllZaUydOtUIBAJGdna2cdpppxn33Xdft/8rYqCx677fd999J/05PO+dknXfed67suO+m1W2Y7127dplXcfz3ilZ992u593TMWgAAIC0QJ8fAACQVgg/AAAgrRB+AABAWiH8AACAtEL4AQAAaYXwAwAA0grhBwAApBXCDwAASCuEHwAAkFYIPwAAIK0QfgAAQFr5/wGP9haAPt9TmgAAAABJRU5ErkJggg==\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "foo = cfin[year].copy()\n", "foo.loc[:,:] = np.roll(foo, -5, axis=0)\n", diff --git a/csp/csp_profiles.py b/csp/csp_profiles.py index 2973667..8cb7b55 100644 --- a/csp/csp_profiles.py +++ b/csp/csp_profiles.py @@ -12,8 +12,8 @@ import shapely os.environ['PROJ_NETWORK'] = 'OFF' -reedspath = os.path.expanduser('~/github/ReEDS-2.0/') -reedspath2 = os.path.expanduser('~/github2/ReEDS-2.0/') +reedspath = os.path.expanduser('~/github/ReEDS/') +reedspath2 = os.path.expanduser('~/github2/ReEDS/') remotepath = '/Volumes/ReEDS' scpath = os.path.join(remotepath,'Supply_Curve_Data') repopath = os.path.dirname(os.path.dirname(__file__)) diff --git a/demand_response/residential_shed/supply_curves_and_multipliers.py b/demand_response/residential_shed/supply_curves_and_multipliers.py index 0a78c29..ed61ef7 100644 --- a/demand_response/residential_shed/supply_curves_and_multipliers.py +++ b/demand_response/residential_shed/supply_curves_and_multipliers.py @@ -15,7 +15,7 @@ from dsgrid2reeds import read_file #%% -reedsdir = os.path.join("C:\\Users\\LSERPE\\Documents\\Repo\\main\\ReEDS-2.0") +reedsdir = os.path.join("C:\\Users\\LSERPE\\Documents\\Repo\\main\\ReEDS") inputs_dir = os.path.join(reedsdir,'inputs') hierarchy = pd.read_csv(os.path.join(inputs_dir,'hierarchy.csv')) diff --git a/emissions/README.md b/emissions/README.md index 5f8f6a9..758bb18 100644 --- a/emissions/README.md +++ b/emissions/README.md @@ -2,6 +2,6 @@ The R script in this folder (egrid.R) is used to compute the share of 2022 emissions by county. These shares are used to downscale the emissions constraint when running sub-nationally. -The script takes in 2022 eGrid data and matches plants to counties using their lat/long and the county shapefile (located in the ReEDS-2.0 repository). +The script takes in 2022 eGrid data and matches plants to counties using their lat/long and the county shapefile (located in the ReEDS repository). -Output from this script is used in ReEDS-2.0 repo under `'inputs','emission_constraints','county_co2_share_egrid_2022.csv'` +Output from this script is used in ReEDS repo under `'inputs','emission_constraints','county_co2_share_egrid_2022.csv'` diff --git a/emissions/egrid.R b/emissions/egrid.R index 526c4a0..dc06576 100644 --- a/emissions/egrid.R +++ b/emissions/egrid.R @@ -8,10 +8,10 @@ library(dplyr) ## paths # path to this script -setwd("~/Documents/github/ReEDS-2.0_Input_Processing/Emissions") +setwd("~/Documents/github/ReEDS_Input_Processing/Emissions") -# set this path to your ReEDS-2.0 repo -reedspath <- "/Users/bsergi/Documents/github/ReEDS-2.0" +# set this path to your ReEDS repo +reedspath <- "/Users/bsergi/Documents/github/ReEDS" ## load data ### diff --git a/existing_capacity_payments/cappayment_calc.R b/existing_capacity_payments/cappayment_calc.R index 995457f..af6e0b0 100644 --- a/existing_capacity_payments/cappayment_calc.R +++ b/existing_capacity_payments/cappayment_calc.R @@ -16,7 +16,7 @@ igdx("c:\\gams\\win64\\24.7") #WriteHintage.R Args=list() #WriteHintage.R -# localrepo <- "D:/ReEDS_YSun/ReEDS-2.0/" +# localrepo <- "D:/ReEDS_YSun/ReEDS/" setwd(localrepo) genunitfile='inputs\\capacitydata\\ReEDS_generator_database_final_EIA-NEMS.csv' retscen = "NukeRefRetireYear" diff --git a/exogenous_canadian_trade/Canadian_import_export_formatter.py b/exogenous_canadian_trade/Canadian_import_export_formatter.py index 8b494f8..7e5454d 100644 --- a/exogenous_canadian_trade/Canadian_import_export_formatter.py +++ b/exogenous_canadian_trade/Canadian_import_export_formatter.py @@ -151,4 +151,4 @@ Imports.round(1).to_csv(os.path.join(Output_folder, 'can_imports.csv')) -print(f"Run complete. See outputs folder for outputs which are inputs to ReEDS-2.0.") \ No newline at end of file +print(f"Run complete. See outputs folder for outputs which are inputs to ReEDS.") \ No newline at end of file diff --git a/exogenous_canadian_trade/README.md b/exogenous_canadian_trade/README.md index abcbe4a..54e264b 100644 --- a/exogenous_canadian_trade/README.md +++ b/exogenous_canadian_trade/README.md @@ -2,7 +2,7 @@ These updates were done 05/31/2024 by Wesley Cole and Max Vanatta and 02/28/2025 The GSw_Canada switch for cases in ReEDS allow for: 0: No Canadian trade -1: Seasonal balancing of Canadian electricity trade which uses the annual balances (can_exports.csv and can_imports.csv in outputs folder, which contains inputs to ReEDS-2.0) and seasonal fractions (can_exports_szn_frac.csv and can_imports_szn_frac.csv in outputs folder). Operated simnilar to Hydro. +1: Seasonal balancing of Canadian electricity trade which uses the annual balances (can_exports.csv and can_imports.csv in outputs folder, which contains inputs to ReEDS) and seasonal fractions (can_exports_szn_frac.csv and can_imports_szn_frac.csv in outputs folder). Operated similar to Hydro. 2: (not implemented in Current ReEDS 2.0) Strict hourly limits defined by can_trade_8760.h5 To generate updated input files for option 1 using Canadian_import_export_formatter.py: diff --git a/financials/README.md b/financials/README.md index c62cca5..f60e0c3 100644 --- a/financials/README.md +++ b/financials/README.md @@ -1,2 +1,2 @@ # Overview -This repo include scripts to calculate nuclear community ITCs used as inputs to ReEDS-2.0 +This repo include scripts to calculate nuclear community ITCs used as inputs to ReEDS diff --git a/financials/calculate_nuclear_energy_communities.py b/financials/calculate_nuclear_energy_communities.py index bfd050c..d5a7ece 100644 --- a/financials/calculate_nuclear_energy_communities.py +++ b/financials/calculate_nuclear_energy_communities.py @@ -96,7 +96,7 @@ def get_nuclear_energy_counties(year, msa_county_map, ct_county_crosswalk): def main(): print("Starting calculation...") - reeds_path = os.path.expanduser('~/github/ReEDS-2.0') + reeds_path = os.path.expanduser('~/github/ReEDS') # Source: # https://www.census.gov/programs-surveys/cbp/technical-documentation/reference/metro-area-geography-reference.html diff --git a/hydrogen/README.md b/hydrogen/README.md index 0fef3a2..4d6fbe4 100644 --- a/hydrogen/README.md +++ b/hydrogen/README.md @@ -1,5 +1,5 @@ # Overview This repo includes several scripts: -- Format H2 costs (storage, pipeline, compressor) for ReEDS-2.0 inputs (`process-h2-production.py`) +- Format H2 costs (storage, pipeline, compressor) for ReEDS inputs (`process-h2-production.py`) - Assign H2 storage types to each county or ba (`calculate_regional_h2_storage_type.py`) - Calculate H2 demand share by county (`calculate_regional_h2_demand_share.py`) diff --git a/hydrogen/calculate_regional_h2_demand_share.py b/hydrogen/calculate_regional_h2_demand_share.py index 9fd5915..877ae03 100644 --- a/hydrogen/calculate_regional_h2_demand_share.py +++ b/hydrogen/calculate_regional_h2_demand_share.py @@ -2,8 +2,8 @@ import os import sys import geopandas as gpd -#reeds_path = os.path.expanduser('~/github/ReEDS-2.0') -reeds_path = os.path.expanduser('~/Documents/Github/ReEDS/ReEDS-2.0/') +#reeds_path = os.path.expanduser('~/github/ReEDS') +reeds_path = os.path.expanduser('~/Documents/Github/ReEDS/ReEDS/') sys.path.append(reeds_path) import reeds diff --git a/hydrogen/calculate_regional_h2_storage_type.py b/hydrogen/calculate_regional_h2_storage_type.py index 4bf0cbc..7ed65b8 100644 --- a/hydrogen/calculate_regional_h2_storage_type.py +++ b/hydrogen/calculate_regional_h2_storage_type.py @@ -5,8 +5,8 @@ os.environ['USE_PYGEOS'] = '0' import geopandas as gpd -#reedspath = os.path.expanduser('~/github/ReEDS-2.0/') -reedspath = os.path.expanduser('~/Documents/Github/ReEDS/ReEDS-2.0/') +#reedspath = os.path.expanduser('~/github/ReEDS/') +reedspath = os.path.expanduser('~/Documents/Github/ReEDS/ReEDS/') reedspath = os.path.expanduser(reedspath) sys.path.append(reedspath) import reeds diff --git a/interconnection_queues/README.md b/interconnection_queues/README.md index 042ee23..afa6be3 100644 --- a/interconnection_queues/README.md +++ b/interconnection_queues/README.md @@ -12,11 +12,11 @@ This repo includes scripts and inputs to preprocess interconnection queues that ![interconnection queue inputs](interconnection_queue_inputs.png) # Input files and params to run process_interconnection_queues.py -All the input files to run the scripts are located in `inputs` folder, including original queue data from LBNL (most recently `lbnl_ix_queue_data_file_thru2024.xlsx`) and county2zone file (read from ReEDS-2.0 repo) to match ReEDS counties to appropriate bas. +All the input files to run the scripts are located in `inputs` folder, including original queue data from LBNL (most recently `lbnl_ix_queue_data_file_thru2024.xlsx`) and county2zone file (read from ReEDS repo) to match ReEDS counties to appropriate bas. # Output - Located in the `outputs` folder -- Final file that will be used to run ReEDS-2.0: `interconnection_queues.csv` +- Final file that will be used to run ReEDS: `interconnection_queues.csv` - Previous version files are also kept there # Comparison figures diff --git a/interconnection_queues/process_interconnection_queues.py b/interconnection_queues/process_interconnection_queues.py index 2396ed7..80f22fe 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-2.0') +reeds_path = os.path.expanduser('~/Documents/Github/ReEDS/ReEDS') sys.path.append(reeds_path) ''' diff --git a/load/detrend_historic_load.ipynb b/load/detrend_historic_load.ipynb index 704bd02..2d9f408 100644 --- a/load/detrend_historic_load.ipynb +++ b/load/detrend_historic_load.ipynb @@ -21,7 +21,7 @@ "metadata": {}, "outputs": [], "source": [ - "reedspath = os.path.expanduser('~/github/ReEDS-2.0')\n", + "reedspath = os.path.expanduser('~/github/ReEDS')\n", "site.addsitedir(reedspath)\n", "import reeds" ] diff --git a/nems_database_processing/README.md b/nems_database_processing/README.md index baac129..00a3d7f 100644 --- a/nems_database_processing/README.md +++ b/nems_database_processing/README.md @@ -34,5 +34,5 @@ All located in `Inputs` folder: * `tech_to_cooling_tech_map.csv` # Output file: -Located in `Outputs` folder. This is the final file that will be used to run ReEDS-2.0: +Located in `Outputs` folder. This is the final file that will be used to run ReEDS: * `ReEDS_generator_database_final_EIA-NEMS.csv` diff --git a/nems_database_processing/c_geospatial_mapping.py b/nems_database_processing/c_geospatial_mapping.py index 3f63add..80dc949 100644 --- a/nems_database_processing/c_geospatial_mapping.py +++ b/nems_database_processing/c_geospatial_mapping.py @@ -21,8 +21,8 @@ reeds_path = gdbnewname = sys.argv[1] # For debugging -# reeds_path = '~/Documents/Github/ReEDS/ReEDS-2.0/' # local -# reeds_path = '/kfs2/projects/stdscen/stdscens_fy25/ReEDS-2.0/' # kestrel +# reeds_path = '~/Documents/Github/ReEDS/' # local +# reeds_path = '/kfs2/projects/stdscen/stdscens_fy25/ReEDS/' # kestrel reeds_path = os.path.expanduser(reeds_path) sys.path.append(reeds_path) diff --git a/nems_database_processing/e_additional_inputs.py b/nems_database_processing/e_additional_inputs.py index 6f9681c..e92f63c 100644 --- a/nems_database_processing/e_additional_inputs.py +++ b/nems_database_processing/e_additional_inputs.py @@ -35,7 +35,7 @@ gdbfinalname = 'ReEDS_generator_database_final_EIA-NEMS.csv' dfin = pd.read_csv(os.path.join('Outputs',gdbinputname), low_memory=False) -#dfin = pd.read_csv("/Users/apham/Documents/GitHub/ReEDS-2.0_Input_Processing/NEMS_database_processing/Outputs/d_to_e.csv") +#dfin = pd.read_csv("/Users/apham/Documents/GitHub/ReEDS_Input_Processing/NEMS_database_processing/Outputs/d_to_e.csv") # Add nuclear retirement bins # Bin 1 indicates that the plant is at greater risk of retirement, which is due @@ -45,7 +45,7 @@ # requesting a license to operate to 80 years. nukebins = pd.read_csv(os.path.join('Inputs','NuclearBins.csv')) -#nukebins = pd.read_csv(os.path.join('/Users/apham/Documents/GitHub/ReEDS-2.0_Input_Processing/NEMS_database_processing/Inputs','NuclearBins.csv')) +#nukebins = pd.read_csv(os.path.join('/Users/apham/Documents/GitHub/ReEDS_Input_Processing/NEMS_database_processing/Inputs','NuclearBins.csv')) nukebins.rename(columns={'PLANT_NAME':'T_PNM'}, inplace=True) diff --git a/nems_database_processing/run.sh b/nems_database_processing/run.sh index b81168c..64c8fe5 100755 --- a/nems_database_processing/run.sh +++ b/nems_database_processing/run.sh @@ -2,8 +2,8 @@ ############## Input parameters ############## ############################################## -# Specify reeds_path (to get data from ReEDS-2.0 repo, locally or on super computer): -reeds_path='~/Documents/Github/ReEDS/ReEDS-2.0/' +# Specify reeds_path (to get data from ReEDS repo, locally or on super computer): +reeds_path='~/Documents/Github/ReEDS/ReEDS/' # Most recent ReEDS fleet and AEO versions: current_reeds_fleet_ver=2024 diff --git a/outage_rates/submit.sh b/outage_rates/submit.sh index 42d1e90..0f34759 100644 --- a/outage_rates/submit.sh +++ b/outage_rates/submit.sh @@ -3,7 +3,7 @@ #SBATCH --time=4:00:00 #SBATCH --nodes=1 #SBATCH --ntasks-per-node=1 -#SBATCH --mail-user=pbrown@nrel.gov +#SBATCH --mail-user=pbrown@nlr.gov #SBATCH --mail-type=FAIL #SBATCH --mem=246000 #SBATCH --output=/projects/reedsweto/pbrown/logs/slurm-%j.out diff --git a/outage_rates/temperature_profiles.py b/outage_rates/temperature_profiles.py index dffd1c2..e64cf36 100644 --- a/outage_rates/temperature_profiles.py +++ b/outage_rates/temperature_profiles.py @@ -1,5 +1,5 @@ """ -- Must be run on the NREL HPC +- Must be run on the NLR HPC - Add rex to your environment first using `pip install nrel-rex==0.2.85` """ @@ -13,8 +13,8 @@ from rex import NSRDBX from tqdm import tqdm -reeds_path = os.path.expanduser('~/github/ReEDS-2.0') -reeds_path = '/projects/reedsweto/pbrown/github/ReEDS-2.0' +reeds_path = os.path.expanduser('~/github/ReEDS') +reeds_path = '/projects/reedsweto/pbrown/github/ReEDS' site.addsitedir(os.path.join(reeds_path)) import reeds diff --git a/psh/calculate_existing_psh_capacities.py b/psh/calculate_existing_psh_capacities.py index 437357c..d9cdce1 100644 --- a/psh/calculate_existing_psh_capacities.py +++ b/psh/calculate_existing_psh_capacities.py @@ -48,7 +48,7 @@ } def main(): - reeds_path = os.path.expanduser('~/github/ReEDS-2.0') + reeds_path = os.path.expanduser('~/github/ReEDS') outdir_path = os.path.join(reeds_path, 'inputs', 'storage') dfcounty = gpd.read_file( os.path.join( diff --git a/state_policies/data_processing.py b/state_policies/data_processing.py index 56ae9a8..c8a6be8 100644 --- a/state_policies/data_processing.py +++ b/state_policies/data_processing.py @@ -41,9 +41,9 @@ Hydrosheet_CES_skiprows = 39 Hydrosheet_CES_nrows = 16 -### Input voluntary RPS data which is downloaded from NREL Green Power Data +### Input voluntary RPS data which is downloaded from NLR Green Power Data ### If update the input file, please make sure the below table parameters are updated accordingly. -### https://www.nrel.gov/analysis/green-power +### https://www.nlr.gov/analysis/green-power ### ----------------------------------------------------------------------------- # These data are used to calculate voluntary RPS fraction and will be appended to `rps_fraction.csv` diff --git a/state_policies/old and new data comparison/old and new data comparison.ipynb b/state_policies/old and new data comparison/old and new data comparison.ipynb index 4c6761e..1cacd59 100644 --- a/state_policies/old and new data comparison/old and new data comparison.ipynb +++ b/state_policies/old and new data comparison/old and new data comparison.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 67, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -21,9 +21,9 @@ "import matplotlib.pyplot as plt\n", "\n", "# === Load data ===\n", - "df1 = pd.read_csv(r\"C:\\ReEDS\\ReEDS-2.0_Input_Processing\\state_policies\\old and new data comparison\\old ReEDS input\\rps_fraction0.csv\")\n", - "df2 = pd.read_csv(r\"C:\\ReEDS\\ReEDS-2.0_Input_Processing\\state_policies\\outputs\\rps_fraction.csv\")\n", - "df3 = pd.read_csv(r\"C:\\ReEDS\\ReEDS-2.0_Input_Processing\\state_policies\\outputs\\intermediate outputs\\rps_fraction_intermediate.csv\")\n", + "df1 = pd.read_csv(r\"C:\\ReEDS\\ReEDS_Input_Processing\\state_policies\\old and new data comparison\\old ReEDS input\\rps_fraction0.csv\")\n", + "df2 = pd.read_csv(r\"C:\\ReEDS\\ReEDS_Input_Processing\\state_policies\\outputs\\rps_fraction.csv\")\n", + "df3 = pd.read_csv(r\"C:\\ReEDS\\ReEDS_Input_Processing\\state_policies\\outputs\\intermediate outputs\\rps_fraction_intermediate.csv\")\n", "\n", "# === Get list of states ===\n", "states = df1['st'].unique()\n", @@ -65,7 +65,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -84,9 +84,9 @@ "import matplotlib.pyplot as plt\n", "\n", "# === Load data ===\n", - "df1 = pd.read_csv(r\"C:\\ReEDS\\ReEDS-2.0_Input_Processing\\state_policies\\old and new data comparison\\old ReEDS input\\rps_fraction0.csv\")\n", - "df2 = pd.read_csv(r\"C:\\ReEDS\\ReEDS-2.0_Input_Processing\\state_policies\\outputs\\rps_fraction.csv\")\n", - "df3 = pd.read_csv(r\"C:\\ReEDS\\ReEDS-2.0_Input_Processing\\state_policies\\outputs\\intermediate outputs\\rps_fraction_intermediate.csv\")\n", + "df1 = pd.read_csv(r\"C:\\ReEDS\\ReEDS_Input_Processing\\state_policies\\old and new data comparison\\old ReEDS input\\rps_fraction0.csv\")\n", + "df2 = pd.read_csv(r\"C:\\ReEDS\\ReEDS_Input_Processing\\state_policies\\outputs\\rps_fraction.csv\")\n", + "df3 = pd.read_csv(r\"C:\\ReEDS\\ReEDS_Input_Processing\\state_policies\\outputs\\intermediate outputs\\rps_fraction_intermediate.csv\")\n", "\n", "# === Get list of states ===\n", "states = df1['st'].unique()\n", @@ -128,7 +128,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -147,9 +147,9 @@ "import matplotlib.pyplot as plt\n", "\n", "# === Load data ===\n", - "df1 = pd.read_csv(r\"C:\\ReEDS\\ReEDS-2.0_Input_Processing\\state_policies\\old and new data comparison\\old ReEDS input\\rps_fraction0.csv\")\n", - "df2 = pd.read_csv(r\"C:\\ReEDS\\ReEDS-2.0_Input_Processing\\state_policies\\outputs\\rps_fraction.csv\")\n", - "df3 = pd.read_csv(r\"C:\\ReEDS\\ReEDS-2.0_Input_Processing\\state_policies\\outputs\\intermediate outputs\\rps_fraction_intermediate.csv\")\n", + "df1 = pd.read_csv(r\"C:\\ReEDS\\ReEDS_Input_Processing\\state_policies\\old and new data comparison\\old ReEDS input\\rps_fraction0.csv\")\n", + "df2 = pd.read_csv(r\"C:\\ReEDS\\ReEDS_Input_Processing\\state_policies\\outputs\\rps_fraction.csv\")\n", + "df3 = pd.read_csv(r\"C:\\ReEDS\\ReEDS_Input_Processing\\state_policies\\outputs\\intermediate outputs\\rps_fraction_intermediate.csv\")\n", "\n", "# === Get list of states ===\n", "states = df1['st'].unique()\n", @@ -191,7 +191,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -210,9 +210,9 @@ "import matplotlib.pyplot as plt\n", "\n", "# Load CES data\n", - "df1 = pd.read_csv(r\"C:\\ReEDS\\ReEDS-2.0_Input_Processing\\state_policies\\old and new data comparison\\old ReEDS input\\ces_fraction0.csv\")\n", - "df2 = pd.read_csv(r\"C:\\ReEDS\\ReEDS-2.0_Input_Processing\\state_policies\\outputs\\ces_fraction.csv\")\n", - "df3 = pd.read_csv(r\"C:\\ReEDS\\ReEDS-2.0_Input_Processing\\state_policies\\outputs\\intermediate outputs\\ces_fraction_intermediate.csv\")\n", + "df1 = pd.read_csv(r\"C:\\ReEDS\\ReEDS_Input_Processing\\state_policies\\old and new data comparison\\old ReEDS input\\ces_fraction0.csv\")\n", + "df2 = pd.read_csv(r\"C:\\ReEDS\\ReEDS_Input_Processing\\state_policies\\outputs\\ces_fraction.csv\")\n", + "df3 = pd.read_csv(r\"C:\\ReEDS\\ReEDS_Input_Processing\\state_policies\\outputs\\intermediate outputs\\ces_fraction_intermediate.csv\")\n", "\n", "# Rename column '*t' to 't' for clarity\n", "df1 = df1.rename(columns={'*t': 't'})\n", @@ -256,7 +256,7 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -286,8 +286,8 @@ "import numpy as np\n", "\n", "# === Load the old and new hydrofrac_policy data ===\n", - "df_old = pd.read_csv(r\"C:\\ReEDS\\ReEDS-2.0_Input_Processing\\state_policies\\old and new data comparison\\old ReEDS input\\hydrofrac_policy0.csv\")\n", - "df_new = pd.read_csv(r\"C:\\ReEDS\\ReEDS-2.0_Input_Processing\\state_policies\\outputs\\hydrofrac_policy.csv\")\n", + "df_old = pd.read_csv(r\"C:\\ReEDS\\ReEDS_Input_Processing\\state_policies\\old and new data comparison\\old ReEDS input\\hydrofrac_policy0.csv\")\n", + "df_new = pd.read_csv(r\"C:\\ReEDS\\ReEDS_Input_Processing\\state_policies\\outputs\\hydrofrac_policy.csv\")\n", "\n", "# === Sort and merge by state for alignment ===\n", "df_old = df_old.sort_values(\"st\")\n", diff --git a/transmission/calc_transmission_cost_existing.py b/transmission/calc_transmission_cost_existing.py index f44f4bd..034325c 100644 --- a/transmission/calc_transmission_cost_existing.py +++ b/transmission/calc_transmission_cost_existing.py @@ -5,7 +5,7 @@ import pandas as pd import geopandas as gpd -sys.path.append(os.path.expanduser('~/github/ReEDS-2.0')) +sys.path.append(os.path.expanduser('~/github/ReEDS')) import reeds repo_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) diff --git a/transmission/collect_transmission_limits.py b/transmission/collect_transmission_limits.py index 154561e..5b2bee3 100644 --- a/transmission/collect_transmission_limits.py +++ b/transmission/collect_transmission_limits.py @@ -7,7 +7,7 @@ from glob import glob import geopandas as gpd -reeds_path = os.path.expanduser('~/github/ReEDS-2.0') +reeds_path = os.path.expanduser('~/github/ReEDS') remotepath = '/Volumes/ReEDS/Users/pbrown/Transmission/TSC/runs/' tscpath = os.path.expanduser('~/github/TSC') diff --git a/transmission/county_links.py b/transmission/county_links.py index c7b4386..4cb5415 100644 --- a/transmission/county_links.py +++ b/transmission/county_links.py @@ -7,7 +7,7 @@ from tqdm import tqdm import geopandas as gpd -sys.path.append(os.path.expanduser('~/github/ReEDS-2.0')) +sys.path.append(os.path.expanduser('~/github/ReEDS')) import reeds sys.path.append(os.path.expanduser('~/github/DLR')) import dlr.helpers diff --git a/transmission/write_lpf_naris.py b/transmission/write_lpf_naris.py index 312e214..171db9d 100644 --- a/transmission/write_lpf_naris.py +++ b/transmission/write_lpf_naris.py @@ -11,7 +11,7 @@ ## Local imports sys.path.append(os.path.expanduser('~/github/TSC')) import tsc -sys.path.append(os.path.expanduser('~/github/ReEDS-2.0')) +sys.path.append(os.path.expanduser('~/github/ReEDS')) import reeds ## https://github.nrel.gov/cobika/DLR diff --git a/transmission/zone_links.py b/transmission/zone_links.py index d6719aa..ac4f5fd 100644 --- a/transmission/zone_links.py +++ b/transmission/zone_links.py @@ -10,7 +10,7 @@ import matplotlib.patheffects as pe import geopandas as gpd -sys.path.append(os.path.expanduser('~/github/ReEDS-2.0')) +sys.path.append(os.path.expanduser('~/github/ReEDS')) import reeds # sys.path.append(os.path.expanduser('~/github/DLR')) # import dlr.helpers diff --git a/weights/README.md b/weights/README.md index f24c836..086c0d9 100644 --- a/weights/README.md +++ b/weights/README.md @@ -27,9 +27,7 @@ Higher county resolution `outputs/county_weights_150kmExpDecay.csv` is the outpu `figures/` gives figures of the weights files for each run, ## Other documentation: -`cendiv_weights.pptx` has figures of the weights file, using different input spatial resolution and varying decay lengths. - -`Supplement_other_method_weighting.xlsx` is a supplementary file, referenced in the `cendiv_weights.pptx` that explains another method of weighting we tried, but ended up not selecting. +`Supplement_other_method_weighting.xlsx` is a supplementary file that explains another method of weighting we tried, but ended up not selecting. diff --git a/weights/cendiv_weights.pptx b/weights/cendiv_weights.pptx deleted file mode 100644 index e706bfb..0000000 Binary files a/weights/cendiv_weights.pptx and /dev/null differ diff --git a/weights/generate_weights.py b/weights/generate_weights.py index 2a9d094..6a02aee 100644 --- a/weights/generate_weights.py +++ b/weights/generate_weights.py @@ -11,8 +11,8 @@ import sys # get ReEDS path -# https://github.com/natlabrockies/ReEDS-2.0 -reeds_path = os.path.expanduser('~/ReEDS-2.0') +# https://github.com/ReEDS-Model/ReEDS +reeds_path = os.path.expanduser('~/ReEDS') sys.path.append(reeds_path) import reeds diff --git a/wind/README.md b/wind/README.md index ebf57c4..e519c3d 100644 --- a/wind/README.md +++ b/wind/README.md @@ -4,7 +4,7 @@ The `cost_and_performance_script.py` file creates the ReEDS inputs `ons-wind_ATB ### Steps for updating to latest ATB - Note that the following steps are for onshore wind. Offshore wind is similar, except cost and performance was provided by Gabe Zuckerman for one "base" fixed and floating plant (rather than taken from ATB 2024). Also, historical cost and performance for offshore wind is currently just set constant to the first year's value from Gabe. - 1. Update `ons-wind_ATB_raw.csv` with the new cost and performance data for the specified technology and class. E.g. for the 2024 update, I used data from https://data.openei.org/files/6006/2024%20v1%20Annual%20Technology%20Baseline%20Workbook%20Original%206-24-2024.xlsx (accessed from https://atb.nrel.gov/electricity/2024/data). + 1. Update `ons-wind_ATB_raw.csv` with the new cost and performance data for the specified technology and class. E.g. for the 2024 update, I used data from https://data.openei.org/files/6006/2024%20v1%20Annual%20Technology%20Baseline%20Workbook%20Original%206-24-2024.xlsx (accessed from https://atb.nlr.gov/electricity/2024/data). 1. If historical years were removed from `ons-wind_ATB_raw.csv`, add rows to `ons-wind_cost-and-performance_historical.csv` for those years, and fill in Overnight Capital Cost, Fixed O&M, and Var O&M with their prior values in `ons-wind_ATB_raw.csv` (before the update). 1. Inflate all costs in `ons-wind_cost-and-performance_historical.csv` based on inflation between dollar year of last ATB and this ATB (including the costs in the new rows that were just added). 1. Update capacity factors in `ons-wind_cost-and-performance_historical.csv` with latest LBNL land-based wind market report. E.g. for the 2024 update, I used data from https://www.energy.gov/sites/default/files/2023-08/land-based-wind-market-report-2023-edition-data_0.xlsx (accessed from https://www.energy.gov/eere/wind/articles/land-based-wind-market-report-2023-edition), "Capacity Factor in 2022 by COD" tab, "Generation-Weighted Average Capacity Factor" field (with 2010 as the weighted average of 2010 and all prior years). diff --git a/zones/make_maps.py b/zones/make_maps.py index f1594f8..11a467d 100644 --- a/zones/make_maps.py +++ b/zones/make_maps.py @@ -28,8 +28,8 @@ sys.path.append(repo_path) import preprocessing -## https://github.nrel.gov/ReEDS/ReEDS-2.0 -reeds_path = os.path.expanduser('~/github/ReEDS-2.0') +## https://github.com/ReEDS-Model/ReEDS +reeds_path = os.path.expanduser('~/github/ReEDS') site.addsitedir(reeds_path) import reeds from reeds import plots