hestia-earth-models 0.74.7__py3-none-any.whl → 0.74.9__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.
Files changed (50) hide show
  1. hestia_earth/models/cache_sites.py +1 -1
  2. hestia_earth/models/data/ecoinventV3/__init__.py +3 -3
  3. hestia_earth/models/data/hestiaAggregatedData/__init__.py +6 -4
  4. hestia_earth/models/faostat2018/liveweightPerHead.py +1 -1
  5. hestia_earth/models/faostat2018/product/price.py +1 -1
  6. hestia_earth/models/geospatialDatabase/ecoClimateZone.py +1 -1
  7. hestia_earth/models/geospatialDatabase/region.py +1 -1
  8. hestia_earth/models/geospatialDatabase/utils.py +1 -1
  9. hestia_earth/models/globalCropWaterModel2008/rootingDepth.py +2 -1
  10. hestia_earth/models/haversineFormula/transport/distance.py +1 -1
  11. hestia_earth/models/hestia/aboveGroundCropResidue.py +1 -3
  12. hestia_earth/models/hestia/cropResidueManagement.py +1 -0
  13. hestia_earth/models/hestia/excretaKgMass.py +1 -1
  14. hestia_earth/models/hestia/landCover.py +13 -6
  15. hestia_earth/models/hestia/landOccupationDuringCycle.py +1 -1
  16. hestia_earth/models/hestia/management.py +25 -11
  17. hestia_earth/models/hestia/pastureGrass.py +1 -1
  18. hestia_earth/models/impact_assessment/post_checks/__init__.py +3 -2
  19. hestia_earth/models/impact_assessment/post_checks/remove_no_value.py +13 -0
  20. hestia_earth/models/ipcc2019/biocharOrganicCarbonPerHa.py +2 -1
  21. hestia_earth/models/ipcc2019/ch4ToAirExcreta.py +4 -1
  22. hestia_earth/models/ipcc2019/organicCarbonPerHa.py +5 -1
  23. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1.py +88 -101
  24. hestia_earth/models/ipcc2019/organicCarbonPerHa_utils.py +21 -0
  25. hestia_earth/models/mocking/search-results.json +1 -1
  26. hestia_earth/models/site/pre_checks/country.py +1 -2
  27. hestia_earth/models/utils/__init__.py +7 -5
  28. hestia_earth/models/utils/blank_node.py +13 -4
  29. hestia_earth/models/utils/completeness.py +1 -2
  30. hestia_earth/models/utils/emission.py +1 -1
  31. hestia_earth/models/utils/indicator.py +1 -1
  32. hestia_earth/models/utils/input.py +1 -1
  33. hestia_earth/models/utils/management.py +1 -1
  34. hestia_earth/models/utils/measurement.py +2 -1
  35. hestia_earth/models/utils/method.py +1 -2
  36. hestia_earth/models/utils/practice.py +1 -1
  37. hestia_earth/models/utils/product.py +2 -1
  38. hestia_earth/models/utils/property.py +2 -1
  39. hestia_earth/models/utils/term.py +1 -27
  40. hestia_earth/models/version.py +1 -1
  41. {hestia_earth_models-0.74.7.dist-info → hestia_earth_models-0.74.9.dist-info}/METADATA +2 -2
  42. {hestia_earth_models-0.74.7.dist-info → hestia_earth_models-0.74.9.dist-info}/RECORD +50 -47
  43. tests/models/hestia/test_aboveGroundCropResidue.py +13 -35
  44. tests/models/hestia/test_landOccupationDuringCycle.py +9 -2
  45. tests/models/impact_assessment/post_checks/test_remove_cache_fields.py +6 -0
  46. tests/models/impact_assessment/post_checks/test_remove_no_value.py +17 -0
  47. tests/models/ipcc2019/test_organicCarbonPerHa_tier_1.py +1 -1
  48. {hestia_earth_models-0.74.7.dist-info → hestia_earth_models-0.74.9.dist-info}/LICENSE +0 -0
  49. {hestia_earth_models-0.74.7.dist-info → hestia_earth_models-0.74.9.dist-info}/WHEEL +0 -0
  50. {hestia_earth_models-0.74.7.dist-info → hestia_earth_models-0.74.9.dist-info}/top_level.txt +0 -0
