hestia-earth-models 0.64.14__py3-none-any.whl → 0.65.0__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.

Files changed (112) hide show
  1. hestia_earth/models/agribalyse2016/fuelElectricity.py +1 -1
  2. hestia_earth/models/cache_sites.py +15 -24
  3. hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsLandTransformation.py +6 -9
  4. hestia_earth/models/cycle/input/hestiaAggregatedData.py +46 -22
  5. hestia_earth/models/cycle/pre_checks/cache_sources.py +3 -25
  6. hestia_earth/models/cycle/product/economicValueShare.py +2 -2
  7. hestia_earth/models/environmentalFootprintV3/soilQualityIndexLandTransformation.py +11 -33
  8. hestia_earth/models/faostat2018/landTransformation100YearAverageDuringCycle.py +34 -0
  9. hestia_earth/models/faostat2018/landTransformation20YearAverageDuringCycle.py +34 -0
  10. hestia_earth/models/faostat2018/utils.py +47 -3
  11. hestia_earth/models/hestia/landCover.py +5 -5
  12. hestia_earth/models/hestia/seed_emissions.py +275 -0
  13. hestia_earth/models/ipcc2019/aboveGroundBiomass.py +2 -2
  14. hestia_earth/models/ipcc2019/belowGroundBiomass.py +8 -2
  15. hestia_earth/models/ipcc2019/biomass_utils.py +11 -4
  16. hestia_earth/models/ipcc2019/ch4ToAirEntericFermentation.py +19 -10
  17. hestia_earth/models/ipcc2019/co2ToAirAboveGroundBiomassStockChange.py +2 -1
  18. hestia_earth/models/ipcc2019/co2ToAirBelowGroundBiomassStockChange.py +2 -1
  19. hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +8 -7
  20. hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChange.py +2 -1
  21. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1_utils.py +28 -34
  22. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2_utils.py +8 -12
  23. hestia_earth/models/ipcc2019/organicCarbonPerHa_utils.py +13 -30
  24. hestia_earth/models/linkedImpactAssessment/{landTransformationFromCropland20YearAverageInputsProduction.py → landTransformation100YearAverageInputsProduction.py} +5 -2
  25. hestia_earth/models/linkedImpactAssessment/{landTransformationFromCropland100YearAverageInputsProduction.py → landTransformation20YearAverageInputsProduction.py} +5 -2
  26. hestia_earth/models/linkedImpactAssessment/utils.py +69 -12
  27. hestia_earth/models/mocking/search-results.json +444 -444
  28. hestia_earth/models/pooreNemecek2018/excretaKgN.py +45 -41
  29. hestia_earth/models/pooreNemecek2018/excretaKgVs.py +89 -63
  30. hestia_earth/models/pooreNemecek2018/saplingsDepreciatedAmountPerCycle.py +8 -8
  31. hestia_earth/models/pooreNemecek2018/utils.py +60 -19
  32. hestia_earth/models/schererPfister2015/nErosionSoilFlux.py +4 -3
  33. hestia_earth/models/schererPfister2015/pErosionSoilFlux.py +4 -3
  34. hestia_earth/models/schererPfister2015/utils.py +12 -9
  35. hestia_earth/models/site/management.py +70 -55
  36. hestia_earth/models/site/pre_checks/cache_sources.py +2 -20
  37. hestia_earth/models/utils/__init__.py +12 -1
  38. hestia_earth/models/utils/aggregated.py +1 -1
  39. hestia_earth/models/utils/blank_node.py +20 -12
  40. hestia_earth/models/utils/cache_sources.py +15 -0
  41. hestia_earth/models/utils/crop.py +5 -0
  42. hestia_earth/models/utils/indicator.py +3 -1
  43. hestia_earth/models/version.py +1 -1
  44. {hestia_earth_models-0.64.14.dist-info → hestia_earth_models-0.65.0.dist-info}/METADATA +2 -2
  45. {hestia_earth_models-0.64.14.dist-info → hestia_earth_models-0.65.0.dist-info}/RECORD +75 -104
  46. tests/models/cml2001Baseline/test_abioticResourceDepletionMineralsAndMetals.py +1 -1
  47. tests/models/cycle/input/test_hestiaAggregatedData.py +5 -2
  48. tests/models/environmentalFootprintV3/test_soilQualityIndexLandTransformation.py +39 -28
  49. tests/models/{hyde32/test_landTransformationFromForest20YearAverageDuringCycle.py → faostat2018/test_landTransformation100YearAverageDuringCycle.py} +5 -5
  50. tests/models/{hyde32/test_landTransformationFromForest100YearAverageDuringCycle.py → faostat2018/test_landTransformation20YearAverageDuringCycle.py} +5 -5
  51. tests/models/faostat2018/test_utils.py +28 -0
  52. tests/models/hestia/test_landCover.py +2 -1
  53. tests/models/hestia/test_seed_emissions.py +27 -0
  54. tests/models/ipcc2019/test_aboveGroundBiomass.py +40 -4
  55. tests/models/ipcc2019/test_belowGroundBiomass.py +40 -4
  56. tests/models/ipcc2019/test_co2ToAirAboveGroundBiomassStockChange.py +52 -15
  57. tests/models/ipcc2019/test_co2ToAirBelowGroundBiomassStockChange.py +50 -14
  58. tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChange.py +53 -32
  59. tests/models/ipcc2019/test_organicCarbonPerHa.py +91 -108
  60. tests/models/ipcc2019/test_organicCarbonPerHa_tier_1_utils.py +33 -50
  61. tests/models/ipcc2019/test_organicCarbonPerHa_tier_2_utils.py +0 -52
  62. tests/models/linkedImpactAssessment/test_freshwaterWithdrawalsInputsProduction.py +6 -4
  63. tests/models/linkedImpactAssessment/test_landOccupationInputsProduction.py +6 -4
  64. tests/models/linkedImpactAssessment/{test_landTransformationFromForest100YearAverageInputsProduction.py → test_landTransformation100YearAverageInputsProduction.py} +7 -5
  65. tests/models/linkedImpactAssessment/{test_landTransformationFromForest20YearAverageInputsProduction.py → test_landTransformation20YearAverageInputsProduction.py} +7 -5
  66. tests/models/pooreNemecek2018/test_excretaKgN.py +2 -2
  67. tests/models/pooreNemecek2018/test_excretaKgVs.py +1 -1
  68. tests/models/pooreNemecek2018/test_utils.py +26 -0
  69. tests/models/site/test_management.py +10 -27
  70. tests/models/test_cache_sites.py +40 -12
  71. tests/models/utils/test_blank_node.py +0 -8
  72. tests/models/utils/test_cache_sources.py +21 -0
  73. hestia_earth/models/blonkConsultants2016/landTransformationFromForest20YearAverageDuringCycle.py +0 -90
  74. hestia_earth/models/faostat2018/landTransformationFromCropland100YearAverage.py +0 -74
  75. hestia_earth/models/faostat2018/landTransformationFromCropland20YearAverage.py +0 -74
  76. hestia_earth/models/hyde32/__init__.py +0 -13
  77. hestia_earth/models/hyde32/landTransformationFromCropland100YearAverageDuringCycle.py +0 -60
  78. hestia_earth/models/hyde32/landTransformationFromCropland20YearAverageDuringCycle.py +0 -60
  79. hestia_earth/models/hyde32/landTransformationFromForest100YearAverageDuringCycle.py +0 -60
  80. hestia_earth/models/hyde32/landTransformationFromForest20YearAverageDuringCycle.py +0 -60
  81. hestia_earth/models/hyde32/landTransformationFromOtherNaturalVegetation100YearAverageDuringCycle.py +0 -61
  82. hestia_earth/models/hyde32/landTransformationFromOtherNaturalVegetation20YearAverageDuringCycle.py +0 -61
  83. hestia_earth/models/hyde32/landTransformationFromPermanentPasture100YearAverageDuringCycle.py +0 -61
  84. hestia_earth/models/hyde32/landTransformationFromPermanentPasture20YearAverageDuringCycle.py +0 -61
  85. hestia_earth/models/hyde32/utils.py +0 -72
  86. hestia_earth/models/linkedImpactAssessment/landTransformationFromForest100YearAverageInputsProduction.py +0 -36
  87. hestia_earth/models/linkedImpactAssessment/landTransformationFromForest20YearAverageInputsProduction.py +0 -36
  88. hestia_earth/models/linkedImpactAssessment/landTransformationFromOtherNaturalVegetation100YearAverageInputsProduction.py +0 -36
  89. hestia_earth/models/linkedImpactAssessment/landTransformationFromOtherNaturalVegetation20YearAverageInputsProduction.py +0 -36
  90. hestia_earth/models/linkedImpactAssessment/landTransformationFromPermanentPasture100YearAverageInputsProduction.py +0 -36
  91. hestia_earth/models/linkedImpactAssessment/landTransformationFromPermanentPasture20YearAverageInputsProduction.py +0 -36
  92. tests/models/blonkConsultants2016/test_landTransformationFromForest20YearAverageDuringCycle.py +0 -36
  93. tests/models/cycle/pre_checks/test_cache_sources.py +0 -25
  94. tests/models/faostat2018/test_landTransformationFromCropland100YearAverage.py +0 -40
  95. tests/models/faostat2018/test_landTransformationFromCropland20YearAverage.py +0 -40
  96. tests/models/hyde32/__init__.py +0 -0
  97. tests/models/hyde32/test_landTransformationFromCropland100YearAverageDuringCycle.py +0 -21
  98. tests/models/hyde32/test_landTransformationFromCropland20YearAverageDuringCycle.py +0 -21
  99. tests/models/hyde32/test_landTransformationFromOtherNaturalVegetation100YearAverageDuringCycle.py +0 -23
  100. tests/models/hyde32/test_landTransformationFromOtherNaturalVegetation20YearAverageDuringCycle.py +0 -21
  101. tests/models/hyde32/test_landTransformationFromPermanentPasture100YearAverageDuringCycle.py +0 -21
  102. tests/models/hyde32/test_landTransformationFromPermanentPasture20YearAverageDuringCycle.py +0 -21
  103. tests/models/linkedImpactAssessment/test_landTransformationFromCropland100YearAverageInputsProduction.py +0 -23
  104. tests/models/linkedImpactAssessment/test_landTransformationFromCropland20YearAverageInputsProduction.py +0 -23
  105. tests/models/linkedImpactAssessment/test_landTransformationFromOtherNaturalVegetation100YearAverageInputsProduction.py +0 -23
  106. tests/models/linkedImpactAssessment/test_landTransformationFromOtherNaturalVegetation20YearAverageInputsProduction.py +0 -23
  107. tests/models/linkedImpactAssessment/test_landTransformationFromPermanentPasture100YearAverageInputsProduction.py +0 -24
  108. tests/models/linkedImpactAssessment/test_landTransformationFromPermanentPasture20YearAverageInputsProduction.py +0 -24
  109. tests/models/site/pre_checks/test_cache_sources.py +0 -21
  110. {hestia_earth_models-0.64.14.dist-info → hestia_earth_models-0.65.0.dist-info}/LICENSE +0 -0
  111. {hestia_earth_models-0.64.14.dist-info → hestia_earth_models-0.65.0.dist-info}/WHEEL +0 -0
  112. {hestia_earth_models-0.64.14.dist-info → hestia_earth_models-0.65.0.dist-info}/top_level.txt +0 -0
