hestia-earth-models 0.70.5__py3-none-any.whl → 0.71.0__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.
- hestia_earth/models/cache_nodes.py +157 -0
- hestia_earth/models/cache_sites.py +1 -1
- hestia_earth/models/config/Cycle.json +0 -30
- hestia_earth/models/data/ecoinventV3/__init__.py +7 -5
- hestia_earth/models/ecoinventV3/__init__.py +8 -1
- hestia_earth/models/ecoinventV3AndEmberClimate/__init__.py +1 -0
- hestia_earth/models/emepEea2019/co2ToAirFuelCombustion.py +20 -15
- hestia_earth/models/emepEea2019/n2OToAirFuelCombustionDirect.py +21 -16
- hestia_earth/models/emepEea2019/noxToAirFuelCombustion.py +20 -15
- hestia_earth/models/emepEea2019/so2ToAirFuelCombustion.py +20 -15
- hestia_earth/models/emepEea2019/utils.py +73 -25
- hestia_earth/models/hestia/aboveGroundCropResidue.py +3 -3
- hestia_earth/models/hestia/management.py +12 -7
- hestia_earth/models/hestia/seed_emissions.py +25 -21
- hestia_earth/models/mocking/search-results.json +1506 -1502
- hestia_earth/models/utils/background_emissions.py +24 -0
- hestia_earth/models/utils/blank_node.py +4 -1
- hestia_earth/models/utils/pesticideAI.py +1 -1
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.70.5.dist-info → hestia_earth_models-0.71.0.dist-info}/METADATA +2 -2
- {hestia_earth_models-0.70.5.dist-info → hestia_earth_models-0.71.0.dist-info}/RECORD +31 -33
- tests/models/emepEea2019/test_co2ToAirFuelCombustion.py +1 -14
- tests/models/emepEea2019/test_n2OToAirFuelCombustionDirect.py +1 -14
- tests/models/emepEea2019/test_noxToAirFuelCombustion.py +1 -14
- tests/models/emepEea2019/test_so2ToAirFuelCombustion.py +1 -14
- tests/models/emepEea2019/test_utils.py +1 -49
- tests/models/hestia/test_management.py +2 -1
- tests/models/test_cache_nodes.py +31 -0
- hestia_earth/models/ipcc2006/co2ToAirOrganicSoilCultivation.py +0 -100
- hestia_earth/models/ipcc2006/n2OToAirOrganicSoilCultivationDirect.py +0 -99
- tests/models/ipcc2006/test_co2ToAirOrganicSoilCultivation.py +0 -49
- tests/models/ipcc2006/test_n2OToAirOrganicSoilCultivationDirect.py +0 -32
- {hestia_earth_models-0.70.5.dist-info → hestia_earth_models-0.71.0.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.70.5.dist-info → hestia_earth_models-0.71.0.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.70.5.dist-info → hestia_earth_models-0.71.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,157 @@
|
|
1
|
+
import os
|
2
|
+
from functools import reduce
|
3
|
+
from hestia_earth.schema import NodeType
|
4
|
+
from hestia_earth.utils.tools import current_time_ms, flatten
|
5
|
+
from hestia_earth.earth_engine import init_gee
|
6
|
+
|
7
|
+
from .log import logger
|
8
|
+
from .utils import CACHE_KEY
|
9
|
+
from .utils.site import years_from_cycles
|
10
|
+
from .utils.source import CACHE_SOURCES_KEY, find_sources
|
11
|
+
from .cache_sites import run as cache_sites
|
12
|
+
|
13
|
+
CACHE_RELATED_KEY = 'related'
|
14
|
+
CACHE_NESTED_KEY = 'nested'
|
15
|
+
|
16
|
+
_CACHE_BATCH_SIZE = int(os.getenv('CACHE_SITES_BATCH_SIZE', '5000'))
|
17
|
+
_ENABLE_CACHE_YEARS = os.getenv('ENABLE_CACHE_YEARS', 'true') == 'true'
|
18
|
+
_ENABLE_CACHE_RELATED_NODES = os.getenv('ENABLE_CACHE_RELATED_NODES', 'true') == 'true'
|
19
|
+
_CACHE_NODE_TYPES = [
|
20
|
+
NodeType.SITE.value,
|
21
|
+
NodeType.CYCLE.value,
|
22
|
+
NodeType.IMPACTASSESSMENT.value
|
23
|
+
]
|
24
|
+
|
25
|
+
|
26
|
+
def _pop_items(values: list, nb_items: int):
|
27
|
+
if len(values) < nb_items:
|
28
|
+
removed_items = values[:] # Get a copy of the entire array
|
29
|
+
values.clear() # Remove all items from the original array
|
30
|
+
else:
|
31
|
+
removed_items = values[:nb_items] # Get the first N items
|
32
|
+
del values[:nb_items] # Remove the first N items from the original array
|
33
|
+
|
34
|
+
return removed_items
|
35
|
+
|
36
|
+
|
37
|
+
def _filter_by_type(nodes: list, type: str): return [n for n in nodes if n.get('@type', n.get('type')) == type]
|
38
|
+
|
39
|
+
|
40
|
+
def _node_key(node: dict): return '/'.join([node.get('type', node.get('@type')), node.get('id', node.get('@id'))])
|
41
|
+
|
42
|
+
|
43
|
+
def _years_from_cycles(nodes: dict): return years_from_cycles(_filter_by_type(nodes, NodeType.CYCLE.value))
|
44
|
+
|
45
|
+
|
46
|
+
def _linked_node(data: dict): return {'type': data.get('type'), 'id': data.get('id')}
|
47
|
+
|
48
|
+
|
49
|
+
def _find_nested_nodes(data) -> list[dict]:
|
50
|
+
if isinstance(data, dict):
|
51
|
+
if data.get('type') in _CACHE_NODE_TYPES and data.get('id'):
|
52
|
+
return [_linked_node(data)]
|
53
|
+
return flatten(_find_nested_nodes(list(data.values())))
|
54
|
+
if isinstance(data, list):
|
55
|
+
return flatten(map(_find_nested_nodes, data))
|
56
|
+
return []
|
57
|
+
|
58
|
+
|
59
|
+
def _nested_nodes(node_keys: list[str]):
|
60
|
+
def exec(group: dict, node: dict):
|
61
|
+
nested_nodes = _find_nested_nodes(list(node.values()))
|
62
|
+
|
63
|
+
for nested_node in nested_nodes:
|
64
|
+
group_id = _node_key(nested_node)
|
65
|
+
group[group_id] = group.get(group_id, {})
|
66
|
+
group[group_id][CACHE_RELATED_KEY] = group.get(group_id, {}).get(CACHE_RELATED_KEY, []) + [
|
67
|
+
_linked_node(node)
|
68
|
+
]
|
69
|
+
|
70
|
+
# cache nodes that current node refers (nesting)
|
71
|
+
if group_id in node_keys:
|
72
|
+
group_id = _node_key(node)
|
73
|
+
group[group_id] = group.get(group_id, {})
|
74
|
+
group[group_id][CACHE_NESTED_KEY] = group.get(group_id, {}).get(CACHE_NESTED_KEY, []) + [
|
75
|
+
_linked_node(nested_node)
|
76
|
+
]
|
77
|
+
|
78
|
+
return group
|
79
|
+
return exec
|
80
|
+
|
81
|
+
|
82
|
+
def _cache_related_nodes(nodes: list):
|
83
|
+
# only cache nodes included in the file
|
84
|
+
nodes_keys = list(map(_node_key, nodes))
|
85
|
+
# for each node, compile list of nested nodes
|
86
|
+
nested_nodes_mapping = reduce(_nested_nodes(nodes_keys), nodes, {})
|
87
|
+
|
88
|
+
def cache_related_node(node: dict):
|
89
|
+
nodes_mapping = nested_nodes_mapping.get(_node_key(node), {})
|
90
|
+
related_nodes = nodes_mapping.get(CACHE_RELATED_KEY) or []
|
91
|
+
nested_nodes = nodes_mapping.get(CACHE_NESTED_KEY) or []
|
92
|
+
# save in cache
|
93
|
+
cached_data = node.get(CACHE_KEY, {}) | {
|
94
|
+
CACHE_RELATED_KEY: related_nodes,
|
95
|
+
CACHE_NESTED_KEY: nested_nodes
|
96
|
+
}
|
97
|
+
return node | {CACHE_KEY: cached_data}
|
98
|
+
|
99
|
+
return list(map(cache_related_node, nodes))
|
100
|
+
|
101
|
+
|
102
|
+
def _cache_sources(nodes: list):
|
103
|
+
sources = find_sources()
|
104
|
+
return [
|
105
|
+
n | ({
|
106
|
+
CACHE_KEY: n.get(CACHE_KEY, {}) | {CACHE_SOURCES_KEY: sources}
|
107
|
+
} if n.get('type', n.get('@type')) in _CACHE_NODE_TYPES else {})
|
108
|
+
for n in nodes
|
109
|
+
]
|
110
|
+
|
111
|
+
|
112
|
+
def _safe_cache_sites(sites: list, years: list):
|
113
|
+
try:
|
114
|
+
return cache_sites(sites, years)
|
115
|
+
except Exception as e:
|
116
|
+
logger.error(f"An error occured while caching nodes on EE: {str(e)}")
|
117
|
+
if 'exceeded' in str(e):
|
118
|
+
logger.debug('Fallback to caching sites one by one')
|
119
|
+
# run one by one in case the batching does not work
|
120
|
+
return flatten([cache_sites([site], years) for site in sites])
|
121
|
+
else:
|
122
|
+
raise e
|
123
|
+
|
124
|
+
|
125
|
+
def _cache_sites(nodes: list, batch_size: int = _CACHE_BATCH_SIZE):
|
126
|
+
start = current_time_ms()
|
127
|
+
|
128
|
+
# build list of nodes by key to update as sites are processed
|
129
|
+
nodes_mapping = {_node_key(n): n for n in nodes}
|
130
|
+
|
131
|
+
years = _years_from_cycles(nodes) if _ENABLE_CACHE_YEARS else []
|
132
|
+
sites = _filter_by_type(nodes, 'Site')
|
133
|
+
|
134
|
+
while len(sites) > 0:
|
135
|
+
batch_values = _pop_items(sites, batch_size)
|
136
|
+
logger.info(f"Processing {len(batch_values)} sites / {len(sites)} remaining.")
|
137
|
+
results = _safe_cache_sites(batch_values, years)
|
138
|
+
for result in results:
|
139
|
+
nodes_mapping[_node_key(result)] = result
|
140
|
+
|
141
|
+
logger.info(f"Done caching sites in {current_time_ms() - start} ms")
|
142
|
+
|
143
|
+
# replace original sites with new cached sites
|
144
|
+
return list(nodes_mapping.values())
|
145
|
+
|
146
|
+
|
147
|
+
def run(nodes: list):
|
148
|
+
init_gee()
|
149
|
+
|
150
|
+
# cache sites data
|
151
|
+
cached_nodes = _cache_sites(nodes)
|
152
|
+
|
153
|
+
# cache related nodes
|
154
|
+
cached_nodes = _cache_related_nodes(cached_nodes) if _ENABLE_CACHE_RELATED_NODES else cached_nodes
|
155
|
+
|
156
|
+
# cache sources
|
157
|
+
return _cache_sources(cached_nodes)
|
@@ -81,7 +81,7 @@ def _run_values(
|
|
81
81
|
site_cache = merge(
|
82
82
|
site.get(CACHE_KEY, {}),
|
83
83
|
{CACHE_GEOSPATIAL_KEY: cached_data},
|
84
|
-
({CACHE_YEARS_KEY: list(set(cached_value(site, CACHE_YEARS_KEY, []) + years))} if years else {})
|
84
|
+
({CACHE_YEARS_KEY: sorted(list(set(cached_value(site, CACHE_YEARS_KEY, []) + years)))} if years else {})
|
85
85
|
)
|
86
86
|
return merge(site, {CACHE_KEY: site_cache})
|
87
87
|
|
@@ -2054,36 +2054,6 @@
|
|
2054
2054
|
},
|
2055
2055
|
"stage": 2
|
2056
2056
|
},
|
2057
|
-
{
|
2058
|
-
"key": "emissions",
|
2059
|
-
"model": "ipcc2006",
|
2060
|
-
"value": "n2OToAirOrganicSoilCultivationDirect",
|
2061
|
-
"runStrategy": "add_blank_node_if_missing",
|
2062
|
-
"runArgs": {
|
2063
|
-
"runNonMeasured": true,
|
2064
|
-
"runNonAddedTerm": true
|
2065
|
-
},
|
2066
|
-
"mergeStrategy": "list",
|
2067
|
-
"mergeArgs": {
|
2068
|
-
"replaceThreshold": ["value", 0.01]
|
2069
|
-
},
|
2070
|
-
"stage": 2
|
2071
|
-
},
|
2072
|
-
{
|
2073
|
-
"key": "emissions",
|
2074
|
-
"model": "ipcc2006",
|
2075
|
-
"value": "co2ToAirOrganicSoilCultivation",
|
2076
|
-
"runStrategy": "add_blank_node_if_missing",
|
2077
|
-
"runArgs": {
|
2078
|
-
"runNonMeasured": true,
|
2079
|
-
"runNonAddedTerm": true
|
2080
|
-
},
|
2081
|
-
"mergeStrategy": "list",
|
2082
|
-
"mergeArgs": {
|
2083
|
-
"replaceThreshold": ["value", 0.01]
|
2084
|
-
},
|
2085
|
-
"stage": 2
|
2086
|
-
},
|
2087
2057
|
{
|
2088
2058
|
"key": "emissions",
|
2089
2059
|
"model": "ipcc2006",
|
@@ -5,17 +5,19 @@ from hestia_earth.utils.tools import non_empty_list
|
|
5
5
|
|
6
6
|
from hestia_earth.models.log import logger
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
_CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
|
9
|
+
_ENV_NAME = 'ECOINVENT_V3_FILEPATH'
|
10
10
|
|
11
11
|
|
12
12
|
@lru_cache()
|
13
13
|
def _get_file():
|
14
|
-
|
15
|
-
|
14
|
+
filepath = os.getenv(_ENV_NAME, f"{os.path.join(_CURRENT_DIR, 'ecoinventV3_excerpt')}.csv")
|
15
|
+
|
16
|
+
if not os.path.exists(filepath):
|
17
|
+
logger.warning('Ecoinvent file not found. Please make sure to set env variable "%s".', _ENV_NAME)
|
16
18
|
return None
|
17
19
|
|
18
|
-
return load_lookup(filepath=
|
20
|
+
return load_lookup(filepath=filepath, keep_in_memory=True)
|
19
21
|
|
20
22
|
|
21
23
|
def ecoinventV3_emissions(ecoinventName: str):
|
@@ -5,7 +5,11 @@ from hestia_earth.utils.tools import flatten, list_sum
|
|
5
5
|
from hestia_earth.models.log import debugValues, logShouldRun, logRequirements
|
6
6
|
from hestia_earth.models.data.ecoinventV3 import ecoinventV3_emissions
|
7
7
|
from hestia_earth.models.utils.emission import _new_emission
|
8
|
-
from hestia_earth.models.utils.background_emissions import
|
8
|
+
from hestia_earth.models.utils.background_emissions import (
|
9
|
+
get_background_inputs,
|
10
|
+
no_gap_filled_background_emissions,
|
11
|
+
log_missing_emissions
|
12
|
+
)
|
9
13
|
from hestia_earth.models.utils.blank_node import group_by_keys
|
10
14
|
from hestia_earth.models.utils.pesticideAI import get_pesticides_from_inputs
|
11
15
|
from hestia_earth.models.utils.fertiliser import get_fertilisers_from_inputs
|
@@ -47,6 +51,7 @@ RETURNS = {
|
|
47
51
|
}]
|
48
52
|
}
|
49
53
|
LOOKUPS = {
|
54
|
+
"emission": "inputProductionGroupId",
|
50
55
|
"electricity": "ecoinventMapping",
|
51
56
|
"fuel": "ecoinventMapping",
|
52
57
|
"inorganicFertiliser": "ecoinventMapping",
|
@@ -97,6 +102,7 @@ def _add_emission(cycle: dict, input: dict):
|
|
97
102
|
|
98
103
|
def _run_input(cycle: dict):
|
99
104
|
no_gap_filled_background_emissions_func = no_gap_filled_background_emissions(cycle)
|
105
|
+
log_missing_emissions_func = log_missing_emissions(cycle, model=MODEL, methodTier=TIER)
|
100
106
|
|
101
107
|
def run(inputs: list):
|
102
108
|
input = inputs[0]
|
@@ -118,6 +124,7 @@ def _run_input(cycle: dict):
|
|
118
124
|
logShouldRun(cycle, MODEL, input_term_id, should_run, methodTier=TIER)
|
119
125
|
|
120
126
|
grouped_emissions = reduce(_add_emission(cycle, input), mappings, {}) if should_run else {}
|
127
|
+
log_missing_emissions_func(input_term_id, list(grouped_emissions.keys()))
|
121
128
|
return [
|
122
129
|
_emission(term_id, value * input_value, input)
|
123
130
|
for term_id, value in grouped_emissions.items()
|
@@ -118,6 +118,7 @@ def _run_input(cycle: dict):
|
|
118
118
|
has_ecoinvent_mappings=has_mappings,
|
119
119
|
ecoinvent_mappings=';'.join([v[0] for v in mappings]),
|
120
120
|
has_no_gap_filled_background_emissions=has_no_gap_filled_background_emissions,
|
121
|
+
termType_electricityFuel_complete=electricity_complete,
|
121
122
|
input_value=input_value)
|
122
123
|
|
123
124
|
should_run = all([electricity_complete, has_mappings, has_no_gap_filled_background_emissions, input_value])
|
@@ -1,8 +1,8 @@
|
|
1
1
|
from hestia_earth.schema import EmissionMethodTier
|
2
|
-
from hestia_earth.utils.tools import list_sum
|
3
2
|
|
4
|
-
from hestia_earth.models.log import logRequirements, logShouldRun
|
5
|
-
from .utils import
|
3
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
|
4
|
+
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
5
|
+
from .utils import get_fuel_inputs, group_fuel_inputs, _emission, _run_inputs
|
6
6
|
from . import MODEL
|
7
7
|
|
8
8
|
REQUIREMENTS = {
|
@@ -20,6 +20,8 @@ REQUIREMENTS = {
|
|
20
20
|
RETURNS = {
|
21
21
|
"Emission": [{
|
22
22
|
"value": "",
|
23
|
+
"inputs": "",
|
24
|
+
"operation": "",
|
23
25
|
"methodTier": "tier 1"
|
24
26
|
}]
|
25
27
|
}
|
@@ -31,23 +33,26 @@ TERM_ID = 'co2ToAirFuelCombustion'
|
|
31
33
|
TIER = EmissionMethodTier.TIER_1.value
|
32
34
|
|
33
35
|
|
34
|
-
def _run(fuel_values: list):
|
35
|
-
value = list_sum(fuel_values)
|
36
|
-
return [_emission(value=value, tier=TIER, term_id=TERM_ID)]
|
37
|
-
|
38
|
-
|
39
36
|
def _should_run(cycle: dict):
|
40
|
-
|
41
|
-
|
37
|
+
electricity_complete = _is_term_type_complete(cycle, 'electricityFuel')
|
38
|
+
fuel_inputs, valid_inputs = get_fuel_inputs(TERM_ID, cycle, LOOKUPS['fuel'])
|
42
39
|
|
43
40
|
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
44
|
-
|
41
|
+
termType_electricityFuel_complete=electricity_complete,
|
42
|
+
fuel_inputs=log_as_table(fuel_inputs))
|
45
43
|
|
46
|
-
should_run = any([
|
44
|
+
should_run = any([bool(valid_inputs), electricity_complete])
|
47
45
|
logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
|
48
|
-
return should_run,
|
46
|
+
return should_run, group_fuel_inputs(valid_inputs)
|
49
47
|
|
50
48
|
|
51
49
|
def run(cycle: dict):
|
52
|
-
should_run,
|
53
|
-
return
|
50
|
+
should_run, fuel_inputs = _should_run(cycle)
|
51
|
+
return (
|
52
|
+
[
|
53
|
+
_run_inputs(inputs, tier=TIER, term_id=TERM_ID)
|
54
|
+
for inputs in fuel_inputs.values()
|
55
|
+
] if fuel_inputs else [
|
56
|
+
_emission(value=0, tier=TIER, term_id=TERM_ID)
|
57
|
+
]
|
58
|
+
) if should_run else []
|
@@ -1,8 +1,8 @@
|
|
1
1
|
from hestia_earth.schema import EmissionMethodTier
|
2
|
-
from hestia_earth.utils.tools import list_sum
|
3
2
|
|
4
|
-
from hestia_earth.models.log import logRequirements, logShouldRun
|
5
|
-
from .utils import
|
3
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
|
4
|
+
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
5
|
+
from .utils import get_fuel_inputs, group_fuel_inputs, _emission, _run_inputs
|
6
6
|
from . import MODEL
|
7
7
|
|
8
8
|
REQUIREMENTS = {
|
@@ -10,7 +10,7 @@ REQUIREMENTS = {
|
|
10
10
|
"or": {
|
11
11
|
"inputs": [
|
12
12
|
{"@type": "Input", "value": "", "term.termType": "fuel", "optional": {
|
13
|
-
|
13
|
+
"operation": ""
|
14
14
|
}}
|
15
15
|
],
|
16
16
|
"completeness.electricityFuel": "True"
|
@@ -20,6 +20,8 @@ REQUIREMENTS = {
|
|
20
20
|
RETURNS = {
|
21
21
|
"Emission": [{
|
22
22
|
"value": "",
|
23
|
+
"inputs": "",
|
24
|
+
"operation": "",
|
23
25
|
"methodTier": "tier 1"
|
24
26
|
}]
|
25
27
|
}
|
@@ -31,23 +33,26 @@ TERM_ID = 'n2OToAirFuelCombustionDirect'
|
|
31
33
|
TIER = EmissionMethodTier.TIER_1.value
|
32
34
|
|
33
35
|
|
34
|
-
def _run(fuel_values: list):
|
35
|
-
value = list_sum(fuel_values)
|
36
|
-
return [_emission(value=value, tier=TIER, term_id=TERM_ID)]
|
37
|
-
|
38
|
-
|
39
36
|
def _should_run(cycle: dict):
|
40
|
-
|
41
|
-
|
37
|
+
electricity_complete = _is_term_type_complete(cycle, 'electricityFuel')
|
38
|
+
fuel_inputs, valid_inputs = get_fuel_inputs(TERM_ID, cycle, LOOKUPS['fuel'])
|
42
39
|
|
43
40
|
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
44
|
-
|
41
|
+
termType_electricityFuel_complete=electricity_complete,
|
42
|
+
fuel_inputs=log_as_table(fuel_inputs))
|
45
43
|
|
46
|
-
should_run = any([
|
44
|
+
should_run = any([bool(valid_inputs), electricity_complete])
|
47
45
|
logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
|
48
|
-
return should_run,
|
46
|
+
return should_run, group_fuel_inputs(valid_inputs)
|
49
47
|
|
50
48
|
|
51
49
|
def run(cycle: dict):
|
52
|
-
should_run,
|
53
|
-
return
|
50
|
+
should_run, fuel_inputs = _should_run(cycle)
|
51
|
+
return (
|
52
|
+
[
|
53
|
+
_run_inputs(inputs, tier=TIER, term_id=TERM_ID)
|
54
|
+
for inputs in fuel_inputs.values()
|
55
|
+
] if fuel_inputs else [
|
56
|
+
_emission(value=0, tier=TIER, term_id=TERM_ID)
|
57
|
+
]
|
58
|
+
) if should_run else []
|
@@ -1,8 +1,8 @@
|
|
1
1
|
from hestia_earth.schema import EmissionMethodTier
|
2
|
-
from hestia_earth.utils.tools import list_sum
|
3
2
|
|
4
|
-
from hestia_earth.models.log import logRequirements, logShouldRun
|
5
|
-
from .utils import
|
3
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
|
4
|
+
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
5
|
+
from .utils import get_fuel_inputs, group_fuel_inputs, _emission, _run_inputs
|
6
6
|
from . import MODEL
|
7
7
|
|
8
8
|
REQUIREMENTS = {
|
@@ -20,6 +20,8 @@ REQUIREMENTS = {
|
|
20
20
|
RETURNS = {
|
21
21
|
"Emission": [{
|
22
22
|
"value": "",
|
23
|
+
"inputs": "",
|
24
|
+
"operation": "",
|
23
25
|
"methodTier": "tier 1"
|
24
26
|
}]
|
25
27
|
}
|
@@ -31,23 +33,26 @@ TERM_ID = 'noxToAirFuelCombustion'
|
|
31
33
|
TIER = EmissionMethodTier.TIER_1.value
|
32
34
|
|
33
35
|
|
34
|
-
def _run(fuel_values: list):
|
35
|
-
value = list_sum(fuel_values)
|
36
|
-
return [_emission(value=value, tier=TIER, term_id=TERM_ID)]
|
37
|
-
|
38
|
-
|
39
36
|
def _should_run(cycle: dict):
|
40
|
-
|
41
|
-
|
37
|
+
electricity_complete = _is_term_type_complete(cycle, 'electricityFuel')
|
38
|
+
fuel_inputs, valid_inputs = get_fuel_inputs(TERM_ID, cycle, LOOKUPS['fuel'])
|
42
39
|
|
43
40
|
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
44
|
-
|
41
|
+
termType_electricityFuel_complete=electricity_complete,
|
42
|
+
fuel_inputs=log_as_table(fuel_inputs))
|
45
43
|
|
46
|
-
should_run = any([
|
44
|
+
should_run = any([bool(valid_inputs), electricity_complete])
|
47
45
|
logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
|
48
|
-
return should_run,
|
46
|
+
return should_run, group_fuel_inputs(valid_inputs)
|
49
47
|
|
50
48
|
|
51
49
|
def run(cycle: dict):
|
52
|
-
should_run,
|
53
|
-
return
|
50
|
+
should_run, fuel_inputs = _should_run(cycle)
|
51
|
+
return (
|
52
|
+
[
|
53
|
+
_run_inputs(inputs, tier=TIER, term_id=TERM_ID)
|
54
|
+
for inputs in fuel_inputs.values()
|
55
|
+
] if fuel_inputs else [
|
56
|
+
_emission(value=0, tier=TIER, term_id=TERM_ID)
|
57
|
+
]
|
58
|
+
) if should_run else []
|
@@ -1,8 +1,8 @@
|
|
1
1
|
from hestia_earth.schema import EmissionMethodTier
|
2
|
-
from hestia_earth.utils.tools import list_sum
|
3
2
|
|
4
|
-
from hestia_earth.models.log import logRequirements, logShouldRun
|
5
|
-
from .utils import
|
3
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
|
4
|
+
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
5
|
+
from .utils import get_fuel_inputs, group_fuel_inputs, _emission, _run_inputs
|
6
6
|
from . import MODEL
|
7
7
|
|
8
8
|
REQUIREMENTS = {
|
@@ -20,6 +20,8 @@ REQUIREMENTS = {
|
|
20
20
|
RETURNS = {
|
21
21
|
"Emission": [{
|
22
22
|
"value": "",
|
23
|
+
"inputs": "",
|
24
|
+
"operation": "",
|
23
25
|
"methodTier": "tier 1"
|
24
26
|
}]
|
25
27
|
}
|
@@ -31,23 +33,26 @@ TERM_ID = 'so2ToAirFuelCombustion'
|
|
31
33
|
TIER = EmissionMethodTier.TIER_1.value
|
32
34
|
|
33
35
|
|
34
|
-
def _run(fuel_values: list):
|
35
|
-
value = list_sum(fuel_values)
|
36
|
-
return [_emission(value=value, tier=TIER, term_id=TERM_ID)]
|
37
|
-
|
38
|
-
|
39
36
|
def _should_run(cycle: dict):
|
40
|
-
|
41
|
-
|
37
|
+
electricity_complete = _is_term_type_complete(cycle, 'electricityFuel')
|
38
|
+
fuel_inputs, valid_inputs = get_fuel_inputs(TERM_ID, cycle, LOOKUPS['fuel'])
|
42
39
|
|
43
40
|
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
44
|
-
|
41
|
+
termType_electricityFuel_complete=electricity_complete,
|
42
|
+
fuel_inputs=log_as_table(fuel_inputs))
|
45
43
|
|
46
|
-
should_run = any([
|
44
|
+
should_run = any([bool(valid_inputs), electricity_complete])
|
47
45
|
logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
|
48
|
-
return should_run,
|
46
|
+
return should_run, group_fuel_inputs(valid_inputs)
|
49
47
|
|
50
48
|
|
51
49
|
def run(cycle: dict):
|
52
|
-
should_run,
|
53
|
-
return
|
50
|
+
should_run, fuel_inputs = _should_run(cycle)
|
51
|
+
return (
|
52
|
+
[
|
53
|
+
_run_inputs(inputs, tier=TIER, term_id=TERM_ID)
|
54
|
+
for inputs in fuel_inputs.values()
|
55
|
+
] if fuel_inputs else [
|
56
|
+
_emission(value=0, tier=TIER, term_id=TERM_ID)
|
57
|
+
]
|
58
|
+
) if should_run else []
|
@@ -1,48 +1,96 @@
|
|
1
|
+
from functools import reduce
|
1
2
|
from hestia_earth.schema import TermTermType, SiteSiteType
|
2
3
|
from hestia_earth.utils.model import filter_list_term_type
|
3
4
|
from hestia_earth.utils.lookup import extract_grouped_data
|
4
|
-
from hestia_earth.utils.tools import list_sum, safe_parse_float
|
5
|
+
from hestia_earth.utils.tools import list_sum, safe_parse_float
|
5
6
|
|
6
7
|
from hestia_earth.models.log import logRequirements, logShouldRun
|
7
8
|
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
9
|
+
from hestia_earth.models.utils.blank_node import group_by_keys
|
8
10
|
from hestia_earth.models.utils.term import get_lookup_value
|
9
11
|
from hestia_earth.models.utils.cycle import get_animals_by_period
|
10
12
|
from hestia_earth.models.utils.emission import _new_emission
|
11
13
|
from . import MODEL
|
12
14
|
|
13
15
|
|
14
|
-
def _emission(value: float, tier: str, term_id: str):
|
16
|
+
def _emission(value: float, tier: str, term_id: str, input: dict = None, operation: dict = None):
|
15
17
|
emission = _new_emission(term_id, MODEL)
|
16
18
|
emission['value'] = [value]
|
17
19
|
emission['methodTier'] = tier
|
20
|
+
if input:
|
21
|
+
emission['inputs'] = [input]
|
22
|
+
if operation:
|
23
|
+
emission['operation'] = operation
|
18
24
|
return emission
|
19
25
|
|
20
26
|
|
21
|
-
def
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
)
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
27
|
+
def _run_inputs(inputs: list, tier: str, term_id: str):
|
28
|
+
total_value = list_sum([
|
29
|
+
(i.get('input-value') or 0) * (i.get('operation-factor') or i.get('input-default-factor') or 0)
|
30
|
+
for i in inputs
|
31
|
+
])
|
32
|
+
input_term = {
|
33
|
+
'@type': 'Term',
|
34
|
+
'@id': inputs[0].get('input-id'),
|
35
|
+
'termType': inputs[0].get('input-termType'),
|
36
|
+
'units': inputs[0].get('input-units'),
|
37
|
+
}
|
38
|
+
operation_term = {
|
39
|
+
'@type': 'Term',
|
40
|
+
'@id': inputs[0].get('operation-id'),
|
41
|
+
'termType': inputs[0].get('operation-termType'),
|
42
|
+
'units': inputs[0].get('operation-units'),
|
43
|
+
} if inputs[0].get('operation-id') else None
|
44
|
+
return _emission(
|
45
|
+
value=total_value,
|
46
|
+
tier=tier,
|
47
|
+
term_id=term_id,
|
48
|
+
input=input_term,
|
49
|
+
operation=operation_term
|
50
|
+
)
|
37
51
|
|
38
|
-
def get_fuel_values(term_id: str, cycle: dict, lookup_col: str):
|
39
|
-
inputs = filter_list_term_type(cycle.get('inputs', []), TermTermType.FUEL)
|
40
|
-
values = non_empty_list(map(_get_fuel_input_value(term_id, lookup_col), inputs))
|
41
52
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
53
|
+
def _fuel_input_data(term_id: str, lookup_col: str, input: dict):
|
54
|
+
input_term = input.get('term', {})
|
55
|
+
input_term_id = input_term.get('@id')
|
56
|
+
operation_term = input.get('operation', {})
|
57
|
+
input_value = list_sum(input.get('value', []), None)
|
58
|
+
|
59
|
+
operation_factor = extract_grouped_data(
|
60
|
+
data=get_lookup_value(operation_term, lookup_col, model=MODEL, term=term_id),
|
61
|
+
key=input_term_id
|
62
|
+
) if operation_term else None
|
63
|
+
input_factor = get_lookup_value(input_term, lookup_col, model=MODEL, term=term_id)
|
64
|
+
|
65
|
+
return {
|
66
|
+
'input-id': input_term_id,
|
67
|
+
'input-termType': input_term.get('termType'),
|
68
|
+
'input-units': input_term.get('units'),
|
69
|
+
'input-value': input_value,
|
70
|
+
'input-default-factor': safe_parse_float(input_factor, default=None),
|
71
|
+
'operation-id': operation_term.get('@id'),
|
72
|
+
'operation-termType': operation_term.get('termType'),
|
73
|
+
'operation-units': operation_term.get('units'),
|
74
|
+
'operation-factor': safe_parse_float(operation_factor, default=None)
|
75
|
+
}
|
76
|
+
|
77
|
+
|
78
|
+
def get_fuel_inputs(term_id: str, cycle: dict, lookup_col: str):
|
79
|
+
inputs = [
|
80
|
+
_fuel_input_data(term_id, lookup_col, i)
|
81
|
+
for i in filter_list_term_type(cycle.get('inputs', []), TermTermType.FUEL)
|
82
|
+
]
|
83
|
+
valid_inputs = [
|
84
|
+
i for i in inputs if all([
|
85
|
+
i.get('input-value') is not None,
|
86
|
+
(i.get('operation-factor') or i.get('input-default-factor')) is not None
|
87
|
+
])
|
88
|
+
]
|
89
|
+
return inputs, valid_inputs
|
90
|
+
|
91
|
+
|
92
|
+
def group_fuel_inputs(inputs: list):
|
93
|
+
return reduce(group_by_keys(['input-id', 'operation-id']), inputs, {}) if len(inputs) > 0 else None
|
46
94
|
|
47
95
|
|
48
96
|
def _get_emissions_factor(animal: dict, lookup_col: str) -> float:
|
@@ -105,9 +105,9 @@ def _run(cycle: dict, total_values: list):
|
|
105
105
|
term_id = model.get('product')
|
106
106
|
value = _run_model(model, cycle, total_value)
|
107
107
|
debugValues(cycle, model=MODEL, term=term_id,
|
108
|
-
|
109
|
-
|
110
|
-
|
108
|
+
total_above_ground_crop_residue=total_value,
|
109
|
+
remaining_crop_residue_value=remaining_value,
|
110
|
+
allocated_value=value)
|
111
111
|
|
112
112
|
if value == 0:
|
113
113
|
values.extend([_product(term_id, value)])
|