brightpath 0.0.1__tar.gz → 0.0.4__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.
Files changed (40) hide show
  1. brightpath-0.0.4/MANIFEST.in +5 -0
  2. {brightpath-0.0.1/brightpath.egg-info → brightpath-0.0.4}/PKG-INFO +24 -2
  3. {brightpath-0.0.1 → brightpath-0.0.4}/README.md +11 -1
  4. {brightpath-0.0.1 → brightpath-0.0.4}/brightpath/__init__.py +3 -3
  5. brightpath-0.0.4/brightpath/bwconverter.py +429 -0
  6. {brightpath-0.0.1 → brightpath-0.0.4}/brightpath/data/export/simapro-biosphere.json +1 -1
  7. brightpath-0.0.4/brightpath/data/export/simapro_to_brightway_biosphere_ei310.yaml +31 -0
  8. brightpath-0.0.4/brightpath/simaproconverter.py +670 -0
  9. {brightpath-0.0.1 → brightpath-0.0.4}/brightpath/utils.py +476 -519
  10. {brightpath-0.0.1 → brightpath-0.0.4/brightpath.egg-info}/PKG-INFO +24 -2
  11. {brightpath-0.0.1 → brightpath-0.0.4}/brightpath.egg-info/SOURCES.txt +6 -1
  12. brightpath-0.0.4/brightpath.egg-info/requires.txt +17 -0
  13. {brightpath-0.0.1 → brightpath-0.0.4}/pyproject.toml +33 -1
  14. {brightpath-0.0.1 → brightpath-0.0.4}/requirements.txt +1 -0
  15. brightpath-0.0.4/tests/test_bwconverter.py +210 -0
  16. brightpath-0.0.4/tests/test_regressions.py +116 -0
  17. brightpath-0.0.4/tests/test_simaproconverter.py +460 -0
  18. brightpath-0.0.4/tests/test_utils.py +311 -0
  19. brightpath-0.0.1/MANIFEST.in +0 -1
  20. brightpath-0.0.1/brightpath/bwconverter.py +0 -616
  21. brightpath-0.0.1/brightpath/simaproconverter.py +0 -463
  22. brightpath-0.0.1/brightpath.egg-info/requires.txt +0 -3
  23. {brightpath-0.0.1 → brightpath-0.0.4}/LICENSE +0 -0
  24. {brightpath-0.0.1 → brightpath-0.0.4}/brightpath/data/export/correspondence_biosphere_flows.yaml +0 -0
  25. {brightpath-0.0.1 → brightpath-0.0.4}/brightpath/data/export/ecoinvent_to_uvek_mapping.csv +0 -0
  26. {brightpath-0.0.1 → brightpath-0.0.4}/brightpath/data/export/ei_transport.csv +0 -0
  27. {brightpath-0.0.1 → brightpath-0.0.4}/brightpath/data/export/flows_biosphere_39.csv +0 -0
  28. {brightpath-0.0.1 → brightpath-0.0.4}/brightpath/data/export/simapro-technosphere-3.5.csv +0 -0
  29. {brightpath-0.0.1 → brightpath-0.0.4}/brightpath/data/export/simapro_blacklist.yaml +0 -0
  30. {brightpath-0.0.1 → brightpath-0.0.4}/brightpath/data/export/simapro_ei_exceptions.yaml +0 -0
  31. {brightpath-0.0.1 → brightpath-0.0.4}/brightpath/data/export/simapro_fields.yaml +0 -0
  32. {brightpath-0.0.1 → brightpath-0.0.4}/brightpath/data/export/simapro_headers.yaml +0 -0
  33. {brightpath-0.0.1 → brightpath-0.0.4}/brightpath/data/export/simapro_subcompartments.yaml +0 -0
  34. {brightpath-0.0.1 → brightpath-0.0.4}/brightpath/data/export/simapro_units.yaml +0 -0
  35. {brightpath-0.0.1 → brightpath-0.0.4}/brightpath/data/export/uvek_blacklist.yaml +0 -0
  36. {brightpath-0.0.1 → brightpath-0.0.4}/brightpath/data/export/uvek_conversion_factors.yaml +0 -0
  37. {brightpath-0.0.1 → brightpath-0.0.4}/brightpath/data/export/waste_exchange_names.yaml +0 -0
  38. {brightpath-0.0.1 → brightpath-0.0.4}/brightpath.egg-info/dependency_links.txt +0 -0
  39. {brightpath-0.0.1 → brightpath-0.0.4}/brightpath.egg-info/top_level.txt +0 -0
  40. {brightpath-0.0.1 → brightpath-0.0.4}/setup.cfg +0 -0