@@ -21,24 +21,25 @@ from hestia_earth.schema import MeasurementMethodClassification, SiteSiteType, T
21
21
  from hestia_earth.utils.model import find_term_match, filter_list_term_type
22
22
  from hestia_earth.utils.blank_node import get_node_value
23
23
 
24
+ from hestia_earth.models.utils import split_on_condition
24
25
  from hestia_earth.models.utils.array_builders import gen_seed
25
26
  from hestia_earth.models.utils.blank_node import (
26
- cumulative_nodes_match, cumulative_nodes_lookup_match, cumulative_nodes_term_match,
27
- node_lookup_match, node_term_match, group_nodes_by_year, validate_start_date_end_date
27
+ cumulative_nodes_match, cumulative_nodes_lookup_match, cumulative_nodes_term_match, node_lookup_match,
28
+ node_term_match, group_nodes_by_year, validate_start_date_end_date
28
29
  )
29
30
  from hestia_earth.models.utils.ecoClimateZone import EcoClimateZone, get_eco_climate_zone_value
30
31
  from hestia_earth.models.utils.descriptive_stats import calc_descriptive_stats
31
32
  from hestia_earth.models.utils.measurement import _new_measurement
32
33
  from hestia_earth.models.utils.property import get_node_property
34
+ from hestia_earth.models.utils.term import get_residue_removed_or_burnt_terms, get_upland_rice_land_cover_terms
33
35
 
