hestia-earth-models 0.58.0__py3-none-any.whl → 0.59.1__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/{irrigated.py → irrigatedTypeUnspecified.py} +4 -4
- hestia_earth/models/cycle/residueIncorporated.py +1 -1
- hestia_earth/models/emepEea2019/nh3ToAirInorganicFertiliser.py +2 -2
- hestia_earth/models/geospatialDatabase/clayContent.py +17 -4
- hestia_earth/models/geospatialDatabase/sandContent.py +17 -4
- hestia_earth/models/impact_assessment/irrigated.py +0 -3
- hestia_earth/models/ipcc2019/co2ToAirSoilCarbonStockChangeManagementChange.py +10 -9
- hestia_earth/models/ipcc2019/n2OToAirCropResidueDecompositionDirect.py +4 -51
- hestia_earth/models/ipcc2019/n2OToAirInorganicFertiliserDirect.py +104 -0
- hestia_earth/models/ipcc2019/n2OToAirOrganicFertiliserDirect.py +105 -0
- hestia_earth/models/ipcc2019/organicCarbonPerHa.py +1059 -1220
- hestia_earth/models/ipcc2019/utils.py +82 -1
- hestia_earth/models/mocking/search-results.json +165 -95
- hestia_earth/models/site/management.py +12 -9
- hestia_earth/models/site/organicCarbonPerHa.py +251 -89
- hestia_earth/models/utils/blank_node.py +157 -34
- hestia_earth/models/utils/cycle.py +6 -3
- hestia_earth/models/utils/measurement.py +1 -1
- hestia_earth/models/utils/site.py +1 -1
- hestia_earth/models/utils/term.py +46 -1
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.58.0.dist-info → hestia_earth_models-0.59.1.dist-info}/METADATA +4 -8
- {hestia_earth_models-0.58.0.dist-info → hestia_earth_models-0.59.1.dist-info}/RECORD +35 -31
- tests/models/cycle/{test_irrigated.py → test_irrigatedTypeUnspecified.py} +1 -1
- tests/models/geospatialDatabase/test_clayContent.py +9 -3
- tests/models/geospatialDatabase/test_sandContent.py +9 -3
- tests/models/ipcc2019/test_n2OToAirInorganicFertiliserDirect.py +74 -0
- tests/models/ipcc2019/test_n2OToAirOrganicFertiliserDirect.py +74 -0
- tests/models/ipcc2019/test_organicCarbonPerHa.py +303 -1044
- tests/models/site/test_organicCarbonPerHa.py +51 -5
- tests/models/utils/test_blank_node.py +102 -42
- tests/models/utils/test_term.py +17 -3
- {hestia_earth_models-0.58.0.dist-info → hestia_earth_models-0.59.1.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.58.0.dist-info → hestia_earth_models-0.59.1.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.58.0.dist-info → hestia_earth_models-0.59.1.dist-info}/top_level.txt +0 -0
|
@@ -24,7 +24,7 @@ RETURNS = {
|
|
|
24
24
|
"value": ""
|
|
25
25
|
}]
|
|
26
26
|
}
|
|
27
|
-
TERM_ID = '
|
|
27
|
+
TERM_ID = 'irrigatedTypeUnspecified'
|
|
28
28
|
MIN_IRRIGATION_M3 = 250
|
|
29
29
|
|
|
30
30
|
|
|
@@ -34,6 +34,9 @@ def _practice(value: float):
|
|
|
34
34
|
return practice
|
|
35
35
|
|
|
36
36
|
|
|
37
|
+
def _is_irrigation_practice(practice: dict): return practice.get('term', {}).get('units', '') in ['%', '% area']
|
|
38
|
+
|
|
39
|
+
|
|
37
40
|
def _run(irrigation_m3: float):
|
|
38
41
|
value = 100 if irrigation_m3 > MIN_IRRIGATION_M3 else 0
|
|
39
42
|
return [_practice(value)]
|
|
@@ -61,9 +64,6 @@ def _should_run(cycle: dict):
|
|
|
61
64
|
return should_run, irrigation_value_m3
|
|
62
65
|
|
|
63
66
|
|
|
64
|
-
def _is_irrigation_practice(practice: dict): return practice.get('term', {}).get('units', '') in ['%', '% area']
|
|
65
|
-
|
|
66
|
-
|
|
67
67
|
def run(cycle: dict):
|
|
68
68
|
should_run, irrigation_m3 = _should_run(cycle)
|
|
69
69
|
return _run(irrigation_m3) if should_run else []
|
|
@@ -15,7 +15,7 @@ REQUIREMENTS = {
|
|
|
15
15
|
{"@type": "Product", "term.@id": "aboveGroundCropResidueTotal", "value": "> 0"},
|
|
16
16
|
{"@type": "Product", "term.@id": "aboveGroundCropResidueIncorporated", "value": "> 0"}
|
|
17
17
|
],
|
|
18
|
-
"
|
|
18
|
+
"none": {
|
|
19
19
|
"practices": [{
|
|
20
20
|
"@type": "Practice",
|
|
21
21
|
"term.@id": [
|
|
@@ -42,11 +42,11 @@ LOOKUPS = {
|
|
|
42
42
|
RETURNS = {
|
|
43
43
|
"Emission": [{
|
|
44
44
|
"value": "",
|
|
45
|
-
"methodTier": "tier
|
|
45
|
+
"methodTier": "tier 2"
|
|
46
46
|
}]
|
|
47
47
|
}
|
|
48
48
|
TERM_ID = 'nh3ToAirInorganicFertiliser'
|
|
49
|
-
TIER = EmissionMethodTier.
|
|
49
|
+
TIER = EmissionMethodTier.TIER_2.value
|
|
50
50
|
UNSPECIFIED_TERM_ID = 'inorganicNitrogenFertiliserUnspecifiedKgN'
|
|
51
51
|
|
|
52
52
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from hestia_earth.schema import MeasurementMethodClassification
|
|
2
2
|
|
|
3
3
|
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
4
|
-
from hestia_earth.models.utils.
|
|
4
|
+
from hestia_earth.models.utils.blank_node import has_original_by_ids
|
|
5
|
+
from hestia_earth.models.utils.measurement import SOIL_TEXTURE_IDS, _new_measurement
|
|
5
6
|
from hestia_earth.models.utils.source import get_source
|
|
6
7
|
from .utils import download, has_geospatial_data, should_download
|
|
7
8
|
from . import MODEL
|
|
@@ -12,7 +13,17 @@ REQUIREMENTS = {
|
|
|
12
13
|
{"latitude": "", "longitude": ""},
|
|
13
14
|
{"boundary": {}},
|
|
14
15
|
{"region": {"@type": "Term", "termType": "region"}}
|
|
15
|
-
]
|
|
16
|
+
],
|
|
17
|
+
"none": {
|
|
18
|
+
"measurements": [{
|
|
19
|
+
"@type": "Measurement",
|
|
20
|
+
"term.@id": [
|
|
21
|
+
"clayContent",
|
|
22
|
+
"sandContent",
|
|
23
|
+
"siltContent"
|
|
24
|
+
]
|
|
25
|
+
}]
|
|
26
|
+
}
|
|
16
27
|
}
|
|
17
28
|
}
|
|
18
29
|
RETURNS = {
|
|
@@ -54,12 +65,14 @@ def _run(site: dict):
|
|
|
54
65
|
def _should_run(site: dict):
|
|
55
66
|
contains_geospatial_data = has_geospatial_data(site)
|
|
56
67
|
below_max_area_size = should_download(TERM_ID, site)
|
|
68
|
+
has_original_texture_measurements = has_original_by_ids(site.get('measurements', []), SOIL_TEXTURE_IDS)
|
|
57
69
|
|
|
58
70
|
logRequirements(site, model=MODEL, term=TERM_ID,
|
|
59
71
|
contains_geospatial_data=contains_geospatial_data,
|
|
60
|
-
below_max_area_size=below_max_area_size
|
|
72
|
+
below_max_area_size=below_max_area_size,
|
|
73
|
+
has_original_texture_measurements=has_original_texture_measurements)
|
|
61
74
|
|
|
62
|
-
should_run = all([contains_geospatial_data, below_max_area_size])
|
|
75
|
+
should_run = all([contains_geospatial_data, below_max_area_size, not has_original_texture_measurements])
|
|
63
76
|
logShouldRun(site, MODEL, TERM_ID, should_run)
|
|
64
77
|
return should_run
|
|
65
78
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from hestia_earth.schema import MeasurementMethodClassification
|
|
2
2
|
|
|
3
3
|
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
4
|
-
from hestia_earth.models.utils.
|
|
4
|
+
from hestia_earth.models.utils.blank_node import has_original_by_ids
|
|
5
|
+
from hestia_earth.models.utils.measurement import SOIL_TEXTURE_IDS, _new_measurement
|
|
5
6
|
from hestia_earth.models.utils.source import get_source
|
|
6
7
|
from .utils import download, has_geospatial_data, should_download
|
|
7
8
|
from . import MODEL
|
|
@@ -12,7 +13,17 @@ REQUIREMENTS = {
|
|
|
12
13
|
{"latitude": "", "longitude": ""},
|
|
13
14
|
{"boundary": {}},
|
|
14
15
|
{"region": {"@type": "Term", "termType": "region"}}
|
|
15
|
-
]
|
|
16
|
+
],
|
|
17
|
+
"none": {
|
|
18
|
+
"measurements": [{
|
|
19
|
+
"@type": "Measurement",
|
|
20
|
+
"term.@id": [
|
|
21
|
+
"clayContent",
|
|
22
|
+
"sandContent",
|
|
23
|
+
"siltContent"
|
|
24
|
+
]
|
|
25
|
+
}]
|
|
26
|
+
}
|
|
16
27
|
}
|
|
17
28
|
}
|
|
18
29
|
RETURNS = {
|
|
@@ -54,12 +65,14 @@ def _run(site: dict):
|
|
|
54
65
|
def _should_run(site: dict):
|
|
55
66
|
contains_geospatial_data = has_geospatial_data(site)
|
|
56
67
|
below_max_area_size = should_download(TERM_ID, site)
|
|
68
|
+
has_original_texture_measurements = has_original_by_ids(site.get('measurements', []), SOIL_TEXTURE_IDS)
|
|
57
69
|
|
|
58
70
|
logRequirements(site, model=MODEL, term=TERM_ID,
|
|
59
71
|
contains_geospatial_data=contains_geospatial_data,
|
|
60
|
-
below_max_area_size=below_max_area_size
|
|
72
|
+
below_max_area_size=below_max_area_size,
|
|
73
|
+
has_original_texture_measurements=has_original_texture_measurements)
|
|
61
74
|
|
|
62
|
-
should_run = all([contains_geospatial_data, below_max_area_size])
|
|
75
|
+
should_run = all([contains_geospatial_data, below_max_area_size, not has_original_texture_measurements])
|
|
63
76
|
logShouldRun(site, MODEL, TERM_ID, should_run)
|
|
64
77
|
return should_run
|
|
65
78
|
|
|
@@ -580,22 +580,22 @@ def _calc_grouped_soc_stock_changes(grouped_soc_stocks: dict) -> dict:
|
|
|
580
580
|
|
|
581
581
|
def _calc_sum_cycle_occupancy(cycles: list[dict]) -> float:
|
|
582
582
|
"""
|
|
583
|
-
Calculate the sum of cycle occupancies based on the `
|
|
584
|
-
function.
|
|
583
|
+
Calculate the sum of cycle occupancies based on the `fraction_of_group_duration` field added by the
|
|
584
|
+
`group_nodes_by_year` function.
|
|
585
585
|
|
|
586
|
-
If a cycle does not have the "
|
|
586
|
+
If a cycle does not have the "fraction_of_group_duration" key, it is treated as zero occupancy for that year.
|
|
587
587
|
|
|
588
588
|
Parameters
|
|
589
589
|
----------
|
|
590
590
|
cycles : List[dict]
|
|
591
|
-
List of cycles, where each cycle dictionary should contain a "
|
|
591
|
+
List of cycles, where each cycle dictionary should contain a "fraction_of_group_duration" key.
|
|
592
592
|
|
|
593
593
|
Returns
|
|
594
594
|
-------
|
|
595
595
|
float
|
|
596
596
|
The sum of cycle occupancies based on the fraction of the year.
|
|
597
597
|
"""
|
|
598
|
-
return sum(cycle.get("
|
|
598
|
+
return sum(cycle.get("fraction_of_group_duration", 0) for cycle in cycles)
|
|
599
599
|
|
|
600
600
|
|
|
601
601
|
def _calc_grouped_share_of_emissions(cycles: list[dict]) -> dict:
|
|
@@ -604,14 +604,14 @@ def _calc_grouped_share_of_emissions(cycles: list[dict]) -> dict:
|
|
|
604
604
|
of an inventory year.
|
|
605
605
|
|
|
606
606
|
This function groups cycles by year, then calculates the share of emissions for each cycle based on the
|
|
607
|
-
"
|
|
608
|
-
dataset to ensure the values represent a valid share.
|
|
607
|
+
"fraction_of_group_duration" value. The share of emissions is normalized by the sum of cycle occupancies for the
|
|
608
|
+
entire dataset to ensure the values represent a valid share.
|
|
609
609
|
|
|
610
610
|
Parameters
|
|
611
611
|
----------
|
|
612
612
|
cycles : List[dict]
|
|
613
613
|
List of [Cycle nodes](https://www.hestia.earth/schema/Cycle), where each cycle dictionary should contain a
|
|
614
|
-
"
|
|
614
|
+
"fraction_of_group_duration" key added by the `group_nodes_by_year` function.
|
|
615
615
|
|
|
616
616
|
Returns
|
|
617
617
|
-------
|
|
@@ -623,7 +623,8 @@ def _calc_grouped_share_of_emissions(cycles: list[dict]) -> dict:
|
|
|
623
623
|
return {
|
|
624
624
|
year: {
|
|
625
625
|
INNER_KEY: {
|
|
626
|
-
cycle["@id"]: cycle.get("
|
|
626
|
+
cycle["@id"]: cycle.get("fraction_of_group_duration", 0) / _calc_sum_cycle_occupancy(cycles)
|
|
627
|
+
for cycle in cycles
|
|
627
628
|
}
|
|
628
629
|
} for year, cycles in grouped_cycles.items()
|
|
629
630
|
}
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
from hestia_earth.schema import EmissionMethodTier, EmissionStatsDefinition, TermTermType
|
|
2
|
-
from hestia_earth.utils.model import filter_list_term_type
|
|
3
|
-
from hestia_earth.utils.tools import safe_parse_float
|
|
4
2
|
|
|
5
3
|
from hestia_earth.models.log import logRequirements, logShouldRun, debugValues, log_as_table
|
|
6
4
|
from hestia_earth.models.utils.constant import Units, get_atomic_conversion
|
|
7
5
|
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
|
8
6
|
from hestia_earth.models.utils.emission import _new_emission
|
|
9
7
|
from hestia_earth.models.utils.cycle import get_ecoClimateZone, get_crop_residue_decomposition_N_total
|
|
10
|
-
from hestia_earth.models.utils.ecoClimateZone import get_ecoClimateZone_lookup_value
|
|
11
|
-
from hestia_earth.models.utils.term import get_lookup_value
|
|
12
8
|
from hestia_earth.models.utils.product import has_flooded_rice
|
|
9
|
+
from .utils import get_N2O_factors
|
|
13
10
|
from . import MODEL
|
|
14
11
|
|
|
15
12
|
REQUIREMENTS = {
|
|
@@ -51,28 +48,6 @@ LOOKUPS = {
|
|
|
51
48
|
}
|
|
52
49
|
TERM_ID = 'n2OToAirCropResidueDecompositionDirect'
|
|
53
50
|
TIER = EmissionMethodTier.TIER_1.value
|
|
54
|
-
FACTORS = {
|
|
55
|
-
'dry': {
|
|
56
|
-
'value': 0.005,
|
|
57
|
-
'min': 0,
|
|
58
|
-
'max': 0.011
|
|
59
|
-
},
|
|
60
|
-
'wet': {
|
|
61
|
-
'value': 0.006,
|
|
62
|
-
'min': 0.001,
|
|
63
|
-
'max': 0.011
|
|
64
|
-
},
|
|
65
|
-
'default': {
|
|
66
|
-
'value': 0.01,
|
|
67
|
-
'min': 0.001,
|
|
68
|
-
'max': 0.018
|
|
69
|
-
},
|
|
70
|
-
'flooded_rice': {
|
|
71
|
-
'value': 0.004,
|
|
72
|
-
'min': 0,
|
|
73
|
-
'max': 0.029
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
51
|
|
|
77
52
|
|
|
78
53
|
def _emission(value: float, min: float, max: float, sd: float, aggregated: bool = False):
|
|
@@ -87,33 +62,11 @@ def _emission(value: float, min: float, max: float, sd: float, aggregated: bool
|
|
|
87
62
|
return emission
|
|
88
63
|
|
|
89
64
|
|
|
90
|
-
def _get_waterRegime_lookup(practice: dict, col: str):
|
|
91
|
-
return safe_parse_float(get_lookup_value(practice.get('term', {}), col, model=MODEL, term=TERM_ID), None)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def _flooded_rice_factors(cycle: dict):
|
|
95
|
-
practices = filter_list_term_type(cycle.get('practices', []), TermTermType.WATERREGIME)
|
|
96
|
-
practice = next((p for p in practices if _get_waterRegime_lookup(p, LOOKUPS['waterRegime'][0]) is not None), None)
|
|
97
|
-
|
|
98
|
-
factors = {
|
|
99
|
-
'value': _get_waterRegime_lookup(practice, LOOKUPS['waterRegime'][0]),
|
|
100
|
-
'min': _get_waterRegime_lookup(practice, LOOKUPS['waterRegime'][1]),
|
|
101
|
-
'max': _get_waterRegime_lookup(practice, LOOKUPS['waterRegime'][2])
|
|
102
|
-
} if practice else FACTORS['flooded_rice']
|
|
103
|
-
|
|
104
|
-
return (factors, practice is None)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
def _is_wet(ecoClimateZone: str = None):
|
|
108
|
-
return get_ecoClimateZone_lookup_value(ecoClimateZone, 'wet') == 1 if ecoClimateZone else None
|
|
109
|
-
|
|
110
|
-
|
|
111
65
|
def _run(cycle: dict, N_total: float, ecoClimateZone: str = None, flooded_rice: bool = False):
|
|
112
66
|
converted_N_total = N_total * get_atomic_conversion(Units.KG_N2O, Units.TO_N)
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
factors, aggregated = _flooded_rice_factors(cycle) if flooded_rice else (FACTORS[factors_key], is_wet is None)
|
|
67
|
+
factors, aggregated = get_N2O_factors(
|
|
68
|
+
TERM_ID, cycle, TermTermType.CROPRESIDUE, ecoClimateZone=ecoClimateZone, flooded_rice=flooded_rice
|
|
69
|
+
)
|
|
117
70
|
|
|
118
71
|
debugValues(cycle, model=MODEL, term=TERM_ID,
|
|
119
72
|
factors_used=log_as_table(factors),
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from hestia_earth.schema import EmissionMethodTier, EmissionStatsDefinition, TermTermType
|
|
2
|
+
|
|
3
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, debugValues, log_as_table
|
|
4
|
+
from hestia_earth.models.utils.constant import Units, get_atomic_conversion
|
|
5
|
+
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
|
6
|
+
from hestia_earth.models.utils.emission import _new_emission
|
|
7
|
+
from hestia_earth.models.utils.cycle import get_ecoClimateZone, get_inorganic_fertiliser_N_total
|
|
8
|
+
from hestia_earth.models.utils.product import has_flooded_rice
|
|
9
|
+
from .utils import get_N2O_factors
|
|
10
|
+
from . import MODEL
|
|
11
|
+
|
|
12
|
+
REQUIREMENTS = {
|
|
13
|
+
"Cycle": {
|
|
14
|
+
"completeness.fertiliser": "True",
|
|
15
|
+
"inputs": [{
|
|
16
|
+
"@type": "Input",
|
|
17
|
+
"value": "",
|
|
18
|
+
"term.units": ["kg", "kg N"],
|
|
19
|
+
"term.termType": "inorganicFertiliser",
|
|
20
|
+
"optional": {
|
|
21
|
+
"properties": [{"@type": "Property", "value": "", "term.@id": "nitrogenContent"}]
|
|
22
|
+
}
|
|
23
|
+
}],
|
|
24
|
+
"optional": {
|
|
25
|
+
"endDate": "",
|
|
26
|
+
"site": {
|
|
27
|
+
"@type": "Site",
|
|
28
|
+
"measurements": [
|
|
29
|
+
{"@type": "Measurement", "value": "", "term.@id": "ecoClimateZone"}
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
"products": [{"@type": "Product", "term.@id": "riceGrainInHuskFlooded"}],
|
|
33
|
+
"practices": [{"@type": "Practice", "term.termType": "waterRegime"}]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
RETURNS = {
|
|
38
|
+
"Emission": [{
|
|
39
|
+
"value": "",
|
|
40
|
+
"min": "",
|
|
41
|
+
"max": "",
|
|
42
|
+
"sd": "",
|
|
43
|
+
"methodTier": "tier 1",
|
|
44
|
+
"statsDefinition": "modelled",
|
|
45
|
+
"methodModelDescription": ["Aggregated version", "Disaggregated version"]
|
|
46
|
+
}]
|
|
47
|
+
}
|
|
48
|
+
LOOKUPS = {
|
|
49
|
+
"waterRegime": ["IPCC_2019_N2O_rice", "IPCC_2019_N2O_rice-min", "IPCC_2019_N2O_rice-max"]
|
|
50
|
+
}
|
|
51
|
+
TERM_ID = 'n2OToAirInorganicFertiliserDirect'
|
|
52
|
+
TIER = EmissionMethodTier.TIER_1.value
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _emission(value: float, min: float, max: float, sd: float, aggregated: bool = False):
|
|
56
|
+
emission = _new_emission(TERM_ID, MODEL)
|
|
57
|
+
emission['value'] = [value]
|
|
58
|
+
emission['min'] = [min]
|
|
59
|
+
emission['max'] = [max]
|
|
60
|
+
emission['sd'] = [sd]
|
|
61
|
+
emission['methodTier'] = TIER
|
|
62
|
+
emission['statsDefinition'] = EmissionStatsDefinition.MODELLED.value
|
|
63
|
+
emission['methodModelDescription'] = 'Aggregated version' if aggregated else 'Disaggregated version'
|
|
64
|
+
return emission
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _run(cycle: dict, N_total: float, ecoClimateZone: str = None, flooded_rice: bool = False):
|
|
68
|
+
converted_N_total = N_total * get_atomic_conversion(Units.KG_N2O, Units.TO_N)
|
|
69
|
+
factors, aggregated = get_N2O_factors(
|
|
70
|
+
TERM_ID, cycle, TermTermType.INORGANICFERTILISER, ecoClimateZone=ecoClimateZone, flooded_rice=flooded_rice
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
debugValues(cycle, model=MODEL, term=TERM_ID,
|
|
74
|
+
factors_used=log_as_table(factors),
|
|
75
|
+
aggregated=aggregated)
|
|
76
|
+
|
|
77
|
+
value = converted_N_total * factors['value']
|
|
78
|
+
min = converted_N_total * factors['min']
|
|
79
|
+
max = converted_N_total * factors['max']
|
|
80
|
+
sd = converted_N_total * (factors['max'] - factors['min'])/4
|
|
81
|
+
return [_emission(value, min, max, sd, aggregated=aggregated)]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _should_run(cycle: dict):
|
|
85
|
+
term_type_complete = _is_term_type_complete(cycle, 'fertiliser')
|
|
86
|
+
N_total = get_inorganic_fertiliser_N_total(cycle)
|
|
87
|
+
ecoClimateZone = get_ecoClimateZone(cycle)
|
|
88
|
+
|
|
89
|
+
flooded_rice = has_flooded_rice(cycle.get('products', []))
|
|
90
|
+
|
|
91
|
+
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
|
92
|
+
term_type_cropResidue_complete=term_type_complete,
|
|
93
|
+
N_total=N_total,
|
|
94
|
+
has_flooded_rice=flooded_rice,
|
|
95
|
+
ecoClimateZone=ecoClimateZone)
|
|
96
|
+
|
|
97
|
+
should_run = all([N_total is not None, term_type_complete])
|
|
98
|
+
logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
|
|
99
|
+
return should_run, N_total, ecoClimateZone, flooded_rice
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def run(cycle: dict):
|
|
103
|
+
should_run, N_total, ecoClimateZone, flooded_rice = _should_run(cycle)
|
|
104
|
+
return _run(cycle, N_total, ecoClimateZone, flooded_rice) if should_run else []
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
from hestia_earth.schema import EmissionMethodTier, EmissionStatsDefinition, TermTermType
|
|
2
|
+
|
|
3
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, debugValues, log_as_table
|
|
4
|
+
from hestia_earth.models.utils.constant import Units, get_atomic_conversion
|
|
5
|
+
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
|
6
|
+
from hestia_earth.models.utils.emission import _new_emission
|
|
7
|
+
from hestia_earth.models.utils.cycle import get_ecoClimateZone, get_organic_fertiliser_N_total
|
|
8
|
+
from hestia_earth.models.utils.product import has_flooded_rice
|
|
9
|
+
from .utils import get_N2O_factors
|
|
10
|
+
from . import MODEL
|
|
11
|
+
|
|
12
|
+
REQUIREMENTS = {
|
|
13
|
+
"Cycle": {
|
|
14
|
+
"completeness.fertiliser": "True",
|
|
15
|
+
"inputs": [{
|
|
16
|
+
"@type": "Input",
|
|
17
|
+
"value": "",
|
|
18
|
+
"term.units": ["kg", "kg N"],
|
|
19
|
+
"term.termType": "organicFertiliser",
|
|
20
|
+
"optional": {
|
|
21
|
+
"properties": [{"@type": "Property", "value": "", "term.@id": "nitrogenContent"}]
|
|
22
|
+
}
|
|
23
|
+
}],
|
|
24
|
+
"optional": {
|
|
25
|
+
"endDate": "",
|
|
26
|
+
"site": {
|
|
27
|
+
"@type": "Site",
|
|
28
|
+
"measurements": [
|
|
29
|
+
{"@type": "Measurement", "value": "", "term.@id": "ecoClimateZone"}
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
"products": [{"@type": "Product", "term.@id": "riceGrainInHuskFlooded"}],
|
|
33
|
+
"practices": [{"@type": "Practice", "term.termType": "waterRegime"}]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
RETURNS = {
|
|
38
|
+
"Emission": [{
|
|
39
|
+
"value": "",
|
|
40
|
+
"min": "",
|
|
41
|
+
"max": "",
|
|
42
|
+
"sd": "",
|
|
43
|
+
"methodTier": "tier 1",
|
|
44
|
+
"statsDefinition": "modelled",
|
|
45
|
+
"methodModelDescription": ["Aggregated version", "Disaggregated version"]
|
|
46
|
+
}]
|
|
47
|
+
}
|
|
48
|
+
LOOKUPS = {
|
|
49
|
+
"waterRegime": ["IPCC_2019_N2O_rice", "IPCC_2019_N2O_rice-min", "IPCC_2019_N2O_rice-max"]
|
|
50
|
+
}
|
|
51
|
+
TERM_ID = 'n2OToAirOrganicFertiliserDirect'
|
|
52
|
+
TIER = EmissionMethodTier.TIER_1.value
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _emission(value: float, min: float, max: float, sd: float, aggregated: bool = False):
|
|
56
|
+
emission = _new_emission(TERM_ID, MODEL)
|
|
57
|
+
emission['value'] = [value]
|
|
58
|
+
emission['min'] = [min]
|
|
59
|
+
emission['max'] = [max]
|
|
60
|
+
emission['sd'] = [sd]
|
|
61
|
+
emission['methodTier'] = TIER
|
|
62
|
+
emission['statsDefinition'] = EmissionStatsDefinition.MODELLED.value
|
|
63
|
+
emission['methodModelDescription'] = 'Aggregated version' if aggregated else 'Disaggregated version'
|
|
64
|
+
return emission
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _run(cycle: dict, N_total: float, ecoClimateZone: str = None, flooded_rice: bool = False):
|
|
68
|
+
converted_N_total = N_total * get_atomic_conversion(Units.KG_N2O, Units.TO_N)
|
|
69
|
+
factors, aggregated = get_N2O_factors(
|
|
70
|
+
TERM_ID, cycle, TermTermType.ORGANICFERTILISER, ecoClimateZone=ecoClimateZone, flooded_rice=flooded_rice
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
debugValues(cycle, model=MODEL, term=TERM_ID,
|
|
74
|
+
factors_used=log_as_table(factors),
|
|
75
|
+
aggregated=aggregated)
|
|
76
|
+
|
|
77
|
+
print(N_total, get_atomic_conversion(Units.KG_N2O, Units.TO_N), factors['value'])
|
|
78
|
+
value = converted_N_total * factors['value']
|
|
79
|
+
min = converted_N_total * factors['min']
|
|
80
|
+
max = converted_N_total * factors['max']
|
|
81
|
+
sd = converted_N_total * (factors['max'] - factors['min'])/4
|
|
82
|
+
return [_emission(value, min, max, sd, aggregated=aggregated)]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _should_run(cycle: dict):
|
|
86
|
+
term_type_complete = _is_term_type_complete(cycle, 'fertiliser')
|
|
87
|
+
N_total = get_organic_fertiliser_N_total(cycle)
|
|
88
|
+
ecoClimateZone = get_ecoClimateZone(cycle)
|
|
89
|
+
|
|
90
|
+
flooded_rice = has_flooded_rice(cycle.get('products', []))
|
|
91
|
+
|
|
92
|
+
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
|
93
|
+
term_type_cropResidue_complete=term_type_complete,
|
|
94
|
+
N_total=N_total,
|
|
95
|
+
has_flooded_rice=flooded_rice,
|
|
96
|
+
ecoClimateZone=ecoClimateZone)
|
|
97
|
+
|
|
98
|
+
should_run = all([N_total is not None, term_type_complete])
|
|
99
|
+
logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
|
|
100
|
+
return should_run, N_total, ecoClimateZone, flooded_rice
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def run(cycle: dict):
|
|
104
|
+
should_run, N_total, ecoClimateZone, flooded_rice = _should_run(cycle)
|
|
105
|
+
return _run(cycle, N_total, ecoClimateZone, flooded_rice) if should_run else []
|