@@ -0,0 +1,5 @@
1
+ recursive-include brightpath *.py
2
+ recursive-include brightpath/data/export *.csv *.json *.yaml
3
+ global-exclude *.py[cod]
4
+ global-exclude __pycache__
5
+ global-exclude .DS_Store
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: brightpath
3
- Version: 0.0.1
3
+ Version: 0.0.4
4
4
  Summary: Convert Brightway2 LCI data to Simapro and back.
5
5
  Author-email: Romain Sacchi <romain.sacchi@psi.ch>
6
6
  License: BSD 3-Clause License
@@ -53,6 +53,18 @@ License-File: LICENSE
53
53
  Requires-Dist: bw2io
54
54
  Requires-Dist: prettytable>=3
55
55
  Requires-Dist: PyYAML>=6
56
+ Requires-Dist: XlsxWriter>=3
57
+ Provides-Extra: test
58
+ Requires-Dist: pytest; extra == "test"
59
+ Requires-Dist: pytest-cov; extra == "test"
60
+ Provides-Extra: dev
61
+ Requires-Dist: bandit; extra == "dev"
62
+ Requires-Dist: black; extra == "dev"
63
+ Requires-Dist: build; extra == "dev"
64
+ Requires-Dist: isort; extra == "dev"
65
+ Requires-Dist: pytest; extra == "dev"
66
+ Requires-Dist: pytest-cov; extra == "dev"
67
+ Requires-Dist: ruff; extra == "dev"
56
68
  Dynamic: license-file
57
69
 
58
70
  # BrightPath
@@ -125,6 +137,17 @@ converter.convert_to_brightway()
125
137
  # converter.i.write_database()
126
138
  ```
127
139
 
140
+ You can also write a Brightway Excel workbook directly, without first creating
141
+ a Brightway database:
142
+
143
+ ```python
144
+ path = converter.convert_to_brightway(
145
+ format="excel",
146
+ filename="lci-my-simapro-import.xlsx",
147
+ )
148
+ print(path)
149
+ ```
150
+
128
151
  ## Development
129
152
 
130
153
  * Source code is formatted with standard Python tools and tested with
@@ -137,4 +160,3 @@ converter.convert_to_brightway()
137
160
 
138
161
  BrightPath is distributed under the
139
162
  [BSD-3-Clause license](LICENSE).
140
-
@@ -68,6 +68,17 @@ converter.convert_to_brightway()
68
68
  # converter.i.write_database()
69
69
  ```
70
70
 
71
+ You can also write a Brightway Excel workbook directly, without first creating
72
+ a Brightway database:
73
+
74
+ ```python
75
+ path = converter.convert_to_brightway(
76
+ format="excel",
77
+ filename="lci-my-simapro-import.xlsx",
78
+ )
79
+ print(path)
80
+ ```
81
+
71
82
  ## Development
72
83
 
73
84
  * Source code is formatted with standard Python tools and tested with
@@ -80,4 +91,3 @@ converter.convert_to_brightway()
80
91
 
81
92
  BrightPath is distributed under the
82
93
  [BSD-3-Clause license](LICENSE).
83
-
@@ -1,9 +1,9 @@
1
1
  __all__ = ("BrightwayConverter", "SimaproConverter", "DATA_DIR")
2
- __version__ = (0, 0, 2)
2
+ __version__ = (0, 0, 4)
3
3
 
4
4
  from pathlib import Path
5
5
 
6
6
  DATA_DIR = Path(__file__).resolve().parent / "data"
7
7
 