@@ -3,11 +3,11 @@ from enum import Enum
3
3
  from pydash.objects import merge
4
4
  from hestia_earth.schema import TermTermType
5
5
  from hestia_earth.utils.tools import flatten, non_empty_list
6
+ from hestia_earth.utils.term import download_term
6
7
 
7
8
  from .log import logger
8
9
  from .utils import CACHE_KEY, cached_value
9
10
  from .utils.site import CACHE_YEARS_KEY
10
- from .utils.term import download_term
11
11
  from .site.pre_checks.cache_geospatialDatabase import (
12
12
  list_vectors, list_rasters, cache_site_results, _should_run
13
13
  )
@@ -4,15 +4,15 @@ from hestia_earth.models.log import logger
4
4
 
5
5
  _CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
6
6
  _ENV_FOLDER = 'ECOINVENT_V3_FOLDER'
7
- _ECOINVENT_FOLDER = os.getenv(_ENV_FOLDER) or _CURRENT_DIR
7
+ _DATA_FOLDER = os.getenv(_ENV_FOLDER) or _CURRENT_DIR
8
8
  _ECOINVENT_VERSION = os.getenv('ECOINVENT_VERSION', '3.9')
9
9
 
10
10
 
11
11
  def get_filepath(term_type: str):
12
12
  filename = f"ecoinventV{_ECOINVENT_VERSION.replace('.', '_')}-{term_type}.csv"
13
- filepath = os.path.join(_ECOINVENT_FOLDER, filename)
13
+ filepath = os.path.join(_DATA_FOLDER, filename)
14
14
  if not os.path.exists(filepath):
15
- logger.warning('Ecoinvent file not found. Please make sure to set env variable "%s".', _ENV_FOLDER)
15
+ logger.warning('%s file not found. Please make sure to set env variable "%s".', filename, _ENV_FOLDER)
16
16
  return None
17
17
 
18
18
  return filepath
@@ -5,8 +5,9 @@ from hestia_earth.utils.storage._s3_client import _load_from_bucket
5
5
  from hestia_earth.utils.api import _safe_get_request
6
6
 
7
7
  _CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
8
+ _ENV_FOLDER = 'HESTIA_AGGREGATED_DATA_FOLDER'
9
+ _DATA_FOLDER = os.getenv(_ENV_FOLDER) or _CURRENT_DIR
8
10
  _FILENAME = 'hestiaAggregatedData.json'
9
- FILEPATH = os.path.join(_CURRENT_DIR, _FILENAME)
10
11
  _CACHED_DATA = {}
11
12
  DEFAULT_COUNTRY_ID = 'region-world'
12
13
 
@@ -23,16 +24,17 @@ def _download_data():
23
24
 
24
25
  def _load_data():
25
26
  data = None
27
+ filepath = os.path.join(_DATA_FOLDER, _FILENAME)
26
28
 
27
- if os.path.exists(FILEPATH):
28
- with open(FILEPATH, 'r') as f:
29
+ if os.path.exists(filepath):
30
+ with open(filepath, 'r') as f:
29
31
  data = json.load(f)
30
32
 
31
33
  is_data_valid = data and data['date'] == _today()
32
34
 
33
35
  if not is_data_valid:
34
36
  data = _download_data()
35
- with open(FILEPATH, 'w') as f:
37
+ with open(filepath, 'w') as f:
36
38
  f.write(json.dumps(data))
37
39
 
38
40
  return data
@@ -2,10 +2,10 @@ from hestia_earth.schema import TermTermType
2
2
  from hestia_earth.utils.lookup import extract_grouped_data_closest_date
3
3
  from hestia_earth.utils.model import filter_list_term_type
4
4
  from hestia_earth.utils.tools import non_empty_list, safe_parse_date, safe_parse_float
5
+ from hestia_earth.utils.term import download_term
5
6
 
6
7
  from hestia_earth.models.log import logRequirements, logShouldRun
7
8
  from hestia_earth.models.utils.constant import Units
8
- from hestia_earth.models.utils.term import download_term
9
9
  from hestia_earth.models.utils.property import _new_property, node_has_no_property
10
10
  from hestia_earth.models.utils.product import convert_product_to_unit
