hestia-earth-models 0.59.4__py3-none-any.whl → 0.59.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of hestia-earth-models might be problematic. Click here for more details.

Files changed (31) hide show
  1. hestia_earth/models/cycle/liveAnimal.py +3 -0
  2. hestia_earth/models/cycle/milkYield.py +1 -1
  3. hestia_earth/models/cycle/utils.py +1 -1
  4. hestia_earth/models/geospatialDatabase/potentialEvapotranspirationLongTermAnnualMean.py +2 -2
  5. hestia_earth/models/geospatialDatabase/potentialEvapotranspirationMonthly.py +9 -8
  6. hestia_earth/models/geospatialDatabase/precipitationMonthly.py +10 -8
  7. hestia_earth/models/geospatialDatabase/temperatureAnnual.py +2 -5
  8. hestia_earth/models/geospatialDatabase/temperatureLongTermAnnualMean.py +2 -3
  9. hestia_earth/models/geospatialDatabase/temperatureMonthly.py +8 -8
  10. hestia_earth/models/geospatialDatabase/utils.py +6 -1
  11. hestia_earth/models/ipcc2019/organicCarbonPerHa.py +70 -110
  12. hestia_earth/models/linkedImpactAssessment/__init__.py +78 -43
  13. hestia_earth/models/mocking/search-results.json +0 -39
  14. hestia_earth/models/schmidt2007/n2OToAirWasteTreatmentDirect.py +58 -0
  15. hestia_earth/models/schmidt2007/nh3ToAirWasteTreatment.py +58 -0
  16. hestia_earth/models/site/management.py +104 -11
  17. hestia_earth/models/site/soilMeasurement.py +9 -9
  18. hestia_earth/models/utils/blank_node.py +3 -3
  19. hestia_earth/models/utils/term.py +0 -23
  20. hestia_earth/models/version.py +1 -1
  21. {hestia_earth_models-0.59.4.dist-info → hestia_earth_models-0.59.5.dist-info}/METADATA +1 -1
  22. {hestia_earth_models-0.59.4.dist-info → hestia_earth_models-0.59.5.dist-info}/RECORD +31 -27
  23. tests/models/ipcc2019/test_organicCarbonPerHa.py +4 -18
  24. tests/models/schmidt2007/test_n2OToAirWasteTreatmentDirect.py +45 -0
  25. tests/models/schmidt2007/test_nh3ToAirWasteTreatment.py +45 -0
  26. tests/models/site/test_management.py +24 -3
  27. tests/models/site/test_soilMeasurement.py +40 -21
  28. tests/models/utils/test_term.py +1 -8
  29. {hestia_earth_models-0.59.4.dist-info → hestia_earth_models-0.59.5.dist-info}/LICENSE +0 -0
  30. {hestia_earth_models-0.59.4.dist-info → hestia_earth_models-0.59.5.dist-info}/WHEEL +0 -0
  31. {hestia_earth_models-0.59.4.dist-info → hestia_earth_models-0.59.5.dist-info}/top_level.txt +0 -0
@@ -43,6 +43,9 @@ RETURNS = {
43
43
  "value": ""
44
44
  }]
45
45
  }
46
+ LOOKUPS = {
47
+ "animalProduct": "liveAnimalTermId"
48
+ }
46
49
  MODEL_KEY = 'liveAnimal'
47
50
  VALID_SITE_TYPES = [SiteSiteType.ANIMAL_HOUSING.value, SiteSiteType.PERMANENT_PASTURE.value]
48
51
 
@@ -48,7 +48,7 @@ RETURNS = {
48
48
  }]
49
49
  }
50
50
  LOOKUPS = {
51
- "animalProduct": ["liveAnimal", "milkYieldPracticeId"]
51
+ "animalProduct": ["liveAnimalTermId", "milkYieldPracticeId"]
52
52
  }
53
53
 
54
54
  MODEL_KEY = 'milkYield'
@@ -6,7 +6,7 @@ from . import MODEL
6
6
 
7
7
 
8
8
  def _get_liveAnimal_term_id(product: dict, **log_ars):
9
- term_id = get_lookup_value(product.get('term', {}), 'liveAnimal', model=MODEL, **log_ars)
9
+ term_id = get_lookup_value(product.get('term', {}), 'liveAnimalTermId', model=MODEL, **log_ars)
10
10
  return term_id.split(';')[0] if term_id else None
11
11
 
