hestia-earth-models 0.50.0__py3-none-any.whl → 0.51.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/agribalyse2016/fuelElectricity.py +7 -4
- hestia_earth/models/agribalyse2016/machineryInfrastructureDepreciatedAmountPerCycle.py +14 -5
- hestia_earth/models/cycle/input/ecoinventV3.py +10 -2
- hestia_earth/models/cycle/input/hestiaAggregatedData.py +5 -2
- hestia_earth/models/emepEea2019/co2ToAirFuelCombustion.py +5 -2
- hestia_earth/models/emepEea2019/n2OToAirFuelCombustionDirect.py +5 -2
- hestia_earth/models/emepEea2019/nh3ToAirExcreta.py +10 -4
- hestia_earth/models/emepEea2019/noxToAirFuelCombustion.py +5 -2
- hestia_earth/models/emepEea2019/so2ToAirFuelCombustion.py +5 -2
- hestia_earth/models/emepEea2019/utils.py +22 -3
- hestia_earth/models/environmentalFootprintV3/{freshwaterEcotoxicityPotentialPaf.py → freshwaterEcotoxicityPotentialCtue.py} +2 -2
- hestia_earth/models/geospatialDatabase/aware.py +5 -4
- hestia_earth/models/geospatialDatabase/ecoregion.py +5 -4
- hestia_earth/models/geospatialDatabase/region.py +7 -11
- hestia_earth/models/geospatialDatabase/utils.py +39 -25
- hestia_earth/models/geospatialDatabase/waterDepth.py +5 -4
- hestia_earth/models/impact_assessment/__init__.py +3 -3
- hestia_earth/models/ipcc2019/ch4ToAirExcreta.py +4 -2
- hestia_earth/models/ipcc2019/croppingDuration.py +1 -1
- hestia_earth/models/ipcc2019/n2OToAirInorganicFertiliserIndirect.py +106 -0
- hestia_earth/models/ipcc2019/n2OToAirOrganicFertiliserIndirect.py +108 -0
- hestia_earth/models/ipcc2019/utils.py +37 -0
- hestia_earth/models/jarvisAndPain1994/__init__.py +13 -0
- hestia_earth/models/jarvisAndPain1994/n2ToAirExcreta.py +53 -0
- hestia_earth/models/koble2014/aboveGroundCropResidue.py +44 -21
- hestia_earth/models/koble2014/utils.py +5 -1
- hestia_earth/models/log.py +19 -0
- hestia_earth/models/mocking/search-results.json +301 -252
- hestia_earth/models/pooreNemecek2018/aboveGroundCropResidueTotal.py +15 -8
- hestia_earth/models/pooreNemecek2018/plantationProductiveLifespan.py +18 -6
- hestia_earth/models/pooreNemecek2018/rotationDuration.py +15 -5
- hestia_earth/models/pooreNemecek2018/utils.py +4 -2
- hestia_earth/models/schmidt2007/__init__.py +13 -0
- hestia_earth/models/schmidt2007/ch4ToAirWasteTreatment.py +60 -0
- hestia_earth/models/schmidt2007/utils.py +16 -0
- hestia_earth/models/transformation/input/excreta.py +4 -2
- hestia_earth/models/transformation/product/excreta.py +2 -2
- hestia_earth/models/usetoxV2/{freshwaterEcotoxicityPotentialPaf.py → freshwaterEcotoxicityPotentialCtue.py} +2 -2
- hestia_earth/models/utils/term.py +6 -0
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.50.0.dist-info → hestia_earth_models-0.51.1.dist-info}/METADATA +2 -2
- {hestia_earth_models-0.50.0.dist-info → hestia_earth_models-0.51.1.dist-info}/RECORD +58 -44
- tests/models/emepEea2019/test_utils.py +17 -3
- tests/models/environmentalFootprintV3/{test_freshwaterEcotoxicityPotentialPaf.py → test_freshwaterEcotoxicityPotentialCtue.py} +1 -1
- tests/models/geospatialDatabase/test_region.py +4 -5
- tests/models/geospatialDatabase/test_utils.py +10 -1
- tests/models/ipcc2019/test_n2OToAirInorganicFertiliserIndirect.py +48 -0
- tests/models/ipcc2019/test_n2OToAirOrganicFertiliserIndirect.py +48 -0
- tests/models/jarvisAndPain1994/__init__.py +0 -0
- tests/models/jarvisAndPain1994/test_n2ToAirExcreta.py +37 -0
- tests/models/koble2014/test_aboveGroundCropResidue.py +13 -0
- tests/models/schmidt2007/__init__.py +0 -0
- tests/models/schmidt2007/test_ch4ToAirWasteTreatment.py +45 -0
- tests/models/schmidt2007/test_utils.py +39 -0
- tests/models/usetoxV2/{test_freshwaterEcotoxicityPotentialPaf.py → test_freshwaterEcotoxicityPotentialCtue.py} +1 -1
- {hestia_earth_models-0.50.0.dist-info → hestia_earth_models-0.51.1.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.50.0.dist-info → hestia_earth_models-0.51.1.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.50.0.dist-info → hestia_earth_models-0.51.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from hestia_earth.schema import EmissionMethodTier, EmissionStatsDefinition, TermTermType
|
|
2
|
+
|
|
3
|
+
from hestia_earth.models.log import debugValues, logRequirements, logShouldRun
|
|
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.cycle import get_inorganic_fertiliser_N_total, get_ecoClimateZone
|
|
7
|
+
from hestia_earth.models.utils.emission import _new_emission
|
|
8
|
+
from .utils import get_nh3_no3_nox_to_n, COEFF_NO3_N2O, COEFF_N_NH3NOX_inorganic, get_FracNH3NOx_N2O, get_FracLEACH_H
|
|
9
|
+
from . import MODEL
|
|
10
|
+
|
|
11
|
+
REQUIREMENTS = {
|
|
12
|
+
"Cycle": {
|
|
13
|
+
"completeness.fertiliser": "True",
|
|
14
|
+
"completeness.water": "True",
|
|
15
|
+
"inputs": [
|
|
16
|
+
{
|
|
17
|
+
"@type": "Input",
|
|
18
|
+
"value": "",
|
|
19
|
+
"term.termType": "inorganicFertiliser",
|
|
20
|
+
"term.units": "kg N"
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"emissions": [
|
|
24
|
+
{"@type": "Emission", "value": "", "term.@id": "no3ToGroundwaterInorganicFertiliser"},
|
|
25
|
+
{"@type": "Emission", "value": "", "term.@id": "nh3ToAirInorganicFertiliser"},
|
|
26
|
+
{"@type": "Emission", "value": "", "term.@id": "noxToAirInorganicFertiliser"}
|
|
27
|
+
],
|
|
28
|
+
"site": {
|
|
29
|
+
"@type": "Site",
|
|
30
|
+
"measurements": [{"@type": "Measurement", "value": "", "term.@id": "ecoClimateZone"}]
|
|
31
|
+
},
|
|
32
|
+
"optional": {
|
|
33
|
+
"practices": [{"@type": "Practice", "value": "", "term.termType": "waterRegime"}]
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
RETURNS = {
|
|
38
|
+
"Emission": [{
|
|
39
|
+
"value": "",
|
|
40
|
+
"sd": "",
|
|
41
|
+
"min": "",
|
|
42
|
+
"max": "",
|
|
43
|
+
"methodTier": "tier 1",
|
|
44
|
+
"statsDefinition": "modelled"
|
|
45
|
+
}]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
TERM_ID = 'n2OToAirInorganicFertiliserIndirect'
|
|
49
|
+
NO3_TERM_ID = 'no3ToGroundwaterInorganicFertiliser'
|
|
50
|
+
NH3_TERM_ID = 'nh3ToAirInorganicFertiliser'
|
|
51
|
+
NOX_TERM_ID = 'noxToAirInorganicFertiliser'
|
|
52
|
+
TIER = EmissionMethodTier.TIER_1.value
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _emission(value: float, sd: float, min: float, max: float):
|
|
56
|
+
emission = _new_emission(TERM_ID, MODEL)
|
|
57
|
+
emission['value'] = [value]
|
|
58
|
+
emission['sd'] = [sd]
|
|
59
|
+
emission['min'] = [min]
|
|
60
|
+
emission['max'] = [max]
|
|
61
|
+
emission['methodTier'] = TIER
|
|
62
|
+
emission['statsDefinition'] = EmissionStatsDefinition.MODELLED.value
|
|
63
|
+
return emission
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _run(cycle: dict):
|
|
67
|
+
N_total = get_inorganic_fertiliser_N_total(cycle)
|
|
68
|
+
nh3_n, no3_n, nox_n = get_nh3_no3_nox_to_n(cycle, NH3_TERM_ID, NO3_TERM_ID, NOX_TERM_ID)
|
|
69
|
+
debugValues(cycle, model=MODEL, term=TERM_ID,
|
|
70
|
+
no3_n=no3_n,
|
|
71
|
+
nh3_n=nh3_n,
|
|
72
|
+
nox_n=nox_n)
|
|
73
|
+
value, min_val, max_val, std = [
|
|
74
|
+
get_FracNH3NOx_N2O(cycle, TERM_ID)[x] * (
|
|
75
|
+
N_total * COEFF_N_NH3NOX_inorganic[x] if nox_n == 0 or nh3_n == 0 else nh3_n + nox_n
|
|
76
|
+
) +
|
|
77
|
+
COEFF_NO3_N2O[x] * (
|
|
78
|
+
N_total * get_FracLEACH_H(cycle, TERM_ID)[x] if no3_n == 0 else no3_n
|
|
79
|
+
) for x in range(4)
|
|
80
|
+
]
|
|
81
|
+
return [_emission(
|
|
82
|
+
value * get_atomic_conversion(Units.KG_N2O, Units.TO_N),
|
|
83
|
+
std * get_atomic_conversion(Units.KG_N2O, Units.TO_N),
|
|
84
|
+
min_val * get_atomic_conversion(Units.KG_N2O, Units.TO_N),
|
|
85
|
+
max_val * get_atomic_conversion(Units.KG_N2O, Units.TO_N)
|
|
86
|
+
)]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _should_run(cycle: dict):
|
|
90
|
+
N_inorganic_fertiliser = get_inorganic_fertiliser_N_total(cycle)
|
|
91
|
+
ecoClimateZone = get_ecoClimateZone(cycle)
|
|
92
|
+
fertiliser_complete = _is_term_type_complete(cycle, {'termType': 'fertiliser'})
|
|
93
|
+
water_complete = _is_term_type_complete(cycle, {'termType': TermTermType.WATER.value})
|
|
94
|
+
|
|
95
|
+
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
|
96
|
+
N_inorganic_fertiliser=N_inorganic_fertiliser,
|
|
97
|
+
ecoClimateZone=ecoClimateZone,
|
|
98
|
+
fertiliser_complete=fertiliser_complete,
|
|
99
|
+
water_complete=water_complete)
|
|
100
|
+
|
|
101
|
+
should_run = all([N_inorganic_fertiliser >= 0, ecoClimateZone, fertiliser_complete, water_complete])
|
|
102
|
+
logShouldRun(cycle, MODEL, TERM_ID, should_run)
|
|
103
|
+
return should_run
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def run(cycle: dict): return _run(cycle) if _should_run(cycle) else []
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from hestia_earth.schema import EmissionMethodTier, EmissionStatsDefinition, TermTermType
|
|
2
|
+
|
|
3
|
+
from hestia_earth.models.log import debugValues, logRequirements, logShouldRun
|
|
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.cycle import get_organic_fertiliser_N_total, get_ecoClimateZone
|
|
7
|
+
from hestia_earth.models.utils.emission import _new_emission
|
|
8
|
+
from .utils import get_nh3_no3_nox_to_n, COEFF_NO3_N2O, COEFF_N_NH3NOX_organic_animal, get_FracNH3NOx_N2O, \
|
|
9
|
+
get_FracLEACH_H
|
|
10
|
+
from . import MODEL
|
|
11
|
+
|
|
12
|
+
REQUIREMENTS = {
|
|
13
|
+
"Cycle": {
|
|
14
|
+
"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
|
+
"emissions": [
|
|
25
|
+
{"@type": "Emission", "value": "", "term.@id": "no3ToGroundwaterInorganicFertiliser"},
|
|
26
|
+
{"@type": "Emission", "value": "", "term.@id": "nh3ToAirInorganicFertiliser"},
|
|
27
|
+
{"@type": "Emission", "value": "", "term.@id": "noxToAirInorganicFertiliser"}
|
|
28
|
+
],
|
|
29
|
+
"site": {
|
|
30
|
+
"@type": "Site",
|
|
31
|
+
"measurements": [{"@type": "Measurement", "value": "", "term.@id": "ecoClimateZone"}]
|
|
32
|
+
},
|
|
33
|
+
"optional": {
|
|
34
|
+
"practices": [{"@type": "Practice", "value": "", "term.termType": "waterRegime"}]
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
RETURNS = {
|
|
39
|
+
"Emission": [{
|
|
40
|
+
"value": "",
|
|
41
|
+
"sd": "",
|
|
42
|
+
"min": "",
|
|
43
|
+
"max": "",
|
|
44
|
+
"methodTier": "tier 1",
|
|
45
|
+
"statsDefinition": "modelled"
|
|
46
|
+
}]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
TERM_ID = 'n2OToAirOrganicFertiliserIndirect'
|
|
50
|
+
NO3_TERM_ID = 'no3ToGroundwaterInorganicFertiliser'
|
|
51
|
+
NH3_TERM_ID = 'nh3ToAirInorganicFertiliser'
|
|
52
|
+
NOX_TERM_ID = 'noxToAirInorganicFertiliser'
|
|
53
|
+
TIER = EmissionMethodTier.TIER_1.value
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _emission(value: float, sd: float, min: float, max: float):
|
|
57
|
+
emission = _new_emission(TERM_ID, MODEL)
|
|
58
|
+
emission['value'] = [value]
|
|
59
|
+
emission['sd'] = [sd]
|
|
60
|
+
emission['min'] = [min]
|
|
61
|
+
emission['max'] = [max]
|
|
62
|
+
emission['methodTier'] = TIER
|
|
63
|
+
emission['statsDefinition'] = EmissionStatsDefinition.MODELLED.value
|
|
64
|
+
return emission
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _run(cycle: dict):
|
|
68
|
+
N_total = get_organic_fertiliser_N_total(cycle)
|
|
69
|
+
nh3_n, no3_n, nox_n = get_nh3_no3_nox_to_n(cycle, NH3_TERM_ID, NO3_TERM_ID, NOX_TERM_ID)
|
|
70
|
+
debugValues(cycle, model=MODEL, term=TERM_ID,
|
|
71
|
+
no3_n=no3_n,
|
|
72
|
+
nh3_n=nh3_n,
|
|
73
|
+
nox_n=nox_n)
|
|
74
|
+
|
|
75
|
+
value, min_val, max_val, std = [
|
|
76
|
+
get_FracNH3NOx_N2O(cycle, TERM_ID)[x] * (
|
|
77
|
+
N_total * COEFF_N_NH3NOX_organic_animal[x] if nox_n == 0 or nh3_n == 0 else nh3_n + nox_n
|
|
78
|
+
) +
|
|
79
|
+
COEFF_NO3_N2O[x] * (
|
|
80
|
+
N_total * get_FracLEACH_H(cycle, TERM_ID)[x] if no3_n == 0 else no3_n
|
|
81
|
+
) for x in range(4)
|
|
82
|
+
]
|
|
83
|
+
return [_emission(
|
|
84
|
+
value * get_atomic_conversion(Units.KG_N2O, Units.TO_N),
|
|
85
|
+
std * get_atomic_conversion(Units.KG_N2O, Units.TO_N),
|
|
86
|
+
min_val * get_atomic_conversion(Units.KG_N2O, Units.TO_N),
|
|
87
|
+
max_val * get_atomic_conversion(Units.KG_N2O, Units.TO_N)
|
|
88
|
+
)]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _should_run(cycle: dict):
|
|
92
|
+
N_organic_fertiliser = get_organic_fertiliser_N_total(cycle)
|
|
93
|
+
ecoClimateZone = get_ecoClimateZone(cycle)
|
|
94
|
+
fertiliser_complete = _is_term_type_complete(cycle, {'termType': 'fertiliser'})
|
|
95
|
+
water_complete = _is_term_type_complete(cycle, {'termType': TermTermType.WATER.value})
|
|
96
|
+
|
|
97
|
+
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
|
98
|
+
N_organic_fertiliser=N_organic_fertiliser,
|
|
99
|
+
ecoClimateZone=ecoClimateZone,
|
|
100
|
+
fertiliser_complete=fertiliser_complete,
|
|
101
|
+
water_complete=water_complete)
|
|
102
|
+
|
|
103
|
+
should_run = all([N_organic_fertiliser >= 0, ecoClimateZone, fertiliser_complete, water_complete])
|
|
104
|
+
logShouldRun(cycle, MODEL, TERM_ID, should_run)
|
|
105
|
+
return should_run
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def run(cycle: dict): return _run(cycle) if _should_run(cycle) else []
|
|
@@ -4,6 +4,25 @@ from hestia_earth.models.log import debugValues
|
|
|
4
4
|
from hestia_earth.models.utils.input import get_total_irrigation_m3
|
|
5
5
|
from hestia_earth.models.utils.cycle import get_ecoClimateZone
|
|
6
6
|
from . import MODEL
|
|
7
|
+
from hestia_earth.models.utils.constant import Units, get_atomic_conversion
|
|
8
|
+
from hestia_earth.models.utils.blank_node import find_terms_value
|
|
9
|
+
|
|
10
|
+
# From IPCC2019 Indirect N2O emission factor, in N [avg, min, max, std]
|
|
11
|
+
COEFF_NO3_N2O = [0.011, 0.00, 0.02, 0.005]
|
|
12
|
+
# Volatilized Nitrogen as NH3-N and NOx-N per kg N applied organic fertilisers and animal dung and urine
|
|
13
|
+
COEFF_N_NH3NOX_organic_animal = [0.21, 0.00, 0.31, 0.0775]
|
|
14
|
+
# Volatilized Nitrogen as NH3-N and NOx-N per kg N applied inorganic fertilisers
|
|
15
|
+
COEFF_N_NH3NOX_inorganic = [0.11, 0.02, 0.33, 0.0775]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def get_nh3_no3_nox_to_n(cycle: dict, nh3_term_id: str, no3_term_id: str, nox_term_id: str):
|
|
19
|
+
nh3 = find_terms_value(cycle.get('emissions', []), nh3_term_id)
|
|
20
|
+
nh3 = nh3 / get_atomic_conversion(Units.KG_NH3, Units.TO_N)
|
|
21
|
+
no3 = find_terms_value(cycle.get('emissions', []), no3_term_id)
|
|
22
|
+
no3 = no3 / get_atomic_conversion(Units.KG_NO3, Units.TO_N)
|
|
23
|
+
nox = find_terms_value(cycle.get('emissions', []), nox_term_id)
|
|
24
|
+
nox = nox / get_atomic_conversion(Units.KG_NOX, Units.TO_N)
|
|
25
|
+
return nh3, no3, nox
|
|
7
26
|
|
|
8
27
|
|
|
9
28
|
def get_FracLEACH_H(cycle: dict, term_id: str):
|
|
@@ -21,3 +40,21 @@ def get_FracLEACH_H(cycle: dict, term_id: str):
|
|
|
21
40
|
is_eco_climate_zone_dry,
|
|
22
41
|
any([irrigation_value_m3 <= 250, is_drip_irrigated])
|
|
23
42
|
]) else (0.24, 0.01, 0.73, 0.18) # value, min, max, sd
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# Indirect N2O emissions from volatilized NH3 and NOx
|
|
46
|
+
def get_FracNH3NOx_N2O(cycle: dict, term_id: str):
|
|
47
|
+
eco_climate_zone = get_ecoClimateZone(cycle)
|
|
48
|
+
is_eco_climate_zone_dry = eco_climate_zone % 2 == 0
|
|
49
|
+
irrigation_value_m3 = get_total_irrigation_m3(cycle)
|
|
50
|
+
is_drip_irrigated = find_term_match(cycle.get('practices', []), 'irrigatedDripIrrigation', None) is not None
|
|
51
|
+
|
|
52
|
+
debugValues(cycle, model=MODEL, term=term_id,
|
|
53
|
+
is_eco_climate_zone_dry=is_eco_climate_zone_dry,
|
|
54
|
+
irrigation_value_m3=irrigation_value_m3,
|
|
55
|
+
is_drip_irrigated=is_drip_irrigated)
|
|
56
|
+
|
|
57
|
+
return (0.005, 0, 0.011, 0.00275) if all([
|
|
58
|
+
is_eco_climate_zone_dry,
|
|
59
|
+
any([irrigation_value_m3 <= 250, is_drip_irrigated])
|
|
60
|
+
]) else (0.014, 0.011, 0.017, 0.0015) # value, min, max, sd
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from os.path import dirname, abspath
|
|
2
|
+
import sys
|
|
3
|
+
from importlib import import_module
|
|
4
|
+
|
|
5
|
+
from hestia_earth.models.utils.blank_node import run_if_required
|
|
6
|
+
|
|
7
|
+
CURRENT_DIR = dirname(abspath(__file__)) + '/'
|
|
8
|
+
sys.path.append(CURRENT_DIR)
|
|
9
|
+
MODEL = 'jarvisAndPain1994'
|
|
10
|
+
PKG = '.'.join(['hestia_earth', 'models', MODEL])
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def run(model: str, data): return run_if_required(MODEL, model, data, import_module(f".{model}", package=PKG))
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from hestia_earth.models.utils.constant import Units, get_atomic_conversion
|
|
2
|
+
from hestia_earth.utils.model import find_term_match
|
|
3
|
+
from hestia_earth.utils.tools import list_sum
|
|
4
|
+
from hestia_earth.schema import EmissionMethodTier, EmissionStatsDefinition
|
|
5
|
+
from . import MODEL
|
|
6
|
+
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
7
|
+
from hestia_earth.models.utils.emission import _new_emission
|
|
8
|
+
|
|
9
|
+
REQUIREMENTS = {
|
|
10
|
+
"Cycle": {
|
|
11
|
+
"emissions": [
|
|
12
|
+
{"@type": "Emission", "value": "", "term.@id": "n2OToAirExcretaDirect"}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
RETURNS = {
|
|
17
|
+
"Emission": [{
|
|
18
|
+
"value": "",
|
|
19
|
+
"methodTier": "tier 1",
|
|
20
|
+
"statsDefinition": "modelled"
|
|
21
|
+
}]
|
|
22
|
+
}
|
|
23
|
+
TERM_ID = 'n2ToAirExcreta'
|
|
24
|
+
TIER = EmissionMethodTier.TIER_1.value
|
|
25
|
+
N2O_TERM_ID = 'n2OToAirExcretaDirect'
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _emission(value: float):
|
|
29
|
+
emission = _new_emission(TERM_ID, MODEL)
|
|
30
|
+
emission['value'] = [value]
|
|
31
|
+
emission['methodTier'] = TIER
|
|
32
|
+
emission['statsDefinition'] = EmissionStatsDefinition.MODELLED.value
|
|
33
|
+
return emission
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _run(n2o: dict):
|
|
37
|
+
value = 3 * list_sum(n2o.get("value", [])) / get_atomic_conversion(Units.KG_N2O, Units.TO_N)
|
|
38
|
+
return [_emission(value)]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _should_run(cycle: dict):
|
|
42
|
+
n2o = find_term_match(cycle.get('emissions', []), N2O_TERM_ID)
|
|
43
|
+
|
|
44
|
+
logRequirements(cycle, model=MODEL, term=TERM_ID, has_n2o=n2o is not None)
|
|
45
|
+
|
|
46
|
+
should_run = all([n2o])
|
|
47
|
+
logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
|
|
48
|
+
return should_run, n2o
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def run(cycle: dict):
|
|
52
|
+
should_run, n2o = _should_run(cycle)
|
|
53
|
+
return _run(n2o) if should_run else []
|
|
@@ -9,10 +9,10 @@ This model returns the amounts and destinations of above ground crop residue, wo
|
|
|
9
9
|
"""
|
|
10
10
|
from functools import reduce
|
|
11
11
|
from hestia_earth.schema import ProductStatsDefinition
|
|
12
|
-
from hestia_earth.utils.model import
|
|
12
|
+
from hestia_earth.utils.model import find_term_match
|
|
13
13
|
from hestia_earth.utils.tools import flatten, list_sum, list_average
|
|
14
14
|
|
|
15
|
-
from hestia_earth.models.log import logRequirements, logShouldRun,
|
|
15
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, debugValues
|
|
16
16
|
from hestia_earth.models.utils.product import _new_product
|
|
17
17
|
from hestia_earth.models.utils.completeness import _is_term_type_incomplete
|
|
18
18
|
from . import MODEL
|
|
@@ -52,6 +52,10 @@ MODELS = [
|
|
|
52
52
|
REMAINING_MODEL = 'aboveGroundCropResidueLeftOnField'
|
|
53
53
|
|
|
54
54
|
|
|
55
|
+
def _get_practices(term_id: str):
|
|
56
|
+
return flatten([model.get('practices', []) for model in MODELS if model.get('term') == term_id])
|
|
57
|
+
|
|
58
|
+
|
|
55
59
|
def _get_practice_value(term_ids: list, cycle: dict) -> float:
|
|
56
60
|
# multiple practices starting with the `@id` might be present, group together
|
|
57
61
|
values = flatten([
|
|
@@ -66,23 +70,28 @@ def _product(term_id: str, value: float):
|
|
|
66
70
|
return product
|
|
67
71
|
|
|
68
72
|
|
|
69
|
-
def _should_run_model(model, cycle: dict,
|
|
73
|
+
def _should_run_model(model, cycle: dict, total_value: float):
|
|
70
74
|
term_id = model.get('term')
|
|
71
75
|
practice_value = _get_practice_value(model.get('practices'), cycle)
|
|
72
76
|
has_product = find_term_match(cycle.get('products', []), term_id, None) is not None
|
|
73
77
|
|
|
74
78
|
logRequirements(cycle, model=MODEL, term=term_id,
|
|
75
79
|
practice_value=practice_value,
|
|
76
|
-
primary_product=(primary_product or {}).get('@id'),
|
|
77
80
|
has_product=has_product)
|
|
78
81
|
|
|
79
|
-
should_run = all([
|
|
82
|
+
should_run = all([
|
|
83
|
+
any([
|
|
84
|
+
practice_value == 0,
|
|
85
|
+
practice_value is not None and total_value > 0
|
|
86
|
+
]),
|
|
87
|
+
not has_product
|
|
88
|
+
])
|
|
80
89
|
logShouldRun(cycle, MODEL, term_id, should_run)
|
|
81
90
|
return should_run, practice_value
|
|
82
91
|
|
|
83
92
|
|
|
84
|
-
def _run_model(model, cycle: dict,
|
|
85
|
-
should_run, practice_value = _should_run_model(model, cycle,
|
|
93
|
+
def _run_model(model, cycle: dict, total_value: float):
|
|
94
|
+
should_run, practice_value = _should_run_model(model, cycle, total_value)
|
|
86
95
|
return total_value * practice_value if should_run else None
|
|
87
96
|
|
|
88
97
|
|
|
@@ -93,7 +102,6 @@ def _model_value(term_id: str, products: list):
|
|
|
93
102
|
|
|
94
103
|
def _run(cycle: dict, total_values: list):
|
|
95
104
|
products = cycle.get('products', [])
|
|
96
|
-
primary_product = find_primary_product(cycle)
|
|
97
105
|
total_value = list_average(total_values)
|
|
98
106
|
# first, calculate the remaining value available after applying all user-uploaded data
|
|
99
107
|
remaining_value = reduce(
|
|
@@ -106,14 +114,19 @@ def _run(cycle: dict, total_values: list):
|
|
|
106
114
|
# then run every model in order up to the remaining value
|
|
107
115
|
for model in MODELS:
|
|
108
116
|
term_id = model.get('term')
|
|
109
|
-
value = _run_model(model, cycle,
|
|
110
|
-
|
|
111
|
-
|
|
117
|
+
value = _run_model(model, cycle, total_value)
|
|
118
|
+
debugValues(cycle, model=MODEL, term=term_id,
|
|
119
|
+
total_value=total_value,
|
|
120
|
+
remaining_value=remaining_value,
|
|
121
|
+
value=value)
|
|
122
|
+
|
|
123
|
+
if value == 0:
|
|
124
|
+
values.extend([_product(term_id, value)])
|
|
125
|
+
elif remaining_value > 0 and value is not None and value >= 0:
|
|
112
126
|
value = value if value < remaining_value else remaining_value
|
|
113
127
|
values.extend([_product(term_id, value)])
|
|
114
128
|
remaining_value = remaining_value - value
|
|
115
129
|
if remaining_value == 0:
|
|
116
|
-
logger.debug('model=%s, term=%s, no more residue - stopping', MODEL, term_id)
|
|
117
130
|
break
|
|
118
131
|
|
|
119
132
|
return values + [
|
|
@@ -122,19 +135,29 @@ def _run(cycle: dict, total_values: list):
|
|
|
122
135
|
] if remaining_value > 0 else values
|
|
123
136
|
|
|
124
137
|
|
|
125
|
-
def
|
|
138
|
+
def _should_run_product(cycle: dict, total_values: list, term_id: str):
|
|
126
139
|
term_type_incomplete = _is_term_type_incomplete(cycle, TOTAL_TERM_ID)
|
|
127
|
-
total_values = find_term_match(cycle.get('products', []), TOTAL_TERM_ID).get('value', [])
|
|
128
140
|
has_aboveGroundCropResidueTotal = len(total_values) > 0
|
|
141
|
+
practice_term_ids = _get_practices(term_id)
|
|
142
|
+
is_value_0 = any([
|
|
143
|
+
find_term_match(cycle.get('practices', []), term_id).get('value', []) == [0] for term_id in practice_term_ids
|
|
144
|
+
])
|
|
145
|
+
|
|
146
|
+
logRequirements(cycle, model=MODEL, term=term_id,
|
|
147
|
+
term_type_cropResidue_incomplete=term_type_incomplete,
|
|
148
|
+
has_aboveGroundCropResidueTotal=has_aboveGroundCropResidueTotal,
|
|
149
|
+
practice_term_ids=';'.join(practice_term_ids),
|
|
150
|
+
practice_value_is_0=is_value_0)
|
|
151
|
+
should_run = all([has_aboveGroundCropResidueTotal or is_value_0, term_type_incomplete])
|
|
152
|
+
logShouldRun(cycle, MODEL, term_id, should_run)
|
|
153
|
+
return should_run
|
|
129
154
|
|
|
130
|
-
should_run = all([has_aboveGroundCropResidueTotal, term_type_incomplete])
|
|
131
|
-
for term_id in TERM_ID.split(','):
|
|
132
|
-
logRequirements(cycle, model=MODEL, term=term_id,
|
|
133
|
-
term_type_cropResidue_incomplete=term_type_incomplete,
|
|
134
|
-
has_aboveGroundCropResidueTotal=has_aboveGroundCropResidueTotal)
|
|
135
|
-
logShouldRun(cycle, MODEL, term_id, should_run)
|
|
136
155
|
|
|
137
|
-
|
|
156
|
+
def _should_run(cycle: dict):
|
|
157
|
+
total_values = find_term_match(cycle.get('products', []), TOTAL_TERM_ID).get('value', [])
|
|
158
|
+
return any([
|
|
159
|
+
_should_run_product(cycle, total_values, term_id) for term_id in TERM_ID.split(',')
|
|
160
|
+
]), total_values
|
|
138
161
|
|
|
139
162
|
|
|
140
163
|
def run(cycle: dict):
|
|
@@ -3,7 +3,7 @@ from hestia_earth.schema import TermTermType, PracticeStatsDefinition
|
|
|
3
3
|
from hestia_earth.utils.model import find_primary_product, find_term_match
|
|
4
4
|
from hestia_earth.utils.tools import list_sum
|
|
5
5
|
|
|
6
|
-
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
6
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
|
|
7
7
|
from hestia_earth.models.utils.completeness import _is_term_type_incomplete
|
|
8
8
|
from hestia_earth.models.utils.practice import _new_practice
|
|
9
9
|
from hestia_earth.models.utils.term import get_crop_residue_management_terms
|
|
@@ -28,6 +28,9 @@ def _should_run(term_id: str, cycle: dict, require_country: bool = False):
|
|
|
28
28
|
practices = cycle.get('practices', [])
|
|
29
29
|
residue_terms = get_crop_residue_management_terms()
|
|
30
30
|
remaining_value = reduce(lambda prev, term: prev - _model_value(term, practices), residue_terms, 100)
|
|
31
|
+
residue_values = log_as_table([
|
|
32
|
+
{'id': term_id, 'value': _model_value(term_id, practices)} for term_id in residue_terms
|
|
33
|
+
])
|
|
31
34
|
has_remaining_value = remaining_value > 0
|
|
32
35
|
|
|
33
36
|
country_id = cycle.get('site', {}).get('country', {}).get('@id')
|
|
@@ -36,6 +39,7 @@ def _should_run(term_id: str, cycle: dict, require_country: bool = False):
|
|
|
36
39
|
has_primary_product=has_primary_product,
|
|
37
40
|
crop_residue_incomplete=crop_residue_incomplete,
|
|
38
41
|
has_remaining_value=has_remaining_value,
|
|
42
|
+
crop_residue_values=residue_values,
|
|
39
43
|
country_id=country_id)
|
|
40
44
|
|
|
41
45
|
should_run = all([
|
hestia_earth/models/log.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import sys
|
|
3
3
|
import logging
|
|
4
|
+
from typing import Union
|
|
4
5
|
|
|
5
6
|
LOG_LEVEL = os.getenv('LOG_LEVEL', 'INFO')
|
|
6
7
|
|
|
@@ -69,3 +70,21 @@ def debugMissingLookup(lookup_name: str, row: str, row_value: str, col: str, val
|
|
|
69
70
|
|
|
70
71
|
def logErrorRun(model: str, term: str, error: str):
|
|
71
72
|
logger.error('model=%s, term=%s, error=%s', model, term, error)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def log_as_table(values: Union[list, dict]):
|
|
76
|
+
"""
|
|
77
|
+
Log a list of values to display as a table.
|
|
78
|
+
Can either use a single dictionary, represented using id/value pair,
|
|
79
|
+
or a list of dictionaries using their keys as columns.
|
|
80
|
+
|
|
81
|
+
Parameters
|
|
82
|
+
----------
|
|
83
|
+
values : list | dict
|
|
84
|
+
Values to display as a table.
|
|
85
|
+
"""
|
|
86
|
+
return ';'.join([
|
|
87
|
+
f"id:{k}_value:{v}" for k, v in values.items()
|
|
88
|
+
] if isinstance(values, dict) else [
|
|
89
|
+
'_'.join([f"{k}:{v}" for k, v in value.items()]) for value in values
|
|
90
|
+
])
|