hestia-earth-models 0.61.8__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 +5 -1
- hestia_earth/models/cycle/product/economicValueShare.py +47 -31
- hestia_earth/models/ipcc2019/animal/pastureGrass.py +30 -24
- hestia_earth/models/ipcc2019/organicCarbonPerHa.py +4 -6
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2_utils.py +10 -10
- hestia_earth/models/ipcc2019/pastureGrass.py +37 -19
- hestia_earth/models/ipcc2019/pastureGrass_utils.py +4 -21
- hestia_earth/models/mocking/search-results.json +237 -233
- hestia_earth/models/utils/blank_node.py +53 -36
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.61.8.dist-info → hestia_earth_models-0.62.0.dist-info}/METADATA +2 -2
- {hestia_earth_models-0.61.8.dist-info → hestia_earth_models-0.62.0.dist-info}/RECORD +21 -21
- tests/models/cycle/product/test_economicValueShare.py +8 -0
- tests/models/ipcc2019/animal/test_pastureGrass.py +2 -2
- tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChangeManagementChange.py +1 -0
- tests/models/ipcc2019/test_organicCarbonPerHa.py +1 -1
- tests/models/ipcc2019/test_pastureGrass.py +0 -16
- tests/models/utils/test_blank_node.py +154 -15
- {hestia_earth_models-0.61.8.dist-info → hestia_earth_models-0.62.0.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.61.8.dist-info → hestia_earth_models-0.62.0.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.61.8.dist-info → hestia_earth_models-0.62.0.dist-info}/top_level.txt +0 -0
|
@@ -9,10 +9,12 @@ from hestia_earth.utils.model import filter_list_term_type
|
|
|
9
9
|
|
|
10
10
|
from hestia_earth.models.log import logRequirements, log_as_table
|
|
11
11
|
from hestia_earth.models.utils.blank_node import get_lookup_value
|
|
12
|
+
from hestia_earth.models.utils.completeness import _is_term_type_complete
|
|
12
13
|
from . import MODEL
|
|
13
14
|
|
|
14
15
|
REQUIREMENTS = {
|
|
15
16
|
"Cycle": {
|
|
17
|
+
"completeness.operation": "True",
|
|
16
18
|
"completeness.electricityFuel": "False",
|
|
17
19
|
"practices": [
|
|
18
20
|
{"@type": "Practice", "value": "", "term.termType": "operation"}
|
|
@@ -42,6 +44,7 @@ def _practice_value(practice: dict):
|
|
|
42
44
|
|
|
43
45
|
|
|
44
46
|
def run(cycle: dict):
|
|
47
|
+
operation_complete = _is_term_type_complete(cycle, TermTermType.OPERATION)
|
|
45
48
|
practices = filter_list_term_type(cycle.get('practices', []), TermTermType.OPERATION)
|
|
46
49
|
combustion_practices = [
|
|
47
50
|
p for p in practices if _lookup_value(p, LOOKUPS['operation'][1]) in _VALID_COMBUSTION_TYPES
|
|
@@ -49,8 +52,9 @@ def run(cycle: dict):
|
|
|
49
52
|
practices_values = list(map(_practice_value, combustion_practices))
|
|
50
53
|
|
|
51
54
|
logRequirements(cycle, model=MODEL, term=None, key=MODEL_KEY,
|
|
55
|
+
term_type_operation_complete=operation_complete,
|
|
52
56
|
values=log_as_table(practices_values))
|
|
53
57
|
|
|
54
|
-
is_complete = all([p.get('fuel_use') for p in practices_values])
|
|
58
|
+
is_complete = all([operation_complete] + [p.get('fuel_use') for p in practices_values])
|
|
55
59
|
|
|
56
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,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 []
|
|
@@ -139,13 +139,11 @@ def run(site: dict) -> list[dict]:
|
|
|
139
139
|
"""
|
|
140
140
|
should_run, run_data = _should_run(site)
|
|
141
141
|
_log_data(site, should_run, run_data)
|
|
142
|
-
return
|
|
143
|
-
(
|
|
144
|
-
|
|
145
|
-
if run_data[method]["should_run"]
|
|
146
|
-
),
|
|
142
|
+
return reduce(
|
|
143
|
+
lambda result, method: result + _run_method(method, **run_data[method]),
|
|
144
|
+
run_data,
|
|
147
145
|
list()
|
|
148
|
-
)
|
|
146
|
+
)
|
|
149
147
|
|
|
150
148
|
|
|
151
149
|
def _should_run(site: dict) -> tuple[bool, dict[ModuleType, dict]]:
|
|
@@ -25,7 +25,7 @@ from hestia_earth.models.utils.array_builders import (
|
|
|
25
25
|
)
|
|
26
26
|
from hestia_earth.models.utils.blank_node import (
|
|
27
27
|
cumulative_nodes_lookup_match, cumulative_nodes_term_match, get_node_value, group_nodes_by_year,
|
|
28
|
-
group_nodes_by_year_and_month, GroupNodesByYearMode
|
|
28
|
+
group_nodes_by_year_and_month, GroupNodesByYearMode, node_term_match
|
|
29
29
|
)
|
|
30
30
|
from hestia_earth.models.utils.cycle import check_cycle_site_ids_identical
|
|
31
31
|
from hestia_earth.models.utils.descriptive_stats import calc_descriptive_stats
|
|
@@ -1227,15 +1227,15 @@ def _check_12_months(inner_dict: dict, keys: set[Any]):
|
|
|
1227
1227
|
def _get_grouped_climate_measurements(grouped_measurements: dict) -> dict:
|
|
1228
1228
|
return {
|
|
1229
1229
|
year: {
|
|
1230
|
-
_InventoryKey.TEMP_MONTHLY: non_empty_list(
|
|
1231
|
-
|
|
1232
|
-
),
|
|
1233
|
-
_InventoryKey.PRECIP_MONTHLY: non_empty_list(
|
|
1234
|
-
|
|
1235
|
-
),
|
|
1236
|
-
_InventoryKey.PET_MONTHLY: non_empty_list(
|
|
1237
|
-
|
|
1238
|
-
)
|
|
1230
|
+
_InventoryKey.TEMP_MONTHLY: non_empty_list(flatten(
|
|
1231
|
+
node.get("value", []) for node in measurements if node_term_match(node, _TEMPERATURE_MONTHLY_TERM_ID)
|
|
1232
|
+
)),
|
|
1233
|
+
_InventoryKey.PRECIP_MONTHLY: non_empty_list(flatten(
|
|
1234
|
+
node.get("value", []) for node in measurements if node_term_match(node, _PRECIPITATION_MONTHLY_TERM_ID)
|
|
1235
|
+
)),
|
|
1236
|
+
_InventoryKey.PET_MONTHLY: non_empty_list(flatten(
|
|
1237
|
+
node.get("value", []) for node in measurements if node_term_match(node, _PET_MONTHLY_TERM_ID)
|
|
1238
|
+
))
|
|
1239
1239
|
} for year, measurements in grouped_measurements.items()
|
|
1240
1240
|
}
|
|
1241
1241
|
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Cycle 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 Cycle level, if at least one Cycle Input is 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
|
|
10
13
|
from hestia_earth.utils.tools import list_sum
|
|
11
14
|
|
|
12
|
-
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
15
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, debugValues
|
|
13
16
|
from hestia_earth.models.utils.input import _new_input
|
|
14
17
|
from hestia_earth.models.utils.term import get_wool_terms
|
|
15
18
|
from hestia_earth.models.utils.completeness import _is_term_type_complete, _is_term_type_incomplete
|
|
@@ -30,7 +33,7 @@ from .pastureGrass_utils import (
|
|
|
30
33
|
REQUIREMENTS = {
|
|
31
34
|
"Cycle": {
|
|
32
35
|
"completeness.animalFeed": "True",
|
|
33
|
-
"completeness.animalPopulation": "
|
|
36
|
+
"completeness.animalPopulation": "True",
|
|
34
37
|
"completeness.freshForage": "False",
|
|
35
38
|
"site": {
|
|
36
39
|
"@type": "Site",
|
|
@@ -45,6 +48,19 @@ REQUIREMENTS = {
|
|
|
45
48
|
"term.termType": "landCover"
|
|
46
49
|
}
|
|
47
50
|
}],
|
|
51
|
+
"inputs": [{
|
|
52
|
+
"@type": "Input",
|
|
53
|
+
"term.units": "kg",
|
|
54
|
+
"value": "> 0",
|
|
55
|
+
"isAnimalFeed": "True",
|
|
56
|
+
"optional": {
|
|
57
|
+
"properties": [{
|
|
58
|
+
"@type": "Property",
|
|
59
|
+
"value": "",
|
|
60
|
+
"term.@id": ["neutralDetergentFibreContent", "energyContentHigherHeatingValue"]
|
|
61
|
+
}]
|
|
62
|
+
}
|
|
63
|
+
}],
|
|
48
64
|
"animals": [{
|
|
49
65
|
"@type": "Animal",
|
|
50
66
|
"value": "> 0",
|
|
@@ -93,19 +109,6 @@ REQUIREMENTS = {
|
|
|
93
109
|
}
|
|
94
110
|
}],
|
|
95
111
|
"optional": {
|
|
96
|
-
"inputs": [{
|
|
97
|
-
"@type": "Input",
|
|
98
|
-
"term.units": "kg",
|
|
99
|
-
"value": "> 0",
|
|
100
|
-
"isAnimalFeed": "True",
|
|
101
|
-
"optional": {
|
|
102
|
-
"properties": [{
|
|
103
|
-
"@type": "Property",
|
|
104
|
-
"value": "",
|
|
105
|
-
"term.@id": ["neutralDetergentFibreContent", "energyContentHigherHeatingValue"]
|
|
106
|
-
}]
|
|
107
|
-
}
|
|
108
|
-
}],
|
|
109
112
|
"products": [{
|
|
110
113
|
"@type": "Product",
|
|
111
114
|
"value": "",
|
|
@@ -169,6 +172,17 @@ def _calculate_GE(
|
|
|
169
172
|
NEwork = _sum_values(values, 3)
|
|
170
173
|
NEp = _sum_values(values, 4)
|
|
171
174
|
NEg = _sum_values(values, 5)
|
|
175
|
+
|
|
176
|
+
debugValues(cycle, model=MODEL, term=MODEL_KEY, model_key=MODEL_KEY,
|
|
177
|
+
NEm=NEm,
|
|
178
|
+
NEa=NEa,
|
|
179
|
+
NEl=NEl,
|
|
180
|
+
NEwork=NEwork,
|
|
181
|
+
NEp=NEp,
|
|
182
|
+
NEg=NEg,
|
|
183
|
+
NEm_feed=NEm_feed,
|
|
184
|
+
NEg_feed=NEg_feed)
|
|
185
|
+
|
|
172
186
|
return (NEm + NEa + NEl + NEwork + NEp - NEm_feed)/REM + (NEg + NEwool - NEg_feed)/REG
|
|
173
187
|
|
|
174
188
|
|
|
@@ -207,17 +221,20 @@ def _run_practice(cycle: dict, meanDE: float, meanECHHV: float, system: dict):
|
|
|
207
221
|
def _should_run(cycle: dict, practices: dict):
|
|
208
222
|
systems = filter_list_term_type(cycle.get('practices', []), TermTermType.SYSTEM)
|
|
209
223
|
animalFeed_complete = _is_term_type_complete(cycle, 'animalFeed')
|
|
210
|
-
|
|
224
|
+
animalPopulation_complete = _is_term_type_complete(cycle, 'animalPopulation')
|
|
211
225
|
freshForage_incomplete = _is_term_type_incomplete(cycle, 'freshForage')
|
|
212
226
|
all_animals_have_value = all([a.get('value', 0) > 0 for a in cycle.get('animals', [])])
|
|
213
227
|
|
|
228
|
+
has_cycle_inputs_feed = any([i.get('isAnimalFeed', False) for i in cycle.get('inputs', [])])
|
|
229
|
+
|
|
214
230
|
meanDE = calculate_meanDE(practices)
|
|
215
231
|
meanECHHV = calculate_meanECHHV(practices)
|
|
216
232
|
|
|
217
233
|
should_run = all([
|
|
218
234
|
animalFeed_complete,
|
|
219
|
-
|
|
235
|
+
animalPopulation_complete,
|
|
220
236
|
freshForage_incomplete,
|
|
237
|
+
has_cycle_inputs_feed,
|
|
221
238
|
all_animals_have_value,
|
|
222
239
|
len(systems) > 0,
|
|
223
240
|
len(practices) > 0,
|
|
@@ -228,8 +245,9 @@ def _should_run(cycle: dict, practices: dict):
|
|
|
228
245
|
for term_id in [MODEL_KEY] + [practice_input_id(p) for p in practices]:
|
|
229
246
|
logRequirements(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
230
247
|
term_type_animalFeed_complete=animalFeed_complete,
|
|
231
|
-
|
|
248
|
+
term_type_animalPopulation_complete=animalPopulation_complete,
|
|
232
249
|
term_type_freshForage_incomplete=freshForage_incomplete,
|
|
250
|
+
has_cycle_inputs_feed=has_cycle_inputs_feed,
|
|
233
251
|
all_animals_have_value=all_animals_have_value,
|
|
234
252
|
meanDE=meanDE,
|
|
235
253
|
meanECHHV=meanECHHV)
|
|
@@ -3,7 +3,7 @@ from hestia_earth.utils.api import download_hestia
|
|
|
3
3
|
from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name, extract_grouped_data
|
|
4
4
|
from hestia_earth.utils.tools import list_sum, safe_parse_float
|
|
5
5
|
|
|
6
|
-
from hestia_earth.models.log import debugValues
|
|
6
|
+
from hestia_earth.models.log import debugValues
|
|
7
7
|
from hestia_earth.models.utils.input import get_feed_inputs
|
|
8
8
|
from hestia_earth.models.utils.term import get_lookup_value
|
|
9
9
|
from hestia_earth.models.utils.property import get_node_property, get_node_property_value, node_property_lookup_value
|
|
@@ -98,7 +98,7 @@ _NEa_BY_GROUPING = {
|
|
|
98
98
|
|
|
99
99
|
def _calculate_NEa(cycle: dict, animal: dict, system: dict, NEm: float) -> float:
|
|
100
100
|
grouping = _get_grouping(animal)
|
|
101
|
-
return _NEa_BY_GROUPING.get(grouping, lambda *args:
|
|
101
|
+
return _NEa_BY_GROUPING.get(grouping, lambda *args: 0)(cycle, animal, system, NEm)
|
|
102
102
|
|
|
103
103
|
|
|
104
104
|
def _calculate_NEl_cattleAndBuffalo(cycle: dict, animal: dict) -> float:
|
|
@@ -156,7 +156,7 @@ _NEl_BY_GROUPING = {
|
|
|
156
156
|
|
|
157
157
|
def _calculate_NEl(cycle: dict, animal: dict) -> float:
|
|
158
158
|
grouping = _get_grouping(animal)
|
|
159
|
-
return _NEl_BY_GROUPING.get(grouping, lambda *args:
|
|
159
|
+
return _NEl_BY_GROUPING.get(grouping, lambda *args: 0)(cycle, animal)
|
|
160
160
|
|
|
161
161
|
|
|
162
162
|
def _calculate_NEwork(cycle: dict, animal: dict, NEm: float) -> float:
|
|
@@ -347,8 +347,6 @@ def calculate_NEfeed(node: dict) -> tuple:
|
|
|
347
347
|
|
|
348
348
|
|
|
349
349
|
def get_animal_values(cycle: dict, animal: dict, system: dict):
|
|
350
|
-
term_id = animal.get('term', {}).get('@id')
|
|
351
|
-
|
|
352
350
|
NEm = _calculate_NEm(cycle, animal)
|
|
353
351
|
NEa = _calculate_NEa(cycle, animal, system, NEm)
|
|
354
352
|
NEl = _calculate_NEl(cycle, animal)
|
|
@@ -356,14 +354,6 @@ def get_animal_values(cycle: dict, animal: dict, system: dict):
|
|
|
356
354
|
NEp = _calculate_NEp(cycle, animal, NEm)
|
|
357
355
|
NEg = _calculate_NEg(cycle, animal)
|
|
358
356
|
|
|
359
|
-
logRequirements(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
360
|
-
NEm=NEm,
|
|
361
|
-
NEa=NEa,
|
|
362
|
-
NEl=NEl,
|
|
363
|
-
NEwork=NEwork,
|
|
364
|
-
NEp=NEp,
|
|
365
|
-
NEg=NEg)
|
|
366
|
-
|
|
367
357
|
return (NEm, NEa, NEl, NEwork, NEp, NEg)
|
|
368
358
|
|
|
369
359
|
|
|
@@ -394,14 +384,7 @@ def should_run_practice(cycle: dict):
|
|
|
394
384
|
term_id = practice.get('term', {}).get('@id')
|
|
395
385
|
key_term_type = practice.get('key', {}).get('termType')
|
|
396
386
|
value = practice.get('value', [])
|
|
397
|
-
|
|
398
|
-
logRequirements(cycle, model=MODEL, term=term_id,
|
|
399
|
-
practice_value=list_sum(value),
|
|
400
|
-
practice_key_term_type=key_term_type)
|
|
401
|
-
|
|
402
|
-
should_run = all([len(value) > 0, term_id == MODEL_KEY, key_term_type in KEY_TERM_TYPES])
|
|
403
|
-
logShouldRun(cycle, MODEL, term_id, should_run)
|
|
404
|
-
return should_run
|
|
387
|
+
return all([len(value) > 0, term_id == MODEL_KEY, key_term_type in KEY_TERM_TYPES])
|
|
405
388
|
|
|
406
389
|
return should_run
|
|
407
390
|
|