hestia-earth-models 0.74.4__py3-none-any.whl → 0.74.6__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 +9 -1
- 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/default_resourceUse.py +18 -16
- 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/indicator.py +1 -3
- 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.4.dist-info → hestia_earth_models-0.74.6.dist-info}/METADATA +2 -3
- {hestia_earth_models-0.74.4.dist-info → hestia_earth_models-0.74.6.dist-info}/RECORD +66 -59
- 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_default_emissions.py +8 -1
- tests/models/hestia/test_default_resourceUse.py +7 -1
- 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.4.dist-info → hestia_earth_models-0.74.6.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.74.4.dist-info → hestia_earth_models-0.74.6.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.74.4.dist-info → hestia_earth_models-0.74.6.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from functools import reduce
|
|
2
|
+
from itertools import zip_longest
|
|
2
3
|
from typing import NamedTuple
|
|
3
4
|
|
|
4
5
|
from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
|
|
@@ -29,9 +30,9 @@ REQUIREMENTS = {
|
|
|
29
30
|
"@type": "Site",
|
|
30
31
|
"country": {"@type": "Term", "termType": "region"}
|
|
31
32
|
},
|
|
32
|
-
"siteArea": "",
|
|
33
|
-
"siteDuration": "",
|
|
34
|
-
"siteUnusedDuration": "",
|
|
33
|
+
"siteArea": ">= 0",
|
|
34
|
+
"siteDuration": ">= 0",
|
|
35
|
+
"siteUnusedDuration": ">= 0",
|
|
35
36
|
"optional": {
|
|
36
37
|
"@doc": "When `otherSites` are provided, `otherSitesArea`, `otherSitesDuration` and `otherSitesUnusedDuration` are required", # noqa: E501
|
|
37
38
|
"otherSites": [{
|
|
@@ -111,82 +112,102 @@ def _calc_land_occupation_m2_per_kg(
|
|
|
111
112
|
return land_occupation_m2_per_ha * economic_value_share * 0.01 / yield_
|
|
112
113
|
|
|
113
114
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
def _extract_site_data(cycle: dict, land_cover_id: dict):
|
|
116
|
+
site = cycle.get("site", {})
|
|
117
|
+
site_data = SiteData(
|
|
118
|
+
id=site.get("@id"),
|
|
119
|
+
area=cycle.get("siteArea"),
|
|
120
|
+
duration=cycle.get("siteDuration"),
|
|
121
|
+
unused_duration=cycle.get("siteUnusedDuration"),
|
|
122
|
+
country_id=site.get("country", {}).get("@id"),
|
|
123
|
+
land_cover_id=land_cover_id or get_landCover_term_id_from_site_type(site.get("siteType"))
|
|
124
|
+
)
|
|
117
125
|
|
|
118
|
-
|
|
119
|
-
field: field.replace("site", "otherSites", 1) for field in _CYCLE_KEYS
|
|
120
|
-
}
|
|
126
|
+
is_valid = _should_run_site_data(site_data)
|
|
121
127
|
|
|
128
|
+
logs = {
|
|
129
|
+
"site_data": _format_inventory([site_data])
|
|
130
|
+
}
|
|
122
131
|
|
|
123
|
-
|
|
124
|
-
product_land_cover_id = get_landCover_term_id(product.get("term", {}), skip_debug=True)
|
|
132
|
+
return is_valid, site_data, logs
|
|
125
133
|
|
|
126
|
-
cycle_data = {
|
|
127
|
-
key: [value] + cycle.get(otherSites_key, []) for key, otherSites_key in _CYCLE_KEY_MAPPING.items()
|
|
128
|
-
if (value := cycle.get(key))
|
|
129
|
-
}
|
|
130
134
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
])
|
|
135
|
+
def _extract_other_sites_data(cycle: dict, land_cover_id: dict):
|
|
136
|
+
other_sites = cycle.get("otherSites", [])
|
|
137
|
+
other_sites_area = cycle.get("otherSitesArea", [])
|
|
138
|
+
other_sites_duration = cycle.get("otherSitesDuration", [])
|
|
139
|
+
other_sites_unused_duration = cycle.get("otherSitesUnusedDuration", [])
|
|
135
140
|
|
|
136
|
-
|
|
141
|
+
other_sites_data = [
|
|
137
142
|
SiteData(
|
|
138
143
|
id=site.get("@id"),
|
|
139
|
-
area=
|
|
140
|
-
duration=
|
|
141
|
-
unused_duration=
|
|
144
|
+
area=area,
|
|
145
|
+
duration=duration,
|
|
146
|
+
unused_duration=unused_duration,
|
|
142
147
|
country_id=site.get("country", {}).get("@id"),
|
|
143
|
-
land_cover_id=
|
|
144
|
-
) for
|
|
145
|
-
|
|
148
|
+
land_cover_id=land_cover_id or get_landCover_term_id_from_site_type(site.get("siteType"))
|
|
149
|
+
) for (
|
|
150
|
+
site,
|
|
151
|
+
area,
|
|
152
|
+
duration,
|
|
153
|
+
unused_duration
|
|
154
|
+
) in zip_longest(
|
|
155
|
+
other_sites,
|
|
156
|
+
other_sites_area,
|
|
157
|
+
other_sites_duration,
|
|
158
|
+
other_sites_unused_duration
|
|
159
|
+
)
|
|
160
|
+
]
|
|
161
|
+
|
|
162
|
+
is_valid = all(_should_run_site_data(other_site) for other_site in other_sites_data)
|
|
146
163
|
|
|
147
164
|
logs = {
|
|
148
|
-
"
|
|
165
|
+
"other_sites_count": len(other_sites),
|
|
166
|
+
"other_sites_data": _format_inventory(other_sites_data, "Not relevant")
|
|
149
167
|
}
|
|
150
168
|
|
|
151
|
-
return
|
|
169
|
+
return is_valid, other_sites_data, logs
|
|
152
170
|
|
|
153
171
|
|
|
154
172
|
def _should_run_site_data(site_data: SiteData) -> bool:
|
|
155
173
|
return all([
|
|
156
|
-
site_data.area
|
|
157
|
-
site_data.duration
|
|
158
|
-
site_data.unused_duration
|
|
174
|
+
site_data.area or site_data.area == 0,
|
|
175
|
+
site_data.duration or site_data.duration == 0,
|
|
176
|
+
site_data.unused_duration or site_data.unused_duration == 0,
|
|
159
177
|
site_data.land_cover_id,
|
|
160
178
|
site_data.country_id
|
|
161
179
|
])
|
|
162
180
|
|
|
163
181
|
|
|
164
|
-
def _format_float(value: float, unit: str = "") -> str:
|
|
182
|
+
def _format_float(value: float, unit: str = "", default: str = "None") -> str:
|
|
165
183
|
return " ".join(
|
|
166
|
-
string for string in [f"{value
|
|
167
|
-
) if value else
|
|
184
|
+
string for string in [f"{value}", unit] if string
|
|
185
|
+
) if isinstance(value, (float, int)) else default
|
|
168
186
|
|
|
169
187
|
|
|
170
188
|
_INVALID_CHARS = {"_", ":", ",", "="}
|
|
171
189
|
_REPLACEMENT_CHAR = "-"
|
|
172
190
|
|
|
173
191
|
|
|
174
|
-
def _format_str(value: str) -> str:
|
|
192
|
+
def _format_str(value: str, default: str = "None") -> str:
|
|
175
193
|
"""Format a string for logging in a table. Remove all characters used to render the table on the front end."""
|
|
176
|
-
return
|
|
194
|
+
return (
|
|
195
|
+
reduce(lambda x, char: x.replace(char, _REPLACEMENT_CHAR), _INVALID_CHARS, str(value))
|
|
196
|
+
if value else default
|
|
197
|
+
)
|
|
177
198
|
|
|
178
199
|
|
|
179
|
-
def _format_inventory(inventory: list[SiteData]) -> str:
|
|
200
|
+
def _format_inventory(inventory: list[SiteData], default: str = "None") -> str:
|
|
180
201
|
return log_as_table(
|
|
181
202
|
{
|
|
182
|
-
"
|
|
203
|
+
"site-id": _format_str(site_data.id),
|
|
183
204
|
"site-area": _format_float(site_data.area, "ha"),
|
|
184
205
|
"site-duration": _format_float(site_data.duration, "days"),
|
|
185
206
|
"site-unused-duration": _format_float(site_data.unused_duration, "days"),
|
|
186
207
|
"land-cover-id": _format_str(site_data.land_cover_id),
|
|
187
208
|
"country-id": _format_str(site_data.country_id)
|
|
188
209
|
} for site_data in inventory
|
|
189
|
-
) if inventory else
|
|
210
|
+
) if inventory else default
|
|
190
211
|
|
|
191
212
|
|
|
192
213
|
def _should_run(impact_assessment: dict):
|
|
@@ -195,13 +216,21 @@ def _should_run(impact_assessment: dict):
|
|
|
195
216
|
functional_unit = cycle.get("functionalUnit")
|
|
196
217
|
|
|
197
218
|
product = get_product(impact_assessment)
|
|
198
|
-
|
|
219
|
+
product_yield = sum(product.get("value", []))
|
|
220
|
+
product_land_cover_id = get_landCover_term_id(product.get("term", {}), skip_debug=True)
|
|
199
221
|
economic_value_share = (
|
|
200
222
|
100 if functional_unit == CycleFunctionalUnit.RELATIVE.value
|
|
201
223
|
else product.get("economicValueShare")
|
|
202
224
|
)
|
|
203
225
|
|
|
204
|
-
|
|
226
|
+
site_data_is_valid, site_data, site_logs = _extract_site_data(cycle, product_land_cover_id)
|
|
227
|
+
(
|
|
228
|
+
other_sites_data_is_valid,
|
|
229
|
+
other_sites_data,
|
|
230
|
+
other_sites_logs
|
|
231
|
+
) = _extract_other_sites_data(cycle, product_land_cover_id)
|
|
232
|
+
|
|
233
|
+
inventory = [site_data] + other_sites_data
|
|
205
234
|
|
|
206
235
|
valid_inventory = inventory and all(_should_run_site_data(site_data) for site_data in inventory)
|
|
207
236
|
|
|
@@ -210,25 +239,25 @@ def _should_run(impact_assessment: dict):
|
|
|
210
239
|
model=MODEL,
|
|
211
240
|
term=TERM_ID,
|
|
212
241
|
functional_unit=functional_unit,
|
|
213
|
-
|
|
242
|
+
product_yield=_format_float(product_yield, product.get("term", {}).get("units")),
|
|
214
243
|
economic_value_share=_format_float(economic_value_share, "pct"),
|
|
215
|
-
site_inventory=_format_inventory(inventory),
|
|
216
244
|
valid_inventory=valid_inventory,
|
|
217
|
-
|
|
245
|
+
site_data_is_valid=site_data_is_valid,
|
|
246
|
+
**site_logs,
|
|
247
|
+
other_sites_data_is_valid=other_sites_data_is_valid,
|
|
248
|
+
**other_sites_logs
|
|
218
249
|
)
|
|
219
250
|
|
|
220
251
|
should_run = all([
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
),
|
|
226
|
-
valid_inventory
|
|
252
|
+
product_yield > 0,
|
|
253
|
+
economic_value_share or economic_value_share == 0,
|
|
254
|
+
site_data_is_valid,
|
|
255
|
+
other_sites_data_is_valid
|
|
227
256
|
])
|
|
228
257
|
|
|
229
258
|
logShouldRun(impact_assessment, MODEL, TERM_ID, should_run)
|
|
230
259
|
|
|
231
|
-
return should_run,
|
|
260
|
+
return should_run, product_yield, economic_value_share, inventory
|
|
232
261
|
|
|
233
262
|
|
|
234
263
|
def _run(
|
|
@@ -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
|
|