hestia-earth-models 0.74.0__py3-none-any.whl → 0.74.2__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.

@@ -28,8 +28,7 @@ from .utils import (
28
28
  TOTAL_CROPLAND,
29
29
  TOTAL_AGRICULTURAL_CHANGE,
30
30
  ALL_LAND_USE_TERMS,
31
- crop_ipcc_land_use_category,
32
- LAND_USE_NAMES_FROM_ID
31
+ crop_ipcc_land_use_category
33
32
  )
34
33
  from . import MODEL
35
34
 
@@ -839,15 +838,17 @@ def _should_run_historical_land_use_change_single_crop(
839
838
  percent_pasture_was=percent_pasture_was
840
839
  )
841
840
 
841
+ fao_name = _get_faostat_name(term)
842
+
842
843
  # Cell E8
843
844
  expansion_factor = _get_ratio_start_and_end_values(
844
- expansion=changes[LAND_USE_NAMES_FROM_ID[term.get("@id")]],
845
+ expansion=changes[land_use_type],
845
846
  fao_name=land_use_type,
846
847
  country_id=country_id,
847
848
  end_year=end_year
848
- ) if term.get("@id") in _TOP_LEVEL_LAND_USE_TYPE_IDS else get_ratio_of_expanded_area(
849
+ ) if term.get("@id") in _TOP_LEVEL_LAND_USE_TYPE_IDS or fao_name == "" else get_ratio_of_expanded_area(
849
850
  country_id=country_id,
850
- fao_name=_get_faostat_name(term),
851
+ fao_name=fao_name,
851
852
  end_year=end_year
852
853
  )
853
854
 
@@ -1,9 +1,9 @@
1
1
  from typing import List
2
2
  from datetime import timedelta, datetime
3
- from hestia_earth.schema import SchemaType, TermTermType, SiteSiteType, COMPLETENESS_MAPPING
3
+ from hestia_earth.schema import SchemaType, TermTermType, COMPLETENESS_MAPPING
4
4
  from hestia_earth.utils.lookup import column_name, get_table_value, download_lookup
5
5
  from hestia_earth.utils.model import filter_list_term_type
6
- from hestia_earth.utils.tools import safe_parse_float, flatten
6
+ from hestia_earth.utils.tools import safe_parse_float, flatten, is_number, is_boolean
7
7
  from hestia_earth.utils.blank_node import get_node_value
8
8
 
9
9
  from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
@@ -16,6 +16,7 @@ from hestia_earth.models.utils.site import (
16
16
  related_cycles, get_land_cover_term_id as get_landCover_term_id_from_site_type
17
17
  )
18
18
  from . import MODEL
19
+ from ..utils.property import get_property_lookup_value
19
20
 
20
21
  REQUIREMENTS = {
21
22
  "Site": {
@@ -67,11 +68,12 @@ RETURNS = {
67
68
  }]
68
69
  }
69
70
  LOOKUPS = {
71
+ "biochar": "inputGapFillManagementTermId",
70
72
  "crop": ["landCoverTermId", "maximumCycleDuration"],
71
73
  "forage": ["landCoverTermId"],
72
- "inorganicFertiliser": "nitrogenContent",
73
- "organicFertiliser": "ANIMAL_MANURE",
74
- "soilAmendment": "PRACTICE_INCREASING_C_INPUT",
74
+ "inorganicFertiliser": "inputGapFillManagementTermId",
75
+ "organicFertiliser": "inputGapFillManagementTermId",
76
+ "soilAmendment": "inputGapFillManagementTermId",
75
77
  "landUseManagement": "GAP_FILL_TO_MANAGEMENT",
76
78
  "property": "GAP_FILL_TO_MANAGEMENT"
77
79
  }
@@ -87,41 +89,6 @@ _PRACTICES_TERM_TYPES = [
87
89
  TermTermType.LANDCOVER
88
90
  ]
89
91
  _PRACTICES_COMPLETENESS_MAPPING = COMPLETENESS_MAPPING.get(SchemaType.PRACTICE.value, {})