34
36
  from .organicCarbonPerHa_utils import (
35
- check_irrigation, DEPTH_LOWER, DEPTH_UPPER, get_cover_crop_property_terms_with_cache,
36
- get_residue_removed_or_burnt_terms_with_cache, get_upland_rice_land_cover_terms_with_cache,
37
- IPCC_SOIL_CATEGORY_TO_SOIL_TYPE_LOOKUP_VALUE, IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE,
38
- IPCC_MANAGEMENT_CATEGORY_TO_GRASSLAND_MANAGEMENT_TERM_ID,
37
+ check_irrigation, DEPTH_LOWER, DEPTH_UPPER, IPCC_SOIL_CATEGORY_TO_SOIL_TYPE_LOOKUP_VALUE,
38
+ IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE, IPCC_MANAGEMENT_CATEGORY_TO_GRASSLAND_MANAGEMENT_TERM_ID,
39
39
  IPCC_MANAGEMENT_CATEGORY_TO_TILLAGE_MANAGEMENT_LOOKUP_VALUE, IpccSoilCategory, IpccCarbonInputCategory,
40
- IpccLandUseCategory, IpccManagementCategory, MIN_AREA_THRESHOLD, sample_constant, sample_plus_minus_error,
41
- sample_plus_minus_uncertainty, SITE_TYPE_TO_IPCC_LAND_USE_CATEGORY, SUPER_MAJORITY_AREA_THRESHOLD, STATS_DEFINITION
40
+ IpccLandUseCategory, IpccManagementCategory, is_cover_crop, MIN_AREA_THRESHOLD, sample_constant,
41
+ sample_plus_minus_error, sample_plus_minus_uncertainty, SITE_TYPE_TO_IPCC_LAND_USE_CATEGORY,
42
+ SUPER_MAJORITY_AREA_THRESHOLD, STATS_DEFINITION
42
43
  )
