Skip to content

Commit

Permalink
Merge branch 'master' into ASEAN-ClimateFinance
Browse files Browse the repository at this point in the history
  • Loading branch information
maartenbrinkerink committed Dec 9, 2024
2 parents 3a9ae3d + 309e2d9 commit b67bcf6
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 75 deletions.
17 changes: 17 additions & 0 deletions config/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,25 @@ geographic_scope:
- "THA"
- "VNM"

# Set to True if transmission should be included
crossborderTrade: True

# Set to True if existing transmission capacities should be included
# from the Global Transmission Database (Brinkerink et al., 2024).
transmission_existing: True

# Set to True if planned transmission capacities should be included
# from the Global Transmission Database (Brinkerink et al., 2024).
transmission_planned: True

# Set to True if existing storage capacities should be included
# from the Global Energy Storage Database (DOE/Sandia).
storage_existing: True

# Set to True if planned storage capacities should be included
# from the Global Energy Storage Database (DOE/Sandia).
storage_planned: True

# Emission Parameters
emission_penalty:
# - [EMISSION, COUNTRY, START_YEAR, END_YEAR, VALUE]
Expand Down
4 changes: 4 additions & 0 deletions workflow/rules/preprocess.smk
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ rule transmission:
transmission_build_rates = 'resources/data/transmission_build_rates.csv',
params:
trade = config['crossborderTrade'],
transmission_existing = config['transmission_existing'],
transmission_planned = config['transmission_planned'],
start_year = config['startYear'],
end_year = config['endYear'],
region_name = 'GLOBAL',
Expand Down Expand Up @@ -262,6 +264,8 @@ rule storage:
gesdb_project_data = 'resources/data/GESDB_Project_Data.json',
gesdb_regional_mapping = 'resources/data/GESDB_region_mapping.csv',
params:
storage_existing = config['storage_existing'],
storage_planned = config['storage_planned'],
start_year = config['startYear'],
end_year = config['endYear'],
region_name = 'GLOBAL',
Expand Down
14 changes: 10 additions & 4 deletions workflow/scripts/osemosys_global/storage/main.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pandas as pd
import os
from typing import Optional, Any

from typing import Optional, Any

import logging

