From c359634bef79ba5185fe069b14fbc822ced93c89 Mon Sep 17 00:00:00 2001 From: Chris Mutel Date: Fri, 21 Jun 2024 10:27:34 +0200 Subject: [PATCH] Keep older SimaPro import functions --- bw2io/__init__.py | 8 +- bw2io/importers/__init__.py | 4 +- bw2io/importers/simapro_block_csv.py | 4 +- bw2io/strategies/__init__.py | 2 + bw2io/strategies/simapro.py | 117 +++++++++++++++++++++- pyproject.toml | 2 +- tests/strategies/simapro_allocation.py | 6 +- tests/strategies/simapro_normalization.py | 78 +++++++-------- 8 files changed, 168 insertions(+), 53 deletions(-) diff --git a/bw2io/__init__.py b/bw2io/__init__.py index c20e1123..08e44f86 100644 --- a/bw2io/__init__.py +++ b/bw2io/__init__.py @@ -38,8 +38,8 @@ "MultiOutputEcospold1Importer", "normalize_units", "restore_project_directory", - # "SimaProCSVImporter", - # "SimaProLCIACSVImporter", + "SimaProCSVImporter", + "SimaProLCIACSVImporter", "SimaProBlockCSVImporter", "SingleOutputEcospold1Importer", "SingleOutputEcospold2Importer", @@ -87,8 +87,8 @@ Exiobase3MonetaryImporter, MultiOutputEcospold1Importer, SimaProBlockCSVImporter, - # SimaProCSVImporter, - # SimaProLCIACSVImporter, + SimaProCSVImporter, + SimaProLCIACSVImporter, SingleOutputEcospold1Importer, SingleOutputEcospold2Importer, ) diff --git a/bw2io/importers/__init__.py b/bw2io/importers/__init__.py index 00fef67e..760cd677 100644 --- a/bw2io/importers/__init__.py +++ b/bw2io/importers/__init__.py @@ -11,8 +11,8 @@ from .excel_lcia import CSVLCIAImporter, ExcelLCIAImporter from .exiobase3_hybrid import Exiobase3HybridImporter from .exiobase3_monetary import Exiobase3MonetaryImporter -# from .simapro_csv import SimaProCSVImporter -# from .simapro_lcia_csv import SimaProLCIACSVImporter +from .simapro_csv import SimaProCSVImporter +from .simapro_lcia_csv import SimaProLCIACSVImporter from .simapro_block_csv import SimaProBlockCSVImporter """ diff --git a/bw2io/importers/simapro_block_csv.py b/bw2io/importers/simapro_block_csv.py index 05c2f690..bab20e48 100644 --- a/bw2io/importers/simapro_block_csv.py +++ b/bw2io/importers/simapro_block_csv.py @@ -24,7 +24,7 @@ normalize_simapro_biosphere_names, normalize_units, set_code_by_activity_hash, - sp_allocate_products, + sp_allocate_functional_products, split_simapro_name_geo, strip_biosphere_exc_locations, update_ecoinvent_locations, @@ -55,7 +55,7 @@ def __init__( self.project_parameters = data['project_parameters'] self.strategies = [ - sp_allocate_products, + sp_allocate_functional_products, assign_only_functional_exchange_as_reference_product, drop_unspecified_subcategories, split_simapro_name_geo, diff --git a/bw2io/strategies/__init__.py b/bw2io/strategies/__init__.py index 3aa60288..06165fe2 100644 --- a/bw2io/strategies/__init__.py +++ b/bw2io/strategies/__init__.py @@ -71,6 +71,7 @@ "set_code_by_activity_hash", "set_lognormal_loc_value", "sp_allocate_products", + "sp_allocate_functional_products", "special", "split_exchanges", "split_simapro_name_geo", @@ -177,5 +178,6 @@ normalize_simapro_biosphere_names, sp_allocate_products, split_simapro_name_geo, + sp_allocate_functional_products, ) from .useeio import remove_random_exchanges, remove_useeio_products diff --git a/bw2io/strategies/simapro.py b/bw2io/strategies/simapro.py index 5e7d074a..643f9167 100644 --- a/bw2io/strategies/simapro.py +++ b/bw2io/strategies/simapro.py @@ -3,6 +3,7 @@ from numbers import Number import numpy as np +import bw2parameters from bw2data import Database from stats_arrays import LognormalUncertainty @@ -27,7 +28,7 @@ def functional(exc: dict) -> bool: return False -def sp_allocate_products(db): +def sp_allocate_functional_products(db): """ Allocate products in a SimaPro dataset by creating a separate dataset for each product. @@ -135,6 +136,118 @@ def sp_allocate_products(db): return db + new_data +def sp_allocate_products(db): + """ + Allocate products in a SimaPro dataset by creating a separate dataset for each product. + + For raw SimaPro datasets, creates a separate dataset for each product, + taking into account the allocation factor if provided. Also handles + waste treatment datasets with a single product. + + Parameters + ---------- + db : list + A list of dictionaries representing raw SimaPro datasets. + + Returns + ------- + new_db : list + A list of dictionaries representing the allocated datasets with separate + entries for each product. + + Examples + -------- + >>> db = [ + ... { + ... "name": "Dataset 1", + ... "exchanges": [ + ... {"type": "production", "name": "Product A", "unit": "kg", "amount": 10, "allocation": 80}, + ... {"type": "production", "name": "Product B", "unit": "kg", "amount": 20, "allocation": 20}, + ... ], + ... } + ... ] + >>> sp_allocate_products(db) + [ + { + "name": "Product A", + "reference product": "Product A", + "unit": "kg", + "production amount": 10, + "exchanges": [ + {"type": "production", "name": "Product A", "unit": "kg", "amount": 10, "allocation": 80}, + {"type": "production", "name": "Product B", "unit": "kg", "amount": 5, "allocation": 20}, + ], + }, + { + "name": "Product B", + "reference product": "Product B", + "unit": "kg", + "production amount": 5, + "exchanges": [ + {"type": "production", "name": "Product A", "unit": "kg", "amount": 2.5, "allocation": 80}, + {"type": "production", "name": "Product B", "unit": "kg", "amount": 5, "allocation": 20}, + ], + }, + ] + """ + new_db = [] + for ds in db: + products = [ + exc for exc in ds.get("exchanges", []) if exc["type"] == "production" + ] + if ds.get("reference product"): + new_db.append(ds) + elif not products: + ds["error"] = True + new_db.append(ds) + elif len(products) == 1: + # Waste treatment datasets only allowed one product + product = products[0] + ds["name"] = ds["reference product"] = product["name"] + ds["unit"] = product["unit"] + ds["production amount"] = product["amount"] + new_db.append(ds) + else: + ds["exchanges"] = [ + exc for exc in ds["exchanges"] if exc["type"] != "production" + ] + for product in products: + product = copy.deepcopy(product) + if (allocation := product.get("allocation")): + if isinstance(product["allocation"], str) and "parameters" in ds: + ds["parameters"] = { + k.lower(): v for k, v in ds["parameters"].items() + } + interp = bw2parameters.ParameterSet( + ds["parameters"] + ).get_interpreter() + interp.add_symbols( + bw2parameters.ParameterSet( + ds["parameters"] + ).evaluate_and_set_amount_field() + ) + allocation = interp( + normalize_simapro_formulae( + product["allocation"].lower(), + settings={"Decimal separator": ","}, + ) + ) + + if allocation != 0: + product["amount"] = product["amount"] * 1 / (allocation / 100) + else: + product["amount"] = 0 # Infinity as zero? :-/ + else: + product["amount"] = 0 + copied = copy.deepcopy(ds) + copied["exchanges"].append(product) + copied["name"] = copied["reference product"] = product["name"] + copied["unit"] = product["unit"] + copied["production amount"] = product["amount"] + new_db.append(copied) + return new_db + + def fix_zero_allocation_products(db): """ Fix datasets with a single production exchange and zero allocation factors. @@ -751,4 +864,4 @@ def assign_only_functional_exchange_as_reference_product(db): ds["name"] = ds["reference product"] = product["name"] ds["production amount"] = product["amount"] ds["unit"] = product.get("unit") or ds.get("unit") - return db \ No newline at end of file + return db diff --git a/pyproject.toml b/pyproject.toml index 9dfc701b..155f6e03 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ requires = ["setuptools>=69.0"] build-backend = "setuptools.build_meta" [project] -name = "bw25io" +name = "bw2io" authors = [ { name="Chris Mutel", email="cmutel@gmail.com" } ] diff --git a/tests/strategies/simapro_allocation.py b/tests/strategies/simapro_allocation.py index f7607a0a..4d704695 100644 --- a/tests/strategies/simapro_allocation.py +++ b/tests/strategies/simapro_allocation.py @@ -1,7 +1,7 @@ -from bw2io.strategies.simapro import sp_allocate_products +from bw2io.strategies.simapro import sp_allocate_functional_products -def test_sp_allocate_products(): +def test_sp_allocate_functional_products(): given = [ { "type": "multifunctional", @@ -168,4 +168,4 @@ def test_sp_allocate_products(): "production amount": 2000.0, }, ] - assert sp_allocate_products(given) == expected + assert sp_allocate_functional_products(given) == expected diff --git a/tests/strategies/simapro_normalization.py b/tests/strategies/simapro_normalization.py index 65d1a36a..392054b7 100644 --- a/tests/strategies/simapro_normalization.py +++ b/tests/strategies/simapro_normalization.py @@ -1,42 +1,42 @@ -# import unittest +import unittest -# from bw2io.compatibility import SIMAPRO_BIO_SUBCATEGORIES, SIMAPRO_BIOSPHERE -# from bw2io.strategies.simapro import ( -# normalize_simapro_biosphere_categories, -# normalize_simapro_biosphere_names, -# ) +from bw2io.compatibility import SIMAPRO_BIO_SUBCATEGORIES, SIMAPRO_BIOSPHERE +from bw2io.strategies.simapro import ( + normalize_simapro_biosphere_categories, + normalize_simapro_biosphere_names, +) -# class SPNormalizationTestCase(unittest.TestCase): -# def test_sp_biosphere_category_normalization(self): -# db = [ -# { -# "exchanges": [ -# {"categories": ["Economic issues", "foo"], "type": "biosphere"}, -# {"categories": ["Resources", "high. pop."], "type": "biosphere"}, -# { -# "categories": ["Economic issues", "high. pop."], -# "type": "not biosphere", -# }, -# ], -# } -# ] -# result = [ -# { -# "exchanges": [ -# {"categories": ("economic", "foo"), "type": "biosphere"}, -# { -# "categories": ("natural resource", "urban air close to ground"), -# "type": "biosphere", -# }, -# { -# "categories": ["Economic issues", "high. pop."], -# "type": "not biosphere", -# }, -# ], -# } -# ] -# self.assertEqual( -# result, -# normalize_simapro_biosphere_categories(db), -# ) +class SPNormalizationTestCase(unittest.TestCase): + def test_sp_biosphere_category_normalization(self): + db = [ + { + "exchanges": [ + {"categories": ["Economic issues", "foo"], "type": "biosphere"}, + {"categories": ["Resources", "high. pop."], "type": "biosphere"}, + { + "categories": ["Economic issues", "high. pop."], + "type": "not biosphere", + }, + ], + } + ] + result = [ + { + "exchanges": [ + {"categories": ("economic", "foo"), "type": "biosphere"}, + { + "categories": ("natural resource", "urban air close to ground"), + "type": "biosphere", + }, + { + "categories": ["Economic issues", "high. pop."], + "type": "not biosphere", + }, + ], + } + ] + self.assertEqual( + result, + normalize_simapro_biosphere_categories(db), + )