lca-algebraic 1.4.1.dev2__tar.gz → 1.4.1.dev4__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.dev2 → lca_algebraic-1.4.1.dev4}/PKG-INFO +2 -1
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/activity.py +16 -6
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/base_utils.py +17 -7
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/cache.py +8 -1
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/lambda_expression.py +31 -10
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/lca.py +154 -106
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/log.py +0 -8
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/matrix.py +38 -13
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/methods.py +1 -6
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/params.py +36 -8
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/settings.py +0 -4
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/stats.py +122 -15
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/sympy_utils.py +1 -3
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic.egg-info/PKG-INFO +2 -1
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic.egg-info/SOURCES.txt +1 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic.egg-info/requires.txt +1 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/pyproject.toml +3 -2
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/test/test_premise.py +1 -33
- lca_algebraic-1.4.1.dev4/test/test_stats.py +86 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/test/test_units.py +78 -8
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/LICENSE +0 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/README.md +0 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/__init__.py +0 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/axis_dict.py +0 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/bw_wrapper/__init__.py +0 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/bw_wrapper/bw2.py +0 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/bw_wrapper/bw25.py +0 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/bw_wrapper/protocol.py +0 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/database.py +0 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/interpolation.py +0 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/io.py +0 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic/units.py +0 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic.egg-info/dependency_links.txt +0 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/lca_algebraic.egg-info/top_level.txt +0 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/setup.cfg +0 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/test/test_axis_dict.py +0 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/test/test_axis_matrices_cache.py +0 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/test/test_cache.py +0 -0
- {lca_algebraic-1.4.1.dev2 → lca_algebraic-1.4.1.dev4}/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.dev4
|
|
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
|
|
@@ -41,6 +41,7 @@ Requires-Dist: pre-commit; extra == "dev"
|
|
|
41
41
|
Requires-Dist: pytest; extra == "dev"
|
|
42
42
|
Requires-Dist: nbmake; extra == "dev"
|
|
43
43
|
Requires-Dist: build; extra == "dev"
|
|
44
|
+
Requires-Dist: twine>=6.1; extra == "dev"
|
|
44
45
|
Dynamic: license-file
|
|
45
46
|
|
|
46
47
|
|
|
@@ -29,6 +29,7 @@ from lca_algebraic.database import (
|
|
|
29
29
|
)
|
|
30
30
|
from lca_algebraic.params import (
|
|
31
31
|
STORE_FORMULA_KEY,
|
|
32
|
+
STORE_FORMULA_TAG,
|
|
32
33
|
DbContext,
|
|
33
34
|
ParamDef,
|
|
34
35
|
_complete_and_expand_params,
|
|
@@ -318,6 +319,10 @@ class ActivityExtended(Activity):
|
|
|
318
319
|
def _amount_to_formula(self, amount: ValueOrExpression, exchange: ExchangeDataset):
|
|
319
320
|
res = dict()
|
|
320
321
|
if isinstance(amount, Basic):
|
|
322
|
+
|
|
323
|
+
if amount.is_number:
|
|
324
|
+
return {"amount": float(amount.evalf())}
|
|
325
|
+
|
|
321
326
|
current_amount = exchange.get("amount", None)
|
|
322
327
|
if current_amount is not None:
|
|
323
328
|
amount = amount.subs(old_amount, current_amount)
|
|
@@ -329,8 +334,9 @@ class ActivityExtended(Activity):
|
|
|
329
334
|
raise Exception("Symbol '%s' not found in params : %s" % (symbol, all_symbols))
|
|
330
335
|
|
|
331
336
|
res[STORE_FORMULA_KEY] = str(amount)
|
|
337
|
+
res[STORE_FORMULA_TAG] = True
|
|
332
338
|
res["amount"] = 0
|
|
333
|
-
elif isinstance(amount, float
|
|
339
|
+
elif isinstance(amount, float|int):
|
|
334
340
|
res["amount"] = amount
|
|
335
341
|
else:
|
|
336
342
|
raise Exception(
|
|
@@ -349,6 +355,9 @@ class ActivityExtended(Activity):
|
|
|
349
355
|
# Update units
|
|
350
356
|
amount = self._transform_unit(amount, exchange["unit"])
|
|
351
357
|
|
|
358
|
+
updates.pop(STORE_FORMULA_TAG, None)
|
|
359
|
+
exchange.pop(STORE_FORMULA_TAG, None)
|
|
360
|
+
|
|
352
361
|
# Extract formula if two separate field "amount" and "formula"
|
|
353
362
|
# Update the list of updates
|
|
354
363
|
updates.update(self._amount_to_formula(amount, exchange))
|
|
@@ -629,9 +638,7 @@ def newActivity(
|
|
|
629
638
|
|
|
630
639
|
|
|
631
640
|
@atomic
|
|
632
|
-
def copyActivity(
|
|
633
|
-
db_name, activity: ActivityExtended, code=None, withExchanges=True, input_mapping=None, **kwargs
|
|
634
|
-
) -> ActivityExtended:
|
|
641
|
+
def copyActivity(db_name, activity: ActivityExtended, code=None, withExchanges=True, stripFormula=True, input_mapping=None, **kwargs) -> ActivityExtended:
|
|
635
642
|
"""Copy an activity and its exchanges into another database. You usually want to copy activities from your background to
|
|
636
643
|
your foreground DB to update them, keeping your background DB clean.
|
|
637
644
|
|
|
@@ -643,7 +650,10 @@ def copyActivity(
|
|
|
643
650
|
Source activity
|
|
644
651
|
code:
|
|
645
652
|
Code of the target activity. Also used as its name
|
|
646
|
-
|
|
653
|
+
withExchange:
|
|
654
|
+
Copy exchanges is True (default), otherwise do not copy them
|
|
655
|
+
stripFormula:
|
|
656
|
+
keep exchanges formula if True, default is False
|
|
647
657
|
|
|
648
658
|
Returns
|
|
649
659
|
-------
|
|
@@ -680,7 +690,7 @@ def copyActivity(
|
|
|
680
690
|
|
|
681
691
|
# Chemical formulas might be in ecoinvent technosphere
|
|
682
692
|
# We don't want them
|
|
683
|
-
if "formula" in data:
|
|
693
|
+
if stripFormula and ("formula" in data) and (STORE_FORMULA_TAG not in data):
|
|
684
694
|
del data["formula"]
|
|
685
695
|
ExchangeDataset.create(**dict_as_exchangedataset(data))
|
|
686
696
|
|
|
@@ -3,14 +3,16 @@ from functools import cache
|
|
|
3
3
|
from inspect import isfunction
|
|
4
4
|
from typing import Dict, Iterable, Tuple, Union
|
|
5
5
|
|
|
6
|
+
import sympy
|
|
6
7
|
import ipywidgets as widgets
|
|
7
8
|
import numpy as np
|
|
8
9
|
import pandas as pd
|
|
9
10
|
from IPython.display import display
|
|
10
11
|
from six import raise_from
|
|
11
12
|
from sympy import Basic
|
|
12
|
-
from
|
|
13
|
+
from pint import Quantity
|
|
13
14
|
|
|
15
|
+
from lca_algebraic.settings import Settings
|
|
14
16
|
from lca_algebraic.bw_wrapper import Activity, Database, is_output_exchange
|
|
15
17
|
|
|
16
18
|
_user_functions = dict()
|
|
@@ -48,14 +50,22 @@ def _getDb(dbname):
|
|
|
48
50
|
return dbs[dbname]
|
|
49
51
|
|
|
50
52
|
|
|
51
|
-
def
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
def _MinMax(op, arg0, *args):
|
|
54
|
+
from lca_algebraic.units import unit_registry as u
|
|
55
|
+
if not Settings.units_enabled:
|
|
56
|
+
return op(arg0, *args)
|
|
57
|
+
same_unit = all(x.units == arg0.units for x in args)
|
|
58
|
+
if not same_unit:
|
|
59
|
+
raise Exception("MinMax argments must have the same units")
|
|
60
|
+
return u.Quantity(op(arg0.magnitude, *[x.magnitude for x in args]), arg0.units)
|
|
54
61
|
|
|
55
62
|
|
|
56
|
-
def
|
|
57
|
-
|
|
58
|
-
|
|
63
|
+
def Max(arg0, *args):
|
|
64
|
+
return _MinMax(sympy.Max, arg0, *args)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def Min(arg0, *args):
|
|
68
|
+
return _MinMax(sympy.Min, arg0, *args)
|
|
59
69
|
|
|
60
70
|
|
|
61
71
|
def _actDesc(act: Activity):
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import time
|
|
3
|
+
from base64 import b32encode
|
|
3
4
|
from collections.abc import MutableMapping
|
|
4
5
|
from datetime import datetime
|
|
5
6
|
from os import path
|
|
@@ -19,6 +20,10 @@ EXPR_CACHE = "expr"
|
|
|
19
20
|
MAPPING_CACHE = "mapping"
|
|
20
21
|
|
|
21
22
|
|
|
23
|
+
def _filename_encode(s: str):
|
|
24
|
+
return b32encode(s.encode("utf-8")).decode("utf-8")
|
|
25
|
+
|
|
26
|
+
|
|
22
27
|
# Overide the behaviour for pickling sympy.UndefineFunction
|
|
23
28
|
class MyPickler(Pickler):
|
|
24
29
|
def reducer_override(self, obj):
|
|
@@ -89,7 +94,9 @@ class SyncDict(MutableMapping):
|
|
|
89
94
|
self.load()
|
|
90
95
|
|
|
91
96
|
def _filename(self):
|
|
92
|
-
return path.join(
|
|
97
|
+
return path.join(
|
|
98
|
+
projects.dir, f"lca_algebraic_cache-{_filename_encode(self.name)}-{_filename_encode(self.db_name)}.pickle"
|
|
99
|
+
)
|
|
93
100
|
|
|
94
101
|
# ---------- core MutableMapping ----------
|
|
95
102
|
def __getitem__(self, key):
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
from copy import copy
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
-
from typing import Dict, List
|
|
3
|
+
from typing import Dict, List
|
|
4
4
|
|
|
5
5
|
from sympy import Basic, Expr, lambdify, parse_expr
|
|
6
|
+
from sympy.core import S
|
|
6
7
|
from sympy.printing.numpy import NumPyPrinter
|
|
7
8
|
|
|
8
9
|
from lca_algebraic.axis_dict import AxisDict
|
|
9
10
|
from lca_algebraic.base_utils import _user_functions
|
|
10
11
|
from lca_algebraic.bw_wrapper import Activity
|
|
11
|
-
from lca_algebraic.log import warn
|
|
12
12
|
from lca_algebraic.params import (
|
|
13
13
|
_complete_params,
|
|
14
14
|
_expand_param_names,
|
|
@@ -19,6 +19,32 @@ from lca_algebraic.settings import CSE, Settings
|
|
|
19
19
|
from lca_algebraic.sympy_utils import find_cses, find_symbols
|
|
20
20
|
|
|
21
21
|
|
|
22
|
+
class NumPyPrinterExt(NumPyPrinter):
|
|
23
|
+
def __init__(self, settings=None):
|
|
24
|
+
super().__init__(settings=settings)
|
|
25
|
+
|
|
26
|
+
def _helper_piecewise(self, *args):
|
|
27
|
+
from sympy.logic.boolalg import ITE, simplify_logic
|
|
28
|
+
|
|
29
|
+
def print_cond(cond):
|
|
30
|
+
"""Problem having an ITE in the cond."""
|
|
31
|
+
if cond.has(ITE):
|
|
32
|
+
return self._print(simplify_logic(cond))
|
|
33
|
+
else:
|
|
34
|
+
return self._print(cond)
|
|
35
|
+
|
|
36
|
+
return "{}({},{},{})".format(
|
|
37
|
+
self._module_format(self._module + ".where"),
|
|
38
|
+
print_cond(args[0].cond),
|
|
39
|
+
self._print(args[0].expr),
|
|
40
|
+
self._helper_piecewise(*args[1:]) if len(args) > 1 else self._print(S.NaN),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def _print_Piecewise(self, expr):
|
|
44
|
+
"Piecewise function printer"
|
|
45
|
+
return self._helper_piecewise(*expr.args)
|
|
46
|
+
|
|
47
|
+
|
|
22
48
|
@dataclass
|
|
23
49
|
class ValueContext:
|
|
24
50
|
"""Represents a result value, with all parameters values used in context"""
|
|
@@ -40,7 +66,7 @@ class LambdaExpr:
|
|
|
40
66
|
|
|
41
67
|
# List of background activities, indexed in the same order as impacts[i]
|
|
42
68
|
self.background_activities: List[Activity] = list()
|
|
43
|
-
self.impacts:
|
|
69
|
+
self.impacts: List[float] = None
|
|
44
70
|
|
|
45
71
|
if isinstance(expr, dict) and not isinstance(expr, AxisDict):
|
|
46
72
|
# Come from JSON serialization
|
|
@@ -124,17 +150,12 @@ class LambdaExpr:
|
|
|
124
150
|
def with_impacts(self, impacts: Dict[Activity, float]):
|
|
125
151
|
"""Transforms a generic LambaExpr toa one with custom impact values"""
|
|
126
152
|
if self.impacts is not None:
|
|
127
|
-
|
|
128
|
-
|
|
153
|
+
raise Exception("You are specializing a LambaExpression with impacts already set.")
|
|
129
154
|
res = copy(self)
|
|
130
155
|
res.impacts = [impacts[bg_act] for bg_act in self.background_activities]
|
|
131
156
|
|
|
132
157
|
return res
|
|
133
158
|
|
|
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
|
-
|
|
138
159
|
|
|
139
160
|
def _filter_param_values(params, expanded_param_names):
|
|
140
161
|
return {key: val for key, val in params.items() if key in expanded_param_names}
|
|
@@ -172,7 +193,7 @@ class LambdWrapper:
|
|
|
172
193
|
def _lambdify(expr: Basic, expanded_params):
|
|
173
194
|
"""Lambdify, handling manually the case of SymDict (for impacts by axis)"""
|
|
174
195
|
|
|
175
|
-
printer =
|
|
196
|
+
printer = NumPyPrinterExt(
|
|
176
197
|
{"fully_qualified_modules": False, "inline": True, "allow_unknown_functions": True, "user_functions": dict()}
|
|
177
198
|
)
|
|
178
199
|
|