12
12
 
@@ -47,9 +47,9 @@ def _measurement(value: float):
47
47
 
48
48
 
49
49
  def _download(site: dict):
50
- scale = 10
50
+ factor = 0.1
51
51
  value = download(TERM_ID, site, EE_PARAMS)
52
- return value / scale if value else None
52
+ return value * factor if value else None
53
53
 
54
54
 
55
55
  def _run(site: dict):
@@ -10,7 +10,7 @@ from hestia_earth.models.utils import first_day_of_month, last_day_of_month
10
10
  from hestia_earth.models.utils.measurement import _new_measurement
11
11
  from hestia_earth.models.utils.source import get_source
12
12
  from hestia_earth.models.utils.site import related_years
13
- from .utils import KELVIN_0, download, has_geospatial_data, should_download
13
+ from .utils import download, has_geospatial_data, should_download
14
14
  from . import MODEL
15
15
 
16
16
  REQUIREMENTS = {
@@ -48,9 +48,6 @@ def _measurement(site: dict, value: list, dates: list):
48
48
  return measurement | get_source(site, BIBLIO_TITLE)
49
49
 
50
50
 
51
- def _to_celcius(kelvin_value: int): return kelvin_value - KELVIN_0 if kelvin_value else None
52
-
53
-
54
51
  def _download(site: dict, start_date: str, end_date: str):
55
52
  return download(
56
53
  TERM_ID,
@@ -63,6 +60,12 @@ def _download(site: dict, start_date: str, end_date: str):
63
60
  )
64
61
 
65
62
 
63
+ def _value_at(site: dict, start_date: str, end_date: str):
64
+ factor = 0.1
65
+ value = _download(site, start_date, end_date)
66
+ return (value * factor, start_date[0:7]) if value is not None else None
67
+
68
+
66
69
  def _run(site: dict, years: list):
67
70
  # fetch from first year to last
68
71
  years = range(years[0], years[-1] + 1) if len(years) > 1 else years
@@ -73,10 +76,8 @@ def _run(site: dict, years: list):
73
76
  for month in range(1, 13)
74
77
  ] for year in years
75
78
  ])
76
- values = non_empty_list([
77
- (_to_celcius(_download(site, start_date, end_date)), start_date[0:7]) for start_date, end_date in dates
78
- ])
79
- return _measurement(site, [v for v, d in values], [d for v, d in values])
79
+ values = non_empty_list([_value_at(site, start_date, end_date) for start_date, end_date in dates])
80
+ return [_measurement(site, [v for v, d in values], [d for v, d in values])] if values else []
80
81
 
81
82
 
82
83
  def run(site: dict):
@@ -10,7 +10,7 @@ from hestia_earth.models.utils import first_day_of_month, last_day_of_month
10
10
  from hestia_earth.models.utils.measurement import _new_measurement
11
11
  from hestia_earth.models.utils.source import get_source
12
12
  from hestia_earth.models.utils.site import related_years
13
- from .utils import KELVIN_0, download, has_geospatial_data, should_download
13
+ from .utils import download, has_geospatial_data, should_download
14
14
  from . import MODEL
15
15
 
16
16
  REQUIREMENTS = {
@@ -48,9 +48,6 @@ def _measurement(site: dict, value: list, dates: list):
48
48
  return measurement | get_source(site, BIBLIO_TITLE)
49
49
 
50
50
 
51
- def _to_celcius(kelvin_value: int): return kelvin_value - KELVIN_0 if kelvin_value else None
52
-
53
-
54
51
  def _download(site: dict, start_date: str, end_date: str):
55
52
  return download(
56
53
  TERM_ID,
@@ -63,6 +60,13 @@ def _download(site: dict, start_date: str, end_date: str):
63
60
  )
64
61
 
65
62
 
63
+ def _value_at(site: dict, start_date: str, end_date: str):
64
+ # collection is in meters, convert to millimeters
65
+ factor = 1000
66
+ value = _download(site, start_date, end_date)
67
+ return (value * factor, start_date[0:7]) if value is not None else None
68
+
69
+
66
70
  def _run(site: dict, years: list):
67
71
  # fetch from first year to last
68
72
  years = range(years[0], years[-1] + 1) if len(years) > 1 else years
@@ -73,10 +77,8 @@ def _run(site: dict, years: list):
73
77
  for month in range(1, 13)
74
78
  ] for year in years
75
79
  ])
