lca-algebraic 1.4.1.dev0__tar.gz → 1.4.1.dev2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/PKG-INFO +2 -1
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic/activity.py +10 -8
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic/base_utils.py +17 -1
- lca_algebraic-1.4.1.dev2/lca_algebraic/bw_wrapper/__init__.py +150 -0
- lca_algebraic-1.4.1.dev2/lca_algebraic/bw_wrapper/bw2.py +98 -0
- lca_algebraic-1.4.1.dev2/lca_algebraic/bw_wrapper/bw25.py +115 -0
- lca_algebraic-1.4.1.dev2/lca_algebraic/bw_wrapper/protocol.py +81 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic/cache.py +15 -5
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic/database.py +10 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic/lambda_expression.py +9 -3
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic/lca.py +322 -49
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic/log.py +8 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic/methods.py +6 -1
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic/params.py +1 -3
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic/settings.py +11 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic.egg-info/PKG-INFO +2 -1
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic.egg-info/SOURCES.txt +5 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic.egg-info/requires.txt +1 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/pyproject.toml +3 -2
- lca_algebraic-1.4.1.dev2/test/test_premise.py +131 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/LICENSE +0 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/README.md +0 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic/__init__.py +0 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic/axis_dict.py +0 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic/interpolation.py +0 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic/io.py +0 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic/matrix.py +0 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic/stats.py +0 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic/sympy_utils.py +0 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic/units.py +0 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic.egg-info/dependency_links.txt +0 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/lca_algebraic.egg-info/top_level.txt +0 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/setup.cfg +0 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/test/test_axis_dict.py +0 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/test/test_axis_matrices_cache.py +0 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/test/test_cache.py +0 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/test/test_sympy.py +0 -0
- {lca_algebraic-1.4.1.dev0 → lca_algebraic-1.4.1.dev2}/test/test_units.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lca_algebraic
|
|
3
|
-
Version: 1.4.1.
|
|
3
|
+
Version: 1.4.1.dev2
|
|
4
4
|
Summary: This library provides a layer above Brightway for defining parametric models and running super fast LCA for monte carlo analysis.
|
|
5
5
|
Author-email: OIE - Mines ParisTech <raphael.jolivet@mines-paristech.fr>
|
|
6
6
|
License: BSD
|
|
@@ -29,6 +29,7 @@ Requires-Dist: deprecation
|
|
|
29
29
|
Requires-Dist: SALib
|
|
30
30
|
Requires-Dist: pint
|
|
31
31
|
Requires-Dist: typing-extensions
|
|
32
|
+
Requires-Dist: xarray>=2022.3.0
|
|
32
33
|
Provides-Extra: bw2
|
|
33
34
|
Requires-Dist: brightway2; extra == "bw2"
|
|
34
35
|
Requires-Dist: pypardiso; platform_machine == "x86_64" and extra == "bw2"
|
|
@@ -168,13 +168,11 @@ class ActivityExtended(Activity):
|
|
|
168
168
|
or dict of attributes, for updating both at once, or any other attribute.
|
|
169
169
|
The amount can reference the symbol 'old_amount' that will be replaced with the current amount of the exchange.
|
|
170
170
|
"""
|
|
171
|
-
|
|
172
|
-
if not _isForeground(self["database"]):
|
|
171
|
+
if not _isForeground(self["database"]) and not Settings.internals:
|
|
173
172
|
msg = f"You are updating a background activity in {self['database']}. Use copyActivity() in a foreground db instead."
|
|
174
173
|
if Settings.strict_mode:
|
|
175
174
|
raise Exception(msg)
|
|
176
|
-
|
|
177
|
-
warn(msg)
|
|
175
|
+
warn(msg)
|
|
178
176
|
|
|
179
177
|
# Update exchanges
|
|
180
178
|
for ex_target_or_name, updates in updates.items():
|
|
@@ -541,12 +539,11 @@ def _equals(val1: ValueOrExpression, val2: ValueOrExpression):
|
|
|
541
539
|
|
|
542
540
|
|
|
543
541
|
def _newAct(db_name, code):
|
|
544
|
-
if not _isForeground(db_name):
|
|
542
|
+
if not _isForeground(db_name) and not Settings.internals:
|
|
545
543
|
msg = f"You are creating activity in background DB {db_name}. Use copyActivity() in a foreground db instead."
|
|
546
544
|
if Settings.strict_mode:
|
|
547
545
|
raise Exception(msg)
|
|
548
|
-
|
|
549
|
-
warn(msg)
|
|
546
|
+
warn(msg)
|
|
550
547
|
|
|
551
548
|
db = _getDb(db_name)
|
|
552
549
|
# Already present : delete it ?
|
|
@@ -632,7 +629,9 @@ def newActivity(
|
|
|
632
629
|
|
|
633
630
|
|
|
634
631
|
@atomic
|
|
635
|
-
def copyActivity(
|
|
632
|
+
def copyActivity(
|
|
633
|
+
db_name, activity: ActivityExtended, code=None, withExchanges=True, input_mapping=None, **kwargs
|
|
634
|
+
) -> ActivityExtended:
|
|
636
635
|
"""Copy an activity and its exchanges into another database. You usually want to copy activities from your background to
|
|
637
636
|
your foreground DB to update them, keeping your background DB clean.
|
|
638
637
|
|
|
@@ -675,6 +674,9 @@ def copyActivity(db_name, activity: ActivityExtended, code=None, withExchanges=T
|
|
|
675
674
|
data["output"] = res.key
|
|
676
675
|
if data["input"] == activity.key:
|
|
677
676
|
data["input"] = res.key
|
|
677
|
+
if input_mapping and data["input"] in input_mapping:
|
|
678
|
+
# Input mapping, used to migrate proxy db linked to premise scenarios
|
|
679
|
+
data["input"] = input_mapping[data["input"]]
|
|
678
680
|
|
|
679
681
|
# Chemical formulas might be in ecoinvent technosphere
|
|
680
682
|
# We don't want them
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from contextlib import AbstractContextManager
|
|
2
|
+
from functools import cache
|
|
2
3
|
from inspect import isfunction
|
|
3
4
|
from typing import Dict, Iterable, Tuple, Union
|
|
4
5
|
|
|
@@ -26,6 +27,20 @@ def _isnumber(value):
|
|
|
26
27
|
dbs = dict()
|
|
27
28
|
|
|
28
29
|
|
|
30
|
+
def invalidate_db(dbname=None):
|
|
31
|
+
"""Drop the pooled Database instances so a fresh one is built on next access.
|
|
32
|
+
|
|
33
|
+
bw25 keeps an internal (session/ORM) view inside a Database object that is not
|
|
34
|
+
invalidated when a database is deleted or recreated. Keep the pool in sync so that
|
|
35
|
+
activities are not resolved to stale (deleted) node ids.
|
|
36
|
+
"""
|
|
37
|
+
global dbs
|
|
38
|
+
if dbname is None:
|
|
39
|
+
dbs = dict()
|
|
40
|
+
else:
|
|
41
|
+
dbs.pop(dbname, None)
|
|
42
|
+
|
|
43
|
+
|
|
29
44
|
def _getDb(dbname):
|
|
30
45
|
"""Pool of Database instances"""
|
|
31
46
|
if dbname not in dbs:
|
|
@@ -174,6 +189,7 @@ def one(it: Iterable):
|
|
|
174
189
|
return it[0]
|
|
175
190
|
|
|
176
191
|
|
|
192
|
+
@cache
|
|
177
193
|
def getActByCode(db_name, code):
|
|
178
194
|
"""Get activity by code"""
|
|
179
195
|
return _getDb(db_name).get(code)
|
|
@@ -181,4 +197,4 @@ def getActByCode(db_name, code):
|
|
|
181
197
|
|
|
182
198
|
# Types
|
|
183
199
|
ValueOrExpression = Union[float, Basic, Quantity]
|
|
184
|
-
MethodKey = Tuple[str]
|
|
200
|
+
MethodKey = Tuple[str, ...]
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
from typing import Callable, Iterable, List, Tuple, Type
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
from .protocol import BrightwayBackend
|
|
6
|
+
|
|
7
|
+
_INSTALL_MSG = (
|
|
8
|
+
"Brightway is required. Install one of:\n" " pip install 'lca_algebraic[bw2]'\n" " pip install 'lca_algebraic[bw25]'"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _bw2data_major_version(bw2data) -> int:
|
|
13
|
+
version = bw2data.__version__
|
|
14
|
+
if isinstance(version, tuple):
|
|
15
|
+
return int(version[0])
|
|
16
|
+
return int(str(version).split(".")[0])
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _load_backend() -> BrightwayBackend:
|
|
20
|
+
try:
|
|
21
|
+
import bw2data
|
|
22
|
+
except ImportError as exc:
|
|
23
|
+
raise ImportError(_INSTALL_MSG) from exc
|
|
24
|
+
|
|
25
|
+
if _bw2data_major_version(bw2data) >= 4:
|
|
26
|
+
from .bw25 import BW25Backend
|
|
27
|
+
|
|
28
|
+
return BW25Backend()
|
|
29
|
+
|
|
30
|
+
from .bw2 import BW2Backend
|
|
31
|
+
|
|
32
|
+
return BW2Backend()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
_backend = _load_backend()
|
|
36
|
+
|
|
37
|
+
Activity = _backend.Activity
|
|
38
|
+
ActivityDataset = _backend.ActivityDataset
|
|
39
|
+
Exchange = _backend.Exchange
|
|
40
|
+
ExchangeDataset = _backend.ExchangeDataset
|
|
41
|
+
ActivityProxyBase = _backend.ActivityProxyBase
|
|
42
|
+
LCIBackend = _backend.LCIBackend
|
|
43
|
+
sqlite3_lci_db = _backend.sqlite3_lci_db
|
|
44
|
+
dict_as_exchangedataset = _backend.dict_as_exchangedataset
|
|
45
|
+
|
|
46
|
+
name = _backend.name
|
|
47
|
+
|
|
48
|
+
_LAZY_ATTRS = frozenset({"databases", "projects", "methods"})
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def __getattr__(attr: str):
|
|
52
|
+
if attr in _LAZY_ATTRS:
|
|
53
|
+
return getattr(_backend, attr)
|
|
54
|
+
raise AttributeError(f"module {__name__!r} has no attribute {attr!r}")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def Database(db_name: str):
|
|
58
|
+
return _backend.Database(db_name)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def Method(key: Tuple):
|
|
62
|
+
return _backend.Method(key)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def get_activity(key):
|
|
66
|
+
return _backend.get_activity(key)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def iter_database(db_name: str) -> Iterable:
|
|
70
|
+
return _backend.iter_database(db_name)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def multi_lca(
|
|
74
|
+
activities: List[dict],
|
|
75
|
+
methods: List[Tuple],
|
|
76
|
+
act_name_fn: Callable,
|
|
77
|
+
method_name_fn: Callable,
|
|
78
|
+
) -> pd.DataFrame:
|
|
79
|
+
return _backend.multi_lca(activities, methods, act_name_fn, method_name_fn)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def is_output_exchange(exc) -> bool:
|
|
83
|
+
return _backend.is_output_exchange(exc)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def exchange_type_for_sub_act(sub_act) -> str:
|
|
87
|
+
return _backend.exchange_type_for_sub_act(sub_act)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def process_node_default() -> str:
|
|
91
|
+
return _backend.process_node_default()
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def production_edge_default() -> str:
|
|
95
|
+
return _backend.production_edge_default()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def activity_not_found() -> Type[Exception]:
|
|
99
|
+
return _backend.activity_not_found()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def new_database_parameters(params, db_name: str) -> None:
|
|
103
|
+
_backend.new_database_parameters(params, db_name)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def new_project_parameters(params) -> None:
|
|
107
|
+
_backend.new_project_parameters(params)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def copy_activity_skip_keys() -> set:
|
|
111
|
+
return _backend.copy_activity_skip_keys()
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def bw2setup() -> None:
|
|
115
|
+
_backend.bw2setup()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def SingleOutputEcospold2Importer(path, dbname, use_mp=False):
|
|
119
|
+
return _backend.SingleOutputEcospold2Importer(path, dbname, use_mp=use_mp)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
__all__ = [
|
|
123
|
+
"Activity",
|
|
124
|
+
"ActivityProxyBase",
|
|
125
|
+
"BrightwayBackend",
|
|
126
|
+
"Database",
|
|
127
|
+
"Exchange",
|
|
128
|
+
"ExchangeDataset",
|
|
129
|
+
"LCIBackend",
|
|
130
|
+
"Method",
|
|
131
|
+
"SingleOutputEcospold2Importer",
|
|
132
|
+
"activity_not_found",
|
|
133
|
+
"bw2setup",
|
|
134
|
+
"copy_activity_skip_keys",
|
|
135
|
+
"databases",
|
|
136
|
+
"dict_as_exchangedataset",
|
|
137
|
+
"exchange_type_for_sub_act",
|
|
138
|
+
"get_activity",
|
|
139
|
+
"is_output_exchange",
|
|
140
|
+
"iter_database",
|
|
141
|
+
"methods",
|
|
142
|
+
"multi_lca",
|
|
143
|
+
"name",
|
|
144
|
+
"new_database_parameters",
|
|
145
|
+
"new_project_parameters",
|
|
146
|
+
"process_node_default",
|
|
147
|
+
"production_edge_default",
|
|
148
|
+
"projects",
|
|
149
|
+
"sqlite3_lci_db",
|
|
150
|
+
]
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
from typing import Callable, List, Tuple
|
|
2
|
+
|
|
3
|
+
import brightway2 as bw
|
|
4
|
+
import pandas as pd
|
|
5
|
+
from bw2data.backends import LCIBackend
|
|
6
|
+
from bw2data.backends.peewee import (
|
|
7
|
+
Activity,
|
|
8
|
+
ActivityDataset,
|
|
9
|
+
Exchange,
|
|
10
|
+
ExchangeDataset,
|
|
11
|
+
sqlite3_lci_db,
|
|
12
|
+
)
|
|
13
|
+
from bw2data.backends.peewee.utils import dict_as_exchangedataset
|
|
14
|
+
from bw2data.proxies import ActivityProxyBase
|
|
15
|
+
from peewee import DoesNotExist
|
|
16
|
+
|
|
17
|
+
_TECHNO_TYPES = [None, "process", "product", "processwithreferenceproduct", "multifunctional"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BW2Backend:
|
|
21
|
+
name = "bw2"
|
|
22
|
+
|
|
23
|
+
Activity = Activity
|
|
24
|
+
ActivityDataset = ActivityDataset
|
|
25
|
+
Exchange = Exchange
|
|
26
|
+
ExchangeDataset = ExchangeDataset
|
|
27
|
+
ActivityProxyBase = ActivityProxyBase
|
|
28
|
+
LCIBackend = LCIBackend
|
|
29
|
+
sqlite3_lci_db = sqlite3_lci_db
|
|
30
|
+
dict_as_exchangedataset = staticmethod(dict_as_exchangedataset)
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def databases(self):
|
|
34
|
+
return bw.databases
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def projects(self):
|
|
38
|
+
return bw.projects
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def methods(self):
|
|
42
|
+
return bw.methods
|
|
43
|
+
|
|
44
|
+
def Database(self, name: str):
|
|
45
|
+
return bw.Database(name)
|
|
46
|
+
|
|
47
|
+
def Method(self, key: Tuple):
|
|
48
|
+
return bw.Method(key)
|
|
49
|
+
|
|
50
|
+
def get_activity(self, key):
|
|
51
|
+
return bw.get_activity(key)
|
|
52
|
+
|
|
53
|
+
def iter_database(self, db_name: str):
|
|
54
|
+
return iter(bw.Database(db_name))
|
|
55
|
+
|
|
56
|
+
def multi_lca(
|
|
57
|
+
self,
|
|
58
|
+
activities: List[dict],
|
|
59
|
+
methods: List[Tuple],
|
|
60
|
+
act_name_fn: Callable,
|
|
61
|
+
method_name_fn: Callable,
|
|
62
|
+
) -> pd.DataFrame:
|
|
63
|
+
bw.calculation_setups["process"] = {"inv": activities, "ia": methods}
|
|
64
|
+
lca = bw.MultiLCA("process")
|
|
65
|
+
cols = [act_name_fn(act) for act_amount in activities for act in act_amount]
|
|
66
|
+
return pd.DataFrame(lca.results.T, index=[method_name_fn(method) for method in methods], columns=cols)
|
|
67
|
+
|
|
68
|
+
def is_output_exchange(self, exc) -> bool:
|
|
69
|
+
return exc.get("type") == "production"
|
|
70
|
+
|
|
71
|
+
def exchange_type_for_sub_act(self, sub_act) -> str:
|
|
72
|
+
if sub_act.get("type") in _TECHNO_TYPES:
|
|
73
|
+
return "technosphere"
|
|
74
|
+
return "biosphere"
|
|
75
|
+
|
|
76
|
+
def process_node_default(self) -> str:
|
|
77
|
+
return "process"
|
|
78
|
+
|
|
79
|
+
def production_edge_default(self) -> str:
|
|
80
|
+
return "production"
|
|
81
|
+
|
|
82
|
+
def activity_not_found(self):
|
|
83
|
+
return DoesNotExist
|
|
84
|
+
|
|
85
|
+
def new_database_parameters(self, params, db_name: str) -> None:
|
|
86
|
+
bw.parameters.new_database_parameters(params, db_name)
|
|
87
|
+
|
|
88
|
+
def new_project_parameters(self, params) -> None:
|
|
89
|
+
bw.parameters.new_project_parameters(params)
|
|
90
|
+
|
|
91
|
+
def copy_activity_skip_keys(self) -> set:
|
|
92
|
+
return {"database", "code"}
|
|
93
|
+
|
|
94
|
+
def bw2setup(self):
|
|
95
|
+
bw.bw2setup()
|
|
96
|
+
|
|
97
|
+
def SingleOutputEcospold2Importer(self, path, dbname, use_mp=False):
|
|
98
|
+
return bw.SingleOutputEcospold2Importer(path, dbname, use_mp=use_mp)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
from typing import Callable, List, Tuple
|
|
2
|
+
|
|
3
|
+
import bw2calc
|
|
4
|
+
import bw2data
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
7
|
+
from bw2data import Database
|
|
8
|
+
from bw2data.backends import (
|
|
9
|
+
Activity,
|
|
10
|
+
ActivityDataset,
|
|
11
|
+
Exchange,
|
|
12
|
+
ExchangeDataset,
|
|
13
|
+
SQLiteBackend,
|
|
14
|
+
sqlite3_lci_db,
|
|
15
|
+
)
|
|
16
|
+
from bw2data.backends.utils import dict_as_exchangedataset
|
|
17
|
+
from bw2data.configuration import labels
|
|
18
|
+
from bw2data.errors import UnknownObject
|
|
19
|
+
from bw2data.parameters import ParameterManager
|
|
20
|
+
from bw2data.proxies import ActivityProxyBase
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class BW25Backend:
|
|
24
|
+
name = "bw25"
|
|
25
|
+
|
|
26
|
+
Activity = Activity
|
|
27
|
+
ActivityDataset = ActivityDataset
|
|
28
|
+
Exchange = Exchange
|
|
29
|
+
ExchangeDataset = ExchangeDataset
|
|
30
|
+
ActivityProxyBase = ActivityProxyBase
|
|
31
|
+
LCIBackend = SQLiteBackend
|
|
32
|
+
sqlite3_lci_db = sqlite3_lci_db
|
|
33
|
+
dict_as_exchangedataset = staticmethod(dict_as_exchangedataset)
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def databases(self):
|
|
37
|
+
return bw2data.databases
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def projects(self):
|
|
41
|
+
return bw2data.projects
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def methods(self):
|
|
45
|
+
return bw2data.methods
|
|
46
|
+
|
|
47
|
+
def Database(self, name: str):
|
|
48
|
+
return Database(name)
|
|
49
|
+
|
|
50
|
+
def Method(self, key: Tuple):
|
|
51
|
+
return bw2data.Method(key)
|
|
52
|
+
|
|
53
|
+
def get_activity(self, key):
|
|
54
|
+
return bw2data.get_activity(key)
|
|
55
|
+
|
|
56
|
+
def iter_database(self, db_name: str):
|
|
57
|
+
return iter(Database(db_name))
|
|
58
|
+
|
|
59
|
+
def multi_lca(
|
|
60
|
+
self,
|
|
61
|
+
activities: List[dict],
|
|
62
|
+
methods: List[Tuple],
|
|
63
|
+
act_name_fn: Callable,
|
|
64
|
+
method_name_fn: Callable,
|
|
65
|
+
) -> pd.DataFrame:
|
|
66
|
+
meth_cfg = {"impact_categories": methods}
|
|
67
|
+
fu = {act_name_fn(act): {act.id: 1} for act_amount in activities for act in act_amount}
|
|
68
|
+
|
|
69
|
+
bw2data.databases.clean()
|
|
70
|
+
data_objs = bw2data.get_multilca_data_objs(fu, meth_cfg)
|
|
71
|
+
lca = bw2calc.MultiLCA(demands=fu, method_config=meth_cfg, data_objs=data_objs)
|
|
72
|
+
lca.lci()
|
|
73
|
+
lca.lcia()
|
|
74
|
+
|
|
75
|
+
rows = [method_name_fn(method) for method in methods]
|
|
76
|
+
cols = [act_name_fn(act) for act_amount in activities for act in act_amount]
|
|
77
|
+
results = lca.scores
|
|
78
|
+
|
|
79
|
+
return pd.DataFrame(
|
|
80
|
+
np.array([[results[m, a] for a in cols] for m in methods]),
|
|
81
|
+
index=rows,
|
|
82
|
+
columns=cols,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def is_output_exchange(self, exc) -> bool:
|
|
86
|
+
return exc.get("type") == labels.production_edge_default
|
|
87
|
+
|
|
88
|
+
def exchange_type_for_sub_act(self, sub_act) -> str:
|
|
89
|
+
if sub_act.get("type") in labels.lci_node_types:
|
|
90
|
+
return labels.consumption_edge_default
|
|
91
|
+
return labels.biosphere_edge_default
|
|
92
|
+
|
|
93
|
+
def process_node_default(self) -> str:
|
|
94
|
+
return labels.process_node_default
|
|
95
|
+
|
|
96
|
+
def production_edge_default(self) -> str:
|
|
97
|
+
return labels.production_edge_default
|
|
98
|
+
|
|
99
|
+
def activity_not_found(self):
|
|
100
|
+
return UnknownObject
|
|
101
|
+
|
|
102
|
+
def new_database_parameters(self, params, db_name: str) -> None:
|
|
103
|
+
ParameterManager().new_database_parameters(params, db_name)
|
|
104
|
+
|
|
105
|
+
def new_project_parameters(self, params) -> None:
|
|
106
|
+
ParameterManager().new_project_parameters(params)
|
|
107
|
+
|
|
108
|
+
def copy_activity_skip_keys(self) -> set:
|
|
109
|
+
return {"database", "code", "id"}
|
|
110
|
+
|
|
111
|
+
def bw2setup(self) -> None:
|
|
112
|
+
raise NotImplementedError("bw2setup is only supported with brightway2")
|
|
113
|
+
|
|
114
|
+
def SingleOutputEcospold2Importer(self, path, dbname, use_mp=False):
|
|
115
|
+
raise NotImplementedError("SingleOutputEcospold2Importer is only supported with brightway2")
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from typing import Any, Callable, Iterable, List, Protocol, Tuple, Type
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BrightwayBackend(Protocol):
|
|
7
|
+
"""Brightway 2 / 2.5 compatibility surface used by lca_algebraic."""
|
|
8
|
+
|
|
9
|
+
Activity: Any
|
|
10
|
+
Exchange: Any
|
|
11
|
+
ActivityDataset: Any
|
|
12
|
+
ExchangeDataset: Any
|
|
13
|
+
ActivityProxyBase: Any
|
|
14
|
+
LCIBackend: Any
|
|
15
|
+
sqlite3_lci_db: Any
|
|
16
|
+
dict_as_exchangedataset: Callable[..., Any]
|
|
17
|
+
|
|
18
|
+
name: str
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def databases(self) -> Any:
|
|
22
|
+
...
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def projects(self) -> Any:
|
|
26
|
+
...
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def methods(self) -> Iterable:
|
|
30
|
+
...
|
|
31
|
+
|
|
32
|
+
def Database(self, name: str) -> Any:
|
|
33
|
+
...
|
|
34
|
+
|
|
35
|
+
def Method(self, key: Tuple) -> Any:
|
|
36
|
+
...
|
|
37
|
+
|
|
38
|
+
def get_activity(self, key) -> Any:
|
|
39
|
+
...
|
|
40
|
+
|
|
41
|
+
def iter_database(self, db_name: str) -> Iterable:
|
|
42
|
+
...
|
|
43
|
+
|
|
44
|
+
def multi_lca(
|
|
45
|
+
self,
|
|
46
|
+
activities: List[dict],
|
|
47
|
+
methods: List[Tuple],
|
|
48
|
+
act_name_fn: Callable,
|
|
49
|
+
method_name_fn: Callable,
|
|
50
|
+
) -> pd.DataFrame:
|
|
51
|
+
...
|
|
52
|
+
|
|
53
|
+
def is_output_exchange(self, exc) -> bool:
|
|
54
|
+
...
|
|
55
|
+
|
|
56
|
+
def exchange_type_for_sub_act(self, sub_act) -> str:
|
|
57
|
+
...
|
|
58
|
+
|
|
59
|
+
def process_node_default(self) -> str:
|
|
60
|
+
...
|
|
61
|
+
|
|
62
|
+
def production_edge_default(self) -> str:
|
|
63
|
+
...
|
|
64
|
+
|
|
65
|
+
def activity_not_found(self) -> Type[Exception]:
|
|
66
|
+
...
|
|
67
|
+
|
|
68
|
+
def new_database_parameters(self, params, db_name: str) -> None:
|
|
69
|
+
...
|
|
70
|
+
|
|
71
|
+
def new_project_parameters(self, params) -> None:
|
|
72
|
+
...
|
|
73
|
+
|
|
74
|
+
def copy_activity_skip_keys(self) -> set:
|
|
75
|
+
...
|
|
76
|
+
|
|
77
|
+
def bw2setup(self) -> None:
|
|
78
|
+
...
|
|
79
|
+
|
|
80
|
+
def SingleOutputEcospold2Importer(self, path, dbname, use_mp=False) -> Any:
|
|
81
|
+
...
|
|
@@ -8,14 +8,15 @@ from typing import Dict, Tuple
|
|
|
8
8
|
from dill import Pickler, load
|
|
9
9
|
from sympy.core.function import UndefinedFunction
|
|
10
10
|
|
|
11
|
-
from lca_algebraic.bw_wrapper import databases, projects
|
|
11
|
+
from lca_algebraic.bw_wrapper import Database, databases, projects
|
|
12
12
|
|
|
13
13
|
from .database import _getMeta
|
|
14
|
-
from .log import
|
|
14
|
+
from .log import logger
|
|
15
15
|
from .settings import PROXY_DB_FLAG, Settings
|
|
16
16
|
|
|
17
17
|
LCIA_CACHE = "lcia"
|
|
18
18
|
EXPR_CACHE = "expr"
|
|
19
|
+
MAPPING_CACHE = "mapping"
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
# Overide the behaviour for pickling sympy.UndefineFunction
|
|
@@ -43,11 +44,15 @@ def flush_caches():
|
|
|
43
44
|
cache.sync()
|
|
44
45
|
|
|
45
46
|
|
|
46
|
-
def get_dependant_dbs(db_name):
|
|
47
|
+
def get_dependant_dbs(db_name, skip_proxy=True):
|
|
47
48
|
"""Recursively get list of dependant db names, including the current one"""
|
|
48
49
|
|
|
50
|
+
# Only work if database is up to date (processed)
|
|
51
|
+
if databases[db_name].get("dirty"):
|
|
52
|
+
Database(db_name).process()
|
|
53
|
+
|
|
49
54
|
# Skip proxy databases
|
|
50
|
-
if _getMeta(db_name, PROXY_DB_FLAG):
|
|
55
|
+
if skip_proxy and _getMeta(db_name, PROXY_DB_FLAG):
|
|
51
56
|
res = set()
|
|
52
57
|
else:
|
|
53
58
|
res = set([db_name])
|
|
@@ -150,7 +155,7 @@ class SyncDict(MutableMapping):
|
|
|
150
155
|
if self.last_update <= file_mtime:
|
|
151
156
|
return
|
|
152
157
|
|
|
153
|
-
info(f"Flushing cache {self.name} / {self.db_name}")
|
|
158
|
+
# info(f"Flushing cache {self.name} / {self.db_name}")
|
|
154
159
|
|
|
155
160
|
tmp = self._filename() + ".tmp"
|
|
156
161
|
with open(tmp, "wb") as f:
|
|
@@ -208,6 +213,11 @@ class ExprCache(_CacheDict):
|
|
|
208
213
|
super().__init__(EXPR_CACHE, db_name)
|
|
209
214
|
|
|
210
215
|
|
|
216
|
+
class MappingCache(_CacheDict):
|
|
217
|
+
def __init__(self, db_name):
|
|
218
|
+
super().__init__(MAPPING_CACHE, db_name)
|
|
219
|
+
|
|
220
|
+
|
|
211
221
|
def clear_caches(local=True, disk=True):
|
|
212
222
|
for cache_name in [LCIA_CACHE, EXPR_CACHE]:
|
|
213
223
|
for db_name in databases:
|
|
@@ -20,6 +20,7 @@ from lca_algebraic.bw_wrapper import (
|
|
|
20
20
|
sqlite3_lci_db,
|
|
21
21
|
)
|
|
22
22
|
|
|
23
|
+
from .base_utils import getActByCode, invalidate_db
|
|
23
24
|
from .log import logger
|
|
24
25
|
from .settings import PROXY_DB_FLAG
|
|
25
26
|
|
|
@@ -87,6 +88,15 @@ def resetDb(db_name, foreground=True):
|
|
|
87
88
|
logger.warning("Db %s was here. Reseting it" % db_name)
|
|
88
89
|
del dbmeta[db_name]
|
|
89
90
|
|
|
91
|
+
# Drop the pooled Database instance : it may hold a stale view (e.g. bw25 node ids)
|
|
92
|
+
# of a previously deleted/recreated database with the same name
|
|
93
|
+
invalidate_db(db_name)
|
|
94
|
+
|
|
95
|
+
# getActByCode() memoizes activity objects by (db_name, code). Activities are
|
|
96
|
+
# recreated with the same code on reset, so drop the memo to avoid returning
|
|
97
|
+
# stale (deleted) node ids.
|
|
98
|
+
getActByCode.cache_clear()
|
|
99
|
+
|
|
90
100
|
db = Database(db_name)
|
|
91
101
|
db.write(dict())
|
|
92
102
|
if foreground:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from copy import copy
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
-
from typing import Dict, List
|
|
3
|
+
from typing import Dict, List, Optional
|
|
4
4
|
|
|
5
5
|
from sympy import Basic, Expr, lambdify, parse_expr
|
|
6
6
|
from sympy.printing.numpy import NumPyPrinter
|
|
@@ -8,6 +8,7 @@ from sympy.printing.numpy import NumPyPrinter
|
|
|
8
8
|
from lca_algebraic.axis_dict import AxisDict
|
|
9
9
|
from lca_algebraic.base_utils import _user_functions
|
|
10
10
|
from lca_algebraic.bw_wrapper import Activity
|
|
11
|
+
from lca_algebraic.log import warn
|
|
11
12
|
from lca_algebraic.params import (
|
|
12
13
|
_complete_params,
|
|
13
14
|
_expand_param_names,
|
|
@@ -39,7 +40,7 @@ class LambdaExpr:
|
|
|
39
40
|
|
|
40
41
|
# List of background activities, indexed in the same order as impacts[i]
|
|
41
42
|
self.background_activities: List[Activity] = list()
|
|
42
|
-
self.impacts: List[float] = None
|
|
43
|
+
self.impacts: Optional[List[float]] = None
|
|
43
44
|
|
|
44
45
|
if isinstance(expr, dict) and not isinstance(expr, AxisDict):
|
|
45
46
|
# Come from JSON serialization
|
|
@@ -123,12 +124,17 @@ class LambdaExpr:
|
|
|
123
124
|
def with_impacts(self, impacts: Dict[Activity, float]):
|
|
124
125
|
"""Transforms a generic LambaExpr toa one with custom impact values"""
|
|
125
126
|
if self.impacts is not None:
|
|
126
|
-
|
|
127
|
+
warn("You are specializing a LambaExpression with impacts already set.")
|
|
128
|
+
|
|
127
129
|
res = copy(self)
|
|
128
130
|
res.impacts = [impacts[bg_act] for bg_act in self.background_activities]
|
|
129
131
|
|
|
130
132
|
return res
|
|
131
133
|
|
|
134
|
+
def impacts_dict(self):
|
|
135
|
+
"""Returns list of impacts as dict"""
|
|
136
|
+
return {bg_act: val for bg_act, val in zip(self.background_activities, self.impacts)}
|
|
137
|
+
|
|
132
138
|
|
|
133
139
|
def _filter_param_values(params, expanded_param_names):
|
|
134
140
|
return {key: val for key, val in params.items() if key in expanded_param_names}
|