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

Files changed (25) hide show
  1. hestia_earth/models/config/Cycle.json +15 -0
  2. hestia_earth/models/cycle/product/economicValueShare.py +4 -4
  3. hestia_earth/models/geospatialDatabase/histosol.py +31 -11
  4. hestia_earth/models/hestia/aboveGroundCropResidueTotal.py +2 -2
  5. hestia_earth/models/hestia/soilClassification.py +31 -13
  6. hestia_earth/models/ipcc2019/animal/pastureGrass.py +3 -1
  7. hestia_earth/models/ipcc2019/burning_utils.py +406 -4
  8. hestia_earth/models/ipcc2019/ch4ToAirExcreta.py +26 -8
  9. hestia_earth/models/ipcc2019/ch4ToAirOrganicSoilCultivation.py +8 -11
  10. hestia_earth/models/ipcc2019/co2ToAirOrganicSoilCultivation.py +9 -12
  11. hestia_earth/models/ipcc2019/emissionsToAirOrganicSoilBurning.py +516 -0
  12. hestia_earth/models/ipcc2019/n2OToAirOrganicSoilCultivationDirect.py +10 -13
  13. hestia_earth/models/ipcc2019/nonCo2EmissionsToAirNaturalVegetationBurning.py +56 -433
  14. hestia_earth/models/ipcc2019/organicSoilCultivation_utils.py +2 -2
  15. hestia_earth/models/ipcc2019/pastureGrass.py +3 -1
  16. hestia_earth/models/mocking/search-results.json +1 -1
  17. hestia_earth/models/utils/blank_node.py +68 -0
  18. hestia_earth/models/utils/impact_assessment.py +3 -0
  19. hestia_earth/models/version.py +1 -1
  20. hestia_earth/orchestrator/strategies/merge/merge_node.py +32 -2
  21. {hestia_earth_models-0.75.1.dist-info → hestia_earth_models-0.75.2.dist-info}/METADATA +1 -1
  22. {hestia_earth_models-0.75.1.dist-info → hestia_earth_models-0.75.2.dist-info}/RECORD +25 -24
  23. {hestia_earth_models-0.75.1.dist-info → hestia_earth_models-0.75.2.dist-info}/WHEEL +0 -0
  24. {hestia_earth_models-0.75.1.dist-info → hestia_earth_models-0.75.2.dist-info}/licenses/LICENSE +0 -0
  25. {hestia_earth_models-0.75.1.dist-info → hestia_earth_models-0.75.2.dist-info}/top_level.txt +0 -0
@@ -1294,6 +1294,21 @@
1294
1294
  },
1295
1295
  "stage": 2
1296
1296
  },
