hestia-earth-models 0.73.8__py3-none-any.whl → 0.74.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.
Potentially problematic release.
This version of hestia-earth-models might be problematic. Click here for more details.
- hestia_earth/models/aware/scarcityWeightedWaterUse.py +7 -6
- hestia_earth/models/aware2_0/__init__.py +14 -0
- hestia_earth/models/aware2_0/scarcityWeightedWaterUse.py +115 -0
- hestia_earth/models/config/Cycle.json +5 -3
- hestia_earth/models/config/ImpactAssessment.json +1 -1
- hestia_earth/models/config/__init__.py +26 -2
- hestia_earth/models/cycle/animal/input/hestiaAggregatedData.py +2 -2
- hestia_earth/models/cycle/animal/input/properties.py +6 -5
- hestia_earth/models/cycle/animal/milkYield.py +8 -3
- hestia_earth/models/cycle/utils.py +6 -6
- hestia_earth/models/data/ecoinventV3/__init__.py +8 -26
- hestia_earth/models/ecoalimV9/cycle.py +26 -10
- hestia_earth/models/ecoalimV9/impact_assessment.py +30 -10
- hestia_earth/models/ecoalimV9/utils.py +12 -72
- hestia_earth/models/ecoinventV3/__init__.py +8 -140
- hestia_earth/models/ecoinventV3/cycle.py +140 -0
- hestia_earth/models/ecoinventV3/utils.py +28 -1
- hestia_earth/models/ecoinventV3AndEmberClimate/__init__.py +8 -137
- hestia_earth/models/ecoinventV3AndEmberClimate/cycle.py +144 -0
- hestia_earth/models/emepEea2019/utils.py +2 -3
- hestia_earth/models/environmentalFootprintV3_1/environmentalFootprintSingleOverallScore.py +5 -7
- hestia_earth/models/frischknechtEtAl2000/ionisingRadiationKbqU235Eq.py +41 -43
- hestia_earth/models/geospatialDatabase/awareWaterBasinId.py +2 -2
- hestia_earth/models/geospatialDatabase/awareWaterBasinId_v1.py +45 -0
- hestia_earth/models/hestia/default_emissions.py +5 -1
- hestia_earth/models/hestia/default_resourceUse.py +5 -1
- hestia_earth/models/hestia/landCover.py +110 -12
- hestia_earth/models/hestia/utils.py +1 -0
- hestia_earth/models/hestia/waterSalinity.py +2 -3
- hestia_earth/models/impact_assessment/emissions.py +1 -1
- hestia_earth/models/linkedImpactAssessment/emissions.py +2 -2
- hestia_earth/models/log.py +8 -3
- hestia_earth/models/mocking/search-results.json +1549 -1545
- hestia_earth/models/utils/__init__.py +3 -0
- hestia_earth/models/utils/background_emissions.py +109 -9
- hestia_earth/models/utils/blank_node.py +1 -11
- hestia_earth/models/utils/feedipedia.py +2 -2
- hestia_earth/models/utils/impact_assessment.py +1 -3
- hestia_earth/models/utils/lookup.py +1 -1
- hestia_earth/models/version.py +1 -1
- hestia_earth/orchestrator/log.py +8 -3
- {hestia_earth_models-0.73.8.dist-info → hestia_earth_models-0.74.0.dist-info}/METADATA +2 -2
- {hestia_earth_models-0.73.8.dist-info → hestia_earth_models-0.74.0.dist-info}/RECORD +58 -49
- tests/models/aware2_0/__init__.py +0 -0
- tests/models/aware2_0/test_scarcityWeightedWaterUse.py +58 -0
- tests/models/ecoinventV3/__init__.py +0 -0
- tests/models/{test_ecoinventV3.py → ecoinventV3/test_cycle.py} +5 -5
- tests/models/ecoinventV3AndEmberClimate/__init__.py +0 -0
- tests/models/{test_ecoinventV3AndEmberClimate.py → ecoinventV3AndEmberClimate/test_cycle.py} +6 -4
- tests/models/environmentalFootprintV3_1/test_environmentalFootprintSingleOverallScore.py +2 -2
- tests/models/frischknechtEtAl2000/test_ionisingRadiationKbqU235Eq.py +18 -27
- tests/models/hestia/test_landCover.py +16 -6
- tests/models/site/pre_checks/test_cache_geospatialDatabase.py +4 -4
- tests/models/test_config.py +53 -7
- tests/models/{ecoalimV9/test_utils.py → utils/test_background_emissions.py} +2 -2
- {hestia_earth_models-0.73.8.dist-info → hestia_earth_models-0.74.0.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.73.8.dist-info → hestia_earth_models-0.74.0.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.73.8.dist-info → hestia_earth_models-0.74.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
from functools import reduce
|
|
2
|
+
from hestia_earth.utils.tools import list_sum, flatten
|
|
3
|
+
from hestia_earth.schema import EmissionMethodTier, TermTermType
|
|
4
|
+
from hestia_earth.utils.blank_node import group_by_keys
|
|
5
|
+
|
|
6
|
+
from hestia_earth.models.log import logShouldRun, logRequirements, debugValues
|
|
7
|
+
from hestia_earth.models.utils.emission import _new_emission
|
|
8
|
+
from hestia_earth.models.utils.background_emissions import get_background_inputs, no_gap_filled_background_emissions
|
|
9
|
+
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
|
10
|
+
from hestia_earth.models.utils.term import get_electricity_grid_mix_terms
|
|
11
|
+
from .utils import get_input_coefficient
|
|
12
|
+
from ..ecoinventV3.utils import get_input_mappings, ecoinvent_values
|
|
13
|
+
from . import MODEL
|
|
14
|
+
|
|
15
|
+
REQUIREMENTS = {
|
|
16
|
+
"Cycle": {
|
|
17
|
+
"completeness.electricityFuel": "True",
|
|
18
|
+
"site": {
|
|
19
|
+
"@type": "Site",
|
|
20
|
+
"country": {"@type": "Term", "termType": "region"}
|
|
21
|
+
},
|
|
22
|
+
"inputs": [{
|
|
23
|
+
"@type": "Input",
|
|
24
|
+
"term.@id": ["electricityGridMarketMix", "electricityGridRenewableMix"],
|
|
25
|
+
"value": "> 0",
|
|
26
|
+
"none": {
|
|
27
|
+
"fromCycle": "True",
|
|
28
|
+
"producedInCycle": "True"
|
|
29
|
+
}
|
|
30
|
+
}],
|
|
31
|
+
"optional": {
|
|
32
|
+
"animals": [{
|
|
33
|
+
"@type": "Animal",
|
|
34
|
+
"inputs": [{
|
|
35
|
+
"@type": "Input",
|
|
36
|
+
"term.@id": ["electricityGridMarketMix", "electricityGridRenewableMix"],
|
|
37
|
+
"value": "> 0",
|
|
38
|
+
"none": {
|
|
39
|
+
"fromCycle": "True",
|
|
40
|
+
"producedInCycle": "True"
|
|
41
|
+
}
|
|
42
|
+
}]
|
|
43
|
+
}]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
RETURNS = {
|
|
48
|
+
"Emission": [{
|
|
49
|
+
"term": "",
|
|
50
|
+
"value": "",
|
|
51
|
+
"methodTier": "background",
|
|
52
|
+
"inputs": "",
|
|
53
|
+
"operation": "",
|
|
54
|
+
"animals": ""
|
|
55
|
+
}]
|
|
56
|
+
}
|
|
57
|
+
LOOKUPS = {
|
|
58
|
+
"emission": "inputProductionGroupId",
|
|
59
|
+
"electricity": "ecoinventMapping",
|
|
60
|
+
"region-ember-energySources": "",
|
|
61
|
+
"ember-ecoinvent-mapping": ["ember", "ecoinventId", "ecoinventName"]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
MODEL_KEY = 'cycle'
|
|
65
|
+
TIER = EmissionMethodTier.BACKGROUND.value
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _emission(term_id: str, value: float, input: dict):
|
|
69
|
+
emission = _new_emission(term_id, MODEL)
|
|
70
|
+
emission['value'] = [value]
|
|
71
|
+
emission['methodTier'] = TIER
|
|
72
|
+
emission['inputs'] = [input.get('term')]
|
|
73
|
+
if input.get('operation'):
|
|
74
|
+
emission['operation'] = input.get('operation')
|
|
75
|
+
if input.get('animal'):
|
|
76
|
+
emission['animals'] = [input.get('animal')]
|
|
77
|
+
return emission
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _add_emission(cycle: dict, input: dict):
|
|
81
|
+
input_term_id = input.get('term', {}).get('@id')
|
|
82
|
+
operation_term_id = input.get('operation', {}).get('@id')
|
|
83
|
+
animal_term_id = input.get('animal', {}).get('@id')
|
|
84
|
+
# TODO: as inputs are grouped, there could be more than 1 country
|
|
85
|
+
country_id = input.get('country', cycle.get('site', {}).get('country', {})).get('@id')
|
|
86
|
+
|
|
87
|
+
def add(prev: dict, mapping: tuple):
|
|
88
|
+
ecoinventName, _coefficient = mapping
|
|
89
|
+
# recalculate the coefficient using the country and year if it should be included
|
|
90
|
+
coefficient = get_input_coefficient(MODEL, cycle, country_id, ecoinventName) if _coefficient > 0 else 0
|
|
91
|
+
emissions = ecoinvent_values(ecoinventName, TermTermType.EMISSION)
|
|
92
|
+
for emission_term_id, data in emissions:
|
|
93
|
+
# log run on each emission so we know it did run
|
|
94
|
+
logShouldRun(cycle, MODEL, input_term_id, True, methodTier=TIER, emission_id=emission_term_id)
|
|
95
|
+
debugValues(cycle, model=MODEL, term=emission_term_id,
|
|
96
|
+
value=data.get('value'),
|
|
97
|
+
coefficient=coefficient,
|
|
98
|
+
input=input_term_id,
|
|
99
|
+
operation=operation_term_id,
|
|
100
|
+
animal=animal_term_id)
|
|
101
|
+
prev[emission_term_id] = prev.get(emission_term_id, 0) + (data.get('value') * coefficient)
|
|
102
|
+
return prev
|
|
103
|
+
return add
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _run_input(cycle: dict):
|
|
107
|
+
electricity_complete = _is_term_type_complete(cycle, 'electricityFuel')
|
|
108
|
+
no_gap_filled_background_emissions_func = no_gap_filled_background_emissions(cycle)
|
|
109
|
+
|
|
110
|
+
def run(inputs: list):
|
|
111
|
+
input = inputs[0]
|
|
112
|
+
input_value = list_sum(flatten(input.get('value', []) for input in inputs))
|
|
113
|
+
input_term_id = input.get('term', {}).get('@id')
|
|
114
|
+
mappings = get_input_mappings(MODEL, input)
|
|
115
|
+
has_mappings = len(mappings) > 0
|
|
116
|
+
|
|
117
|
+
# skip input that has background emissions we have already gap-filled (model run before)
|
|
118
|
+
has_no_gap_filled_background_emissions = no_gap_filled_background_emissions_func(input)
|
|
119
|
+
|
|
120
|
+
logRequirements(cycle, model=MODEL, term=input_term_id,
|
|
121
|
+
has_ecoinvent_mappings=has_mappings,
|
|
122
|
+
ecoinvent_mappings=';'.join([v[0] for v in mappings]),
|
|
123
|
+
has_no_gap_filled_background_emissions=has_no_gap_filled_background_emissions,
|
|
124
|
+
termType_electricityFuel_complete=electricity_complete,
|
|
125
|
+
input_value=input_value)
|
|
126
|
+
|
|
127
|
+
should_run = all([electricity_complete, has_mappings, has_no_gap_filled_background_emissions, input_value])
|
|
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
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def run(cycle: dict):
|
|
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 = group_by_keys(inputs, ['term', 'operation', 'animal'])
|
|
144
|
+
return flatten(map(_run_input(cycle), grouped_inputs.values()))
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
from functools import reduce
|
|
2
1
|
from hestia_earth.schema import TermTermType, SiteSiteType
|
|
3
2
|
from hestia_earth.utils.model import filter_list_term_type
|
|
4
3
|
from hestia_earth.utils.lookup import extract_grouped_data
|
|
4
|
+
from hestia_earth.utils.blank_node import group_by_keys
|
|
5
5
|
from hestia_earth.utils.tools import list_sum, safe_parse_float
|
|
6
6
|
|
|
7
7
|
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
8
8
|
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
|
9
|
-
from hestia_earth.models.utils.blank_node import group_by_keys
|
|
10
9
|
from hestia_earth.models.utils.term import get_lookup_value
|
|
11
10
|
from hestia_earth.models.utils.cycle import get_animals_by_period
|
|
12
11
|
from hestia_earth.models.utils.emission import _new_emission
|
|
@@ -90,7 +89,7 @@ def get_fuel_inputs(term_id: str, cycle: dict, lookup_col: str):
|
|
|
90
89
|
|
|
91
90
|
|
|
92
91
|
def group_fuel_inputs(inputs: list):
|
|
93
|
-
return
|
|
92
|
+
return group_by_keys(inputs, ['input-id', 'operation-id']) if len(inputs) > 0 else None
|
|
94
93
|
|
|
95
94
|
|
|
96
95
|
def _get_emissions_factor(animal: dict, lookup_col: str) -> float:
|
|
@@ -88,20 +88,18 @@ def _indicator_factors(impact_assessment: dict, indicator: dict):
|
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
|
|
91
|
-
def _map_input_ids(value: dict) -> set[str]:
|
|
92
|
-
return set(map(lambda i: i.get('@id'), value.get('inputs', [])))
|
|
93
|
-
|
|
94
|
-
|
|
95
91
|
def _count_duplicate_indicators(reference_indicator: dict, indicators: list) -> int:
|
|
96
92
|
"""
|
|
97
93
|
Counts the number of `reference_indicator` indicators found in a list of indicators.
|
|
98
|
-
Uses indicator.term.@id and indicator.
|
|
94
|
+
Uses indicator.term.@id and indicator.key to determine uniqueness.
|
|
99
95
|
"""
|
|
100
96
|
return sum([
|
|
101
97
|
1
|
|
102
98
|
for i in indicators
|
|
103
|
-
if (
|
|
104
|
-
|
|
99
|
+
if all([
|
|
100
|
+
i["term"]["@id"] == reference_indicator["term"]["@id"],
|
|
101
|
+
i.get("key", {}).get("@id") == reference_indicator.get("key", {}).get("@id")
|
|
102
|
+
])
|
|
105
103
|
])
|
|
106
104
|
|
|
107
105
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
from functools import reduce
|
|
2
1
|
from hestia_earth.schema import TermTermType
|
|
3
2
|
from hestia_earth.utils.lookup import get_table_value, download_lookup, column_name
|
|
4
3
|
from hestia_earth.utils.model import filter_list_term_type
|
|
5
4
|
from hestia_earth.utils.tools import flatten, list_sum
|
|
5
|
+
from hestia_earth.utils.blank_node import group_by_keys
|
|
6
6
|
|
|
7
7
|
from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
|
|
8
|
-
from hestia_earth.models.utils
|
|
8
|
+
from hestia_earth.models.utils import unique_values, _omit, _include
|
|
9
9
|
from hestia_earth.models.utils.indicator import _new_indicator
|
|
10
10
|
from hestia_earth.models.utils.lookup import _node_value
|
|
11
11
|
from . import MODEL
|
|
@@ -20,7 +20,7 @@ REQUIREMENTS = {
|
|
|
20
20
|
"ionisingCompoundsToWaterInputsProduction",
|
|
21
21
|
"ionisingCompoundsToSaltwaterInputsProduction"
|
|
22
22
|
],
|
|
23
|
-
"
|
|
23
|
+
"key": {"@type": "Term", "term.termType": "waste", "term.units": "kg"}
|
|
24
24
|
}]
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -34,6 +34,7 @@ LOOKUPS = {
|
|
|
34
34
|
RETURNS = {
|
|
35
35
|
"Indicator": [{
|
|
36
36
|
"value": "",
|
|
37
|
+
"key": "",
|
|
37
38
|
"inputs": ""
|
|
38
39
|
}]
|
|
39
40
|
}
|
|
@@ -41,31 +42,32 @@ RETURNS = {
|
|
|
41
42
|
TERM_ID = 'ionisingRadiationKbqU235Eq'
|
|
42
43
|
|
|
43
44
|
|
|
44
|
-
def
|
|
45
|
-
return input.get('units', '').startswith("kg") and input.get('termType', '') == TermTermType.WASTE.value
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
def _valid_emission(emission: dict) -> bool:
|
|
49
|
-
return len(emission.get('inputs', [])) == 1 and isinstance(_node_value(emission), (int, float))
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
def _indicator(value: float, input: dict) -> dict:
|
|
45
|
+
def _indicator(value: float, key: dict, inputs: list) -> dict:
|
|
53
46
|
indicator = _new_indicator(TERM_ID, MODEL)
|
|
47
|
+
indicator['key'] = key
|
|
54
48
|
indicator['value'] = value
|
|
55
|
-
|
|
49
|
+
if inputs:
|
|
50
|
+
indicator['inputs'] = inputs
|
|
56
51
|
return indicator
|
|
57
52
|
|
|
58
53
|
|
|
59
54
|
def _run(emissions: list) -> list[dict]:
|
|
60
55
|
indicators = [
|
|
61
|
-
_indicator(
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
_indicator(
|
|
57
|
+
value=list_sum([emission['value'] * emission['coefficient'] for emission in emission_group]),
|
|
58
|
+
key=emission_group[0]['key'],
|
|
59
|
+
inputs=unique_values(flatten([emission.get('inputs', []) for emission in emission_group]))
|
|
60
|
+
)
|
|
61
|
+
for emission_group in group_by_keys(emissions, ['key']).values()
|
|
64
62
|
]
|
|
65
63
|
|
|
66
64
|
return indicators
|
|
67
65
|
|
|
68
66
|
|
|
67
|
+
def _valid_key(term: dict) -> bool:
|
|
68
|
+
return term.get('units', '').startswith("kg") and term.get('termType') == TermTermType.WASTE.value
|
|
69
|
+
|
|
70
|
+
|
|
69
71
|
def _should_run(impact_assessment: dict) -> tuple[bool, list]:
|
|
70
72
|
emissions = [
|
|
71
73
|
emission
|
|
@@ -75,38 +77,34 @@ def _should_run(impact_assessment: dict) -> tuple[bool, list]:
|
|
|
75
77
|
|
|
76
78
|
has_emissions = bool(emissions)
|
|
77
79
|
|
|
78
|
-
emissions_unpacked =
|
|
79
|
-
|
|
80
|
-
[
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
]
|
|
96
|
-
)
|
|
80
|
+
emissions_unpacked = [
|
|
81
|
+
{
|
|
82
|
+
"key-term-id": emission['key'].get('@id'),
|
|
83
|
+
"key-term-type": emission['key'].get('termType'),
|
|
84
|
+
"key-is-valid": _valid_key(emission['key']),
|
|
85
|
+
"indicator-term-id": emission['term']['@id'],
|
|
86
|
+
"value": _node_value(emission),
|
|
87
|
+
"coefficient": get_table_value(
|
|
88
|
+
lookup=download_lookup(filename="waste.csv"),
|
|
89
|
+
col_match='termid',
|
|
90
|
+
col_match_with=emission['key'].get('@id'),
|
|
91
|
+
col_val=column_name(emission['term']['@id'])
|
|
92
|
+
)
|
|
93
|
+
} | _include(emission, ['key', 'inputs'])
|
|
94
|
+
for emission in emissions
|
|
95
|
+
if emission.get('key')
|
|
96
|
+
]
|
|
97
97
|
|
|
98
98
|
valid_emission_with_cf = [
|
|
99
99
|
em for em in emissions_unpacked if all([
|
|
100
100
|
em['coefficient'] is not None,
|
|
101
|
-
em['
|
|
102
|
-
em['indicator-input-is-valid'] is True
|
|
101
|
+
em['key-is-valid'] is True
|
|
103
102
|
])
|
|
104
103
|
]
|
|
105
104
|
|
|
106
|
-
|
|
105
|
+
valid_key_requirements = all([
|
|
107
106
|
all([
|
|
108
|
-
em['
|
|
109
|
-
em['indicator-input-is-valid']
|
|
107
|
+
em['key-is-valid']
|
|
110
108
|
])
|
|
111
109
|
for em in emissions_unpacked
|
|
112
110
|
])
|
|
@@ -117,12 +115,12 @@ def _should_run(impact_assessment: dict) -> tuple[bool, list]:
|
|
|
117
115
|
|
|
118
116
|
logRequirements(impact_assessment, model=MODEL, term=TERM_ID,
|
|
119
117
|
has_emissions=has_emissions,
|
|
120
|
-
|
|
118
|
+
valid_key_requirements=valid_key_requirements,
|
|
121
119
|
all_emissions_have_known_CF=all_emissions_have_known_cf,
|
|
122
|
-
emissions=log_as_table(emissions_unpacked)
|
|
120
|
+
emissions=log_as_table([_omit(v, ['key', 'inputs']) for v in emissions_unpacked])
|
|
123
121
|
)
|
|
124
122
|
|
|
125
|
-
should_run =
|
|
123
|
+
should_run = all([emissions_unpacked, valid_key_requirements])
|
|
126
124
|
|
|
127
125
|
logShouldRun(impact_assessment, MODEL, TERM_ID, should_run)
|
|
128
126
|
return should_run, valid_emission_with_cf
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
2
|
+
from .utils import download, has_geospatial_data, should_download
|
|
3
|
+
from . import MODEL
|
|
4
|
+
|
|
5
|
+
REQUIREMENTS = {
|
|
6
|
+
"Site": {
|
|
7
|
+
"or": [
|
|
8
|
+
{"latitude": "", "longitude": ""},
|
|
9
|
+
{"boundary": {}},
|
|
10
|
+
{"region": {"@type": "Term", "termType": "region"}}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
RETURNS = {
|
|
15
|
+
"The AWARE water basin identifier as a `string`": ""
|
|
16
|
+
}
|
|
17
|
+
MODEL_KEY = 'awareWaterBasinId_v1'
|
|
18
|
+
EE_PARAMS = {
|
|
19
|
+
'collection': 'AWARE',
|
|
20
|
+
'ee_type': 'vector',
|
|
21
|
+
'fields': 'Name'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _download(site: dict):
|
|
26
|
+
return download(MODEL_KEY, site, EE_PARAMS)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _run(site: dict): return _download(site)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _should_run(site: dict):
|
|
33
|
+
contains_geospatial_data = has_geospatial_data(site)
|
|
34
|
+
below_max_area_size = should_download(MODEL_KEY, site)
|
|
35
|
+
|
|
36
|
+
logRequirements(site, model=MODEL, model_key=MODEL_KEY,
|
|
37
|
+
contains_geospatial_data=contains_geospatial_data,
|
|
38
|
+
below_max_area_size=below_max_area_size)
|
|
39
|
+
|
|
40
|
+
should_run = all([contains_geospatial_data, below_max_area_size])
|
|
41
|
+
logShouldRun(site, MODEL, None, should_run, model_key=MODEL_KEY)
|
|
42
|
+
return should_run
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def run(site: dict): return _run(site) if _should_run(site) else None
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from hestia_earth.schema import EmissionMethodTier
|
|
2
2
|
from hestia_earth.utils.tools import flatten, safe_parse_float
|
|
3
3
|
|
|
4
|
-
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
4
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, debugValues
|
|
5
5
|
from hestia_earth.models.utils import _omit
|
|
6
6
|
from hestia_earth.models.utils.emission import _new_emission, background_emissions_in_system_boundary
|
|
7
7
|
from hestia_earth.models.utils.background_emissions import no_gap_filled_background_emissions
|
|
@@ -59,6 +59,10 @@ def _run_input(cycle: dict):
|
|
|
59
59
|
|
|
60
60
|
for emission_id in required_emission_term_ids:
|
|
61
61
|
logShouldRun(cycle, MODEL, term_id, True, methodTier=TIER, model_key=MODEL_KEY, emission_id=emission_id)
|
|
62
|
+
debugValues(cycle, model=MODEL, term=emission_id,
|
|
63
|
+
value=value,
|
|
64
|
+
coefficient=1,
|
|
65
|
+
input=term_id)
|
|
62
66
|
|
|
63
67
|
return [
|
|
64
68
|
_emission(term_id, value, input_term) for term_id in required_emission_term_ids
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from hestia_earth.schema import IndicatorMethodTier, TermTermType
|
|
2
2
|
from hestia_earth.utils.tools import flatten, safe_parse_float
|
|
3
3
|
|
|
4
|
-
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
4
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, debugValues
|
|
5
5
|
from hestia_earth.models.utils import _omit
|
|
6
6
|
from hestia_earth.models.utils.emission import background_emissions_in_system_boundary
|
|
7
7
|
from hestia_earth.models.utils.indicator import _new_indicator
|
|
@@ -63,6 +63,10 @@ def _run_input(impact: dict):
|
|
|
63
63
|
|
|
64
64
|
for emission_id in required_resourceUse_term_ids:
|
|
65
65
|
logShouldRun(impact, MODEL, term_id, True, methodTier=TIER, model_key=MODEL_KEY, emission_id=emission_id)
|
|
66
|
+
debugValues(impact, model=MODEL, term=emission_id,
|
|
67
|
+
value=value,
|
|
68
|
+
coefficient=1,
|
|
69
|
+
input=term_id)
|
|
66
70
|
|
|
67
71
|
return [
|
|
68
72
|
_indicator(term_id, value, input_term) for term_id in required_resourceUse_term_ids
|
|
@@ -29,6 +29,7 @@ from .utils import (
|
|
|
29
29
|
TOTAL_AGRICULTURAL_CHANGE,
|
|
30
30
|
ALL_LAND_USE_TERMS,
|
|
31
31
|
crop_ipcc_land_use_category,
|
|
32
|
+
LAND_USE_NAMES_FROM_ID
|
|
32
33
|
)
|
|
33
34
|
from . import MODEL
|
|
34
35
|
|
|
@@ -101,7 +102,8 @@ _BUILDING_SITE_TYPES = [
|
|
|
101
102
|
_DEFAULT_WINDOW_IN_YEARS = 20
|
|
102
103
|
_DATE_TOLERANCE_IN_YEARS = 2
|
|
103
104
|
_OUTPUT_SIGNIFICANT_DIGITS = 3
|
|
104
|
-
_ALLOWED_LAND_USE_TYPES = [ANNUAL_CROPLAND, PERMANENT_CROPLAND, PERMANENT_PASTURE]
|
|
105
|
+
_ALLOWED_LAND_USE_TYPES = [ANNUAL_CROPLAND, PERMANENT_CROPLAND, PERMANENT_PASTURE, TOTAL_CROPLAND]
|
|
106
|
+
_TOP_LEVEL_LAND_USE_TYPE_IDS = {"annualCropland", "permanentCropland", "permanentPasture", "cropland"}
|
|
105
107
|
_LOOKUP_EXPANSION = "region-crop-cropGroupingFaostatProduction-areaHarvestedUpTo20YearExpansion.csv"
|
|
106
108
|
_COMPLETE_CHANGES_OTHER_LAND = {
|
|
107
109
|
OTHER_LAND: 1,
|
|
@@ -214,7 +216,7 @@ def _should_group_landCover(management_node: dict):
|
|
|
214
216
|
)
|
|
215
217
|
|
|
216
218
|
|
|
217
|
-
def _get_changes(country_id: str, end_year: int) -> tuple[dict,
|
|
219
|
+
def _get_changes(country_id: str, end_year: int) -> tuple[dict, list]:
|
|
218
220
|
"""
|
|
219
221
|
For each entry in ALL_LAND_USE_TERMS, creates a key: value in output dictionary, also TOTAL
|
|
220
222
|
"""
|
|
@@ -250,6 +252,25 @@ def _get_ratio_start_and_end_values(
|
|
|
250
252
|
return max(0.0, _safe_divide(numerator=expansion, denominator=end_value))
|
|
251
253
|
|
|
252
254
|
|
|
255
|
+
def _get_ratio_between_land_use_types(
|
|
256
|
+
country_id: str,
|
|
257
|
+
end_year: int,
|
|
258
|
+
first_land_use_term: str,
|
|
259
|
+
second_land_use_term: str
|
|
260
|
+
) -> tuple:
|
|
261
|
+
"""Returns a tuple of the values of the two land use terms for the same country and year."""
|
|
262
|
+
return tuple([
|
|
263
|
+
safe_parse_float(value=extract_grouped_data(
|
|
264
|
+
get_region_lookup_value(
|
|
265
|
+
'region-faostatArea.csv', country_id, land_use_term, model=MODEL, key=MODEL_KEY
|
|
266
|
+
),
|
|
267
|
+
str(end_year)),
|
|
268
|
+
default=None
|
|
269
|
+
)
|
|
270
|
+
for land_use_term in [first_land_use_term, second_land_use_term]
|
|
271
|
+
])
|
|
272
|
+
|
|
273
|
+
|
|
253
274
|
def _estimate_maximum_forest_change(
|
|
254
275
|
forest_change: float, total_cropland_change: float, pasture_change: float, total_agricultural_change: float
|
|
255
276
|
):
|
|
@@ -384,7 +405,7 @@ def _estimate_conversions_to_permanent_cropland(
|
|
|
384
405
|
forest_loss_to_cropland: float,
|
|
385
406
|
other_land_loss_to_annual_cropland: float
|
|
386
407
|
) -> dict:
|
|
387
|
-
"""Estimate percentage of land sources when converted to:
|
|
408
|
+
"""Estimate percentage of land sources when converted to: Permanent cropland"""
|
|
388
409
|
|
|
389
410
|
def conversion_to_permanent_cropland(factor: float):
|
|
390
411
|
return _safe_divide(
|
|
@@ -642,15 +663,92 @@ def _scale_site_area_errors(site_area: dict) -> dict:
|
|
|
642
663
|
if negative_errors and abs(negative_errors[0]) < 1 and all([v < 1 for v in site_area.values()]) else site_area
|
|
643
664
|
|
|
644
665
|
|
|
666
|
+
def _new_landCover_term(new_land_use_term) -> dict:
|
|
667
|
+
return {
|
|
668
|
+
"@id": LAND_USE_TERMS_FOR_TRANSFORMATION[new_land_use_term][0],
|
|
669
|
+
"name": LAND_USE_TERMS_FOR_TRANSFORMATION[new_land_use_term][1],
|
|
670
|
+
"@type": "Term",
|
|
671
|
+
"termType": TermTermType.LANDCOVER.value
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
def _scaled_value(
|
|
676
|
+
permanent_crops_value: float,
|
|
677
|
+
annual_crops_value: float,
|
|
678
|
+
permanent_crops_factor: float,
|
|
679
|
+
annual_crops_factor: float,
|
|
680
|
+
):
|
|
681
|
+
total_area = permanent_crops_factor + annual_crops_factor
|
|
682
|
+
permanent_crops_scaled = permanent_crops_value * permanent_crops_factor / total_area
|
|
683
|
+
annual_crops_scaled = annual_crops_value * annual_crops_factor / total_area
|
|
684
|
+
return annual_crops_scaled + permanent_crops_scaled
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
def _scale_from_annual_and_permanent_results(
|
|
688
|
+
annual_cropland_results: dict,
|
|
689
|
+
permanent_cropland_results: dict,
|
|
690
|
+
annual_cropland_factor: float,
|
|
691
|
+
permanent_cropland_factor: float
|
|
692
|
+
) -> dict:
|
|
693
|
+
return {
|
|
694
|
+
land_key: _scaled_value(
|
|
695
|
+
permanent_crops_value=permanent_cropland_results[land_key],
|
|
696
|
+
annual_crops_value=land_value,
|
|
697
|
+
permanent_crops_factor=permanent_cropland_factor,
|
|
698
|
+
annual_crops_factor=annual_cropland_factor
|
|
699
|
+
)
|
|
700
|
+
for land_key, land_value in annual_cropland_results.items()
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
|
|
645
704
|
def _should_run_historical_land_use_change(site: dict, nodes: list, land_use_type: str) -> tuple[bool, dict]:
|
|
646
705
|
# Assume a single management node for single-cropping.
|
|
647
|
-
return
|
|
706
|
+
return (
|
|
707
|
+
_should_run_historical_land_use_change_total_cropland(site, nodes) if land_use_type == TOTAL_CROPLAND else
|
|
708
|
+
_should_run_historical_land_use_change_single_crop(
|
|
709
|
+
site=site,
|
|
710
|
+
term=nodes[0].get("term", {}),
|
|
711
|
+
country_id=site.get("country", {}).get("@id"),
|
|
712
|
+
end_year=_get_year_from_landCover(nodes[0]),
|
|
713
|
+
land_use_type=land_use_type
|
|
714
|
+
)
|
|
715
|
+
)
|
|
716
|
+
|
|
717
|
+
|
|
718
|
+
def _should_run_historical_land_use_change_total_cropland(site: dict, nodes: list) -> tuple[bool, dict]:
|
|
719
|
+
end_year = _get_year_from_landCover(nodes[0])
|
|
720
|
+
country_id = site.get("country", {}).get("@id")
|
|
721
|
+
|
|
722
|
+
# Run _should_run_historical_land_use_change_single_crop for annual and permanent
|
|
723
|
+
should_run_annual, areas_for_annual_cropland = _should_run_historical_land_use_change_single_crop(
|
|
648
724
|
site=site,
|
|
649
|
-
term=
|
|
650
|
-
country_id=
|
|
651
|
-
end_year=
|
|
652
|
-
land_use_type=
|
|
725
|
+
term=_new_landCover_term(ANNUAL_CROPLAND),
|
|
726
|
+
country_id=country_id,
|
|
727
|
+
end_year=end_year,
|
|
728
|
+
land_use_type=ANNUAL_CROPLAND
|
|
653
729
|
)
|
|
730
|
+
should_run_permanent, areas_for_permanent_cropland = _should_run_historical_land_use_change_single_crop(
|
|
731
|
+
site=site,
|
|
732
|
+
term=_new_landCover_term(PERMANENT_CROPLAND),
|
|
733
|
+
country_id=country_id,
|
|
734
|
+
end_year=end_year,
|
|
735
|
+
land_use_type=PERMANENT_CROPLAND
|
|
736
|
+
)
|
|
737
|
+
# Get current ratios ("Arable land" vs "Permanent crops")
|
|
738
|
+
annual_cropland_factor, permanent_crops_factor = _get_ratio_between_land_use_types(
|
|
739
|
+
country_id=country_id,
|
|
740
|
+
end_year=end_year,
|
|
741
|
+
first_land_use_term=ANNUAL_CROPLAND,
|
|
742
|
+
second_land_use_term=PERMANENT_CROPLAND
|
|
743
|
+
) if should_run_annual and should_run_permanent else tuple([0, 0])
|
|
744
|
+
scaled_results = _scale_from_annual_and_permanent_results(
|
|
745
|
+
annual_cropland_results=areas_for_annual_cropland,
|
|
746
|
+
permanent_cropland_results=areas_for_permanent_cropland,
|
|
747
|
+
annual_cropland_factor=annual_cropland_factor,
|
|
748
|
+
permanent_cropland_factor=permanent_crops_factor
|
|
749
|
+
) if should_run_annual and should_run_permanent else {}
|
|
750
|
+
# Scale results according to current ratios.
|
|
751
|
+
return all([should_run_annual, should_run_permanent]), scaled_results
|
|
654
752
|
|
|
655
753
|
|
|
656
754
|
def _should_run_historical_land_use_change_single_crop(
|
|
@@ -743,11 +841,11 @@ def _should_run_historical_land_use_change_single_crop(
|
|
|
743
841
|
|
|
744
842
|
# Cell E8
|
|
745
843
|
expansion_factor = _get_ratio_start_and_end_values(
|
|
746
|
-
expansion=changes[
|
|
747
|
-
fao_name=
|
|
844
|
+
expansion=changes[LAND_USE_NAMES_FROM_ID[term.get("@id")]],
|
|
845
|
+
fao_name=land_use_type,
|
|
748
846
|
country_id=country_id,
|
|
749
847
|
end_year=end_year
|
|
750
|
-
) if
|
|
848
|
+
) if term.get("@id") in _TOP_LEVEL_LAND_USE_TYPE_IDS else get_ratio_of_expanded_area(
|
|
751
849
|
country_id=country_id,
|
|
752
850
|
fao_name=_get_faostat_name(term),
|
|
753
851
|
end_year=end_year
|
|
@@ -774,7 +872,7 @@ def _should_run_historical_land_use_change_single_crop(
|
|
|
774
872
|
) if land_use_type == ANNUAL_CROPLAND else 1
|
|
775
873
|
)
|
|
776
874
|
|
|
777
|
-
# E10: Compare changes to annual/
|
|
875
|
+
# E10: Compare changes to annual/permanent cropland from net expansion.
|
|
778
876
|
net_expansion_cultivated_vs_harvested = _get_net_expansion_cultivated_vs_harvested(
|
|
779
877
|
annual_crops_net_expansion=annual_crops_net_expansion,
|
|
780
878
|
changes=changes,
|
|
@@ -35,6 +35,7 @@ LAND_USE_TERMS_FOR_TRANSFORMATION = {
|
|
|
35
35
|
PERMANENT_PASTURE: ("permanentPasture", "Permanent pasture"),
|
|
36
36
|
OTHER_LAND: ("otherLand", OTHER_LAND) # Not used yet
|
|
37
37
|
}
|
|
38
|
+
LAND_USE_NAMES_FROM_ID = {v[0]: k for k, v in LAND_USE_TERMS_FOR_TRANSFORMATION.items()} | {"cropland": TOTAL_CROPLAND}
|
|
38
39
|
|
|
39
40
|
|
|
40
41
|
def crop_ipcc_land_use_category(
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
from functools import reduce
|
|
2
1
|
from hestia_earth.schema import MeasurementMethodClassification, TermTermType
|
|
3
2
|
from hestia_earth.utils.model import filter_list_term_type
|
|
4
3
|
from hestia_earth.utils.tools import safe_parse_float, list_average, non_empty_list
|
|
4
|
+
from hestia_earth.utils.blank_node import group_by_keys
|
|
5
5
|
|
|
6
6
|
from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
|
|
7
7
|
from hestia_earth.models.utils.measurement import _new_measurement
|
|
8
8
|
from hestia_earth.models.utils.site import related_cycles
|
|
9
9
|
from hestia_earth.models.utils.term import get_lookup_value
|
|
10
|
-
from hestia_earth.models.utils.blank_node import group_by_keys
|
|
11
10
|
from . import MODEL
|
|
12
11
|
|
|
13
12
|
REQUIREMENTS = {
|
|
@@ -75,7 +74,7 @@ def _should_run(site: dict):
|
|
|
75
74
|
|
|
76
75
|
def run(site: dict):
|
|
77
76
|
should_run, values = _should_run(site)
|
|
78
|
-
grouped_values =
|
|
77
|
+
grouped_values = group_by_keys(values, ['start-date', 'end-date'])
|
|
79
78
|
return non_empty_list([
|
|
80
79
|
_measurement(
|
|
81
80
|
list_average([v.get('lookup-value') for v in value if v.get('lookup-value')], default=0),
|