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

@@ -1,8 +1,7 @@
1
- from typing import List, Optional, Tuple
2
-
3
1
  from hestia_earth.schema import TermTermType
4
2
  from hestia_earth.utils.model import filter_list_term_type
5
3
  from hestia_earth.utils.tools import list_sum
4
+ from typing import List, Optional, Tuple
6
5
 
7
6
  from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table, debugValues
8
7
  from hestia_earth.models.utils.blank_node import get_lookup_value
@@ -89,6 +88,23 @@ def _indicator_factors(impact_assessment: dict, indicator: dict):
89
88
  }
90
89
 
91
90
 
91
+ def _map_input_ids(value: dict) -> set[str]:
92
+ return set(map(lambda i: i.get('@id'), value.get('inputs', [])))
93
+
94
+
95
+ def _count_duplicate_indicators(reference_indicator: dict, indicators: list) -> int:
96
+ """
97
+ Counts the number of `reference_indicator` indicators found in a list of indicators.
98
+ Uses indicator.term.@id and indicator.inputs to determine uniqueness.
99
+ """
100
+ return sum([
101
+ 1
102
+ for i in indicators
103
+ if (i["term"]["@id"] == reference_indicator["term"]["@id"]) and (
104
+ _map_input_ids(i) == _map_input_ids(reference_indicator))
105
+ ])
106
+
107
+
92
108
  def _indicator(value: float) -> dict:
93
109
  indicator = _new_indicator(TERM_ID, MODEL)
94
110
  indicator['value'] = value
@@ -115,7 +131,7 @@ def _should_run(impact_assessment: dict) -> Tuple[bool, list[dict]]:
115
131
  processed_indicators = [{
116
132
  "indicator": indicator['term']['@id'],
117
133
  "valid-indicator": _valid_indicator(indicator),
118
- "one-indicator-for-category": sum(1 for i in indicators if i['term']['@id'] == indicator['term']['@id']) == 1,
134
+ "one-indicator-for-category": _count_duplicate_indicators(indicator, indicators) == 1,
119
135
  "indicator-pef-category": indicator.get('term', {}).get('@id'),
120
136
  } | _indicator_factors(impact_assessment, indicator) for indicator in indicators]
121
137
 
@@ -148,7 +148,7 @@ def cap_values(dictionary: dict, lower_limit: float = 0, upper_limit: float = 1)
148
148
 
149
149
  def site_area_sum_to_100(dict_of_percentages: dict):
150
150
  return False if dict_of_percentages == {} else (
151
- math.isclose(sum(dict_of_percentages.values()), 1.0, rel_tol=0.01) or
151
+ math.isclose(sum(dict_of_percentages.values()), 1.0, rel_tol=0.05) or
152
152
  math.isclose(sum(dict_of_percentages.values()), 0.0, rel_tol=0.01)
153
153
  )
154
154
 
@@ -581,6 +581,14 @@ def _get_year_from_landCover(node: dict):
581
581
  return int(date[:4])
582
582
 
583
583
 
584
+ def _scale_site_area_errors(site_area: dict) -> dict:
585
+ """Redistribute the result of any rounding error in proportion to the other land use types."""
586
+ # Positive errors would not have been capped, so won't be missing.
587
+ negative_errors = [v for v in site_area.values() if v < 0.0]
588
+ return {k: v + negative_errors[0] * v for k, v in site_area.items()} \
589
+ if negative_errors and abs(negative_errors[0]) < 1 and all([v < 1 for v in site_area.values()]) else site_area
590
+
591
+
584
592
  def _should_run_historical_land_use_change(site: dict, nodes: list, land_use_type: str) -> tuple[bool, dict]:
585
593
  # Assume a single management node for single-cropping.