11
11
  from hestia_earth.models.utils.animalProduct import FAO_LOOKUP_COLUMN, get_animalProduct_lookup_value
@@ -1,10 +1,10 @@
1
1
  from hestia_earth.schema import TermTermType
2
2
  from hestia_earth.utils.lookup import extract_grouped_data
3
3
  from hestia_earth.utils.tools import non_empty_list, safe_parse_float, safe_parse_date
4
+ from hestia_earth.utils.term import download_term
4
5
 
5
6
  from hestia_earth.models.log import debugValues, logRequirements, logShouldRun
6
7
  from hestia_earth.models.utils.constant import Units
7
- from hestia_earth.models.utils.term import download_term
8
8
  from hestia_earth.models.utils.currency import DEFAULT_CURRENCY
9
9
  from hestia_earth.models.utils.crop import FAOSTAT_PRODUCTION_LOOKUP_COLUMN, get_crop_grouping_faostat_production
10
10
  from hestia_earth.models.utils.animalProduct import FAO_LOOKUP_COLUMN, get_animalProduct_grouping_fao
@@ -77,4 +77,4 @@ def _should_run(site: dict):
77
77
  return should_run
78
78
 
79
79
 
80
- def run(site: dict): return _run(site) if _should_run(site) else _run_default(site)
80
+ def run(site: dict): return (_run(site) if _should_run(site) else []) or _run_default(site)
@@ -1,9 +1,9 @@
1
1
  from hestia_earth.schema import TermTermType
2
2
  from hestia_earth.utils.model import linked_node
3
+ from hestia_earth.utils.term import download_term
3
4
 
4
5
  from hestia_earth.models.log import debugValues, logRequirements, logShouldRun
5
6
  from .utils import download, has_coordinates
6
- from hestia_earth.models.utils.term import download_term
7
7
  from . import MODEL
8
8
 
