diff --git a/premise/data/metals/transport_activities_mapping.yml b/premise/data/metals/transport_activities_mapping.yaml similarity index 90% rename from premise/data/metals/transport_activities_mapping.yml rename to premise/data/metals/transport_activities_mapping.yaml index 52a10b0d..4835aa90 100644 --- a/premise/data/metals/transport_activities_mapping.yml +++ b/premise/data/metals/transport_activities_mapping.yaml @@ -1,238 +1,240 @@ -stibnite: - - stibnite concentrate - -bauxite: - - Bauxite - -chromite: - - chromite ore concentrate - -cobalt_ore: - - copper-cobalt ore - -copper_ore: - - copper concentrate, sulfide ore - -iron_ore: - - iron ore concentrate - -lead_ore: - - lead concentrate - -manganese_ore: - - manganese concentrate - -molybdenite: - - molybdenite - -nickel_ore: - - nickel concentrate, 16% Ni - -phosphate_rock: - - phosphate rock, beneficiated - -silver_ore: - - silver - -tantalum_vanadium_niobium_ore: - - pyrochlore concentrate - - tantalum concentrate, 30% Ta2O5 - - vanadium bearing magnetite - -tin_ore: - - tin concentrate - -titanium_ore: - - ilmenite, 54% titanium dioxide - - rutile, 95% titanium dioxide - -tungsten_ore: - - tungsten concentrate - -uranium_ore: - - uranium ore, as U - -zinc_ore: - - zinc concentrate - -aluminium_hydroxide: - - aluminium hydroxide - -aluminium_oxide: - - aluminium oxide, metallurgical - -other_hydroxide: - - beryllium hydroxide - -other_borate: - - calcium borates - -sodium_borate: - - sodium borates - -boric_oxid_acid: ### CHECK WHETHER BORIC OXIDE SHOULD BE EXCLUDED - - boric acid, anhydrous, powder - - boric oxide - -cobalt_hydroxide: - - cobalt hydroxide - -other_sulfate: - - cobalt sulfate - - strontium sulfate, 90% SrSO4 - -phosphorous: - - phosphorus, white, liquid - -phosphoric_acid: - - phosphoric acid, fertiliser grade, without water, in 70% solution state - -potash: - - potash salt - -reo_scandium_yttrium: ## I left out the rare earth carbonate to avoid double counting - - dysprosium oxide - - erbium oxide - - europium oxide - - gadolinium oxide - - ytterbium oxide - - terbium oxide - - thulium oxide - - yttrium oxide - - samarium oxide - - scandium oxide - - praseodymium oxide - - neodymium oxide - - lutetium oxide - - lanthanum oxide - - holmium oxide - - cerium oxide - - lanthanum-cerium oxide - -selenium: - - Selenium - -sulfur: - - sulfur - -vanadium_oxide: - - vanadium pentoxide - -lithium_hydroxide: - - lithium hydroxide, battery grade - -other_chloride: - -lithium_carbonate: - - lithium carbonate, battery grade - -other_carbonate: - -dolomite: - - dolomite - -other_carbide: - -boron carbide - -other_fluoride: - -silicon: - - silicon, metallurgical grade - -antimony: - - antimony - -aluminium: - - Aluminium, primary, liquid - - aluminium, primary, ingot - -aluminium_cast_alloy: - - aluminium, cast alloy - -bentonite: - - bentonite - -beryllium: - - beryllium - -boron_tellurium: - - boron trifluoride - - tellurium, semiconductor-grade - -cadmium: - - Cadmium - -chromium: - - chromium - -cobalt: - - cobalt - -copper_anode: - - copper, anode - -copper_cathode: - - copper, cathode - -ga_ge_hf_in_nb_rh_va: - - gallium, semiconductor-grade - - germanium, concentrate - - hafnium, sponge -# - hafnium, tetrachloride - - indium - - Rhenium - -gold: - - Gold - -graphite: - - graphite - -pgm: - - iridium - - platinum group metal concentrate - - ruthenium - -pig_iron: - - pig iron - -lead: - - lead - -magnesium: - - magnesium - -manganese: - - manganese - -molybdenum: - - molybdenum - -nickel: - - nickel, class 1 - -niobium: - - ferroniobium, 66% Nb - -palladium: - - palladium - -platinum: - - platinum - -silica_sand: - - silica sand - -tin: - - tin - -titanium: ## I exclude titanium tetrachloride and sponge - - titanium - -zinc: - - zinc - -zirconium: ### RE RUN NOTEBOOK ADDING THIS! - - zirconium - +stibnite: + - stibnite concentrate + +bauxite: + - Bauxite + +chromite: + - chromite ore concentrate + +cobalt_ore: + - copper-cobalt ore + +copper_ore: + - copper concentrate, sulfide ore + +iron_ore: + - iron ore concentrate + +lead_ore: + - lead concentrate + +manganese_ore: + - manganese concentrate + +molybdenite: + - molybdenite + +nickel_ore: + - nickel concentrate, 16% Ni + +phosphate_rock: + - phosphate rock, beneficiated + +silver_ore: + - silver + +tantalum_vanadium_niobium_ore: + - pyrochlore concentrate + - tantalum concentrate, 30% Ta2O5 + - vanadium bearing magnetite + +tin_ore: + - tin concentrate + +titanium_ore: + - ilmenite, 54% titanium dioxide + - rutile, 95% titanium dioxide + +tungsten_ore: + - tungsten concentrate + +uranium_ore: + - uranium ore, as U + +zinc_ore: + - zinc concentrate + +aluminium_hydroxide: + - aluminium hydroxide + +aluminium_oxide: + - aluminium oxide, metallurgical + +other_hydroxide: + - beryllium hydroxide + +other_borate: + - calcium borates + +sodium_borate: + - sodium borates + +boric_oxid_acid: ### CHECK WHETHER BORIC OXIDE SHOULD BE EXCLUDED + - boric acid, anhydrous, powder + - boric oxide + +cobalt_hydroxide: + - cobalt hydroxide + +other_sulfate: + - cobalt sulfate + - strontium sulfate, 90% SrSO4 + +phosphorous: + - phosphorus, white, liquid + +phosphoric_acid: + - phosphoric acid, fertiliser grade, without water, in 70% solution state + +potash: + - potash salt + +reo_scandium_yttrium: ## I left out the rare earth carbonate to avoid double counting + - dysprosium oxide + - erbium oxide + - europium oxide + - gadolinium oxide + - ytterbium oxide + - terbium oxide + - thulium oxide + - yttrium oxide + - samarium oxide + - scandium oxide + - praseodymium oxide + - neodymium oxide + - lutetium oxide + - lanthanum oxide + - holmium oxide + - cerium oxide + - lanthanum-cerium oxide + +selenium: + - Selenium + +sulfur: + - sulfur + +vanadium_oxide: + - vanadium pentoxide + +lithium_hydroxide: + - lithium hydroxide, battery grade + +# other_chloride: + +lithium_carbonate: + - lithium carbonate, battery grade + +# other_carbonate: + +dolomite: + - dolomite + +other_carbide: + - boron carbide + +# other_fluoride: + +silicon: + - silicon, metallurgical grade + +antimony: + - antimony + +aluminium: + - Aluminium, primary, liquid + - aluminium, primary, ingot + +aluminium_cast_alloy: + - aluminium, cast alloy + +bentonite: + - bentonite + +beryllium: + - beryllium + +boron_tellurium: + - boron trifluoride + - tellurium, semiconductor-grade + +cadmium: + - Cadmium + +chromium: + - chromium + +cobalt: + - cobalt + +copper_anode: + - copper, anode + +copper_cathode: + - copper, cathode + +ga_ge_hf_in_nb_rh_va: + - gallium, semiconductor-grade + - germanium, concentrate + - hafnium sponge + - hafnium tetrachloride + - indium + - indium rich leaching residues, from zinc production + - Rhenium + +gold: + - Gold + +graphite: + - graphite + - graphite ore, mined + +pgm: + - iridium + - platinum group metal concentrate + - ruthenium + +pig_iron: + - pig iron + +lead: + - lead + +magnesium: + - magnesium + +manganese: + - manganese + +molybdenum: + - molybdenum + +nickel: + - nickel, class 1 + +niobium: + - ferroniobium, 66% Nb + +palladium: + - palladium + +platinum: + - platinum + +silica_sand: + - silica sand + +tin: + - tin + +titanium: ## I exclude titanium tetrachloride and sponge + - titanium + +zinc: + - zinc + +zirconium: ### RE RUN NOTEBOOK ADDING THIS! + - zirconium + ### Missing: spodumene, sand, ZIRCONIUM (BUT IT IS THERE,810920) \ No newline at end of file diff --git a/premise/metals.py b/premise/metals.py index 8edd2107..431a34e4 100644 --- a/premise/metals.py +++ b/premise/metals.py @@ -6,6 +6,8 @@ import uuid from functools import lru_cache from typing import Optional, Tuple +from _operator import itemgetter +from itertools import groupby import country_converter as coco import pandas as pd @@ -45,6 +47,49 @@ def _update_metals(scenario, version, system_model, modified_datasets): return scenario, modified_datasets +def load_metals_alternative_names(): + """ + Load dataframe with alternative names for metals + """ + + filepath = DATA_DIR / "metals" / "transport_activities_mapping.yaml" + + with open(filepath, "r", encoding="utf-8") as stream: + out = yaml.safe_load(stream) + + # this dictionary has lists as values + + # create a reversed dictionary where + # the keys are the alternative names + # and the values are the metals + + rev_out = {} + + for k, v in out.items(): + for i in v: + rev_out[i] = k + + return rev_out + + +def load_metals_transport(): + """ + Load dataframe with metals transport + """ + + filepath = DATA_DIR / "metals" / "transport_markets_data.csv" + df = pd.read_csv(filepath, sep=",") + + # remove rows without values under Weighted Average Distance + df = df.loc[~df["Weighted Average Distance"].isnull()] + # remove rows with value 0 under Weighted Average Distance + df = df.loc[df["Weighted Average Distance"] != 0] + + df["country"] = country_short = coco.convert(df["Origin Label"], to="ISO2") + + return df + + def load_mining_shares_mapping(): """ Load mapping between mining shares from the different sources and ecoinvent @@ -276,6 +321,8 @@ def __init__( ) self.biosphere_flow_codes = biosphere_flows_dictionary(version=self.version) + self.metals_transport = load_metals_transport() + self.alt_names = load_metals_alternative_names() def update_metals_use_in_database(self): """ @@ -526,6 +573,9 @@ def get_shares(self, df: pd.DataFrame, new_locations: dict, name, ref_prod) -> d share = share.values[0] shares[(name, ref_prod, short_location)] = share + # normalize shares to 1 + shares = {k: v / sum(shares.values()) for k, v in shares.items()} + return shares def get_geo_mapping(self, df: pd.DataFrame, new_locations: dict) -> dict: @@ -639,13 +689,94 @@ def create_market(self, metal, df): "code": str(uuid.uuid4()), } + # add mining exchanges dataset["exchanges"].extend(self.create_region_specific_markets(df)) - + # add transport exchanges + trspt_exc = self.add_transport_to_market(dataset, metal) + if len(trspt_exc) > 0: + dataset["exchanges"].extend(trspt_exc) # filter out None dataset["exchanges"] = [exc for exc in dataset["exchanges"] if exc] return dataset + def add_transport_to_market(self, dataset, metal) -> list: + excs = [] + + origin_shares = { + e["location"]: e["amount"] + for e in dataset["exchanges"] + if e["type"] == "technosphere" + } + + # multiply shares with the weighted transport distance from + # the transport dataset + for c, share in origin_shares.items(): + if metal in self.alt_names: + trspt_data = self.get_weighted_average_distance(c, metal) + for i, row in trspt_data.iterrows(): + distance = row["Weighted Average Distance"] + mode = row["TransportMode Label"] + tkm = distance / 1000 * share # convert to tonne-kilometers x share + + if mode == "Air": + name = "transport, freight, aircraft, belly-freight, long haul" + reference_product = "transport, freight, aircraft, long haul" + loc = "GLO" + elif mode == "Sea": + name = "transport, freight, sea, container ship" + reference_product = "transport, freight, sea, container ship" + loc = "GLO" + elif mode == "Railway": + name = "market group for transport, freight train" + reference_product = "transport, freight train" + loc = "GLO" + else: + name = "market for transport, freight, lorry, unspecified" + reference_product = "transport, freight, lorry, unspecified" + loc = "RoW" + + excs.append( + { + "name": name, + "product": reference_product, + "location": loc, + "amount": tkm, + "type": "technosphere", + "unit": "ton kilometer", + } + ) + + else: + print( + f"Metal {metal} not found in alternative names. Skipping transport." + ) + + # sum up duplicates + excs = [ + { + "name": name, + "product": prod, + "location": location, + "unit": unit, + "type": "technosphere", + "amount": sum([exc["amount"] for exc in exchanges]), + } + for (name, prod, location, unit), exchanges in groupby( + sorted(excs, key=itemgetter("name", "product", "location", "unit")), + key=itemgetter("name", "product", "location", "unit"), + ) + ] + + return excs + + def get_weighted_average_distance(self, country, metal): + return self.metals_transport.loc[ + (self.metals_transport["country"] == country) + & (self.metals_transport["Metal"] == self.alt_names[metal]), + ["TransportMode Label", "Weighted Average Distance"], + ] + def create_metal_markets(self): self.post_allocation_correction()