hestia-earth-models 0.62.2__py3-none-any.whl → 0.62.3__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.
Potentially problematic release.
This version of hestia-earth-models might be problematic. Click here for more details.
- hestia_earth/models/agribalyse2016/fuelElectricity.py +1 -1
- hestia_earth/models/cycle/animal/input/hestiaAggregatedData.py +1 -1
- hestia_earth/models/cycle/cycleDuration.py +7 -2
- hestia_earth/models/cycle/input/hestiaAggregatedData.py +1 -1
- hestia_earth/models/cycle/product/price.py +5 -1
- hestia_earth/models/cycle/product/revenue.py +6 -7
- hestia_earth/models/ecoinventV3/__init__.py +25 -52
- hestia_earth/models/ecoinventV3/utils.py +40 -0
- hestia_earth/models/ecoinventV3AndEmberClimate/__init__.py +92 -91
- hestia_earth/models/ecoinventV3AndEmberClimate/utils.py +15 -105
- hestia_earth/models/faostat2018/product/price.py +1 -2
- hestia_earth/models/geospatialDatabase/croppingIntensity.py +2 -1
- hestia_earth/models/geospatialDatabase/utils.py +1 -1
- hestia_earth/models/ipcc2019/aboveGroundCropResidueTotal.py +15 -10
- hestia_earth/models/ipcc2019/belowGroundCropResidue.py +16 -11
- hestia_earth/models/ipcc2019/carbonContent.py +1 -1
- hestia_earth/models/ipcc2019/croppingDuration.py +2 -2
- hestia_earth/models/ipcc2019/ligninContent.py +1 -1
- hestia_earth/models/ipcc2019/nitrogenContent.py +1 -1
- hestia_earth/models/ipcc2019/organicCarbonPerHa.py +3 -3
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1_utils.py +5 -5
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2_utils.py +17 -17
- hestia_earth/models/koble2014/cropResidueManagement.py +1 -1
- hestia_earth/models/linkedImpactAssessment/emissions.py +15 -14
- hestia_earth/models/mocking/search-results.json +3 -155
- hestia_earth/models/pooreNemecek2018/longFallowPeriod.py +1 -1
- hestia_earth/models/preload_requests.py +1 -1
- hestia_earth/models/requirements.py +6 -6
- hestia_earth/models/site/management.py +29 -52
- hestia_earth/models/site/organicCarbonPerHa.py +1 -1
- hestia_earth/models/utils/__init__.py +1 -1
- hestia_earth/models/utils/blank_node.py +14 -9
- hestia_earth/models/utils/cycle.py +12 -12
- hestia_earth/models/utils/measurement.py +3 -3
- hestia_earth/models/utils/site.py +0 -7
- hestia_earth/models/utils/term.py +3 -22
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.62.2.dist-info → hestia_earth_models-0.62.3.dist-info}/METADATA +12 -12
- {hestia_earth_models-0.62.2.dist-info → hestia_earth_models-0.62.3.dist-info}/RECORD +47 -46
- {hestia_earth_models-0.62.2.dist-info → hestia_earth_models-0.62.3.dist-info}/WHEEL +1 -1
- tests/models/cycle/product/test_revenue.py +0 -3
- tests/models/cycle/test_cycleDuration.py +1 -1
- tests/models/site/test_management.py +3 -31
- tests/models/test_ecoinventV3.py +12 -0
- tests/models/test_ecoinventV3AndEmberClimate.py +5 -72
- {hestia_earth_models-0.62.2.dist-info → hestia_earth_models-0.62.3.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.62.2.dist-info → hestia_earth_models-0.62.3.dist-info}/top_level.txt +0 -0
|
@@ -93,7 +93,7 @@ def _should_run_operation(cycle: dict):
|
|
|
93
93
|
|
|
94
94
|
logRequirements(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
95
95
|
has_value=has_value,
|
|
96
|
-
|
|
96
|
+
has_fuelUse_lookup_value=has_lookup_value)
|
|
97
97
|
|
|
98
98
|
should_run = all([has_value, has_lookup_value])
|
|
99
99
|
logShouldRun(cycle, MODEL, term_id, should_run, model_key=MODEL_KEY)
|
|
@@ -10,7 +10,7 @@ from hestia_earth.schema import TermTermType
|
|
|
10
10
|
from hestia_earth.utils.model import find_term_match, find_primary_product
|
|
11
11
|
from hestia_earth.utils.date import diff_in_days
|
|
12
12
|
|
|
13
|
-
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
13
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, debugValues
|
|
14
14
|
from hestia_earth.models.utils.crop import get_crop_grouping_fao
|
|
15
15
|
from . import MODEL
|
|
16
16
|
|
|
@@ -66,7 +66,12 @@ def _run_by_crop(cycle: dict):
|
|
|
66
66
|
grouping = get_crop_grouping_fao(MODEL, MODEL_KEY, product.get('term', {}))
|
|
67
67
|
is_permanent_crop = grouping == 'Permanent crops'
|
|
68
68
|
croppingIntensity = find_term_match(cycle.get('practices', []), 'croppingIntensity').get('value', [1])[0]
|
|
69
|
-
|
|
69
|
+
ratio = 1 if is_permanent_crop else croppingIntensity
|
|
70
|
+
debugValues(cycle, model=MODEL, key=MODEL_KEY, by='product',
|
|
71
|
+
crop_grouping=grouping,
|
|
72
|
+
croppingIntensity=croppingIntensity,
|
|
73
|
+
ratio=ratio)
|
|
74
|
+
return round(DEFAULT_DURATION * ratio, 3)
|
|
70
75
|
|
|
71
76
|
|
|
72
77
|
def _should_run_by_crop(cycle: dict):
|
|
@@ -42,9 +42,13 @@ def _should_run_product_by_share_0(cycle: dict, product: dict):
|
|
|
42
42
|
return should_run
|
|
43
43
|
|
|
44
44
|
|
|
45
|
+
def _should_run_product(product: dict): return product.get(MODEL_KEY) is None
|
|
46
|
+
|
|
47
|
+
|
|
45
48
|
def run(cycle: dict):
|
|
49
|
+
products = list(filter(_should_run_product, cycle.get('products', [])))
|
|
46
50
|
return non_empty_list([
|
|
47
51
|
(
|
|
48
52
|
_product(p, 0) if _should_run_product_by_share_0(cycle, p) else None
|
|
49
|
-
) for p in
|
|
53
|
+
) for p in products
|
|
50
54
|
])
|
|
@@ -53,23 +53,22 @@ def _should_run(cycle: dict):
|
|
|
53
53
|
has_yield_and_price = has_yield and has_price
|
|
54
54
|
is_yield_0 = list_sum(product.get('value', []), -1) == 0
|
|
55
55
|
is_price_0 = product.get('price', -1) == 0
|
|
56
|
-
not_already_set = MODEL_KEY not in product.keys()
|
|
57
56
|
|
|
58
57
|
logRequirements(cycle, model=MODEL, term=term_id, key=MODEL_KEY,
|
|
59
|
-
not_already_set=not_already_set,
|
|
60
58
|
has_yield_and_price=has_yield_and_price,
|
|
61
59
|
is_yield_0=is_yield_0,
|
|
62
60
|
is_price_0=is_price_0)
|
|
63
61
|
|
|
64
|
-
should_run =
|
|
65
|
-
not_already_set,
|
|
66
|
-
any([has_yield_and_price, is_yield_0, is_price_0])
|
|
67
|
-
])
|
|
62
|
+
should_run = any([has_yield_and_price, is_yield_0, is_price_0])
|
|
68
63
|
logShouldRun(cycle, MODEL, term_id, should_run, key=MODEL_KEY)
|
|
69
64
|
return should_run
|
|
70
65
|
return should_run_product
|
|
71
66
|
|
|
72
67
|
|
|
68
|
+
def _should_run_product(product: dict): return product.get(MODEL_KEY) is None
|
|
69
|
+
|
|
70
|
+
|
|
73
71
|
def run(cycle: dict):
|
|
74
|
-
products = list(filter(
|
|
72
|
+
products = list(filter(_should_run_product, cycle.get('products', [])))
|
|
73
|
+
products = list(filter(_should_run(cycle), products))
|
|
75
74
|
return non_empty_list(map(_run(cycle), products))
|
|
@@ -4,7 +4,7 @@ ecoinvent v3
|
|
|
4
4
|
This model calculates background emissions related to the production of Inputs from the ecoinvent database, version 3.
|
|
5
5
|
|
|
6
6
|
Note: to use the `ecoinventV3` model locally or in the
|
|
7
|
-
[
|
|
7
|
+
[HESTIA Community Edition](https://gitlab.com/hestia-earth/hestia-community-edition) you need a valid ecoinvent license.
|
|
8
8
|
Please contact us at community@hestia.earth for instructions to download the required file to run the model.
|
|
9
9
|
|
|
10
10
|
**Pesticide Brand Name**
|
|
@@ -15,16 +15,15 @@ default list of [Pesticide Active Ingredient](https://hestia.earth/glossary?pest
|
|
|
15
15
|
"""
|
|
16
16
|
from functools import reduce
|
|
17
17
|
from hestia_earth.schema import EmissionMethodTier
|
|
18
|
-
from hestia_earth.utils.
|
|
19
|
-
from hestia_earth.utils.tools import flatten, list_sum, non_empty_list
|
|
18
|
+
from hestia_earth.utils.tools import flatten, list_sum
|
|
20
19
|
|
|
21
|
-
from hestia_earth.models.log import debugValues,
|
|
20
|
+
from hestia_earth.models.log import debugValues, logShouldRun, logRequirements
|
|
22
21
|
from hestia_earth.models.data.ecoinventV3 import ecoinventV3_emissions
|
|
23
|
-
from hestia_earth.models.utils.term import get_lookup_value
|
|
24
22
|
from hestia_earth.models.utils.emission import _new_emission
|
|
25
23
|
from hestia_earth.models.utils.blank_node import group_by_keys
|
|
26
24
|
from hestia_earth.models.utils.pesticideAI import get_pesticides_from_inputs
|
|
27
25
|
from hestia_earth.models.utils.fertiliser import get_fertilisers_from_inputs
|
|
26
|
+
from .utils import get_background_inputs, get_input_mappings
|
|
28
27
|
|
|
29
28
|
REQUIREMENTS = {
|
|
30
29
|
"Cycle": {
|
|
@@ -96,69 +95,43 @@ def _add_emission(cycle: dict, input: dict):
|
|
|
96
95
|
def add(prev: dict, mapping: tuple):
|
|
97
96
|
ecoinventName, coefficient = mapping
|
|
98
97
|
emissions = ecoinventV3_emissions(ecoinventName)
|
|
99
|
-
for
|
|
98
|
+
for emission_term_id, value in emissions:
|
|
100
99
|
# log run on each emission so we know it did run
|
|
101
|
-
debugValues(cycle, model=MODEL, term=
|
|
100
|
+
debugValues(cycle, model=MODEL, term=emission_term_id,
|
|
102
101
|
value=value,
|
|
103
102
|
coefficient=coefficient,
|
|
104
103
|
input=input_term_id,
|
|
105
104
|
operation=operation_term_id,
|
|
106
105
|
animal=animal_term_id)
|
|
107
|
-
prev[
|
|
106
|
+
prev[emission_term_id] = prev.get(emission_term_id, 0) + (value * coefficient)
|
|
108
107
|
return prev
|
|
109
108
|
return add
|
|
110
109
|
|
|
111
110
|
|
|
112
|
-
def _get_input_mappings(cycle: dict, input: dict):
|
|
113
|
-
term = input.get('term', {})
|
|
114
|
-
term_id = term.get('@id')
|
|
115
|
-
value = get_lookup_value(term, 'ecoinventMapping', model=MODEL, term=term_id)
|
|
116
|
-
mappings = non_empty_list(value.split(';')) if value else []
|
|
117
|
-
logRequirements(cycle, model=MODEL, term=term_id,
|
|
118
|
-
mappings=';'.join(mappings))
|
|
119
|
-
return [(m.split(':')[0], float(m.split(':')[1])) for m in mappings]
|
|
120
|
-
|
|
121
|
-
|
|
122
111
|
def _run_input(cycle: dict):
|
|
123
112
|
def run(inputs: list):
|
|
124
113
|
input = inputs[0]
|
|
114
|
+
input_term_id = input.get('term', {}).get('@id')
|
|
125
115
|
input_value = list_sum(flatten(input.get('value', []) for input in inputs))
|
|
126
|
-
mappings =
|
|
127
|
-
|
|
128
|
-
should_run = len(mappings) > 0
|
|
129
|
-
logShouldRun(cycle, MODEL, term_id, should_run, methodTier=TIER)
|
|
130
|
-
grouped_emissions = reduce(_add_emission(cycle, input), mappings, {}) if should_run else {}
|
|
131
|
-
return [_emission(term_id, value * input_value, input) for term_id, value in grouped_emissions.items()]
|
|
132
|
-
return run
|
|
116
|
+
mappings = get_input_mappings(MODEL, cycle, input)
|
|
117
|
+
has_mappings = len(mappings) > 0
|
|
133
118
|
|
|
119
|
+
logRequirements(cycle, model=MODEL, term=input_term_id,
|
|
120
|
+
has_ecoinvent_mappings=has_mappings,
|
|
121
|
+
ecoinvent_mappings=';'.join([v[0] for v in mappings]))
|
|
134
122
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
not input.get('producedInCycle', False)
|
|
144
|
-
])
|
|
145
|
-
return should_run
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
def _animal_inputs(animal: dict):
|
|
149
|
-
inputs = animal.get('inputs', [])
|
|
150
|
-
return [(input | {'animal': animal.get('term', {})}) for input in inputs]
|
|
123
|
+
should_run = all([has_mappings])
|
|
124
|
+
logShouldRun(cycle, MODEL, input_term_id, should_run, methodTier=TIER)
|
|
125
|
+
grouped_emissions = reduce(_add_emission(cycle, input), mappings, {}) if should_run else {}
|
|
126
|
+
return [
|
|
127
|
+
_emission(term_id, value * input_value, input)
|
|
128
|
+
for term_id, value in grouped_emissions.items()
|
|
129
|
+
]
|
|
130
|
+
return run
|
|
151
131
|
|
|
152
132
|
|
|
153
133
|
def run(_, cycle: dict):
|
|
154
|
-
|
|
155
|
-
inputs =
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
get_pesticides_from_inputs(cycle) +
|
|
159
|
-
get_fertilisers_from_inputs(cycle)
|
|
160
|
-
)
|
|
161
|
-
inputs = list(filter(_should_run_input(cycle.get('products', [])), inputs))
|
|
162
|
-
# group inputs with same id/operation to avoid adding emissions twice
|
|
163
|
-
inputs = reduce(group_by_keys(['term', 'operation', 'animal']), inputs, {})
|
|
164
|
-
return flatten(map(_run_input(cycle), inputs.values()))
|
|
134
|
+
extra_inputs = get_pesticides_from_inputs(cycle) + get_fertilisers_from_inputs(cycle)
|
|
135
|
+
inputs = get_background_inputs(cycle, extra_inputs=extra_inputs)
|
|
136
|
+
grouped_inputs = reduce(group_by_keys(['term', 'operation', 'animal']), inputs, {})
|
|
137
|
+
return flatten(map(_run_input(cycle), grouped_inputs.values()))
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from hestia_earth.utils.model import find_term_match
|
|
2
|
+
from hestia_earth.utils.tools import list_sum, flatten, non_empty_list
|
|
3
|
+
|
|
4
|
+
from hestia_earth.models.utils.term import get_lookup_value
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _animal_inputs(animal: dict):
|
|
8
|
+
inputs = animal.get('inputs', [])
|
|
9
|
+
return [(input | {'animal': animal.get('term', {})}) for input in inputs]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _should_run_input(products: list):
|
|
13
|
+
def should_run(input: dict):
|
|
14
|
+
return all([
|
|
15
|
+
list_sum(input.get('value', [])) > 0,
|
|
16
|
+
# make sure Input is not a Product as well or we might double-count emissions
|
|
17
|
+
find_term_match(products, input.get('term', {}).get('@id'), None) is None,
|
|
18
|
+
# ignore inputs which are flagged as Product of the Cycle
|
|
19
|
+
not input.get('fromCycle', False),
|
|
20
|
+
not input.get('producedInCycle', False)
|
|
21
|
+
])
|
|
22
|
+
return should_run
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_background_inputs(cycle: dict, extra_inputs: list = []):
|
|
26
|
+
# add all the properties of some Term that inlcude others with the mapping
|
|
27
|
+
inputs = flatten(
|
|
28
|
+
cycle.get('inputs', []) +
|
|
29
|
+
list(map(_animal_inputs, cycle.get('animals', []))) +
|
|
30
|
+
extra_inputs
|
|
31
|
+
)
|
|
32
|
+
return list(filter(_should_run_input(cycle.get('products', [])), inputs))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_input_mappings(model: str, cycle: dict, input: dict):
|
|
36
|
+
term = input.get('term', {})
|
|
37
|
+
term_id = term.get('@id')
|
|
38
|
+
value = get_lookup_value(term, 'ecoinventMapping', model=model, term=term_id)
|
|
39
|
+
mappings = non_empty_list(value.split(';')) if value else []
|
|
40
|
+
return [(m.split(':')[0], float(m.split(':')[1])) for m in mappings]
|
|
@@ -1,25 +1,30 @@
|
|
|
1
1
|
"""
|
|
2
2
|
ecoinvent v3 and Ember Climate
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
This model calculates background emissions related to the production of Inputs from the ecoinvent database, version 3,
|
|
5
|
+
and using the percentage of production for each energy source from
|
|
6
|
+
[Ember](https://ember-climate.org/data-catalogue/yearly-electricity-data/).
|
|
7
|
+
|
|
8
|
+
Note: to use the `ecoinventV3AndEmberClimate` model locally or in the
|
|
9
|
+
[Hestia Community Edition](https://gitlab.com/hestia-earth/hestia-community-edition) you need a valid ecoinvent license.
|
|
10
|
+
Please contact us at community@hestia.earth for instructions to download the required file to run the model.
|
|
5
11
|
"""
|
|
6
12
|
from functools import reduce
|
|
7
|
-
from typing import Tuple
|
|
8
|
-
|
|
9
13
|
from hestia_earth.utils.tools import list_sum, flatten
|
|
10
|
-
from hestia_earth.utils.model import linked_node
|
|
11
14
|
from hestia_earth.schema import EmissionMethodTier
|
|
12
15
|
|
|
13
|
-
from hestia_earth.models.log import logShouldRun, logRequirements,
|
|
16
|
+
from hestia_earth.models.log import logShouldRun, logRequirements, debugValues
|
|
14
17
|
from hestia_earth.models.utils.emission import _new_emission
|
|
15
18
|
from hestia_earth.models.utils.blank_node import group_by_keys
|
|
16
19
|
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
|
17
20
|
from hestia_earth.models.utils.term import get_electricity_grid_mix_terms
|
|
18
|
-
from hestia_earth.models.
|
|
19
|
-
from .utils import
|
|
21
|
+
from hestia_earth.models.data.ecoinventV3 import ecoinventV3_emissions
|
|
22
|
+
from .utils import get_input_coefficient
|
|
23
|
+
from ..ecoinventV3.utils import get_background_inputs, get_input_mappings
|
|
20
24
|
|
|
21
25
|
REQUIREMENTS = {
|
|
22
26
|
"Cycle": {
|
|
27
|
+
"completeness.electricityFuel": "True",
|
|
23
28
|
"site": {
|
|
24
29
|
"@type": "Site",
|
|
25
30
|
"country": {"@type": "Term", "termType": "region"}
|
|
@@ -27,9 +32,26 @@ REQUIREMENTS = {
|
|
|
27
32
|
"inputs": [{
|
|
28
33
|
"@type": "Input",
|
|
29
34
|
"term.@id": ["electricityGridMarketMix", "electricityGridRenewableMix"],
|
|
30
|
-
"value": ""
|
|
35
|
+
"value": "> 0",
|
|
36
|
+
"none": {
|
|
37
|
+
"fromCycle": "True",
|
|
38
|
+
"producedInCycle": "True"
|
|
39
|
+
}
|
|
31
40
|
}],
|
|
32
|
-
"
|
|
41
|
+
"optional": {
|
|
42
|
+
"animals": [{
|
|
43
|
+
"@type": "Animal",
|
|
44
|
+
"inputs": [{
|
|
45
|
+
"@type": "Input",
|
|
46
|
+
"term.@id": ["electricityGridMarketMix", "electricityGridRenewableMix"],
|
|
47
|
+
"value": "> 0",
|
|
48
|
+
"none": {
|
|
49
|
+
"fromCycle": "True",
|
|
50
|
+
"producedInCycle": "True"
|
|
51
|
+
}
|
|
52
|
+
}]
|
|
53
|
+
}]
|
|
54
|
+
}
|
|
33
55
|
}
|
|
34
56
|
}
|
|
35
57
|
RETURNS = {
|
|
@@ -41,6 +63,7 @@ RETURNS = {
|
|
|
41
63
|
}]
|
|
42
64
|
}
|
|
43
65
|
LOOKUPS = {
|
|
66
|
+
"electricity": "ecoinventMapping",
|
|
44
67
|
"region-ember-energySources": "using `country`",
|
|
45
68
|
"ember-ecoinvent-mapping": ["ember", "ecoinventId", "ecoinventName"]
|
|
46
69
|
}
|
|
@@ -50,94 +73,72 @@ MODEL_KEY = 'impactAssessment' # keep to generate entry in "model-links.json"
|
|
|
50
73
|
TIER = EmissionMethodTier.BACKGROUND.value
|
|
51
74
|
|
|
52
75
|
|
|
53
|
-
def _emission(
|
|
76
|
+
def _emission(term_id: str, value: float, input: dict):
|
|
54
77
|
emission = _new_emission(term_id, MODEL)
|
|
55
78
|
emission['value'] = [value]
|
|
56
79
|
emission['methodTier'] = TIER
|
|
57
|
-
emission['inputs'] =
|
|
58
|
-
if operation:
|
|
59
|
-
emission[
|
|
80
|
+
emission['inputs'] = [input.get('term')]
|
|
81
|
+
if input.get('operation'):
|
|
82
|
+
emission['operation'] = input.get('operation')
|
|
83
|
+
if input.get('animal'):
|
|
84
|
+
emission['animals'] = [input.get('animal')]
|
|
60
85
|
return emission
|
|
61
86
|
|
|
62
87
|
|
|
63
|
-
def
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
) for electricity_grid_term in electricity_grid_terms
|
|
112
|
-
])
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def _should_run_emission(cycle: dict, electricity_grid_terms: list, term_id: str) -> Tuple[bool, list]:
|
|
116
|
-
term_type_complete = _is_term_type_complete(cycle, 'electricityFuel')
|
|
117
|
-
inputs = _grid_inputs(cycle.get('inputs', []), electricity_grid_terms)
|
|
118
|
-
has_relevant_inputs = bool(inputs)
|
|
119
|
-
has_country = bool(cycle.get("site", {}).get("country", {}))
|
|
120
|
-
has_end_date = bool(cycle.get("endDate"))
|
|
121
|
-
|
|
122
|
-
logRequirements(cycle, model=MODEL, term=term_id,
|
|
123
|
-
term_type_electricityFuel_complete=term_type_complete,
|
|
124
|
-
input_ids=log_blank_nodes_id(inputs),
|
|
125
|
-
has_relevant_inputs=has_relevant_inputs,
|
|
126
|
-
has_country=has_country,
|
|
127
|
-
has_end_date=has_end_date)
|
|
128
|
-
|
|
129
|
-
should_run = all([term_type_complete, has_relevant_inputs, has_country, has_end_date])
|
|
130
|
-
logShouldRun(cycle, MODEL, term_id, should_run, methodTier=TIER)
|
|
131
|
-
return should_run, inputs
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
def _run_emissions(cycle: dict, electricity_grid_terms: list):
|
|
135
|
-
def run_emissions_for_term(term_id: str) -> list:
|
|
136
|
-
should_run, inputs = _should_run_emission(cycle, electricity_grid_terms, term_id)
|
|
137
|
-
return _run_emission(cycle, electricity_grid_terms, inputs, term_id) if should_run else []
|
|
138
|
-
return run_emissions_for_term
|
|
88
|
+
def _add_emission(cycle: dict, input: dict):
|
|
89
|
+
input_term_id = input.get('term', {}).get('@id')
|
|
90
|
+
operation_term_id = input.get('operation', {}).get('@id')
|
|
91
|
+
animal_term_id = input.get('animal', {}).get('@id')
|
|
92
|
+
# TODO: as inputs are grouped, there could be more than 1 country
|
|
93
|
+
country_id = input.get('country', cycle.get('site', {}).get('country', {})).get('@id')
|
|
94
|
+
|
|
95
|
+
def add(prev: dict, mapping: tuple):
|
|
96
|
+
ecoinventName, _coefficient = mapping
|
|
97
|
+
# recalculate the coefficient using the country and year if it should be included
|
|
98
|
+
coefficient = get_input_coefficient(MODEL, cycle, country_id, ecoinventName) if _coefficient > 0 else 0
|
|
99
|
+
emissions = ecoinventV3_emissions(ecoinventName)
|
|
100
|
+
for emission_term_id, value in emissions:
|
|
101
|
+
# log run on each emission so we know it did run
|
|
102
|
+
debugValues(cycle, model=MODEL, term=emission_term_id,
|
|
103
|
+
value=value,
|
|
104
|
+
coefficient=coefficient,
|
|
105
|
+
input=input_term_id,
|
|
106
|
+
operation=operation_term_id,
|
|
107
|
+
animal=animal_term_id)
|
|
108
|
+
prev[emission_term_id] = prev.get(emission_term_id, 0) + (value * coefficient)
|
|
109
|
+
return prev
|
|
110
|
+
return add
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _run_input(cycle: dict):
|
|
114
|
+
electricity_complete = _is_term_type_complete(cycle, 'electricityFuel')
|
|
115
|
+
|
|
116
|
+
def run(inputs: list):
|
|
117
|
+
input = inputs[0]
|
|
118
|
+
input_value = list_sum(flatten(input.get('value', []) for input in inputs))
|
|
119
|
+
input_term_id = input.get('term', {}).get('@id')
|
|
120
|
+
mappings = get_input_mappings(MODEL, cycle, input)
|
|
121
|
+
has_mappings = len(mappings) > 0
|
|
122
|
+
|
|
123
|
+
logRequirements(cycle, model=MODEL, term=input_term_id,
|
|
124
|
+
has_ecoinvent_mappings=has_mappings,
|
|
125
|
+
ecoinvent_mappings=';'.join([v[0] for v in mappings]))
|
|
126
|
+
|
|
127
|
+
should_run = all([electricity_complete, has_mappings])
|
|
128
|
+
logShouldRun(cycle, MODEL, input_term_id, should_run, methodTier=TIER)
|
|
129
|
+
|
|
130
|
+
grouped_emissions = reduce(_add_emission(cycle, input), mappings, {}) if should_run else {}
|
|
131
|
+
return [
|
|
132
|
+
_emission(term_id, value * input_value, input)
|
|
133
|
+
for term_id, value in grouped_emissions.items()
|
|
134
|
+
]
|
|
135
|
+
return run
|
|
139
136
|
|
|
140
137
|
|
|
141
138
|
def run(_, cycle: dict):
|
|
142
|
-
|
|
143
|
-
|
|
139
|
+
terms = get_electricity_grid_mix_terms()
|
|
140
|
+
inputs = get_background_inputs(cycle)
|
|
141
|
+
# only keep the inputs matching the grid terms
|
|
142
|
+
inputs = [i for i in inputs if i.get('term', {}).get('@id') in terms]
|
|
143
|
+
grouped_inputs = reduce(group_by_keys(['term', 'operation', 'animal']), inputs, {})
|
|
144
|
+
return flatten(map(_run_input(cycle), grouped_inputs.values()))
|
|
@@ -1,116 +1,26 @@
|
|
|
1
|
-
from typing import Dict, Any, Union, List
|
|
2
|
-
|
|
3
1
|
from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name, extract_grouped_data
|
|
4
|
-
from hestia_earth.utils.tools import safe_parse_float
|
|
2
|
+
from hestia_earth.utils.tools import safe_parse_float
|
|
5
3
|
|
|
6
4
|
from hestia_earth.models.log import debugMissingLookup
|
|
7
|
-
from hestia_earth.models.
|
|
8
|
-
|
|
5
|
+
from hestia_earth.models.utils.cycle import cycle_end_year
|
|
9
6
|
|
|
10
7
|
EMBER_ECOINVENT_LOOKUP_NAME = "ember-ecoinvent-mapping.csv"
|
|
8
|
+
REGION_EMBER_SOURCES_LOOKUP_NAME = "region-ember-energySources.csv"
|
|
11
9
|
|
|
12
10
|
|
|
13
|
-
def
|
|
14
|
-
|
|
15
|
-
column = column_name(grouping)
|
|
16
|
-
data = get_table_value(lookup, 'termid', country_id, column)
|
|
17
|
-
percentage = extract_grouped_data(data, year)
|
|
18
|
-
debugMissingLookup(lookup_name, 'termid', country_id, column, percentage, year=year, term=term_id, **log_args)
|
|
19
|
-
return safe_parse_float(percentage, None)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def _convert_name(name: str) -> str: return name.replace(";", ",")
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def _zero_from_non_numeric(value: Any) -> float:
|
|
26
|
-
try:
|
|
27
|
-
return float(value)
|
|
28
|
-
except (ValueError, TypeError):
|
|
29
|
-
return 0.0
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def _extract_emission_value(value_iter: Any) -> Union[float, None]:
|
|
33
|
-
value_list = list(value_iter)
|
|
34
|
-
try:
|
|
35
|
-
if len(list(value_list)) > 0 and len(list(value_list)[0]) > 1:
|
|
36
|
-
return safe_parse_float(list(value_list)[0][1], None)
|
|
37
|
-
except ValueError:
|
|
38
|
-
return None
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def _get_emission_rate_per_source(ember_ecoinvent_mapping: Dict, emission_term_id: str) -> Dict:
|
|
42
|
-
"""
|
|
43
|
-
Returns the emissions rate in kg/kWh as a mapping indexed by ember energy source name.
|
|
44
|
-
eg: {"bioenergy": 0.8947372128046288, "coal": 0.000124581084822, ... }
|
|
45
|
-
"""
|
|
11
|
+
def get_input_coefficient(model: str, cycle: dict, country_id: str, ecoinventName: str):
|
|
12
|
+
year = cycle_end_year(cycle)
|
|
46
13
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
lambda x: x[0] == emission_term_id,
|
|
50
|
-
ecoinventV3_emissions(ecoinvent_lookup["ecoinventname"])
|
|
51
|
-
))
|
|
52
|
-
for ember_source, ecoinvent_lookup in ember_ecoinvent_mapping.items()
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def get_emission(term_id: str, country: str, year: int, energy: float, model: str) -> float:
|
|
57
|
-
"""
|
|
58
|
-
Get the <term_id> emissions in kg for the energy consumed.
|
|
59
|
-
a: ecoInventId of each source - from "ember-ecoinvent-mapping.csv"
|
|
60
|
-
b: Ember sources list - from b
|
|
61
|
-
c: Percentages per source - from region-ember-energySources.csv (country, ember-source, year:value)
|
|
62
|
-
d: Energy per source (kWh) - from energy * each of c
|
|
63
|
-
e: Emissions/kWh per source - from ecoinvent (using id from b)
|
|
64
|
-
f: Emissions per source (kg) - from e * d for each source
|
|
65
|
-
g: Total emissions - from sum(f)
|
|
66
|
-
"""
|
|
67
|
-
# a: ecoInventId of each source
|
|
14
|
+
# find the matching ember source with the ecoinventName.
|
|
15
|
+
# example: "electricity, high voltage, electricity production, hard coal" > "Coal"
|
|
68
16
|
ember_ecoinvent_lookup = download_lookup(EMBER_ECOINVENT_LOOKUP_NAME)
|
|
69
|
-
|
|
70
|
-
row["ember"].lower(): {"ecoinventid": row["ecoinventid"], "ecoinventname": _convert_name(row["ecoinventname"])}
|
|
71
|
-
for row in ember_ecoinvent_lookup
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
# b: Ember sources
|
|
75
|
-
ember_sources = list(ember_ecoinvent_mapping.keys())
|
|
76
|
-
|
|
77
|
-
# c: Percentages per source
|
|
78
|
-
percentages_per_source = {
|
|
79
|
-
source: _lookup_data(
|
|
80
|
-
term_id=source,
|
|
81
|
-
grouping=source,
|
|
82
|
-
country_id=country,
|
|
83
|
-
year=str(year),
|
|
84
|
-
lookup_name="region-ember-energySources.csv",
|
|
85
|
-
model=model
|
|
86
|
-
)
|
|
87
|
-
for source in ember_sources
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
# d: Energy per source (kWh)
|
|
91
|
-
energy_per_source = {
|
|
92
|
-
source: energy * _zero_from_non_numeric(percentage) / 100
|
|
93
|
-
for source, percentage in percentages_per_source.items()
|
|
94
|
-
}
|
|
17
|
+
source_name = get_table_value(ember_ecoinvent_lookup, column_name('ecoinventName'), ecoinventName, 'ember')
|
|
95
18
|
|
|
96
|
-
#
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
19
|
+
# find the ratio for the country / year
|
|
20
|
+
region_ember_sources_lookup = download_lookup(REGION_EMBER_SOURCES_LOOKUP_NAME)
|
|
21
|
+
data = get_table_value(region_ember_sources_lookup, 'termid', country_id, column_name(source_name))
|
|
22
|
+
percentage = extract_grouped_data(data, str(year))
|
|
23
|
+
debugMissingLookup(REGION_EMBER_SOURCES_LOOKUP_NAME,
|
|
24
|
+
'termid', country_id, source_name, percentage, year=year, model=model)
|
|
101
25
|
|
|
102
|
-
|
|
103
|
-
emissions_per_source = {
|
|
104
|
-
source: energy_per_source[source] * emission_rate_per_source[source]
|
|
105
|
-
for source in ember_sources
|
|
106
|
-
if energy_per_source[source] is not None and emission_rate_per_source[source] is not None
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
# g: Total emissions (kg of <term_id>)
|
|
110
|
-
return sum(emissions_per_source.values())
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def get_all_emission_terms() -> List[str]:
|
|
114
|
-
ember_ecoinvent_lookup = download_lookup(EMBER_ECOINVENT_LOOKUP_NAME)
|
|
115
|
-
eco_invent_name = _convert_name(ember_ecoinvent_lookup[0]["ecoinventname"])
|
|
116
|
-
return [e[0] for e in non_empty_list(ecoinventV3_emissions(eco_invent_name))]
|
|
26
|
+
return safe_parse_float(percentage, 0) / 100
|
|
@@ -157,8 +157,7 @@ def _run_by_country(cycle: dict, product: dict, country_id: str, year: int = Non
|
|
|
157
157
|
return _product(product, value / 1000 * conversion_to_number) if value is not None else None
|
|
158
158
|
|
|
159
159
|
|
|
160
|
-
def _should_run_product(product: dict):
|
|
161
|
-
return product.get(MODEL_KEY) is None
|
|
160
|
+
def _should_run_product(product: dict): return product.get(MODEL_KEY) is None
|
|
162
161
|
|
|
163
162
|
|
|
164
163
|
def run(cycle: dict):
|
|
@@ -37,7 +37,8 @@ TERM_ID = 'croppingIntensity'
|
|
|
37
37
|
|
|
38
38
|
def _practice(value: float):
|
|
39
39
|
practice = _new_practice(TERM_ID)
|
|
40
|
-
|
|
40
|
+
# force conversion to float from numpy, avoiding errors when reading it in other models
|
|
41
|
+
practice['value'] = [float(round(value, 7))]
|
|
41
42
|
return practice
|
|
42
43
|
|
|
43
44
|
|
|
@@ -152,7 +152,7 @@ def _get_cached_data(term: str, site: dict, data: dict):
|
|
|
152
152
|
|
|
153
153
|
def download(term: str, site: dict, data: dict, only_coordinates=False) -> dict:
|
|
154
154
|
"""
|
|
155
|
-
Downloads data from
|
|
155
|
+
Downloads data from HESTIA Earth Engine API.
|
|
156
156
|
|
|
157
157
|
Returns
|
|
158
158
|
-------
|