9
9
  REQUIREMENTS = {
@@ -4,10 +4,10 @@ from area import area
4
4
  from functools import reduce, lru_cache
5
5
  from hestia_earth.schema import TermTermType
6
6
  from hestia_earth.utils.tools import non_empty_list
7
+ from hestia_earth.utils.term import download_term
7
8
 
8
9
  from hestia_earth.models.log import debugValues, logErrorRun, logRequirements
9
10
  from hestia_earth.models.utils.site import cached_value, region_factor, region_level_1_id
10
- from hestia_earth.models.utils.term import download_term
11
11
  from . import MODEL
12
12
 
13
13
  MAX_AREA_SIZE = int(os.getenv('MAX_AREA_SIZE', '5000'))
@@ -1,10 +1,11 @@
1
1
  from hestia_earth.schema import CycleFunctionalUnit, TermTermType
2
2
  from hestia_earth.utils.model import find_term_match, filter_list_term_type
3
3
  from hestia_earth.utils.tools import list_sum, non_empty_list, safe_parse_float
4
+ from hestia_earth.utils.term import download_term
4
5
 
5
6
  from hestia_earth.models.log import logRequirements, logShouldRun
6
7
  from hestia_earth.models.utils.property import _new_property, node_has_no_property
7
- from hestia_earth.models.utils.term import get_irrigation_terms, download_term
8
+ from hestia_earth.models.utils.term import get_irrigation_terms
8
9
  from hestia_earth.models.utils.crop import get_crop_lookup_value
9
10
  from hestia_earth.models.utils.completeness import _is_term_type_complete
10
11
  from . import MODEL
@@ -1,10 +1,10 @@
1
1
  from haversine import haversine
2
2
  from hestia_earth.schema import TermTermType
3
3
  from hestia_earth.utils.tools import non_empty_list
4
+ from hestia_earth.utils.term import download_term
4
5
 
5
6
  from hestia_earth.models.log import logRequirements, logShouldRun, debugValues
6
7
  from hestia_earth.models.utils.method import include_methodModel
7
- from hestia_earth.models.utils.term import download_term
8
8
  from .. import MODEL
9
9
 
10
10
  REQUIREMENTS = {
@@ -112,12 +112,10 @@ def _run(cycle: dict, total_values: list):
112
112
 
113
113
  if value == 0:
114
114
  values.extend([_product(term_id, value)])
115
- elif remaining_value > 0 and value is not None and value >= 0:
115
+ elif remaining_value >= 0 and value is not None and value >= 0:
116
116
  value = value if value < remaining_value else remaining_value
117
117
  values.extend([_product(term_id, value)])
118
118
  remaining_value = remaining_value - value
119
- if remaining_value == 0:
120
- break
121
119
 
122
120
  return values + [
123
121
  # whatever remains is "left on field"
@@ -21,6 +21,7 @@ RETURNS = {
21
21
  }]
22
22
  }
23
23
  MODEL_KEY = 'cropResidueManagement'
24
+ TERM_ID = 'residueBurnt,residueIncorporated,residueLeftOnField,residueRemoved,residueIncorporatedLessThan30DaysBeforeCultivation,residueIncorporatedMoreThan30DaysBeforeCultivation' # noqa: E501
24
25
  PRACTICE_IDS = [
25
26
  residueBurnt.TERM_ID,
26
27
  residueIncorporated.TERM_ID,
@@ -1,10 +1,10 @@
1
1
  from hestia_earth.schema import NodeType, TermTermType
2
2
  from hestia_earth.utils.model import filter_list_term_type, find_term_match
3
3
  from hestia_earth.utils.tools import non_empty_list, list_sum
4
+ from hestia_earth.utils.term import download_term
4
5
 
5
6
  from hestia_earth.models.log import debugValues, logRequirements, logShouldRun
6
7
  from hestia_earth.models.utils import get_kg_term_id, get_kg_N_term_id, get_kg_VS_term_id, _filter_list_term_unit
7
- from hestia_earth.models.utils.term import download_term
8
8
  from hestia_earth.models.utils.constant import Units
9
9
  from hestia_earth.models.utils.product import _new_product, convert_product_to_unit
10
10
  from . import MODEL
@@ -10,7 +10,7 @@ from hestia_earth.utils.model import filter_list_term_type
10
10
  from hestia_earth.utils.tools import safe_parse_float, to_precision
11
11
 
12
12
  from hestia_earth.models.log import logRequirements, log_as_table, logShouldRun
13
- from hestia_earth.models.utils import _omit
13
+ from hestia_earth.models.utils import _omit, clamp
14
14
  from hestia_earth.models.utils.constant import DAYS_IN_YEAR
15
15
  from hestia_earth.models.utils.management import _new_management
16
16
  from hestia_earth.models.utils.term import get_lookup_value
@@ -896,11 +896,15 @@ def _should_run_historical_land_use_change_single_crop(
896
896
  land_use_type=land_use_type,
897
897
  permanent_crops_net_expansion=permanent_crops_net_expansion
898
898
  )
899
+ capped_expansion_factor = clamp(
900
+ value=expansion_factor * e9_net_expansion * net_expansion_cultivated_vs_harvested,
901
+ min_value=0,
902
+ max_value=1
903
+ )
899
904
 
900
905
  site_area = {
901
- land_type: (
902
- shares_of_expansion[land_type] * expansion_factor * e9_net_expansion * net_expansion_cultivated_vs_harvested
903
- ) for land_type in LAND_USE_TERMS_FOR_TRANSFORMATION.keys()
906
+ land_type: (shares_of_expansion[land_type] * capped_expansion_factor)
907
+ for land_type in LAND_USE_TERMS_FOR_TRANSFORMATION.keys()
904
908
  if land_type != land_use_type
905
909
  }
906
910
  site_area[land_use_type] = 1 - sum(site_area.values())
@@ -941,6 +945,9 @@ def _get_land_use_term_from_node(node: dict):
941
945
  return _get_lookup_with_cache(lookup_term=node.get("term", {}), column=LOOKUPS.get("landCover")[1])
942
946
 
943
947
 
948
+ def _date_strip(date: str): return date[:10] if date else None
949
+
950
+
944
951
  def _collect_land_use_types(nodes: list) -> list:
945
952
  """Look up the land use type from management nodes."""
946
953
  return [
@@ -949,8 +956,8 @@ def _collect_land_use_types(nodes: list) -> list:
949
956
  "term": node.get("term", {}),
950
957
  "id": node.get("term", {}).get("@id"),
951
958
  "land-use-type": _get_land_use_term_from_node(node),
952
- "endDate": _gapfill_datestr(datestr=node.get("endDate"), mode=DatestrGapfillMode.END)[:10],
953
- "startDate": _gapfill_datestr(datestr=node.get("startDate"), mode=DatestrGapfillMode.START)[:10]
959
+ "endDate": _date_strip(_gapfill_datestr(datestr=node.get("endDate"), mode=DatestrGapfillMode.END)),
960
+ "startDate": _date_strip(_gapfill_datestr(datestr=node.get("startDate"), mode=DatestrGapfillMode.START))
954
961
  } for node in nodes
955
962
  ]
956
963
 
@@ -212,7 +212,7 @@ def _format_inventory(inventory: list[SiteData], default: str = "None") -> str:
212
212
 
213
213
  def _should_run(impact_assessment: dict):
214
214
 
215
- cycle = impact_assessment.get("cycle")
215
+ cycle = impact_assessment.get("cycle", {})
216
216
  functional_unit = cycle.get("functionalUnit")
217
217
 
218
218
  product = get_product(impact_assessment)
@@ -286,7 +286,7 @@ def _dates_overlap(target_practice: dict, node: dict, cycle: dict, site_type_id:
286
286
  ])
