diff --git a/message_ix_models/tools/costs/config.py b/message_ix_models/tools/costs/config.py index 47eb0d9f4d..2025e0c114 100644 --- a/message_ix_models/tools/costs/config.py +++ b/message_ix_models/tools/costs/config.py @@ -20,6 +20,8 @@ class Config: """Configuration for :mod:`.costs`.""" + test_val: int = 2 + #: Base year for projections. base_year: int = BASE_YEAR diff --git a/message_ix_models/tools/costs/demo.py b/message_ix_models/tools/costs/demo.py index 1f8c4d9140..15b18240bb 100644 --- a/message_ix_models/tools/costs/demo.py +++ b/message_ix_models/tools/costs/demo.py @@ -1,43 +1,88 @@ +from message_ix_models.tools.costs.config import Config from message_ix_models.tools.costs.projections import create_cost_projections -# By default, the create_cost_projections() function will run for R12 +# Example 1: By default, the Config fill will run for: +# R12 # for the base suite of technologies, # with NAM as reference region, # using GDP as the cost driver, -# and the updated data version. +# and the updated data version +# and outputs in MESSAGE format. # The function will also run for all SSP scenarios, # for all years from 2021 to 2100. -inv, fix = create_cost_projections() +default = Config() +out_default = create_cost_projections( + node=default.node, + ref_region=default.ref_region, + base_year=default.base_year, + module=default.module, + method=default.method, + scenario_version=default.scenario_version, + scenario=default.scenario, + convergence_year=default.convergence_year, + fom_rate=default.fom_rate, + format=default.format, +) -# Example 1: Get cost projections for SSP2 scenario in R12, +# Example 2: Get cost projections for SSP2 scenario in R12, +# using WEU as the reference region, +# with convergence as the method, # for materials technologies, # using GDP (updated data) -inv2, fix2 = create_cost_projections( - sel_node="r12", - sel_ref_region="R12_NAM", - sel_base_year=2021, - sel_module="materials", - sel_scenario_version="updated", - sel_scenario="ssp2", - sel_method="gdp", +# You can either put the inputs directly into the create_cost_projections function, +# or you can create a Config object and pass that in. +default = Config() + +# Option 1: Directly input the parameters +out_materials_ssp2 = create_cost_projections( + node=default.node, + ref_region="R12_WEU", + base_year=default.base_year, + module="materials", + method="convergence", + scenario_version=default.scenario_version, + scenario="SSP2", + convergence_year=default.convergence_year, + fom_rate=default.fom_rate, + format=default.format, ) -# Example 2: Get cost projections in R11 (with WEU as reference region), using learning -# (this will run for all SSP scenarios) -inv, fix = create_cost_projections( - sel_node="r11", - sel_ref_region="R11_WEU", - sel_base_year=2021, - sel_method="learning", - sel_scenario_version="updated", +# Option 2: Create a Config object and pass that in +config = Config( + module="materials", scenario="SSP2", ref_region="R12_WEU", method="convergence" ) -# Example 3: Get cost projections in R12, using convergence -inv, fix = create_cost_projections( - sel_node="r12", - sel_base_year=2021, - sel_method="convergence", +out_materials_ssp2 = create_cost_projections( + node=config.node, + ref_region=config.ref_region, + base_year=config.base_year, + module=config.module, + method=config.method, + scenario_version=config.scenario_version, + scenario=config.scenario, + convergence_year=config.convergence_year, + fom_rate=config.fom_rate, + format=config.format, ) -# Example 4: Get cost projections in R11 using previous/original SSP scenarios -inv, fix = create_cost_projections(sel_node="r11", sel_scenario_version="original") +# Example 3: Get cost projections for SSP5 scenario in R12, +# using LAM as the reference region, +# with learning as the method, +# for materials technologies, + +config = Config( + module="materials", scenario="SSP5", ref_region="R12_LAM", method="learning" +) + +out_materials_ssp5 = create_cost_projections( + node=config.node, + ref_region=config.ref_region, + base_year=config.base_year, + module=config.module, + method=config.method, + scenario_version=config.scenario_version, + scenario=config.scenario, + convergence_year=config.convergence_year, + fom_rate=config.fom_rate, + format=config.format, +) diff --git a/message_ix_models/tools/costs/gdp.py b/message_ix_models/tools/costs/gdp.py index 1a74d96a53..120178480a 100644 --- a/message_ix_models/tools/costs/gdp.py +++ b/message_ix_models/tools/costs/gdp.py @@ -8,7 +8,7 @@ # Function to read in (under-review) SSP data -def process_raw_ssp_data(input_node, input_ref_region) -> pd.DataFrame: +def process_raw_ssp_data(node, ref_region) -> pd.DataFrame: """Read in raw SSP data and process it This function takes in the raw SSP data (in IAMC format), aggregates \ @@ -38,22 +38,22 @@ def process_raw_ssp_data(input_node, input_ref_region) -> pd.DataFrame: (in units of billion US$2005/yr / million) """ # Change node selection to upper case - node_up = input_node.upper() + node_up = node.upper() # Check if node selection is valid if node_up not in ["R11", "R12", "R20"]: print("Please select a valid region: R11, R12, or R20") # Set default reference region - if input_ref_region is None: - if input_node.upper() == "R11": - input_ref_region = "R11_NAM" - if input_node.upper() == "R12": - input_ref_region = "R12_NAM" - if input_node.upper() == "R20": - input_ref_region = "R20_NAM" + if ref_region is None: + if node.upper() == "R11": + ref_region = "R11_NAM" + if node.upper() == "R12": + ref_region = "R12_NAM" + if node.upper() == "R20": + ref_region = "R20_NAM" else: - input_ref_region = input_ref_region + ref_region = ref_region # Set data path for node file node_file = package_data_path("node", node_up + ".yaml") @@ -162,7 +162,7 @@ def process_raw_ssp_data(input_node, input_ref_region) -> pd.DataFrame: ) # If reference region is not in the list of regions, print error message - reference_region = input_ref_region.upper() + reference_region = ref_region.upper() if reference_region not in df.region.unique(): print("Please select a valid reference region: " + str(df.region.unique())) # If reference region is in the list of regions, calculate GDP ratios @@ -212,10 +212,10 @@ def process_raw_ssp_data(input_node, input_ref_region) -> pd.DataFrame: # Function to calculate adjusted region-differentiated cost ratios def calculate_indiv_adjusted_region_cost_ratios( - region_diff_df, input_node, input_ref_region, input_base_year + region_diff_df, node, ref_region, base_year ): df_gdp = ( - process_raw_ssp_data(input_node=input_node, input_ref_region=input_ref_region) + process_raw_ssp_data(node=node, ref_region=ref_region) .query("year >= 2020") .drop(columns=["total_gdp", "total_population"]) ) @@ -223,11 +223,11 @@ def calculate_indiv_adjusted_region_cost_ratios( # If base year does not exist in GDP data, then use earliest year in GDP data # and give warning - base_year = int(input_base_year) + base_year = int(base_year) if int(base_year) not in df_gdp.year.unique(): base_year = int(min(df_gdp.year.unique())) print( - f"Base year {input_base_year} not found in GDP data. \ + f"Base year {base_year} not found in GDP data. \ Using {base_year} for GDP data instead." ) @@ -236,15 +236,15 @@ def calculate_indiv_adjusted_region_cost_ratios( # If specified node is R12, then use R12_NAM as the reference region # If specified node is R20, then use R20_NAM as the reference region # However, if a reference region is specified, then use that instead - if input_ref_region is None: - if input_node.upper() == "R11": + if ref_region is None: + if node.upper() == "R11": reference_region = "R11_NAM" - if input_node.upper() == "R12": + if node.upper() == "R12": reference_region = "R12_NAM" - if input_node.upper() == "R20": + if node.upper() == "R20": reference_region = "R20_NAM" else: - reference_region = input_ref_region + reference_region = ref_region gdp_base_year = df_gdp.query("year == @base_year").reindex( ["scenario_version", "scenario", "region", "gdp_ratio_reg_to_reference"], axis=1 diff --git a/message_ix_models/tools/costs/learning.py b/message_ix_models/tools/costs/learning.py index f24c00c41d..ef2265ce2a 100644 --- a/message_ix_models/tools/costs/learning.py +++ b/message_ix_models/tools/costs/learning.py @@ -11,7 +11,7 @@ # Function to get GEA based cost reduction data -def get_cost_reduction_data(input_module) -> pd.DataFrame: +def get_cost_reduction_data(module) -> pd.DataFrame: """Get cost reduction data Raw data on cost reduction in 2100 for technologies are read from \ @@ -45,10 +45,10 @@ def get_cost_reduction_data(input_module) -> pd.DataFrame: .reset_index(drop=1) ) - if input_module == "base": + if module == "base": return base_rates - elif input_module == "materials": + elif module == "materials": # Read in materials technology mapping file materials_file_path = package_data_path("costs", "technology_materials_map.csv") df_materials_tech = pd.read_csv(materials_file_path) @@ -78,9 +78,7 @@ def get_cost_reduction_data(input_module) -> pd.DataFrame: # Function to get technology learning scenarios data -def get_technology_learning_scenarios_data( - input_base_year, input_module -) -> pd.DataFrame: +def get_technology_learning_scenarios_data(base_year, module) -> pd.DataFrame: """Read in technology first year and learning scenarios data Raw data on technology first year and learning scenarios are read from \ @@ -90,7 +88,7 @@ def get_technology_learning_scenarios_data( Parameters ---------- - input_base_year : int, optional + base_year : int, optional The base year, by default set to global BASE_YEAR Returns @@ -109,9 +107,9 @@ def get_technology_learning_scenarios_data( pd.read_csv(file) .assign( first_technology_year=lambda x: np.where( - x.first_year_original > input_base_year, + x.first_year_original > base_year, x.first_year_original, - input_base_year, + base_year, ), ) .drop(columns=["first_year_original"]) @@ -122,10 +120,10 @@ def get_technology_learning_scenarios_data( ) ) - if input_module == "base": + if module == "base": return base_learn - elif input_module == "materials": + elif module == "materials": # Read in materials technology mapping file materials_file_path = package_data_path("costs", "technology_materials_map.csv") df_materials_tech = pd.read_csv(materials_file_path) @@ -157,10 +155,10 @@ def get_technology_learning_scenarios_data( # Function to project reference region investment cost using learning rates def project_ref_region_inv_costs_using_learning_rates( regional_diff_df: pd.DataFrame, - input_node, - input_ref_region, - input_base_year, - input_module, + node, + ref_region, + base_year, + module, ) -> pd.DataFrame: """Project investment costs using learning rates for reference region @@ -172,11 +170,11 @@ def project_ref_region_inv_costs_using_learning_rates( ---------- regional_diff_df : pandas.DataFrame Dataframe output from :func:`get_weo_region_differentiated_costs` - input_node : str, optional + node : str, optional The reference node, by default "r12" - input_ref_region : str, optional + ref_region : str, optional The reference region, by default None (defaults set in function) - input_base_year : int, optional + base_year : int, optional The base year, by default set to global BASE_YEAR Returns @@ -191,21 +189,21 @@ def project_ref_region_inv_costs_using_learning_rates( """ # Set default reference region - if input_ref_region is None: - if input_node.upper() == "R11": + if ref_region is None: + if node.upper() == "R11": reference_region = "R11_NAM" - if input_node.upper() == "R12": + if node.upper() == "R12": reference_region = "R12_NAM" - if input_node.upper() == "R20": + if node.upper() == "R20": reference_region = "R20_NAM" else: - reference_region = input_ref_region + reference_region = ref_region # Get cost reduction data - df_cost_reduction = get_cost_reduction_data(input_module) + df_cost_reduction = get_cost_reduction_data(module) # Get learning rates data - df_learning = get_technology_learning_scenarios_data(input_base_year, input_module) + df_learning = get_technology_learning_scenarios_data(base_year, module) # Merge cost reduction data with learning rates data df_learning_reduction = df_learning.merge( @@ -221,7 +219,7 @@ def project_ref_region_inv_costs_using_learning_rates( cost_region_2100=lambda x: x.reg_cost_base_year - (x.reg_cost_base_year * x.cost_reduction), b=lambda x: (1 - PRE_LAST_YEAR_RATE) * x.cost_region_2100, - r=lambda x: (1 / (LAST_MODEL_YEAR - input_base_year)) + r=lambda x: (1 / (LAST_MODEL_YEAR - base_year)) * np.log((x.cost_region_2100 - x.b) / (x.reg_cost_base_year - x.b)), reference_region=reference_region, ) diff --git a/message_ix_models/tools/costs/projections.py b/message_ix_models/tools/costs/projections.py index 6072c4981f..e0d3c29012 100644 --- a/message_ix_models/tools/costs/projections.py +++ b/message_ix_models/tools/costs/projections.py @@ -47,30 +47,30 @@ def create_projections_learning( # If it specified, then filter as below: if in_scenario is not None: if in_scenario == "all": - sel_scen = ["SSP1", "SSP2", "SSP3", "SSP4", "SSP5", "LED"] + scen = ["SSP1", "SSP2", "SSP3", "SSP4", "SSP5", "LED"] else: - sel_scen = in_scenario.upper() + scen = in_scenario.upper() # Repeating to avoid linting error - sel_scen = sel_scen + scen = scen df_region_diff = get_weo_region_differentiated_costs( - input_node=in_node, - input_ref_region=in_ref_region, - input_base_year=in_base_year, - input_module=in_module, + node=in_node, + ref_region=in_ref_region, + base_year=in_base_year, + module=in_module, ) df_ref_reg_learning = project_ref_region_inv_costs_using_learning_rates( df_region_diff, - input_node=in_node, - input_ref_region=in_ref_region, - input_base_year=in_base_year, - input_module=in_module, + node=in_node, + ref_region=in_ref_region, + base_year=in_base_year, + module=in_module, ) if in_scenario is not None: - df_ref_reg_learning = df_ref_reg_learning.query("scenario == @sel_scen") + df_ref_reg_learning = df_ref_reg_learning.query("scenario == @scen") df_costs = ( df_region_diff.merge(df_ref_reg_learning, on="message_technology") @@ -81,9 +81,11 @@ def create_projections_learning( x.inv_cost_ref_region_learning * x.reg_cost_ratio, ), fix_cost=lambda x: x.inv_cost * x.fix_to_inv_cost_ratio, + scenario_version="Not applicable", ) .reindex( [ + "scenario_version", "scenario", "message_technology", "region", @@ -109,50 +111,50 @@ def create_projections_gdp( # If it specified, then filter as below: if in_scenario is not None: if in_scenario == "all": - sel_scen = ["SSP1", "SSP2", "SSP3", "SSP4", "SSP5", "LED"] + scen = ["SSP1", "SSP2", "SSP3", "SSP4", "SSP5", "LED"] else: - sel_scen = in_scenario.upper() + scen = in_scenario.upper() # If no scenario version is specified, do not filter for scenario version # If it specified, then filter as below: if in_scenario_version is not None: if in_scenario_version == "all": - sel_scen_vers = ["Review (2023)", "Previous (2013)"] + scen_vers = ["Review (2023)", "Previous (2013)"] elif in_scenario_version == "updated": - sel_scen_vers = ["Review (2023)"] + scen_vers = ["Review (2023)"] elif in_scenario_version == "original": - sel_scen_vers = ["Previous (2013)"] + scen_vers = ["Previous (2013)"] # Repeating to avoid linting error - sel_scen = sel_scen - sel_scen_vers = sel_scen_vers + scen = scen + scen_vers = scen_vers df_region_diff = get_weo_region_differentiated_costs( - input_node=in_node, - input_ref_region=in_ref_region, - input_base_year=in_base_year, - input_module=in_module, + node=in_node, + ref_region=in_ref_region, + base_year=in_base_year, + module=in_module, ) df_ref_reg_learning = project_ref_region_inv_costs_using_learning_rates( df_region_diff, - input_node=in_node, - input_ref_region=in_ref_region, - input_base_year=in_base_year, - input_module=in_module, + node=in_node, + ref_region=in_ref_region, + base_year=in_base_year, + module=in_module, ) df_adj_cost_ratios = calculate_indiv_adjusted_region_cost_ratios( df_region_diff, - input_node=in_node, - input_ref_region=in_ref_region, - input_base_year=in_base_year, + node=in_node, + ref_region=in_ref_region, + base_year=in_base_year, ) if in_scenario is not None: - df_ref_reg_learning = df_ref_reg_learning.query("scenario == @sel_scen") + df_ref_reg_learning = df_ref_reg_learning.query("scenario == @scen") df_adj_cost_ratios = df_adj_cost_ratios.query( - "scenario_version == @sel_scen_vers and scenario == @sel_scen" + "scenario_version == @scen_vers and scenario == @scen" ) df_costs = ( @@ -200,30 +202,30 @@ def create_projections_converge( # If it specified, then filter as below: if in_scenario is not None: if in_scenario == "all": - sel_scen = ["SSP1", "SSP2", "SSP3", "SSP4", "SSP5", "LED"] + scen = ["SSP1", "SSP2", "SSP3", "SSP4", "SSP5", "LED"] else: - sel_scen = in_scenario.upper() + scen = in_scenario.upper() # Repeating to avoid linting error - sel_scen = sel_scen + scen = scen df_region_diff = get_weo_region_differentiated_costs( - input_node=in_node, - input_ref_region=in_ref_region, - input_base_year=in_base_year, - input_module=in_module, + node=in_node, + ref_region=in_ref_region, + base_year=in_base_year, + module=in_module, ) df_ref_reg_learning = project_ref_region_inv_costs_using_learning_rates( df_region_diff, - input_node=in_node, - input_ref_region=in_ref_region, - input_base_year=in_base_year, - input_module=in_module, + node=in_node, + ref_region=in_ref_region, + base_year=in_base_year, + module=in_module, ) if in_scenario is not None: - df_ref_reg_learning = df_ref_reg_learning.query("scenario == @sel_scen") + df_ref_reg_learning = df_ref_reg_learning.query("scenario == @scen") df_pre_costs = df_region_diff.merge( df_ref_reg_learning, on="message_technology" @@ -242,7 +244,7 @@ def create_projections_converge( df_splines = apply_splines_to_convergence( df_pre_costs, column_name="inv_cost_converge", - input_convergence_year=in_convergence_year, + convergence_year=in_convergence_year, ) df_costs = ( @@ -252,9 +254,13 @@ def create_projections_converge( how="outer", ) .rename(columns={"inv_cost_splines": "inv_cost"}) - .assign(fix_cost=lambda x: x.inv_cost * x.fix_to_inv_cost_ratio) + .assign( + fix_cost=lambda x: x.inv_cost * x.fix_to_inv_cost_ratio, + scenario_version="Not applicable", + ) .reindex( [ + "scenario_version", "scenario", "message_technology", "region", @@ -269,12 +275,12 @@ def create_projections_converge( return df_costs -def create_message_outputs(input_df_projections: pd.DataFrame, fom_rate: float): +def create_message_outputs(df_projections: pd.DataFrame, fom_rate: float): """Create MESSAGEix outputs for investment and fixed costs. Parameters ---------- - input_df_projections : pd.DataFrame + df_projections : pd.DataFrame Dataframe containing the cost projections for each technology. \ Output of func:`create_cost_projections`. fom_rate : float @@ -292,10 +298,10 @@ def create_message_outputs(input_df_projections: pd.DataFrame, fom_rate: float): df_prod = pd.DataFrame( product( - input_df_projections.scenario_version.unique(), - input_df_projections.scenario.unique(), - input_df_projections.message_technology.unique(), - input_df_projections.region.unique(), + df_projections.scenario_version.unique(), + df_projections.scenario.unique(), + df_projections.message_technology.unique(), + df_projections.region.unique(), seq_years, ), columns=[ @@ -308,13 +314,13 @@ def create_message_outputs(input_df_projections: pd.DataFrame, fom_rate: float): ) val_2020 = ( - input_df_projections.query("year == 2020") + df_projections.query("year == 2020") .rename(columns={"inv_cost": "inv_cost_2020", "fix_cost": "fix_cost_2020"}) .drop(columns=["year"]) ) val_2100 = ( - input_df_projections.query("year == 2100") + df_projections.query("year == 2100") .drop(columns=["year"]) .rename(columns={"inv_cost": "inv_cost_2100", "fix_cost": "fix_cost_2100"}) ) @@ -330,7 +336,7 @@ def create_message_outputs(input_df_projections: pd.DataFrame, fom_rate: float): on=["scenario_version", "scenario", "message_technology", "region"], ) .merge( - input_df_projections, + df_projections, on=[ "scenario_version", "scenario", @@ -381,6 +387,15 @@ def create_message_outputs(input_df_projections: pd.DataFrame, fom_rate: float): ], axis=1, ) + .assign( + scenario_version=lambda x: x.scenario_version.astype("string"), + scenario=lambda x: x.scenario.astype("string"), + node_loc=lambda x: x.node_loc.astype("string"), + technology=lambda x: x.technology.astype("string"), + unit=lambda x: x.unit.astype("string"), + year_vtg=lambda x: x.year_vtg.astype(int), + value=lambda x: x.value.astype(float), + ) .query("year_vtg <= 2060 or year_vtg % 10 == 0") .reset_index(drop=True) ) @@ -411,8 +426,6 @@ def create_message_outputs(input_df_projections: pd.DataFrame, fom_rate: float): "region": "node_loc", } ) - .query("year_vtg <= 2060 or year_vtg % 10 == 0") - .query("year_act <= 2060 or year_act % 10 == 0") .reindex( [ "scenario_version", @@ -426,21 +439,32 @@ def create_message_outputs(input_df_projections: pd.DataFrame, fom_rate: float): ], axis=1, ) + .assign( + scenario_version=lambda x: x.scenario_version.astype("string"), + scenario=lambda x: x.scenario.astype("string"), + node_loc=lambda x: x.node_loc.astype("string"), + technology=lambda x: x.technology.astype("string"), + unit=lambda x: x.unit.astype("string"), + year_vtg=lambda x: x.year_vtg.astype(int), + year_act=lambda x: x.year_vtg.astype(int), + value=lambda x: x.value.astype(float), + ) + .query("year_vtg <= 2060 or year_vtg % 10 == 0") .reset_index(drop=True) ) return inv, fom -def create_iamc_outputs(input_msg_inv: pd.DataFrame, input_msg_fix: pd.DataFrame): +def create_iamc_outputs(msg_inv: pd.DataFrame, msg_fix: pd.DataFrame): """Create IAMC outputs for investment and fixed costs. Parameters ---------- - input_msg_inv : pd.DataFrame + msg_inv : pd.DataFrame Dataframe containing investment costs in MESSAGEix format. \ Output of func:`create_message_outputs`. - input_msg_fix : pd.DataFrame + msg_fix : pd.DataFrame Dataframe containing fixed operating and maintenance costs in MESSAGEix \ format. Output of func:`create_message_outputs`. @@ -453,7 +477,7 @@ def create_iamc_outputs(input_msg_inv: pd.DataFrame, input_msg_fix: pd.DataFrame """ iamc_inv = ( ( - input_msg_inv.assign( + msg_inv.assign( Variable=lambda x: "Capital Cost|Electricity|" + x.technology, ) .rename( @@ -484,7 +508,7 @@ def create_iamc_outputs(input_msg_inv: pd.DataFrame, input_msg_fix: pd.DataFrame iamc_fix = ( ( - input_msg_fix.assign( + msg_fix.assign( Variable=lambda x: "OM Cost|Electricity|" + x.technology + "|Vintage=" @@ -520,52 +544,53 @@ def create_iamc_outputs(input_msg_inv: pd.DataFrame, input_msg_fix: pd.DataFrame def create_cost_projections( - sel_node: str = "r12", - sel_ref_region=None, - sel_base_year: int = BASE_YEAR, - sel_module: str = "base", - sel_method: str = "gdp", - sel_scenario_version="updated", - sel_scenario="all", - sel_convergence_year: int = 2050, - sel_fom_rate: float = 0.025, - sel_format: str = "message", + node, + ref_region, + base_year, + module, + method, + scenario_version, + scenario, + convergence_year, + fom_rate, + format, ): """Get investment and fixed cost projections Parameters ---------- - sel_node : str, optional + node : str, optional Spatial resolution, by default "r12". Options are "r11", "r12", and "r20" - sel_ref_region : str, optional + ref_region : str, optional Reference region, by default R12_NAM for R12, R11_NAM for R11, and \ R20_NAM for R20 - sel_base_year : int, optional + base_year : int, optional Base year, by default BASE_YEAR specified in the config file - sel_module : str, optional + module : str, optional Module to use, by default "base". Options are "base" and "materials" - sel_method : str, optional + method : str, optional Method to use, by default "gdp". Options are "learning", "gdp", \ and "convergence" - sel_scenario_version : str, optional + scenario_version : str, optional Scenario version, by default "updated". Options are "updated" and "original" - sel_scenario : str, optional + scenario : str, optional Scenario, by default "all" - sel_convergence_year : int, optional + convergence_year : int, optional Year to converge costs to, by default 2050 - sel_fom_rate : float, optional + fom_rate : float, optional Rate of increase/decrease of fixed operating and maintenance costs, \ by default 0.025 - sel_format : str, optional + format : str, optional Format of output, by default "message". Options are "message" and "iamc" Returns ------- - pandas.DataFrame - Dataframe containing cost projections + projections + Object containing investment and fixed cost projections + """ # Change node selection to upper case - node_up = sel_node.upper() + node_up = node.upper() # Check if node selection is valid if node_up not in ["R11", "R12", "R20"]: @@ -576,67 +601,67 @@ def create_cost_projections( # If specified node is R12, then use R12_NAM as the reference region # If specified node is R20, then use R20_NAM as the reference region # However, if a reference region is specified, then use that instead - if sel_ref_region is None: + if ref_region is None: if node_up == "R11": - sel_ref_region = "R11_NAM" + ref_region = "R11_NAM" if node_up == "R12": - sel_ref_region = "R12_NAM" + ref_region = "R12_NAM" if node_up == "R20": - sel_ref_region = "R20_NAM" - elif sel_ref_region is not None: - sel_ref_region = sel_ref_region.upper() + ref_region = "R20_NAM" + elif ref_region is not None: + ref_region = ref_region.upper() # Print final selection of regions, reference regions, and base year print("Selected node: " + node_up) - print("Selected reference region: " + sel_ref_region) - print("Selected base year: " + str(sel_base_year)) - print("Selected module: " + sel_module) + print("Selected reference region: " + ref_region) + print("Selected base year: " + str(base_year)) + print("Selected module: " + module) - print("Selected method: " + sel_method) + print("Selected method: " + method) # If method is learning, then use the learning method - if sel_method == "learning": + if method == "learning": df_costs = create_projections_learning( in_node=node_up, - in_ref_region=sel_ref_region, - in_base_year=sel_base_year, - in_module=sel_module, - in_scenario=sel_scenario, + in_ref_region=ref_region, + in_base_year=base_year, + in_module=module, + in_scenario=scenario, ) # If method is GDP, then use the GDP method - if sel_method == "gdp": + if method == "gdp": df_costs = create_projections_gdp( in_node=node_up, - in_ref_region=sel_ref_region, - in_base_year=sel_base_year, - in_module=sel_module, - in_scenario=sel_scenario, - in_scenario_version=sel_scenario_version, + in_ref_region=ref_region, + in_base_year=base_year, + in_module=module, + in_scenario=scenario, + in_scenario_version=scenario_version, ) # If method is convergence, then use the convergence method - if sel_method == "convergence": + if method == "convergence": df_costs = create_projections_converge( in_node=node_up, - in_ref_region=sel_ref_region, - in_base_year=sel_base_year, - in_module=sel_module, - in_scenario=sel_scenario, - in_convergence_year=sel_convergence_year, + in_ref_region=ref_region, + in_base_year=base_year, + in_module=module, + in_scenario=scenario, + in_convergence_year=convergence_year, ) - print("Selected fixed O&M rate: " + str(sel_fom_rate)) - print("Selected format: " + sel_format) + print("Selected fixed O&M rate: " + str(fom_rate)) + print("Selected format: " + format) - if sel_format == "message": - df_inv, df_fom = create_message_outputs(df_costs, fom_rate=sel_fom_rate) + if format == "message": + df_inv, df_fom = create_message_outputs(df_costs, fom_rate=fom_rate) proj = projections(df_inv, df_fom) return proj - if sel_format == "iamc": - df_inv, df_fom = create_message_outputs(df_costs, fom_rate=sel_fom_rate) + if format == "iamc": + df_inv, df_fom = create_message_outputs(df_costs, fom_rate=fom_rate) df_inv_iamc, df_fom_iamc = create_iamc_outputs(df_inv, df_fom) proj = projections(df_inv_iamc, df_fom_iamc) diff --git a/message_ix_models/tools/costs/splines.py b/message_ix_models/tools/costs/splines.py index cb831d41c6..b6cb29794a 100644 --- a/message_ix_models/tools/costs/splines.py +++ b/message_ix_models/tools/costs/splines.py @@ -14,23 +14,23 @@ # Function to apply polynomial regression to convergence costs def apply_splines_to_convergence( - input_df: pd.DataFrame, + df_reg, column_name, - input_convergence_year, + convergence_year, ): """Apply polynomial regression and splines to convergence""" - # un_vers = input_df.scenario_version.unique() - un_ssp = input_df.scenario.unique() - un_tech = input_df.message_technology.unique() - un_reg = input_df.region.unique() + # un_vers = df.scenario_version.unique() + un_ssp = df_reg.scenario.unique() + un_tech = df_reg.message_technology.unique() + un_reg = df_reg.region.unique() data_reg = [] for i, j, k in product(un_ssp, un_tech, un_reg): - tech = input_df.query( + tech = df_reg.query( "scenario == @i and message_technology == @j \ and region == @k" - ).query("year == @FIRST_MODEL_YEAR or year >= @input_convergence_year") + ).query("year == @FIRST_MODEL_YEAR or year >= @convergence_year") if tech.size == 0: continue @@ -74,7 +74,7 @@ def apply_splines_to_convergence( df_reg = pd.concat(data_reg).reset_index(drop=1) df_wide = ( - input_df.reindex( + df.reindex( [ "scenario", "message_technology", diff --git a/message_ix_models/tools/costs/weo.py b/message_ix_models/tools/costs/weo.py index 14c6a69788..3774e794bc 100644 --- a/message_ix_models/tools/costs/weo.py +++ b/message_ix_models/tools/costs/weo.py @@ -190,7 +190,7 @@ def get_weo_data() -> pd.DataFrame: # Function to read in technology mapping file -def get_technology_mapping(input_module) -> pd.DataFrame: +def get_technology_mapping(module) -> pd.DataFrame: """Read in technology mapping file Returns @@ -207,10 +207,10 @@ def get_technology_mapping(input_module) -> pd.DataFrame: base_file_path = package_data_path("costs", "technology_base_map.csv") raw_map_base = pd.read_csv(base_file_path, skiprows=2) - if input_module == "base": + if module == "base": return raw_map_base - if input_module == "materials": + if module == "materials": materials_file_path = package_data_path("costs", "technology_materials_map.csv") # Read in materials mapping and do following processing: @@ -312,18 +312,18 @@ def get_technology_mapping(input_module) -> pd.DataFrame: # Function to get WEO-based regional differentiation def get_weo_region_differentiated_costs( - input_node, input_ref_region, input_base_year, input_module + node, ref_region, base_year, module ) -> pd.DataFrame: """Calculate regionally differentiated costs and fixed-to-investment cost ratios Parameters ---------- - input_node : str, optional + node : str, optional MESSAGEix node, by default "r12" - input_ref_region : str, optional + ref_region : str, optional Reference region, by default "r12_nam" - input_base_year : int, optional + base_year : int, optional Base year, by default BASE_YEAR Returns @@ -342,36 +342,36 @@ def get_weo_region_differentiated_costs( # If specified node is R12, then use R12_NAM as the reference region # If specified node is R20, then use R20_NAM as the reference region # However, if a reference region is specified, then use that instead - if input_ref_region is None: - if input_node.upper() == "R11": - input_ref_region = "R11_NAM" - if input_node.upper() == "R12": - input_ref_region = "R12_NAM" - if input_node.upper() == "R20": - input_ref_region = "R20_NAM" + if ref_region is None: + if node.upper() == "R11": + ref_region = "R11_NAM" + if node.upper() == "R12": + ref_region = "R12_NAM" + if node.upper() == "R20": + ref_region = "R20_NAM" else: - input_ref_region = input_ref_region + ref_region = ref_region - if input_node.upper() == "R11": + if node.upper() == "R11": dict_regions = DICT_WEO_R11 - if input_node.upper() == "R12": + if node.upper() == "R12": dict_regions = DICT_WEO_R12 - if input_node.upper() == "R20": + if node.upper() == "R20": dict_regions = DICT_WEO_R20 # Grab WEO data and keep only investment costs df_weo = get_weo_data() # Grab technology mapping data - df_tech_map = get_technology_mapping(input_module) + df_tech_map = get_technology_mapping(module) # If base year does not exist in WEO data, then use earliest year and give # warning - base_year = str(input_base_year) + base_year = str(base_year) if base_year not in df_weo.year.unique(): base_year = str(min(df_weo.year.unique())) print( - f"Base year {input_base_year} not found in WEO data. \ + f"Base year {base_year} not found in WEO data. \ Using {base_year} instead." ) @@ -401,7 +401,7 @@ def get_weo_region_differentiated_costs( df_sel_weo = pd.concat(l_sel_weo) # If specified reference region is not in WEO data, then give error - ref_region = input_ref_region.upper() + ref_region = ref_region.upper() if ref_region not in df_sel_weo.region.unique(): raise ValueError( f"Reference region {ref_region} not found in WEO data. \