hestia-earth-models 0.64.2__py3-none-any.whl → 0.64.3__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 (45) hide show
  1. hestia_earth/models/agribalyse2016/machineryInfrastructureDepreciatedAmountPerCycle.py +2 -2
  2. hestia_earth/models/cycle/animal/input/hestiaAggregatedData.py +5 -2
  3. hestia_earth/models/cycle/animal/input/properties.py +2 -1
  4. hestia_earth/models/cycle/animal/milkYield.py +2 -1
  5. hestia_earth/models/cycle/concentrateFeed.py +8 -8
  6. hestia_earth/models/cycle/cycleDuration.py +4 -5
  7. hestia_earth/models/cycle/siteDuration.py +15 -5
  8. hestia_earth/models/cycle/startDateDefinition.py +3 -4
  9. hestia_earth/models/cycle/stockingDensityAnimalHousingAverage.py +52 -0
  10. hestia_earth/models/ipcc2019/aboveGroundBiomass.py +762 -0
  11. hestia_earth/models/ipcc2019/aboveGroundBiomass_utils.py +180 -0
  12. hestia_earth/models/ipcc2019/animal/liveweightGain.py +88 -0
  13. hestia_earth/models/ipcc2019/animal/liveweightPerHead.py +88 -0
  14. hestia_earth/models/ipcc2019/animal/pastureGrass.py +51 -42
  15. hestia_earth/models/ipcc2019/animal/utils.py +20 -0
  16. hestia_earth/models/ipcc2019/animal/weightAtMaturity.py +10 -15
  17. hestia_earth/models/ipcc2019/ch4ToAirExcreta.py +2 -2
  18. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1_utils.py +37 -50
  19. hestia_earth/models/ipcc2019/organicCarbonPerHa_utils.py +0 -19
  20. hestia_earth/models/ipcc2019/pastureGrass.py +44 -31
  21. hestia_earth/models/ipcc2019/pastureGrass_utils.py +38 -22
  22. hestia_earth/models/mocking/search-results.json +223 -223
  23. hestia_earth/models/poschEtAl2008/terrestrialEutrophicationPotentialAccumulatedExceedance.py +40 -0
  24. hestia_earth/models/utils/blank_node.py +20 -1
  25. hestia_earth/models/utils/crop.py +4 -0
  26. hestia_earth/models/utils/ecoClimateZone.py +99 -0
  27. hestia_earth/models/utils/productivity.py +1 -1
  28. hestia_earth/models/utils/property.py +2 -2
  29. hestia_earth/models/version.py +1 -1
  30. {hestia_earth_models-0.64.2.dist-info → hestia_earth_models-0.64.3.dist-info}/METADATA +1 -1
  31. {hestia_earth_models-0.64.2.dist-info → hestia_earth_models-0.64.3.dist-info}/RECORD +45 -31
  32. tests/models/cycle/test_siteDuration.py +22 -0
  33. tests/models/cycle/test_stockingDensityAnimalHousingAverage.py +42 -0
  34. tests/models/ipcc2019/animal/test_liveweightGain.py +20 -0
  35. tests/models/ipcc2019/animal/test_liveweightPerHead.py +20 -0
  36. tests/models/ipcc2019/animal/test_pastureGrass.py +1 -1
  37. tests/models/ipcc2019/test_aboveGroundBiomass.py +182 -0
  38. tests/models/ipcc2019/test_aboveGroundBiomass_utils.py +92 -0
  39. tests/models/ipcc2019/test_organicCarbonPerHa_tier_1_utils.py +3 -2
  40. tests/models/ipcc2019/test_pastureGrass.py +2 -2
  41. tests/models/poschEtAl2008/test_terrestrialEutrophicationPotentialAccumulatedExceedance.py +44 -0
  42. tests/models/utils/test_ecoClimateZone.py +152 -0
  43. {hestia_earth_models-0.64.2.dist-info → hestia_earth_models-0.64.3.dist-info}/LICENSE +0 -0
  44. {hestia_earth_models-0.64.2.dist-info → hestia_earth_models-0.64.3.dist-info}/WHEEL +0 -0
  45. {hestia_earth_models-0.64.2.dist-info → hestia_earth_models-0.64.3.dist-info}/top_level.txt +0 -0
