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.
- hestia_earth/models/agribalyse2016/fuelElectricity.py +1 -1
- hestia_earth/models/cycle/animal/input/hestiaAggregatedData.py +1 -1
- hestia_earth/models/cycle/coldCarcassWeightPerHead.py +4 -2
- hestia_earth/models/cycle/coldDressedCarcassWeightPerHead.py +2 -2
- hestia_earth/models/cycle/concentrateFeed.py +3 -3
- hestia_earth/models/cycle/cycleDuration.py +7 -2
- hestia_earth/models/cycle/feedConversionRatio/feedConversionRatioNitrogen.py +2 -1
- hestia_earth/models/cycle/input/hestiaAggregatedData.py +1 -1
- hestia_earth/models/cycle/product/price.py +5 -1
- hestia_earth/models/cycle/product/revenue.py +6 -7
- hestia_earth/models/cycle/readyToCookWeightPerHead.py +2 -2
- hestia_earth/models/ecoinventV3/__init__.py +25 -52
- hestia_earth/models/ecoinventV3/utils.py +40 -0
- hestia_earth/models/ecoinventV3AndEmberClimate/__init__.py +92 -91
- hestia_earth/models/ecoinventV3AndEmberClimate/utils.py +15 -105
- hestia_earth/models/faostat2018/product/price.py +1 -2
- hestia_earth/models/geospatialDatabase/croppingIntensity.py +2 -1
- hestia_earth/models/geospatialDatabase/utils.py +1 -1
- hestia_earth/models/ipcc2019/aboveGroundCropResidueTotal.py +15 -10
- hestia_earth/models/ipcc2019/animal/pastureGrass.py +50 -40
- hestia_earth/models/ipcc2019/belowGroundCropResidue.py +16 -11
- hestia_earth/models/ipcc2019/carbonContent.py +1 -1
- hestia_earth/models/ipcc2019/croppingDuration.py +2 -2
- hestia_earth/models/ipcc2019/ligninContent.py +1 -1
- hestia_earth/models/ipcc2019/nitrogenContent.py +1 -1
- hestia_earth/models/ipcc2019/organicCarbonPerHa.py +3 -3
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1_utils.py +5 -5
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2_utils.py +217 -48
- hestia_earth/models/ipcc2019/organicCarbonPerHa_utils.py +2 -6
- hestia_earth/models/ipcc2019/pastureGrass.py +43 -41
- hestia_earth/models/ipcc2019/pastureGrass_utils.py +63 -109
- hestia_earth/models/koble2014/cropResidueManagement.py +1 -1
- hestia_earth/models/linkedImpactAssessment/emissions.py +15 -14
- hestia_earth/models/mocking/search-results.json +249 -257
- hestia_earth/models/pooreNemecek2018/longFallowPeriod.py +1 -1
- hestia_earth/models/preload_requests.py +1 -1
- hestia_earth/models/requirements.py +6 -6
- hestia_earth/models/site/organicCarbonPerHa.py +1 -1
- hestia_earth/models/utils/__init__.py +1 -1
- hestia_earth/models/utils/blank_node.py +52 -9
- hestia_earth/models/utils/cycle.py +12 -12
- hestia_earth/models/utils/measurement.py +3 -3
- hestia_earth/models/utils/property.py +6 -6
- hestia_earth/models/utils/term.py +2 -1
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.3.dist-info}/METADATA +12 -12
- {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.3.dist-info}/RECORD +56 -55
- {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.3.dist-info}/WHEEL +1 -1
- tests/models/cycle/product/test_revenue.py +0 -3
- tests/models/cycle/test_cycleDuration.py +1 -1
- tests/models/ipcc2019/test_organicCarbonPerHa.py +9 -20
- tests/models/ipcc2019/test_organicCarbonPerHa_tier_2_utils.py +0 -8
- tests/models/test_ecoinventV3.py +12 -0
- tests/models/test_ecoinventV3AndEmberClimate.py +5 -72
- {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.3.dist-info}/LICENSE +0 -0
- {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
|
-
[
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
1288
|
-
for year,
|
|
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
|
|
1317
|
+
def _get_carbon_sources(cycle: dict) -> list[CarbonSource]:
|
|
1301
1318
|
"""
|
|
1302
|
-
|
|
1319
|
+
Extract and format the carbon source data from a cycle's inputs and products.
|
|
1303
1320
|
|
|
1304
|
-
Carbon sources can be either a
|
|
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
|
-
|
|
1309
|
-
A
|
|
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
|
|
1331
|
+
A formatted list of `CarbonSource`s.
|
|
1315
1332
|
"""
|
|
1316
|
-
inputs_and_products =
|
|
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
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
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
|
|
1468
|
+
def _calc_carbon_source(node: dict, *_) -> Union[CarbonSource, None]:
|
|
1330
1469
|
"""
|
|
1331
|
-
|
|
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
|
|
1337
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
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
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
14
|
-
|
|
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,
|
|
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
|
-
|
|
197
|
-
) if
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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=
|
|
253
|
-
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
|
|