hestia-earth-models 0.59.7__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 (39) hide show
  1. hestia_earth/models/cache_sites.py +4 -2
  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 +14 -2
  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.0.dist-info}/LICENSE +1 -1
  14. {hestia_earth_models-0.59.7.dist-info → hestia_earth_models-0.60.0.dist-info}/METADATA +1 -1
  15. {hestia_earth_models-0.59.7.dist-info → hestia_earth_models-0.60.0.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 +22 -7
  38. {hestia_earth_models-0.59.7.dist-info → hestia_earth_models-0.60.0.dist-info}/WHEEL +0 -0
  39. {hestia_earth_models-0.59.7.dist-info → hestia_earth_models-0.60.0.dist-info}/top_level.txt +0 -0
@@ -112,7 +112,7 @@ def _group_sites(sites: dict):
112
112
  }
113
113
 
114
114
 
115
- def run(sites: list, years: list = None, include_region: bool = False):
115
+ def run(sites: list, years: list = None, include_region: bool = False, years_only: bool = False):
116
116
  """
117
117
  Run all queries at once for the list of provided Sites.
118
118
  Note: Earth Engine needs to be initiliased with `init_gee()` before running this function.
@@ -126,8 +126,10 @@ def run(sites: list, years: list = None, include_region: bool = False):
126
126
  include_region : bool
127
127
  Prefecth region IDs.
128
128
  This will cache region-level data and will make the request slower. Only use if needed.
129
+ years_only : bool
130
+ Run only the collections that depend on the years (if provided).
129
131
  """
130
- rasters, vectors = list_collections(years, include_region)
132
+ rasters, vectors = list_collections(years, include_region, years_only)
131
133
 
132
134
  filtered_data = _group_sites(sites)
133
135
 
