hestia-earth-models 0.59.1__py3-none-any.whl → 0.59.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of hestia-earth-models might be problematic. Click here for more details.
- hestia_earth/models/cycle/irrigatedTypeUnspecified.py +6 -7
- hestia_earth/models/impact_assessment/irrigated.py +4 -1
- hestia_earth/models/ipcc2006/n2OToAirCropResidueDecompositionIndirect.py +3 -3
- hestia_earth/models/ipcc2006/n2OToAirExcretaIndirect.py +3 -3
- hestia_earth/models/ipcc2006/n2OToAirInorganicFertiliserIndirect.py +5 -4
- hestia_earth/models/ipcc2006/n2OToAirOrganicFertiliserIndirect.py +5 -4
- hestia_earth/models/ipcc2006/utils.py +0 -16
- hestia_earth/models/ipcc2019/n2OToAirCropResidueDecompositionDirect.py +8 -7
- hestia_earth/models/ipcc2019/n2OToAirCropResidueDecompositionIndirect.py +100 -0
- hestia_earth/models/ipcc2019/n2OToAirExcretaIndirect.py +100 -0
- hestia_earth/models/ipcc2019/n2OToAirInorganicFertiliserIndirect.py +54 -61
- hestia_earth/models/ipcc2019/n2OToAirOrganicFertiliserIndirect.py +58 -66
- hestia_earth/models/ipcc2019/nh3ToAirOrganicFertiliser.py +104 -0
- hestia_earth/models/ipcc2019/noxToAirOrganicFertiliser.py +104 -0
- hestia_earth/models/ipcc2019/organicCarbonPerHa.py +56 -18
- hestia_earth/models/ipcc2019/utils.py +28 -16
- hestia_earth/models/site/soilMeasurement.py +197 -0
- hestia_earth/models/utils/cycle.py +7 -6
- hestia_earth/models/utils/emission.py +15 -0
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.59.1.dist-info → hestia_earth_models-0.59.2.dist-info}/METADATA +1 -1
- {hestia_earth_models-0.59.1.dist-info → hestia_earth_models-0.59.2.dist-info}/RECORD +33 -23
- tests/models/ipcc2019/test_n2OToAirCropResidueDecompositionIndirect.py +71 -0
- tests/models/ipcc2019/test_n2OToAirExcretaIndirect.py +71 -0
- tests/models/ipcc2019/test_n2OToAirInorganicFertiliserIndirect.py +36 -13
- tests/models/ipcc2019/test_n2OToAirOrganicFertiliserIndirect.py +36 -13
- tests/models/ipcc2019/test_nh3ToAirOrganicFertiliser.py +35 -0
- tests/models/ipcc2019/test_noxToAirOrganicFertiliser.py +35 -0
- tests/models/ipcc2019/test_organicCarbonPerHa.py +45 -4
- tests/models/site/test_soilMeasurement.py +159 -0
- {hestia_earth_models-0.59.1.dist-info → hestia_earth_models-0.59.2.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.59.1.dist-info → hestia_earth_models-0.59.2.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.59.1.dist-info → hestia_earth_models-0.59.2.dist-info}/top_level.txt +0 -0
|
@@ -1,108 +1,100 @@
|
|
|
1
|
-
from hestia_earth.schema import EmissionMethodTier, EmissionStatsDefinition
|
|
1
|
+
from hestia_earth.schema import EmissionMethodTier, EmissionStatsDefinition
|
|
2
2
|
|
|
3
|
-
from hestia_earth.models.log import debugValues, logRequirements, logShouldRun
|
|
3
|
+
from hestia_earth.models.log import debugValues, logRequirements, logShouldRun, log_as_table
|
|
4
4
|
from hestia_earth.models.utils.constant import Units, get_atomic_conversion
|
|
5
5
|
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
|
6
|
-
from hestia_earth.models.utils.
|
|
7
|
-
from hestia_earth.models.utils.
|
|
8
|
-
from .utils import
|
|
9
|
-
get_FracLEACH_H
|
|
6
|
+
from hestia_earth.models.utils.emission import _new_emission, get_nh3_no3_nox_to_n
|
|
7
|
+
from hestia_earth.models.utils.cycle import get_ecoClimateZone
|
|
8
|
+
from .utils import ecoClimate_factors, EF4_FACTORS, EF5_FACTORS
|
|
10
9
|
from . import MODEL
|
|
11
10
|
|
|
12
11
|
REQUIREMENTS = {
|
|
13
12
|
"Cycle": {
|
|
14
13
|
"completeness.fertiliser": "True",
|
|
15
|
-
"completeness.water": "True",
|
|
16
|
-
"inputs": [
|
|
17
|
-
{
|
|
18
|
-
"@type": "Input",
|
|
19
|
-
"value": "",
|
|
20
|
-
"term.termType": "organicFertiliser",
|
|
21
|
-
"term.units": "kg N"
|
|
22
|
-
}
|
|
23
|
-
],
|
|
24
14
|
"emissions": [
|
|
25
|
-
{"@type": "Emission", "value": "", "term.@id": "
|
|
26
|
-
{"@type": "Emission", "value": "", "term.@id": "
|
|
27
|
-
{"@type": "Emission", "value": "", "term.@id": "
|
|
15
|
+
{"@type": "Emission", "value": "", "term.@id": "no3ToGroundwaterOrganicFertiliser"},
|
|
16
|
+
{"@type": "Emission", "value": "", "term.@id": "nh3ToAirOrganicFertiliser"},
|
|
17
|
+
{"@type": "Emission", "value": "", "term.@id": "noxToAirOrganicFertiliser"}
|
|
28
18
|
],
|
|
29
|
-
"site": {
|
|
30
|
-
"@type": "Site",
|
|
31
|
-
"measurements": [{"@type": "Measurement", "value": "", "term.@id": "ecoClimateZone"}]
|
|
32
|
-
},
|
|
33
19
|
"optional": {
|
|
34
|
-
"
|
|
20
|
+
"site": {
|
|
21
|
+
"@type": "Site",
|
|
22
|
+
"measurements": [
|
|
23
|
+
{"@type": "Measurement", "value": "", "term.@id": "ecoClimateZone"}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
35
26
|
}
|
|
36
27
|
}
|
|
37
28
|
}
|
|
38
29
|
RETURNS = {
|
|
39
30
|
"Emission": [{
|
|
40
31
|
"value": "",
|
|
41
|
-
"
|
|
42
|
-
"min": "",
|
|
43
|
-
"max": "",
|
|
44
|
-
"methodTier": "tier 1",
|
|
45
|
-
"statsDefinition": "modelled"
|
|
32
|
+
"methodTier": "tier 1"
|
|
46
33
|
}]
|
|
47
34
|
}
|
|
48
|
-
|
|
49
35
|
TERM_ID = 'n2OToAirOrganicFertiliserIndirect'
|
|
50
|
-
NO3_TERM_ID = 'no3ToGroundwaterInorganicFertiliser'
|
|
51
|
-
NH3_TERM_ID = 'nh3ToAirInorganicFertiliser'
|
|
52
|
-
NOX_TERM_ID = 'noxToAirInorganicFertiliser'
|
|
53
36
|
TIER = EmissionMethodTier.TIER_1.value
|
|
37
|
+
NO3_TERM_ID = 'no3ToGroundwaterOrganicFertiliser'
|
|
38
|
+
NH3_TERM_ID = 'nh3ToAirOrganicFertiliser'
|
|
39
|
+
NOX_TERM_ID = 'noxToAirOrganicFertiliser'
|
|
54
40
|
|
|
55
41
|
|
|
56
|
-
def _emission(value: float,
|
|
42
|
+
def _emission(value: float, min: float, max: float, aggregated: bool = False):
|
|
57
43
|
emission = _new_emission(TERM_ID, MODEL)
|
|
58
44
|
emission['value'] = [value]
|
|
59
|
-
emission['sd'] = [sd]
|
|
60
45
|
emission['min'] = [min]
|
|
61
46
|
emission['max'] = [max]
|
|
62
47
|
emission['methodTier'] = TIER
|
|
63
48
|
emission['statsDefinition'] = EmissionStatsDefinition.MODELLED.value
|
|
49
|
+
emission['methodModelDescription'] = 'Aggregated version' if aggregated else 'Disaggregated version'
|
|
64
50
|
return emission
|
|
65
51
|
|
|
66
52
|
|
|
67
|
-
def _run(cycle: dict):
|
|
68
|
-
|
|
69
|
-
|
|
53
|
+
def _run(cycle: dict, no3: float, nh3: float, nox: float):
|
|
54
|
+
ecoClimateZone = get_ecoClimateZone(cycle)
|
|
55
|
+
|
|
56
|
+
ef4, aggregated = ecoClimate_factors(EF4_FACTORS, ecoClimateZone=ecoClimateZone)
|
|
57
|
+
ef5 = EF5_FACTORS.get('default')
|
|
58
|
+
|
|
70
59
|
debugValues(cycle, model=MODEL, term=TERM_ID,
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
60
|
+
ecoClimateZone=ecoClimateZone,
|
|
61
|
+
ef4_factors_used=log_as_table(ef4),
|
|
62
|
+
ef5_factors_used=log_as_table(ef5),
|
|
63
|
+
aggregated=aggregated)
|
|
74
64
|
|
|
75
|
-
value
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
65
|
+
value = sum([
|
|
66
|
+
no3 * ef5['value'],
|
|
67
|
+
nh3 * ef4['value'],
|
|
68
|
+
nox * ef4['value']
|
|
69
|
+
]) * get_atomic_conversion(Units.KG_N2O, Units.TO_N)
|
|
70
|
+
min = sum([
|
|
71
|
+
no3 * ef5['min'],
|
|
72
|
+
nh3 * ef4['min'],
|
|
73
|
+
nox * ef4['min']
|
|
74
|
+
]) * get_atomic_conversion(Units.KG_N2O, Units.TO_N)
|
|
75
|
+
max = sum([
|
|
76
|
+
no3 * ef5['max'],
|
|
77
|
+
nh3 * ef4['max'],
|
|
78
|
+
nox * ef4['max']
|
|
79
|
+
]) * get_atomic_conversion(Units.KG_N2O, Units.TO_N)
|
|
80
|
+
return [_emission(value, min, max, aggregated=aggregated)]
|
|
89
81
|
|
|
90
82
|
|
|
91
83
|
def _should_run(cycle: dict):
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
fertiliser_complete = _is_term_type_complete(cycle, 'fertiliser')
|
|
95
|
-
water_complete = _is_term_type_complete(cycle, TermTermType.WATER)
|
|
84
|
+
nh3_n, no3_n, nox_n = get_nh3_no3_nox_to_n(cycle, NH3_TERM_ID, NO3_TERM_ID, NOX_TERM_ID)
|
|
85
|
+
term_type_complete = _is_term_type_complete(cycle, 'fertiliser')
|
|
96
86
|
|
|
97
87
|
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
88
|
+
no3_n=no3_n,
|
|
89
|
+
nh3_n=nh3_n,
|
|
90
|
+
nox_n=nox_n,
|
|
91
|
+
term_type_cropResidue_complete=term_type_complete)
|
|
102
92
|
|
|
103
|
-
should_run = all([
|
|
104
|
-
logShouldRun(cycle, MODEL, TERM_ID, should_run)
|
|
105
|
-
return should_run
|
|
93
|
+
should_run = all([no3_n is not None, nh3_n is not None, nox_n is not None, term_type_complete])
|
|
94
|
+
logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
|
|
95
|
+
return should_run, no3_n, nh3_n, nox_n
|
|
106
96
|
|
|
107
97
|
|
|
108
|
-
def run(cycle: dict):
|
|
98
|
+
def run(cycle: dict):
|
|
99
|
+
should_run, no3, nh3, nox = _should_run(cycle)
|
|
100
|
+
return _run(cycle, no3, nh3, nox) if should_run else []
|
|
@@ -0,0 +1,104 @@
|
|
|
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 list_sum
|
|
4
|
+
|
|
5
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, debugValues, log_as_table
|
|
6
|
+
from hestia_earth.models.utils.blank_node import get_N_total
|
|
7
|
+
from hestia_earth.models.utils.constant import Units, get_atomic_conversion
|
|
8
|
+
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
|
9
|
+
from hestia_earth.models.utils.emission import _new_emission
|
|
10
|
+
from hestia_earth.models.utils.cycle import get_organic_fertiliser_N_total
|
|
11
|
+
from hestia_earth.models.utils.term import get_lookup_value
|
|
12
|
+
from . import MODEL
|
|
13
|
+
|
|
14
|
+
REQUIREMENTS = {
|
|
15
|
+
"Cycle": {
|
|
16
|
+
"completeness.fertiliser": "True",
|
|
17
|
+
"inputs": [
|
|
18
|
+
{
|
|
19
|
+
"@type": "Input",
|
|
20
|
+
"value": "",
|
|
21
|
+
"term.termType": "organicFertiliser",
|
|
22
|
+
"optional": {
|
|
23
|
+
"properties": [{"@type": "Property", "value": "", "term.@id": "nitrogenContent"}]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
RETURNS = {
|
|
30
|
+
"Emission": [{
|
|
31
|
+
"value": "",
|
|
32
|
+
"sd": "",
|
|
33
|
+
"min": "",
|
|
34
|
+
"max": "",
|
|
35
|
+
"methodTier": "tier 1",
|
|
36
|
+
"statsDefinition": "modelled",
|
|
37
|
+
"methodModelDescription": "Aggregated version"
|
|
38
|
+
}]
|
|
39
|
+
}
|
|
40
|
+
LOOKUPS = {
|
|
41
|
+
"organicFertiliser": ["IPCC_2019_FRACGASM_NH3-N", "IPCC_2019_FRACGASM_NH3-N-min", "IPCC_2019_FRACGASM_NH3-N-max"]
|
|
42
|
+
}
|
|
43
|
+
TERM_ID = 'nh3ToAirOrganicFertiliser'
|
|
44
|
+
TIER = EmissionMethodTier.TIER_1.value
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _emission(value: float, min: float, max: float):
|
|
48
|
+
emission = _new_emission(TERM_ID, MODEL)
|
|
49
|
+
emission['value'] = [value]
|
|
50
|
+
emission['min'] = [min]
|
|
51
|
+
emission['max'] = [max]
|
|
52
|
+
emission['methodTier'] = TIER
|
|
53
|
+
emission['statsDefinition'] = EmissionStatsDefinition.MODELLED.value
|
|
54
|
+
emission['methodModelDescription'] = 'Aggregated version'
|
|
55
|
+
return emission
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _input_values(input: dict):
|
|
59
|
+
N_total = list_sum(get_N_total([input]))
|
|
60
|
+
return {
|
|
61
|
+
'id': input.get('term', {}).get('@id'),
|
|
62
|
+
'N': N_total,
|
|
63
|
+
'value': get_lookup_value(input.get('term', {}), LOOKUPS['organicFertiliser'][0]),
|
|
64
|
+
'min': get_lookup_value(input.get('term', {}), LOOKUPS['organicFertiliser'][1]),
|
|
65
|
+
'max': get_lookup_value(input.get('term', {}), LOOKUPS['organicFertiliser'][2])
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _run(cycle: dict):
|
|
70
|
+
inputs = filter_list_term_type(cycle.get('inputs', []), TermTermType.ORGANICFERTILISER)
|
|
71
|
+
input_values = list(map(_input_values, inputs))
|
|
72
|
+
|
|
73
|
+
debugValues(cycle, model=MODEL, term=TERM_ID,
|
|
74
|
+
input_values=log_as_table(input_values))
|
|
75
|
+
|
|
76
|
+
value = list_sum([
|
|
77
|
+
v.get('N', 0) * v.get('value', 0) for v in input_values
|
|
78
|
+
]) * get_atomic_conversion(Units.KG_NH3, Units.TO_N)
|
|
79
|
+
|
|
80
|
+
min = list_sum([
|
|
81
|
+
v.get('N', 0) * v.get('min', 0) for v in input_values
|
|
82
|
+
]) * get_atomic_conversion(Units.KG_NH3, Units.TO_N)
|
|
83
|
+
|
|
84
|
+
max = list_sum([
|
|
85
|
+
v.get('N', 0) * v.get('max', 0) for v in input_values
|
|
86
|
+
]) * get_atomic_conversion(Units.KG_NH3, Units.TO_N)
|
|
87
|
+
|
|
88
|
+
return [_emission(value, min, max)]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _should_run(cycle: dict):
|
|
92
|
+
N_organic_fertiliser = get_organic_fertiliser_N_total(cycle)
|
|
93
|
+
fertiliser_complete = _is_term_type_complete(cycle, 'fertiliser')
|
|
94
|
+
|
|
95
|
+
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
|
96
|
+
N_organic_fertiliser=N_organic_fertiliser,
|
|
97
|
+
term_type_fertiliser_complete=fertiliser_complete)
|
|
98
|
+
|
|
99
|
+
should_run = all([N_organic_fertiliser is not None, fertiliser_complete])
|
|
100
|
+
logShouldRun(cycle, MODEL, TERM_ID, should_run)
|
|
101
|
+
return should_run
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def run(cycle: dict): return _run(cycle) if _should_run(cycle) else []
|
|
@@ -0,0 +1,104 @@
|
|
|
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 list_sum
|
|
4
|
+
|
|
5
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, debugValues, log_as_table
|
|
6
|
+
from hestia_earth.models.utils.blank_node import get_N_total
|
|
7
|
+
from hestia_earth.models.utils.constant import Units, get_atomic_conversion
|
|
8
|
+
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
|
9
|
+
from hestia_earth.models.utils.emission import _new_emission
|
|
10
|
+
from hestia_earth.models.utils.cycle import get_organic_fertiliser_N_total
|
|
11
|
+
from hestia_earth.models.utils.term import get_lookup_value
|
|
12
|
+
from . import MODEL
|
|
13
|
+
|
|
14
|
+
REQUIREMENTS = {
|
|
15
|
+
"Cycle": {
|
|
16
|
+
"completeness.fertiliser": "True",
|
|
17
|
+
"inputs": [
|
|
18
|
+
{
|
|
19
|
+
"@type": "Input",
|
|
20
|
+
"value": "",
|
|
21
|
+
"term.termType": "organicFertiliser",
|
|
22
|
+
"optional": {
|
|
23
|
+
"properties": [{"@type": "Property", "value": "", "term.@id": "nitrogenContent"}]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
RETURNS = {
|
|
30
|
+
"Emission": [{
|
|
31
|
+
"value": "",
|
|
32
|
+
"sd": "",
|
|
33
|
+
"min": "",
|
|
34
|
+
"max": "",
|
|
35
|
+
"methodTier": "tier 1",
|
|
36
|
+
"statsDefinition": "modelled",
|
|
37
|
+
"methodModelDescription": "Aggregated version"
|
|
38
|
+
}]
|
|
39
|
+
}
|
|
40
|
+
LOOKUPS = {
|
|
41
|
+
"organicFertiliser": ["IPCC_2019_FRACGASM_NOx-N", "IPCC_2019_FRACGASM_NOx-N-min", "IPCC_2019_FRACGASM_NOx-N-max"]
|
|
42
|
+
}
|
|
43
|
+
TERM_ID = 'noxToAirOrganicFertiliser'
|
|
44
|
+
TIER = EmissionMethodTier.TIER_1.value
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _emission(value: float, min: float, max: float):
|
|
48
|
+
emission = _new_emission(TERM_ID, MODEL)
|
|
49
|
+
emission['value'] = [value]
|
|
50
|
+
emission['min'] = [min]
|
|
51
|
+
emission['max'] = [max]
|
|
52
|
+
emission['methodTier'] = TIER
|
|
53
|
+
emission['statsDefinition'] = EmissionStatsDefinition.MODELLED.value
|
|
54
|
+
emission['methodModelDescription'] = 'Aggregated version'
|
|
55
|
+
return emission
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _input_values(input: dict):
|
|
59
|
+
N_total = list_sum(get_N_total([input]))
|
|
60
|
+
return {
|
|
61
|
+
'id': input.get('term', {}).get('@id'),
|
|
62
|
+
'N': N_total,
|
|
63
|
+
'value': get_lookup_value(input.get('term', {}), LOOKUPS['organicFertiliser'][0]),
|
|
64
|
+
'min': get_lookup_value(input.get('term', {}), LOOKUPS['organicFertiliser'][1]),
|
|
65
|
+
'max': get_lookup_value(input.get('term', {}), LOOKUPS['organicFertiliser'][2])
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _run(cycle: dict):
|
|
70
|
+
inputs = filter_list_term_type(cycle.get('inputs', []), TermTermType.ORGANICFERTILISER)
|
|
71
|
+
input_values = list(map(_input_values, inputs))
|
|
72
|
+
|
|
73
|
+
debugValues(cycle, model=MODEL, term=TERM_ID,
|
|
74
|
+
input_values=log_as_table(input_values))
|
|
75
|
+
|
|
76
|
+
value = list_sum([
|
|
77
|
+
v.get('N', 0) * v.get('value', 0) for v in input_values
|
|
78
|
+
]) * get_atomic_conversion(Units.KG_NOX, Units.TO_N)
|
|
79
|
+
|
|
80
|
+
min = list_sum([
|
|
81
|
+
v.get('N', 0) * v.get('min', 0) for v in input_values
|
|
82
|
+
]) * get_atomic_conversion(Units.KG_NOX, Units.TO_N)
|
|
83
|
+
|
|
84
|
+
max = list_sum([
|
|
85
|
+
v.get('N', 0) * v.get('max', 0) for v in input_values
|
|
86
|
+
]) * get_atomic_conversion(Units.KG_NOX, Units.TO_N)
|
|
87
|
+
|
|
88
|
+
return [_emission(value, min, max)]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _should_run(cycle: dict):
|
|
92
|
+
N_organic_fertiliser = get_organic_fertiliser_N_total(cycle)
|
|
93
|
+
fertiliser_complete = _is_term_type_complete(cycle, 'fertiliser')
|
|
94
|
+
|
|
95
|
+
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
|
96
|
+
N_organic_fertiliser=N_organic_fertiliser,
|
|
97
|
+
term_type_fertiliser_complete=fertiliser_complete)
|
|
98
|
+
|
|
99
|
+
should_run = all([N_organic_fertiliser is not None, fertiliser_complete])
|
|
100
|
+
logShouldRun(cycle, MODEL, TERM_ID, should_run)
|
|
101
|
+
return should_run
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def run(cycle: dict): return _run(cycle) if _should_run(cycle) else []
|
|
@@ -18,12 +18,15 @@ from typing import (
|
|
|
18
18
|
from hestia_earth.schema import (
|
|
19
19
|
CycleFunctionalUnit,
|
|
20
20
|
MeasurementMethodClassification,
|
|
21
|
+
SchemaType,
|
|
21
22
|
SiteSiteType,
|
|
22
23
|
TermTermType,
|
|
23
24
|
)
|
|
25
|
+
from hestia_earth.utils.api import find_related
|
|
24
26
|
from hestia_earth.utils.model import find_term_match, filter_list_term_type
|
|
25
27
|
from hestia_earth.utils.tools import flatten, list_sum, non_empty_list
|
|
26
28
|
|
|
29
|
+
from hestia_earth.models.utils import _load_calculated_node
|
|
27
30
|
from hestia_earth.models.log import log_as_table, logRequirements, logShouldRun
|
|
28
31
|
from hestia_earth.models.utils.blank_node import (
|
|
29
32
|
cumulative_nodes_match,
|
|
@@ -42,7 +45,6 @@ from hestia_earth.models.utils.measurement import (
|
|
|
42
45
|
_new_measurement,
|
|
43
46
|
)
|
|
44
47
|
from hestia_earth.models.utils.property import get_node_property
|
|
45
|
-
from hestia_earth.models.utils.site import related_cycles
|
|
46
48
|
from hestia_earth.models.utils.term import (
|
|
47
49
|
get_cover_crop_property_terms,
|
|
48
50
|
get_crop_residue_incorporated_or_left_on_field_terms,
|
|
@@ -182,7 +184,7 @@ CARBON_CONTENT_TERM_ID = "carbonContent"
|
|
|
182
184
|
NITROGEN_CONTENT_TERM_ID = "nitrogenContent"
|
|
183
185
|
LIGNIN_CONTENT_TERM_ID = "ligninContent"
|
|
184
186
|
|
|
185
|
-
|
|
187
|
+
CARBON_INPUT_PROPERTY_TERM_IDS = [
|
|
186
188
|
CARBON_CONTENT_TERM_ID,
|
|
187
189
|
NITROGEN_CONTENT_TERM_ID,
|
|
188
190
|
LIGNIN_CONTENT_TERM_ID
|
|
@@ -190,8 +192,7 @@ CROP_RESIDUE_PROPERTY_TERM_IDS = [
|
|
|
190
192
|
|
|
191
193
|
CARBON_SOURCE_TERM_TYPES = [
|
|
192
194
|
TermTermType.ORGANICFERTILISER.value,
|
|
193
|
-
TermTermType.SOILAMENDMENT.value
|
|
194
|
-
TermTermType.SEED.value
|
|
195
|
+
TermTermType.SOILAMENDMENT.value
|
|
195
196
|
]
|
|
196
197
|
|
|
197
198
|
MIN_RUN_IN_PERIOD = 5
|
|
@@ -1834,17 +1835,12 @@ def _iterate_carbon_source(node: dict) -> Union[CarbonSource, None]:
|
|
|
1834
1835
|
CarbonSource | None
|
|
1835
1836
|
A `CarbonSource` named tuple if the node is a carbon source with the required properties, else `None`.
|
|
1836
1837
|
"""
|
|
1837
|
-
term = node.get("term", {})
|
|
1838
1838
|
mass = list_sum(node.get("value", []))
|
|
1839
1839
|
carbon_content, nitrogen_content, lignin_content = (
|
|
1840
|
-
get_node_property(node, term_id
|
|
1840
|
+
get_node_property(node, term_id).get("value", 0)/100 for term_id in CARBON_INPUT_PROPERTY_TERM_IDS
|
|
1841
1841
|
)
|
|
1842
1842
|
|
|
1843
1843
|
should_run = all([
|
|
1844
|
-
any([
|
|
1845
|
-
term.get("@id", None) in get_crop_residue_incorporated_or_left_on_field_terms(),
|
|
1846
|
-
term.get("termType") in CARBON_SOURCE_TERM_TYPES
|
|
1847
|
-
]),
|
|
1848
1844
|
mass > 0,
|
|
1849
1845
|
0 < carbon_content <= 1,
|
|
1850
1846
|
0 < nitrogen_content <= 1,
|
|
@@ -1878,7 +1874,13 @@ def _get_carbon_sources_from_cycles(cycles: dict) -> list[CarbonSource]:
|
|
|
1878
1874
|
[cycle.get("inputs", []) + cycle.get("products", []) for cycle in cycles]
|
|
1879
1875
|
))
|
|
1880
1876
|
|
|
1881
|
-
return non_empty_list([
|
|
1877
|
+
return non_empty_list([
|
|
1878
|
+
_iterate_carbon_source(node) for node in inputs_and_products
|
|
1879
|
+
if any([
|
|
1880
|
+
node.get("term", {}).get("@id") in get_crop_residue_incorporated_or_left_on_field_terms(),
|
|
1881
|
+
node.get("term", {}).get("termType") in CARBON_SOURCE_TERM_TYPES
|
|
1882
|
+
])
|
|
1883
|
+
])
|
|
1882
1884
|
|
|
1883
1885
|
|
|
1884
1886
|
# --- TIER 2 SOC MODEL ---
|
|
@@ -1925,6 +1927,8 @@ def _run_tier_2(
|
|
|
1925
1927
|
The length of the run-in period in years, must be greater than or equal to 1, default value: `5`.
|
|
1926
1928
|
run_with_irrigation : bool, optional
|
|
1927
1929
|
`True` if the model should run while taking into account irrigation, `False` if not.
|
|
1930
|
+
sand_content : float, optional
|
|
1931
|
+
A back-up sand content for if none are found in the inventory, decimal proportion, default value: `0.33`.
|
|
1928
1932
|
params : dict | None, optional
|
|
1929
1933
|
Overrides for the model parameters. If `None` only default parameters will be used.
|
|
1930
1934
|
|
|
@@ -1952,8 +1956,11 @@ def _run_tier_2(
|
|
|
1952
1956
|
)
|
|
1953
1957
|
|
|
1954
1958
|
sand_content = next(
|
|
1955
|
-
|
|
1956
|
-
|
|
1959
|
+
(
|
|
1960
|
+
group[_InventoryKey.SAND_CONTENT] for group in valid_inventory.values()
|
|
1961
|
+
if _InventoryKey.SAND_CONTENT in group
|
|
1962
|
+
),
|
|
1963
|
+
sand_content
|
|
1957
1964
|
)
|
|
1958
1965
|
|
|
1959
1966
|
# --- MERGE ANY USER-SET PARAMETERS WITH THE IPCC DEFAULTS ---
|
|
@@ -3539,7 +3546,7 @@ def _should_run(site: dict) -> tuple[bool, dict]:
|
|
|
3539
3546
|
site_type = site.get("siteType", "")
|
|
3540
3547
|
management_nodes = site.get("management", [])
|
|
3541
3548
|
measurement_nodes = site.get("measurements", [])
|
|
3542
|
-
cycles =
|
|
3549
|
+
cycles = _calculated_cycles(site.get("@id"))
|
|
3543
3550
|
|
|
3544
3551
|
has_management = len(management_nodes) > 0
|
|
3545
3552
|
has_measurements = len(measurement_nodes) > 0
|
|
@@ -3596,6 +3603,26 @@ def _should_run(site: dict) -> tuple[bool, dict]:
|
|
|
3596
3603
|
return should_run_tier_1, should_run_tier_2, inventory, kwargs
|
|
3597
3604
|
|
|
3598
3605
|
|
|
3606
|
+
def _calculated_cycles(site_id: str):
|
|
3607
|
+
"""
|
|
3608
|
+
Get the list of `Cycle`s related to the `Site`. Gets the `recalculated` data if available, else `original`.
|
|
3609
|
+
|
|
3610
|
+
Parameters
|
|
3611
|
+
----------
|
|
3612
|
+
site_id : str
|
|
3613
|
+
The `@id` of the `Site`.
|
|
3614
|
+
|
|
3615
|
+
Returns
|
|
3616
|
+
-------
|
|
3617
|
+
list[dict]
|
|
3618
|
+
The related `Cycle`s as `dict`.
|
|
3619
|
+
"""
|
|
3620
|
+
nodes = find_related(SchemaType.SITE, site_id, SchemaType.CYCLE)
|
|
3621
|
+
return list(
|
|
3622
|
+
map(lambda node: _load_calculated_node(node, SchemaType.CYCLE), nodes or [])
|
|
3623
|
+
)
|
|
3624
|
+
|
|
3625
|
+
|
|
3599
3626
|
def _should_run_tier_1(
|
|
3600
3627
|
inventory: dict,
|
|
3601
3628
|
*,
|
|
@@ -3615,6 +3642,8 @@ def _should_run_tier_1(
|
|
|
3615
3642
|
|
|
3616
3643
|
def _should_run_tier_2(
|
|
3617
3644
|
inventory: dict,
|
|
3645
|
+
*,
|
|
3646
|
+
sand_content: float = None,
|
|
3618
3647
|
**_
|
|
3619
3648
|
) -> bool:
|
|
3620
3649
|
"""
|
|
@@ -3624,7 +3653,7 @@ def _should_run_tier_2(
|
|
|
3624
3653
|
return all([
|
|
3625
3654
|
len(valid_years) >= MIN_RUN_IN_PERIOD,
|
|
3626
3655
|
check_consecutive(valid_years),
|
|
3627
|
-
any(inventory.get(year).get(_InventoryKey.SAND_CONTENT) for year in valid_years)
|
|
3656
|
+
any(inventory.get(year).get(_InventoryKey.SAND_CONTENT) for year in valid_years) or sand_content
|
|
3628
3657
|
])
|
|
3629
3658
|
|
|
3630
3659
|
|
|
@@ -3663,7 +3692,7 @@ def _log_inventory(inventory: dict) -> str:
|
|
|
3663
3692
|
),
|
|
3664
3693
|
"irrigated-monthly": (
|
|
3665
3694
|
" ".join(str(val) for val in group.get(_InventoryKey.IRRIGATED_MONTHLY, []))
|
|
3666
|
-
if group.get(_InventoryKey.
|
|
3695
|
+
if group.get(_InventoryKey.IRRIGATED_MONTHLY) else None
|
|
3667
3696
|
),
|
|
3668
3697
|
"sand-content": group.get(_InventoryKey.SAND_CONTENT, None),
|
|
3669
3698
|
"carbon-input": group.get(_InventoryKey.CARBON_INPUT, None),
|
|
@@ -3715,8 +3744,17 @@ def _build_inventory_tier_2(
|
|
|
3715
3744
|
}
|
|
3716
3745
|
|
|
3717
3746
|
inventory = merge(grouped_data, grouped_should_run)
|
|
3747
|
+
|
|
3748
|
+
# get a back-up value for sand content if no dated ones are available
|
|
3749
|
+
sand_content = get_node_value(find_term_match(
|
|
3750
|
+
[m for m in measurement_nodes if m.get("depthUpper") == DEPTH_UPPER and m.get("depthLower") == DEPTH_LOWER],
|
|
3751
|
+
SAND_CONTENT_TERM_ID,
|
|
3752
|
+
{}
|
|
3753
|
+
)) / 100
|
|
3754
|
+
|
|
3718
3755
|
kwargs = {
|
|
3719
|
-
"run_with_irrigation": True
|
|
3756
|
+
"run_with_irrigation": True,
|
|
3757
|
+
"sand_content": sand_content
|
|
3720
3758
|
}
|
|
3721
3759
|
|
|
3722
3760
|
return inventory, kwargs
|
|
@@ -3811,7 +3849,7 @@ def _get_grouped_sand_content_measurements(grouped_measurements: dict) -> dict:
|
|
|
3811
3849
|
}
|
|
3812
3850
|
|
|
3813
3851
|
return {
|
|
3814
|
-
year: {_InventoryKey.SAND_CONTENT: get_node_value(measurement)}
|
|
3852
|
+
year: {_InventoryKey.SAND_CONTENT: get_node_value(measurement)/100}
|
|
3815
3853
|
for year, measurement in grouped_sand_content_measurements.items() if measurement
|
|
3816
3854
|
}
|
|
3817
3855
|
|
|
@@ -5,8 +5,6 @@ from hestia_earth.utils.tools import safe_parse_float
|
|
|
5
5
|
from hestia_earth.models.log import debugValues
|
|
6
6
|
from hestia_earth.models.utils.input import get_total_irrigation_m3
|
|
7
7
|
from hestia_earth.models.utils.cycle import get_ecoClimateZone
|
|
8
|
-
from hestia_earth.models.utils.constant import Units, get_atomic_conversion
|
|
9
|
-
from hestia_earth.models.utils.blank_node import find_terms_value
|
|
10
8
|
from hestia_earth.models.utils.term import get_lookup_value, get_milkYield_terms
|
|
11
9
|
from hestia_earth.models.utils.ecoClimateZone import get_ecoClimateZone_lookup_value
|
|
12
10
|
from . import MODEL
|
|
@@ -19,16 +17,6 @@ COEFF_N_NH3NOX_organic_animal = [0.21, 0.00, 0.31, 0.0775]
|
|
|
19
17
|
COEFF_N_NH3NOX_inorganic = [0.11, 0.02, 0.33, 0.0775]
|
|
20
18
|
|
|
21
19
|
|
|
22
|
-
def get_nh3_no3_nox_to_n(cycle: dict, nh3_term_id: str, no3_term_id: str, nox_term_id: str):
|
|
23
|
-
nh3 = find_terms_value(cycle.get('emissions', []), nh3_term_id)
|
|
24
|
-
nh3 = nh3 / get_atomic_conversion(Units.KG_NH3, Units.TO_N)
|
|
25
|
-
no3 = find_terms_value(cycle.get('emissions', []), no3_term_id)
|
|
26
|
-
no3 = no3 / get_atomic_conversion(Units.KG_NO3, Units.TO_N)
|
|
27
|
-
nox = find_terms_value(cycle.get('emissions', []), nox_term_id)
|
|
28
|
-
nox = nox / get_atomic_conversion(Units.KG_NOX, Units.TO_N)
|
|
29
|
-
return nh3, no3, nox
|
|
30
|
-
|
|
31
|
-
|
|
32
20
|
def get_FracLEACH_H(cycle: dict, term_id: str):
|
|
33
21
|
eco_climate_zone = get_ecoClimateZone(cycle)
|
|
34
22
|
is_eco_climate_zone_dry = eco_climate_zone % 2 == 0
|
|
@@ -130,6 +118,31 @@ N2O_FACTORS = {
|
|
|
130
118
|
'max': 0.029
|
|
131
119
|
}
|
|
132
120
|
}
|
|
121
|
+
EF4_FACTORS = {
|
|
122
|
+
'dry': {
|
|
123
|
+
'value': 0.005,
|
|
124
|
+
'min': 0,
|
|
125
|
+
'max': 0.011
|
|
126
|
+
|
|
127
|
+
},
|
|
128
|
+
'wet': {
|
|
129
|
+
'value': 0.014,
|
|
130
|
+
'min': 0.011,
|
|
131
|
+
'max': 0.017
|
|
132
|
+
},
|
|
133
|
+
'default': {
|
|
134
|
+
'value': 0.01,
|
|
135
|
+
'min': 0.002,
|
|
136
|
+
'max': 0.018
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
EF5_FACTORS = {
|
|
140
|
+
'default': {
|
|
141
|
+
'value': 0.011,
|
|
142
|
+
'min': 0.0,
|
|
143
|
+
'max': 0.02
|
|
144
|
+
}
|
|
145
|
+
}
|
|
133
146
|
|
|
134
147
|
|
|
135
148
|
def _get_waterRegime_lookup(model_term_id: str, practice: dict, col: str):
|
|
@@ -140,11 +153,10 @@ def _is_wet(ecoClimateZone: str = None):
|
|
|
140
153
|
return get_ecoClimateZone_lookup_value(ecoClimateZone, 'wet') == 1 if ecoClimateZone else None
|
|
141
154
|
|
|
142
155
|
|
|
143
|
-
def
|
|
156
|
+
def ecoClimate_factors(factors: dict, input_term_type: TermTermType = None, ecoClimateZone: str = None):
|
|
144
157
|
is_wet = _is_wet(ecoClimateZone)
|
|
145
158
|
factors_key = 'default' if is_wet is None else 'wet' if is_wet else 'dry'
|
|
146
|
-
factors
|
|
147
|
-
return (factors.get(input_term_type) if factors_key == 'wet' else factors, is_wet is None)
|
|
159
|
+
return (factors[factors_key].get(input_term_type, factors[factors_key]), ecoClimateZone is None)
|
|
148
160
|
|
|
149
161
|
|
|
150
162
|
def _flooded_rice_factors(model_term_id: str, cycle: dict):
|
|
@@ -169,4 +181,4 @@ def get_N2O_factors(
|
|
|
169
181
|
flooded_rice: bool = False
|
|
170
182
|
):
|
|
171
183
|
return _flooded_rice_factors(model_term_id, cycle) if flooded_rice \
|
|
172
|
-
else
|
|
184
|
+
else ecoClimate_factors(N2O_FACTORS, input_term_type, ecoClimateZone)
|