hestia-earth-models 0.62.1__py3-none-any.whl → 0.62.3__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 (56) hide show
  1. hestia_earth/models/agribalyse2016/fuelElectricity.py +1 -1
  2. hestia_earth/models/cycle/animal/input/hestiaAggregatedData.py +1 -1
  3. hestia_earth/models/cycle/coldCarcassWeightPerHead.py +4 -2
  4. hestia_earth/models/cycle/coldDressedCarcassWeightPerHead.py +2 -2
  5. hestia_earth/models/cycle/concentrateFeed.py +3 -3
  6. hestia_earth/models/cycle/cycleDuration.py +7 -2
  7. hestia_earth/models/cycle/feedConversionRatio/feedConversionRatioNitrogen.py +2 -1
  8. hestia_earth/models/cycle/input/hestiaAggregatedData.py +1 -1
  9. hestia_earth/models/cycle/product/price.py +5 -1
  10. hestia_earth/models/cycle/product/revenue.py +6 -7
  11. hestia_earth/models/cycle/readyToCookWeightPerHead.py +2 -2
  12. hestia_earth/models/ecoinventV3/__init__.py +25 -52
  13. hestia_earth/models/ecoinventV3/utils.py +40 -0
  14. hestia_earth/models/ecoinventV3AndEmberClimate/__init__.py +92 -91
  15. hestia_earth/models/ecoinventV3AndEmberClimate/utils.py +15 -105
  16. hestia_earth/models/faostat2018/product/price.py +1 -2
  17. hestia_earth/models/geospatialDatabase/croppingIntensity.py +2 -1
  18. hestia_earth/models/geospatialDatabase/utils.py +1 -1
  19. hestia_earth/models/ipcc2019/aboveGroundCropResidueTotal.py +15 -10
  20. hestia_earth/models/ipcc2019/animal/pastureGrass.py +50 -40
  21. hestia_earth/models/ipcc2019/belowGroundCropResidue.py +16 -11
  22. hestia_earth/models/ipcc2019/carbonContent.py +1 -1
  23. hestia_earth/models/ipcc2019/croppingDuration.py +2 -2
  24. hestia_earth/models/ipcc2019/ligninContent.py +1 -1
  25. hestia_earth/models/ipcc2019/nitrogenContent.py +1 -1
  26. hestia_earth/models/ipcc2019/organicCarbonPerHa.py +3 -3
  27. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1_utils.py +5 -5
  28. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2_utils.py +217 -48
  29. hestia_earth/models/ipcc2019/organicCarbonPerHa_utils.py +2 -6
  30. hestia_earth/models/ipcc2019/pastureGrass.py +43 -41
  31. hestia_earth/models/ipcc2019/pastureGrass_utils.py +63 -109
  32. hestia_earth/models/koble2014/cropResidueManagement.py +1 -1
  33. hestia_earth/models/linkedImpactAssessment/emissions.py +15 -14
  34. hestia_earth/models/mocking/search-results.json +249 -257
  35. hestia_earth/models/pooreNemecek2018/longFallowPeriod.py +1 -1
  36. hestia_earth/models/preload_requests.py +1 -1
  37. hestia_earth/models/requirements.py +6 -6
  38. hestia_earth/models/site/organicCarbonPerHa.py +1 -1
  39. hestia_earth/models/utils/__init__.py +1 -1
  40. hestia_earth/models/utils/blank_node.py +52 -9
  41. hestia_earth/models/utils/cycle.py +12 -12
  42. hestia_earth/models/utils/measurement.py +3 -3
  43. hestia_earth/models/utils/property.py +6 -6
  44. hestia_earth/models/utils/term.py +2 -1
  45. hestia_earth/models/version.py +1 -1
  46. {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.3.dist-info}/METADATA +12 -12
  47. {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.3.dist-info}/RECORD +56 -55
  48. {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.3.dist-info}/WHEEL +1 -1
  49. tests/models/cycle/product/test_revenue.py +0 -3
  50. tests/models/cycle/test_cycleDuration.py +1 -1
  51. tests/models/ipcc2019/test_organicCarbonPerHa.py +9 -20
  52. tests/models/ipcc2019/test_organicCarbonPerHa_tier_2_utils.py +0 -8
  53. tests/models/test_ecoinventV3.py +12 -0
  54. tests/models/test_ecoinventV3AndEmberClimate.py +5 -72
  55. {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.3.dist-info}/LICENSE +0 -0
  56. {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.3.dist-info}/top_level.txt +0 -0
