hestia-earth-models 0.72.0__py3-none-any.whl → 0.72.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.
Files changed (21) hide show
  1. hestia_earth/models/cml2001Baseline/resourceUseEnergyDepletionInputsProduction.py +2 -1
  2. hestia_earth/models/cml2001Baseline/resourceUseMineralsAndMetalsInputsProduction.py +2 -1
  3. hestia_earth/models/config/ImpactAssessment.json +1964 -1918
  4. hestia_earth/models/ecoalimV9/cycle.py +1 -0
  5. hestia_earth/models/ecoalimV9/impact_assessment.py +1 -0
  6. hestia_earth/models/ecoinventV3/__init__.py +1 -0
  7. hestia_earth/models/ecoinventV3AndEmberClimate/__init__.py +1 -0
  8. hestia_earth/models/environmentalFootprintV3_1/environmentalFootprintSingleOverallScore.py +19 -3
  9. hestia_earth/models/hestia/landCover.py +10 -2
  10. hestia_earth/models/ipcc2019/co2ToAirUreaHydrolysis.py +27 -27
  11. hestia_earth/models/linkedImpactAssessment/emissions.py +1 -0
  12. hestia_earth/models/linkedImpactAssessment/utils.py +16 -16
  13. hestia_earth/models/mocking/search-results.json +660 -660
  14. hestia_earth/models/version.py +1 -1
  15. {hestia_earth_models-0.72.0.dist-info → hestia_earth_models-0.72.2.dist-info}/METADATA +1 -1
  16. {hestia_earth_models-0.72.0.dist-info → hestia_earth_models-0.72.2.dist-info}/RECORD +21 -21
  17. tests/models/environmentalFootprintV3_1/test_environmentalFootprintSingleOverallScore.py +22 -2
  18. tests/models/ipcc2019/test_co2ToAirUreaHydrolysis.py +10 -34
  19. {hestia_earth_models-0.72.0.dist-info → hestia_earth_models-0.72.2.dist-info}/LICENSE +0 -0
  20. {hestia_earth_models-0.72.0.dist-info → hestia_earth_models-0.72.2.dist-info}/WHEEL +0 -0
  21. {hestia_earth_models-0.72.0.dist-info → hestia_earth_models-0.72.2.dist-info}/top_level.txt +0 -0
@@ -81,6 +81,7 @@ def _add_emission(cycle: dict, input: dict):
81
81
  emissions = ecoalim_values(ecoalim_key, 'emission')
82
82
  for emission_term_id, value in emissions:
83
83
  # log run on each emission so we know it did run
