lca-modeller 0.0.0__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_modeller-0.0.0/PKG-INFO +69 -0
- lca_modeller-0.0.0/README.md +39 -0
- lca_modeller-0.0.0/lca_modeller/__init__.py +0 -0
- lca_modeller-0.0.0/lca_modeller/gui/__init__.py +0 -0
- lca_modeller-0.0.0/lca_modeller/gui/plots.py +88 -0
- lca_modeller-0.0.0/lca_modeller/helpers.py +206 -0
- lca_modeller-0.0.0/lca_modeller/io/__init__.py +0 -0
- lca_modeller-0.0.0/lca_modeller/io/configuration.py +1145 -0
- lca_modeller-0.0.0/lca_modeller/io/resources/__init__.py +0 -0
- lca_modeller-0.0.0/lca_modeller/io/resources/configuration.json +317 -0
- lca_modeller-0.0.0/lca_modeller/io/tests/__init__.py +0 -0
- lca_modeller-0.0.0/lca_modeller/notebook/data/advanced_example.yaml +90 -0
- lca_modeller-0.0.0/lca_modeller/notebook/data/basic_example.yaml +50 -0
- lca_modeller-0.0.0/lca_modeller/notebook/data/basic_example_split_impacts.yaml +54 -0
- lca_modeller-0.0.0/lca_modeller/notebook/data/custom_lcia_methods.yaml +59 -0
- lca_modeller-0.0.0/lca_modeller/notebook/data/lcia_gwp100_custom.csv +3 -0
- lca_modeller-0.0.0/lca_modeller/notebook/data/metadata_example.yaml +77 -0
- lca_modeller-0.0.0/lca_modeller/notebook/data/prospective_lca.yaml +65 -0
- lca_modeller-0.0.0/lca_modeller/notebook/tutorial-notebook.ipynb +416 -0
- lca_modeller-0.0.0/pyproject.toml +39 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: lca-modeller
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Python library to build parametric LCA models from a configuration file.
|
|
5
|
+
License: GPL-3.0-only
|
|
6
|
+
Keywords: life cycle assessment,LCA,parametric model,prospective LCA
|
|
7
|
+
Author: Félix POLLET
|
|
8
|
+
Author-email: felix.pollet@isae-supaero.fr
|
|
9
|
+
Requires-Python: >=3.10,<3.12
|
|
10
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Requires-Dist: bw2calc (>=1.8.2,<2.0.0)
|
|
15
|
+
Requires-Dist: bw2data (>=3.6.6,<4.0.0)
|
|
16
|
+
Requires-Dist: bw2io (>=0.8.12,<0.9.0)
|
|
17
|
+
Requires-Dist: eight (>=1.0.1,<2.0.0) ; sys_platform == "darwin"
|
|
18
|
+
Requires-Dist: ensure (>=1.0.0,<2.0.0)
|
|
19
|
+
Requires-Dist: intel-openmp (==2023.2.0) ; sys_platform == "darwin"
|
|
20
|
+
Requires-Dist: jupyterlab
|
|
21
|
+
Requires-Dist: lca-algebraic (>=1.2,<2.0)
|
|
22
|
+
Requires-Dist: matplotlib (>=3.7,<4.0)
|
|
23
|
+
Requires-Dist: mkl (==2023.2.2) ; sys_platform == "darwin"
|
|
24
|
+
Requires-Dist: premise (>=2.2.3,<3.0.0)
|
|
25
|
+
Requires-Dist: pyvis (>=0.3.0,<0.4.0)
|
|
26
|
+
Requires-Dist: ruamel.yaml (>=0.15,<0.19)
|
|
27
|
+
Requires-Dist: tbb (==2021.10.0) ; sys_platform == "darwin"
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
🚀 LCA-Modeller
|
|
31
|
+
----------------
|
|
32
|
+
*LCA-Modeller* offers a streamlined interface to facilitate the creation of **parametric LCA models** with **prospective capabilities**. It builds on the open-source libraries [*lca-algebraic*](https://lca-algebraic.readthedocs.io/) and [*premise*](https://premise.readthedocs.io/), so having a basic understanding of these tools is recommended.
|
|
33
|
+
|
|
34
|
+
The core functionality of *LCA-Modeller* revolves around reading a user-provided configuration file that defines the LCA model. From this configuration file, *LCA-Modeller* generates a parametric LCA model with *lca-algebraic*, which can then be evaluated for any parameter values using *lca-algebraic*'s built-in functions.
|
|
35
|
+
<br>
|
|
36
|
+
If prospective scenarios are provided, *premise* is used to adapt the EcoInvent database to future conditions. The parametric LCA model then interpolates the prospective databases to enable the evaluation for any year specified by the user.
|
|
37
|
+
|
|
38
|
+
Additional features include the definition of custom impact assessment methods and the ability to modify existing activities in the EcoInvent database by adding or updating flows.
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
📦 Installation
|
|
42
|
+
----------------
|
|
43
|
+
**No pip installation is provided at the moment.** <br>
|
|
44
|
+
To install *LCA-Modeller*, setup a separate conda environment:
|
|
45
|
+
```bash
|
|
46
|
+
conda create -n lca_modeller python==3.10
|
|
47
|
+
conda activate lca_modeller
|
|
48
|
+
```
|
|
49
|
+
And git clone the repository. Then, install *LCA-Modeller* using [poetry](https://python-poetry.org/docs/) by setting the current directory to the root of the repository and running:
|
|
50
|
+
```bash
|
|
51
|
+
poetry install
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
A tutorial notebook is provided in the `notebooks` directory to help you get started with *LCA-Modeller*.
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
✈️ Applications
|
|
58
|
+
----------------
|
|
59
|
+
*LCA-Modeller* is currently being used in the following projects:
|
|
60
|
+
* [AeroMAPS](https://github.com/AeroMAPS/AeroMAPS) : Multidisciplinary Assessment of Prospective Scenarios for air transport.
|
|
61
|
+
* [FAST-UAV](https://github.com/SizingLab/FAST-UAV): Future Aircraft Sizing Tool - Unmanned Aerial Vehicles
|
|
62
|
+
* [FAST-OAD](https://github.com/fast-aircraft-design/FAST-OAD): Future Aircraft Sizing Tool - Overall Aircraft Design
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
🤝 Questions and contributions
|
|
66
|
+
-------------------------
|
|
67
|
+
* Félix POLLET [felix.pollet@isae-supaero.fr](felix.pollet@isae-supaero.fr)
|
|
68
|
+
|
|
69
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
🚀 LCA-Modeller
|
|
2
|
+
----------------
|
|
3
|
+
*LCA-Modeller* offers a streamlined interface to facilitate the creation of **parametric LCA models** with **prospective capabilities**. It builds on the open-source libraries [*lca-algebraic*](https://lca-algebraic.readthedocs.io/) and [*premise*](https://premise.readthedocs.io/), so having a basic understanding of these tools is recommended.
|
|
4
|
+
|
|
5
|
+
The core functionality of *LCA-Modeller* revolves around reading a user-provided configuration file that defines the LCA model. From this configuration file, *LCA-Modeller* generates a parametric LCA model with *lca-algebraic*, which can then be evaluated for any parameter values using *lca-algebraic*'s built-in functions.
|
|
6
|
+
<br>
|
|
7
|
+
If prospective scenarios are provided, *premise* is used to adapt the EcoInvent database to future conditions. The parametric LCA model then interpolates the prospective databases to enable the evaluation for any year specified by the user.
|
|
8
|
+
|
|
9
|
+
Additional features include the definition of custom impact assessment methods and the ability to modify existing activities in the EcoInvent database by adding or updating flows.
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
📦 Installation
|
|
13
|
+
----------------
|
|
14
|
+
**No pip installation is provided at the moment.** <br>
|
|
15
|
+
To install *LCA-Modeller*, setup a separate conda environment:
|
|
16
|
+
```bash
|
|
17
|
+
conda create -n lca_modeller python==3.10
|
|
18
|
+
conda activate lca_modeller
|
|
19
|
+
```
|
|
20
|
+
And git clone the repository. Then, install *LCA-Modeller* using [poetry](https://python-poetry.org/docs/) by setting the current directory to the root of the repository and running:
|
|
21
|
+
```bash
|
|
22
|
+
poetry install
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
A tutorial notebook is provided in the `notebooks` directory to help you get started with *LCA-Modeller*.
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
✈️ Applications
|
|
29
|
+
----------------
|
|
30
|
+
*LCA-Modeller* is currently being used in the following projects:
|
|
31
|
+
* [AeroMAPS](https://github.com/AeroMAPS/AeroMAPS) : Multidisciplinary Assessment of Prospective Scenarios for air transport.
|
|
32
|
+
* [FAST-UAV](https://github.com/SizingLab/FAST-UAV): Future Aircraft Sizing Tool - Unmanned Aerial Vehicles
|
|
33
|
+
* [FAST-OAD](https://github.com/fast-aircraft-design/FAST-OAD): Future Aircraft Sizing Tool - Overall Aircraft Design
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
🤝 Questions and contributions
|
|
37
|
+
-------------------------
|
|
38
|
+
* Félix POLLET [felix.pollet@isae-supaero.fr](felix.pollet@isae-supaero.fr)
|
|
39
|
+
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Plots for life cycle assessments interpretation
|
|
3
|
+
"""
|
|
4
|
+
from pyvis.network import Network
|
|
5
|
+
import os
|
|
6
|
+
from IPython.display import display, HTML, IFrame
|
|
7
|
+
from lca_algebraic.activity import Activity
|
|
8
|
+
from lca_modeller.helpers import list_processes
|
|
9
|
+
import matplotlib
|
|
10
|
+
import matplotlib.pyplot as plt
|
|
11
|
+
|
|
12
|
+
USER_DB = 'Foreground DB'
|
|
13
|
+
default_process_tree_filename = 'process_tree.html'
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def process_tree(model: Activity,
|
|
17
|
+
foreground_only: bool = True,
|
|
18
|
+
outfile: str = default_process_tree_filename,
|
|
19
|
+
colormap: str = 'Pastel2'):
|
|
20
|
+
"""
|
|
21
|
+
Plots an interactive tree to visualize the activities and exchanges declared in the LCA module.
|
|
22
|
+
:param model: The model containing activities and exchanges.
|
|
23
|
+
:param foreground_only: Boolean flag to include only foreground activities.
|
|
24
|
+
:param outfile: Output filename for the generated tree.
|
|
25
|
+
:param colormap: Colormap to use for node coloring.
|
|
26
|
+
"""
|
|
27
|
+
# Init network
|
|
28
|
+
net = Network(notebook=True, directed=True, layout=True, cdn_resources='remote', filter_menu=False)
|
|
29
|
+
|
|
30
|
+
# Get processes hierarchy
|
|
31
|
+
df = list_processes(model, foreground_only)
|
|
32
|
+
df['description'] = df['activity'] + '\n' + df['unit'].fillna('')
|
|
33
|
+
|
|
34
|
+
# Colors
|
|
35
|
+
unique_values = df['database'].unique()
|
|
36
|
+
value_indices = {value: i for i, value in enumerate(unique_values)}
|
|
37
|
+
norm = matplotlib.colors.Normalize(vmin=0, vmax=len(unique_values), clip=True)
|
|
38
|
+
mapper = plt.cm.ScalarMappable(norm=norm, cmap=plt.get_cmap(colormap))
|
|
39
|
+
colors = df['database'].apply(lambda x: matplotlib.colors.to_hex(mapper.to_rgba(value_indices[x])))
|
|
40
|
+
|
|
41
|
+
# Populate network
|
|
42
|
+
edge_data = zip(df['activity'], df['description'], df['parents'], df['amounts'], df['level'], df['database'], colors)
|
|
43
|
+
|
|
44
|
+
edges = {}
|
|
45
|
+
for activity, description, parents, amounts, level, database, color in edge_data:
|
|
46
|
+
src = activity if database == USER_DB else f"{activity}\n[{database}]"
|
|
47
|
+
#desc_array = description.split(',')
|
|
48
|
+
#desc_db = description.split('(')
|
|
49
|
+
#desc = ',\n'.join(desc_array[:2]) if len(desc_array) > 1 else desc_array[0]
|
|
50
|
+
#desc += f"\n({desc_db[-1]}" if len(desc_db) > 1 else ''
|
|
51
|
+
desc = description
|
|
52
|
+
|
|
53
|
+
net.add_node(src, desc, title=src, level=level + 1, shape='box', color=color)
|
|
54
|
+
|
|
55
|
+
for parent, amount in zip(parents, amounts):
|
|
56
|
+
if parent:
|
|
57
|
+
edge_key = (src, parent)
|
|
58
|
+
if edge_key in edges: # Multiple edges between the same nodes
|
|
59
|
+
edges[edge_key].append(str(amount))
|
|
60
|
+
else: # first time edge links act and its parent: create new node for parent
|
|
61
|
+
edges[edge_key] = [str(amount)]
|
|
62
|
+
|
|
63
|
+
# Add edges to the network with unique labels
|
|
64
|
+
for (src, dst), labels in edges.items():
|
|
65
|
+
if len(labels) == 1:
|
|
66
|
+
net.add_edge(src, dst, label=labels[0], title=f"Exch. 1: {labels[0]}")
|
|
67
|
+
else:
|
|
68
|
+
num_edges = len(labels)
|
|
69
|
+
roundness_step = 0.1 / num_edges # Adjust the denominator to control spacing
|
|
70
|
+
for i, label in enumerate(labels):
|
|
71
|
+
curve_type = 'curvedCW' if i % 2 == 0 else 'curvedCCW'
|
|
72
|
+
roundness = roundness_step * (i + 1) # Adjust roundness to avoid excessive curvature
|
|
73
|
+
net.add_edge(src, dst, label=label, title=f"Exch. {i + 1}: {label}", smooth={'enabled': True, 'type': curve_type, 'roundness': roundness})
|
|
74
|
+
|
|
75
|
+
# Set options
|
|
76
|
+
net.set_edge_smooth('vertical')
|
|
77
|
+
net.toggle_physics(False)
|
|
78
|
+
net.show_buttons(filter_=True)
|
|
79
|
+
net.options.layout.hierarchical.nodeSpacing = 200
|
|
80
|
+
|
|
81
|
+
# Save
|
|
82
|
+
net.save_graph(outfile)
|
|
83
|
+
|
|
84
|
+
# Display in Jupyter Notebook
|
|
85
|
+
display(IFrame(src=os.path.relpath(outfile), width="100%", height=700))
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import lca_algebraic as agb
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from sympy.parsing.sympy_parser import parse_expr
|
|
4
|
+
from sympy.printing.str import StrPrinter
|
|
5
|
+
from sympy import Float, factor
|
|
6
|
+
import bw2data
|
|
7
|
+
|
|
8
|
+
USER_DB = 'Foreground DB'
|
|
9
|
+
DEFAULT_PROJECT = 'lca_modeller_default_project'
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def list_processes(model, foreground_only: bool = True, custom_attribute: str = None) -> pd.DataFrame:
|
|
13
|
+
"""
|
|
14
|
+
Traverses the tree of sub-activities (sub-processes) until the background database is reached.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def _recursive_activities(act, activities, units, locations, parents, amounts, levels, dbs, custom_attributes,
|
|
18
|
+
parent: str = "", exc: dict = None, level: int = 0):
|
|
19
|
+
|
|
20
|
+
if exc is None:
|
|
21
|
+
exc = {}
|
|
22
|
+
name = act.as_dict()['name']
|
|
23
|
+
unit = act.as_dict()['unit']
|
|
24
|
+
loc = act.as_dict().get('location', "")
|
|
25
|
+
if loc not in ['GLO', ''] and f'[{loc}]' not in name:
|
|
26
|
+
name += f' [{loc}]'
|
|
27
|
+
amount = _getAmountOrFormula(exc)
|
|
28
|
+
db = act.as_dict()['database']
|
|
29
|
+
custom_attr = act.as_dict().get(custom_attribute, "") # get any additional attribute asked by the user
|
|
30
|
+
|
|
31
|
+
# Stop BEFORE reaching the first level of background activities
|
|
32
|
+
if foreground_only and db != USER_DB:
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
if name in activities: # Multiple parents for the same activity.
|
|
36
|
+
# TODO: implement better check than just the name and database, e.g. by adding the act object in the lists (but not in the final df)
|
|
37
|
+
# Populate lists accordingly and stop recursion since the activity has already been processed
|
|
38
|
+
idx = activities.index(name)
|
|
39
|
+
if dbs[idx] == db:
|
|
40
|
+
parents[idx].append(parent)
|
|
41
|
+
amounts[idx].append(amount)
|
|
42
|
+
return
|
|
43
|
+
|
|
44
|
+
activities.append(name)
|
|
45
|
+
units.append(unit)
|
|
46
|
+
locations.append(loc)
|
|
47
|
+
parents.append([parent])
|
|
48
|
+
amounts.append([amount])
|
|
49
|
+
levels.append(level)
|
|
50
|
+
dbs.append(db)
|
|
51
|
+
custom_attributes.append(custom_attr)
|
|
52
|
+
|
|
53
|
+
# Stop AFTER reaching the first level of background activities
|
|
54
|
+
if db != USER_DB:
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
for exc in act.exchanges():
|
|
58
|
+
if agb.base_utils._isOutputExch(exc): # skip production exchange to only go down the tree
|
|
59
|
+
continue
|
|
60
|
+
_recursive_activities(exc.input, activities, units, locations, parents, amounts, levels, dbs, custom_attributes,
|
|
61
|
+
parent=name,
|
|
62
|
+
exc=exc,
|
|
63
|
+
level=level + 1)
|
|
64
|
+
return
|
|
65
|
+
|
|
66
|
+
# Initialize lists
|
|
67
|
+
activities = []
|
|
68
|
+
units = []
|
|
69
|
+
locations = []
|
|
70
|
+
parents = []
|
|
71
|
+
amounts = []
|
|
72
|
+
levels = []
|
|
73
|
+
dbs = []
|
|
74
|
+
custom_attributes = []
|
|
75
|
+
|
|
76
|
+
# Recursively populate lists
|
|
77
|
+
_recursive_activities(model, activities, units, locations, parents, amounts, levels, dbs, custom_attributes)
|
|
78
|
+
data = {'activity': activities,
|
|
79
|
+
'unit': units,
|
|
80
|
+
'location': locations,
|
|
81
|
+
'level': levels,
|
|
82
|
+
'database': dbs,
|
|
83
|
+
'parents': parents,
|
|
84
|
+
'amounts': amounts,
|
|
85
|
+
}
|
|
86
|
+
if custom_attribute:
|
|
87
|
+
data[custom_attribute] = custom_attributes
|
|
88
|
+
|
|
89
|
+
# Create DataFrame
|
|
90
|
+
df = pd.DataFrame(data, index=activities)
|
|
91
|
+
|
|
92
|
+
return df
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def get_parameter(key: str):
|
|
96
|
+
param = agb.params._param_registry().__getitem__(key)
|
|
97
|
+
return param
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def expandParams(param, value=None):
|
|
101
|
+
"""
|
|
102
|
+
Modified version of expandParams from classes EnumParam and ParamDef from lca_algebraic library.
|
|
103
|
+
For enum (switch) parameters, returns a dictionary of single enum values as sympy symbols,
|
|
104
|
+
with only a single one set to 1.
|
|
105
|
+
For float parameters, returns a dictionary with either the user-provided value or the default parameter value.
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
# Enum (e.g. switch) parameters
|
|
109
|
+
if param['type'] == 'enum':
|
|
110
|
+
values = param['values'] + [None]
|
|
111
|
+
|
|
112
|
+
# Bad value ?
|
|
113
|
+
if value not in values:
|
|
114
|
+
raise Exception("Invalid value %s for param %s. Should be in %s" %
|
|
115
|
+
(value, param['name'], str(param['values'])))
|
|
116
|
+
|
|
117
|
+
res = dict()
|
|
118
|
+
for enum_val in values:
|
|
119
|
+
var_name = "%s_%s" % (param['name'], enum_val if enum_val is not None else "default")
|
|
120
|
+
res[var_name] = 1.0 if enum_val == value else 0.0
|
|
121
|
+
return res
|
|
122
|
+
|
|
123
|
+
# Float parameters
|
|
124
|
+
else:
|
|
125
|
+
if value is None:
|
|
126
|
+
value = param['default']
|
|
127
|
+
return {param['name']: value}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def completeParamValues(params, param_registry, setDefaults=True):
|
|
131
|
+
"""
|
|
132
|
+
Modified version of completeParamValues from lca_algebraic library.
|
|
133
|
+
Sets default values for missing parameters and expand enum params.
|
|
134
|
+
|
|
135
|
+
Returns
|
|
136
|
+
-------
|
|
137
|
+
Dict of param_name => float value
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
# Set default variables for missing values
|
|
141
|
+
if setDefaults:
|
|
142
|
+
for name, param in param_registry.items():
|
|
143
|
+
if not name in params:
|
|
144
|
+
params[name] = param['default']
|
|
145
|
+
agb.warn(
|
|
146
|
+
"Required param '%s' was missing, replacing by default value : %s" % (name, str(param['default'])))
|
|
147
|
+
|
|
148
|
+
res = dict()
|
|
149
|
+
for key, val in params.items():
|
|
150
|
+
if key in param_registry:
|
|
151
|
+
param = param_registry[key]
|
|
152
|
+
else:
|
|
153
|
+
continue
|
|
154
|
+
# raise Exception("Parameter not found : %s. Valid parameters : %s" % (key, list(param_registry.keys())))
|
|
155
|
+
|
|
156
|
+
if isinstance(val, list):
|
|
157
|
+
newvals = [expandParams(param, val) for val in val]
|
|
158
|
+
res.update(agb.params._listOfDictToDictOflist(newvals))
|
|
159
|
+
else:
|
|
160
|
+
res.update(expandParams(param, val))
|
|
161
|
+
|
|
162
|
+
return res
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def format_number(num, precision=2):
|
|
166
|
+
"""
|
|
167
|
+
This function takes a number (either a Python float or a SymPy Float) and a precision,
|
|
168
|
+
and returns a string that represents the number with the given precision.
|
|
169
|
+
If the number is too large or too small, it switches to scientific notation.
|
|
170
|
+
|
|
171
|
+
:param num: The number to format.
|
|
172
|
+
:param precision: The number of decimal places to use.
|
|
173
|
+
:return: A string that represents the number with the given precision.
|
|
174
|
+
"""
|
|
175
|
+
if isinstance(num, Float): # SymPy float
|
|
176
|
+
num = num.evalf()
|
|
177
|
+
sci_num = "{:.{}e}".format(num, precision)
|
|
178
|
+
if 'e+00' in sci_num:
|
|
179
|
+
return "{:.{}f}".format(num, precision)
|
|
180
|
+
else:
|
|
181
|
+
return sci_num
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class CustomStrPrinter(StrPrinter):
|
|
185
|
+
def _print_Float(self, expr):
|
|
186
|
+
return '{:.2e}'.format(expr)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _getAmountOrFormula(ex):
|
|
190
|
+
""" Return either a fixed float value or an expression for the amount of this exchange"""
|
|
191
|
+
if 'formula' in ex:
|
|
192
|
+
expr = parse_expr(ex['formula'], evaluate=False)
|
|
193
|
+
return CustomStrPrinter().doprint(factor(expr))
|
|
194
|
+
elif 'amount' in ex:
|
|
195
|
+
return format_number(ex['amount'])
|
|
196
|
+
return ""
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def safe_delete_brightway_project(projectname: str) -> None:
|
|
200
|
+
try:
|
|
201
|
+
bw2data.projects.delete_project(
|
|
202
|
+
name = projectname,
|
|
203
|
+
delete_dir = True
|
|
204
|
+
)
|
|
205
|
+
except:
|
|
206
|
+
pass
|
|
File without changes
|