76
- values = non_empty_list([
77
- (_to_celcius(_download(site, start_date, end_date)), start_date[0:7]) for start_date, end_date in dates
78
- ])
79
- return _measurement(site, [v for v, d in values], [d for v, d in values])
80
+ values = non_empty_list([_value_at(site, start_date, end_date) for start_date, end_date in dates])
81
+ return [_measurement(site, [v for v, d in values], [d for v, d in values])] if values else []
80
82
 
81
83
 
82
84
  def run(site: dict):
@@ -9,7 +9,7 @@ from hestia_earth.models.log import logRequirements, logShouldRun
9
9
  from hestia_earth.models.utils.measurement import _new_measurement
10
10
  from hestia_earth.models.utils.source import get_source
11
11
  from hestia_earth.models.utils.site import related_years
12
- from .utils import KELVIN_0, download, has_geospatial_data, should_download
12
+ from .utils import to_celcius, download, has_geospatial_data, should_download
13
13
  from . import MODEL
14
14
 
15
15
  REQUIREMENTS = {
@@ -49,9 +49,6 @@ def _measurement(site: dict, value: float, year: int):
49
49
  return measurement | get_source(site, BIBLIO_TITLE)
50
50
 
51
51
 
52
- def _to_celcius(kelvin_value: int): return kelvin_value - KELVIN_0 if kelvin_value else None
53
-
54
-
55
52
  def _download(site: dict, year: int):
56
53
  return download(
57
54
  TERM_ID,
@@ -64,7 +61,7 @@ def _download(site: dict, year: int):
64
61
 
65
62
 
66
63
  def _run(site: dict, year: int):
67
- value = _to_celcius(_download(site, year))
64
+ value = to_celcius(_download(site, year))
68
65
  return _measurement(site, value, year) if value else None
69
66
 
70
67
 
@@ -3,9 +3,8 @@ from hestia_earth.schema import MeasurementMethodClassification
3
3
  from hestia_earth.models.log import logRequirements, logShouldRun
4
4
  from hestia_earth.models.utils.measurement import _new_measurement
5
5
  from hestia_earth.models.utils.source import get_source
6
- from .utils import download, has_geospatial_data, should_download
6
+ from .utils import to_celcius, download, has_geospatial_data, should_download
7
7
  from . import MODEL
8
- from .temperatureAnnual import _to_celcius
9
8
 
10
9
  REQUIREMENTS = {
11
10
  "Site": {
@@ -50,7 +49,7 @@ def _measurement(site: dict, value: float):
50
49
 
51
50
 
52
51
  def _run(site: dict):
53
- value = _to_celcius(download(TERM_ID, site, EE_PARAMS))
52
+ value = to_celcius(download(TERM_ID, site, EE_PARAMS))
54
53
  return [_measurement(site, value)] if value is not None else []
55
54
 
56
55
 
@@ -10,7 +10,7 @@ from hestia_earth.models.utils import first_day_of_month, last_day_of_month
10
10
  from hestia_earth.models.utils.measurement import _new_measurement
11
11
  from hestia_earth.models.utils.source import get_source
12
12
  from hestia_earth.models.utils.site import related_years
13
- from .utils import KELVIN_0, download, has_geospatial_data, should_download
13
+ from .utils import to_celcius, download, has_geospatial_data, should_download
14
14
  from . import MODEL
15
15
 
16
16
  REQUIREMENTS = {
@@ -48,9 +48,6 @@ def _measurement(site: dict, value: list, dates: list):
48
48
  return measurement | get_source(site, BIBLIO_TITLE)
49
49
 
50
50
 
51
- def _to_celcius(kelvin_value: int): return kelvin_value - KELVIN_0 if kelvin_value else None
52
-
53
-
54
51
  def _download(site: dict, start_date: str, end_date: str):
55
52
  return download(
56
53
  TERM_ID,
@@ -63,6 +60,11 @@ def _download(site: dict, start_date: str, end_date: str):
63
60
  )
64
61
 
65
62
 
63
+ def _value_at(site: dict, start_date: str, end_date: str):
64
+ value = _download(site, start_date, end_date)
65
+ return (to_celcius(value), start_date[0:7]) if value is not None else None
66
+
67
+
66
68
  def _run(site: dict, years: list):
67
69
  # fetch from first year to last
68
70
  years = range(years[0], years[-1] + 1) if len(years) > 1 else years
@@ -73,10 +75,8 @@ def _run(site: dict, years: list):
73
75
  for month in range(1, 13)
74
76
  ] for year in years
75
77
  ])
76
- values = non_empty_list([
77
- (_to_celcius(_download(site, start_date, end_date)), start_date[0:7]) for start_date, end_date in dates
78
- ])
79
- return _measurement(site, [v for v, d in values], [d for v, d in values])
78
+ values = non_empty_list([_value_at(site, start_date, end_date) for start_date, end_date in dates])
79
+ return [_measurement(site, [v for v, d in values], [d for v, d in values])] if values else []
80
80
 
81
81
 
82
82
  def run(site: dict):
@@ -23,6 +23,9 @@ GEOPANDAS_COLLECTION_NAME = {
23
23
  KELVIN_0 = 273.15
24
24
 
25
25
 
26
+ def to_celcius(kelvin_value: int): return kelvin_value - KELVIN_0 if kelvin_value else None
27
+
28
+
26
29
  def use_geopandas(): return os.getenv('HEE_USE_GEOPANDAS', 'false') == 'true'
27
30
 
28
31
 
@@ -139,7 +142,9 @@ def _get_cached_data(term: str, site: dict, data: dict):
139
142
  data.get('end_date')
140
143
  ]))
