From 398d7d01a85c9f7132f68d504a29801eb48b58a9 Mon Sep 17 00:00:00 2001 From: paulboosz Date: Mon, 16 Sep 2024 16:32:05 +0200 Subject: [PATCH] first commit object export --- data/object/activities.json | 222 ++++++++++++++++++++++++++++++++++++ data/object/export.py | 157 +++++++++++++++++++++++++ 2 files changed, 379 insertions(+) create mode 100644 data/object/activities.json create mode 100644 data/object/export.py diff --git a/data/object/activities.json b/data/object/activities.json new file mode 100644 index 000000000..6d5bbad5a --- /dev/null +++ b/data/object/activities.json @@ -0,0 +1,222 @@ +[ + { + "name": "Sawnwood, board, hardwood, dried (u=10%), planed {Europe without Switzerland}| market for sawnwood, board, hardwood, dried (u=10%), planed | Cut-off, S", + "step_usage": "Production des composants", + "Nom français": "Planche (bois de feuillus)", + "Nom anglais": "Beam (hardwood)", + "unit": "m3", + "density": 600, + "impacts": { "ecs": 22733 }, + "source": "Ecoinvent", + "comment": "" + }, + { + "name": "Sawnwood, board, softwood, dried (u=10%), planed {Europe without Switzerland}| market for sawnwood, board, softwood, dried (u=10%), planed | Cut-off, S", + "step_usage": "Production des composants", + "Nom français": "Planche (bois de résineux)", + "Nom anglais": "Beam (softwood)", + "unit": "m3", + "density": 450, + "impacts": { "ecs": 17202 }, + "source": "Ecoinvent", + "comment": "" + }, + { + "name": "Oriented strand board {RER}| market for oriented strand board | APOS, S", + "step_usage": "Production des composants", + "Nom français": "Panneau (OSB)", + "Nom anglais": "Oriented strand board (OSB)", + "unit": "m3", + "density": 607, + "impacts": { "ecs": 64851 }, + "source": "Ecoinvent", + "comment": "" + }, + { + "name": "Plywood {RER}| market for plywood | APOS, S", + "step_usage": "Production des composants", + "Nom français": "Panneau (Contreplaqué)", + "Nom anglais": "Plywood", + "unit": "m3", + "density": 500, + "impacts": { "ecs": 78973 }, + "source": "Ecoinvent", + "comment": "" + }, + { + "name": "Particleboard, uncoated {RER}| market for particleboard, uncoated | APOS, S", + "step_usage": "Production des composants", + "Nom français": "Panneau (aggloméré sans revêtement)", + "Nom anglais": "Particleboard (uncoated)", + "unit": "m3", + "density": 637, + "impacts": { "ecs": 46808 }, + "source": "Ecoinvent", + "comment": "" + }, + { + "name": "Medium density fibreboard {RER}| market for medium density fibreboard | APOS, S", + "step_usage": "Production des composants", + "Nom français": "Panneau (MDF)", + "Nom anglais": "Medium density fibreboard (MDF)", + "unit": "m3", + "density": 750, + "impacts": { "ecs": 81372 }, + "source": "Ecoinvent", + "comment": "" + }, + { + "name": "Glued laminated timber, average glue mix {Europe without Switzerland}| market for glued laminated timber, average glue mix | APOS, S", + "step_usage": "Production des composants", + "Nom français": "Panneau (lamellé-collé)", + "Nom anglais": "Glued laminated timber", + "unit": "m3", + "density": 500, + "impacts": { "ecs": 90814 }, + "source": "Ecoinvent", + "comment": "" + }, + { + "name": "Cross-laminated timber {RER}| market for cross-laminated timber | APOS, S", + "step_usage": "Production des composants", + "Nom français": "Panneau (lamellé-croisé)", + "Nom anglais": "Cross laminated timber", + "unit": "m3", + "density": 500, + "impacts": { "ecs": 31444 }, + "source": "Ecoinvent", + "comment": "" + }, + { + "name": "Fibreboard, hard {RER}| market for fibreboard, hard | APOS, U", + "step_usage": "Production des composants", + "Nom français": "Panneau (fibres dures)", + "Nom anglais": "Fibreboard (hard)", + "unit": "m3", + "density": 956, + "impacts": { "ecs": 102394 }, + "source": "Ecoinvent", + "comment": "" + }, + { + "name": "Fibreboard, soft {RER}| market for fibreboard, soft | APOS, S", + "step_usage": "Production des composants", + "Nom français": "Panneau (fibres tendres)", + "Nom anglais": "Fibreboard (soft)", + "unit": "m3", + "density": 159, + "impacts": { "ecs": 98524 }, + "source": "Ecoinvent", + "comment": "" + }, + { + "name": "Corrugated board box {RER}| market for corrugated board box | APOS, S", + "step_usage": "Production des composants", + "Nom français": "Emballage (carton ondulé)", + "Nom anglais": "Corrugated board box", + "unit": "kg", + "density": 400, + "impacts": { "ecs": 103 }, + "source": "Ecoinvent", + "comment": "" + }, + { + "name": "Packaging film, low density polyethylene {GLO}| market for | APOS, U", + "step_usage": "Production des composants", + "Nom français": "Emballage (film plastique)", + "Nom anglais": "Packaging film (low density PET)", + "unit": "kg", + "density": 940, + "impacts": { "ecs": 289 }, + "source": "Ecoinvent", + "comment": "" + }, + { + "name": "Polyurethane, flexible foam {RER}| market for polyurethane, flexible foam | APOS, U", + "step_usage": "Production des composants", + "Nom français": "Mousse PUR (flexible)", + "Nom anglais": "Mousse PUR (flexible)", + "unit": "kg", + "density": 80, + "impacts": { "ecs": 1058 }, + "source": "Ecoinvent", + "comment": "" + }, + { + "name": "Polyurethane, rigid foam {RER}| market for polyurethane, rigid foam | APOS, U", + "step_usage": "Production des composants", + "Nom français": "Mousse PUR (rigide)", + "Nom anglais": "Mousse PUR (rigide)", + "unit": "kg", + "density": 35, + "impacts": { "ecs": 1646 }, + "source": "Ecoinvent", + "comment": "" + }, + { + "name": "Acrylic filler {RER}| market for acrylic filler | Cut-off, U", + "step_usage": "Production des composants", + "Nom français": "Mastic (acrylique)", + "Nom anglais": "Acrylic filler", + "unit": "kg", + "density": 750, + "impacts": { "ecs": 57 }, + "source": "Ecoinvent", + "comment": "" + }, + { + "name": "Synthetic rubber {GLO}| market for | APOS, U", + "step_usage": "Production des composants", + "Nom français": "Caoutchouc", + "Nom anglais": "Synthetic rubber", + "unit": "kg", + "density": 1000, + "impacts": { "ecs": 341 }, + "source": "Ecoinvent", + "comment": "" + }, + { + "name": "Plastic frame (PE)", + "step_usage": "Production des composants", + "Nom français": "Composant en plastique (PE)", + "Nom anglais": "Plastic frame (PE)", + "unit": "kg", + "density": 920, + "impacts": { "ecs": 325 }, + "source": "Ecobalyse", + "comment": "modélisation d'un composant générique (+ d'info dans la Doc)" + }, + { + "name": "Plastic frame (PP)", + "step_usage": "Production des composants", + "Nom français": "Composant en plastique (PP)", + "Nom anglais": "Plastic frame (PP)", + "unit": "kg", + "density": 900, + "impacts": { "ecs": 382 }, + "source": "Ecobalyse", + "comment": "modélisation d'un composant générique (+ d'info dans la Doc)" + }, + { + "name": "Metal frame (aluminium)", + "step_usage": "Production des composants", + "Nom français": "Composant en aluminium", + "Nom anglais": "Metal frame (aluminium)", + "unit": "kg", + "density": 2700, + "impacts": { "ecs": 1150 }, + "source": "Ecobalyse", + "comment": "modélisation d'un composant générique (+ d'info dans la Doc)" + }, + { + "name": "Metal frame (steel)", + "step_usage": "Production des composants", + "Nom français": "Composant en acier", + "Nom anglais": "Metal frame (steel)", + "unit": "kg", + "density": 7800, + "impacts": { "ecs": 501 }, + "source": "Ecobalyse", + "comment": "modélisation d'un composant générique (+ d'info dans la Doc)" + } +] diff --git a/data/object/export.py b/data/object/export.py new file mode 100644 index 000000000..779ed1599 --- /dev/null +++ b/data/object/export.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python +# coding: utf-8 + +"""Export des ingrédients et des processes de l'objet""" + +import json +import os +import sys +import urllib.parse +from os.path import dirname + +import bw2calc +import requests +from bw2data.project import projects +from common.export import ( + cached_search, + export_json, + load_json, + progress_bar, + spproject, + with_corrected_impacts, + with_subimpacts, +) +from common.impacts import bytrigram, main_method +from common.impacts import impacts as definitions +from frozendict import frozendict + +PROJECT_ROOT_DIR = dirname(dirname(dirname(__file__))) +ECOBALYSE_DATA_DIR = os.environ.get("ECOBALYSE_DATA_DIR") +if not ECOBALYSE_DATA_DIR: + print( + "\n🚨 ERROR: For the export to work properly, you need to specify ECOBALYSE_DATA_DIR env variable. It needs to point to the https://github.com/MTES-MCT/ecobalyse-private/ repository. Please, edit your .env file accordingly." + ) + sys.exit(1) + +# Configuration +CONFIG = { + "PROJECT": "default", + "ECOINVENT": "Ecoinvent 3.9.1", + "BIOSPHERE": "biosphere3", + "ACTIVITIES_FILE": f"{PROJECT_ROOT_DIR}/data/object/activities.json", + "COMPARED_IMPACTS_FILE": f"{PROJECT_ROOT_DIR}/data/object/compared_impacts.csv", + "IMPACTS_FILE": f"{PROJECT_ROOT_DIR}/public/data/impacts.json", + "PROCESSES_FILE": f"{ECOBALYSE_DATA_DIR}/data/object/processes_impacts.json", +} +with open(CONFIG["IMPACTS_FILE"]) as f: + IMPACTS_DEF_ECOBALYSE = json.load(f) + + +def find_id(dbname, activity): + return cached_search(dbname, activity["search"]).get( + "Process identifier", activity["id"] + ) + + +def compute_simapro_impacts(activity, method): + strprocess = urllib.parse.quote(activity["name"], encoding=None, errors=None) + project = urllib.parse.quote(spproject(activity), encoding=None, errors=None) + method = urllib.parse.quote(main_method, encoding=None, errors=None) + return bytrigram( + definitions, + json.loads( + requests.get( + f"http://simapro.ecobalyse.fr:8000/impact?process={strprocess}&project={project}&method={method}" + ).content + ), + ) + + +def compute_brightway_impacts(activity, method): + results = dict() + lca = bw2calc.LCA({activity: 1}) + lca.lci() + for key, method in definitions.items(): + lca.switch_method(method) + lca.lcia() + results[key] = float("{:.10g}".format(lca.score)) + return results + + +def compute_impacts(processes_fd): + """Add impacts to processes dictionary + + Args: + processes_fd (frozendict): dictionary of processes of which we want to compute the impacts + Returns: + dictionary of processes with impacts. Example : + + {"sunflower-oil-organic": { + "id": "sunflower-oil-organic", + name": "...", + "impacts": { + "acd": 3.14, + ... + "ecs": 34.3, + }, + "unit": ... + }, + "tomato":{ + ... + } + """ + processes = dict(processes_fd) + print("Computing impacts:") + for index, (_, process) in enumerate(processes.items()): + progress_bar(index, len(processes)) + # simapro + activity = cached_search( + process.get("source", CONFIG["ECOINVENT"]), process["search"] + ) + results = compute_simapro_impacts(activity, main_method) + # WARNING assume remote is in m3 or MJ (couldn't find unit from COM intf) + if process["unit"] == "kilowatt hour" and isinstance(results, dict): + results = {k: v * 3.6 for k, v in results.items()} + if process["unit"] == "litre" and isinstance(results, dict): + results = {k: v / 1000 for k, v in results.items()} + + process["impacts"] = results + + if isinstance(results, dict) and results: + # simapro succeeded + process["impacts"] = results + print(f"got impacts from simapro for: {process['name']}") + else: + # simapro failed (unexisting Ecobalyse project or some other reason) + # brightway + process["impacts"] = compute_brightway_impacts(activity, main_method) + print(f"got impacts from brightway for: {process['name']}") + + # compute subimpacts + process["impacts"] = with_subimpacts(process["impacts"]) + + # remove unneeded attributes + for attribute in ["search"]: + if attribute in process: + del process[attribute] + + return frozendict({k: frozendict(v) for k, v in processes.items()}) + + +if __name__ == "__main__": + projects.set_current(CONFIG["PROJECT"]) + # bw2data.config.p["biosphere_database"] = CONFIG["BIOSPHERE"] + + # keep the previous processes with old impacts + # oldprocesses = load_json(CONFIG["PROCESSES_FILE"]) + activities = tuple(load_json(CONFIG["ACTIVITIES_FILE"])) + + processes_impacts = compute_impacts(activities) + + processes_corrected_impacts = with_corrected_impacts( + IMPACTS_DEF_ECOBALYSE, processes_impacts + ) + + # Export + # display_changes("id", oldprocesses, processes_corrected_impacts) + export_json(list(processes_corrected_impacts.values()), CONFIG["PROCESSES_FILE"])