From af44fd942fd96d6c5037e44c1ba7c255cbdcb3da Mon Sep 17 00:00:00 2001 From: trevorb1 Date: Fri, 14 Feb 2025 13:28:24 -0800 Subject: [PATCH] costs updated --- workflow/rules/postprocess.smk | 11 +- .../scripts/osemosys_global/summary/costs.py | 125 ++++++++++-------- .../osemosys_global/summary/headline.py | 17 +-- .../scripts/osemosys_global/summary/utils.py | 15 +++ workflow/scripts/osemosys_global/utils.py | 2 +- 5 files changed, 95 insertions(+), 75 deletions(-) diff --git a/workflow/rules/postprocess.smk b/workflow/rules/postprocess.smk index c12d251..4186950 100644 --- a/workflow/rules/postprocess.smk +++ b/workflow/rules/postprocess.smk @@ -126,6 +126,10 @@ rule calculate_carbon_intensity: rule calculate_costs: message: "Calculating Costs..." + params: + otoole_input = "resources/otoole.yaml", + input_dir = "results/{scenario}/data/", + result_dir = "results/{scenario}/results/" input: discounted_cost_by_technology = "results/{scenario}/results/DiscountedCostByTechnology.csv", demand = "results/{scenario}/results/Demand.csv", @@ -177,10 +181,9 @@ rule calcualte_headline_metrics: "Calculating Headline Metrics..." params: storage = config['storage_parameters'], - otoole_input = "resources/otoole.yaml" - otoole_results = "resources/otoole.yaml" - input_dir = "results/{scenario}/data/" - result_dir = "results/{scenario}/results/" + otoole_input = "resources/otoole.yaml", + input_dir = "results/{scenario}/data/", + result_dir = "results/{scenario}/results/", input: annual_emissions = "results/{scenario}/results/AnnualEmissions.csv", production_by_technology = "results/{scenario}/results/ProductionByTechnologyAnnual.csv", diff --git a/workflow/scripts/osemosys_global/summary/costs.py b/workflow/scripts/osemosys_global/summary/costs.py index 2bc3362..7fb5be2 100644 --- a/workflow/scripts/osemosys_global/summary/costs.py +++ b/workflow/scripts/osemosys_global/summary/costs.py @@ -2,6 +2,8 @@ import pandas as pd from pathlib import Path +from otoole import read +from utils import _read_result_data, get_discount_factor def get_tech_cost(discounted_cost_tech: pd.DataFrame, country: bool) -> pd.DataFrame: @@ -47,13 +49,14 @@ def get_storage_cost( .sum() ) -def get_transmission_cost(discounted_cost_tech: pd.DataFrame, country: bool) -> pd.DataFrame: + +def get_transmission_cost( + discounted_cost_tech: pd.DataFrame, country: bool +) -> pd.DataFrame: df = discounted_cost_tech.copy() - df1 = df[ - (df.index.get_level_values("TECHNOLOGY").str.startswith("TRN")) - ].copy() + df1 = df[(df.index.get_level_values("TECHNOLOGY").str.startswith("TRN"))].copy() if country: r = "COUNTRY" @@ -61,10 +64,8 @@ def get_transmission_cost(discounted_cost_tech: pd.DataFrame, country: bool) -> else: r = "NODE" df1[r] = df1.index.get_level_values("TECHNOLOGY").str[3:8] - - df2 = df.copy()[ - (df.index.get_level_values("TECHNOLOGY").str.startswith("TRN")) - ] + + df2 = df.copy()[(df.index.get_level_values("TECHNOLOGY").str.startswith("TRN"))] if country: r = "COUNTRY" @@ -72,10 +73,10 @@ def get_transmission_cost(discounted_cost_tech: pd.DataFrame, country: bool) -> else: r = "NODE" df2[r] = df2.index.get_level_values("TECHNOLOGY").str[8:13] - + for df in [df1, df2]: - df['VALUE'] = df['VALUE'] / 2 - + df["VALUE"] = df["VALUE"] / 2 + df = pd.concat([df1, df2]) return ( @@ -84,6 +85,7 @@ def get_transmission_cost(discounted_cost_tech: pd.DataFrame, country: bool) -> .sum() ) + def get_demand(demand: pd.DataFrame, country: bool) -> pd.DataFrame: df = demand.copy() @@ -114,22 +116,20 @@ def get_pwr_cost(demand: pd.DataFrame, cost: pd.DataFrame) -> pd.DataFrame: if __name__ == "__main__": if "snakemake" in globals(): - discounted_cost_by_technology_csv = ( - snakemake.input.discounted_cost_by_technology - ) - demand_csv = snakemake.input.demand + input_data_dir = snakemake.params.input_dir + result_data_dir = snakemake.params.result_dir + otoole_config = snakemake.params.otoole_input power_cost_node_csv = snakemake.output.node_pwr_cost power_cost_country_csv = snakemake.output.country_pwr_cost - power_cost_global_csv = snakemake.output.global_pwr_cost + power_cost_global_csv = snakemake.output.global_pwr_cost total_cost_node_csv = snakemake.output.node_cost total_cost_country_csv = snakemake.output.country_cost total_cost_global_csv = snakemake.output.global_cost - + else: - discounted_cost_by_technology_csv = ( - "results/India/results/DiscountedCostByTechnology.csv" - ) - demand_csv = "results/India/results/Demand.csv" + input_data_dir = "results/India/data" + result_data_dir = "results/India/results" + otoole_config = "resources/otoole.yaml" power_cost_node_csv = "results/India/result_summaries/PowerCostNode.csv" power_cost_country_csv = "results/India/result_summaries/PowerCostCountry.csv" power_cost_global_csv = "results/India/result_summaries/PowerCostGlobal.csv" @@ -137,21 +137,29 @@ def get_pwr_cost(demand: pd.DataFrame, cost: pd.DataFrame) -> pd.DataFrame: total_cost_country_csv = "results/India/result_summaries/TotalCostCountry.csv" total_cost_global_csv = "results/India/result_summaries/TotalCostGlobal.csv" - discounted_cost_by_technology = pd.read_csv( - discounted_cost_by_technology_csv, index_col=[0, 1, 2] - ) - demand_raw = pd.read_csv(demand_csv, index_col=[0, 1, 2, 3]) + input_data, input_defaults = read(otoole_config, "csv", input_data_dir) + result_data = _read_result_data(result_data_dir) + + if input_data["DiscountRate"].empty: + regions = input_data["REGION"].VALUE.to_list() + assert len(regions) == 1 + input_data["DiscountRate"] = pd.DataFrame( + [[regions[0], input_defaults["DiscountRate"]]], columns=["REGION", "VALUE"] + ).set_index("REGION") + + discounted_cost_by_technology = result_data["DiscountedCostByTechnology"] + demand = result_data["Demand"] + + years = input_data["YEAR"].VALUE.to_list() + discount_factor = get_discount_factor(years, input_data["DiscountRate"]) + result_data["DiscountedDemand"] = result_data["Demand"].div(discount_factor) # handle the storage discounted costs outside of snakemake # if running a model without storage, its not guaranteed that result # csvs will be generated with all solvers try: - parent = Path(discounted_cost_by_technology_csv).parent - discounted_cost_by_storage_csv = Path(parent, "DiscountedCostByStorage.csv") - discounted_cost_by_storage = pd.read_csv( - discounted_cost_by_storage_csv, index_col=[0, 1, 2] - ) - except FileNotFoundError: + discounted_cost_by_storage = result_data["DiscountedCostByStorage"] + except KeyError: discounted_cost_by_storage = pd.DataFrame( columns=["REGION", "STORAGE", "YEAR", "VALUE"] ).set_index(["REGION", "STORAGE", "YEAR"]) @@ -160,12 +168,15 @@ def get_pwr_cost(demand: pd.DataFrame, cost: pd.DataFrame) -> pd.DataFrame: tech_cost = get_tech_cost(discounted_cost_by_technology, country=False) storage_cost = get_storage_cost(discounted_cost_by_storage, country=False) - transmission_cost = get_transmission_cost(discounted_cost_by_technology, country=False) - - cost = tech_cost.add(storage_cost, fill_value = 0 - ).add(transmission_cost, fill_value = 0) - - demand = get_demand(demand_raw, country=False) + transmission_cost = get_transmission_cost( + discounted_cost_by_technology, country=False + ) + + cost = tech_cost.add(storage_cost, fill_value=0).add( + transmission_cost, fill_value=0 + ) + + demand = get_demand(result_data["DiscountedDemand"], country=False) pwr_cost = get_pwr_cost(demand, cost) pwr_cost.to_csv(power_cost_node_csv, index=True) @@ -175,29 +186,35 @@ def get_pwr_cost(demand: pd.DataFrame, cost: pd.DataFrame) -> pd.DataFrame: tech_cost_country = get_tech_cost(discounted_cost_by_technology, country=True) storage_cost_country = get_storage_cost(discounted_cost_by_storage, country=True) - transmission_cost_country = get_transmission_cost(discounted_cost_by_technology, country=True) - - cost_country = tech_cost_country.add(storage_cost_country, fill_value = 0 - ).add(transmission_cost_country, fill_value = 0) - - demand_country = get_demand(demand_raw, country=True) + transmission_cost_country = get_transmission_cost( + discounted_cost_by_technology, country=True + ) + + cost_country = tech_cost_country.add(storage_cost_country, fill_value=0).add( + transmission_cost_country, fill_value=0 + ) + + demand_country = get_demand(result_data["DiscountedDemand"], country=True) pwr_cost_country = get_pwr_cost(demand_country, cost_country) pwr_cost_country.to_csv(power_cost_country_csv, index=True) cost_country.to_csv(total_cost_country_csv, index=True) - + # global level metrics - - cost_global = tech_cost_country.add(storage_cost_country, - fill_value = 0 - ).add(transmission_cost_country, fill_value = 0 - ).groupby(["YEAR"]).sum() - - demand_global = get_demand(demand_raw, country=True) - demand_global = demand_global.groupby([ - demand_global.index.get_level_values("YEAR")]).sum() + + cost_global = ( + tech_cost_country.add(storage_cost_country, fill_value=0) + .add(transmission_cost_country, fill_value=0) + .groupby(["YEAR"]) + .sum() + ) + + demand_global = get_demand(result_data["DiscountedDemand"], country=True) + demand_global = demand_global.groupby( + [demand_global.index.get_level_values("YEAR")] + ).sum() pwr_cost_global = get_pwr_cost(demand_global, cost_global) pwr_cost_global.to_csv(power_cost_global_csv, index=True) - cost_global.to_csv(total_cost_global_csv, index=True) \ No newline at end of file + cost_global.to_csv(total_cost_global_csv, index=True) diff --git a/workflow/scripts/osemosys_global/summary/headline.py b/workflow/scripts/osemosys_global/summary/headline.py index 3608af0..05eb680 100644 --- a/workflow/scripts/osemosys_global/summary/headline.py +++ b/workflow/scripts/osemosys_global/summary/headline.py @@ -4,8 +4,7 @@ from typing import Optional from constants import RENEWABLES, FOSSIL, CLEAN from otoole import read -from utils import get_discount_factor -from pathlib import Path +from utils import get_discount_factor, _read_result_data def get_emissions(annual_emissions: pd.DataFrame) -> pd.DataFrame: @@ -108,20 +107,6 @@ def get_gen_shares( return pd.DataFrame(data, columns=["Metric", "Unit", "Value"]) -def _read_result_data(result_dir: str) -> dict[str, pd.DataFrame]: - """Reads in result CSVs - - Issue with otoole config - """ - data = {} - files = [Path(x) for x in Path(result_dir).iterdir()] - for f in files: - df = pd.read_csv(f) - df = df.set_index(df.columns[:-1].tolist()) - data[f.stem] = df - return data - - if __name__ == "__main__": if "snakemake" in globals(): storage = snakemake.params.storage diff --git a/workflow/scripts/osemosys_global/summary/utils.py b/workflow/scripts/osemosys_global/summary/utils.py index b4d5cab..f76069e 100644 --- a/workflow/scripts/osemosys_global/summary/utils.py +++ b/workflow/scripts/osemosys_global/summary/utils.py @@ -1,4 +1,5 @@ import pandas as pd +from pathlib import Path def get_discount_factor( @@ -36,3 +37,17 @@ def get_discount_factor( return discount_factor.reset_index()[["REGION", "YEAR", "VALUE"]].set_index( ["REGION", "YEAR"] ) + + +def _read_result_data(result_dir: str) -> dict[str, pd.DataFrame]: + """Reads in result CSVs + + Issue with otoole config + """ + data = {} + files = [Path(x) for x in Path(result_dir).iterdir()] + for f in files: + df = pd.read_csv(f) + df = df.set_index(df.columns[:-1].tolist()) + data[f.stem] = df + return data diff --git a/workflow/scripts/osemosys_global/utils.py b/workflow/scripts/osemosys_global/utils.py index f54adf2..f81e9fd 100644 --- a/workflow/scripts/osemosys_global/utils.py +++ b/workflow/scripts/osemosys_global/utils.py @@ -2,7 +2,7 @@ import pandas as pd from typing import Optional -from .constants import SET_DTYPES +from constants import SET_DTYPES import logging