43
44
 
44
45
  _LOOKUPS = {
@@ -647,7 +648,7 @@ def should_run(site: dict) -> tuple[bool, dict, dict]:
647
648
  A tuple containing `(should_run_, inventory, kwargs, logs)`.
648
649
  """
649
650
  site_type = site.get("siteType", "")
650
- management_nodes = site.get("management", [])
651
+ management_nodes = get_valid_management_nodes(site)
651
652
  measurement_nodes = site.get("measurements", [])
652
653
 
653
654
  eco_climate_zone = get_eco_climate_zone_value(site, as_enum=True)
@@ -695,6 +696,11 @@ def should_run(site: dict) -> tuple[bool, dict, dict]:
695
696
  return should_run_, inventory, kwargs, logs
696
697
 
697
698
 
699
+ def get_valid_management_nodes(site: dict) -> list[dict]:
700
+ """Retrieve valid mangement nodes from a site."""
701
+ return [node for node in site.get("management", []) if validate_start_date_end_date(node)]
702
+
703
+
698
704
  def run(
699
705
  inventory: dict,
700
706
  *,
@@ -1209,22 +1215,20 @@ def _assign_ipcc_land_use_category(
1209
1215
  DECISION_TREE = _LAND_USE_CATEGORY_DECISION_TREE
1210
1216
  DEFAULT = IpccLandUseCategory.OTHER
1211
1217
 
1212
- land_cover_nodes = [
1213
- node for node in filter_list_term_type(management_nodes, [TermTermType.LANDCOVER])
1214
- if validate_start_date_end_date(node)
1215
- ]
1216
- water_regime_nodes = [
1217
- node for node in filter_list_term_type(management_nodes, [TermTermType.WATERREGIME])
1218
- if validate_start_date_end_date(node)
1219
- ]
1218
+ cover_crop_nodes, land_cover_nodes = split_on_condition(
1219
+ filter_list_term_type(management_nodes, [TermTermType.LANDCOVER]),
1220
+ is_cover_crop
1221
+ )
1222
+
1223
+ water_regime_nodes = filter_list_term_type(management_nodes, [TermTermType.WATERREGIME])
1220
1224
 
1221
1225
  has_irrigation = check_irrigation(water_regime_nodes)
1222
1226
  has_upland_rice = _has_upland_rice(land_cover_nodes)
1223
1227
  has_irrigated_upland_rice = has_upland_rice and has_irrigation
1224
- has_long_fallow = _has_long_fallow(land_cover_nodes)
1228
+ has_long_fallow = _has_long_fallow(cover_crop_nodes)
1225
1229
  has_wetland_soils = ipcc_soil_category is IpccSoilCategory.WETLAND_SOILS
1226
1230
 
1227
- should_run_ = bool(land_cover_nodes)
1231
+ should_run_ = land_cover_nodes or cover_crop_nodes
1228
1232
 
1229
1233
  return next(
1230
1234
  (
@@ -1257,7 +1261,7 @@ def _has_upland_rice(land_cover_nodes: list[dict]) -> bool:
1257
1261
  """
1258
1262
  return cumulative_nodes_term_match(
1259
1263
  land_cover_nodes,
1260
- target_term_ids=get_upland_rice_land_cover_terms_with_cache(),
1264
+ target_term_ids=get_upland_rice_land_cover_terms(),
1261
1265
  cumulative_threshold=SUPER_MAJORITY_AREA_THRESHOLD
1262
1266
  )
1263
1267
 
@@ -1278,14 +1282,7 @@ def _has_long_fallow(land_cover_nodes: list[dict]) -> bool:
1278
1282
  bool
1279
1283
  `True` if long fallow is present, `False` otherwise.
1280
1284
  """
1281
- LOOKUP = _LOOKUPS["landCover"][0]
1282
- TARGET_LOOKUP_VALUE = "Set aside"
1283
- return cumulative_nodes_lookup_match(
1284
- land_cover_nodes,
1285
- lookup=LOOKUP,
1286
- target_lookup_values=TARGET_LOOKUP_VALUE,
1287
- cumulative_threshold=SUPER_MAJORITY_AREA_THRESHOLD
1288
- ) or cumulative_nodes_match(
1285
+ return cumulative_nodes_match(
1289
1286
  lambda node: get_node_property(node, _LONG_FALLOW_CROP_TERM_ID, False).get("value", 0),
1290
1287
  land_cover_nodes,
1291
1288
  cumulative_threshold=SUPER_MAJORITY_AREA_THRESHOLD
@@ -1337,7 +1334,6 @@ def _check_ipcc_land_use_category(*, key: IpccLandUseCategory, land_cover_nodes:
1337
1334
 
1338
1335
  _IPCC_LAND_USE_CATEGORY_TO_VALIDATION_KWARGS = {
1339
1336
  IpccLandUseCategory.ANNUAL_CROPS_WET: {"has_wetland_soils"},
1340
- IpccLandUseCategory.SET_ASIDE: {"has_long_fallow"},
1341
1337
  }
1342
1338
  """
1343
1339
  Keyword arguments that need to be validated in addition to the `landCover` lookup match for specific
@@ -1345,6 +1341,7 @@ Keyword arguments that need to be validated in addition to the `landCover` looku
1345
1341
  """
1346
1342
 
1347
1343
  _IPCC_LAND_USE_CATEGORY_TO_OVERRIDE_KWARGS = {
1344
+ IpccLandUseCategory.SET_ASIDE: {"has_long_fallow"},
1348
1345
  IpccLandUseCategory.PADDY_RICE_CULTIVATION: {"has_irrigated_upland_rice"}
1349
1346
  }
1350
1347
  """
@@ -1960,10 +1957,7 @@ def _get_carbon_input_kwargs(
1960
1957
  )
1961
1958
 
1962
1959
  has_cover_crop = cumulative_nodes_match(
1963
- lambda node: any(
1964
- get_node_property(node, term_id, False).get("value", False)
1965
- for term_id in get_cover_crop_property_terms_with_cache()
1966
- ),
1960
+ is_cover_crop,
1967
1961
  land_cover_nodes,
1968
1962
  cumulative_threshold=MIN_AREA_THRESHOLD
1969
1963
  )
@@ -2007,7 +2001,7 @@ def _get_carbon_input_kwargs(
2007
2001
 
2008
2002
  has_residue_removed_or_burnt = cumulative_nodes_term_match(
2009
2003
  crop_residue_management_nodes,
2010
- target_term_ids=get_residue_removed_or_burnt_terms_with_cache(),
2004
+ target_term_ids=get_residue_removed_or_burnt_terms(),
2011
2005
  cumulative_threshold=MIN_AREA_THRESHOLD
2012
2006
  )
2013
2007
 
@@ -25,22 +25,21 @@ from hestia_earth.models.utils.array_builders import (
25
25
  avg_run_in_columnwise, gen_seed, grouped_avg, repeat_1d_array_as_columns
26
26
  )
27
27
  from hestia_earth.models.utils.blank_node import (
28
- cumulative_nodes_lookup_match, cumulative_nodes_term_match, group_nodes_by_year,
29
- group_nodes_by_year_and_month, GroupNodesByYearMode, node_lookup_match, node_term_match
28
+ cumulative_nodes_lookup_match, cumulative_nodes_term_match, group_nodes_by_year, group_nodes_by_year_and_month,
29
+ GroupNodesByYearMode, node_lookup_match, node_term_match
30
30
  )
31
31
  from hestia_earth.models.utils.cycle import check_cycle_site_ids_identical
32
32
  from hestia_earth.models.utils.descriptive_stats import calc_descriptive_stats
33
33
  from hestia_earth.models.utils.measurement import _new_measurement
34
34
  from hestia_earth.models.utils.property import get_node_property
35
35
  from hestia_earth.models.utils.site import related_cycles
36
+ from hestia_earth.models.utils.term import get_upland_rice_crop_terms, get_upland_rice_land_cover_terms
36
37
 
37
38
  from .organicCarbonPerHa_utils import (
38
39
  CarbonSource, check_consecutive, DEPTH_LOWER, DEPTH_UPPER, check_irrigation,
39
- get_cover_crop_property_terms_with_cache, get_upland_rice_crop_terms_with_cache,
40
- get_upland_rice_land_cover_terms_with_cache, IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE,
41
- IPCC_MANAGEMENT_CATEGORY_TO_TILLAGE_MANAGEMENT_LOOKUP_VALUE, IpccLandUseCategory, IpccManagementCategory,
42
- MIN_AREA_THRESHOLD, MIN_YIELD_THRESHOLD, sample_constant, sample_plus_minus_uncertainty, sample_truncated_normal,
43
- STATS_DEFINITION
40
+ IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE, IPCC_MANAGEMENT_CATEGORY_TO_TILLAGE_MANAGEMENT_LOOKUP_VALUE,
41
+ IpccLandUseCategory, IpccManagementCategory, is_cover_crop, MIN_AREA_THRESHOLD, MIN_YIELD_THRESHOLD,
42
+ sample_constant, sample_plus_minus_uncertainty, sample_truncated_normal, STATS_DEFINITION
44
43
  )
45
44
 
46
45
  _LOOKUPS = {
@@ -1410,10 +1409,7 @@ def _should_run_carbon_source_cover_crop(node: dict) -> bool:
1410
1409
  node_lookup_match(
1411
1410
  node, LOOKUP, IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE[IpccLandUseCategory.ANNUAL_CROPS]
1412
1411
  ),
1413
- any(
1414
- get_node_property(node, term_id, False).get("value", False)
1415
- for term_id in get_cover_crop_property_terms_with_cache()
1416
- )
1412
+ is_cover_crop(node)
1417
1413
  ])
1418
1414
 
1419
1415
 
@@ -1744,7 +1740,7 @@ def _check_is_paddy_rice(cycles: list[dict]) -> bool:
1744
1740
  cycle.get("products", []) + cycle.get("practices", []),
1745
1741
  [TermTermType.CROP, TermTermType.FORAGE, TermTermType.LANDCOVER]
1746
1742
  ),
