hestia-earth-models 0.64.8__py3-none-any.whl → 0.64.10__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/cml2001Baseline/abioticResourceDepletionFossilFuels.py +175 -0
- hestia_earth/models/cml2001Baseline/abioticResourceDepletionMineralsAndMetals.py +136 -0
- hestia_earth/models/cycle/siteArea.py +2 -1
- hestia_earth/models/environmentalFootprintV3/soilQualityIndexLandOccupation.py +73 -82
- hestia_earth/models/environmentalFootprintV3/soilQualityIndexLandTransformation.py +102 -116
- hestia_earth/models/environmentalFootprintV3/soilQualityIndexTotalLandUseEffects.py +27 -16
- hestia_earth/models/faostat2018/landTransformationFromCropland100YearAverage.py +3 -2
- hestia_earth/models/faostat2018/landTransformationFromCropland20YearAverage.py +3 -2
- hestia_earth/models/frischknechtEtAl2000/ionisingRadiationKbqU235Eq.py +69 -37
- hestia_earth/models/ipcc2019/aboveGroundBiomass.py +31 -243
- hestia_earth/models/ipcc2019/animal/fatContent.py +38 -0
- hestia_earth/models/ipcc2019/animal/liveweightGain.py +3 -54
- hestia_earth/models/ipcc2019/animal/liveweightPerHead.py +3 -54
- hestia_earth/models/ipcc2019/animal/pregnancyRateTotal.py +38 -0
- hestia_earth/models/ipcc2019/animal/trueProteinContent.py +38 -0
- hestia_earth/models/ipcc2019/animal/utils.py +87 -3
- hestia_earth/models/ipcc2019/animal/weightAtMaturity.py +4 -10
- hestia_earth/models/ipcc2019/belowGroundBiomass.py +529 -0
- hestia_earth/models/ipcc2019/biomass_utils.py +406 -0
- hestia_earth/models/ipcc2019/{co2ToAirAboveGroundBiomassStockChangeLandUseChange.py → co2ToAirAboveGroundBiomassStockChange.py} +19 -7
- hestia_earth/models/ipcc2019/{co2ToAirBelowGroundBiomassStockChangeLandUseChange.py → co2ToAirBelowGroundBiomassStockChange.py} +19 -7
- hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +402 -73
- hestia_earth/models/ipcc2019/{co2ToAirSoilOrganicCarbonStockChangeManagementChange.py → co2ToAirSoilOrganicCarbonStockChange.py} +20 -8
- hestia_earth/models/ipcc2019/organicCarbonPerHa.py +3 -1
- hestia_earth/models/ipcc2019/pastureGrass_utils.py +6 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToFreshwaterEcosystemsFreshwaterEutrophication.py +2 -2
- hestia_earth/models/lcImpactAllEffects100Years/damageToFreshwaterEcosystemsWaterStress.py +2 -2
- hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthParticulateMatterFormation.py +2 -2
- hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthPhotochemicalOzoneFormation.py +2 -2
- hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthWaterStress.py +2 -2
- hestia_earth/models/lcImpactAllEffects100Years/damageToMarineEcosystemsMarineEutrophication.py +2 -2
- hestia_earth/models/lcImpactAllEffects100Years/damageToTerrestrialEcosystemsPhotochemicalOzoneFormation.py +2 -2
- hestia_earth/models/lcImpactAllEffects100Years/damageToTerrestrialEcosystemsTerrestrialAcidification.py +2 -2
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToFreshwaterEcosystemsFreshwaterEutrophication.py +2 -2
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToFreshwaterEcosystemsWaterStress.py +2 -2
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthParticulateMatterFormation.py +2 -2
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthPhotochemicalOzoneFormation.py +2 -2
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthWaterStress.py +2 -2
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToMarineEcosystemsMarineEutrophication.py +2 -2
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToTerrestrialEcosystemsPhotochemicalOzoneFormation.py +2 -2
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToTerrestrialEcosystemsTerrestrialAcidification.py +2 -2
- hestia_earth/models/lcImpactCertainEffects100Years/damageToFreshwaterEcosystemsFreshwaterEutrophication.py +2 -2
- hestia_earth/models/lcImpactCertainEffects100Years/damageToFreshwaterEcosystemsWaterStress.py +2 -2
- hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthParticulateMatterFormation.py +2 -2
- hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthPhotochemicalOzoneFormation.py +2 -2
- hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthWaterStress.py +2 -2
- hestia_earth/models/lcImpactCertainEffects100Years/damageToMarineEcosystemsMarineEutrophication.py +2 -2
- hestia_earth/models/lcImpactCertainEffects100Years/damageToTerrestrialEcosystemsPhotochemicalOzoneFormation.py +2 -2
- hestia_earth/models/lcImpactCertainEffects100Years/damageToTerrestrialEcosystemsTerrestrialAcidification.py +2 -2
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToFreshwaterEcosystemsFreshwaterEutrophication.py +2 -2
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToFreshwaterEcosystemsWaterStress.py +2 -2
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthParticulateMatterFormation.py +2 -2
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthPhotochemicalOzoneFormation.py +2 -2
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthWaterStress.py +2 -2
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToMarineEcosystemsMarineEutrophication.py +2 -2
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToTerrestrialEcosystemsPhotochemicalOzoneFormation.py +2 -2
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToTerrestrialEcosystemsTerrestrialAcidification.py +2 -2
- hestia_earth/models/mocking/build_mock_search.py +44 -0
- hestia_earth/models/mocking/mock_search.py +8 -49
- hestia_earth/models/mocking/search-results.json +3078 -575
- hestia_earth/models/poschEtAl2008/terrestrialAcidificationPotentialAccumulatedExceedance.py +6 -3
- hestia_earth/models/poschEtAl2008/terrestrialEutrophicationPotentialAccumulatedExceedance.py +6 -3
- hestia_earth/models/preload_requests.py +1 -1
- hestia_earth/models/schmidt2007/utils.py +13 -4
- hestia_earth/models/utils/__init__.py +5 -4
- hestia_earth/models/utils/blank_node.py +73 -3
- hestia_earth/models/utils/constant.py +8 -1
- hestia_earth/models/utils/cycle.py +10 -13
- hestia_earth/models/utils/fuel.py +1 -1
- hestia_earth/models/utils/impact_assessment.py +39 -15
- hestia_earth/models/utils/lookup.py +36 -7
- hestia_earth/models/utils/pesticideAI.py +1 -1
- hestia_earth/models/utils/property.py +11 -4
- hestia_earth/models/utils/term.py +15 -8
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.64.8.dist-info → hestia_earth_models-0.64.10.dist-info}/METADATA +2 -2
- {hestia_earth_models-0.64.8.dist-info → hestia_earth_models-0.64.10.dist-info}/RECORD +103 -90
- {hestia_earth_models-0.64.8.dist-info → hestia_earth_models-0.64.10.dist-info}/WHEEL +1 -1
- tests/models/cml2001Baseline/test_abioticResourceDepletionFossilFuels.py +196 -0
- tests/models/cml2001Baseline/test_abioticResourceDepletionMineralsAndMetals.py +124 -0
- tests/models/edip2003/test_ozoneDepletionPotential.py +1 -13
- tests/models/environmentalFootprintV3/test_soilQualityIndexLandOccupation.py +97 -66
- tests/models/environmentalFootprintV3/test_soilQualityIndexLandTransformation.py +136 -74
- tests/models/environmentalFootprintV3/test_soilQualityIndexTotalLandUseEffects.py +15 -10
- tests/models/frischknechtEtAl2000/test_ionisingRadiationKbqU235Eq.py +67 -44
- tests/models/impact_assessment/test_emissions.py +1 -0
- tests/models/ipcc2019/animal/test_fatContent.py +22 -0
- tests/models/ipcc2019/animal/test_liveweightGain.py +4 -2
- tests/models/ipcc2019/animal/test_liveweightPerHead.py +4 -2
- tests/models/ipcc2019/animal/test_pregnancyRateTotal.py +22 -0
- tests/models/ipcc2019/animal/test_trueProteinContent.py +22 -0
- tests/models/ipcc2019/animal/test_weightAtMaturity.py +2 -1
- tests/models/ipcc2019/test_aboveGroundBiomass.py +27 -63
- tests/models/ipcc2019/test_belowGroundBiomass.py +146 -0
- tests/models/ipcc2019/test_biomass_utils.py +115 -0
- tests/models/ipcc2019/{test_co2ToAirAboveGroundBiomassStockChangeLandUseChange.py → test_co2ToAirAboveGroundBiomassStockChange.py} +5 -5
- tests/models/ipcc2019/{test_co2ToAirBelowGroundBiomassStockChangeLandUseChange.py → test_co2ToAirBelowGroundBiomassStockChange.py} +5 -5
- tests/models/ipcc2019/{test_co2ToAirSoilOrganicCarbonStockChangeManagementChange.py → test_co2ToAirSoilOrganicCarbonStockChange.py} +5 -5
- tests/models/ipcc2021/test_gwp100.py +2 -2
- tests/models/poschEtAl2008/test_terrestrialAcidificationPotentialAccumulatedExceedance.py +30 -17
- tests/models/poschEtAl2008/test_terrestrialEutrophicationPotentialAccumulatedExceedance.py +28 -14
- hestia_earth/models/ipcc2019/aboveGroundBiomass_utils.py +0 -180
- tests/models/ipcc2019/test_aboveGroundBiomass_utils.py +0 -92
- {hestia_earth_models-0.64.8.dist-info → hestia_earth_models-0.64.10.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.64.8.dist-info → hestia_earth_models-0.64.10.dist-info}/top_level.txt +0 -0
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
from functools import reduce
|
|
3
|
-
from math import isclose
|
|
4
3
|
from numpy import average, copy, random, vstack
|
|
5
4
|
from numpy.typing import NDArray
|
|
6
|
-
from typing import
|
|
5
|
+
from typing import Optional, Union
|
|
7
6
|
|
|
8
7
|
from hestia_earth.schema import (
|
|
9
8
|
MeasurementMethodClassification,
|
|
@@ -12,7 +11,6 @@ from hestia_earth.schema import (
|
|
|
12
11
|
TermTermType
|
|
13
12
|
)
|
|
14
13
|
|
|
15
|
-
from hestia_earth.utils.blank_node import get_node_value
|
|
16
14
|
from hestia_earth.utils.model import filter_list_term_type
|
|
17
15
|
from hestia_earth.utils.tools import non_empty_list
|
|
18
16
|
|
|
@@ -23,15 +21,16 @@ from hestia_earth.models.utils.blank_node import group_nodes_by_year
|
|
|
23
21
|
from hestia_earth.models.utils.descriptive_stats import calc_descriptive_stats
|
|
24
22
|
from hestia_earth.models.utils.ecoClimateZone import EcoClimateZone, get_eco_climate_zone_value
|
|
25
23
|
from hestia_earth.models.utils.measurement import _new_measurement
|
|
26
|
-
from hestia_earth.models.utils.term import get_lookup_value
|
|
27
24
|
|
|
28
25
|
from . import MODEL
|
|
29
|
-
from .
|
|
26
|
+
from .biomass_utils import (
|
|
27
|
+
BiomassCategory, detect_land_cover_change, group_by_biomass_category, group_by_term_id, sample_biomass_equilibrium,
|
|
28
|
+
summarise_land_cover_nodes
|
|
29
|
+
)
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
REQUIREMENTS = {
|
|
33
33
|
"Site": {
|
|
34
|
-
"siteType": ["cropland", "permanent pasture", "forest", "other natural vegetation"],
|
|
35
34
|
"management": [
|
|
36
35
|
{
|
|
37
36
|
"@type": "Management",
|
|
@@ -49,7 +48,10 @@ REQUIREMENTS = {
|
|
|
49
48
|
"value": ["1", "2", "3", "4", "7", "8", "9", "10", "11", "12"],
|
|
50
49
|
"term.@id": "ecoClimateZone"
|
|
51
50
|
}
|
|
52
|
-
]
|
|
51
|
+
],
|
|
52
|
+
"none": {
|
|
53
|
+
"siteType": ["glass or high accessible cover"]
|
|
54
|
+
}
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
57
|
LOOKUPS = {
|
|
@@ -93,28 +95,13 @@ _METHOD_CLASSIFICATION = MeasurementMethodClassification.TIER_1_MODEL.value
|
|
|
93
95
|
_STATS_DEFINITION = MeasurementStatsDefinition.SIMULATED.value
|
|
94
96
|
|
|
95
97
|
_LAND_COVER_TERM_TYPE = TermTermType.LANDCOVER
|
|
96
|
-
_TARGET_LAND_COVER = 100
|
|
97
98
|
|
|
98
99
|
_EQUILIBRIUM_TRANSITION_PERIOD = 20
|
|
99
100
|
_EXCLUDED_ECO_CLIMATE_ZONES = {EcoClimateZone.POLAR_MOIST, EcoClimateZone.POLAR_DRY}
|
|
100
|
-
|
|
101
|
-
SiteSiteType.
|
|
102
|
-
SiteSiteType.FOREST.value,
|
|
103
|
-
SiteSiteType.OTHER_NATURAL_VEGETATION.value,
|
|
104
|
-
SiteSiteType.PERMANENT_PASTURE.value
|
|
101
|
+
_EXCLUDED_SITE_TYPES = {
|
|
102
|
+
SiteSiteType.GLASS_OR_HIGH_ACCESSIBLE_COVER.value
|
|
105
103
|
}
|
|
106
104
|
|
|
107
|
-
_GROUP_LAND_COVER_BY_BIOMASS_CATEGORY = [
|
|
108
|
-
BiomassCategory.ANNUAL_CROPS,
|
|
109
|
-
BiomassCategory.GRASSLAND,
|
|
110
|
-
BiomassCategory.OTHER,
|
|
111
|
-
BiomassCategory.SHORT_ROTATION_COPPICE
|
|
112
|
-
]
|
|
113
|
-
"""
|
|
114
|
-
Terms associated with these biomass categories can be grouped together when summarising land cover coverage in
|
|
115
|
-
`_group_by_term_id`.
|
|
116
|
-
"""
|
|
117
|
-
|
|
118
105
|
|
|
119
106
|
class _InventoryKey(Enum):
|
|
120
107
|
"""
|
|
@@ -171,7 +158,7 @@ def _should_run(site: dict) -> tuple[bool, dict, dict]:
|
|
|
171
158
|
|
|
172
159
|
land_cover = filter_list_term_type(site.get("management", []), _LAND_COVER_TERM_TYPE)
|
|
173
160
|
|
|
174
|
-
has_valid_site_type = site_type in
|
|
161
|
+
has_valid_site_type = site_type not in _EXCLUDED_SITE_TYPES
|
|
175
162
|
has_valid_eco_climate_zone = all([
|
|
176
163
|
eco_climate_zone,
|
|
177
164
|
eco_climate_zone not in _EXCLUDED_ECO_CLIMATE_ZONES
|
|
@@ -267,12 +254,12 @@ def _compile_inventory(land_cover_nodes: list[dict]) -> dict:
|
|
|
267
254
|
prev_year, current_year = year_pair
|
|
268
255
|
land_cover_nodes = land_cover_grouped.get(current_year, {})
|
|
269
256
|
|
|
270
|
-
biomass_category_summary =
|
|
271
|
-
land_cover_summary =
|
|
257
|
+
biomass_category_summary = summarise_land_cover_nodes(land_cover_nodes, group_by_biomass_category)
|
|
258
|
+
land_cover_summary = summarise_land_cover_nodes(land_cover_nodes, group_by_term_id)
|
|
272
259
|
|
|
273
260
|
prev_land_cover_summary = inventory.get(prev_year, {}).get(_InventoryKey.LAND_COVER_SUMMARY, {})
|
|
274
261
|
|
|
275
|
-
is_lcc_event =
|
|
262
|
+
is_lcc_event = detect_land_cover_change(land_cover_summary, prev_land_cover_summary)
|
|
276
263
|
|
|
277
264
|
time_delta = current_year - prev_year
|
|
278
265
|
prev_years_since_lcc_event = inventory.get(prev_year, {}).get(_InventoryKey.YEARS_SINCE_LCC_EVENT, 0)
|
|
@@ -295,11 +282,11 @@ def _compile_inventory(land_cover_nodes: list[dict]) -> dict:
|
|
|
295
282
|
|
|
296
283
|
initial = {
|
|
297
284
|
start_year: {
|
|
298
|
-
_InventoryKey.BIOMASS_CATEGORY_SUMMARY:
|
|
299
|
-
initial_land_cover_nodes,
|
|
285
|
+
_InventoryKey.BIOMASS_CATEGORY_SUMMARY: summarise_land_cover_nodes(
|
|
286
|
+
initial_land_cover_nodes, group_by_biomass_category
|
|
300
287
|
),
|
|
301
|
-
_InventoryKey.LAND_COVER_SUMMARY:
|
|
302
|
-
initial_land_cover_nodes,
|
|
288
|
+
_InventoryKey.LAND_COVER_SUMMARY: summarise_land_cover_nodes(
|
|
289
|
+
initial_land_cover_nodes, group_by_term_id
|
|
303
290
|
),
|
|
304
291
|
_InventoryKey.LAND_COVER_CHANGE_EVENT: False,
|
|
305
292
|
_InventoryKey.YEARS_SINCE_LCC_EVENT: _EQUILIBRIUM_TRANSITION_PERIOD,
|
|
@@ -314,216 +301,6 @@ def _compile_inventory(land_cover_nodes: list[dict]) -> dict:
|
|
|
314
301
|
)
|
|
315
302
|
|
|
316
303
|
|
|
317
|
-
def _group_by_biomass_category(result: dict[BiomassCategory, float], node: dict) -> dict[BiomassCategory, float]:
|
|
318
|
-
"""
|
|
319
|
-
Reducer function for `_group_land_cover_nodes_by` that groups and sums node value by their associated
|
|
320
|
-
`BiomassCategory`.
|
|
321
|
-
|
|
322
|
-
Parameters
|
|
323
|
-
----------
|
|
324
|
-
result : dict
|
|
325
|
-
A dict with the shape `{category (BiomassCategory): sum_value (float), ...categories}`.
|
|
326
|
-
node : dict
|
|
327
|
-
A HESTIA `Management` node with `term.termType` = `landCover`.
|
|
328
|
-
|
|
329
|
-
Returns
|
|
330
|
-
-------
|
|
331
|
-
result : dict
|
|
332
|
-
A dict with the shape `{category (BiomassCategory): sum_value (float), ...categories}`.
|
|
333
|
-
"""
|
|
334
|
-
biomass_category = _retrieve_biomass_category(node)
|
|
335
|
-
value = get_node_value(node)
|
|
336
|
-
|
|
337
|
-
update_dict = {biomass_category: result.get(biomass_category, 0) + value}
|
|
338
|
-
|
|
339
|
-
should_run = biomass_category and value
|
|
340
|
-
return result | update_dict if should_run else result
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
def _group_by_term_id(
|
|
344
|
-
result: dict[Union[str, BiomassCategory], float], node: dict
|
|
345
|
-
) -> dict[Union[str, BiomassCategory], float]:
|
|
346
|
-
"""
|
|
347
|
-
Reducer function for `_group_land_cover_nodes_by` that groups and sums node value by their `term.@id` if a the land
|
|
348
|
-
cover is a woody plant, else by their associated `BiomassCategory`
|
|
349
|
-
|
|
350
|
-
Land cover events can be triggered by changes in land cover within the same `BiomassCategory` (e.g., `peachTree` to
|
|
351
|
-
`appleTree`) due to the requirement to clear the previous woody biomass to establish the new land cover.
|
|
352
|
-
|
|
353
|
-
Some land covers (e.g., land covers associated with the `BiomassCategory` = `Annual crops`, `Grassland`, `Other` or
|
|
354
|
-
`Short rotation coppice`) are exempt from this rule due to the Tier 1 assumptions that biomass does not accumulate
|
|
355
|
-
within the category or the maturity cycle of the land cover is significantly shorter than the amortisation period of
|
|
356
|
-
20 years.
|
|
357
|
-
|
|
358
|
-
Parameters
|
|
359
|
-
----------
|
|
360
|
-
result : dict
|
|
361
|
-
A dict with the shape `{category (str | BiomassCategory): sum_value (float), ...categories}`.
|
|
362
|
-
node : dict
|
|
363
|
-
A HESTIA `Management` node with `term.termType` = `landCover`.
|
|
364
|
-
|
|
365
|
-
Returns
|
|
366
|
-
-------
|
|
367
|
-
result : dict
|
|
368
|
-
A dict with the shape `{category (str | BiomassCategory): sum_value (float), ...categories}`.
|
|
369
|
-
"""
|
|
370
|
-
term_id = node.get("term", {}).get("@id")
|
|
371
|
-
biomass_category = _retrieve_biomass_category(node)
|
|
372
|
-
value = get_node_value(node)
|
|
373
|
-
|
|
374
|
-
key = biomass_category if biomass_category in _GROUP_LAND_COVER_BY_BIOMASS_CATEGORY else term_id
|
|
375
|
-
|
|
376
|
-
update_dict = {key: result.get(key, 0) + value}
|
|
377
|
-
|
|
378
|
-
should_run = biomass_category and value
|
|
379
|
-
return result | update_dict if should_run else result
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
def _retrieve_biomass_category(node: dict) -> Optional[BiomassCategory]:
|
|
383
|
-
"""
|
|
384
|
-
Retrieve the `BiomassCategory` associated with a land cover using the `BIOMASS_CATEGORY` lookup.
|
|
385
|
-
|
|
386
|
-
If lookup value is missing, return `None`.
|
|
387
|
-
|
|
388
|
-
Parameters
|
|
389
|
-
----------
|
|
390
|
-
node : dict
|
|
391
|
-
A valid `Management` node with `term.termType` = `landCover`.
|
|
392
|
-
|
|
393
|
-
Returns
|
|
394
|
-
-------
|
|
395
|
-
BiomassCategory | None
|
|
396
|
-
The associated `BiomassCategory` or `None`
|
|
397
|
-
"""
|
|
398
|
-
LOOKUP = LOOKUPS["landCover"]
|
|
399
|
-
term = node.get("term", {})
|
|
400
|
-
lookup_value = get_lookup_value(term, LOOKUP)
|
|
401
|
-
|
|
402
|
-
return assign_biomass_category(lookup_value) if lookup_value else None
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
def _summarise_land_cover_nodes(
|
|
406
|
-
land_cover_nodes: list[dict],
|
|
407
|
-
group_by_func: Callable[[dict, dict], dict] = _group_by_biomass_category
|
|
408
|
-
) -> dict[Union[str, BiomassCategory], float]:
|
|
409
|
-
"""
|
|
410
|
-
Group land cover nodes using `group_by_func`.
|
|
411
|
-
|
|
412
|
-
Parameters
|
|
413
|
-
----------
|
|
414
|
-
land_cover_nodes : list[dict]
|
|
415
|
-
A list of HESTIA `Management` nodes with `term.termType` = `landCover`.
|
|
416
|
-
|
|
417
|
-
Returns
|
|
418
|
-
-------
|
|
419
|
-
result : dict
|
|
420
|
-
A dict with the shape `{category (str | BiomassCategory): sum_value (float), ...categories}`.
|
|
421
|
-
"""
|
|
422
|
-
category_cover = reduce(group_by_func, land_cover_nodes, dict())
|
|
423
|
-
return _rescale_category_cover(category_cover)
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
def _rescale_category_cover(
|
|
427
|
-
category_cover: dict[Union[BiomassCategory, str], float]
|
|
428
|
-
) -> dict[Union[BiomassCategory, str], float]:
|
|
429
|
-
"""
|
|
430
|
-
Enforce a land cover coverage of 100%.
|
|
431
|
-
|
|
432
|
-
If input coverage is less than 100%, fill the remainder with `BiomassCategory.OTHER`. If the input coverage is
|
|
433
|
-
greater than 100%, proportionally downscale all categories.
|
|
434
|
-
|
|
435
|
-
Parameters
|
|
436
|
-
----------
|
|
437
|
-
category_cover : dict[BiomassCategory | str, float]
|
|
438
|
-
The input category cover dict.
|
|
439
|
-
|
|
440
|
-
Returns
|
|
441
|
-
-------
|
|
442
|
-
result : dict[BiomassCategory | str, float]
|
|
443
|
-
The rescaled category cover dict.
|
|
444
|
-
"""
|
|
445
|
-
total_cover = sum(category_cover.values())
|
|
446
|
-
return (
|
|
447
|
-
_fill_category_cover(category_cover) if total_cover < _TARGET_LAND_COVER
|
|
448
|
-
else _squash_category_cover(category_cover) if total_cover > _TARGET_LAND_COVER
|
|
449
|
-
else category_cover
|
|
450
|
-
)
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
def _fill_category_cover(
|
|
454
|
-
category_cover: dict[Union[BiomassCategory, str], float]
|
|
455
|
-
) -> dict[Union[BiomassCategory, str], float]:
|
|
456
|
-
"""
|
|
457
|
-
Fill the land cover coverage with `BiomassCategory.OTHER` to enforce a total coverage of 100%.
|
|
458
|
-
|
|
459
|
-
Parameters
|
|
460
|
-
----------
|
|
461
|
-
category_cover : dict[BiomassCategory | str, float]
|
|
462
|
-
The input category cover dict.
|
|
463
|
-
|
|
464
|
-
Returns
|
|
465
|
-
-------
|
|
466
|
-
result : dict[BiomassCategory | str, float]
|
|
467
|
-
The rescaled category cover dict.
|
|
468
|
-
"""
|
|
469
|
-
total_cover = sum(category_cover.values())
|
|
470
|
-
update_dict = {
|
|
471
|
-
BiomassCategory.OTHER: category_cover.get(BiomassCategory.OTHER, 0) + (_TARGET_LAND_COVER - total_cover)
|
|
472
|
-
}
|
|
473
|
-
return category_cover | update_dict
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
def _squash_category_cover(
|
|
477
|
-
category_cover: dict[Union[BiomassCategory, str], float]
|
|
478
|
-
) -> dict[Union[BiomassCategory, str], float]:
|
|
479
|
-
"""
|
|
480
|
-
Proportionally shrink all land cover categories to enforce a total coverage of 100%.
|
|
481
|
-
|
|
482
|
-
Parameters
|
|
483
|
-
----------
|
|
484
|
-
category_cover : dict[BiomassCategory | str, float]
|
|
485
|
-
The input category cover dict.
|
|
486
|
-
|
|
487
|
-
Returns
|
|
488
|
-
-------
|
|
489
|
-
result : dict[BiomassCategory | str, float]
|
|
490
|
-
The rescaled category cover dict.
|
|
491
|
-
"""
|
|
492
|
-
total_cover = sum(category_cover.values())
|
|
493
|
-
return {
|
|
494
|
-
category: (cover / total_cover) * _TARGET_LAND_COVER
|
|
495
|
-
for category, cover in category_cover.items()
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
def _is_lcc_event(
|
|
500
|
-
a: dict[Union[BiomassCategory, str], float],
|
|
501
|
-
b: dict[Union[BiomassCategory, str], float]
|
|
502
|
-
) -> bool:
|
|
503
|
-
"""
|
|
504
|
-
Land cover values (% area) are compared with an absolute tolerance of 0.0001, which is equivalent to 1 m2 per
|
|
505
|
-
hectare.
|
|
506
|
-
|
|
507
|
-
Parameters
|
|
508
|
-
----------
|
|
509
|
-
a : dict[BiomassCategory | str, float]
|
|
510
|
-
The first land-cover summary dict.
|
|
511
|
-
b : dict[BiomassCategory | str, float]
|
|
512
|
-
The second land-cover summary dict.
|
|
513
|
-
|
|
514
|
-
Returns
|
|
515
|
-
-------
|
|
516
|
-
bool
|
|
517
|
-
Whether a land-cover change event has occured.
|
|
518
|
-
"""
|
|
519
|
-
keys_match = sorted(str(key) for key in b.keys()) == sorted(str(key) for key in a.keys())
|
|
520
|
-
values_close = all(
|
|
521
|
-
isclose(b.get(key), a.get(key, -999), abs_tol=0.0001) for key in b.keys()
|
|
522
|
-
)
|
|
523
|
-
|
|
524
|
-
return not all([keys_match, values_close])
|
|
525
|
-
|
|
526
|
-
|
|
527
304
|
def _format_inventory(inventory: dict) -> str:
|
|
528
305
|
"""
|
|
529
306
|
Format the SOC inventory for logging as a table. Rows represent inventory years, columns represent soc stock change
|
|
@@ -665,7 +442,7 @@ def _run(
|
|
|
665
442
|
timestamps = list(inventory.keys())
|
|
666
443
|
|
|
667
444
|
factor_cache = {
|
|
668
|
-
category: sample_biomass_equilibrium(iterations, category, eco_climate_zone, rng)
|
|
445
|
+
category: sample_biomass_equilibrium(iterations, category, eco_climate_zone, _build_col_name, seed=rng)
|
|
669
446
|
for category in unique_biomass_categories
|
|
670
447
|
}
|
|
671
448
|
|
|
@@ -710,6 +487,17 @@ def _run(
|
|
|
710
487
|
return [_measurement(timestamps, **descriptive_stats)]
|
|
711
488
|
|
|
712
489
|
|
|
490
|
+
def _build_col_name(biomass_category: BiomassCategory) -> str:
|
|
491
|
+
"""
|
|
492
|
+
Get the column name for the `ecoClimateZone-lookup.csv` for a specific biomass category equilibrium.
|
|
493
|
+
"""
|
|
494
|
+
COL_NAME_ROOT = "AG_BIOMASS_EQUILIBRIUM_KG_C_HECTARE_"
|
|
495
|
+
return (
|
|
496
|
+
f"{COL_NAME_ROOT}{biomass_category.name}" if isinstance(biomass_category, BiomassCategory)
|
|
497
|
+
else f"{COL_NAME_ROOT}OTHER"
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
|
|
713
501
|
def _measurement(
|
|
714
502
|
timestamps: list[int],
|
|
715
503
|
value: list[float],
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from .utils import should_run_by_productivity_lookup, run_animal_by_productivity
|
|
2
|
+
|
|
3
|
+
REQUIREMENTS = {
|
|
4
|
+
"Cycle": {
|
|
5
|
+
"site": {
|
|
6
|
+
"@type": "Site",
|
|
7
|
+
"country": {"@type": "Term", "termType": "region"}
|
|
8
|
+
},
|
|
9
|
+
"animals": [{
|
|
10
|
+
"@type": "Animal",
|
|
11
|
+
"term.termType": "liveAnimal",
|
|
12
|
+
"practices": {
|
|
13
|
+
"@type": "Practice",
|
|
14
|
+
"term.termType": "animalManagement"
|
|
15
|
+
}
|
|
16
|
+
}]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
LOOKUPS = {
|
|
20
|
+
"region-liveAnimal-milkFatContent": "fat content",
|
|
21
|
+
"liveAnimal": "milkYieldPracticeTermIds"
|
|
22
|
+
}
|
|
23
|
+
RETURNS = {
|
|
24
|
+
"Animal": [{
|
|
25
|
+
"practices": [{
|
|
26
|
+
"@type": "Practice",
|
|
27
|
+
"value": ""
|
|
28
|
+
}]
|
|
29
|
+
}]
|
|
30
|
+
}
|
|
31
|
+
TERM_ID = 'fatContent'
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def run(cycle: dict):
|
|
35
|
+
animals = should_run_by_productivity_lookup(
|
|
36
|
+
TERM_ID, cycle, list(LOOKUPS.keys())[0], practice_id=LOOKUPS['liveAnimal']
|
|
37
|
+
)
|
|
38
|
+
return list(map(run_animal_by_productivity(TERM_ID, include_practice=True), animals))
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
from
|
|
2
|
-
from hestia_earth.utils.model import filter_list_term_type
|
|
3
|
-
|
|
4
|
-
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
5
|
-
from hestia_earth.models.utils.blank_node import merge_blank_nodes
|
|
6
|
-
from hestia_earth.models.utils.property import _new_property, node_has_no_property
|
|
7
|
-
from .utils import productivity_lookup_value
|
|
8
|
-
from .. import MODEL
|
|
1
|
+
from .utils import should_run_by_productivity_lookup, run_animal_by_productivity
|
|
9
2
|
|
|
10
3
|
REQUIREMENTS = {
|
|
11
4
|
"Cycle": {
|
|
@@ -40,50 +33,6 @@ RETURNS = {
|
|
|
40
33
|
TERM_ID = 'liveweightGain'
|
|
41
34
|
|
|
42
35
|
|
|
43
|
-
def _property(value: float):
|
|
44
|
-
prop = _new_property(TERM_ID, MODEL)
|
|
45
|
-
prop['value'] = value
|
|
46
|
-
return prop
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def _run_animal(data: dict):
|
|
50
|
-
animal = data.get('animal')
|
|
51
|
-
value = data.get('value')
|
|
52
|
-
return animal | {
|
|
53
|
-
'properties': merge_blank_nodes(animal.get('properties', []), [_property(value)])
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def _should_run(cycle: dict):
|
|
58
|
-
country = cycle.get('site', {}).get('country', {})
|
|
59
|
-
country_id = country.get('@id')
|
|
60
|
-
live_animals = filter_list_term_type(cycle.get('animals', []), TermTermType.LIVEANIMAL)
|
|
61
|
-
live_animals = list(filter(node_has_no_property(TERM_ID), live_animals))
|
|
62
|
-
live_animals_with_value = [{
|
|
63
|
-
'animal': animal,
|
|
64
|
-
'value': productivity_lookup_value(TERM_ID, list(LOOKUPS.keys())[0], country, animal)
|
|
65
|
-
} for animal in live_animals]
|
|
66
|
-
|
|
67
|
-
def _should_run_animal(value: dict):
|
|
68
|
-
animal = value.get('animal')
|
|
69
|
-
lookup_value = value.get('value')
|
|
70
|
-
term_id = animal.get('term').get('@id')
|
|
71
|
-
|
|
72
|
-
logRequirements(cycle, model=MODEL, term=term_id, property=TERM_ID,
|
|
73
|
-
country_id=country_id,
|
|
74
|
-
liveweightGain=lookup_value)
|
|
75
|
-
|
|
76
|
-
should_run = all([
|
|
77
|
-
country_id,
|
|
78
|
-
lookup_value is not None
|
|
79
|
-
])
|
|
80
|
-
logShouldRun(cycle, MODEL, term_id, should_run, property=TERM_ID)
|
|
81
|
-
|
|
82
|
-
return should_run
|
|
83
|
-
|
|
84
|
-
return list(filter(_should_run_animal, live_animals_with_value))
|
|
85
|
-
|
|
86
|
-
|
|
87
36
|
def run(cycle: dict):
|
|
88
|
-
animals =
|
|
89
|
-
return list(map(
|
|
37
|
+
animals = should_run_by_productivity_lookup(TERM_ID, cycle, list(LOOKUPS.keys())[0])
|
|
38
|
+
return list(map(run_animal_by_productivity(TERM_ID), animals))
|
|
@@ -1,11 +1,4 @@
|
|
|
1
|
-
from
|
|
2
|
-
from hestia_earth.utils.model import filter_list_term_type
|
|
3
|
-
|
|
4
|
-
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
5
|
-
from hestia_earth.models.utils.blank_node import merge_blank_nodes
|
|
6
|
-
from hestia_earth.models.utils.property import _new_property, node_has_no_property
|
|
7
|
-
from .utils import productivity_lookup_value
|
|
8
|
-
from .. import MODEL
|
|
1
|
+
from .utils import should_run_by_productivity_lookup, run_animal_by_productivity
|
|
9
2
|
|
|
10
3
|
REQUIREMENTS = {
|
|
11
4
|
"Cycle": {
|
|
@@ -40,50 +33,6 @@ RETURNS = {
|
|
|
40
33
|
TERM_ID = 'liveweightPerHead'
|
|
41
34
|
|
|
42
35
|
|
|
43
|
-
def _property(value: float):
|
|
44
|
-
prop = _new_property(TERM_ID, MODEL)
|
|
45
|
-
prop['value'] = value
|
|
46
|
-
return prop
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
def _run_animal(data: dict):
|
|
50
|
-
animal = data.get('animal')
|
|
51
|
-
value = data.get('value')
|
|
52
|
-
return animal | {
|
|
53
|
-
'properties': merge_blank_nodes(animal.get('properties', []), [_property(value)])
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
def _should_run(cycle: dict):
|
|
58
|
-
country = cycle.get('site', {}).get('country', {})
|
|
59
|
-
country_id = country.get('@id')
|
|
60
|
-
live_animals = filter_list_term_type(cycle.get('animals', []), TermTermType.LIVEANIMAL)
|
|
61
|
-
live_animals = list(filter(node_has_no_property(TERM_ID), live_animals))
|
|
62
|
-
live_animals_with_value = [{
|
|
63
|
-
'animal': animal,
|
|
64
|
-
'value': productivity_lookup_value(TERM_ID, list(LOOKUPS.keys())[0], country, animal)
|
|
65
|
-
} for animal in live_animals]
|
|
66
|
-
|
|
67
|
-
def _should_run_animal(value: dict):
|
|
68
|
-
animal = value.get('animal')
|
|
69
|
-
lookup_value = value.get('value')
|
|
70
|
-
term_id = animal.get('term').get('@id')
|
|
71
|
-
|
|
72
|
-
logRequirements(cycle, model=MODEL, term=term_id, property=TERM_ID,
|
|
73
|
-
country_id=country_id,
|
|
74
|
-
liveweightPerHead=lookup_value)
|
|
75
|
-
|
|
76
|
-
should_run = all([
|
|
77
|
-
country_id,
|
|
78
|
-
lookup_value is not None
|
|
79
|
-
])
|
|
80
|
-
logShouldRun(cycle, MODEL, term_id, should_run, property=TERM_ID)
|
|
81
|
-
|
|
82
|
-
return should_run
|
|
83
|
-
|
|
84
|
-
return list(filter(_should_run_animal, live_animals_with_value))
|
|
85
|
-
|
|
86
|
-
|
|
87
36
|
def run(cycle: dict):
|
|
88
|
-
animals =
|
|
89
|
-
return list(map(
|
|
37
|
+
animals = should_run_by_productivity_lookup(TERM_ID, cycle, list(LOOKUPS.keys())[0])
|
|
38
|
+
return list(map(run_animal_by_productivity(TERM_ID), animals))
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from .utils import should_run_by_productivity_lookup, run_animal_by_productivity
|
|
2
|
+
|
|
3
|
+
REQUIREMENTS = {
|
|
4
|
+
"Cycle": {
|
|
5
|
+
"site": {
|
|
6
|
+
"@type": "Site",
|
|
7
|
+
"country": {"@type": "Term", "termType": "region"}
|
|
8
|
+
},
|
|
9
|
+
"animals": [{
|
|
10
|
+
"@type": "Animal",
|
|
11
|
+
"term.termType": "liveAnimal",
|
|
12
|
+
"none": {
|
|
13
|
+
"properties": [{
|
|
14
|
+
"@type": "Property",
|
|
15
|
+
"value": "",
|
|
16
|
+
"term.@id": "pregnancyRateTotal"
|
|
17
|
+
}]
|
|
18
|
+
}
|
|
19
|
+
}]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
LOOKUPS = {
|
|
23
|
+
"region-liveAnimal-pregnancyRateTotal": "pregnancy rate"
|
|
24
|
+
}
|
|
25
|
+
RETURNS = {
|
|
26
|
+
"Animal": [{
|
|
27
|
+
"properties": [{
|
|
28
|
+
"@type": "Property",
|
|
29
|
+
"value": ""
|
|
30
|
+
}]
|
|
31
|
+
}]
|
|
32
|
+
}
|
|
33
|
+
TERM_ID = 'pregnancyRateTotal'
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def run(cycle: dict):
|
|
37
|
+
animals = should_run_by_productivity_lookup(TERM_ID, cycle, list(LOOKUPS.keys())[0])
|
|
38
|
+
return list(map(run_animal_by_productivity(TERM_ID), animals))
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from .utils import should_run_by_productivity_lookup, run_animal_by_productivity
|
|
2
|
+
|
|
3
|
+
REQUIREMENTS = {
|
|
4
|
+
"Cycle": {
|
|
5
|
+
"site": {
|
|
6
|
+
"@type": "Site",
|
|
7
|
+
"country": {"@type": "Term", "termType": "region"}
|
|
8
|
+
},
|
|
9
|
+
"animals": [{
|
|
10
|
+
"@type": "Animal",
|
|
11
|
+
"term.termType": "liveAnimal",
|
|
12
|
+
"practices": {
|
|
13
|
+
"@type": "Practice",
|
|
14
|
+
"term.termType": "animalManagement"
|
|
15
|
+
}
|
|
16
|
+
}]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
LOOKUPS = {
|
|
20
|
+
"region-liveAnimal-milkTrueProteinContent": "protein content",
|
|
21
|
+
"liveAnimal": "milkYieldPracticeTermIds"
|
|
22
|
+
}
|
|
23
|
+
RETURNS = {
|
|
24
|
+
"Animal": [{
|
|
25
|
+
"practices": [{
|
|
26
|
+
"@type": "Practice",
|
|
27
|
+
"value": ""
|
|
28
|
+
}]
|
|
29
|
+
}]
|
|
30
|
+
}
|
|
31
|
+
TERM_ID = 'trueProteinContent'
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def run(cycle: dict):
|
|
35
|
+
animals = should_run_by_productivity_lookup(
|
|
36
|
+
TERM_ID, cycle, list(LOOKUPS.keys())[0], practice_id=LOOKUPS['liveAnimal']
|
|
37
|
+
)
|
|
38
|
+
return list(map(run_animal_by_productivity(TERM_ID, include_practice=True), animals))
|