hestia-earth-models 0.74.3__py3-none-any.whl → 0.74.5__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/cml2001Baseline/abioticResourceDepletionMineralsAndMetals.py +0 -1
- hestia_earth/models/config/Cycle.json +15 -0
- hestia_earth/models/config/ImpactAssessment.json +30 -11
- hestia_earth/models/cycle/animal/input/hestiaAggregatedData.py +3 -3
- hestia_earth/models/cycle/completeness/seed.py +1 -1
- hestia_earth/models/cycle/input/hestiaAggregatedData.py +25 -16
- hestia_earth/models/data/hestiaAggregatedData/__init__.py +73 -0
- hestia_earth/models/environmentalFootprintV3_1/scarcityWeightedWaterUse.py +1 -1
- hestia_earth/models/environmentalFootprintV3_1/soilQualityIndexLandOccupation.py +5 -6
- hestia_earth/models/environmentalFootprintV3_1/soilQualityIndexLandTransformation.py +10 -13
- hestia_earth/models/fantkeEtAl2016/damageToHumanHealthParticulateMatterFormation.py +1 -1
- hestia_earth/models/hestia/landCover.py +24 -0
- hestia_earth/models/hestia/landOccupationDuringCycle.py +80 -51
- hestia_earth/models/hestia/landTransformation100YearAverageDuringCycle.py +7 -1
- hestia_earth/models/hestia/landTransformation20YearAverageDuringCycle.py +7 -1
- hestia_earth/models/hestia/resourceUse_utils.py +58 -119
- hestia_earth/models/hestia/waterSalinity.py +57 -12
- hestia_earth/models/impact_assessment/post_checks/__init__.py +3 -2
- hestia_earth/models/impact_assessment/post_checks/remove_cache_fields.py +9 -0
- hestia_earth/models/impact_assessment/pre_checks/cache_emissionsResourceUse.py +21 -0
- hestia_earth/models/impact_assessment/pre_checks/cycle.py +5 -0
- hestia_earth/models/ipcc2019/co2ToAirAboveGroundBiomassStockChange.py +6 -64
- hestia_earth/models/ipcc2019/co2ToAirBelowGroundBiomassStockChange.py +9 -87
- hestia_earth/models/ipcc2019/co2ToAirBiocharStockChange.py +140 -0
- hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +329 -217
- hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChange.py +10 -87
- hestia_earth/models/mocking/__init__.py +2 -2
- hestia_earth/models/mocking/mock_search.py +20 -10
- hestia_earth/models/mocking/search-results.json +1 -7679
- hestia_earth/models/pooreNemecek2018/landOccupationDuringCycle.py +8 -7
- hestia_earth/models/poschEtAl2008/terrestrialAcidificationPotentialAccumulatedExceedance.py +1 -1
- hestia_earth/models/poschEtAl2008/terrestrialEutrophicationPotentialAccumulatedExceedance.py +1 -1
- hestia_earth/models/preload_requests.py +18 -4
- hestia_earth/models/schmidt2007/utils.py +3 -3
- hestia_earth/models/utils/__init__.py +4 -1
- hestia_earth/models/utils/aggregated.py +21 -68
- hestia_earth/models/utils/cycle.py +3 -3
- hestia_earth/models/utils/impact_assessment.py +45 -41
- hestia_earth/models/utils/lookup.py +92 -67
- hestia_earth/models/version.py +1 -1
- hestia_earth/orchestrator/models/__init__.py +47 -10
- hestia_earth/orchestrator/models/transformations.py +3 -1
- hestia_earth/orchestrator/strategies/merge/__init__.py +1 -2
- hestia_earth/orchestrator/strategies/merge/merge_list.py +31 -8
- hestia_earth/orchestrator/utils.py +29 -0
- {hestia_earth_models-0.74.3.dist-info → hestia_earth_models-0.74.5.dist-info}/METADATA +2 -3
- {hestia_earth_models-0.74.3.dist-info → hestia_earth_models-0.74.5.dist-info}/RECORD +62 -55
- tests/models/cycle/animal/input/test_hestiaAggregatedData.py +3 -3
- tests/models/cycle/input/test_hestiaAggregatedData.py +9 -18
- tests/models/data/__init__.py +0 -0
- tests/models/data/test_hestiaAggregatedData.py +32 -0
- tests/models/hestia/test_landCover.py +32 -1
- tests/models/hestia/test_waterSalinity.py +16 -4
- tests/models/ipcc2019/test_co2ToAirAboveGroundBiomassStockChange.py +1 -6
- tests/models/ipcc2019/test_co2ToAirBelowGroundBiomassStockChange.py +1 -6
- tests/models/ipcc2019/test_co2ToAirBiocharStockChange.py +90 -0
- tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChange.py +1 -6
- tests/models/pooreNemecek2018/test_landOccupationDuringCycle.py +1 -0
- tests/orchestrator/strategies/merge/test_merge_list.py +5 -0
- {hestia_earth_models-0.74.3.dist-info → hestia_earth_models-0.74.5.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.74.3.dist-info → hestia_earth_models-0.74.5.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.74.3.dist-info → hestia_earth_models-0.74.5.dist-info}/top_level.txt +0 -0
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
from dateutil.relativedelta import relativedelta
|
|
3
3
|
from hestia_earth.schema import TermTermType
|
|
4
|
-
from hestia_earth.utils.tools import to_precision
|
|
4
|
+
from hestia_earth.utils.tools import to_precision, list_sum
|
|
5
5
|
|
|
6
|
-
from hestia_earth.models.log import logRequirements, logShouldRun,
|
|
6
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
|
|
7
7
|
from hestia_earth.models.utils.blank_node import _gapfill_datestr, DatestrGapfillMode, DatestrFormat, _str_dates_match
|
|
8
8
|
from hestia_earth.models.utils.impact_assessment import get_site
|
|
9
9
|
from hestia_earth.models.utils.indicator import _new_indicator
|
|
10
|
-
from .utils import
|
|
11
|
-
LAND_USE_TERMS_FOR_TRANSFORMATION,
|
|
12
|
-
crop_ipcc_land_use_category,
|
|
13
|
-
)
|
|
10
|
+
from .utils import LAND_USE_TERMS_FOR_TRANSFORMATION, crop_ipcc_land_use_category
|
|
14
11
|
from . import MODEL
|
|
15
12
|
|
|
16
13
|
_MAXIMUM_OFFSET_DAYS = 365 * 2
|
|
@@ -41,7 +38,7 @@ def _gap_filled_date_obj(date_str: str) -> datetime:
|
|
|
41
38
|
)
|
|
42
39
|
|
|
43
40
|
|
|
44
|
-
def
|
|
41
|
+
def _find_closest_node_date(
|
|
45
42
|
ia_date_str: str,
|
|
46
43
|
management_nodes: list,
|
|
47
44
|
historic_date_offset: int,
|
|
@@ -62,32 +59,22 @@ def _find_closest_node(
|
|
|
62
59
|
return filtered_dates[min(filtered_dates.keys())] if filtered_dates else ""
|
|
63
60
|
|
|
64
61
|
|
|
65
|
-
def should_run(
|
|
66
|
-
impact_assessment
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
) -> tuple[bool, dict, str, str]:
|
|
71
|
-
relevant_emission_resource_use = [
|
|
72
|
-
node for node in impact_assessment.get("emissionsResourceUse", [])
|
|
73
|
-
if node.get("term", {}).get("@id", "") == _RESOURCE_USE_TERM_ID and node.get("value", -1) >= 0
|
|
74
|
-
]
|
|
62
|
+
def should_run(impact_assessment: dict, term_id: str, historic_date_offset: int) -> tuple[bool, dict, str, str]:
|
|
63
|
+
cycle = impact_assessment.get('cycle', {})
|
|
64
|
+
has_otherSites = len(cycle.get('otherSites') or []) != 0
|
|
65
|
+
|
|
66
|
+
site = get_site(impact_assessment)
|
|
75
67
|
filtered_management_nodes = [
|
|
76
68
|
node for node in site.get("management", [])
|
|
77
|
-
if node.get("value", -1) >= 0 and node.get("term", {}).get("termType"
|
|
69
|
+
if node.get("value", -1) >= 0 and node.get("term", {}).get("termType") == TermTermType.LANDCOVER.value
|
|
78
70
|
]
|
|
79
|
-
land_occupation_during_cycle_found = any(
|
|
80
|
-
node.get("term", {}).get("@id") in
|
|
81
|
-
{node.get("landCover", {}).get("@id") for node in relevant_emission_resource_use}
|
|
82
|
-
for node in filtered_management_nodes
|
|
83
|
-
)
|
|
84
71
|
match_mode = (
|
|
85
72
|
DatestrGapfillMode.START if impact_assessment.get("cycle", {}).get("aggregated") is True
|
|
86
73
|
else DatestrGapfillMode.END
|
|
87
74
|
)
|
|
88
75
|
match_date = "startDate" if match_mode == DatestrGapfillMode.START else "endDate"
|
|
89
76
|
|
|
90
|
-
closest_date =
|
|
77
|
+
closest_date = _find_closest_node_date(
|
|
91
78
|
ia_date_str=impact_assessment.get(match_date, ""),
|
|
92
79
|
management_nodes=filtered_management_nodes,
|
|
93
80
|
historic_date_offset=historic_date_offset,
|
|
@@ -104,98 +91,53 @@ def should_run(
|
|
|
104
91
|
None
|
|
105
92
|
)
|
|
106
93
|
current_node = filtered_management_nodes.pop(current_node_index) if current_node_index is not None else None
|
|
94
|
+
landCover_term_id = (current_node or {}).get('term', {}).get('@id')
|
|
107
95
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
should_run_result = all([
|
|
115
|
-
relevant_emission_resource_use,
|
|
116
|
-
land_occupation_during_cycle_found,
|
|
117
|
-
current_node,
|
|
118
|
-
closest_end_date or closest_start_date
|
|
119
|
-
])
|
|
120
|
-
logShouldRun(impact_assessment, MODEL, term=term_id, should_run=should_run_result)
|
|
121
|
-
|
|
122
|
-
return should_run_result, current_node, closest_end_date, closest_start_date
|
|
96
|
+
prior_management_nodes = [
|
|
97
|
+
node for node in filtered_management_nodes
|
|
98
|
+
if _str_dates_match(node.get("endDate", ""), closest_end_date) or
|
|
99
|
+
_str_dates_match(node.get("startDate", ""), closest_start_date)
|
|
100
|
+
]
|
|
123
101
|
|
|
102
|
+
ipcc_land_use_category = crop_ipcc_land_use_category(landCover_term_id)
|
|
124
103
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
Returns the sum of all land occupation for the specified land_use_category.
|
|
128
|
-
"""
|
|
129
|
-
return sum(
|
|
130
|
-
node.get("value", 0) for node in impact_assessment.get("emissionsResourceUse", [])
|
|
104
|
+
total_landOccupationDuringCycle = list_sum([
|
|
105
|
+
node.get("value") for node in impact_assessment.get("emissionsResourceUse", [])
|
|
131
106
|
if node.get("term", {}).get("@id", "") == _RESOURCE_USE_TERM_ID
|
|
132
107
|
and crop_ipcc_land_use_category(node.get("landCover", {}).get("@id", "")) == ipcc_land_use_category
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def _calculate_indicator_value(
|
|
137
|
-
impact_assessment: dict,
|
|
138
|
-
term_id: str,
|
|
139
|
-
management_nodes: list,
|
|
140
|
-
ipcc_land_use_category: str,
|
|
141
|
-
previous_land_cover_id: str,
|
|
142
|
-
historic_date_offset: int
|
|
143
|
-
) -> float:
|
|
144
|
-
"""
|
|
145
|
-
Land transformation from [land type] previous management nodes
|
|
146
|
-
= (Land occupation, during Cycle * Historic Site Percentage Area [land type] / 100) / HISTORIC_DATE_OFFSET
|
|
147
|
-
"""
|
|
148
|
-
land_occupation_for_cycle = _get_land_occupation_for_land_use_type(
|
|
149
|
-
impact_assessment=impact_assessment,
|
|
150
|
-
ipcc_land_use_category=ipcc_land_use_category
|
|
151
|
-
)
|
|
152
|
-
historical_land_use = sum(
|
|
153
|
-
node.get("value", 0) for node in management_nodes
|
|
154
|
-
if node.get("term", {}).get("@id", "") == previous_land_cover_id
|
|
155
|
-
)
|
|
156
|
-
|
|
157
|
-
debugValues(impact_assessment, model=MODEL, term=term_id,
|
|
158
|
-
ipcc_land_use_category=ipcc_land_use_category,
|
|
159
|
-
land_occupation_for_cycle=land_occupation_for_cycle,
|
|
160
|
-
historical_land_use=historical_land_use,
|
|
161
|
-
historic_date_offset=historic_date_offset)
|
|
108
|
+
], default=None)
|
|
162
109
|
|
|
163
|
-
|
|
110
|
+
indicators = [
|
|
111
|
+
{
|
|
112
|
+
'landCover-id': landCover_term_id,
|
|
113
|
+
'previous-landCover-id': previous_land_cover_id,
|
|
114
|
+
'landOccupationDuringCycle': total_landOccupationDuringCycle,
|
|
115
|
+
'historical-landUse-change': list_sum([
|
|
116
|
+
node.get("value") for node in prior_management_nodes
|
|
117
|
+
if node.get("term", {}).get("@id") == previous_land_cover_id
|
|
118
|
+
], default=None),
|
|
119
|
+
}
|
|
120
|
+
for previous_land_cover_id in [t[0] for t in LAND_USE_TERMS_FOR_TRANSFORMATION.values()]
|
|
121
|
+
] if landCover_term_id else []
|
|
122
|
+
valid_indicators = [indicator for indicator in indicators if indicator['historical-landUse-change'] is not None]
|
|
164
123
|
|
|
124
|
+
logRequirements(impact_assessment, model=MODEL, term=term_id,
|
|
125
|
+
has_otherSites=has_otherSites,
|
|
126
|
+
closest_end_date=closest_end_date,
|
|
127
|
+
closest_start_date=closest_start_date,
|
|
128
|
+
landCover_term_id=landCover_term_id,
|
|
129
|
+
ipcc_land_use_category=ipcc_land_use_category,
|
|
130
|
+
indicators=log_as_table(indicators))
|
|
165
131
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
historic_date_offset: int
|
|
174
|
-
) -> list:
|
|
175
|
-
"""
|
|
176
|
-
Calculate land transformation for all land use categories.
|
|
177
|
-
"""
|
|
178
|
-
indicators = [
|
|
179
|
-
_new_indicator_with_value(
|
|
180
|
-
term_id=term_id,
|
|
181
|
-
land_cover_id=current_node.get("term", {}).get("@id"),
|
|
182
|
-
previous_land_cover_id=previous_land_cover_id,
|
|
183
|
-
value=_calculate_indicator_value(
|
|
184
|
-
impact_assessment=impact_assessment,
|
|
185
|
-
term_id=term_id,
|
|
186
|
-
management_nodes=[
|
|
187
|
-
node for node in site.get("management", [])
|
|
188
|
-
if _str_dates_match(node.get("endDate", ""), closest_end_date) or
|
|
189
|
-
_str_dates_match(node.get("startDate", ""), closest_start_date)
|
|
190
|
-
],
|
|
191
|
-
ipcc_land_use_category=crop_ipcc_land_use_category(current_node.get("term", {}).get("@id", "")),
|
|
192
|
-
previous_land_cover_id=previous_land_cover_id,
|
|
193
|
-
historic_date_offset=historic_date_offset
|
|
194
|
-
)
|
|
195
|
-
) for previous_land_cover_id in [t[0] for t in LAND_USE_TERMS_FOR_TRANSFORMATION.values()]
|
|
196
|
-
]
|
|
132
|
+
should_run_result = all([
|
|
133
|
+
not has_otherSites,
|
|
134
|
+
ipcc_land_use_category,
|
|
135
|
+
total_landOccupationDuringCycle is not None,
|
|
136
|
+
valid_indicators
|
|
137
|
+
])
|
|
138
|
+
logShouldRun(impact_assessment, MODEL, term=term_id, should_run=should_run_result)
|
|
197
139
|
|
|
198
|
-
return
|
|
140
|
+
return should_run_result, valid_indicators
|
|
199
141
|
|
|
200
142
|
|
|
201
143
|
def run_resource_use(
|
|
@@ -203,19 +145,16 @@ def run_resource_use(
|
|
|
203
145
|
historic_date_offset: int,
|
|
204
146
|
term_id: str
|
|
205
147
|
) -> list:
|
|
206
|
-
|
|
207
|
-
_should_run, current_node, closest_end_date, closest_start_date = should_run(
|
|
148
|
+
_should_run, indicators = should_run(
|
|
208
149
|
impact_assessment=impact_assessment,
|
|
209
|
-
site=site,
|
|
210
150
|
term_id=term_id,
|
|
211
151
|
historic_date_offset=historic_date_offset
|
|
212
152
|
)
|
|
213
|
-
return
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
) if _should_run else []
|
|
153
|
+
return [
|
|
154
|
+
_new_indicator_with_value(
|
|
155
|
+
term_id=term_id,
|
|
156
|
+
land_cover_id=i['landCover-id'],
|
|
157
|
+
previous_land_cover_id=i['previous-landCover-id'],
|
|
158
|
+
value=(i['landOccupationDuringCycle'] * i['historical-landUse-change'] / 100) / historic_date_offset
|
|
159
|
+
) for i in indicators
|
|
160
|
+
] if _should_run else []
|
|
@@ -4,6 +4,7 @@ from hestia_earth.utils.tools import safe_parse_float, list_average, non_empty_l
|
|
|
4
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
|
+
from hestia_earth.models.utils import is_from_model
|
|
7
8
|
from hestia_earth.models.utils.measurement import _new_measurement
|
|
8
9
|
from hestia_earth.models.utils.site import related_cycles
|
|
9
10
|
from hestia_earth.models.utils.term import get_lookup_value
|
|
@@ -11,14 +12,21 @@ from . import MODEL
|
|
|
11
12
|
|
|
12
13
|
REQUIREMENTS = {
|
|
13
14
|
"Site": {
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"@type": "
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
"optional": {
|
|
16
|
+
"measurements": [{
|
|
17
|
+
"@type": "Measurement",
|
|
18
|
+
"term.@id": ["brackishWater", "salineWater", "freshWater"],
|
|
19
|
+
"value": "true"
|
|
20
|
+
}],
|
|
21
|
+
"related": {
|
|
22
|
+
"Cycle": {
|
|
23
|
+
"@type": "Cycle",
|
|
24
|
+
"products": [{
|
|
25
|
+
"@type": "Product",
|
|
26
|
+
"primary": "True",
|
|
27
|
+
"term.termType": "liveAquaticSpecies"
|
|
28
|
+
}]
|
|
29
|
+
}
|
|
22
30
|
}
|
|
23
31
|
}
|
|
24
32
|
}
|
|
@@ -35,19 +43,26 @@ LOOKUPS = {
|
|
|
35
43
|
"liveAquaticSpecies": "defaultSalinity"
|
|
36
44
|
}
|
|
37
45
|
TERM_ID = 'waterSalinity'
|
|
46
|
+
_MEASUREMENT_TERM_IDS = ["brackishWater", "salineWater", "freshWater"]
|
|
47
|
+
_DEFAULT_SALINITY = {
|
|
48
|
+
'freshWater': 0,
|
|
49
|
+
'brackishWater': 5000,
|
|
50
|
+
'salineWater': 32000
|
|
51
|
+
}
|
|
38
52
|
|
|
39
53
|
|
|
40
54
|
def _measurement(value: float, start_date: str = None, end_date: str = None):
|
|
41
55
|
data = _new_measurement(TERM_ID, MODEL)
|
|
42
56
|
data['value'] = [value]
|
|
43
|
-
data['endDate'] = end_date
|
|
44
57
|
if start_date:
|
|
45
58
|
data['startDate'] = start_date
|
|
59
|
+
if end_date:
|
|
60
|
+
data['endDate'] = end_date
|
|
46
61
|
data['methodClassification'] = MeasurementMethodClassification.EXPERT_OPINION.value
|
|
47
62
|
return data if value is not None else None
|
|
48
63
|
|
|
49
64
|
|
|
50
|
-
def
|
|
65
|
+
def _should_run_by_products(site: dict):
|
|
51
66
|
cycles = related_cycles(site)
|
|
52
67
|
relevant_products = [
|
|
53
68
|
{
|
|
@@ -72,8 +87,8 @@ def _should_run(site: dict):
|
|
|
72
87
|
return should_run, relevant_products
|
|
73
88
|
|
|
74
89
|
|
|
75
|
-
def
|
|
76
|
-
should_run, values =
|
|
90
|
+
def _run_by_products(site: dict):
|
|
91
|
+
should_run, values = _should_run_by_products(site)
|
|
77
92
|
grouped_values = group_by_keys(values, ['start-date', 'end-date'])
|
|
78
93
|
return non_empty_list([
|
|
79
94
|
_measurement(
|
|
@@ -83,3 +98,33 @@ def run(site: dict):
|
|
|
83
98
|
)
|
|
84
99
|
for value in grouped_values.values()
|
|
85
100
|
]) if should_run else []
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _should_run_by_measurement(site: dict):
|
|
104
|
+
measurements = [
|
|
105
|
+
m for m in site.get('measurements', [])
|
|
106
|
+
if all([
|
|
107
|
+
m.get('term', {}).get('@id') in _MEASUREMENT_TERM_IDS,
|
|
108
|
+
not is_from_model(m),
|
|
109
|
+
m.get('value')
|
|
110
|
+
])
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
logRequirements(site, model=MODEL, term=TERM_ID,
|
|
114
|
+
existing_measurements=';'.join([m.get('term', {}).get('@id') for m in measurements]))
|
|
115
|
+
|
|
116
|
+
should_run = all([measurements])
|
|
117
|
+
logShouldRun(site, MODEL, TERM_ID, should_run)
|
|
118
|
+
return should_run, measurements
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _run_by_measurement(site: dict):
|
|
122
|
+
should_run, measurements = _should_run_by_measurement(site)
|
|
123
|
+
return [
|
|
124
|
+
_measurement(_DEFAULT_SALINITY[m.get('term', {}).get('@id')], m.get('startDate'), m.get('endDate'))
|
|
125
|
+
for m in measurements
|
|
126
|
+
] if should_run else []
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def run(site: dict):
|
|
130
|
+
return _run_by_measurement(site) or _run_by_products(site)
|
|
@@ -2,14 +2,15 @@ from os.path import dirname, abspath
|
|
|
2
2
|
import sys
|
|
3
3
|
|
|
4
4
|
from hestia_earth.models.utils import _run_in_serie
|
|
5
|
-
from . import cycle, site
|
|
5
|
+
from . import cycle, site, remove_cache_fields
|
|
6
6
|
|
|
7
7
|
CURRENT_DIR = dirname(abspath(__file__)) + '/'
|
|
8
8
|
sys.path.append(CURRENT_DIR)
|
|
9
9
|
|
|
10
10
|
MODELS = [
|
|
11
11
|
cycle.run,
|
|
12
|
-
site.run
|
|
12
|
+
site.run,
|
|
13
|
+
remove_cache_fields.run
|
|
13
14
|
]
|
|
14
15
|
|
|
15
16
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from hestia_earth.utils.blank_node import group_by_keys, get_node_value, ArrayTreatment
|
|
2
|
+
from hestia_earth.utils.tools import non_empty_list
|
|
3
|
+
|
|
4
|
+
_GROUP_KEYS = ['term', 'methodModel', 'inputs', 'operation', 'country', 'region']
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _grouped_blank_node(blank_nodes: list):
|
|
8
|
+
blank_node = {
|
|
9
|
+
key: blank_nodes[0].get(key) for key in _GROUP_KEYS if blank_nodes[0].get(key)
|
|
10
|
+
} | {
|
|
11
|
+
'value': non_empty_list([v.get('value') for v in blank_nodes])
|
|
12
|
+
}
|
|
13
|
+
value = get_node_value(blank_node, default_array_treatment=ArrayTreatment.SUM)
|
|
14
|
+
return blank_node | {'value': value}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def run(impact: dict):
|
|
18
|
+
blank_nodes = impact.get('emissionsResourceUse', [])
|
|
19
|
+
grouped_blank_nodes = group_by_keys(blank_nodes, _GROUP_KEYS)
|
|
20
|
+
grouped_blank_nodes = [_grouped_blank_node(value) for value in grouped_blank_nodes.values()]
|
|
21
|
+
return grouped_blank_nodes
|
|
@@ -23,6 +23,11 @@ def _run(impact: dict):
|
|
|
23
23
|
site = _load_calculated_node(cycle.get('site', {}), SchemaType.SITE)
|
|
24
24
|
if site:
|
|
25
25
|
cycle['site'] = site
|
|
26
|
+
|
|
27
|
+
# need to download `otherSites` as well
|
|
28
|
+
if 'otherSites' in cycle:
|
|
29
|
+
cycle['otherSites'] = [_load_calculated_node(s, SchemaType.SITE) for s in cycle['otherSites']]
|
|
30
|
+
|
|
26
31
|
return cycle
|
|
27
32
|
|
|
28
33
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
from hestia_earth.schema import
|
|
1
|
+
from hestia_earth.schema import EmissionMethodTier, MeasurementMethodClassification
|
|
2
2
|
|
|
3
3
|
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
4
|
-
from hestia_earth.models.utils.blank_node import cumulative_nodes_term_match
|
|
5
4
|
from hestia_earth.models.utils.emission import _new_emission
|
|
6
5
|
|
|
7
6
|
from .biomass_utils import detect_land_cover_change, get_valid_management_nodes, summarise_land_cover_nodes
|
|
@@ -61,13 +60,6 @@ The list of `MeasurementMethodClassification`s that can be used to calculate SOC
|
|
|
61
60
|
order from strongest to weakest.
|
|
62
61
|
"""
|
|
63
62
|
|
|
64
|
-
_SITE_TYPE_SYSTEMS_MAPPING = {
|
|
65
|
-
SiteSiteType.GLASS_OR_HIGH_ACCESSIBLE_COVER.value: [
|
|
66
|
-
"protectedCroppingSystemSoilBased",
|
|
67
|
-
"protectedCroppingSystemSoilAndSubstrateBased"
|
|
68
|
-
]
|
|
69
|
-
}
|
|
70
|
-
|
|
71
63
|
|
|
72
64
|
def _emission(
|
|
73
65
|
*,
|
|
@@ -129,12 +121,12 @@ def run(cycle: dict) -> list[dict]:
|
|
|
129
121
|
A list of [Emission nodes](https://www.hestia.earth/schema/Emission) containing model results.
|
|
130
122
|
"""
|
|
131
123
|
should_run_exec = create_should_run_function(
|
|
132
|
-
|
|
124
|
+
_CARBON_STOCK_TERM_ID,
|
|
125
|
+
measurements_mandatory=False, # Model can allocate zero emissions to LUC with enough landCover data
|
|
126
|
+
measurement_method_ranking=_MEASUREMENT_METHOD_RANKING,
|
|
133
127
|
get_valid_management_nodes_func=get_valid_management_nodes,
|
|
134
|
-
should_compile_inventory_func=_should_compile_inventory_func,
|
|
135
128
|
summarise_land_use_func=summarise_land_cover_nodes,
|
|
136
129
|
detect_land_use_change_func=detect_land_cover_change,
|
|
137
|
-
measurement_method_ranking=_MEASUREMENT_METHOD_RANKING
|
|
138
130
|
)
|
|
139
131
|
|
|
140
132
|
run_exec = create_run_function(
|
|
@@ -143,60 +135,10 @@ def run(cycle: dict) -> list[dict]:
|
|
|
143
135
|
management_change_emission_term_id=_MG_EMISSION_TERM_ID
|
|
144
136
|
)
|
|
145
137
|
|
|
146
|
-
should_run,
|
|
138
|
+
should_run, cycle_id, inventory, logs = should_run_exec(cycle)
|
|
147
139
|
|
|
148
140
|
for term_id in [_LU_EMISSION_TERM_ID, _MG_EMISSION_TERM_ID]:
|
|
149
141
|
logRequirements(cycle, model=MODEL, term=term_id, **logs)
|
|
150
142
|
logShouldRun(cycle, MODEL, term_id, should_run)
|
|
151
143
|
|
|
152
|
-
return run_exec(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
def _should_compile_inventory_func(
|
|
156
|
-
site: dict, cycles: list[dict], carbon_stock_measurements: list[dict]
|
|
157
|
-
) -> tuple[bool, dict]:
|
|
158
|
-
"""
|
|
159
|
-
Determine whether a site is suitable and has enough data to compile a carbon stock inventory.
|
|
160
|
-
|
|
161
|
-
Parameters
|
|
162
|
-
----------
|
|
163
|
-
site : dict
|
|
164
|
-
A HESTIA (Site node)[https://www.hestia.earth/schema/Site]
|
|
165
|
-
cycles : list[dict]
|
|
166
|
-
A list of HESTIA (Cycle nodes)[https://www.hestia.earth/schema/Cycle] that are related to the site.
|
|
167
|
-
carbon_stock_measurements : list[dict]
|
|
168
|
-
A list of HESTIA carbon stock (Measurement nodes)[https://www.hestia.earth/schema/Measurement] that are related
|
|
169
|
-
to the site.
|
|
170
|
-
|
|
171
|
-
Returns
|
|
172
|
-
-------
|
|
173
|
-
tuple[bool, dict]
|
|
174
|
-
`(should_run, logs)`.
|
|
175
|
-
"""
|
|
176
|
-
site_type = site.get("siteType")
|
|
177
|
-
has_soil = site_type not in _SITE_TYPE_SYSTEMS_MAPPING or all(
|
|
178
|
-
cumulative_nodes_term_match(
|
|
179
|
-
cycle.get("practices", []),
|
|
180
|
-
target_term_ids=_SITE_TYPE_SYSTEMS_MAPPING[site_type],
|
|
181
|
-
cumulative_threshold=0
|
|
182
|
-
) for cycle in cycles
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
has_cycles = len(cycles) > 0
|
|
186
|
-
has_functional_unit_1_ha = all(cycle.get('functionalUnit') == CycleFunctionalUnit._1_HA.value for cycle in cycles)
|
|
187
|
-
|
|
188
|
-
should_run = all([
|
|
189
|
-
has_soil,
|
|
190
|
-
has_cycles,
|
|
191
|
-
has_functional_unit_1_ha
|
|
192
|
-
])
|
|
193
|
-
|
|
194
|
-
logs = {
|
|
195
|
-
"site_type": site_type,
|
|
196
|
-
"has_soil": has_soil,
|
|
197
|
-
"carbon_stock_term": _CARBON_STOCK_TERM_ID,
|
|
198
|
-
"has_cycles": has_cycles,
|
|
199
|
-
"has_functional_unit_1_ha": has_functional_unit_1_ha,
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return should_run, logs
|
|
144
|
+
return run_exec(cycle_id, inventory) if should_run else []
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
from hestia_earth.schema import
|
|
1
|
+
from hestia_earth.schema import EmissionMethodTier
|
|
2
2
|
|
|
3
3
|
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
4
|
-
from hestia_earth.models.utils.blank_node import cumulative_nodes_term_match
|
|
5
4
|
from hestia_earth.models.utils.emission import _new_emission
|
|
6
5
|
|
|
7
6
|
from .biomass_utils import detect_land_cover_change, get_valid_management_nodes, summarise_land_cover_nodes
|
|
@@ -19,7 +18,7 @@ REQUIREMENTS = {
|
|
|
19
18
|
"dates": "",
|
|
20
19
|
"depthUpper": "0",
|
|
21
20
|
"depthLower": "30",
|
|
22
|
-
"term.@id": "
|
|
21
|
+
"term.@id": "belowGroundBiomass"
|
|
23
22
|
}
|
|
24
23
|
]
|
|
25
24
|
},
|
|
@@ -52,13 +51,6 @@ _DEPTH_LOWER = 30
|
|
|
52
51
|
|
|
53
52
|
_CARBON_STOCK_TERM_ID = 'belowGroundBiomass'
|
|
54
53
|
|
|
55
|
-
_SITE_TYPE_SYSTEMS_MAPPING = {
|
|
56
|
-
SiteSiteType.GLASS_OR_HIGH_ACCESSIBLE_COVER.value: [
|
|
57
|
-
"protectedCroppingSystemSoilBased",
|
|
58
|
-
"protectedCroppingSystemSoilAndSubstrateBased"
|
|
59
|
-
]
|
|
60
|
-
}
|
|
61
|
-
|
|
62
54
|
|
|
63
55
|
def _emission(
|
|
64
56
|
*,
|
|
@@ -121,12 +113,13 @@ def run(cycle: dict) -> list[dict]:
|
|
|
121
113
|
A list of [Emission nodes](https://www.hestia.earth/schema/Emission) containing model results.
|
|
122
114
|
"""
|
|
123
115
|
should_run_exec = create_should_run_function(
|
|
124
|
-
|
|
116
|
+
_CARBON_STOCK_TERM_ID,
|
|
117
|
+
depth_upper=_DEPTH_UPPER,
|
|
118
|
+
depth_lower=_DEPTH_LOWER,
|
|
119
|
+
measurements_mandatory=False, # Model can allocate zero emissions to LUC with enough landCover data
|
|
125
120
|
get_valid_management_nodes_func=get_valid_management_nodes,
|
|
126
|
-
should_compile_inventory_func=_should_compile_inventory_func,
|
|
127
121
|
summarise_land_use_func=summarise_land_cover_nodes,
|
|
128
|
-
detect_land_use_change_func=detect_land_cover_change
|
|
129
|
-
should_run_measurement_func=_should_run_measurement_func
|
|
122
|
+
detect_land_use_change_func=detect_land_cover_change
|
|
130
123
|
)
|
|
131
124
|
|
|
132
125
|
run_exec = create_run_function(
|
|
@@ -135,81 +128,10 @@ def run(cycle: dict) -> list[dict]:
|
|
|
135
128
|
management_change_emission_term_id=_MG_EMISSION_TERM_ID
|
|
136
129
|
)
|
|
137
130
|
|
|
138
|
-
should_run,
|
|
131
|
+
should_run, cycle_id, inventory, logs = should_run_exec(cycle)
|
|
139
132
|
|
|
140
133
|
for term_id in [_LU_EMISSION_TERM_ID, _MG_EMISSION_TERM_ID]:
|
|
141
134
|
logRequirements(cycle, model=MODEL, term=term_id, **logs)
|
|
142
135
|
logShouldRun(cycle, MODEL, term_id, should_run)
|
|
143
136
|
|
|
144
|
-
return run_exec(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
def _should_run_measurement_func(node: dict) -> bool:
|
|
148
|
-
"""
|
|
149
|
-
Validate a [Measurement](https://www.hestia.earth/schema/Measurement) to determine whether it is a valid
|
|
150
|
-
`organicCarbonPerHa` node.
|
|
151
|
-
|
|
152
|
-
Parameters
|
|
153
|
-
----------
|
|
154
|
-
node : dict
|
|
155
|
-
The node to be validated.
|
|
156
|
-
|
|
157
|
-
Returns
|
|
158
|
-
-------
|
|
159
|
-
bool
|
|
160
|
-
`True` if the node passes all validation criteria, `False` otherwise.
|
|
161
|
-
"""
|
|
162
|
-
return all([
|
|
163
|
-
node.get("depthLower") == _DEPTH_LOWER,
|
|
164
|
-
node.get("depthUpper") == _DEPTH_UPPER
|
|
165
|
-
])
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
def _should_compile_inventory_func(
|
|
169
|
-
site: dict, cycles: list[dict], carbon_stock_measurements: list[dict]
|
|
170
|
-
) -> tuple[bool, dict]:
|
|
171
|
-
"""
|
|
172
|
-
Determine whether a site is suitable and has enough data to compile a carbon stock inventory.
|
|
173
|
-
|
|
174
|
-
Parameters
|
|
175
|
-
----------
|
|
176
|
-
site : dict
|
|
177
|
-
A HESTIA (Site node)[https://www.hestia.earth/schema/Site]
|
|
178
|
-
cycles : list[dict]
|
|
179
|
-
A list of HESTIA (Cycle nodes)[https://www.hestia.earth/schema/Cycle] that are related to the site.
|
|
180
|
-
carbon_stock_measurements : list[dict]
|
|
181
|
-
A list of HESTIA carbon stock (Measurement nodes)[https://www.hestia.earth/schema/Measurement] that are related
|
|
182
|
-
to the site.
|
|
183
|
-
|
|
184
|
-
Returns
|
|
185
|
-
-------
|
|
186
|
-
tuple[bool, dict]
|
|
187
|
-
`(should_run, logs)`.
|
|
188
|
-
"""
|
|
189
|
-
site_type = site.get("siteType")
|
|
190
|
-
has_soil = site_type not in _SITE_TYPE_SYSTEMS_MAPPING or all(
|
|
191
|
-
cumulative_nodes_term_match(
|
|
192
|
-
cycle.get("practices", []),
|
|
193
|
-
target_term_ids=_SITE_TYPE_SYSTEMS_MAPPING[site_type],
|
|
194
|
-
cumulative_threshold=0
|
|
195
|
-
) for cycle in cycles
|
|
196
|
-
)
|
|
197
|
-
|
|
198
|
-
has_cycles = len(cycles) > 0
|
|
199
|
-
has_functional_unit_1_ha = all(cycle.get('functionalUnit') == CycleFunctionalUnit._1_HA.value for cycle in cycles)
|
|
200
|
-
|
|
201
|
-
should_run = all([
|
|
202
|
-
has_soil,
|
|
203
|
-
has_cycles,
|
|
204
|
-
has_functional_unit_1_ha
|
|
205
|
-
])
|
|
206
|
-
|
|
207
|
-
logs = {
|
|
208
|
-
"site_type": site_type,
|
|
209
|
-
"has_soil": has_soil,
|
|
210
|
-
"carbon_stock_term": _CARBON_STOCK_TERM_ID,
|
|
211
|
-
"has_cycles": has_cycles,
|
|
212
|
-
"has_functional_unit_1_ha": has_functional_unit_1_ha,
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
return should_run, logs
|
|
137
|
+
return run_exec(cycle_id, inventory) if should_run else []
|