1747
- target_term_ids=get_upland_rice_crop_terms_with_cache() + get_upland_rice_land_cover_terms_with_cache(),
1743
+ target_term_ids=get_upland_rice_crop_terms() + get_upland_rice_land_cover_terms(),
1748
1744
  cumulative_threshold=MIN_YIELD_THRESHOLD,
1749
1745
  default_node_value=MIN_YIELD_THRESHOLD
1750
1746
  ) for cycle in cycles)
@@ -1,5 +1,4 @@
1
1
  from enum import Enum
2
- from functools import lru_cache
3
2
  from numpy.typing import NDArray
4
3
  from typing import NamedTuple, Optional
5
4
 
@@ -8,11 +7,8 @@ from hestia_earth.schema import MeasurementStatsDefinition, SiteSiteType
8
7
  from hestia_earth.models.utils.array_builders import (
9
8
  repeat_single, plus_minus_uncertainty_to_normal_1d, truncated_normal_1d
10
9
  )
11
- from hestia_earth.models.utils.blank_node import cumulative_nodes_term_match
12
- from hestia_earth.models.utils.term import (
13
- get_cover_crop_property_terms, get_irrigated_terms, get_residue_removed_or_burnt_terms, get_upland_rice_crop_terms,
14
- get_upland_rice_land_cover_terms
15
- )
10
+ from hestia_earth.models.utils.blank_node import cumulative_nodes_term_match, node_term_match
11
+ from hestia_earth.models.utils.term import get_cover_crop_property_terms, get_irrigated_terms
16
12
 