141
144
  # data can be grouped by year when required
142
- value = cache.get(cache_sub_key) if cache_sub_key and cache is not None else cache
145
+ value = cache.get(cache_sub_key) if (
146
+ isinstance(cache, dict) and cache_sub_key in cache and cache is not None
147
+ ) else cache
143
148
  if value is not None:
144
149
  debugValues(site, model=MODEL, term=term, value_from_cache=value)
145
150
  return value
@@ -46,7 +46,6 @@ from hestia_earth.models.utils.term import (
46
46
  get_cover_crop_property_terms,
47
47
  get_crop_residue_incorporated_or_left_on_field_terms,
48
48
  get_irrigated_terms,
49
- get_long_fallow_land_cover_terms,
50
49
  get_residue_removed_or_burnt_terms,
51
50
  get_upland_rice_crop_terms,
52
51
  get_upland_rice_land_cover_terms
@@ -610,25 +609,26 @@ A dictionary mapping IPCC soil categories to corresponding soil type and USDA so
610
609
  `"IPCC_SOIL_CATEGORY"` column.
611
610
  """
612
611
 
613
- IPCC_LAND_USE_CATEGORY_TO_SITE_TYPE = {
614
- IpccLandUseCategory.GRASSLAND: SiteSiteType.PERMANENT_PASTURE.value,
615
- IpccLandUseCategory.PERENNIAL_CROPS: SiteSiteType.CROPLAND.value,
616
- IpccLandUseCategory.PADDY_RICE_CULTIVATION: SiteSiteType.CROPLAND.value,
617
- IpccLandUseCategory.ANNUAL_CROPS_WET: SiteSiteType.CROPLAND.value,
618
- IpccLandUseCategory.ANNUAL_CROPS: SiteSiteType.CROPLAND.value,
619
- IpccLandUseCategory.SET_ASIDE: SiteSiteType.CROPLAND.value,
620
- IpccLandUseCategory.FOREST: SiteSiteType.FOREST.value,
621
- IpccLandUseCategory.NATIVE: SiteSiteType.OTHER_NATURAL_VEGETATION.value
612
+ SITE_TYPE_TO_IPCC_LAND_USE_CATEGORY = {
613
+ SiteSiteType.PERMANENT_PASTURE.value: IpccLandUseCategory.GRASSLAND,
614
+ SiteSiteType.FOREST.value: IpccLandUseCategory.FOREST,
615
+ SiteSiteType.OTHER_NATURAL_VEGETATION.value: IpccLandUseCategory.NATIVE
622
616
  }
623
617
  """
624
- A dictionary mapping IPCC land use categories to corresponding site types.
618
+ A dictionary mapping site types to corresponding IPCC land use categories.
625
619
  """
626
620
 
627
621
  IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE = {
622
+ IpccLandUseCategory.GRASSLAND: "Grassland",
628
623
  IpccLandUseCategory.PERENNIAL_CROPS: "Perennial crops",
629
624
  IpccLandUseCategory.PADDY_RICE_CULTIVATION: "Paddy rice cultivation",
630
625
  IpccLandUseCategory.ANNUAL_CROPS_WET: "Annual crops",
631
- IpccLandUseCategory.ANNUAL_CROPS: "Annual crops"
626
+ IpccLandUseCategory.ANNUAL_CROPS: "Annual crops",
627
+ IpccLandUseCategory.SET_ASIDE: [
628
+ "Annual crops", "Paddy rice cultivation", "Perennial crops", "Set aside"
629
+ ],
630
+ IpccLandUseCategory.FOREST: "Forest",
631
+ IpccLandUseCategory.NATIVE: "Native"
632
632
  }
633
633
  """
634
634
  A dictionary mapping IPCC land use categories to corresponding land cover lookup values in the
@@ -2525,7 +2525,7 @@ def _has_irrigation(water_regime_nodes: list[dict]) -> bool:
2525
2525
 
2526
2526
  Parameters
2527
2527
  ----------
2528
- water_regime_nodes : List[dict]
2528
+ water_regime_nodes : list[dict]
2529
2529
  List of water regime nodes to be checked.
2530
2530
 
2531
2531
  Returns
@@ -2542,13 +2542,13 @@ def _has_irrigation(water_regime_nodes: list[dict]) -> bool:
2542
2542
 
2543
2543
  def _has_long_fallow(land_cover_nodes: list[dict]) -> bool:
2544
2544
  """
2545
- Check if long fallow terms is present in the land cover nodes.
2545
+ Check if long fallow terms are present in the land cover nodes.
2546
2546
 
2547
2547
  n.b., a super majority of the site area must be under long fallow for it to be classified as set aside.
2548
2548
 
2549
2549
  Parameters
2550
2550
  ----------
2551
- land_cover_nodes : List[dict]
2551
+ land_cover_nodes : list[dict]
2552
2552
  List of land cover nodes to be checked.
2553
2553
 
2554
2554
  Returns
@@ -2556,9 +2556,12 @@ def _has_long_fallow(land_cover_nodes: list[dict]) -> bool:
2556
2556
  bool
2557
2557
  `True` if long fallow is present, `False` otherwise.
2558
2558
  """
2559
- return cumulative_nodes_term_match(
2559
+ LOOKUP = LOOKUPS["landCover"][0]
2560
+ TARGET_LOOKUP_VALUE = "Set aside"
2561
+ return cumulative_nodes_lookup_match(
2560
2562
  land_cover_nodes,
2561
- target_term_ids=get_long_fallow_land_cover_terms(),
2563
+ lookup=LOOKUP,
2564
+ target_lookup_values=TARGET_LOOKUP_VALUE,
2562
2565
  cumulative_threshold=SUPER_MAJORITY_AREA_THRESHOLD
2563
2566
  ) or cumulative_nodes_match(
2564
2567
  lambda node: get_node_property(node, LONG_FALLOW_CROP_TERM_ID, False).get("value", 0),
@@ -2573,7 +2576,7 @@ def _has_upland_rice(land_cover_nodes: list[dict]) -> bool:
2573
2576
 
2574
2577
  Parameters
2575
2578
  ----------
2576
- land_cover_nodes : List[dict]
2579
+ land_cover_nodes : list[dict]
2577
2580
  List of land cover nodes to be checked.
2578
2581
 
2579
2582
  Returns
@@ -2589,63 +2592,39 @@ def _has_upland_rice(land_cover_nodes: list[dict]) -> bool:
2589
2592
 
2590
2593
 
2591
2594
  IPCC_LAND_USE_CATEGORY_TO_VALIDATION_KWARGS = {
2595
+ IpccLandUseCategory.ANNUAL_CROPS_WET: {"has_wetland_soils"},
2592
2596
  IpccLandUseCategory.SET_ASIDE: {"has_long_fallow"},
2593
- IpccLandUseCategory.ANNUAL_CROPS_WET: {"has_wetland_soils"}
2594
2597
  }
2595
2598
  """
2596
- Keyword arguments that need to be checked for specific `IpccLandUseCategory`s.
2599
+ Keyword arguments that need to be validated in addition to the `landCover` lookup match for specific
2600
+ `IpccLandUseCategory`s.
2597
2601
  """
2598
2602
 
2599
-
2600
- def _check_ipcc_land_use_category(*, key: IpccLandUseCategory, site_type: str, **kwargs) -> bool:
2601
- """
2602
- Check if the site type matches the target site type for the given key.
2603
-
2604
- Parameters
2605
- ----------
2606
- key : IpccLandUseCategory
2607
- The IPCC land use category to check.
2608
- site_type : str
2609
- The site type to check.
2610
-
2611
- Keyword Args
2612
- ------------
2613
- has_long_fallow : bool
2614
- Indicates whether long fallow is present on more than 30% of the site.
2615
- has_wetland_soils : bool
2616
- Indicates whether wetland soils are present to more than 30% of the site.
2617
-
2618
- Returns
2619
- -------
2620
- bool
2621
- `True` if the conditions match the specified land use category, `False` otherwise.
2622
-
2623
- """
2624
- target_site_type = IPCC_LAND_USE_CATEGORY_TO_SITE_TYPE.get(key, None)
2625
- validation_kwargs = IPCC_LAND_USE_CATEGORY_TO_VALIDATION_KWARGS.get(key, set())
2626
- valid_kwargs = all(v for k, v in kwargs.items() if k in validation_kwargs)
2627
- return site_type == target_site_type and valid_kwargs
2603
+ IPCC_LAND_USE_CATEGORY_TO_OVERRIDE_KWARGS = {
2604
+ IpccLandUseCategory.PADDY_RICE_CULTIVATION: {"has_irrigated_upland_rice"}
2605
+ }
2606
+ """
2607
+ Keyword arguments that can override the `landCover` lookup match for specific `IpccLandUseCategory`s.
2608
+ """
2628
2609
 
2629
2610
 
2630
- def _check_cropland_land_use_category(
2631
- *, key: IpccLandUseCategory, site_type: str, land_cover_nodes: list[dict], **kwargs
2632
- ) -> bool:
2611
+ def _check_ipcc_land_use_category(*, key: IpccLandUseCategory, land_cover_nodes: list[dict], **kwargs) -> bool:
2633
2612
  """
2634
- Check if the site type and land cover nodes match the target conditions for a cropland IpccLandUseCategory.
2635
-
2636
- This function is special case of `_check_ipcc_land_use_category`.
2613
+ Check if the land cover nodes and keyword args satisfy the requirements for the given key.
2637
2614
 
2638
2615
  Parameters
2639
2616
  ----------
2640
2617
  key : IpccLandUseCategory
2641
2618
  The IPCC land use category to check.
2642
- site_type : str
2643
- The site type to check.
2619
+ land_cover_nodes : list[dict]
2620
+ List of land cover nodes to be checked.
2644
2621
 
2645
2622
  Keyword Args
2646
2623
  ------------
2624
+ has_irrigated_upland_rice : bool
2625
+ Indicates whether irrigated upland rice is present on more than 30% of the site.
2647
2626
  has_long_fallow : bool
2648
- Indicates whether long fallow is present on more than 30% of the site.
2627
+ Indicates whether long fallow is present on more than 70% of the site.
2649
2628
  has_wetland_soils : bool
2650
2629
  Indicates whether wetland soils are present to more than 30% of the site.
2651
2630
 
@@ -2662,48 +2641,23 @@ def _check_cropland_land_use_category(
2662
2641
  target_lookup_values=target_lookup_values,
2663
2642
  cumulative_threshold=MIN_AREA_THRESHOLD
2664
2643
  )
2665
- return _check_ipcc_land_use_category(key=key, site_type=site_type, **kwargs) and valid_lookup
2666
2644
 
2645
+ validation_kwargs = IPCC_LAND_USE_CATEGORY_TO_VALIDATION_KWARGS.get(key, set())
2646
+ valid_kwargs = all(v for k, v in kwargs.items() if k in validation_kwargs)
2667
2647
 
2668
- def _check_paddy_rice_cultivation_land_use_category(
2669
- *, key: IpccLandUseCategory, site_type: str, has_irrigated_upland_rice: bool, **kwargs
2670
- ) -> bool:
2671
- """
2672
- Check if the site type and land cover nodes match the target conditions for a cropland IpccLandUseCategory.
2648
+ override_kwargs = IPCC_LAND_USE_CATEGORY_TO_OVERRIDE_KWARGS.get(key, set())
2649
+ valid_override = any(v for k, v in kwargs.items() if k in override_kwargs)
2673
2650
 
2674
- This function is special case of `_check_cropland_land_use_category`.
2675
-
2676
- Parameters
2677
- ----------
2678
- key : IpccLandUseCategory
2679
- The IPCC land use category to check.
2680
- site_type : str
2681
- The site type to check.
2682
-
2683
- Keyword Args
2684
- ------------
2685
- has_irrigated_upland_rice : bool
2686
- Indicates whether irrigated upland rice is present on more than 30% of the site.
2687
- has_long_fallow : bool
2688
- Indicates whether long fallow is present on more than 30% of the site.
2689
- has_wetland_soils : bool
2690
- Indicates whether wetland soils are present to more than 30% of the site.
2691
-
2692
- Returns
2693
- -------
2694
- bool
2695
- `True` if the conditions match the specified land use category, `False` otherwise.
2696
- """
2697
- return _check_cropland_land_use_category(key=key, site_type=site_type, **kwargs) or has_irrigated_upland_rice
2651
+ return (valid_lookup and valid_kwargs) or valid_override
2698
2652
 
2699
2653
 
2700
2654
  LAND_USE_CATEGORY_DECISION_TREE = {
2701
2655
  IpccLandUseCategory.GRASSLAND: _check_ipcc_land_use_category,
2702
2656
  IpccLandUseCategory.SET_ASIDE: _check_ipcc_land_use_category,
2703
- IpccLandUseCategory.PERENNIAL_CROPS: _check_cropland_land_use_category,
2704
- IpccLandUseCategory.PADDY_RICE_CULTIVATION: _check_paddy_rice_cultivation_land_use_category,
2705
- IpccLandUseCategory.ANNUAL_CROPS_WET: _check_cropland_land_use_category,
2706
- IpccLandUseCategory.ANNUAL_CROPS: _check_cropland_land_use_category,
2657
+ IpccLandUseCategory.PERENNIAL_CROPS: _check_ipcc_land_use_category,
2658
+ IpccLandUseCategory.PADDY_RICE_CULTIVATION: _check_ipcc_land_use_category,
2659
+ IpccLandUseCategory.ANNUAL_CROPS_WET: _check_ipcc_land_use_category,
2660
+ IpccLandUseCategory.ANNUAL_CROPS: _check_ipcc_land_use_category,
2707
2661
  IpccLandUseCategory.FOREST: _check_ipcc_land_use_category,
2708
2662
  IpccLandUseCategory.NATIVE: _check_ipcc_land_use_category,
2709
2663
  IpccLandUseCategory.OTHER: _check_ipcc_land_use_category
@@ -2712,21 +2666,19 @@ LAND_USE_CATEGORY_DECISION_TREE = {
2712
2666
  A decision tree mapping IPCC soil categories to corresponding check functions.
2713
2667
 
2714
2668
  Key: IpccLandUseCategory
2715
- Value: Corresponding function for checking the match of the given land use category based on site type
2716
- and land cover nodes.
2669
+ Value: Corresponding function for checking the match of the given land use category based on land cover nodes
2670
+ and additional kwargs.
2717
2671
  """
2718
2672
 
2719
2673
 
2720
2674
  def _assign_ipcc_land_use_category(
2721
- site_type: str, management_nodes: list[dict], ipcc_soil_category: IpccSoilCategory
2675
+ management_nodes: list[dict], ipcc_soil_category: IpccSoilCategory,
2722
2676
  ) -> IpccLandUseCategory:
2723
2677
  """
2724
- Assigns IPCC land use category based on site type, management nodes, and soil category.
2678
+ Assigns IPCC land use category based on management nodes and soil category.
2725
2679
 
2726
2680
  Parameters
2727
2681
  ----------
2728
- site_type : str
2729
- The type of the site.
2730
2682
  management_nodes : list[dict]
2731
2683
  List of management nodes.
2732
2684
  ipcc_soil_category : IpccSoilCategory
@@ -2749,14 +2701,13 @@ def _assign_ipcc_land_use_category(
2749
2701
  has_long_fallow = _has_long_fallow(land_cover_nodes)
2750
2702
  has_wetland_soils = ipcc_soil_category is IpccSoilCategory.WETLAND_SOILS
2751
2703
 
2752
- should_run = bool(site_type)
2704
+ should_run = bool(land_cover_nodes)
2753
2705
 
2754
2706
  return next(
2755
2707
  (
2756
2708
  key for key in DECISION_TREE
2757
2709
  if DECISION_TREE[key](
2758
2710
  key=key,
2759
- site_type=site_type,
2760
2711
  land_cover_nodes=land_cover_nodes,
2761
2712
  has_long_fallow=has_long_fallow,
2762
2713
  has_irrigated_upland_rice=has_irrigated_upland_rice,
@@ -2780,7 +2731,7 @@ def _check_grassland_ipcc_management_category(
2780
2731
  ----------
2781
2732
  key : IpccManagementCategory
2782
2733
  The IPCC management category to check.
2783
- land_cover_nodes : List[dict]
2734
+ land_cover_nodes : list[dict]
2784
2735
  List of land cover nodes to be checked.
2785
2736
 
2786
2737
  Returns
@@ -2806,7 +2757,7 @@ def _check_tillage_ipcc_management_category(
2806
2757
  ----------
2807
2758
  key : IpccManagementCategory
2808
2759
  The IPCC management category to check.
2809
- tillage_nodes : List[dict]
2760
+ tillage_nodes : list[dict]
2810
2761
  List of tillage nodes to be checked.
2811
2762
 
2812
2763
  Returns
@@ -3783,11 +3734,15 @@ def _should_run_inventory_year_tier_2(group: dict) -> bool:
3783
3734
  def _get_grouped_climate_measurements(grouped_measurements: dict) -> dict:
3784
3735
  return {
3785
3736
  year: {
3786
- _InventoryKey.TEMP_MONTHLY: find_term_match(measurements, TEMPERATURE_MONTHLY_TERM_ID, {}).get("value", []),
3787
- _InventoryKey.PRECIP_MONTHLY: (
3737
+ _InventoryKey.TEMP_MONTHLY: non_empty_list(
3738
+ find_term_match(measurements, TEMPERATURE_MONTHLY_TERM_ID, {}).get("value", [])
3739
+ ),
3740
+ _InventoryKey.PRECIP_MONTHLY: non_empty_list(
3788
3741
  find_term_match(measurements, PRECIPITATION_MONTHLY_TERM_ID, {}).get("value", [])
3789
3742
  ),
3790
- _InventoryKey.PET_MONTHLY: find_term_match(measurements, PET_MONTHLY_TERM_ID, {}).get("value", [])
3743
+ _InventoryKey.PET_MONTHLY: non_empty_list(
3744
+ find_term_match(measurements, PET_MONTHLY_TERM_ID, {}).get("value", [])
3745
+ )
3791
3746
  } for year, measurements in grouped_measurements.items()
3792
3747
  }
3793
3748
 
@@ -3917,15 +3872,19 @@ def _build_inventory_tier_1(
3917
3872
  eco_climate_zone = _get_eco_climate_zone(measurement_nodes)
3918
3873
  ipcc_soil_category = _assign_ipcc_soil_category(measurement_nodes)
3919
3874
  soc_ref = _retrieve_soc_ref(eco_climate_zone, ipcc_soil_category)
3875
+ grouped_management = group_nodes_by_year(management_nodes)
3876
+
3877
+ # If no `landCover` nodes in `site.management` use `site.siteType` to assign static `IpccLandUseCategory`
3878
+ run_with_site_type = len(filter_list_term_type(management_nodes, [TermTermType.LANDCOVER])) == 0
3879
+ site_type_ipcc_land_use_category = SITE_TYPE_TO_IPCC_LAND_USE_CATEGORY.get(site_type, IpccLandUseCategory.OTHER)
3920
3880
 
3921
3881
  grouped_management = group_nodes_by_year(management_nodes)
3922
3882
 
3923
3883
  grouped_land_use_categories = {
3924
3884
  year: {
3925
- _InventoryKey.LU_CATEGORY: _assign_ipcc_land_use_category(
3926
- site_type,
3927
- nodes,
3928
- ipcc_soil_category
3885
+ _InventoryKey.LU_CATEGORY: (
3886
+ site_type_ipcc_land_use_category if run_with_site_type
3887
+ else _assign_ipcc_land_use_category(nodes, ipcc_soil_category)
3929
3888
  )
3930
3889
  } for year, nodes in grouped_management.items()
3931
3890
  }
@@ -3963,7 +3922,8 @@ def _build_inventory_tier_1(
3963
3922
  kwargs = {
3964
3923
  "eco_climate_zone": eco_climate_zone,
3965
3924
  "ipcc_soil_category": ipcc_soil_category,
3966
- "soc_ref": soc_ref,
3925
+ "run_with_site_type": run_with_site_type,
3926
+ "soc_ref": soc_ref
3967
3927
  }
3968
3928
 
3969
3929
  return inventory, kwargs