brightpath 0.0.1__py3-none-any.whl
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.
- brightpath/__init__.py +9 -0
- brightpath/bwconverter.py +616 -0
- brightpath/data/export/correspondence_biosphere_flows.yaml +247 -0
- brightpath/data/export/ecoinvent_to_uvek_mapping.csv +659 -0
- brightpath/data/export/ei_transport.csv +277 -0
- brightpath/data/export/flows_biosphere_39.csv +4711 -0
- brightpath/data/export/simapro-biosphere.json +2842 -0
- brightpath/data/export/simapro-technosphere-3.5.csv +46564 -0
- brightpath/data/export/simapro_blacklist.yaml +15 -0
- brightpath/data/export/simapro_ei_exceptions.yaml +5 -0
- brightpath/data/export/simapro_fields.yaml +37 -0
- brightpath/data/export/simapro_headers.yaml +15 -0
- brightpath/data/export/simapro_subcompartments.yaml +16 -0
- brightpath/data/export/simapro_units.yaml +15 -0
- brightpath/data/export/uvek_blacklist.yaml +7 -0
- brightpath/data/export/uvek_conversion_factors.yaml +31 -0
- brightpath/data/export/waste_exchange_names.yaml +11 -0
- brightpath/simaproconverter.py +463 -0
- brightpath/utils.py +1181 -0
- brightpath-0.0.1.dist-info/METADATA +140 -0
- brightpath-0.0.1.dist-info/RECORD +24 -0
- brightpath-0.0.1.dist-info/WHEEL +5 -0
- brightpath-0.0.1.dist-info/licenses/LICENSE +29 -0
- brightpath-0.0.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
- Gangue, in ground
|
|
2
|
+
- Water, turbine use, unspecified natural origin
|
|
3
|
+
- Oxygen
|
|
4
|
+
- Volume occupied, reservoir
|
|
5
|
+
- Xenon-135
|
|
6
|
+
- Noble gases, radioactive, unspecified
|
|
7
|
+
- Radon-222
|
|
8
|
+
- Xenon-133
|
|
9
|
+
- Hydrogen-3, Tritium
|
|
10
|
+
- Radon-222
|
|
11
|
+
- Radon-220
|
|
12
|
+
- Oxygen
|
|
13
|
+
- Occupation, traffic area, road network
|
|
14
|
+
- Energy, gross calorific value, in biomass, primary forest
|
|
15
|
+
- Carbon-14
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
- Process
|
|
2
|
+
- Category type
|
|
3
|
+
- Type
|
|
4
|
+
- Process name
|
|
5
|
+
- Time period
|
|
6
|
+
- Geography
|
|
7
|
+
- Technology
|
|
8
|
+
- Comment
|
|
9
|
+
- Representativeness
|
|
10
|
+
- Cut off rules
|
|
11
|
+
- Capital goods
|
|
12
|
+
- Date
|
|
13
|
+
- Boundary with nature
|
|
14
|
+
- Infrastructure
|
|
15
|
+
- Record
|
|
16
|
+
- Generator
|
|
17
|
+
- Literature references
|
|
18
|
+
- External documents
|
|
19
|
+
- Collection method
|
|
20
|
+
- Data treatment
|
|
21
|
+
- Verification
|
|
22
|
+
- System description
|
|
23
|
+
- Allocation rules
|
|
24
|
+
- Products
|
|
25
|
+
- Waste treatment
|
|
26
|
+
- Materials/fuels
|
|
27
|
+
- Electricity/heat
|
|
28
|
+
- Resources
|
|
29
|
+
- Emissions to air
|
|
30
|
+
- Emissions to water
|
|
31
|
+
- Emissions to soil
|
|
32
|
+
- Final waste flows
|
|
33
|
+
- Non material emission
|
|
34
|
+
- Social issues
|
|
35
|
+
- Economic issues
|
|
36
|
+
- Waste to treatment
|
|
37
|
+
- End
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
- "{SimaPro 9.1.1.1}"
|
|
2
|
+
- "{processes}"
|
|
3
|
+
- "{Project: project_name}"
|
|
4
|
+
- "{Date: today_date}"
|
|
5
|
+
- "{CSV Format version: 9.0.0}"
|
|
6
|
+
- "{CSV separator: Semicolon}"
|
|
7
|
+
- "{Decimal separator: .}"
|
|
8
|
+
- "{Date separator: .}"
|
|
9
|
+
- "{Short date format: dd.MM.yyyy}"
|
|
10
|
+
- "{Export platform IDs: No}"
|
|
11
|
+
- "{Skip empty fields: No}"
|
|
12
|
+
- "{Convert expressions to constants: No}"
|
|
13
|
+
- "{Selection: Selection(1)}"
|
|
14
|
+
- "{Related objects(system descriptions, substances, units, etc.): Yes}"
|
|
15
|
+
- "{Include sub product stages and processes: Yes}"
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
low population density, long-term: low. pop., long-term
|
|
2
|
+
lower stratosphere + upper troposphere: stratosphere + troposphere
|
|
3
|
+
non-urban air or from high stacks: low. pop.
|
|
4
|
+
urban air close to ground: high. pop.
|
|
5
|
+
biotic: biotic
|
|
6
|
+
in air: in air
|
|
7
|
+
in ground: in ground
|
|
8
|
+
in water: in water
|
|
9
|
+
land: land
|
|
10
|
+
agricultural: agricultural
|
|
11
|
+
forestry: forestry
|
|
12
|
+
industrial: industrial
|
|
13
|
+
ground-: groundwater
|
|
14
|
+
ground-, long-term: groundwater, long-term
|
|
15
|
+
ocean: ocean
|
|
16
|
+
surface water: river
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
kilogram: kg
|
|
2
|
+
cubic meter: m3
|
|
3
|
+
kilowatt hour: kWh
|
|
4
|
+
kilometer: km
|
|
5
|
+
ton kilometer: tkm
|
|
6
|
+
megajoule: MJ
|
|
7
|
+
unit: p
|
|
8
|
+
square meter: m2
|
|
9
|
+
kilowatt: p
|
|
10
|
+
hour: hr
|
|
11
|
+
square meter-year: m2a
|
|
12
|
+
meter: m
|
|
13
|
+
vehicle-kilometer: vkm
|
|
14
|
+
person-kilometer: personkm
|
|
15
|
+
meter-year: my
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
market for heat, from steam, in chemical industry:
|
|
2
|
+
factor: 0.36
|
|
3
|
+
unit: kilogram
|
|
4
|
+
steam production, as energy carrier, in chemical industry:
|
|
5
|
+
factor: 0.36
|
|
6
|
+
unit: kilogram
|
|
7
|
+
market for natural gas, high pressure, vehicle grade:
|
|
8
|
+
factor: 0.842
|
|
9
|
+
unit: megajoule
|
|
10
|
+
market for chemical factory:
|
|
11
|
+
factor: 833333
|
|
12
|
+
unit: unit
|
|
13
|
+
market for used powertrain from electric passenger car, manual dismantling:
|
|
14
|
+
factor: -1
|
|
15
|
+
market for manganese sulfate:
|
|
16
|
+
factor: 0.34
|
|
17
|
+
market for nickel sulfate:
|
|
18
|
+
factor: 0.445
|
|
19
|
+
market for manual dismantling of used electric passenger car:
|
|
20
|
+
factor: 0.000833
|
|
21
|
+
unit: kilogram
|
|
22
|
+
market for manual dismantling of electric scooter:
|
|
23
|
+
unit: kilogram
|
|
24
|
+
market for transport, pipeline, onshore, petroleum:
|
|
25
|
+
unit: kilometer
|
|
26
|
+
manual dismantling of used passenger car with internal combustion engine:
|
|
27
|
+
factor: 0.000833
|
|
28
|
+
market for manual dismantling of used passenger car with internal combustion engine:
|
|
29
|
+
factor: 0.000833
|
|
30
|
+
manual dismantling of used electric scooter:
|
|
31
|
+
factor: 0.0111
|
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains the class SimaproConverter, which is used to convert
|
|
3
|
+
Simapro inventories to Brightway inventory files.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import csv
|
|
7
|
+
import logging
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
import bw2io
|
|
12
|
+
|
|
13
|
+
from . import DATA_DIR
|
|
14
|
+
from .utils import (
|
|
15
|
+
check_simapro_inventory,
|
|
16
|
+
get_simapro_biosphere,
|
|
17
|
+
get_simapro_subcompartments,
|
|
18
|
+
get_simapro_technosphere,
|
|
19
|
+
get_waste_exchange_names,
|
|
20
|
+
load_biosphere_correspondence,
|
|
21
|
+
load_ei_biosphere_flows,
|
|
22
|
+
lower_cap_first_letter,
|
|
23
|
+
remove_duplicates,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
WASTE_TERMS = get_waste_exchange_names()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def format_technosphere_exchange(txt: str):
|
|
30
|
+
"""Split and normalise a technosphere exchange name from SimaPro.
|
|
31
|
+
|
|
32
|
+
:param txt: Raw exchange string as found in a SimaPro CSV export.
|
|
33
|
+
:type txt: str
|
|
34
|
+
:return: Tuple containing the cleaned name, reference product and location.
|
|
35
|
+
:rtype: tuple[str, str, str]
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
location_correction = {
|
|
39
|
+
"WECC, US only": "US-WECC",
|
|
40
|
+
"ASCC, US only": "US-ASCC",
|
|
41
|
+
"HICC, US only": "US-HICC",
|
|
42
|
+
"MRO, US only": "US-MRO",
|
|
43
|
+
"NPCC, US only": "US-NPCC",
|
|
44
|
+
"PR, US only": "US-PR",
|
|
45
|
+
"RFC, US only": "US-RFC",
|
|
46
|
+
"SERC, US only": "US-SERC",
|
|
47
|
+
"TRE, US only": "US-TRE",
|
|
48
|
+
"HICC": "US-HICC",
|
|
49
|
+
"SERC": "US-SERC",
|
|
50
|
+
"RFC": "US-RFC",
|
|
51
|
+
"ASCC": "US-ASCC",
|
|
52
|
+
"TRE": "US-TRE",
|
|
53
|
+
"FRCC": "US-FRCC",
|
|
54
|
+
"SPP": "US-SPP",
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
correct_name = [
|
|
58
|
+
"production",
|
|
59
|
+
"production mix",
|
|
60
|
+
"processing",
|
|
61
|
+
"market for",
|
|
62
|
+
"market group for",
|
|
63
|
+
"production, at grid",
|
|
64
|
+
"at market",
|
|
65
|
+
"cut-off, U",
|
|
66
|
+
"generic production",
|
|
67
|
+
"market",
|
|
68
|
+
"processing, mass based",
|
|
69
|
+
"transport",
|
|
70
|
+
"treatment of",
|
|
71
|
+
"purification",
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
reference_product, name, location = "", "", ""
|
|
75
|
+
if len(txt.split("|")) == 1:
|
|
76
|
+
reference_product = txt.split("|")[0]
|
|
77
|
+
name = reference_product
|
|
78
|
+
|
|
79
|
+
if len(txt.split("|")) == 2:
|
|
80
|
+
reference_product, name = txt.split("|")
|
|
81
|
+
|
|
82
|
+
if len(txt.split("|")) == 3:
|
|
83
|
+
reference_product, name, _ = txt.split("|")
|
|
84
|
+
|
|
85
|
+
if len(txt.split("|")) == 4:
|
|
86
|
+
reference_product, name, _, _ = txt.split("|")
|
|
87
|
+
|
|
88
|
+
reference_product = lower_cap_first_letter(reference_product)
|
|
89
|
+
|
|
90
|
+
if "{" in reference_product:
|
|
91
|
+
reference_product, location = reference_product.split("{")
|
|
92
|
+
location, reference_product_ = location.split("}")
|
|
93
|
+
if len(reference_product_) > 0:
|
|
94
|
+
reference_product_ = reference_product_.strip()
|
|
95
|
+
reference_product = f"{reference_product} {reference_product_}"
|
|
96
|
+
reference_product = reference_product.replace(" ", " ")
|
|
97
|
+
else:
|
|
98
|
+
location = "GLO"
|
|
99
|
+
|
|
100
|
+
if location in ["French Guiana", "French Guinana"]:
|
|
101
|
+
location = "FG"
|
|
102
|
+
|
|
103
|
+
name = name.replace(
|
|
104
|
+
"Cut-off, U",
|
|
105
|
+
"",
|
|
106
|
+
)
|
|
107
|
+
name = name.replace(
|
|
108
|
+
"cut-off, U",
|
|
109
|
+
"",
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
if name[-3:] in [", U", ", S"]:
|
|
113
|
+
name = name[:-3]
|
|
114
|
+
|
|
115
|
+
if "{" in name:
|
|
116
|
+
name = name.split("{")[0]
|
|
117
|
+
|
|
118
|
+
name = name.strip()
|
|
119
|
+
|
|
120
|
+
if name == "":
|
|
121
|
+
name = reference_product
|
|
122
|
+
|
|
123
|
+
reference_product = reference_product.strip()
|
|
124
|
+
if reference_product[-1] in [",", "."]:
|
|
125
|
+
reference_product = reference_product[:-1]
|
|
126
|
+
|
|
127
|
+
if name in correct_name:
|
|
128
|
+
if name in ["market for", "market group for", "treatment of"]:
|
|
129
|
+
name = f"{name} {reference_product}"
|
|
130
|
+
elif name == "market":
|
|
131
|
+
name = f"market for {reference_product}"
|
|
132
|
+
elif name == "production":
|
|
133
|
+
if ", " in reference_product:
|
|
134
|
+
name = f"{reference_product.split(', ')[0]} production, {', '.join(reference_product.split(', ')[1:])}"
|
|
135
|
+
else:
|
|
136
|
+
name = f"{reference_product} production"
|
|
137
|
+
elif name == "processing":
|
|
138
|
+
name = reference_product
|
|
139
|
+
else:
|
|
140
|
+
name = f"{reference_product} {name}"
|
|
141
|
+
|
|
142
|
+
if name.startswith("treatment of,"):
|
|
143
|
+
name = f"treatment of {reference_product}, {name.split(', ')[-1]}"
|
|
144
|
+
|
|
145
|
+
if name == "construction":
|
|
146
|
+
if ", " in reference_product:
|
|
147
|
+
name = f"{reference_product.split(', ')[0]} construction, {', '.join(reference_product.split(', ')[1:])}"
|
|
148
|
+
else:
|
|
149
|
+
name = f"{reference_product} construction"
|
|
150
|
+
|
|
151
|
+
if name.startswith("production, from") or name.startswith("production from"):
|
|
152
|
+
name = f"{reference_product} {name}"
|
|
153
|
+
|
|
154
|
+
location = location.replace("}", "").strip()
|
|
155
|
+
location = location_correction.get(location, location)
|
|
156
|
+
|
|
157
|
+
return name, reference_product, location
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def load_ecoinvent_activities(version: str) -> list:
|
|
161
|
+
"""Load the list of ecoinvent activities for the given version.
|
|
162
|
+
|
|
163
|
+
:param version: Ecoinvent version identifier, e.g. ``"3.9"``.
|
|
164
|
+
:type version: str
|
|
165
|
+
:return: Rows describing ecoinvent activities.
|
|
166
|
+
:rtype: list[list[str]]
|
|
167
|
+
"""
|
|
168
|
+
with open(DATA_DIR / "export" / f"list_ei{version}_cutoff_activities.csv") as f:
|
|
169
|
+
reader = csv.reader(f)
|
|
170
|
+
next(reader)
|
|
171
|
+
return [l for l in reader]
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def format_biosphere_exchange(exc, ei_version, bio_flows, bio_mapping):
|
|
175
|
+
"""Normalise a biosphere exchange to match ecoinvent conventions.
|
|
176
|
+
|
|
177
|
+
:param exc: Exchange to adjust in-place.
|
|
178
|
+
:type exc: dict
|
|
179
|
+
:param ei_version: Version of ecoinvent used for interpretation.
|
|
180
|
+
:type ei_version: str
|
|
181
|
+
:param bio_flows: Known biosphere flows for the version.
|
|
182
|
+
:type bio_flows: list[tuple[str, str, str]]
|
|
183
|
+
:param bio_mapping: Mapping to resolve outdated flow names.
|
|
184
|
+
:type bio_mapping: dict
|
|
185
|
+
:return: The updated exchange dictionary.
|
|
186
|
+
:rtype: dict
|
|
187
|
+
"""
|
|
188
|
+
if "in ground" in exc["name"]:
|
|
189
|
+
if ei_version not in ["3.5", "3.6", "3.7", "3.8"]:
|
|
190
|
+
exc["name"] = exc["name"].replace(", in ground", "")
|
|
191
|
+
exc["categories"] = ("natural resource", "in ground")
|
|
192
|
+
|
|
193
|
+
if exc["name"].startswith("Water, well"):
|
|
194
|
+
exc["name"] = "Water, well, in ground"
|
|
195
|
+
|
|
196
|
+
if exc["name"].startswith("Water, lake"):
|
|
197
|
+
exc["name"] = "Water, lake"
|
|
198
|
+
|
|
199
|
+
if exc["name"].startswith("Water, cooling"):
|
|
200
|
+
exc["name"] = "Water, cooling, unspecified natural origin"
|
|
201
|
+
|
|
202
|
+
if exc["name"].startswith("Water,"):
|
|
203
|
+
if "in air" not in exc["name"]:
|
|
204
|
+
exc["categories"] = ("natural resource", "in water")
|
|
205
|
+
|
|
206
|
+
key = (
|
|
207
|
+
exc["name"],
|
|
208
|
+
exc["categories"][0],
|
|
209
|
+
"unspecified" if len(exc["categories"]) == 1 else exc["categories"][1],
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
if key not in bio_flows:
|
|
213
|
+
try:
|
|
214
|
+
if exc["name"] in bio_mapping[exc["categories"][0]]:
|
|
215
|
+
exc["name"] = bio_mapping[exc["categories"][0]][exc["name"]]
|
|
216
|
+
key = list(key)
|
|
217
|
+
key[0] = exc["name"]
|
|
218
|
+
key = tuple(key)
|
|
219
|
+
except:
|
|
220
|
+
logging.warning(f"Could not find biosphere flow for {exc['name']}.")
|
|
221
|
+
pass
|
|
222
|
+
|
|
223
|
+
if key not in bio_flows:
|
|
224
|
+
if exc["categories"][0] == "natural resource":
|
|
225
|
+
for i in [
|
|
226
|
+
"in ground",
|
|
227
|
+
"biotic",
|
|
228
|
+
"in air",
|
|
229
|
+
"land",
|
|
230
|
+
]:
|
|
231
|
+
key = list(key)
|
|
232
|
+
key[2] = i
|
|
233
|
+
key = tuple(key)
|
|
234
|
+
if key in bio_flows:
|
|
235
|
+
exc["categories"] = (exc["categories"][0], i)
|
|
236
|
+
break
|
|
237
|
+
|
|
238
|
+
if exc["categories"][0] == "soil":
|
|
239
|
+
for i in [
|
|
240
|
+
"agricultural",
|
|
241
|
+
"forestry",
|
|
242
|
+
"industrial",
|
|
243
|
+
]:
|
|
244
|
+
key = list(key)
|
|
245
|
+
key[2] = i
|
|
246
|
+
key = tuple(key)
|
|
247
|
+
if key in bio_flows:
|
|
248
|
+
exc["categories"] = (exc["categories"][0], i)
|
|
249
|
+
break
|
|
250
|
+
|
|
251
|
+
if exc["categories"][0] == "air":
|
|
252
|
+
for i in [
|
|
253
|
+
"low population density, long-term",
|
|
254
|
+
"lower stratosphere + upper troposphere",
|
|
255
|
+
"non-urban air or from high stacks",
|
|
256
|
+
"urban air close to ground",
|
|
257
|
+
]:
|
|
258
|
+
key = list(key)
|
|
259
|
+
key[2] = i
|
|
260
|
+
key = tuple(key)
|
|
261
|
+
if key in bio_flows:
|
|
262
|
+
exc["categories"] = (exc["categories"][0], i)
|
|
263
|
+
break
|
|
264
|
+
|
|
265
|
+
if exc["categories"] == ("natural resource", "in ground"):
|
|
266
|
+
if ei_version in ["3.5", "3.6", "3.7", "3.8"]:
|
|
267
|
+
if "in ground" not in exc["name"]:
|
|
268
|
+
exc["name"] += ", in ground"
|
|
269
|
+
|
|
270
|
+
return exc
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class SimaproConverter:
|
|
274
|
+
"""Convert SimaPro CSV exports into Brightway-compatible datasets.
|
|
275
|
+
|
|
276
|
+
:param filepath: Path to the SimaPro CSV file.
|
|
277
|
+
:type filepath: str
|
|
278
|
+
:param ecoinvent_version: Version of ecoinvent to align biosphere data to.
|
|
279
|
+
:type ecoinvent_version: str
|
|
280
|
+
:param db_name: Optional name of the Brightway database to create.
|
|
281
|
+
:type db_name: str | None
|
|
282
|
+
:ivar filepath: Normalised path to the validated CSV file.
|
|
283
|
+
:vartype filepath: pathlib.Path
|
|
284
|
+
:ivar i: Instance of :class:`bw2io.SimaProCSVImporter` handling the data.
|
|
285
|
+
:vartype i: bw2io.SimaProCSVImporter
|
|
286
|
+
:ivar db_name: Name of the Brightway database that will be created.
|
|
287
|
+
:vartype db_name: str
|
|
288
|
+
:ivar ecoinvent_version: Version of the ecoinvent database in use.
|
|
289
|
+
:vartype ecoinvent_version: str
|
|
290
|
+
:ivar biosphere: Mapping between SimaPro and Brightway biosphere flows.
|
|
291
|
+
:vartype biosphere: dict
|
|
292
|
+
:ivar technosphere: Mapping between SimaPro and Brightway technosphere
|
|
293
|
+
exchanges.
|
|
294
|
+
:vartype technosphere: dict
|
|
295
|
+
:ivar subcompartments: Mapping of sub-compartments between the databases.
|
|
296
|
+
:vartype subcompartments: dict
|
|
297
|
+
:ivar ei_biosphere_flows: Known biosphere flows for the selected version.
|
|
298
|
+
:vartype ei_biosphere_flows: list[tuple[str, str, str]]
|
|
299
|
+
:ivar biosphere_flows_correspondence: Mapping of outdated biosphere names.
|
|
300
|
+
:vartype biosphere_flows_correspondence: dict
|
|
301
|
+
"""
|
|
302
|
+
|
|
303
|
+
def __init__(
|
|
304
|
+
self, filepath: str, ecoinvent_version: str = "3.9", db_name: str = None
|
|
305
|
+
):
|
|
306
|
+
"""Initialise the converter and load the SimaPro inventory.
|
|
307
|
+
|
|
308
|
+
:param filepath: Path to the SimaPro CSV export to convert.
|
|
309
|
+
:type filepath: str
|
|
310
|
+
:param ecoinvent_version: Ecoinvent version that should be used when
|
|
311
|
+
reconciling biosphere flows.
|
|
312
|
+
:type ecoinvent_version: str
|
|
313
|
+
:param db_name: Optional Brightway database name override.
|
|
314
|
+
:type db_name: str | None
|
|
315
|
+
:raises FileNotFoundError: If the provided CSV file cannot be found.
|
|
316
|
+
"""
|
|
317
|
+
|
|
318
|
+
logging.basicConfig(
|
|
319
|
+
level=logging.DEBUG,
|
|
320
|
+
filename="brightpath.log", # Log file to save the entries
|
|
321
|
+
filemode="a", # Append to the log file if it exists, 'w' to overwrite
|
|
322
|
+
format="%(asctime)s - %(levelname)s - %(module)s - %(message)s",
|
|
323
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
if not Path(filepath).exists():
|
|
327
|
+
raise FileNotFoundError(f"File {filepath} not found.")
|
|
328
|
+
|
|
329
|
+
self.filepath = Path(check_simapro_inventory(filepath))
|
|
330
|
+
self.i = bw2io.SimaProCSVImporter(filepath=self.filepath, name=db_name)
|
|
331
|
+
self.db_name = db_name or self.filepath.stem
|
|
332
|
+
self.i = bw2io.SimaProCSVImporter(filepath=self.filepath, name=self.db_name)
|
|
333
|
+
self.i.apply_strategies()
|
|
334
|
+
self.i.data = remove_duplicates(self.i.data)
|
|
335
|
+
|
|
336
|
+
self.ecoinvent_version = ecoinvent_version
|
|
337
|
+
self.biosphere = get_simapro_biosphere()
|
|
338
|
+
self.technosphere = get_simapro_technosphere()
|
|
339
|
+
self.subcompartments = get_simapro_subcompartments()
|
|
340
|
+
|
|
341
|
+
self.ei_biosphere_flows = load_ei_biosphere_flows()
|
|
342
|
+
self.biosphere_flows_correspondence = load_biosphere_correspondence()
|
|
343
|
+
|
|
344
|
+
self.i.db_name = self.db_name
|
|
345
|
+
|
|
346
|
+
def check_database_name(self):
|
|
347
|
+
"""Ensure exchanges reference the correct Brightway database name."""
|
|
348
|
+
for act in self.i.data:
|
|
349
|
+
act["database"] = self.i.db_name
|
|
350
|
+
|
|
351
|
+
for exc in act["exchanges"]:
|
|
352
|
+
if exc.get("type") in ["production", "technosphere"]:
|
|
353
|
+
if "input" in exc:
|
|
354
|
+
if exc["input"][0] is None:
|
|
355
|
+
exc["input"] = (self.i.db_name, exc["input"][1])
|
|
356
|
+
|
|
357
|
+
def convert_to_brightway(self):
|
|
358
|
+
"""Convert the imported SimaPro data into Brightway inventories."""
|
|
359
|
+
print("- format exchanges")
|
|
360
|
+
internal_datasets = []
|
|
361
|
+
for ds in self.i.data:
|
|
362
|
+
|
|
363
|
+
if "Comment" in ds.get("simapro metadata", {}).keys():
|
|
364
|
+
# rename "Comment" to "comment"
|
|
365
|
+
ds["comment"] = ds["simapro metadata"]["Comment"]
|
|
366
|
+
del ds["simapro metadata"]["Comment"]
|
|
367
|
+
|
|
368
|
+
ds["name"], ds["reference product"], ds["location"] = (
|
|
369
|
+
format_technosphere_exchange(ds["name"])
|
|
370
|
+
)
|
|
371
|
+
internal_datasets.append(
|
|
372
|
+
(ds["name"], ds["reference product"], ds["location"])
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
for exc in ds["exchanges"]:
|
|
376
|
+
exc["simapro name"] = exc["name"]
|
|
377
|
+
|
|
378
|
+
if exc["type"] == "production":
|
|
379
|
+
exc["name"] = ds["name"]
|
|
380
|
+
exc["product"] = ds["reference product"]
|
|
381
|
+
exc["reference product"] = ds["reference product"]
|
|
382
|
+
exc["location"] = ds["location"]
|
|
383
|
+
|
|
384
|
+
if any(x in exc["name"] for x in WASTE_TERMS):
|
|
385
|
+
logging.info(
|
|
386
|
+
msg=f"{exc['name']} considered waste treatment "
|
|
387
|
+
f"(input amount made negative)."
|
|
388
|
+
)
|
|
389
|
+
print(
|
|
390
|
+
f"{exc['name']} considered waste treatment: sign of production exchange made negative."
|
|
391
|
+
)
|
|
392
|
+
exc["amount"] *= -1
|
|
393
|
+
|
|
394
|
+
if exc["type"] in ["technosphere", "substitution"]:
|
|
395
|
+
exc["name"], exc["product"], exc["location"] = (
|
|
396
|
+
format_technosphere_exchange(exc["name"])
|
|
397
|
+
)
|
|
398
|
+
exc["reference product"] = exc["product"]
|
|
399
|
+
|
|
400
|
+
if any(x in exc["name"] for x in WASTE_TERMS):
|
|
401
|
+
logging.info(
|
|
402
|
+
msg=f"{exc['name']} considered waste treatment "
|
|
403
|
+
f"(input amount made negative)."
|
|
404
|
+
)
|
|
405
|
+
exc["amount"] *= -1
|
|
406
|
+
|
|
407
|
+
if exc["type"] == "substitution":
|
|
408
|
+
exc["type"] = "technosphere"
|
|
409
|
+
exc["amount"] *= -1
|
|
410
|
+
|
|
411
|
+
if exc["type"] == "biosphere":
|
|
412
|
+
format_biosphere_exchange(
|
|
413
|
+
exc,
|
|
414
|
+
self.ecoinvent_version,
|
|
415
|
+
self.ei_biosphere_flows,
|
|
416
|
+
self.biosphere_flows_correspondence,
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
self.check_database_name()
|
|
420
|
+
|
|
421
|
+
print("- remove empty datasets")
|
|
422
|
+
self.remove_empty_datasets()
|
|
423
|
+
print("- remove empty exchanges")
|
|
424
|
+
self.remove_empty_exchanges()
|
|
425
|
+
print("- check inventories")
|
|
426
|
+
self.check_inventories()
|
|
427
|
+
print("Done!")
|
|
428
|
+
|
|
429
|
+
def remove_empty_datasets(self):
|
|
430
|
+
"""Remove datasets that contain no exchanges."""
|
|
431
|
+
self.i.data = [ds for ds in self.i.data if len(ds["exchanges"]) >= 1]
|
|
432
|
+
|
|
433
|
+
def remove_empty_exchanges(self):
|
|
434
|
+
"""Remove exchanges that have a zero amount."""
|
|
435
|
+
for ds in self.i.data:
|
|
436
|
+
ds["exchanges"] = [e for e in ds["exchanges"] if e["amount"] != 0.0]
|
|
437
|
+
|
|
438
|
+
def check_inventories(self):
|
|
439
|
+
"""Perform basic validation of the converted inventories."""
|
|
440
|
+
for ds in self.i.data:
|
|
441
|
+
if len([x for x in ds["exchanges"] if x["type"] == "production"]) != 1:
|
|
442
|
+
print(
|
|
443
|
+
f"WARNING: {ds['name'], ds['reference product'], ds['location']} has more"
|
|
444
|
+
f"than one production flow."
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
for e in ds["exchanges"]:
|
|
448
|
+
if e["type"] not in ["production", "technosphere", "biosphere"]:
|
|
449
|
+
print(
|
|
450
|
+
f"WARNING: {ds['name'], ds['reference product'], ds['location']} has an"
|
|
451
|
+
f"unknown flow type {e['type']}."
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
if e["type"] == "production":
|
|
455
|
+
if (e["name"], e["product"], e["location"]) != (
|
|
456
|
+
ds["name"],
|
|
457
|
+
ds["reference product"],
|
|
458
|
+
ds["location"],
|
|
459
|
+
):
|
|
460
|
+
print(
|
|
461
|
+
f"WARNING: {ds['name'], ds['reference product'], ds['location']} has an"
|
|
462
|
+
f"incorrect production flow {e}."
|
|
463
|
+
)
|