17
13
  STATS_DEFINITION = MeasurementStatsDefinition.SIMULATED.value
18
14
  DEPTH_UPPER = 0
@@ -23,26 +19,6 @@ SUPER_MAJORITY_AREA_THRESHOLD = 100 - MIN_AREA_THRESHOLD
23
19
  MIN_YIELD_THRESHOLD = 1
24
20
 
25
21
 
26
- def get_cover_crop_property_terms_with_cache():
27
- return lru_cache()(get_cover_crop_property_terms)()
28
-
29
-
30
- def get_irrigated_terms_with_cache():
31
- return lru_cache()(get_irrigated_terms)()
32
-
33
-
34
- def get_residue_removed_or_burnt_terms_with_cache():
35
- return lru_cache()(get_residue_removed_or_burnt_terms)()
36
-
37
-
38
- def get_upland_rice_crop_terms_with_cache():
39
- return lru_cache()(get_upland_rice_crop_terms)()
40
-
41
-
42
- def get_upland_rice_land_cover_terms_with_cache():
43
- return lru_cache()(get_upland_rice_land_cover_terms)()
44
-
45
-
46
22
  class IpccSoilCategory(Enum):
47
23
  """
48
24
  Enum representing IPCC Soil Categories.
@@ -106,9 +82,7 @@ IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE = {
106
82
  IpccLandUseCategory.PADDY_RICE_CULTIVATION: "Paddy rice cultivation",
107
83
  IpccLandUseCategory.ANNUAL_CROPS_WET: "Annual crops",
108
84
  IpccLandUseCategory.ANNUAL_CROPS: "Annual crops",
109
- IpccLandUseCategory.SET_ASIDE: [
110
- "Annual crops", "Paddy rice cultivation", "Perennial crops", "Set aside"
111
- ],
85
+ IpccLandUseCategory.SET_ASIDE: "Set aside",
112
86
  IpccLandUseCategory.FOREST: "Forest",
113
87
  IpccLandUseCategory.NATIVE: "Native"
114
88
  }
@@ -236,11 +210,20 @@ def check_irrigation(water_regime_nodes: list[dict]) -> bool:
236
210
  """