@@ -3,7 +3,7 @@ The IPCC Tier 2 methodology for estimating soil organic carbon stock changes in
3
3
  management changes.
4
4
 
5
5
  More information on this model, including data requirements **and** recommendations, and examples can be found in the
6
- [Hestia SOC wiki](https://gitlab.com/hestia-earth/hestia-engine-models/-/wikis/Soil-organic-carbon-modelling).
6
+ [HESTIA SOC wiki](https://gitlab.com/hestia-earth/hestia-engine-models/-/wikis/Soil-organic-carbon-modelling).
7
7
 
8
8
  Source: [IPCC 2019, Vol. 4, Chapter 5](https://www.ipcc-nggip.iges.or.jp/public/2019rf/vol4.html).
9
9
  """
@@ -26,7 +26,7 @@ from hestia_earth.models.utils.array_builders import (
26
26
  )
27
27
  from hestia_earth.models.utils.blank_node import (
28
28
  cumulative_nodes_lookup_match, cumulative_nodes_term_match, group_nodes_by_year,
29
- group_nodes_by_year_and_month, GroupNodesByYearMode, node_term_match
29
+ group_nodes_by_year_and_month, 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
@@ -36,16 +36,16 @@ from hestia_earth.models.utils.site import related_cycles
36
36
 
37
37
  from .organicCarbonPerHa_utils import (
38
38
  CarbonSource, check_consecutive, DEPTH_LOWER, DEPTH_UPPER, check_irrigation,
39
- get_crop_residue_inc_or_left_terms_with_cache,
40
- get_upland_rice_crop_terms_with_cache,
41
- get_upland_rice_land_cover_terms_with_cache,
42
- IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE, IPCC_MANAGEMENT_CATEGORY_TO_TILLAGE_MANAGEMENT_LOOKUP_VALUE,
43
- IpccLandUseCategory, IpccManagementCategory, MIN_AREA_THRESHOLD, MIN_YIELD_THRESHOLD, sample_constant,
44
- sample_plus_minus_uncertainty, sample_truncated_normal, STATS_DEFINITION
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
45
44
  )
46
45
 
47
46
  _LOOKUPS = {
48
47
  "crop": "IPCC_LAND_USE_CATEGORY",
48
+ "landCover": "IPCC_LAND_USE_CATEGORY",
49
49
  "tillage": "IPCC_TILLAGE_MANAGEMENT_CATEGORY"
50
50
  }
51
51
 
@@ -59,10 +59,27 @@ _NUMBER_OF_TILLAGES_TERM_ID = "numberOfTillages"
59
59
  _TEMPERATURE_MONTHLY_TERM_ID = "temperatureMonthly"
60
60
  _PRECIPITATION_MONTHLY_TERM_ID = "precipitationMonthly"
61
61
  _PET_MONTHLY_TERM_ID = "potentialEvapotranspirationMonthly"
62
+ _ABOVE_GROUND_CROP_RESIDUE_TOTAL_TERM_ID = "aboveGroundCropResidueTotal"
62
63
  _CARBON_CONTENT_TERM_ID = "carbonContent"
63
64
  _NITROGEN_CONTENT_TERM_ID = "nitrogenContent"
64
65
  _LIGNIN_CONTENT_TERM_ID = "ligninContent"
65
66
 
67
+ _CROP_RESIDUE_MANAGEMENT_TERM_IDS = [
68
+ "residueIncorporated",
69
+ "residueIncorporatedLessThan30DaysBeforeCultivation",
70
+ "residueIncorporatedMoreThan30DaysBeforeCultivation",
71
+ "residueLeftOnField"
72
+ ]
73
+
74
+ _CARBON_SOURCE_TERM_IDS = [
75
+ "discardedCropIncorporated",
76
+ "discardedCropLeftOnField",
77
+ "belowGroundCropResidue",
78
+ ]
79
+
80
+ _MIN_RESIDUE_LEFT_ON_FIELD = 20 # TODO: Confirm assumption
81
+ _DEFAULT_COVER_CROP_BIOMASS = 4000 # TODO: Confirm assumption, Source PAS 2050-1:2012
82
+
66
83
  _CARBON_INPUT_PROPERTY_TERM_IDS = [
67
84
  _CARBON_CONTENT_TERM_ID,
68
85
  _NITROGEN_CONTENT_TERM_ID,
@@ -88,7 +105,7 @@ def _measurement(
88
105
  descriptive_stats_dict: dict
89
106
  ) -> dict:
90
107
  """
91
- Build a Hestia `Measurement` node to contain a value and descriptive statistics calculated by the models.
108
+ Build a HESTIA `Measurement` node to contain a value and descriptive statistics calculated by the models.
92
109
 
93
110
  The `descriptive_stats_dict` parameter should include the following keys and values from the
94
111
  [Measurement](https://www-staging.hestia.earth/schema/Measurement) schema:
@@ -113,7 +130,7 @@ def _measurement(
113
130
  Returns
114
131
  -------
115
132
  dict
116
- A valid Hestia `Measurement` node, see: https://www.hestia.earth/schema/Measurement.
133
+ A valid HESTIA `Measurement` node, see: https://www.hestia.earth/schema/Measurement.
117
134
  """
118
135
  measurement = _new_measurement(_TERM_ID) | descriptive_stats_dict
119
136
  measurement["dates"] = [f"{year}-12-31" for year in timestamps]
@@ -367,7 +384,7 @@ def should_run(site: dict) -> tuple[bool, dict, dict]:
367
384
  Parameters
368
385
  ----------
369
386
  site : dict
370
- A Hestia `Site` node, see: https://www.hestia.earth/schema/Site.
387
+ A HESTIA `Site` node, see: https://www.hestia.earth/schema/Site.
371
388
 
372
389
  Returns
373
390
  -------
@@ -1284,8 +1301,8 @@ def _get_grouped_sand_content_measurements(grouped_measurements: dict) -> dict:
1284
1301
 
1285
1302
  def _get_grouped_carbon_input_data(grouped_cycles: dict) -> dict:
1286
1303
  grouped_carbon_sources = {
1287
- year: _get_carbon_sources_from_cycles(cycle)
1288
- for year, cycle in grouped_cycles.items()
1304
+ year: flatten([_get_carbon_sources(cycle) for cycle in cycles])
1305
+ for year, cycles in grouped_cycles.items()
1289
1306
  }
1290
1307
 
1291
1308
  return {
@@ -1297,67 +1314,219 @@ def _get_grouped_carbon_input_data(grouped_cycles: dict) -> dict:
1297
1314
  }
1298
1315
 
1299
1316
 
1300
- def _get_carbon_sources_from_cycles(cycles: dict) -> list[CarbonSource]:
1317
+ def _get_carbon_sources(cycle: dict) -> list[CarbonSource]:
1301
1318
  """
1302
- Retrieves and formats all of the valid carbon sources from a list of cycles.
1319
+ Extract and format the carbon source data from a cycle's inputs and products.
1303
1320
 
1304
- Carbon sources can be either a Hestia `Product` node (e.g. crop residue) or `Input` node (e.g. organic amendment).
1321
+ Carbon sources can be either a HESTIA `Product` node (e.g. crop residue) or `Input` node (e.g. organic amendment).
1305
1322
 
1306
1323
  Parameters
1307
1324
  ----------
1308
- cycles : list[dict]
1309
- A list of Hestia `Cycle` nodes, see: https://www.hestia.earth/schema/Cycle.
1325
+ cycle : list[dict]
1326
+ A HESTIA `Cycle` node, see: https://www.hestia.earth/schema/Cycle.
1310
1327
 
1311
1328
  Returns
1312
1329
  -------
1313
1330
  list[CarbonSource]
1314
- A formatted list of `CarbonSource`s for the inputted `Cycle`s.
1331
+ A formatted list of `CarbonSource`s.
1315
1332
  """
1316
- inputs_and_products = non_empty_list(flatten(
1317
- [cycle.get("inputs", []) + cycle.get("products", []) for cycle in cycles]
1318
- ))
1319
-
1333
+ inputs_and_products = cycle.get("inputs", []) + cycle.get("products", [])
1320
1334
  return non_empty_list([
1321
- _iterate_carbon_source(node) for node in inputs_and_products
1322
- if any([
1323
- node.get("term", {}).get("@id") in get_crop_residue_inc_or_left_terms_with_cache(),
1324
- node.get("term", {}).get("termType") in _CARBON_SOURCE_TERM_TYPES
1325
- ])
1335
+ next(
1336
+ (_func(node, cycle) for validator, _func in _CARBON_SOURCE_DECISION_TREE.items() if validator(node)),
1337
+ None
1338
+ ) for node in inputs_and_products
1339
+ ])
1340
+
1341
+
1342
+ def _should_run_carbon_source_ag_residue(node: dict) -> bool:
1343
+ """
1344
+ Determine whether an input or product is a valid above-ground biomass carbon source.
1345
+
1346
+ Parameters
1347
+ ----------
1348
+ node : dict
1349
+ A HESTIA [Input](https://www.hestia.earth/schema/Input) or [Product](https://www.hestia.earth/schema/Product)
1350
+ node.
1351
+
1352
+ Returns
1353
+ -------
1354
+ bool
1355
+ Whether the node satisfies the critera.
1356
+ """
1357
+ return node.get("term", {}).get("@id") == _ABOVE_GROUND_CROP_RESIDUE_TOTAL_TERM_ID
1358
+
1359
+
1360
+ def _calc_carbon_source_ag_crop_residue(node: dict, cycle: dict) -> Union[CarbonSource, None]:
1361
+ """
1362
+ Extract and format the carbon source data for above-ground crop residues.
1363
+
1364
+ n.b., We assume that even if a cycle's residue management states that 100% of above-ground crop residues are
1365
+ removed or burnt, a minimal amount of crop residues do remain on site to become a carbon source (see
1366
+ `_MIN_RESIDUE_LEFT_ON_FIELD` variable).
1367
+
1368
+ Parameters
1369
+ ----------
1370
+ node : dict
1371
+ A HESTIA [Product](https://www.hestia.earth/schema/Product) node with `term.termType` == `landCover`.
1372
+
1373
+ Returns
1374
+ -------
1375
+ CarbonSource | None
1376
+ The carbon source data of the above-ground residues, or `None` if carbon source data incomplete.
1377
+ """
1378
+ value = get_node_value(node)
1379
+ residue_left_on_field = list_sum([
1380
+ get_node_value(practice) for practice in cycle.get("practices", [])
1381
+ if node_term_match(practice, _CROP_RESIDUE_MANAGEMENT_TERM_IDS)
1382
+ ])
1383
+ mass = value * max(residue_left_on_field, _MIN_RESIDUE_LEFT_ON_FIELD) / 100
1384
+
1385
+ carbon_source = CarbonSource(
1386
+ mass,
1387
+ *_retrieve_carbon_source_properties(node)
1388
+ )
1389
+
1390
+ return carbon_source if _validate_carbon_source(carbon_source) else None
1391
+
1392
+
1393
+ def _should_run_carbon_source_cover_crop(node: dict) -> bool:
1394
+ """
1395
+ Determine whether a product is a valid above cover crop carbon source.
1396
+
1397
+ Parameters
1398
+ ----------
1399
+ node : dict
1400
+ A HESTIA [Input](https://www.hestia.earth/schema/Input) or [Product](https://www.hestia.earth/schema/Product)
1401
+ node.
1402
+
1403
+ Returns
1404
+ -------
1405
+ bool
1406
+ Whether the node satisfies the critera.
1407
+ """
1408
+ LOOKUP = _LOOKUPS["landCover"]
1409
+ return all([
1410
+ node.get("term", {}).get("termType") in [TermTermType.LANDCOVER.value],
1411
+ node_lookup_match(
1412
+ node, LOOKUP, IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE[IpccLandUseCategory.ANNUAL_CROPS]
1413
+ ),
1414
+ any(
1415
+ get_node_property(node, term_id, False).get("value", False)
1416
+ for term_id in get_cover_crop_property_terms_with_cache()
1417
+ )
1418
+ ])
1419
+
1420
+
1421
+ def _calc_carbon_source_cover_crop(node: dict, *_) -> Union[CarbonSource, None]:
1422
+ """
1423
+ Extract and format the carbon source data for an annual cover crop.
1424
+
1425
+ n.b., We make the assumption that the entirety of the cover crop's biomass remains on site.
1426
+
1427
+ Parameters
1428
+ ----------
1429
+ node : dict
1430
+ A HESTIA [Product](https://www.hestia.earth/schema/Product) node with `term.termType` == `landCover`.
1431
+
1432
+ Returns
1433
+ -------
1434
+ CarbonSource | None
1435
+ The carbon source data of the cover crop, or `None` if carbon source data incomplete.
1436
+ """
1437
+ value = get_node_value(node)
1438
+ carbon_source = CarbonSource(
1439
+ _DEFAULT_COVER_CROP_BIOMASS * value / 100,
1440
+ _Parameter.DEFAULT_CARBON_CONTENT.value.get("value"),
1441
+ _Parameter.DEFAULT_NITROGEN_CONTENT.value.get("value"),
1442
+ _Parameter.DEFAULT_NITROGEN_CONTENT.value.get("value")
1443
+ )
1444
+ return carbon_source
1445
+
1446
+
1447
+ def _should_run_carbon_source(node: dict) -> bool:
1448
+ """
1449
+ Determine whether an input or product is a valid carbon source.
1450
+
1451
+ Parameters
1452
+ ----------
1453
+ node : dict
1454
+ A HESTIA [Input](https://www.hestia.earth/schema/Input) or [Product](https://www.hestia.earth/schema/Product)
1455
+ node.
1456
+
1457
+ Returns
1458
+ -------
1459
+ bool
1460
+ Whether the node satisfies the critera.
1461
+ """
1462
+ return any([
1463
+ node.get("term", {}).get("@id") in _CARBON_SOURCE_TERM_IDS,
1464
+ node.get("term", {}).get("termType") in _CARBON_SOURCE_TERM_TYPES
1326
1465
  ])
1327
1466
 
1328
1467
 
1329
- def _iterate_carbon_source(node: dict) -> Union[CarbonSource, None]:
1468
+ def _calc_carbon_source(node: dict, *_) -> Union[CarbonSource, None]:
1330
1469
  """
1331
- Validates whether a node is a valid carbon source and returns a `CarbonSource` named tuple if yes.
1470
+ Extract and format the carbon source data for an input or product.
1332
1471
 
1333
1472
  Parameters
1334
1473
  ----------
1335
1474
  node : dict
1336
- A Hestia `Product` or `Input` node, see: https://www.hestia.earth/schema/Product
1337
- or https://www.hestia.earth/schema/Input.
1475
+ A HESTIA [Input](https://www.hestia.earth/schema/Input) or [Product](https://www.hestia.earth/schema/Product)
1476
+ node.
1338
1477
 
1339
1478
  Returns
1340
1479
  -------
1341
1480
  CarbonSource | None
1342
- A `CarbonSource` named tuple if the node is a carbon source with the required properties, else `None`.
1481
+ The carbon source data of the cover crop, or `None` if carbon source data incomplete.
1482
+ """
1483
+ carbon_source = CarbonSource(
1484
+ get_node_value(node),
1485
+ *_retrieve_carbon_source_properties(node)
1486
+ )
1487
+
1488
+ return carbon_source if _validate_carbon_source(carbon_source) else None
1489
+
1490
+
1491
+ def _retrieve_carbon_source_properties(node: dict) -> tuple[float, float, float]:
1492
+ """
1493
+ Extract the carbon source properties from an input or product node or, if required, retrieve them from default
1494
+ properties.
1495
+
1496
+ Parameters
1497
+ ----------
1498
+ node : dict
1499
+ A HESTIA [Input](https://www.hestia.earth/schema/Input) or [Product](https://www.hestia.earth/schema/Product)
1500
+ node.
1501
+
1502
+ Returns
1503
+ -------
1504
+ tuple[float, float, float]
1505
+ `(carbon_content, nitrogen_content, lignin_content)`
1343
1506
  """
1344
- mass = list_sum(node.get("value", []))
1345
1507
  carbon_content, nitrogen_content, lignin_content = (
1346
1508
  get_node_property(node, term_id).get("value", 0)/100 for term_id in _CARBON_INPUT_PROPERTY_TERM_IDS
1347
1509
  )
1510
+ return carbon_content, nitrogen_content, lignin_content
1348
1511
 
1349
- should_run_ = all([
1350
- mass > 0,
1351
- 0 < carbon_content <= 1,
1352
- 0 < nitrogen_content <= 1,
1353
- 0 < lignin_content <= 1
1512
+
1513
+ def _validate_carbon_source(carbon_source: CarbonSource) -> bool:
1514
+ """
1515
+ Validate that a `CarbonSource` named tuple is data complete.
1516
+ """
1517
+ return all([
1518
+ carbon_source.mass > 0,
1519
+ 0 < carbon_source.carbon_content <= 1,
1520
+ 0 < carbon_source.nitrogen_content <= 1,
1521
+ 0 < carbon_source.lignin_content <= 1
1354
1522
  ])
1355
1523
 
1356
- return (
1357
- CarbonSource(
1358
- mass, carbon_content, nitrogen_content, lignin_content
1359
- ) if should_run_ else None
1360
- )
1524
+
1525
+ _CARBON_SOURCE_DECISION_TREE = {
1526
+ _should_run_carbon_source_ag_residue: _calc_carbon_source_ag_crop_residue,
1527
+ _should_run_carbon_source_cover_crop: _calc_carbon_source_cover_crop,
1528
+ _should_run_carbon_source: _calc_carbon_source,
1529
+ }
1361
1530
 
1362
1531
 
1363
1532
  def _calc_total_organic_carbon_input(
@@ -1450,12 +1619,12 @@ def _assign_tier_2_ipcc_tillage_management_category(
1450
1619
  default: IpccManagementCategory = IpccManagementCategory.OTHER
1451
1620
  ) -> IpccManagementCategory:
1452
1621
  """
1453
- Assigns a tillage `IpccManagementCategory` to a list of Hestia `Cycle`s.
1622
+ Assigns a tillage `IpccManagementCategory` to a list of HESTIA `Cycle`s.
1454
1623
 
1455
1624
  Parameters
1456
1625
  ----------
1457
1626
  cycles : list[dict])
1458
- A list of Hestia `Cycle` nodes, see: https://www.hestia.earth/schema/Cycle.
1627
+ A list of HESTIA `Cycle` nodes, see: https://www.hestia.earth/schema/Cycle.
1459
1628
 
1460
1629
  Returns
1461
1630
  -------
@@ -1499,7 +1668,7 @@ def _check_cycle_tillage_management_category(
1499
1668
  Parameters
1500
1669
  ----------
1501
1670
  cycle : dict
1502
- A Hestia `Cycle` node, see: https://www.hestia.earth/schema/Cycle.
1671
+ A HESTIA `Cycle` node, see: https://www.hestia.earth/schema/Cycle.
1503
1672
  key : IpccManagementCategory
1504
1673
  The `IpccManagementCategory` to match.
1505
1674
 
@@ -1534,7 +1703,7 @@ def _check_zero_tillages(practices: list[dict]) -> bool:
1534
1703
  Parameters
1535
1704
  ----------
1536
1705
  practices : list[dict]
1537
- A list of Hestia `Practice` nodes, see: https://www.hestia.earth/schema/Practice.
1706
+ A list of HESTIA `Practice` nodes, see: https://www.hestia.earth/schema/Practice.
1538
1707
 
1539
1708
  Returns
1540
1709
  -------
@@ -10,8 +10,8 @@ from hestia_earth.models.utils.array_builders import (
10
10
  )
11
11
  from hestia_earth.models.utils.blank_node import cumulative_nodes_term_match
12
12
  from hestia_earth.models.utils.term import (
13
- get_cover_crop_property_terms, get_crop_residue_incorporated_or_left_on_field_terms, get_irrigated_terms,
14
- get_residue_removed_or_burnt_terms, get_upland_rice_crop_terms, get_upland_rice_land_cover_terms
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
15
  )
16
16
 
17
17
  STATS_DEFINITION = MeasurementStatsDefinition.SIMULATED.value
@@ -27,10 +27,6 @@ def get_cover_crop_property_terms_with_cache():
27
27
  return lru_cache()(get_cover_crop_property_terms)()
28
28
 
29
29
 
30
- def get_crop_residue_inc_or_left_terms_with_cache():
31
- return lru_cache()(get_crop_residue_incorporated_or_left_on_field_terms)()
32
-
33
-
34
30
  def get_irrigated_terms_with_cache():
35
31
  return lru_cache()(get_irrigated_terms)()
36
32
 
@@ -12,7 +12,8 @@ from hestia_earth.schema import TermTermType
12
12
  from hestia_earth.utils.model import filter_list_term_type
13
13
  from hestia_earth.utils.tools import list_sum
14
14
 
15
- from hestia_earth.models.log import logRequirements, logShouldRun, debugValues
15
+ from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
16
+ from hestia_earth.models.utils.blank_node import lookups_logs, properties_logs
16
17
  from hestia_earth.models.utils.input import _new_input
17
18
  from hestia_earth.models.utils.term import get_wool_terms
18
19
  from hestia_earth.models.utils.completeness import _is_term_type_complete, _is_term_type_incomplete
@@ -25,6 +26,7 @@ from .pastureGrass_utils import (
25
26
  calculate_REM,
26
27
  calculate_REG,
27
28
  calculate_NEfeed,
29
+ calculate_GE,
28
30
  product_wool_energy,
29
31
  get_animals,
30
32
  get_animal_values
@@ -119,7 +121,8 @@ REQUIREMENTS = {
119
121
  }
120
122
  LOOKUPS = {
121
123
  "animalManagement": [
122
- "mjKgEvMilkIpcc2019"
124
+ "mjKgEvMilkIpcc2019",
125
+ "defaultFatContentEvMilkIpcc2019"
123
126
  ],
124
127
  "animalProduct": ["mjKgEvWoolNetEnergyWoolIpcc2019"],
125
128
  "liveAnimal": [
@@ -127,7 +130,8 @@ LOOKUPS = {
127
130
  "mjDayKgCfiNetEnergyMaintenanceIpcc2019",
128
131
  "ratioCPregnancyNetEnergyPregnancyIpcc2019",
129
132
  "ratioCNetEnergyGrowthCattleBuffaloIpcc2019",
130
- "mjKgABNetEnergyGrowthSheepGoatsIpcc2019"
133
+ "mjKgABNetEnergyGrowthSheepGoatsIpcc2019",
134
+ "isWoolProducingAnimal"
131
135
  ],
132
136
  "system-liveAnimal-activityCoefficient-ipcc2019": "using animal term @id",
133
137
  "crop-property": ["energyDigestibilityRuminants", "energyContentHigherHeatingValue"],
@@ -150,9 +154,6 @@ def _input(term_id: str, value: float):
150
154
  return node
151
155
 
152
156
 
153
- def _sum_values(values: list, index=0): return list_sum([v[index] for v in values])
154
-
155
-
156
157
  def calculate_NEwool(cycle: dict) -> float:
157
158
  term_ids = get_wool_terms()
158
159
  products = [p for p in cycle.get('products', []) if p.get('term', {}).get('@id') in term_ids]
@@ -162,39 +163,20 @@ def calculate_NEwool(cycle: dict) -> float:
162
163
  return sum([value * lookup_value for (value, lookup_value) in product_values])
163
164
 
164
165
 
165
- def _calculate_GE(
166
- cycle: dict, animals: list, REM: float, REG: float, NEwool: float, NEm_feed: float, NEg_feed: float, system: dict
167
- ) -> float:
168
- values = [get_animal_values(cycle, animal, system, log_node=cycle) for animal in animals]
169
- NEm = _sum_values(values, 0)
170
- NEa = _sum_values(values, 1)
171
- NEl = _sum_values(values, 2)
172
- NEwork = _sum_values(values, 3)
173
- NEp = _sum_values(values, 4)
174
- NEg = _sum_values(values, 5)
175
-
176
- debugValues(cycle, model=MODEL, term=MODEL_KEY, model_key=MODEL_KEY,
177
- NEm=NEm,
178
- NEa=NEa,
179
- NEl=NEl,
180
- NEwork=NEwork,
181
- NEp=NEp,
182
- NEg=NEg,
183
- NEm_feed=NEm_feed,
184
- NEg_feed=NEg_feed)
185
-
186
- return (NEm + NEa + NEl + NEwork + NEp - NEm_feed)/REM + (NEg + NEwool - NEg_feed)/REG
187
-
188
-
189
166
  def _run_practice(cycle: dict, meanDE: float, meanECHHV: float, system: dict):
190
167
  animals = get_animals(cycle)
191
168
  REM = calculate_REM(meanDE)
192
169
  REG = calculate_REG(meanDE)
193
170
  NEwool = calculate_NEwool(cycle)
194
171
  NEm_feed, NEg_feed = calculate_NEfeed(cycle)
172
+
173
+ animal_values = [{
174
+ 'animalId': animal.get('term', {}).get('@id')
175
+ } | get_animal_values(cycle, animal, system) for animal in animals]
176
+
195
177
  GE = (
196
- _calculate_GE(cycle, animals, REM, REG, NEwool, NEm_feed, NEg_feed, system) / (meanDE/100)
197
- ) if all([REM, REG]) else 0
178
+ calculate_GE(animal_values, REM, REG, NEwool, NEm_feed, NEg_feed) / (meanDE/100)
179
+ ) if meanDE else 0
198
180
 
199
181
  def run(practice: dict):
200
182
  key = practice.get('key', {})
@@ -202,14 +184,34 @@ def _run_practice(cycle: dict, meanDE: float, meanECHHV: float, system: dict):
202
184
  input_term_id = practice_input_id(practice)
203
185
  value = (GE / meanECHHV) * (list_sum(practice.get('value', [0])) / 100)
204
186
 
187
+ logs = log_as_table([v | {
188
+ 'practiceKeyId': key_id,
189
+ 'REM': REM,
190
+ 'REG': REG,
191
+ 'NEwool': NEwool,
192
+ 'NEmFeed': NEm_feed,
193
+ 'NEgFeed': NEg_feed,
194
+ 'GE': GE,
195
+ 'meanECHHV': meanECHHV,
196
+ 'meanDE': meanDE
197
+ } for v in animal_values])
198
+ animal_lookups = lookups_logs(MODEL, animals, LOOKUPS, model_key=MODEL_KEY, term=input_term_id)
199
+ animal_properties = properties_logs(animals, properties=[
200
+ 'liveweightPerHead',
201
+ 'hoursWorkedPerDay',
202
+ 'animalsPerBirth',
203
+ 'pregnancyRateTotal',
204
+ 'weightAtMaturity',
205
+ 'liveweightGain',
206
+ 'weightAtWeaning',
207
+ 'weightAtOneYear',
208
+ 'weightAtSlaughter'
209
+ ])
210
+
205
211
  logRequirements(cycle, model=MODEL, term=input_term_id, model_key=MODEL_KEY,
206
- REM=REM,
207
- REG=REG,
208
- NEwool=NEwool,
209
- NEm_feed=NEm_feed,
210
- NEg_feed=NEg_feed,
211
- GE=GE,
212
- practice_key_id=key_id)
212
+ animal_logs=logs,
213
+ animal_lookups=animal_lookups,
214
+ animal_properties=animal_properties)
213
215
 
214
216
  logShouldRun(cycle, MODEL, input_term_id, True, model_key=MODEL_KEY)
215
217
 
@@ -249,8 +251,8 @@ def _should_run(cycle: dict, practices: dict):
249
251
  term_type_freshForage_incomplete=freshForage_incomplete,
250
252
  has_cycle_inputs_feed=has_cycle_inputs_feed,
251
253
  all_animals_have_value=all_animals_have_value,
252
- meanDE=meanDE,
253
- meanECHHV=meanECHHV)
254
+ meanDE=calculate_meanDE(practices, term=term_id),
255
+ meanECHHV=calculate_meanECHHV(practices, term=term_id))
254
256
 
255
257
  logShouldRun(cycle, MODEL, term_id, should_run, model_key=MODEL_KEY)
256
258