hestia-earth-models 0.69.0__py3-none-any.whl → 0.70.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.
Files changed (75) hide show
  1. hestia_earth/models/cache_sites.py +3 -2
  2. hestia_earth/models/cml2001Baseline/abioticResourceDepletionFossilFuels.py +2 -1
  3. hestia_earth/models/cml2001Baseline/abioticResourceDepletionMineralsAndMetals.py +3 -2
  4. hestia_earth/models/config/Cycle.json +35 -1
  5. hestia_earth/models/{koble2014 → cycle}/aboveGroundCropResidue.py +1 -3
  6. hestia_earth/models/cycle/aboveGroundCropResidueTotal.py +1 -1
  7. hestia_earth/models/cycle/animal/input/hestiaAggregatedData.py +1 -1
  8. hestia_earth/models/cycle/animal/input/properties.py +1 -1
  9. hestia_earth/models/cycle/cycleDuration.py +2 -2
  10. hestia_earth/models/cycle/energyContentLowerHeatingValue.py +1 -1
  11. hestia_earth/models/cycle/input/hestiaAggregatedData.py +12 -14
  12. hestia_earth/models/cycle/input/properties.py +1 -1
  13. hestia_earth/models/cycle/siteDuration.py +3 -3
  14. hestia_earth/models/frischknechtEtAl2000/ionisingRadiationKbqU235Eq.py +1 -1
  15. hestia_earth/models/geospatialDatabase/croppingIntensity.py +4 -4
  16. hestia_earth/models/geospatialDatabase/longFallowRatio.py +4 -4
  17. hestia_earth/models/geospatialDatabase/region.py +3 -2
  18. hestia_earth/models/geospatialDatabase/utils.py +6 -5
  19. hestia_earth/models/haversineFormula/transport/distance.py +5 -4
  20. hestia_earth/models/hestia/landCover.py +1 -5
  21. hestia_earth/models/hestia/landTransformation100YearAverageDuringCycle.py +2 -1
  22. hestia_earth/models/hestia/landTransformation20YearAverageDuringCycle.py +2 -1
  23. hestia_earth/models/hestia/seed_emissions.py +1 -1
  24. hestia_earth/models/impact_assessment/emissions.py +1 -1
  25. hestia_earth/models/impact_assessment/product/economicValueShare.py +1 -1
  26. hestia_earth/models/impact_assessment/product/value.py +1 -1
  27. hestia_earth/models/ipcc2019/animal/fatContent.py +2 -2
  28. hestia_earth/models/ipcc2019/animal/milkYieldPerAnimal.py +2 -2
  29. hestia_earth/models/ipcc2019/animal/trueProteinContent.py +2 -2
  30. hestia_earth/models/ipcc2019/ch4ToAirEntericFermentation.py +7 -2
  31. hestia_earth/models/ipcc2019/co2ToAirAboveGroundBiomassStockChange.py +1 -0
  32. hestia_earth/models/ipcc2019/co2ToAirBelowGroundBiomassStockChange.py +2 -1
  33. hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChange.py +2 -1
  34. hestia_earth/models/ipcc2019/nonCo2EmissionsToAirNaturalVegetationBurning.py +1 -0
  35. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1.py +8 -8
  36. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2.py +40 -12
  37. hestia_earth/models/koble2014/cropResidueManagement.py +1 -1
  38. hestia_earth/models/koble2014/residueBurnt.py +1 -1
  39. hestia_earth/models/koble2014/residueRemoved.py +1 -1
  40. hestia_earth/models/koble2014/utils.py +3 -3
  41. hestia_earth/models/mocking/search-results.json +1179 -1137
  42. hestia_earth/models/pooreNemecek2018/excretaKgN.py +1 -1
  43. hestia_earth/models/pooreNemecek2018/freshwaterWithdrawalsDuringCycle.py +1 -1
  44. hestia_earth/models/pooreNemecek2018/utils.py +6 -3
  45. hestia_earth/models/schmidt2007/ch4ToAirWasteTreatment.py +1 -3
  46. hestia_earth/models/schmidt2007/h2SToAirWasteTreatment.py +1 -3
  47. hestia_earth/models/schmidt2007/n2OToAirWasteTreatmentDirect.py +1 -3
  48. hestia_earth/models/schmidt2007/nh3ToAirWasteTreatment.py +1 -3
  49. hestia_earth/models/site/management.py +5 -3
  50. hestia_earth/models/site/pre_checks/country.py +4 -2
  51. hestia_earth/models/transformation/input/excreta.py +1 -1
  52. hestia_earth/models/utils/aggregated.py +12 -15
  53. hestia_earth/models/utils/blank_node.py +15 -1
  54. hestia_earth/models/utils/product.py +1 -1
  55. hestia_earth/models/utils/source.py +2 -1
  56. hestia_earth/models/utils/term.py +26 -1
  57. hestia_earth/models/version.py +1 -1
  58. {hestia_earth_models-0.69.0.dist-info → hestia_earth_models-0.70.0.dist-info}/METADATA +1 -1
  59. {hestia_earth_models-0.69.0.dist-info → hestia_earth_models-0.70.0.dist-info}/RECORD +75 -75
  60. tests/models/cycle/input/test_hestiaAggregatedData.py +18 -16
  61. tests/models/{koble2014 → cycle}/test_aboveGroundCropResidue.py +3 -3
  62. tests/models/geospatialDatabase/test_region.py +1 -1
  63. tests/models/geospatialDatabase/test_utils.py +1 -1
  64. tests/models/haversineFormula/transport/test_distance.py +2 -2
  65. tests/models/ipcc2019/test_ch4ToAirEntericFermentation.py +11 -0
  66. tests/models/ipcc2019/test_nonCo2EmissionsToAirNaturalVegetationBurning.py +19 -6
  67. tests/models/ipcc2019/test_organicCarbonPerHa.py +4 -2
  68. tests/models/pooreNemecek2018/test_landOccupationDuringCycle.py +3 -0
  69. tests/models/site/pre_checks/test_country.py +4 -3
  70. tests/models/test_ecoinventV3.py +2 -2
  71. tests/models/utils/test_source.py +15 -5
  72. tests/orchestrator/test_models.py +1 -0
  73. {hestia_earth_models-0.69.0.dist-info → hestia_earth_models-0.70.0.dist-info}/LICENSE +0 -0
  74. {hestia_earth_models-0.69.0.dist-info → hestia_earth_models-0.70.0.dist-info}/WHEEL +0 -0
  75. {hestia_earth_models-0.69.0.dist-info → hestia_earth_models-0.70.0.dist-info}/top_level.txt +0 -0
