hestia-earth-models 0.62.0__py3-none-any.whl → 0.62.2__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 (68) hide show
  1. hestia_earth/models/blonkConsultants2016/utils.py +3 -2
  2. hestia_earth/models/cycle/coldCarcassWeightPerHead.py +4 -2
  3. hestia_earth/models/cycle/coldDressedCarcassWeightPerHead.py +2 -2
  4. hestia_earth/models/cycle/concentrateFeed.py +3 -3
  5. hestia_earth/models/cycle/feedConversionRatio/feedConversionRatioNitrogen.py +2 -1
  6. hestia_earth/models/cycle/post_checks/__init__.py +3 -2
  7. hestia_earth/models/cycle/post_checks/otherSites.py +40 -0
  8. hestia_earth/models/cycle/pre_checks/__init__.py +2 -1
  9. hestia_earth/models/cycle/pre_checks/otherSites.py +42 -0
  10. hestia_earth/models/cycle/pre_checks/site.py +1 -1
  11. hestia_earth/models/cycle/readyToCookWeightPerHead.py +2 -2
  12. hestia_earth/models/ecoinventV3AndEmberClimate/utils.py +1 -1
  13. hestia_earth/models/emepEea2019/utils.py +4 -3
  14. hestia_earth/models/geospatialDatabase/heavyWinterPrecipitation.py +1 -1
  15. hestia_earth/models/ipcc2019/animal/pastureGrass.py +53 -43
  16. hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChangeManagementChange.py +30 -4
  17. hestia_earth/models/ipcc2019/n2OToAirExcretaDirect.py +6 -2
  18. hestia_earth/models/ipcc2019/n2OToAirExcretaIndirect.py +1 -1
  19. hestia_earth/models/ipcc2019/n2OToAirInorganicFertiliserDirect.py +1 -1
  20. hestia_earth/models/ipcc2019/n2OToAirInorganicFertiliserIndirect.py +1 -1
  21. hestia_earth/models/ipcc2019/n2OToAirOrganicFertiliserIndirect.py +1 -1
  22. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1_utils.py +4 -2
  23. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2_utils.py +210 -40
  24. hestia_earth/models/ipcc2019/organicCarbonPerHa_utils.py +2 -6
  25. hestia_earth/models/ipcc2019/pastureGrass.py +44 -42
  26. hestia_earth/models/ipcc2019/pastureGrass_utils.py +46 -92
  27. hestia_earth/models/mocking/search-results.json +378 -234
  28. hestia_earth/models/schererPfister2015/utils.py +2 -2
  29. hestia_earth/models/site/brackishWater.py +1 -1
  30. hestia_earth/models/site/flowingWater.py +1 -1
  31. hestia_earth/models/site/freshWater.py +1 -1
  32. hestia_earth/models/site/management.py +79 -38
  33. hestia_earth/models/site/pre_checks/cache_sources.py +9 -13
  34. hestia_earth/models/site/salineWater.py +1 -1
  35. hestia_earth/models/stehfestBouwman2006/n2OToAirCropResidueDecompositionDirect.py +12 -2
  36. hestia_earth/models/stehfestBouwman2006/n2OToAirExcretaDirect.py +12 -2
  37. hestia_earth/models/stehfestBouwman2006/n2OToAirInorganicFertiliserDirect.py +11 -1
  38. hestia_earth/models/stehfestBouwman2006/n2OToAirOrganicFertiliserDirect.py +11 -1
  39. hestia_earth/models/stehfestBouwman2006/noxToAirCropResidueDecomposition.py +12 -2
  40. hestia_earth/models/stehfestBouwman2006/noxToAirExcreta.py +12 -2
  41. hestia_earth/models/stehfestBouwman2006/noxToAirInorganicFertiliser.py +11 -1
  42. hestia_earth/models/stehfestBouwman2006/noxToAirOrganicFertiliser.py +11 -1
  43. hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirCropResidueDecomposition.py +12 -2
  44. hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirExcreta.py +12 -2
  45. hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirInorganicFertiliser.py +11 -1
  46. hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirOrganicFertiliser.py +11 -1
  47. hestia_earth/models/utils/blank_node.py +144 -110
  48. hestia_earth/models/utils/constant.py +2 -0
  49. hestia_earth/models/utils/lookup.py +19 -6
  50. hestia_earth/models/utils/property.py +6 -6
  51. hestia_earth/models/utils/site.py +7 -0
  52. hestia_earth/models/utils/source.py +1 -1
  53. hestia_earth/models/utils/term.py +21 -1
  54. hestia_earth/models/version.py +1 -1
  55. {hestia_earth_models-0.62.0.dist-info → hestia_earth_models-0.62.2.dist-info}/METADATA +2 -2
  56. {hestia_earth_models-0.62.0.dist-info → hestia_earth_models-0.62.2.dist-info}/RECORD +68 -63
  57. tests/models/cycle/post_checks/test_otherSites.py +15 -0
  58. tests/models/cycle/pre_checks/test_otherSites.py +21 -0
  59. tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChangeManagementChange.py +5 -3
  60. tests/models/ipcc2019/test_organicCarbonPerHa.py +10 -20
  61. tests/models/ipcc2019/test_organicCarbonPerHa_tier_2_utils.py +0 -8
  62. tests/models/site/pre_checks/test_cache_sources.py +6 -10
  63. tests/models/site/test_management.py +192 -4
  64. tests/models/utils/test_blank_node.py +0 -281
  65. tests/models/utils/test_lookup.py +10 -0
  66. {hestia_earth_models-0.62.0.dist-info → hestia_earth_models-0.62.2.dist-info}/LICENSE +0 -0
  67. {hestia_earth_models-0.62.0.dist-info → hestia_earth_models-0.62.2.dist-info}/WHEEL +0 -0
  68. {hestia_earth_models-0.62.0.dist-info → hestia_earth_models-0.62.2.dist-info}/top_level.txt +0 -0