237
211
  return cumulative_nodes_term_match(
238
212
  water_regime_nodes,
239
- target_term_ids=get_irrigated_terms_with_cache(),
213
+ target_term_ids=get_irrigated_terms(),
240
214
  cumulative_threshold=MIN_AREA_THRESHOLD
241
215
  )
242
216
 
243
217
 
218
+ def is_cover_crop(node: dict) -> bool:
219
+ """Check if a `landCover` node represents a cover crop."""
220
+ COVER_CROP_TERM_IDS = get_cover_crop_property_terms()
221
+ return any(
222
+ prop.get("value", False) for prop in node.get("properties", [])
223
+ if node_term_match(prop, COVER_CROP_TERM_IDS)
224
+ )
225
+
226
+
244
227
  def sample_truncated_normal(
245
228
  *, iterations: int, value: float, sd: float, min: float, max: float, seed: Optional[int] = None, **_
246
229
  ) -> NDArray:
@@ -17,7 +17,10 @@ REQUIREMENTS = {
17
17
  "@type": "ImpactAssessment",
18
18
  "emissionsResourceUse": [{
19
19
  "@type": "Indicator",
20
- "term.@id": "landTransformationFromCropland20YearAverageDuringCycle",
20
+ "term.@id": [
21
+ "landTransformation100YearAverageDuringCycle",
22
+ "landTransformation100YearAverageInputsProduction"
23
+ ],
21
24
  "value": "> 0"
22
25
  }]
23
26
  }
@@ -30,7 +33,7 @@ RETURNS = {
30
33
  "value": ""
31
34
  }]
32
35
  }
33
- TERM_ID = 'landTransformationFromCropland20YearAverageInputsProduction'
36
+ TERM_ID = 'landTransformation100YearAverageInputsProduction'
34
37
 
35
38
 
36
39
  def run(impact_assessment: dict): return run_inputs_production(impact_assessment, TERM_ID)
@@ -17,7 +17,10 @@ REQUIREMENTS = {
17
17
  "@type": "ImpactAssessment",
18
18
  "emissionsResourceUse": [{
19
19
  "@type": "Indicator",
20
- "term.@id": "landTransformationFromCropland100YearAverageDuringCycle",
20
+ "term.@id": [
21
+ "landTransformation20YearAverageDuringCycle",
22
+ "landTransformation20YearAverageInputsProduction"
23
+ ],
21
24
  "value": "> 0"
22
25
  }]
23
26
  }
@@ -30,7 +33,7 @@ RETURNS = {
30
33
  "value": ""
31
34
  }]
32
35
  }
33
- TERM_ID = 'landTransformationFromCropland100YearAverageInputsProduction'
36
+ TERM_ID = 'landTransformation20YearAverageInputsProduction'
34
37
 
35
38
 
36
39
  def run(impact_assessment: dict): return run_inputs_production(impact_assessment, TERM_ID)
@@ -1,10 +1,11 @@
1
- from hestia_earth.utils.tools import non_empty_list
1
+ from functools import reduce
2
+ from hestia_earth.utils.tools import non_empty_list, flatten, list_sum
2
3
 
3
- from hestia_earth.models.log import debugValues, logRequirements, logShouldRun
4
- from hestia_earth.models.utils import sum_values
4
+ from hestia_earth.models.log import log_as_table, logRequirements, logShouldRun
5
+ from hestia_earth.models.utils import sum_values, _include
5
6
  from hestia_earth.models.utils.indicator import _new_indicator
