hestia-earth-models 0.69.0__py3-none-any.whl → 0.70.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.
Files changed (75) hide show
  1. hestia_earth/models/cache_sites.py +3 -2
  2. hestia_earth/models/cml2001Baseline/abioticResourceDepletionFossilFuels.py +2 -1
  3. hestia_earth/models/cml2001Baseline/abioticResourceDepletionMineralsAndMetals.py +3 -2
  4. hestia_earth/models/config/Cycle.json +35 -1
  5. hestia_earth/models/{koble2014 → cycle}/aboveGroundCropResidue.py +1 -3
  6. hestia_earth/models/cycle/aboveGroundCropResidueTotal.py +1 -1
  7. hestia_earth/models/cycle/animal/input/hestiaAggregatedData.py +1 -1
  8. hestia_earth/models/cycle/animal/input/properties.py +1 -1
  9. hestia_earth/models/cycle/cycleDuration.py +2 -2
  10. hestia_earth/models/cycle/energyContentLowerHeatingValue.py +1 -1
  11. hestia_earth/models/cycle/input/hestiaAggregatedData.py +12 -14
  12. hestia_earth/models/cycle/input/properties.py +1 -1
  13. hestia_earth/models/cycle/siteDuration.py +3 -3
  14. hestia_earth/models/frischknechtEtAl2000/ionisingRadiationKbqU235Eq.py +1 -1
  15. hestia_earth/models/geospatialDatabase/croppingIntensity.py +4 -4
  16. hestia_earth/models/geospatialDatabase/longFallowRatio.py +4 -4
  17. hestia_earth/models/geospatialDatabase/region.py +3 -2
  18. hestia_earth/models/geospatialDatabase/utils.py +6 -5
  19. hestia_earth/models/haversineFormula/transport/distance.py +5 -4
  20. hestia_earth/models/hestia/landCover.py +1 -5
  21. hestia_earth/models/hestia/landTransformation100YearAverageDuringCycle.py +2 -1
  22. hestia_earth/models/hestia/landTransformation20YearAverageDuringCycle.py +2 -1
  23. hestia_earth/models/hestia/seed_emissions.py +1 -1
  24. hestia_earth/models/impact_assessment/emissions.py +1 -1
  25. hestia_earth/models/impact_assessment/product/economicValueShare.py +1 -1
  26. hestia_earth/models/impact_assessment/product/value.py +1 -1
  27. hestia_earth/models/ipcc2019/animal/fatContent.py +2 -2
  28. hestia_earth/models/ipcc2019/animal/milkYieldPerAnimal.py +2 -2
  29. hestia_earth/models/ipcc2019/animal/trueProteinContent.py +2 -2
  30. hestia_earth/models/ipcc2019/ch4ToAirEntericFermentation.py +7 -2
  31. hestia_earth/models/ipcc2019/co2ToAirAboveGroundBiomassStockChange.py +1 -0
  32. hestia_earth/models/ipcc2019/co2ToAirBelowGroundBiomassStockChange.py +2 -1
  33. hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChange.py +2 -1
  34. hestia_earth/models/ipcc2019/nonCo2EmissionsToAirNaturalVegetationBurning.py +1 -0
  35. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1.py +8 -8
  36. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2.py +40 -12
  37. hestia_earth/models/koble2014/cropResidueManagement.py +1 -1
  38. hestia_earth/models/koble2014/residueBurnt.py +1 -1
  39. hestia_earth/models/koble2014/residueRemoved.py +1 -1
  40. hestia_earth/models/koble2014/utils.py +3 -3
  41. hestia_earth/models/mocking/search-results.json +1179 -1137
  42. hestia_earth/models/pooreNemecek2018/excretaKgN.py +1 -1
  43. hestia_earth/models/pooreNemecek2018/freshwaterWithdrawalsDuringCycle.py +1 -1
  44. hestia_earth/models/pooreNemecek2018/utils.py +6 -3
  45. hestia_earth/models/schmidt2007/ch4ToAirWasteTreatment.py +1 -3
  46. hestia_earth/models/schmidt2007/h2SToAirWasteTreatment.py +1 -3
  47. hestia_earth/models/schmidt2007/n2OToAirWasteTreatmentDirect.py +1 -3
  48. hestia_earth/models/schmidt2007/nh3ToAirWasteTreatment.py +1 -3
  49. hestia_earth/models/site/management.py +5 -3
  50. hestia_earth/models/site/pre_checks/country.py +4 -2
  51. hestia_earth/models/transformation/input/excreta.py +1 -1
  52. hestia_earth/models/utils/aggregated.py +12 -15
  53. hestia_earth/models/utils/blank_node.py +15 -1
  54. hestia_earth/models/utils/product.py +1 -1
  55. hestia_earth/models/utils/source.py +2 -1
  56. hestia_earth/models/utils/term.py +26 -1
  57. hestia_earth/models/version.py +1 -1
  58. {hestia_earth_models-0.69.0.dist-info → hestia_earth_models-0.70.0.dist-info}/METADATA +1 -1
  59. {hestia_earth_models-0.69.0.dist-info → hestia_earth_models-0.70.0.dist-info}/RECORD +75 -75
  60. tests/models/cycle/input/test_hestiaAggregatedData.py +18 -16
  61. tests/models/{koble2014 → cycle}/test_aboveGroundCropResidue.py +3 -3
  62. tests/models/geospatialDatabase/test_region.py +1 -1
  63. tests/models/geospatialDatabase/test_utils.py +1 -1
  64. tests/models/haversineFormula/transport/test_distance.py +2 -2
  65. tests/models/ipcc2019/test_ch4ToAirEntericFermentation.py +11 -0
  66. tests/models/ipcc2019/test_nonCo2EmissionsToAirNaturalVegetationBurning.py +19 -6
  67. tests/models/ipcc2019/test_organicCarbonPerHa.py +4 -2
  68. tests/models/pooreNemecek2018/test_landOccupationDuringCycle.py +3 -0
  69. tests/models/site/pre_checks/test_country.py +4 -3
  70. tests/models/test_ecoinventV3.py +2 -2
  71. tests/models/utils/test_source.py +15 -5
  72. tests/orchestrator/test_models.py +1 -0
  73. {hestia_earth_models-0.69.0.dist-info → hestia_earth_models-0.70.0.dist-info}/LICENSE +0 -0
  74. {hestia_earth_models-0.69.0.dist-info → hestia_earth_models-0.70.0.dist-info}/WHEEL +0 -0
  75. {hestia_earth_models-0.69.0.dist-info → hestia_earth_models-0.70.0.dist-info}/top_level.txt +0 -0