@@ -19,13 +19,14 @@ from hestia_earth.schema import (
19
19
  )
20
20
  from hestia_earth.utils.model import find_term_match, filter_list_term_type
21
21
  from hestia_earth.utils.tools import flatten, list_sum, non_empty_list
22
+ from hestia_earth.utils.blank_node import get_node_value
22
23
 
23
24
  from hestia_earth.models.utils.array_builders import (
24
25
  avg_run_in_columnwise, gen_seed, grouped_avg, repeat_1d_array_as_columns
25
26
  )
26
27
  from hestia_earth.models.utils.blank_node import (
27
- cumulative_nodes_lookup_match, cumulative_nodes_term_match, get_node_value, group_nodes_by_year,
28
- group_nodes_by_year_and_month, GroupNodesByYearMode, node_term_match
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
29
30
  )
30
31
  from hestia_earth.models.utils.cycle import check_cycle_site_ids_identical
31
32
  from hestia_earth.models.utils.descriptive_stats import calc_descriptive_stats
@@ -35,16 +36,16 @@ from hestia_earth.models.utils.site import related_cycles
35
36
 
36
37
  from .organicCarbonPerHa_utils import (
37
38
  CarbonSource, check_consecutive, DEPTH_LOWER, DEPTH_UPPER, check_irrigation,
38
- get_crop_residue_inc_or_left_terms_with_cache,
39
- get_upland_rice_crop_terms_with_cache,
40
- get_upland_rice_land_cover_terms_with_cache,
41
- IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE, IPCC_MANAGEMENT_CATEGORY_TO_TILLAGE_MANAGEMENT_LOOKUP_VALUE,
42
- IpccLandUseCategory, IpccManagementCategory, MIN_AREA_THRESHOLD, MIN_YIELD_THRESHOLD, sample_constant,
43
- 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
44
44
  )
45
45
 
46
46
  _LOOKUPS = {
47
47
  "crop": "IPCC_LAND_USE_CATEGORY",
48
+ "landCover": "IPCC_LAND_USE_CATEGORY",
48
49
  "tillage": "IPCC_TILLAGE_MANAGEMENT_CATEGORY"
49
50
  }
50
51
 
@@ -58,10 +59,27 @@ _NUMBER_OF_TILLAGES_TERM_ID = "numberOfTillages"
58
59
  _TEMPERATURE_MONTHLY_TERM_ID = "temperatureMonthly"
59
60
  _PRECIPITATION_MONTHLY_TERM_ID = "precipitationMonthly"
60
61
  _PET_MONTHLY_TERM_ID = "potentialEvapotranspirationMonthly"
62
+ _ABOVE_GROUND_CROP_RESIDUE_TOTAL_TERM_ID = "aboveGroundCropResidueTotal"
61
63
  _CARBON_CONTENT_TERM_ID = "carbonContent"
62
64
  _NITROGEN_CONTENT_TERM_ID = "nitrogenContent"
63
65
  _LIGNIN_CONTENT_TERM_ID = "ligninContent"