1297
+ {
1298
+ "key": "emissions",
1299
+ "model": "ipcc2019",
1300
+ "value": "emissionsToAirOrganicSoilBurning",
1301
+ "runStrategy": "always",
1302
+ "runArgs": {
1303
+ "runNonMeasured": true,
1304
+ "runNonAddedTerm": true
1305
+ },
1306
+ "mergeStrategy": "list",
1307
+ "mergeArgs": {
1308
+ "replaceThreshold": ["value", 0.01]
1309
+ },
1310
+ "stage": 2
1311
+ },
1297
1312
  {
1298
1313
  "key": "emissions",
1299
1314
  "model": "schmidt2007",
@@ -1,5 +1,5 @@
1
1
  from hestia_earth.utils.model import find_term_match
2
- from hestia_earth.utils.tools import list_sum
2
+ from hestia_earth.utils.tools import list_sum, to_precision
3
3
 
4
4
  from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
5
5
  from hestia_earth.models.utils.term import get_lookup_value
@@ -43,7 +43,7 @@ MIN_COMPLETE_VALUE = 80 # when the products are complete lower the min threshol
43
43
 
44
44
 
45
45
  def _product(product: dict, value: float):
46
- return product | {MODEL_KEY: value}
46
+ return product | {MODEL_KEY: to_precision(value, 2 if value < 1 else 3 if value < 10 else 4)}
47
47
 
48
48
 
49
49
  def _is_complete(cycle: dict): return cycle.get('completeness', {}).get('product', False)
@@ -57,11 +57,11 @@ def _total_evs(products: list): return sum([p.get(MODEL_KEY, 0) for p in product
57
57
 
58
58
  def _product_with_value(product: dict):
59
59
  value = product.get(MODEL_KEY, lookup_share(MODEL_KEY, product))
60
- return product | {MODEL_KEY: value} if value is not None else product
60
+ return _product(product, value) if value is not None else product
61
61
 
62
62
 
63
63
  def _rescale_value(products: list, total: float):
64
- return list(map(lambda p: {**p, MODEL_KEY: p.get(MODEL_KEY) * 100 / total}, products))
64
+ return list(map(lambda p: _product(p, p.get(MODEL_KEY) * 100 / total), products))
65
65
 
66
66
 
67
67
  def _should_run_by_default(cycle: dict, products: list):
@@ -1,7 +1,10 @@
1
- from hestia_earth.schema import MeasurementMethodClassification
1
+ from hestia_earth.schema import MeasurementMethodClassification, TermTermType
2
+ from hestia_earth.utils.model import filter_list_term_type
3
+ from hestia_earth.utils.blank_node import get_node_value
4
+ from hestia_earth.utils.tools import pick
2
5
 
3
- from hestia_earth.models.log import logRequirements, logShouldRun
4
- from hestia_earth.models.utils.measurement import _new_measurement, total_other_soilType_value
6
+ from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
7
+ from hestia_earth.models.utils.measurement import _new_measurement
5
8
  from hestia_earth.models.utils.source import get_source
6
9
  from .utils import download, has_geospatial_data, should_download
7
10
  from . import MODEL
@@ -16,10 +19,10 @@ REQUIREMENTS = {
16
19
  "none": {
17
20
  "measurements": [{
18
21
  "@type": "Measurement",
19
- "value": "100",
22
+ "value": "> 0",
20
23
  "depthUpper": "0",
21
24
  "depthLower": "30",
22
- "term.termType": "soilType"
25
+ "term.termType": ["soilType", "usdaSoilType"]
23
26
  }]
24
27
  }
25
28
  }
@@ -39,12 +42,14 @@ EE_PARAMS = {
39
42
  'reducer': 'mean'
40
43
  }
41
44
  BIBLIO_TITLE = 'Harmonized World Soil Database Version 1.2. Food and Agriculture Organization of the United Nations (FAO).' # noqa: E501
45
+ _DEPTH_UPPER = 0
46
+ _DEPTH_LOWER = 30
42
47
 
43
48
 
44
49
  def _measurement(site: dict, value: float):
45
50
  measurement = _new_measurement(term=TERM_ID, value=value)
46
- measurement['depthUpper'] = 0
47
- measurement['depthLower'] = 30
51
+ measurement['depthUpper'] = _DEPTH_UPPER
52
+ measurement['depthLower'] = _DEPTH_LOWER
48
53
  measurement['methodClassification'] = MeasurementMethodClassification.GEOSPATIAL_DATASET.value
49
54
  return measurement | get_source(site, BIBLIO_TITLE)
50
55
 
@@ -58,15 +63,30 @@ def _should_run(site: dict):
58
63
  contains_geospatial_data = has_geospatial_data(site)
59
64
  below_max_area_size = should_download(TERM_ID, site)
60
65
 
61
- total_measurements_value = total_other_soilType_value(site.get('measurements', []), TERM_ID)
66
+ measurements = filter_list_term_type(site.get('measurements', []), [
67
+ TermTermType.SOILTYPE,
68
+ TermTermType.USDASOILTYPE
69
+ ])
70
+ measurements = [m for m in measurements if all([
71
+ m.get('depthUpper', -1) == _DEPTH_UPPER,
72
+ m.get('depthLower', 0) == _DEPTH_LOWER,
73
+ get_node_value(m) > 0
74
+ ])]
75
+ has_soil_type_measurements = len(measurements) > 0
62
76
 
63
77
  logRequirements(site, model=MODEL, term=TERM_ID,
64
78
  contains_geospatial_data=contains_geospatial_data,
65
79
  below_max_area_size=below_max_area_size,
66
- total_soilType_measurements_value=total_measurements_value,
67
- total_soilType_measurements_value_is_0=total_measurements_value == 0)
80
+ has_soil_type_measurements=has_soil_type_measurements,
81
+ soil_type_measurements=log_as_table([
82
+ {
83
+ 'id': m.get('term', {}).get('@id'),
84
+ 'termType': m.get('term', {}).get('termType'),
85
+ 'value': get_node_value(m)
86
+ } | pick(m, ['depthUpper', 'depthLower']) for m in measurements
87
+ ]))
68
88
 
69
- should_run = all([contains_geospatial_data, below_max_area_size, total_measurements_value == 0])
89
+ should_run = all([contains_geospatial_data, below_max_area_size, not has_soil_type_measurements])
70
90
  logShouldRun(site, MODEL, TERM_ID, should_run)
71
91
  return should_run
72
92
 
@@ -9,8 +9,8 @@ from . import MODEL
9
9
 
10
10
  REQUIREMENTS = {
11
11
  "Cycle": {
12
- "practices": [{"@type": "Practice", "value": "", "term.termType": "cropResidue"}],
13
- "products": [{"@type": "Product", "value": "", "term.termType": "cropResidueManagement"}]
12
+ "practices": [{"@type": "Practice", "value": "", "term.termType": "cropResidueManagement"}],
13
+ "products": [{"@type": "Product", "value": "", "term.termType": "cropResidue"}]
14
14
  }
15
15
  }
16
16
  RETURNS = {
@@ -19,16 +19,18 @@ from . import MODEL
19
19
  REQUIREMENTS = {
20
20
  "Site": {
21
21
  "optional": {
22
- "measurements": [{
23
- "@type": "Measurement",
24
- "value": "",
25
- "depthUpper": "",
26
- "depthLower": "",
27
- "term.termType": "soilType",
28
- "optional": {
29
- "dates": ""
22
+ "measurements": [
23
+ {
24
+ "@type": "Measurement",
25
+ "value": "",
26
+ "depthUpper": "",
27
+ "depthLower": "",
28
+ "term.termType": ["soilType", "usdaSoilType"],
29
+ "optional": {
30
+ "dates": ""
31
+ }
30
32
  }
31
- }]
33
+ ]
32
34
  }
33
35
  }
34
36
  }
@@ -41,7 +43,8 @@ RETURNS = {
41
43
  }]
42
44
  }
43
45
  LOOKUPS = {
44
- "soilType": "IPCC_SOIL_CATEGORY"
46
+ "soilType": "IPCC_SOIL_CATEGORY",
47
+ "usdaSoilType": "IPCC_SOIL_CATEGORY"
45
48
  }
46
49
  TERM_ID = 'organicSoils,mineralSoils'
47
50
 
@@ -50,6 +53,10 @@ ORGANIC_SOILS_TERM_ID = MEASUREMENT_TERM_IDS[0]
50
53
  MINERAL_SOILS_TERM_ID = MEASUREMENT_TERM_IDS[1]
51
54
  METHOD = MeasurementMethodClassification.MODELLED_USING_OTHER_MEASUREMENTS.value
52
55
 
56
+ _INPUT_TERM_TYPES = (
57
+ TermTermType.SOILTYPE,
58
+ TermTermType.USDASOILTYPE
59
+ )
53
60
  TARGET_LOOKUP_VALUE = IPCC_SOIL_CATEGORY_TO_SOIL_TYPE_LOOKUP_VALUE[IpccSoilCategory.ORGANIC_SOILS]
54
61
 
55
62
  IS_100_THRESHOLD = 99.5
@@ -65,6 +72,7 @@ def _measurement(term_id: str, **kwargs):
65
72
 
66
73
  class _SoilTypeDatum(NamedTuple):
67
74
  term_id: str
75
+ term_type: str
68
76
  depth_upper: float
69
77
  depth_lower: float
70
78
  dates: list[str]
@@ -105,14 +113,16 @@ def _extract_soil_type_data(node: dict) -> _SoilTypeDatum:
105
113
  depth_upper = node.get("depthUpper")
106
114
  depth_lower = node.get("depthLower")
107
115
  depth_interval = (depth_upper, depth_lower)
116
+ term_type = node.get("term", {}).get("termType")
108
117
 
109
118
  return _SoilTypeDatum(
110
119
  term_id=node.get("term", {}).get("@id"),
120
+ term_type=term_type,
111
121
  depth_upper=depth_upper,
112
122
  depth_lower=depth_lower,
113
123
  dates=node.get("dates", []),
114
124
  value=get_node_value(node),
115
- is_organic=node_lookup_match(node, LOOKUPS["soilType"], TARGET_LOOKUP_VALUE),
125
+ is_organic=node_lookup_match(node, LOOKUPS[term_type], TARGET_LOOKUP_VALUE),
116
126
  is_complete_depth=all(depth is not None for depth in depth_interval),
117
127
  is_standard_depth=depth_interval in STANDARD_DEPTHS,
118
128
  )
@@ -126,7 +136,7 @@ def _classify_soil_type_data(soil_type_data: list[_SoilTypeDatum]):
126
136
 
127
137
  def classify(inventory: _SoilTypeInventory, datum: _SoilTypeDatum) -> _SoilTypeInventory:
128
138
  """
129
- Sum the values of organic and mineral `soilType` Measurements by depth interval and date.
139
+ Sum the values of organic and mineral `soilType`/`usdaSoilType` Measurements by depth interval and date.
130
140
  """
131
141
  keys = _soil_type_data_to_inventory_keys(datum)
132
142
 
@@ -252,7 +262,7 @@ def _filter_data_by_depth_availability(data: list[_SoilTypeDatum]):
252
262
 
253
263
  def _should_run(site: dict):
254
264
  soil_type_nodes = split_nodes_by_dates(
255
- filter_list_term_type(site.get("measurements", []), TermTermType.SOILTYPE)
265
+ get_soil_type_nodes(site)
256
266
  )
257
267
 
258
268
  filtered_by, soil_type_data = _filter_data_by_depth_availability(
@@ -280,6 +290,14 @@ def _should_run(site: dict):
280
290
  return should_run, inventory
281
291
 
282
292
 
293
+ def get_soil_type_nodes(site: dict) -> list[dict]:
294
+ measurements = site.get("measurements", [])
295
+ return next(
296
+ (nodes for term_type in _INPUT_TERM_TYPES if (nodes := filter_list_term_type(measurements, term_type))),
297
+ []
298
+ )
299
+
300
+
283
301
  _INVENTORY_KEY_TO_FIELD_KEY = {
284
302
  "depth_upper": "depthUpper",
285
303
  "depth_lower": "depthLower",
@@ -200,6 +200,7 @@ def _run_practice(
200
200
  GE = (
201
201
  calculate_GE([values], REM, REG, NEwool, NEm_feed, NEg_feed) / (meanDE/100)
202
202
  ) if meanDE else 0
203
+ has_positive_GE_value = GE >= 0
203
204
 
204
205
  value = (GE / meanECHHV) * (list_sum(practice.get('value', [0])) / 100)
205
206
 
@@ -229,11 +230,12 @@ def _run_practice(
229
230
  logRequirements(cycle, model=MODEL, term=input_term_id, animalId=animal.get('animalId'), model_key=MODEL_KEY,
230
231
  feed_logs=log_as_table(log_feed),
231
232
  has_positive_feed_values=has_positive_feed_values,
233
+ has_positive_GE_value=has_positive_GE_value,
232
234
  animal_logs=logs,
233
235
  animal_lookups=animal_lookups,
234
236
  animal_properties=animal_properties)
235
237
 
236
- should_run = all([has_positive_feed_values])
238
+ should_run = all([has_positive_feed_values, has_positive_GE_value])
237
239
  logShouldRun(cycle, MODEL, input_term_id, should_run, animalId=animal.get('animalId'), model_key=MODEL_KEY)
238
240
 
239
241
  return _input(input_term_id, value) if should_run else None