84
+ logShouldRun(cycle, MODEL, input_term_id, True, methodTier=TIER, emission_id=emission_term_id)
84
85
  debugValues(cycle, model=MODEL, term=emission_term_id, model_key=MODEL_KEY,
85
86
  value=value,
86
87
  coefficient=coefficient,
@@ -81,6 +81,7 @@ def _add_indicator(cycle: dict, input: dict):
81
81
  indicators = ecoalim_values(ecoalim_key, 'resourceUse')
82
82
  for indicator_term_id, value in indicators:
83
83
  # log run on each indicator so we know it did run
84
+ logShouldRun(cycle, MODEL, input_term_id, True, methodTier=TIER, emission_id=indicator_term_id)
84
85
  debugValues(cycle, model=MODEL, term=indicator_term_id, model_key=MODEL_KEY,
85
86
  value=value,
86
87
  coefficient=coefficient,
@@ -89,6 +89,7 @@ def _add_emission(cycle: dict, input: dict):
89
89
  emissions = ecoinventV3_emissions(ecoinventName)
90
90
  for emission_term_id, value in emissions:
91
91
  # log run on each emission so we know it did run
92
+ logShouldRun(cycle, MODEL, input_term_id, True, methodTier=TIER, emission_id=emission_term_id)
92
93
  debugValues(cycle, model=MODEL, term=emission_term_id,
93
94
  value=value,
94
95
  coefficient=coefficient,
@@ -89,6 +89,7 @@ def _add_emission(cycle: dict, input: dict):
89
89
  emissions = ecoinventV3_emissions(ecoinventName)
90
90
  for emission_term_id, value in emissions:
91
91
  # log run on each emission so we know it did run
92
+ logShouldRun(cycle, MODEL, input_term_id, True, methodTier=TIER, emission_id=emission_term_id)
92
93
  debugValues(cycle, model=MODEL, term=emission_term_id,
93
94
  value=value,
94
95
  coefficient=coefficient,
@@ -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 []
@@ -77,6 +77,7 @@ def _run_emission(cycle: dict, emission_term_id: str, data: dict):
77
77
 
78
78
  # log run on each emission so we know it did run
79
79
  details = values.get('details', {})
80
+ logShouldRun(cycle, MODEL, input_term_id, True, methodTier=TIER, emission_id=emission_term_id)
80
81
  debugValues(cycle, model=model, term=emission_term_id,
81
82
  value=value,
82
83
  coefficient=1,
@@ -8,14 +8,14 @@ from hestia_earth.models.utils.input import load_impacts
8
8
  from . import MODEL
9
9
 
10
10
 
11
- def _indicator(term_id: str, value: float, input: dict):
12
- indicator = _new_indicator(term_id, MODEL)
11
+ def _indicator(model: str, term_id: str, value: float, input: dict):
12
+ indicator = _new_indicator(term_id, model)
13
13
  indicator['value'] = value
14
14
  indicator['inputs'] = [input]
15
15
  return indicator
16
16
 
17
17
 
18
- def _run_indicators(impact_assessment: dict, product: dict, term_id: str):
18
+ def _run_indicators(impact_assessment: dict, product: dict, term_id: str, model: str):
19
19
  def run(values: list):
20
20
  input = values[0].get('input').get('term', {})
21
21
  indicator = values[0].get('indicator')
@@ -24,22 +24,22 @@ def _run_indicators(impact_assessment: dict, product: dict, term_id: str):
24
24
  for value in values
25
25
  ])
26
26
  value = convert_value_from_cycle(
27
- impact_assessment, product, sum_values(values_from_cycle), model=MODEL, term_id=term_id
27
+ impact_assessment, product, sum_values(values_from_cycle), model=model, term_id=term_id
28
28
  )
29
29
 
30
30
  # show values per input in the logs
31
- debugValues(impact_assessment, model=MODEL, term=term_id,
31
+ debugValues(impact_assessment, model=model, term=term_id,
32
32
  value=value,
33
33
  coefficient=1,
34
34
  input=input.get('@id'))
35
35
 
36
36
  return (
37
- _indicator(term_id, value, input) | _include(indicator, ['landCover', 'previousLandCover'])
37
+ _indicator(model, term_id, value, input) | _include(indicator, ['landCover', 'previousLandCover'])
38
38
  ) if value is not None else None
39
39
  return run
40
40
 
41
41
 
42
- def _run_inputs_production(impact_assessment: dict, product: dict, term_id: str):
42
+ def _run_inputs_production(impact_assessment: dict, product: dict, term_id: str, model: str):
43
43
  cycle = impact_assessment.get('cycle', {})
44
44
 
45
45
  # group all indicators per `landCover` and `previousLandCover`
@@ -71,7 +71,7 @@ def _run_inputs_production(impact_assessment: dict, product: dict, term_id: str)
71
71
  ])
72
72
  has_indicators = bool(valid_indicators)
73
73
 
74
- logRequirements(impact_assessment, model=MODEL, term=term_id,
74
+ logRequirements(impact_assessment, model=model, term=term_id,
75
75
  indicators=log_as_table([
76
76
  {
77
77
  'indicator-id': value.get('indicator').get('term', {}).get('@id'),
@@ -83,30 +83,30 @@ def _run_inputs_production(impact_assessment: dict, product: dict, term_id: str)
83
83
  ]))
84
84
 
85
85
  should_run = all([has_indicators])
86
- logShouldRun(impact_assessment, MODEL, term_id, should_run)
86
+ logShouldRun(impact_assessment, model, term_id, should_run)
87
87
 
88
88
  return non_empty_list(flatten(
89
- map(_run_indicators(impact_assessment, product, term_id), grouped_indicators.values())
89
+ map(_run_indicators(impact_assessment, product, term_id, model), grouped_indicators.values())
90
90
  ))
91
91
 
92
92
 
93
- def _should_run_inputs_production(impact_assessment: dict, term_id: str):
93
+ def _should_run_inputs_production(impact_assessment: dict, term_id: str, model: str):
94
94
  product = get_product(impact_assessment) or {}
95
95
  product_id = product.get('term', {}).get('@id')
96
96
 
97
97
  product_value = list_sum(product.get('value', []), default=None)
98
98
  economic_value = product.get('economicValueShare')
99
99
 
100
- logRequirements(impact_assessment, model=MODEL, term=term_id,
100
+ logRequirements(impact_assessment, model=model, term=term_id,
101
101
  product_id=product_id,
102
102
  product_value=product_value,
103
103
  product_economicValueShare=economic_value)
104
104
 
105
105
  should_run = all([product, product_value, economic_value])
106
- logShouldRun(impact_assessment, MODEL, term_id, should_run)
106
+ logShouldRun(impact_assessment, model, term_id, should_run)
107
107
  return should_run, product
108
108
 
109
109
 
110
- def run_inputs_production(impact_assessment: dict, term_id: str):
111
- should_run, product = _should_run_inputs_production(impact_assessment, term_id)
112
- return _run_inputs_production(impact_assessment, product, term_id) if should_run else []
110
+ def run_inputs_production(impact_assessment: dict, term_id: str, model: str = MODEL):
111
+ should_run, product = _should_run_inputs_production(impact_assessment, term_id, model)
112
+ return _run_inputs_production(impact_assessment, product, term_id, model) if should_run else []