90
- _ANIMAL_MANURE_USED_TERM_ID = "animalManureUsed"
91
- _INORGANIC_NITROGEN_FERTILISER_USED_TERM_ID = "inorganicNitrogenFertiliserUsed"
92
- _ORGANIC_FERTILISER_USED_TERM_ID = "organicFertiliserUsed"
93
- _AMENDMENT_INCREASING_C_USED_TERM_ID = "amendmentIncreasingSoilCarbonUsed"
94
- _INPUT_RULES = {
95
- TermTermType.INORGANICFERTILISER.value: (
96
- (
97
- TermTermType.INORGANICFERTILISER.value, # Lookup column
98
- lambda x: safe_parse_float(x, default=0) > 0, # Condition
99
- _INORGANIC_NITROGEN_FERTILISER_USED_TERM_ID # New term.
100
- ),
101
- ),
102
- TermTermType.SOILAMENDMENT.value: (
103
- (
104
- TermTermType.SOILAMENDMENT.value,
105
- lambda x: bool(x) is True,
106
- _AMENDMENT_INCREASING_C_USED_TERM_ID
107
- ),
108
- ),
109
- TermTermType.ORGANICFERTILISER.value: (
110
- (
111
- TermTermType.SOILAMENDMENT.value,
112
- lambda x: bool(x) is True,
113
- _ORGANIC_FERTILISER_USED_TERM_ID
114
- ),
115
- (
116
- TermTermType.ORGANICFERTILISER.value,
117
- lambda x: bool(x) is True,
118
- _ANIMAL_MANURE_USED_TERM_ID
119
- )
120
- )
121
- }
122
- _SKIP_LAND_COVER_SITE_TYPES = [
123
- SiteSiteType.CROPLAND.value
124
- ]
125
92
 
126
93
 
127
94
  def management(data: dict):
@@ -135,6 +102,10 @@ def management(data: dict):
135
102
  return node
136
103
 
137
104
 
105
+ def _is_cover_crop(term_id: str) -> bool:
106
+ return get_property_lookup_value(model=MODEL, term_id=term_id, column="blankNodesGroup") == "Cover crops"
107
+
108
+
138
109
  def _get_cycle_duration(cycle: dict, land_cover_id: str = None):
139
110
  cycle_duration = cycle.get('cycleDuration')
