hestia-earth-models 0.59.6__py3-none-any.whl → 0.60.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.

Files changed (49) hide show
  1. hestia_earth/models/cache_sites.py +8 -8
  2. hestia_earth/models/cycle/siteDuration.py +1 -1
  3. hestia_earth/models/emepEea2019/nh3ToAirInorganicFertiliser.py +9 -6
  4. hestia_earth/models/faostat2018/product/price.py +14 -3
  5. hestia_earth/models/geospatialDatabase/utils.py +0 -1
  6. hestia_earth/models/ipcc2019/animal/__init__.py +0 -0
  7. hestia_earth/models/ipcc2019/animal/pastureGrass.py +298 -0
  8. hestia_earth/models/ipcc2019/co2ToAirLimeHydrolysis.py +1 -1
  9. hestia_earth/models/ipcc2019/{co2ToAirSoilCarbonStockChangeManagementChange.py → co2ToAirSoilOrganicCarbonStockChangeManagementChange.py} +2 -2
  10. hestia_earth/models/ipcc2019/co2ToAirUreaHydrolysis.py +2 -7
  11. hestia_earth/models/ipcc2019/organicCarbonPerHa.py +7 -2
  12. hestia_earth/models/ipcc2019/pastureGrass.py +73 -447
  13. hestia_earth/models/ipcc2019/pastureGrass_utils.py +415 -0
  14. hestia_earth/models/mocking/search-results.json +215 -207
  15. hestia_earth/models/site/pre_checks/cache_geospatialDatabase.py +14 -2
  16. hestia_earth/models/utils/completeness.py +17 -14
  17. hestia_earth/models/utils/feedipedia.py +23 -23
  18. hestia_earth/models/utils/property.py +4 -1
  19. hestia_earth/models/utils/site.py +7 -4
  20. hestia_earth/models/version.py +1 -1
  21. {hestia_earth_models-0.59.6.dist-info → hestia_earth_models-0.60.0.dist-info}/LICENSE +1 -1
  22. {hestia_earth_models-0.59.6.dist-info → hestia_earth_models-0.60.0.dist-info}/METADATA +1 -1
  23. {hestia_earth_models-0.59.6.dist-info → hestia_earth_models-0.60.0.dist-info}/RECORD +49 -44
  24. tests/models/cycle/animal/input/test_properties.py +3 -1
  25. tests/models/cycle/animal/test_properties.py +4 -2
  26. tests/models/cycle/input/test_properties.py +3 -1
  27. tests/models/cycle/product/test_properties.py +2 -1
  28. tests/models/cycle/test_coldCarcassWeightPerHead.py +1 -0
  29. tests/models/cycle/test_coldDressedCarcassWeightPerHead.py +1 -0
  30. tests/models/cycle/test_energyContentLowerHeatingValue.py +1 -0
  31. tests/models/cycle/test_feedConversionRatio.py +10 -0
  32. tests/models/cycle/test_readyToCookWeightPerHead.py +1 -0
  33. tests/models/faostat2018/product/test_price.py +15 -3
  34. tests/models/ipcc2006/test_n2OToAirCropResidueDecompositionDirect.py +4 -1
  35. tests/models/ipcc2019/animal/__init__.py +0 -0
  36. tests/models/ipcc2019/animal/test_pastureGrass.py +45 -0
  37. tests/models/ipcc2019/test_ch4ToAirEntericFermentation.py +32 -8
  38. tests/models/ipcc2019/{test_co2ToAirSoilCarbonStockChangeManagementChange.py → test_co2ToAirSoilOrganicCarbonStockChangeManagementChange.py} +1 -1
  39. tests/models/ipcc2019/test_n2OToAirCropResidueDecompositionDirect.py +6 -1
  40. tests/models/ipcc2019/test_n2OToAirInorganicFertiliserDirect.py +6 -1
  41. tests/models/ipcc2019/test_n2OToAirOrganicFertiliserDirect.py +6 -1
  42. tests/models/ipcc2019/test_organicCarbonPerHa.py +95 -40
  43. tests/models/ipcc2019/test_pastureGrass.py +32 -8
  44. tests/models/pooreNemecek2018/test_excretaKgN.py +5 -0
  45. tests/models/pooreNemecek2018/test_excretaKgVs.py +5 -0
  46. tests/models/pooreNemecek2018/test_no3ToGroundwaterSoilFlux.py +1 -0
  47. tests/models/test_cache_sites.py +22 -7
  48. {hestia_earth_models-0.59.6.dist-info → hestia_earth_models-0.60.0.dist-info}/WHEEL +0 -0
  49. {hestia_earth_models-0.59.6.dist-info → hestia_earth_models-0.60.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,415 @@
1
+ from hestia_earth.schema import TermTermType, AnimalReferencePeriod
2
+ from hestia_earth.utils.api import download_hestia
3
+ from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name, extract_grouped_data
4
+ from hestia_earth.utils.tools import list_sum, safe_parse_float
5
+
6
+ from hestia_earth.models.log import debugValues, logRequirements, logShouldRun
7
+ from hestia_earth.models.utils.input import get_feed_inputs
8
+ from hestia_earth.models.utils.term import get_lookup_value
9
+ from hestia_earth.models.utils.property import get_node_property, get_node_property_value, node_property_lookup_value
10
+ from .utils import get_milkYield_practice
11
+ from . import MODEL
12
+
13
+ MODEL_KEY = 'pastureGrass'
14
+ KEY_TERM_TYPES = [
15
+ TermTermType.LANDCOVER.value
16
+ ]
17
+
18
+
19
+ def practice_input_id(practice: dict):
20
+ return get_lookup_value(practice.get('key', {}), 'grazedPastureGrassInputId', model=MODEL, model_key=MODEL_KEY)
21
+
22
+
23
+ def _get_grouping(animal: dict) -> str:
24
+ term = animal.get('term', {})
25
+ return get_lookup_value(term, 'ipcc2019AnimalTypeGrouping', model=MODEL, model_key=MODEL_KEY)
26
+
27
+
28
+ def _get_activityCoefficient(cycle: dict, animal: dict, system: dict) -> float:
29
+ term = animal.get('term', {})
30
+ term_id = term.get('@id')
31
+ system_id = system.get('term', {}).get('@id')
32
+ lookup = download_lookup('system-liveAnimal-activityCoefficient-ipcc2019.csv')
33
+ activityCoefficient = safe_parse_float(get_table_value(lookup, 'termid', system_id, column_name(term_id)), 0)
34
+
35
+ debugValues(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
36
+ activityCoefficient=activityCoefficient)
37
+
38
+ return activityCoefficient
39
+
40
+
41
+ def _calculate_NEm(cycle: dict, animal: dict) -> float:
42
+ term = animal.get('term', {})
43
+ term_id = term.get('@id')
44
+
45
+ mjDayKgCfiNetEnergyMaintenance = safe_parse_float(
46
+ get_lookup_value(term, 'mjDayKgCfiNetEnergyMaintenanceIpcc2019', model=MODEL, model_key=MODEL_KEY), 0
47
+ )
48
+ liveweightPerHead = get_node_property(animal, 'liveweightPerHead', False).get('value', 0)
49
+ animal_value = animal.get('value', 0)
50
+ cycleDuration = cycle.get('cycleDuration', 365)
51
+ NEm = mjDayKgCfiNetEnergyMaintenance * pow(liveweightPerHead, 0.75) * animal_value * cycleDuration
52
+
53
+ debugValues(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
54
+ mjDayKgCfiNetEnergyMaintenance=mjDayKgCfiNetEnergyMaintenance,
55
+ liveweightPerHead=liveweightPerHead,
56
+ NEm=NEm)
57
+
58
+ return NEm
59
+
60
+
61
+ def _calculate_NEa_cattleAndBuffalo(cycle: dict, animal: dict, system: dict, NEm: float) -> float:
62
+ term = animal.get('term', {})
63
+ term_id = term.get('@id')
64
+
65
+ activityCoefficient = _get_activityCoefficient(cycle, animal, system)
66
+
67
+ NEa = activityCoefficient * NEm
68
+
69
+ debugValues(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
70
+ NEa=NEa)
71
+
72
+ return NEa
73
+
74
+
75
+ def _calculate_NEa_sheepAndGoat(cycle: dict, animal: dict, system: dict, _NEm: float) -> float:
76
+ term = animal.get('term', {})
77
+ term_id = term.get('@id')
78
+
79
+ activityCoefficient = _get_activityCoefficient(cycle, animal, system)
80
+
81
+ liveweightPerHead = get_node_property(animal, 'liveweightPerHead', False).get('value', 0)
82
+ animal_value = animal.get('value', 0)
83
+ cycleDuration = cycle.get('cycleDuration', 365)
84
+ NEa = activityCoefficient * liveweightPerHead * animal_value * cycleDuration
85
+
86
+ debugValues(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
87
+ liveweightPerHead=liveweightPerHead,
88
+ NEa=NEa)
89
+
90
+ return NEa
91
+
92
+
93
+ _NEa_BY_GROUPING = {
94
+ 'cattleAndBuffalo': _calculate_NEa_cattleAndBuffalo,
95
+ 'sheepAndGoat': _calculate_NEa_sheepAndGoat
96
+ }
97
+
98
+
99
+ def _calculate_NEa(cycle: dict, animal: dict, system: dict, NEm: float) -> float:
100
+ grouping = _get_grouping(animal)
101
+ return _NEa_BY_GROUPING.get(grouping, lambda *args: None)(cycle, animal, system, NEm)
102
+
103
+
104
+ def _calculate_NEl_cattleAndBuffalo(cycle: dict, animal: dict) -> float:
105
+ term = animal.get('term', {})
106
+ term_id = term.get('@id')
107
+
108
+ milkYieldPractice = get_milkYield_practice(animal)
109
+ milkYield = list_sum(milkYieldPractice.get('value', []))
110
+ fatContent = get_node_property(milkYieldPractice, 'fatContent').get('value', 0)
111
+ animal_value = animal.get('value', 0)
112
+ cycleDuration = cycle.get('cycleDuration', 365)
113
+ NEl = milkYield * (1.47 + (0.4 * fatContent)) * animal_value * cycleDuration
114
+
115
+ debugValues(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
116
+ milkYield=milkYield,
117
+ fatContent=fatContent,
118
+ NEl=NEl)
119
+
120
+ return NEl
121
+
122
+
123
+ def _calculate_NEl_sheepAndGoat(cycle: dict, animal: dict) -> float:
124
+ term = animal.get('term', {})
125
+ term_id = term.get('@id')
126
+
127
+ milkYieldPractice = get_milkYield_practice(animal)
128
+ milkYield = list_sum(milkYieldPractice.get('value', []))
129
+ EV_milk = safe_parse_float(
130
+ get_lookup_value(milkYieldPractice.get('term', {}), 'mjKgEvMilkIpcc2019', model=MODEL, model_key=MODEL_KEY),
131
+ 0
132
+ )
133
+ default_fatContent = safe_parse_float(
134
+ get_lookup_value(milkYieldPractice.get('term', {}),
135
+ 'defaultFatContentEvMilkIpcc2019', model=MODEL, model_key=MODEL_KEY),
136
+ 7
137
+ )
138
+ fatContent = get_node_property(milkYieldPractice, 'fatContent').get('value', 0)
139
+ animal_value = animal.get('value', 0)
140
+ cycleDuration = cycle.get('cycleDuration', 365)
141
+ NEl = milkYield * (EV_milk * fatContent/default_fatContent) * animal_value * cycleDuration
142
+
143
+ debugValues(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
144
+ milkYield=milkYield,
145
+ EV_milk=EV_milk,
146
+ NEl=NEl)
147
+
148
+ return NEl
149
+
150
+
151
+ _NEl_BY_GROUPING = {
152
+ 'cattleAndBuffalo': _calculate_NEl_cattleAndBuffalo,
153
+ 'sheepAndGoat': _calculate_NEl_sheepAndGoat
154
+ }
155
+
156
+
157
+ def _calculate_NEl(cycle: dict, animal: dict) -> float:
158
+ grouping = _get_grouping(animal)
159
+ return _NEl_BY_GROUPING.get(grouping, lambda *args: None)(cycle, animal)
160
+
161
+
162
+ def _calculate_NEwork(cycle: dict, animal: dict, NEm: float) -> float:
163
+ term = animal.get('term', {})
164
+ term_id = term.get('@id')
165
+
166
+ hoursWorkedPerDay = get_node_property(animal, 'hoursWorkedPerDay').get('value', 0)
167
+ NEwork = 0.1 * NEm * hoursWorkedPerDay
168
+
169
+ debugValues(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
170
+ hoursWorkedPerDay=hoursWorkedPerDay,
171
+ NEwork=NEwork)
172
+
173
+ return NEwork
174
+
175
+
176
+ def _get_pregnancy_ratio_per_birth(animal: dict, value: str) -> float:
177
+ animalsPerBirth = get_node_property(animal, 'animalsPerBirth').get('value', 3)
178
+ single = safe_parse_float(extract_grouped_data(value, 'singleBirth'), 0)
179
+ double = safe_parse_float(extract_grouped_data(value, 'doubleBirth'), 0)
180
+ tripple = safe_parse_float(extract_grouped_data(value, 'tripleBirthOrMore'))
181
+ return (
182
+ single if animalsPerBirth <= 1 else
183
+ ((animalsPerBirth-1)/2)*single * (1-((animalsPerBirth-1)/2)*double) if 1 < animalsPerBirth < 2 else
184
+ double if animalsPerBirth == 2 else
185
+ ((animalsPerBirth-2)/3)*double * (1-((animalsPerBirth-2)/3)*tripple) if 2 < animalsPerBirth < 3 else
186
+ tripple
187
+ )
188
+
189
+
190
+ def _get_pregnancy_ratio(animal: dict) -> float:
191
+ term = animal.get('term', {})
192
+ value = get_lookup_value(term, 'ratioCPregnancyNetEnergyPregnancyIpcc2019', model=MODEL, model_key=MODEL_KEY)
193
+ return _get_pregnancy_ratio_per_birth(animal, value) if ';' in value else safe_parse_float(value, 0)
194
+
195
+
196
+ def _calculate_NEp(cycle: dict, animal: dict, NEm: float) -> float:
197
+ term = animal.get('term', {})
198
+ term_id = term.get('@id')
199
+
200
+ ratioCPregnancyNetEnergyPregnancy = _get_pregnancy_ratio(animal)
201
+ pregnancyRateTotal = get_node_property(animal, 'pregnancyRateTotal').get('value', 0)
202
+ NEp = ratioCPregnancyNetEnergyPregnancy * pregnancyRateTotal/100 * NEm
203
+
204
+ debugValues(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
205
+ ratioCPregnancyNetEnergyPregnancy=ratioCPregnancyNetEnergyPregnancy,
206
+ pregnancyRateTotal=pregnancyRateTotal,
207
+ NEp=NEp)
208
+
209
+ return NEp
210
+
211
+
212
+ def _calculate_NEg_cattleAndBuffalo(cycle: dict, animal: dict) -> float:
213
+ term = animal.get('term', {})
214
+ term_id = term.get('@id')
215
+
216
+ ratioCNetEnergyGrowthCattleBuffalo = safe_parse_float(
217
+ get_lookup_value(term, 'ratioCNetEnergyGrowthCattleBuffaloIpcc2019', model=MODEL, model_key=MODEL_KEY), 0
218
+ )
219
+ liveweightPerHead = get_node_property(animal, 'liveweightPerHead').get('value', 0)
220
+ weightAtMaturity = get_node_property(animal, 'weightAtMaturity').get('value', 0)
221
+ liveweightGain = get_node_property(animal, 'liveweightGain').get('value', 0)
222
+ animal_value = animal.get('value', 0)
223
+ cycleDuration = cycle.get('cycleDuration', 365)
224
+ NEg = 22.02 * \
225
+ pow(liveweightPerHead / (ratioCNetEnergyGrowthCattleBuffalo * weightAtMaturity), 0.75) * \
226
+ pow(liveweightGain, 1.097) * \
227
+ animal_value * cycleDuration if all([
228
+ ratioCNetEnergyGrowthCattleBuffalo * weightAtMaturity > 0
229
+ ]) else 0
230
+
231
+ debugValues(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
232
+ ratioCNetEnergyGrowthCattleBuffalo=ratioCNetEnergyGrowthCattleBuffalo,
233
+ liveweightPerHead=liveweightPerHead,
234
+ weightAtMaturity=weightAtMaturity,
235
+ liveweightGain=liveweightGain,
236
+ NEg=NEg)
237
+
238
+ return NEg
239
+
240
+
241
+ def _calculate_NEg_sheepAndGoat(cycle: dict, animal: dict) -> float:
242
+ term = animal.get('term', {})
243
+ term_id = term.get('@id')
244
+
245
+ MjKgABNetEnergyGrowthSheepGoats = get_lookup_value(
246
+ term, 'MjKgABNetEnergyGrowthSheepGoatsIpcc2019', model=MODEL, model_key=MODEL_KEY)
247
+ MjKg_a = safe_parse_float(extract_grouped_data(MjKgABNetEnergyGrowthSheepGoats, 'a'), 0)
248
+ MjKg_b = safe_parse_float(extract_grouped_data(MjKgABNetEnergyGrowthSheepGoats, 'b'), 0)
249
+ BWi = get_node_property(animal, 'weightAtWeaning').get('value', 0)
250
+ BWf = get_node_property(animal, 'weightAtOneYear').get('value', 0) or \
251
+ get_node_property(animal, 'weightAtSlaughter').get('value', 0)
252
+ animal_value = animal.get('value', 0)
253
+ cycleDuration = cycle.get('cycleDuration', 365)
254
+ NEg = (BWf - BWi) * (MjKg_a + 0.5 * MjKg_b * (BWi + BWf)) / 365 * animal_value * cycleDuration
255
+
256
+ debugValues(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
257
+ MjKg_a=MjKg_a,
258
+ MjKg_b=MjKg_b,
259
+ BWi=BWi,
260
+ BWf=BWf,
261
+ NEg=NEg)
262
+
263
+ return NEg
264
+
265
+
266
+ _NEg_BY_GROUPING = {
267
+ 'cattleAndBuffalo': _calculate_NEg_cattleAndBuffalo,
268
+ 'sheepAndGoat': _calculate_NEg_sheepAndGoat
269
+ }
270
+
271
+
272
+ def _calculate_NEg(cycle: dict, animal: dict) -> float:
273
+ grouping = _get_grouping(animal)
274
+ return _NEg_BY_GROUPING.get(grouping, lambda *args: None)(cycle, animal)
275
+
276
+
277
+ def _pastureGrass_key_property_value(practice: dict, column: dict):
278
+ term_id = practice_input_id(practice)
279
+ term = download_hestia(term_id)
280
+ term_type = term.get('termType')
281
+ value = list_sum(practice.get('value', [0]))
282
+ lookup_value = node_property_lookup_value(MODEL, {'@id': term_id, 'termType': term_type}, column, default=0)
283
+ return (lookup_value, value)
284
+
285
+
286
+ def calculate_REM(energy: float = 0) -> float:
287
+ return 1.123 - (4.092/1000 * energy) + (1.126/100000 * pow(energy, 2)) - (25.4/energy) if energy > 0 else 0
288
+
289
+
290
+ def calculate_REG(energy: float = 0) -> float:
291
+ return 1.164 - (5.16/1000 * energy) + (1.308/100000 * pow(energy, 2)) - (37.4/energy) if energy > 0 else 0
292
+
293
+
294
+ def _calculate_feed_meanDE(node: dict, input: dict) -> float:
295
+ term_id = input.get('term', {}).get('@id')
296
+
297
+ energyContent = get_node_property_value(MODEL, input, 'energyContentHigherHeatingValue')
298
+ energyDigestibility = get_node_property_value(MODEL, input, 'energyDigestibilityRuminants')
299
+ meanDE = energyContent * energyDigestibility if all([energyContent, energyDigestibility]) else 0
300
+
301
+ debugValues(node, model=MODEL, term=term_id, model_key=MODEL_KEY,
302
+ energyContent=energyContent,
303
+ energyDigestibility=energyDigestibility,
304
+ meanDE=meanDE)
305
+
306
+ return meanDE
307
+
308
+
309
+ def _calculate_NEfeed_m(node: dict, input: dict, meanDE: float) -> float:
310
+ term_id = input.get('term', {}).get('@id')
311
+
312
+ energyDigestibility = get_node_property_value(MODEL, input, 'energyDigestibilityRuminants', default=0)
313
+ REm = calculate_REM(energyDigestibility * 100)
314
+
315
+ debugValues(node, model=MODEL, term=term_id, model_key=MODEL_KEY,
316
+ REm=REm)
317
+
318
+ input_value = list_sum(input.get('value'))
319
+ return meanDE * REm * input_value
320
+
321
+
322
+ def _calculate_NEfeed_g(node: dict, input: dict, meanDE: float) -> float:
323
+ term_id = input.get('term', {}).get('@id')
324
+
325
+ energyDigestibility = get_node_property_value(MODEL, input, 'energyDigestibilityRuminants', default=0)
326
+ REg = calculate_REG(energyDigestibility * 100)
327
+
328
+ debugValues(node, model=MODEL, term=term_id, model_key=MODEL_KEY,
329
+ REg=REg)
330
+
331
+ input_value = list_sum(input.get('value'))
332
+ return meanDE * REg * input_value
333
+
334
+
335
+ def calculate_NEfeed(node: dict) -> tuple:
336
+ inputs = get_feed_inputs(node)
337
+ # calculate meanDE for each input first
338
+ inputs = [(input, _calculate_feed_meanDE(node, input)) for input in inputs]
339
+ NEfeed_m = sum([
340
+ _calculate_NEfeed_m(node, input, meanDE) for (input, meanDE) in inputs
341
+ ]) if len(inputs) > 0 else 0
342
+ NEfeed_g = sum([
343
+ _calculate_NEfeed_g(node, input, meanDE) for (input, meanDE) in inputs
344
+ ]) if len(inputs) > 0 else 0
345
+
346
+ return (NEfeed_m, NEfeed_g)
347
+
348
+
349
+ def get_animal_values(cycle: dict, animal: dict, system: dict):
350
+ term_id = animal.get('term', {}).get('@id')
351
+
352
+ NEm = _calculate_NEm(cycle, animal)
353
+ NEa = _calculate_NEa(cycle, animal, system, NEm)
354
+ NEl = _calculate_NEl(cycle, animal)
355
+ NEwork = _calculate_NEwork(cycle, animal, NEm)
356
+ NEp = _calculate_NEp(cycle, animal, NEm)
357
+ NEg = _calculate_NEg(cycle, animal)
358
+
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
+ return (NEm, NEa, NEl, NEwork, NEp, NEg)
368
+
369
+
370
+ def calculate_meanECHHV(practices: list) -> float:
371
+ values = list(map(lambda p: _pastureGrass_key_property_value(p, 'energyContentHigherHeatingValue'), practices))
372
+ total_weight = sum([weight/100 for _value, weight in values])
373
+ return sum([
374
+ (value * weight/100 if all([value, weight]) else 0) for value, weight in values
375
+ ]) / total_weight if total_weight > 0 else 0
376
+
377
+
378
+ def calculate_meanDE(practices: list) -> float:
379
+ values = list(map(lambda p: _pastureGrass_key_property_value(p, 'energyDigestibilityRuminants'), practices))
380
+ total_weight = sum([weight/100 for _value, weight in values])
381
+ meanDE = sum([
382
+ (value * weight/100 if all([value, weight]) else 0) for value, weight in values
383
+ ]) / total_weight if total_weight > 0 else 0
384
+
385
+ return meanDE
386
+
387
+
388
+ def product_wool_energy(product: dict):
389
+ return safe_parse_float(get_lookup_value(product.get('term', {}), 'mjKgEvWoolNetEnergyWoolIpcc2019'), 24)
390
+
391
+
392
+ def should_run_practice(cycle: dict):
393
+ def should_run(practice: dict):
394
+ term_id = practice.get('term', {}).get('@id')
395
+ key_term_type = practice.get('key', {}).get('termType')
396
+ 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
405
+
406
+ return should_run
407
+
408
+
409
+ def get_animals(cycle: dict):
410
+ return [
411
+ a for a in cycle.get('animals', []) if all([
412
+ a.get('value'),
413
+ a.get('referencePeriod') == AnimalReferencePeriod.AVERAGE.value
414
+ ])
415
+ ]