8
- from .bwconverter import BrightwayConverter
9
- from .simaproconverter import SimaproConverter
8
+ from .bwconverter import BrightwayConverter # noqa: E402
9
+ from .simaproconverter import SimaproConverter # noqa: E402
@@ -0,0 +1,429 @@
1
+ """
2
+ This module contains the class BrightwayConverter, which is used to convert
3
+ Brightway2 inventories to Simapro CSV files.
4
+ """
5
+
6
+ import csv
7
+ import datetime
8
+ import logging
9
+ from copy import deepcopy
10
+ from pathlib import Path
11
+
12
+ from .utils import (
13
+ add_distri_transport,
14
+ check_exchanges_for_conversion,
15
+ collect_unused_exchanges,
16
+ convert_sd_to_sd2,
17
+ escape_spreadsheet_formula,
18
+ find_production_exchange,
19
+ flag_exchanges,
20
+ format_exchange_name,
21
+ get_biosphere_exchanges,
22
+ get_simapro_biosphere,
23
+ get_simapro_ecoinvent_blacklist,
24
+ get_simapro_fields_list,
25
+ get_simapro_headers,
26
+ get_simapro_subcompartments,
27
+ get_simapro_technosphere,
28
+ get_simapro_uncertainty_type,
29
+ get_simapro_units,
30
+ get_subcategory,
31
+ get_technosphere_exchanges,
32
+ import_bw_inventories,
33
+ is_a_waste_treatment,
34
+ is_activity_waste_treatment,
35
+ is_blacklisted,
36
+ load_inventory_metadata,
37
+ round_floats_in_string,
38
+ validate_brightway_inventory,
39
+ )
40
+
41
+ logger = logging.getLogger(__name__)
42
+
43
+
44
+ class BrightwayConverter:
45
+ """
46
+ Convert Brightway2 inventories to Simapro CSV files.
47
+ """
48
+
49
+ def __init__(
50
+ self,
51
+ filepath: str = None,
52
+ data: list = None,
53
+ metadata: str = None,
54
+ ecoinvent_version: str = "3.9",
55
+ export_dir: str = None,
56
+ ):
57
+ """
58
+ :param filepath: path to the BW inventory spreadsheet file
59
+ """
60
+ if filepath and data is not None:
61
+ raise ValueError("Provide either `filepath` or `data`, not both.")
62
+ if not filepath and data is None:
63
+ raise ValueError("Provide either `filepath` or `data`.")
64
+
65
+ self.filepath = filepath
66
+ self.inventories = import_bw_inventories(filepath) if self.filepath else deepcopy(data)
67
+ validate_brightway_inventory(self.inventories)
68
+ self.simapro_blacklist = get_simapro_ecoinvent_blacklist()
69
+ self.simapro_fields = get_simapro_fields_list()
70
+ self.simapro_units = get_simapro_units()
71
+ self.simapro_headers = get_simapro_headers()
72
+ self.simapro_technosphere = get_simapro_technosphere()
73
+ self.simapro_biosphere = get_simapro_biosphere()
74
+ self.simapro_subcompartment = get_simapro_subcompartments()
75
+ self.ei_version = ecoinvent_version
76
+ self.metadata = {}
77
+ self.unused_exchanges = []
78
+
79
+ if metadata:
80
+ self.metadata = load_inventory_metadata(metadata)
81
+
82
+ # export directory is the current working
83
+ # directory unless specified otherwise
84
+ self.export_dir = Path(export_dir) if export_dir else Path.cwd()
85
+
86
+ def format_inventories_for_simapro(self, database: str):
87
+ """
88
+ Format inventories to Simapro format.
89
+ :param database: name of the database to link to.
90
+ :return: list
91
+ """
92
+ rows = self._header_rows()
93
+ inventories = [self._prepare_activity(activity, database) for activity in self.inventories]
94
+
95
+ for activity in inventories:
96
+ rows.extend(self._format_activity_rows(activity, database))
97
+
98
+ rows.extend(self._metadata_rows())
99
+ self.unused_exchanges = collect_unused_exchanges(inventories)
100
+ if self.unused_exchanges:
101
+ logger.warning("%s exchanges were not converted.", len(self.unused_exchanges))
102
+ else:
103
+ logger.info("All exchanges have been converted.")
104
+
105
+ return rows
106
+
107
+ def _header_rows(self):
108
+ rows = [
109
+ (
110
+ [item.replace("today_date", datetime.datetime.today().strftime("%d.%m.%Y"))]
111
+ if item.startswith("{Date")
112
+ else [item]
113
+ )
114
+ for item in self.simapro_headers
115
+ ]
116
+ rows.append([])
117
+ return rows
118
+
119
+ def _metadata_rows(self):
120
+ rows = []
121
+ for field in (
122
+ "System description",
123
+ "Literature reference",
124
+ ):
125
+ if field.lower() in self.metadata:
126
+ rows.extend([[field], []])
127
+ for key, val in self.metadata[field.lower()].items():
128
+ rows.extend(
129
+ [
130
+ [key],
131
+ [val],
132
+ [],
133
+ ]
134
+ )
135
+ rows.append([])
136
+ rows.extend([["End"], []])
137
+ return rows
138
+
139
+ def _prepare_activity(self, activity: dict, database: str):
140
+ activity = deepcopy(activity)
141
+ if database == "uvek":
142
+ activity = add_distri_transport(activity)
143
+ return flag_exchanges(activity)
144
+
145
+ def _format_activity_rows(self, activity: dict, database: str):
146
+ rows = []
147
+ dataset_name = ""
148
+ is_a_waste_treatment_activity = is_activity_waste_treatment(activity, database)
149
+
150
+ for field in self.simapro_fields:
151
+ if is_a_waste_treatment_activity is True and field == "Products":
152
+ continue
153
+
154
+ if is_a_waste_treatment_activity is False and field == "Waste treatment":
155
+ continue
156
+
157
+ rows.append([field])
158
+ if field in ["Process", "End"]:
159
+ rows.append([])
160
+ continue
161
+
162
+ if field == "Process name":
163
+ dataset_name = format_exchange_name(
164
+ activity["name"], activity["reference product"], activity["location"], activity["unit"], database
165
+ )
166
+ rows.extend([[dataset_name], []])
167
+
168
+ if field == "Type":
169
+ rows.extend([["Unit process"], []])
170
+
171
+ if field == "Comment":
172
+ rows.extend([[self._comment_text(activity)], []])
173
+
174
+ if field == "Category type":
175
+ prod_exchange = find_production_exchange(activity)
176
+ rows.extend([[prod_exchange["simapro category"].split("/")[0]], []])
177
+
178
+ if field == "Geography":
179
+ rows.extend([[activity["location"]], []])
180
+
181
+ if field == "Date":
182
+ rows.extend([[f"{datetime.datetime.today():%d.%m.%Y}"], []])
183
+
184
+ if field in (
185
+ "Time period",
186
+ "Record",
187
+ "Generator",
188
+ "Cut off rules",
189
+ "Capital goods",
190
+ "Technology",
191
+ "Representativeness",
192
+ "Boundary with nature",
193
+ "Infrastructure",
194
+ "External documents",
195
+ "System description",
196
+ "Allocation rules",
197
+ "Literature references",
198
+ "Collection method",
199
+ "Data treatment",
200
+ "Verification",
201
+ ):
202
+ rows.extend(self._activity_metadata_field_rows(field, activity))
203
+
204
+ if field in (
205
+ "Final waste flows",
206
+ "Non material emission",
207
+ "Social issues",
208
+ "Economic issues",
209
+ ):
210
+ rows.append([])
211
+
212
+ if field in ("Waste treatment", "Products"):
213
+ rows.extend(self._product_or_waste_rows(field, dataset_name, activity))
214
+
215
+ if field in ["Materials/fuels", "Electricity/heat"]:
216
+ rows.extend(
217
+ self._technosphere_rows(
218
+ field,
219
+ activity,
220
+ database,
221
+ is_a_waste_treatment_activity,
222
+ )
223
+ )
224
+
225
+ if field == "Resources":
226
+ rows.extend(self._biosphere_rows(activity, database, "natural resource"))
227
+
228
+ if field.startswith("Emissions to"):
229
+ rows.extend(self._biosphere_rows(activity, database, field.split(" ")[-1].lower()))
230
+
231
+ if field == "Waste to treatment":
232
+ rows.extend(self._waste_to_treatment_rows(activity, database))
233
+
234
+ rows.append([])
235
+ return rows
236
+
237
+ def _comment_text(self, activity: dict) -> str:
238
+ string = ""
239
+ if activity.get("comment"):
240
+ string = f"{round_floats_in_string(activity['comment'])} "
241
+
242
+ if activity.get("source"):
243
+ string += f"Source: {activity['source']} "
244
+
245
+ return string.replace("\n", " ")
246
+
247
+ def _activity_metadata_field_rows(self, field: str, activity: dict):
248
+ if field.lower() in activity:
249
+ return [[activity[field.lower()]], []]
250
+
251
+ if field == "Infrastructure":
252
+ return [["No"], []]
253
+ if field == "System description" and "system description" in self.metadata:
254
+ return [[self.metadata["system description"]["name"]], []]
255
+ if field == "Literature references" and "literature reference" in self.metadata:
256
+ return [[self.metadata["literature reference"]["name"]], []]
257
+ return [["Unspecified"], []]
258
+
259
+ def _product_or_waste_rows(self, field: str, dataset_name: str, activity: dict):
260
+ prod_exchange = find_production_exchange(activity)
261
+ category = get_subcategory(prod_exchange["simapro category"])
262
+ amount = prod_exchange["amount"]
263
+ if field == "Waste treatment" and amount < 0:
264
+ amount = abs(amount)
265
+
266
+ prod_exchange["used"] = True
267
+ return [
268
+ [
269
+ dataset_name,
270
+ self.simapro_units[prod_exchange["unit"]],
271
+ "{:.3E}".format(amount),
272
+ "100",
273
+ "not defined",
274
+ category,
275
+ "not defined",
276
+ ],
277
+ [],
278
+ ]
279
+
280
+ def _technosphere_rows(self, field: str, activity: dict, database: str, is_waste_treatment_activity: bool):
281
+ rows = []
282
+ energy_units = {"megajoule", "kilowatt hour"}
283
+ want_energy = field == "Electricity/heat"
284
+
285
+ for source_exc in get_technosphere_exchanges(activity):
286
+ is_energy = source_exc["unit"] in energy_units
287
+ if is_energy != want_energy:
288
+ continue
289
+ if is_blacklisted(source_exc, database):
290
+ continue
291
+ if is_a_waste_treatment(source_exc["name"], database=database):
292
+ continue
293
+
294
+ exc = check_exchanges_for_conversion([source_exc], database)[0]
295
+ if is_waste_treatment_activity is True and exc["amount"] < 0:
296
+ exc["amount"] = abs(exc["amount"])
297
+
298
+ rows.append(self._technosphere_exchange_row(exc, database))
299
+ source_exc["used"] = True
300
+
301
+ rows.append([])
302
+ return rows
303
+
304
+ def _waste_to_treatment_rows(self, activity: dict, database: str):
305
+ rows = []
306
+ for source_exc in get_technosphere_exchanges(activity):
307
+ if is_blacklisted(source_exc, database):
308
+ continue
309
+ if not is_a_waste_treatment(source_exc["name"], database=database):
310
+ continue
311
+
312
+ exc = check_exchanges_for_conversion([source_exc], database)[0]
313
+ if exc["amount"] < 0:
314
+ exc["amount"] = abs(exc["amount"])
315
+
316
+ rows.append(self._technosphere_exchange_row(exc, database))
317
+ source_exc["used"] = True
318
+
319
+ rows.append([])
320
+ return rows
321
+
322
+ def _technosphere_exchange_row(self, exc: dict, database: str):
323
+ exchange_name = format_exchange_name(
324
+ exc["name"], exc["reference product"], exc.get("location", "GLO"), exc["unit"], database
325
+ )
326
+ u_type = get_simapro_uncertainty_type(exc.get("uncertainty type"))
327
+ return [
328
+ f"{exchange_name}",
329
+ self.simapro_units[exc["unit"]],
330
+ "{:.3E}".format(exc["amount"]),
331
+ u_type,
332
+ "{:.3E}".format(convert_sd_to_sd2(exc.get("scale", 1), u_type)),
333
+ "{:.3E}".format(exc.get("min", 0)),
334
+ "{:.3E}".format(exc.get("max", 0)),
335
+ exc.get("comment"),
336
+ ]
337
+
338
+ def _biosphere_rows(self, activity: dict, database: str, category: str):
339
+ rows = []
340
+ for source_exc in get_biosphere_exchanges(activity, category):
341
+ if is_blacklisted(source_exc, database):
342
+ continue
343
+
344
+ exc = deepcopy(source_exc)
345
+ if category != "natural resource" and exc["name"].lower() == "water":
346
+ exc["unit"] = "kilogram"
347
+ exc["amount"] *= 1000
348
+
349
+ rows.append(self._biosphere_exchange_row(exc))
350
+ source_exc["used"] = True
351
+
352
+ rows.append([])
353
+ return rows
354
+
355
+ def _biosphere_exchange_row(self, exc: dict):
356
+ u_type = get_simapro_uncertainty_type(exc.get("uncertainty type"))
357
+ return [
358
+ f"{self.simapro_biosphere.get(exc['name'], exc['name'])}",
359
+ self._subcompartment(exc),
360
+ self.simapro_units[exc["unit"]],
361
+ "{:.3E}".format(exc["amount"]),
362
+ u_type,
363
+ "{:.3E}".format(convert_sd_to_sd2(exc.get("scale", 1), u_type)),
364
+ "{:.3E}".format(exc.get("min", 0)),
365
+ "{:.3E}".format(exc.get("max", 0)),
366
+ exc.get("comment"),
367
+ ]
368
+
369
+ def _subcompartment(self, exc: dict) -> str:
370
+ if len(exc["categories"]) <= 1:
371
+ return ""
372
+
373
+ subcategory = exc["categories"][1]
374
+ if subcategory not in self.simapro_subcompartment:
375
+ raise ValueError(
376
+ f"No SimaPro subcompartment mapping for {subcategory!r} " f"on biosphere exchange {exc.get('name')!r}."
377
+ )
378
+ return self.simapro_subcompartment[subcategory]
379
+
380
+ def convert_to_simapro(self, database: str = "ecoinvent", format: str = "csv", filename: str = None) -> [str, list]:
381
+ """
382
+ Convert the inventories to Simapro CSV files.
383
+ :param database: Name of the database to link to. Default is `ecoinvent`, but can be `uvek`.
384
+ """
385
+
386
+ if database not in ("ecoinvent", "uvek"):
387
+ raise ValueError("Database must be either `ecoinvent` or `uvek`")
388
+
389
+ if format not in ("csv", "data"):
390
+ raise ValueError("Format must be either `csv` or `data`.")
391
+
392
+ data = self.format_inventories_for_simapro(database)
393
+
394
+ if format == "data":
395
+ return data
396
+
397
+ filepath = self._export_filepath(database, filename)
398
+ filepath.parent.mkdir(parents=True, exist_ok=True)
399
+
400
+ with open(filepath, "w", newline="", encoding="utf-8") as csvFile:
401
+ writer = csv.writer(csvFile, delimiter=";")
402
+ for row in data:
403
+ writer.writerow([escape_spreadsheet_formula(value) for value in row])
404
+
405
+ return f"Inventories export to: {filepath}"
406
+
407
+ def _export_filepath(self, database: str, filename: str = None) -> Path:
408
+ if filename:
409
+ filepath = Path(filename)
410
+ if filepath.suffix == "":
411
+ filepath = filepath.with_suffix(".csv")
412
+ if not filepath.is_absolute():
413
+ filepath = self.export_dir / filepath
414
+ else:
415
+ stamp = datetime.datetime.today().strftime("%Y-%m-%d_%H%M%S")
416
+ filepath = self.export_dir / f"simapro_{database}_{stamp}.csv"
417
+
418
+ if not filepath.exists():
419
+ return filepath
420
+
421
+ stem = filepath.stem
422
+ suffix = filepath.suffix
423
+ parent = filepath.parent
424
+ counter = 1
425
+ while True:
426
+ candidate = parent / f"{stem}_{counter}{suffix}"
427
+ if not candidate.exists():
428
+ return candidate
429
+ counter += 1
@@ -76,7 +76,7 @@
76
76
  ],
