From 4c8ba520f28c133e5a2cae6dbb17c7c37749fb48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20P=C3=A9ralta?= Date: Wed, 15 Oct 2025 10:47:55 +0200 Subject: [PATCH 1/5] (fix) Do not longer evaluate params at build time. (#40) * (fix) Do not longer evaluate params at build time. Fix sample and end to end test data. Update end to end test to run the built impact model. * (chore) Bump apparun requirement version * (fix) Use approx instead of equal in end to end test * (update) Change parameters declaration to ensure no persistence between runs. Add two tests to detect unknown parameters in default values. --- appabuild/config/lca.py | 14 -- appabuild/database/databases.py | 25 +--- appabuild/exceptions.py | 6 + appabuild/model/builder.py | 134 +++++++++++++----- pyproject.toml | 2 +- requirements.txt | 2 +- samples/conf/nvidia_ai_gpu_chip_lca_conf.yaml | 16 ++- .../nvidia_ai_gpu/nvidia_ai_gpu_chip.yaml | 2 +- .../nvidia_gpu_die_manufacturing.yaml | 4 +- .../valids/appalca_conf_lca_confs.yaml | 5 + tests/data/cmd_build/appalca_conf.yaml | 2 +- tests/data/cmd_build/expected_scores.yaml | 3 + .../nvidia_ai_gpu_chip.yaml | 2 +- .../nvidia_gpu_die_manufacturing.yaml | 4 +- .../nvidia_ai_gpu_chip_expected.yaml | 78 +++++----- .../nvidia_ai_gpu_chip_lca_conf.yaml | 34 +++-- .../valids/ai_use_phase.yaml | 19 +++ .../valids/electricity_no_ei.yaml | 25 ++++ .../parameter_in_default_missing_in_conf.yaml | 50 +++++++ .../invalids/parameter_missing_in_conf.yaml | 36 +++++ tests/end_to_end/test_cmd_build.py | 20 ++- tests/functional_no_db/test_lca_conf.py | 37 +++++ 22 files changed, 380 insertions(+), 140 deletions(-) create mode 100644 tests/data/appalca_confs/valids/appalca_conf_lca_confs.yaml create mode 100644 tests/data/cmd_build/expected_scores.yaml create mode 100644 tests/data/foreground_datasets/valids/ai_use_phase.yaml create mode 100644 tests/data/foreground_datasets/valids/electricity_no_ei.yaml create mode 100644 tests/data/lca_confs/invalids/parameter_in_default_missing_in_conf.yaml create mode 100644 tests/data/lca_confs/invalids/parameter_missing_in_conf.yaml diff --git a/appabuild/config/lca.py b/appabuild/config/lca.py index d940aae..5a3b4b8 100644 --- a/appabuild/config/lca.py +++ b/appabuild/config/lca.py @@ -99,20 +99,6 @@ def parse_parameters(cls, parameters: List[dict]) -> List[ImpactModelParam]: return parsed_parameters - @field_validator("parameters", mode="after") - @classmethod - def eval_exprs(cls, parameters: List[ImpactModelParam]) -> List[ImpactModelParam]: - exprs_set = ParamsValuesSet.build( - {param.name: param.default for param in parameters}, - ImpactModelParams.from_list(parameters), - ) - - values = exprs_set.evaluate() - for param in parameters: - param.update_default(values[param.name]) - - return parameters - def dump_parameters(self) -> List[dict]: return list(map(lambda p: p.model_dump(), self.parameters)) diff --git a/appabuild/database/databases.py b/appabuild/database/databases.py index aaa0a6a..95b94ec 100644 --- a/appabuild/database/databases.py +++ b/appabuild/database/databases.py @@ -1,6 +1,5 @@ """ Contains classes to import LCI databases in Brightway project. -Initialize parameters_registry object which must be filled before Impact Model build. """ from __future__ import annotations @@ -9,12 +8,11 @@ import json import os import re -from collections import ChainMap -from typing import Dict, List, Optional +from typing import Optional import brightway2 as bw import yaml -from apparun.parameters import ImpactModelParam +from apparun.parameters import ImpactModelParams from lca_algebraic import resetParams, setForeground from lxml.etree import XMLSyntaxError from pydantic_core import ValidationError @@ -25,8 +23,6 @@ from appabuild.exceptions import BwDatabaseError, SerializedDataError from appabuild.logger import log_validation_error, logger -parameters_registry = {} - class Database: """ @@ -219,12 +215,12 @@ def __init__(self, name, path): """ Database.__init__(self, name, path) self.fu_name = "" - self.parameters = {} + self.parameters = None self.context = UserDatabaseContext( serialized_activities=[], activities=[], database=BwDatabase(name=name) ) - def set_functional_unit(self, fu_name: str, parameters: dict): + def set_functional_unit(self, fu_name: str, parameters: ImpactModelParams): self.fu_name = fu_name self.parameters = parameters @@ -278,7 +274,6 @@ def execute_at_startup(self): self.find_activities_on_disk() def execute_at_build_time(self): - self.declare_parameters() self.import_in_project() def import_in_project(self) -> None: @@ -313,15 +308,3 @@ def import_in_project(self) -> None: ] bw_database.write(dict(to_write_activities)) setForeground(self.name) - - def declare_parameters(self) -> None: - """ - Initialize each object's parameter as a ImpactModelParam and store it in - parameters_registry object. parameters_registry is used to build Impact Model's - parameters section. - :return: - """ - for parameter in self.parameters: - parameters_registry[parameter["name"]] = ImpactModelParam.from_dict( - parameter - ) diff --git a/appabuild/exceptions.py b/appabuild/exceptions.py index 5c665e5..564a3e1 100644 --- a/appabuild/exceptions.py +++ b/appabuild/exceptions.py @@ -17,3 +17,9 @@ class SerializedDataError(Exception): """Raised when any problem concerning yaml/json dataset is encountered.""" pass + + +class ParameterError(Exception): + """Raised when any problem concerning impact model parameterization is encountered.""" + + pass diff --git a/appabuild/model/builder.py b/appabuild/model/builder.py index 33ddfb8..6e39d83 100644 --- a/appabuild/model/builder.py +++ b/appabuild/model/builder.py @@ -6,6 +6,7 @@ from __future__ import annotations import itertools +import logging import os import types from collections import OrderedDict @@ -28,13 +29,19 @@ _multiLCAWithCache, _replace_fixed_params, ) -from lca_algebraic.params import _fixed_params, newEnumParam, newFloatParam +from lca_algebraic.params import ( + _fixed_params, + _param_registry, + newEnumParam, + newFloatParam, +) from pydantic import ValidationError from sympy import Expr, simplify, symbols, sympify +from sympy.parsing.sympy_parser import parse_expr from appabuild.config.lca import LCAConfig -from appabuild.database.databases import ForegroundDatabase, parameters_registry -from appabuild.exceptions import BwDatabaseError, BwMethodError +from appabuild.database.databases import ForegroundDatabase +from appabuild.exceptions import BwDatabaseError, BwMethodError, ParameterError from appabuild.logger import logger act_symbols = {} # Cache of act = > symbol @@ -76,7 +83,7 @@ def __init__( output_path: str, metadata: Optional[ModelMetadata] = ModelMetadata(), compile_models: bool = True, - parameters: Optional[dict] = None, + parameters: Optional[ImpactModelParams] = None, ): """ Initialize the model builder @@ -110,20 +117,16 @@ def from_yaml(lca_config_path: str) -> ImpactModelBuilder: lca_config = LCAConfig.from_yaml(lca_config_path) builder = ImpactModelBuilder( - lca_config.scope.fu.database, # lca_config["scope"]["fu"]["database"], - lca_config.scope.fu.name, # lca_config["scope"]["fu"]["name"], - lca_config.scope.methods, # lca_config["scope"]["methods"], - # os.path.join( - # lca_config["outputs"]["model"]["path"], - # f"{lca_config['outputs']['model']['name']}.yaml", - # ), + lca_config.scope.fu.database, + lca_config.scope.fu.name, + lca_config.scope.methods, os.path.join( lca_config.model.path, lca_config.model.name + ".yaml", ), - lca_config.model.metadata, # lca_config["outputs"]["model"]["metadata"], - lca_config.model.compile, # lca_config["outputs"]["model"]["compile"], - lca_config.model.dump_parameters(), # lca_config["outputs"]["model"]["parameters"], + lca_config.model.metadata, + lca_config.model.compile, + ImpactModelParams.from_list(lca_config.model.parameters), ) return builder @@ -183,6 +186,9 @@ def build_impact_tree_and_parameters( method format is Appa Run method keys. :return: root node (corresponding to the reference flow) and used parameters. """ + # lcaa param registry can be populated if a model has already been built + _param_registry().clear() + methods_bw = [to_bw_method(MethodFullName[method]) for method in methods] tree = ImpactTreeNode( name=functional_unit_bw["name"], @@ -192,7 +198,45 @@ def build_impact_tree_and_parameters( # print("computing model to expression for %s" % model) self.actToExpression(functional_unit_bw, tree) - # Find required parameters by inspecting symbols + # Check if each symbol corresponds to a known parameter + + # TODO move that in a FloatParam method + params_in_default = [ + parameter.default + for parameter in self.parameters + if parameter.type == "float" + and ( + isinstance(parameter.default, str) + or isinstance(parameter.default, dict) + ) + ] + while ( + len( + [ + parameter + for parameter in params_in_default + if isinstance(parameter, dict) + ] + ) + > 0 + ): + params_in_default_str = [ + parameter + for parameter in params_in_default + if isinstance(parameter, str) + ] + params_in_default_dict = [ + [value for value in parameter.values()] + for parameter in params_in_default + if isinstance(parameter, dict) + ] + params_in_default = ( + list(itertools.chain.from_iterable(params_in_default_dict)) + + params_in_default_str + ) + params_in_default = [ + parameter for parameter in params_in_default if isinstance(parameter, str) + ] # there can be int params at this point free_symbols = set( list( itertools.chain.from_iterable( @@ -211,19 +255,29 @@ def build_impact_tree_and_parameters( ] ) ) + + [ + str(symb) + for symb in list( + itertools.chain.from_iterable( + [ + parse_expr(params_in_default).free_symbols + for params_in_default in params_in_default + ] + ) + ) + ] ) + activity_symbols = set([str(symb["symbol"]) for _, symb in act_symbols.items()]) expected_parameter_symbols = free_symbols - activity_symbols - known_parameters = ImpactModelParams.from_list(parameters_registry.values()) - forbidden_parameter_names = list( itertools.chain( *[ [ elem.name - for elem in known_parameters.find_corresponding_parameter( + for elem in self.parameters.find_corresponding_parameter( activity_symbol, must_find_one=False ) ] @@ -234,35 +288,39 @@ def build_impact_tree_and_parameters( try: if len(forbidden_parameter_names) > 0: - raise ValueError( + raise ParameterError( f"Parameter names {forbidden_parameter_names} are forbidden as they " f"correspond to background activities." ) - except ValueError: - logger.exception("ValueError") - raise - - used_parameters = [ - known_parameters.find_corresponding_parameter(expected_parameter_symbol) - for expected_parameter_symbol in expected_parameter_symbols - ] - unique_used_parameters = [] - [ - unique_used_parameters.append(i) - for i in used_parameters - if i not in unique_used_parameters - ] - unique_used_parameters = ImpactModelParams.from_list(unique_used_parameters) + except ParameterError as e: + logger.exception(e) + raise ParameterError(e) + for expected_parameter_symbol in expected_parameter_symbols: + try: + self.parameters.find_corresponding_parameter(expected_parameter_symbol) + except ValueError: + e = ( + f"ValueError : {expected_parameter_symbol} is required in the impact" + f" model but is unknown in the config. Please check in the LCA " + f"config." + ) + logger.error(e) + raise ParameterError(e) # Declare used parameters in conf file as a lca_algebraic parameter to enable # model building (will not be used afterwards) - for parameter in unique_used_parameters: + + for parameter in self.parameters: + if parameter.name in _param_registry().keys(): + e = f"Parameter {parameter.name} already in lcaa registry." + logging.error(e) + raise ParameterError(e) if isinstance(parameter, FloatParam): newFloatParam( name=parameter.name, default=parameter.default, save=False, - dbname="", + dbname=self.user_database_name, min=0.0, ) if isinstance(parameter, EnumParam): @@ -270,7 +328,7 @@ def build_impact_tree_and_parameters( name=parameter.name, values=parameter.weights, default=parameter.default, - dbname="", + dbname=self.user_database_name, ) # Create dummy reference to biosphere @@ -299,7 +357,7 @@ def build_impact_tree_and_parameters( } ) node.direct_impacts[method] = model_expr.xreplace(sub) - return tree, unique_used_parameters + return tree, self.parameters @staticmethod @with_db_context diff --git a/pyproject.toml b/pyproject.toml index 0676e19..48184c1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,7 +42,7 @@ dependencies = [ "kaleido", "tqdm", "ruamel.yaml", - "apparun==0.3.6", + "apparun==0.3.7", "typer==0.15.1", "ipython>=7.6.0,<=8.34.0", "mermaid-py==0.7.1" diff --git a/requirements.txt b/requirements.txt index 6a58470..724a69e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -20,7 +20,7 @@ aenum kaleido tqdm ruamel.yaml -apparun==0.3.6 +apparun==0.3.7 ipython>=7.6.0,<=8.34.0 pre-commit hatchling diff --git a/samples/conf/nvidia_ai_gpu_chip_lca_conf.yaml b/samples/conf/nvidia_ai_gpu_chip_lca_conf.yaml index 0b32752..e5a4ee3 100644 --- a/samples/conf/nvidia_ai_gpu_chip_lca_conf.yaml +++ b/samples/conf/nvidia_ai_gpu_chip_lca_conf.yaml @@ -20,14 +20,17 @@ model: report: link: https://appalca.github.io/ description: "A mock example of Appa LCA's impact model corresponding to a fictive AI chip accelerator based on NVIDIA GPU." - date: 03/11/2023 + date: 07/10/2025 version: "1" license: proprietary - appabuild_version: "0.2" + appabuild_version: "0.3.6" parameters: - name: cuda_core type: float - default: 512 + default: + architecture: + Maxwell: 1344 + Pascal: 1280 min: 256 max: 4096 - name: architecture @@ -44,7 +47,10 @@ model: EU: 1 - name: energy_per_inference type: float - default: 110.0 + default: + architecture: #we model energy by inference using a model of the TDP function of the number of cuda cores. We suppose that TDP power corresponds to running inferences at 90fps. + Maxwell: "0.0878*cuda_core*1000/(90*3600)" + Pascal: "0.0679*cuda_core*1000/(90*3600)" pm_perc: 0.2 - name: lifespan type: float @@ -52,6 +58,6 @@ model: pm: 1 - name: inference_per_day type: float - default: 86400000 + default: "30*3600*8" #30fps 8 hours a day min: 0 max: 86400000 \ No newline at end of file diff --git a/samples/datasets/user_database/nvidia_ai_gpu/nvidia_ai_gpu_chip.yaml b/samples/datasets/user_database/nvidia_ai_gpu/nvidia_ai_gpu_chip.yaml index ead8610..9aa9783 100644 --- a/samples/datasets/user_database/nvidia_ai_gpu/nvidia_ai_gpu_chip.yaml +++ b/samples/datasets/user_database/nvidia_ai_gpu/nvidia_ai_gpu_chip.yaml @@ -7,7 +7,7 @@ parameters: - cuda_core - architecture # Only Maxwell or Pascal supported - usage_location # Only FR or EU supported -- energy_per_inference # In mW +- energy_per_inference # In mWh - inference_per_day - lifespan # in years include_in_tree: True diff --git a/samples/datasets/user_database/nvidia_ai_gpu/nvidia_gpu_die_manufacturing.yaml b/samples/datasets/user_database/nvidia_ai_gpu/nvidia_gpu_die_manufacturing.yaml index 0336144..f5a0715 100644 --- a/samples/datasets/user_database/nvidia_ai_gpu/nvidia_gpu_die_manufacturing.yaml +++ b/samples/datasets/user_database/nvidia_ai_gpu/nvidia_gpu_die_manufacturing.yaml @@ -17,14 +17,14 @@ exchanges: options: - name: Pascal parameters_matching: - defect_density: 0.05 + defect_density: 0.0005 technology_node: 16 # is actually 14 for 2 chips, and 16 for 4 chips. fab_location: TW: 1 area: 0.13184623155305694*cuda_core + 21.707425626610416 - name: Maxwell # also includes Maxwell 2.0 parameters_matching: - defect_density: 0.02 + defect_density: 0.0001 technology_node: 28 fab_location: TW: 1 diff --git a/tests/data/appalca_confs/valids/appalca_conf_lca_confs.yaml b/tests/data/appalca_confs/valids/appalca_conf_lca_confs.yaml new file mode 100644 index 0000000..8ae15b9 --- /dev/null +++ b/tests/data/appalca_confs/valids/appalca_conf_lca_confs.yaml @@ -0,0 +1,5 @@ +project_name: cmd_build_lca_confs_test +databases: + foreground: + name: user_database + path: './tests/data/foreground_datasets/valids/' diff --git a/tests/data/cmd_build/appalca_conf.yaml b/tests/data/cmd_build/appalca_conf.yaml index fdc6e70..01b579c 100644 --- a/tests/data/cmd_build/appalca_conf.yaml +++ b/tests/data/cmd_build/appalca_conf.yaml @@ -1,4 +1,4 @@ -project_name: cmd_build_ete_test2 +project_name: cmd_build_ete_test databases: foreground: name: user_database diff --git a/tests/data/cmd_build/expected_scores.yaml b/tests/data/cmd_build/expected_scores.yaml new file mode 100644 index 0000000..eb1b181 --- /dev/null +++ b/tests/data/cmd_build/expected_scores.yaml @@ -0,0 +1,3 @@ +ai_use_phase: 4.8272749055999995 +nvidia_ai_gpu_chip: 13.988936117453397 +nvidia_gpu_chip_manufacturing: 9.161661211853398 diff --git a/tests/data/cmd_build/foreground_datasets/nvidia_ai_gpu_chip.yaml b/tests/data/cmd_build/foreground_datasets/nvidia_ai_gpu_chip.yaml index ead8610..9aa9783 100644 --- a/tests/data/cmd_build/foreground_datasets/nvidia_ai_gpu_chip.yaml +++ b/tests/data/cmd_build/foreground_datasets/nvidia_ai_gpu_chip.yaml @@ -7,7 +7,7 @@ parameters: - cuda_core - architecture # Only Maxwell or Pascal supported - usage_location # Only FR or EU supported -- energy_per_inference # In mW +- energy_per_inference # In mWh - inference_per_day - lifespan # in years include_in_tree: True diff --git a/tests/data/cmd_build/foreground_datasets/nvidia_gpu_die_manufacturing.yaml b/tests/data/cmd_build/foreground_datasets/nvidia_gpu_die_manufacturing.yaml index 0336144..f5a0715 100644 --- a/tests/data/cmd_build/foreground_datasets/nvidia_gpu_die_manufacturing.yaml +++ b/tests/data/cmd_build/foreground_datasets/nvidia_gpu_die_manufacturing.yaml @@ -17,14 +17,14 @@ exchanges: options: - name: Pascal parameters_matching: - defect_density: 0.05 + defect_density: 0.0005 technology_node: 16 # is actually 14 for 2 chips, and 16 for 4 chips. fab_location: TW: 1 area: 0.13184623155305694*cuda_core + 21.707425626610416 - name: Maxwell # also includes Maxwell 2.0 parameters_matching: - defect_density: 0.02 + defect_density: 0.0001 technology_node: 28 fab_location: TW: 1 diff --git a/tests/data/cmd_build/nvidia_ai_gpu_chip_expected.yaml b/tests/data/cmd_build/nvidia_ai_gpu_chip_expected.yaml index ea33b4d..1d8d74d 100644 --- a/tests/data/cmd_build/nvidia_ai_gpu_chip_expected.yaml +++ b/tests/data/cmd_build/nvidia_ai_gpu_chip_expected.yaml @@ -11,10 +11,10 @@ metadata: link: https://appalca.github.io/ description: A mock example of Appa LCA's impact model corresponding to a fictive AI chip accelerator based on NVIDIA GPU. - date: 03/11/2023 + date: 07/10/2025 version: '1' license: proprietary - appabuild_version: '0.2' + appabuild_version: 0.3.6 parameters: - name: architecture default: Maxwell @@ -23,39 +23,45 @@ parameters: Maxwell: 1.0 Pascal: 1.0 - name: cuda_core - default: 512.0 + default: + architecture: + Maxwell: 1344 + Pascal: 1280 type: float - min: 460.8 - max: 563.2 + min: 256.0 + max: 4096.0 distrib: linear pm: null - pm_perc: 0.1 + pm_perc: null - name: energy_per_inference - default: 0.05 + default: + architecture: + Maxwell: 0.0878*cuda_core*1000/(90*3600) + Pascal: 0.0679*cuda_core*1000/(90*3600) type: float - min: 0.01 - max: 0.1 + min: null + max: null distrib: linear pm: null - pm_perc: null + pm_perc: 0.2 - name: inference_per_day - default: 3600.0 + default: 30*3600*8 type: float - min: 3240.0 - max: 3960.0 + min: 0.0 + max: 86400000.0 distrib: linear pm: null - pm_perc: 0.1 + pm_perc: null - name: lifespan - default: 3.6 + default: 2.0 type: float - min: 3.24 - max: 3.96 + min: null + max: null distrib: linear - pm: null - pm_perc: 0.1 + pm: 1.0 + pm_perc: null - name: usage_location - default: EU + default: FR type: enum weights: FR: 1.0 @@ -64,12 +70,12 @@ tree: name: nvidia_ai_gpu_chip models: EFV3_CLIMATE_CHANGE: 12500.0*architecture_Maxwell*(4.6212599075297227e-9*cuda_core - + 7.37132179656539e-6) + 289.6776199311062*architecture_Maxwell*(0.009702834627645097*cuda_core - + 1)**2/((1 - 0.6773699850611761*exp(-0.003779619385733156*cuda_core))**2*(70685.775/(0.1889809692866578*cuda_core + + 7.37132179656539e-6) + 0.007241940498277656*architecture_Maxwell*(0.009702834627645097*cuda_core + + 1)**2/((1 - 0.9980542072708582*exp(-1.889809692866578e-5*cuda_core))**2*(70685.775/(0.1889809692866578*cuda_core + 19.47688243064738) - 106.7778184271516*sqrt(2)/sqrt(0.009702834627645097*cuda_core + 1))) + 12500.0*architecture_Pascal*(4.6891975579761074e-9*cuda_core + 7.808281424221127e-6) - + 2626.882558417281*architecture_Pascal*(0.0060737847877931227*cuda_core + 1)**2/((1 - - 0.33777635255702983*exp(-0.0065923115776528474*cuda_core))**2*(70685.775/(0.13184623155305694*cuda_core + + 0.26268825584172803*architecture_Pascal*(0.0060737847877931227*cuda_core + + 1)**2/((1 - 0.98920497620445418*exp(-6.5923115776528471e-5*cuda_core))**2*(70685.775/(0.13184623155305694*cuda_core + 21.707425626610416) - 101.14318001667067*sqrt(2)/sqrt(0.0060737847877931227*cuda_core + 1))) + 0.00036525*energy_per_inference*inference_per_day*lifespan*(0.005*usage_location_EU + 0.021*usage_location_FR) @@ -94,32 +100,32 @@ tree: - name: nvidia_gpu_chip_manufacturing models: EFV3_CLIMATE_CHANGE: 12500.0*architecture_Maxwell*(4.6212599075297227e-9*cuda_core - + 7.37132179656539e-6) + 289.6776199311062*architecture_Maxwell*(0.009702834627645097*cuda_core - + 1)**2/((1 - 0.6773699850611761*exp(-0.003779619385733156*cuda_core))**2*(70685.775/(0.1889809692866578*cuda_core + + 7.37132179656539e-6) + 0.007241940498277656*architecture_Maxwell*(0.009702834627645097*cuda_core + + 1)**2/((1 - 0.9980542072708582*exp(-1.889809692866578e-5*cuda_core))**2*(70685.775/(0.1889809692866578*cuda_core + 19.47688243064738) - 106.7778184271516*sqrt(2)/sqrt(0.009702834627645097*cuda_core + 1))) + 12500.0*architecture_Pascal*(4.6891975579761074e-9*cuda_core + 7.808281424221127e-6) - + 2626.882558417281*architecture_Pascal*(0.0060737847877931227*cuda_core + - 1)**2/((1 - 0.33777635255702983*exp(-0.0065923115776528474*cuda_core))**2*(70685.775/(0.13184623155305694*cuda_core + + 0.26268825584172803*architecture_Pascal*(0.0060737847877931227*cuda_core + + 1)**2/((1 - 0.98920497620445418*exp(-6.5923115776528471e-5*cuda_core))**2*(70685.775/(0.13184623155305694*cuda_core + 21.707425626610416) - 101.14318001667067*sqrt(2)/sqrt(0.0060737847877931227*cuda_core + 1))) direct_impacts: EFV3_CLIMATE_CHANGE: 12500.0*architecture_Maxwell*(4.6212599075297227e-9*cuda_core - + 7.37132179656539e-6) + 289.6776199311062*architecture_Maxwell*(0.009702834627645097*cuda_core - + 1)**2/((1 - 0.6773699850611761*exp(-0.003779619385733156*cuda_core))**2*(70685.775/(0.1889809692866578*cuda_core + + 7.37132179656539e-6) + 0.007241940498277656*architecture_Maxwell*(0.009702834627645097*cuda_core + + 1)**2/((1 - 0.9980542072708582*exp(-1.889809692866578e-5*cuda_core))**2*(70685.775/(0.1889809692866578*cuda_core + 19.47688243064738) - 106.7778184271516*sqrt(2)/sqrt(0.009702834627645097*cuda_core + 1))) + 12500.0*architecture_Pascal*(4.6891975579761074e-9*cuda_core + 7.808281424221127e-6) - + 2626.882558417281*architecture_Pascal*(0.0060737847877931227*cuda_core + - 1)**2/((1 - 0.33777635255702983*exp(-0.0065923115776528474*cuda_core))**2*(70685.775/(0.13184623155305694*cuda_core + + 0.26268825584172803*architecture_Pascal*(0.0060737847877931227*cuda_core + + 1)**2/((1 - 0.98920497620445418*exp(-6.5923115776528471e-5*cuda_core))**2*(70685.775/(0.13184623155305694*cuda_core + 21.707425626610416) - 101.14318001667067*sqrt(2)/sqrt(0.0060737847877931227*cuda_core + 1))) scaled_direct_impacts: EFV3_CLIMATE_CHANGE: 12500.0*architecture_Maxwell*(4.6212599075297227e-9*cuda_core - + 7.37132179656539e-6) + 289.6776199311062*architecture_Maxwell*(0.009702834627645097*cuda_core - + 1)**2/((1 - 0.6773699850611761*exp(-0.003779619385733156*cuda_core))**2*(70685.775/(0.1889809692866578*cuda_core + + 7.37132179656539e-6) + 0.007241940498277656*architecture_Maxwell*(0.009702834627645097*cuda_core + + 1)**2/((1 - 0.9980542072708582*exp(-1.889809692866578e-5*cuda_core))**2*(70685.775/(0.1889809692866578*cuda_core + 19.47688243064738) - 106.7778184271516*sqrt(2)/sqrt(0.009702834627645097*cuda_core + 1))) + 12500.0*architecture_Pascal*(4.6891975579761074e-9*cuda_core + 7.808281424221127e-6) - + 2626.882558417281*architecture_Pascal*(0.0060737847877931227*cuda_core + - 1)**2/((1 - 0.33777635255702983*exp(-0.0065923115776528474*cuda_core))**2*(70685.775/(0.13184623155305694*cuda_core + + 0.26268825584172803*architecture_Pascal*(0.0060737847877931227*cuda_core + + 1)**2/((1 - 0.98920497620445418*exp(-6.5923115776528471e-5*cuda_core))**2*(70685.775/(0.13184623155305694*cuda_core + 21.707425626610416) - 101.14318001667067*sqrt(2)/sqrt(0.0060737847877931227*cuda_core + 1))) children: [] diff --git a/tests/data/cmd_build/nvidia_ai_gpu_chip_lca_conf.yaml b/tests/data/cmd_build/nvidia_ai_gpu_chip_lca_conf.yaml index 06e20f8..e5a4ee3 100644 --- a/tests/data/cmd_build/nvidia_ai_gpu_chip_lca_conf.yaml +++ b/tests/data/cmd_build/nvidia_ai_gpu_chip_lca_conf.yaml @@ -20,18 +20,19 @@ model: report: link: https://appalca.github.io/ description: "A mock example of Appa LCA's impact model corresponding to a fictive AI chip accelerator based on NVIDIA GPU." - date: 03/11/2023 + date: 07/10/2025 version: "1" license: proprietary - appabuild_version: "0.2" + appabuild_version: "0.3.6" parameters: - name: cuda_core type: float default: architecture: - Maxwell: 512 - Pascal: 560 - pm_perc: 0.1 + Maxwell: 1344 + Pascal: 1280 + min: 256 + max: 4096 - name: architecture type: enum default: Maxwell @@ -40,20 +41,23 @@ model: Pascal: 1 - name: usage_location type: enum - default: EU + default: FR weights: FR: 1 EU: 1 - name: energy_per_inference type: float - default: 0.05 - min: 0.01 - max: 0.1 - - name: inference_per_day - type: float - default: 3600 - pm_perc: 0.1 + default: + architecture: #we model energy by inference using a model of the TDP function of the number of cuda cores. We suppose that TDP power corresponds to running inferences at 90fps. + Maxwell: "0.0878*cuda_core*1000/(90*3600)" + Pascal: "0.0679*cuda_core*1000/(90*3600)" + pm_perc: 0.2 - name: lifespan type: float - default: inference_per_day / 1000 - pm_perc: 0.1 \ No newline at end of file + default: 2.0 + pm: 1 + - name: inference_per_day + type: float + default: "30*3600*8" #30fps 8 hours a day + min: 0 + max: 86400000 \ No newline at end of file diff --git a/tests/data/foreground_datasets/valids/ai_use_phase.yaml b/tests/data/foreground_datasets/valids/ai_use_phase.yaml new file mode 100644 index 0000000..0bbdfb6 --- /dev/null +++ b/tests/data/foreground_datasets/valids/ai_use_phase.yaml @@ -0,0 +1,19 @@ +name: ai_use_phase +location: RER +type: process +unit: unit +amount: 1 +parameters: +- usage_location # Only FR or RER supported +- inference +- energy_per_inference +include_in_tree: True +comment: "Use phase for AI inference." +exchanges: +- database: user_database + name: electricity + type: technosphere + amount: "inference * energy_per_inference / 1000000" + input: + uuid: "electricity_no_ei" + database: user_database diff --git a/tests/data/foreground_datasets/valids/electricity_no_ei.yaml b/tests/data/foreground_datasets/valids/electricity_no_ei.yaml new file mode 100644 index 0000000..916ef97 --- /dev/null +++ b/tests/data/foreground_datasets/valids/electricity_no_ei.yaml @@ -0,0 +1,25 @@ +name: electricity_no_ei +location: GLO +type: process +unit: kwh +amount: 1 +parameters: +- usage_location # Only FR or RER supported +comment: "Low voltage electricity using 2023 https://www.eea.europa.eu/ data." +exchanges: +- database: user_database + name: electricity + type: technosphere + switch: + name: usage_location + options: + - name: EU + amount: 0.005 + input: + database: impact_proxies + uuid: "('EF v3.0', 'climate change', 'global warming potential (GWP100)')_technosphere_proxy" + - name: FR + amount: 0.021 + input: + database: impact_proxies + uuid: "('EF v3.0', 'climate change', 'global warming potential (GWP100)')_technosphere_proxy" \ No newline at end of file diff --git a/tests/data/lca_confs/invalids/parameter_in_default_missing_in_conf.yaml b/tests/data/lca_confs/invalids/parameter_in_default_missing_in_conf.yaml new file mode 100644 index 0000000..7b75052 --- /dev/null +++ b/tests/data/lca_confs/invalids/parameter_in_default_missing_in_conf.yaml @@ -0,0 +1,50 @@ +scope: + fu: + name: 'ai_use_phase' + database: "user_database" + methods: + - "EFV3_CLIMATE_CHANGE" +model: + path: "." + name: "ai_use_phase" + compile: True + metadata: + author: + name: Maxime PERALTA + organization: CEA + mail: maxime.peralta@cea.fr + reviewer: + name: Mathias TORCASO + organization: CEA + mail: + report: + link: https://appalca.github.io/ + description: "A mock example of Appa LCA's impact model corresponding to a fictive AI chip accelerator based on NVIDIA GPU." + date: 03/11/2023 + version: "1" + license: proprietary + appabuild_version: "0.2" + parameters: + - name: energy_per_inference + type: float + default: 0.05*inference + min: 0.01 + max: 0.1 + - name: inference + type: float + default: + usage_location: + FR: inference_per_day*365.25*lifetime + EU: inference_per_day*365.25*lifetime + pm_perc: 0.1 + - name: usage_location + type: enum + default: EU + weights: + FR: 1 + EU: 1 + - name: lifetime + type: float + default: 2 + min: 0 + max: 3 \ No newline at end of file diff --git a/tests/data/lca_confs/invalids/parameter_missing_in_conf.yaml b/tests/data/lca_confs/invalids/parameter_missing_in_conf.yaml new file mode 100644 index 0000000..0b9d96c --- /dev/null +++ b/tests/data/lca_confs/invalids/parameter_missing_in_conf.yaml @@ -0,0 +1,36 @@ +scope: + fu: + name: 'ai_use_phase' + database: "user_database" + methods: + - "EFV3_CLIMATE_CHANGE" +model: + path: "." + name: "ai_use_phase" + compile: True + metadata: + author: + name: Maxime PERALTA + organization: CEA + mail: maxime.peralta@cea.fr + reviewer: + name: Mathias TORCASO + organization: CEA + mail: + report: + link: https://appalca.github.io/ + description: "A mock example of Appa LCA's impact model corresponding to a fictive AI chip accelerator based on NVIDIA GPU." + date: 03/11/2023 + version: "1" + license: proprietary + appabuild_version: "0.2" + parameters: + - name: energy_per_inference + type: float + default: 0.05 + min: 0.01 + max: 0.1 + - name: inference + type: float + default: "60*60" + pm_perc: 0.1 diff --git a/tests/end_to_end/test_cmd_build.py b/tests/end_to_end/test_cmd_build.py index 30a532e..8979511 100644 --- a/tests/end_to_end/test_cmd_build.py +++ b/tests/end_to_end/test_cmd_build.py @@ -4,7 +4,9 @@ import os +import pytest import yaml +from apparun.impact_model import ImpactModel from typer.testing import CliRunner from appabuild.cli.main import cli_app @@ -19,6 +21,7 @@ def test_build_command(): expected_file = os.path.join( DATA_DIR, "cmd_build", "nvidia_ai_gpu_chip_expected.yaml" ) + expected_scores_file = os.path.join(DATA_DIR, "cmd_build", "expected_scores.yaml") result = runner.invoke( cli_app, @@ -39,6 +42,19 @@ def test_build_command(): with open("nvidia_ai_gpu_chip.yaml", "r") as stream: value = yaml.safe_load(stream) - os.remove("nvidia_ai_gpu_chip.yaml") - assert expected == value, "result file not the same as expected file " + + # Check that the generated impact model can be run by Appa Run + model = ImpactModel.from_yaml("nvidia_ai_gpu_chip.yaml") + scores = model.get_nodes_scores() + scores = { + score.name: score.lcia_scores.scores["EFV3_CLIMATE_CHANGE"][0] + for score in scores + } + + with open(expected_scores_file, "r") as stream: + expected_scores = yaml.safe_load(stream) + + assert scores == pytest.approx(expected_scores) + + os.remove("nvidia_ai_gpu_chip.yaml") diff --git a/tests/functional_no_db/test_lca_conf.py b/tests/functional_no_db/test_lca_conf.py index 2ed2338..257b834 100644 --- a/tests/functional_no_db/test_lca_conf.py +++ b/tests/functional_no_db/test_lca_conf.py @@ -6,7 +6,10 @@ import pytest from pydantic import ValidationError +from appabuild import setup +from appabuild.cli.lca import build from appabuild.config.lca import LCAConfig +from appabuild.exceptions import ParameterError from tests import DATA_DIR @@ -57,6 +60,40 @@ def test_parameter_empty_name(): assert error["loc"] in invalid_params_loc +def test_parameter_missing_in_conf(): + """ + Check that an exception is raised if a parameter is missing in the LCA conf. + """ + appa_conf_file = os.path.join( + DATA_DIR, "appalca_confs", "valids", "appalca_conf_lca_confs.yaml" + ) + lca_conf_file = os.path.join( + DATA_DIR, "lca_confs", "invalids", "parameter_missing_in_conf.yaml" + ) + + foreground_database = setup.initialize(appa_conf_file) + with pytest.raises(ParameterError): + setup.build(lca_conf_file, foreground_database) + os.remove("ai_use_phase.yaml") + + +def test_parameter_in_default_missing_in_conf(): + """ + Check that an exception is raised if a parameter is missing in the LCA conf. + """ + appa_conf_file = os.path.join( + DATA_DIR, "appalca_confs", "valids", "appalca_conf_lca_confs.yaml" + ) + lca_conf_file = os.path.join( + DATA_DIR, "lca_confs", "invalids", "parameter_in_default_missing_in_conf.yaml" + ) + + foreground_database = setup.initialize(appa_conf_file) + with pytest.raises(ParameterError): + setup.build(lca_conf_file, foreground_database) + os.remove("ai_use_phase.yaml") + + def test_valid(): """ Check no exception is raised for a valid LCA configuration. From 39bca922ca2e332f2f8f9899a2d95ac8787ca36f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20P=C3=A9ralta?= Date: Thu, 16 Oct 2025 13:48:19 +0200 Subject: [PATCH 2/5] (chore) Bump appabuild package version to 0.3.7 (#42) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 48184c1..62ecbf1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ packages = ["appabuild"] [project] name = "appabuild" -version = "0.3.6" +version = "0.3.7" authors = [{ name = "Maxime Peralta", email = "maxime.peralta@cea.fr"}] maintainers= [{name = "Maxime Peralta", email = "maxime.peralta@cea.fr"}] description = "Appa Build is a package to build impact models" From f2bb5c781d62b5904b0c998f27ad71c8d6045fdc Mon Sep 17 00:00:00 2001 From: Maxime PERALTA Date: Sat, 25 Oct 2025 10:11:14 +0200 Subject: [PATCH 3/5] (draft) Functional data import and model building --- appabuild/config/appa_lca.py | 12 +- appabuild/database/databases.py | 121 +++--- appabuild/exceptions.py | 4 + appabuild/model/builder.py | 359 +++++------------- appabuild/setup.py | 67 ++-- pyproject.toml | 11 +- requirements.txt | 11 +- samples/conf/appalca_conf.yaml | 2 +- samples/conf/appalca_conf_with_ei.yaml | 8 + .../nvidia_ai_gpu/ai_use_phase.yaml | 2 +- .../nvidia_ai_gpu/electricity.yaml | 4 +- .../nvidia_ai_gpu/nvidia_ai_gpu_chip.yaml | 2 +- 12 files changed, 208 insertions(+), 395 deletions(-) create mode 100644 samples/conf/appalca_conf_with_ei.yaml diff --git a/appabuild/config/appa_lca.py b/appabuild/config/appa_lca.py index 16cc4c8..7d76f32 100644 --- a/appabuild/config/appa_lca.py +++ b/appabuild/config/appa_lca.py @@ -5,7 +5,7 @@ from __future__ import annotations import os -from typing import Dict +from typing import Dict, Optional, Union import yaml from pydantic import BaseModel, ValidationError, field_validator @@ -14,7 +14,13 @@ from appabuild.logger import log_validation_error, logger -class DatabaseConfig(BaseModel): +class EcoInventDatabaseConfig(BaseModel): + version: str + system_model: str + replace: Optional[bool] = False + + +class ForegroundDatabaseConfig(BaseModel): """ Database entry in an Appa LCA configuration. @@ -45,7 +51,7 @@ class AppaLCAConfig(BaseModel): """ project_name: str - databases: Dict[str, DatabaseConfig] + databases: Dict[str, Union[EcoInventDatabaseConfig, ForegroundDatabaseConfig]] @field_validator("databases", mode="before") @classmethod diff --git a/appabuild/database/databases.py b/appabuild/database/databases.py index 95b94ec..e1acb34 100644 --- a/appabuild/database/databases.py +++ b/appabuild/database/databases.py @@ -10,7 +10,8 @@ import re from typing import Optional -import brightway2 as bw +import bw2data as bd +import bw2io as bi import yaml from apparun.parameters import ImpactModelParams from lca_algebraic import resetParams, setForeground @@ -29,15 +30,13 @@ class Database: Abstract class of a Database. Defines two mandatory methods for import. """ - def __init__(self, name: str, path: Optional[str]): + def __init__(self, name: str): """ Initializes a Database from its name and optionally its path. :param name: name of the database. Should be consistent with the name used in datasets. - :param path: path of the database on disk. """ self.name = name - self.path = path def execute_at_startup(self) -> None: """ @@ -47,8 +46,6 @@ def execute_at_startup(self) -> None: :return: """ resetParams(self.name) - if self.name not in bw.databases: - self.import_in_project() @abc.abstractmethod def import_in_project(self) -> None: @@ -59,27 +56,6 @@ def import_in_project(self) -> None: return -class BiosphereDatabase(Database): - """ - Class to import biosphere3 database, which is the Brightway database containing all - biosphere flows. Biosphere flows are used to compute impacts thanks to - characterization factors. This class currently works only with EcoInvent 3.9. - """ - - def __init__(self): - Database.__init__(self, name="biosphere3", path=None) - - def execute_at_startup(self) -> None: - resetParams(self.name) - self.import_in_project() - - def import_in_project(self) -> None: - logger.info("Loading biosphere database...") - bw.bw2setup() - bw.add_ecoinvent_39_biosphere_flows() - logger.info("Biosphere database successfully loaded") - - class ImpactProxiesDatabase(Database): """ Impact proxies are datasets used to generate impacts without communicating with @@ -92,13 +68,13 @@ class ImpactProxiesDatabase(Database): """ def __init__(self): - Database.__init__(self, name="impact_proxies", path=None) + Database.__init__(self, name="impact_proxies") def execute_at_startup(self): - resetParams(self.name) - if self.name in bw.databases: + super().execute_at_startup() + if self.name in bd.databases: resetParams(self.name) - del bw.databases[self.name] + del bd.databases[self.name] self.import_in_project() def import_in_project(self) -> None: @@ -113,9 +89,9 @@ def import_in_project(self) -> None: :return: """ logger.info("Loading impact proxies...") - bw_database = bw.Database(self.name) + bw_database = bd.Database(self.name) datasets = {} - for method in bw.methods: + for method in bd.methods: datasets[self.name, f"{method}_proxy"] = { "name": f"Impact proxy for {method}", "unit": "unit", @@ -137,8 +113,8 @@ def import_in_project(self) -> None: ], } bw_database.write(datasets) - for method in bw.methods: - characterisation_factors = bw.Method(method).load() + for method in bd.methods: + characterisation_factors = bd.Method(method).load() if ( len( [ @@ -150,51 +126,39 @@ def import_in_project(self) -> None: != 1 ): characterisation_factors.append(((self.name, f"{method}_proxy"), 1)) - bw.Method(method).write(characterisation_factors) + bd.Method(method).write(characterisation_factors) logger.info("Impact proxies successfully loaded") class EcoInventDatabase(Database): - def __init__(self, name, path): - Database.__init__(self, name, path) + def __init__(self, version, system_model, replace: Optional[bool] = False): + self.version = version + self.system_model = system_model + self.replace = replace + Database.__init__(self, f"ecoinvent-{self.version}-{self.system_model}") - def import_in_project(self): - logger.info("Loading Eco Invent database...") - self.validate() - try: - importer = bw.SingleOutputEcospold2Importer( - dirpath=self.path, db_name=self.name, use_mp=False - ) - importer.apply_strategies() - importer.statistics() - importer.write_database() - except XMLSyntaxError as e: - # At least one dataset is invalid - msg = "{} in file {}".format(e.msg, e.filename) - logger.error(msg) - raise BwDatabaseError(msg, exception_type="eco_invent_invalid_dataset") - except UnicodeDecodeError: - # At least one dataset is not in UTF-8 format - msg = "One of the file in the Eco Invent database is not in UTF-8 format" - logger.error(msg) - raise BwDatabaseError(msg, exception_type="eco_invent_invalid_dataset") - except AttributeError as e: - # Missing field - msg = "Missing field {} in file {}".format(e.name, e.obj.base) - logger.error(msg) - raise BwDatabaseError(msg, exception_type="eco_invent_invalid_dataset") + def execute_at_startup(self): + super().execute_at_startup() + if self.name not in bd.databases or self.replace: + if self.name in bd.databases: + del bd.databases[self.name] + self.import_in_project() else: - logger.info("Eco Invent database successfully loaded") + logger.info( + "EcoInvent and biosphere already imported in project. Use the replace " + "flag if you want to import it again." + ) - def validate(self): - """ - Checks the eco invent database exists and is valid. - If not the program exits with the exit code 1. - """ - if not os.path.exists(self.path) or len(os.listdir(self.path)) == 0: - msg = "No EcoInvent database found at the given path {}".format(self.path) - logger.error(msg) - raise BwDatabaseError(msg, exception_type="eco_invent_invalid_path") + def import_in_project(self): + logger.info(f"Downloading EcoInvent under the name {self.name}...") + bi.import_ecoinvent_release( + version=self.version, + system_model=self.system_model, # can be cutoff / apos / consequential / EN15804 + username=os.environ["BW_USER"], + password=os.environ["BW_PASS"], + biosphere_write_mode="replace", + use_mp=False, + ) class ForegroundDatabase(Database): @@ -210,10 +174,11 @@ def __init__(self, name, path): Reference flow has to be specified as import is done in a tree way with reference flow as a root. :param name: user database name - :param path: user datasets location + :param path: user datasets root location """ - Database.__init__(self, name, path) + Database.__init__(self, name) + self.path = path self.fu_name = "" self.parameters = None self.context = UserDatabaseContext( @@ -267,9 +232,9 @@ def find_activities_on_disk(self) -> None: logger.info("Foreground datasets successfully loaded") def execute_at_startup(self): - if self.name in bw.databases: + if self.name in bd.databases: resetParams(self.name) - del bw.databases[self.name] + del bd.databases[self.name] self.find_activities_on_disk() @@ -285,7 +250,7 @@ def import_in_project(self) -> None: Parameters are then propagated from the reference flow to the leaf activities. :return: """ - bw_database = bw.Database(self.name) + bw_database = bd.Database(self.name) serialized_fu = [ serialized_activity for serialized_activity in self.context.serialized_activities diff --git a/appabuild/exceptions.py b/appabuild/exceptions.py index 564a3e1..432ed36 100644 --- a/appabuild/exceptions.py +++ b/appabuild/exceptions.py @@ -23,3 +23,7 @@ class ParameterError(Exception): """Raised when any problem concerning impact model parameterization is encountered.""" pass + + +class ForegroundDatabaseError(Exception): + """Raised when any problem concerning the foreground data is encountered.""" diff --git a/appabuild/model/builder.py b/appabuild/model/builder.py index 6e39d83..3ae64c5 100644 --- a/appabuild/model/builder.py +++ b/appabuild/model/builder.py @@ -8,44 +8,35 @@ import itertools import logging import os -import types -from collections import OrderedDict -from typing import List, Optional, Tuple +from typing import List, Optional, Set, Tuple -import brightway2 as bw +import bw2data as bd import lca_algebraic as lcaa -import yaml from apparun.impact_methods import MethodFullName from apparun.impact_model import ImpactModel, ModelMetadata from apparun.impact_tree import ImpactTreeNode from apparun.parameters import EnumParam, FloatParam, ImpactModelParams from apparun.tree_node import NodeProperties -from bw2data.backends.peewee import Activity -from lca_algebraic import ActivityExtended, with_db_context -from lca_algebraic.base_utils import _getAmountOrFormula, _getDb, debug -from lca_algebraic.helpers import _isForeground -from lca_algebraic.lca import ( - _createTechProxyForBio, - _multiLCAWithCache, - _replace_fixed_params, -) +from lca_algebraic import ActivityExtended +from lca_algebraic.base_utils import _getDb from lca_algebraic.params import ( - _fixed_params, + _getAmountOrFormula, _param_registry, newEnumParam, newFloatParam, ) -from pydantic import ValidationError -from sympy import Expr, simplify, symbols, sympify from sympy.parsing.sympy_parser import parse_expr from appabuild.config.lca import LCAConfig from appabuild.database.databases import ForegroundDatabase -from appabuild.exceptions import BwDatabaseError, BwMethodError, ParameterError +from appabuild.exceptions import ( + BwDatabaseError, + BwMethodError, + ForegroundDatabaseError, + ParameterError, +) from appabuild.logger import logger -act_symbols = {} # Cache of act = > symbol - def to_bw_method(method_full_name: MethodFullName) -> Tuple[str, str, str]: """ @@ -54,7 +45,7 @@ def to_bw_method(method_full_name: MethodFullName) -> Tuple[str, str, str]: :return: Brightway representation of the method. """ matching_methods = [ - method for method in bw.methods if method_full_name in str(method) + method for method in bd.methods if method_full_name in str(method) ] try: if len(matching_methods) < 1: @@ -63,9 +54,9 @@ def to_bw_method(method_full_name: MethodFullName) -> Tuple[str, str, str]: raise BwMethodError( f"Too many methods matching {method_full_name}: {matching_methods}." ) - except BwMethodError: - logger.exception("BwMethodError") - raise + except BwMethodError as e: + logger.exception(e) + raise e return matching_methods[0] @@ -82,7 +73,6 @@ def __init__( methods: list[str], output_path: str, metadata: Optional[ModelMetadata] = ModelMetadata(), - compile_models: bool = True, parameters: Optional[ImpactModelParams] = None, ): """ @@ -104,8 +94,7 @@ def __init__( self.methods = methods self.metadata = metadata self.output_path = output_path - self.compile_models = compile_models - self.bw_user_database = bw.Database(self.user_database_name) + self.bw_user_database = bd.Database(self.user_database_name) @staticmethod def from_yaml(lca_config_path: str) -> ImpactModelBuilder: @@ -125,7 +114,6 @@ def from_yaml(lca_config_path: str) -> ImpactModelBuilder: lca_config.model.name + ".yaml", ), lca_config.model.metadata, - lca_config.model.compile, ImpactModelParams.from_list(lca_config.model.parameters), ) return builder @@ -138,66 +126,34 @@ def build_impact_model( :param foreground_database: database containing the functional unit :return: built impact model. """ - if foreground_database is not None: foreground_database.set_functional_unit( self.functional_unit, self.parameters ) foreground_database.execute_at_build_time() - functional_unit_bw = self.find_functional_unit_in_bw() - tree, params = self.build_impact_tree_and_parameters( - functional_unit_bw, self.methods - ) - impact_model = ImpactModel(tree=tree, parameters=params, metadata=self.metadata) - return impact_model + functional_unit_bw = self.find_activity_in_bw(self.functional_unit) + methods_bw = { + method: to_bw_method(MethodFullName[method]) for method in self.methods + } - def find_functional_unit_in_bw(self) -> ActivityExtended: - """ - Find the bw activity matching the functional unit in the bw database. A single activity - should be found as it is to be used as the root of the tree. - """ - functional_unit_bw = [ - i for i in self.bw_user_database if self.functional_unit == i["name"] - ] - try: - if len(functional_unit_bw) < 1: - raise BwDatabaseError( - f"Cannot find activity {self.functional_unit} for FU." - ) - if len(functional_unit_bw) > 1: - raise BwDatabaseError( - f"Too many activities matching {self.functional_unit} for FU: " - f"{functional_unit_bw}." - ) - except BwDatabaseError: - logger.exception("BwDatabaseError") - raise - functional_unit_bw = functional_unit_bw[0] - return functional_unit_bw - - def build_impact_tree_and_parameters( - self, functional_unit_bw: ActivityExtended, methods: List[str] - ) -> Tuple[ImpactTreeNode, ImpactModelParams]: - """ - Perform LCA, construct all arithmetic models and collect used parameters. - :param functional_unit_bw: Brightway activity producing the reference flow. - :param methods: list of methods to generate arithmetic models for. Expected - method format is Appa Run method keys. - :return: root node (corresponding to the reference flow) and used parameters. - """ - # lcaa param registry can be populated if a model has already been built - _param_registry().clear() - - methods_bw = [to_bw_method(MethodFullName[method]) for method in methods] - tree = ImpactTreeNode( + root_node = ImpactTreeNode( name=functional_unit_bw["name"], amount=1, properties=NodeProperties.from_dict(functional_unit_bw["properties"]), ) - # print("computing model to expression for %s" % model) - self.actToExpression(functional_unit_bw, tree) + self.declare_parameters_in_lcaa() + self.build_tree_node(root_node) + free_symbols = set() + self.compute_node_models(root_node, methods_bw, free_symbols) + self.check_symbols_are_known_parameters(free_symbols) + + impact_model = ImpactModel( + tree=root_node, parameters=self.parameters, metadata=self.metadata + ) + return impact_model + def check_symbols_are_known_parameters(self, symbols_in_amount: Set[str]): # Check if each symbol corresponds to a known parameter # TODO move that in a FloatParam method @@ -237,25 +193,9 @@ def build_impact_tree_and_parameters( params_in_default = [ parameter for parameter in params_in_default if isinstance(parameter, str) ] # there can be int params at this point - free_symbols = set( - list( - itertools.chain.from_iterable( - [ - [str(symb) for symb in node._raw_direct_impact.free_symbols] - for node in tree.unnested_descendants - ] - ) - ) - + list( - itertools.chain.from_iterable( - [ - [str(symb) for symb in node.amount.free_symbols] - for node in tree.unnested_descendants - if isinstance(node.amount, Expr) - ] - ) - ) - + [ + + symbols_in_default = set( + [ str(symb) for symb in list( itertools.chain.from_iterable( @@ -268,47 +208,24 @@ def build_impact_tree_and_parameters( ] ) - activity_symbols = set([str(symb["symbol"]) for _, symb in act_symbols.items()]) - - expected_parameter_symbols = free_symbols - activity_symbols - - forbidden_parameter_names = list( - itertools.chain( - *[ - [ - elem.name - for elem in self.parameters.find_corresponding_parameter( - activity_symbol, must_find_one=False - ) - ] - for activity_symbol in activity_symbols - ] - ) - ) - - try: - if len(forbidden_parameter_names) > 0: - raise ParameterError( - f"Parameter names {forbidden_parameter_names} are forbidden as they " - f"correspond to background activities." - ) - except ParameterError as e: - logger.exception(e) - raise ParameterError(e) - for expected_parameter_symbol in expected_parameter_symbols: + free_symbols = symbols_in_amount.union(symbols_in_default) + for free_symbol in free_symbols: try: - self.parameters.find_corresponding_parameter(expected_parameter_symbol) + self.parameters.find_corresponding_parameter(free_symbol) except ValueError: e = ( - f"ValueError : {expected_parameter_symbol} is required in the impact" + f"ParameterError: {free_symbol} is required in the impact" f" model but is unknown in the config. Please check in the LCA " f"config." ) logger.error(e) raise ParameterError(e) - # Declare used parameters in conf file as a lca_algebraic parameter to enable - # model building (will not be used afterwards) + def declare_parameters_in_lcaa(self): + """ + Declare used parameters in conf file as a lca_algebraic parameter to enable + model building (will not be used afterwards) + """ for parameter in self.parameters: if parameter.name in _param_registry().keys(): @@ -331,142 +248,58 @@ def build_impact_tree_and_parameters( dbname=self.user_database_name, ) - # Create dummy reference to biosphere - # We cannot run LCA to biosphere activities - # We create a technosphere activity mapping exactly to 1 biosphere item - pureTechActBySymbol = OrderedDict() - for act, name in [ - (act, name) for act, name in act_symbols.items() if name["to_compile"] - ]: - pureTechActBySymbol[name["symbol"]] = _createTechProxyForBio( - act, functional_unit_bw.key[0] - ) - - # Compute LCA for background activities - lcas = _multiLCAWithCache(pureTechActBySymbol.values(), methods_bw) - - # For each method, compute an algebric expression with activities replaced by their values - for node in tree.unnested_descendants: - model_expr = node._raw_direct_impact - for method in methods: - # Replace activities by their value in expression for this method - sub = dict( - { - symbol: lcas[(act, to_bw_method(MethodFullName[method]))] - for symbol, act in pureTechActBySymbol.items() - } - ) - node.direct_impacts[method] = model_expr.xreplace(sub) - return tree, self.parameters + def build_tree_node(self, tree_node: ImpactTreeNode): + act = self.find_activity_in_bw(tree_node.name) + for exch in act.exchanges(): + input_db, input_code = exch["input"] + if input_db == self.user_database_name: + sub_act = _getDb(input_db).get(input_code) + if tree_node.name_already_in_tree(sub_act.get("name")): + e = f"Found recursive activity: {sub_act.get('name')}" + logger.exception(e) + raise ForegroundDatabaseError(e) + if sub_act.get("include_in_tree"): + amount = _getAmountOrFormula(exch) + child_tree_node = tree_node.new_child( + name=sub_act["name"], + amount=amount, + properties=NodeProperties.from_dict(sub_act["properties"]), + ) + self.build_tree_node(child_tree_node) - @staticmethod - @with_db_context - def actToExpression(act: Activity, impact_model_tree_node: ImpactTreeNode): + def compute_node_models( + self, tree_node: ImpactTreeNode, methods, free_symbols: Set + ): + act = self.find_activity_in_bw(tree_node.name) + models, param_symbols = lcaa.lca._modelToExpr( + act, methods=list(methods.values()) + ) + param_symbols = [ + param_symbol + for param_symbol in param_symbols + if not param_symbol.endswith("_default") + ] + free_symbols.update(set(param_symbols)) + tree_node.models = { + list(methods.keys())[i]: tree_node.combined_amount * models[i] + for i in range(len(methods.keys())) + } + for child in tree_node.children: + self.compute_node_models(child, methods, free_symbols) + + def find_activity_in_bw(self, activity_name) -> ActivityExtended: """ - Determines the arithmetic model corresponding to activity's impact function of - model's parameters. - :param act: Brightway activity corresponding to the node. - :param impact_model_tree_node: node of the tree to store result in. - :return: + Find the bw activity matching the functional unit in the bw database. A single activity + should be found as it is to be used as the root of the tree. """ - - def act_to_symbol(sub_act, to_compile: bool = True): - """Transform an activity to a named symbol and keep cache of it""" - - db_name, code = sub_act.key - - # Look in cache - if not (db_name, code) in act_symbols: - act = _getDb(db_name).get(code) - name = act["name"] - base_slug = ImpactTreeNode.node_name_to_symbol_name(name) - - slug = base_slug - i = 1 - while symbols(slug) in [ - act_symbol["symbol"] for act_symbol in list(act_symbols.values()) - ]: - slug = f"{base_slug}{i}" - i += 1 - - act_symbols[(db_name, code)] = { - "symbol": symbols(slug), - "to_compile": to_compile, - } - - return act_symbols[(db_name, code)]["symbol"] - - def rec_func(act: Activity, impact_model_tree_node: ImpactTreeNode): - res = 0 - outputAmount = act.getOutputAmount() - - if not _isForeground(act["database"]): - # We reached a background DB ? => stop developping and create reference - # to activity - return act_to_symbol(act) - - for exch in act.exchanges(): - amount = _getAmountOrFormula(exch) - if isinstance(amount, types.FunctionType): - # Some amounts in EIDB are functions ... we ignore them - continue - - # Production exchange - if exch["input"] == exch["output"]: - continue - - input_db, input_code = exch["input"] - sub_act = _getDb(input_db).get(input_code) - - # Background DB or tracked foreground activity => reference it as a - # symbol - if not _isForeground(input_db): - act_expr = act_to_symbol(sub_act) - else: - try: - if impact_model_tree_node.name_already_in_tree(sub_act["name"]): - raise Exception( - f"Found recursive activity: {sub_act['name']}" - ) - except Exception: - logger.exception("Exception") - if sub_act.get("include_in_tree"): - # act_expr = act_to_symbol(sub_act, to_compile=False) - ImpactModelBuilder.actToExpression( - sub_act, - impact_model_tree_node.new_child( - name=sub_act["name"], - amount=amount, - properties=NodeProperties.from_dict( - sub_act["properties"] - ), - ), - ) - amount = 1 # amount is already handled in tree node - act_expr = 0 # no direct impact - # Our model : recursively it to a symbolic expression - else: - act_expr = rec_func(sub_act, impact_model_tree_node) - - avoidedBurden = 1 - - if exch.get("type") == "production" and not exch.get( - "input" - ) == exch.get("output"): - debug("Avoided burden", exch[lcaa.helpers.name]) - avoidedBurden = -1 - - # debug("adding sub act : ", sub_act, formula, act_expr) - - res += amount * act_expr * avoidedBurden - - return res / outputAmount - - expr = rec_func(act, impact_model_tree_node) - - if isinstance(expr, float): - expr = simplify(expr) - else: - # Replace fixed params with their default value - expr = _replace_fixed_params(expr, _fixed_params().values()) - impact_model_tree_node._raw_direct_impact = expr + act = [i for i in self.bw_user_database if activity_name == i["name"]] + try: + if len(act) < 1: + raise BwDatabaseError(f"Cannot find activity {activity_name} for FU.") + if len(act) > 1: + raise BwDatabaseError(f"Too many activities matching {activity_name}.") + except BwDatabaseError: + logger.exception("BwDatabaseError") + raise + act = act[0] + return act diff --git a/appabuild/setup.py b/appabuild/setup.py index 7c80bd9..6c97a23 100644 --- a/appabuild/setup.py +++ b/appabuild/setup.py @@ -3,11 +3,11 @@ """ from typing import Optional -import brightway2 as bw +import bw2data as bd +import bw2io as bi from appabuild.config.appa_lca import AppaLCAConfig from appabuild.database.databases import ( - BiosphereDatabase, EcoInventDatabase, ForegroundDatabase, ImpactProxiesDatabase, @@ -27,29 +27,24 @@ def initialize(appabuild_config_path: str) -> ForegroundDatabase: appabuild_config = AppaLCAConfig.from_yaml(appabuild_config_path) - ecoinvent_name = ( - appabuild_config.databases["ecoinvent"].name - if "ecoinvent" in appabuild_config.databases - else None - ) - - ecoinvent_path = ( - appabuild_config.databases["ecoinvent"].path - if "ecoinvent" in appabuild_config.databases - else None - ) + if "ecoinvent" in appabuild_config.databases: + ecoinvent_version = appabuild_config.databases["ecoinvent"].version - if ecoinvent_path is None: - logger.warning( - "No path given for ecoinvent databases, building the impact model will be done without" + ecoinvent_system_model = appabuild_config.databases["ecoinvent"].system_model + ecoinvent_replace = appabuild_config.databases["ecoinvent"].replace + logger.info( + f"Loading EcoInvent database {ecoinvent_version}-{ecoinvent_system_model}..." ) else: - logger.info(f"Loading EcoInvent database from {ecoinvent_path}") - + ecoinvent_version = None + ecoinvent_system_model = None + ecoinvent_replace = None + logger.warning("No EcoInvent database in LCA conf.") return project_setup( project_name=appabuild_config.project_name, - ecoinvent_name=ecoinvent_name, - ecoinvent_path=ecoinvent_path, + ecoinvent_version=ecoinvent_version, + ecoinvent_system_model=ecoinvent_system_model, + ecoinvent_replace=ecoinvent_replace, foreground_name=appabuild_config.databases["foreground"].name, foreground_path=appabuild_config.databases["foreground"].path, ) @@ -72,9 +67,7 @@ def build( impact_model = impact_model_builder.build_impact_model(foreground_database) logger.info("Impact model successfully built") - impact_model.to_yaml( - impact_model_builder.output_path, impact_model_builder.compile_models - ) + impact_model.to_yaml(impact_model_builder.output_path) return impact_model @@ -83,33 +76,39 @@ def project_setup( project_name: str, foreground_name: str, foreground_path: str, - ecoinvent_name: Optional[str] = None, - ecoinvent_path: Optional[str] = None, + ecoinvent_version: Optional[str] = None, + ecoinvent_system_model: Optional[str] = None, + ecoinvent_replace: Optional[bool] = False, ) -> ForegroundDatabase: """ Triggers all Brightway functions and database import necessary to build an Impact Model. :param project_name: Brightway project name. - :param ecoinvent_name: how EcoInvent is referred to in user datasets. - :param ecoinvent_path: path to EcoInvent database. :param foreground_name: how user database is referred to. :param foreground_path: path to folder containing user datasets. + :param ecoinvent_version: #TODO + :param ecoinvent_system_model: #TODO + :param ecoinvent_replace: if set to True, EcoInvent and Biosphere DB will be recreated """ - bw.projects.set_current(project_name) + bd.projects.set_current(project_name) + databases = [] + if ecoinvent_version is not None: + ecoinvent_database = EcoInventDatabase( + version=ecoinvent_version, + system_model=ecoinvent_system_model, + replace=ecoinvent_replace, + ) + databases.append(ecoinvent_database) + foreground_database = ForegroundDatabase( name=foreground_name, path=foreground_path, ) - databases = [ - BiosphereDatabase(), + databases += [ ImpactProxiesDatabase(), foreground_database, ] - if ecoinvent_path is not None: - ecoinvent_database = EcoInventDatabase(name=ecoinvent_name, path=ecoinvent_path) - databases.append(ecoinvent_database) - for external_database in databases: external_database.execute_at_startup() diff --git a/pyproject.toml b/pyproject.toml index 62ecbf1..5a9c853 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,17 +26,16 @@ requires-python = ">=3.11,<3.12" dependencies = [ "click<=8.1.8", "numpy==1.26.4", - "pandas==2.0.0", - "brightway2==2.4.7", - "bw2io==0.8.12", - "lca_algebraic==1.0.0", + "pandas==2.3.3", + "brightway25==1.1.0", + "lca-algebraic-bw25==1.3.1", "fastapi", "uvicorn[standard]", "omegaconf", "pydantic", "scipy", - "matplotlib==3.10.0", - "seaborn==0.12.2", + "matplotlib", + "seaborn", "plotly", "aenum", "kaleido", diff --git a/requirements.txt b/requirements.txt index 724a69e..1be2cd6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,7 @@ numpy==1.26.4 -pandas==2.0.0 -brightway2==2.4.7 -bw2io==0.8.12 -lca_algebraic==1.0.0 +pandas==2.3.3 +brightway25==1.1.0 +lca-algebraic-bw25==1.3.1 fastapi uvicorn[standard] flake8 @@ -12,8 +11,8 @@ typer==0.15.1 omegaconf pydantic scipy -matplotlib==3.10.0 -seaborn==0.12.2 +matplotlib +seaborn pytest plotly aenum diff --git a/samples/conf/appalca_conf.yaml b/samples/conf/appalca_conf.yaml index 9bd3939..ef3d656 100644 --- a/samples/conf/appalca_conf.yaml +++ b/samples/conf/appalca_conf.yaml @@ -1,4 +1,4 @@ -project_name: 'sample_project' +project_name: 'sample_project25' databases: foreground: name: user_database diff --git a/samples/conf/appalca_conf_with_ei.yaml b/samples/conf/appalca_conf_with_ei.yaml new file mode 100644 index 0000000..5860e4e --- /dev/null +++ b/samples/conf/appalca_conf_with_ei.yaml @@ -0,0 +1,8 @@ +project_name: 'sample_project25_3' +databases: + foreground: + name: user_database + path: './samples/datasets/user_database/nvidia_ai_gpu' + ecoinvent: + version: "3.11" + system_model: "cutoff" diff --git a/samples/datasets/user_database/nvidia_ai_gpu/ai_use_phase.yaml b/samples/datasets/user_database/nvidia_ai_gpu/ai_use_phase.yaml index 0bbdfb6..2af5dca 100644 --- a/samples/datasets/user_database/nvidia_ai_gpu/ai_use_phase.yaml +++ b/samples/datasets/user_database/nvidia_ai_gpu/ai_use_phase.yaml @@ -15,5 +15,5 @@ exchanges: type: technosphere amount: "inference * energy_per_inference / 1000000" input: - uuid: "electricity_no_ei" + uuid: "electricity" database: user_database diff --git a/samples/datasets/user_database/nvidia_ai_gpu/electricity.yaml b/samples/datasets/user_database/nvidia_ai_gpu/electricity.yaml index 134801b..62248f7 100644 --- a/samples/datasets/user_database/nvidia_ai_gpu/electricity.yaml +++ b/samples/datasets/user_database/nvidia_ai_gpu/electricity.yaml @@ -18,9 +18,9 @@ exchanges: input: name: "market (group )?for electricity, low voltage" location: "RER" - database: ecoinvent_3.9.1_cutoff + database: ecoinvent-3.11-cutoff - name: FR input: name: "market (group )?for electricity, low voltage" location: "FR" - database: ecoinvent_3.9.1_cutoff \ No newline at end of file + database: ecoinvent-3.11-cutoff \ No newline at end of file diff --git a/samples/datasets/user_database/nvidia_ai_gpu/nvidia_ai_gpu_chip.yaml b/samples/datasets/user_database/nvidia_ai_gpu/nvidia_ai_gpu_chip.yaml index 9aa9783..43a0a00 100644 --- a/samples/datasets/user_database/nvidia_ai_gpu/nvidia_ai_gpu_chip.yaml +++ b/samples/datasets/user_database/nvidia_ai_gpu/nvidia_ai_gpu_chip.yaml @@ -18,7 +18,7 @@ exchanges: type: technosphere parameters_matching: inference: inference_per_day * lifespan * 365.25 - amount: 1 + amount: cuda_core input: database: user_database uuid: ai_use_phase From e6838a86389fb2f271a7ad80c3dd3a4870aabe8e Mon Sep 17 00:00:00 2001 From: Maxime PERALTA Date: Wed, 29 Oct 2025 16:15:37 +0100 Subject: [PATCH 4/5] (update) Impact proxies are functional with bw25 but necessitate EcoInvent. Automatically add production exchange for every foreground activity. Determine free symbols in models without lcaa. Fix package versions in requirements. --- appabuild/cli/lca.py | 29 +++++++----- appabuild/database/bw_databases.py | 27 +++++------ appabuild/database/databases.py | 42 +++++++++------- appabuild/database/user_database_elements.py | 50 +++++++++++--------- appabuild/model/builder.py | 18 +++---- appabuild/setup.py | 7 ++- pyproject.toml | 10 ++++ requirements.txt | 17 +++++-- samples/conf/appalca_conf.yaml | 2 +- samples/conf/appalca_conf_with_ei.yaml | 3 +- 10 files changed, 119 insertions(+), 86 deletions(-) diff --git a/appabuild/cli/lca.py b/appabuild/cli/lca.py index cf62506..d8f47d1 100644 --- a/appabuild/cli/lca.py +++ b/appabuild/cli/lca.py @@ -32,23 +32,26 @@ def build( An AppaBuild environment is initialized (background and foreground databases), unless --no-init is specified. """ - try: - foreground_database = None - if init: - if not appabuild_config_path: - logger.error( - "AppaBuild configuration file and LCA config file are required for initialization", - exc_info=True, - ) - raise ValueError() - foreground_database = setup.initialize(appabuild_config_path) - - setup.build(lca_config_path, foreground_database) - except (ValueError, ValidationError, BwDatabaseError): + # try: + foreground_database = None + if init: + if not appabuild_config_path: + logger.error( + "AppaBuild configuration file and LCA config file are required for initialization", + exc_info=True, + ) + raise ValueError() + foreground_database = setup.initialize(appabuild_config_path) + + setup.build(lca_config_path, foreground_database) + + """ + except (ValueError, ValidationError, BwDatabaseError) as e: sys.exit(1) except Exception as e: logger.exception(str(e)) sys.exit(1) + """ @app.command() diff --git a/appabuild/database/bw_databases.py b/appabuild/database/bw_databases.py index 5e5466e..150e6e0 100644 --- a/appabuild/database/bw_databases.py +++ b/appabuild/database/bw_databases.py @@ -7,11 +7,12 @@ import re from typing import List, Optional, Union -import brightway2 as bw +import bw2data as bd from pydantic import BaseModel from appabuild.database.serialized_data import ActivityIdentifier -from appabuild.exceptions import BwDatabaseError +from appabuild.exceptions import BwDatabaseError, SerializedDataError +from appabuild.logger import logger class BwDatabase(BaseModel): @@ -27,7 +28,7 @@ def database(self): Brightway database object :return: """ - return bw.Database(self.name) + return bd.Database(self.name) def search_activity( self, regexes: dict, must_find_only_one: Optional[bool] = False @@ -48,14 +49,13 @@ def search_activity( ) matching_activities = list(set.intersection(*map(set, matching_acts))) if must_find_only_one and len(matching_activities) < 1: - raise BwDatabaseError( - f"Cannot find any activity resolving the following regexes: {regexes}." - ) + e = f"Cannot find any activity resolving the following regexes: {regexes}." + logger.exception(e) + raise BwDatabaseError(e) if must_find_only_one and len(matching_activities) > 1: - raise BwDatabaseError( - f"Too many activity matching the following regexes {regexes}. Matches " - f"are {matching_activities}." - ) + e = f"Too many activity matching the following regexes {regexes}. Matches are {matching_activities}." + logger.exception(e) + raise BwDatabaseError(e) if must_find_only_one: return ActivityIdentifier( database=matching_activities[0][0], uuid=matching_activities[0][1] @@ -79,10 +79,9 @@ def resolve_activity_identifier( regexes = unresolved_activity_identifier.model_dump() database = regexes.pop("database") if database != self.name: - raise ValueError( - f"Cannot search activity on a different database ({database} != " - f"{self.name})." - ) + e = f"Cannot search activity on a different database ({database} != {self.name})." + logger.exception(e) + raise SerializedDataError(e) activity_identifier = self.search_activity( regexes=regexes, must_find_only_one=True ) diff --git a/appabuild/database/databases.py b/appabuild/database/databases.py index e1acb34..fc4bef0 100644 --- a/appabuild/database/databases.py +++ b/appabuild/database/databases.py @@ -67,15 +67,17 @@ class ImpactProxiesDatabase(Database): "{bw_method_name}_technosphere_proxy". """ - def __init__(self): + def __init__(self, biosphere_name: str, replace: Optional[bool] = False): Database.__init__(self, name="impact_proxies") + self.biosphere_name = biosphere_name + self.replace = replace def execute_at_startup(self): super().execute_at_startup() - if self.name in bd.databases: - resetParams(self.name) - del bd.databases[self.name] - self.import_in_project() + if self.name not in bd.databases or self.replace: + if self.name in bd.databases: + del bd.databases[self.name] + self.import_in_project() def import_in_project(self) -> None: """ @@ -89,18 +91,21 @@ def import_in_project(self) -> None: :return: """ logger.info("Loading impact proxies...") - bw_database = bd.Database(self.name) - datasets = {} + proxy_tech_database = bd.Database(self.name) + tech_datasets = {} + ei_bio_database = bd.Database(self.biosphere_name) for method in bd.methods: - datasets[self.name, f"{method}_proxy"] = { - "name": f"Impact proxy for {method}", + bio_dataset = { + "code": f"{method[1:]}_proxy", + "name": f"Impact proxy for {method[1:]}", "unit": "unit", "exchanges": [], - "type": "biosphere", - "location": "GLO", + "type": "emission", } - datasets[self.name, f"{method}_technosphere_proxy"] = { - "name": f"Technosphere proxy for {method}", + bio_node = ei_bio_database.new_node(**bio_dataset) + bio_node.save() + tech_datasets[self.name, f"{method[1:]}_technosphere_proxy"] = { + "name": f"Technosphere proxy for {method[1:]}", "unit": "unit", "location": "GLO", "production amount": 1, @@ -108,24 +113,25 @@ def import_in_project(self) -> None: { "type": "biosphere", "amount": 1, - "input": [self.name, f"{method}_proxy"], + "input": [self.biosphere_name, f"{method[1:]}_proxy"], } ], } - bw_database.write(datasets) + proxy_tech_database.write(tech_datasets) for method in bd.methods: characterisation_factors = bd.Method(method).load() + biosphere_method_proxy_id = ei_bio_database.get(f"{method[1:]}_proxy").id if ( len( [ cf for cf in characterisation_factors - if cf[0] == (self.name, f"{method}_proxy") + if cf[0] == biosphere_method_proxy_id ] ) != 1 ): - characterisation_factors.append(((self.name, f"{method}_proxy"), 1)) + characterisation_factors.append((biosphere_method_proxy_id, 1)) bd.Method(method).write(characterisation_factors) logger.info("Impact proxies successfully loaded") @@ -142,6 +148,8 @@ def execute_at_startup(self): if self.name not in bd.databases or self.replace: if self.name in bd.databases: del bd.databases[self.name] + if f"ecoinvent-{self.version}-biosphere" in bd.databases: + del bd.databases[f"ecoinvent-{self.version}-biosphere"] self.import_in_project() else: logger.info( diff --git a/appabuild/database/user_database_elements.py b/appabuild/database/user_database_elements.py index 2a1f73e..2d76fed 100644 --- a/appabuild/database/user_database_elements.py +++ b/appabuild/database/user_database_elements.py @@ -14,6 +14,7 @@ from typing import Dict, List, Optional, Tuple, Union import pydantic +from lca_algebraic import STORE_FORMULA_KEY from sympy import parse_expr from appabuild.database.bw_databases import BwDatabase @@ -21,8 +22,6 @@ ActivityIdentifier, SerializedActivity, SerializedExchange, - Switch, - SwitchOption, ) from appabuild.exceptions import SerializedDataError @@ -101,7 +100,7 @@ def to_bw_format(self) -> Dict: } if self.formula is not None: # Handling formula separately otherwise None gets transformed into str - exchange["formula"] = self.formula + exchange[STORE_FORMULA_KEY] = self.formula return exchange @classmethod @@ -154,11 +153,9 @@ def from_serialized_exchange( == solved_serialized_exchange.input.uuid ] if len(serialized_input) != 1: - raise ValueError( - f"Cannot find a unique serialized activity with uuid " - f"{solved_serialized_exchange.input.uuid} (found " - f"{len(serialized_input)})." - ) + e = f"Cannot find a unique serialized activity with uuid {solved_serialized_exchange.input.uuid} (found {len(serialized_input)})." + logger.exception(e) + raise SerializedDataError(e) serialized_input = serialized_input[0] if solved_serialized_exchange.use_exchange_name: serialized_input.name = solved_serialized_exchange.name @@ -210,9 +207,9 @@ def replace_parameters( try: formula = parse_expr(self.formula) except AttributeError as e: - raise SerializedDataError( - f"Invalid amount for exchange {self.name}: {e}" - ) + msg = f"Invalid amount for exchange {self.name}: {e}" + logger.exception(msg) + raise SerializedDataError(msg) for param_to_replace, param_replacing in parameters_matching.items(): if isinstance(param_replacing, dict): all_oh_params = { @@ -248,9 +245,9 @@ def replace_parameters( try: formula = formula.subs(param_to_replace, param_replacing) except TypeError as e: - raise SerializedDataError( - f"Invalid amount for exchange {self.name}: {e}" - ) + msg = f"Invalid amount for exchange {self.name}: {e}" + logger.exception(msg) + raise SerializedDataError(msg) logger.info( f"Parameters replacement; exchange {self.name}; replacing " f"{param_to_replace} by {param_replacing} in formula." @@ -338,7 +335,16 @@ def to_bw_format(self) -> Tuple[Tuple[str, str], dict]: "comment": self.comment, "amount": self.amount, "include_in_tree": self.include_in_tree, - "exchanges": [exchange.to_bw_format() for exchange in self.exchanges], + "exchanges": [exchange.to_bw_format() for exchange in self.exchanges] + + [ + { + "input": (self.database, self.code), + "output": (self.database, self.code), + "name": self.name, + "type": "production", + "amount": 1, + } + ], "properties": self.properties, } @@ -380,10 +386,9 @@ def from_serialized_activity( ) activity_code = f"{activity_code}_{amount_of_copies}" if activity_code in [activity.code for activity in context.activities]: - raise SerializedDataError( - f"Cannot create a duplicate {serialized_activity.name} activity. " - f"Code {activity_code} is not unique." - ) + e = f"Cannot create a duplicate {serialized_activity.name} activity. Code {activity_code} is not unique." + logger.exception(e) + raise SerializedDataError(e) activity_name = serialized_activity.name if ( len( @@ -405,10 +410,9 @@ def from_serialized_activity( ) activity_name = f"{activity_name}_{amount_of_copies}" if activity_name in [activity.name for activity in context.activities]: - raise SerializedDataError( - f"Cannot create a duplicate {serialized_activity.name} activity. " - f"Name {activity_name} is not unique, and activity will be a node." - ) + msg = f"Cannot create a duplicate {serialized_activity.name} activity. Name {activity_name} is not unique, and activity will be a node." + logger.exception(msg) + raise SerializedDataError(msg) new_activity = Activity( code=activity_code, diff --git a/appabuild/model/builder.py b/appabuild/model/builder.py index 3ae64c5..f8e9158 100644 --- a/appabuild/model/builder.py +++ b/appabuild/model/builder.py @@ -45,7 +45,7 @@ def to_bw_method(method_full_name: MethodFullName) -> Tuple[str, str, str]: :return: Brightway representation of the method. """ matching_methods = [ - method for method in bd.methods if method_full_name in str(method) + method for method in bd.methods if method_full_name in str(method[1:]) ] try: if len(matching_methods) < 1: @@ -252,7 +252,8 @@ def build_tree_node(self, tree_node: ImpactTreeNode): act = self.find_activity_in_bw(tree_node.name) for exch in act.exchanges(): input_db, input_code = exch["input"] - if input_db == self.user_database_name: + _, output_code = exch["output"] + if input_db == self.user_database_name and output_code != input_code: sub_act = _getDb(input_db).get(input_code) if tree_node.name_already_in_tree(sub_act.get("name")): e = f"Found recursive activity: {sub_act.get('name')}" @@ -271,15 +272,10 @@ def compute_node_models( self, tree_node: ImpactTreeNode, methods, free_symbols: Set ): act = self.find_activity_in_bw(tree_node.name) - models, param_symbols = lcaa.lca._modelToExpr( - act, methods=list(methods.values()) - ) - param_symbols = [ - param_symbol - for param_symbol in param_symbols - if not param_symbol.endswith("_default") - ] - free_symbols.update(set(param_symbols)) + models = lcaa.lca._modelToExpr(act, methods=list(methods.values())) + all_param_symbols = [model.free_symbols for model in models] + for param_symbols in all_param_symbols: + free_symbols.update({str(param_symbol) for param_symbol in param_symbols}) tree_node.models = { list(methods.keys())[i]: tree_node.combined_amount * models[i] for i in range(len(methods.keys())) diff --git a/appabuild/setup.py b/appabuild/setup.py index 6c97a23..8d83fca 100644 --- a/appabuild/setup.py +++ b/appabuild/setup.py @@ -98,14 +98,17 @@ def project_setup( system_model=ecoinvent_system_model, replace=ecoinvent_replace, ) - databases.append(ecoinvent_database) + proxy_database = ImpactProxiesDatabase( + f"ecoinvent-{ecoinvent_database.version}-biosphere", + replace=ecoinvent_replace, + ) + databases += [ecoinvent_database, proxy_database] foreground_database = ForegroundDatabase( name=foreground_name, path=foreground_path, ) databases += [ - ImpactProxiesDatabase(), foreground_database, ] diff --git a/pyproject.toml b/pyproject.toml index 5a9c853..701b146 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,10 +24,20 @@ classifiers = [ ] requires-python = ">=3.11,<3.12" dependencies = [ + "apparun==0.3.7", "click<=8.1.8", "numpy==1.26.4", + "pyarrow==22.0.0", "pandas==2.3.3", "brightway25==1.1.0", + "bw-migrations==0.2", + "bw-processing==1.0", + "bw-simapro-csv==0.4.3", + "bw2analyzer==0.11.8", + "bw2calc==2.2.1", + "bw2data==4.5.1", + "bw2io==0.9.11", + "bw2parameters==1.1.0", "lca-algebraic-bw25==1.3.1", "fastapi", "uvicorn[standard]", diff --git a/requirements.txt b/requirements.txt index 1be2cd6..2cc0f9a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,16 @@ -numpy==1.26.4 -pandas==2.3.3 +apparun==0.3.7 +numpy +pyarrow +pandas brightway25==1.1.0 +bw-migrations==0.2 +bw-processing==1.0 +bw-simapro-csv==0.4.3 +bw2analyzer==0.11.8 +bw2calc==2.2.1 +bw2data==4.5.1 +bw2io==0.9.11 +bw2parameters==1.1.0 lca-algebraic-bw25==1.3.1 fastapi uvicorn[standard] @@ -19,9 +29,8 @@ aenum kaleido tqdm ruamel.yaml -apparun==0.3.7 ipython>=7.6.0,<=8.34.0 pre-commit hatchling click==8.1.8 -mermaid-py==0.7.1 \ No newline at end of file +mermaid-py==0.7.1 diff --git a/samples/conf/appalca_conf.yaml b/samples/conf/appalca_conf.yaml index ef3d656..7a24671 100644 --- a/samples/conf/appalca_conf.yaml +++ b/samples/conf/appalca_conf.yaml @@ -1,4 +1,4 @@ -project_name: 'sample_project25' +project_name: 'sample_project_bw25' databases: foreground: name: user_database diff --git a/samples/conf/appalca_conf_with_ei.yaml b/samples/conf/appalca_conf_with_ei.yaml index 5860e4e..ddb41d4 100644 --- a/samples/conf/appalca_conf_with_ei.yaml +++ b/samples/conf/appalca_conf_with_ei.yaml @@ -1,4 +1,4 @@ -project_name: 'sample_project25_3' +project_name: 'sample_project_bw25' databases: foreground: name: user_database @@ -6,3 +6,4 @@ databases: ecoinvent: version: "3.11" system_model: "cutoff" + replace: False From 467ee3dcd17dff981ca181b8d91916a968ff4209 Mon Sep 17 00:00:00 2001 From: Maxime PERALTA Date: Fri, 31 Oct 2025 13:58:55 +0100 Subject: [PATCH 5/5] (update) Can create methods and impact proxies without ecoinvent. Update tests. Fix samples. --- appabuild/config/appa_lca.py | 9 +- appabuild/database/databases.py | 59 +- appabuild/setup.py | 31 +- data/methods/ecoinvent_3.11_methods.csv | 639 ++++++++++++++++++ samples/conf/appalca_conf_with_ei.yaml | 4 +- ...alca_conf.yaml => appalca_conf_wo_ei.yaml} | 3 +- .../nvidia_ai_gpu/ai_use_phase.yaml | 2 +- ...ctricity.yaml => electricity_with_ei.yaml} | 2 +- .../nvidia_ai_gpu/electricity_wo_ei.yaml | 6 +- .../nvidia_ai_gpu/nvidia_ai_gpu_chip.yaml | 2 +- .../invalids/missing_foreground.yaml | 4 +- tests/data/appalca_confs/valids/valid.yaml | 4 +- ...alca_conf.yaml => appalca_conf_wo_ei.yaml} | 1 + tests/data/cmd_build/expected_scores.yaml | 6 +- .../foreground_datasets/ai_use_phase.yaml | 2 +- .../electricity_wo_ei.yaml | 6 +- .../nvidia_ai_gpu_chip_expected.yaml | 72 +- .../incomplete_dataset.spold | 5 - .../invalid_datasets/empty_dataset.spold | 0 tests/end_to_end/test_cmd_build.py | 2 +- tests/functional_db/test_ecoinvent.py | 71 +- 21 files changed, 786 insertions(+), 144 deletions(-) create mode 100644 data/methods/ecoinvent_3.11_methods.csv rename samples/conf/{appalca_conf.yaml => appalca_conf_wo_ei.yaml} (64%) rename samples/datasets/user_database/nvidia_ai_gpu/{electricity.yaml => electricity_with_ei.yaml} (95%) rename tests/data/cmd_build/foreground_datasets/electricity_no_ei.yaml => samples/datasets/user_database/nvidia_ai_gpu/electricity_wo_ei.yaml (90%) rename tests/data/cmd_build/{appalca_conf.yaml => appalca_conf_wo_ei.yaml} (88%) rename samples/datasets/user_database/nvidia_ai_gpu/electricity_no_ei.yaml => tests/data/cmd_build/foreground_datasets/electricity_wo_ei.yaml (90%) delete mode 100644 tests/data/eco_invent/invalids/incomplete_datasets/incomplete_dataset.spold delete mode 100644 tests/data/eco_invent/invalids/invalid_datasets/empty_dataset.spold diff --git a/appabuild/config/appa_lca.py b/appabuild/config/appa_lca.py index 7d76f32..aa49a4d 100644 --- a/appabuild/config/appa_lca.py +++ b/appabuild/config/appa_lca.py @@ -14,10 +14,14 @@ from appabuild.logger import log_validation_error, logger +class DatabasesConfig(BaseModel): + foreground: ForegroundDatabaseConfig + ecoinvent: Optional[EcoInventDatabaseConfig] = None + + class EcoInventDatabaseConfig(BaseModel): version: str system_model: str - replace: Optional[bool] = False class ForegroundDatabaseConfig(BaseModel): @@ -51,7 +55,8 @@ class AppaLCAConfig(BaseModel): """ project_name: str - databases: Dict[str, Union[EcoInventDatabaseConfig, ForegroundDatabaseConfig]] + replace_bg: Optional[bool] = False + databases: DatabasesConfig @field_validator("databases", mode="before") @classmethod diff --git a/appabuild/database/databases.py b/appabuild/database/databases.py index fc4bef0..0df0e76 100644 --- a/appabuild/database/databases.py +++ b/appabuild/database/databases.py @@ -5,6 +5,7 @@ from __future__ import annotations import abc +import csv import json import os import re @@ -24,6 +25,8 @@ from appabuild.exceptions import BwDatabaseError, SerializedDataError from appabuild.logger import log_validation_error, logger +PATH_TO_METHODS = "data/methods/ecoinvent_3.11_methods.csv" + class Database: """ @@ -67,9 +70,14 @@ class ImpactProxiesDatabase(Database): "{bw_method_name}_technosphere_proxy". """ - def __init__(self, biosphere_name: str, replace: Optional[bool] = False): + def __init__( + self, biosphere_name: Optional[str] = None, replace: Optional[bool] = False + ): Database.__init__(self, name="impact_proxies") - self.biosphere_name = biosphere_name + self.biosphere_name = ( + biosphere_name if biosphere_name is not None else self.name + ) + self.with_ecoinvent = biosphere_name is not None self.replace = replace def execute_at_startup(self): @@ -93,17 +101,39 @@ def import_in_project(self) -> None: logger.info("Loading impact proxies...") proxy_tech_database = bd.Database(self.name) tech_datasets = {} + bio_datasets = {} ei_bio_database = bd.Database(self.biosphere_name) + + if not self.with_ecoinvent: + with open(PATH_TO_METHODS, newline="") as csvfile: + reader = csv.reader(csvfile, delimiter=";") + for row in reader: + method, category, indicator, unit = row + key = self.name, method, category, indicator + # todo add all empty methods + # key = self.name = tuple csv + if key not in bd.methods: + method = bd.Method(key) + method.register( + unit=unit, + filepath=PATH_TO_METHODS, + ecoinvent_version="3.11", + database=self.name, + ) + for method in bd.methods: bio_dataset = { - "code": f"{method[1:]}_proxy", "name": f"Impact proxy for {method[1:]}", "unit": "unit", "exchanges": [], "type": "emission", } - bio_node = ei_bio_database.new_node(**bio_dataset) - bio_node.save() + if not self.with_ecoinvent: + bio_datasets[self.name, f"{method[1:]}_proxy"] = bio_dataset + else: + bio_dataset["code"] = f"{method[1:]}_proxy" + bio_node = ei_bio_database.new_node(**bio_dataset) + bio_node.save() tech_datasets[self.name, f"{method[1:]}_technosphere_proxy"] = { "name": f"Technosphere proxy for {method[1:]}", "unit": "unit", @@ -117,9 +147,15 @@ def import_in_project(self) -> None: } ], } - proxy_tech_database.write(tech_datasets) + if not self.with_ecoinvent: + proxy_tech_database.write({**bio_datasets, **tech_datasets}) + else: + proxy_tech_database.write(tech_datasets) for method in bd.methods: - characterisation_factors = bd.Method(method).load() + if self.with_ecoinvent: + characterisation_factors = bd.Method(method).load() + else: + characterisation_factors = [] biosphere_method_proxy_id = ei_bio_database.get(f"{method[1:]}_proxy").id if ( len( @@ -137,7 +173,9 @@ def import_in_project(self) -> None: class EcoInventDatabase(Database): - def __init__(self, version, system_model, replace: Optional[bool] = False): + def __init__( + self, version: str, system_model: str, replace: Optional[bool] = False + ): self.version = version self.system_model = system_model self.replace = replace @@ -150,6 +188,11 @@ def execute_at_startup(self): del bd.databases[self.name] if f"ecoinvent-{self.version}-biosphere" in bd.databases: del bd.databases[f"ecoinvent-{self.version}-biosphere"] + if "impact_proxies" in bd.databases: + del bd.databases["impact_proxies"] + method_names = [method for method in bd.methods] + for method in method_names: + del bd.methods[method] self.import_in_project() else: logger.info( diff --git a/appabuild/setup.py b/appabuild/setup.py index 8d83fca..18da511 100644 --- a/appabuild/setup.py +++ b/appabuild/setup.py @@ -27,26 +27,23 @@ def initialize(appabuild_config_path: str) -> ForegroundDatabase: appabuild_config = AppaLCAConfig.from_yaml(appabuild_config_path) - if "ecoinvent" in appabuild_config.databases: - ecoinvent_version = appabuild_config.databases["ecoinvent"].version - - ecoinvent_system_model = appabuild_config.databases["ecoinvent"].system_model - ecoinvent_replace = appabuild_config.databases["ecoinvent"].replace + if appabuild_config.databases.ecoinvent is not None: + ecoinvent_version = appabuild_config.databases.ecoinvent.version + ecoinvent_system_model = appabuild_config.databases.ecoinvent.system_model logger.info( f"Loading EcoInvent database {ecoinvent_version}-{ecoinvent_system_model}..." ) else: ecoinvent_version = None ecoinvent_system_model = None - ecoinvent_replace = None logger.warning("No EcoInvent database in LCA conf.") return project_setup( project_name=appabuild_config.project_name, ecoinvent_version=ecoinvent_version, ecoinvent_system_model=ecoinvent_system_model, - ecoinvent_replace=ecoinvent_replace, - foreground_name=appabuild_config.databases["foreground"].name, - foreground_path=appabuild_config.databases["foreground"].path, + replace_bg=appabuild_config.replace_bg, + foreground_name=appabuild_config.databases.foreground.name, + foreground_path=appabuild_config.databases.foreground.path, ) @@ -78,7 +75,7 @@ def project_setup( foreground_path: str, ecoinvent_version: Optional[str] = None, ecoinvent_system_model: Optional[str] = None, - ecoinvent_replace: Optional[bool] = False, + replace_bg: Optional[bool] = False, ) -> ForegroundDatabase: """ Triggers all Brightway functions and database import necessary to build an Impact @@ -88,7 +85,8 @@ def project_setup( :param foreground_path: path to folder containing user datasets. :param ecoinvent_version: #TODO :param ecoinvent_system_model: #TODO - :param ecoinvent_replace: if set to True, EcoInvent and Biosphere DB will be recreated + :param replace_bg: if set to True, LCIA methods, biosphere DB and EcoInvent DB will + be recreated. """ bd.projects.set_current(project_name) databases = [] @@ -96,14 +94,19 @@ def project_setup( ecoinvent_database = EcoInventDatabase( version=ecoinvent_version, system_model=ecoinvent_system_model, - replace=ecoinvent_replace, + replace=replace_bg, ) proxy_database = ImpactProxiesDatabase( f"ecoinvent-{ecoinvent_database.version}-biosphere", - replace=ecoinvent_replace, + replace=replace_bg, ) databases += [ecoinvent_database, proxy_database] - + else: + proxy_database = ImpactProxiesDatabase( + biosphere_name=None, + replace=replace_bg, + ) + databases += [proxy_database] foreground_database = ForegroundDatabase( name=foreground_name, path=foreground_path, diff --git a/data/methods/ecoinvent_3.11_methods.csv b/data/methods/ecoinvent_3.11_methods.csv new file mode 100644 index 0000000..736358b --- /dev/null +++ b/data/methods/ecoinvent_3.11_methods.csv @@ -0,0 +1,639 @@ +CML v4.8 2016 no LT;acidification no LT;acidification (incl. fate, average Europe total, A&B) no LT;kg SO2-Eq +CML v4.8 2016 no LT;climate change no LT;global warming potential (GWP100) no LT;kg CO2-Eq +CML v4.8 2016 no LT;ecotoxicity: freshwater no LT;freshwater aquatic ecotoxicity (FAETP inf) no LT;kg 1,4-DCB-Eq +CML v4.8 2016 no LT;ecotoxicity: marine no LT;marine aquatic ecotoxicity (MAETP inf) no LT;kg 1,4-DCB-Eq +CML v4.8 2016 no LT;ecotoxicity: terrestrial no LT;terrestrial ecotoxicity (TETP inf) no LT;kg 1,4-DCB-Eq +CML v4.8 2016 no LT;energy resources: non-renewable no LT;abiotic depletion potential (ADP): fossil fuels no LT;MJ +CML v4.8 2016 no LT;eutrophication no LT;eutrophication (fate not incl.) no LT;kg PO4-Eq +CML v4.8 2016 no LT;human toxicity no LT;human toxicity (HTP inf) no LT;kg 1,4-DCB-Eq +CML v4.8 2016 no LT;material resources: metals/minerals no LT;abiotic depletion potential (ADP): elements (ultimate reserves) no LT;kg Sb-Eq +CML v4.8 2016 no LT;ozone depletion no LT;ozone layer depletion (ODP steady state) no LT;kg CFC-11-Eq +CML v4.8 2016 no LT;photochemical oxidant formation no LT;photochemical oxidation (high NOx) no LT;kg ethylene-Eq +CML v4.8 2016;acidification;acidification (incl. fate, average Europe total, A&B);kg SO2-Eq +CML v4.8 2016;climate change;global warming potential (GWP100);kg CO2-Eq +CML v4.8 2016;ecotoxicity: freshwater;freshwater aquatic ecotoxicity (FAETP inf);kg 1,4-DCB-Eq +CML v4.8 2016;ecotoxicity: marine;marine aquatic ecotoxicity (MAETP inf);kg 1,4-DCB-Eq +CML v4.8 2016;ecotoxicity: terrestrial;terrestrial ecotoxicity (TETP inf);kg 1,4-DCB-Eq +CML v4.8 2016;energy resources: non-renewable;abiotic depletion potential (ADP): fossil fuels;MJ +CML v4.8 2016;eutrophication;eutrophication (fate not incl.);kg PO4-Eq +CML v4.8 2016;human toxicity;human toxicity (HTP inf);kg 1,4-DCB-Eq +CML v4.8 2016;material resources: metals/minerals;abiotic depletion potential (ADP): elements (ultimate reserves);kg Sb-Eq +CML v4.8 2016;ozone depletion;ozone layer depletion (ODP steady state);kg CFC-11-Eq +CML v4.8 2016;photochemical oxidant formation;photochemical oxidation (high NOx);kg ethylene-Eq +Crustal Scarcity Indicator 2020;material resources: metals/minerals;crustal scarcity potential (CSP);kg Si-Eq +Cumulative Energy Demand (CED);energy resources: non-renewable;energy content (HHV);MJ-Eq +Cumulative Energy Demand (CED);energy resources: non-renewable, biomass;energy content (HHV);MJ-Eq +Cumulative Energy Demand (CED);energy resources: non-renewable, fossil;energy content (HHV);MJ-Eq +Cumulative Energy Demand (CED);energy resources: non-renewable, nuclear;energy content (HHV);MJ-Eq +Cumulative Energy Demand (CED);energy resources: renewable;energy content (HHV);MJ-Eq +Cumulative Energy Demand (CED);energy resources: renewable, biomass;energy content (HHV);MJ-Eq +Cumulative Energy Demand (CED);energy resources: renewable, geothermal;energy content (HHV);MJ-Eq +Cumulative Energy Demand (CED);energy resources: renewable, geothermal, solar, wind;energy content (HHV);MJ-Eq +Cumulative Energy Demand (CED);energy resources: renewable, solar;energy content (HHV);MJ-Eq +Cumulative Energy Demand (CED);energy resources: renewable, water;energy content (HHV);MJ-Eq +Cumulative Energy Demand (CED);energy resources: renewable, wind;energy content (HHV);MJ-Eq +Cumulative Energy Demand (CED);total;energy content (HHV);MJ-Eq +Cumulative Exergy Demand (CExD);energy resources: non-renewable;exergy content;MJ-Eq +Cumulative Exergy Demand (CExD);energy resources: non-renewable, biomass;exergy content;MJ-Eq +Cumulative Exergy Demand (CExD);energy resources: non-renewable, fossil;exergy content;MJ-Eq +Cumulative Exergy Demand (CExD);energy resources: non-renewable, nuclear;exergy content;MJ-Eq +Cumulative Exergy Demand (CExD);energy resources: renewable;exergy content;MJ-Eq +Cumulative Exergy Demand (CExD);energy resources: renewable, biomass;exergy content;MJ-Eq +Cumulative Exergy Demand (CExD);energy resources: renewable, solar;exergy content;MJ-Eq +Cumulative Exergy Demand (CExD);energy resources: renewable, water;exergy content;MJ-Eq +Cumulative Exergy Demand (CExD);energy resources: renewable, wind;exergy content;MJ-Eq +Cumulative Exergy Demand (CExD);material resources;exergy content;MJ-Eq +Cumulative Exergy Demand (CExD);material resources: metals;exergy content;MJ-Eq +Cumulative Exergy Demand (CExD);material resources: minerals;exergy content;MJ-Eq +Cumulative Exergy Demand (CExD);material resources: water;exergy content;MJ-Eq +Cumulative Exergy Demand (CExD);total;exergy content;MJ-Eq +Ecological Footprint;CO2;global hectares;m2a +Ecological Footprint;land occupation;global hectares;m2a +Ecological Footprint;nuclear;global hectares;m2a +Ecological Footprint;total;global hectares;m2a +Ecological Scarcity 2021 no LT;climate change no LT;global warming potential (GWP100) no LT;UBP +Ecological Scarcity 2021 no LT;emissions to air no LT;air pollutants and PM no LT;UBP +Ecological Scarcity 2021 no LT;emissions to air no LT;carcinogenic substances into air no LT;UBP +Ecological Scarcity 2021 no LT;emissions to air no LT;heavy metals into air no LT;UBP +Ecological Scarcity 2021 no LT;emissions to air no LT;radioactive substances into air no LT;UBP +Ecological Scarcity 2021 no LT;emissions to soil no LT;heavy metals into soil no LT;UBP +Ecological Scarcity 2021 no LT;emissions to soil no LT;pesticides into soil no LT;UBP +Ecological Scarcity 2021 no LT;emissions to water no LT;heavy metals into water no LT;UBP +Ecological Scarcity 2021 no LT;emissions to water no LT;POP into water no LT;UBP +Ecological Scarcity 2021 no LT;emissions to water no LT;radioactive substances into water no LT;UBP +Ecological Scarcity 2021 no LT;emissions to water no LT;water pollutants no LT;UBP +Ecological Scarcity 2021 no LT;energy resources no LT;energy resources no LT;UBP +Ecological Scarcity 2021 no LT;land use no LT;land use no LT;UBP +Ecological Scarcity 2021 no LT;mineral resources no LT;mineral resources no LT;UBP +Ecological Scarcity 2021 no LT;natural resources no LT;biotic resources no LT;UBP +Ecological Scarcity 2021 no LT;ozone depletion no LT;ozone depletion no LT;UBP +Ecological Scarcity 2021 no LT;total no LT;UBP no LT;UBP +Ecological Scarcity 2021 no LT;waste disposal no LT;radioactive waste to deposit no LT;UBP +Ecological Scarcity 2021 no LT;waste disposal no LT;waste, non radioactive no LT;UBP +Ecological Scarcity 2021 no LT;water use no LT;water resources, evaporated no LT;UBP +Ecological Scarcity 2021;climate change;global warming potential (GWP100);UBP +Ecological Scarcity 2021;emissions to air;air pollutants and PM;UBP +Ecological Scarcity 2021;emissions to air;carcinogenic substances into air;UBP +Ecological Scarcity 2021;emissions to air;heavy metals into air;UBP +Ecological Scarcity 2021;emissions to air;radioactive substances into air;UBP +Ecological Scarcity 2021;emissions to soil;heavy metals into soil;UBP +Ecological Scarcity 2021;emissions to soil;pesticides into soil;UBP +Ecological Scarcity 2021;emissions to water;heavy metals into water;UBP +Ecological Scarcity 2021;emissions to water;POP into water;UBP +Ecological Scarcity 2021;emissions to water;radioactive substances into water;UBP +Ecological Scarcity 2021;emissions to water;water pollutants;UBP +Ecological Scarcity 2021;energy resources;energy resources;UBP +Ecological Scarcity 2021;land use;land use;UBP +Ecological Scarcity 2021;mineral resources;mineral resources;UBP +Ecological Scarcity 2021;natural resources;biotic resources;UBP +Ecological Scarcity 2021;ozone depletion;ozone depletion;UBP +Ecological Scarcity 2021;total;UBP;UBP +Ecological Scarcity 2021;waste disposal;radioactive waste to deposit;UBP +Ecological Scarcity 2021;waste disposal;waste, non radioactive;UBP +Ecological Scarcity 2021;water use;water resources, evaporated;UBP +Ecosystem Damage Potential;land occupation;ecosystem damage potential;points +Ecosystem Damage Potential;land transformation;ecosystem damage potential;points +Ecosystem Damage Potential;total;ecosystem damage potential;points +EF v3.0 no LT;acidification no LT;accumulated exceedance (AE) no LT;mol H+-Eq +EF v3.0 no LT;climate change no LT;global warming potential (GWP100) no LT;kg CO2-Eq +EF v3.0 no LT;climate change: biogenic no LT;global warming potential (GWP100) no LT;kg CO2-Eq +EF v3.0 no LT;climate change: fossil no LT;global warming potential (GWP100) no LT;kg CO2-Eq +EF v3.0 no LT;climate change: land use and land use change no LT;global warming potential (GWP100) no LT;kg CO2-Eq +EF v3.0 no LT;ecotoxicity: freshwater no LT;comparative toxic unit for ecosystems (CTUe) no LT;CTUe +EF v3.0 no LT;ecotoxicity: freshwater, inorganics no LT;comparative toxic unit for ecosystems (CTUe) no LT;CTUe +EF v3.0 no LT;ecotoxicity: freshwater, metals no LT;comparative toxic unit for ecosystems (CTUe) no LT;CTUe +EF v3.0 no LT;ecotoxicity: freshwater, organics no LT;comparative toxic unit for ecosystems (CTUe) no LT;CTUe +EF v3.0 no LT;energy resources: non-renewable no LT;abiotic depletion potential (ADP): fossil fuels no LT;MJ, net calorific value +EF v3.0 no LT;eutrophication: freshwater no LT;fraction of nutrients reaching freshwater end compartment (P) no LT;kg P-Eq +EF v3.0 no LT;eutrophication: marine no LT;fraction of nutrients reaching marine end compartment (N) no LT;kg N-Eq +EF v3.0 no LT;eutrophication: terrestrial no LT;accumulated exceedance (AE) no LT;mol N-Eq +EF v3.0 no LT;human toxicity: carcinogenic no LT;comparative toxic unit for human (CTUh) no LT;CTUh +EF v3.0 no LT;human toxicity: carcinogenic, inorganics no LT;comparative toxic unit for human (CTUh) no LT;CTUh +EF v3.0 no LT;human toxicity: carcinogenic, metals no LT;comparative toxic unit for human (CTUh) no LT;CTUh +EF v3.0 no LT;human toxicity: carcinogenic, organics no LT;comparative toxic unit for human (CTUh) no LT;CTUh +EF v3.0 no LT;human toxicity: non-carcinogenic no LT;comparative toxic unit for human (CTUh) no LT;CTUh +EF v3.0 no LT;human toxicity: non-carcinogenic, inorganics no LT;comparative toxic unit for human (CTUh) no LT;CTUh +EF v3.0 no LT;human toxicity: non-carcinogenic, metals no LT;comparative toxic unit for human (CTUh) no LT;CTUh +EF v3.0 no LT;human toxicity: non-carcinogenic, organics no LT;comparative toxic unit for human (CTUh) no LT;CTUh +EF v3.0 no LT;ionising radiation: human health no LT;human exposure efficiency relative to u235 no LT;kBq U235-Eq +EF v3.0 no LT;land use no LT;soil quality index no LT;dimensionless +EF v3.0 no LT;material resources: metals/minerals no LT;abiotic depletion potential (ADP): elements (ultimate reserves) no LT;kg Sb-Eq +EF v3.0 no LT;ozone depletion no LT;ozone depletion potential (ODP) no LT;kg CFC-11-Eq +EF v3.0 no LT;particulate matter formation no LT;impact on human health no LT;disease incidence +EF v3.0 no LT;photochemical oxidant formation: human health no LT;tropospheric ozone concentration increase no LT;kg NMVOC-Eq +EF v3.0 no LT;water use no LT;user deprivation potential (deprivation-weighted water consumption) no LT;m3 world Eq deprived +EF v3.0;acidification;accumulated exceedance (AE);mol H+-Eq +EF v3.0;climate change;global warming potential (GWP100);kg CO2-Eq +EF v3.0;climate change: biogenic;global warming potential (GWP100);kg CO2-Eq +EF v3.0;climate change: fossil;global warming potential (GWP100);kg CO2-Eq +EF v3.0;climate change: land use and land use change;global warming potential (GWP100);kg CO2-Eq +EF v3.0;ecotoxicity: freshwater;comparative toxic unit for ecosystems (CTUe);CTUe +EF v3.0;ecotoxicity: freshwater, inorganics;comparative toxic unit for ecosystems (CTUe);CTUe +EF v3.0;ecotoxicity: freshwater, metals;comparative toxic unit for ecosystems (CTUe);CTUe +EF v3.0;ecotoxicity: freshwater, organics;comparative toxic unit for ecosystems (CTUe);CTUe +EF v3.0;energy resources: non-renewable;abiotic depletion potential (ADP): fossil fuels;MJ, net calorific value +EF v3.0;eutrophication: freshwater;fraction of nutrients reaching freshwater end compartment (P);kg P-Eq +EF v3.0;eutrophication: marine;fraction of nutrients reaching marine end compartment (N);kg N-Eq +EF v3.0;eutrophication: terrestrial;accumulated exceedance (AE);mol N-Eq +EF v3.0;human toxicity: carcinogenic;comparative toxic unit for human (CTUh);CTUh +EF v3.0;human toxicity: carcinogenic, inorganics;comparative toxic unit for human (CTUh);CTUh +EF v3.0;human toxicity: carcinogenic, metals;comparative toxic unit for human (CTUh);CTUh +EF v3.0;human toxicity: carcinogenic, organics;comparative toxic unit for human (CTUh);CTUh +EF v3.0;human toxicity: non-carcinogenic;comparative toxic unit for human (CTUh);CTUh +EF v3.0;human toxicity: non-carcinogenic, inorganics;comparative toxic unit for human (CTUh);CTUh +EF v3.0;human toxicity: non-carcinogenic, metals;comparative toxic unit for human (CTUh);CTUh +EF v3.0;human toxicity: non-carcinogenic, organics;comparative toxic unit for human (CTUh);CTUh +EF v3.0;ionising radiation: human health;human exposure efficiency relative to u235;kBq U235-Eq +EF v3.0;land use;soil quality index;dimensionless +EF v3.0;material resources: metals/minerals;abiotic depletion potential (ADP): elements (ultimate reserves);kg Sb-Eq +EF v3.0;ozone depletion;ozone depletion potential (ODP);kg CFC-11-Eq +EF v3.0;particulate matter formation;impact on human health;disease incidence +EF v3.0;photochemical oxidant formation: human health;tropospheric ozone concentration increase;kg NMVOC-Eq +EF v3.0;water use;user deprivation potential (deprivation-weighted water consumption);m3 world Eq deprived +EF v3.1 no LT;acidification no LT;accumulated exceedance (AE) no LT;mol H+-Eq +EF v3.1 no LT;climate change no LT;global warming potential (GWP100) no LT;kg CO2-Eq +EF v3.1 no LT;climate change: biogenic no LT;global warming potential (GWP100) no LT;kg CO2-Eq +EF v3.1 no LT;climate change: fossil no LT;global warming potential (GWP100) no LT;kg CO2-Eq +EF v3.1 no LT;climate change: land use and land use change no LT;global warming potential (GWP100) no LT;kg CO2-Eq +EF v3.1 no LT;ecotoxicity: freshwater no LT;comparative toxic unit for ecosystems (CTUe) no LT;CTUe +EF v3.1 no LT;ecotoxicity: freshwater, inorganics no LT;comparative toxic unit for ecosystems (CTUe) no LT;CTUe +EF v3.1 no LT;ecotoxicity: freshwater, organics no LT;comparative toxic unit for ecosystems (CTUe) no LT;CTUe +EF v3.1 no LT;energy resources: non-renewable no LT;abiotic depletion potential (ADP): fossil fuels no LT;MJ, net calorific value +EF v3.1 no LT;eutrophication: freshwater no LT;fraction of nutrients reaching freshwater end compartment (P) no LT;kg P-Eq +EF v3.1 no LT;eutrophication: marine no LT;fraction of nutrients reaching marine end compartment (N) no LT;kg N-Eq +EF v3.1 no LT;eutrophication: terrestrial no LT;accumulated exceedance (AE) no LT;mol N-Eq +EF v3.1 no LT;human toxicity: carcinogenic no LT;comparative toxic unit for human (CTUh) no LT;CTUh +EF v3.1 no LT;human toxicity: carcinogenic, inorganics no LT;comparative toxic unit for human (CTUh) no LT;CTUh +EF v3.1 no LT;human toxicity: carcinogenic, organics no LT;comparative toxic unit for human (CTUh) no LT;CTUh +EF v3.1 no LT;human toxicity: non-carcinogenic no LT;comparative toxic unit for human (CTUh) no LT;CTUh +EF v3.1 no LT;human toxicity: non-carcinogenic, inorganics no LT;comparative toxic unit for human (CTUh) no LT;CTUh +EF v3.1 no LT;human toxicity: non-carcinogenic, organics no LT;comparative toxic unit for human (CTUh) no LT;CTUh +EF v3.1 no LT;ionising radiation: human health no LT;human exposure efficiency relative to u235 no LT;kBq U235-Eq +EF v3.1 no LT;land use no LT;soil quality index no LT;dimensionless +EF v3.1 no LT;material resources: metals/minerals no LT;abiotic depletion potential (ADP): elements (ultimate reserves) no LT;kg Sb-Eq +EF v3.1 no LT;ozone depletion no LT;ozone depletion potential (ODP) no LT;kg CFC-11-Eq +EF v3.1 no LT;particulate matter formation no LT;impact on human health no LT;disease incidence +EF v3.1 no LT;photochemical oxidant formation: human health no LT;tropospheric ozone concentration increase no LT;kg NMVOC-Eq +EF v3.1 no LT;water use no LT;user deprivation potential (deprivation-weighted water consumption) no LT;m3 world Eq deprived +EF v3.1;acidification;accumulated exceedance (AE);mol H+-Eq +EF v3.1;climate change;global warming potential (GWP100);kg CO2-Eq +EF v3.1;climate change: biogenic;global warming potential (GWP100);kg CO2-Eq +EF v3.1;climate change: fossil;global warming potential (GWP100);kg CO2-Eq +EF v3.1;climate change: land use and land use change;global warming potential (GWP100);kg CO2-Eq +EF v3.1;ecotoxicity: freshwater;comparative toxic unit for ecosystems (CTUe);CTUe +EF v3.1;ecotoxicity: freshwater, inorganics;comparative toxic unit for ecosystems (CTUe);CTUe +EF v3.1;ecotoxicity: freshwater, organics;comparative toxic unit for ecosystems (CTUe);CTUe +EF v3.1;energy resources: non-renewable;abiotic depletion potential (ADP): fossil fuels;MJ, net calorific value +EF v3.1;eutrophication: freshwater;fraction of nutrients reaching freshwater end compartment (P);kg P-Eq +EF v3.1;eutrophication: marine;fraction of nutrients reaching marine end compartment (N);kg N-Eq +EF v3.1;eutrophication: terrestrial;accumulated exceedance (AE);mol N-Eq +EF v3.1;human toxicity: carcinogenic;comparative toxic unit for human (CTUh);CTUh +EF v3.1;human toxicity: carcinogenic, inorganics;comparative toxic unit for human (CTUh);CTUh +EF v3.1;human toxicity: carcinogenic, organics;comparative toxic unit for human (CTUh);CTUh +EF v3.1;human toxicity: non-carcinogenic;comparative toxic unit for human (CTUh);CTUh +EF v3.1;human toxicity: non-carcinogenic, inorganics;comparative toxic unit for human (CTUh);CTUh +EF v3.1;human toxicity: non-carcinogenic, organics;comparative toxic unit for human (CTUh);CTUh +EF v3.1;ionising radiation: human health;human exposure efficiency relative to u235;kBq U235-Eq +EF v3.1;land use;soil quality index;dimensionless +EF v3.1;material resources: metals/minerals;abiotic depletion potential (ADP): elements (ultimate reserves);kg Sb-Eq +EF v3.1;ozone depletion;ozone depletion potential (ODP);kg CFC-11-Eq +EF v3.1;particulate matter formation;impact on human health;disease incidence +EF v3.1;photochemical oxidant formation: human health;tropospheric ozone concentration increase;kg NMVOC-Eq +EF v3.1;water use;user deprivation potential (deprivation-weighted water consumption);m3 world Eq deprived +EN15804+A2 - Additional impact categories and indicators;particulate matter formation;impact on human health;disease incidence +EN15804+A2 - Additional impact categories and indicators;ionising radiation: human health;human exposure efficiency relative to u235;kBq U235-Eq +EN15804+A2 - Additional impact categories and indicators;ecotoxicity: freshwater (EF v3.0);comparative toxic unit for ecosystems (CTUe);CTUe +EN15804+A2 - Additional impact categories and indicators;human toxicity: carcinogenic;comparative toxic unit for human (CTUh);CTUh +EN15804+A2 - Additional impact categories and indicators;human toxicity: non-carcinogenic (EF v3.0);comparative toxic unit for human (CTUh);CTUh +EN15804+A2 - Additional impact categories and indicators;land use;soil quality index;dimensionless +EN15804+A2 - Additional impact categories and indicators;ecotoxicity: freshwater (EF v3.1);comparative toxic unit for ecosystems (CTUe);CTUe +EN15804+A2 - Additional impact categories and indicators;human toxicity: non-carcinogenic (EF v3.1);comparative toxic unit for human (CTUh);CTUh +EN15804+A2 - Core impact categories and indicators;climate change: total (EF v3.0 - IPCC 2013);global warming potential (GWP100);kg CO2-Eq +EN15804+A2 - Core impact categories and indicators;climate change: fossil (EF v3.0 - IPCC 2013);global warming potential (GWP100);kg CO2-Eq +EN15804+A2 - Core impact categories and indicators;climate change: biogenic (EF v3.0 - IPCC 2013);global warming potential (GWP100);kg CO2-Eq +EN15804+A2 - Core impact categories and indicators;climate change: land use and land use change (EF v3.0 - IPCC 2013);global warming potential (GWP100);kg CO2-Eq +EN15804+A2 - Core impact categories and indicators;ozone depletion;ozone depletion potential (ODP);kg CFC-11-Eq +EN15804+A2 - Core impact categories and indicators;acidification;accumulated exceedance (AE);mol H+-Eq +EN15804+A2 - Core impact categories and indicators;eutrophication: freshwater;fraction of nutrients reaching freshwater end compartment (P);kg P-Eq +EN15804+A2 - Core impact categories and indicators;eutrophication: marine;fraction of nutrients reaching marine end compartment (N);kg N-Eq +EN15804+A2 - Core impact categories and indicators;eutrophication: terrestrial;accumulated exceedance (AE);mol H+-Eq +EN15804+A2 - Core impact categories and indicators;photochemical oxidant formation: human health;tropospheric ozone concentration increase;kg NMVOC-Eq +EN15804+A2 - Core impact categories and indicators;material resources: metals/minerals;abiotic depletion potential (ADP): elements (ultimate reserves);kg Sb-Eq +EN15804+A2 - Core impact categories and indicators;energy resources: non-renewable;abiotic depletion potential (ADP): fossil fuels;MJ, net calorific value +EN15804+A2 - Core impact categories and indicators;water use;user deprivation potential (deprivation-weighted water consumption);m3 world Eq deprived +EN15804+A2 - Core impact categories and indicators;climate change: total (EF v3.1 - IPCC 2021);global warming potential (GWP100);kg CO2-Eq +EN15804+A2 - Core impact categories and indicators;climate change: fossil (EF v3.1 - IPCC 2021);global warming potential (GWP100);kg CO2-Eq +EN15804+A2 - Core impact categories and indicators;climate change: biogenic (EF v3.1 - IPCC 2021);global warming potential (GWP100);kg CO2-Eq +EN15804+A2 - Core impact categories and indicators;climate change: land use and land use change (EF v3.1 - IPCC 2021);global warming potential (GWP100);kg CO2-Eq +EN15804+A2 - Indicators describing output flows;exported energy - electricity;EEE;MJ +EN15804+A2 - Indicators describing output flows;exported energy - heat;EET;MJ +EN15804+A2 - Indicators describing output flows;materials for energy recovery;MER;kg +EN15804+A2 - Indicators describing output flows;materials for recycling;MFR;kg +EN15804+A2 - Indicators describing output flows;exported energy;EE;MJ +EN15804+A2 - Indicators describing output flows;components for re-use;CRU;kg +EN15804+A2 - Indicators describing resource use;net use of fresh water;FW;m3 +EN15804+A2 - Indicators describing resource use;use of renewable secondary fuels;RSF;MJ +EN15804+A2 - Indicators describing resource use;use of non-renewable secondary fuels;NRSF;MJ +EN15804+A2 - Indicators describing resource use;use of secondary material;SM;kg +EN15804+A2 - Indicators describing resource use;total use of renewable primary energy resources (primary energy and primary energy resources used as raw materials);PERT;MJ +EN15804+A2 - Indicators describing resource use;total use of non-renewable primary energy resources (primary energy and primary energy resources used as raw materials);PENRT;MJ +EN15804+A2 - Indicators describing waste categories;hazardous waste disposed;HWD;kg +EN15804+A2 - Indicators describing waste categories;high-level radioactive waste disposed;HLRW;kg +EN15804+A2 - Indicators describing waste categories;intermediate and low-level radioactive waste disposed;ILLRW;kg +EN15804+A2 - Indicators describing waste categories;non-hazardous waste disposed;NHWD;kg +EN15804+A2 - Indicators describing biogenic carbon content at factory gate;biogenic carbon content in accompanying packaging;BCCAP;kg C +EN15804+A2 - Indicators describing biogenic carbon content at factory gate;biogenic carbon content in product;BCCP;kg C +EN15804+A2 - Indicators describing resource use;use of non-renewable primary energy excluding non-renewable primary energy resources used as raw materials;PENRE;MJ +EN15804+A2 - Indicators describing resource use;use of non-renewable primary energy resources used as raw materials;PENRM;MJ +EN15804+A2 - Indicators describing resource use;use of renewable primary energy excluding renewable primary energy resources used as raw materials;PERE;MJ +EN15804+A2 - Indicators describing resource use;use of renewable primary energy resources used as raw materials;PERM;MJ +EPS 2020d no LT;emissions to air no LT;monetary impact value no LT;EUR 2018 +EPS 2020d no LT;emissions to soil no LT;monetary impact value no LT;EUR 2018 +EPS 2020d no LT;emissions to water no LT;monetary impact value no LT;EUR 2018 +EPS 2020d no LT;energy resources: non-renewable, fossil no LT;monetary impact value no LT;EUR 2018 +EPS 2020d no LT;land use no LT;monetary impact value no LT;EUR 2018 +EPS 2020d no LT;material resources: metals no LT;monetary impact value no LT;EUR 2018 +EPS 2020d no LT;total no LT;monetary impact value no LT;EUR 2018 +EPS 2020d;emissions to air;monetary impact value;EUR 2018 +EPS 2020d;emissions to soil;monetary impact value;EUR 2018 +EPS 2020d;emissions to water;monetary impact value;EUR 2018 +EPS 2020d;energy resources: non-renewable, fossil;monetary impact value;EUR 2018 +EPS 2020d;land use;monetary impact value;EUR 2018 +EPS 2020d;material resources: metals;monetary impact value;EUR 2018 +EPS 2020d;total;monetary impact value;EUR 2018 +IMPACT World+ v2.1, footprint version;climate change;carbon footprint;kg CO2-Eq +IMPACT World+ v2.1, footprint version;ecosystem quality;remaining ecosystem quality damage;PDF.m2.yr +IMPACT World+ v2.1, footprint version;energy resources: non-renewable;fossil and nuclear energy use;MJ deprived +IMPACT World+ v2.1, footprint version;human health;remaining human health damage;DALYs +IMPACT World+ v2.1, footprint version;water use;water scarcity footprint;m3 world Eq deprived +Inventory results and indicators;emissions to air;total particulate matter;kg +Inventory results and indicators;emissions to air;particulate matter, < 2.5 um;kg +Inventory results and indicators;emissions to air;particulate matter, >2.5 um and <10;kg +Inventory results and indicators;emissions to air;particulate matter >10 um;kg +Inventory results and indicators;emissions to air;carbon dioxide, fossil and land use;kg +Inventory results and indicators;emissions to air;total carbon monoxide;kg +Inventory results and indicators;emissions to air;total methane;kg +Inventory results and indicators;emissions to air;carbon, non-fossil, fixed;kg +Inventory results and indicators;emissions to air;NMVOCs;kg +Inventory results and indicators;emissions to air;N2O;kg +Inventory results and indicators;emissions to air;NOx;kg +Inventory results and indicators;emissions to air;SOx;kg +Inventory results and indicators;emissions to air;ammonia;kg +Inventory results and indicators;emissions to air;lead;kg +Inventory results and indicators;emissions to air;photochemical oxidants (including ozone);kg +Inventory results and indicators;emissions to air;HAPs;kg +Inventory results and indicators;emissions to air;total radioactive emissions;kg +Inventory results and indicators;emissions to water;total radioactive emissions;kg +Inventory results and indicators;resources;total surface occupation;m2a +Inventory results and indicators;resources;land occupation;m2a +Inventory results and indicators;resources;land occupation by flooding;m2a +Inventory results and indicators;resources;seabed occupation;m2a +Inventory results and indicators;resources;total water extraction;m3 +Inventory results and indicators;resources;total freshwater extraction;m3 +Inventory results and indicators;resources;freshwater extraction, surface water;m3 +Inventory results and indicators;resources;freshwater extraction, groundwater;m3 +Inventory results and indicators;resources;water extraction, saltwater;m3 +Inventory results and indicators;resources;water extraction, undefined;m3 +Inventory results and indicators;waste disposal;total area for dump sites;m2 +Inventory results and indicators;waste disposal;total volume of underground deposits;m3 +Inventory results and indicators;waste disposal;total volume of radioactive waste;m3 +Inventory results and indicators;waste disposal;total mass of waste;kg +Inventory results and indicators;waste disposal;total mass for dump sites;kg +Inventory results and indicators;waste disposal;total mass for underground deposits;kg +Inventory results and indicators;waste disposal;total mass of radioactive waste;kg +IPCC 2013 no LT;climate change no LT;global temperature change potential (GTP100) no LT;kg CO2-Eq +IPCC 2013 no LT;climate change no LT;global temperature change potential (GTP20) no LT;kg CO2-Eq +IPCC 2013 no LT;climate change no LT;global warming potential (GWP100) no LT;kg CO2-Eq +IPCC 2013 no LT;climate change no LT;global warming potential (GWP20) no LT;kg CO2-Eq +IPCC 2013;climate change;global temperature change potential (GTP100);kg CO2-Eq +IPCC 2013;climate change;global temperature change potential (GTP20);kg CO2-Eq +IPCC 2013;climate change;global warming potential (GWP100);kg CO2-Eq +IPCC 2013;climate change;global warming potential (GWP20);kg CO2-Eq +IPCC 2021 (incl. biogenic CO2) no LT;climate change: biogenic (incl. CO2) no LT;global warming potential (GWP100) no LT;kg CO2-Eq +IPCC 2021 (incl. biogenic CO2) no LT;climate change: biogenic emissions (incl. CO2) no LT;global warming potential (GWP100) no LT;kg CO2-Eq +IPCC 2021 (incl. biogenic CO2) no LT;climate change: biogenic removals (incl. CO2) no LT;global warming potential (GWP100) no LT;kg CO2-Eq +IPCC 2021 (incl. biogenic CO2) no LT;climate change: total (incl. biogenic CO2) no LT;global warming potential (GWP100) no LT;kg CO2-Eq +IPCC 2021 (incl. biogenic CO2) no LT;climate change: total (incl. biogenic CO2, incl. SLCFs) no LT;global warming potential (GWP100) no LT;kg CO2-Eq +IPCC 2021 (incl. biogenic CO2);climate change: biogenic (incl. CO2);global warming potential (GWP100);kg CO2-Eq +IPCC 2021 (incl. biogenic CO2);climate change: biogenic emissions (incl. CO2);global warming potential (GWP100);kg CO2-Eq +IPCC 2021 (incl. biogenic CO2);climate change: biogenic removals (incl. CO2);global warming potential (GWP100);kg CO2-Eq +IPCC 2021 (incl. biogenic CO2);climate change: total (incl. biogenic CO2);global warming potential (GWP100);kg CO2-Eq +IPCC 2021 (incl. biogenic CO2);climate change: total (incl. biogenic CO2, incl. SLCFs);global warming potential (GWP100);kg CO2-Eq +IPCC 2021 no LT;climate change: aircraft emissions no LT;global warming potential (GWP100) no LT;kg CO2-Eq +IPCC 2021 no LT;climate change: biogenic (excl. CO2) no LT;global warming potential (GWP100) no LT;kg CO2-Eq +IPCC 2021 no LT;climate change: direct land use change no LT;global warming potential (GWP100) no LT;kg CO2-Eq +IPCC 2021 no LT;climate change: emissions from direct land use change no LT;global warming potential (GWP100) no LT;kg CO2-Eq +IPCC 2021 no LT;climate change: fossil no LT;global warming potential (GWP100) no LT;kg CO2-Eq +IPCC 2021 no LT;climate change: fossil emissions (excl. aircraft emissions) no LT;global warming potential (GWP100) no LT;kg CO2-Eq +IPCC 2021 no LT;climate change: removals from direct land use change no LT;global warming potential (GWP100) no LT;kg CO2-Eq +IPCC 2021 no LT;climate change: total (excl. biogenic CO2) no LT;global warming potential (GWP500) no LT;kg CO2-Eq +IPCC 2021 no LT;climate change: total (excl. biogenic CO2) no LT;global warming potential (GWP20) no LT;kg CO2-Eq +IPCC 2021 no LT;climate change: total (excl. biogenic CO2) no LT;global warming potential (GWP100) no LT;kg CO2-Eq +IPCC 2021 no LT;climate change: total (excl. biogenic CO2) no LT;global temperature change potential (GTP50) no LT;kg CO2-Eq +IPCC 2021 no LT;climate change: total (excl. biogenic CO2) no LT;global temperature change potential (GTP100) no LT;kg CO2-Eq +IPCC 2021 no LT;climate change: total (excl. biogenic CO2, incl. SLCFs) no LT;global warming potential (GWP100) no LT;kg CO2-Eq +IPCC 2021 no LT;climate change: biogenic (excl. CO2, incl. SLCFs) no LT;global warming potential (GWP100) no LT;kg CO2-Eq +IPCC 2021 no LT;climate change: fossil (excl. biogenic CO2, incl. SLCFs) no LT;global warming potential (GWP100) no LT;kg CO2-Eq +IPCC 2021 no LT;climate change: direct land use change (incl. SLCFs) no LT;global warming potential (GWP100) no LT;kg CO2-Eq +IPCC 2021;climate change: aircraft emissions;global warming potential (GWP100);kg CO2-Eq +IPCC 2021;climate change: biogenic (excl. CO2);global warming potential (GWP100);kg CO2-Eq +IPCC 2021;climate change: direct land use change;global warming potential (GWP100);kg CO2-Eq +IPCC 2021;climate change: emissions from direct land use change;global warming potential (GWP100);kg CO2-Eq +IPCC 2021;climate change: fossil;global warming potential (GWP100);kg CO2-Eq +IPCC 2021;climate change: fossil emissions (excl. aircraft emissions);global warming potential (GWP100);kg CO2-Eq +IPCC 2021;climate change: removals from direct land use change;global warming potential (GWP100);kg CO2-Eq +IPCC 2021;climate change: total (excl. biogenic CO2);global warming potential (GWP500);kg CO2-Eq +IPCC 2021;climate change: total (excl. biogenic CO2);global warming potential (GWP20);kg CO2-Eq +IPCC 2021;climate change: total (excl. biogenic CO2);global warming potential (GWP100);kg CO2-Eq +IPCC 2021;climate change: total (excl. biogenic CO2);global temperature change potential (GTP50);kg CO2-Eq +IPCC 2021;climate change: total (excl. biogenic CO2);global temperature change potential (GTP100);kg CO2-Eq +IPCC 2021;climate change: total (excl. biogenic CO2, incl. SLCFs);global warming potential (GWP100);kg CO2-Eq +IPCC 2021;climate change: biogenic (excl. CO2, incl. SLCFs);global warming potential (GWP100);kg CO2-Eq +IPCC 2021;climate change: fossil (excl. biogenic CO2, incl. SLCFs);global warming potential (GWP100);kg CO2-Eq +IPCC 2021;climate change: direct land use change (incl. SLCFs);global warming potential (GWP100);kg CO2-Eq +ReCiPe 2016 v1.03, endpoint (E) no LT;ecosystem quality no LT;acidification: terrestrial no LT;species.yr +ReCiPe 2016 v1.03, endpoint (E) no LT;ecosystem quality no LT;climate change: freshwater ecosystems no LT;species.yr +ReCiPe 2016 v1.03, endpoint (E) no LT;ecosystem quality no LT;climate change: terrestrial ecosystems no LT;species.yr +ReCiPe 2016 v1.03, endpoint (E) no LT;ecosystem quality no LT;ecotoxicity: freshwater no LT;species.yr +ReCiPe 2016 v1.03, endpoint (E) no LT;ecosystem quality no LT;ecotoxicity: marine no LT;species.yr +ReCiPe 2016 v1.03, endpoint (E) no LT;ecosystem quality no LT;ecotoxicity: terrestrial no LT;species.yr +ReCiPe 2016 v1.03, endpoint (E) no LT;ecosystem quality no LT;eutrophication: freshwater no LT;species.yr +ReCiPe 2016 v1.03, endpoint (E) no LT;ecosystem quality no LT;eutrophication: marine no LT;species.yr +ReCiPe 2016 v1.03, endpoint (E) no LT;ecosystem quality no LT;land use no LT;species.yr +ReCiPe 2016 v1.03, endpoint (E) no LT;ecosystem quality no LT;photochemical oxidant formation: terrestrial ecosystems no LT;species.yr +ReCiPe 2016 v1.03, endpoint (E) no LT;ecosystem quality no LT;water use: aquatic ecosystems no LT;species.yr +ReCiPe 2016 v1.03, endpoint (E) no LT;ecosystem quality no LT;water use: terrestrial ecosystems no LT;species.yr +ReCiPe 2016 v1.03, endpoint (E) no LT;human health no LT;climate change: human health no LT;DALYs +ReCiPe 2016 v1.03, endpoint (E) no LT;human health no LT;human toxicity: carcinogenic no LT;DALYs +ReCiPe 2016 v1.03, endpoint (E) no LT;human health no LT;human toxicity: non-carcinogenic no LT;DALYs +ReCiPe 2016 v1.03, endpoint (E) no LT;human health no LT;ionising radiation no LT;DALYs +ReCiPe 2016 v1.03, endpoint (E) no LT;human health no LT;ozone depletion no LT;DALYs +ReCiPe 2016 v1.03, endpoint (E) no LT;human health no LT;particulate matter formation no LT;DALYs +ReCiPe 2016 v1.03, endpoint (E) no LT;human health no LT;photochemical oxidant formation: human health no LT;DALYs +ReCiPe 2016 v1.03, endpoint (E) no LT;human health no LT;water use: human health no LT;DALYs +ReCiPe 2016 v1.03, endpoint (E) no LT;natural resources no LT;energy resources: non-renewable, fossil no LT;USD 2013 +ReCiPe 2016 v1.03, endpoint (E) no LT;natural resources no LT;material resources: metals/minerals no LT;USD 2013 +ReCiPe 2016 v1.03, endpoint (E) no LT;total: ecosystem quality no LT;ecosystem quality no LT;species.yr +ReCiPe 2016 v1.03, endpoint (E) no LT;total: human health no LT;human health no LT;DALYs +ReCiPe 2016 v1.03, endpoint (E) no LT;total: natural resources no LT;natural resources no LT;USD 2013 +ReCiPe 2016 v1.03, endpoint (E);ecosystem quality;acidification: terrestrial;species.yr +ReCiPe 2016 v1.03, endpoint (E);ecosystem quality;climate change: freshwater ecosystems;species.yr +ReCiPe 2016 v1.03, endpoint (E);ecosystem quality;climate change: terrestrial ecosystems;species.yr +ReCiPe 2016 v1.03, endpoint (E);ecosystem quality;ecotoxicity: freshwater;species.yr +ReCiPe 2016 v1.03, endpoint (E);ecosystem quality;ecotoxicity: marine;species.yr +ReCiPe 2016 v1.03, endpoint (E);ecosystem quality;ecotoxicity: terrestrial;species.yr +ReCiPe 2016 v1.03, endpoint (E);ecosystem quality;eutrophication: freshwater;species.yr +ReCiPe 2016 v1.03, endpoint (E);ecosystem quality;eutrophication: marine;species.yr +ReCiPe 2016 v1.03, endpoint (E);ecosystem quality;land use;species.yr +ReCiPe 2016 v1.03, endpoint (E);ecosystem quality;photochemical oxidant formation: terrestrial ecosystems;species.yr +ReCiPe 2016 v1.03, endpoint (E);ecosystem quality;water use: aquatic ecosystems;species.yr +ReCiPe 2016 v1.03, endpoint (E);ecosystem quality;water use: terrestrial ecosystems;species.yr +ReCiPe 2016 v1.03, endpoint (E);human health;climate change: human health;DALYs +ReCiPe 2016 v1.03, endpoint (E);human health;human toxicity: carcinogenic;DALYs +ReCiPe 2016 v1.03, endpoint (E);human health;human toxicity: non-carcinogenic;DALYs +ReCiPe 2016 v1.03, endpoint (E);human health;ionising radiation;DALYs +ReCiPe 2016 v1.03, endpoint (E);human health;ozone depletion;DALYs +ReCiPe 2016 v1.03, endpoint (E);human health;particulate matter formation;DALYs +ReCiPe 2016 v1.03, endpoint (E);human health;photochemical oxidant formation: human health;DALYs +ReCiPe 2016 v1.03, endpoint (E);human health;water use: human health;DALYs +ReCiPe 2016 v1.03, endpoint (E);natural resources;energy resources: non-renewable, fossil;USD 2013 +ReCiPe 2016 v1.03, endpoint (E);natural resources;material resources: metals/minerals;USD 2013 +ReCiPe 2016 v1.03, endpoint (E);total: ecosystem quality;ecosystem quality;species.yr +ReCiPe 2016 v1.03, endpoint (E);total: human health;human health;DALYs +ReCiPe 2016 v1.03, endpoint (E);total: natural resources;natural resources;USD 2013 +ReCiPe 2016 v1.03, endpoint (H) no LT;ecosystem quality no LT;acidification: terrestrial no LT;species.yr +ReCiPe 2016 v1.03, endpoint (H) no LT;ecosystem quality no LT;climate change: freshwater ecosystems no LT;species.yr +ReCiPe 2016 v1.03, endpoint (H) no LT;ecosystem quality no LT;climate change: terrestrial ecosystems no LT;species.yr +ReCiPe 2016 v1.03, endpoint (H) no LT;ecosystem quality no LT;ecotoxicity: freshwater no LT;species.yr +ReCiPe 2016 v1.03, endpoint (H) no LT;ecosystem quality no LT;ecotoxicity: marine no LT;species.yr +ReCiPe 2016 v1.03, endpoint (H) no LT;ecosystem quality no LT;ecotoxicity: terrestrial no LT;species.yr +ReCiPe 2016 v1.03, endpoint (H) no LT;ecosystem quality no LT;eutrophication: freshwater no LT;species.yr +ReCiPe 2016 v1.03, endpoint (H) no LT;ecosystem quality no LT;eutrophication: marine no LT;species.yr +ReCiPe 2016 v1.03, endpoint (H) no LT;ecosystem quality no LT;land use no LT;species.yr +ReCiPe 2016 v1.03, endpoint (H) no LT;ecosystem quality no LT;photochemical oxidant formation: terrestrial ecosystems no LT;species.yr +ReCiPe 2016 v1.03, endpoint (H) no LT;ecosystem quality no LT;water use: aquatic ecosystems no LT;species.yr +ReCiPe 2016 v1.03, endpoint (H) no LT;ecosystem quality no LT;water use: terrestrial ecosystems no LT;species.yr +ReCiPe 2016 v1.03, endpoint (H) no LT;human health no LT;climate change: human health no LT;DALYs +ReCiPe 2016 v1.03, endpoint (H) no LT;human health no LT;human toxicity: carcinogenic no LT;DALYs +ReCiPe 2016 v1.03, endpoint (H) no LT;human health no LT;human toxicity: non-carcinogenic no LT;DALYs +ReCiPe 2016 v1.03, endpoint (H) no LT;human health no LT;ionising radiation no LT;DALYs +ReCiPe 2016 v1.03, endpoint (H) no LT;human health no LT;ozone depletion no LT;DALYs +ReCiPe 2016 v1.03, endpoint (H) no LT;human health no LT;particulate matter formation no LT;DALYs +ReCiPe 2016 v1.03, endpoint (H) no LT;human health no LT;photochemical oxidant formation: human health no LT;DALYs +ReCiPe 2016 v1.03, endpoint (H) no LT;human health no LT;water use: human health no LT;DALYs +ReCiPe 2016 v1.03, endpoint (H) no LT;natural resources no LT;energy resources: non-renewable, fossil no LT;USD 2013 +ReCiPe 2016 v1.03, endpoint (H) no LT;natural resources no LT;material resources: metals/minerals no LT;USD 2013 +ReCiPe 2016 v1.03, endpoint (H) no LT;total: ecosystem quality no LT;ecosystem quality no LT;species.yr +ReCiPe 2016 v1.03, endpoint (H) no LT;total: human health no LT;human health no LT;DALYs +ReCiPe 2016 v1.03, endpoint (H) no LT;total: natural resources no LT;natural resources no LT;USD 2013 +ReCiPe 2016 v1.03, endpoint (H);ecosystem quality;acidification: terrestrial;species.yr +ReCiPe 2016 v1.03, endpoint (H);ecosystem quality;climate change: freshwater ecosystems;species.yr +ReCiPe 2016 v1.03, endpoint (H);ecosystem quality;climate change: terrestrial ecosystems;species.yr +ReCiPe 2016 v1.03, endpoint (H);ecosystem quality;ecotoxicity: freshwater;species.yr +ReCiPe 2016 v1.03, endpoint (H);ecosystem quality;ecotoxicity: marine;species.yr +ReCiPe 2016 v1.03, endpoint (H);ecosystem quality;ecotoxicity: terrestrial;species.yr +ReCiPe 2016 v1.03, endpoint (H);ecosystem quality;eutrophication: freshwater;species.yr +ReCiPe 2016 v1.03, endpoint (H);ecosystem quality;eutrophication: marine;species.yr +ReCiPe 2016 v1.03, endpoint (H);ecosystem quality;land use;species.yr +ReCiPe 2016 v1.03, endpoint (H);ecosystem quality;photochemical oxidant formation: terrestrial ecosystems;species.yr +ReCiPe 2016 v1.03, endpoint (H);ecosystem quality;water use: aquatic ecosystems;species.yr +ReCiPe 2016 v1.03, endpoint (H);ecosystem quality;water use: terrestrial ecosystems;species.yr +ReCiPe 2016 v1.03, endpoint (H);human health;climate change: human health;DALYs +ReCiPe 2016 v1.03, endpoint (H);human health;human toxicity: carcinogenic;DALYs +ReCiPe 2016 v1.03, endpoint (H);human health;human toxicity: non-carcinogenic;DALYs +ReCiPe 2016 v1.03, endpoint (H);human health;ionising radiation;DALYs +ReCiPe 2016 v1.03, endpoint (H);human health;ozone depletion;DALYs +ReCiPe 2016 v1.03, endpoint (H);human health;particulate matter formation;DALYs +ReCiPe 2016 v1.03, endpoint (H);human health;photochemical oxidant formation: human health;DALYs +ReCiPe 2016 v1.03, endpoint (H);human health;water use: human health;DALYs +ReCiPe 2016 v1.03, endpoint (H);natural resources;energy resources: non-renewable, fossil;USD 2013 +ReCiPe 2016 v1.03, endpoint (H);natural resources;material resources: metals/minerals;USD 2013 +ReCiPe 2016 v1.03, endpoint (H);total: ecosystem quality;ecosystem quality;species.yr +ReCiPe 2016 v1.03, endpoint (H);total: human health;human health;DALYs +ReCiPe 2016 v1.03, endpoint (H);total: natural resources;natural resources;USD 2013 +ReCiPe 2016 v1.03, endpoint (I) no LT;ecosystem quality no LT;acidification: terrestrial no LT;species.yr +ReCiPe 2016 v1.03, endpoint (I) no LT;ecosystem quality no LT;climate change: freshwater ecosystems no LT;species.yr +ReCiPe 2016 v1.03, endpoint (I) no LT;ecosystem quality no LT;climate change: terrestrial ecosystems no LT;species.yr +ReCiPe 2016 v1.03, endpoint (I) no LT;ecosystem quality no LT;ecotoxicity: freshwater no LT;species.yr +ReCiPe 2016 v1.03, endpoint (I) no LT;ecosystem quality no LT;ecotoxicity: marine no LT;species.yr +ReCiPe 2016 v1.03, endpoint (I) no LT;ecosystem quality no LT;ecotoxicity: terrestrial no LT;species.yr +ReCiPe 2016 v1.03, endpoint (I) no LT;ecosystem quality no LT;eutrophication: freshwater no LT;species.yr +ReCiPe 2016 v1.03, endpoint (I) no LT;ecosystem quality no LT;eutrophication: marine no LT;species.yr +ReCiPe 2016 v1.03, endpoint (I) no LT;ecosystem quality no LT;land use no LT;species.yr +ReCiPe 2016 v1.03, endpoint (I) no LT;ecosystem quality no LT;photochemical oxidant formation: terrestrial ecosystems no LT;species.yr +ReCiPe 2016 v1.03, endpoint (I) no LT;ecosystem quality no LT;water use: aquatic ecosystems no LT;species.yr +ReCiPe 2016 v1.03, endpoint (I) no LT;human health no LT;climate change: human health no LT;DALYs +ReCiPe 2016 v1.03, endpoint (I) no LT;human health no LT;human toxicity: carcinogenic no LT;DALYs +ReCiPe 2016 v1.03, endpoint (I) no LT;human health no LT;human toxicity: non-carcinogenic no LT;DALYs +ReCiPe 2016 v1.03, endpoint (I) no LT;human health no LT;ionising radiation no LT;DALYs +ReCiPe 2016 v1.03, endpoint (I) no LT;human health no LT;ozone depletion no LT;DALYs +ReCiPe 2016 v1.03, endpoint (I) no LT;human health no LT;particulate matter formation no LT;DALYs +ReCiPe 2016 v1.03, endpoint (I) no LT;human health no LT;photochemical oxidant formation: human health no LT;DALYs +ReCiPe 2016 v1.03, endpoint (I) no LT;human health no LT;water use: human health no LT;DALYs +ReCiPe 2016 v1.03, endpoint (I) no LT;natural resources no LT;energy resources: non-renewable, fossil no LT;USD 2013 +ReCiPe 2016 v1.03, endpoint (I) no LT;natural resources no LT;material resources: metals/minerals no LT;USD 2013 +ReCiPe 2016 v1.03, endpoint (I) no LT;total: ecosystem quality no LT;ecosystem quality no LT;species.yr +ReCiPe 2016 v1.03, endpoint (I) no LT;total: human health no LT;human health no LT;DALYs +ReCiPe 2016 v1.03, endpoint (I) no LT;total: natural resources no LT;natural resources no LT;USD 2013 +ReCiPe 2016 v1.03, endpoint (I);ecosystem quality;acidification: terrestrial;species.yr +ReCiPe 2016 v1.03, endpoint (I);ecosystem quality;climate change: freshwater ecosystems;species.yr +ReCiPe 2016 v1.03, endpoint (I);ecosystem quality;climate change: terrestrial ecosystems;species.yr +ReCiPe 2016 v1.03, endpoint (I);ecosystem quality;ecotoxicity: freshwater;species.yr +ReCiPe 2016 v1.03, endpoint (I);ecosystem quality;ecotoxicity: marine;species.yr +ReCiPe 2016 v1.03, endpoint (I);ecosystem quality;ecotoxicity: terrestrial;species.yr +ReCiPe 2016 v1.03, endpoint (I);ecosystem quality;eutrophication: freshwater;species.yr +ReCiPe 2016 v1.03, endpoint (I);ecosystem quality;eutrophication: marine;species.yr +ReCiPe 2016 v1.03, endpoint (I);ecosystem quality;land use;species.yr +ReCiPe 2016 v1.03, endpoint (I);ecosystem quality;photochemical oxidant formation: terrestrial ecosystems;species.yr +ReCiPe 2016 v1.03, endpoint (I);ecosystem quality;water use: aquatic ecosystems;species.yr +ReCiPe 2016 v1.03, endpoint (I);human health;climate change: human health;DALYs +ReCiPe 2016 v1.03, endpoint (I);human health;human toxicity: carcinogenic;DALYs +ReCiPe 2016 v1.03, endpoint (I);human health;human toxicity: non-carcinogenic;DALYs +ReCiPe 2016 v1.03, endpoint (I);human health;ionising radiation;DALYs +ReCiPe 2016 v1.03, endpoint (I);human health;ozone depletion;DALYs +ReCiPe 2016 v1.03, endpoint (I);human health;particulate matter formation;DALYs +ReCiPe 2016 v1.03, endpoint (I);human health;photochemical oxidant formation: human health;DALYs +ReCiPe 2016 v1.03, endpoint (I);human health;water use: human health;DALYs +ReCiPe 2016 v1.03, endpoint (I);natural resources;energy resources: non-renewable, fossil;USD 2013 +ReCiPe 2016 v1.03, endpoint (I);natural resources;material resources: metals/minerals;USD 2013 +ReCiPe 2016 v1.03, endpoint (I);total: ecosystem quality;ecosystem quality;species.yr +ReCiPe 2016 v1.03, endpoint (I);total: human health;human health;DALYs +ReCiPe 2016 v1.03, endpoint (I);total: natural resources;natural resources;USD 2013 +ReCiPe 2016 v1.03, midpoint (E) no LT;acidification: terrestrial no LT;terrestrial acidification potential (TAP) no LT;kg SO2-Eq +ReCiPe 2016 v1.03, midpoint (E) no LT;climate change no LT;global warming potential (GWP1000) no LT;kg CO2-Eq +ReCiPe 2016 v1.03, midpoint (E) no LT;ecotoxicity: freshwater no LT;freshwater ecotoxicity potential (FETP) no LT;kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (E) no LT;ecotoxicity: marine no LT;marine ecotoxicity potential (METP) no LT;kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (E) no LT;ecotoxicity: terrestrial no LT;terrestrial ecotoxicity potential (TETP) no LT;kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (E) no LT;energy resources: non-renewable, fossil no LT;fossil fuel potential (FFP) no LT;kg oil-Eq +ReCiPe 2016 v1.03, midpoint (E) no LT;eutrophication: freshwater no LT;freshwater eutrophication potential (FEP) no LT;kg P-Eq +ReCiPe 2016 v1.03, midpoint (E) no LT;eutrophication: marine no LT;marine eutrophication potential (MEP) no LT;kg N-Eq +ReCiPe 2016 v1.03, midpoint (E) no LT;human toxicity: carcinogenic no LT;human toxicity potential (HTPc) no LT;kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (E) no LT;human toxicity: non-carcinogenic no LT;human toxicity potential (HTPnc) no LT;kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (E) no LT;ionising radiation no LT;ionising radiation potential (IRP) no LT;kBq Co-60-Eq +ReCiPe 2016 v1.03, midpoint (E) no LT;land use no LT;agricultural land occupation (LOP) no LT;m2*a crop-Eq +ReCiPe 2016 v1.03, midpoint (E) no LT;material resources: metals/minerals no LT;surplus ore potential (SOP) no LT;kg Cu-Eq +ReCiPe 2016 v1.03, midpoint (E) no LT;ozone depletion no LT;ozone depletion potential (ODPinfinite) no LT;kg CFC-11-Eq +ReCiPe 2016 v1.03, midpoint (E) no LT;particulate matter formation no LT;particulate matter formation potential (PMFP) no LT;kg PM2.5-Eq +ReCiPe 2016 v1.03, midpoint (E) no LT;photochemical oxidant formation: human health no LT;photochemical oxidant formation potential: humans (HOFP) no LT;kg NOx-Eq +ReCiPe 2016 v1.03, midpoint (E) no LT;photochemical oxidant formation: terrestrial ecosystems no LT;photochemical oxidant formation potential: ecosystems (EOFP) no LT;kg NOx-Eq +ReCiPe 2016 v1.03, midpoint (E) no LT;water use no LT;water consumption potential (WCP) no LT;m3 +ReCiPe 2016 v1.03, midpoint (E);acidification: terrestrial;terrestrial acidification potential (TAP);kg SO2-Eq +ReCiPe 2016 v1.03, midpoint (E);climate change;global warming potential (GWP1000);kg CO2-Eq +ReCiPe 2016 v1.03, midpoint (E);ecotoxicity: freshwater;freshwater ecotoxicity potential (FETP);kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (E);ecotoxicity: marine;marine ecotoxicity potential (METP);kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (E);ecotoxicity: terrestrial;terrestrial ecotoxicity potential (TETP);kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (E);energy resources: non-renewable, fossil;fossil fuel potential (FFP);kg oil-Eq +ReCiPe 2016 v1.03, midpoint (E);eutrophication: freshwater;freshwater eutrophication potential (FEP);kg P-Eq +ReCiPe 2016 v1.03, midpoint (E);eutrophication: marine;marine eutrophication potential (MEP);kg N-Eq +ReCiPe 2016 v1.03, midpoint (E);human toxicity: carcinogenic;human toxicity potential (HTPc);kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (E);human toxicity: non-carcinogenic;human toxicity potential (HTPnc);kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (E);ionising radiation;ionising radiation potential (IRP);kBq Co-60-Eq +ReCiPe 2016 v1.03, midpoint (E);land use;agricultural land occupation (LOP);m2*a crop-Eq +ReCiPe 2016 v1.03, midpoint (E);material resources: metals/minerals;surplus ore potential (SOP);kg Cu-Eq +ReCiPe 2016 v1.03, midpoint (E);ozone depletion;ozone depletion potential (ODPinfinite);kg CFC-11-Eq +ReCiPe 2016 v1.03, midpoint (E);particulate matter formation;particulate matter formation potential (PMFP);kg PM2.5-Eq +ReCiPe 2016 v1.03, midpoint (E);photochemical oxidant formation: human health;photochemical oxidant formation potential: humans (HOFP);kg NOx-Eq +ReCiPe 2016 v1.03, midpoint (E);photochemical oxidant formation: terrestrial ecosystems;photochemical oxidant formation potential: ecosystems (EOFP);kg NOx-Eq +ReCiPe 2016 v1.03, midpoint (E);water use;water consumption potential (WCP);m3 +ReCiPe 2016 v1.03, midpoint (H) no LT;acidification: terrestrial no LT;terrestrial acidification potential (TAP) no LT;kg SO2-Eq +ReCiPe 2016 v1.03, midpoint (H) no LT;climate change no LT;global warming potential (GWP100) no LT;kg CO2-Eq +ReCiPe 2016 v1.03, midpoint (H) no LT;ecotoxicity: freshwater no LT;freshwater ecotoxicity potential (FETP) no LT;kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (H) no LT;ecotoxicity: marine no LT;marine ecotoxicity potential (METP) no LT;kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (H) no LT;ecotoxicity: terrestrial no LT;terrestrial ecotoxicity potential (TETP) no LT;kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (H) no LT;energy resources: non-renewable, fossil no LT;fossil fuel potential (FFP) no LT;kg oil-Eq +ReCiPe 2016 v1.03, midpoint (H) no LT;eutrophication: freshwater no LT;freshwater eutrophication potential (FEP) no LT;kg P-Eq +ReCiPe 2016 v1.03, midpoint (H) no LT;eutrophication: marine no LT;marine eutrophication potential (MEP) no LT;kg N-Eq +ReCiPe 2016 v1.03, midpoint (H) no LT;human toxicity: carcinogenic no LT;human toxicity potential (HTPc) no LT;kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (H) no LT;human toxicity: non-carcinogenic no LT;human toxicity potential (HTPnc) no LT;kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (H) no LT;ionising radiation no LT;ionising radiation potential (IRP) no LT;kBq Co-60-Eq +ReCiPe 2016 v1.03, midpoint (H) no LT;land use no LT;agricultural land occupation (LOP) no LT;m2*a crop-Eq +ReCiPe 2016 v1.03, midpoint (H) no LT;material resources: metals/minerals no LT;surplus ore potential (SOP) no LT;kg Cu-Eq +ReCiPe 2016 v1.03, midpoint (H) no LT;ozone depletion no LT;ozone depletion potential (ODPinfinite) no LT;kg CFC-11-Eq +ReCiPe 2016 v1.03, midpoint (H) no LT;particulate matter formation no LT;particulate matter formation potential (PMFP) no LT;kg PM2.5-Eq +ReCiPe 2016 v1.03, midpoint (H) no LT;photochemical oxidant formation: human health no LT;photochemical oxidant formation potential: humans (HOFP) no LT;kg NOx-Eq +ReCiPe 2016 v1.03, midpoint (H) no LT;photochemical oxidant formation: terrestrial ecosystems no LT;photochemical oxidant formation potential: ecosystems (EOFP) no LT;kg NOx-Eq +ReCiPe 2016 v1.03, midpoint (H) no LT;water use no LT;water consumption potential (WCP) no LT;m3 +ReCiPe 2016 v1.03, midpoint (H);acidification: terrestrial;terrestrial acidification potential (TAP);kg SO2-Eq +ReCiPe 2016 v1.03, midpoint (H);climate change;global warming potential (GWP100);kg CO2-Eq +ReCiPe 2016 v1.03, midpoint (H);ecotoxicity: freshwater;freshwater ecotoxicity potential (FETP);kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (H);ecotoxicity: marine;marine ecotoxicity potential (METP);kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (H);ecotoxicity: terrestrial;terrestrial ecotoxicity potential (TETP);kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (H);energy resources: non-renewable, fossil;fossil fuel potential (FFP);kg oil-Eq +ReCiPe 2016 v1.03, midpoint (H);eutrophication: freshwater;freshwater eutrophication potential (FEP);kg P-Eq +ReCiPe 2016 v1.03, midpoint (H);eutrophication: marine;marine eutrophication potential (MEP);kg N-Eq +ReCiPe 2016 v1.03, midpoint (H);human toxicity: carcinogenic;human toxicity potential (HTPc);kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (H);human toxicity: non-carcinogenic;human toxicity potential (HTPnc);kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (H);ionising radiation;ionising radiation potential (IRP);kBq Co-60-Eq +ReCiPe 2016 v1.03, midpoint (H);land use;agricultural land occupation (LOP);m2*a crop-Eq +ReCiPe 2016 v1.03, midpoint (H);material resources: metals/minerals;surplus ore potential (SOP);kg Cu-Eq +ReCiPe 2016 v1.03, midpoint (H);ozone depletion;ozone depletion potential (ODPinfinite);kg CFC-11-Eq +ReCiPe 2016 v1.03, midpoint (H);particulate matter formation;particulate matter formation potential (PMFP);kg PM2.5-Eq +ReCiPe 2016 v1.03, midpoint (H);photochemical oxidant formation: human health;photochemical oxidant formation potential: humans (HOFP);kg NOx-Eq +ReCiPe 2016 v1.03, midpoint (H);photochemical oxidant formation: terrestrial ecosystems;photochemical oxidant formation potential: ecosystems (EOFP);kg NOx-Eq +ReCiPe 2016 v1.03, midpoint (H);water use;water consumption potential (WCP);m3 +ReCiPe 2016 v1.03, midpoint (I) no LT;acidification: terrestrial no LT;terrestrial acidification potential (TAP) no LT;kg SO2-Eq +ReCiPe 2016 v1.03, midpoint (I) no LT;climate change no LT;global warming potential (GWP20) no LT;kg CO2-Eq +ReCiPe 2016 v1.03, midpoint (I) no LT;ecotoxicity: freshwater no LT;freshwater ecotoxicity potential (FETP) no LT;kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (I) no LT;ecotoxicity: marine no LT;marine ecotoxicity potential (METP) no LT;kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (I) no LT;ecotoxicity: terrestrial no LT;terrestrial ecotoxicity potential (TETP) no LT;kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (I) no LT;energy resources: non-renewable, fossil no LT;fossil fuel potential (FFP) no LT;kg oil-Eq +ReCiPe 2016 v1.03, midpoint (I) no LT;eutrophication: freshwater no LT;freshwater eutrophication potential (FEP) no LT;kg P-Eq +ReCiPe 2016 v1.03, midpoint (I) no LT;eutrophication: marine no LT;marine eutrophication potential (MEP) no LT;kg N-Eq +ReCiPe 2016 v1.03, midpoint (I) no LT;human toxicity: carcinogenic no LT;human toxicity potential (HTPc) no LT;kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (I) no LT;human toxicity: non-carcinogenic no LT;human toxicity potential (HTPnc) no LT;kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (I) no LT;ionising radiation no LT;ionising radiation potential (IRP) no LT;kBq Co-60-Eq +ReCiPe 2016 v1.03, midpoint (I) no LT;land use no LT;agricultural land occupation (LOP) no LT;m2*a crop-Eq +ReCiPe 2016 v1.03, midpoint (I) no LT;material resources: metals/minerals no LT;surplus ore potential (SOP) no LT;kg Cu-Eq +ReCiPe 2016 v1.03, midpoint (I) no LT;ozone depletion no LT;ozone depletion potential (ODPinfinite) no LT;kg CFC-11-Eq +ReCiPe 2016 v1.03, midpoint (I) no LT;particulate matter formation no LT;particulate matter formation potential (PMFP) no LT;kg PM2.5-Eq +ReCiPe 2016 v1.03, midpoint (I) no LT;photochemical oxidant formation: human health no LT;photochemical oxidant formation potential: humans (HOFP) no LT;kg NOx-Eq +ReCiPe 2016 v1.03, midpoint (I) no LT;photochemical oxidant formation: terrestrial ecosystems no LT;photochemical oxidant formation potential: ecosystems (EOFP) no LT;kg NOx-Eq +ReCiPe 2016 v1.03, midpoint (I) no LT;water use no LT;water consumption potential (WCP) no LT;m3 +ReCiPe 2016 v1.03, midpoint (I);acidification: terrestrial;terrestrial acidification potential (TAP);kg SO2-Eq +ReCiPe 2016 v1.03, midpoint (I);climate change;global warming potential (GWP20);kg CO2-Eq +ReCiPe 2016 v1.03, midpoint (I);ecotoxicity: freshwater;freshwater ecotoxicity potential (FETP);kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (I);ecotoxicity: marine;marine ecotoxicity potential (METP);kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (I);ecotoxicity: terrestrial;terrestrial ecotoxicity potential (TETP);kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (I);energy resources: non-renewable, fossil;fossil fuel potential (FFP);kg oil-Eq +ReCiPe 2016 v1.03, midpoint (I);eutrophication: freshwater;freshwater eutrophication potential (FEP);kg P-Eq +ReCiPe 2016 v1.03, midpoint (I);eutrophication: marine;marine eutrophication potential (MEP);kg N-Eq +ReCiPe 2016 v1.03, midpoint (I);human toxicity: carcinogenic;human toxicity potential (HTPc);kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (I);human toxicity: non-carcinogenic;human toxicity potential (HTPnc);kg 1,4-DCB-Eq +ReCiPe 2016 v1.03, midpoint (I);ionising radiation;ionising radiation potential (IRP);kBq Co-60-Eq +ReCiPe 2016 v1.03, midpoint (I);land use;agricultural land occupation (LOP);m2*a crop-Eq +ReCiPe 2016 v1.03, midpoint (I);material resources: metals/minerals;surplus ore potential (SOP);kg Cu-Eq +ReCiPe 2016 v1.03, midpoint (I);ozone depletion;ozone depletion potential (ODPinfinite);kg CFC-11-Eq +ReCiPe 2016 v1.03, midpoint (I);particulate matter formation;particulate matter formation potential (PMFP);kg PM2.5-Eq +ReCiPe 2016 v1.03, midpoint (I);photochemical oxidant formation: human health;photochemical oxidant formation potential: humans (HOFP);kg NOx-Eq +ReCiPe 2016 v1.03, midpoint (I);photochemical oxidant formation: terrestrial ecosystems;photochemical oxidant formation potential: ecosystems (EOFP);kg NOx-Eq +ReCiPe 2016 v1.03, midpoint (I);water use;water consumption potential (WCP);m3 +TRACI v2.1 no LT;acidification no LT;acidification potential (AP) no LT;kg SO2-Eq +TRACI v2.1 no LT;climate change no LT;global warming potential (GWP100) no LT;kg CO2-Eq +TRACI v2.1 no LT;ecotoxicity: freshwater no LT;ecotoxicity: freshwater no LT;CTUe +TRACI v2.1 no LT;eutrophication no LT;eutrophication potential no LT;kg N-Eq +TRACI v2.1 no LT;human toxicity: carcinogenic no LT;human toxicity: carcinogenic no LT;CTUh +TRACI v2.1 no LT;human toxicity: non-carcinogenic no LT;human toxicity: non-carcinogenic no LT;CTUh +TRACI v2.1 no LT;ozone depletion no LT;ozone depletion potential (ODP) no LT;kg CFC-11-Eq +TRACI v2.1 no LT;particulate matter formation no LT;particulate matter formation potential (PMFP) no LT;kg PM2.5-Eq +TRACI v2.1 no LT;photochemical oxidant formation no LT;maximum incremental reactivity (MIR) no LT;kg O3-Eq +TRACI v2.1;acidification;acidification potential (AP);kg SO2-Eq +TRACI v2.1;climate change;global warming potential (GWP100);kg CO2-Eq +TRACI v2.1;ecotoxicity: freshwater;ecotoxicity: freshwater;CTUe +TRACI v2.1;eutrophication;eutrophication potential;kg N-Eq +TRACI v2.1;human toxicity: carcinogenic;human toxicity: carcinogenic;CTUh +TRACI v2.1;human toxicity: non-carcinogenic;human toxicity: non-carcinogenic;CTUh +TRACI v2.1;ozone depletion;ozone depletion potential (ODP);kg CFC-11-Eq +TRACI v2.1;particulate matter formation;particulate matter formation potential (PMFP);kg PM2.5-Eq +TRACI v2.1;photochemical oxidant formation;maximum incremental reactivity (MIR);kg O3-Eq +USEtox v2.13, endpoint no LT;ecosystem quality no LT;ecotoxicity: freshwater no LT;PDF.m3.yr +USEtox v2.13, endpoint no LT;human health no LT;human toxicity: carcinogenic no LT;DALYs +USEtox v2.13, endpoint no LT;human health no LT;human toxicity: non-carcinogenic no LT;DALYs +USEtox v2.13, endpoint;ecosystem quality;ecotoxicity: freshwater;PDF.m3.yr +USEtox v2.13, endpoint;human health;human toxicity: carcinogenic;DALYs +USEtox v2.13, endpoint;human health;human toxicity: non-carcinogenic;DALYs +USEtox v2.13, midpoint no LT;ecotoxicity: freshwater no LT;comparative toxic unit for ecosystems (CTUe) no LT;CTUe +USEtox v2.13, midpoint no LT;human toxicity: carcinogenic no LT;comparative toxic unit for human (CTUh) no LT;CTUh +USEtox v2.13, midpoint no LT;human toxicity: non-carcinogenic no LT;comparative toxic unit for human (CTUh) no LT;CTUh +USEtox v2.13, midpoint;ecotoxicity: freshwater;comparative toxic unit for ecosystems (CTUe);CTUe +USEtox v2.13, midpoint;human toxicity: carcinogenic;comparative toxic unit for human (CTUh);CTUh +USEtox v2.13, midpoint;human toxicity: non-carcinogenic;comparative toxic unit for human (CTUh);CTUh diff --git a/samples/conf/appalca_conf_with_ei.yaml b/samples/conf/appalca_conf_with_ei.yaml index ddb41d4..7fa02a9 100644 --- a/samples/conf/appalca_conf_with_ei.yaml +++ b/samples/conf/appalca_conf_with_ei.yaml @@ -1,4 +1,5 @@ -project_name: 'sample_project_bw25' +project_name: 'sample_project_with_ei_bw25' +replace_bg: False databases: foreground: name: user_database @@ -6,4 +7,3 @@ databases: ecoinvent: version: "3.11" system_model: "cutoff" - replace: False diff --git a/samples/conf/appalca_conf.yaml b/samples/conf/appalca_conf_wo_ei.yaml similarity index 64% rename from samples/conf/appalca_conf.yaml rename to samples/conf/appalca_conf_wo_ei.yaml index 7a24671..de718be 100644 --- a/samples/conf/appalca_conf.yaml +++ b/samples/conf/appalca_conf_wo_ei.yaml @@ -1,4 +1,5 @@ -project_name: 'sample_project_bw25' +project_name: 'sample_project_wo_ei_bw25' +replace_bg: False databases: foreground: name: user_database diff --git a/samples/datasets/user_database/nvidia_ai_gpu/ai_use_phase.yaml b/samples/datasets/user_database/nvidia_ai_gpu/ai_use_phase.yaml index 2af5dca..6a39980 100644 --- a/samples/datasets/user_database/nvidia_ai_gpu/ai_use_phase.yaml +++ b/samples/datasets/user_database/nvidia_ai_gpu/ai_use_phase.yaml @@ -15,5 +15,5 @@ exchanges: type: technosphere amount: "inference * energy_per_inference / 1000000" input: - uuid: "electricity" + uuid: "electricity_wo_ei" database: user_database diff --git a/samples/datasets/user_database/nvidia_ai_gpu/electricity.yaml b/samples/datasets/user_database/nvidia_ai_gpu/electricity_with_ei.yaml similarity index 95% rename from samples/datasets/user_database/nvidia_ai_gpu/electricity.yaml rename to samples/datasets/user_database/nvidia_ai_gpu/electricity_with_ei.yaml index 62248f7..6e260bd 100644 --- a/samples/datasets/user_database/nvidia_ai_gpu/electricity.yaml +++ b/samples/datasets/user_database/nvidia_ai_gpu/electricity_with_ei.yaml @@ -1,4 +1,4 @@ -name: electricity +name: electricity_with_ei location: GLO type: process unit: kwh diff --git a/tests/data/cmd_build/foreground_datasets/electricity_no_ei.yaml b/samples/datasets/user_database/nvidia_ai_gpu/electricity_wo_ei.yaml similarity index 90% rename from tests/data/cmd_build/foreground_datasets/electricity_no_ei.yaml rename to samples/datasets/user_database/nvidia_ai_gpu/electricity_wo_ei.yaml index 916ef97..c7a655a 100644 --- a/tests/data/cmd_build/foreground_datasets/electricity_no_ei.yaml +++ b/samples/datasets/user_database/nvidia_ai_gpu/electricity_wo_ei.yaml @@ -1,4 +1,4 @@ -name: electricity_no_ei +name: electricity_wo_ei location: GLO type: process unit: kwh @@ -14,12 +14,12 @@ exchanges: name: usage_location options: - name: EU - amount: 0.005 + amount: 0.207 input: database: impact_proxies uuid: "('EF v3.0', 'climate change', 'global warming potential (GWP100)')_technosphere_proxy" - name: FR - amount: 0.021 + amount: 0.048 input: database: impact_proxies uuid: "('EF v3.0', 'climate change', 'global warming potential (GWP100)')_technosphere_proxy" \ No newline at end of file diff --git a/samples/datasets/user_database/nvidia_ai_gpu/nvidia_ai_gpu_chip.yaml b/samples/datasets/user_database/nvidia_ai_gpu/nvidia_ai_gpu_chip.yaml index 43a0a00..9aa9783 100644 --- a/samples/datasets/user_database/nvidia_ai_gpu/nvidia_ai_gpu_chip.yaml +++ b/samples/datasets/user_database/nvidia_ai_gpu/nvidia_ai_gpu_chip.yaml @@ -18,7 +18,7 @@ exchanges: type: technosphere parameters_matching: inference: inference_per_day * lifespan * 365.25 - amount: cuda_core + amount: 1 input: database: user_database uuid: ai_use_phase diff --git a/tests/data/appalca_confs/invalids/missing_foreground.yaml b/tests/data/appalca_confs/invalids/missing_foreground.yaml index 042d84d..90f8cba 100644 --- a/tests/data/appalca_confs/invalids/missing_foreground.yaml +++ b/tests/data/appalca_confs/invalids/missing_foreground.yaml @@ -1,5 +1,5 @@ project_name: 'missing_foreground_test' databases: ecoinvent: - name: ecoinvent_3.9.1_cutoff - path: './placeholder' + version: "3.11" + system_model: cutoff diff --git a/tests/data/appalca_confs/valids/valid.yaml b/tests/data/appalca_confs/valids/valid.yaml index 46fc8c5..d97b320 100644 --- a/tests/data/appalca_confs/valids/valid.yaml +++ b/tests/data/appalca_confs/valids/valid.yaml @@ -1,8 +1,8 @@ project_name: 'valid_appa_lca_conf' databases: ecoinvent: - name: ecoinvent_3.9.1_cutoff - path: '.' + version: "3.11" + system_model: cutoff foreground: name: user_database path: '.' diff --git a/tests/data/cmd_build/appalca_conf.yaml b/tests/data/cmd_build/appalca_conf_wo_ei.yaml similarity index 88% rename from tests/data/cmd_build/appalca_conf.yaml rename to tests/data/cmd_build/appalca_conf_wo_ei.yaml index 01b579c..0925455 100644 --- a/tests/data/cmd_build/appalca_conf.yaml +++ b/tests/data/cmd_build/appalca_conf_wo_ei.yaml @@ -1,4 +1,5 @@ project_name: cmd_build_ete_test +replace_bg: True databases: foreground: name: user_database diff --git a/tests/data/cmd_build/expected_scores.yaml b/tests/data/cmd_build/expected_scores.yaml index eb1b181..557bc97 100644 --- a/tests/data/cmd_build/expected_scores.yaml +++ b/tests/data/cmd_build/expected_scores.yaml @@ -1,3 +1,3 @@ -ai_use_phase: 4.8272749055999995 -nvidia_ai_gpu_chip: 13.988936117453397 -nvidia_gpu_chip_manufacturing: 9.161661211853398 +ai_use_phase: 11.0337712128 +nvidia_ai_gpu_chip: 20.195432424653397 +nvidia_gpu_chip_manufacturing: 9.161661211853396 diff --git a/tests/data/cmd_build/foreground_datasets/ai_use_phase.yaml b/tests/data/cmd_build/foreground_datasets/ai_use_phase.yaml index 0bbdfb6..6a39980 100644 --- a/tests/data/cmd_build/foreground_datasets/ai_use_phase.yaml +++ b/tests/data/cmd_build/foreground_datasets/ai_use_phase.yaml @@ -15,5 +15,5 @@ exchanges: type: technosphere amount: "inference * energy_per_inference / 1000000" input: - uuid: "electricity_no_ei" + uuid: "electricity_wo_ei" database: user_database diff --git a/samples/datasets/user_database/nvidia_ai_gpu/electricity_no_ei.yaml b/tests/data/cmd_build/foreground_datasets/electricity_wo_ei.yaml similarity index 90% rename from samples/datasets/user_database/nvidia_ai_gpu/electricity_no_ei.yaml rename to tests/data/cmd_build/foreground_datasets/electricity_wo_ei.yaml index 916ef97..c7a655a 100644 --- a/samples/datasets/user_database/nvidia_ai_gpu/electricity_no_ei.yaml +++ b/tests/data/cmd_build/foreground_datasets/electricity_wo_ei.yaml @@ -1,4 +1,4 @@ -name: electricity_no_ei +name: electricity_wo_ei location: GLO type: process unit: kwh @@ -14,12 +14,12 @@ exchanges: name: usage_location options: - name: EU - amount: 0.005 + amount: 0.207 input: database: impact_proxies uuid: "('EF v3.0', 'climate change', 'global warming potential (GWP100)')_technosphere_proxy" - name: FR - amount: 0.021 + amount: 0.048 input: database: impact_proxies uuid: "('EF v3.0', 'climate change', 'global warming potential (GWP100)')_technosphere_proxy" \ No newline at end of file diff --git a/tests/data/cmd_build/nvidia_ai_gpu_chip_expected.yaml b/tests/data/cmd_build/nvidia_ai_gpu_chip_expected.yaml index 1d8d74d..ab6c971 100644 --- a/tests/data/cmd_build/nvidia_ai_gpu_chip_expected.yaml +++ b/tests/data/cmd_build/nvidia_ai_gpu_chip_expected.yaml @@ -69,65 +69,35 @@ parameters: tree: name: nvidia_ai_gpu_chip models: - EFV3_CLIMATE_CHANGE: 12500.0*architecture_Maxwell*(4.6212599075297227e-9*cuda_core - + 7.37132179656539e-6) + 0.007241940498277656*architecture_Maxwell*(0.009702834627645097*cuda_core - + 1)**2/((1 - 0.9980542072708582*exp(-1.889809692866578e-5*cuda_core))**2*(70685.775/(0.1889809692866578*cuda_core - + 19.47688243064738) - 106.7778184271516*sqrt(2)/sqrt(0.009702834627645097*cuda_core - + 1))) + 12500.0*architecture_Pascal*(4.6891975579761074e-9*cuda_core + 7.808281424221127e-6) - + 0.26268825584172803*architecture_Pascal*(0.0060737847877931227*cuda_core + - 1)**2/((1 - 0.98920497620445418*exp(-6.5923115776528471e-5*cuda_core))**2*(70685.775/(0.13184623155305694*cuda_core - + 21.707425626610416) - 101.14318001667067*sqrt(2)/sqrt(0.0060737847877931227*cuda_core - + 1))) + 0.00036525*energy_per_inference*inference_per_day*lifespan*(0.005*usage_location_EU - + 0.021*usage_location_FR) - direct_impacts: - EFV3_CLIMATE_CHANGE: '0.0' - scaled_direct_impacts: - EFV3_CLIMATE_CHANGE: '0' + EFV3_CLIMATE_CHANGE: 1.0*energy_per_inference*inference_per_day*lifespan*(7.560675e-5*usage_location_EU + + 1.7532e-5*usage_location_FR) - 12500.0*architecture_Maxwell*(-4.6212599075297227e-9*cuda_core + - 7.37132179656539e-6) + 1.90904456522695e-5*architecture_Maxwell*(0.1889809692866578*cuda_core + + 19.47688243064738)**2/((1 - 0.9980542072708582*exp(-1.889809692866578e-5*cuda_core))**2*(70685.775/(0.1889809692866578*cuda_core + + 19.47688243064738) - 471.2385*sqrt(2)/sqrt(0.1889809692866578*cuda_core + + 19.47688243064738))) - 12500.0*architecture_Pascal*(-4.6891975579761074e-9*cuda_core + - 7.808281424221127e-6) + 0.00055747322513291*architecture_Pascal*(0.13184623155305694*cuda_core + + 21.707425626610416)**2/((1 - 0.98920497620445418*exp(-6.5923115776528471e-5*cuda_core))**2*(70685.775/(0.13184623155305694*cuda_core + + 21.707425626610416) - 471.2385*sqrt(2)/sqrt(0.13184623155305694*cuda_core + + 21.707425626610416))) children: - name: ai_use_phase models: - EFV3_CLIMATE_CHANGE: 0.00036525*energy_per_inference*inference_per_day*lifespan*(0.005*usage_location_EU - + 0.021*usage_location_FR) - direct_impacts: - EFV3_CLIMATE_CHANGE: 0.00036525*energy_per_inference*inference_per_day*lifespan*(0.005*usage_location_EU - + 0.021*usage_location_FR) - scaled_direct_impacts: - EFV3_CLIMATE_CHANGE: 0.00036525*energy_per_inference*inference_per_day*lifespan*(0.005*usage_location_EU - + 0.021*usage_location_FR) + EFV3_CLIMATE_CHANGE: 1.0*energy_per_inference*inference_per_day*lifespan*(7.560675e-5*usage_location_EU + + 1.7532e-5*usage_location_FR) children: [] properties: {} amount: '1.0' - name: nvidia_gpu_chip_manufacturing models: - EFV3_CLIMATE_CHANGE: 12500.0*architecture_Maxwell*(4.6212599075297227e-9*cuda_core - + 7.37132179656539e-6) + 0.007241940498277656*architecture_Maxwell*(0.009702834627645097*cuda_core - + 1)**2/((1 - 0.9980542072708582*exp(-1.889809692866578e-5*cuda_core))**2*(70685.775/(0.1889809692866578*cuda_core - + 19.47688243064738) - 106.7778184271516*sqrt(2)/sqrt(0.009702834627645097*cuda_core - + 1))) + 12500.0*architecture_Pascal*(4.6891975579761074e-9*cuda_core + 7.808281424221127e-6) - + 0.26268825584172803*architecture_Pascal*(0.0060737847877931227*cuda_core - + 1)**2/((1 - 0.98920497620445418*exp(-6.5923115776528471e-5*cuda_core))**2*(70685.775/(0.13184623155305694*cuda_core - + 21.707425626610416) - 101.14318001667067*sqrt(2)/sqrt(0.0060737847877931227*cuda_core - + 1))) - direct_impacts: - EFV3_CLIMATE_CHANGE: 12500.0*architecture_Maxwell*(4.6212599075297227e-9*cuda_core - + 7.37132179656539e-6) + 0.007241940498277656*architecture_Maxwell*(0.009702834627645097*cuda_core - + 1)**2/((1 - 0.9980542072708582*exp(-1.889809692866578e-5*cuda_core))**2*(70685.775/(0.1889809692866578*cuda_core - + 19.47688243064738) - 106.7778184271516*sqrt(2)/sqrt(0.009702834627645097*cuda_core - + 1))) + 12500.0*architecture_Pascal*(4.6891975579761074e-9*cuda_core + 7.808281424221127e-6) - + 0.26268825584172803*architecture_Pascal*(0.0060737847877931227*cuda_core - + 1)**2/((1 - 0.98920497620445418*exp(-6.5923115776528471e-5*cuda_core))**2*(70685.775/(0.13184623155305694*cuda_core - + 21.707425626610416) - 101.14318001667067*sqrt(2)/sqrt(0.0060737847877931227*cuda_core - + 1))) - scaled_direct_impacts: - EFV3_CLIMATE_CHANGE: 12500.0*architecture_Maxwell*(4.6212599075297227e-9*cuda_core - + 7.37132179656539e-6) + 0.007241940498277656*architecture_Maxwell*(0.009702834627645097*cuda_core - + 1)**2/((1 - 0.9980542072708582*exp(-1.889809692866578e-5*cuda_core))**2*(70685.775/(0.1889809692866578*cuda_core - + 19.47688243064738) - 106.7778184271516*sqrt(2)/sqrt(0.009702834627645097*cuda_core - + 1))) + 12500.0*architecture_Pascal*(4.6891975579761074e-9*cuda_core + 7.808281424221127e-6) - + 0.26268825584172803*architecture_Pascal*(0.0060737847877931227*cuda_core - + 1)**2/((1 - 0.98920497620445418*exp(-6.5923115776528471e-5*cuda_core))**2*(70685.775/(0.13184623155305694*cuda_core - + 21.707425626610416) - 101.14318001667067*sqrt(2)/sqrt(0.0060737847877931227*cuda_core - + 1))) + EFV3_CLIMATE_CHANGE: -12500.0*architecture_Maxwell*(-4.6212599075297227e-9*cuda_core + - 7.37132179656539e-6) + 1.90904456522695e-5*architecture_Maxwell*(0.1889809692866578*cuda_core + + 19.47688243064738)**2/((1 - 0.9980542072708582*exp(-1.889809692866578e-5*cuda_core))**2*(70685.775/(0.1889809692866578*cuda_core + + 19.47688243064738) - 471.2385*sqrt(2)/sqrt(0.1889809692866578*cuda_core + + 19.47688243064738))) - 12500.0*architecture_Pascal*(-4.6891975579761074e-9*cuda_core + - 7.808281424221127e-6) + 0.00055747322513291*architecture_Pascal*(0.13184623155305694*cuda_core + + 21.707425626610416)**2/((1 - 0.98920497620445418*exp(-6.5923115776528471e-5*cuda_core))**2*(70685.775/(0.13184623155305694*cuda_core + + 21.707425626610416) - 471.2385*sqrt(2)/sqrt(0.13184623155305694*cuda_core + + 21.707425626610416))) children: [] properties: {} amount: '1.0' diff --git a/tests/data/eco_invent/invalids/incomplete_datasets/incomplete_dataset.spold b/tests/data/eco_invent/invalids/incomplete_datasets/incomplete_dataset.spold deleted file mode 100644 index af8d83d..0000000 --- a/tests/data/eco_invent/invalids/incomplete_datasets/incomplete_dataset.spold +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/tests/data/eco_invent/invalids/invalid_datasets/empty_dataset.spold b/tests/data/eco_invent/invalids/invalid_datasets/empty_dataset.spold deleted file mode 100644 index e69de29..0000000 diff --git a/tests/end_to_end/test_cmd_build.py b/tests/end_to_end/test_cmd_build.py index 8979511..4ee9d70 100644 --- a/tests/end_to_end/test_cmd_build.py +++ b/tests/end_to_end/test_cmd_build.py @@ -16,7 +16,7 @@ def test_build_command(): - appaconf_file = os.path.join(DATA_DIR, "cmd_build", "appalca_conf.yaml") + appaconf_file = os.path.join(DATA_DIR, "cmd_build", "appalca_conf_wo_ei.yaml") conf_file = os.path.join(DATA_DIR, "cmd_build", "nvidia_ai_gpu_chip_lca_conf.yaml") expected_file = os.path.join( DATA_DIR, "cmd_build", "nvidia_ai_gpu_chip_expected.yaml" diff --git a/tests/functional_db/test_ecoinvent.py b/tests/functional_db/test_ecoinvent.py index cf6e301..7ba58f8 100644 --- a/tests/functional_db/test_ecoinvent.py +++ b/tests/functional_db/test_ecoinvent.py @@ -4,67 +4,52 @@ import os import pytest +from requests import HTTPError from appabuild.database.databases import EcoInventDatabase -from appabuild.exceptions import BwDatabaseError -from tests import DATA_DIR -def test_not_existing_path(): +def test_no_ecoinvent_version(): """ - Check an exception is raised when the path for the eco invent datasets doesn't exist. + Check an exception is raised when no ecoinvent version is provided. """ - path = os.path.join(DATA_DIR, "imaginary_path") - - try: - db = EcoInventDatabase(path=path, name="name") - db.execute_at_startup() - pytest.fail("An eco invent database can't be loaded from an non-existing path") - except BwDatabaseError as e: - assert e.exception_type == "eco_invent_invalid_path" - - -def test_empty_dataset(): - """ - Check an exception is raised when there is no dataset to load. - """ - path = os.path.join(DATA_DIR, "eco_invent", "invalids", "no_datasets") - + os.environ["BW_USER"] = "test" + os.environ["BW_PASS"] = "test" try: - db = EcoInventDatabase(path=path, name="name") + db = EcoInventDatabase(system_model="cutoff") db.execute_at_startup() - pytest.fail("An eco invent database can't be loaded if there is no datasets") - except BwDatabaseError as e: - assert e.exception_type == "eco_invent_invalid_path" + pytest.fail("EcoInvent database requires a version") + except TypeError as e: + assert ( + str(e) + == "EcoInventDatabase.__init__() missing 1 required positional argument: 'version'" + ) -def test_invalid_dataset(): +def test_no_ecoinvent_system_model(): """ - Check an exception is raised when one of the dataset is invalid. + Check an exception is raised when no ecoinvent version is provided. """ - path = os.path.join(DATA_DIR, "eco_invent", "invalids", "invalid_datasets") - + os.environ["BW_USER"] = "test" + os.environ["BW_PASS"] = "test" try: - db = EcoInventDatabase(path=path, name="name") + db = EcoInventDatabase(version="3.11") db.execute_at_startup() - pytest.fail( - "An eco invent database can't be loaded if at least one dataset is invalid" + pytest.fail("EcoInvent database requires a system model") + except TypeError as e: + assert ( + str(e) + == "EcoInventDatabase.__init__() missing 1 required positional argument: 'system_model'" ) - except BwDatabaseError as e: - assert e.exception_type == "eco_invent_invalid_dataset" -def test_incomplete_dataset(): +def test_no_ecoinvent_credentials(): """ - Check an exception is raised when one of the dataset is incomplete. + Check an exception is raised when no ecoinvent version is provided. """ - path = os.path.join(DATA_DIR, "eco_invent", "invalids", "incomplete_datasets") - try: - db = EcoInventDatabase(path=path, name="name") + db = EcoInventDatabase(version="3.11", system_model="cutoff") db.execute_at_startup() - pytest.fail( - "An eco invent database can't be loaded if at least one dataset is incomplete" - ) - except BwDatabaseError as e: - assert e.exception_type == "eco_invent_invalid_dataset" + pytest.fail("EcoInvent database requires credentials as environment variables") + except HTTPError as e: + assert e.response.reason == "Unauthorized"