Skip to content

Commit

Permalink
Merge pull request #50 from TimoDiepers/fix_49
Browse files Browse the repository at this point in the history
Allow id and key as method identifier
  • Loading branch information
TimoDiepers authored Jun 7, 2024
2 parents 7e6eaac + 89167aa commit f7cba08
Show file tree
Hide file tree
Showing 10 changed files with 572 additions and 6 deletions.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Empty file added tests/fixtures/__init__.py
Empty file.
469 changes: 469 additions & 0 deletions tests/fixtures/test_databases.py

Large diffs are not rendered by default.

85 changes: 85 additions & 0 deletions tests/test_electric_vehicle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
"""
Testing the results of the static LCA and the timexLCA for a simple test case of electric vehicle to see if the new interpolated amounts are correct.
"""
from pathlib import Path
import pytest
import bw2calc as bc
import bw2data as bd
from timex_lca import TimexLCA
from datetime import datetime


from .fixtures.test_databases import create_electric_vehicle_dbs

# for now, one test class, but could be set up more modularly
class TestClass_EV:

create_electric_vehicle_dbs() #moved database creation to fixtures to avoid cluttered tests, used aggregated LCIs for all excahnges in CO2-eq

bd.projects.set_current("__test_EV__")

foreground = bd.Database("foreground")
electric_vehicle = bd.get_activity(("foreground", "EV"))


database_date_dict = {
"db_2020": datetime.strptime("2020", "%Y"),
"db_2030": datetime.strptime("2030", "%Y"),
"db_2040": datetime.strptime("2040", "%Y"),
"foreground": "dynamic",
}

tlca = TimexLCA(demand={electric_vehicle.key: 1}, method=("GWP", "example"), database_date_dict = database_date_dict)

tlca.build_timeline()
tlca.lci()
tlca.static_lcia()
tlca.static_score


def test_static_lca_score(self):
slca = bc.LCA({self.electric_vehicle.key: 1}, method=("GWP", "example"))
slca.lci()
slca.lcia()
expected_static_score = slca.score

assert self.tlca.static_lca.score == expected_static_score


def test_timex_lca_score(self):
ELECTRICITY_CONSUMPTION = 0.2 # kWh/km
MILEAGE = 150_000 # km
LIFETIME = 16 # years

# Overall mass: 1200 kg
MASS_GLIDER = 840 # kg
MASS_POWERTRAIN = 80 # kg
MASS_BATTERY = 280 # kg

expected_glider_score = ( 0.6 * MASS_GLIDER * 0.8 * [exc["amount"] for exc in bd.get_activity(("db_2020", "glider")).biosphere()][0] + # 2022 -> 80% 2020, 20% 2030
0.6 * MASS_GLIDER * 0.2 * [exc["amount"] for exc in bd.get_activity(("db_2030", "glider")).biosphere()][0] +
0.4 * MASS_GLIDER * 0.7 * [exc["amount"] for exc in bd.get_activity(("db_2020", "glider")).biosphere()][0] + # 2023 -> 70% 2020, 30% 2030
0.4 * MASS_GLIDER * 0.3 * [exc["amount"] for exc in bd.get_activity(("db_2030", "glider")).biosphere()][0])

expected_powertrain_score = ( 0.6 * MASS_POWERTRAIN * 0.8 * [exc["amount"] for exc in bd.get_activity(("db_2020", "powertrain")).biosphere()][0] + # 2022 -> 80% 2020, 20% 2030
0.6 * MASS_POWERTRAIN * 0.2 * [exc["amount"] for exc in bd.get_activity(("db_2030", "powertrain")).biosphere()][0] +
0.4 * MASS_POWERTRAIN * 0.7 * [exc["amount"] for exc in bd.get_activity(("db_2020", "powertrain")).biosphere()][0] + # 2023 -> 70% 2020, 30% 2030
0.4 * MASS_POWERTRAIN * 0.3 * [exc["amount"] for exc in bd.get_activity(("db_2030", "powertrain")).biosphere()][0])

expected_battery_score = ( 0.6 * MASS_BATTERY * 0.8 * [exc["amount"] for exc in bd.get_activity(("db_2020", "battery")).biosphere()][0] + # 2022 -> 80% 2020, 20% 2030
0.6 * MASS_BATTERY * 0.2 * [exc["amount"] for exc in bd.get_activity(("db_2030", "battery")).biosphere()][0] +
0.4 * MASS_BATTERY * 0.7 * [exc["amount"] for exc in bd.get_activity(("db_2020", "battery")).biosphere()][0] + # 2023 -> 70% 2020, 30% 2030
0.4 * MASS_BATTERY * 0.3 * [exc["amount"] for exc in bd.get_activity(("db_2030", "battery")).biosphere()][0])

expected_electricity_score = (MILEAGE * ELECTRICITY_CONSUMPTION * 0.8 * [exc["amount"] for exc in bd.get_activity(("db_2030", "electricity")).biosphere()][0] + # electricity in year 2032 -> 80% 2030, 20% 2040
MILEAGE * ELECTRICITY_CONSUMPTION * 0.2 * [exc["amount"] for exc in bd.get_activity(("db_2040", "electricity")).biosphere()][0])

expected_glider_recycling_score = (MASS_GLIDER * [exc["amount"] for exc in bd.get_activity(("db_2040", "dismantling")).biosphere()][0]) # dismantling 2041 -> 100% 2040


expected_battery_recycling_score = -(MASS_BATTERY * [exc["amount"] for exc in bd.get_activity(("db_2040", "battery_recycling")).biosphere()][0]) # dismantling 2041 -> 100% 2040, negative sign because of waste flow

expected_timex_score = expected_glider_score + expected_powertrain_score + expected_battery_score + expected_electricity_score + expected_glider_recycling_score + expected_battery_recycling_score

assert self.tlca.static_score == pytest.approx(expected_timex_score, abs = 0.5) #allow for some rounding errors

24 changes: 18 additions & 6 deletions timex_lca/dynamic_characterization.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import json

from typing import Union, Tuple, Optional, Callable, List
from collections.abc import Collection
from datetime import datetime


Expand Down Expand Up @@ -338,18 +339,29 @@ def add_default_characterization_functions(self):
"""
self.characterization_function_dict = dict()

bioflows_in_lcia_method = bd.Method(self.method).load()

# load pre-calculated decay multipliers for GHGs (except for CO2, CH4, N2O & CO)
filepath = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "data", "decay_multipliers.json"
)

with open(filepath) as json_file:
decay_multipliers = json.load(json_file)

for flow in bioflows_in_lcia_method:
node = bd.get_node(database=flow[0][0], code=flow[0][1])


# look up which GHGs are characterized in the selected static LCA method
method_data = bd.Method(self.method).load()

# the bioflow-identifier stored in the method data can be the database id or the tuple (database, code)
def get_bioflow_node(identifier):
if isinstance(identifier, Collection) and len(identifier) == 2: # is code tuple
return bd.get_node(database=identifier[0], code=identifier[1])
elif isinstance(identifier, int): # id is an int
return bd.get_node(id=identifier)
else:
raise ValueError("The flow-identifier stored in the selected method is neither an id nor the tuple (database, code). No automatic matching possible.")

bioflow_nodes = set(get_bioflow_node(identifier) for identifier, _ in method_data)

for node in bioflow_nodes:
if "carbon dioxide" in node["name"].lower():
if "soil" in node.get("categories", []):
self.characterization_function_dict[node.id] = (
Expand Down

0 comments on commit f7cba08

Please sign in to comment.