Skip to content

Commit

Permalink
Merge branch 'backstop' into ASEAN-ClimateFinance
Browse files Browse the repository at this point in the history
  • Loading branch information
maartenbrinkerink committed Dec 5, 2024
2 parents 7c0592f + 07fc15e commit 23e22a0
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 31 deletions.
27 changes: 27 additions & 0 deletions workflow/scripts/osemosys_global/check_backstop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""Prints warning if backstop techs are used"""

import pandas as pd
import sys
from pathlib import Path

if __name__ == "__main__":

if len(sys.argv) != 2:
raise ValueError("Usage: python create_checkbackstop.py <sceanrio_name>")
else:
scenario = sys.argv[1]

new_capacity_csv = Path("results", scenario, "results", "NewCapacity.csv")
try:
new_capacity = pd.read_csv(new_capacity_csv)
except FileNotFoundError:
sys.exit()

backstop = new_capacity[new_capacity.TECHNOLOGY.str.startswith("PWRBCK")]

if not backstop.empty:
techs = backstop.TECHNOLOGY.unique().tolist()
print("\n*****\n")
print("The following Backstop Technologies are being used:\n")
print(techs)
print("\n*****\n")
66 changes: 66 additions & 0 deletions workflow/scripts/osemosys_global/powerplant/backstop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""Sets backstop parameters"""

import pandas as pd


def get_backstop_data(
tech_set: pd.DataFrame, year_set: pd.DataFrame, region: str
) -> tuple[pd.DataFrame]:
"""Gets the following backstop data
- Technologies
- OAR
- Capital Cost
- Fixed Cost
- CapacityToActivity
"""

df = tech_set.copy()

# technologies
techs = df[df.VALUE.str.startswith("PWR")].copy() # pwrtrn not added yet
techs["VALUE"] = "PWRBCK" + df.VALUE.str[-7:-2]
techs = techs.drop_duplicates()
bck_techs = techs.VALUE.to_list()

# activity ratios
years = year_set.VALUE.to_list()
oar = pd.DataFrame(
index=pd.MultiIndex.from_product(
[bck_techs, years], names=["TECHNOLOGY", "YEAR"]
)
).reset_index()
oar["REGION"] = region
oar["FUEL"] = oar.TECHNOLOGY.str.replace("PWRBCK", "ELC")
oar["FUEL"] = oar.FUEL + "01"
oar["MODE_OF_OPERATION"] = 1
oar["VALUE"] = 1
oar = oar[["REGION", "TECHNOLOGY", "FUEL", "MODE_OF_OPERATION", "YEAR", "VALUE"]]

# capital cost
capex = pd.DataFrame(
index=pd.MultiIndex.from_product(
[bck_techs, years], names=["TECHNOLOGY", "YEAR"]
)
).reset_index()
capex["REGION"] = region
capex["VALUE"] = 999999
capex = capex[["REGION", "TECHNOLOGY", "YEAR", "VALUE"]]

# fixed costs
opex = pd.DataFrame(
index=pd.MultiIndex.from_product(
[bck_techs, years], names=["TECHNOLOGY", "YEAR"]
)
).reset_index()
opex["REGION"] = region
opex["VALUE"] = 999999
opex = opex[["REGION", "TECHNOLOGY", "YEAR", "VALUE"]]

# capacity to activity
capact = pd.DataFrame(index=pd.Index(bck_techs, name="TECHNOLOGY")).reset_index()
capact["REGION"] = region
capact["VALUE"] = 31.536
capact = capact[["REGION", "TECHNOLOGY", "VALUE"]]

return techs, oar, capex, opex, capact
33 changes: 14 additions & 19 deletions workflow/scripts/osemosys_global/powerplant/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@

from calibration import apply_calibration

from backstop import get_backstop_data

