def test_price_duality(test_mp): years = [2020, 2025, 2030, 2040, 2050] for c in [0.25, 0.5, 0.75]: # set up a scenario for cumulative constraints scen = Scenario(test_mp, MODEL, "cum_many_tecs", version="new") model_setup(scen, years, simple_tecs=False) scen.add_cat("year", "cumulative", years) scen.add_par("bound_emission", ["World", "ghg", "all", "cumulative"], 0.5, "tCO2") scen.commit("initialize test scenario") scen.solve() # set up a new scenario with emissions taxes tax_scen = Scenario(test_mp, MODEL, "tax_many_tecs", version="new") model_setup(tax_scen, years, simple_tecs=False) for y in years: tax_scen.add_cat("year", y, y) # use emission prices from cumulative-constraint scenario as taxes taxes = scen.var("PRICE_EMISSION").rename(columns={ "year": "type_year", "lvl": "value" }) taxes["unit"] = "USD/tCO2" tax_scen.add_par("tax_emission", taxes) tax_scen.commit("initialize test scenario for taxes") tax_scen.solve() # check that emissions are close between cumulative and tax scenario filters = {"node": "World"} emiss = scen.var("EMISS", filters).set_index("year").lvl emiss_tax = tax_scen.var("EMISS", filters).set_index("year").lvl npt.assert_allclose(emiss, emiss_tax, rtol=0.20)
def test_price_duality(test_mp): years = [2020, 2025, 2030, 2040, 2050] for c in [0.25, 0.5, 0.75]: # set up a scenario for cumulative constraints scen = Scenario(test_mp, MODEL, 'cum_many_tecs', version='new') model_setup(scen, years, simple_tecs=False) scen.add_cat('year', 'cumulative', years) scen.add_par('bound_emission', ['World', 'ghg', 'all', 'cumulative'], 0.5, 'tCO2') scen.commit('initialize test scenario') scen.solve() # set up a new scenario with emissions taxes tax_scen = Scenario(test_mp, MODEL, 'tax_many_tecs', version='new') model_setup(tax_scen, years, simple_tecs=False) for y in years: tax_scen.add_cat('year', y, y) # use emission prices from cumulative-constraint scenario as taxes taxes = scen.var('PRICE_EMISSION')\ .rename(columns={'year': 'type_year', 'lvl': 'value'}) taxes['unit'] = 'USD/tCO2' tax_scen.add_par('tax_emission', taxes) tax_scen.commit('initialize test scenario for taxes') tax_scen.solve() # check that emissions are close between cumulative and tax scenario filters = {'node': 'World'} emiss = scen.var('EMISS', filters).set_index('year').lvl emiss_tax = tax_scen.var('EMISS', filters).set_index('year').lvl npt.assert_allclose(emiss, emiss_tax, rtol=0.20)
def check_solution(scen: Scenario) -> None: """Perform several assertions about the solution of `scen`.""" # Reading "ACT" and "CAP" from the solution act = scen.var("ACT").set_index(["technology", "time"]) cap = scen.var("CAP").set_index(["technology"]) # 1) ACT is zero when capacity factor is zero cf = scen.par("capacity_factor").set_index(["technology", "time"]) cf_zero = cf.loc[cf["value"] == 0] for i in cf_zero.index: assert act.loc[i, "lvl"] == 0 # 2) CAP is correctly calculated based on ACT and capacity_factor for i in act.loc[act["lvl"] > 0].index: # Correct ACT based on duration of each time slice duration = float(scen.par("duration_time", {"time": i[1]})["value"]) act.loc[i, "duration-corrected"] = act.loc[i, "lvl"] / duration # Divide by (non-zero) capacity factor act.loc[i, "cf-corrected"] = act.loc[i, "duration-corrected"] / float( cf.loc[i, "value"] ) act = act.fillna(0).reset_index().set_index(["technology"]) # CAP = max("ACT" / "duration_time" / "capcity_factor") for i in cap.index: assert max(act.loc[i, "cf-corrected"]) == float(cap.loc[i, "lvl"])
def test_commodity_price(test_mp): scen = Scenario(test_mp, "test_commodity_price", "standard", version="new") model_setup(scen) scen.commit("initialize test model") scen.solve(case="price_commodity_standard") assert scen.var("OBJ")["lvl"] == 1 assert scen.var("PRICE_COMMODITY")["lvl"][0] == 1
def test_commodity_price(test_mp): scen = Scenario(test_mp, 'test_commodity_price', 'standard', version='new') model_setup(scen) scen.commit('initialize test model') scen.solve(case='price_commodity_standard') assert scen.var('OBJ')['lvl'] == 1 assert scen.var('PRICE_COMMODITY')['lvl'][0] == 1
def test_solve_legacy_scenario(test_legacy_mp): scen = Scenario(test_legacy_mp, *msg_args) exp = scen.var('OBJ')['lvl'] # solve scenario, assert that the new objective value is close to previous scen.remove_solution() scen.solve() assert np.isclose(exp, scen.var('OBJ')['lvl'])
def test_no_constraint(test_mp): scen = Scenario(test_mp, MODEL, 'no_constraint', version='new') model_setup(scen, [2020, 2030]) scen.commit('initialize test scenario') scen.solve() # without emissions constraint, the zero-cost technology satisfies demand assert scen.var('OBJ')['lvl'] == 0 # without emissions constraint, there are no emission prices assert scen.var('PRICE_EMISSION').empty
def test_no_constraint(test_mp): scen = Scenario(test_mp, MODEL, "no_constraint", version="new") model_setup(scen, [2020, 2030]) scen.commit("initialize test scenario") scen.solve(quiet=True) # without emissions constraint, the zero-cost technology satisfies demand assert scen.var("OBJ")["lvl"] == 0 # without emissions constraint, there are no emission prices assert scen.var("PRICE_EMISSION").empty
def test_solve_legacy_scenario(tmp_path, test_data_path): db_path = create_test_platform(tmp_path, test_data_path, 'legacy') mp = Platform(backend='jdbc', driver='hsqldb', path=db_path) scen = Scenario(mp, model='canning problem (MESSAGE scheme)', scenario='standard') exp = scen.var('OBJ')['lvl'] # solve scenario, assert that the new objective value is close to previous scen.remove_solution() scen.solve() assert np.isclose(exp, scen.var('OBJ')['lvl'])
def test_solve_legacy_scenario(tmp_path, test_data_path): db_path = create_test_platform(tmp_path, test_data_path, "legacy") mp = Platform(backend="jdbc", driver="hsqldb", path=db_path) scen = Scenario(mp, model="canning problem (MESSAGE scheme)", scenario="standard") exp = scen.var("OBJ")["lvl"] # solve scenario, assert that the new objective value is close to previous scen = scen.clone(keep_solution=False) scen.solve() assert np.isclose(exp, scen.var("OBJ")["lvl"])
def test_init(test_mp): scen = Scenario(test_mp, *msg_args) obs = scen.var('OBJ')['lvl'] scen = scen.clone('foo', 'bar', keep_solution=False) scen.check_out() macro.init(scen) scen.commit('foo') scen.solve() exp = scen.var('OBJ')['lvl'] assert np.isclose(obs, exp)
def test_solve_modified(caplog, message_test_mp): base = Scenario(message_test_mp, **SCENARIO["dantzig"]) # Base objective value base.solve() base_obj = base.var("OBJ")["lvl"] with solve_modified(base, "new scenario name") as scenario: # Scenario is not yet solved assert not scenario.has_solution() # Scenario has the indicated new name assert "new scenario name" == scenario.scenario # Change one demand parameter from 325 to 326 scenario.add_par( "demand", make_df( "demand", node=["new-york"], commodity=["cases"], level="consumption", year=1963, time="year", value=326, unit="case", ), ) # Scenario is solved at the end of the with: statement assert scenario.has_solution() assert scenario.var("OBJ")["lvl"] != base_obj
def test_commodity_price_equality(test_mp): scen = Scenario(test_mp, "test_commodity_price", "equality", version="new") model_setup(scen, var_cost=-1) scen.commit("initialize test model with negative variable costs") # negative variable costs and supply >= demand causes an unbounded ray pytest.raises(CalledProcessError, scen.solve) # use the commodity-balance equality feature scen.check_out() scen.add_set("balance_equality", ["comm", "level"]) scen.commit("set commodity-balance for `[comm, level]` as equality") scen.solve(case="price_commodity_equality") assert scen.var("OBJ")["lvl"] == -1 assert scen.var("PRICE_COMMODITY")["lvl"][0] == -1
def test_add_bound_activity_up_all_modes(message_test_mp): scen = Scenario(message_test_mp, **SCENARIO['dantzig']).clone() scen.solve() # data for act bound exp = 0.95 * calculate_activity(scen).sum() data = pd.DataFrame( { 'node_loc': 'seattle', 'technology': 'transport_from_seattle', 'year_act': _year, 'time': 'year', 'unit': 'cases', 'mode': 'all', 'value': exp, }, index=[0]) # test limiting all modes clone = scen.clone('foo', 'baz', keep_solution=False) clone.check_out() clone.add_par('bound_activity_up', data) clone.commit('foo') clone.solve() obs = calculate_activity(clone).sum() assert np.isclose(obs, exp) orig_obj = scen.var('OBJ')['lvl'] new_obj = clone.var('OBJ')['lvl'] assert new_obj >= orig_obj
def test_add_bound_activity_up(test_mp): scen = Scenario(test_mp, *msg_args) scen.solve() # data for act bound exp = 0.5 * calculate_activity(scen).sum() data = pd.DataFrame({ 'node_loc': 'seattle', 'technology': 'transport_from_seattle', 'year_act': 2010, 'time': 'year', 'unit': 'cases', 'mode': 'to_chicago', 'value': exp, }, index=[0]) # test limiting one mode clone = scen.clone('foo', 'bar', keep_solution=False) clone.check_out() clone.add_par('bound_activity_up', data) clone.commit('foo') clone.solve() obs = calculate_activity(clone).loc['to_chicago'] assert np.isclose(obs, exp) orig_obj = scen.var('OBJ')['lvl'] new_obj = clone.var('OBJ')['lvl'] assert new_obj >= orig_obj
def test_cumulative_equidistant(test_mp): scen = Scenario(test_mp, MODEL, "cum_equidistant", version="new") years = [2020, 2030, 2040] model_setup(scen, years) scen.add_cat("year", "cumulative", years) scen.add_par("bound_emission", ["World", "ghg", "all", "cumulative"], 0, "tCO2") scen.commit("initialize test scenario") scen.solve(quiet=True) # with emissions constraint, the technology with costs satisfies demand assert scen.var("OBJ")["lvl"] > 0 # under a cumulative constraint, the price must increase with the discount # rate starting from the marginal relaxation in the first year obs = scen.var("PRICE_EMISSION")["lvl"].values npt.assert_allclose(obs, [1.05 ** (y - years[0]) for y in years])
def test_commodity_price_equality(test_mp): scen = Scenario(test_mp, 'test_commodity_price', 'equality', version='new') model_setup(scen, var_cost=-1) scen.commit('initialize test model with negative variable costs') # negative variable costs and supply >= demand causes an unbounded ray pytest.raises(CalledProcessError, scen.solve) # use the commodity-balance equality feature scen.check_out() scen.add_set('balance_equality', ['comm', 'level']) scen.commit('set commodity-balance for `[comm, level]` as equality') scen.solve(case='price_commodity_equality') assert scen.var('OBJ')['lvl'] == -1 assert scen.var('PRICE_COMMODITY')['lvl'][0] == -1
def test_add_bound_activity_up_all_modes(message_test_mp): scen = Scenario(message_test_mp, **SCENARIO["dantzig"]).clone() scen.solve(quiet=True) # data for act bound exp = 0.95 * calculate_activity(scen).sum() data = pd.DataFrame( { "node_loc": "seattle", "technology": "transport_from_seattle", "year_act": _year, "time": "year", "unit": "cases", "mode": "all", "value": exp, }, index=[0], ) # test limiting all modes clone = scen.clone("foo", "baz", keep_solution=False) clone.check_out() clone.add_par("bound_activity_up", data) clone.commit("foo") clone.solve() obs = calculate_activity(clone).sum() assert np.isclose(obs, exp) orig_obj = scen.var("OBJ")["lvl"] new_obj = clone.var("OBJ")["lvl"] assert new_obj >= orig_obj
def test_per_period_equidistant(test_mp): scen = Scenario(test_mp, MODEL, "per_period_equidistant", version="new") years = [2020, 2030, 2040] model_setup(scen, years) for y in years: scen.add_cat("year", y, y) scen.add_par("bound_emission", ["World", "ghg", "all", y], 0, "tCO2") scen.commit("initialize test scenario") scen.solve() # with emissions constraint, the technology with costs satisfies demand assert scen.var("OBJ")["lvl"] > 0 # under per-year emissions constraints, the emission price must be equal to # the marginal relaxation, ie. the difference in costs between technologies npt.assert_allclose(scen.var("PRICE_EMISSION")["lvl"], [1] * 3)
def test_per_period_equidistant(test_mp): scen = Scenario(test_mp, MODEL, 'per_period_equidistant', version='new') years = [2020, 2030, 2040] model_setup(scen, years) for y in years: scen.add_cat('year', y, y) scen.add_par('bound_emission', ['World', 'ghg', 'all', y], 0, 'tCO2') scen.commit('initialize test scenario') scen.solve() # with emissions constraint, the technology with costs satisfies demand assert scen.var('OBJ')['lvl'] > 0 # under per-year emissions constraints, the emission price must be equal to # the marginal relaxation, ie. the difference in costs between technologies npt.assert_allclose(scen.var('PRICE_EMISSION')['lvl'], [1] * 3)
def test_cumulative_equidistant(test_mp): scen = Scenario(test_mp, MODEL, 'cum_equidistant', version='new') years = [2020, 2030, 2040] model_setup(scen, years) scen.add_cat('year', 'cumulative', years) scen.add_par('bound_emission', ['World', 'ghg', 'all', 'cumulative'], 0, 'tCO2') scen.commit('initialize test scenario') scen.solve() # with emissions constraint, the technology with costs satisfies demand assert scen.var('OBJ')['lvl'] > 0 # under a cumulative constraint, the price must increase with the discount # rate starting from the marginal relaxation in the first year obs = scen.var('PRICE_EMISSION')['lvl'].values npt.assert_allclose(obs, [1.05**(y - years[0]) for y in years])
def test_share_commodity_lo(test_mp): scen = Scenario(test_mp, *msg_args) scen.solve() # data for share bound def calc_share(s): a = calculate_activity( s, tec='transport_from_seattle').loc['to_new-york'] b = calculate_activity( s, tec='transport_from_san-diego').loc['to_new-york'] return a / (a + b) exp = 1. * calc_share(scen) # add share constraints clone = scen.clone(scenario='share_commodity_lo', keep_solution=False) clone.check_out() clone.add_cat('technology', 'share', 'transport_from_seattle') clone.add_cat('technology', 'total', ['transport_from_seattle', 'transport_from_san-diego']) clone.add_set('shares', 'test-share') clone.add_set('map_shares_commodity_share', pd.DataFrame({ 'shares': 'test-share', 'node_share': 'new-york', 'node': 'new-york', 'type_tec': 'share', 'mode': 'all', 'commodity': 'cases', 'level': 'consumption', }, index=[0])) clone.add_set('map_shares_commodity_total', pd.DataFrame({ 'shares': 'test-share', 'node_share': 'new-york', 'node': 'new-york', 'type_tec': 'total', 'mode': 'all', 'commodity': 'cases', 'level': 'consumption', }, index=[0])) clone.add_par('share_commodity_lo', pd.DataFrame({ 'shares': 'test-share', 'node_share': 'new-york', 'year_act': 2010, 'time': 'year', 'unit': 'cases', 'value': exp, }, index=[0])) clone.commit('foo') clone.solve() obs = calc_share(clone) assert np.isclose(obs, exp) orig_obj = scen.var('OBJ')['lvl'] new_obj = clone.var('OBJ')['lvl'] assert new_obj >= orig_obj
def test_excel_read_write(message_test_mp, tmp_path): # Path to temporary file tmp_path /= 'excel_read_write.xlsx' # Convert to string to ensure this can be handled fname = str(tmp_path) scen1 = Scenario(message_test_mp, **SCENARIO['dantzig']) scen1 = scen1.clone(keep_solution=False) scen1.check_out() scen1.init_set('new_set') scen1.add_set('new_set', 'member') scen1.init_par('new_par', idx_sets=['new_set']) scen1.add_par('new_par', 'member', 2, '-') scen1.commit('new set and parameter added.') # Writing to Excel without solving scen1.to_excel(fname) # Writing to Excel when scenario has a solution scen1.solve() scen1.to_excel(fname) scen2 = Scenario(message_test_mp, model='foo', scenario='bar', version='new') # Fails without init_items=True with pytest.raises(ValueError, match="no set 'new_set'"): scen2.read_excel(fname) # Succeeds with init_items=True scen2.read_excel(fname, init_items=True, commit_steps=True) exp = scen1.par('input') obs = scen2.par('input') pdt.assert_frame_equal(exp, obs) assert scen2.has_par('new_par') assert float(scen2.par('new_par')['value']) == 2 scen2.commit('foo') # must be checked in scen2.solve() assert np.isclose(scen2.var('OBJ')['lvl'], scen1.var('OBJ')['lvl'])
def test_excel_read_write(message_test_mp, tmp_path): # Path to temporary file tmp_path /= "excel_read_write.xlsx" # Convert to string to ensure this can be handled fname = str(tmp_path) scen1 = Scenario(message_test_mp, **SCENARIO["dantzig"]) scen1 = scen1.clone(keep_solution=False) scen1.check_out() scen1.init_set("new_set") scen1.add_set("new_set", "member") scen1.init_par("new_par", idx_sets=["new_set"]) scen1.add_par("new_par", "member", 2, "-") scen1.commit("new set and parameter added.") # Writing to Excel without solving scen1.to_excel(fname) # Writing to Excel when scenario has a solution scen1.solve() scen1.to_excel(fname) scen2 = Scenario(message_test_mp, model="foo", scenario="bar", version="new") # Fails without init_items=True with pytest.raises(ValueError, match="no set 'new_set'"): scen2.read_excel(fname) # Succeeds with init_items=True scen2.read_excel(fname, init_items=True, commit_steps=True) exp = scen1.par("input") obs = scen2.par("input") pdt.assert_frame_equal(exp, obs) assert scen2.has_par("new_par") assert float(scen2.par("new_par")["value"]) == 2 scen2.solve() assert np.isclose(scen2.var("OBJ")["lvl"], scen1.var("OBJ")["lvl"])
def test_init(test_mp): scen = Scenario(test_mp, *msg_args) scen = scen.clone('foo', 'bar') scen.check_out() macro.init(scen) scen.commit('foo') scen.solve() assert np.isclose(scen.var('OBJ')['lvl'], 153.675)
def test_multi_db_run(tmpdir): # create a new instance of the transport problem and solve it mp1 = Platform(driver="hsqldb", path=tmpdir / "mp1") scen1 = make_dantzig(mp1, solve=True) mp2 = Platform(driver="hsqldb", path=tmpdir / "mp2") # add other unit to make sure that the mapping is correct during clone mp2.add_unit("wrong_unit") mp2.add_region("wrong_region", "country") # check that cloning across platforms must copy the full solution dest = dict(platform=mp2) pytest.raises(NotImplementedError, scen1.clone, keep_solution=False, **dest) pytest.raises(NotImplementedError, scen1.clone, shift_first_model_year=1964, **dest) # clone solved model across platforms (with default settings) scen1.clone(platform=mp2, keep_solution=True) # close the db to ensure that data and solution of the clone are saved mp2.close_db() del mp2 # reopen the connection to the second platform and reload scenario _mp2 = Platform(driver="hsqldb", path=tmpdir / "mp2") scen2 = Scenario(_mp2, **SCENARIO["dantzig"]) assert_multi_db(mp1, _mp2) # check that sets, variables and parameter were copied correctly npt.assert_array_equal(scen1.set("node"), scen2.set("node")) scen2.firstmodelyear == 1963 assert_frame_equal(scen1.par("var_cost"), scen2.par("var_cost")) assert np.isclose(scen2.var("OBJ")["lvl"], 153.675) assert_frame_equal(scen1.var("ACT"), scen2.var("ACT")) # check that custom unit, region and timeseries are migrated correctly assert_frame_equal(scen2.timeseries(iamc=True), TS_DF)
def test_excel_read_write(test_mp): fname = 'test_excel_read_write.xlsx' scen1 = Scenario(test_mp, *msg_args) scen1.to_excel(fname) scen2 = Scenario(test_mp, model='foo', scenario='bar', version='new') scen2.read_excel(fname) exp = scen1.par('input') obs = scen2.par('input') pdt.assert_frame_equal(exp, obs) scen1.solve() scen2.commit('foo') # must be checked in scen2.solve() exp = scen1.var('OBJ')['lvl'] obs = scen2.var('OBJ')['lvl'] assert exp == obs os.remove(fname)
def group_data(var: str, results: message_ix.Scenario) -> pd.DataFrame: # TODO: add as variable units = { 'ACT': 'GWa/a', 'CAP': 'GW', 'CAP_NEW': 'GW/a', 'EMISS': 'MtCO2/a' } historicals = { 'ACT': 'historical_activity', 'CAP_NEW': 'historical_new_capacity' } df = results.var(var) df = df.loc[df.lvl != 0] if var in historicals: df_hist = results.par(historicals[var]) df_hist = df_hist.rename(columns={'value': 'lvl'}) df_hist = df_hist.loc[df_hist.lvl != 0] df = df.append(df_hist, sort=False) # group Variable by technology and reshape to timeseries format if 'year_act' in df.columns: df = df[['node_loc', 'technology', 'year_act', 'lvl']] df = df.groupby(['node_loc', 'year_act', 'technology'], as_index=False).sum().copy() df = df.rename(columns={'node_loc': 'node', 'year_act': 'year'}) elif var == 'EMISS': df_hist = results.par('historical_emission') df_hist = df_hist.rename(columns={ 'type_emission': 'emission', 'type_year': 'year', 'value': 'lvl' }) df_hist = df_hist.loc[df_hist.lvl != 0] df = df.append(df_hist, sort=False) df['year'] = df.year.astype(int) df = df[['node', 'emission', 'year', 'lvl']] df = df.groupby(['node', 'year', 'emission'], as_index=False).sum().copy() else: df = df[['node_loc', 'technology', 'year_vtg', 'lvl']] df = df.groupby(['node_loc', 'year_vtg', 'technology'], as_index=False).sum().copy() df = df.rename(columns={'node_loc': 'node', 'year_vtg': 'year'}) df['unit'] = units[var] df['variable'] = var return df
def test_rename_technology(test_mp): scen = Scenario(test_mp, *msg_args) scen.solve() assert scen.par('output')['technology'].isin(['canning_plant']).any() exp_obj = scen.var('OBJ')['lvl'] clone = scen.clone('foo', 'bar', keep_solution=False) clone.rename('technology', {'canning_plant': 'foo_bar'}) assert not clone.par('output')['technology'].isin(['canning_plant']).any() assert clone.par('output')['technology'].isin(['foo_bar']).any() clone.solve() obs_obj = clone.var('OBJ')['lvl'] assert obs_obj == exp_obj
def check_solution(scen: Scenario) -> None: # Reading "ACT" greater than zero from the solution act = scen.var("ACT")[scen.var("ACT")["lvl"] > 0] # 1) Testing if linkage between "gas_ppl" with "gas_supply" technology is made assert "gas_supply" in set(act["technology"]) # 2) Testing if "ACT" of "gas_ppl" is correct (with respect to "duration_time_rel") # i.e., sum("ACT" of "gas_ppl") = sum("ACT" of "gas_supply") = sum("demand") assert (sum(act.loc[act["technology"] == "gas_ppl"]["lvl"]) == sum( act.loc[act["technology"] == "gas_supply"]["lvl"]) == sum( scen.par("demand")["value"])) # 3) Test if "CAP" of "gas_ppl" is correctly calculated based on "ACT" # i.e., "CAP" = max("ACT" / "duration_time") for h in act.loc[act["technology"] == "gas_ppl"]["time"]: act.loc[(act["time"] == h) & (act["technology"] == "gas_ppl"), "capacity-corrected", ] = act["lvl"] / float( scen.par("duration_time", {"time": h})["value"]) assert max(act.loc[act["technology"] == "gas_ppl", "capacity-corrected"]) == float( scen.var("CAP", {"technology": "gas_ppl"})["lvl"])