287
287
 
288
288
 
289
- def _should_run_practice(management_nodes: list, cycle: dict, site_type_id: str):
289
+ def _should_run_practice(site: dict, management_nodes: list, cycle: dict, site_type_id: str):
290
290
  """
291
291
  Include only landUseManagement practices where GAP_FILL_TO_MANAGEMENT = True
292
292
  """
@@ -297,21 +297,34 @@ def _should_run_practice(management_nodes: list, cycle: dict, site_type_id: str)
297
297
  for node in filter_list_term_type(management_nodes, TermTermType.LANDCOVER)
298
298
  ]
299
299
 
300
- def run(practice: dict):
300
+ def exec(practice: dict):
301
301
  term = practice.get('term', {})
302
- target_group = get_lookup_value(practice.get("term", {}), 'sumIs100Group', skip_debug=True, model=MODEL)
303
- has_other_land_cover_in_same_group = next((
302
+ term_id = term['@id']
303
+ should_gap_fill = term.get('termType') != TermTermType.LANDUSEMANAGEMENT.value or _should_gap_fill(term)
304
+ target_group = get_lookup_value(term, 'sumIs100Group', skip_debug=True, model=MODEL)
305
+ no_other_land_cover_in_same_group = next((
304
306
  True for node in landCover_management_nodes
305
307
  if (
306
308
  node['sumIs100Group'] == target_group and
307
309
  _dates_overlap(target_practice=practice, node=node, cycle=cycle, site_type_id=site_type_id)
308
310
  )
309
- ), None) is not None
310
- return (
311
- (term.get('termType') != TermTermType.LANDUSEMANAGEMENT.value or _should_gap_fill(term)) and
312
- not has_other_land_cover_in_same_group
313
- )
314
- return run
311
+ ), None) is None
312
+ # cannot gap-fill landCover without a `startDate`
313
+ has_required_startDate = term.get('termType') != TermTermType.LANDCOVER.value or practice.get('startDate')
314
+
315
+ should_run = all([
316
+ should_gap_fill,
317
+ has_required_startDate,
318
+ no_other_land_cover_in_same_group
319
+ ])
320
+ if not should_run:
321
+ logRequirements(site, model=MODEL, term=term_id, model_key=MODEL_KEY,
322
+ should_gap_fill=should_gap_fill,
323
+ has_required_startDate=has_required_startDate,
324
+ no_other_land_cover_in_same_group=no_other_land_cover_in_same_group)
325
+ logShouldRun(site, MODEL, term_id, False, model_key=MODEL_KEY)
326
+ return should_run
327
+ return exec
315
328
 
316
329
 
317
330
  def _run_from_practices(site: dict, cycle: dict, site_type_id: str):
@@ -330,7 +343,8 @@ def _run_from_practices(site: dict, cycle: dict, site_type_id: str):
330
343
  ]
331
344
  management_nodes = site.get("management", [])