Expand Down Expand Up @@ -155,7 +156,8 @@ def main(

# Set residual capacity ('PWR') and residual capacity storage.
res_cap, res_cap_storage = res_capacity_storage(gesdb_data, gesdb_mapping,
res_cap_base, op_life_dict,
res_cap_base, storage_existing,
storage_planned, op_life_dict,
storage_parameters,
GESDB_TECH_MAP, DURATION_TYPE,
BUILD_YEAR, RETIREMENT_YEAR,
Expand Down Expand Up @@ -250,7 +252,9 @@ def main(
file_storage_build_rates = snakemake.input.storage_build_rates
file_default_op_life = snakemake.input.default_op_life
file_gesdb_project_data = snakemake.input.gesdb_project_data
file_gesdb_regional_mapping = snakemake.input.gesdb_regional_mapping
file_gesdb_regional_mapping = snakemake.input.gesdb_regional_mapping
storage_existing = snakemake.params.storage_existing
storage_planned = snakemake.params.storage_planned
start_year = snakemake.params.start_year
end_year = snakemake.params.end_year
region_name = snakemake.params.region_name
Expand Down Expand Up @@ -282,7 +286,9 @@ def main(
file_storage_build_rates = 'resources/data/storage_build_rates.csv'
file_default_op_life = 'resources/data/operational_life.csv'
file_gesdb_project_data = 'resources/data/GESDB_Project_Data.json'
file_gesdb_regional_mapping = 'resources/data/GESDB_region_mapping.csv'
file_gesdb_regional_mapping = 'resources/data/GESDB_region_mapping.csv'
storage_existing = True
storage_planned = True
start_year = 2021
end_year = 2050
region_name = 'GLOBAL'
Expand Down
60 changes: 37 additions & 23 deletions workflow/scripts/osemosys_global/storage/residual_capacity.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
def res_capacity_storage(gesdb_data,
gesdb_regional_mapping,
res_cap_base,
storage_existing,
storage_planned,
op_life_dict,
storage_param,
gesdb_tech_map,
Expand Down Expand Up @@ -137,29 +139,41 @@ def res_capacity_storage(gesdb_data,
(residual['YEAR'] == (year - 1))]
new_row.loc[new_row['YEAR'] == (year - 1), 'YEAR'] = year

residual = pd.concat([residual, new_row])
residual = pd.concat([residual, new_row])

if not storage_existing:
residual = residual.loc[residual['YEAR'] > start_year]

if not storage_planned:
residual = residual.loc[residual['YEAR'] == start_year]

if not residual.empty:

residual['REGION'] = region_name
residual = residual.drop_duplicates(subset=['REGION', 'STORAGE', 'YEAR']
, keep = 'last').set_index(['REGION', 'STORAGE', 'YEAR'])

# Pull power rating data for ResidualCapacity.csv
residual_capacity = residual[['rated_power']].reset_index(
drop = False).rename(columns = {'rated_power' : 'VALUE', 'STORAGE' : 'TECHNOLOGY'})

# Convert from kW to GW
residual_capacity['VALUE'] = round(residual_capacity['VALUE'] / 1000000, 4)
residual_capacity['TECHNOLOGY'] = 'PWR' + residual_capacity['TECHNOLOGY']

# Pull storage capacity data for ResidualStorageCapacity.csv
residual_storage_capacity = residual[['storage_capacity']].reset_index(
drop = False).rename(columns = {'storage_capacity' : 'VALUE'})

# Convert from kWh to PJ
residual_storage_capacity['VALUE'] = round(residual_storage_capacity['VALUE'
] / 277777777.77778, 5)

# Combine residual capacity df's.
residual_capacity = pd.concat([res_cap_base, residual_capacity]).reset_index(drop = True)
residual['REGION'] = region_name
residual = residual.drop_duplicates(subset=['REGION', 'STORAGE', 'YEAR']
, keep = 'last').set_index(['REGION', 'STORAGE', 'YEAR'])

# Pull power rating data for ResidualCapacity.csv
residual_capacity = residual[['rated_power']].reset_index(
drop = False).rename(columns = {'rated_power' : 'VALUE', 'STORAGE' : 'TECHNOLOGY'})

# Convert from kW to GW
residual_capacity['VALUE'] = round(residual_capacity['VALUE'] / 1000000, 4)
residual_capacity['TECHNOLOGY'] = 'PWR' + residual_capacity['TECHNOLOGY']

# Pull storage capacity data for ResidualStorageCapacity.csv
residual_storage_capacity = residual[['storage_capacity']].reset_index(
drop = False).rename(columns = {'storage_capacity' : 'VALUE'})

# Convert from kWh to PJ
residual_storage_capacity['VALUE'] = round(residual_storage_capacity['VALUE'
] / 277777777.77778, 5)

# Combine residual capacity df's.
residual_capacity = pd.concat([res_cap_base, residual_capacity]).reset_index(drop = True)

else:
residual_capacity = res_cap_base.copy()
residual_storage_capacity = pd.DataFrame(columns = ['REGION', 'STORAGE', 'YEAR','VALUE'])

return residual_capacity, residual_storage_capacity
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,12 @@ def set_user_defined_capacity_sto(tech_capacity_sto,
'STORAGE'].str.startswith(tech), 'VALUE'] = df_res_cap_sto_ud_final.loc[
df_res_cap_sto_ud_final['STORAGE'].str.startswith(tech), 'VALUE'] * \
duration / 277.777778

df_res_sto_cap = pd.concat([res_cap_sto_base, df_res_cap_sto_ud_final
if not df_res_cap_sto_ud_final.empty else None])

if not res_cap_sto_base.empty:
df_res_sto_cap = pd.concat([res_cap_sto_base, df_res_cap_sto_ud_final
if not df_res_cap_sto_ud_final.empty else None])
else:
df_res_sto_cap = df_res_cap_sto_ud_final.copy()

# Group residual capacities in case user defined technology entries already exist.
df_res_sto_cap = df_res_sto_cap.groupby(['REGION', 'STORAGE', 'YEAR']
Expand Down
18 changes: 13 additions & 5 deletions workflow/scripts/osemosys_global/transmission/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,8 @@ def main(
region_name)

# Set residual capacity.
res_cap_trn = res_capacity_transmission(gtd_exist_corrected, gtd_planned_corrected,
res_cap_trn = res_capacity_transmission(gtd_exist_corrected, gtd_planned_corrected,
transmission_existing, transmission_planned,
res_cap_base, op_life_dict,
start_year, end_year, region_name,
RETIREMENT_YEAR_TRANSMISSION,
Expand Down Expand Up @@ -230,6 +231,8 @@ def main(
no_investment_techs = snakemake.params.no_investment_techs
transmission_parameters = snakemake.params.transmission_parameters
cross_border_trade = snakemake.params.trade
transmission_existing = snakemake.params.transmission_existing
transmission_planned = snakemake.params.transmission_planned
output_data_dir = snakemake.params.output_data_dir
input_data_dir = snakemake.params.input_data_dir
powerplant_data_dir = snakemake.params.powerplant_data_dir
Expand Down Expand Up @@ -263,17 +266,22 @@ def main(
region_name = 'GLOBAL'

custom_nodes = []
tech_capacity_trn = {'trn1': ['TRNINDEAINDNE', 5, 1975, 2030, 10, 350, 13, 4, 95],
'trn2': ['TRNINDEAINDNE', 1, 1990, 2030, 10, 350, 13, 4, 95],
'trn3': ['TRNINDEAINDNE', 2, 2035, 2030, 10, 350, 13, 4, 95],
'trn4': ['TRNINDNOINDSO', 0, 2020, 2025, 0.5, 620, 24, 4, 92]}
tech_capacity_trn = {'trn1': ['TRNINDEAINDNE', 5, 1975, 2025, 2025, 0, 350, 13, 4, 95],
'trn2': ['TRNINDEAINDNE', 1, 1990, 2025, 2025, 0, 350, 13, 4, 95],
'trn3': ['TRNINDEAINDNE', 2, 2035, 2025, 2025, 0, 350, 13, 4, 95],
'trn4': ['TRNINDEAINDNE', 0, 2020, 2025, 2025, 1, 350, 13, 4, 95],
'trn5': ['TRNINDEAINDNE', 0, 2020, 2030, 2040, 2, 350, 13, 4, 95],
'trn6': ['TRNINDEAINDNE', 0, 2020, 2040, 2050, 3, 350, 13, 4, 95],
'trn7': ['TRNINDNOINDSO', 0, 2020, 2025, 2025, 0.5, 620, 24, 4, 92]}

no_investment_techs = ["CSP", "WAV", "URN", "OTH", "WAS",
"COG", "GEO", "BIO", "PET"]
transmission_parameters = {'HVAC': [779, 95400, 6.75, 0, 3.5, 4],
'HVDC': [238, 297509, 3.5, 1.3, 3.5, 4],
'HVDC_subsea': [295, 297509, 3.5, 1.3, 3.5, 4]}
cross_border_trade = True
transmission_existing = True
transmission_planned = True
output_data_dir = 'results/data'
input_data_dir = 'resources/data'
powerplant_data_dir = 'results/data/powerplant'
Expand Down
95 changes: 55 additions & 40 deletions workflow/scripts/osemosys_global/transmission/residual_capacity.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,52 +4,67 @@
from data import get_years

def res_capacity_transmission(df_exist_corrected, df_plan_corrected,
transmission_existing, transmission_planned,
res_cap_base, op_life_dict,
start_year, end_year, region_name,
retirement_year_transmission,
planned_build_year_transmission):

"""Set build year for existing capacities as the differential between assumed
retirement year and the set operational life for transmission technologies."""
df_res_cap_exist = df_exist_corrected.copy()
df_res_cap_exist['YEAR'] = retirement_year_transmission - op_life_dict.get('TRN')
df_res_cap = None

"""Set assumed build year for planned capacities from the GTD dataset that do not have
a commissioning year attached."""
df_res_cap_plan = df_plan_corrected.copy()
df_res_cap_plan.loc[df_res_cap_plan['YEAR'] == '-', 'YEAR'] = planned_build_year_transmission
if transmission_existing:
"""Set build year for existing capacities as the differential between assumed
retirement year and the set operational life for transmission technologies."""
df_res_cap_exist = df_exist_corrected.copy()
df_res_cap_exist['YEAR'] = retirement_year_transmission - op_life_dict.get('TRN')

if transmission_planned:
"""Set assumed build year for planned capacities from the GTD dataset that do not have
a commissioning year attached."""
df_res_cap_plan = df_plan_corrected.copy()
df_res_cap_plan.loc[df_res_cap_plan['YEAR'] == '-', 'YEAR'] = planned_build_year_transmission

# Combine dfs and filter out rows without capacities.
df_res_cap = pd.concat([df_res_cap_exist, df_res_cap_plan])
df_res_cap = df_res_cap.loc[df_res_cap['VALUE'] != 0
].reset_index(drop = True
).rename(columns = {'YEAR' :'build_year'})

# Set retirement years.
df_res_cap['build_year'] = df_res_cap['build_year'].astype(int)
df_res_cap['retirement_year'] = df_res_cap['build_year'] + op_life_dict.get('TRN')

# Set residual capacity for all model horizon years.
df_res_cap['YEAR'] = [get_years(start_year, end_year)] * len(df_res_cap)
df_res_cap = df_res_cap.explode('YEAR').reset_index(drop = True)

# Convert from MW to GW.
df_res_cap['VALUE'] = df_res_cap['VALUE'] / 1000

df_res_cap.loc[~
((df_res_cap["YEAR"] >= df_res_cap["build_year"])
& (df_res_cap["YEAR"] <= df_res_cap["retirement_year"])),
"VALUE",
] = 0

# Group capacities by year and technology.
df_res_cap = df_res_cap.groupby(['TECHNOLOGY', 'YEAR']).sum('VALUE').reset_index()

# Reorder columns
df_res_cap['REGION'] = region_name
df_res_cap = df_res_cap[['REGION', 'TECHNOLOGY', 'YEAR', 'VALUE']]

# Combine powerplant and transmission resisdual capacity df's.
df_res_cap = pd.concat([res_cap_base, df_res_cap]).reset_index(drop = True)
if transmission_existing and transmission_planned:
# Combine dfs and filter out rows without capacities.
df_res_cap = pd.concat([df_res_cap_exist, df_res_cap_plan])
if transmission_existing and not transmission_planned:
df_res_cap = df_res_cap_exist.copy()
if not transmission_existing and transmission_planned:
df_res_cap = df_res_cap_plan.copy()

if df_res_cap is not None:
df_res_cap = df_res_cap.loc[df_res_cap['VALUE'] != 0
].reset_index(drop = True
).rename(columns = {'YEAR' :'build_year'})

# Set retirement years.
df_res_cap['build_year'] = df_res_cap['build_year'].astype(int)
df_res_cap['retirement_year'] = df_res_cap['build_year'] + op_life_dict.get('TRN')

# Set residual capacity for all model horizon years.
df_res_cap['YEAR'] = [get_years(start_year, end_year)] * len(df_res_cap)
df_res_cap = df_res_cap.explode('YEAR').reset_index(drop = True)

# Convert from MW to GW.
df_res_cap['VALUE'] = df_res_cap['VALUE'] / 1000

df_res_cap.loc[~
((df_res_cap["YEAR"] >= df_res_cap["build_year"])
& (df_res_cap["YEAR"] <= df_res_cap["retirement_year"])),
"VALUE",
] = 0

# Group capacities by year and technology.
df_res_cap = df_res_cap.groupby(['TECHNOLOGY', 'YEAR']).sum('VALUE').reset_index()

# Reorder columns
df_res_cap['REGION'] = region_name
df_res_cap = df_res_cap[['REGION', 'TECHNOLOGY', 'YEAR', 'VALUE']]

# Combine powerplant and transmission resisdual capacity df's.
df_res_cap = pd.concat([res_cap_base, df_res_cap]).reset_index(drop = True)

else:
df_res_cap = res_cap_base.copy()

return df_res_cap

0 comments on commit b67bcf6

Please sign in to comment.