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_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_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_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_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_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_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_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_reporter(test_mp): scen = Scenario(test_mp, 'canning problem (MESSAGE scheme)', 'standard') # Varies between local & CI contexts # DEBUG may be due to reuse of test_mp in a non-deterministic order if not scen.has_solution(): scen.solve() # IXMPReporter can be initialized on a MESSAGE Scenario rep_ix = ixmp_Reporter.from_scenario(scen) # message_ix.Reporter can also be initialized rep = Reporter.from_scenario(scen) # Number of quantities available in a rudimentary MESSAGEix Scenario assert len(rep.graph['all']) == 120 # Quantities have short dimension names assert 'demand:n-c-l-y-h' in rep.graph # Aggregates are available assert 'demand:n-l-h' in rep.graph # Quantities contain expected data dims = dict(coords=['chicago new-york topeka'.split()], dims=['n']) demand = xr.DataArray([300, 325, 275], **dims) # NB the call to squeeze() drops the length-1 dimensions c-l-y-h obs = rep.get('demand:n-c-l-y-h').squeeze(drop=True) # TODO: Squeeze on AttrSeries still returns full index, whereas xarray # drops everything except node obs = obs.reset_index(['c', 'l', 'y', 'h'], drop=True) # check_dtype is false because of casting in pd.Series to float # check_attrsis false because we don't get the unit addition in bare xarray assert_qty_equal(obs.sort_index(), demand, check_attrs=False, check_dtype=False) # ixmp.Reporter pre-populated with only model quantities and aggregates assert len(rep_ix.graph) == 5088 # message_ix.Reporter pre-populated with additional, derived quantities assert len(rep.graph) == 7975 # Derived quantities have expected dimensions vom_key = rep.full_key('vom') assert vom_key not in rep_ix assert vom_key == 'vom:nl-t-yv-ya-m-h' # …and expected values vom = ( rep.get(rep.full_key('ACT')) * rep.get(rep.full_key('var_cost')) ).dropna() # check_attrs false because `vom` multiply above does not add units assert_qty_equal(vom, rep.get(vom_key), check_attrs=False)
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_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_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_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_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_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_rename_technology_no_rm(test_mp): scen = Scenario(test_mp, *msg_args) scen.solve() assert scen.par('output')['technology'].isin(['canning_plant']).any() clone = scen.clone('foo', 'bar', keep_solution=False) # also test if already checked out clone.check_out() clone.rename('technology', {'canning_plant': 'foo_bar'}, keep=True) assert clone.par('output')['technology'].isin(['canning_plant']).any() assert clone.par('output')['technology'].isin(['foo_bar']).any()
def test_as_pyam(message_test_mp): scen = Scenario(message_test_mp, **SCENARIO['dantzig']) if not scen.has_solution(): scen.solve() rep = Reporter.from_scenario(scen) # Quantities for 'ACT' variable at full resolution qty = rep.get(rep.full_key('ACT')) # Call as_pyam() with an empty quantity p = computations.as_pyam(scen, qty[0:0], year_time_dim='ya') assert isinstance(p, pyam.IamDataFrame)
def test_as_pyam(message_test_mp): scen = Scenario(message_test_mp, **SCENARIO["dantzig"]) if not scen.has_solution(): scen.solve() rep = Reporter.from_scenario(scen) # Quantities for 'ACT' variable at full resolution qty = rep.get(rep.full_key("ACT")) # Call as_pyam() with an empty quantity as_pyam = rep.get_comp("as_pyam") p = as_pyam(scen, qty[0:0], rename=dict(nl="region", ya="year")) assert isinstance(p, pyam.IamDataFrame)
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 test_reporter_from_scenario(message_test_mp): scen = Scenario(message_test_mp, **SCENARIO["dantzig"]) # Varies between local & CI contexts # DEBUG may be due to reuse of test_mp in a non-deterministic order if not scen.has_solution(): scen.solve(quiet=True) # IXMPReporter can be initialized on a MESSAGE Scenario rep_ix = ixmp_Reporter.from_scenario(scen) # message_ix.Reporter can also be initialized rep = Reporter.from_scenario(scen) # Number of quantities available in a rudimentary MESSAGEix Scenario assert len(rep.graph["all"]) == 123 # Quantities have short dimension names assert "demand:n-c-l-y-h" in rep # Aggregates are available assert "demand:n-l-h" in rep # Quantities contain expected data dims = dict(coords=["chicago new-york topeka".split()], dims=["n"]) demand = Quantity(xr.DataArray([300, 325, 275], **dims), name="demand") # NB the call to squeeze() drops the length-1 dimensions c-l-y-h obs = rep.get("demand:n-c-l-y-h").squeeze(drop=True) # check_attrs False because we don't get the unit addition in bare xarray assert_qty_equal(obs, demand, check_attrs=False) # ixmp.Reporter pre-populated with only model quantities and aggregates assert len(rep_ix.graph) == 5225 # message_ix.Reporter pre-populated with additional, derived quantities # This is the same value as in test_tutorials.py assert len(rep.graph) == 12690 # Derived quantities have expected dimensions vom_key = rep.full_key("vom") assert vom_key not in rep_ix assert vom_key == "vom:nl-t-yv-ya-m-h" # …and expected values var_cost = rep.get(rep.full_key("var_cost")) ACT = rep.get(rep.full_key("ACT")) product = rep.get_comp("product") vom = product(var_cost, ACT) # check_attrs false because `vom` multiply above does not add units assert_qty_equal(vom, rep.get(vom_key))
def test_addon_up(test_mp): scen = Scenario(test_mp, *msg_args).clone(scenario='addon_up', keep_solution=False) add_addon(scen, costs=-1, zero_output=True) scen.check_out() scen.add_par('addon_up', addon_share) scen.commit('adding upper bound on addon technology') scen.solve() exp = scen.var('ACT', f)['lvl'] * 0.5 obs = scen.var('ACT', g)['lvl'] assert np.isclose(exp, obs)
def test_init(message_test_mp): scen = Scenario(message_test_mp, **SCENARIO["dantzig"]) scen = scen.clone("foo", "bar") scen.check_out() MACRO.initialize(scen) scen.commit("foo") scen.solve(quiet=True) assert np.isclose(scen.var("OBJ")["lvl"], 153.675) assert "mapping_macro_sector" in scen.set_list() assert "aeei" in scen.par_list() assert "DEMAND" in scen.var_list() assert "COST_ACCOUNTING_NODAL" in scen.equ_list()
def test_init(message_test_mp): scen = Scenario(message_test_mp, **SCENARIO['dantzig']) scen = scen.clone('foo', 'bar') scen.check_out() MACRO.initialize(scen) scen.commit('foo') scen.solve() assert np.isclose(scen.var('OBJ')['lvl'], 153.675) assert 'mapping_macro_sector' in scen.set_list() assert 'aeei' in scen.par_list() assert 'DEMAND' in scen.var_list() assert 'COST_ACCOUNTING_NODAL' in scen.equ_list()
def test_addon_lo(message_test_mp): scen = Scenario(message_test_mp, **SCENARIO['dantzig']) \ .clone(scenario='addon_lo', keep_solution=False) add_addon(scen, costs=1, zero_output=True) scen.check_out() scen.add_par('addon_lo', addon_share) scen.commit('adding lower bound on addon technology') scen.solve() exp = scen.var('ACT', f)['lvl'] * 0.5 obs = scen.var('ACT', g)['lvl'] assert np.isclose(exp, obs)
def test_addon_up(message_test_mp): scen = Scenario(message_test_mp, **SCENARIO["dantzig"]).clone(scenario="addon_up", keep_solution=False) add_addon(scen, costs=-1, zero_output=True) scen.check_out() scen.add_par("addon_up", addon_share) scen.commit("adding upper bound on addon technology") scen.solve() exp = scen.var("ACT", f)["lvl"] * 0.5 obs = scen.var("ACT", g)["lvl"] assert np.isclose(exp, obs)
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_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_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 with pytest.raises(ModelError, match="GAMS errored with return code 3"): scen.solve(quiet=True) # 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_addon_tec(test_mp): scen = Scenario(test_mp, *msg_args).clone(scenario='addon', keep_solution=False) add_addon(scen, costs=-1) scen.check_out() bda = scen.par('bound_activity_up', f) bda['value'] = bda['value'] / 2 scen.add_par('bound_activity_up', bda) scen.commit('changing output and bounds') scen.solve() exp = scen.var('ACT', f)['lvl'] obs = scen.var('ACT', g)['lvl'] assert np.isclose(exp, obs)