hestia-earth-models 0.59.3__py3-none-any.whl → 0.59.5__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/cycle/liveAnimal.py +3 -0
- hestia_earth/models/cycle/milkYield.py +1 -1
- hestia_earth/models/cycle/utils.py +1 -1
- hestia_earth/models/geospatialDatabase/potentialEvapotranspirationLongTermAnnualMean.py +2 -2
- hestia_earth/models/geospatialDatabase/potentialEvapotranspirationMonthly.py +99 -0
- hestia_earth/models/geospatialDatabase/precipitationMonthly.py +100 -0
- hestia_earth/models/geospatialDatabase/temperatureAnnual.py +2 -6
- hestia_earth/models/geospatialDatabase/temperatureLongTermAnnualMean.py +2 -3
- hestia_earth/models/geospatialDatabase/temperatureMonthly.py +98 -0
- hestia_earth/models/geospatialDatabase/utils.py +13 -1
- hestia_earth/models/ipcc2019/organicCarbonPerHa.py +72 -135
- hestia_earth/models/linkedImpactAssessment/__init__.py +78 -43
- hestia_earth/models/mocking/search-results.json +8 -47
- hestia_earth/models/schmidt2007/n2OToAirWasteTreatmentDirect.py +58 -0
- hestia_earth/models/schmidt2007/nh3ToAirWasteTreatment.py +58 -0
- hestia_earth/models/site/management.py +106 -13
- hestia_earth/models/site/pre_checks/cache_geospatialDatabase.py +27 -7
- hestia_earth/models/site/soilMeasurement.py +9 -9
- hestia_earth/models/site/utils.py +2 -6
- hestia_earth/models/utils/__init__.py +9 -0
- hestia_earth/models/utils/blank_node.py +3 -3
- hestia_earth/models/utils/site.py +8 -5
- hestia_earth/models/utils/term.py +0 -23
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.59.3.dist-info → hestia_earth_models-0.59.5.dist-info}/METADATA +2 -2
- {hestia_earth_models-0.59.3.dist-info → hestia_earth_models-0.59.5.dist-info}/RECORD +39 -29
- tests/models/geospatialDatabase/test_potentialEvapotranspirationMonthly.py +20 -0
- tests/models/geospatialDatabase/test_precipitationMonthly.py +20 -0
- tests/models/geospatialDatabase/test_temperatureMonthly.py +20 -0
- tests/models/ipcc2019/test_organicCarbonPerHa.py +8 -39
- tests/models/schmidt2007/test_n2OToAirWasteTreatmentDirect.py +45 -0
- tests/models/schmidt2007/test_nh3ToAirWasteTreatment.py +45 -0
- tests/models/site/test_management.py +37 -16
- tests/models/site/test_soilMeasurement.py +40 -21
- tests/models/utils/test_site.py +1 -1
- tests/models/utils/test_term.py +1 -8
- {hestia_earth_models-0.59.3.dist-info → hestia_earth_models-0.59.5.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.59.3.dist-info → hestia_earth_models-0.59.5.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.59.3.dist-info → hestia_earth_models-0.59.5.dist-info}/top_level.txt +0 -0
|
@@ -18,15 +18,12 @@ from typing import (
|
|
|
18
18
|
from hestia_earth.schema import (
|
|
19
19
|
CycleFunctionalUnit,
|
|
20
20
|
MeasurementMethodClassification,
|
|
21
|
-
SchemaType,
|
|
22
21
|
SiteSiteType,
|
|
23
22
|
TermTermType,
|
|
24
23
|
)
|
|
25
|
-
from hestia_earth.utils.api import find_related
|
|
26
24
|
from hestia_earth.utils.model import find_term_match, filter_list_term_type
|
|
27
25
|
from hestia_earth.utils.tools import flatten, list_sum, non_empty_list
|
|
28
26
|
|
|
29
|
-
from hestia_earth.models.utils import _load_calculated_node
|
|
30
27
|
from hestia_earth.models.log import log_as_table, logRequirements, logShouldRun
|
|
31
28
|
from hestia_earth.models.utils.blank_node import (
|
|
32
29
|
cumulative_nodes_match,
|
|
@@ -49,12 +46,11 @@ from hestia_earth.models.utils.term import (
|
|
|
49
46
|
get_cover_crop_property_terms,
|
|
50
47
|
get_crop_residue_incorporated_or_left_on_field_terms,
|
|
51
48
|
get_irrigated_terms,
|
|
52
|
-
get_long_fallow_land_cover_terms,
|
|
53
49
|
get_residue_removed_or_burnt_terms,
|
|
54
50
|
get_upland_rice_crop_terms,
|
|
55
51
|
get_upland_rice_land_cover_terms
|
|
56
52
|
)
|
|
57
|
-
|
|
53
|
+
from hestia_earth.models.utils.site import related_cycles
|
|
58
54
|
from .utils import check_consecutive
|
|
59
55
|
from . import MODEL
|
|
60
56
|
|
|
@@ -613,25 +609,26 @@ A dictionary mapping IPCC soil categories to corresponding soil type and USDA so
|
|
|
613
609
|
`"IPCC_SOIL_CATEGORY"` column.
|
|
614
610
|
"""
|
|
615
611
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
IpccLandUseCategory.ANNUAL_CROPS_WET: SiteSiteType.CROPLAND.value,
|
|
621
|
-
IpccLandUseCategory.ANNUAL_CROPS: SiteSiteType.CROPLAND.value,
|
|
622
|
-
IpccLandUseCategory.SET_ASIDE: SiteSiteType.CROPLAND.value,
|
|
623
|
-
IpccLandUseCategory.FOREST: SiteSiteType.FOREST.value,
|
|
624
|
-
IpccLandUseCategory.NATIVE: SiteSiteType.OTHER_NATURAL_VEGETATION.value
|
|
612
|
+
SITE_TYPE_TO_IPCC_LAND_USE_CATEGORY = {
|
|
613
|
+
SiteSiteType.PERMANENT_PASTURE.value: IpccLandUseCategory.GRASSLAND,
|
|
614
|
+
SiteSiteType.FOREST.value: IpccLandUseCategory.FOREST,
|
|
615
|
+
SiteSiteType.OTHER_NATURAL_VEGETATION.value: IpccLandUseCategory.NATIVE
|
|
625
616
|
}
|
|
626
617
|
"""
|
|
627
|
-
A dictionary mapping IPCC land use categories
|
|
618
|
+
A dictionary mapping site types to corresponding IPCC land use categories.
|
|
628
619
|
"""
|
|
629
620
|
|
|
630
621
|
IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE = {
|
|
622
|
+
IpccLandUseCategory.GRASSLAND: "Grassland",
|
|
631
623
|
IpccLandUseCategory.PERENNIAL_CROPS: "Perennial crops",
|
|
632
624
|
IpccLandUseCategory.PADDY_RICE_CULTIVATION: "Paddy rice cultivation",
|
|
633
625
|
IpccLandUseCategory.ANNUAL_CROPS_WET: "Annual crops",
|
|
634
|
-
IpccLandUseCategory.ANNUAL_CROPS: "Annual crops"
|
|
626
|
+
IpccLandUseCategory.ANNUAL_CROPS: "Annual crops",
|
|
627
|
+
IpccLandUseCategory.SET_ASIDE: [
|
|
628
|
+
"Annual crops", "Paddy rice cultivation", "Perennial crops", "Set aside"
|
|
629
|
+
],
|
|
630
|
+
IpccLandUseCategory.FOREST: "Forest",
|
|
631
|
+
IpccLandUseCategory.NATIVE: "Native"
|
|
635
632
|
}
|
|
636
633
|
"""
|
|
637
634
|
A dictionary mapping IPCC land use categories to corresponding land cover lookup values in the
|
|
@@ -2528,7 +2525,7 @@ def _has_irrigation(water_regime_nodes: list[dict]) -> bool:
|
|
|
2528
2525
|
|
|
2529
2526
|
Parameters
|
|
2530
2527
|
----------
|
|
2531
|
-
water_regime_nodes :
|
|
2528
|
+
water_regime_nodes : list[dict]
|
|
2532
2529
|
List of water regime nodes to be checked.
|
|
2533
2530
|
|
|
2534
2531
|
Returns
|
|
@@ -2545,13 +2542,13 @@ def _has_irrigation(water_regime_nodes: list[dict]) -> bool:
|
|
|
2545
2542
|
|
|
2546
2543
|
def _has_long_fallow(land_cover_nodes: list[dict]) -> bool:
|
|
2547
2544
|
"""
|
|
2548
|
-
Check if long fallow terms
|
|
2545
|
+
Check if long fallow terms are present in the land cover nodes.
|
|
2549
2546
|
|
|
2550
2547
|
n.b., a super majority of the site area must be under long fallow for it to be classified as set aside.
|
|
2551
2548
|
|
|
2552
2549
|
Parameters
|
|
2553
2550
|
----------
|
|
2554
|
-
land_cover_nodes :
|
|
2551
|
+
land_cover_nodes : list[dict]
|
|
2555
2552
|
List of land cover nodes to be checked.
|
|
2556
2553
|
|
|
2557
2554
|
Returns
|
|
@@ -2559,9 +2556,12 @@ def _has_long_fallow(land_cover_nodes: list[dict]) -> bool:
|
|
|
2559
2556
|
bool
|
|
2560
2557
|
`True` if long fallow is present, `False` otherwise.
|
|
2561
2558
|
"""
|
|
2562
|
-
|
|
2559
|
+
LOOKUP = LOOKUPS["landCover"][0]
|
|
2560
|
+
TARGET_LOOKUP_VALUE = "Set aside"
|
|
2561
|
+
return cumulative_nodes_lookup_match(
|
|
2563
2562
|
land_cover_nodes,
|
|
2564
|
-
|
|
2563
|
+
lookup=LOOKUP,
|
|
2564
|
+
target_lookup_values=TARGET_LOOKUP_VALUE,
|
|
2565
2565
|
cumulative_threshold=SUPER_MAJORITY_AREA_THRESHOLD
|
|
2566
2566
|
) or cumulative_nodes_match(
|
|
2567
2567
|
lambda node: get_node_property(node, LONG_FALLOW_CROP_TERM_ID, False).get("value", 0),
|
|
@@ -2576,7 +2576,7 @@ def _has_upland_rice(land_cover_nodes: list[dict]) -> bool:
|
|
|
2576
2576
|
|
|
2577
2577
|
Parameters
|
|
2578
2578
|
----------
|
|
2579
|
-
land_cover_nodes :
|
|
2579
|
+
land_cover_nodes : list[dict]
|
|
2580
2580
|
List of land cover nodes to be checked.
|
|
2581
2581
|
|
|
2582
2582
|
Returns
|
|
@@ -2592,63 +2592,39 @@ def _has_upland_rice(land_cover_nodes: list[dict]) -> bool:
|
|
|
2592
2592
|
|
|
2593
2593
|
|
|
2594
2594
|
IPCC_LAND_USE_CATEGORY_TO_VALIDATION_KWARGS = {
|
|
2595
|
+
IpccLandUseCategory.ANNUAL_CROPS_WET: {"has_wetland_soils"},
|
|
2595
2596
|
IpccLandUseCategory.SET_ASIDE: {"has_long_fallow"},
|
|
2596
|
-
IpccLandUseCategory.ANNUAL_CROPS_WET: {"has_wetland_soils"}
|
|
2597
2597
|
}
|
|
2598
2598
|
"""
|
|
2599
|
-
Keyword arguments that need to be
|
|
2599
|
+
Keyword arguments that need to be validated in addition to the `landCover` lookup match for specific
|
|
2600
|
+
`IpccLandUseCategory`s.
|
|
2600
2601
|
"""
|
|
2601
2602
|
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
----------
|
|
2609
|
-
key : IpccLandUseCategory
|
|
2610
|
-
The IPCC land use category to check.
|
|
2611
|
-
site_type : str
|
|
2612
|
-
The site type to check.
|
|
2613
|
-
|
|
2614
|
-
Keyword Args
|
|
2615
|
-
------------
|
|
2616
|
-
has_long_fallow : bool
|
|
2617
|
-
Indicates whether long fallow is present on more than 30% of the site.
|
|
2618
|
-
has_wetland_soils : bool
|
|
2619
|
-
Indicates whether wetland soils are present to more than 30% of the site.
|
|
2620
|
-
|
|
2621
|
-
Returns
|
|
2622
|
-
-------
|
|
2623
|
-
bool
|
|
2624
|
-
`True` if the conditions match the specified land use category, `False` otherwise.
|
|
2625
|
-
|
|
2626
|
-
"""
|
|
2627
|
-
target_site_type = IPCC_LAND_USE_CATEGORY_TO_SITE_TYPE.get(key, None)
|
|
2628
|
-
validation_kwargs = IPCC_LAND_USE_CATEGORY_TO_VALIDATION_KWARGS.get(key, set())
|
|
2629
|
-
valid_kwargs = all(v for k, v in kwargs.items() if k in validation_kwargs)
|
|
2630
|
-
return site_type == target_site_type and valid_kwargs
|
|
2603
|
+
IPCC_LAND_USE_CATEGORY_TO_OVERRIDE_KWARGS = {
|
|
2604
|
+
IpccLandUseCategory.PADDY_RICE_CULTIVATION: {"has_irrigated_upland_rice"}
|
|
2605
|
+
}
|
|
2606
|
+
"""
|
|
2607
|
+
Keyword arguments that can override the `landCover` lookup match for specific `IpccLandUseCategory`s.
|
|
2608
|
+
"""
|
|
2631
2609
|
|
|
2632
2610
|
|
|
2633
|
-
def
|
|
2634
|
-
*, key: IpccLandUseCategory, site_type: str, land_cover_nodes: list[dict], **kwargs
|
|
2635
|
-
) -> bool:
|
|
2611
|
+
def _check_ipcc_land_use_category(*, key: IpccLandUseCategory, land_cover_nodes: list[dict], **kwargs) -> bool:
|
|
2636
2612
|
"""
|
|
2637
|
-
Check if the
|
|
2638
|
-
|
|
2639
|
-
This function is special case of `_check_ipcc_land_use_category`.
|
|
2613
|
+
Check if the land cover nodes and keyword args satisfy the requirements for the given key.
|
|
2640
2614
|
|
|
2641
2615
|
Parameters
|
|
2642
2616
|
----------
|
|
2643
2617
|
key : IpccLandUseCategory
|
|
2644
2618
|
The IPCC land use category to check.
|
|
2645
|
-
|
|
2646
|
-
|
|
2619
|
+
land_cover_nodes : list[dict]
|
|
2620
|
+
List of land cover nodes to be checked.
|
|
2647
2621
|
|
|
2648
2622
|
Keyword Args
|
|
2649
2623
|
------------
|
|
2624
|
+
has_irrigated_upland_rice : bool
|
|
2625
|
+
Indicates whether irrigated upland rice is present on more than 30% of the site.
|
|
2650
2626
|
has_long_fallow : bool
|
|
2651
|
-
Indicates whether long fallow is present on more than
|
|
2627
|
+
Indicates whether long fallow is present on more than 70% of the site.
|
|
2652
2628
|
has_wetland_soils : bool
|
|
2653
2629
|
Indicates whether wetland soils are present to more than 30% of the site.
|
|
2654
2630
|
|
|
@@ -2665,48 +2641,23 @@ def _check_cropland_land_use_category(
|
|
|
2665
2641
|
target_lookup_values=target_lookup_values,
|
|
2666
2642
|
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
2667
2643
|
)
|
|
2668
|
-
return _check_ipcc_land_use_category(key=key, site_type=site_type, **kwargs) and valid_lookup
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
def _check_paddy_rice_cultivation_land_use_category(
|
|
2672
|
-
*, key: IpccLandUseCategory, site_type: str, has_irrigated_upland_rice: bool, **kwargs
|
|
2673
|
-
) -> bool:
|
|
2674
|
-
"""
|
|
2675
|
-
Check if the site type and land cover nodes match the target conditions for a cropland IpccLandUseCategory.
|
|
2676
2644
|
|
|
2677
|
-
|
|
2645
|
+
validation_kwargs = IPCC_LAND_USE_CATEGORY_TO_VALIDATION_KWARGS.get(key, set())
|
|
2646
|
+
valid_kwargs = all(v for k, v in kwargs.items() if k in validation_kwargs)
|
|
2678
2647
|
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
key : IpccLandUseCategory
|
|
2682
|
-
The IPCC land use category to check.
|
|
2683
|
-
site_type : str
|
|
2684
|
-
The site type to check.
|
|
2648
|
+
override_kwargs = IPCC_LAND_USE_CATEGORY_TO_OVERRIDE_KWARGS.get(key, set())
|
|
2649
|
+
valid_override = any(v for k, v in kwargs.items() if k in override_kwargs)
|
|
2685
2650
|
|
|
2686
|
-
|
|
2687
|
-
------------
|
|
2688
|
-
has_irrigated_upland_rice : bool
|
|
2689
|
-
Indicates whether irrigated upland rice is present on more than 30% of the site.
|
|
2690
|
-
has_long_fallow : bool
|
|
2691
|
-
Indicates whether long fallow is present on more than 30% of the site.
|
|
2692
|
-
has_wetland_soils : bool
|
|
2693
|
-
Indicates whether wetland soils are present to more than 30% of the site.
|
|
2694
|
-
|
|
2695
|
-
Returns
|
|
2696
|
-
-------
|
|
2697
|
-
bool
|
|
2698
|
-
`True` if the conditions match the specified land use category, `False` otherwise.
|
|
2699
|
-
"""
|
|
2700
|
-
return _check_cropland_land_use_category(key=key, site_type=site_type, **kwargs) or has_irrigated_upland_rice
|
|
2651
|
+
return (valid_lookup and valid_kwargs) or valid_override
|
|
2701
2652
|
|
|
2702
2653
|
|
|
2703
2654
|
LAND_USE_CATEGORY_DECISION_TREE = {
|
|
2704
2655
|
IpccLandUseCategory.GRASSLAND: _check_ipcc_land_use_category,
|
|
2705
2656
|
IpccLandUseCategory.SET_ASIDE: _check_ipcc_land_use_category,
|
|
2706
|
-
IpccLandUseCategory.PERENNIAL_CROPS:
|
|
2707
|
-
IpccLandUseCategory.PADDY_RICE_CULTIVATION:
|
|
2708
|
-
IpccLandUseCategory.ANNUAL_CROPS_WET:
|
|
2709
|
-
IpccLandUseCategory.ANNUAL_CROPS:
|
|
2657
|
+
IpccLandUseCategory.PERENNIAL_CROPS: _check_ipcc_land_use_category,
|
|
2658
|
+
IpccLandUseCategory.PADDY_RICE_CULTIVATION: _check_ipcc_land_use_category,
|
|
2659
|
+
IpccLandUseCategory.ANNUAL_CROPS_WET: _check_ipcc_land_use_category,
|
|
2660
|
+
IpccLandUseCategory.ANNUAL_CROPS: _check_ipcc_land_use_category,
|
|
2710
2661
|
IpccLandUseCategory.FOREST: _check_ipcc_land_use_category,
|
|
2711
2662
|
IpccLandUseCategory.NATIVE: _check_ipcc_land_use_category,
|
|
2712
2663
|
IpccLandUseCategory.OTHER: _check_ipcc_land_use_category
|
|
@@ -2715,21 +2666,19 @@ LAND_USE_CATEGORY_DECISION_TREE = {
|
|
|
2715
2666
|
A decision tree mapping IPCC soil categories to corresponding check functions.
|
|
2716
2667
|
|
|
2717
2668
|
Key: IpccLandUseCategory
|
|
2718
|
-
Value: Corresponding function for checking the match of the given land use category based on
|
|
2719
|
-
and
|
|
2669
|
+
Value: Corresponding function for checking the match of the given land use category based on land cover nodes
|
|
2670
|
+
and additional kwargs.
|
|
2720
2671
|
"""
|
|
2721
2672
|
|
|
2722
2673
|
|
|
2723
2674
|
def _assign_ipcc_land_use_category(
|
|
2724
|
-
|
|
2675
|
+
management_nodes: list[dict], ipcc_soil_category: IpccSoilCategory,
|
|
2725
2676
|
) -> IpccLandUseCategory:
|
|
2726
2677
|
"""
|
|
2727
|
-
Assigns IPCC land use category based on
|
|
2678
|
+
Assigns IPCC land use category based on management nodes and soil category.
|
|
2728
2679
|
|
|
2729
2680
|
Parameters
|
|
2730
2681
|
----------
|
|
2731
|
-
site_type : str
|
|
2732
|
-
The type of the site.
|
|
2733
2682
|
management_nodes : list[dict]
|
|
2734
2683
|
List of management nodes.
|
|
2735
2684
|
ipcc_soil_category : IpccSoilCategory
|
|
@@ -2752,14 +2701,13 @@ def _assign_ipcc_land_use_category(
|
|
|
2752
2701
|
has_long_fallow = _has_long_fallow(land_cover_nodes)
|
|
2753
2702
|
has_wetland_soils = ipcc_soil_category is IpccSoilCategory.WETLAND_SOILS
|
|
2754
2703
|
|
|
2755
|
-
should_run = bool(
|
|
2704
|
+
should_run = bool(land_cover_nodes)
|
|
2756
2705
|
|
|
2757
2706
|
return next(
|
|
2758
2707
|
(
|
|
2759
2708
|
key for key in DECISION_TREE
|
|
2760
2709
|
if DECISION_TREE[key](
|
|
2761
2710
|
key=key,
|
|
2762
|
-
site_type=site_type,
|
|
2763
2711
|
land_cover_nodes=land_cover_nodes,
|
|
2764
2712
|
has_long_fallow=has_long_fallow,
|
|
2765
2713
|
has_irrigated_upland_rice=has_irrigated_upland_rice,
|
|
@@ -2783,7 +2731,7 @@ def _check_grassland_ipcc_management_category(
|
|
|
2783
2731
|
----------
|
|
2784
2732
|
key : IpccManagementCategory
|
|
2785
2733
|
The IPCC management category to check.
|
|
2786
|
-
land_cover_nodes :
|
|
2734
|
+
land_cover_nodes : list[dict]
|
|
2787
2735
|
List of land cover nodes to be checked.
|
|
2788
2736
|
|
|
2789
2737
|
Returns
|
|
@@ -2809,7 +2757,7 @@ def _check_tillage_ipcc_management_category(
|
|
|
2809
2757
|
----------
|
|
2810
2758
|
key : IpccManagementCategory
|
|
2811
2759
|
The IPCC management category to check.
|
|
2812
|
-
tillage_nodes :
|
|
2760
|
+
tillage_nodes : list[dict]
|
|
2813
2761
|
List of tillage nodes to be checked.
|
|
2814
2762
|
|
|
2815
2763
|
Returns
|
|
@@ -3554,7 +3502,7 @@ def _should_run(site: dict) -> tuple[bool, dict]:
|
|
|
3554
3502
|
site_type = site.get("siteType", "")
|
|
3555
3503
|
management_nodes = site.get("management", [])
|
|
3556
3504
|
measurement_nodes = site.get("measurements", [])
|
|
3557
|
-
cycles =
|
|
3505
|
+
cycles = related_cycles(site.get("@id"))
|
|
3558
3506
|
|
|
3559
3507
|
has_management = len(management_nodes) > 0
|
|
3560
3508
|
has_measurements = len(measurement_nodes) > 0
|
|
@@ -3611,26 +3559,6 @@ def _should_run(site: dict) -> tuple[bool, dict]:
|
|
|
3611
3559
|
return should_run_tier_1, should_run_tier_2, inventory, kwargs
|
|
3612
3560
|
|
|
3613
3561
|
|
|
3614
|
-
def _calculated_cycles(site_id: str):
|
|
3615
|
-
"""
|
|
3616
|
-
Get the list of `Cycle`s related to the `Site`. Gets the `recalculated` data if available, else `original`.
|
|
3617
|
-
|
|
3618
|
-
Parameters
|
|
3619
|
-
----------
|
|
3620
|
-
site_id : str
|
|
3621
|
-
The `@id` of the `Site`.
|
|
3622
|
-
|
|
3623
|
-
Returns
|
|
3624
|
-
-------
|
|
3625
|
-
list[dict]
|
|
3626
|
-
The related `Cycle`s as `dict`.
|
|
3627
|
-
"""
|
|
3628
|
-
nodes = find_related(SchemaType.SITE, site_id, SchemaType.CYCLE)
|
|
3629
|
-
return list(
|
|
3630
|
-
map(lambda node: _load_calculated_node(node, SchemaType.CYCLE), nodes or [])
|
|
3631
|
-
)
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
3562
|
def _should_run_tier_1(
|
|
3635
3563
|
inventory: dict,
|
|
3636
3564
|
*,
|
|
@@ -3806,11 +3734,15 @@ def _should_run_inventory_year_tier_2(group: dict) -> bool:
|
|
|
3806
3734
|
def _get_grouped_climate_measurements(grouped_measurements: dict) -> dict:
|
|
3807
3735
|
return {
|
|
3808
3736
|
year: {
|
|
3809
|
-
_InventoryKey.TEMP_MONTHLY:
|
|
3810
|
-
|
|
3737
|
+
_InventoryKey.TEMP_MONTHLY: non_empty_list(
|
|
3738
|
+
find_term_match(measurements, TEMPERATURE_MONTHLY_TERM_ID, {}).get("value", [])
|
|
3739
|
+
),
|
|
3740
|
+
_InventoryKey.PRECIP_MONTHLY: non_empty_list(
|
|
3811
3741
|
find_term_match(measurements, PRECIPITATION_MONTHLY_TERM_ID, {}).get("value", [])
|
|
3812
3742
|
),
|
|
3813
|
-
_InventoryKey.PET_MONTHLY:
|
|
3743
|
+
_InventoryKey.PET_MONTHLY: non_empty_list(
|
|
3744
|
+
find_term_match(measurements, PET_MONTHLY_TERM_ID, {}).get("value", [])
|
|
3745
|
+
)
|
|
3814
3746
|
} for year, measurements in grouped_measurements.items()
|
|
3815
3747
|
}
|
|
3816
3748
|
|
|
@@ -3940,15 +3872,19 @@ def _build_inventory_tier_1(
|
|
|
3940
3872
|
eco_climate_zone = _get_eco_climate_zone(measurement_nodes)
|
|
3941
3873
|
ipcc_soil_category = _assign_ipcc_soil_category(measurement_nodes)
|
|
3942
3874
|
soc_ref = _retrieve_soc_ref(eco_climate_zone, ipcc_soil_category)
|
|
3875
|
+
grouped_management = group_nodes_by_year(management_nodes)
|
|
3876
|
+
|
|
3877
|
+
# If no `landCover` nodes in `site.management` use `site.siteType` to assign static `IpccLandUseCategory`
|
|
3878
|
+
run_with_site_type = len(filter_list_term_type(management_nodes, [TermTermType.LANDCOVER])) == 0
|
|
3879
|
+
site_type_ipcc_land_use_category = SITE_TYPE_TO_IPCC_LAND_USE_CATEGORY.get(site_type, IpccLandUseCategory.OTHER)
|
|
3943
3880
|
|
|
3944
3881
|
grouped_management = group_nodes_by_year(management_nodes)
|
|
3945
3882
|
|
|
3946
3883
|
grouped_land_use_categories = {
|
|
3947
3884
|
year: {
|
|
3948
|
-
_InventoryKey.LU_CATEGORY:
|
|
3949
|
-
|
|
3950
|
-
nodes,
|
|
3951
|
-
ipcc_soil_category
|
|
3885
|
+
_InventoryKey.LU_CATEGORY: (
|
|
3886
|
+
site_type_ipcc_land_use_category if run_with_site_type
|
|
3887
|
+
else _assign_ipcc_land_use_category(nodes, ipcc_soil_category)
|
|
3952
3888
|
)
|
|
3953
3889
|
} for year, nodes in grouped_management.items()
|
|
3954
3890
|
}
|
|
@@ -3986,7 +3922,8 @@ def _build_inventory_tier_1(
|
|
|
3986
3922
|
kwargs = {
|
|
3987
3923
|
"eco_climate_zone": eco_climate_zone,
|
|
3988
3924
|
"ipcc_soil_category": ipcc_soil_category,
|
|
3989
|
-
"
|
|
3925
|
+
"run_with_site_type": run_with_site_type,
|
|
3926
|
+
"soc_ref": soc_ref
|
|
3990
3927
|
}
|
|
3991
3928
|
|
|
3992
3929
|
return inventory, kwargs
|
|
@@ -8,7 +8,7 @@ from hestia_earth.schema import EmissionMethodTier
|
|
|
8
8
|
from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name
|
|
9
9
|
from hestia_earth.utils.tools import flatten, list_sum
|
|
10
10
|
|
|
11
|
-
from hestia_earth.models.log import debugValues, logRequirements, logShouldRun
|
|
11
|
+
from hestia_earth.models.log import debugValues, logRequirements, logShouldRun, log_as_table
|
|
12
12
|
from hestia_earth.models.utils.emission import _new_emission
|
|
13
13
|
from hestia_earth.models.utils.input import load_impacts
|
|
14
14
|
from hestia_earth.models.utils.blank_node import group_by_keys
|
|
@@ -52,71 +52,99 @@ MODEL_AGGREGATED = 'hestiaAggregatedData'
|
|
|
52
52
|
TIER = EmissionMethodTier.BACKGROUND.value
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
def _emission(
|
|
56
|
-
# log run on each emission so we know it did run
|
|
57
|
-
input_term_id = input.get('term', {}).get('@id')
|
|
58
|
-
operation_term_id = input.get('operation', {}).get('@id')
|
|
59
|
-
animal_term_id = input.get('animal', {}).get('@id')
|
|
60
|
-
|
|
61
|
-
logShouldRun(cycle, model, term_id, True, methodTier=TIER,
|
|
62
|
-
input=input_term_id,
|
|
63
|
-
operation=operation_term_id,
|
|
64
|
-
animal=animal_term_id)
|
|
65
|
-
|
|
55
|
+
def _emission(model: str, term_id: str, value: float, input: dict, operation={}, animal={}):
|
|
66
56
|
emission = _new_emission(term_id, model)
|
|
67
57
|
emission['value'] = [value]
|
|
68
58
|
emission['methodTier'] = TIER
|
|
69
|
-
emission['inputs'] = [input
|
|
70
|
-
if
|
|
71
|
-
emission['operation'] =
|
|
72
|
-
if
|
|
73
|
-
emission['animals'] = [
|
|
59
|
+
emission['inputs'] = [input]
|
|
60
|
+
if operation:
|
|
61
|
+
emission['operation'] = operation
|
|
62
|
+
if animal:
|
|
63
|
+
emission['animals'] = [animal]
|
|
74
64
|
return emission
|
|
75
65
|
|
|
76
66
|
|
|
67
|
+
def _run_emission(cycle: dict, term_id: str, data: dict):
|
|
68
|
+
def run_input(values: dict):
|
|
69
|
+
value = values.get('value', 0)
|
|
70
|
+
term = values.get('term', {})
|
|
71
|
+
operation = values.get('operation', {})
|
|
72
|
+
animal = values.get('animal', {})
|
|
73
|
+
is_aggregated = any(values.get('aggregated', []))
|
|
74
|
+
model = MODEL_AGGREGATED if is_aggregated else MODEL
|
|
75
|
+
|
|
76
|
+
details = values.get('details', {})
|
|
77
|
+
logRequirements(cycle, model=model, term=term_id,
|
|
78
|
+
values=log_as_table([{'impact_assessment_id': key} | value for key, value in details.items()]))
|
|
79
|
+
|
|
80
|
+
logShouldRun(cycle, model, term_id, True, methodTier=TIER,
|
|
81
|
+
input=term.get('@id'),
|
|
82
|
+
operation=operation.get('@id'),
|
|
83
|
+
animal=animal.get('@id'))
|
|
84
|
+
|
|
85
|
+
return _emission(model, term_id, value, input=term, operation=operation, animal=animal)
|
|
86
|
+
|
|
87
|
+
return list(map(run_input, data.values()))
|
|
88
|
+
|
|
89
|
+
|
|
77
90
|
def _emission_group(term_id: str):
|
|
78
91
|
lookup = download_lookup('emission.csv', True)
|
|
79
92
|
return get_table_value(lookup, 'termid', term_id, column_name('inputProductionGroupId'))
|
|
80
93
|
|
|
81
94
|
|
|
82
95
|
def _group_emissions(impact: dict):
|
|
83
|
-
def _group_by(
|
|
96
|
+
def _group_by(group: dict, emission: dict):
|
|
84
97
|
term_id = emission.get('term', {}).get('@id')
|
|
85
98
|
grouping = _emission_group(term_id)
|
|
86
99
|
value = emission.get('value') or 0
|
|
87
100
|
if grouping:
|
|
88
|
-
|
|
89
|
-
return
|
|
101
|
+
group[grouping] = group.get(grouping, 0) + value
|
|
102
|
+
return group
|
|
90
103
|
|
|
91
104
|
emissions = impact.get('emissionsResourceUse', [])
|
|
92
105
|
return reduce(_group_by, emissions, {})
|
|
93
106
|
|
|
94
107
|
|
|
95
|
-
def _run_input(cycle: dict):
|
|
96
|
-
def run(inputs: list):
|
|
97
|
-
input = inputs[0]
|
|
98
|
-
input_value = list_sum(flatten(input.get('value', []) for input in inputs))
|
|
99
|
-
term_id = input.get('term', {}).get('@id')
|
|
100
|
-
impact = input.get('impactAssessment')
|
|
101
|
-
model = MODEL_AGGREGATED if impact.get('aggregated', False) else MODEL
|
|
102
|
-
emissions = _group_emissions(impact)
|
|
103
|
-
|
|
104
|
-
logRequirements(cycle, model=model, term=term_id,
|
|
105
|
-
impact_assessment_id=input.get('impactAssessment', {}).get('@id'),
|
|
106
|
-
input_value=input_value)
|
|
107
|
-
logShouldRun(cycle, model, term_id, True, methodTier=TIER)
|
|
108
|
-
|
|
109
|
-
return [
|
|
110
|
-
_emission(cycle, id, input_value * value, input, model) for id, value in emissions.items()
|
|
111
|
-
]
|
|
112
|
-
return run
|
|
113
|
-
|
|
114
|
-
|
|
115
108
|
def _animal_inputs(animal: dict):
|
|
116
109
|
inputs = load_impacts(animal.get('inputs', []))
|
|
117
110
|
return [(input | {'animal': animal.get('term', {})}) for input in inputs]
|
|
118
111
|
|
|
119
112
|
|
|
113
|
+
def _group_input_emissions(input: dict):
|
|
114
|
+
impact = input.get('impactAssessment')
|
|
115
|
+
emissions = _group_emissions(impact)
|
|
116
|
+
return input | {'emissions': emissions}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _group_inputs(group: dict, values: tuple):
|
|
120
|
+
# input_group_key = 'group-id'
|
|
121
|
+
# inputs = [{'term': {}, 'value':[], 'impactAssessment': {}, 'emissions': {'co2ToAirInputsProduction': 10}}]
|
|
122
|
+
input_group_key, inputs = values
|
|
123
|
+
for input in inputs:
|
|
124
|
+
input_value = list_sum(input.get('value'))
|
|
125
|
+
emissions = input.get('emissions', {})
|
|
126
|
+
for emission_term_id, emission_value in emissions.items():
|
|
127
|
+
group[emission_term_id] = group.get(emission_term_id, {})
|
|
128
|
+
|
|
129
|
+
grouped_inputs = group[emission_term_id].get(input_group_key, {
|
|
130
|
+
'term': input.get('term', {}),
|
|
131
|
+
'operation': input.get('operation', {}),
|
|
132
|
+
'animal': input.get('animal', {}),
|
|
133
|
+
'value': 0,
|
|
134
|
+
'aggregated': [],
|
|
135
|
+
'details': {}
|
|
136
|
+
})
|
|
137
|
+
grouped_inputs['aggregated'].append(input.get('impactAssessment', {}).get('agregated', False))
|
|
138
|
+
grouped_inputs['value'] = grouped_inputs['value'] + (emission_value * input_value)
|
|
139
|
+
# for logging
|
|
140
|
+
grouped_inputs['details'][input.get('impactAssessment', {}).get('@id')] = {
|
|
141
|
+
'emission_value': emission_value,
|
|
142
|
+
'input_value': input_value
|
|
143
|
+
}
|
|
144
|
+
group[emission_term_id][input_group_key] = grouped_inputs
|
|
145
|
+
return group
|
|
146
|
+
|
|
147
|
+
|
|
120
148
|
def run(_, cycle: dict):
|
|
121
149
|
inputs = flatten(
|
|
122
150
|
load_impacts(cycle.get('inputs', [])) +
|
|
@@ -124,9 +152,16 @@ def run(_, cycle: dict):
|
|
|
124
152
|
)
|
|
125
153
|
inputs = [i for i in inputs if list_sum(i.get('value', [])) > 0]
|
|
126
154
|
|
|
155
|
+
# group inputs with same term/operation/animal to avoid adding emissions twice
|
|
156
|
+
# inputs = {'group-id': [{'term': {},'value':[10],'impactAssessment': {}}]}
|
|
157
|
+
inputs = reduce(group_by_keys(['term', 'operation', 'animal']), inputs, {})
|
|
158
|
+
inputs = {key: list(map(_group_input_emissions, value)) for key, value in inputs.items()}
|
|
159
|
+
|
|
127
160
|
debugValues(cycle, model=MODEL,
|
|
128
161
|
nb_inputs=len(inputs))
|
|
129
162
|
|
|
130
|
-
# group
|
|
131
|
-
|
|
132
|
-
|
|
163
|
+
# finally group everything by emission so we can log inputs together
|
|
164
|
+
# emissions = {'co2ToAirInputsProduct': {'group-id':{'term':{},'value':10,'details':{}}}}
|
|
165
|
+
emissions = reduce(_group_inputs, inputs.items(), {})
|
|
166
|
+
|
|
167
|
+
return flatten([_run_emission(cycle, term_id, data) for term_id, data in emissions.items()])
|
|
@@ -918,11 +918,11 @@
|
|
|
918
918
|
},
|
|
919
919
|
{
|
|
920
920
|
"@type": "Term",
|
|
921
|
-
"@id": "
|
|
921
|
+
"@id": "residueIncorporatedMoreThan30DaysBeforeCultivation"
|
|
922
922
|
},
|
|
923
923
|
{
|
|
924
924
|
"@type": "Term",
|
|
925
|
-
"@id": "
|
|
925
|
+
"@id": "residueIncorporated"
|
|
926
926
|
},
|
|
927
927
|
{
|
|
928
928
|
"@type": "Term",
|
|
@@ -1299,7 +1299,7 @@
|
|
|
1299
1299
|
"@type": "Term",
|
|
1300
1300
|
"name": "Generic crop, seed",
|
|
1301
1301
|
"@id": "genericCropSeed",
|
|
1302
|
-
"_score": 23.
|
|
1302
|
+
"_score": 23.910892
|
|
1303
1303
|
}
|
|
1304
1304
|
]
|
|
1305
1305
|
},
|
|
@@ -1405,11 +1405,11 @@
|
|
|
1405
1405
|
"results": [
|
|
1406
1406
|
{
|
|
1407
1407
|
"@type": "Term",
|
|
1408
|
-
"@id": "
|
|
1408
|
+
"@id": "waterDrainageCanal"
|
|
1409
1409
|
},
|
|
1410
1410
|
{
|
|
1411
1411
|
"@type": "Term",
|
|
1412
|
-
"@id": "
|
|
1412
|
+
"@id": "waterMarine"
|
|
1413
1413
|
},
|
|
1414
1414
|
{
|
|
1415
1415
|
"@type": "Term",
|
|
@@ -1532,45 +1532,6 @@
|
|
|
1532
1532
|
}
|
|
1533
1533
|
]
|
|
1534
1534
|
},
|
|
1535
|
-
{
|
|
1536
|
-
"name": "get_long_fallow_land_cover_terms",
|
|
1537
|
-
"query": {
|
|
1538
|
-
"bool": {
|
|
1539
|
-
"must": [
|
|
1540
|
-
{
|
|
1541
|
-
"match": {
|
|
1542
|
-
"@type": "Term"
|
|
1543
|
-
}
|
|
1544
|
-
},
|
|
1545
|
-
{
|
|
1546
|
-
"match": {
|
|
1547
|
-
"termType.keyword": "landCover"
|
|
1548
|
-
}
|
|
1549
|
-
},
|
|
1550
|
-
{
|
|
1551
|
-
"match_phrase_prefix": {
|
|
1552
|
-
"name": "long"
|
|
1553
|
-
}
|
|
1554
|
-
},
|
|
1555
|
-
{
|
|
1556
|
-
"match": {
|
|
1557
|
-
"name": "fallow"
|
|
1558
|
-
}
|
|
1559
|
-
}
|
|
1560
|
-
]
|
|
1561
|
-
}
|
|
1562
|
-
},
|
|
1563
|
-
"results": [
|
|
1564
|
-
{
|
|
1565
|
-
"@type": "Term",
|
|
1566
|
-
"@id": "longFallow"
|
|
1567
|
-
},
|
|
1568
|
-
{
|
|
1569
|
-
"@type": "Term",
|
|
1570
|
-
"@id": "longBareFallow"
|
|
1571
|
-
}
|
|
1572
|
-
]
|
|
1573
|
-
},
|
|
1574
1535
|
{
|
|
1575
1536
|
"name": "get_milkYield_terms",
|
|
1576
1537
|
"query": {
|
|
@@ -1740,15 +1701,15 @@
|
|
|
1740
1701
|
},
|
|
1741
1702
|
{
|
|
1742
1703
|
"@type": "Term",
|
|
1743
|
-
"@id": "
|
|
1704
|
+
"@id": "fullTillage"
|
|
1744
1705
|
},
|
|
1745
1706
|
{
|
|
1746
1707
|
"@type": "Term",
|
|
1747
|
-
"@id": "
|
|
1708
|
+
"@id": "minimumTillage"
|
|
1748
1709
|
},
|
|
1749
1710
|
{
|
|
1750
1711
|
"@type": "Term",
|
|
1751
|
-
"@id": "
|
|
1712
|
+
"@id": "fullInversionTillage"
|
|
1752
1713
|
},
|
|
1753
1714
|
{
|
|
1754
1715
|
"@type": "Term",
|