hestia-earth-models 0.65.4__py3-none-any.whl → 0.65.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/agribalyse2016/fuelElectricity.py +40 -24
- hestia_earth/models/aware/scarcityWeightedWaterUse.py +1 -1
- hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsLandOccupation.py +1 -1
- hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsLandTransformation.py +1 -1
- hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsTotalLandUseEffects.py +1 -1
- hestia_earth/models/cml2001Baseline/abioticResourceDepletionFossilFuels.py +9 -5
- hestia_earth/models/cycle/completeness/electricityFuel.py +4 -2
- hestia_earth/models/geospatialDatabase/precipitationAnnual.py +2 -2
- hestia_earth/models/geospatialDatabase/precipitationLongTermAnnualMean.py +2 -2
- hestia_earth/models/geospatialDatabase/precipitationMonthly.py +2 -2
- hestia_earth/models/geospatialDatabase/temperatureAnnual.py +2 -2
- hestia_earth/models/geospatialDatabase/temperatureLongTermAnnualMean.py +2 -2
- hestia_earth/models/geospatialDatabase/temperatureMonthly.py +2 -2
- hestia_earth/models/hestia/landCover.py +101 -68
- hestia_earth/models/hestia/landTransformation100YearAverageDuringCycle.py +49 -0
- hestia_earth/models/hestia/landTransformation20YearAverageDuringCycle.py +49 -0
- hestia_earth/models/hestia/resourceUse_utils.py +200 -0
- hestia_earth/models/hestia/seed_emissions.py +35 -21
- hestia_earth/models/hestia/utils.py +48 -0
- hestia_earth/models/impact_assessment/emissions.py +20 -5
- hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +66 -28
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1_utils.py +26 -142
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2_utils.py +3 -3
- hestia_earth/models/ipcc2019/organicCarbonPerHa_utils.py +8 -5
- hestia_earth/models/linkedImpactAssessment/utils.py +3 -1
- hestia_earth/models/mocking/search-results.json +27 -4504
- hestia_earth/models/pooreNemecek2018/freshwaterWithdrawalsDuringCycle.py +4 -1
- hestia_earth/models/schererPfister2015/nErosionSoilFlux.py +23 -14
- hestia_earth/models/schererPfister2015/pErosionSoilFlux.py +23 -15
- hestia_earth/models/schererPfister2015/utils.py +3 -5
- hestia_earth/models/site/management.py +82 -22
- hestia_earth/models/utils/blank_node.py +28 -0
- hestia_earth/models/utils/crop.py +5 -1
- hestia_earth/models/utils/fuel.py +4 -1
- hestia_earth/models/utils/impact_assessment.py +7 -5
- hestia_earth/models/utils/pesticideAI.py +1 -0
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.65.4.dist-info → hestia_earth_models-0.65.6.dist-info}/METADATA +2 -2
- {hestia_earth_models-0.65.4.dist-info → hestia_earth_models-0.65.6.dist-info}/RECORD +52 -46
- tests/models/cml2001Baseline/test_abioticResourceDepletionFossilFuels.py +1 -1
- tests/models/hestia/test_landCover.py +2 -1
- tests/models/hestia/test_landTransformation100YearAverageDuringCycle.py +30 -0
- tests/models/hestia/test_landTransformation20YearAverageDuringCycle.py +31 -0
- tests/models/ipcc2019/test_co2ToAirAboveGroundBiomassStockChange.py +3 -1
- tests/models/ipcc2019/test_co2ToAirBelowGroundBiomassStockChange.py +3 -1
- tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChange.py +3 -1
- tests/models/ipcc2019/test_organicCarbonPerHa.py +3 -2
- tests/models/ipcc2019/test_organicCarbonPerHa_tier_1_utils.py +15 -11
- tests/models/utils/test_blank_node.py +22 -7
- {hestia_earth_models-0.65.4.dist-info → hestia_earth_models-0.65.6.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.65.4.dist-info → hestia_earth_models-0.65.6.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.65.4.dist-info → hestia_earth_models-0.65.6.dist-info}/top_level.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Fuel and Electricity
|
|
3
3
|
|
|
4
|
-
This model calculates fuel and electricity data from the number of hours each machine is
|
|
4
|
+
This model calculates fuel and electricity data from the number of hours each machine is used for.
|
|
5
5
|
"""
|
|
6
6
|
from hestia_earth.schema import TermTermType
|
|
7
7
|
from hestia_earth.utils.model import filter_list_term_type
|
|
@@ -68,43 +68,59 @@ def _run_operation(cycle: dict):
|
|
|
68
68
|
return exec
|
|
69
69
|
|
|
70
70
|
|
|
71
|
-
def
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
values = practice.get('value', [])
|
|
76
|
-
value = list_sum(values) if all([not isinstance(v, str) for v in values]) else 0 # str allowed for Practice
|
|
77
|
-
has_value = value > 0
|
|
71
|
+
def _operation_data(practice: dict):
|
|
72
|
+
term = practice.get('term', {})
|
|
73
|
+
values = practice.get('value', [])
|
|
74
|
+
value = list_sum(values) if all([not isinstance(v, str) for v in values]) else None # str allowed for Practice
|
|
78
75
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
has_lookup_value = len(inputs) > 0
|
|
76
|
+
coeffs = get_lookup_value(term, LOOKUPS['operation'], model=MODEL, model_key=MODEL_KEY)
|
|
77
|
+
values = non_empty_list(coeffs.split(';')) if coeffs else []
|
|
78
|
+
inputs = [{'id': c.split(':')[0], 'value': float(c.split(':')[1])} for c in values]
|
|
83
79
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
return [{'term': term, 'value': value, 'input': input} for input in inputs] if should_run else []
|
|
91
|
-
return exec
|
|
80
|
+
return [{
|
|
81
|
+
'term': term,
|
|
82
|
+
'value': value,
|
|
83
|
+
'input': input,
|
|
84
|
+
'dates': ';'.join(practice.get('dates', []))
|
|
85
|
+
} for input in inputs]
|
|
92
86
|
|
|
93
87
|
|
|
94
88
|
def _should_run(cycle: dict):
|
|
95
89
|
is_incomplete = not cycle.get('completeness', {}).get('electricityFuel', False)
|
|
96
90
|
operations = filter_list_term_type(cycle.get('practices', []), TermTermType.OPERATION)
|
|
97
|
-
|
|
98
|
-
|
|
91
|
+
|
|
92
|
+
operations = flatten(map(_operation_data, operations))
|
|
93
|
+
term_ids = list(set(non_empty_list(map(lambda v: v.get('term', {}).get('@id'), operations))))
|
|
94
|
+
|
|
95
|
+
valid_operations = [v for v in operations if (v.get('value') or 0) > 0]
|
|
96
|
+
has_operations = len(valid_operations) > 0
|
|
97
|
+
|
|
98
|
+
# group operations by term to show in logs
|
|
99
|
+
grouped_operations = group_by(operations, ['term.@id'])
|
|
100
|
+
|
|
101
|
+
for term_id, operations in grouped_operations.items():
|
|
102
|
+
logs = [
|
|
103
|
+
{
|
|
104
|
+
'value': operation.get('value'),
|
|
105
|
+
'dates': operation.get('dates'),
|
|
106
|
+
'input-id': operation.get('input', {}).get('@id'),
|
|
107
|
+
}
|
|
108
|
+
for operation in operations
|
|
109
|
+
]
|
|
110
|
+
logRequirements(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
111
|
+
details=log_as_table(logs))
|
|
112
|
+
|
|
113
|
+
should_run = any([(v.get('value') or 0) > 0 for v in operations])
|
|
114
|
+
logShouldRun(cycle, MODEL, term_id, should_run, model_key=MODEL_KEY)
|
|
99
115
|
|
|
100
116
|
logRequirements(cycle, model=MODEL, model_key=MODEL_KEY,
|
|
101
117
|
is_term_type_electricityFuel_incomplete=is_incomplete,
|
|
102
118
|
has_operations=has_operations,
|
|
103
|
-
operations=';'.join(
|
|
119
|
+
operations=';'.join(term_ids))
|
|
104
120
|
|
|
105
121
|
should_run = all([is_incomplete, has_operations])
|
|
106
122
|
logShouldRun(cycle, MODEL, None, should_run, model_key=MODEL_KEY)
|
|
107
|
-
return should_run,
|
|
123
|
+
return should_run, valid_operations
|
|
108
124
|
|
|
109
125
|
|
|
110
126
|
def run(cycle: dict):
|
|
@@ -89,7 +89,7 @@ def _run(impact_assessment: dict):
|
|
|
89
89
|
_get_factor_from_basinId(site, aware_id) if aware_id else None
|
|
90
90
|
) or _get_factor_from_region(impact_assessment, site)
|
|
91
91
|
inputs_value = convert_value_from_cycle(
|
|
92
|
-
product, sum_input_impacts(cycle.get('inputs', []), TERM_ID), model=MODEL, term_id=TERM_ID
|
|
92
|
+
impact_assessment, product, sum_input_impacts(cycle.get('inputs', []), TERM_ID), model=MODEL, term_id=TERM_ID
|
|
93
93
|
)
|
|
94
94
|
|
|
95
95
|
logRequirements(impact_assessment, model=MODEL, term=TERM_ID,
|
|
@@ -73,7 +73,7 @@ def _run(impact_assessment: dict):
|
|
|
73
73
|
land_occupation_m2_kg = land_occupation_per_kg(MODEL, TERM_ID, cycle, site, product)
|
|
74
74
|
factor = get_region_factor(TERM_ID, impact_assessment, LOOKUP_SUFFIX, 'medium_intensity')
|
|
75
75
|
inputs_value = convert_value_from_cycle(
|
|
76
|
-
product, sum_input_impacts(cycle.get('inputs', []), TERM_ID), model=MODEL, term_id=TERM_ID
|
|
76
|
+
impact_assessment, product, sum_input_impacts(cycle.get('inputs', []), TERM_ID), model=MODEL, term_id=TERM_ID
|
|
77
77
|
)
|
|
78
78
|
logRequirements(impact_assessment, model=MODEL, term=TERM_ID,
|
|
79
79
|
landOccupation=land_occupation_m2_kg,
|
|
@@ -68,7 +68,7 @@ def _run(impact_assessment: dict):
|
|
|
68
68
|
landTransformation = _value(impact_assessment, _TRANSFORMATION_TERM_ID)
|
|
69
69
|
region_factor = get_region_factor(TERM_ID, impact_assessment, _LOOKUP_SUFFIX, 'medium_intensity')
|
|
70
70
|
inputs_value = convert_value_from_cycle(
|
|
71
|
-
product, sum_input_impacts(cycle.get('inputs', []), TERM_ID), model=MODEL, term_id=TERM_ID
|
|
71
|
+
impact_assessment, product, sum_input_impacts(cycle.get('inputs', []), TERM_ID), model=MODEL, term_id=TERM_ID
|
|
72
72
|
)
|
|
73
73
|
logRequirements(impact_assessment, model=MODEL, term=TERM_ID,
|
|
74
74
|
landTransformation=landTransformation,
|
|
@@ -57,7 +57,7 @@ def run(impact_assessment: dict):
|
|
|
57
57
|
cycle = impact_assessment.get('cycle', {})
|
|
58
58
|
product = get_product(impact_assessment)
|
|
59
59
|
inputs_value = convert_value_from_cycle(
|
|
60
|
-
product, sum_input_impacts(cycle.get('inputs', []), TERM_ID), model=MODEL, term_id=TERM_ID
|
|
60
|
+
impact_assessment, product, sum_input_impacts(cycle.get('inputs', []), TERM_ID), model=MODEL, term_id=TERM_ID
|
|
61
61
|
)
|
|
62
62
|
value = sum_values([landUseEffects, inputs_value])
|
|
63
63
|
logRequirements(impact_assessment, model=MODEL, term=TERM_ID,
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
"""
|
|
2
|
-
This model calculates the abiotic resource depletion, fossil fuels. Based on the models CML 2002 (Guinée et al., 2002)
|
|
2
|
+
This model calculates the abiotic resource depletion, fossil fuels. Based on the models CML 2002 (Guinée et al., 2002)
|
|
3
|
+
and van Oers et al. 2002 (method, v.4.8)
|
|
3
4
|
|
|
4
|
-
>The earth contains a finite amount of non-renewable resources, such as fossil fuels like coal, oil and gas.
|
|
5
|
+
> The earth contains a finite amount of non-renewable resources, such as fossil fuels like coal, oil and gas.
|
|
6
|
+
The basic idea behind this impact category is that extracting resources today will force future generations to extract
|
|
7
|
+
less or different resources. For example, the depletion of V2.0 – 25th August 2023 28 fossil fuels may lead to the
|
|
8
|
+
non-availability of fossil fuels for future generations. The amount of materials contributing to resource use, fossils,
|
|
9
|
+
are converted into MJ.
|
|
5
10
|
|
|
6
11
|
Source : [Life Cycle Assessment & the EF methods - Comprehensive coverage of impacts](https://green-business.ec.europa.eu/environmental-footprint-methods/life-cycle-assessment-ef-methods_en)
|
|
7
12
|
|
|
@@ -89,9 +94,8 @@ def download_all_non_renewable_terms(lookup_file_name: str) -> list:
|
|
|
89
94
|
lookup = download_lookup(lookup_file_name)
|
|
90
95
|
results = lookup[
|
|
91
96
|
lookup[column_name("abioticResourceDepletionFossilFuelsCml2001Baseline")] == True # noqa: E712
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
return terms_ids
|
|
97
|
+
]["termid"]
|
|
98
|
+
return list(map(str, results))
|
|
95
99
|
|
|
96
100
|
|
|
97
101
|
def _valid_resource_indicator(resource: dict) -> bool:
|
|
@@ -40,7 +40,7 @@ def _lookup_value(practice: dict, lookup_name: str):
|
|
|
40
40
|
def _practice_value(practice: dict):
|
|
41
41
|
term = practice.get('term', {})
|
|
42
42
|
fuel_use = _lookup_value(practice, LOOKUPS['operation'][0])
|
|
43
|
-
return {'id': term.get('@id'), 'fuel_use': fuel_use}
|
|
43
|
+
return {'id': term.get('@id'), 'value': practice.get('value'), 'fuel_use': fuel_use}
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
def run(cycle: dict):
|
|
@@ -55,6 +55,8 @@ def run(cycle: dict):
|
|
|
55
55
|
term_type_operation_complete=operation_complete,
|
|
56
56
|
values=log_as_table(practices_values))
|
|
57
57
|
|
|
58
|
-
is_complete = all([operation_complete] + [
|
|
58
|
+
is_complete = all([operation_complete] + [
|
|
59
|
+
all([p.get('fuel_use'), p.get('value') is not None]) for p in practices_values
|
|
60
|
+
])
|
|
59
61
|
|
|
60
62
|
return is_complete
|
|
@@ -31,8 +31,8 @@ RETURNS = {
|
|
|
31
31
|
}
|
|
32
32
|
TERM_ID = 'precipitationAnnual'
|
|
33
33
|
EE_PARAMS = {
|
|
34
|
-
'collection': 'ECMWF/
|
|
35
|
-
'band_name': '
|
|
34
|
+
'collection': 'ECMWF/ERA5_LAND/MONTHLY_AGGR',
|
|
35
|
+
'band_name': 'total_precipitation_sum',
|
|
36
36
|
'ee_type': 'raster',
|
|
37
37
|
'reducer': 'mean',
|
|
38
38
|
'reducer_annual': 'sum'
|
|
@@ -27,8 +27,8 @@ TERM_ID = 'precipitationLongTermAnnualMean'
|
|
|
27
27
|
START_DATE = '1979-01-01'
|
|
28
28
|
END_DATE = '2020-12-31'
|
|
29
29
|
EE_PARAMS = {
|
|
30
|
-
'collection': 'ECMWF/
|
|
31
|
-
'band_name': '
|
|
30
|
+
'collection': 'ECMWF/ERA5_LAND/MONTHLY_AGGR',
|
|
31
|
+
'band_name': 'total_precipitation_sum',
|
|
32
32
|
'ee_type': 'raster',
|
|
33
33
|
'reducer': 'mean',
|
|
34
34
|
'reducer_annual': 'sum',
|
|
@@ -31,8 +31,8 @@ RETURNS = {
|
|
|
31
31
|
}
|
|
32
32
|
TERM_ID = 'precipitationMonthly'
|
|
33
33
|
EE_PARAMS = {
|
|
34
|
-
'collection': 'ECMWF/
|
|
35
|
-
'band_name': '
|
|
34
|
+
'collection': 'ECMWF/ERA5_LAND/MONTHLY_AGGR',
|
|
35
|
+
'band_name': 'total_precipitation_sum',
|
|
36
36
|
'ee_type': 'raster',
|
|
37
37
|
'reducer': 'mean',
|
|
38
38
|
'reducer_annual': 'sum'
|
|
@@ -31,8 +31,8 @@ RETURNS = {
|
|
|
31
31
|
}
|
|
32
32
|
TERM_ID = 'temperatureAnnual'
|
|
33
33
|
EE_PARAMS = {
|
|
34
|
-
'collection': 'ECMWF/
|
|
35
|
-
'band_name': '
|
|
34
|
+
'collection': 'ECMWF/ERA5_LAND/MONTHLY_AGGR',
|
|
35
|
+
'band_name': 'temperature_2m',
|
|
36
36
|
'ee_type': 'raster',
|
|
37
37
|
'reducer': 'mean',
|
|
38
38
|
'reducer_annual': 'mean'
|
|
@@ -27,8 +27,8 @@ TERM_ID = 'temperatureLongTermAnnualMean'
|
|
|
27
27
|
START_DATE = '1979-01-01'
|
|
28
28
|
END_DATE = '2020-12-31'
|
|
29
29
|
EE_PARAMS = {
|
|
30
|
-
'collection': 'ECMWF/
|
|
31
|
-
'band_name': '
|
|
30
|
+
'collection': 'ECMWF/ERA5_LAND/MONTHLY_AGGR',
|
|
31
|
+
'band_name': 'temperature_2m',
|
|
32
32
|
'ee_type': 'raster',
|
|
33
33
|
'reducer': 'mean',
|
|
34
34
|
'reducer_annual': 'mean',
|
|
@@ -31,8 +31,8 @@ RETURNS = {
|
|
|
31
31
|
}
|
|
32
32
|
TERM_ID = 'temperatureMonthly'
|
|
33
33
|
EE_PARAMS = {
|
|
34
|
-
'collection': 'ECMWF/
|
|
35
|
-
'band_name': '
|
|
34
|
+
'collection': 'ECMWF/ERA5_LAND/MONTHLY_AGGR',
|
|
35
|
+
'band_name': 'temperature_2m',
|
|
36
36
|
'ee_type': 'raster',
|
|
37
37
|
'reducer': 'mean',
|
|
38
38
|
'reducer_annual': 'mean'
|
|
@@ -6,6 +6,8 @@ functionality of the Blonk model.
|
|
|
6
6
|
"""
|
|
7
7
|
import math
|
|
8
8
|
from collections import defaultdict
|
|
9
|
+
from datetime import datetime, timedelta
|
|
10
|
+
|
|
9
11
|
from hestia_earth.schema import SiteSiteType, TermTermType
|
|
10
12
|
from hestia_earth.utils.lookup import (
|
|
11
13
|
download_lookup, get_table_value, column_name,
|
|
@@ -17,7 +19,23 @@ from hestia_earth.utils.tools import safe_parse_float, to_precision, non_empty_v
|
|
|
17
19
|
from hestia_earth.models.log import logRequirements, log_as_table, logShouldRun
|
|
18
20
|
from hestia_earth.models.utils.management import _new_management
|
|
19
21
|
from hestia_earth.models.utils.term import get_lookup_value
|
|
22
|
+
from .utils import (
|
|
23
|
+
IPCC_LAND_USE_CATEGORY_ANNUAL,
|
|
24
|
+
IPCC_LAND_USE_CATEGORY_PERENNIAL,
|
|
25
|
+
LAND_USE_TERMS_FOR_TRANSFORMATION,
|
|
26
|
+
ANNUAL_CROPLAND,
|
|
27
|
+
PERMANENT_CROPLAND,
|
|
28
|
+
FOREST_LAND,
|
|
29
|
+
OTHER_LAND,
|
|
30
|
+
PERMANENT_PASTURE,
|
|
31
|
+
TOTAL_CROPLAND,
|
|
32
|
+
TOTAL_AGRICULTURAL_CHANGE,
|
|
33
|
+
ALL_LAND_USE_TERMS,
|
|
34
|
+
crop_ipcc_land_use_category,
|
|
35
|
+
)
|
|
20
36
|
from . import MODEL
|
|
37
|
+
from ..utils.blank_node import _node_date, DatestrFormat
|
|
38
|
+
from ..utils.constant import DAYS_IN_YEAR
|
|
21
39
|
|
|
22
40
|
REQUIREMENTS = {
|
|
23
41
|
"Site": {
|
|
@@ -69,30 +87,6 @@ LOOKUPS = {
|
|
|
69
87
|
MODEL_KEY = 'landCover'
|
|
70
88
|
|
|
71
89
|
LAND_AREA = LOOKUPS["region-faostatArea"][3]
|
|
72
|
-
TOTAL_CROPLAND = "Cropland"
|
|
73
|
-
ANNUAL_CROPLAND = "Arable land"
|
|
74
|
-
FOREST_LAND = "Forest land"
|
|
75
|
-
OTHER_LAND = "Other land"
|
|
76
|
-
PERMANENT_CROPLAND = "Permanent crops"
|
|
77
|
-
PERMANENT_PASTURE = "Permanent meadows and pastures"
|
|
78
|
-
TOTAL_AGRICULTURAL_CHANGE = "Total agricultural change"
|
|
79
|
-
ALL_LAND_USE_TERMS = [
|
|
80
|
-
FOREST_LAND,
|
|
81
|
-
TOTAL_CROPLAND,
|
|
82
|
-
ANNUAL_CROPLAND,
|
|
83
|
-
PERMANENT_CROPLAND,
|
|
84
|
-
PERMANENT_PASTURE,
|
|
85
|
-
OTHER_LAND
|
|
86
|
-
]
|
|
87
|
-
# Mapping from Land use terms to Management node terms.
|
|
88
|
-
# land use term: (@id, name)
|
|
89
|
-
LAND_USE_TERMS_FOR_TRANSFORMATION = {
|
|
90
|
-
FOREST_LAND: ("forest", "Forest"),
|
|
91
|
-
ANNUAL_CROPLAND: ("annualCropland", "Annual cropland"),
|
|
92
|
-
PERMANENT_CROPLAND: ("permanentCropland", "Permanent cropland"),
|
|
93
|
-
PERMANENT_PASTURE: ("permanentPasture", "Permanent pasture"),
|
|
94
|
-
OTHER_LAND: ("otherLand", OTHER_LAND) # Not used yet
|
|
95
|
-
}
|
|
96
90
|
SITE_TYPES = {
|
|
97
91
|
SiteSiteType.CROPLAND.value,
|
|
98
92
|
SiteSiteType.FOREST.value,
|
|
@@ -100,8 +94,7 @@ SITE_TYPES = {
|
|
|
100
94
|
SiteSiteType.PERMANENT_PASTURE.value
|
|
101
95
|
}
|
|
102
96
|
DEFAULT_WINDOW_IN_YEARS = 20
|
|
103
|
-
|
|
104
|
-
IPCC_LAND_USE_CATEGORY_PERENNIAL = "Perennial crops"
|
|
97
|
+
DATE_TOLERANCE_IN_YEARS = 2
|
|
105
98
|
OUTPUT_SIGNIFICANT_DIGITS = 3
|
|
106
99
|
|
|
107
100
|
|
|
@@ -127,32 +120,6 @@ def site_area_sum_to_100(dict_of_percentages: dict):
|
|
|
127
120
|
math.isclose(sum(dict_of_percentages.values()), 0.0, rel_tol=0.01))
|
|
128
121
|
|
|
129
122
|
|
|
130
|
-
def _lookup_land_use_type(nodes: list) -> str:
|
|
131
|
-
"""Look up the land use type from a management node."""
|
|
132
|
-
return "" if nodes == [] else get_lookup_value(
|
|
133
|
-
lookup_term=nodes[0].get("term", {}),
|
|
134
|
-
column=LOOKUPS.get("landCover")[1],
|
|
135
|
-
model=MODEL,
|
|
136
|
-
term=nodes[0].get("term", {})
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
def _crop_ipcc_land_use_category(
|
|
141
|
-
crop_term_id: str,
|
|
142
|
-
lookup_term_type: str = TermTermType.LANDCOVER.value
|
|
143
|
-
) -> str:
|
|
144
|
-
"""
|
|
145
|
-
Looks up the crop in the lookup.
|
|
146
|
-
Returns the IPCC_LAND_USE_CATEGORY.
|
|
147
|
-
"""
|
|
148
|
-
return get_lookup_value(
|
|
149
|
-
lookup_term={"@id": crop_term_id, "type": "Term", "termType": lookup_term_type},
|
|
150
|
-
column=LOOKUPS.get("crop")[1],
|
|
151
|
-
model=MODEL,
|
|
152
|
-
term={"@id": crop_term_id, "type": "Term", "termType": lookup_term_type}
|
|
153
|
-
)
|
|
154
|
-
|
|
155
|
-
|
|
156
123
|
def get_changes(country_id: str, end_year: int) -> dict:
|
|
157
124
|
"""
|
|
158
125
|
For each entry in ALL_LAND_USE_TERMS, creates a key: value in output dictionary, also TOTAL
|
|
@@ -399,7 +366,7 @@ def _get_complete_faostat_to_crop_mapping() -> dict:
|
|
|
399
366
|
get_table_value(lookup, 'termid', crop_term_id, column_name("cropGroupingFaostatArea"))
|
|
400
367
|
)
|
|
401
368
|
if key:
|
|
402
|
-
mappings[key].append(
|
|
369
|
+
mappings[key].append(crop_ipcc_land_use_category(crop_term_id=crop_term_id, lookup_term_type="crop"))
|
|
403
370
|
return {
|
|
404
371
|
fao_name: max(set(crop_terms), key=crop_terms.count)
|
|
405
372
|
for fao_name, crop_terms in mappings.items()
|
|
@@ -420,6 +387,18 @@ def _get_harvested_area(country_id: str, year: int, faostat_name: str) -> float:
|
|
|
420
387
|
)
|
|
421
388
|
|
|
422
389
|
|
|
390
|
+
def _get_term_id_for_crop(nodes: set, land_type: str) -> str:
|
|
391
|
+
"""Use the original crop term id for permanent/perennial crops and the land use id for other types."""
|
|
392
|
+
result = next(
|
|
393
|
+
(node for node in nodes if crop_ipcc_land_use_category(node[0]) == IPCC_LAND_USE_CATEGORY_PERENNIAL), None
|
|
394
|
+
)
|
|
395
|
+
return (
|
|
396
|
+
# Take first perennial crop - not multi-cropping
|
|
397
|
+
result[0] if land_type == PERMANENT_CROPLAND and result else
|
|
398
|
+
LAND_USE_TERMS_FOR_TRANSFORMATION[land_type][0]
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
|
|
423
402
|
def _run_make_management_nodes(existing_nodes: list, percentage_transformed_from: dict, start_year: int) -> list:
|
|
424
403
|
"""Creates a list of new management nodes, excluding any dates matching existing ones."""
|
|
425
404
|
existing_nodes_set = {
|
|
@@ -435,7 +414,8 @@ def _run_make_management_nodes(existing_nodes: list, percentage_transformed_from
|
|
|
435
414
|
"percentage": 0 if ratio == -0.0 else to_precision(
|
|
436
415
|
number=ratio * 100,
|
|
437
416
|
digits=OUTPUT_SIGNIFICANT_DIGITS
|
|
438
|
-
)
|
|
417
|
+
),
|
|
418
|
+
"term_id": _get_term_id_for_crop(existing_nodes_set, land_type=land_type)
|
|
439
419
|
}
|
|
440
420
|
for land_type, ratio in percentage_transformed_from.items()
|
|
441
421
|
]
|
|
@@ -443,7 +423,7 @@ def _run_make_management_nodes(existing_nodes: list, percentage_transformed_from
|
|
|
443
423
|
|
|
444
424
|
return [
|
|
445
425
|
_management(
|
|
446
|
-
term_id=
|
|
426
|
+
term_id=value.get("term_id"),
|
|
447
427
|
value=value.get("percentage"),
|
|
448
428
|
start_date=value.get("land_management_key")[1],
|
|
449
429
|
end_date=value.get("land_management_key")[2]
|
|
@@ -527,14 +507,13 @@ def _get_net_expansion_cultivated_vs_harvested(annual_crops_net_expansion, chang
|
|
|
527
507
|
return net_expansion_cultivated_vs_harvested
|
|
528
508
|
|
|
529
509
|
|
|
530
|
-
def _should_run_historical_land_use_change(site: dict, land_use_type: str) -> tuple[bool, dict]:
|
|
531
|
-
management_nodes = filter_list_term_type(site.get("management", []), TermTermType.LANDCOVER)
|
|
510
|
+
def _should_run_historical_land_use_change(site: dict, nodes: list, land_use_type: str) -> tuple[bool, dict]:
|
|
532
511
|
# Assume a single management node for single-cropping.
|
|
533
512
|
return _should_run_historical_land_use_change_single_crop(
|
|
534
513
|
site=site,
|
|
535
|
-
term=
|
|
514
|
+
term=nodes[0].get("term", {}),
|
|
536
515
|
country_id=site.get("country", {}).get("@id"),
|
|
537
|
-
end_year=int(
|
|
516
|
+
end_year=int(nodes[0].get("endDate")[:4]),
|
|
538
517
|
land_use_type=land_use_type
|
|
539
518
|
)
|
|
540
519
|
|
|
@@ -698,23 +677,77 @@ def _should_run_historical_land_use_change_single_crop(
|
|
|
698
677
|
sum_of_site_areas_is_100
|
|
699
678
|
]
|
|
700
679
|
)
|
|
701
|
-
logShouldRun(site, MODEL, term
|
|
680
|
+
logShouldRun(site, MODEL, term.get("@id"), should_run, model_key=MODEL_KEY)
|
|
702
681
|
|
|
703
682
|
return should_run, site_area
|
|
704
683
|
|
|
705
684
|
|
|
685
|
+
def _get_land_use_term_from_node(node: dict) -> str:
|
|
686
|
+
return get_lookup_value(
|
|
687
|
+
lookup_term=node.get("term", {}),
|
|
688
|
+
column=LOOKUPS.get("landCover")[1],
|
|
689
|
+
model=MODEL,
|
|
690
|
+
term=node.get("term", {})
|
|
691
|
+
)
|
|
692
|
+
|
|
693
|
+
|
|
694
|
+
def _collect_land_use_types(nodes: list) -> list:
|
|
695
|
+
"""Look up the land use type from management nodes."""
|
|
696
|
+
return [
|
|
697
|
+
{
|
|
698
|
+
"id": node.get("term", {}).get("@id"),
|
|
699
|
+
"land-use-type": _get_land_use_term_from_node(node),
|
|
700
|
+
"endDate": node.get("endDate")
|
|
701
|
+
} for node in nodes
|
|
702
|
+
]
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
def _no_prior_land_cover_data(nodes: list, end_date: str) -> bool:
|
|
706
|
+
target_date = (
|
|
707
|
+
datetime.strptime(end_date, DatestrFormat.YEAR_MONTH_DAY.value)
|
|
708
|
+
- timedelta(days=DEFAULT_WINDOW_IN_YEARS * DAYS_IN_YEAR)
|
|
709
|
+
)
|
|
710
|
+
previous_nodes = [
|
|
711
|
+
node for node in nodes
|
|
712
|
+
if abs(_node_date(node) - target_date) < timedelta(days=DATE_TOLERANCE_IN_YEARS * DAYS_IN_YEAR)
|
|
713
|
+
]
|
|
714
|
+
return len(previous_nodes) == 0
|
|
715
|
+
|
|
716
|
+
|
|
706
717
|
def _should_run(site: dict) -> tuple[bool, dict]:
|
|
707
718
|
management_nodes = filter_list_term_type(site.get("management", []), TermTermType.LANDCOVER)
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
719
|
+
summarised_nodes = _collect_land_use_types(management_nodes)
|
|
720
|
+
allowed_land_use_types = [ANNUAL_CROPLAND, PERMANENT_CROPLAND, PERMANENT_PASTURE]
|
|
721
|
+
relevant_nodes = sorted(
|
|
722
|
+
[
|
|
723
|
+
node for node in summarised_nodes
|
|
724
|
+
if node["land-use-type"] in allowed_land_use_types
|
|
725
|
+
],
|
|
726
|
+
key=lambda n: n.get("endDate")
|
|
715
727
|
)
|
|
728
|
+
land_use_type = relevant_nodes[0].get("land-use-type") if relevant_nodes else None
|
|
716
729
|
|
|
717
|
-
|
|
730
|
+
has_no_prior_land_cover_data = _no_prior_land_cover_data(
|
|
731
|
+
nodes=management_nodes,
|
|
732
|
+
end_date=relevant_nodes[-1:][0].get("endDate")
|
|
733
|
+
) if relevant_nodes else None
|
|
734
|
+
|
|
735
|
+
should_run_nodes, site_area = _should_run_historical_land_use_change(
|
|
736
|
+
site=site,
|
|
737
|
+
nodes=management_nodes,
|
|
738
|
+
land_use_type=land_use_type
|
|
739
|
+
) if all([land_use_type, has_no_prior_land_cover_data]) else (False, {})
|
|
740
|
+
|
|
741
|
+
logRequirements(site, model=MODEL, model_key=MODEL_KEY,
|
|
742
|
+
has_management_nodes=bool(management_nodes),
|
|
743
|
+
land_use_type=land_use_type,
|
|
744
|
+
allowed_land_use_types=';'.join(allowed_land_use_types),
|
|
745
|
+
has_no_prior_land_cover_data=has_no_prior_land_cover_data,
|
|
746
|
+
summarised_nodes=log_as_table(summarised_nodes))
|
|
747
|
+
|
|
748
|
+
should_run = all([land_use_type, has_no_prior_land_cover_data, should_run_nodes])
|
|
749
|
+
logShouldRun(site, MODEL, None, should_run, model_key=MODEL_KEY)
|
|
750
|
+
return should_run_nodes, site_area
|
|
718
751
|
|
|
719
752
|
|
|
720
753
|
def run(site: dict) -> list:
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Creates an [emissionsResourceUse](https://hestia.earth/schema/Emission) for every landCover land transformation.
|
|
3
|
+
contained within the [ImpactAssesment.cycle](https://hestia.earth/schema/ImpactAssessment#cycle), averaged over the last
|
|
4
|
+
100 years.
|
|
5
|
+
|
|
6
|
+
It does this by multiplying the land occupation during the cycle by the
|
|
7
|
+
[Site](https://www-staging.hestia.earth/schema/Site) area 100 years ago and dividing by 100.
|
|
8
|
+
|
|
9
|
+
Land transformation from [land type] 100 years =
|
|
10
|
+
(Land occupation, during Cycle * Site Percentage Area 100 years ago [land type] / 100) / 100
|
|
11
|
+
"""
|
|
12
|
+
from .resourceUse_utils import run_resource_use
|
|
13
|
+
|
|
14
|
+
REQUIREMENTS = {
|
|
15
|
+
"ImpactAssessment": {
|
|
16
|
+
"Site": {
|
|
17
|
+
"management": [{"@type": "Management", "value": ">=0", "term.termType": "landCover", "endDate": ""}]
|
|
18
|
+
},
|
|
19
|
+
"emissionsResourceUse": [
|
|
20
|
+
{
|
|
21
|
+
"@type": "Indicator",
|
|
22
|
+
"term.@id": "landOccupationDuringCycle",
|
|
23
|
+
"landCover": {
|
|
24
|
+
"@type": "Term",
|
|
25
|
+
"termType": "landCover"
|
|
26
|
+
},
|
|
27
|
+
"value": ">=0"
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"endDate": ""
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
RETURNS = {
|
|
34
|
+
"Indicator": [{
|
|
35
|
+
"value": "",
|
|
36
|
+
"landCover": "",
|
|
37
|
+
"previousLandCover": ""
|
|
38
|
+
}]
|
|
39
|
+
}
|
|
40
|
+
TERM_ID = 'landTransformation100YearAverageDuringCycle'
|
|
41
|
+
_HISTORIC_DATE_OFFSET = 100
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def run(impact_assessment: dict):
|
|
45
|
+
return run_resource_use(
|
|
46
|
+
impact_assessment=impact_assessment,
|
|
47
|
+
historic_date_offset=_HISTORIC_DATE_OFFSET,
|
|
48
|
+
term_id=TERM_ID
|
|
49
|
+
)
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Creates an [emissionsResourceUse](https://hestia.earth/schema/Emission) for every landCover land transformation.
|
|
3
|
+
contained within the [ImpactAssesment.cycle](https://hestia.earth/schema/ImpactAssessment#cycle), averaged over the last
|
|
4
|
+
20 years.
|
|
5
|
+
|
|
6
|
+
It does this by multiplying the land occupation during the cycle by the
|
|
7
|
+
[Site](https://www-staging.hestia.earth/schema/Site) area 20 years ago and dividing by 20.
|
|
8
|
+
|
|
9
|
+
Land transformation from [land type] 20 years =
|
|
10
|
+
(Land occupation, during Cycle * Site Percentage Area 20 years ago [land type] / 100) / 20
|
|
11
|
+
"""
|
|
12
|
+
from .resourceUse_utils import run_resource_use
|
|
13
|
+
|
|
14
|
+
REQUIREMENTS = {
|
|
15
|
+
"ImpactAssessment": {
|
|
16
|
+
"Site": {
|
|
17
|
+
"management": [{"@type": "Management", "value": ">=0", "term.termType": "landCover", "endDate": ""}]
|
|
18
|
+
},
|
|
19
|
+
"emissionsResourceUse": [
|
|
20
|
+
{
|
|
21
|
+
"@type": "Indicator",
|
|
22
|
+
"term.@id": "landOccupationDuringCycle",
|
|
23
|
+
"landCover": {
|
|
24
|
+
"@type": "Term",
|
|
25
|
+
"termType": "landCover"
|
|
26
|
+
},
|
|
27
|
+
"value": ">=0"
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"endDate": ""
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
RETURNS = {
|
|
34
|
+
"Indicator": [{
|
|
35
|
+
"value": "",
|
|
36
|
+
"landCover": "",
|
|
37
|
+
"previousLandCover": ""
|
|
38
|
+
}]
|
|
39
|
+
}
|
|
40
|
+
TERM_ID = 'landTransformation20YearAverageDuringCycle'
|
|
41
|
+
_HISTORIC_DATE_OFFSET = 20
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def run(impact_assessment: dict):
|
|
45
|
+
return run_resource_use(
|
|
46
|
+
impact_assessment=impact_assessment,
|
|
47
|
+
historic_date_offset=_HISTORIC_DATE_OFFSET,
|
|
48
|
+
term_id=TERM_ID
|
|
49
|
+
)
|