def get_spec(context) -> Mapping[str, ScenarioInfo]: """Return the spec for the MESSAGE-GLOBIOM global model RES. Returns ------- :class:`dict` of :class:`.ScenarioInfo` objects """ context.use_defaults(SETTINGS) add = ScenarioInfo() # Add technologies add.set["technology"] = copy(get_codes("technology")) # Add regions # Load configuration for the specified region mapping nodes = get_codes(f"node/{context.regions}") # Top-level "World" node # FIXME typing ignored temporarily for PR#9 world = nodes[nodes.index("World")] # type: ignore [arg-type] # Set elements: World, followed by the direct children of World add.set["node"] = [world] + world.child # Initialize time periods add.year_from_codes(get_codes(f"year/{context.years}")) # Add levels add.set["level"] = get_codes("level") # Add commodities add.set["commodity"] = get_codes("commodity") # Add units, associated with commodities units = set( eval_anno(commodity, "unit") for commodity in add.set["commodity"]) # Deduplicate by converting to a set and then back; not strictly necessary, # but reduces duplicate log entries add.set["unit"] = sorted(filter(None, units)) if context.res_with_dummies: # Add dummy technologies add.set["technology"].extend( [Code(id="dummy"), Code(id="dummy source")]) # Add a dummy commodity add.set["commodity"].append(Code(id="dummy")) # The RES is the base, so does not require/remove any elements return dict(add=add, remove=ScenarioInfo(), require=ScenarioInfo())
def test_levels(self): data = get_codes("level") # Some expected commodities are present for check in "primary", "useful": assert check in data # Descriptions are parsed without new lines assert "\n" not in str(data[data.index("primary")].description)
def test_year(self, codelist, length): """Year code lists can be loaded and contain the correct number of codes. :seealso: :meth:`.TestScenarioInfo.test_year_from_codes`. """ # Year codelist can be loaded data = get_codes(f"year/{codelist}") # List contains the expected number of codes assert len(data) == length
def test_hierarchy(self): """get_codes() returns objects with the expected hierarchical relationship.""" codes = get_codes("node/R11") AUT = codes[codes.index("AUT")] R11_WEU = codes[codes.index("R11_WEU")] World = codes[codes.index("World")] assert R11_WEU is AUT.parent assert AUT in R11_WEU.child assert World is R11_WEU.parent assert R11_WEU in World.child
def test_nodes(self, codelist, to_check, length, member): """Tests of node codelists.""" # Node codelist can be loaded data = get_codes(f"node/{codelist}") # List contains a particular region assert to_check in data # Region contains the correct number of countries code = data[data.index(to_check)] assert len(code.child) == length # A specific country is present in the region assert member in code.child
def test_commodities(self): data = get_codes("commodity") # Some expected commodities are present for check in "coal", "electr": assert check in data # Units for one commodity can be retrieved and parsed coal = data[data.index("coal")] registry(str(coal.get_annotation(id="unit").text)) # Descriptions are parsed without new lines crudeoil = data[data.index("crudeoil")] assert "\n" not in str(crudeoil.description)
def input(): """Fixture: test data for :func:`.adapt_R11_R14`.""" R11_all = get_codes("node/R11") R11_reg = R11_all[R11_all.index("World")].child df = make_df(PAR, technology="coal_ppl", year_vtg=[2021, 2022], value=[1.2, 3.4], unit="year").pipe(broadcast, node_loc=R11_reg) # Set a specific value for the regions to be broadcast df["value"] = df["value"].where(df["node_loc"] != "R11_FSU", VALUE) return {PAR: df}
def test_technologies(self): # Retrieve the tech info without calling technologies.cli data = get_codes("technology") # Check the length of the returned dataframe assert len(data) == 377 # Get info on a certain technology h2_fc_trp = data[data.index("h2_fc_trp")] assert ["transport", "useful" ] == eval(str(h2_fc_trp.get_annotation(id="output").text)) # Check that the default value for 'vintaged' is False when omitted from the # YAML file elec_exp = data[data.index("elec_exp")] assert False is eval(str(elec_exp.get_annotation(id="vintaged").text))
def identify_nodes(scenario: Scenario) -> str: """Return the ID of a node codelist given the contents of `scenario`. Returns ------- str The ID of the :doc:`/pkg-data/node` containing the regions of `scenario`. Raises ------ ValueError if no codelist can be identified, or the nodes in the scenario do not match the children of the “World” node in the codelist. """ from message_ix_models.model.structure import get_codes nodes = sorted(scenario.set("node")) # Candidate ID: split e.g. "R14_AFR" to "R14" id = nodes[0].split("_")[0] try: # Get the corresponding codelist codes = get_codes(f"node/{id}") except FileNotFoundError: raise ValueError(f"Couldn't identify node codelist from {repr(nodes)}") glb_node = [n.endswith("_GLB") for n in nodes] if any(glb_node): omit = nodes.pop(glb_node.index(True)) log.info(f"Omit known, non-standard node '{omit}' from set to match") # Expected list of nodes world = codes[codes.index("World")] # type: ignore [arg-type] codes = [world] + world.child try: assert set(nodes) == set(map(str, codes)) except AssertionError: raise ValueError("\n".join([ f"Node IDs suggest codelist {repr(id)}, values do not match:", repr(nodes), repr(codes), ])) else: log.info(f"Identified node codelist {repr(id)}") return id
def test_adapt_R11_R14_0(input): """:func:`.adapt_R11_R14` handles :class:`pandas.DataFrame`.""" # Function runs output = adapt_R11_R14(input) # Output is a dict containing 1 entry df_out = output.pop(PAR) assert 0 == len(output) # Output covers all R14 regions R14_all = get_codes("node/R14") R14_reg = R14_all[R14_all.index("World")].child assert set(R14_reg) == set(df_out["node_loc"]) # Output has expected length assert 14 * 2 == len(df_out) # Output values for new regions match input value for R11_FSU target_nodes = ("R14_CAS", "R14_RUS", "R14_SCS", "R14_UBM") assert ( VALUE == df_out[df_out["node_loc"].isin(target_nodes)]["value"]).all()
def test_year_from_codes(self, caplog, codelist, y0, N_all, N_Y, y_m1, dp_checks): caplog.set_level(logging.DEBUG, logger="message_ix_models") info = ScenarioInfo() codes = get_codes(f"year/{codelist}") info.year_from_codes(codes) # First model period assert y0 == info.y0 assert ("firstmodelyear", y0) in info.set["cat_year"] # Total number of periods assert N_all == len(info.set["year"]) # Number of model periods assert N_Y == len(info.Y) # Final period assert y_m1 == info.Y[-1] # Convert the data frame to a series dp = info.par["duration_period"].set_index("year")["value"] # duration_period entries are as expected for key, expected in dp_checks: assert expected == dp[key] # Test logging assert 0 == len(caplog.messages) info.year_from_codes(codes) assert 3 == len(caplog.messages) assert all( msg.startswith("Discard existing") for msg in caplog.messages)
def test_node_historic_country(self): """get_codes() handles ISO 3166 alpha-3 codes for historic countries.""" assert "SCG" in get_codes("node/R11")
def test_get_codes(self, name): """The included code lists can be loaded.""" get_codes(name)