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

Files changed (39) hide show
  1. hestia_earth/models/cache_sites.py +40 -16
  2. hestia_earth/models/ipcc2019/animal/__init__.py +0 -0
  3. hestia_earth/models/ipcc2019/animal/pastureGrass.py +298 -0
  4. hestia_earth/models/ipcc2019/{co2ToAirSoilCarbonStockChangeManagementChange.py → co2ToAirSoilOrganicCarbonStockChangeManagementChange.py} +2 -2
  5. hestia_earth/models/ipcc2019/pastureGrass.py +73 -447
  6. hestia_earth/models/ipcc2019/pastureGrass_utils.py +415 -0
  7. hestia_earth/models/mocking/search-results.json +215 -207
  8. hestia_earth/models/site/pre_checks/cache_geospatialDatabase.py +16 -4
  9. hestia_earth/models/utils/completeness.py +17 -14
  10. hestia_earth/models/utils/feedipedia.py +23 -23
  11. hestia_earth/models/utils/property.py +3 -1
  12. hestia_earth/models/version.py +1 -1
  13. {hestia_earth_models-0.59.7.dist-info → hestia_earth_models-0.60.1.dist-info}/LICENSE +1 -1
  14. {hestia_earth_models-0.59.7.dist-info → hestia_earth_models-0.60.1.dist-info}/METADATA +1 -1
  15. {hestia_earth_models-0.59.7.dist-info → hestia_earth_models-0.60.1.dist-info}/RECORD +39 -34
  16. tests/models/cycle/animal/input/test_properties.py +3 -1
  17. tests/models/cycle/animal/test_properties.py +4 -2
  18. tests/models/cycle/input/test_properties.py +3 -1
  19. tests/models/cycle/product/test_properties.py +2 -1
  20. tests/models/cycle/test_coldCarcassWeightPerHead.py +1 -0
  21. tests/models/cycle/test_coldDressedCarcassWeightPerHead.py +1 -0
  22. tests/models/cycle/test_energyContentLowerHeatingValue.py +1 -0
  23. tests/models/cycle/test_feedConversionRatio.py +10 -0
  24. tests/models/cycle/test_readyToCookWeightPerHead.py +1 -0
  25. tests/models/ipcc2006/test_n2OToAirCropResidueDecompositionDirect.py +4 -1
  26. tests/models/ipcc2019/animal/__init__.py +0 -0
  27. tests/models/ipcc2019/animal/test_pastureGrass.py +45 -0
  28. tests/models/ipcc2019/test_ch4ToAirEntericFermentation.py +32 -8
  29. tests/models/ipcc2019/{test_co2ToAirSoilCarbonStockChangeManagementChange.py → test_co2ToAirSoilOrganicCarbonStockChangeManagementChange.py} +1 -1
  30. tests/models/ipcc2019/test_n2OToAirCropResidueDecompositionDirect.py +6 -1
  31. tests/models/ipcc2019/test_n2OToAirInorganicFertiliserDirect.py +6 -1
  32. tests/models/ipcc2019/test_n2OToAirOrganicFertiliserDirect.py +6 -1
  33. tests/models/ipcc2019/test_pastureGrass.py +32 -8
  34. tests/models/pooreNemecek2018/test_excretaKgN.py +5 -0
  35. tests/models/pooreNemecek2018/test_excretaKgVs.py +5 -0
  36. tests/models/pooreNemecek2018/test_no3ToGroundwaterSoilFlux.py +1 -0
  37. tests/models/test_cache_sites.py +10 -7
  38. {hestia_earth_models-0.59.7.dist-info → hestia_earth_models-0.60.1.dist-info}/WHEEL +0 -0
  39. {hestia_earth_models-0.59.7.dist-info → hestia_earth_models-0.60.1.dist-info}/top_level.txt +0 -0