File without changes
@@ -0,0 +1,298 @@
1
+ """
2
+ Full Grass Consumption
3
+
4
+ This model estimates the energetic requirements of ruminants and can be used to estimate the amount of grass they graze.
5
+ Source:
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
+ from hestia_earth.schema import TermTermType
9
+ from hestia_earth.utils.model import filter_list_term_type
10
+ from hestia_earth.utils.tools import list_sum
11
+
12
+ from hestia_earth.models.log import logRequirements, logShouldRun, debugValues
13
+ from hestia_earth.models.utils.input import _new_input
14
+ from hestia_earth.models.utils.term import get_wool_terms, get_lookup_value
15
+ from hestia_earth.models.utils.completeness import _is_term_type_complete, _is_term_type_incomplete
16
+ from hestia_earth.models.utils.property import get_node_property
17
+ from .. import MODEL
18
+ from ..pastureGrass_utils import (
19
+ practice_input_id,
20
+ should_run_practice,
21
+ calculate_meanDE,
22
+ calculate_meanECHHV,
23
+ calculate_REM,
24
+ calculate_REG,
25
+ calculate_NEfeed,
26
+ product_wool_energy,
27
+ get_animals,
28
+ get_animal_values
29
+ )
30
+
31
+ REQUIREMENTS = {
32
+ "Cycle": {
33
+ "completeness.animalFeed": "True",
34
+ "completeness.animalPopulation": "True",
35
+ "completeness.freshForage": "False",
36
+ "site": {
37
+ "@type": "Site",
38
+ "siteType": "permanent pasture"
39
+ },
40
+ "practices": [{
41
+ "@type": "Practice",
42
+ "value": "",
43
+ "term.@id": "pastureGrass",
44
+ "key": {
45
+ "@type": "Term",
46
+ "term.termType": "landCover"
47
+ }
48
+ }],
49
+ "animals": [{
50
+ "@type": "Animal",
51
+ "value": "> 0",
52
+ "term.termType": "liveAnimal",
53
+ "referencePeriod": "average",
54
+ "properties": [{
55
+ "@type": "Property",
56
+ "value": "",
57
+ "term.@id": [
58
+ "liveweightPerHead",
59
+ "weightAtMaturity"
60
+ ]
61
+ }],
62
+ "optional": {
63
+ "properties": [{
64
+ "@type": "Property",
65
+ "value": "",
66
+ "term.@id": [
67
+ "hoursWorkedPerDay",
68
+ "pregnancyRateTotal",
69
+ "animalsPerBirth"
70
+ ]
71
+ }],
72
+ "inputs": [{
73
+ "@type": "Input",
74
+ "term.units": "kg",
75
+ "value": "> 0",
76
+ "optional": {
77
+ "properties": [{
78
+ "@type": "Property",
79
+ "value": "",
80
+ "term.@id": ["neutralDetergentFibreContent", "energyContentHigherHeatingValue"]
81
+ }]
82
+ }
83
+ }],
84
+ "practices": [{
85
+ "@type": "Practice",
86
+ "value": "",
87
+ "term.termType": "animalManagement",
88
+ "properties": [{
89
+ "@type": "Property",
90
+ "value": "",
91
+ "term.@id": "fatContent"
92
+ }]
93
+ }]
94
+ }
95
+ }],
96
+ "none": {
97
+ "inputs": [{
98
+ "@type": "Input",
99
+ "term.units": "kg",
100
+ "value": "> 0",
101
+ "isAnimalFeed": "True"
102
+ }]
103
+ },
104
+ "optional": {
105
+ "products": [{
106
+ "@type": "Product",
107
+ "value": "",
108
+ "term.@id": "animalProduct"
109
+ }]
110
+ }
111
+ }
112
+ }
113
+ LOOKUPS = {
114
+ "animalManagement": [
115
+ "mjKgEvMilkIpcc2019"
116
+ ],
117
+ "animalProduct": ["mjKgEvWoolNetEnergyWoolIpcc2019", "allowedLiveAnimalTermIds"],
118
+ "liveAnimal": [
119
+ "ipcc2019AnimalTypeGrouping",
120
+ "mjDayKgCfiNetEnergyMaintenanceIpcc2019",
121
+ "ratioCPregnancyNetEnergyPregnancyIpcc2019",
122
+ "ratioCNetEnergyGrowthCattleBuffaloIpcc2019",
123
+ "mjKgABNetEnergyGrowthSheepGoatsIpcc2019",
124
+ "isWoolProducingAnimal"
125
+ ],
126
+ "system-liveAnimal-activityCoefficient-ipcc2019": "using animal term @id",
127
+ "crop-property": ["energyDigestibilityRuminants", "energyContentHigherHeatingValue"],
128
+ "crop": "grazedPastureGrassInputId",
129
+ "forage-property": ["energyDigestibilityRuminants", "energyContentHigherHeatingValue"],
130
+ "landCover": "grazedPastureGrassInputId"
131
+ }
132
+ RETURNS = {
133
+ "Animal": [{
134
+ "Input": [{
135
+ "@type": "Input",
136
+ "term.termType": ["crop", "forage"],
137
+ "value": ""
138
+ }]
139
+ }]
140
+ }
141
+ MODEL_KEY = 'animal/pastureGrass'
142
+
143
+
144
+ def _input(term_id: str, value: float):
145
+ node = _new_input(term_id, MODEL)
146
+ node['value'] = [value]
147
+ return node
148
+
149
+
150
+ def _sum_liveWeightPerHead(animals: list):
151
+ return list_sum([
152
+ animal.get('value', 0) * get_node_property(animal, 'liveweightPerHead', False).get('value', 0)
153
+ for animal in animals
154
+ ])
155
+
156
+
157
+ def _isNEwool_animal(animal: dict):
158
+ value = get_lookup_value(animal.get('term', {}), 'isWoolProducingAnimal', model=MODEL, model_key=MODEL_KEY)
159
+ return not (not value)
160
+
161
+
162
+ def _is_NEwool_product(product: dict, animal: dict):
163
+ animal_term_ids = get_lookup_value(product, 'allowedLiveAnimalTermIds', model=MODEL, model_key=MODEL_KEY).split(';')
164
+ return animal.get('term', {}).get('@id') in animal_term_ids
165
+
166
+
167
+ def calculate_NEwool(cycle: dict, animal: dict, products: list, total_weight: float) -> float:
168
+ term_id = animal.get('term', {}).get('@id')
169
+
170
+ wool_products = [p for p in products if _is_NEwool_product(p.get('term', {}), animal)]
171
+ total_energy = list_sum([
172
+ list_sum(product.get('value', [])) * product_wool_energy(product) for product in wool_products
173
+ ])
174
+ animal_weight = _sum_liveWeightPerHead([animal])
175
+
176
+ debugValues(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
177
+ total_energy=total_energy,
178
+ animal_liveWeightPerHead=animal_weight,
179
+ total_liveWeightPerHead=total_weight)
180
+
181
+ return total_energy * animal_weight/total_weight
182
+
183
+
184
+ def _calculate_GE(
185
+ cycle: dict, animal: dict, REM: float, REG: float, NEwool: float, system: dict
186
+ ) -> float:
187
+ term_id = animal.get('term', {}).get('@id')
188
+
189
+ NEm, NEa, NEl, NEwork, NEp, NEg = get_animal_values(cycle, animal, system)
190
+
191
+ NEm_feed, NEg_feed = calculate_NEfeed(animal)
192
+ debugValues(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
193
+ NEm_feed=NEm_feed,
194
+ NEg_feed=NEg_feed)
195
+
196
+ return (NEm + NEa + NEl + NEwork + NEp - NEm_feed)/REM + (NEg + NEwool - NEg_feed)/REG
197
+
198
+
199
+ def _run_practice(cycle: dict, GE: float, meanECHHV: float):
200
+ def run(practice: dict):
201
+ key = practice.get('key', {})
202
+ key_id = key.get('@id')
203
+ input_term_id = practice_input_id(practice)
204
+ value = (GE / meanECHHV) * (list_sum(practice.get('value', [0])) / 100)
205
+
206
+ logRequirements(cycle, model=MODEL, term=input_term_id, model_key=MODEL_KEY,
207
+ GE=GE,
208
+ meanECHHV=meanECHHV,
209
+ key_id=key_id)
210
+
211
+ logShouldRun(cycle, MODEL, input_term_id, True, model_key=MODEL_KEY)
212
+
213
+ return _input(input_term_id, value)
214
+
215
+ return run
216
+
217
+
218
+ def _run_animal(cycle: dict, meanDE: float, meanECHHV: float, system: dict, practices: list):
219
+ REM = calculate_REM(meanDE)
220
+ REG = calculate_REG(meanDE)
221
+
222
+ wool_term_ids = get_wool_terms()
223
+ # list of animal product
224
+ wool_products = [p for p in cycle.get('products', []) if p.get('term', {}).get('@id') in wool_term_ids]
225
+ animals = list(filter(_isNEwool_animal, cycle.get('animals', [])))
226
+ total_liveWeightPerHead = _sum_liveWeightPerHead(animals)
227
+
228
+ def run(animal: dict):
229
+ term_id = animal.get('term', {}).get('@id')
230
+
231
+ NEwool = calculate_NEwool(cycle, animal, wool_products, total_liveWeightPerHead) if (
232
+ total_liveWeightPerHead > 0
233
+ ) else 0
234
+ GE = (_calculate_GE(cycle, animal, REM, REG, NEwool, system) / (meanDE/100)) if all([REM, REG]) else 0
235
+
236
+ debugValues(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
237
+ REM=REM,
238
+ REG=REG,
239
+ NEwool=NEwool,
240
+ GE=GE,
241
+ meanDE=meanDE)
242
+
243
+ inputs = list(map(_run_practice(cycle, GE, meanECHHV), practices))
244
+ return animal | {
245
+ 'inputs': animal.get('inputs', []) + inputs
246
+ }
247
+
248
+ return run
249
+
250
+
251
+ def _run(cycle: dict, meanDE: float, meanECHHV: float, system: dict, practices: list):
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):
257
+ systems = filter_list_term_type(cycle.get('practices', []), TermTermType.SYSTEM)
258
+ animalFeed_complete = _is_term_type_complete(cycle, 'animalFeed')
259
+ animalPopulation_complete = _is_term_type_complete(cycle, 'animalPopulation')
260
+ freshForage_incomplete = _is_term_type_incomplete(cycle, 'freshForage')
261
+ all_animals_have_value = all([a.get('value', 0) > 0 for a in cycle.get('animals', [])])
262
+
263
+ no_cycle_inputs_feed = all([not input.get('isAnimalFeed', False) for input in cycle.get('inputs', [])])
264
+
265
+ meanDE = calculate_meanDE(practices)
266
+ meanECHHV = calculate_meanECHHV(practices)
267
+
268
+ should_run = all([
269
+ animalFeed_complete,
270
+ animalPopulation_complete,
271
+ freshForage_incomplete,
272
+ no_cycle_inputs_feed,
273
+ all_animals_have_value,
274
+ len(systems) > 0,
275
+ len(practices) > 0,
276
+ meanDE > 0,
277
+ meanECHHV > 0
278
+ ])
279
+
280
+ for term_id in [MODEL_KEY] + [practice_input_id(p) for p in practices]:
281
+ logRequirements(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
282
+ term_type_animalFeed_complete=animalFeed_complete,
283
+ term_type_animalPopulation_complete=animalPopulation_complete,
284
+ term_type_freshForage_incomplete=freshForage_incomplete,
285
+ no_cycle_inputs_feed=no_cycle_inputs_feed,
286
+ all_animals_have_value=all_animals_have_value,
287
+ meanDE=meanDE,
288
+ meanECHHV=meanECHHV)
289
+
290
+ logShouldRun(cycle, MODEL, term_id, should_run, model_key=MODEL_KEY)
291
+
292
+ return should_run, meanDE, meanECHHV, systems[0] if systems else None
293
+
294
+
295
+ def run(cycle: dict):
296
+ practices = list(filter(should_run_practice(cycle), cycle.get('practices', [])))
297
+ should_run, meanDE, meanECHHV, system = _should_run(cycle, practices)
298
+ return _run(cycle, meanDE, meanECHHV, system, practices) if should_run else []
@@ -50,7 +50,7 @@ RETURNS = {
50
50
  "depth": "30"
51
51
  }]
52
52
  }
53
- TERM_ID = 'co2ToAirSoilCarbonStockChangeManagementChange'
53
+ TERM_ID = 'co2ToAirSoilOrganicCarbonStockChangeManagementChange'
54
54
 
55
55
  DEPTH_UPPER = 0
56
56
  DEPTH_LOWER = 30
@@ -101,7 +101,7 @@ MEASUREMENT_METHOD_RANKING = [
101
101
  ]
102
102
  """
103
103
  A ranking of `MeasurementMethodClassification`s from weakest to strongest used to determine the `EmissionMethodTier` of
104
- the `co2ToAirSoilCarbonStockChangeManagementChange` output.
104
+ the `co2ToAirSoilOrganicCarbonStockChangeManagementChange` output.
105
105
 
106
106
  The `EmissionMethodTier` should be based on the weakest `MeasurementMethodClassification` between the current SOC and
107
107
  previous SOC.