shrecc 0.1.0.dev3__tar.gz → 0.1.2.dev1__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.
- {shrecc-0.1.0.dev3/shrecc.egg-info → shrecc-0.1.2.dev1}/PKG-INFO +28 -7
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/README.md +27 -4
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/pyproject.toml +1 -3
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/__init__.py +3 -1
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/activity_mapping.py +2 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/analysis.py +3 -2
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/database.py +203 -108
- shrecc-0.1.2.dev1/shrecc/lcia.py +644 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/mapping.py +170 -35
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/pipeline.py +233 -25
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/premise_mapping.py +41 -37
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/tyndp.py +22 -4
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1/shrecc.egg-info}/PKG-INFO +28 -7
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc.egg-info/SOURCES.txt +3 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc.egg-info/requires.txt +0 -2
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/tests/test_analysis.py +4 -1
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/tests/test_database.py +120 -129
- shrecc-0.1.2.dev1/tests/test_lcia.py +331 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/tests/test_mapping.py +84 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/tests/test_pipeline.py +251 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/tests/test_premise_mapping.py +18 -0
- shrecc-0.1.2.dev1/tests/test_tyndp_paths_download.py +125 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/LICENSE +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/MANIFEST.in +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/setup.cfg +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/_legacy_database.py +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/_legacy_treatment.py +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/_legacy_tyndp.py +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/data/__init__.py +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/data/el_map_all_norm.csv +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/data/el_map_all_norm_w_ned.csv +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/data/generation_units_by_country.csv +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/data/techs_agg.json +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/data/tyndp/__init__.py +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/data/tyndp/remind-eu-topology.json +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/data/tyndp/tyndp_activities.csv +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/data/tyndp/tyndp_connections.csv +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/data/tyndp/tyndp_countries.csv +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/download.py +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/energy_charts.py +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/result_store.py +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/solver.py +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/treatment.py +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc/treatment_fiona.py +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc.egg-info/dependency_links.txt +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/shrecc.egg-info/top_level.txt +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/tests/test_architecture.py +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/tests/test_energy_charts.py +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/tests/test_energy_charts_api.py +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/tests/test_result_store.py +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/tests/test_solver.py +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/tests/test_treatment.py +0 -0
- {shrecc-0.1.0.dev3 → shrecc-0.1.2.dev1}/tests/test_tyndp_scenario.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: shrecc
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2.dev1
|
|
4
4
|
Summary: SHRECC: Smooth Hourly Resolution Electricity Consumption Calculation
|
|
5
5
|
Author-email: Sabina Bednářová <sabina.bednarova@list.lu>
|
|
6
6
|
Maintainer-email: Sabina Bednářová <sabina.bednarova@list.lu>
|
|
@@ -55,13 +55,11 @@ Requires-Dist: jupyterlab; extra == "notebooks"
|
|
|
55
55
|
Requires-Dist: jupyter-black>=0.4.0; extra == "notebooks"
|
|
56
56
|
Requires-Dist: matplotlib>=3.10.9; extra == "notebooks"
|
|
57
57
|
Provides-Extra: docs
|
|
58
|
-
Requires-Dist: furo==2024.1.29; extra == "docs"
|
|
59
58
|
Requires-Dist: myst_parser==2.0.0; extra == "docs"
|
|
60
59
|
Requires-Dist: sphinx==7.2.6; extra == "docs"
|
|
61
60
|
Requires-Dist: sphinx-autobuild>=2024.10.3; extra == "docs"
|
|
62
61
|
Requires-Dist: ipython>=8.37.0; extra == "docs"
|
|
63
62
|
Requires-Dist: sphinx-rtd-theme>=3.0.2; extra == "docs"
|
|
64
|
-
Requires-Dist: myst-parser>=2.0.0; extra == "docs"
|
|
65
63
|
Requires-Dist: sphinx-design>=0.6.1; extra == "docs"
|
|
66
64
|
Requires-Dist: sphinx-copybutton>=0.5.2; extra == "docs"
|
|
67
65
|
Requires-Dist: sphinx-autoapi>=3.5.0; extra == "docs"
|
|
@@ -85,7 +83,8 @@ scenario data for prospective years, while exposing the same workflow for both.
|
|
|
85
83
|
- Hourly production, trade, consumption-volume, and consumption-mix results.
|
|
86
84
|
- One reduced country-network solver shared by Energy Charts and TYNDP data.
|
|
87
85
|
- Historical ecoinvent allocation and prospective premise/IAM mapping.
|
|
88
|
-
-
|
|
86
|
+
- Annual or monthly Brightway inventories from range-based or explicit
|
|
87
|
+
timestamp selections, plus hourly LCIA without hourly foreground activities.
|
|
89
88
|
- Inspectable intermediate results before any Brightway database is changed.
|
|
90
89
|
- Resumable source acquisition and compressed, time-chunked result caches.
|
|
91
90
|
- Brightway 2 and 2.5 database writing through `NewDatabase`.
|
|
@@ -153,6 +152,7 @@ electricity = NewDatabase(
|
|
|
153
152
|
hour_range=[10, 14],
|
|
154
153
|
project_name="SHRECCei311",
|
|
155
154
|
source="auto",
|
|
155
|
+
inventory_resolution="annual",
|
|
156
156
|
)
|
|
157
157
|
|
|
158
158
|
electricity.create()
|
|
@@ -162,10 +162,30 @@ electricity.write()
|
|
|
162
162
|
For a multi-year run, the month/day/time selection is reused for each year and
|
|
163
163
|
`write()` creates one foreground database per year. A year suffix is added to
|
|
164
164
|
`my_db_name` automatically unless explicit year-specific names are supplied.
|
|
165
|
-
Foreground activity names and metadata also record their
|
|
165
|
+
Foreground activity names and metadata also record their modeled period.
|
|
166
166
|
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
`inventory_resolution` controls how many foreground activities are written:
|
|
168
|
+
`"annual"` creates one activity per country and year, `"monthly"` creates one
|
|
169
|
+
per country and selected calendar month. Both resolutions apply
|
|
170
|
+
`consumption_profile` within each period. Hourly inventories are not written
|
|
171
|
+
as Brightway activities.
|
|
172
|
+
|
|
173
|
+
Hourly LCIA is calculated directly from the retained hourly mixes. By default,
|
|
174
|
+
SHRECC selects every installed EF v3.1 impact category, scores each unique
|
|
175
|
+
background input once, and combines those scores with the hourly coefficients:
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
lcia_results = electricity.lcia()
|
|
179
|
+
hourly_intensity = lcia_results.hourly(2040)["intensity"]
|
|
180
|
+
annual_intensity = lcia_results.annual()
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Use `electricity.lcia(methods=..., engine="multilca")` to validate the
|
|
184
|
+
optimized calculation with composite Brightway MultiLCA functional units.
|
|
185
|
+
Neither engine writes hourly foreground activities.
|
|
186
|
+
|
|
187
|
+
Intermediate canonical results and annual/monthly mapped tables remain
|
|
188
|
+
available on the class instance through `results(year)` and `table(year)`. Use
|
|
169
189
|
`mapping_report(year)` to inspect any historical technologies assigned to a
|
|
170
190
|
country-specific high-voltage fallback activity.
|
|
171
191
|
|
|
@@ -173,6 +193,7 @@ country-specific high-voltage fallback activity.
|
|
|
173
193
|
## Contributing
|
|
174
194
|
|
|
175
195
|
Please take a look at the [DEVELOPPING.md](https://git.list.lu/shrecc_project/shrecc/-/blob/main/DEVELOPPING.md) file for details on how to contribute code to the repository.
|
|
196
|
+
Notably for the versioning.
|
|
176
197
|
|
|
177
198
|
## License
|
|
178
199
|
|
|
@@ -13,7 +13,8 @@ scenario data for prospective years, while exposing the same workflow for both.
|
|
|
13
13
|
- Hourly production, trade, consumption-volume, and consumption-mix results.
|
|
14
14
|
- One reduced country-network solver shared by Energy Charts and TYNDP data.
|
|
15
15
|
- Historical ecoinvent allocation and prospective premise/IAM mapping.
|
|
16
|
-
-
|
|
16
|
+
- Annual or monthly Brightway inventories from range-based or explicit
|
|
17
|
+
timestamp selections, plus hourly LCIA without hourly foreground activities.
|
|
17
18
|
- Inspectable intermediate results before any Brightway database is changed.
|
|
18
19
|
- Resumable source acquisition and compressed, time-chunked result caches.
|
|
19
20
|
- Brightway 2 and 2.5 database writing through `NewDatabase`.
|
|
@@ -81,6 +82,7 @@ electricity = NewDatabase(
|
|
|
81
82
|
hour_range=[10, 14],
|
|
82
83
|
project_name="SHRECCei311",
|
|
83
84
|
source="auto",
|
|
85
|
+
inventory_resolution="annual",
|
|
84
86
|
)
|
|
85
87
|
|
|
86
88
|
electricity.create()
|
|
@@ -90,10 +92,30 @@ electricity.write()
|
|
|
90
92
|
For a multi-year run, the month/day/time selection is reused for each year and
|
|
91
93
|
`write()` creates one foreground database per year. A year suffix is added to
|
|
92
94
|
`my_db_name` automatically unless explicit year-specific names are supplied.
|
|
93
|
-
Foreground activity names and metadata also record their
|
|
95
|
+
Foreground activity names and metadata also record their modeled period.
|
|
94
96
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
+
`inventory_resolution` controls how many foreground activities are written:
|
|
98
|
+
`"annual"` creates one activity per country and year, `"monthly"` creates one
|
|
99
|
+
per country and selected calendar month. Both resolutions apply
|
|
100
|
+
`consumption_profile` within each period. Hourly inventories are not written
|
|
101
|
+
as Brightway activities.
|
|
102
|
+
|
|
103
|
+
Hourly LCIA is calculated directly from the retained hourly mixes. By default,
|
|
104
|
+
SHRECC selects every installed EF v3.1 impact category, scores each unique
|
|
105
|
+
background input once, and combines those scores with the hourly coefficients:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
lcia_results = electricity.lcia()
|
|
109
|
+
hourly_intensity = lcia_results.hourly(2040)["intensity"]
|
|
110
|
+
annual_intensity = lcia_results.annual()
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Use `electricity.lcia(methods=..., engine="multilca")` to validate the
|
|
114
|
+
optimized calculation with composite Brightway MultiLCA functional units.
|
|
115
|
+
Neither engine writes hourly foreground activities.
|
|
116
|
+
|
|
117
|
+
Intermediate canonical results and annual/monthly mapped tables remain
|
|
118
|
+
available on the class instance through `results(year)` and `table(year)`. Use
|
|
97
119
|
`mapping_report(year)` to inspect any historical technologies assigned to a
|
|
98
120
|
country-specific high-voltage fallback activity.
|
|
99
121
|
|
|
@@ -101,6 +123,7 @@ country-specific high-voltage fallback activity.
|
|
|
101
123
|
## Contributing
|
|
102
124
|
|
|
103
125
|
Please take a look at the [DEVELOPPING.md](https://git.list.lu/shrecc_project/shrecc/-/blob/main/DEVELOPPING.md) file for details on how to contribute code to the repository.
|
|
126
|
+
Notably for the versioning.
|
|
104
127
|
|
|
105
128
|
## License
|
|
106
129
|
|
|
@@ -79,20 +79,18 @@ notebooks = [
|
|
|
79
79
|
"matplotlib>=3.10.9",
|
|
80
80
|
]
|
|
81
81
|
docs = [
|
|
82
|
-
"furo==2024.1.29",
|
|
83
82
|
"myst_parser==2.0.0",
|
|
84
83
|
"sphinx==7.2.6",
|
|
85
84
|
"sphinx-autobuild>=2024.10.3",
|
|
86
85
|
"ipython>=8.37.0",
|
|
87
86
|
"sphinx-rtd-theme>=3.0.2",
|
|
88
|
-
"myst-parser>=2.0.0",
|
|
89
87
|
"sphinx-design>=0.6.1",
|
|
90
88
|
"sphinx-copybutton>=0.5.2",
|
|
91
89
|
"sphinx-autoapi>=3.5.0",
|
|
92
90
|
"astroid<4",
|
|
93
91
|
"sphinx-notfound-page>=1.1.0",
|
|
94
92
|
"myst-nb>=1.2.0",
|
|
95
|
-
|
|
93
|
+
]
|
|
96
94
|
|
|
97
95
|
[tool.setuptools]
|
|
98
96
|
include-package-data = true
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
__all__ = (
|
|
9
9
|
"__version__",
|
|
10
10
|
"NewDatabase",
|
|
11
|
+
"LCIAResults",
|
|
11
12
|
"create_database",
|
|
12
13
|
"filt_cutoff",
|
|
13
14
|
"get_data",
|
|
@@ -15,6 +16,7 @@ __all__ = (
|
|
|
15
16
|
)
|
|
16
17
|
from .database import create_database, filt_cutoff
|
|
17
18
|
from .energy_charts import data_processing, get_data
|
|
19
|
+
from .lcia import LCIAResults
|
|
18
20
|
from .pipeline import NewDatabase
|
|
19
21
|
|
|
20
|
-
__version__ = "0.1.
|
|
22
|
+
__version__ = "0.1.2.dev1"
|
|
@@ -13,6 +13,7 @@ from shrecc.mapping import (
|
|
|
13
13
|
map_consumption_mix_to_ecoinvent_activities,
|
|
14
14
|
mapping_gap_to_report,
|
|
15
15
|
validate_consumption_profile,
|
|
16
|
+
validate_inventory_resolution,
|
|
16
17
|
)
|
|
17
18
|
|
|
18
19
|
__all__ = (
|
|
@@ -25,4 +26,5 @@ __all__ = (
|
|
|
25
26
|
"map_consumption_mix_to_ecoinvent_activities",
|
|
26
27
|
"mapping_gap_to_report",
|
|
27
28
|
"validate_consumption_profile",
|
|
29
|
+
"validate_inventory_resolution",
|
|
28
30
|
)
|
|
@@ -176,7 +176,8 @@ def compare_electricity_generation_mixes(
|
|
|
176
176
|
Args:
|
|
177
177
|
foreground_databases: Mapping from display label to ``{year:
|
|
178
178
|
database_name}`` mappings. Foreground databases are expected to
|
|
179
|
-
contain activities named
|
|
179
|
+
contain activities named
|
|
180
|
+
``"electricity, consumption mix, {year}"``.
|
|
180
181
|
background_databases: Mapping from year to ecoinvent or premise
|
|
181
182
|
background database name.
|
|
182
183
|
countries: Consumer-country codes to compare.
|
|
@@ -396,7 +397,7 @@ def _comparison_roots(
|
|
|
396
397
|
roots = {
|
|
397
398
|
label: resolver.find_unique(
|
|
398
399
|
databases[year],
|
|
399
|
-
f"
|
|
400
|
+
f"electricity, consumption mix, {year}",
|
|
400
401
|
country,
|
|
401
402
|
)
|
|
402
403
|
for label, databases in foreground_databases.items()
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
# Licensed under the MIT License (see LICENSE file for details).
|
|
3
3
|
# Authors: [Sabina Bednářová, Thomas Gibon]
|
|
4
4
|
|
|
5
|
+
from collections import defaultdict
|
|
6
|
+
from dataclasses import dataclass
|
|
5
7
|
from datetime import datetime
|
|
6
8
|
from importlib.resources import files
|
|
7
9
|
from packaging.version import parse as vparse
|
|
@@ -10,7 +12,6 @@ import re
|
|
|
10
12
|
|
|
11
13
|
import bw2data as bd
|
|
12
14
|
import pandas as pd
|
|
13
|
-
from bw2data.query import Filter, Query
|
|
14
15
|
|
|
15
16
|
from shrecc._legacy_database import (
|
|
16
17
|
apply_mapping,
|
|
@@ -25,6 +26,7 @@ from shrecc.mapping import (
|
|
|
25
26
|
activity_mix_to_database_table,
|
|
26
27
|
load_ecoinvent_mapping as load_mapping_data,
|
|
27
28
|
map_consumption_mix_to_ecoinvent_activities,
|
|
29
|
+
validate_inventory_resolution,
|
|
28
30
|
)
|
|
29
31
|
from shrecc.result_store import (
|
|
30
32
|
MANIFEST_FILENAME,
|
|
@@ -34,6 +36,65 @@ from shrecc.result_store import (
|
|
|
34
36
|
|
|
35
37
|
UNUSED_SOURCE = "Import balance (physical)"
|
|
36
38
|
DEFAULT_ACTIVITY_FALLBACK_LOCATIONS = ("RER", "RoW", "GLO")
|
|
39
|
+
FOREGROUND_ACTIVITY_NAME = "electricity, consumption mix"
|
|
40
|
+
FOREGROUND_REFERENCE_PRODUCT = "electricity"
|
|
41
|
+
FOREGROUND_UNIT = "kilowatt hour"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class BackgroundActivityIndex:
|
|
46
|
+
"""Indexed Brightway activity keys for one background database."""
|
|
47
|
+
|
|
48
|
+
database_name: str
|
|
49
|
+
by_name_location_unit: dict
|
|
50
|
+
by_name_location: dict
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def build_background_activity_index(eidb_name):
|
|
54
|
+
"""Load and index one Brightway background database for fast matching."""
|
|
55
|
+
database = bd.Database(eidb_name)
|
|
56
|
+
by_name_location_unit = defaultdict(list)
|
|
57
|
+
by_name_location = defaultdict(list)
|
|
58
|
+
if hasattr(database, "nodes_to_dataframe"):
|
|
59
|
+
nodes = database.nodes_to_dataframe(
|
|
60
|
+
columns=["database", "code", "name", "location", "unit"],
|
|
61
|
+
return_sorted=False,
|
|
62
|
+
)
|
|
63
|
+
records = (
|
|
64
|
+
(
|
|
65
|
+
(row.database, row.code),
|
|
66
|
+
row.name,
|
|
67
|
+
row.location,
|
|
68
|
+
row.unit,
|
|
69
|
+
)
|
|
70
|
+
for row in nodes.itertuples(index=False)
|
|
71
|
+
)
|
|
72
|
+
else:
|
|
73
|
+
records = (
|
|
74
|
+
(
|
|
75
|
+
key,
|
|
76
|
+
activity.get("name"),
|
|
77
|
+
activity.get("location"),
|
|
78
|
+
activity.get("unit"),
|
|
79
|
+
)
|
|
80
|
+
for key, activity in database.load().items()
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
for key, name, location, unit in records:
|
|
84
|
+
if name is None or location is None:
|
|
85
|
+
continue
|
|
86
|
+
by_name_location[(name, location)].append(key)
|
|
87
|
+
if unit is not None:
|
|
88
|
+
by_name_location_unit[(name, location, unit)].append(key)
|
|
89
|
+
return BackgroundActivityIndex(
|
|
90
|
+
database_name=eidb_name,
|
|
91
|
+
by_name_location_unit={
|
|
92
|
+
key: tuple(values) for key, values in by_name_location_unit.items()
|
|
93
|
+
},
|
|
94
|
+
by_name_location={
|
|
95
|
+
key: tuple(values) for key, values in by_name_location.items()
|
|
96
|
+
},
|
|
97
|
+
)
|
|
37
98
|
|
|
38
99
|
|
|
39
100
|
def filt_cutoff(
|
|
@@ -199,6 +260,7 @@ def map_known_inputs(
|
|
|
199
260
|
strict=False,
|
|
200
261
|
include_network=True,
|
|
201
262
|
fallback_locations=DEFAULT_ACTIVITY_FALLBACK_LOCATIONS,
|
|
263
|
+
background_index=None,
|
|
202
264
|
):
|
|
203
265
|
"""
|
|
204
266
|
Maps known inputs from the ecoinvent database to the filtered dataframe.
|
|
@@ -210,12 +272,19 @@ def map_known_inputs(
|
|
|
210
272
|
include_network (bool): Also resolve the fixed electricity-network inputs.
|
|
211
273
|
fallback_locations: Ordered fallback geographies tried when the requested
|
|
212
274
|
activity name and unit do not exist at the requested geography.
|
|
275
|
+
background_index: Optional prebuilt index for ``eidb_name``. Reuse this
|
|
276
|
+
when resolving several tables against the same background database.
|
|
213
277
|
|
|
214
278
|
Returns:
|
|
215
279
|
dict: A dictionary mapping known inputs to their corresponding entries in the ecoinvent database.
|
|
216
280
|
"""
|
|
217
|
-
|
|
218
|
-
|
|
281
|
+
if background_index is None:
|
|
282
|
+
background_index = build_background_activity_index(eidb_name)
|
|
283
|
+
if background_index.database_name != eidb_name:
|
|
284
|
+
raise ValueError(
|
|
285
|
+
"Background activity index belongs to "
|
|
286
|
+
f"{background_index.database_name!r}, not {eidb_name!r}"
|
|
287
|
+
)
|
|
219
288
|
known_inputs = {}
|
|
220
289
|
missing_inputs = []
|
|
221
290
|
country_to_code = {
|
|
@@ -247,20 +316,19 @@ def map_known_inputs(
|
|
|
247
316
|
for fallback_loc in fallback_locations or ()
|
|
248
317
|
if fallback_loc != loc
|
|
249
318
|
)
|
|
250
|
-
results =
|
|
319
|
+
results = ()
|
|
251
320
|
resolved_loc = loc
|
|
252
321
|
for candidate_loc in candidate_locations:
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
results = q(ei_db_data)
|
|
322
|
+
results = background_index.by_name_location_unit.get(
|
|
323
|
+
(name, candidate_loc, "kilowatt hour"),
|
|
324
|
+
(),
|
|
325
|
+
)
|
|
258
326
|
resolved_loc = candidate_loc
|
|
259
327
|
if results:
|
|
260
328
|
break
|
|
261
329
|
|
|
262
330
|
if len(results) == 1:
|
|
263
|
-
exchange =
|
|
331
|
+
exchange = results[0]
|
|
264
332
|
known_inputs[(original_loc, original_name, unit)] = exchange
|
|
265
333
|
known_inputs[(loc, name, unit)] = exchange
|
|
266
334
|
known_inputs[(resolved_loc, name, unit)] = exchange
|
|
@@ -278,14 +346,9 @@ def map_known_inputs(
|
|
|
278
346
|
for act in network:
|
|
279
347
|
loc = act["loc"]
|
|
280
348
|
name = act["name"]
|
|
281
|
-
|
|
282
|
-
filter_name = Filter("name", "is", name)
|
|
283
|
-
filter_loc = Filter("location", "is", loc)
|
|
284
|
-
q.add(filter_name)
|
|
285
|
-
q.add(filter_loc)
|
|
286
|
-
results = q(ei_db_data)
|
|
349
|
+
results = background_index.by_name_location.get((name, loc), ())
|
|
287
350
|
if len(results) == 1:
|
|
288
|
-
known_inputs_network[(loc, name)] =
|
|
351
|
+
known_inputs_network[(loc, name)] = results[0]
|
|
289
352
|
else:
|
|
290
353
|
print("Couldnt find activity:" + name)
|
|
291
354
|
missing_inputs.append((loc, name, len(results)))
|
|
@@ -359,6 +422,71 @@ def get_network_activities(eidb_name):
|
|
|
359
422
|
return network_act
|
|
360
423
|
|
|
361
424
|
|
|
425
|
+
def get_country_network_activities(eidb_name, country):
|
|
426
|
+
"""Return the fixed network exchanges included for one consumer country."""
|
|
427
|
+
specific_network = {
|
|
428
|
+
"land": (
|
|
429
|
+
"market for transmission network, electricity, high voltage "
|
|
430
|
+
"direct current land cable"
|
|
431
|
+
),
|
|
432
|
+
"subsea": (
|
|
433
|
+
"market for transmission network, electricity, high voltage "
|
|
434
|
+
"direct current subsea cable"
|
|
435
|
+
),
|
|
436
|
+
"construction": (
|
|
437
|
+
"transmission network construction, electricity, high voltage"
|
|
438
|
+
),
|
|
439
|
+
}
|
|
440
|
+
land_cable_countries = {
|
|
441
|
+
"AT",
|
|
442
|
+
"BE",
|
|
443
|
+
"DK",
|
|
444
|
+
"EE",
|
|
445
|
+
"ES",
|
|
446
|
+
"FI",
|
|
447
|
+
"FR",
|
|
448
|
+
"GR",
|
|
449
|
+
"HR",
|
|
450
|
+
"IT",
|
|
451
|
+
"LT",
|
|
452
|
+
"LV",
|
|
453
|
+
"ME",
|
|
454
|
+
"MT",
|
|
455
|
+
"NO",
|
|
456
|
+
"SE",
|
|
457
|
+
"TR",
|
|
458
|
+
"UK",
|
|
459
|
+
}
|
|
460
|
+
subsea_cable_countries = {
|
|
461
|
+
"DK",
|
|
462
|
+
"EE",
|
|
463
|
+
"ES",
|
|
464
|
+
"FI",
|
|
465
|
+
"FR",
|
|
466
|
+
"GR",
|
|
467
|
+
"HR",
|
|
468
|
+
"IT",
|
|
469
|
+
"LT",
|
|
470
|
+
"ME",
|
|
471
|
+
"MT",
|
|
472
|
+
"NO",
|
|
473
|
+
"PL",
|
|
474
|
+
"UK",
|
|
475
|
+
}
|
|
476
|
+
selected = []
|
|
477
|
+
for exchange in get_network_activities(eidb_name):
|
|
478
|
+
name = exchange["name"]
|
|
479
|
+
if name not in specific_network.values():
|
|
480
|
+
selected.append(exchange)
|
|
481
|
+
elif name == specific_network["land"] and country in land_cable_countries:
|
|
482
|
+
selected.append(exchange)
|
|
483
|
+
elif name == specific_network["subsea"] and country in subsea_cable_countries:
|
|
484
|
+
selected.append(exchange)
|
|
485
|
+
elif name == specific_network["construction"] and country == "CH":
|
|
486
|
+
selected.append(exchange)
|
|
487
|
+
return selected
|
|
488
|
+
|
|
489
|
+
|
|
362
490
|
def create_activity_dict(
|
|
363
491
|
dataframe_filt,
|
|
364
492
|
known_inputs,
|
|
@@ -367,6 +495,7 @@ def create_activity_dict(
|
|
|
367
495
|
eidb_name=None,
|
|
368
496
|
year=None,
|
|
369
497
|
consumption_profile=None,
|
|
498
|
+
inventory_resolution=None,
|
|
370
499
|
):
|
|
371
500
|
"""
|
|
372
501
|
Creates a dictionary of activities for the BW database based on the filtered dataframe and known inputs.
|
|
@@ -383,24 +512,30 @@ def create_activity_dict(
|
|
|
383
512
|
name is left unchanged to avoid repeating it.
|
|
384
513
|
consumption_profile (str, optional): Temporal weighting represented by
|
|
385
514
|
the foreground inventory, stored as activity metadata.
|
|
515
|
+
inventory_resolution (str, optional): ``"annual"`` or ``"monthly"``.
|
|
516
|
+
When omitted, time-indexed columns are treated as monthly and
|
|
517
|
+
country-only columns as annual for compatibility.
|
|
386
518
|
|
|
387
519
|
Returns:
|
|
388
520
|
dict: A dictionary containing activities to be written to the BW2 database.
|
|
389
521
|
"""
|
|
390
522
|
activities = {}
|
|
391
523
|
activity_year = int(year) if year is not None else None
|
|
524
|
+
resolution = (
|
|
525
|
+
validate_inventory_resolution(inventory_resolution)
|
|
526
|
+
if inventory_resolution is not None
|
|
527
|
+
else ("monthly" if dataframe_filt.columns.nlevels > 1 else "annual")
|
|
528
|
+
)
|
|
392
529
|
for i, col in enumerate(dataframe_filt.columns):
|
|
393
530
|
if dataframe_filt.columns.nlevels > 1:
|
|
394
531
|
time, country = col
|
|
395
|
-
|
|
396
|
-
name = f"{activity} in {country}"
|
|
397
|
-
name_contains_year = str(activity_year) in str(time)
|
|
532
|
+
period = _format_inventory_period(time, resolution)
|
|
398
533
|
else:
|
|
399
534
|
country = col
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
if
|
|
403
|
-
name = f"{name}, {
|
|
535
|
+
period = str(activity_year) if activity_year is not None else None
|
|
536
|
+
name = FOREGROUND_ACTIVITY_NAME
|
|
537
|
+
if period is not None:
|
|
538
|
+
name = f"{name}, {period}"
|
|
404
539
|
code = f"electricity {i}"
|
|
405
540
|
bd_version = bd.__version__
|
|
406
541
|
if not isinstance(bd_version, str):
|
|
@@ -414,11 +549,17 @@ def create_activity_dict(
|
|
|
414
549
|
prod_exchange_type = bd.labels.production_edge_default
|
|
415
550
|
act = {
|
|
416
551
|
"name": name,
|
|
417
|
-
"unit":
|
|
552
|
+
"unit": FOREGROUND_UNIT,
|
|
418
553
|
"code": code,
|
|
419
554
|
"location": str(country),
|
|
420
|
-
"reference product":
|
|
555
|
+
"reference product": FOREGROUND_REFERENCE_PRODUCT,
|
|
421
556
|
"type": act_type,
|
|
557
|
+
"inventory_resolution": resolution,
|
|
558
|
+
"comment": _inventory_comment(
|
|
559
|
+
resolution,
|
|
560
|
+
period,
|
|
561
|
+
consumption_profile,
|
|
562
|
+
),
|
|
422
563
|
"exchanges": [],
|
|
423
564
|
}
|
|
424
565
|
if activity_year is not None:
|
|
@@ -431,7 +572,7 @@ def create_activity_dict(
|
|
|
431
572
|
"input": (db_name, code),
|
|
432
573
|
"name": name,
|
|
433
574
|
"location": str(country),
|
|
434
|
-
"unit":
|
|
575
|
+
"unit": FOREGROUND_UNIT,
|
|
435
576
|
"amount": 1,
|
|
436
577
|
"type": prod_exchange_type,
|
|
437
578
|
}
|
|
@@ -447,91 +588,16 @@ def create_activity_dict(
|
|
|
447
588
|
"type": "technosphere",
|
|
448
589
|
}
|
|
449
590
|
act["exchanges"].append(new_exchange)
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
]
|
|
456
|
-
land_cable = [
|
|
457
|
-
"FR",
|
|
458
|
-
"IT",
|
|
459
|
-
"GR",
|
|
460
|
-
"DK",
|
|
461
|
-
"AT",
|
|
462
|
-
"BE",
|
|
463
|
-
"EE",
|
|
464
|
-
"ES",
|
|
465
|
-
"FI",
|
|
466
|
-
"HR",
|
|
467
|
-
"IT",
|
|
468
|
-
"LT",
|
|
469
|
-
"LV",
|
|
470
|
-
"ME",
|
|
471
|
-
"MT",
|
|
472
|
-
"NO",
|
|
473
|
-
"SE",
|
|
474
|
-
"TR",
|
|
475
|
-
"UK",
|
|
476
|
-
]
|
|
477
|
-
subsea_cable = [
|
|
478
|
-
"FR",
|
|
479
|
-
"IT",
|
|
480
|
-
"GR",
|
|
481
|
-
"DK",
|
|
482
|
-
"EE",
|
|
483
|
-
"ES",
|
|
484
|
-
"FI",
|
|
485
|
-
"HR",
|
|
486
|
-
"IT",
|
|
487
|
-
"LT",
|
|
488
|
-
"ME",
|
|
489
|
-
"MT",
|
|
490
|
-
"NO",
|
|
491
|
-
"PL",
|
|
492
|
-
"UK",
|
|
493
|
-
]
|
|
494
|
-
for exch_net in network:
|
|
495
|
-
if exch_net["name"] not in specific_network:
|
|
496
|
-
exchange = known_inputs_network.get((exch_net["loc"], exch_net["name"]))
|
|
497
|
-
if exchange:
|
|
498
|
-
new_exchange = {
|
|
591
|
+
for exch_net in get_country_network_activities(eidb_name or db_name, country):
|
|
592
|
+
exchange = known_inputs_network.get((exch_net["loc"], exch_net["name"]))
|
|
593
|
+
if exchange:
|
|
594
|
+
act["exchanges"].append(
|
|
595
|
+
{
|
|
499
596
|
"input": exchange,
|
|
500
597
|
"amount": float(exch_net["val"]),
|
|
501
598
|
"type": "technosphere",
|
|
502
599
|
}
|
|
503
|
-
|
|
504
|
-
if (
|
|
505
|
-
country in land_cable and exch_net["name"] == specific_network[0]
|
|
506
|
-
): # writing the land cable
|
|
507
|
-
exchange = known_inputs_network.get((exch_net["loc"], exch_net["name"]))
|
|
508
|
-
if exchange:
|
|
509
|
-
new_exchange = {
|
|
510
|
-
"input": exchange,
|
|
511
|
-
"amount": float(exch_net["val"]),
|
|
512
|
-
"type": "technosphere",
|
|
513
|
-
}
|
|
514
|
-
act["exchanges"].append(new_exchange)
|
|
515
|
-
if (
|
|
516
|
-
country in subsea_cable and exch_net["name"] == specific_network[1]
|
|
517
|
-
): # writing the subsea cable
|
|
518
|
-
exchange = known_inputs_network.get((exch_net["loc"], exch_net["name"]))
|
|
519
|
-
if exchange:
|
|
520
|
-
new_exchange = {
|
|
521
|
-
"input": exchange,
|
|
522
|
-
"amount": float(exch_net["val"]),
|
|
523
|
-
"type": "technosphere",
|
|
524
|
-
}
|
|
525
|
-
act["exchanges"].append(new_exchange)
|
|
526
|
-
if country == "CH" and exch_net["name"] == specific_network[2]:
|
|
527
|
-
exchange = known_inputs_network.get((exch_net["loc"], exch_net["name"]))
|
|
528
|
-
if exchange:
|
|
529
|
-
new_exchange = {
|
|
530
|
-
"input": exchange,
|
|
531
|
-
"amount": float(exch_net["val"]),
|
|
532
|
-
"type": "technosphere",
|
|
533
|
-
}
|
|
534
|
-
act["exchanges"].append(new_exchange)
|
|
600
|
+
)
|
|
535
601
|
activities[(db_name, code)] = act
|
|
536
602
|
return activities
|
|
537
603
|
|
|
@@ -545,6 +611,7 @@ def create_database(
|
|
|
545
611
|
strict=False,
|
|
546
612
|
year=None,
|
|
547
613
|
consumption_profile=None,
|
|
614
|
+
inventory_resolution=None,
|
|
548
615
|
):
|
|
549
616
|
"""
|
|
550
617
|
Creates an "ecoinvent-like" BW database based on a previously filtered dataframe.
|
|
@@ -563,6 +630,8 @@ def create_database(
|
|
|
563
630
|
and stored as activity metadata.
|
|
564
631
|
consumption_profile (str, optional): Temporal weighting stored as
|
|
565
632
|
foreground activity metadata.
|
|
633
|
+
inventory_resolution (str, optional): Temporal resolution stored in
|
|
634
|
+
activity names, metadata, and comments.
|
|
566
635
|
|
|
567
636
|
Returns:
|
|
568
637
|
None
|
|
@@ -585,6 +654,32 @@ def create_database(
|
|
|
585
654
|
eidb_name=eidb_name,
|
|
586
655
|
year=year,
|
|
587
656
|
consumption_profile=consumption_profile,
|
|
657
|
+
inventory_resolution=inventory_resolution,
|
|
588
658
|
)
|
|
589
659
|
elec_db = setup_database(project_name, db_name)
|
|
590
660
|
elec_db.write(activities)
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
def _format_inventory_period(time, inventory_resolution):
|
|
664
|
+
"""Format a table-column timestamp for an activity name."""
|
|
665
|
+
timestamp = pd.Timestamp(time)
|
|
666
|
+
if inventory_resolution == "monthly":
|
|
667
|
+
return timestamp.strftime("%Y-%m")
|
|
668
|
+
return str(timestamp.year)
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
def _inventory_comment(
|
|
672
|
+
inventory_resolution,
|
|
673
|
+
period,
|
|
674
|
+
consumption_profile,
|
|
675
|
+
):
|
|
676
|
+
"""Build ActivityBrowser documentation for a foreground inventory."""
|
|
677
|
+
parts = [
|
|
678
|
+
"SHRECC electricity consumption mix.",
|
|
679
|
+
f"Inventory resolution: {inventory_resolution}.",
|
|
680
|
+
]
|
|
681
|
+
if period is not None:
|
|
682
|
+
parts.append(f"Inventory period: {period}.")
|
|
683
|
+
if consumption_profile is not None:
|
|
684
|
+
parts.append(f"Consumption profile: {consumption_profile}.")
|
|
685
|
+
return " ".join(parts)
|