586
594
  return _should_run_historical_land_use_change_single_crop(
@@ -728,7 +736,7 @@ def _should_run_historical_land_use_change_single_crop(
728
736
  if land_type != land_use_type
729
737
  }
730
738
  site_area[land_use_type] = 1 - sum(site_area.values())
731
- capped_site_area = cap_values(dictionary=site_area, lower_limit=0, upper_limit=1)
739
+ capped_site_area = cap_values(dictionary=_scale_site_area_errors(site_area))
732
740
 
733
741
  sum_of_site_areas_is_100 = site_area_sum_to_100(capped_site_area)
734
742
  site_type_allowed = site.get("siteType") in SITE_TYPES
@@ -2,7 +2,8 @@ from hestia_earth.schema import EmissionMethodTier
2
2
  from hestia_earth.utils.tools import list_sum, safe_parse_float, non_empty_list
3
3
  from hestia_earth.utils.model import find_term_match
4
4
 
5
- from hestia_earth.models.log import debugValues, logRequirements, logShouldRun
5
+ from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
6
+ from hestia_earth.models.utils import multiply_values
6
7
  from hestia_earth.models.utils.completeness import _is_term_type_complete
7
8
  from hestia_earth.models.utils.emission import _new_emission
8
9
  from hestia_earth.models.utils.term import get_urea_terms
@@ -36,66 +37,65 @@ def _emission(value: float):
36
37
  return emission
37
38
 
38
39
 
39
- def _urea_input_value(cycle: dict):
40
- def exec(data: dict):
41
- term_id = data.get('id')
42
- values = data.get('values')
43
- coeff = safe_parse_float(get_term_lookup(term_id, LOOKUPS['inorganicFertiliser'][2]), 1)
44
- debugValues(cycle, model=MODEL, term=TERM_ID,
45
- product=term_id,
46
- coefficient=coeff)
47
- return list_sum(values) * coeff
48
- return exec
40
+ def _urea_emission_factor(term_id: str):
41
+ return safe_parse_float(get_term_lookup(term_id, LOOKUPS['inorganicFertiliser'][2]), None)
49
42
 
50
43
 
51
- def _run(cycle: dict, urea_values: list):
52
- value = list_sum(list(map(_urea_input_value(cycle), urea_values)))
44
+ def _run(urea_values: list):
45
+ value = list_sum([v.get('value') * v.get('factor') for v in urea_values if v.get('value')])
53
46
  return [_emission(value)]
54
47
 
55
48
 
56
- def _get_urea_values(cycle: dict, inputs: list, term_id: str):
49
+ def _get_urea_value(cycle: dict, inputs: list, term_id: str):
57
50
  inputs = list(filter(lambda i: i.get('term', {}).get('@id') == term_id, inputs))
58
51
  values = [list_sum(i.get('value'), 0) for i in inputs if len(i.get('value', [])) > 0]
59
- return [0] if len(inputs) == 0 and _is_term_type_complete(cycle, 'fertiliser') else values
52
+ return list_sum(values, default=None)
60
53
 
61
54
 
62
55
  def _should_run(cycle: dict):
56
+ is_fertiliser_complete = _is_term_type_complete(cycle, 'fertiliser')
63
57
  inputs = cycle.get('inputs', [])
64
58
  term_ids = get_urea_terms()
65
59
 
66
60
  country_id = cycle.get('site', {}).get('country', {}).get('@id')
67
61
  urea_share = get_country_breakdown(MODEL, TERM_ID, country_id, LOOKUPS['inorganicFertiliser'][0])
68
62
  uan_share = get_country_breakdown(MODEL, TERM_ID, country_id, LOOKUPS['inorganicFertiliser'][1])
69
- urea_unspecified_as_n = list_sum(find_term_match(inputs, UNSPECIFIED_TERM_ID).get('value', []))
63
+ urea_unspecified_as_n = list_sum(find_term_match(inputs, UNSPECIFIED_TERM_ID).get('value', []), default=None)
70
64
 
71
65
  urea_values = [
72
66
  {
73
- 'id': id,
74
- 'values': _get_urea_values(cycle, inputs, id)
75
- } for id in term_ids
67
+ 'id': term_id,
68
+ 'value': _get_urea_value(cycle, inputs, term_id),
69
+ 'factor': _urea_emission_factor(term_id)
70
+ } for term_id in term_ids
76
71
  ] + non_empty_list([
77
72
  {
78
73
  'id': 'ureaKgN',
79
- 'values': [urea_unspecified_as_n * urea_share]
80
- } if urea_share is not None else None,
74
+ 'value': multiply_values([urea_unspecified_as_n, urea_share]),
75
+ 'factor': _urea_emission_factor('ureaKgN')
76
+ },
81
77
  {
82
78
  'id': 'ureaAmmoniumNitrateKgN',
83
- 'values': [urea_unspecified_as_n * uan_share]
84
- } if urea_share is not None else None
85
- ] if urea_unspecified_as_n > 0 else [])
86
- has_urea_value = any([len(data.get('values')) > 0 for data in urea_values])
79
+ 'value': multiply_values([urea_unspecified_as_n, uan_share]),
80
+ 'factor': _urea_emission_factor('ureaAmmoniumNitrateKgN')
81
+ }
82
+ ] if urea_unspecified_as_n is not None else [])
83
+
84
+ has_urea_value = any([data.get('value') is not None for data in urea_values])
87
85
 
88
86
  logRequirements(cycle, model=MODEL, term=TERM_ID,
87
+ is_term_type_fertiliser_complete=is_fertiliser_complete,
89
88
  has_urea_value=has_urea_value,
89
+ urea_values=log_as_table(urea_values),
90
90
  urea_unspecified_as_n=urea_unspecified_as_n,
91
91
  urea_share=urea_share,
92
92
  uan_share=uan_share)
93
93
 
94
- should_run = all([has_urea_value])
94
+ should_run = has_urea_value or is_fertiliser_complete
95
95
  logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
96
96
  return should_run, urea_values
97
97
 
98
98
 
99
99
  def run(cycle: dict):
100
100
  should_run, urea_values = _should_run(cycle)
101
- return _run(cycle, urea_values) if should_run else []
101
+ return _run(urea_values) if should_run else []