From ffbfdeb8b4cc3166eb95fbae139aa03c9677db85 Mon Sep 17 00:00:00 2001 From: adrivinca Date: Tue, 22 Oct 2024 11:30:12 +0200 Subject: [PATCH 1/9] Auto-adjusting cooling technology constraints move from hard-coded contraints to automatically set, to avoid infeasibilities --- .../model/water/data/water_for_ppl.py | 145 +++++++++++++----- 1 file changed, 110 insertions(+), 35 deletions(-) diff --git a/message_ix_models/model/water/data/water_for_ppl.py b/message_ix_models/model/water/data/water_for_ppl.py index 0664d4016..a5d412fc6 100644 --- a/message_ix_models/model/water/data/water_for_ppl.py +++ b/message_ix_models/model/water/data/water_for_ppl.py @@ -26,16 +26,22 @@ def missing_tech(x: pd.Series) -> pd.Series: data_dic = { "geo_hpl": 1 / 0.850, "geo_ppl": 1 / 0.385, + "gas_hpl": 1 / 0.3, + "foil_hpl": 1 / 0.25, "nuc_hc": 1 / 0.326, "nuc_lc": 1 / 0.326, "solar_th_ppl": 1 / 0.385, } - if data_dic.get(x["technology"]): + if pd.notna(x["technology"]) and x["technology"] in data_dic: + value = data_dic.get(x["technology"]) + if x["value"] < 1: + value = max(x["value"], value) + # for backwards compatibility if x["level"] == "cooling": - return pd.Series((data_dic.get(x["technology"]), "dummy_supply")) + return pd.Series({"value": value, "level": "dummy_supply"}) else: - return pd.Series((data_dic.get(x["technology"]), x["level"])) + return pd.Series({"value": value, "level": x["level"]}) else: return pd.Series({"value": x["value"], "level": x["level"]}) @@ -171,6 +177,87 @@ def hist_cap(x: pd.Series, context: "Context", hold_cost: pd.DataFrame) -> list: ] +def relax_growth_constraint(ref_hist, scen, cooling_df, g_lo, constraint_type): + """ + Function to check if the parent technologies are shut down and require relaxing the growth constraint. + + Parameters: + ref_hist (pd.DataFrame): Historical data. + scen (Scenario): Scenario object to retrieve parameters. + cooling_df (pd.DataFrame): DataFrame containing cooling technologies and their parent technologies. + g_lo (pd.DataFrame): DataFrame containing growth constraints. + constraint_type (str): Type of constraint to check ("activity" or "capacity"). + + Returns: + pd.DataFrame: Updated g_lo DataFrame with relaxed growth constraints. + """ + if constraint_type == "activity": + year_type = "year_act" + bound_param = "bound_activity_up" + elif constraint_type == "new_capacity": + year_type = "year_vtg" + bound_param = "bound_new_capacity_up" + else: + raise ValueError( + "Invalid constraint_type. Must be 'activity' or 'new_capacity'." + ) + + # Group by all variables of ref_hist apart from year_type and hist_value_col and only keep rows with max year_type + max_year_hist = ( + ref_hist.loc[ref_hist.groupby(["node_loc", "technology"])[year_type].idxmax()] + .drop(columns="unit") + .rename(columns={year_type: "hist_year", "value": "hist_value"}) + ) + + # Step 2: Check for bound_activity_up or bound_new_capacity_up conditions + bound_up_pare = scen.par(bound_param, {"technology": cooling_df["parent_tech"]}) + # Get a set with unique year_type values and order them + years = np.sort(bound_up_pare[year_type].unique()) + + # In max_year_hist add the next year from years corresponding to the hist_year columns + max_year_hist["next_year"] = max_year_hist["hist_year"].apply( + lambda x: years[years > x][0] + ) + + # Merge the max_year_hist with bound_up_pare + bound_up = pd.merge(bound_up_pare, max_year_hist, how="left") + # Look at years just after the historical year + bound_up1 = bound_up[bound_up[year_type] == bound_up["next_year"]] + # Categories that might break the growth constraints + bound_up1 = bound_up1[bound_up1["value"] < 0.9 * bound_up1["hist_value"]] + # not look ad sudden contraints after sthe starting year + bound_up = bound_up.sort_values(by=["node_loc", "technology", year_type]) + # Check if value for a year is greater than the value of the next year (after sorting) + bound_up["next_value"] = bound_up.groupby(["node_loc", "technology"])[ + "value" + ].shift(-1) + bound_up2 = bound_up[bound_up["value"] > 0.9 * bound_up["next_value"]] + bound_up2 = bound_up2.drop(columns=["next_value"]) + # combine bound 1 and 2 + combined_bound = ( + pd.concat([bound_up1, bound_up2]).drop_duplicates().reset_index(drop=True) + ) + # Keep only node_loc, technology, and year_type + combined_bound = combined_bound[["node_loc", "technology", year_type]] + # Add columns with value "remove" to be able to use make_matched_dfs + combined_bound["rem"] = "remove" + combined_bound.rename(columns={"technology": "parent_tech"}, inplace=True) + + # map_par tec to parent tec + map_parent = cooling_df[["technology_name", "parent_tech"]] + map_parent.rename(columns={"technology_name": "technology"}, inplace=True) + # expand bound_up to all cooling technologies in map_parent + combined_bound = pd.merge(combined_bound, map_parent, how="left") + + # Merge to g_lo to be able to remove the technologies + g_lo = pd.merge(g_lo, combined_bound, how="left") + g_lo = g_lo[g_lo["rem"] != "remove"] + # Remove column rem and parent_tech + g_lo = g_lo.drop(columns=["rem", "parent_tech"]) + + return g_lo + + # water & electricity for cooling technologies @minimum_version("message_ix 3.7") def cool_tech(context: "Context") -> dict[str, pd.DataFrame]: @@ -257,11 +344,9 @@ def cool_tech(context: "Context") -> dict[str, pd.DataFrame]: ) # cooling fraction = H_cool = Hi - 1 - Hi*(h_fg) # where h_fg (flue gasses losses) = 0.1 - ref_input["cooling_fraction"] = ref_input["value"] * 0.9 - 1 + # ref_input["cooling_fraction"] = ref_input["value"] * 0.9 - 1 # probably obsolete - ref_input[["value", "level"]] = ref_input[["technology", "value", "level"]].apply( - missing_tech, axis=1 - )[["value", "level"]] + ref_input[["value", "level"]] = ref_input.apply(missing_tech, axis=1) # Combines the input df of parent_tech with water withdrawal data input_cool = ( @@ -276,18 +361,22 @@ def cool_tech(context: "Context") -> dict[str, pd.DataFrame]: # Convert year values into integers to be compatibel for model input_cool.year_vtg = input_cool.year_vtg.astype(int) input_cool.year_act = input_cool.year_act.astype(int) - # Drops extra technologies from the data + # Drops extra technologies from the data. backwards compatibility input_cool = input_cool[ (input_cool["level"] != "water_supply") & (input_cool["level"] != "cooling") ] - + # heat plants need no cooling input_cool = input_cool[ ~input_cool["technology_name"].str.contains("hpl", na=False) ] - input_cool = input_cool[ - (input_cool["node_loc"] != f"{context.regions}_GLB") - & (input_cool["node_origin"] != f"{context.regions}_GLB") - ] + # Swap node_loc if node_loc equals "{context.regions}_GLB" + input_cool.loc[input_cool["node_loc"] == f"{context.regions}_GLB", "node_loc"] = ( + input_cool["node_origin"] + ) + # Swap node_origin if node_origin equals "{context.regions}_GLB" + input_cool.loc[ + input_cool["node_origin"] == f"{context.regions}_GLB", "node_origin" + ] = input_cool["node_loc"] input_cool["cooling_fraction"] = input_cool.apply(cooling_fr, axis=1) @@ -347,7 +436,7 @@ def cool_tech(context: "Context") -> dict[str, pd.DataFrame]: con1 = input_cool["technology_name"].str.endswith("ot_saline", na=False) con2 = input_cool["technology_name"].str.endswith("air", na=False) icmse_df = input_cool[(~con1) & (~con2)] - + # electricity inputs inp = make_df( "input", node_loc=electr["node_loc"], @@ -747,14 +836,11 @@ def cool_tech(context: "Context") -> dict[str, pd.DataFrame]: unit="%", time="year", ).pipe(broadcast, year_act=info.Y, node_loc=node_region) - # Alligining certain technologies with growth constriants - g_lo.loc[g_lo["technology"].str.contains("bio_ppl|loil_ppl"), "value"] = -0.5 - g_lo.loc[g_lo["technology"].str.contains("coal_ppl_u|coal_ppl"), "value"] = -0.5 - g_lo.loc[ - (g_lo["technology"].str.contains("coal_ppl_u|coal_ppl")) - & (g_lo["node_loc"].str.contains("CPA|PAS")), - "value", - ] = -1 + + # relax growth constraints for activity jumps + g_lo = relax_growth_constraint(ref_hist_act, scen, cooling_df, g_lo, "activity") + # relax growth constraints for capacity jumps + g_lo = relax_growth_constraint(ref_hist_cap, scen, cooling_df, g_lo, "new_capacity") results["growth_activity_lo"] = g_lo # growth activity up on saline water @@ -769,18 +855,6 @@ def cool_tech(context: "Context") -> dict[str, pd.DataFrame]: ).pipe(broadcast, year_act=info.Y, node_loc=node_region) results["growth_activity_up"] = g_up - # # adding initial activity - # in_lo = h_act.copy() - # in_lo.drop(columns='mode', inplace=True) - # in_lo = in_lo[in_lo['year_act'] == 2015] - # in_lo_1 = make_df('initial_activity_lo', - # node_loc=in_lo['node_loc'], - # technology=in_lo['technology'], - # time='year', - # value=in_lo['value'], - # unit='GWa').pipe(broadcast, year_act=[2015, 2020]) - # results['initial_activity_lo'] = in_lo_1 - return results @@ -839,7 +913,8 @@ def non_cooling_tec(context: "Context") -> dict[str, pd.DataFrame]: n_cool_df = scen.par("output", {"technology": non_cool_tech}) n_cool_df = n_cool_df[ - (n_cool_df["node_loc"] != "R11_GLB") & (n_cool_df["node_dest"] != "R11_GLB") + (n_cool_df["node_loc"] != f"{context.regions}_GLB") + & (n_cool_df["node_dest"] != f"{context.regions}_GLB") ] n_cool_df_merge = pd.merge(n_cool_df, non_cool_df, on="technology", how="right") n_cool_df_merge.dropna(inplace=True) From adca5b8a5d2cb515e297dc9bac400008f630807d Mon Sep 17 00:00:00 2001 From: adrivinca Date: Tue, 22 Oct 2024 11:31:34 +0200 Subject: [PATCH 2/9] remove all dependencies from R11 this was is form another older branch --- message_ix_models/model/water/data/water_supply.py | 6 ++++-- message_ix_models/model/water/report.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/message_ix_models/model/water/data/water_supply.py b/message_ix_models/model/water/data/water_supply.py index 471513c35..517316151 100644 --- a/message_ix_models/model/water/data/water_supply.py +++ b/message_ix_models/model/water/data/water_supply.py @@ -54,7 +54,7 @@ def map_basin_region_wat(context: "Context") -> pd.DataFrame: df_sw["MSGREG"] = ( context.map_ISO_c[context.regions] if context.type_reg == "country" - else f"{context.regions}_" + df_sw["BCU_name"].str[-3:] + else f"{context.regions}_" + df_sw["BCU_name"].str.split("|").str[-1] ) df_sw = df_sw.set_index(["MSGREG", "BCU_name"]) @@ -97,7 +97,9 @@ def map_basin_region_wat(context: "Context") -> pd.DataFrame: if context.type_reg == "country": df_sw["MSGREG"] = context.map_ISO_c[context.regions] else: - df_sw["MSGREG"] = f"{context.regions}_" + df_sw["BCU_name"].str[-3:] + df_sw["MSGREG"] = ( + f"{context.regions}_" + df_sw["BCU_name"].str.split("|").str[-1] + ) df_sw = df_sw.set_index(["MSGREG", "BCU_name"]) df_sw.drop(columns="Unnamed: 0", inplace=True) diff --git a/message_ix_models/model/water/report.py b/message_ix_models/model/water/report.py index 39ee421ee..15e9b3910 100644 --- a/message_ix_models/model/water/report.py +++ b/message_ix_models/model/water/report.py @@ -1113,7 +1113,7 @@ def report(sc: Scenario, reg: str, sdgs: bool = False) -> None: for ur in ["urban", "rural"]: # CHANGE TO URBAN AND RURAL POP pop_tot = sc.timeseries(variable=("Population|" + ur.capitalize())) - pop_tot = pop_tot[-(pop_tot.region == "GLB region (R11)")] + pop_tot = pop_tot[-(pop_tot.region == "GLB region (R11)")] # ONLY R11!!! pop_reg = np.unique(pop_tot["region"]) # need to change names reg_map = mp2.regions() From ebb0c5fbf5b46c2a838e4412a231acfbd253279d Mon Sep 17 00:00:00 2001 From: adrivinca Date: Tue, 22 Oct 2024 12:04:46 +0200 Subject: [PATCH 3/9] Adding new test_relax_growth_constraint --- .../model/water/data/water_for_ppl.py | 2 + .../model/water/data/test_water_for_ppl.py | 106 +++++++++++++++++- 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/message_ix_models/model/water/data/water_for_ppl.py b/message_ix_models/model/water/data/water_for_ppl.py index a5d412fc6..5a245801e 100644 --- a/message_ix_models/model/water/data/water_for_ppl.py +++ b/message_ix_models/model/water/data/water_for_ppl.py @@ -248,6 +248,8 @@ def relax_growth_constraint(ref_hist, scen, cooling_df, g_lo, constraint_type): map_parent.rename(columns={"technology_name": "technology"}, inplace=True) # expand bound_up to all cooling technologies in map_parent combined_bound = pd.merge(combined_bound, map_parent, how="left") + # rename tear_type to year_act, because g_lo use it + combined_bound.rename(columns={year_type: "year_act"}, inplace=True) # Merge to g_lo to be able to remove the technologies g_lo = pd.merge(g_lo, combined_bound, how="left") diff --git a/message_ix_models/tests/model/water/data/test_water_for_ppl.py b/message_ix_models/tests/model/water/data/test_water_for_ppl.py index 869cff406..f6af40668 100644 --- a/message_ix_models/tests/model/water/data/test_water_for_ppl.py +++ b/message_ix_models/tests/model/water/data/test_water_for_ppl.py @@ -5,7 +5,11 @@ from message_ix_models import ScenarioInfo # from message_ix_models.model.structure import get_codes -from message_ix_models.model.water.data.water_for_ppl import cool_tech, non_cooling_tec +from message_ix_models.model.water.data.water_for_ppl import ( + cool_tech, + non_cooling_tec, + relax_growth_constraint, +) @cool_tech.minimum_version @@ -201,3 +205,103 @@ def test_non_cooling_tec(request, test_context): "year_act", ] ) + + +# Mock function for scen.par +class MockScenario: + def par(self, param, filters): + if param == "bound_activity_up": + return pd.DataFrame( + { + "node_loc": ["R12_AFR", "R12_AFR", "R12_AFR"], + "technology": ["coal_ppl", "coal_ppl", "coal_ppl"], + "year_act": [2030, 2040, 2050], + "value": [30, 15, 0], + } + ) + elif param == "bound_new_capacity_up": + return pd.DataFrame( + { + "node_loc": ["R12_AFR", "R12_AFR", "R12_AFR"], + "technology": ["coal_ppl", "coal_ppl", "coal_ppl"], + "year_vtg": [2030, 2040, 2050], + "value": [30, 15, 0], + } + ) + return pd.DataFrame() + + +@pytest.mark.parametrize("constraint_type", ["activity", "new_capacity"]) +def test_relax_growth_constraint(constraint_type): + # Sample data for g_lo + if constraint_type == "activity": + year_type = "year_act" + elif constraint_type == "new_capacity": + year_type = "year_vtg" + else: + raise ValueError( + "Invalid constraint_type. Must be 'activity' or 'new_capacity'." + ) + + g_lo = pd.DataFrame( + { + "node_loc": ["R12_AFR", "R12_AFR", "R12_AFR", "R12_AFR"], + "technology": [ + "coal_ppl__ot_fresh", + "coal_ppl__ot_fresh", + "coal_ppl__ot_fresh", + "gas_ppl__ot_fresh", + ], + "year_act": [2030, 2040, 2050, 2030], + "time": ["year", "year", "year", "year"], + "value": [-0.05, -0.05, -0.05, -0.05], + "unit": ["%", "%", "%", "%"], + } + ) + + # Sample data for ref_hist + ref_hist = pd.DataFrame( + { + "node_loc": ["R12_AFR", "R12_AFR", "R12_AFR"], + "technology": ["coal_ppl", "coal_ppl", "coal_ppl"], + year_type: [2015, 2020, 2025], + "time": ["year", "year", "year"], + "value": [30, 50, 80], + "unit": ["GWa", "GWa", "GWa"], + } + ) + + # Sample data for cooling_df + cooling_df = pd.DataFrame( + { + "technology_name": [ + "coal_ppl__ot_fresh", + "coal_ppl__ot_fresh", + "coal_ppl__ot_fresh", + ], + "parent_tech": ["coal_ppl", "coal_ppl", "coal_ppl"], + } + ) + + # Instantiate mock scenario + scen = MockScenario() + + # Call the function with mock data + result = relax_growth_constraint(ref_hist, scen, cooling_df, g_lo, constraint_type) + # reset_index to make the comparison easier + result = result.reset_index(drop=True) + + # Expected result (this is an example, you should adjust this based on expected behavior) + expected_result = pd.DataFrame( + { + "node_loc": ["R12_AFR", "R12_AFR"], + "technology": ["coal_ppl__ot_fresh", "gas_ppl__ot_fresh"], + "year_act": [2050, 2030], + "time": ["year", "year"], + "value": [-0.05, -0.05], + "unit": ["%", "%"], + } + ) + + # Assert that the result matches the expected DataFrame + pd.testing.assert_frame_equal(result, expected_result) From 36949dd860e5c69d4dd73a7daef05c6db2d4e0cc Mon Sep 17 00:00:00 2001 From: adrivinca Date: Tue, 22 Oct 2024 14:03:29 +0200 Subject: [PATCH 4/9] Shortening comments for Ruff --- message_ix_models/model/water/data/water_for_ppl.py | 12 +++++++----- .../tests/model/water/data/test_water_for_ppl.py | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/message_ix_models/model/water/data/water_for_ppl.py b/message_ix_models/model/water/data/water_for_ppl.py index 5a245801e..36fa4b685 100644 --- a/message_ix_models/model/water/data/water_for_ppl.py +++ b/message_ix_models/model/water/data/water_for_ppl.py @@ -179,12 +179,14 @@ def hist_cap(x: pd.Series, context: "Context", hold_cost: pd.DataFrame) -> list: def relax_growth_constraint(ref_hist, scen, cooling_df, g_lo, constraint_type): """ - Function to check if the parent technologies are shut down and require relaxing the growth constraint. + Function to check if the parent technologies are shut down and require + relaxing the growth constraint. Parameters: ref_hist (pd.DataFrame): Historical data. scen (Scenario): Scenario object to retrieve parameters. - cooling_df (pd.DataFrame): DataFrame containing cooling technologies and their parent technologies. + cooling_df (pd.DataFrame): DataFrame containing cooling technologies and + their parent technologies. g_lo (pd.DataFrame): DataFrame containing growth constraints. constraint_type (str): Type of constraint to check ("activity" or "capacity"). @@ -202,7 +204,7 @@ def relax_growth_constraint(ref_hist, scen, cooling_df, g_lo, constraint_type): "Invalid constraint_type. Must be 'activity' or 'new_capacity'." ) - # Group by all variables of ref_hist apart from year_type and hist_value_col and only keep rows with max year_type + # keep rows with max year_type max_year_hist = ( ref_hist.loc[ref_hist.groupby(["node_loc", "technology"])[year_type].idxmax()] .drop(columns="unit") @@ -214,7 +216,7 @@ def relax_growth_constraint(ref_hist, scen, cooling_df, g_lo, constraint_type): # Get a set with unique year_type values and order them years = np.sort(bound_up_pare[year_type].unique()) - # In max_year_hist add the next year from years corresponding to the hist_year columns + # In max_year_hist add the next year from years matching the hist_year columns max_year_hist["next_year"] = max_year_hist["hist_year"].apply( lambda x: years[years > x][0] ) @@ -227,7 +229,7 @@ def relax_growth_constraint(ref_hist, scen, cooling_df, g_lo, constraint_type): bound_up1 = bound_up1[bound_up1["value"] < 0.9 * bound_up1["hist_value"]] # not look ad sudden contraints after sthe starting year bound_up = bound_up.sort_values(by=["node_loc", "technology", year_type]) - # Check if value for a year is greater than the value of the next year (after sorting) + # Check if value for a year is greater than the value of the next year bound_up["next_value"] = bound_up.groupby(["node_loc", "technology"])[ "value" ].shift(-1) diff --git a/message_ix_models/tests/model/water/data/test_water_for_ppl.py b/message_ix_models/tests/model/water/data/test_water_for_ppl.py index f6af40668..d11afc9df 100644 --- a/message_ix_models/tests/model/water/data/test_water_for_ppl.py +++ b/message_ix_models/tests/model/water/data/test_water_for_ppl.py @@ -291,7 +291,7 @@ def test_relax_growth_constraint(constraint_type): # reset_index to make the comparison easier result = result.reset_index(drop=True) - # Expected result (this is an example, you should adjust this based on expected behavior) + # Expected result expected_result = pd.DataFrame( { "node_loc": ["R12_AFR", "R12_AFR"], From a5dde01bdd16e17b51287260e4e598decdf8a549 Mon Sep 17 00:00:00 2001 From: adrivinca Date: Wed, 23 Oct 2024 10:55:29 +0200 Subject: [PATCH 5/9] Improving docstring and code with Literal --- .../model/water/data/water_for_ppl.py | 61 +++++++++++-------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/message_ix_models/model/water/data/water_for_ppl.py b/message_ix_models/model/water/data/water_for_ppl.py index 36fa4b685..1af7bb074 100644 --- a/message_ix_models/model/water/data/water_for_ppl.py +++ b/message_ix_models/model/water/data/water_for_ppl.py @@ -1,6 +1,6 @@ """Prepare data for water use for cooling & energy technologies.""" -from typing import Any +from typing import Any, Literal, Union import numpy as np import pandas as pd @@ -177,32 +177,43 @@ def hist_cap(x: pd.Series, context: "Context", hold_cost: pd.DataFrame) -> list: ] -def relax_growth_constraint(ref_hist, scen, cooling_df, g_lo, constraint_type): +def relax_growth_constraint( + ref_hist: pd.DataFrame, + scen, + cooling_df: pd.DataFrame, + g_lo: pd.DataFrame, + constraint_type: Literal[Union["activity", "new_capacity"]], +) -> pd.DataFrame: """ - Function to check if the parent technologies are shut down and require - relaxing the growth constraint. - - Parameters: - ref_hist (pd.DataFrame): Historical data. - scen (Scenario): Scenario object to retrieve parameters. - cooling_df (pd.DataFrame): DataFrame containing cooling technologies and - their parent technologies. - g_lo (pd.DataFrame): DataFrame containing growth constraints. - constraint_type (str): Type of constraint to check ("activity" or "capacity"). - - Returns: - pd.DataFrame: Updated g_lo DataFrame with relaxed growth constraints. + Checks if the parent technologies are shut down and require relaxing + the growth constraint. + + Parameters + ---------- + ref_hist : pd.DataFrame + Historical data in the reference scenario. + scen : Scenario + Scenario object to retrieve necessary parameters. + cooling_df : pd.DataFrame + DataFrame containing information on cooling technologies and their + parent technologies. + g_lo : pd.DataFrame + DataFrame containing growth constraints for each technology. + constraint_type : {"activity", "new_capacity"} + Type of constraint to check, either "activity" for operational limits or + "new_capacity" for capacity expansion limits. + + Returns + ------- + pd.DataFrame + Updated `g_lo` DataFrame with relaxed growth constraints. """ - if constraint_type == "activity": - year_type = "year_act" - bound_param = "bound_activity_up" - elif constraint_type == "new_capacity": - year_type = "year_vtg" - bound_param = "bound_new_capacity_up" - else: - raise ValueError( - "Invalid constraint_type. Must be 'activity' or 'new_capacity'." - ) + year_type = "year_act" if constraint_type == "activity" else "year_vtg" + bound_param = ( + "bound_activity_up" + if constraint_type == "activity" + else "bound_new_capacity_up" + ) # keep rows with max year_type max_year_hist = ( From 649ce76f6a9dceed57c220f506fb7b50a6781e09 Mon Sep 17 00:00:00 2001 From: adrivinca Date: Wed, 23 Oct 2024 10:56:18 +0200 Subject: [PATCH 6/9] Improving Mock and mark.para in test function --- .../model/water/data/test_water_for_ppl.py | 55 ++++++++----------- 1 file changed, 23 insertions(+), 32 deletions(-) diff --git a/message_ix_models/tests/model/water/data/test_water_for_ppl.py b/message_ix_models/tests/model/water/data/test_water_for_ppl.py index d11afc9df..b4aff5063 100644 --- a/message_ix_models/tests/model/water/data/test_water_for_ppl.py +++ b/message_ix_models/tests/model/water/data/test_water_for_ppl.py @@ -1,3 +1,5 @@ +from typing import Literal + import pandas as pd import pytest from message_ix import Scenario @@ -209,40 +211,29 @@ def test_non_cooling_tec(request, test_context): # Mock function for scen.par class MockScenario: - def par(self, param, filters): - if param == "bound_activity_up": - return pd.DataFrame( - { - "node_loc": ["R12_AFR", "R12_AFR", "R12_AFR"], - "technology": ["coal_ppl", "coal_ppl", "coal_ppl"], - "year_act": [2030, 2040, 2050], - "value": [30, 15, 0], - } - ) - elif param == "bound_new_capacity_up": - return pd.DataFrame( - { - "node_loc": ["R12_AFR", "R12_AFR", "R12_AFR"], - "technology": ["coal_ppl", "coal_ppl", "coal_ppl"], - "year_vtg": [2030, 2040, 2050], - "value": [30, 15, 0], - } - ) - return pd.DataFrame() - - -@pytest.mark.parametrize("constraint_type", ["activity", "new_capacity"]) -def test_relax_growth_constraint(constraint_type): - # Sample data for g_lo - if constraint_type == "activity": - year_type = "year_act" - elif constraint_type == "new_capacity": - year_type = "year_vtg" - else: - raise ValueError( - "Invalid constraint_type. Must be 'activity' or 'new_capacity'." + def par( + self, + param: Literal["bound_activity_up", "bound_new_capacity_up"], + filters: dict, + ) -> pd.DataFrame: + year_type = "year_act" if param == "bound_activity_up" else "year_vtg" + + return pd.DataFrame( + { + "node_loc": ["R12_AFR", "R12_AFR", "R12_AFR"], + "technology": ["coal_ppl", "coal_ppl", "coal_ppl"], + year_type: [2030, 2040, 2050], + "value": [30, 15, 0], + } ) + +@pytest.mark.parametrize( + "constraint_type, year_type", + [("activity", "year_act"), ("new_capacity", "year_vtg")], +) +def test_relax_growth_constraint(constraint_type, year_type): + # Sample data for g_lo g_lo = pd.DataFrame( { "node_loc": ["R12_AFR", "R12_AFR", "R12_AFR", "R12_AFR"], From a39de1dfc6a8ef1104a916a109b81f92a7fea9b9 Mon Sep 17 00:00:00 2001 From: adrivinca Date: Wed, 23 Oct 2024 10:57:03 +0200 Subject: [PATCH 7/9] Clarifying report comment and recoding water supply fix --- message_ix_models/model/water/data/water_supply.py | 11 +++++------ message_ix_models/model/water/report.py | 3 ++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/message_ix_models/model/water/data/water_supply.py b/message_ix_models/model/water/data/water_supply.py index 517316151..f13548790 100644 --- a/message_ix_models/model/water/data/water_supply.py +++ b/message_ix_models/model/water/data/water_supply.py @@ -94,12 +94,11 @@ def map_basin_region_wat(context: "Context") -> pd.DataFrame: # Reading data, the data is spatially and temporally aggregated from GHMs df_sw["BCU_name"] = df_x["BCU_name"] - if context.type_reg == "country": - df_sw["MSGREG"] = context.map_ISO_c[context.regions] - else: - df_sw["MSGREG"] = ( - f"{context.regions}_" + df_sw["BCU_name"].str.split("|").str[-1] - ) + df_sw["MSGREG"] = ( + context.map_ISO_c[context.regions] + if context.type_reg == "country" + else f"{context.regions}_" + df_sw["BCU_name"].str.split("|").str[-1] + ) df_sw = df_sw.set_index(["MSGREG", "BCU_name"]) df_sw.drop(columns="Unnamed: 0", inplace=True) diff --git a/message_ix_models/model/water/report.py b/message_ix_models/model/water/report.py index 15e9b3910..219cb7cbd 100644 --- a/message_ix_models/model/water/report.py +++ b/message_ix_models/model/water/report.py @@ -1113,7 +1113,8 @@ def report(sc: Scenario, reg: str, sdgs: bool = False) -> None: for ur in ["urban", "rural"]: # CHANGE TO URBAN AND RURAL POP pop_tot = sc.timeseries(variable=("Population|" + ur.capitalize())) - pop_tot = pop_tot[-(pop_tot.region == "GLB region (R11)")] # ONLY R11!!! + # ONLY R11!!! Need to fix when updating the reporting to work with any region + pop_tot = pop_tot[-(pop_tot.region == "GLB region (R11)")] pop_reg = np.unique(pop_tot["region"]) # need to change names reg_map = mp2.regions() From ef055d728e37af5d4944ef6a98153d9dbc8445a9 Mon Sep 17 00:00:00 2001 From: adrivinca Date: Wed, 23 Oct 2024 11:58:02 +0200 Subject: [PATCH 8/9] Fixing breaking test --- message_ix_models/model/water/data/water_for_ppl.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/message_ix_models/model/water/data/water_for_ppl.py b/message_ix_models/model/water/data/water_for_ppl.py index 1af7bb074..e47a4154d 100644 --- a/message_ix_models/model/water/data/water_for_ppl.py +++ b/message_ix_models/model/water/data/water_for_ppl.py @@ -229,12 +229,13 @@ def relax_growth_constraint( # In max_year_hist add the next year from years matching the hist_year columns max_year_hist["next_year"] = max_year_hist["hist_year"].apply( - lambda x: years[years > x][0] + lambda x: years[years > x][0] if any(years > x) else None ) # Merge the max_year_hist with bound_up_pare bound_up = pd.merge(bound_up_pare, max_year_hist, how="left") - # Look at years just after the historical year + # subset of first year after the historical + # if next_year = None (single year test case) bound_up1 is simply empty bound_up1 = bound_up[bound_up[year_type] == bound_up["next_year"]] # Categories that might break the growth constraints bound_up1 = bound_up1[bound_up1["value"] < 0.9 * bound_up1["hist_value"]] From 2f9625fe956843d92dee2a597d94fcb473b3328f Mon Sep 17 00:00:00 2001 From: adrivinca Date: Wed, 23 Oct 2024 11:58:27 +0200 Subject: [PATCH 9/9] Add set as default for the cooling function --- message_ix_models/model/water/cli.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/message_ix_models/model/water/cli.py b/message_ix_models/model/water/cli.py index 5b658e177..2b345448b 100644 --- a/message_ix_models/model/water/cli.py +++ b/message_ix_models/model/water/cli.py @@ -267,6 +267,9 @@ def cooling(context, regions, rcps, rels): # Build build(context, scen) + # Set scenario as default + scen.set_as_default() + # Solve scen.solve(solve_options={"lpmethod": "4"}, case=caseName)