@@ -5,23 +5,32 @@ This model estimates the energetic requirements of ruminants and can be used to
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
7
  """
8
- from hestia_earth.schema import TermTermType, AnimalReferencePeriod
9
- from hestia_earth.utils.api import download_hestia
10
- from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name, extract_grouped_data
8
+ from hestia_earth.schema import TermTermType
11
9
  from hestia_earth.utils.model import filter_list_term_type
12
- from hestia_earth.utils.tools import list_sum, safe_parse_float, non_empty_list
10
+ from hestia_earth.utils.tools import list_sum
13
11
 
14
- from hestia_earth.models.log import debugValues, logRequirements, logShouldRun
15
- from hestia_earth.models.utils.input import _new_input, get_feed_inputs
12
+ from hestia_earth.models.log import logRequirements, logShouldRun
13
+ from hestia_earth.models.utils.input import _new_input
14
+ from hestia_earth.models.utils.term import get_wool_terms
16
15
  from hestia_earth.models.utils.completeness import _is_term_type_complete, _is_term_type_incomplete
17
- from hestia_earth.models.utils.term import get_lookup_value, get_wool_terms
18
- from hestia_earth.models.utils.property import get_node_property, get_node_property_value, node_property_lookup_value
19
- from .utils import get_milkYield_practice
20
16
  from . import MODEL
17
+ from .pastureGrass_utils import (
18
+ practice_input_id,
19
+ should_run_practice,
20
+ calculate_meanDE,
21
+ calculate_meanECHHV,
22
+ calculate_REM,
23
+ calculate_REG,
24
+ calculate_NEfeed,
25
+ product_wool_energy,
26
+ get_animals,
27
+ get_animal_values
28
+ )
21
29
 
22
30
  REQUIREMENTS = {
23
31
  "Cycle": {
24
32
  "completeness.animalFeed": "True",
33
+ "completeness.animalPopulation": "False",
25
34
  "completeness.freshForage": "False",
26
35
  "site": {
27
36
  "@type": "Site",
@@ -109,7 +118,7 @@ LOOKUPS = {
109
118
  "animalManagement": [
110
119
  "mjKgEvMilkIpcc2019"
111
120
  ],
112
- "animalProduct": "mjKgEvWoolNetEnergyWoolIpcc2019",
121
+ "animalProduct": ["mjKgEvWoolNetEnergyWoolIpcc2019"],
113
122
  "liveAnimal": [
114
123
  "ipcc2019AnimalTypeGrouping",
115
124
  "mjDayKgCfiNetEnergyMaintenanceIpcc2019",
@@ -130,9 +139,6 @@ RETURNS = {
130
139
  }]
131
140
  }
132
141
  MODEL_KEY = 'pastureGrass'
133
- KEY_TERM_TYPES = [
134
- TermTermType.LANDCOVER.value
135
- ]
136
142
 
137
143
 
138
144
  def _input(term_id: str, value: float):
@@ -141,479 +147,99 @@ def _input(term_id: str, value: float):
141
147
  return node
142
148
 
143
149
 
144
- def _practice_input_id(practice: dict): return get_lookup_value(practice.get('key', {}), 'grazedPastureGrassInputId')
150
+ def _sum_values(values: list, index=0): return sum([v[index] for v in values])
145
151
 
146
152
 
147
- def _get_grouping(animal: dict):
148
- term = animal.get('term', {})
149
- return get_lookup_value(term, 'ipcc2019AnimalTypeGrouping')
150
-
151
-
152
- def _get_activityCoefficient(cycle: dict, animal: dict, system: dict):
153
- term = animal.get('term', {})
154
- term_id = term.get('@id')
155
- system_id = system.get('term', {}).get('@id')
156
- lookup = download_lookup('system-liveAnimal-activityCoefficient-ipcc2019.csv')
157
- activityCoefficient = safe_parse_float(get_table_value(lookup, 'termid', system_id, column_name(term_id)), 0)
158
-
159
- debugValues(cycle, model=MODEL, term=term_id,
160
- activityCoefficient=activityCoefficient)
161
-
162
- return activityCoefficient
163
-
164
-
165
- def _calculate_NEm(cycle: dict, animal: dict):
166
- term = animal.get('term', {})
167
- term_id = term.get('@id')
168
-
169
- mjDayKgCfiNetEnergyMaintenance = safe_parse_float(
170
- get_lookup_value(term, 'mjDayKgCfiNetEnergyMaintenanceIpcc2019'), 0
171
- )
172
- liveweightPerHead = get_node_property(animal, 'liveweightPerHead', False).get('value', 0)
173
- animal_value = animal.get('value', 0)
174
- cycleDuration = cycle.get('cycleDuration', 365)
175
- NEm = mjDayKgCfiNetEnergyMaintenance * pow(liveweightPerHead, 0.75) * animal_value * cycleDuration
176
-
177
- debugValues(cycle, model=MODEL, term=term_id,
178
- mjDayKgCfiNetEnergyMaintenance=mjDayKgCfiNetEnergyMaintenance,
179
- liveweightPerHead=liveweightPerHead,
180
- NEm=NEm)
181
-
182
- return NEm
183
-
184
-
185
- def _calculate_NEa_cattleAndBuffalo(cycle: dict, animal: dict, system: dict, NEm: float):
186
- term = animal.get('term', {})
187
- term_id = term.get('@id')
188
-
189
- activityCoefficient = _get_activityCoefficient(cycle, animal, system)
190
-
191
- NEa = activityCoefficient * NEm
192
-
193
- debugValues(cycle, model=MODEL, term=term_id,
194
- NEa=NEa)
195
-
196
- return term_id, NEa
197
-
198
-
199
- def _calculate_NEa_sheepAndGoat(cycle: dict, animal: dict, system: dict, _NEm: float):
200
- term = animal.get('term', {})
201
- term_id = term.get('@id')
202
-
203
- activityCoefficient = _get_activityCoefficient(cycle, animal, system)
204
-
205
- liveweightPerHead = get_node_property(animal, 'liveweightPerHead', False).get('value', 0)
206
- animal_value = animal.get('value', 0)
207
- cycleDuration = cycle.get('cycleDuration', 365)
208
- NEa = activityCoefficient * liveweightPerHead * animal_value * cycleDuration
209
-
210
- debugValues(cycle, model=MODEL, term=term_id,
211
- liveweightPerHead=liveweightPerHead,
212
- NEa=NEa)
213
-
214
- return term_id, NEa
215
-
216
-
217
- _NEa_BY_GROUPING = {
218
- 'cattleAndBuffalo': _calculate_NEa_cattleAndBuffalo,
219
- 'sheepAndGoat': _calculate_NEa_sheepAndGoat
220
- }
221
-
222
-
223
- def _calculate_NEa(cycle: dict, animal: dict, system: dict, NEm: float):
224
- grouping = _get_grouping(animal)
225
- return _NEa_BY_GROUPING.get(grouping, lambda *args: None)(cycle, animal, system, NEm)
226
-
227
-
228
- def _calculate_NEl_cattleAndBuffalo(cycle: dict, animal: dict):
229
- term = animal.get('term', {})
230
- term_id = term.get('@id')
231
-
232
- milkYieldPractice = get_milkYield_practice(animal)
233
- milkYield = list_sum(milkYieldPractice.get('value', []))
234
- fatContent = get_node_property(milkYieldPractice, 'fatContent').get('value', 0)
235
- animal_value = animal.get('value', 0)
236
- cycleDuration = cycle.get('cycleDuration', 365)
237
- NEl = milkYield * (1.47 + (0.4 * fatContent)) * animal_value * cycleDuration
238
-
239
- debugValues(cycle, model=MODEL, term=term_id,
240
- milkYield=milkYield,
241
- fatContent=fatContent,
242
- NEl=NEl)
243
-
244
- return term_id, NEl
245
-
246
-
247
- def _calculate_NEl_sheepAndGoat(cycle: dict, animal: dict):
248
- term = animal.get('term', {})
249
- term_id = term.get('@id')
250
-
251
- milkYieldPractice = get_milkYield_practice(animal)
252
- milkYield = list_sum(milkYieldPractice.get('value', []))
253
- EV_milk = safe_parse_float(get_lookup_value(milkYieldPractice.get('term', {}), 'mjKgEvMilkIpcc2019'), 0)
254
- default_fatContent = safe_parse_float(
255
- get_lookup_value(milkYieldPractice.get('term', {}), 'defaultFatContentEvMilkIpcc2019'),
256
- 7
257
- )
258
- fatContent = get_node_property(milkYieldPractice, 'fatContent').get('value', 0)
259
- animal_value = animal.get('value', 0)
260
- cycleDuration = cycle.get('cycleDuration', 365)
261
- NEl = milkYield * (EV_milk * fatContent/default_fatContent) * animal_value * cycleDuration
262
-
263
- debugValues(cycle, model=MODEL, term=term_id,
264
- milkYield=milkYield,
265
- EV_milk=EV_milk,
266
- NEl=NEl)
267
-
268
- return term_id, NEl
269
-
270
-
271
- _NEl_BY_GROUPING = {
272
- 'cattleAndBuffalo': _calculate_NEl_cattleAndBuffalo,
273
- 'sheepAndGoat': _calculate_NEl_sheepAndGoat
274
- }
275
-
276
-
277
- def _calculate_NEl(cycle: dict, animal: dict):
278
- grouping = _get_grouping(animal)
279
- return _NEl_BY_GROUPING.get(grouping, lambda *args: None)(cycle, animal)
280
-
281
-
282
- def _calculate_NEwork(cycle: dict, animal: dict, NEm: float):
283
- term = animal.get('term', {})
284
- term_id = term.get('@id')
285
-
286
- hoursWorkedPerDay = get_node_property(animal, 'hoursWorkedPerDay').get('value', 0)
287
- NEwork = 0.1 * NEm * hoursWorkedPerDay
288
-
289
- debugValues(cycle, model=MODEL, term=term_id,
290
- hoursWorkedPerDay=hoursWorkedPerDay,
291
- NEwork=NEwork)
292
-
293
- return term_id, NEwork
294
-
295
-
296
- def _get_pregnancy_ratio_per_birth(animal: dict, value: str):
297
- animalsPerBirth = get_node_property(animal, 'animalsPerBirth').get('value', 3)
298
- single = safe_parse_float(extract_grouped_data(value, 'singleBirth'), 0)
299
- double = safe_parse_float(extract_grouped_data(value, 'doubleBirth'), 0)
300
- tripple = safe_parse_float(extract_grouped_data(value, 'tripleBirthOrMore'))
301
- return (
302
- single if animalsPerBirth <= 1 else
303
- ((animalsPerBirth-1)/2)*single * (1-((animalsPerBirth-1)/2)*double) if 1 < animalsPerBirth < 2 else
304
- double if animalsPerBirth == 2 else
305
- ((animalsPerBirth-2)/3)*double * (1-((animalsPerBirth-2)/3)*tripple) if 2 < animalsPerBirth < 3 else
306
- tripple
307
- )
308
-
309
-
310
- def _get_pregnancy_ratio(animal: dict):
311
- term = animal.get('term', {})
312
- value = get_lookup_value(term, 'ratioCPregnancyNetEnergyPregnancyIpcc2019')
313
- return _get_pregnancy_ratio_per_birth(animal, value) if ';' in value else safe_parse_float(value, 0)
314
-
315
-
316
- def _calculate_NEp(cycle: dict, animal: dict, NEm: float):
317
- term = animal.get('term', {})
318
- term_id = term.get('@id')
319
-
320
- ratioCPregnancyNetEnergyPregnancy = _get_pregnancy_ratio(animal)
321
- pregnancyRateTotal = get_node_property(animal, 'pregnancyRateTotal').get('value', 0)
322
- NEp = ratioCPregnancyNetEnergyPregnancy * pregnancyRateTotal/100 * NEm
323
-
324
- debugValues(cycle, model=MODEL, term=term_id,
325
- ratioCPregnancyNetEnergyPregnancy=ratioCPregnancyNetEnergyPregnancy,
326
- pregnancyRateTotal=pregnancyRateTotal,
327
- NEp=NEp)
328
-
329
- return term_id, NEp
330
-
331
-
332
- def _calculate_NEg_cattleAndBuffalo(cycle: dict, animal: dict):
333
- term = animal.get('term', {})
334
- term_id = term.get('@id')
335
-
336
- ratioCNetEnergyGrowthCattleBuffalo = safe_parse_float(
337
- get_lookup_value(term, 'ratioCNetEnergyGrowthCattleBuffaloIpcc2019'), 0
338
- )
339
- liveweightPerHead = get_node_property(animal, 'liveweightPerHead').get('value', 0)
340
- weightAtMaturity = get_node_property(animal, 'weightAtMaturity').get('value', 0)
341
- liveweightGain = get_node_property(animal, 'liveweightGain').get('value', 0)
342
- animal_value = animal.get('value', 0)
343
- cycleDuration = cycle.get('cycleDuration', 365)
344
- NEg = 22.02 * \
345
- pow(liveweightPerHead / (ratioCNetEnergyGrowthCattleBuffalo * weightAtMaturity), 0.75) * \
346
- pow(liveweightGain, 1.097) * \
347
- animal_value * cycleDuration if all([
348
- ratioCNetEnergyGrowthCattleBuffalo * weightAtMaturity > 0
349
- ]) else 0
350
-
351
- debugValues(cycle, model=MODEL, term=term_id,
352
- ratioCNetEnergyGrowthCattleBuffalo=ratioCNetEnergyGrowthCattleBuffalo,
353
- liveweightPerHead=liveweightPerHead,
354
- weightAtMaturity=weightAtMaturity,
355
- liveweightGain=liveweightGain,
356
- NEg=NEg)
357
-
358
- return term_id, NEg
359
-
360
-
361
- def _calculate_NEg_sheepAndGoat(cycle: dict, animal: dict):
362
- term = animal.get('term', {})
363
- term_id = term.get('@id')
364
-
365
- MjKgABNetEnergyGrowthSheepGoats = get_lookup_value(term, 'MjKgABNetEnergyGrowthSheepGoatsIpcc2019')
366
- MjKg_a = safe_parse_float(extract_grouped_data(MjKgABNetEnergyGrowthSheepGoats, 'a'), 0)
367
- MjKg_b = safe_parse_float(extract_grouped_data(MjKgABNetEnergyGrowthSheepGoats, 'b'), 0)
368
- BWi = get_node_property(animal, 'weightAtWeaning').get('value', 0)
369
- BWf = get_node_property(animal, 'weightAtOneYear').get('value', 0) or \
370
- get_node_property(animal, 'weightAtSlaughter').get('value', 0)
371
- animal_value = animal.get('value', 0)
372
- cycleDuration = cycle.get('cycleDuration', 365)
373
- NEg = (BWf - BWi) * (MjKg_a + 0.5 * MjKg_b * (BWi + BWf)) / 365 * animal_value * cycleDuration
374
-
375
- debugValues(cycle, model=MODEL, term=term_id,
376
- MjKg_a=MjKg_a,
377
- MjKg_b=MjKg_b,
378
- BWi=BWi,
379
- BWf=BWf,
380
- NEg=NEg)
381
-
382
- return term_id, NEg
383
-
384
-
385
- _NEg_BY_GROUPING = {
386
- 'cattleAndBuffalo': _calculate_NEg_cattleAndBuffalo,
387
- 'sheepAndGoat': _calculate_NEg_sheepAndGoat
388
- }
389
-
390
-
391
- def _calculate_NEg(cycle: dict, animal: dict):
392
- grouping = _get_grouping(animal)
393
- return _NEg_BY_GROUPING.get(grouping, lambda *args: None)(cycle, animal)
394
-
395
-
396
- def _calculate_NEwool(cycle: dict):
397
- terms = get_wool_terms()
398
- products = [p for p in cycle.get('products', []) if p.get('term', {}).get('@id') in terms]
153
+ def calculate_NEwool(cycle: dict) -> float:
154
+ term_ids = get_wool_terms()
155
+ products = [p for p in cycle.get('products', []) if p.get('term', {}).get('@id') in term_ids]
399
156
  product_values = [
400
- (
401
- list_sum(p.get('value', [])),
402
- safe_parse_float(get_lookup_value(p.get('term', {}), LOOKUPS['animalProduct']), 24)
403
- ) for p in products
157
+ (list_sum(p.get('value', [])), product_wool_energy(p)) for p in products
404
158
  ]
405
-
406
159
  return sum([value * lookup_value for (value, lookup_value) in product_values])
407
160
 
408
161
 
409
- def _pastureGrass_key_property_value(practice: dict, column: dict):
410
- term_id = _practice_input_id(practice)
411
- term = download_hestia(term_id)
412
- term_type = term.get('termType')
413
- value = list_sum(practice.get('value', [0]))
414
- lookup_value = node_property_lookup_value(MODEL, {'@id': term_id, 'termType': term_type}, column, default=0)
415
- return (lookup_value, value)
416
-
417
-
418
- def _calculate_meanDE(practices: list):
419
- values = list(map(lambda p: _pastureGrass_key_property_value(p, 'energyDigestibilityRuminants'), practices))
420
- total_weight = sum([weight/100 for _value, weight in values])
421
- meanDE = sum([
422
- (value * weight/100 if all([value, weight]) else 0) for value, weight in values
423
- ]) / total_weight if total_weight > 0 else 0
424
-
425
- return meanDE
426
-
427
-
428
- def _calculate_REM(energy: float = 0):
429
- return 1.123 - (4.092/1000 * energy) + (1.126/100000 * pow(energy, 2)) - (25.4/energy) if energy > 0 else 0
430
-
431
-
432
- def _calculate_REG(energy: float = 0):
433
- return 1.164 - (5.16/1000 * energy) + (1.308/100000 * pow(energy, 2)) - (37.4/energy) if energy > 0 else 0
434
-
435
-
436
- def _calculate_feed_meanDE(cycle: dict, input: dict):
437
- term_id = input.get('term', {}).get('@id')
438
-
439
- energyContent = get_node_property_value(MODEL, input, 'energyContentHigherHeatingValue')
440
- energyDigestibility = get_node_property_value(MODEL, input, 'energyDigestibilityRuminants')
441
- meanDE = energyContent * energyDigestibility if all([energyContent, energyDigestibility]) else 0
442
-
443
- debugValues(cycle, model=MODEL, term=term_id,
444
- energyContent=energyContent,
445
- energyDigestibility=energyDigestibility,
446
- meanDE=meanDE)
447
-
448
- return meanDE
449
-
450
-
451
- def _calculate_NEfeed_m(cycle: dict, input: dict, meanDE: float):
452
- term_id = input.get('term', {}).get('@id')
453
-
454
- energyDigestibility = get_node_property_value(MODEL, input, 'energyDigestibilityRuminants', default=0)
455
- REm = _calculate_REM(energyDigestibility * 100)
456
-
457
- debugValues(cycle, model=MODEL, term=term_id,
458
- REm=REm)
459
-
460
- input_value = list_sum(input.get('value'))
461
- return meanDE * REm * input_value
162
+ def _calculate_GE(
163
+ cycle: dict, animals: list, REM: float, REG: float, NEwool: float, NEm_feed: float, NEg_feed: float, system: dict
164
+ ) -> float:
165
+ values = [get_animal_values(cycle, animal, system) for animal in animals]
166
+ NEm = _sum_values(values, 0)
167
+ NEa = _sum_values(values, 1)
168
+ NEl = _sum_values(values, 2)
169
+ NEwork = _sum_values(values, 3)
170
+ NEp = _sum_values(values, 4)
171
+ NEg = _sum_values(values, 5)
172
+ return (NEm + NEa + NEl + NEwork + NEp - NEm_feed)/REM + (NEg + NEwool - NEg_feed)/REG
173
+
174
+
175
+ def _run_practice(cycle: dict, meanDE: float, meanECHHV: float, system: dict):
176
+ animals = get_animals(cycle)
177
+ REM = calculate_REM(meanDE)
178
+ REG = calculate_REG(meanDE)
179
+ NEwool = calculate_NEwool(cycle)
180
+ NEm_feed, NEg_feed = calculate_NEfeed(cycle)
181
+ GE = (
182
+ _calculate_GE(cycle, animals, REM, REG, NEwool, NEm_feed, NEg_feed, system) / (meanDE/100)
183
+ ) if all([REM, REG]) else 0
462
184
 
463
-
464
- def _calculate_NEfeed_g(cycle: dict, input: dict, meanDE: float):
465
- term_id = input.get('term', {}).get('@id')
466
-
467
- energyDigestibility = get_node_property_value(MODEL, input, 'energyDigestibilityRuminants', default=0)
468
- REg = _calculate_REG(energyDigestibility * 100)
469
-
470
- debugValues(cycle, model=MODEL, term=term_id,
471
- REg=REg)
472
-
473
- input_value = list_sum(input.get('value'))
474
- return meanDE * REg * input_value
475
-
476
-
477
- def _calculate_NEfeed(cycle: dict):
478
- inputs = get_feed_inputs(cycle)
479
- # calculate meanDE for each input first
480
- inputs = [(input, _calculate_feed_meanDE(cycle, input)) for input in inputs]
481
- NEfeed_m = sum([
482
- _calculate_NEfeed_m(cycle, input, meanDE) for (input, meanDE) in inputs
483
- ]) if len(inputs) > 0 else 0
484
- NEfeed_g = sum([
485
- _calculate_NEfeed_g(cycle, input, meanDE) for (input, meanDE) in inputs
486
- ]) if len(inputs) > 0 else 0
487
-
488
- return (NEfeed_m, NEfeed_g)
489
-
490
-
491
- def _group_logs(values: list): return ';'.join([f"id:{term_id}_value:{value}" for term_id, value in values if term_id])
492
-
493
-
494
- def _sum_values(values): return sum([value for term_id, value in values])
495
-
496
-
497
- def _calculate_GE(cycle: dict, meanDE: float, system: dict):
498
- animals = [
499
- a for a in cycle.get('animals', []) if all([
500
- a.get('value'),
501
- a.get('referencePeriod') == AnimalReferencePeriod.AVERAGE.value
502
- ])
503
- ]
504
-
505
- # calculate NEm first and re-use in other places
506
- animals = [(animal, _calculate_NEm(cycle, animal)) for animal in animals]
507
-
508
- NEm = sum(non_empty_list([NEm for (animal, NEm) in animals]))
509
-
510
- NEa = non_empty_list([_calculate_NEa(cycle, animal, system, NEm) for (animal, NEm) in animals])
511
- NEl = non_empty_list([_calculate_NEl(cycle, animal) for (animal, _NEm) in animals])
512
- NEwork = non_empty_list([_calculate_NEwork(cycle, animal, NEm) for (animal, NEm) in animals])
513
- NEp = non_empty_list([_calculate_NEp(cycle, animal, NEm) for (animal, NEm) in animals])
514
- NEg = non_empty_list([_calculate_NEg(cycle, animal) for (animal, _NEm) in animals])
515
-
516
- NEwool = _calculate_NEwool(cycle)
517
- REM = _calculate_REM(meanDE) if meanDE > 0 else None
518
- REG = _calculate_REG(meanDE) if meanDE > 0 else None
519
-
520
- NEfeed_m, NEfeed_g = _calculate_NEfeed(cycle)
521
-
522
- logRequirements(cycle, model=MODEL, term=MODEL_KEY,
523
- NEm=_group_logs([(animal.get('term', {}).get('@id'), NEm) for (animal, NEm) in animals]),
524
- NEa=_group_logs(NEa),
525
- NEl=_group_logs(NEl),
526
- NEwork=_group_logs(NEwork),
527
- NEp=_group_logs(NEp),
528
- NEg=_group_logs(NEg),
529
- NEwool=NEwool,
530
- REM=REM,
531
- REG=REG,
532
- NEfeed_m=NEfeed_m,
533
- NEfeed_g=NEfeed_g)
534
-
535
- return (
536
- (NEm + _sum_values(NEa) + _sum_values(NEl) + _sum_values(NEwork) + _sum_values(NEp) - NEfeed_m)/REM +
537
- (_sum_values(NEg) + NEwool - NEfeed_g)/REG
538
- ) / (meanDE/100) if all([REM, REG]) else 0
539
-
540
-
541
- def _calculate_meanECHHV(practices: list):
542
- values = list(map(lambda p: _pastureGrass_key_property_value(p, 'energyContentHigherHeatingValue'), practices))
543
- total_weight = sum([weight/100 for _value, weight in values])
544
- return sum([
545
- (value * weight/100 if all([value, weight]) else 0) for value, weight in values
546
- ]) / total_weight if total_weight > 0 else 0
547
-
548
-
549
- def _run_practice(cycle: dict, GE: float, meanECHHV: float):
550
185
  def run(practice: dict):
551
186
  key = practice.get('key', {})
552
187
  key_id = key.get('@id')
553
- input_term_id = _practice_input_id(practice)
188
+ input_term_id = practice_input_id(practice)
554
189
  value = (GE / meanECHHV) * (list_sum(practice.get('value', [0])) / 100)
555
190
 
556
- logRequirements(cycle, model=MODEL, term=input_term_id,
191
+ logRequirements(cycle, model=MODEL, term=input_term_id, model_key=MODEL_KEY,
192
+ REM=REM,
193
+ REG=REG,
194
+ NEwool=NEwool,
195
+ NEm_feed=NEm_feed,
196
+ NEg_feed=NEg_feed,
557
197
  GE=GE,
558
- meanECHHV=meanECHHV,
559
198
  key_id=key_id)
560
199
 
561
- logShouldRun(cycle, MODEL, input_term_id, True)
200
+ logShouldRun(cycle, MODEL, input_term_id, True, model_key=MODEL_KEY)
562
201
 
563
202
  return _input(input_term_id, value)
564
203
 
565
204
  return run
566
205
 
567
206
 
568
- def _should_run_practice(cycle: dict):
569
- def should_run(practice: dict):
570
- term_id = practice.get('term', {}).get('@id')
571
- key_term_type = practice.get('key', {}).get('termType')
572
- value = practice.get('value', [])
573
-
574
- logRequirements(cycle, model=MODEL, term=term_id,
575
- practice_value=list_sum(value),
576
- practice_key_term_type=key_term_type)
577
-
578
- should_run = all([len(value) > 0, term_id == MODEL_KEY, key_term_type in KEY_TERM_TYPES])
579
- logShouldRun(cycle, MODEL, term_id, should_run)
580
- return should_run
581
-
582
- return should_run
583
-
584
-
585
207
  def _should_run(cycle: dict, practices: dict):
586
208
  systems = filter_list_term_type(cycle.get('practices', []), TermTermType.SYSTEM)
587
209
  animalFeed_complete = _is_term_type_complete(cycle, 'animalFeed')
210
+ animalPopulation_incomplete = _is_term_type_incomplete(cycle, 'animalPopulation')
588
211
  freshForage_incomplete = _is_term_type_incomplete(cycle, 'freshForage')
589
212
  all_animals_have_value = all([a.get('value', 0) > 0 for a in cycle.get('animals', [])])
590
213
 
591
- meanDE = _calculate_meanDE(practices)
592
- meanECHHV = _calculate_meanECHHV(practices)
593
- GE = _calculate_GE(cycle, meanDE, systems[0]) if all([meanDE > 0, len(systems) > 0]) else 0
214
+ meanDE = calculate_meanDE(practices)
215
+ meanECHHV = calculate_meanECHHV(practices)
594
216
 
595
217
  should_run = all([
596
- animalFeed_complete, freshForage_incomplete,
218
+ animalFeed_complete,
219
+ animalPopulation_incomplete,
220
+ freshForage_incomplete,
597
221
  all_animals_have_value,
598
- len(systems) > 0, len(practices) > 0,
599
- GE > 0, meanECHHV > 0
222
+ len(systems) > 0,
223
+ len(practices) > 0,
224
+ meanDE > 0,
225
+ meanECHHV > 0
600
226
  ])
601
227
 
602
- for term_id in [MODEL_KEY] + [_practice_input_id(p) for p in practices]:
603
- logRequirements(cycle, model=MODEL, term=term_id,
228
+ for term_id in [MODEL_KEY] + [practice_input_id(p) for p in practices]:
229
+ logRequirements(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
604
230
  term_type_animalFeed_complete=animalFeed_complete,
231
+ term_type_animalPopulation_incomplete=animalPopulation_incomplete,
605
232
  term_type_freshForage_incomplete=freshForage_incomplete,
606
233
  all_animals_have_value=all_animals_have_value,
607
234
  meanDE=meanDE,
608
- meanECHHV=meanECHHV,
609
- GE=GE)
235
+ meanECHHV=meanECHHV)
610
236
 
611
- logShouldRun(cycle, MODEL, term_id, should_run)
237
+ logShouldRun(cycle, MODEL, term_id, should_run, model_key=MODEL_KEY)
612
238
 
613
- return should_run, GE, meanECHHV
239
+ return should_run, meanDE, meanECHHV, systems[0] if systems else None
614
240
 
615
241
 
616
242
  def run(cycle: dict):
617
- practices = list(filter(_should_run_practice(cycle), cycle.get('practices', [])))
618
- should_run, GE, meanECHHV = _should_run(cycle, practices)
619
- return list(map(_run_practice(cycle, GE, meanECHHV), practices)) if should_run else []
243
+ practices = list(filter(should_run_practice(cycle), cycle.get('practices', [])))
244
+ should_run, meanDE, meanECHHV, system = _should_run(cycle, practices)
245
+ return list(map(_run_practice(cycle, meanDE, meanECHHV, system), practices)) if should_run else []