64
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
+
65
83
  _CARBON_INPUT_PROPERTY_TERM_IDS = [
66
84
  _CARBON_CONTENT_TERM_ID,
67
85
  _NITROGEN_CONTENT_TERM_ID,
@@ -1283,8 +1301,8 @@ def _get_grouped_sand_content_measurements(grouped_measurements: dict) -> dict:
1283
1301
 
1284
1302
  def _get_grouped_carbon_input_data(grouped_cycles: dict) -> dict:
1285
1303
  grouped_carbon_sources = {
1286
- year: _get_carbon_sources_from_cycles(cycle)
1287
- 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()
1288
1306
  }
1289
1307
 
1290
1308
  return {
@@ -1296,67 +1314,219 @@ def _get_grouped_carbon_input_data(grouped_cycles: dict) -> dict:
1296
1314
  }
1297
1315
 
1298
1316
 
1299
- def _get_carbon_sources_from_cycles(cycles: dict) -> list[CarbonSource]:
1317
+ def _get_carbon_sources(cycle: dict) -> list[CarbonSource]:
1300
1318
  """
1301
- 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.
1302
1320
 
1303
1321
  Carbon sources can be either a Hestia `Product` node (e.g. crop residue) or `Input` node (e.g. organic amendment).
1304
1322
 
1305
1323
  Parameters
1306
1324
  ----------
1307
- cycles : list[dict]
1308
- 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.
1309
1327
 
1310
1328
  Returns
1311
1329
  -------
1312
1330
  list[CarbonSource]
1313
- A formatted list of `CarbonSource`s for the inputted `Cycle`s.
1331
+ A formatted list of `CarbonSource`s.
1314
1332
  """
1315
- inputs_and_products = non_empty_list(flatten(
1316
- [cycle.get("inputs", []) + cycle.get("products", []) for cycle in cycles]
1317
- ))
1318
-
1333
+ inputs_and_products = cycle.get("inputs", []) + cycle.get("products", [])
1319
1334
  return non_empty_list([
1320
- _iterate_carbon_source(node) for node in inputs_and_products
1321
- if any([
1322
- node.get("term", {}).get("@id") in get_crop_residue_inc_or_left_terms_with_cache(),
1323
- node.get("term", {}).get("termType") in _CARBON_SOURCE_TERM_TYPES
1324
- ])
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
+ )
1325
1418
  ])
1326
1419
 
1327
1420
 
1328
- def _iterate_carbon_source(node: dict) -> Union[CarbonSource, None]:
1421
+ def _calc_carbon_source_cover_crop(node: dict, *_) -> Union[CarbonSource, None]:
1329
1422
  """
1330
- Validates whether a node is a valid carbon source and returns a `CarbonSource` named tuple if yes.
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.
1331
1426
 
1332
1427
  Parameters
1333
1428
  ----------
1334
1429
  node : dict
1335
- A Hestia `Product` or `Input` node, see: https://www.hestia.earth/schema/Product
1336
- or https://www.hestia.earth/schema/Input.
1430
+ A Hestia [Product](https://www.hestia.earth/schema/Product) node with `term.termType` == `landCover`.
1337
1431
 
1338
1432
  Returns
1339
1433
  -------
1340
1434
  CarbonSource | None
1341
- A `CarbonSource` named tuple if the node is a carbon source with the required properties, else `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
1465
+ ])
1466
+
1467
+
1468
+ def _calc_carbon_source(node: dict, *_) -> Union[CarbonSource, None]:
1469
+ """
1470
+ Extract and format the carbon source data for an input or product.
1471
+
1472
+ Parameters
1473
+ ----------
1474
+ node : dict
1475
+ A Hestia [Input](https://www.hestia.earth/schema/Input) or [Product](https://www.hestia.earth/schema/Product)
1476
+ node.
1477
+
1478
+ Returns
1479
+ -------
1480
+ CarbonSource | 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)`
1342
1506
  """
1343
- mass = list_sum(node.get("value", []))
1344
1507
  carbon_content, nitrogen_content, lignin_content = (
1345
1508
  get_node_property(node, term_id).get("value", 0)/100 for term_id in _CARBON_INPUT_PROPERTY_TERM_IDS
1346
1509
  )
1510
+ return carbon_content, nitrogen_content, lignin_content
1347
1511
 
1348
- should_run_ = all([
1349
- mass > 0,
1350
- 0 < carbon_content <= 1,
1351
- 0 < nitrogen_content <= 1,
1352
- 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
1353
1522
  ])
1354
1523
 
1355
- return (
1356
- CarbonSource(
1357
- mass, carbon_content, nitrogen_content, lignin_content
1358
- ) if should_run_ else None
1359
- )
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
+ }
1360
1530
 
1361
1531
 
1362
1532
  def _calc_total_organic_carbon_input(
@@ -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) 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
- 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
 
@@ -242,15 +244,15 @@ def _should_run(cycle: dict, practices: dict):
242
244
  meanECHHV > 0
243
245
  ])
244
246
 
245
- for term_id in [MODEL_KEY] + [practice_input_id(p) for p in practices]:
247
+ for term_id in [practice_input_id(p) for p in practices] or [MODEL_KEY]:
246
248
  logRequirements(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
247
249
  term_type_animalFeed_complete=animalFeed_complete,
248
250
  term_type_animalPopulation_complete=animalPopulation_complete,
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