hestia-earth-models 0.62.1__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/coldCarcassWeightPerHead.py +4 -2
- hestia_earth/models/cycle/coldDressedCarcassWeightPerHead.py +2 -2
- hestia_earth/models/cycle/concentrateFeed.py +3 -3
- hestia_earth/models/cycle/cycleDuration.py +7 -2
- hestia_earth/models/cycle/feedConversionRatio/feedConversionRatioNitrogen.py +2 -1
- 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/cycle/readyToCookWeightPerHead.py +2 -2
- 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/animal/pastureGrass.py +50 -40
- 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 +217 -48
- hestia_earth/models/ipcc2019/organicCarbonPerHa_utils.py +2 -6
- hestia_earth/models/ipcc2019/pastureGrass.py +43 -41
- hestia_earth/models/ipcc2019/pastureGrass_utils.py +63 -109
- hestia_earth/models/koble2014/cropResidueManagement.py +1 -1
- hestia_earth/models/linkedImpactAssessment/emissions.py +15 -14
- hestia_earth/models/mocking/search-results.json +249 -257
- 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/organicCarbonPerHa.py +1 -1
- hestia_earth/models/utils/__init__.py +1 -1
- hestia_earth/models/utils/blank_node.py +52 -9
- hestia_earth/models/utils/cycle.py +12 -12
- hestia_earth/models/utils/measurement.py +3 -3
- hestia_earth/models/utils/property.py +6 -6
- hestia_earth/models/utils/term.py +2 -1
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.3.dist-info}/METADATA +12 -12
- {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.3.dist-info}/RECORD +56 -55
- {hestia_earth_models-0.62.1.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/ipcc2019/test_organicCarbonPerHa.py +9 -20
- tests/models/ipcc2019/test_organicCarbonPerHa_tier_2_utils.py +0 -8
- tests/models/test_ecoinventV3.py +12 -0
- tests/models/test_ecoinventV3AndEmberClimate.py +5 -72
- {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.3.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.62.1.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)
|
|
@@ -42,8 +42,10 @@ def _property(value: float):
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
def _run(product: dict):
|
|
45
|
-
liveweightPerHead = get_node_property_value(MODEL, product, 'liveweightPerHead')
|
|
46
|
-
processingConversion = get_node_property_value(
|
|
45
|
+
liveweightPerHead = get_node_property_value(MODEL, product, 'liveweightPerHead', term=TERM_ID)
|
|
46
|
+
processingConversion = get_node_property_value(
|
|
47
|
+
MODEL, product, 'processingConversionLiveweightToColdCarcassWeight', term=TERM_ID
|
|
48
|
+
)
|
|
47
49
|
value = liveweightPerHead * processingConversion if all([liveweightPerHead, processingConversion]) else None
|
|
48
50
|
prop = _property(value) if value else None
|
|
49
51
|
return {**product, 'properties': product.get('properties', []) + [prop]} if prop else None
|
|
@@ -46,9 +46,9 @@ def _property(value: float):
|
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
def _run(product: dict):
|
|
49
|
-
liveweightPerHead = get_node_property_value(MODEL, product, 'liveweightPerHead')
|
|
49
|
+
liveweightPerHead = get_node_property_value(MODEL, product, 'liveweightPerHead', term=TERM_ID)
|
|
50
50
|
processingConversion = get_node_property_value(
|
|
51
|
-
MODEL, product, 'processingConversionLiveweightToColdDressedCarcassWeight'
|
|
51
|
+
MODEL, product, 'processingConversionLiveweightToColdDressedCarcassWeight', term=TERM_ID
|
|
52
52
|
)
|
|
53
53
|
value = liveweightPerHead * processingConversion if all([liveweightPerHead, processingConversion]) else None
|
|
54
54
|
prop = _property(value) if value else None
|
|
@@ -79,7 +79,7 @@ def _calculate_value(cycle: dict, product: dict, inputs: list, property_id: str,
|
|
|
79
79
|
def _calculate_default_value(cycle: dict, product: dict, inputs: list, property_id: str):
|
|
80
80
|
values = [(
|
|
81
81
|
i.get('term', {}).get('@id'),
|
|
82
|
-
get_node_property_value(MODEL, i, property_id),
|
|
82
|
+
get_node_property_value(MODEL, i, property_id, term=TERM_ID),
|
|
83
83
|
list_sum(i.get('value', []))
|
|
84
84
|
) for i in inputs]
|
|
85
85
|
return _calculate_value(cycle, product, inputs, property_id, values)
|
|
@@ -88,8 +88,8 @@ def _calculate_default_value(cycle: dict, product: dict, inputs: list, property_
|
|
|
88
88
|
def _calculate_N_value(cycle: dict, product: dict, inputs: list, property_id: str):
|
|
89
89
|
values = [(
|
|
90
90
|
i.get('term', {}).get('@id'),
|
|
91
|
-
get_node_property_value(MODEL, i, property_id) or
|
|
92
|
-
get_node_property_value(MODEL, i, 'crudeProteinContent', default=0) * 0.16,
|
|
91
|
+
get_node_property_value(MODEL, i, property_id, term=TERM_ID) or
|
|
92
|
+
get_node_property_value(MODEL, i, 'crudeProteinContent', default=0, term=TERM_ID) * 0.16,
|
|
93
93
|
list_sum(i.get('value', []))
|
|
94
94
|
) for i in inputs]
|
|
95
95
|
return _calculate_value(cycle, product, inputs, property_id, values)
|
|
@@ -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):
|
|
@@ -72,5 +72,6 @@ TERM_ID = 'feedConversionRatioNitrogen'
|
|
|
72
72
|
def run(cycle: dict, feed: float):
|
|
73
73
|
inputs = get_feed_inputs(cycle)
|
|
74
74
|
return list_sum(non_empty_list([
|
|
75
|
-
get_node_property_value_converted(MODEL, input, 'crudeProteinContent', default=0) / 6.25
|
|
75
|
+
get_node_property_value_converted(MODEL, input, 'crudeProteinContent', default=0, term=TERM_ID) / 6.25
|
|
76
|
+
for input in inputs
|
|
76
77
|
]))
|
|
@@ -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))
|
|
@@ -42,9 +42,9 @@ def _property(value: float):
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
def _run(product: dict):
|
|
45
|
-
liveweightPerHead = get_node_property_value(MODEL, product, 'liveweightPerHead')
|
|
45
|
+
liveweightPerHead = get_node_property_value(MODEL, product, 'liveweightPerHead', term=TERM_ID)
|
|
46
46
|
processingConversion = get_node_property_value(
|
|
47
|
-
MODEL, product, 'processingConversionLiveweightToReadyToCookWeight'
|
|
47
|
+
MODEL, product, 'processingConversionLiveweightToReadyToCookWeight', term=TERM_ID
|
|
48
48
|
)
|
|
49
49
|
value = liveweightPerHead * processingConversion if all([liveweightPerHead, processingConversion]) else None
|
|
50
50
|
prop = _property(value) if value else None
|
|
@@ -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()))
|