def main(
plexos_prop: pd.DataFrame,
plexos_memb: pd.DataFrame,
Expand Down Expand Up @@ -139,22 +141,15 @@ def main(
region_name)
# Calculate residual capacity.
df_res_cap = res_capacity(gen_table, DUPLICATE_TECHS, start_year, end_year, region_name)

# if custom_nodes:


# Adds residual capacity for custom entries.
df_res_cap, custom_techs = add_custom_res_cap(df_res_cap, custom_res_cap,
tech_list, start_year,
end_year, region_name)

# Creates sets for TECHNOLOGIES including custom entries.
tech_set = create_sets('TECHNOLOGY', df_oar_final, powerplant_data_dir, custom_techs)
# else:
# custom_techs = []

# Creates sets for TECHNOLOGIES absent custom entries.
# tech_set = create_sets('TECHNOLOGY', df_oar_final, powerplant_data_dir, [])


# Creates sets for FUEL.
fuel_set = create_sets('FUEL', df_oar_final, powerplant_data_dir, [])

Expand Down Expand Up @@ -237,6 +232,14 @@ def main(
df_res_cap,
region_name)

# add backstop technologies
bck_techs, bck_oar, bck_capex, bck_opex, bck_capact = get_backstop_data(tech_set, years_set, region_name)
tech_set = pd.concat([tech_set, bck_techs])
df_oar_final = pd.concat([df_oar_final, bck_oar])
df_cap_cost_final = pd.concat([df_cap_cost_final, bck_capex])
df_fix_cost_final = pd.concat([df_fix_cost_final, bck_opex])
df_capact_final = pd.concat([df_capact_final, bck_capact])

# OUTPUT CSV's USED AS INPUT FOR TRANSMISSION RULE

df_res_cap.to_csv(os.path.join(powerplant_data_dir, "ResidualCapacity.csv"), index=None)
Expand Down Expand Up @@ -312,8 +315,6 @@ def main(
input_data_dir = snakemake.params.input_data_dir
powerplant_data_dir = snakemake.params.powerplant_data_dir
file_specified_annual_demand = f'{output_data_dir}/SpecifiedAnnualDemand.csv'

# if custom_nodes:
file_custom_res_cap = snakemake.input.custom_res_cap
file_custom_res_potentials = snakemake.input.custom_res_potentials

Expand Down Expand Up @@ -351,13 +352,11 @@ def main(
fossil_capacity_targets = [["BTNXX", 'COA', 2030, 2050, 'ABS', 1],
["INDNE", 'CCG', 2040, 2050, 'MIN', 10],
["INDSO", 'OCG', 2025, 2050, 'MAX', 25]]
min_generation_factors = {'OCG1': [50, "IND", 2021]}
calibration = {'OCG1': [50, "IND", 2021]}
output_data_dir = 'results/data'
input_data_dir = 'resources/data'
powerplant_data_dir = 'results/data/powerplant'
file_specified_annual_demand = f'{output_data_dir}/SpecifiedAnnualDemand.csv'

#if custom_nodes:
file_custom_res_cap = 'resources/data/custom_nodes/residual_capacity.csv'
file_custom_res_potentials = 'resources/data/custom_nodes/RE_potentials.csv'

Expand Down Expand Up @@ -385,13 +384,9 @@ def main(

availability = import_afs(file_default_af_factors)
specified_annual_demand = import_specified_annual_demand(file_specified_annual_demand)

#if custom_nodes:

custom_res_cap = import_custom_res_cap(file_custom_res_cap)
custom_res_potentials = import_custom_res_potentials(file_custom_res_potentials)
#else:
# custom_res_cap = []
# custom_res_potentials = []

input_data = {
"plexos_prop": plexos_prop,
Expand Down
33 changes: 29 additions & 4 deletions workflow/scripts/osemosys_global/powerplant/variable_costs.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,26 @@ def _get_country_nodes_mapper(nodes: list[str]) -> dict[str, list[str]]:
return df.set_index(["REGION", "TECHNOLOGY", "MODE_OF_OPERATION", "YEAR"])


def get_backstop_var_costs(
techs: pd.Series, years: pd.Series, regions: pd.Series
) -> pd.Series:
"""Gets backstop variable costs"""

t = techs[techs.str.startswith("PWRBCK")].unique().tolist()
y = years.unique().tolist()
r = regions.unique().tolist()[0] # only one region

df = pd.DataFrame(
index=pd.MultiIndex.from_product(
[[r], t, y], names=["REGION", "TECHNOLOGY", "YEAR"]
)
).reset_index()
df["MODE_OF_OPERATION"] = 1
df["VALUE"] = 999999

return df.set_index(["REGION", "TECHNOLOGY", "MODE_OF_OPERATION", "YEAR"])


def main(
cmo_forecasts: pd.DataFrame,
cmo_data_year: int,
Expand Down Expand Up @@ -403,6 +423,11 @@ def main(
var_costs = pd.concat([mining_data, renewable_data])
var_costs = var_costs[var_costs.index.get_level_values("YEAR").isin(y)]
var_costs = filter_var_cost_technologies(var_costs, technologies)

bck_var_costs = get_backstop_var_costs(technologies, years, regions)

var_costs = pd.concat([var_costs, bck_var_costs])

return var_costs.round(3)


Expand All @@ -418,10 +443,10 @@ def main(
else:
file_cmo_forecasts = "resources/data/CMO-October-2024-Forecasts.xlsx"
file_fuel_prices = "resources/data/fuel_prices.csv"
file_regions = "results/India/data/REGION.csv"
file_years = "results/India/data/YEAR.csv"
file_technologies = "results/India/data/TECHNOLOGY.csv"
file_var_costs = "results/India/data/VariableCosts.csv"
file_regions = "results/data/REGION.csv"
file_years = "results/data/YEAR.csv"
file_technologies = "results/data/powerplant/TECHNOLOGY.csv"
file_var_costs = "results/data/powerplant/VariableCosts.csv"

cmo_forecasts = import_cmo_forecasts(file_cmo_forecasts)
user_fuel_prices = import_fuel_prices(file_fuel_prices)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
def set_reserve_margin_technologies(margins_technologies,
tech_set, start_year,
end_year, region_name):

years = get_years(start_year, end_year)
data = pd.DataFrame()

'''Check if transmission is added to the config to contribute to
reserves and develop outputs.'''
for tech, val in margins_technologies.items():
Expand All @@ -20,21 +20,45 @@ def set_reserve_margin_technologies(margins_technologies,
for x in tech_set["VALUE"].unique()
if x.startswith("TRN")
]

else:
rm_techs = [
x
for x in tech_set["VALUE"].unique()
if x.startswith("PWR")
if x[3:6] == tech
]

tech_data = pd.DataFrame(
list(itertools.product([region_name], rm_techs, years)),
columns=["REGION", "TECHNOLOGY", "YEAR"],
)

tech_data['VALUE'] = val / 100
data = pd.concat([data, tech_data])

return data
backstop = get_backstop_rm(tech_set, region_name, start_year, end_year)

return pd.concat([data, backstop])


def get_backstop_rm(
tech_set: pd.DataFrame, region: str, start_year: int, end_year: int
) -> pd.DataFrame:
"""Backstop does not contribute to reserve margin"""

bck_techs = tech_set[tech_set.VALUE.str.startswith("PWRBCK")]

if bck_techs.empty:
return pd.DataFrame(columns=["REGION", "TECHNOLOGY", "YEAR", "VALUE"])

t = bck_techs.VALUE.unique().tolist()
y = get_years(start_year, end_year)

df = pd.DataFrame(
index=pd.MultiIndex.from_product([t, y], names=["TECHNOLOGY", "YEAR"])
).reset_index()
df["REGION"] = region
df["VALUE"] = 0

return df[["REGION", "TECHNOLOGY", "YEAR", "VALUE"]]
4 changes: 2 additions & 2 deletions workflow/scripts/osemosys_global/visualise.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def plot_generation_hourly(
else:
fig_file = os.path.join(save_dir, 'GenerationHourly.html')
return fig.write_html(fig_file)

def midpoint(x1, y1, x2, y2):
return ((x1 + x2)/2, (y1 + y2)/2)

Expand Down Expand Up @@ -445,4 +445,4 @@ def plot_transmission_flow(

except FileNotFoundError:
print(f"Usage: python {sys.argv[0]} <input_data.csv> <result_data.csv> <scenario_figs_dir> <countries> <results_by_country> <years> <custom_nodes>")
sys.exit(1)
sys.exit(1)
1 change: 1 addition & 0 deletions workflow/snakefile
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ include: "rules/validate.smk"
# handlers

onsuccess:
shell(f"python workflow/scripts/osemosys_global/check_backstop.py {config['scenario']}")
print('Workflow finished successfully!')

# Will fix this in next update so that preprocessing steps dont always need to be rerun
Expand Down

0 comments on commit 23e22a0

Please sign in to comment.