@@ -26,15 +26,14 @@ from hestia_earth.models.utils.blank_node import (
26
26
  cumulative_nodes_match, cumulative_nodes_lookup_match, cumulative_nodes_term_match,
27
27
  node_lookup_match, node_term_match, group_nodes_by_year
28
28
  )
29
+ from hestia_earth.models.utils.ecoClimateZone import EcoClimateZone, get_eco_climate_zone_value
29
30
  from hestia_earth.models.utils.descriptive_stats import calc_descriptive_stats
30
31
  from hestia_earth.models.utils.measurement import _new_measurement
31
32
  from hestia_earth.models.utils.property import get_node_property
32
33
 
33
34
  from .organicCarbonPerHa_utils import (
34
- check_irrigation, DEPTH_LOWER, DEPTH_UPPER, EcoClimateZone,
35
- get_cover_crop_property_terms_with_cache,
36
- get_residue_removed_or_burnt_terms_with_cache,
37
- get_upland_rice_land_cover_terms_with_cache,
35
+ check_irrigation, DEPTH_LOWER, DEPTH_UPPER, get_cover_crop_property_terms_with_cache,
36
+ get_residue_removed_or_burnt_terms_with_cache, get_upland_rice_land_cover_terms_with_cache,
38
37
  IPCC_SOIL_CATEGORY_TO_SOIL_TYPE_LOOKUP_VALUE, IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE,
39
38
  IPCC_MANAGEMENT_CATEGORY_TO_GRASSLAND_MANAGEMENT_TERM_ID,
40
39
  IPCC_MANAGEMENT_CATEGORY_TO_TILLAGE_MANAGEMENT_LOOKUP_VALUE, IpccSoilCategory, IpccCarbonInputCategory,
@@ -175,15 +174,15 @@ _SOC_REFS = {
175
174
  IpccSoilCategory.SANDY_SOILS: {
176
175
  EcoClimateZone.WARM_TEMPERATE_MOIST: {"value": 36000, "uncertainty": 23, "observations": 39},
177
176
  EcoClimateZone.WARM_TEMPERATE_DRY: {"value": 10000, "uncertainty": 5, "observations": 338},
178
- EcoClimateZone.COOL_TEMPERATE_MOIST: {"value": 81000, "uncertainty": 5, "observations": 334},
179
- EcoClimateZone.COOL_TEMPERATE_DRY: {"value": 51000, "uncertainty": 13, "observations": 126},
177
+ EcoClimateZone.COOL_TEMPERATE_MOIST: {"value": 51000, "uncertainty": 13, "observations": 126},
178
+ EcoClimateZone.COOL_TEMPERATE_DRY: {"value": 13000, "uncertainty": 33, "observations": 10},
180
179
  EcoClimateZone.POLAR_MOIST: {"value": 27000, "uncertainty": 67, "observations": 18},
181
180
  EcoClimateZone.POLAR_DRY: {"value": 27000, "uncertainty": 67, "observations": 18},
182
181
  EcoClimateZone.BOREAL_MOIST: {"value": 10000, "uncertainty": 90},
183
182
  EcoClimateZone.BOREAL_DRY: {"value": 10000, "uncertainty": 90},
184
183
  EcoClimateZone.TROPICAL_MONTANE: {"value": 52000, "uncertainty": 34, "observations": 11},
185
184
  EcoClimateZone.TROPICAL_WET: {"value": 46000, "uncertainty": 20, "observations": 43},
186
- EcoClimateZone.TROPICAL_MOIST: {"value": 27, "uncertainty": 12, "observations": 76},
185
+ EcoClimateZone.TROPICAL_MOIST: {"value": 27000, "uncertainty": 12, "observations": 76},
187
186
  EcoClimateZone.TROPICAL_DRY: {"value": 9000, "uncertainty": 9, "observations": 164}
188
187
  },
189
188
  IpccSoilCategory.SPODIC_SOILS: {
@@ -645,35 +644,48 @@ def should_run(site: dict) -> tuple[bool, dict, dict]:
645
644
  Returns
646
645
  -------
647
646
  tuple[bool, dict, dict]
648
- A tuple containing `(should_run_, inventory, kwargs)`.
647
+ A tuple containing `(should_run_, inventory, kwargs, logs)`.
649
648
  """
650
649
  site_type = site.get("siteType", "")
651
650
  management_nodes = site.get("management", [])
652
651
  measurement_nodes = site.get("measurements", [])
653
652
 
653
+ eco_climate_zone = get_eco_climate_zone_value(site, as_enum=True)
654
+ ipcc_soil_category = _assign_ipcc_soil_category(measurement_nodes)
655
+ soc_ref = _get_soc_ref_preview(ipcc_soil_category, eco_climate_zone)
656
+
654
657
  has_management = len(management_nodes) > 0
655
658
  has_measurements = len(measurement_nodes) > 0
656
659
 
657
660
  should_compile_inventory = all([
658
661
  site_type in _VALID_SITE_TYPES,
662
+ eco_climate_zone not in _EXCLUDED_ECO_CLIMATE_ZONES,
663
+ soc_ref or -9999 > 0,
659
664
  has_management,
660
665
  has_measurements
661
666
  ])
662
667
 
663
- inventory, kwargs = (
664
- _compile_inventory(site_type, management_nodes, measurement_nodes)
665
- if should_compile_inventory else ({}, {})
668
+ inventory, inventory_logs = (
669
+ _compile_inventory(
670
+ site_type,
671
+ management_nodes,
672
+ ipcc_soil_category
673
+ ) if should_compile_inventory else ({}, {})
666
674
  )
667
- kwargs["seed"] = gen_seed(site)
668
675
 
669
- should_run_ = all([
670
- kwargs.get("eco_climate_zone") not in _EXCLUDED_ECO_CLIMATE_ZONES,
671
- (kwargs.get("soc_ref") or -9999) > 0,
672
- any(year for year, group in inventory.items() if group.get(_InventoryKey.SHOULD_RUN))
673
- ])
676
+ kwargs = {
677
+ "seed": gen_seed(site),
678
+ "eco_climate_zone": eco_climate_zone,
679
+ "ipcc_soil_category": ipcc_soil_category,
680
+ }
674
681
 
675
- logs = {
682
+ should_run_ = any(
683
+ year for year, group in inventory.items() if group.get(_InventoryKey.SHOULD_RUN)
684
+ )
685
+
686
+ logs = inventory_logs | {
676
687
  "site_type": site_type,
688
+ "soc_ref": soc_ref,
677
689
  "has_management": has_management,
678
690
  "has_measurements": has_measurements,
679
691
  "should_compile_inventory_tier_1": should_compile_inventory,
@@ -965,7 +977,7 @@ def _calc_soc_stocks(
965
977
 
966
978
 
967
979
  def _compile_inventory(
968
- site_type: str, management_nodes: list[dict], measurement_nodes: list[dict]
980
+ site_type: str, management_nodes: list[dict], ipcc_soil_category: IpccSoilCategory
969
981
  ) -> tuple[dict, dict]:
970
982
  """
971
983
  Builds an annual inventory of data and a dictionary of keyword arguments for the tier 1 model.
@@ -978,17 +990,14 @@ def _compile_inventory(
978
990
  A valid [site type](https://www-staging.hestia.earth/schema/Site#siteType).
979
991
  management_nodes : list[dict]
980
992
  A list of [Management nodes](https://www-staging.hestia.earth/schema/Management).
981
- measurement_nodes: list[dict]
982
- A list of [Measurement nodes](https://www-staging.hestia.earth/schema/Measurement).
993
+ ipcc_soil_category : IpccSoilCategory
994
+ The site's assigned IPCC soil category.
983
995
 
984
996
  Returns
985
997
  -------
986
998
  tuple[dict, dict]
987
- A tuple containing `(inventory, kwargs)`.
999
+ A tuple containing `(inventory, logs)`.
988
1000
  """
989
- eco_climate_zone = _get_eco_climate_zone(measurement_nodes)
990
- ipcc_soil_category = _assign_ipcc_soil_category(measurement_nodes)
991
- soc_ref = _get_soc_ref_preview(ipcc_soil_category, eco_climate_zone)
992
1001
  grouped_management = group_nodes_by_year(management_nodes)
993
1002
 
994
1003
  # If no `landCover` nodes in `site.management` use `site.siteType` to assign static `IpccLandUseCategory`.
@@ -1036,33 +1045,11 @@ def _compile_inventory(
1036
1045
  }
1037
1046
 
1038
1047
  inventory = merge(grouped_data, grouped_should_run)
1039
- kwargs = {
1040
- "eco_climate_zone": eco_climate_zone,
1041
- "ipcc_soil_category": ipcc_soil_category,
1042
- "run_with_site_type": run_with_site_type,
1043
- "soc_ref": soc_ref
1048
+ logs = {
1049
+ "run_with_site_type": run_with_site_type
1044
1050
  }
1045
1051
 
1046
- return inventory, kwargs
1047
-
1048
-
1049
- def _get_eco_climate_zone(measurement_nodes: list[dict]) -> Union[EcoClimateZone, None]:
1050
- """
1051
- Get the eco-climate zone value from a list of Measurement nodes.
1052
-
1053
- Parameters
1054
- ----------
1055
- measurement_nodes : list[dict]
1056
- A list of [Measurement nodes](https://www-staging.hestia.earth/schema/Measurement).
1057
-
1058
- Returns
1059
- -------
1060
- EcoClimateZone | None
1061
- The eco-climate zone value if found, otherwise `None`.
1062
- """
1063
- eco_climate_zone = find_term_match(measurement_nodes, "ecoClimateZone")
1064
- value = get_node_value(eco_climate_zone)
1065
- return EcoClimateZone(value) if value else None
1052
+ return inventory, logs
1066
1053
 
1067
1054
 
1068
1055
  def _assign_ipcc_soil_category(
@@ -43,25 +43,6 @@ def get_upland_rice_land_cover_terms_with_cache():
43
43
  return lru_cache()(get_upland_rice_land_cover_terms)()
44
44
 
45
45
 
46
- class EcoClimateZone(Enum):
47
- """
48
- Enum representing eco-climate zones. The value of each member of the Enum correctly corresponds with the values of
49
- `ecoClimateZone` term and the `ecoClimateZone-lookup.csv`.
50
- """
51
- WARM_TEMPERATE_MOIST = 1
52
- WARM_TEMPERATE_DRY = 2
53
- COOL_TEMPERATE_MOIST = 3
54
- COOL_TEMPERATE_DRY = 4
55
- POLAR_MOIST = 5
56
- POLAR_DRY = 6
57
- BOREAL_MOIST = 7
58
- BOREAL_DRY = 8
59
- TROPICAL_MONTANE = 9
60
- TROPICAL_WET = 10
61
- TROPICAL_MOIST = 11
62
- TROPICAL_DRY = 12
63
-
64
-
65
46
  class IpccSoilCategory(Enum):
66
47
  """
67
48
  Enum representing IPCC Soil Categories.
@@ -10,7 +10,7 @@ This version of the model will run at the Cycle level, if at least one Cycle Inp
10
10
  """
11
11
  from hestia_earth.schema import TermTermType
12
12
  from hestia_earth.utils.model import filter_list_term_type
13
- from hestia_earth.utils.tools import list_sum
13
+ from hestia_earth.utils.tools import list_sum, non_empty_list
14
14
 
15
15
  from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
16
16
  from hestia_earth.models.utils.blank_node import lookups_logs, properties_logs
@@ -41,15 +41,22 @@ REQUIREMENTS = {
41
41
  "@type": "Site",
42
42
  "siteType": "permanent pasture"
43
43
  },
44
- "practices": [{
45
- "@type": "Practice",
46
- "value": "",
47
- "term.@id": "pastureGrass",
48
- "key": {
49
- "@type": "Term",
50
- "term.termType": "landCover"
44
+ "practices": [
45
+ {
46
+ "@type": "Practice",
47
+ "value": "",
48
+ "term.termType": "system"
49
+ },
50
+ {
51
+ "@type": "Practice",
52
+ "value": "",
53
+ "term.@id": "pastureGrass",
54
+ "key": {
55
+ "@type": "Term",
56
+ "term.termType": "landCover"
57
+ }
51
58
  }
52
- }],
59
+ ],
53
60
  "inputs": [{
54
61
  "@type": "Input",
55
62
  "term.units": "kg",
@@ -142,7 +149,8 @@ LOOKUPS = {
142
149
  RETURNS = {
143
150
  "Input": [{
144
151
  "term.termType": ["crop", "forage"],
145
- "value": ""
152
+ "value": "",
153
+ "isAnimalFeed": "True"
146
154
  }]
147
155
  }
148
156
  MODEL_KEY = 'pastureGrass'
@@ -151,6 +159,7 @@ MODEL_KEY = 'pastureGrass'
151
159
  def _input(term_id: str, value: float):
152
160
  node = _new_input(term_id, MODEL)
153
161
  node['value'] = [value]
162
+ node['isAnimalFeed'] = True
154
163
  return node
155
164
 
156
165
 
@@ -163,16 +172,14 @@ def calculate_NEwool(cycle: dict) -> float:
163
172
  return sum([value * lookup_value for (value, lookup_value) in product_values])
164
173
 
165
174
 
166
- def _run_practice(cycle: dict, meanDE: float, meanECHHV: float, system: dict):
175
+ def _run_practice(cycle: dict, meanDE: float, meanECHHV: float, REM: float, REG: float, systems: list):
167
176
  animals = get_animals_by_period(cycle)
168
- REM = calculate_REM(meanDE)
169
- REG = calculate_REG(meanDE)
170
177
  NEwool = calculate_NEwool(cycle)
171
- NEm_feed, NEg_feed = calculate_NEfeed(cycle)
178
+ NEm_feed, NEg_feed, log_feed = calculate_NEfeed(cycle)
172
179
 
173
180
  animal_values = [{
174
- 'animalId': animal.get('term', {}).get('@id')
175
- } | get_animal_values(cycle, animal, system) for animal in animals]
181
+ 'id': animal.get('term', {}).get('@id')
182
+ } | get_animal_values(cycle, animal, systems) for animal in animals]
176
183
 
177
184
  GE = (
178
185
  calculate_GE(animal_values, REM, REG, NEwool, NEm_feed, NEg_feed) / (meanDE/100)
@@ -185,15 +192,11 @@ def _run_practice(cycle: dict, meanDE: float, meanECHHV: float, system: dict):
185
192
  value = (GE / meanECHHV) * (list_sum(practice.get('value', [0])) / 100)
186
193
 
187
194
  logs = log_as_table([v | {
188
- 'practiceKeyId': key_id,
189
- 'REM': REM,
190
- 'REG': REG,
191
195
  'NEwool': NEwool,
192
- 'NEmFeed': NEm_feed,
193
- 'NEgFeed': NEg_feed,
194
- 'GE': GE,
195
- 'meanECHHV': meanECHHV,
196
- 'meanDE': meanDE
196
+ 'total-feed-NEm': NEm_feed,
197
+ 'total-feed-NEg': NEg_feed,
198
+ 'practiceKeyId': key_id,
199
+ 'GE': GE
197
200
  } for v in animal_values])
198
201
  animal_lookups = lookups_logs(MODEL, animals, LOOKUPS, model_key=MODEL_KEY, term=input_term_id)
199
202
  animal_properties = properties_logs(animals, properties=[
@@ -207,15 +210,19 @@ def _run_practice(cycle: dict, meanDE: float, meanECHHV: float, system: dict):
207
210
  'weightAtOneYear',
208
211
  'weightAtSlaughter'
209
212
  ])
213
+ has_positive_feed_values = all([NEm_feed > 0, NEg_feed > 0])
210
214
 
211
215
  logRequirements(cycle, model=MODEL, term=input_term_id, model_key=MODEL_KEY,
216
+ feed_logs=log_as_table(log_feed),
217
+ has_positive_feed_values=has_positive_feed_values,
212
218
  animal_logs=logs,
213
219
  animal_lookups=animal_lookups,
214
220
  animal_properties=animal_properties)
215
221
 
216
- logShouldRun(cycle, MODEL, input_term_id, True, model_key=MODEL_KEY)
222
+ should_run = all([has_positive_feed_values])
223
+ logShouldRun(cycle, MODEL, input_term_id, should_run, model_key=MODEL_KEY)
217
224
 
218
- return _input(input_term_id, value)
225
+ return _input(input_term_id, value) if should_run else None
219
226
 
220
227
  return run
221
228
 
@@ -231,6 +238,8 @@ def _should_run(cycle: dict, practices: dict):
231
238
 
232
239
  meanDE = calculate_meanDE(practices)
233
240
  meanECHHV = calculate_meanECHHV(practices)
241
+ REM = calculate_REM(meanDE)
242
+ REG = calculate_REG(meanDE)
234
243
 
235
244
  should_run = all([
236
245
  animalFeed_complete,
@@ -251,15 +260,19 @@ def _should_run(cycle: dict, practices: dict):
251
260
  term_type_freshForage_incomplete=freshForage_incomplete,
252
261
  has_cycle_inputs_feed=has_cycle_inputs_feed,
253
262
  all_animals_have_value=all_animals_have_value,
254
- meanDE=calculate_meanDE(practices, term=term_id),
255
- meanECHHV=calculate_meanECHHV(practices, term=term_id))
263
+ grass_MeanDE=calculate_meanDE(practices, term=term_id),
264
+ grass_MeanECHHV=calculate_meanECHHV(practices, term=term_id),
265
+ grass_REM=REM,
266
+ grass_REG=REG)
256
267
 
257
268
  logShouldRun(cycle, MODEL, term_id, should_run, model_key=MODEL_KEY)
258
269
 
259
- return should_run, meanDE, meanECHHV, systems[0] if systems else None
270
+ return should_run, meanDE, meanECHHV, REM, REG, systems
260
271
 
261
272
 
262
273
  def run(cycle: dict):
263
274
  practices = list(filter(should_run_practice(cycle), cycle.get('practices', [])))
264
- should_run, meanDE, meanECHHV, system = _should_run(cycle, practices)
265
- return list(map(_run_practice(cycle, meanDE, meanECHHV, system), practices)) if should_run else []
275
+ should_run, meanDE, meanECHHV, REM, REG, systems = _should_run(cycle, practices)
276
+ return non_empty_list(
277
+ map(_run_practice(cycle, meanDE, meanECHHV, REM, REG, systems), practices)
278
+ ) if should_run else []
@@ -25,12 +25,20 @@ def _get_grouping(animal: dict) -> str:
25
25
  return get_lookup_value(term, 'ipcc2019AnimalTypeGrouping', model=MODEL, model_key=MODEL_KEY)
26
26
 
27
27
 
28
- def _get_activityCoefficient(cycle: dict, animal: dict, system: dict) -> float:
28
+ def _get_activityCoefficient(animal: dict, systems: list) -> float:
29
29
  term = animal.get('term', {})
30
30
  term_id = term.get('@id')
31
- system_id = system.get('term', {}).get('@id')
32
31
  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)
32
+
33
+ activityCoefficient = list_sum([
34
+ safe_parse_float(
35
+ get_table_value(lookup, 'termid', system.get('term', {}).get('@id'), column_name(term_id)),
36
+ 0
37
+ ) * list_sum(system.get('value', [0]))
38
+ for system in systems
39
+ ]) / list_sum([
40
+ list_sum(system.get('value', [0])) for system in systems
41
+ ])
34
42
 
35
43
  return activityCoefficient
36
44
 
@@ -49,16 +57,16 @@ def _calculate_NEm(cycle: dict, animal: dict) -> float:
49
57
  return NEm
50
58
 
51
59
 
52
- def _calculate_NEa_cattleAndBuffalo(cycle: dict, animal: dict, system: dict, NEm: float) -> float:
53
- activityCoefficient = _get_activityCoefficient(cycle, animal, system)
60
+ def _calculate_NEa_cattleAndBuffalo(cycle: dict, animal: dict, systems: list, NEm: float) -> float:
61
+ activityCoefficient = _get_activityCoefficient(animal, systems)
54
62
 
55
63
  NEa = activityCoefficient * NEm
56
64
 
57
65
  return NEa
58
66
 
59
67
 
60
- def _calculate_NEa_sheepAndGoat(cycle: dict, animal: dict, system: dict, _NEm: float) -> float:
61
- activityCoefficient = _get_activityCoefficient(cycle, animal, system)
68
+ def _calculate_NEa_sheepAndGoat(cycle: dict, animal: dict, systems: list, _NEm: float) -> float:
69
+ activityCoefficient = _get_activityCoefficient(animal, systems)
62
70
 
63
71
  liveweightPerHead = get_node_property(animal, 'liveweightPerHead', False).get('value', 0)
64
72
  animal_value = animal.get('value', 0)
@@ -74,9 +82,9 @@ _NEa_BY_GROUPING = {
74
82
  }
75
83
 
76
84
 
77
- def _calculate_NEa(cycle: dict, animal: dict, system: dict, NEm: float) -> float:
85
+ def _calculate_NEa(cycle: dict, animal: dict, systems: list, NEm: float) -> float:
78
86
  grouping = _get_grouping(animal)
79
- return _NEa_BY_GROUPING.get(grouping, lambda *args: 0)(cycle, animal, system, NEm)
87
+ return _NEa_BY_GROUPING.get(grouping, lambda *args: 0)(cycle, animal, systems, NEm)
80
88
 
81
89
 
82
90
  def _calculate_NEl_cattleAndBuffalo(cycle: dict, animal: dict) -> float:
@@ -234,7 +242,7 @@ def _calculate_feed_meanDE(log_node: dict, input: dict) -> float:
234
242
  debugValues(log_node, model=MODEL, term=term_id, model_key=MODEL_KEY,
235
243
  energyContent=energyContent,
236
244
  energyDigestibility=energyDigestibility,
237
- meanDE=meanDE)
245
+ feed_MeanDE=meanDE)
238
246
 
239
247
  return meanDE
240
248
 
@@ -246,7 +254,7 @@ def _calculate_NEfeed_m(log_node: dict, input: dict, meanDE: float) -> float:
246
254
  REm = calculate_REM(energyDigestibility * 100)
247
255
 
248
256
  debugValues(log_node, model=MODEL, term=term_id, model_key=MODEL_KEY,
249
- REm=REm)
257
+ feed_REm=REm)
250
258
 
251
259
  input_value = list_sum(input.get('value'))
252
260
  return meanDE * REm * input_value
@@ -259,7 +267,7 @@ def _calculate_NEfeed_g(log_node: dict, input: dict, meanDE: float) -> float:
259
267
  REg = calculate_REG(energyDigestibility * 100)
260
268
 
261
269
  debugValues(log_node, model=MODEL, term=term_id, model_key=MODEL_KEY,
262
- REg=REg)
270
+ feed_REg=REg)
263
271
 
264
272
  input_value = list_sum(input.get('value'))
265
273
  return meanDE * REg * input_value
@@ -267,21 +275,29 @@ def _calculate_NEfeed_g(log_node: dict, input: dict, meanDE: float) -> float:
267
275
 
268
276
  def calculate_NEfeed(node: dict) -> tuple:
269
277
  inputs = get_feed_inputs(node)
278
+
270
279
  # calculate meanDE for each input first
271
- inputs = [(input, _calculate_feed_meanDE(node, input)) for input in inputs]
272
- NEfeed_m = sum([
273
- _calculate_NEfeed_m(node, input, meanDE) for (input, meanDE) in inputs
274
- ]) if len(inputs) > 0 else 0
275
- NEfeed_g = sum([
276
- _calculate_NEfeed_g(node, input, meanDE) for (input, meanDE) in inputs
277
- ]) if len(inputs) > 0 else 0
280
+ values = [
281
+ (i, {'id': i.get('term', {}).get('@id'), 'meanDE': _calculate_feed_meanDE(node, i)})
282
+ for i in inputs
283
+ ]
284
+ values = [
285
+ value | {
286
+ 'NEm': _calculate_NEfeed_m(node, input, value.get('meanDE')),
287
+ 'NEg': _calculate_NEfeed_g(node, input, value.get('meanDE'))
288
+ }
289
+ for input, value in values
290
+ ]
291
+
292
+ NEfeed_m = sum([value.get('NEm') for value in values]) if len(values) > 0 else 0
293
+ NEfeed_g = sum([value.get('NEg') for value in values]) if len(values) > 0 else 0
278
294
 
279
- return (NEfeed_m, NEfeed_g)
295
+ return (NEfeed_m, NEfeed_g, values)
280
296
 
281
297
 
282
- def get_animal_values(cycle: dict, animal: dict, system: dict) -> dict:
298
+ def get_animal_values(cycle: dict, animal: dict, systems: list) -> dict:
283
299
  NEm = _calculate_NEm(cycle, animal)
284
- NEa = _calculate_NEa(cycle, animal, system, NEm)
300
+ NEa = _calculate_NEa(cycle, animal, systems, NEm)
285
301
  NEl = _calculate_NEl(cycle, animal)
286
302
  NEwork = _calculate_NEwork(cycle, animal, NEm)
287
303
  NEp = _calculate_NEp(cycle, animal, NEm)