77
77
  [
78
78
  "air",
79
- "Carbon dioxide, biogenic",
79
+ "Carbon dioxide, land transformation",
80
80
  "Carbon dioxide, from soil or biomass stock"
81
81
  ],
82
82
  [
@@ -0,0 +1,31 @@
1
+ # Version-specific SimaPro flow names that differ from ecoinvent 3.10 Brightway
2
+ # biosphere names. Rule-based normalizers handle oxidation-state parentheses,
3
+ # water unit/location suffixes, and ", ion" suffixes before these entries are
4
+ # used as a final explicit name mapping.
5
+ ---
6
+ global:
7
+ AOX, Adsorbable Organic Halogen: AOX, Adsorbable Organic Halides
8
+ Benzene, hexachloro-: Hexachlorobenzene
9
+ BOD5 (Biological Oxygen Demand): BOD5, Biological Oxygen Demand
10
+ Carbon monoxide biogenic: Carbon monoxide, non-fossil
11
+ COD (Chemical Oxygen Demand): COD, Chemical Oxygen Demand
12
+ Nitrogen, atmospheric: Nitrogen
13
+ Transformation, to rivers, artificial: Transformation, to river, artificial
14
+ Xylene: Xylenes, unspecified
15
+
16
+ natural resource:
17
+ Argon-40/kg: Argon
18
+
19
+ air:
20
+ Chromium, ion: Chromium VI
21
+ Chromium ion: Chromium VI
22
+
23
+ soil:
24
+ Chromium, ion: Chromium VI
25
+ Chromium ion: Chromium VI
26
+
27
+ water:
28
+ Ammonium, ion: Ammonium
29
+ Ammonium ion: Ammonium
30
+ Chromium, ion: Chromium III
31
+ Chromium ion: Chromium III