6
7
  from hestia_earth.models.utils.impact_assessment import get_product, convert_value_from_cycle
7
- from hestia_earth.models.utils.input import sum_input_impacts
8
+ from hestia_earth.models.utils.input import load_impacts
8
9
  from . import MODEL
9
10
 
10
11
 
@@ -14,17 +15,72 @@ def _indicator(term_id: str, value: float):
14
15
  return indicator
15
16
 
16
17
 
18
+ def _run_indicators(product: dict, term_id: str):
19
+ def run(values: list):
20
+ indicator = values[0].get('indicator')
21
+ values_from_cycle = non_empty_list([
22
+ list_sum(value.get('input').get('value')) * value.get('indicator').get('value')
23
+ for value in values
24
+ ])
25
+ value = convert_value_from_cycle(product, sum_values(values_from_cycle), model=MODEL, term_id=term_id)
26
+ return _indicator(term_id, value) | _include(indicator, ['landCover', 'previousLandCover'])
27
+ return run
28
+
29
+
30
+ def _group_indicator(group: dict, value: dict):
31
+ group_key = ';'.join(non_empty_list([
32
+ value.get('indicator').get('landCover', {}).get('@id'),
33
+ value.get('indicator').get('previousLandCover', {}).get('@id')
34
+ ]))
35
+ group[group_key] = group.get(group_key, []) + [value]
36
+ return group
37
+
38
+
17
39
  def _run_inputs_production(impact_assessment: dict, product: dict, term_id: str):
18
40
  cycle = impact_assessment.get('cycle', {})
19
- values_from_cycle = non_empty_list([
20
- sum_input_impacts(cycle.get('inputs', []), term_id),
21
- sum_input_impacts(cycle.get('inputs', []), term_id.replace('InputsProduction', 'DuringCycle'))
41
+
42
+ # group all indicators per `landCover` and `previousLandCover`
43
+ all_indicators = flatten([
44
+ {
45
+ 'indicator': indicator,
46
+ 'input': input
47
+ }
48
+ for input in load_impacts(cycle.get('inputs', []))
49
+ for indicator in (
50
+ input.get('impactAssessment', {}).get('emissionsResourceUse', []) +
51
+ input.get('impactAssessment', {}).get('impacts', [])
52
+ )
53
+ if indicator.get('term', {}).get('@id') in [
54
+ term_id,
55
+ term_id.replace('InputsProduction', 'DuringCycle')
56
+ ]
22
57
  ])
23
- value = convert_value_from_cycle(product, sum_values(values_from_cycle), model=MODEL, term_id=term_id)
24
- debugValues(impact_assessment, model=MODEL, term=term_id,
25
- has_values_from_cycle=len(values_from_cycle) > 0)
26
- logShouldRun(impact_assessment, MODEL, term_id, value is not None)
27
- return [] if value is None else [_indicator(term_id, value)]
58
+ valid_indicators = [
59
+ value
60
+ for value in all_indicators
61
+ if all([
62
+ value.get('indicator').get('value', -1) > 0,
63
+ list_sum(value.get('input').get('value', [-1]), 0) > 0
64
+ ])
65
+ ]
66
+ grouped_indicators = reduce(_group_indicator, valid_indicators, {})
67
+ has_indicators = bool(valid_indicators)
68
+
69
+ logRequirements(impact_assessment, model=MODEL, term=term_id,
70
+ indicators=log_as_table([
71
+ {
72
+ 'indicator-id': value.get('indicator').get('term', {}).get('@id'),
73
+ 'indicator-value': value.get('indicator').get('value'),
74
+ 'input-id': value.get('input').get('term', {}).get('@id'),
75
+ 'input-value': list_sum(value.get('input').get('value')),
76
+ }
77
+ for value in all_indicators
78
+ ]))
79
+
80
+ should_run = all([has_indicators])
81
+ logShouldRun(impact_assessment, MODEL, term_id, should_run)
82
+
83
+ return flatten(map(_run_indicators(product, term_id), grouped_indicators.values()))
28
84
 
29
85
 
30
86
  def _should_run_inputs_production(impact_assessment: dict, term_id: str):
@@ -35,6 +91,7 @@ def _should_run_inputs_production(impact_assessment: dict, term_id: str):
35
91
  product=product_id)
36
92
 
37
93
  should_run = all([product])
94
+ logShouldRun(impact_assessment, MODEL, term_id, should_run)
38
95
  return should_run, product
39
96
 
40
97