hestia-earth-models 0.64.7__py3-none-any.whl → 0.64.8__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/cycle/animal/milkYield.py +10 -22
- hestia_earth/models/cycle/unknownPreSeasonWaterRegime.py +0 -1
- hestia_earth/models/environmentalFootprintV3/soilQualityIndexLandOccupation.py +2 -2
- hestia_earth/models/environmentalFootprintV3/soilQualityIndexLandTransformation.py +182 -0
- hestia_earth/models/environmentalFootprintV3/soilQualityIndexTotalLandUseEffects.py +66 -0
- hestia_earth/models/environmentalFootprintV3/utils.py +1 -1
- hestia_earth/models/hyde32/utils.py +4 -0
- hestia_earth/models/ipcc2019/animal/pastureGrass.py +3 -1
- hestia_earth/models/ipcc2019/co2ToAirAboveGroundBiomassStockChangeLandUseChange.py +191 -0
- hestia_earth/models/ipcc2019/co2ToAirBelowGroundBiomassStockChangeLandUseChange.py +204 -0
- hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +255 -35
- hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChangeManagementChange.py +63 -149
- hestia_earth/models/ipcc2019/pastureGrass.py +3 -1
- hestia_earth/models/mocking/search-results.json +2020 -26
- hestia_earth/models/pooreNemecek2018/landOccupationDuringCycle.py +1 -1
- hestia_earth/models/site/management.py +3 -5
- hestia_earth/models/utils/input.py +5 -2
- hestia_earth/models/utils/site.py +4 -2
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.64.7.dist-info → hestia_earth_models-0.64.8.dist-info}/METADATA +2 -2
- {hestia_earth_models-0.64.7.dist-info → hestia_earth_models-0.64.8.dist-info}/RECORD +33 -25
- tests/models/cycle/animal/test_milkYield.py +1 -14
- tests/models/environmentalFootprintV3/test_soilQualityIndexLandTransformation.py +113 -0
- tests/models/environmentalFootprintV3/test_soilQualityIndexTotalLandUseEffects.py +50 -0
- tests/models/ipcc2019/test_co2ToAirAboveGroundBiomassStockChangeLandUseChange.py +83 -0
- tests/models/ipcc2019/test_co2ToAirBelowGroundBiomassStockChangeLandUseChange.py +83 -0
- tests/models/ipcc2019/test_co2ToAirCarbonStockChange_utils.py +6 -6
- tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChangeManagementChange.py +5 -4
- tests/models/site/test_management.py +4 -1
- tests/models/utils/test_input.py +65 -1
- {hestia_earth_models-0.64.7.dist-info → hestia_earth_models-0.64.8.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.64.7.dist-info → hestia_earth_models-0.64.8.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.64.7.dist-info → hestia_earth_models-0.64.8.dist-info}/top_level.txt +0 -0
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Milk Yield
|
|
3
3
|
|
|
4
|
-
This model gap-fills the practice "Milk yield per animal X (raw/FPCM)" (e.g. `Milk yield per cow (raw)`) when
|
|
5
|
-
|
|
6
|
-
- the Animal blank node Term has a lookup value of `kgDayMilkForFeedingOffspring`.
|
|
4
|
+
This model gap-fills the practice "Milk yield per animal X (raw/FPCM)" (e.g. `Milk yield per cow (raw)`) when
|
|
5
|
+
the practice is added to the Cycle itself, and we can match it with the `term` of an Animal blank node.
|
|
7
6
|
"""
|
|
8
7
|
from hestia_earth.schema import TermTermType
|
|
9
8
|
from hestia_earth.utils.model import filter_list_term_type
|
|
10
|
-
from hestia_earth.utils.tools import non_empty_list
|
|
9
|
+
from hestia_earth.utils.tools import non_empty_list
|
|
11
10
|
|
|
12
11
|
from hestia_earth.models.log import logShouldRun, logRequirements, log_blank_nodes_id
|
|
13
12
|
from hestia_earth.models.utils.blank_node import merge_blank_nodes
|
|
14
13
|
from hestia_earth.models.utils.term import get_lookup_value
|
|
15
|
-
from hestia_earth.models.utils.practice import _new_practice
|
|
16
14
|
from .. import MODEL
|
|
17
15
|
|
|
18
16
|
REQUIREMENTS = {
|
|
@@ -21,12 +19,10 @@ REQUIREMENTS = {
|
|
|
21
19
|
"@type": "Animal",
|
|
22
20
|
"term.termType": "liveAnimal"
|
|
23
21
|
}],
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}]
|
|
29
|
-
}
|
|
22
|
+
"practices": [{
|
|
23
|
+
"@type": "Practice",
|
|
24
|
+
"term.termType": "animalManagement"
|
|
25
|
+
}]
|
|
30
26
|
}
|
|
31
27
|
}
|
|
32
28
|
RETURNS = {
|
|
@@ -38,27 +34,19 @@ RETURNS = {
|
|
|
38
34
|
}]
|
|
39
35
|
}
|
|
40
36
|
LOOKUPS = {
|
|
41
|
-
"liveAnimal":
|
|
37
|
+
"liveAnimal": "milkYieldPracticeTermIds"
|
|
42
38
|
}
|
|
43
39
|
|
|
44
40
|
MODEL_KEY = 'milkYield'
|
|
45
41
|
|
|
46
42
|
|
|
47
|
-
def _default_practice(animal: dict, practice_term_id: str):
|
|
48
|
-
term = animal.get('term', {})
|
|
49
|
-
value = get_lookup_value(term, LOOKUPS['liveAnimal'][1], model=MODEL, model_key=MODEL_KEY)
|
|
50
|
-
return (_new_practice(practice_term_id) | {'value': [safe_parse_float(value)]}) if value else None
|
|
51
|
-
|
|
52
|
-
|
|
53
43
|
def _run(cycle: dict, animal: dict):
|
|
54
44
|
term = animal.get('term', {})
|
|
55
45
|
term_id = term.get('@id')
|
|
56
|
-
value = get_lookup_value(term, LOOKUPS['liveAnimal']
|
|
46
|
+
value = get_lookup_value(term, LOOKUPS['liveAnimal'], model=MODEL, model_key=MODEL_KEY)
|
|
57
47
|
practice_ids = non_empty_list((value or '').split(';'))
|
|
58
48
|
practices = non_empty_list(
|
|
59
|
-
[p for p in cycle.get('practices', []) if p.get('term', {}).get('@id') in practice_ids]
|
|
60
|
-
[_default_practice(animal, practice_ids[0])] if practice_ids else []
|
|
61
|
-
)
|
|
49
|
+
[p for p in cycle.get('practices', []) if p.get('term', {}).get('@id') in practice_ids]
|
|
62
50
|
)
|
|
63
51
|
|
|
64
52
|
logRequirements(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
@@ -36,7 +36,6 @@ def _practice():
|
|
|
36
36
|
def _should_run(cycle: dict):
|
|
37
37
|
practices = cycle.get('practices', [])
|
|
38
38
|
flooded_terms = get_flooded_pre_season_terms()
|
|
39
|
-
print(flooded_terms)
|
|
40
39
|
existing_practice = next((p for p in practices if p.get('term', {}).get('@id') in flooded_terms), None)
|
|
41
40
|
|
|
42
41
|
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
|
@@ -77,8 +77,8 @@ def _should_run(impact_assessment: dict):
|
|
|
77
77
|
{
|
|
78
78
|
'site-type': site.get('siteType'),
|
|
79
79
|
'country-id': site.get('country', {}).get('@id'),
|
|
80
|
-
'area': site_areas[index],
|
|
81
|
-
'duration': site_durations[index],
|
|
80
|
+
'area': site_areas[index] if len(site_areas) >= index + 1 else None,
|
|
81
|
+
'duration': site_durations[index] if len(site_durations) >= index + 1 else None,
|
|
82
82
|
'landCover-id': (most_relevant_blank_node_by_type(
|
|
83
83
|
site.get("management", []),
|
|
84
84
|
term_type=TermTermType.LANDCOVER.value,
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Characterises [soilQualityIndexLandTransformation](https://hestia.earth/term/soilQualityIndexLandTransformation)
|
|
3
|
+
based on an updated [LANCA model (De Laurentiis et al. 2019)](
|
|
4
|
+
http://publications.jrc.ec.europa.eu/repository/handle/JRC113865) and on the LANCA (Regionalised) Characterisation
|
|
5
|
+
Factors version 2.5 (Horn and Meier, 2018).
|
|
6
|
+
"""
|
|
7
|
+
from typing import List
|
|
8
|
+
|
|
9
|
+
from hestia_earth.schema import TermTermType
|
|
10
|
+
from hestia_earth.utils.lookup import download_lookup
|
|
11
|
+
from hestia_earth.utils.model import filter_list_term_type
|
|
12
|
+
from hestia_earth.utils.tools import list_sum, safe_parse_date, non_empty_list
|
|
13
|
+
|
|
14
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
|
|
15
|
+
from hestia_earth.models.utils import hectar_to_square_meter
|
|
16
|
+
from hestia_earth.models.utils import pairwise
|
|
17
|
+
from . import MODEL
|
|
18
|
+
from .utils import get_coefficient_factor
|
|
19
|
+
from ..utils.indicator import _new_indicator
|
|
20
|
+
from ..utils.landCover import get_pef_grouping
|
|
21
|
+
from ..utils.lookup import fallback_country
|
|
22
|
+
|
|
23
|
+
REQUIREMENTS = {
|
|
24
|
+
"ImpactAssessment": {
|
|
25
|
+
"cycle": {
|
|
26
|
+
"@type": "Cycle",
|
|
27
|
+
"site": {
|
|
28
|
+
"area": "> 0",
|
|
29
|
+
"@type": "Site",
|
|
30
|
+
"management": [{"@type": "Management", "term.termType": "landCover"}],
|
|
31
|
+
"optional": {"country": {"@type": "Term", "termType": "region"}}
|
|
32
|
+
},
|
|
33
|
+
"optional": {
|
|
34
|
+
"otherSitesArea": "> 0",
|
|
35
|
+
"otherSites": [{
|
|
36
|
+
"@type": "Site",
|
|
37
|
+
"management": [{"@type": "Management", "term.termType": "landCover"}],
|
|
38
|
+
"optional": {"country": {"@type": "Term", "termType": "region"}}
|
|
39
|
+
}]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
LOOKUPS = {
|
|
46
|
+
"@doc": "Uses `landCover.csv` for column headers and region-pefTermGrouping-landTransformation-X.csv for to/from CFs. (CFs in `region-pefTermGrouping-landTransformation-from.csv` appear to be the opposite values as those in `region-pefTermGrouping-landTransformation-to.csv` but can be different in some cases)", # noqa: E501
|
|
47
|
+
"region-pefTermGrouping-landTransformation-from": "",
|
|
48
|
+
"region-pefTermGrouping-landTransformation-to": "",
|
|
49
|
+
"landCover": "pefTermGrouping"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
from_lookup_file = f"{list(LOOKUPS.keys())[1]}.csv"
|
|
53
|
+
to_lookup_file = f"{list(LOOKUPS.keys())[2]}.csv"
|
|
54
|
+
|
|
55
|
+
RETURNS = {
|
|
56
|
+
"Indicator": {
|
|
57
|
+
"value": ""
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
TERM_ID = 'soilQualityIndexLandTransformation'
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _indicator(value: float):
|
|
65
|
+
indicator = _new_indicator(TERM_ID, MODEL)
|
|
66
|
+
indicator['value'] = value
|
|
67
|
+
return indicator
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _run(sites: List[dict]):
|
|
71
|
+
result = []
|
|
72
|
+
for site in sites:
|
|
73
|
+
values = [(transformation_from_factor + transformation_to_factor) * hectar_to_square_meter(site['area'])
|
|
74
|
+
for transformation_from_factor, transformation_to_factor in site['transformation_factors']]
|
|
75
|
+
result.append(list_sum(values))
|
|
76
|
+
return _indicator(list_sum(result)) if result else None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _should_run(impact_assessment: dict):
|
|
80
|
+
cycle = impact_assessment.get('cycle', {})
|
|
81
|
+
|
|
82
|
+
has_site = bool(cycle.get('site', {}))
|
|
83
|
+
site_area = cycle.get('site', {}).get("area", False)
|
|
84
|
+
has_area = site_area > 0
|
|
85
|
+
|
|
86
|
+
has_other_sites = bool(cycle.get('otherSites', []))
|
|
87
|
+
|
|
88
|
+
all_sites = non_empty_list([cycle.get('site')] + cycle.get('otherSites', []))
|
|
89
|
+
site_areas = [cycle.get('site', {}).get('area')] + cycle.get('otherSitesArea', [])
|
|
90
|
+
|
|
91
|
+
sites = [
|
|
92
|
+
{
|
|
93
|
+
'site_id': site.get('@id', site.get('id')),
|
|
94
|
+
'transformation_pairs': list(pairwise(
|
|
95
|
+
sorted(filter_list_term_type(site.get("management", []), TermTermType.LANDCOVER.value),
|
|
96
|
+
key=lambda d: safe_parse_date(d['endDate'])))),
|
|
97
|
+
'country_id_str': site.get('country', {}).get('@id', ''),
|
|
98
|
+
'area': site_areas[index] if len(site_areas) >= index + 1 else None,
|
|
99
|
+
}
|
|
100
|
+
for index, site in enumerate(all_sites)
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
sites = [
|
|
104
|
+
site for site in sites
|
|
105
|
+
if all([
|
|
106
|
+
(site.get('area') or 0) > 0,
|
|
107
|
+
site.get('transformation_pairs', [])
|
|
108
|
+
])
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
sites = [
|
|
112
|
+
site |
|
|
113
|
+
{
|
|
114
|
+
'column_names': [(get_pef_grouping(from_transformation['term']['@id']),
|
|
115
|
+
get_pef_grouping(to_transformation['term']['@id']))
|
|
116
|
+
for from_transformation, to_transformation in site['transformation_pairs']],
|
|
117
|
+
} for site in sites
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
has_lookup_column = lambda s: s['column_names'] and all([all(pair) for pair in s['column_names']]) # noqa: E731
|
|
121
|
+
valid_sites = [site for site in sites if has_lookup_column(site)]
|
|
122
|
+
|
|
123
|
+
has_valid_sites = bool(valid_sites)
|
|
124
|
+
|
|
125
|
+
valid_sites = [
|
|
126
|
+
site |
|
|
127
|
+
{
|
|
128
|
+
'country_id': fallback_country(site['country_id_str'],
|
|
129
|
+
[download_lookup(from_lookup_file), download_lookup(to_lookup_file)]
|
|
130
|
+
)
|
|
131
|
+
} for site in valid_sites
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
valid_sites = [
|
|
135
|
+
site |
|
|
136
|
+
{
|
|
137
|
+
'transformation_factors': [(get_coefficient_factor(lookup_name=from_lookup_file,
|
|
138
|
+
country_id=site['country_id'], term_id=TERM_ID,
|
|
139
|
+
occupation_type=from_transformation_header),
|
|
140
|
+
get_coefficient_factor(lookup_name=to_lookup_file,
|
|
141
|
+
country_id=site['country_id'], term_id=TERM_ID,
|
|
142
|
+
occupation_type=to_transformation_header))
|
|
143
|
+
for from_transformation_header, to_transformation_header in site['column_names']]
|
|
144
|
+
} for site in valid_sites
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
log_equivalent_eu_pef_land_use_names = [
|
|
148
|
+
[{
|
|
149
|
+
'site-id': site['site_id'],
|
|
150
|
+
'hestia-term': hestia_term,
|
|
151
|
+
'corine-term': corine_term,
|
|
152
|
+
} for hestia_term, corine_term in site['column_names']
|
|
153
|
+
] for site in valid_sites]
|
|
154
|
+
|
|
155
|
+
log_transformation_factors = [
|
|
156
|
+
[{
|
|
157
|
+
'site-id': site['site_id'],
|
|
158
|
+
'country-id-used-for-factors': site['country_id'],
|
|
159
|
+
'country-id-in-input': site['country_id_str'],
|
|
160
|
+
'factor-from': from_transformation_header,
|
|
161
|
+
'factor-to': to_transformation_header,
|
|
162
|
+
} for from_transformation_header, to_transformation_header in site['transformation_factors']
|
|
163
|
+
] for site in valid_sites]
|
|
164
|
+
|
|
165
|
+
logRequirements(impact_assessment, model=MODEL, term=TERM_ID,
|
|
166
|
+
has_site=has_site,
|
|
167
|
+
has_area=has_area,
|
|
168
|
+
has_other_sites=has_other_sites,
|
|
169
|
+
has_valid_sites=has_valid_sites,
|
|
170
|
+
equivalent_EU_PEF_landUse_names=log_as_table(log_equivalent_eu_pef_land_use_names),
|
|
171
|
+
transformation_factors=log_as_table(log_transformation_factors)
|
|
172
|
+
)
|
|
173
|
+
should_run = has_valid_sites
|
|
174
|
+
|
|
175
|
+
logShouldRun(impact_assessment, MODEL, TERM_ID, should_run)
|
|
176
|
+
|
|
177
|
+
return should_run, valid_sites
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def run(impact_assessment: dict):
|
|
181
|
+
should_run, sites = _should_run(impact_assessment)
|
|
182
|
+
return _run(sites) if should_run else None
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Characterises [soilQualityIndexTotalLandUseEffects](https://hestia.earth/term/soilQualityIndexTotalLandUseEffects)
|
|
3
|
+
based on an updated [LANCA model (De Laurentiis et al. 2019)](
|
|
4
|
+
http://publications.jrc.ec.europa.eu/repository/handle/JRC113865) and on the LANCA (Regionalised) Characterisation
|
|
5
|
+
Factors version 2.5 (Horn and Meier, 2018).
|
|
6
|
+
"""
|
|
7
|
+
from hestia_earth.utils.model import find_term_match
|
|
8
|
+
|
|
9
|
+
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
10
|
+
from . import MODEL
|
|
11
|
+
from ..utils.indicator import _new_indicator
|
|
12
|
+
|
|
13
|
+
REQUIREMENTS = {
|
|
14
|
+
"ImpactAssessment": {
|
|
15
|
+
"emissionsResourceUse": [
|
|
16
|
+
{"@type": "Indicator", "value": "", "term.@id": "soilQualityIndexLandOccupation"},
|
|
17
|
+
{"@type": "Indicator", "value": "", "term.@id": "soilQualityIndexLandTransformation"}
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
RETURNS = {
|
|
23
|
+
"Indicator": {
|
|
24
|
+
"value": "",
|
|
25
|
+
"methodTier": "tier 1",
|
|
26
|
+
"statsDefinition": "modelled"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
TERM_ID = 'soilQualityIndexTotalLandUseEffects'
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _indicator(value: float):
|
|
33
|
+
indicator = _new_indicator(TERM_ID, MODEL)
|
|
34
|
+
indicator['value'] = value
|
|
35
|
+
return indicator
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _run(land_occupation_indicator, transformation_indicator):
|
|
39
|
+
value = land_occupation_indicator['value'] + transformation_indicator['value']
|
|
40
|
+
return _indicator(value) if value else None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _should_run(impactassessment: dict):
|
|
44
|
+
land_occupation_indicator = find_term_match(impactassessment.get('emissionsResourceUse', []),
|
|
45
|
+
"soilQualityIndexLandOccupation")
|
|
46
|
+
transformation_indicator = find_term_match(impactassessment.get('emissionsResourceUse', []),
|
|
47
|
+
"soilQualityIndexLandTransformation")
|
|
48
|
+
|
|
49
|
+
has_valid_values = all([isinstance(land_occupation_indicator.get('value', None), (int, float)),
|
|
50
|
+
isinstance(transformation_indicator.get('value', None), (int, float))])
|
|
51
|
+
|
|
52
|
+
logRequirements(impactassessment, model=MODEL, term=TERM_ID,
|
|
53
|
+
transformation_indicator=transformation_indicator,
|
|
54
|
+
land_occupation_indicator=land_occupation_indicator,
|
|
55
|
+
has_valid_values=has_valid_values
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
should_run = all([transformation_indicator, land_occupation_indicator, has_valid_values])
|
|
59
|
+
|
|
60
|
+
logShouldRun(impactassessment, MODEL, TERM_ID, should_run)
|
|
61
|
+
return should_run, land_occupation_indicator, transformation_indicator
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def run(impactassessment: dict):
|
|
65
|
+
should_run, land_occupation_indicator, transformation_indicator = _should_run(impactassessment)
|
|
66
|
+
return _run(land_occupation_indicator, transformation_indicator) if should_run else None
|
|
@@ -12,6 +12,6 @@ def get_coefficient_factor(lookup_name: str, country_id: str, occupation_type: O
|
|
|
12
12
|
"""
|
|
13
13
|
coefficient = get_table_value(download_lookup(lookup_name), 'termid', country_id, column_name(occupation_type))
|
|
14
14
|
debugMissingLookup(
|
|
15
|
-
lookup_name, 'termid', country_id,
|
|
15
|
+
lookup_name, 'termid', country_id, occupation_type, coefficient, model=MODEL, term=term_id
|
|
16
16
|
)
|
|
17
17
|
return coefficient
|
|
@@ -45,6 +45,8 @@ def _run(impact_assessment: dict, term_id: str, land_occupation_m2: float, facto
|
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
def _should_run(impact_assessment: dict, term_id: str, from_site_type: SiteSiteType, years: int):
|
|
48
|
+
site = get_site(impact_assessment)
|
|
49
|
+
has_site = bool(site)
|
|
48
50
|
cycle = impact_assessment.get('cycle', {})
|
|
49
51
|
product = get_product(impact_assessment)
|
|
50
52
|
site = get_site(impact_assessment)
|
|
@@ -52,10 +54,12 @@ def _should_run(impact_assessment: dict, term_id: str, from_site_type: SiteSiteT
|
|
|
52
54
|
land_transformation_factor = _get_emission_factor(term_id, impact_assessment, years, from_site_type)
|
|
53
55
|
|
|
54
56
|
logRequirements(impact_assessment, model=MODEL, term=term_id,
|
|
57
|
+
has_site=has_site,
|
|
55
58
|
land_occupation_m2_kg=land_occupation_m2_kg,
|
|
56
59
|
land_transformation_factor=land_transformation_factor)
|
|
57
60
|
|
|
58
61
|
should_run = all([
|
|
62
|
+
has_site,
|
|
59
63
|
land_occupation_m2_kg is not None,
|
|
60
64
|
land_occupation_m2_kg == 0 or land_transformation_factor is not None
|
|
61
65
|
])
|
|
@@ -137,10 +137,12 @@ LOOKUPS = {
|
|
|
137
137
|
"isWoolProducingAnimal"
|
|
138
138
|
],
|
|
139
139
|
"system-liveAnimal-activityCoefficient-ipcc2019": "using animal term @id",
|
|
140
|
+
"landCover": "grazedPastureGrassInputId",
|
|
140
141
|
"crop-property": ["energyDigestibilityRuminants", "energyContentHigherHeatingValue"],
|
|
141
142
|
"crop": "grazedPastureGrassInputId",
|
|
142
143
|
"forage-property": ["energyDigestibilityRuminants", "energyContentHigherHeatingValue"],
|
|
143
|
-
"
|
|
144
|
+
"feedFoodAdditive": "hasEnergyContent",
|
|
145
|
+
"feedFoodAdditive-property": ["energyDigestibilityRuminants", "energyContentHigherHeatingValue"]
|
|
144
146
|
}
|
|
145
147
|
RETURNS = {
|
|
146
148
|
"Animal": [{
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
from hestia_earth.schema import CycleFunctionalUnit, EmissionMethodTier, MeasurementMethodClassification, SiteSiteType
|
|
2
|
+
|
|
3
|
+
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
4
|
+
from hestia_earth.models.utils.blank_node import cumulative_nodes_term_match
|
|
5
|
+
from hestia_earth.models.utils.emission import _new_emission
|
|
6
|
+
|
|
7
|
+
from .co2ToAirCarbonStockChange_utils import create_run_function, create_should_run_function
|
|
8
|
+
from . import MODEL
|
|
9
|
+
|
|
10
|
+
REQUIREMENTS = {
|
|
11
|
+
"Cycle": {
|
|
12
|
+
"site": {
|
|
13
|
+
"measurements": [
|
|
14
|
+
{
|
|
15
|
+
"@type": "Measurement",
|
|
16
|
+
"value": "",
|
|
17
|
+
"dates": "",
|
|
18
|
+
"depthUpper": "0",
|
|
19
|
+
"depthLower": "30",
|
|
20
|
+
"term.@id": "aboveGroundBiomass"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
"functionalUnit": "1 ha",
|
|
25
|
+
"endDate": "",
|
|
26
|
+
"optional": {
|
|
27
|
+
"startDate": ""
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
RETURNS = {
|
|
32
|
+
"Emission": [{
|
|
33
|
+
"value": "",
|
|
34
|
+
"sd": "",
|
|
35
|
+
"min": "",
|
|
36
|
+
"max": "",
|
|
37
|
+
"statsDefinition": "simulated",
|
|
38
|
+
"observations": "",
|
|
39
|
+
"methodTier": ""
|
|
40
|
+
}]
|
|
41
|
+
}
|
|
42
|
+
TERM_ID = 'co2ToAirAboveGroundBiomassStockChangeLandUseChange'
|
|
43
|
+
|
|
44
|
+
_CARBON_STOCK_TERM_ID = 'aboveGroundBiomass'
|
|
45
|
+
|
|
46
|
+
_MEASUREMENT_METHOD_RANKING = [
|
|
47
|
+
MeasurementMethodClassification.ON_SITE_PHYSICAL_MEASUREMENT,
|
|
48
|
+
MeasurementMethodClassification.MODELLED_USING_OTHER_MEASUREMENTS,
|
|
49
|
+
MeasurementMethodClassification.TIER_3_MODEL,
|
|
50
|
+
MeasurementMethodClassification.TIER_2_MODEL,
|
|
51
|
+
MeasurementMethodClassification.TIER_1_MODEL,
|
|
52
|
+
MeasurementMethodClassification.GEOSPATIAL_DATASET
|
|
53
|
+
]
|
|
54
|
+
"""
|
|
55
|
+
The list of `MeasurementMethodClassification`s that can be used to calculate SOC stock change emissions, ranked in
|
|
56
|
+
order from strongest to weakest.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
_SITE_TYPE_SYSTEMS_MAPPING = {
|
|
60
|
+
SiteSiteType.GLASS_OR_HIGH_ACCESSIBLE_COVER.value: [
|
|
61
|
+
"protectedCroppingSystemSoilBased",
|
|
62
|
+
"protectedCroppingSystemSoilAndSubstrateBased"
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _emission(
|
|
68
|
+
*,
|
|
69
|
+
value: list[float],
|
|
70
|
+
method_tier: EmissionMethodTier,
|
|
71
|
+
sd: list[float] = None,
|
|
72
|
+
min: list[float] = None,
|
|
73
|
+
max: list[float] = None,
|
|
74
|
+
statsDefinition: str = None,
|
|
75
|
+
observations: list[int] = None
|
|
76
|
+
) -> dict:
|
|
77
|
+
"""
|
|
78
|
+
Create an emission node based on the provided value and method tier.
|
|
79
|
+
|
|
80
|
+
See [Emission schema](https://www.hestia.earth/schema/Emission) for more information.
|
|
81
|
+
|
|
82
|
+
Parameters
|
|
83
|
+
----------
|
|
84
|
+
value : float
|
|
85
|
+
The emission value (kg CO2 ha-1).
|
|
86
|
+
sd : float
|
|
87
|
+
The standard deviation (kg CO2 ha-1).
|
|
88
|
+
method_tier : EmissionMethodTier
|
|
89
|
+
The emission method tier.
|
|
90
|
+
|
|
91
|
+
Returns
|
|
92
|
+
-------
|
|
93
|
+
dict
|
|
94
|
+
The emission dictionary with keys 'depth', 'value', and 'methodTier'.
|
|
95
|
+
"""
|
|
96
|
+
update_dict = {
|
|
97
|
+
"value": value,
|
|
98
|
+
"sd": sd,
|
|
99
|
+
"min": min,
|
|
100
|
+
"max": max,
|
|
101
|
+
"statsDefinition": statsDefinition,
|
|
102
|
+
"observations": observations,
|
|
103
|
+
"methodTier": method_tier.value
|
|
104
|
+
}
|
|
105
|
+
emission = _new_emission(TERM_ID, MODEL) | {
|
|
106
|
+
key: value for key, value in update_dict.items() if value
|
|
107
|
+
}
|
|
108
|
+
return emission
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def run(cycle: dict) -> list[dict]:
|
|
112
|
+
"""
|
|
113
|
+
Run the `ipcc2019.co2ToAirAboveGroundBiomassStockChangeManagementChange`.
|
|
114
|
+
|
|
115
|
+
Parameters
|
|
116
|
+
----------
|
|
117
|
+
cycle : dict
|
|
118
|
+
A HESTIA (Cycle node)[https://www.hestia.earth/schema/Cycle].
|
|
119
|
+
|
|
120
|
+
Returns
|
|
121
|
+
-------
|
|
122
|
+
list[dict]
|
|
123
|
+
A list of [Emission nodes](https://www.hestia.earth/schema/Emission) containing model results.
|
|
124
|
+
"""
|
|
125
|
+
should_run_exec = create_should_run_function(
|
|
126
|
+
_CARBON_STOCK_TERM_ID,
|
|
127
|
+
_should_compile_inventory_func,
|
|
128
|
+
measurement_method_ranking=_MEASUREMENT_METHOD_RANKING
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
run_exec = create_run_function(_emission)
|
|
132
|
+
|
|
133
|
+
should_run, cycle_id, inventory, logs = should_run_exec(cycle)
|
|
134
|
+
|
|
135
|
+
logRequirements(cycle, model=MODEL, term=TERM_ID, **logs)
|
|
136
|
+
logShouldRun(cycle, MODEL, TERM_ID, should_run)
|
|
137
|
+
|
|
138
|
+
return run_exec(cycle_id, inventory) if should_run else []
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _should_compile_inventory_func(
|
|
142
|
+
site: dict, cycles: list[dict], carbon_stock_measurements: list[dict]
|
|
143
|
+
) -> tuple[bool, dict]:
|
|
144
|
+
"""
|
|
145
|
+
Determine whether a site is suitable and has enough data to compile a carbon stock inventory.
|
|
146
|
+
|
|
147
|
+
Parameters
|
|
148
|
+
----------
|
|
149
|
+
site : dict
|
|
150
|
+
A HESTIA (Site node)[https://www.hestia.earth/schema/Site]
|
|
151
|
+
cycles : list[dict]
|
|
152
|
+
A list of HESTIA (Cycle nodes)[https://www.hestia.earth/schema/Cycle] that are related to the site.
|
|
153
|
+
carbon_stock_measurements : list[dict]
|
|
154
|
+
A list of HESTIA carbon stock (Measurement nodes)[https://www.hestia.earth/schema/Measurement] that are related
|
|
155
|
+
to the site.
|
|
156
|
+
|
|
157
|
+
Returns
|
|
158
|
+
-------
|
|
159
|
+
tuple[bool, dict]
|
|
160
|
+
`(should_run, logs)`.
|
|
161
|
+
"""
|
|
162
|
+
site_type = site.get("siteType")
|
|
163
|
+
has_soil = site_type not in _SITE_TYPE_SYSTEMS_MAPPING or all(
|
|
164
|
+
cumulative_nodes_term_match(
|
|
165
|
+
cycle.get("practices", []),
|
|
166
|
+
target_term_ids=_SITE_TYPE_SYSTEMS_MAPPING[site_type],
|
|
167
|
+
cumulative_threshold=0
|
|
168
|
+
) for cycle in cycles
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
has_stock_measurements = len(carbon_stock_measurements) > 0
|
|
172
|
+
has_cycles = len(cycles) > 0
|
|
173
|
+
has_functional_unit_1_ha = all(cycle.get('functionalUnit') == CycleFunctionalUnit._1_HA.value for cycle in cycles)
|
|
174
|
+
|
|
175
|
+
should_run = all([
|
|
176
|
+
has_soil,
|
|
177
|
+
has_stock_measurements,
|
|
178
|
+
has_cycles,
|
|
179
|
+
has_functional_unit_1_ha
|
|
180
|
+
])
|
|
181
|
+
|
|
182
|
+
logs = {
|
|
183
|
+
"site_type": site_type,
|
|
184
|
+
"has_soil": has_soil,
|
|
185
|
+
"carbon_stock_term": _CARBON_STOCK_TERM_ID,
|
|
186
|
+
"has_stock_measurements": has_stock_measurements,
|
|
187
|
+
"has_cycles": has_cycles,
|
|
188
|
+
"has_functional_unit_1_ha": has_functional_unit_1_ha,
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return should_run, logs
|