lca-algebraic 1.4.1.dev4__tar.gz → 1.4.1.dev6__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.dev4 → lca_algebraic-1.4.1.dev6}/PKG-INFO +1 -1
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/activity.py +38 -11
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/base_utils.py +1 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/bw_wrapper/__init__.py +5 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/bw_wrapper/bw2.py +10 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/bw_wrapper/bw25.py +10 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/bw_wrapper/protocol.py +3 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/cache.py +10 -2
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/lambda_expression.py +9 -3
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/lca.py +172 -67
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/log.py +8 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/methods.py +15 -8
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/params.py +48 -10
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/settings.py +4 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/stats.py +2 -2
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic.egg-info/PKG-INFO +1 -1
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic.egg-info/SOURCES.txt +5 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/pyproject.toml +1 -1
- lca_algebraic-1.4.1.dev6/test/test_expand_params.py +26 -0
- lca_algebraic-1.4.1.dev6/test/test_methods.py +35 -0
- lca_algebraic-1.4.1.dev6/test/test_overwrite.py +73 -0
- lca_algebraic-1.4.1.dev6/test/test_params.py +90 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/test/test_premise.py +35 -1
- lca_algebraic-1.4.1.dev6/test/test_proxy_cache.py +47 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/test/test_stats.py +13 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/test/test_units.py +4 -10
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/LICENSE +0 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/README.md +0 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/__init__.py +0 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/axis_dict.py +0 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/database.py +0 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/interpolation.py +0 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/io.py +0 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/matrix.py +0 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/sympy_utils.py +0 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic/units.py +0 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic.egg-info/dependency_links.txt +0 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic.egg-info/requires.txt +0 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/lca_algebraic.egg-info/top_level.txt +0 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/setup.cfg +0 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/test/test_axis_dict.py +0 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/test/test_axis_matrices_cache.py +0 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/test/test_cache.py +0 -0
- {lca_algebraic-1.4.1.dev4 → lca_algebraic-1.4.1.dev6}/test/test_sympy.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.dev6
|
|
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
|
|
@@ -13,12 +13,14 @@ from lca_algebraic.bw_wrapper import (
|
|
|
13
13
|
Activity,
|
|
14
14
|
Exchange,
|
|
15
15
|
ExchangeDataset,
|
|
16
|
+
activity_not_found,
|
|
16
17
|
copy_activity_skip_keys,
|
|
17
18
|
dict_as_exchangedataset,
|
|
18
19
|
exchange_type_for_sub_act,
|
|
19
20
|
get_activity,
|
|
20
21
|
process_node_default,
|
|
21
22
|
production_edge_default,
|
|
23
|
+
purge_activity_content,
|
|
22
24
|
)
|
|
23
25
|
from lca_algebraic.database import (
|
|
24
26
|
_find_biosphere_db,
|
|
@@ -168,6 +170,11 @@ class ActivityExtended(Activity):
|
|
|
168
170
|
or activity for updating only input,
|
|
169
171
|
or dict of attributes, for updating both at once, or any other attribute.
|
|
170
172
|
The amount can reference the symbol 'old_amount' that will be replaced with the current amount of the exchange.
|
|
173
|
+
|
|
174
|
+
Passing a dict of attributes is also the way to tag a single exchange with an axis \
|
|
175
|
+
(see ``compute_impacts``) : only that exchange will be ventilated under the category::
|
|
176
|
+
|
|
177
|
+
myActivity.updateExchanges({some_act: dict(amount=2, phase="transport")})
|
|
171
178
|
"""
|
|
172
179
|
if not _isForeground(self["database"]) and not Settings.internals:
|
|
173
180
|
msg = f"You are updating a background activity in {self['database']}. Use copyActivity() in a foreground db instead."
|
|
@@ -319,7 +326,6 @@ class ActivityExtended(Activity):
|
|
|
319
326
|
def _amount_to_formula(self, amount: ValueOrExpression, exchange: ExchangeDataset):
|
|
320
327
|
res = dict()
|
|
321
328
|
if isinstance(amount, Basic):
|
|
322
|
-
|
|
323
329
|
if amount.is_number:
|
|
324
330
|
return {"amount": float(amount.evalf())}
|
|
325
331
|
|
|
@@ -336,7 +342,7 @@ class ActivityExtended(Activity):
|
|
|
336
342
|
res[STORE_FORMULA_KEY] = str(amount)
|
|
337
343
|
res[STORE_FORMULA_TAG] = True
|
|
338
344
|
res["amount"] = 0
|
|
339
|
-
elif isinstance(amount, float|int):
|
|
345
|
+
elif isinstance(amount, float | int):
|
|
340
346
|
res["amount"] = amount
|
|
341
347
|
else:
|
|
342
348
|
raise Exception(
|
|
@@ -401,7 +407,14 @@ class ActivityExtended(Activity):
|
|
|
401
407
|
yield exch
|
|
402
408
|
|
|
403
409
|
def updateMeta(self, **kwargs):
|
|
404
|
-
"""Update any property
|
|
410
|
+
"""Update any custom property of the activity.
|
|
411
|
+
|
|
412
|
+
This is typically used to tag an activity with an axis : all its outgoing
|
|
413
|
+
exchanges will then be ventilated under that category by ``compute_impacts``::
|
|
414
|
+
|
|
415
|
+
myActivity.updateMeta(phase="manufacturing")
|
|
416
|
+
compute_impacts(model, methods, axis="phase")
|
|
417
|
+
"""
|
|
405
418
|
for key, val in kwargs.items():
|
|
406
419
|
self._data[key] = val
|
|
407
420
|
self.save()
|
|
@@ -484,6 +497,11 @@ def findActivity(
|
|
|
484
497
|
search = search.lower()
|
|
485
498
|
search = search.replace(",", " ")
|
|
486
499
|
|
|
500
|
+
# bw2data >= 4 backs `search` with FTS5, which rejects blank queries
|
|
501
|
+
# while bw2data 3 silently returned nothing.
|
|
502
|
+
if not search.strip():
|
|
503
|
+
return []
|
|
504
|
+
|
|
487
505
|
# Find candidates via index
|
|
488
506
|
# candidates = _find_candidates(db_name, name_key)
|
|
489
507
|
candidates = _getDb(db_name).search(search, limit=limit)
|
|
@@ -491,7 +509,8 @@ def findActivity(
|
|
|
491
509
|
if len(candidates) == 0:
|
|
492
510
|
# Try again removing strange caracters
|
|
493
511
|
search = re.sub(r"\w*[^a-zA-Z ]+\w*", " ", search)
|
|
494
|
-
|
|
512
|
+
if search.strip():
|
|
513
|
+
candidates = _getDb(db_name).search(search, limit=limit)
|
|
495
514
|
|
|
496
515
|
# Exact match
|
|
497
516
|
return list(filter(act_filter, candidates))
|
|
@@ -555,13 +574,19 @@ def _newAct(db_name, code):
|
|
|
555
574
|
warn(msg)
|
|
556
575
|
|
|
557
576
|
db = _getDb(db_name)
|
|
558
|
-
# Already present : delete it ?
|
|
559
|
-
for act in db:
|
|
560
|
-
if act["code"] == code:
|
|
561
|
-
warn("Activity '%s' was already in '%s'. Overwriting it" % (code, db_name))
|
|
562
|
-
act.delete()
|
|
563
577
|
|
|
564
|
-
|
|
578
|
+
try:
|
|
579
|
+
act = getActByCode(db_name, code)
|
|
580
|
+
except activity_not_found():
|
|
581
|
+
return db.new_activity(code)
|
|
582
|
+
|
|
583
|
+
# Already present : overwrite it in place.
|
|
584
|
+
# We must not delete+recreate the activity : on brightway 2.5, deleting an
|
|
585
|
+
# activity also removes the incoming links (exchanges from other activities
|
|
586
|
+
# consuming it), which would silently break previously built models.
|
|
587
|
+
warn("Activity '%s' was already in '%s'. Overwriting it" % (code, db_name))
|
|
588
|
+
purge_activity_content(act)
|
|
589
|
+
return act
|
|
565
590
|
|
|
566
591
|
|
|
567
592
|
def newActivity(
|
|
@@ -638,7 +663,9 @@ def newActivity(
|
|
|
638
663
|
|
|
639
664
|
|
|
640
665
|
@atomic
|
|
641
|
-
def copyActivity(
|
|
666
|
+
def copyActivity(
|
|
667
|
+
db_name, activity: ActivityExtended, code=None, withExchanges=True, stripFormula=True, input_mapping=None, **kwargs
|
|
668
|
+
) -> ActivityExtended:
|
|
642
669
|
"""Copy an activity and its exchanges into another database. You usually want to copy activities from your background to
|
|
643
670
|
your foreground DB to update them, keeping your background DB clean.
|
|
644
671
|
|
|
@@ -111,6 +111,10 @@ def copy_activity_skip_keys() -> set:
|
|
|
111
111
|
return _backend.copy_activity_skip_keys()
|
|
112
112
|
|
|
113
113
|
|
|
114
|
+
def purge_activity_content(act) -> None:
|
|
115
|
+
_backend.purge_activity_content(act)
|
|
116
|
+
|
|
117
|
+
|
|
114
118
|
def bw2setup() -> None:
|
|
115
119
|
_backend.bw2setup()
|
|
116
120
|
|
|
@@ -146,5 +150,6 @@ __all__ = [
|
|
|
146
150
|
"process_node_default",
|
|
147
151
|
"production_edge_default",
|
|
148
152
|
"projects",
|
|
153
|
+
"purge_activity_content",
|
|
149
154
|
"sqlite3_lci_db",
|
|
150
155
|
]
|
|
@@ -91,6 +91,16 @@ class BW2Backend:
|
|
|
91
91
|
def copy_activity_skip_keys(self) -> set:
|
|
92
92
|
return {"database", "code"}
|
|
93
93
|
|
|
94
|
+
def purge_activity_content(self, act) -> None:
|
|
95
|
+
"""Remove an activity's outgoing exchanges and non-identity attributes.
|
|
96
|
+
|
|
97
|
+
The activity row, its code and the incoming links (exchanges of other
|
|
98
|
+
activities pointing to it) are preserved.
|
|
99
|
+
"""
|
|
100
|
+
act.exchanges().delete()
|
|
101
|
+
for key in [k for k in act._data if k not in {"database", "code"}]:
|
|
102
|
+
del act._data[key]
|
|
103
|
+
|
|
94
104
|
def bw2setup(self):
|
|
95
105
|
bw.bw2setup()
|
|
96
106
|
|
|
@@ -108,6 +108,16 @@ class BW25Backend:
|
|
|
108
108
|
def copy_activity_skip_keys(self) -> set:
|
|
109
109
|
return {"database", "code", "id"}
|
|
110
110
|
|
|
111
|
+
def purge_activity_content(self, act) -> None:
|
|
112
|
+
"""Remove an activity's outgoing exchanges and non-identity attributes.
|
|
113
|
+
|
|
114
|
+
The activity row, its code and the incoming links (exchanges of other
|
|
115
|
+
activities pointing to it) are preserved.
|
|
116
|
+
"""
|
|
117
|
+
act.exchanges().delete(allow_in_sourced_project=True)
|
|
118
|
+
for key in [k for k in act._data if k not in {"database", "code", "id"}]:
|
|
119
|
+
del act._data[key]
|
|
120
|
+
|
|
111
121
|
def bw2setup(self) -> None:
|
|
112
122
|
raise NotImplementedError("bw2setup is only supported with brightway2")
|
|
113
123
|
|
|
@@ -49,8 +49,16 @@ def flush_caches():
|
|
|
49
49
|
cache.sync()
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
def get_dependant_dbs(db_name, skip_proxy=
|
|
53
|
-
"""Recursively get list of dependant db names, including the current one
|
|
52
|
+
def get_dependant_dbs(db_name, skip_proxy=None):
|
|
53
|
+
"""Recursively get list of dependant db names, including the current one
|
|
54
|
+
|
|
55
|
+
Proxy databases are skipped unless background factorization is enabled : with
|
|
56
|
+
``factorize_static_bg`` the developed expressions / LCIA of a database refer to
|
|
57
|
+
the acts of its ``<db>-proxy`` databases, so a change of those proxies must be
|
|
58
|
+
visible (see the staleness check in ``_CacheDict``).
|
|
59
|
+
"""
|
|
60
|
+
if skip_proxy is None:
|
|
61
|
+
skip_proxy = not Settings.factorize_static_bg
|
|
54
62
|
|
|
55
63
|
# Only work if database is up to date (processed)
|
|
56
64
|
if databases[db_name].get("dirty"):
|
|
@@ -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.core import S
|
|
@@ -9,6 +9,7 @@ from sympy.printing.numpy import NumPyPrinter
|
|
|
9
9
|
from lca_algebraic.axis_dict import AxisDict
|
|
10
10
|
from lca_algebraic.base_utils import _user_functions
|
|
11
11
|
from lca_algebraic.bw_wrapper import Activity
|
|
12
|
+
from lca_algebraic.log import warn
|
|
12
13
|
from lca_algebraic.params import (
|
|
13
14
|
_complete_params,
|
|
14
15
|
_expand_param_names,
|
|
@@ -66,7 +67,7 @@ class LambdaExpr:
|
|
|
66
67
|
|
|
67
68
|
# List of background activities, indexed in the same order as impacts[i]
|
|
68
69
|
self.background_activities: List[Activity] = list()
|
|
69
|
-
self.impacts: List[float] = None
|
|
70
|
+
self.impacts: Optional[List[float]] = None
|
|
70
71
|
|
|
71
72
|
if isinstance(expr, dict) and not isinstance(expr, AxisDict):
|
|
72
73
|
# Come from JSON serialization
|
|
@@ -150,12 +151,17 @@ class LambdaExpr:
|
|
|
150
151
|
def with_impacts(self, impacts: Dict[Activity, float]):
|
|
151
152
|
"""Transforms a generic LambaExpr toa one with custom impact values"""
|
|
152
153
|
if self.impacts is not None:
|
|
153
|
-
|
|
154
|
+
warn("You are specializing a LambaExpression with impacts already set.")
|
|
155
|
+
|
|
154
156
|
res = copy(self)
|
|
155
157
|
res.impacts = [impacts[bg_act] for bg_act in self.background_activities]
|
|
156
158
|
|
|
157
159
|
return res
|
|
158
160
|
|
|
161
|
+
def impacts_dict(self):
|
|
162
|
+
"""Returns list of impacts as dict"""
|
|
163
|
+
return {bg_act: val for bg_act, val in zip(self.background_activities, self.impacts)}
|
|
164
|
+
|
|
159
165
|
|
|
160
166
|
def _filter_param_values(params, expanded_param_names):
|
|
161
167
|
return {key: val for key, val in params.items() if key in expanded_param_names}
|