#!/usr/bin/env python """Load the food-processing gate-to-gate LCI inventories into a Brightway project. For an EXTERNAL user (e.g. Claudio Beretta / ZHAW FCM, in Brightway or Firefly). Imports the layered, self-contained `food_processing_inventory.bw2package` (BAFU/UVEK OGD closure — NO ecoinvent, NO biosphere3, NO EDB) and registers the LCIA methods from `food_processing_methods_cf.json` (keyed by each elementary flow's original (database, code), so they bind to the freshly-imported bafu_biosphere flows regardless of new internal ids). The package keeps full provenance alive as SEPARATE databases — nothing is flattened: food_processing_replacements one node per shipped ecoinvent reference; each carries its FoodOn/LangUaL/FoodEx2 identity in `sediment.terms` and the five inline `provenance` facets. Computing a node yields the full technosphere+biosphere supply chain. food_processing_lci, fruit_crops_lci, ... (the closure subset, by name) bafu BAFU/UVEK 2025 technosphere (closure subset) bafu_biosphere BAFU/UVEK + EF 3.1 elementary flows After loading you can compute any node against EF 3.1 (16 categories), GLAM, UBP, or GWP100 with zero further prerequisites. Usage: python load_food_processing.py --project my_project \ --package food_processing_inventory.bw2package \ --methods food_processing_methods_cf.json """ from __future__ import annotations import argparse import json import sys from pathlib import Path HERE = Path(__file__).parent TOP_DB = "food_processing_replacements" def main() -> int: ap = argparse.ArgumentParser() ap.add_argument("--project", required=True) ap.add_argument("--package", default=str(HERE / "food_processing_inventory.bw2package")) ap.add_argument("--methods", default=str(HERE / "food_processing_methods_cf.json")) args = ap.parse_args() import bw2data import bw2io bw2data.projects.set_current(args.project) if TOP_DB not in bw2data.databases: # The tributary middle layers cross-link cyclically (e.g. dairy_lci butter # -> livestock_lci milk, and back), so bw2io's default per-database import # (each Database.write triggers process() + cross-db exchange validation # while a dependency layer is still unwritten) fails with UnknownObject. # Replicate BW2Package._create_obj but DEFER process(): write every layer # with process=False FIRST so all nodes exist, then process() all. loaded = bw2io.BW2Package.load_file(args.package) instances = [] for data in loaded: inst = data["class"](data["name"]) if data["name"] not in inst._metadata: inst.register(**data["metadata"]) else: inst.backup() inst.metadata = data["metadata"] inst.write(data["data"], process=False) instances.append(inst) for inst in instances: inst.process() print("[load] imported layered package (deferred-process):") for db in bw2data.databases: print(f"[load] {db}: {len(bw2data.Database(db))} nodes") else: print(f"[load] {TOP_DB} already present — skipping import") # Rebind method CFs to the imported flows by original (database, code). methods = json.loads(Path(args.methods).read_text()) code_index: dict[tuple, int] = {} for db in bw2data.databases: for act in bw2data.Database(db): code_index[(db, act["code"])] = act.id registered = 0 for label, payload in methods.items(): # v2 schema: {unit, description, cfs}; legacy: bare list of [key, cf]. if isinstance(payload, dict): unit = payload.get("unit", "") description = payload.get("description", "") raw_cfs = payload.get("cfs", []) else: unit, description, raw_cfs = "", "", payload cfs = [] for (flow_key, cf) in raw_cfs: fk = tuple(flow_key) bw_id = code_index.get(fk) if bw_id is not None: cfs.append((bw_id, cf)) if not cfs: continue method_tuple = tuple(label.split(" | ")) if method_tuple in bw2data.methods: bw2data.Method(method_tuple).deregister() m = bw2data.Method(method_tuple) m.register(unit=unit, description=description) m.write(cfs) registered += 1 print(f"[load] registered {registered} LCIA methods (EF 3.1 / GLAM / UBP / GWP100)") print("[load] done. Example — compute a processing node:") print(f"[load] import bw2data, bw2calc as bc") print(f"[load] act = next(a for a in bw2data.Database('{TOP_DB}') " f"if 'tomato' in (a.get('name') or '').lower())") print(f"[load] lca = bc.LCA({{act:1}}, ('EF 3.1 (BAFU)','Climate Change'))") print(f"[load] lca.lci(); lca.lcia(); print(lca.score, 'kg CO2eq/kg')") return 0 if __name__ == "__main__": sys.exit(main())