@@ -24,6 +24,7 @@ from .biomass_utils import BiomassCategory, get_valid_management_nodes, summaris
24
24
  REQUIREMENTS = {
25
25
  "Cycle": {
26
26
  "site": {
27
+ "@type": "Site",
27
28
  "management": [
28
29
  {
29
30
  "@type": "Management",
@@ -31,7 +31,7 @@ from .organicCarbonPerHa_utils import (
31
31
  )
32
32
  from . import MODEL
33
33
 
34
- _LOOKUPS = {
34
+ LOOKUPS = {
35
35
  "crop": "IPCC_LAND_USE_CATEGORY",
36
36
  "landCover": [
37
37
  "IPCC_LAND_USE_CATEGORY",
@@ -992,8 +992,8 @@ def _check_soil_category(
992
992
  bool
993
993
  `True` if the soil category matches, `False` otherwise.
994
994
  """
995
- SOIL_TYPE_LOOKUP = _LOOKUPS["soilType"]
996
- USDA_SOIL_TYPE_LOOKUP = _LOOKUPS["usdaSoilType"]
995
+ SOIL_TYPE_LOOKUP = LOOKUPS["soilType"]
996
+ USDA_SOIL_TYPE_LOOKUP = LOOKUPS["usdaSoilType"]
997
997
 
998
998
  target_lookup_values = IPCC_SOIL_CATEGORY_TO_SOIL_TYPE_LOOKUP_VALUE.get(key, None)
999
999
 
@@ -1183,7 +1183,7 @@ def _check_ipcc_land_use_category(*, key: IpccLandUseCategory, land_cover_nodes:
1183
1183
  bool
1184
1184
  `True` if the conditions match the specified land use category, `False` otherwise.
1185
1185
  """
1186
- LOOKUP = _LOOKUPS["landCover"][0]
1186
+ LOOKUP = LOOKUPS["landCover"][0]
1187
1187
  target_lookup_values = IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE.get(key, None)
1188
1188
  valid_lookup = cumulative_nodes_lookup_match(
1189
1189
  land_cover_nodes,
@@ -1340,7 +1340,7 @@ def _check_tillage_ipcc_management_category(
1340
1340
  bool
1341
1341
  `True` if the conditions match the specified management category, `False` otherwise.
1342
1342
  """
1343
- LOOKUP = _LOOKUPS["tillage"]
1343
+ LOOKUP = LOOKUPS["tillage"]
1344
1344
  target_lookup_values = IPCC_MANAGEMENT_CATEGORY_TO_TILLAGE_MANAGEMENT_LOOKUP_VALUE.get(key, None)
1345
1345
  return cumulative_nodes_lookup_match(
1346
1346
  tillage_nodes,
@@ -1798,9 +1798,9 @@ def _get_carbon_input_kwargs(
1798
1798
  The carbon input keyword arguments.
1799
1799
  """
1800
1800
 
1801
- PRACTICE_INCREASING_C_INPUT_LOOKUP = _LOOKUPS["landUseManagement"]
1802
- LOW_RESIDUE_PRODUCING_CROP_LOOKUP = _LOOKUPS["landCover"][1]
1803
- N_FIXING_CROP_LOOKUP = _LOOKUPS["landCover"][2]
1801
+ PRACTICE_INCREASING_C_INPUT_LOOKUP = LOOKUPS["landUseManagement"]
1802
+ LOW_RESIDUE_PRODUCING_CROP_LOOKUP = LOOKUPS["landCover"][1]
1803
+ N_FIXING_CROP_LOOKUP = LOOKUPS["landCover"][2]
1804
1804
 
1805
1805
  # To prevent double counting already explicitly checked practices.
1806
1806
  EXCLUDED_PRACTICE_TERM_IDS = {
@@ -33,7 +33,7 @@ from .organicCarbonPerHa_utils import (
33
33
  )
34
34
  from . import MODEL
35
35
 
36
- _LOOKUPS = {
36
+ LOOKUPS = {
37
37
  "crop": "IPCC_LAND_USE_CATEGORY",
38
38
  "landCover": "IPCC_LAND_USE_CATEGORY",
39
39
  "tillage": "IPCC_TILLAGE_MANAGEMENT_CATEGORY"
@@ -1173,7 +1173,7 @@ def _compile_inventory(
1173
1173
  TODO: implement long-term average climate data and annual climate data as back ups for monthly data
1174
1174
  TODO: implement randomisation for `irrigationMonthly` if `startDate` and `endDate` are not provided
1175
1175
  """
1176
- grouped_cycles = group_nodes_by_year(cycles)
1176
+ grouped_cycles = group_nodes_by_year(cycles, include_spillovers=True)
1177
1177
  grouped_measurements = group_nodes_by_year(measurement_nodes, mode=GroupNodesByYearMode.DATES)
1178
1178
 
1179
1179
  grouped_climate_data = _get_grouped_climate_measurements(grouped_measurements)
@@ -1322,9 +1322,23 @@ def _get_carbon_sources(cycle: dict) -> list[CarbonSource]:
1322
1322
  A formatted list of `CarbonSource`s.
1323
1323
  """
1324
1324
  inputs_and_products = cycle.get("inputs", []) + cycle.get("products", [])
1325
+
1326
+ group_fac = cycle.get('fraction_of_group_duration')
1327
+ node_fac = cycle.get('fraction_of_node_duration')
1328
+
1329
+ scaling_fac = group_fac if round(group_fac * 100) >= round(node_fac * 100) else 1
1330
+
1331
+ kwargs = {
1332
+ "cycle": cycle,
1333
+ "scaling_factor": scaling_fac
1334
+ }
1335
+
1325
1336
  return non_empty_list([
1326
1337
  next(
1327
- (_func(node, cycle) for validator, _func in _CARBON_SOURCE_DECISION_TREE.items() if validator(node)),
1338
+ (
1339
+ _func(node, **kwargs) for validator, _func in _CARBON_SOURCE_DECISION_TREE.items()
1340
+ if validator(node)
1341
+ ),
1328
1342
  None
1329
1343
  ) for node in inputs_and_products
1330
1344
  ])
@@ -1348,7 +1362,9 @@ def _should_run_carbon_source_ag_residue(node: dict) -> bool:
1348
1362
  return node.get("term", {}).get("@id") == _ABOVE_GROUND_CROP_RESIDUE_TOTAL_TERM_ID
1349
1363
 
1350
1364
 
1351
- def _calc_carbon_source_ag_crop_residue(node: dict, cycle: dict) -> Union[CarbonSource, None]:
1365
+ def _calc_carbon_source_ag_crop_residue(
1366
+ node: dict, *, cycle: dict, scaling_factor: float, **_
1367
+ ) -> Union[CarbonSource, None]:
1352
1368
  """
1353
1369
  Extract and format the carbon source data for above-ground crop residues.
1354
1370
 
@@ -1360,6 +1376,11 @@ def _calc_carbon_source_ag_crop_residue(node: dict, cycle: dict) -> Union[Carbon
1360
1376
  ----------
1361
1377
  node : dict
1362
1378
  A HESTIA [Product](https://www.hestia.earth/schema/Product) node with `term.termType` == `landCover`.
1379
+ cycle : dict
1380
+ The HESTIA [Cycle](https://www.hestia.earth/schema/Cycle) that produces the crop residue.
1381
+ scaling_factor : float
1382
+ The scaling factor for the mass of carbon input, calculated by estimating how much the cycle overlaps
1383
+ with the current inventory year.
1363
1384
 
1364
1385
  Returns
1365
1386
  -------
@@ -1371,7 +1392,7 @@ def _calc_carbon_source_ag_crop_residue(node: dict, cycle: dict) -> Union[Carbon
1371
1392
  get_node_value(practice) for practice in cycle.get("practices", [])
1372
1393
  if node_term_match(practice, _CROP_RESIDUE_MANAGEMENT_TERM_IDS)
1373
1394
  ])
1374
- mass = value * max(residue_left_on_field, _MIN_RESIDUE_LEFT_ON_FIELD) / 100
1395
+ mass = value * max(residue_left_on_field, _MIN_RESIDUE_LEFT_ON_FIELD) * scaling_factor / 100
1375
1396
 
1376
1397
  carbon_content, nitrogen_content, lignin_content, dry_matter = _retrieve_carbon_source_properties(node)
1377
1398
 
@@ -1400,7 +1421,7 @@ def _should_run_carbon_source_cover_crop(node: dict) -> bool:
1400
1421
  bool
1401
1422
  Whether the node satisfies the critera.
1402
1423
  """
1403
- LOOKUP = _LOOKUPS["landCover"]
1424
+ LOOKUP = LOOKUPS["landCover"]
1404
1425
  TARGET_LOOKUP_VALUES = IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE[IpccLandUseCategory.ANNUAL_CROPS]
1405
1426
 
1406
1427
  return (
@@ -1410,7 +1431,7 @@ def _should_run_carbon_source_cover_crop(node: dict) -> bool:
1410
1431
  )
1411
1432
 
1412
1433
 
1413
- def _calc_carbon_source_cover_crop(node: dict, *_) -> Union[CarbonSource, None]:
1434
+ def _calc_carbon_source_cover_crop(node: dict, *, scaling_factor: float, **_) -> Union[CarbonSource, None]:
1414
1435
  """
1415
1436
  Extract and format the carbon source data for an annual cover crop.
1416
1437
 
@@ -1420,6 +1441,9 @@ def _calc_carbon_source_cover_crop(node: dict, *_) -> Union[CarbonSource, None]:
1420
1441
  ----------
1421
1442
  node : dict
1422
1443
  A HESTIA [Product](https://www.hestia.earth/schema/Product) node with `term.termType` == `landCover`.
1444
+ scaling_factor : float
1445
+ The scaling factor for the mass of carbon input, calculated by estimating how much the cycle overlaps
1446
+ with the current inventory year.
1423
1447
 
1424
1448
  Returns
1425
1449
  -------
@@ -1428,7 +1452,7 @@ def _calc_carbon_source_cover_crop(node: dict, *_) -> Union[CarbonSource, None]:
1428
1452
  """
1429
1453
  value = get_node_value(node)
1430
1454
  carbon_source = CarbonSource(
1431
- _DEFAULT_COVER_CROP_BIOMASS * value / 100,
1455
+ _DEFAULT_COVER_CROP_BIOMASS * value * scaling_factor / 100,
1432
1456
  _Parameter.DEFAULT_CARBON_CONTENT.value.get("value"),
1433
1457
  _Parameter.DEFAULT_NITROGEN_CONTENT.value.get("value"),
1434
1458
  _Parameter.DEFAULT_NITROGEN_CONTENT.value.get("value")
@@ -1457,7 +1481,7 @@ def _should_run_carbon_source(node: dict) -> bool:
1457
1481
  ])
1458
1482
 
1459
1483
 
1460
- def _calc_carbon_source(node: dict, *_) -> Union[CarbonSource, None]:
1484
+ def _calc_carbon_source(node: dict, *, scaling_factor: float, **_) -> Union[CarbonSource, None]:
1461
1485
  """
1462
1486
  Extract and format the carbon source data for an input or product.
1463
1487
 
@@ -1466,13 +1490,17 @@ def _calc_carbon_source(node: dict, *_) -> Union[CarbonSource, None]:
1466
1490
  node : dict
1467
1491
  A HESTIA [Input](https://www.hestia.earth/schema/Input) or [Product](https://www.hestia.earth/schema/Product)
1468
1492
  node.
1493
+ scaling_factor : float
1494
+ The scaling factor for the mass of carbon input, calculated by estimating how much the cycle overlaps
1495
+ with the current inventory year.
1496
+
1469
1497
 
1470
1498
  Returns
1471
1499
  -------
1472
1500
  CarbonSource | None
1473
1501
  The carbon source data of the cover crop, or `None` if carbon source data incomplete.
1474
1502
  """
1475
- mass = get_node_value(node)
1503
+ mass = get_node_value(node) * scaling_factor
1476
1504
  carbon_content, nitrogen_content, lignin_content, dry_matter = _retrieve_carbon_source_properties(node)
1477
1505
 
1478
1506
  carbon_source = CarbonSource(
@@ -1673,7 +1701,7 @@ def _check_cycle_tillage_management_category(
1673
1701
  bool
1674
1702
  Whether or not the cycle meets the requirements for the category.
1675
1703
  """
1676
- LOOKUP = _LOOKUPS["tillage"]
1704
+ LOOKUP = LOOKUPS["tillage"]
1677
1705
  target_lookup_values = IPCC_MANAGEMENT_CATEGORY_TO_TILLAGE_MANAGEMENT_LOOKUP_VALUE.get(key, None)
1678
1706
 
1679
1707
  practices = cycle.get("practices", [])
@@ -1720,7 +1748,7 @@ def _get_grouped_is_paddy_rice(grouped_cycles: dict) -> dict:
1720
1748
 
1721
1749
 
1722
1750
  def _check_is_paddy_rice(cycles: list[dict]) -> bool:
1723
- LOOKUP = _LOOKUPS["crop"]
1751
+ LOOKUP = LOOKUPS["crop"]
1724
1752
  TARGET_LOOKUP_VALUES = IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE.get(
1725
1753
  IpccLandUseCategory.PADDY_RICE_CULTIVATION, None
1726
1754
  )
@@ -39,7 +39,7 @@ def _run_practice(cycle: dict, ratio: float, practice: dict):
39
39
  rescale_ratio=ratio,
40
40
  value_before_rescale=value)
41
41
  logShouldRun(cycle, MODEL, term.get('@id'), True)
42
- return _practice(term, round(value * ratio, 2))
42
+ return _practice(term, round(value * ratio, 7))
43
43
 
44
44
 
45
45
  def _run(cycle: dict):
@@ -45,7 +45,7 @@ def _get_default_percent(cycle: dict, term: dict, country_id: str):
45
45
  def _run(cycle: dict, remaining_value: float, primary_product: dict, country_id: str):
46
46
  term = primary_product.get('term', {})
47
47
  value = _get_default_percent(cycle, term, country_id)
48
- return [] if value is None else [_practice(TERM_ID, min(round(value * 100, 2), remaining_value))]
48
+ return [] if value is None else [_practice(TERM_ID, min(round(value * 100, 7), remaining_value))]
49
49
 
50
50
 
51
51
  def run(cycle: dict):
@@ -43,7 +43,7 @@ def _get_default_percent(cycle: dict, term: dict, country_id: str):
43
43
  def _run(cycle: dict, remaining_value: float, primary_product: dict, country_id: str):
44
44
  term = primary_product.get('term', {})
45
45
  value = _get_default_percent(cycle, term, country_id)
46
- return [] if value is None else [_practice(TERM_ID, min(round(value * 100, 2), remaining_value))]
46
+ return [] if value is None else [_practice(TERM_ID, min(round(value * 100, 7), remaining_value))]
47
47
 
48
48
 
49
49
  def run(cycle: dict):
@@ -43,7 +43,7 @@ def _should_run(term_id: str, cycle: dict, require_country: bool = False):
43
43
  not is_from_model(p)
44
44
  ])
45
45
  ]
46
- has_provided_cropResidue_products = len(provided_cropResidue_products) > 0
46
+ no_provided_cropResidue_products = len(provided_cropResidue_products) == 0
47
47
 
48
48
  country_id = cycle.get('site', {}).get('country', {}).get('@id')
49
49
 
@@ -53,13 +53,13 @@ def _should_run(term_id: str, cycle: dict, require_country: bool = False):
53
53
  has_remaining_value=has_remaining_value,
54
54
  crop_residue_values=residue_values,
55
55
  country_id=country_id,
56
- has_provided_cropResidue_products=has_provided_cropResidue_products,
56
+ no_provided_cropResidue_products=no_provided_cropResidue_products,
57
57
  provided_cropResidue_product_ids=log_blank_nodes_id(provided_cropResidue_products))
58
58
 
59
59
  should_run = all([
60
60
  has_primary_product, crop_residue_incomplete, has_remaining_value,
61
61
  not require_country or country_id,
62
- not has_provided_cropResidue_products
62
+ no_provided_cropResidue_products
63
63
  ])
64
64
  logShouldRun(cycle, MODEL, term_id, should_run)
65
65
  return should_run, remaining_value, primary_product, country_id