332
345
  return list(map(_map_to_value, filter(
333
- _should_run_practice(management_nodes=management_nodes, cycle=cycle, site_type_id=site_type_id), practices
346
+ _should_run_practice(site, management_nodes, cycle, site_type_id),
347
+ practices
334
348
  )))
335
349
 
336
350
 
@@ -1,9 +1,9 @@
1
1
  from hestia_earth.schema import SiteSiteType, TermTermType
2
2
  from hestia_earth.utils.model import linked_node
3
+ from hestia_earth.utils.term import download_term
3
4
 
4
5
  from hestia_earth.models.log import logRequirements, logShouldRun
5
6
  from hestia_earth.models.utils.practice import _new_practice
6
- from hestia_earth.models.utils.term import download_term
7
7
  from . import MODEL
8
8
 
9
9
  REQUIREMENTS = {
@@ -2,7 +2,7 @@ from os.path import dirname, abspath
2
2
  import sys
3
3
 
4
4
  from hestia_earth.models.utils import _run_in_serie
5
- from . import cycle, site, remove_cache_fields
5
+ from . import cycle, site, remove_cache_fields, remove_no_value
6
6
 
7
7
  CURRENT_DIR = dirname(abspath(__file__)) + '/'
8
8
  sys.path.append(CURRENT_DIR)
@@ -10,7 +10,8 @@ sys.path.append(CURRENT_DIR)
10
10
  MODELS = [
11
11
  cycle.run,
12
12
  site.run,
13
- remove_cache_fields.run
13
+ remove_cache_fields.run,
14
+ remove_no_value.run
14
15
  ]
15
16
 
16
17
 
@@ -0,0 +1,13 @@
1
+ _KEYS = ['impacts', 'endpoints']
2
+
3
+
4
+ def _has_value(blank_node: dict):
5
+ return blank_node.get('value') is not None
6
+
7
+
8
+ def _filter_has_value(impact: dict, key: str):
9
+ return list(filter(_has_value, impact[key]))
10
+
11
+
12
+ def run(impact: dict):
13
+ return impact | {key: _filter_has_value(impact, key) for key in _KEYS if impact.get(key)}
@@ -110,7 +110,7 @@ def _should_run(site: dict) -> tuple[bool, dict]:
110
110
  """
111
111
  cycles = related_cycles(site)
112
112
  site_type = site.get("siteType")
113
- ipcc_soil_category = _assign_ipcc_soil_category(site.get("measurements", []))
113
+ ipcc_soil_category, soil_logs = _assign_ipcc_soil_category(site.get("measurements", []))
114
114
 
115
115
  has_cycles = len(cycles) > 0
116
116
  has_valid_site_type = site_type in _VALID_SITE_TYPES
@@ -140,6 +140,7 @@ def _should_run(site: dict) -> tuple[bool, dict]:
140
140
  should_compile_inventory=should_compile_inventory,
141
141
  seed=seed,
142
142
  inventory=_format_inventory(inventory),
143
+ **soil_logs,
143
144
  **_format_logs(logs)
144
145
  )
145
146
 
@@ -120,7 +120,10 @@ def _get_ch4_conv_factor(cycle: dict):
120
120
  ecoClimateZone=ecoClimateZone,
121
121
  practice_id=practice_id)
122
122
 
123
- return _get_excretaManagement_MCF_from_lookup(practice_id, ecoClimateZone, duration_key) if practice_id else 0
123
+ return _get_excretaManagement_MCF_from_lookup(practice_id, ecoClimateZone, duration_key) if all([
124
+ practice_id,
125
+ ecoClimateZone is not None
126
+ ]) else 0
124
127
 
125
128
 
126
129
  def _should_run(cycle: dict):
@@ -23,7 +23,11 @@ REQUIREMENTS = {
23
23
  ],
24
24
  "optional": {
25
25
  "measurements": [
26
- {"@type": "Measurement", "value": "", "term.termType": ["soilType", "usdaSoilType"]}
26
+ {
27
+ "@doc": "The model cannot run on sites with more than 30 percent organic soils (`histols`, `histosol` and their subclasses).", # noqa: E501
28
+ "@type": "Measurement", "value": "",
29
+ "term.termType": ["soilType", "usdaSoilType"]
30
+ }
27
31
  ],
28
32
  "management": [
29
33
  {