140
111
  lookup_value = None if cycle_duration or not land_cover_id else safe_parse_float(get_table_value(
@@ -227,43 +198,63 @@ def _get_relevant_items(cycle: dict, item_name: str, term_types: List[TermTermTy
227
198
  ]
228
199
 
229
200
 
230
- def _process_rule(node: dict, term: dict) -> list:
231
- term_types = []
232
- for column, condition, new_term in _INPUT_RULES[term.get('termType')]:
233
- lookup_result = get_lookup_value(term, LOOKUPS[column], model=MODEL, term=term.get('@id'), model_key=MODEL_KEY)
201
+ def _input_gap_fill_term_id(input: dict):
202
+ return get_lookup_value(input.get('term'), 'inputGapFillManagementTermId')
234
203
 
235
- if condition(lookup_result):
236
- term_types.append(node | {'id': new_term})
237
204
 
238
- return term_types
205
+ def _input_value_valid(input: dict):
206
+ value = get_node_value(input)
207
+ return value > 0 if is_number(value) else bool(value) is True if is_boolean(value) else False
239
208
 
240
209
 
241
- def _run_from_inputs(site: dict, cycle: dict) -> list:
242
- inputs = flatten([
243
- _process_rule(node={
210
+ def _run_from_inputs(cycle: dict) -> list:
211
+ inputs_with_ids = [
212
+ {
213
+ 'input-id': input.get('term', {}).get('@id'),
214
+ 'input-valid': _input_value_valid(input),
215
+ 'term-id': _input_gap_fill_term_id(input)
216
+ } for input in cycle.get('inputs', [])
217
+ ]
218
+ return [
219
+ {
220
+ 'id': input.get('term-id'),
244
221
  'value': True,
245
222
  'startDate': cycle.get('startDate'),
246
223
  'endDate': cycle.get('endDate')
247
- }, term=input.get('term'))
248
- for input in cycle.get('inputs', [])
249
- if input.get('term', {}).get('termType') in _INPUT_RULES
224
+ }
225
+ for input in inputs_with_ids
226
+ if all([
227
+ input.get('term-id'),
228
+ input.get('input-valid')
229
+ ])
230
+ ]
231
+
232
+
233
+ def _cycle_has_existing_non_cover_land_cover_nodes(cycle: dict) -> bool:
234
+ # if there are any landCover blank nodes in Practices without a Property from the
235
+ # blankNodesGroup = Cover crops lookup, return True, else False
236
+ return any([
237
+ practice for practice in cycle.get("practices", [])
238
+ if practice.get("term", {}).get("termType") == TermTermType.LANDCOVER.value
239
+ and not any(prop for prop in practice.get("properties", [])
240
+ if _is_cover_crop(prop.get("term", {}).get("@id")))
250
241
  ])
251
- return inputs
252
242
 
253
243
 
254
244
  def _run_from_siteType(site: dict, cycle: dict):
255
245
  site_type = site.get('siteType')
256
- site_type_id = get_landCover_term_id_from_site_type(site_type) if site_type not in _SKIP_LAND_COVER_SITE_TYPES \
257
- else None
246
+ site_type_id = get_landCover_term_id_from_site_type(site_type)
258
247
  start_date = cycle.get('startDate') or _gap_filled_start_date(
259
248
  cycle=cycle,
260
249
  end_date=cycle.get('endDate'),
261
250
  land_cover_id=site_type_id
262
251
  ).get('startDate')
252
+ no_land_cover_blank_node = not _cycle_has_existing_non_cover_land_cover_nodes(cycle)
263
253
 
264
- should_run = all([site_type_id, start_date])
254
+ should_run = all([site_type_id, start_date, no_land_cover_blank_node])
265
255
  return [{
266
256
  'id': site_type_id,
257
+ 'termType': TermTermType.LANDCOVER.value,
267
258
  'value': 100,
268
259
  'startDate': start_date,
269
260
  'endDate': cycle.get('endDate')
@@ -296,7 +287,7 @@ def _run_from_practices(cycle: dict):
296
287
 
297
288
 
298
289
  def _run_cycle(site: dict, cycle: dict):
299
- inputs = _run_from_inputs(site, cycle)
290
+ inputs = _run_from_inputs(cycle)
300
291
  site_types = _run_from_siteType(site=site, cycle=cycle)
301
292
  practices = _run_from_practices(cycle)
302
293
  return [
@@ -35,7 +35,6 @@ LAND_USE_TERMS_FOR_TRANSFORMATION = {
35
35
  PERMANENT_PASTURE: ("permanentPasture", "Permanent pasture"),
36
36
  OTHER_LAND: ("otherLand", OTHER_LAND) # Not used yet
37
37
  }
38
- LAND_USE_NAMES_FROM_ID = {v[0]: k for k, v in LAND_USE_TERMS_FOR_TRANSFORMATION.items()} | {"cropland": TOTAL_CROPLAND}
39
38
 
40
39
 
41
40
  def crop_ipcc_land_use_category(
@@ -225,7 +225,7 @@ def _run_practice(animal: dict, values: dict, meanDE: float, meanECHHV: float, R
225
225
  ])
226
226
  has_positive_feed_values = all([NEm_feed >= 0, NEg_feed >= 0])
227
227
 
228
- logRequirements(animal, model=MODEL, term=input_term_id, model_key=MODEL_KEY,
228
+ logRequirements(animal, model=MODEL, term=input_term_id, animalId=animal.get('animalId'), model_key=MODEL_KEY,
229
229
  feed_logs=log_as_table(log_feed),
230
230
  has_positive_feed_values=has_positive_feed_values,
231
231
  animal_logs=logs,
@@ -233,7 +233,7 @@ def _run_practice(animal: dict, values: dict, meanDE: float, meanECHHV: float, R
233
233
  animal_properties=animal_properties)
234
234
 
235
235
  should_run = all([has_positive_feed_values])
236
- logShouldRun(animal, MODEL, input_term_id, should_run, model_key=MODEL_KEY)
236
+ logShouldRun(animal, MODEL, input_term_id, should_run, animalId=animal.get('animalId'), model_key=MODEL_KEY)
237
237
 
238
238
  return _input(input_term_id, value) if should_run else None
239
239
 
@@ -278,32 +278,37 @@ def _should_run(cycle: dict, animals: list, practices: dict):
278
278
  REM = calculate_REM(meanDE)
279
279
  REG = calculate_REG(meanDE)
280
280
 
281
+ has_practice_termType_system = len(systems) > 0
282
+ has_practice_pastureGrass_with_landCover_key = len(practices) > 0
283
+
281
284
  should_run = all([
282
285
  animalFeed_complete,
283
286
  animalPopulation_complete,
284
287
  freshForage_incomplete,
285
288
  no_cycle_inputs_feed,
286
289
  all_animals_have_value,
287
- len(systems) > 0,
288
- len(practices) > 0,
290
+ has_practice_termType_system,
291
+ has_practice_pastureGrass_with_landCover_key,
289
292
  meanDE > 0,
290
293
  meanECHHV > 0
291
294
  ])
292
295
 
293
296
  for term_id in [MODEL_KEY] + [practice_input_id(p) for p in practices]:
294
297
  for animal in animals:
295
- logRequirements(animal, model=MODEL, term=term_id, model_key=MODEL_KEY,
298
+ logRequirements(cycle, model=MODEL, term=term_id, animalId=animal.get('animalId'), model_key=MODEL_KEY,
296
299
  term_type_animalFeed_complete=animalFeed_complete,
297
300
  term_type_animalPopulation_complete=animalPopulation_complete,
298
301
  term_type_freshForage_incomplete=freshForage_incomplete,
299
302
  no_cycle_inputs_feed=no_cycle_inputs_feed,
300
303
  all_animals_have_value=all_animals_have_value,
304
+ has_practice_termType_system=has_practice_termType_system,
305
+ has_practice_pastureGrass_with_landCover_key=has_practice_pastureGrass_with_landCover_key,
301
306
  grass_MeanDE=calculate_meanDE(practices, term=term_id),
302
307
  grass_MeanECHHV=calculate_meanECHHV(practices, term=term_id),
303
308
  grass_REM=REM,
304
309
  grass_REG=REG)
305
310
 
306
- logShouldRun(animal, MODEL, term_id, should_run, model_key=MODEL_KEY)
311
+ logShouldRun(cycle, MODEL, term_id, should_run, animalId=animal.get('animalId'), model_key=MODEL_KEY)
307
312
 
308
313
  return should_run, meanDE, meanECHHV, REM, REG, systems
309
314
 
@@ -62,7 +62,7 @@ def should_run_by_productivity_lookup(
62
62
  practice = value.get('practice')
63
63
  animal_term_id = animal.get('term').get('@id')
64
64
 
65
- logRequirements(cycle, model=MODEL, term=animal_term_id, property=term_id,
65
+ logRequirements(cycle, model=MODEL, term=animal_term_id, animalId=animal.get('animalId'), property=term_id,
66
66
  country_id=country_id,
67
67
  **({
68
68
  lookup_col.replace('-', '_'): lookup_value
@@ -75,7 +75,7 @@ def should_run_by_productivity_lookup(
75
75
  not practice_column or bool(practice),
76
76
  lookup_value is not None
77
77
  ])
78
- logShouldRun(cycle, MODEL, animal_term_id, should_run, property=term_id)
78
+ logShouldRun(cycle, MODEL, animal_term_id, should_run, animalId=animal.get('animalId'), property=term_id)
79
79
 
80
80
  return should_run
81
81
 
@@ -233,14 +233,17 @@ def _should_run(cycle: dict, practices: dict):
233
233
  REM = calculate_REM(meanDE)
234
234
  REG = calculate_REG(meanDE)
235
235
 
236
+ has_practice_termType_system = len(systems) > 0
237
+ has_practice_pastureGrass_with_landCover_key = len(practices) > 0
238
+
236
239
  should_run = all([
237
240
  animalFeed_complete,
238
241
  animalPopulation_complete,
239
242
  freshForage_incomplete,
240
243
  has_cycle_inputs_feed,
241
244
  all_animals_have_value,
242
- len(systems) > 0,
243
- len(practices) > 0,
245
+ has_practice_termType_system,
246
+ has_practice_pastureGrass_with_landCover_key,
244
247
  meanDE > 0,
245
248
  meanECHHV > 0
246
249
  ])
@@ -252,6 +255,8 @@ def _should_run(cycle: dict, practices: dict):
252
255
  term_type_freshForage_incomplete=freshForage_incomplete,
253
256
  has_cycle_inputs_feed=has_cycle_inputs_feed,
254
257
  all_animals_have_value=all_animals_have_value,
258
+ has_practice_termType_system=has_practice_termType_system,
259
+ has_practice_pastureGrass_with_landCover_key=has_practice_pastureGrass_with_landCover_key,
255
260
  grass_MeanDE=calculate_meanDE(practices, term=term_id),
256
261
  grass_MeanECHHV=calculate_meanECHHV(practices, term=term_id),
257
262
  grass_REM=REM,