@@ -52,7 +52,7 @@ REQUIREMENTS = {
52
52
  }
53
53
  ],
54
54
  "optional": {
55
- "practices": [{"@type": "Practice", "term.termType": "systemn"}]
55
+ "practices": [{"@type": "Practice", "term.termType": "system"}]
56
56
  }
57
57
  }
58
58
  }
@@ -13,7 +13,7 @@ from . import MODEL
13
13
 
14
14
  REQUIREMENTS = {
15
15
  "ImpactAssessment": {
16
- "product": {"@type": "Product", "term": {"@type": "Term"}},
16
+ "product": {"@type": "Product"},
17
17
  "cycle": {
18
18
  "@type": "Cycle",
19
19
  "products": [{
@@ -80,11 +80,14 @@ def get_excreta_product_with_ratio(cycle: dict, lookup: str, **log_args):
80
80
  ) | {'value': [practice.get('ratio')]}
81
81
  for product, practice in practices_with_products
82
82
  # ignore matching products with an existing value
83
- if not product or not product.get('value', [])
83
+ if all([
84
+ not product or not product.get('value', []),
85
+ product or practice.get('product-id')
86
+ ])
84
87
  ] if practices_with_products else None
85
88
 
86
- return products or [{
89
+ return products or non_empty_list([{
87
90
  '@type': 'Product',
88
91
  'term': {'@type': 'Term', '@id': default_product_id},
89
92
  'value': [100]
90
- }]
93
+ } if default_product_id else None])
@@ -9,9 +9,7 @@ from . import MODEL
9
9
  REQUIREMENTS = {
10
10
  "Cycle": {
11
11
  "or": {
12
- "product": [
13
- {"@type": "Product", "value": "", "term.termType": "waste"}
14
- ],
12
+ "products": [{"@type": "Product", "value": "", "term.termType": "waste"}],
15
13
  "completeness.waste": ""
16
14
  }
17
15
  }
@@ -9,9 +9,7 @@ from . import MODEL
9
9
  REQUIREMENTS = {
10
10
  "Cycle": {
11
11
  "or": {
12
- "product": [
13
- {"@type": "Product", "value": "", "term.termType": "waste"}
14
- ],
12
+ "products": [{"@type": "Product", "value": "", "term.termType": "waste"}],
15
13
  "completeness.waste": ""
16
14
  }
17
15
  }
@@ -9,9 +9,7 @@ from . import MODEL
9
9
  REQUIREMENTS = {
10
10
  "Cycle": {
11
11
  "or": {
12
- "product": [
13
- {"@type": "Product", "value": "", "term.termType": "waste"}
14
- ],
12
+ "products": [{"@type": "Product", "value": "", "term.termType": "waste"}],
15
13
  "completeness.waste": ""
16
14
  }
17
15
  }
@@ -9,9 +9,7 @@ from . import MODEL
9
9
  REQUIREMENTS = {
10
10
  "Cycle": {
11
11
  "or": {
12
- "product": [
13
- {"@type": "Product", "value": "", "term.termType": "waste"}
14
- ],
12
+ "products": [{"@type": "Product", "value": "", "term.termType": "waste"}],
15
13
  "completeness.waste": ""
16
14
  }
17
15
  }
@@ -140,12 +140,14 @@ def management(data: dict):
140
140
 
141
141
 
142
142
  def _get_cycle_duration(cycle: dict, land_cover_id: str):
143
- return cycle.get('cycleDuration') or safe_parse_float(get_table_value(
143
+ cycle_duration = cycle.get('cycleDuration')
144
+ lookup_value = None if cycle_duration else safe_parse_float(get_table_value(
144
145
  download_lookup("crop.csv"),
145
146
  column_name('landCoverTermId'),
146
147
  land_cover_id,
147
148
  column_name('maximumCycleDuration')
148
- ))
149
+ ), default=None)
150
+ return cycle_duration or (lookup_value - 1 if lookup_value else None)
149
151
 
150
152
 
151
153
  def _gap_filled_date_only_str(date_str: str, mode: str = DatestrGapfillMode.END) -> str:
@@ -164,7 +166,7 @@ def _gap_filled_start_date(land_cover_id: str, end_date: str, cycle: dict) -> di
164
166
  cycle_duration = _get_cycle_duration(cycle, land_cover_id)
165
167
  return {
166
168
  "startDate": max(
167
- _gap_filled_date_obj(end_date) - timedelta(days=cycle_duration - 1)
169
+ _gap_filled_date_obj(end_date) - timedelta(days=cycle_duration)
168
170
  if cycle_duration else datetime.fromtimestamp(0),
169
171
  _gap_filled_date_obj(cycle.get("startDate"), mode=DatestrGapfillMode.START)
170
172
  if cycle.get("startDate") else datetime.fromtimestamp(0)
@@ -1,4 +1,6 @@
1
- from hestia_earth.utils.api import download_hestia
1
+ from hestia_earth.schema import TermTermType
2
2
 
3
+ from hestia_earth.models.utils.term import download_term
3
4
 
4
- def run(site: dict): return site | {'country': download_hestia(site.get('country', {}).get('@id'))}
5
+
6
+ def run(site: dict): return site | {'country': download_term(site.get('country', {}).get('@id'), TermTermType.REGION)}
@@ -28,7 +28,7 @@ RETURNS = {
28
28
  "@type": "Input",
29
29
  "term.termType": "excreta",
30
30
  "value": "",
31
- "fromCycle": "true"
31
+ "fromCycle": "True"
32
32
  }]
33
33
  }]
34
34
  }
@@ -10,7 +10,6 @@ from . import current_year
10
10
  from .cycle import is_organic
11
11
 
12
12
  MODEL_KEY = 'impactAssessment'
13
- MATCH_WORLD_QUERY = {'match': {'country.name.keyword': {'query': 'World', 'boost': 1}}}
14
13
 
15
14
 
16
15
  def aggregated_end_date(end_date: str):
@@ -18,26 +17,25 @@ def aggregated_end_date(end_date: str):
18
17
  return min([round(math.floor(year / 10) * 10) + 9, current_year()])
19
18
 
20
19
 
21
- def _match_region_country(region: dict, country: dict):
22
- region_name = region.get('name') if region else None
20
+ def _match_country_query(name: str = 'World', boost: int = 1):
21
+ return {'match': {'country.name.keyword': {'query': name, 'boost': boost}}}
22
+
23
+
24
+ def _match_country(country: dict):
23
25
  country_name = country.get('name') if country else None
24
26
  return {
25
27
  'bool': {
26
28
  # either get with exact country, or default to global
27
29
  'should': non_empty_list([
28
- (
29
- {'match': {'region.name.keyword': {'query': region_name, 'boost': 1000}}} if region_name else
30
- {'match': {'country.name.keyword': {'query': country_name, 'boost': 1000}}} if country_name else
31
- None
32
- ),
33
- MATCH_WORLD_QUERY
30
+ _match_country_query(name=country_name, boost=1000) if country_name else None,
31
+ _match_country_query()
34
32
  ]),
35
33
  'minimum_should_match': 1
36
34
  }
37
35
  }
38
36
 
39
37
 
40
- def find_closest_impact(cycle: dict, end_date: str, term: dict, region: dict, country: dict, must_queries=[]):
38
+ def find_closest_impact(cycle: dict, end_date: str, term: dict, country: dict, must_queries=[]):
41
39
  query = {
42
40
  'bool': {
43
41
  'must': non_empty_list([
@@ -54,7 +52,7 @@ def find_closest_impact(cycle: dict, end_date: str, term: dict, region: dict, co
54
52
  'minimum_should_match': 1
55
53
  }
56
54
  } if term else None,
57
- _match_region_country(region, country)
55
+ _match_country(country)
58
56
  ]) + must_queries,
59
57
  'should': [
60
58
  # if the Cycle is organic, we can try to match organic aggregate first
@@ -76,14 +74,13 @@ def _link_input_to_impact(model: str, cycle: dict, date: int):
76
74
  def run(input: dict):
77
75
  term = input.get('term', {})
78
76
  term_id = term.get('@id')
79
- region = input.get('region')
80
77
  country = input.get('country')
81
- impact = find_closest_impact(cycle, date, term, region, country)
78
+ impact = find_closest_impact(cycle, date, term, country)
82
79
 
83
- search_by_region_id = (region or country or {}).get('@id') or 'region-world'
80
+ search_by_country_id = (country or {}).get('@id') or 'region-world'
84
81
  debugValues(cycle, model=model, term=term_id, key=MODEL_KEY,
85
82
  search_by_input_term_id=term_id,
86
- search_by_region_id=search_by_region_id,
83
+ search_by_country_id=search_by_country_id,
87
84
  search_by_end_date=str(date),
88
85
  impact_assessment_id_found=(impact or {}).get('@id'))
89
86
 
@@ -398,9 +398,17 @@ def convert_to_nitrogen(node: dict, model: str, blank_nodes: list, **log_args):
398
398
  return value or get_node_property_value(model, input, 'crudeProteinContent', default=0, **log_args) / 6.25
399
399
 
400
400
  values = [(i, prop_value(i)) for i in blank_nodes]
401
- missing_nitrogen_property = [i.get('term', {}).get('@id') for i, p_value in values if not p_value]
401
+ missing_nitrogen_property = [i.get('term', {}).get('@id') for i, value in values if not value]
402
402
 
403
403
  debugValues(node, model=model,
404
+ convertion_details=log_as_table([
405
+ {
406
+ 'id': i.get('term', {}).get('@id'),
407
+ 'value': list_sum(i.get('value', [])),
408
+ 'conversion-factor': value
409
+ }
410
+ for i, value in values
411
+ ]),
404
412
  missing_nitrogen_property=';'.join(set(missing_nitrogen_property)),
405
413
  **log_args)
406
414
 
@@ -1010,6 +1018,7 @@ def group_nodes_by_year(
1010
1018
  nodes: list[dict],
1011
1019
  default_node_duration: int = 1,
1012
1020
  sort_result: bool = True,
1021
+ include_spillovers: bool = False,
1013
1022
  inner_key: Union[Any, None] = None,
1014
1023
  mode: GroupNodesByYearMode = GroupNodesByYearMode.START_AND_END_DATE
1015
1024
  ) -> dict[int, list[dict]]:
@@ -1026,6 +1035,10 @@ def group_nodes_by_year(
1026
1035
  Default duration of a node years if start date is not available, by default 1.
1027
1036
  sort_result : bool, optional
1028
1037
  Flag to sort the result by year, by default True.
1038
+ include_spillovers : bool, optional
1039
+ If grouping by start and end date, flag to determine whether nodes should be included in year groups that they
1040
+ spill-over into. If `False` year groups will not include nodes that overlap with them by less than 30% of a
1041
+ year, unless it is the only year group it overlaps with. By default False.
1029
1042
  inner_key: Any | None
1030
1043
  An optional inner dictionary key for the outputted annualised groups (can be used to merge annualised
1031
1044
  dictionaries together), default value: `None`.
@@ -1066,6 +1079,7 @@ def group_nodes_by_year(
1066
1079
 
1067
1080
  should_run = (
1068
1081
  mode == GroupNodesByYearMode.DATES
1082
+ or include_spillovers
1069
1083
  or _validate_time_fraction_dict(
1070
1084
  time_fraction_dict,
1071
1085
  is_final_year
@@ -153,7 +153,7 @@ def get_animal_produced_nitrogen(model: str, products: list) -> float:
153
153
  def product_value(product: dict):
154
154
  value = convert_product_to_unit(product, Units.KG_LIVEWEIGHT)
155
155
  property = prop_value(product)
156
- return value * property / 100 if all([value, property]) else 0
156
+ return value * property if all([value, property]) else 0
157
157
 
158
158
  return list_sum(list(map(product_value, products)))
159
159
 
@@ -52,7 +52,8 @@ def _extract(content: str):
52
52
 
53
53
  def _list_sources():
54
54
  dir = pathlib.Path(ROOT_DIR)
55
- files = list(dir.rglob('**/*.py'))
55
+ # ignore current file
56
+ files = list(filter(lambda f: not str(f).endswith('utils/source.py'), list(dir.rglob('**/*.py'))))
56
57
  return list(set(flatten([_extract(open(f, 'r').read().replace('\n', '')) for f in files])))
57
58
 
58
59
 
@@ -715,7 +715,7 @@ def get_land_cover_terms():
715
715
 
716
716
  Returns
717
717
  -------
718
- List of landCover terms with associated siteTypes.
718
+ List of landCover terms IDs.
719
719
  """
720
720
  terms = search({
721
721
  "bool": {
@@ -726,3 +726,28 @@ def get_land_cover_terms():
726
726
  },
727
727
  }, limit=LIMIT, fields=['@id'])
728
728
  return list(map(lambda n: n["@id"], terms))
729
+
730
+
731
+ def get_ionophore_terms():
732
+ """
733
+ Find all `Ionophore` terms from the Glossary: https://hestia.earth/glossary?query=ionophore
734
+
735
+ Returns
736
+ -------
737
+ List of ionophore term IDs.
738
+ """
739
+ terms = search({
740
+ "bool": {
741
+ "must": [
742
+ {"match": {"@type": SchemaType.TERM.value}},
743
+ {"match_phrase_prefix": {"name": "ionophore"}}
744
+ ],
745
+ "should": [
746
+ {"match": {"termType": TermTermType.FEEDFOODADDITIVE.value}},
747
+ {"match": {"termType": TermTermType.VETERINARYDRUG.value}},
748
+
749
+ ],
750
+ "minimum_should_match": 1
751
+ },
752
+ }, limit=LIMIT, fields=['@id'])
753
+ return list(map(lambda n: n["@id"], terms))
@@ -1 +1 @@
1
- VERSION = '0.69.0'
1
+ VERSION = '0.70.0'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: hestia-earth-models
3
- Version: 0.69.0
3
+ Version: 0.70.0
4
4
  Summary: HESTIA's set of modules for filling gaps in the activity data using external datasets (e.g. populating soil properties with a geospatial dataset using provided coordinates) and internal lookups (e.g. populating machinery use from fuel use). Includes rules for when gaps should be filled versus not (e.g. never gap fill yield, gap fill crop residue if yield provided etc.).
5
5
  Home-page: https://gitlab.com/hestia-earth/hestia-engine-models
6
6
  Author: HESTIA Team