hestia-earth-models 0.70.3__py3-none-any.whl → 0.70.4__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.
- hestia_earth/models/geospatialDatabase/altitude.py +1 -0
- hestia_earth/models/geospatialDatabase/clayContent.py +3 -3
- hestia_earth/models/geospatialDatabase/sandContent.py +3 -3
- hestia_earth/models/geospatialDatabase/utils.py +1 -2
- hestia_earth/models/hestia/landCover.py +25 -12
- hestia_earth/models/ipcc2019/ch4ToAirOrganicSoilCultivation.py +7 -7
- hestia_earth/models/ipcc2019/co2ToAirOrganicSoilCultivation.py +5 -4
- hestia_earth/models/ipcc2019/n2OToAirOrganicSoilCultivationDirect.py +7 -7
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1.py +2 -2
- hestia_earth/models/ipcc2019/organicSoilCultivation_utils.py +11 -3
- hestia_earth/models/mocking/search-results.json +1296 -1310
- hestia_earth/models/utils/site.py +2 -1
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.70.3.dist-info → hestia_earth_models-0.70.4.dist-info}/METADATA +1 -1
- {hestia_earth_models-0.70.3.dist-info → hestia_earth_models-0.70.4.dist-info}/RECORD +23 -23
- tests/models/hestia/test_landCover.py +24 -1
- tests/models/ipcc2019/test_ch4ToAirOrganicSoilCultivation.py +2 -1
- tests/models/ipcc2019/test_co2ToAirOrganicSoilCultivation.py +2 -1
- tests/models/ipcc2019/test_n2OToAirOrganicSoilCultivationDirect.py +2 -1
- tests/models/ipcc2019/test_organicCarbonPerHa_tier_1.py +8 -3
- {hestia_earth_models-0.70.3.dist-info → hestia_earth_models-0.70.4.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.70.3.dist-info → hestia_earth_models-0.70.4.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.70.3.dist-info → hestia_earth_models-0.70.4.dist-info}/top_level.txt +0 -0
@@ -27,6 +27,7 @@ EE_PARAMS = {
|
|
27
27
|
'collection': 'USGS/GMTED2010_FULL',
|
28
28
|
'ee_type': 'raster',
|
29
29
|
'band_name': 'med',
|
30
|
+
'reducer': 'mode',
|
30
31
|
'is_image': True
|
31
32
|
}
|
32
33
|
BIBLIO_TITLE = 'An Enhanced Global Elevation Model Generalized From Multiple Higher Resolution Source Datasets'
|
@@ -92,14 +92,14 @@ def _run(site: dict):
|
|
92
92
|
def _should_run(site: dict):
|
93
93
|
contains_geospatial_data = has_geospatial_data(site)
|
94
94
|
below_max_area_size = should_download(TERM_ID, site)
|
95
|
-
|
95
|
+
has_no_original_texture_measurements = not has_original_by_ids(site.get('measurements', []), SOIL_TEXTURE_IDS)
|
96
96
|
|
97
97
|
logRequirements(site, model=MODEL, term=TERM_ID,
|
98
98
|
contains_geospatial_data=contains_geospatial_data,
|
99
99
|
below_max_area_size=below_max_area_size,
|
100
|
-
|
100
|
+
has_no_original_texture_measurements=has_no_original_texture_measurements)
|
101
101
|
|
102
|
-
should_run = all([contains_geospatial_data, below_max_area_size,
|
102
|
+
should_run = all([contains_geospatial_data, below_max_area_size, has_no_original_texture_measurements])
|
103
103
|
logShouldRun(site, MODEL, TERM_ID, should_run)
|
104
104
|
return should_run
|
105
105
|
|
@@ -92,14 +92,14 @@ def _run(site: dict):
|
|
92
92
|
def _should_run(site: dict):
|
93
93
|
contains_geospatial_data = has_geospatial_data(site)
|
94
94
|
below_max_area_size = should_download(TERM_ID, site)
|
95
|
-
|
95
|
+
has_no_original_texture_measurements = not has_original_by_ids(site.get('measurements', []), SOIL_TEXTURE_IDS)
|
96
96
|
|
97
97
|
logRequirements(site, model=MODEL, term=TERM_ID,
|
98
98
|
contains_geospatial_data=contains_geospatial_data,
|
99
99
|
below_max_area_size=below_max_area_size,
|
100
|
-
|
100
|
+
has_no_original_texture_measurements=has_no_original_texture_measurements)
|
101
101
|
|
102
|
-
should_run = all([contains_geospatial_data, below_max_area_size,
|
102
|
+
should_run = all([contains_geospatial_data, below_max_area_size, has_no_original_texture_measurements])
|
103
103
|
logShouldRun(site, MODEL, TERM_ID, should_run)
|
104
104
|
return should_run
|
105
105
|
|
@@ -157,8 +157,7 @@ def _get_cached_data(term: str, site: dict, data: dict):
|
|
157
157
|
isinstance(cache, dict),
|
158
158
|
cache_sub_key
|
159
159
|
]) else cache
|
160
|
-
|
161
|
-
debugValues(site, model=MODEL, term=term, value_from_cache=value)
|
160
|
+
debugValues(site, model=MODEL, term=term, value_from_cache=value)
|
162
161
|
return value
|
163
162
|
|
164
163
|
|
@@ -16,7 +16,7 @@ from hestia_earth.models.utils.constant import DAYS_IN_YEAR
|
|
16
16
|
from hestia_earth.models.utils.management import _new_management
|
17
17
|
from hestia_earth.models.utils.term import get_lookup_value
|
18
18
|
from hestia_earth.models.utils.lookup import get_region_lookup_value
|
19
|
-
from hestia_earth.models.utils.blank_node import
|
19
|
+
from hestia_earth.models.utils.blank_node import DatestrFormat, _gapfill_datestr, DatestrGapfillMode
|
20
20
|
from .utils import (
|
21
21
|
IPCC_LAND_USE_CATEGORY_ANNUAL,
|
22
22
|
IPCC_LAND_USE_CATEGORY_PERENNIAL,
|
@@ -410,8 +410,15 @@ def _get_faostat_name(term: dict) -> str:
|
|
410
410
|
return _get_lookup_with_cache(term, "cropGroupingFaostatArea")
|
411
411
|
|
412
412
|
|
413
|
+
def _get_most_common_or_alphabetically_first(crop_terms: list) -> str:
|
414
|
+
histogram = {term: crop_terms.count(term) for term in crop_terms}
|
415
|
+
max_freq = max(histogram.values())
|
416
|
+
# Sorted; to be deterministic
|
417
|
+
return sorted([term for term, freq in histogram.items() if freq == max_freq])[0]
|
418
|
+
|
419
|
+
|
413
420
|
def _get_complete_faostat_to_crop_mapping() -> dict:
|
414
|
-
"""Returns mapping in the format: {faostat_name:
|
421
|
+
"""Returns mapping in the format: {faostat_name: IPCC_LAND_USE_CATEGORY, ...}"""
|
415
422
|
lookup = download_lookup("crop.csv")
|
416
423
|
mappings = defaultdict(list)
|
417
424
|
for crop_term_id in [row[0] for row in lookup]:
|
@@ -421,7 +428,7 @@ def _get_complete_faostat_to_crop_mapping() -> dict:
|
|
421
428
|
if key:
|
422
429
|
mappings[key].append(crop_ipcc_land_use_category(crop_term_id=crop_term_id, lookup_term_type="crop"))
|
423
430
|
return {
|
424
|
-
fao_name:
|
431
|
+
fao_name: _get_most_common_or_alphabetically_first(crop_terms)
|
425
432
|
for fao_name, crop_terms in mappings.items()
|
426
433
|
}
|
427
434
|
|
@@ -758,26 +765,32 @@ def _collect_land_use_types(nodes: list) -> list:
|
|
758
765
|
"id": node.get("term", {}).get("@id"),
|
759
766
|
"land-use-type": _get_land_use_term_from_node(node),
|
760
767
|
"endDate": _gapfill_datestr(datestr=node.get("endDate"), mode=DatestrGapfillMode.END)[:10],
|
761
|
-
"startDate": _gapfill_datestr(
|
762
|
-
datestr=node.get("startDate"), mode=DatestrGapfillMode.START
|
763
|
-
)[:10] if node.get("startDate") else None
|
768
|
+
"startDate": _gapfill_datestr(datestr=node.get("startDate"), mode=DatestrGapfillMode.START)[:10]
|
764
769
|
} for node in nodes
|
765
770
|
]
|
766
771
|
|
767
772
|
|
768
|
-
def _no_prior_land_cover_data(nodes: list,
|
773
|
+
def _no_prior_land_cover_data(nodes: list, target_node: dict) -> bool:
|
774
|
+
"""
|
775
|
+
Returns true if there are no nodes whose start & end dates the target_node falls within,
|
776
|
+
including a tolerance.
|
777
|
+
"""
|
769
778
|
target_date = (
|
770
|
-
datetime.strptime(
|
779
|
+
datetime.strptime(target_node.get('startDate') or target_node.get('endDate'),
|
780
|
+
DatestrFormat.YEAR_MONTH_DAY.value)
|
771
781
|
- timedelta(days=DEFAULT_WINDOW_IN_YEARS * DAYS_IN_YEAR)
|
772
782
|
)
|
783
|
+
tolerance = timedelta(days=DATE_TOLERANCE_IN_YEARS * DAYS_IN_YEAR)
|
773
784
|
previous_nodes = [
|
774
785
|
node for node in nodes
|
775
|
-
if
|
786
|
+
if datetime.strptime(node.get("startDate"), DatestrFormat.YEAR_MONTH_DAY.value) - tolerance
|
787
|
+
< target_date <
|
788
|
+
datetime.strptime(node.get("endDate"), DatestrFormat.YEAR_MONTH_DAY.value) + tolerance
|
776
789
|
]
|
777
790
|
return len(previous_nodes) == 0
|
778
791
|
|
779
792
|
|
780
|
-
def _should_run(site: dict) -> tuple[bool, dict]:
|
793
|
+
def _should_run(site: dict) -> tuple[bool, list, dict]:
|
781
794
|
management_nodes = _collect_land_use_types(
|
782
795
|
[
|
783
796
|
node for node in filter_list_term_type(site.get("management", []), TermTermType.LANDCOVER)
|
@@ -795,8 +808,8 @@ def _should_run(site: dict) -> tuple[bool, dict]:
|
|
795
808
|
land_use_type = relevant_nodes[0].get("land-use-type") if relevant_nodes else None
|
796
809
|
|
797
810
|
has_no_prior_land_cover_data = _no_prior_land_cover_data(
|
798
|
-
nodes=
|
799
|
-
|
811
|
+
nodes=management_nodes,
|
812
|
+
target_node=relevant_nodes[-1:][0]
|
800
813
|
) if relevant_nodes else None
|
801
814
|
|
802
815
|
should_run_nodes, site_area = _should_run_historical_land_use_change(
|
@@ -16,8 +16,8 @@ from hestia_earth.models.utils.measurement import most_relevant_measurement_valu
|
|
16
16
|
from hestia_earth.models.utils.site import valid_site_type
|
17
17
|
|
18
18
|
from .organicSoilCultivation_utils import (
|
19
|
-
assign_ditch_category, assign_organic_soil_category, calc_emission, DitchCategory,
|
20
|
-
get_emission_factor, OrganicSoilCategory, remap_categories, valid_eco_climate_zone
|
19
|
+
assign_ditch_category, assign_organic_soil_category, calc_emission, DitchCategory, format_nd_array, format_number,
|
20
|
+
get_ditch_frac, get_emission_factor, OrganicSoilCategory, remap_categories, valid_eco_climate_zone
|
21
21
|
)
|
22
22
|
from . import MODEL
|
23
23
|
|
@@ -224,11 +224,11 @@ def _should_run(cycle: dict):
|
|
224
224
|
eco_climate_zone=eco_climate_zone,
|
225
225
|
organic_soil_category=organic_soil_category,
|
226
226
|
ditch_category=ditch_category,
|
227
|
-
emission_factor=
|
228
|
-
ditch_factor=
|
229
|
-
ditch_frac=
|
230
|
-
land_occupation=land_occupation,
|
231
|
-
histosol=histosol
|
227
|
+
emission_factor=format_nd_array(emission_factor),
|
228
|
+
ditch_factor=format_nd_array(ditch_factor),
|
229
|
+
ditch_frac=format_nd_array(ditch_frac),
|
230
|
+
land_occupation=format_number(land_occupation),
|
231
|
+
histosol=format_number(histosol)
|
232
232
|
)
|
233
233
|
|
234
234
|
should_run = all([
|
@@ -14,7 +14,8 @@ from hestia_earth.models.utils.measurement import most_relevant_measurement_valu
|
|
14
14
|
from hestia_earth.models.utils.site import valid_site_type
|
15
15
|
|
16
16
|
from .organicSoilCultivation_utils import (
|
17
|
-
assign_organic_soil_category, calc_emission,
|
17
|
+
assign_organic_soil_category, calc_emission, format_nd_array, format_number, get_emission_factor,
|
18
|
+
OrganicSoilCategory, valid_eco_climate_zone
|
18
19
|
)
|
19
20
|
from . import MODEL
|
20
21
|
|
@@ -182,9 +183,9 @@ def _should_run(cycle: dict):
|
|
182
183
|
cycle, model=MODEL, term=TERM_ID,
|
183
184
|
eco_climate_zone=eco_climate_zone,
|
184
185
|
organic_soil_category=organic_soil_category,
|
185
|
-
emission_factor=
|
186
|
-
land_occupation=land_occupation,
|
187
|
-
histosol=histosol
|
186
|
+
emission_factor=format_nd_array(emission_factor),
|
187
|
+
land_occupation=format_number(land_occupation),
|
188
|
+
histosol=format_number(histosol)
|
188
189
|
)
|
189
190
|
|
190
191
|
should_run = all([
|
@@ -8,8 +8,8 @@ from hestia_earth.models.utils.measurement import most_relevant_measurement_valu
|
|
8
8
|
from hestia_earth.models.utils.site import valid_site_type
|
9
9
|
|
10
10
|
from .organicSoilCultivation_utils import (
|
11
|
-
assign_organic_soil_category, calc_emission, get_emission_factor, OrganicSoilCategory,
|
12
|
-
valid_eco_climate_zone
|
11
|
+
assign_organic_soil_category, calc_emission, format_number, get_emission_factor, OrganicSoilCategory,
|
12
|
+
remap_categories, valid_eco_climate_zone
|
13
13
|
)
|
14
14
|
from . import MODEL
|
15
15
|
|
@@ -127,9 +127,9 @@ def _should_run(cycle: dict):
|
|
127
127
|
cycle, model=MODEL, term=TERM_ID,
|
128
128
|
eco_climate_zone=eco_climate_zone,
|
129
129
|
organic_soil_category=organic_soil_category,
|
130
|
-
emission_factor=f"{emission_factor_mean} ± {emission_factor_sd}",
|
131
|
-
land_occupation=land_occupation,
|
132
|
-
histosol=histosol
|
130
|
+
emission_factor=f"{format_number(emission_factor_mean)} ± {format_number(emission_factor_sd)}",
|
131
|
+
land_occupation=format_number(land_occupation),
|
132
|
+
histosol=format_number(histosol)
|
133
133
|
)
|
134
134
|
|
135
135
|
should_run = all([
|
@@ -151,8 +151,8 @@ def _should_run(cycle: dict):
|
|
151
151
|
|
152
152
|
|
153
153
|
def _run(emission_factor_mean: float, emission_factor_sd: float, histosol: float, land_occupation: float):
|
154
|
-
value = calc_emission(TERM_ID, emission_factor_mean, histosol, land_occupation)
|
155
|
-
sd = calc_emission(TERM_ID, emission_factor_sd, histosol, land_occupation)
|
154
|
+
value = round(calc_emission(TERM_ID, emission_factor_mean, histosol, land_occupation), 6)
|
155
|
+
sd = round(calc_emission(TERM_ID, emission_factor_sd, histosol, land_occupation), 6)
|
156
156
|
return [_emission(value, sd)]
|
157
157
|
|
158
158
|
|
@@ -1346,7 +1346,7 @@ def _assign_ipcc_management_category(
|
|
1346
1346
|
ipcc_land_use_category, IpccManagementCategory.NOT_RELEVANT
|
1347
1347
|
)
|
1348
1348
|
|
1349
|
-
land_cover_nodes = filter_list_term_type(management_nodes, [TermTermType.
|
1349
|
+
land_cover_nodes = filter_list_term_type(management_nodes, [TermTermType.PASTUREMANAGEMENT])
|
1350
1350
|
tillage_nodes = filter_list_term_type(management_nodes, [TermTermType.TILLAGE])
|
1351
1351
|
|
1352
1352
|
should_run_ = any([
|
@@ -1461,7 +1461,7 @@ Value: Corresponding decision tree for IPCC management categories based on land
|
|
1461
1461
|
"""
|
1462
1462
|
|
1463
1463
|
_IPCC_LAND_USE_CATEGORY_TO_DEFAULT_IPCC_MANAGEMENT_CATEGORY = {
|
1464
|
-
IpccLandUseCategory.GRASSLAND: IpccManagementCategory.
|
1464
|
+
IpccLandUseCategory.GRASSLAND: IpccManagementCategory.NOMINALLY_MANAGED,
|
1465
1465
|
IpccLandUseCategory.ANNUAL_CROPS_WET: IpccManagementCategory.UNKNOWN,
|
1466
1466
|
IpccLandUseCategory.ANNUAL_CROPS: IpccManagementCategory.UNKNOWN
|
1467
1467
|
}
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from enum import Enum
|
2
2
|
from typing import Literal
|
3
|
+
import numpy as np
|
3
4
|
|
4
5
|
from hestia_earth.schema import SiteSiteType
|
5
6
|
from hestia_earth.utils.model import find_primary_product
|
@@ -22,8 +23,7 @@ _NETHERLANDS_TERM_ID = "GADM-NLD"
|
|
22
23
|
|
23
24
|
_CONVERSION_FACTORS = {
|
24
25
|
"co2ToAirOrganicSoilCultivation": 1000 * get_atomic_conversion(Units.KG_CO2, Units.TO_C),
|
25
|
-
"
|
26
|
-
"n2OToAirOrganicSoilCultivationDirect": 1000 * get_atomic_conversion(Units.KG_N2O, Units.TO_N)
|
26
|
+
"n2OToAirOrganicSoilCultivationDirect": get_atomic_conversion(Units.KG_N2O, Units.TO_N)
|
27
27
|
}
|
28
28
|
_DEFAULT_FACTOR = {"value": 0}
|
29
29
|
_EXCLUDED_ECO_CLIMATE_ZONES = [EcoClimateZone.POLAR_MOIST, EcoClimateZone.POLAR_DRY]
|
@@ -136,7 +136,7 @@ def calc_emission(
|
|
136
136
|
"""
|
137
137
|
Calculate the emission and convert it to kg/ha-1.
|
138
138
|
"""
|
139
|
-
return emission_factor * land_occupation * histosol * _CONVERSION_FACTORS
|
139
|
+
return emission_factor * land_occupation * histosol * _CONVERSION_FACTORS.get(emission_id, 1) / 100
|
140
140
|
|
141
141
|
|
142
142
|
def remap_categories(
|
@@ -157,3 +157,11 @@ def valid_eco_climate_zone(
|
|
157
157
|
Validate that the model should run for a specific eco-climate zone.
|
158
158
|
"""
|
159
159
|
return isinstance(eco_climate_zone, EcoClimateZone) and eco_climate_zone not in _EXCLUDED_ECO_CLIMATE_ZONES
|
160
|
+
|
161
|
+
|
162
|
+
def format_number(value) -> str:
|
163
|
+
return f"{value:.3g}" if isinstance(value, (float, int)) else "None"
|
164
|
+
|
165
|
+
|
166
|
+
def format_nd_array(value) -> str:
|
167
|
+
return f"{np.mean(value):.3g} ± {np.std(value):.3g}" if isinstance(value, np.ndarray) else "None"
|