hestia-earth-models 0.61.7__py3-none-any.whl → 0.62.0__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/completeness/electricityFuel.py +60 -0
- hestia_earth/models/cycle/product/economicValueShare.py +47 -31
- hestia_earth/models/emepEea2019/nh3ToAirInorganicFertiliser.py +44 -59
- hestia_earth/models/geospatialDatabase/histosol.py +4 -0
- hestia_earth/models/ipcc2006/co2ToAirOrganicSoilCultivation.py +4 -2
- hestia_earth/models/ipcc2006/n2OToAirOrganicSoilCultivationDirect.py +1 -1
- hestia_earth/models/ipcc2019/aboveGroundCropResidueTotal.py +1 -1
- hestia_earth/models/ipcc2019/animal/pastureGrass.py +30 -24
- hestia_earth/models/ipcc2019/belowGroundCropResidue.py +1 -1
- hestia_earth/models/ipcc2019/ch4ToAirExcreta.py +1 -1
- hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChangeManagementChange.py +511 -458
- hestia_earth/models/ipcc2019/co2ToAirUreaHydrolysis.py +5 -1
- hestia_earth/models/ipcc2019/organicCarbonPerHa.py +116 -3882
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1_utils.py +2060 -0
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2_utils.py +1630 -0
- hestia_earth/models/ipcc2019/organicCarbonPerHa_utils.py +324 -0
- hestia_earth/models/ipcc2019/pastureGrass.py +37 -19
- hestia_earth/models/ipcc2019/pastureGrass_utils.py +4 -21
- hestia_earth/models/mocking/search-results.json +293 -289
- hestia_earth/models/site/organicCarbonPerHa.py +58 -44
- hestia_earth/models/site/soilMeasurement.py +18 -13
- hestia_earth/models/utils/__init__.py +28 -0
- hestia_earth/models/utils/array_builders.py +578 -0
- hestia_earth/models/utils/blank_node.py +55 -39
- hestia_earth/models/utils/descriptive_stats.py +285 -0
- hestia_earth/models/utils/emission.py +73 -2
- hestia_earth/models/utils/inorganicFertiliser.py +2 -2
- hestia_earth/models/utils/measurement.py +118 -4
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.61.7.dist-info → hestia_earth_models-0.62.0.dist-info}/METADATA +2 -2
- {hestia_earth_models-0.61.7.dist-info → hestia_earth_models-0.62.0.dist-info}/RECORD +51 -39
- tests/models/cycle/completeness/test_electricityFuel.py +21 -0
- tests/models/cycle/product/test_economicValueShare.py +8 -0
- tests/models/emepEea2019/test_nh3ToAirInorganicFertiliser.py +2 -2
- tests/models/ipcc2019/animal/test_pastureGrass.py +2 -2
- tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChangeManagementChange.py +55 -165
- tests/models/ipcc2019/test_organicCarbonPerHa.py +219 -460
- tests/models/ipcc2019/test_organicCarbonPerHa_tier_1_utils.py +471 -0
- tests/models/ipcc2019/test_organicCarbonPerHa_tier_2_utils.py +208 -0
- tests/models/ipcc2019/test_organicCarbonPerHa_utils.py +75 -0
- tests/models/ipcc2019/test_pastureGrass.py +0 -16
- tests/models/site/test_organicCarbonPerHa.py +3 -12
- tests/models/site/test_soilMeasurement.py +3 -18
- tests/models/utils/test_array_builders.py +253 -0
- tests/models/utils/test_blank_node.py +154 -15
- tests/models/utils/test_descriptive_stats.py +134 -0
- tests/models/utils/test_emission.py +51 -1
- tests/models/utils/test_measurement.py +54 -2
- {hestia_earth_models-0.61.7.dist-info → hestia_earth_models-0.62.0.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.61.7.dist-info → hestia_earth_models-0.62.0.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.61.7.dist-info → hestia_earth_models-0.62.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Completeness Electricity Fuel
|
|
3
|
+
|
|
4
|
+
This model checks if we have the requirements below and updates the
|
|
5
|
+
[Data Completeness](https://hestia.earth/schema/Completeness#electricityFuel) value.
|
|
6
|
+
"""
|
|
7
|
+
from hestia_earth.schema import TermTermType
|
|
8
|
+
from hestia_earth.utils.model import filter_list_term_type
|
|
9
|
+
|
|
10
|
+
from hestia_earth.models.log import logRequirements, log_as_table
|
|
11
|
+
from hestia_earth.models.utils.blank_node import get_lookup_value
|
|
12
|
+
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
|
13
|
+
from . import MODEL
|
|
14
|
+
|
|
15
|
+
REQUIREMENTS = {
|
|
16
|
+
"Cycle": {
|
|
17
|
+
"completeness.operation": "True",
|
|
18
|
+
"completeness.electricityFuel": "False",
|
|
19
|
+
"practices": [
|
|
20
|
+
{"@type": "Practice", "value": "", "term.termType": "operation"}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
RETURNS = {
|
|
25
|
+
"Completeness": {
|
|
26
|
+
"electricityFuel": ""
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
LOOKUPS = {
|
|
30
|
+
"operation": ["fuelUse", "combustionType"]
|
|
31
|
+
}
|
|
32
|
+
MODEL_KEY = 'electricityFuel'
|
|
33
|
+
_VALID_COMBUSTION_TYPES = ['mobile', 'stationary']
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _lookup_value(practice: dict, lookup_name: str):
|
|
37
|
+
return get_lookup_value(practice.get('term', {}), lookup_name, model=MODEL, model_key=MODEL_KEY)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _practice_value(practice: dict):
|
|
41
|
+
term = practice.get('term', {})
|
|
42
|
+
fuel_use = _lookup_value(practice, LOOKUPS['operation'][0])
|
|
43
|
+
return {'id': term.get('@id'), 'fuel_use': fuel_use}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def run(cycle: dict):
|
|
47
|
+
operation_complete = _is_term_type_complete(cycle, TermTermType.OPERATION)
|
|
48
|
+
practices = filter_list_term_type(cycle.get('practices', []), TermTermType.OPERATION)
|
|
49
|
+
combustion_practices = [
|
|
50
|
+
p for p in practices if _lookup_value(p, LOOKUPS['operation'][1]) in _VALID_COMBUSTION_TYPES
|
|
51
|
+
]
|
|
52
|
+
practices_values = list(map(_practice_value, combustion_practices))
|
|
53
|
+
|
|
54
|
+
logRequirements(cycle, model=MODEL, term=None, key=MODEL_KEY,
|
|
55
|
+
term_type_operation_complete=operation_complete,
|
|
56
|
+
values=log_as_table(practices_values))
|
|
57
|
+
|
|
58
|
+
is_complete = all([operation_complete] + [p.get('fuel_use') for p in practices_values])
|
|
59
|
+
|
|
60
|
+
return is_complete
|
|
@@ -2,20 +2,21 @@
|
|
|
2
2
|
Product Economic Value Share
|
|
3
3
|
|
|
4
4
|
This model quantifies the relative economic value share of each marketable Product in a Cycle.
|
|
5
|
-
Marketable Products are all Products
|
|
5
|
+
Marketable Products are all Products identified with the lookup "generateImpactAssessment = TRUE".
|
|
6
6
|
|
|
7
7
|
It works in the following order:
|
|
8
|
-
1.
|
|
8
|
+
1. For every product with no `value`, the `economicValueShare` is set to `0`.
|
|
9
|
+
2. If revenue data are provided for all marketable products,
|
|
9
10
|
the `economicValueShare` is directly calculated as the share of revenue of each Product;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
3. Based on a lookup table containing typical global average economic value shares
|
|
12
|
+
drawn from [Poore & Nemecek (2018)](https://science.sciencemag.org/content/360/6392/987), if all products have a
|
|
13
|
+
corresponding value, the `economicValueShare` will be proportionally distributed among the products.
|
|
13
14
|
"""
|
|
14
|
-
from hestia_earth.schema import TermTermType
|
|
15
15
|
from hestia_earth.utils.model import find_term_match
|
|
16
16
|
from hestia_earth.utils.tools import list_sum
|
|
17
17
|
|
|
18
18
|
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
19
|
+
from hestia_earth.models.utils.blank_node import get_lookup_value
|
|
19
20
|
from hestia_earth.models.utils.cycle import unique_currencies
|
|
20
21
|
from .utils import lookup_share
|
|
21
22
|
from .. import MODEL
|
|
@@ -25,9 +26,11 @@ REQUIREMENTS = {
|
|
|
25
26
|
"products": [{
|
|
26
27
|
"@type": "Product",
|
|
27
28
|
"value": "",
|
|
28
|
-
"
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
"optional": {
|
|
30
|
+
"economicValueShare": "",
|
|
31
|
+
"revenue": "",
|
|
32
|
+
"currency": ""
|
|
33
|
+
}
|
|
31
34
|
}],
|
|
32
35
|
"optional": {
|
|
33
36
|
"completeness.product": ""
|
|
@@ -42,7 +45,8 @@ RETURNS = {
|
|
|
42
45
|
LOOKUPS = {
|
|
43
46
|
"@doc": "Depending on the primary product [termType](https://hestia.earth/schema/Product#term)",
|
|
44
47
|
"crop": "global_economic_value_share",
|
|
45
|
-
"excreta": "global_economic_value_share"
|
|
48
|
+
"excreta": "global_economic_value_share",
|
|
49
|
+
"animalProduct": "global_economic_value_share"
|
|
46
50
|
}
|
|
47
51
|
MODEL_KEY = 'economicValueShare'
|
|
48
52
|
MAX_VALUE = 100.5
|
|
@@ -60,7 +64,7 @@ def _is_complete(cycle: dict): return cycle.get('completeness', {}).get('product
|
|
|
60
64
|
def _no_yield(product): return list_sum(product.get('value', [-1]), -1) == 0
|
|
61
65
|
|
|
62
66
|
|
|
63
|
-
def
|
|
67
|
+
def _total_evs(products: list): return sum([p.get(MODEL_KEY, 0) for p in products])
|
|
64
68
|
|
|
65
69
|
|
|
66
70
|
def _product_with_value(product: dict):
|
|
@@ -72,18 +76,33 @@ def _rescale_value(products: list, total: float):
|
|
|
72
76
|
return list(map(lambda p: {**p, MODEL_KEY: p.get(MODEL_KEY) * 100 / total}, products))
|
|
73
77
|
|
|
74
78
|
|
|
79
|
+
def _should_run_by_default(cycle: dict, products: list):
|
|
80
|
+
run_by = 'default'
|
|
81
|
+
# only run if all products have the lookup data or value will be incorrect
|
|
82
|
+
products = list(map(_product_with_value, products))
|
|
83
|
+
all_with_economicValueShare = all([p.get(MODEL_KEY) is not None for p in products])
|
|
84
|
+
|
|
85
|
+
should_run = all([all_with_economicValueShare])
|
|
86
|
+
|
|
87
|
+
for p in products:
|
|
88
|
+
term_id = p.get('term', {}).get('@id')
|
|
89
|
+
logRequirements(cycle, model=MODEL, term=term_id, key=MODEL_KEY, by=run_by,
|
|
90
|
+
all_with_economicValueShare=all_with_economicValueShare)
|
|
91
|
+
logShouldRun(cycle, MODEL, term_id, should_run, key=MODEL_KEY, by=run_by)
|
|
92
|
+
|
|
93
|
+
return should_run
|
|
94
|
+
|
|
95
|
+
|
|
75
96
|
def _run_by_default(cycle: dict, products: list):
|
|
76
97
|
run_by = 'default'
|
|
77
98
|
is_complete = _is_complete(cycle)
|
|
78
99
|
products = list(map(_product_with_value, products))
|
|
79
|
-
# remove results where lookup share was not found
|
|
80
|
-
results = list(filter(lambda p: p.get(MODEL_KEY) is not None, products))
|
|
81
100
|
# only return list if the new total of evs is not above threshold
|
|
82
|
-
|
|
83
|
-
below_max_threshold =
|
|
84
|
-
should_rescale = is_complete and MIN_COMPLETE_VALUE <=
|
|
85
|
-
above_min_threshold = True if should_rescale else
|
|
86
|
-
results = _rescale_value(
|
|
101
|
+
total_economicValueShare = _total_evs(products)
|
|
102
|
+
below_max_threshold = total_economicValueShare <= MAX_VALUE
|
|
103
|
+
should_rescale = is_complete and MIN_COMPLETE_VALUE <= total_economicValueShare <= MAX_VALUE
|
|
104
|
+
above_min_threshold = True if should_rescale else total_economicValueShare >= MIN_VALUE if is_complete else True
|
|
105
|
+
results = _rescale_value(products, total_economicValueShare) if should_rescale else products
|
|
87
106
|
|
|
88
107
|
should_run = all([below_max_threshold, above_min_threshold])
|
|
89
108
|
|
|
@@ -93,7 +112,7 @@ def _run_by_default(cycle: dict, products: list):
|
|
|
93
112
|
logRequirements(cycle, model=MODEL, term=term_id, key=MODEL_KEY, by=run_by,
|
|
94
113
|
below_max_threshold=below_max_threshold,
|
|
95
114
|
above_min_threshold=above_min_threshold,
|
|
96
|
-
|
|
115
|
+
total_economicValueShare=total_economicValueShare)
|
|
97
116
|
logShouldRun(cycle, MODEL, term_id, p_should_run, key=MODEL_KEY, by=run_by)
|
|
98
117
|
|
|
99
118
|
return results if should_run else []
|
|
@@ -109,8 +128,8 @@ def _run_by_revenue(products: list):
|
|
|
109
128
|
def _should_run_by_revenue(cycle: dict, products: list):
|
|
110
129
|
run_by = 'revenue'
|
|
111
130
|
is_complete = _is_complete(cycle)
|
|
112
|
-
|
|
113
|
-
below_threshold =
|
|
131
|
+
total_economicValueShare = _total_evs(products)
|
|
132
|
+
below_threshold = total_economicValueShare < MAX_VALUE
|
|
114
133
|
# ignore products with no yield
|
|
115
134
|
products = list(filter(lambda p: not _no_yield(p), products))
|
|
116
135
|
currencies = unique_currencies(cycle)
|
|
@@ -122,7 +141,7 @@ def _should_run_by_revenue(cycle: dict, products: list):
|
|
|
122
141
|
term_id = p.get('term', {}).get('@id')
|
|
123
142
|
logRequirements(cycle, model=MODEL, term=term_id, key=MODEL_KEY, by=run_by,
|
|
124
143
|
is_complete=is_complete,
|
|
125
|
-
|
|
144
|
+
total_economicValueShare=total_economicValueShare,
|
|
126
145
|
below_threshold=below_threshold,
|
|
127
146
|
all_with_revenue=all_with_revenue,
|
|
128
147
|
currencies=';'.join(currencies),
|
|
@@ -133,14 +152,14 @@ def _should_run_by_revenue(cycle: dict, products: list):
|
|
|
133
152
|
|
|
134
153
|
|
|
135
154
|
def _run_single_missing_evs(products: list):
|
|
136
|
-
total_value =
|
|
155
|
+
total_value = _total_evs(products)
|
|
137
156
|
return list(map(lambda p: _product(p, 100 - total_value) if p.get(MODEL_KEY) is None else p, products))
|
|
138
157
|
|
|
139
158
|
|
|
140
159
|
def _should_run_single_missing_evs(cycle: dict, products: list):
|
|
141
160
|
run_by = '1-missing-evs'
|
|
142
161
|
is_complete = _is_complete(cycle)
|
|
143
|
-
total_value =
|
|
162
|
+
total_value = _total_evs(products)
|
|
144
163
|
# ignore products with no yield
|
|
145
164
|
products = list(filter(lambda p: not _no_yield(p), products))
|
|
146
165
|
missing_values = [p for p in products if p.get(MODEL_KEY) is None]
|
|
@@ -175,20 +194,17 @@ def _should_run_no_value(cycle: dict, product: dict):
|
|
|
175
194
|
|
|
176
195
|
|
|
177
196
|
def _should_have_evs(product: dict):
|
|
178
|
-
|
|
179
|
-
return
|
|
180
|
-
TermTermType.CROPRESIDUE.value,
|
|
181
|
-
TermTermType.EXCRETA.value
|
|
182
|
-
]
|
|
197
|
+
should_generate_ia = get_lookup_value(product.get('term', {}), 'generateImpactAssessment')
|
|
198
|
+
return str(should_generate_ia).lower() != 'false'
|
|
183
199
|
|
|
184
200
|
|
|
185
201
|
def run(cycle: dict):
|
|
186
202
|
products = cycle.get('products', [])
|
|
187
|
-
# skip any product that
|
|
203
|
+
# skip any product that is not marketable
|
|
188
204
|
products = list(filter(_should_have_evs, products))
|
|
189
205
|
products = list(map(lambda p: _product(p, 0) if _should_run_no_value(cycle, p) else p, products))
|
|
190
206
|
return (
|
|
191
207
|
_run_single_missing_evs(products) if _should_run_single_missing_evs(cycle, products) else
|
|
192
208
|
_run_by_revenue(products) if _should_run_by_revenue(cycle, products) else
|
|
193
|
-
_run_by_default(cycle, products)
|
|
209
|
+
_run_by_default(cycle, products) if _should_run_by_default(cycle, products) else []
|
|
194
210
|
)
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
from functools import reduce
|
|
2
2
|
from hestia_earth.schema import EmissionMethodTier
|
|
3
|
-
from hestia_earth.utils.lookup import download_lookup
|
|
4
3
|
from hestia_earth.utils.model import find_term_match
|
|
5
|
-
from hestia_earth.utils.tools import list_sum
|
|
4
|
+
from hestia_earth.utils.tools import list_sum, non_empty_list
|
|
6
5
|
|
|
7
|
-
from hestia_earth.models.log import
|
|
6
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
|
|
8
7
|
from hestia_earth.models.utils import _filter_list_term_unit
|
|
9
8
|
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
|
10
9
|
from hestia_earth.models.utils.inorganicFertiliser import (
|
|
11
|
-
get_NH3_emission_factor, get_terms, get_term_lookup,
|
|
10
|
+
get_NH3_emission_factor, get_terms, get_term_lookup, get_country_breakdown, get_cycle_inputs
|
|
12
11
|
)
|
|
13
12
|
from hestia_earth.models.utils.constant import Units
|
|
14
13
|
from hestia_earth.models.utils.emission import _new_emission
|
|
@@ -66,23 +65,21 @@ def _emission(value: float):
|
|
|
66
65
|
return emission
|
|
67
66
|
|
|
68
67
|
|
|
69
|
-
def
|
|
68
|
+
def _input_with_factor(soilPh: float, temperature: float):
|
|
70
69
|
def get_value(input: dict):
|
|
71
70
|
term_id = input.get('term', {}).get('@id')
|
|
72
|
-
factor = get_NH3_emission_factor(term_id, soilPh, temperature)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
71
|
+
factor = get_NH3_emission_factor(term_id, soilPh, temperature) if all([
|
|
72
|
+
soilPh is not None,
|
|
73
|
+
temperature is not None
|
|
74
|
+
]) else None
|
|
75
|
+
value = list_sum(input.get('value'), None)
|
|
76
|
+
return {'id': term_id, 'value': value, 'factor': factor} if all([
|
|
77
|
+
value is not None,
|
|
78
|
+
factor is not None
|
|
79
|
+
]) else None
|
|
79
80
|
return get_value
|
|
80
81
|
|
|
81
82
|
|
|
82
|
-
def _run(cycle: dict, temperature: float, soilPh: float, inputs: float):
|
|
83
|
-
return list_sum(list(map(_get_input_value(cycle, soilPh, temperature), inputs)))
|
|
84
|
-
|
|
85
|
-
|
|
86
83
|
def _get_groupings():
|
|
87
84
|
term_ids = get_terms()
|
|
88
85
|
|
|
@@ -93,23 +90,22 @@ def _get_groupings():
|
|
|
93
90
|
return reduce(get_grouping, term_ids, {})
|
|
94
91
|
|
|
95
92
|
|
|
96
|
-
def
|
|
97
|
-
|
|
98
|
-
value = get_country_breakdown(MODEL, TERM_ID, country_id, grouping)
|
|
99
|
-
debugValues(cycle, model=MODEL, term=TERM_ID,
|
|
100
|
-
grouping=grouping,
|
|
101
|
-
NH3_factor=factor,
|
|
102
|
-
country_breakdown=value)
|
|
103
|
-
return value * factor
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
def _run_with_unspecified(cycle: dict, temperature: float, soilPh: float, unspecifiedKgN_value: float, country_id: str):
|
|
93
|
+
def _unspecified_inputs_with_factor(temperature: float, soilPh: float, unspecifiedKgN_value: float, site: dict):
|
|
94
|
+
country_id = site.get('country', {}).get('@id')
|
|
107
95
|
# creates a dictionary grouping => term_id with only a single key per group (avoid counting twice)
|
|
108
96
|
groupings = _get_groupings()
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
97
|
+
breakdown_inputs = [(
|
|
98
|
+
term_id, get_country_breakdown(MODEL, TERM_ID, country_id, grouping)
|
|
99
|
+
) for grouping, term_id in groupings.items()] if all([country_id, unspecifiedKgN_value is not None]) else []
|
|
100
|
+
# create inputs from country breakdown
|
|
101
|
+
N_inputs = [
|
|
102
|
+
{
|
|
103
|
+
'term': {'@id': term_id},
|
|
104
|
+
'value': [value * unspecifiedKgN_value]
|
|
105
|
+
}
|
|
106
|
+
for term_id, value in breakdown_inputs if value is not None
|
|
107
|
+
]
|
|
108
|
+
return non_empty_list(map(_input_with_factor(soilPh, temperature), N_inputs))
|
|
113
109
|
|
|
114
110
|
|
|
115
111
|
def _should_run(cycle: dict):
|
|
@@ -121,49 +117,38 @@ def _should_run(cycle: dict):
|
|
|
121
117
|
measurements, 'temperatureAnnual', end_date) or most_relevant_measurement_value(
|
|
122
118
|
measurements, 'temperatureLongTermAnnualMean', end_date)
|
|
123
119
|
|
|
124
|
-
|
|
125
|
-
N_inputs = _filter_list_term_unit(inputs, Units.KG_N)
|
|
120
|
+
N_inputs = _filter_list_term_unit(get_cycle_inputs(cycle), Units.KG_N)
|
|
126
121
|
has_N_inputs = len(N_inputs) > 0
|
|
127
122
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
has_unspecifiedKgN = len(unspecifiedKgN) > 0 or fertiliser_complete
|
|
123
|
+
N_inputs_with_factor = non_empty_list(map(_input_with_factor(soilPh, temperature), N_inputs))
|
|
124
|
+
has_N_inputs_with_factor = len(N_inputs_with_factor) > 0
|
|
131
125
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
126
|
+
# fallback using country averages of fertilisers usage
|
|
127
|
+
unspecifiedKgN_value = list_sum(find_term_match(N_inputs, UNSPECIFIED_TERM_ID).get('value'), None)
|
|
128
|
+
unspecified_inputs_with_factor = _unspecified_inputs_with_factor(temperature, soilPh, unspecifiedKgN_value, site)
|
|
129
|
+
has_unspecified_inputs_with_factor = len(unspecified_inputs_with_factor) > 0
|
|
135
130
|
|
|
136
|
-
|
|
137
|
-
unspecifiedKgN = (
|
|
138
|
-
0 if len(unspecifiedKgN) == 0 and fertiliser_complete else list_sum(unspecifiedKgN, None)
|
|
139
|
-
) if run_with_unspecified else None
|
|
131
|
+
fertiliser_complete = _is_term_type_complete(cycle, 'fertiliser')
|
|
140
132
|
|
|
141
133
|
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
|
134
|
+
term_type_fertiliser_complete=fertiliser_complete,
|
|
142
135
|
temperature=temperature,
|
|
143
136
|
soilPh=soilPh,
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
run_with_unspecified=run_with_unspecified,
|
|
148
|
-
has_N_inputs=has_N_inputs)
|
|
137
|
+
has_N_inputs=has_N_inputs,
|
|
138
|
+
inorganic_fertiliser_inputs=log_as_table(N_inputs_with_factor),
|
|
139
|
+
unspecified_fertiliser_inputs=log_as_table(unspecified_inputs_with_factor))
|
|
149
140
|
|
|
150
141
|
should_run = all([
|
|
142
|
+
fertiliser_complete,
|
|
151
143
|
temperature,
|
|
152
144
|
soilPh,
|
|
153
|
-
|
|
154
|
-
run_with_unspecified,
|
|
155
|
-
has_N_inputs,
|
|
156
|
-
not has_N_inputs and fertiliser_complete
|
|
157
|
-
])
|
|
145
|
+
not has_N_inputs or has_N_inputs_with_factor or has_unspecified_inputs_with_factor
|
|
158
146
|
])
|
|
159
147
|
logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
|
|
160
|
-
return should_run,
|
|
148
|
+
return should_run, N_inputs_with_factor or unspecified_inputs_with_factor
|
|
161
149
|
|
|
162
150
|
|
|
163
151
|
def run(cycle: dict):
|
|
164
|
-
should_run,
|
|
165
|
-
value =
|
|
166
|
-
_run_with_unspecified(cycle, temperature, soilPh, unspecifiedKgN, country_id)
|
|
167
|
-
if unspecifiedKgN is not None else None
|
|
168
|
-
) if should_run else None
|
|
152
|
+
should_run, N_inputs_with_factor = _should_run(cycle)
|
|
153
|
+
value = list_sum([i.get('value') * i.get('factor') for i in N_inputs_with_factor]) if should_run else None
|
|
169
154
|
return [_emission(value)] if value is not None else []
|
|
@@ -21,6 +21,8 @@ REQUIREMENTS = {
|
|
|
21
21
|
RETURNS = {
|
|
22
22
|
"Measurement": [{
|
|
23
23
|
"value": "",
|
|
24
|
+
"depthUpper": "0",
|
|
25
|
+
"depthLower": "30",
|
|
24
26
|
"methodClassification": "geospatial dataset"
|
|
25
27
|
}]
|
|
26
28
|
}
|
|
@@ -36,6 +38,8 @@ BIBLIO_TITLE = 'Harmonized World Soil Database Version 1.2. Food and Agriculture
|
|
|
36
38
|
def _measurement(site: dict, value: float):
|
|
37
39
|
measurement = _new_measurement(TERM_ID)
|
|
38
40
|
measurement['value'] = [round(value, 7)]
|
|
41
|
+
measurement['depthUpper'] = 0
|
|
42
|
+
measurement['depthLower'] = 30
|
|
39
43
|
measurement['methodClassification'] = MeasurementMethodClassification.GEOSPATIAL_DATASET.value
|
|
40
44
|
return measurement | get_source(site, BIBLIO_TITLE)
|
|
41
45
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from hestia_earth.schema import EmissionMethodTier
|
|
2
2
|
|
|
3
3
|
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
4
|
+
from hestia_earth.models.utils.constant import Units, get_atomic_conversion
|
|
4
5
|
from hestia_earth.models.utils.emission import _new_emission
|
|
5
6
|
from hestia_earth.models.utils.measurement import most_relevant_measurement_value
|
|
6
7
|
from hestia_earth.models.utils.ecoClimateZone import get_ecoClimateZone_lookup_value
|
|
@@ -39,7 +40,6 @@ RETURNS = {
|
|
|
39
40
|
}
|
|
40
41
|
TERM_ID = 'co2ToAirOrganicSoilCultivation'
|
|
41
42
|
TIER = EmissionMethodTier.TIER_1.value
|
|
42
|
-
CONVERT_FACTOR = 44 / 120
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
def _emission(value: float):
|
|
@@ -55,7 +55,9 @@ def _run(histosol: float, organic_soil_factor: float, land_occupation: float):
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
def _get_CO2_factor(eco_climate_zone: str, site_type: str):
|
|
58
|
-
return get_ecoClimateZone_lookup_value(
|
|
58
|
+
return get_ecoClimateZone_lookup_value(
|
|
59
|
+
eco_climate_zone, LOOKUPS['ecoClimateZone'], site_type
|
|
60
|
+
) * 1000 * get_atomic_conversion(Units.KG_CO2, Units.TO_C)
|
|
59
61
|
|
|
60
62
|
|
|
61
63
|
def _should_run(cycle: dict):
|
|
@@ -57,7 +57,7 @@ def _run(histosol: float, organic_soil_factor: float, land_occupation: float):
|
|
|
57
57
|
def _get_N2O_factor(eco_climate_zone: str):
|
|
58
58
|
return get_ecoClimateZone_lookup_value(
|
|
59
59
|
eco_climate_zone, LOOKUPS['ecoClimateZone']
|
|
60
|
-
) * get_atomic_conversion(Units.KG_N2O, Units.TO_N)
|
|
60
|
+
) * get_atomic_conversion(Units.KG_N2O, Units.TO_N)
|
|
61
61
|
|
|
62
62
|
|
|
63
63
|
def _should_run(cycle: dict):
|
|
@@ -75,7 +75,7 @@ def _should_run(cycle: dict):
|
|
|
75
75
|
term_type_incomplete = _is_term_type_incomplete(cycle, TERM_ID)
|
|
76
76
|
|
|
77
77
|
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
|
78
|
-
|
|
78
|
+
has_crop_forage_products_with_dryMatter=has_crop_forage_products,
|
|
79
79
|
term_type_cropResidue_incomplete=term_type_incomplete)
|
|
80
80
|
|
|
81
81
|
should_run = all([term_type_incomplete, has_crop_forage_products])
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Animal Pasture Grass
|
|
3
3
|
|
|
4
4
|
This model estimates the energetic requirements of ruminants and can be used to estimate the amount of grass they graze.
|
|
5
5
|
Source:
|
|
6
6
|
[IPCC 2019, Vol.4, Chapter 10](https://www.ipcc-nggip.iges.or.jp/public/2019rf/pdf/4_Volume4/19R_V4_Ch10_Livestock.pdf).
|
|
7
|
+
|
|
8
|
+
This version of the model will run at the Animal Blank Node level, if none of the Cycle Input are given as feed
|
|
9
|
+
(see https://www.hestia.earth/schema/Input#isAnimalFeed).
|
|
7
10
|
"""
|
|
8
11
|
from hestia_earth.schema import TermTermType
|
|
9
12
|
from hestia_earth.utils.model import filter_list_term_type
|
|
@@ -138,7 +141,7 @@ RETURNS = {
|
|
|
138
141
|
}]
|
|
139
142
|
}]
|
|
140
143
|
}
|
|
141
|
-
MODEL_KEY = '
|
|
144
|
+
MODEL_KEY = 'pastureGrass'
|
|
142
145
|
|
|
143
146
|
|
|
144
147
|
def _input(term_id: str, value: float):
|
|
@@ -173,7 +176,7 @@ def calculate_NEwool(cycle: dict, animal: dict, products: list, total_weight: fl
|
|
|
173
176
|
])
|
|
174
177
|
animal_weight = _sum_liveWeightPerHead([animal])
|
|
175
178
|
|
|
176
|
-
debugValues(
|
|
179
|
+
debugValues(animal, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
177
180
|
total_energy=total_energy,
|
|
178
181
|
animal_liveWeightPerHead=animal_weight,
|
|
179
182
|
total_liveWeightPerHead=total_weight)
|
|
@@ -189,7 +192,13 @@ def _calculate_GE(
|
|
|
189
192
|
NEm, NEa, NEl, NEwork, NEp, NEg = get_animal_values(cycle, animal, system)
|
|
190
193
|
|
|
191
194
|
NEm_feed, NEg_feed = calculate_NEfeed(animal)
|
|
192
|
-
debugValues(
|
|
195
|
+
debugValues(animal, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
196
|
+
NEm=NEm,
|
|
197
|
+
NEa=NEa,
|
|
198
|
+
NEl=NEl,
|
|
199
|
+
NEwork=NEwork,
|
|
200
|
+
NEp=NEp,
|
|
201
|
+
NEg=NEg,
|
|
193
202
|
NEm_feed=NEm_feed,
|
|
194
203
|
NEg_feed=NEg_feed)
|
|
195
204
|
|
|
@@ -233,7 +242,7 @@ def _run_animal(cycle: dict, meanDE: float, meanECHHV: float, system: dict, prac
|
|
|
233
242
|
) else 0
|
|
234
243
|
GE = (_calculate_GE(cycle, animal, REM, REG, NEwool, system) / (meanDE/100)) if all([REM, REG]) else 0
|
|
235
244
|
|
|
236
|
-
debugValues(
|
|
245
|
+
debugValues(animal, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
237
246
|
REM=REM,
|
|
238
247
|
REG=REG,
|
|
239
248
|
NEwool=NEwool,
|
|
@@ -248,17 +257,12 @@ def _run_animal(cycle: dict, meanDE: float, meanECHHV: float, system: dict, prac
|
|
|
248
257
|
return run
|
|
249
258
|
|
|
250
259
|
|
|
251
|
-
def
|
|
252
|
-
animals = get_animals(cycle)
|
|
253
|
-
return list(map(_run_animal(cycle, meanDE, meanECHHV, system, practices), animals))
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
def _should_run(cycle: dict, practices: dict):
|
|
260
|
+
def _should_run(cycle: dict, animals: list, practices: dict):
|
|
257
261
|
systems = filter_list_term_type(cycle.get('practices', []), TermTermType.SYSTEM)
|
|
258
262
|
animalFeed_complete = _is_term_type_complete(cycle, 'animalFeed')
|
|
259
263
|
animalPopulation_complete = _is_term_type_complete(cycle, 'animalPopulation')
|
|
260
264
|
freshForage_incomplete = _is_term_type_incomplete(cycle, 'freshForage')
|
|
261
|
-
all_animals_have_value = all([a.get('value', 0) > 0 for a in
|
|
265
|
+
all_animals_have_value = all([a.get('value', 0) > 0 for a in animals])
|
|
262
266
|
|
|
263
267
|
no_cycle_inputs_feed = all([not input.get('isAnimalFeed', False) for input in cycle.get('inputs', [])])
|
|
264
268
|
|
|
@@ -278,21 +282,23 @@ def _should_run(cycle: dict, practices: dict):
|
|
|
278
282
|
])
|
|
279
283
|
|
|
280
284
|
for term_id in [MODEL_KEY] + [practice_input_id(p) for p in practices]:
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
285
|
+
for animal in animals:
|
|
286
|
+
logRequirements(animal, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
287
|
+
term_type_animalFeed_complete=animalFeed_complete,
|
|
288
|
+
term_type_animalPopulation_complete=animalPopulation_complete,
|
|
289
|
+
term_type_freshForage_incomplete=freshForage_incomplete,
|
|
290
|
+
no_cycle_inputs_feed=no_cycle_inputs_feed,
|
|
291
|
+
all_animals_have_value=all_animals_have_value,
|
|
292
|
+
meanDE=meanDE,
|
|
293
|
+
meanECHHV=meanECHHV)
|
|
294
|
+
|
|
295
|
+
logShouldRun(animal, MODEL, term_id, should_run, model_key=MODEL_KEY)
|
|
291
296
|
|
|
292
297
|
return should_run, meanDE, meanECHHV, systems[0] if systems else None
|
|
293
298
|
|
|
294
299
|
|
|
295
300
|
def run(cycle: dict):
|
|
301
|
+
animals = get_animals(cycle)
|
|
296
302
|
practices = list(filter(should_run_practice(cycle), cycle.get('practices', [])))
|
|
297
|
-
should_run, meanDE, meanECHHV, system = _should_run(cycle, practices)
|
|
298
|
-
return
|
|
303
|
+
should_run, meanDE, meanECHHV, system = _should_run(cycle, animals, practices)
|
|
304
|
+
return list(map(_run_animal(cycle, meanDE, meanECHHV, system, practices), animals)) if should_run else []
|
|
@@ -82,7 +82,7 @@ def _should_run(cycle: dict):
|
|
|
82
82
|
term_type_incomplete = _is_term_type_incomplete(cycle, TERM_ID)
|
|
83
83
|
|
|
84
84
|
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
|
85
|
-
|
|
85
|
+
has_crop_forage_products_with_dryMatter=has_crop_forage_products,
|
|
86
86
|
term_type_cropResidue_incomplete=term_type_incomplete)
|
|
87
87
|
|
|
88
88
|
should_run = all([term_type_incomplete, has_crop_forage_products])
|
|
@@ -95,7 +95,7 @@ def _get_excreta_b0(country: dict, input: dict):
|
|
|
95
95
|
)
|
|
96
96
|
|
|
97
97
|
|
|
98
|
-
def _get_excretaManagement_MCF_from_lookup(term_id: str, ecoClimateZone: int, duration_key:
|
|
98
|
+
def _get_excretaManagement_MCF_from_lookup(term_id: str, ecoClimateZone: int, duration_key: DURATION):
|
|
99
99
|
lookup_name = 'excretaManagement-ecoClimateZone-CH4conv.csv'
|
|
100
100
|
lookup = download_lookup(lookup_name)
|
|
101
101
|
data_values = get_table_value(lookup, 'termid', term_id, str(ecoClimateZone))
|