hestia-earth-models 0.62.1__py3-none-any.whl → 0.62.3__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 (56) hide show
  1. hestia_earth/models/agribalyse2016/fuelElectricity.py +1 -1
  2. hestia_earth/models/cycle/animal/input/hestiaAggregatedData.py +1 -1
  3. hestia_earth/models/cycle/coldCarcassWeightPerHead.py +4 -2
  4. hestia_earth/models/cycle/coldDressedCarcassWeightPerHead.py +2 -2
  5. hestia_earth/models/cycle/concentrateFeed.py +3 -3
  6. hestia_earth/models/cycle/cycleDuration.py +7 -2
  7. hestia_earth/models/cycle/feedConversionRatio/feedConversionRatioNitrogen.py +2 -1
  8. hestia_earth/models/cycle/input/hestiaAggregatedData.py +1 -1
  9. hestia_earth/models/cycle/product/price.py +5 -1
  10. hestia_earth/models/cycle/product/revenue.py +6 -7
  11. hestia_earth/models/cycle/readyToCookWeightPerHead.py +2 -2
  12. hestia_earth/models/ecoinventV3/__init__.py +25 -52
  13. hestia_earth/models/ecoinventV3/utils.py +40 -0
  14. hestia_earth/models/ecoinventV3AndEmberClimate/__init__.py +92 -91
  15. hestia_earth/models/ecoinventV3AndEmberClimate/utils.py +15 -105
  16. hestia_earth/models/faostat2018/product/price.py +1 -2
  17. hestia_earth/models/geospatialDatabase/croppingIntensity.py +2 -1
  18. hestia_earth/models/geospatialDatabase/utils.py +1 -1
  19. hestia_earth/models/ipcc2019/aboveGroundCropResidueTotal.py +15 -10
  20. hestia_earth/models/ipcc2019/animal/pastureGrass.py +50 -40
  21. hestia_earth/models/ipcc2019/belowGroundCropResidue.py +16 -11
  22. hestia_earth/models/ipcc2019/carbonContent.py +1 -1
  23. hestia_earth/models/ipcc2019/croppingDuration.py +2 -2
  24. hestia_earth/models/ipcc2019/ligninContent.py +1 -1
  25. hestia_earth/models/ipcc2019/nitrogenContent.py +1 -1
  26. hestia_earth/models/ipcc2019/organicCarbonPerHa.py +3 -3
  27. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1_utils.py +5 -5
  28. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2_utils.py +217 -48
  29. hestia_earth/models/ipcc2019/organicCarbonPerHa_utils.py +2 -6
  30. hestia_earth/models/ipcc2019/pastureGrass.py +43 -41
  31. hestia_earth/models/ipcc2019/pastureGrass_utils.py +63 -109
  32. hestia_earth/models/koble2014/cropResidueManagement.py +1 -1
  33. hestia_earth/models/linkedImpactAssessment/emissions.py +15 -14
  34. hestia_earth/models/mocking/search-results.json +249 -257
  35. hestia_earth/models/pooreNemecek2018/longFallowPeriod.py +1 -1
  36. hestia_earth/models/preload_requests.py +1 -1
  37. hestia_earth/models/requirements.py +6 -6
  38. hestia_earth/models/site/organicCarbonPerHa.py +1 -1
  39. hestia_earth/models/utils/__init__.py +1 -1
  40. hestia_earth/models/utils/blank_node.py +52 -9
  41. hestia_earth/models/utils/cycle.py +12 -12
  42. hestia_earth/models/utils/measurement.py +3 -3
  43. hestia_earth/models/utils/property.py +6 -6
  44. hestia_earth/models/utils/term.py +2 -1
  45. hestia_earth/models/version.py +1 -1
  46. {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.3.dist-info}/METADATA +12 -12
  47. {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.3.dist-info}/RECORD +56 -55
  48. {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.3.dist-info}/WHEEL +1 -1
  49. tests/models/cycle/product/test_revenue.py +0 -3
  50. tests/models/cycle/test_cycleDuration.py +1 -1
  51. tests/models/ipcc2019/test_organicCarbonPerHa.py +9 -20
  52. tests/models/ipcc2019/test_organicCarbonPerHa_tier_2_utils.py +0 -8
  53. tests/models/test_ecoinventV3.py +12 -0
  54. tests/models/test_ecoinventV3AndEmberClimate.py +5 -72
  55. {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.3.dist-info}/LICENSE +0 -0
  56. {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.3.dist-info}/top_level.txt +0 -0
@@ -93,7 +93,7 @@ def _should_run_operation(cycle: dict):
93
93
 
94
94
  logRequirements(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
95
95
  has_value=has_value,
96
- has_lookup_value=has_lookup_value)
96
+ has_fuelUse_lookup_value=has_lookup_value)
97
97
 
98
98
  should_run = all([has_value, has_lookup_value])
99
99
  logShouldRun(cycle, MODEL, term_id, should_run, model_key=MODEL_KEY)
@@ -1,5 +1,5 @@
1
1
  """
2
- Animal Input Hestia Aggregated Data
2
+ Animal Input HESTIA Aggregated Data
3
3
 
4
4
  This model adds `impactAssessment` to Animal inputs based on data which has been aggregated into country level averages.
5
5
  Note: to get more accurate impacts, we recommend setting the
@@ -42,8 +42,10 @@ def _property(value: float):
42
42
 
43
43
 
44
44
  def _run(product: dict):
45
- liveweightPerHead = get_node_property_value(MODEL, product, 'liveweightPerHead')
46
- processingConversion = get_node_property_value(MODEL, product, 'processingConversionLiveweightToColdCarcassWeight')
45
+ liveweightPerHead = get_node_property_value(MODEL, product, 'liveweightPerHead', term=TERM_ID)
46
+ processingConversion = get_node_property_value(
47
+ MODEL, product, 'processingConversionLiveweightToColdCarcassWeight', term=TERM_ID
48
+ )
47
49
  value = liveweightPerHead * processingConversion if all([liveweightPerHead, processingConversion]) else None
48
50
  prop = _property(value) if value else None
49
51
  return {**product, 'properties': product.get('properties', []) + [prop]} if prop else None
@@ -46,9 +46,9 @@ def _property(value: float):
46
46
 
47
47
 
48
48
  def _run(product: dict):
49
- liveweightPerHead = get_node_property_value(MODEL, product, 'liveweightPerHead')
49
+ liveweightPerHead = get_node_property_value(MODEL, product, 'liveweightPerHead', term=TERM_ID)
50
50
  processingConversion = get_node_property_value(
51
- MODEL, product, 'processingConversionLiveweightToColdDressedCarcassWeight'
51
+ MODEL, product, 'processingConversionLiveweightToColdDressedCarcassWeight', term=TERM_ID
52
52
  )
53
53
  value = liveweightPerHead * processingConversion if all([liveweightPerHead, processingConversion]) else None
54
54
  prop = _property(value) if value else None
@@ -79,7 +79,7 @@ def _calculate_value(cycle: dict, product: dict, inputs: list, property_id: str,
79
79
  def _calculate_default_value(cycle: dict, product: dict, inputs: list, property_id: str):
80
80
  values = [(
81
81
  i.get('term', {}).get('@id'),
82
- get_node_property_value(MODEL, i, property_id),
82
+ get_node_property_value(MODEL, i, property_id, term=TERM_ID),
83
83
  list_sum(i.get('value', []))
84
84
  ) for i in inputs]
85
85
  return _calculate_value(cycle, product, inputs, property_id, values)
@@ -88,8 +88,8 @@ def _calculate_default_value(cycle: dict, product: dict, inputs: list, property_
88
88
  def _calculate_N_value(cycle: dict, product: dict, inputs: list, property_id: str):
89
89
  values = [(
90
90
  i.get('term', {}).get('@id'),
91
- get_node_property_value(MODEL, i, property_id) or
92
- get_node_property_value(MODEL, i, 'crudeProteinContent', default=0) * 0.16,
91
+ get_node_property_value(MODEL, i, property_id, term=TERM_ID) or
92
+ get_node_property_value(MODEL, i, 'crudeProteinContent', default=0, term=TERM_ID) * 0.16,
93
93
  list_sum(i.get('value', []))
94
94
  ) for i in inputs]
95
95
  return _calculate_value(cycle, product, inputs, property_id, values)
@@ -10,7 +10,7 @@ from hestia_earth.schema import TermTermType
10
10
  from hestia_earth.utils.model import find_term_match, find_primary_product
11
11
  from hestia_earth.utils.date import diff_in_days
12
12
 
13
- from hestia_earth.models.log import logRequirements, logShouldRun
13
+ from hestia_earth.models.log import logRequirements, logShouldRun, debugValues
14
14
  from hestia_earth.models.utils.crop import get_crop_grouping_fao
15
15
  from . import MODEL
16
16
 
@@ -66,7 +66,12 @@ def _run_by_crop(cycle: dict):
66
66
  grouping = get_crop_grouping_fao(MODEL, MODEL_KEY, product.get('term', {}))
67
67
  is_permanent_crop = grouping == 'Permanent crops'
68
68
  croppingIntensity = find_term_match(cycle.get('practices', []), 'croppingIntensity').get('value', [1])[0]
69
- return DEFAULT_DURATION * (1 if is_permanent_crop else croppingIntensity)
69
+ ratio = 1 if is_permanent_crop else croppingIntensity
70
+ debugValues(cycle, model=MODEL, key=MODEL_KEY, by='product',
71
+ crop_grouping=grouping,
72
+ croppingIntensity=croppingIntensity,
73
+ ratio=ratio)
74
+ return round(DEFAULT_DURATION * ratio, 3)
70
75
 
71
76
 
72
77
  def _should_run_by_crop(cycle: dict):
@@ -72,5 +72,6 @@ TERM_ID = 'feedConversionRatioNitrogen'
72
72
  def run(cycle: dict, feed: float):
73
73
  inputs = get_feed_inputs(cycle)
74
74
  return list_sum(non_empty_list([
75
- get_node_property_value_converted(MODEL, input, 'crudeProteinContent', default=0) / 6.25 for input in inputs
75
+ get_node_property_value_converted(MODEL, input, 'crudeProteinContent', default=0, term=TERM_ID) / 6.25
76
+ for input in inputs
76
77
  ]))
@@ -1,5 +1,5 @@
1
1
  """
2
- Input Hestia Aggregated Data
2
+ Input HESTIA Aggregated Data
3
3
 
4
4
  This model adds `impactAssessment` to inputs based on data which has been aggregated into country level averages.
5
5
  Note: to get more accurate impacts, we recommend setting the
@@ -42,9 +42,13 @@ def _should_run_product_by_share_0(cycle: dict, product: dict):
42
42
  return should_run
43
43
 
44
44
 
45
+ def _should_run_product(product: dict): return product.get(MODEL_KEY) is None
46
+
47
+
45
48
  def run(cycle: dict):
49
+ products = list(filter(_should_run_product, cycle.get('products', [])))
46
50
  return non_empty_list([
47
51
  (
48
52
  _product(p, 0) if _should_run_product_by_share_0(cycle, p) else None
49
- ) for p in cycle.get('products', [])
53
+ ) for p in products
50
54
  ])
@@ -53,23 +53,22 @@ def _should_run(cycle: dict):
53
53
  has_yield_and_price = has_yield and has_price
54
54
  is_yield_0 = list_sum(product.get('value', []), -1) == 0
55
55
  is_price_0 = product.get('price', -1) == 0
56
- not_already_set = MODEL_KEY not in product.keys()
57
56
 
58
57
  logRequirements(cycle, model=MODEL, term=term_id, key=MODEL_KEY,
59
- not_already_set=not_already_set,
60
58
  has_yield_and_price=has_yield_and_price,
61
59
  is_yield_0=is_yield_0,
62
60
  is_price_0=is_price_0)
63
61
 
64
- should_run = all([
65
- not_already_set,
66
- any([has_yield_and_price, is_yield_0, is_price_0])
67
- ])
62
+ should_run = any([has_yield_and_price, is_yield_0, is_price_0])
68
63
  logShouldRun(cycle, MODEL, term_id, should_run, key=MODEL_KEY)
69
64
  return should_run
70
65
  return should_run_product
71
66
 
72
67
 
68
+ def _should_run_product(product: dict): return product.get(MODEL_KEY) is None
69
+
70
+
73
71
  def run(cycle: dict):
74
- products = list(filter(_should_run(cycle), cycle.get('products', [])))
72
+ products = list(filter(_should_run_product, cycle.get('products', [])))
73
+ products = list(filter(_should_run(cycle), products))
75
74
  return non_empty_list(map(_run(cycle), products))
@@ -42,9 +42,9 @@ def _property(value: float):
42
42
 
43
43
 
44
44
  def _run(product: dict):
45
- liveweightPerHead = get_node_property_value(MODEL, product, 'liveweightPerHead')
45
+ liveweightPerHead = get_node_property_value(MODEL, product, 'liveweightPerHead', term=TERM_ID)
46
46
  processingConversion = get_node_property_value(
47
- MODEL, product, 'processingConversionLiveweightToReadyToCookWeight'
47
+ MODEL, product, 'processingConversionLiveweightToReadyToCookWeight', term=TERM_ID
48
48
  )
49
49
  value = liveweightPerHead * processingConversion if all([liveweightPerHead, processingConversion]) else None
50
50
  prop = _property(value) if value else None
@@ -4,7 +4,7 @@ ecoinvent v3
4
4
  This model calculates background emissions related to the production of Inputs from the ecoinvent database, version 3.
5
5
 
6
6
  Note: to use the `ecoinventV3` model locally or in the
7
- [Hestia Community Edition](https://gitlab.com/hestia-earth/hestia-community-edition) you need a valid ecoinvent license.
7
+ [HESTIA Community Edition](https://gitlab.com/hestia-earth/hestia-community-edition) you need a valid ecoinvent license.
8
8
  Please contact us at community@hestia.earth for instructions to download the required file to run the model.
9
9
 
10
10
  **Pesticide Brand Name**
@@ -15,16 +15,15 @@ default list of [Pesticide Active Ingredient](https://hestia.earth/glossary?pest
15
15
  """
16
16
  from functools import reduce
17
17
  from hestia_earth.schema import EmissionMethodTier
18
- from hestia_earth.utils.model import find_term_match
19
- from hestia_earth.utils.tools import flatten, list_sum, non_empty_list
18
+ from hestia_earth.utils.tools import flatten, list_sum
20
19
 
21
- from hestia_earth.models.log import debugValues, logRequirements, logShouldRun
20
+ from hestia_earth.models.log import debugValues, logShouldRun, logRequirements
22
21
  from hestia_earth.models.data.ecoinventV3 import ecoinventV3_emissions
23
- from hestia_earth.models.utils.term import get_lookup_value
24
22
  from hestia_earth.models.utils.emission import _new_emission
25
23
  from hestia_earth.models.utils.blank_node import group_by_keys
26
24
  from hestia_earth.models.utils.pesticideAI import get_pesticides_from_inputs
27
25
  from hestia_earth.models.utils.fertiliser import get_fertilisers_from_inputs
26
+ from .utils import get_background_inputs, get_input_mappings
28
27
 
29
28
  REQUIREMENTS = {
30
29
  "Cycle": {
@@ -96,69 +95,43 @@ def _add_emission(cycle: dict, input: dict):
96
95
  def add(prev: dict, mapping: tuple):
97
96
  ecoinventName, coefficient = mapping
98
97
  emissions = ecoinventV3_emissions(ecoinventName)
99
- for id, value in emissions:
98
+ for emission_term_id, value in emissions:
100
99
  # log run on each emission so we know it did run
101
- debugValues(cycle, model=MODEL, term=id,
100
+ debugValues(cycle, model=MODEL, term=emission_term_id,
102
101
  value=value,
103
102
  coefficient=coefficient,
104
103
  input=input_term_id,
105
104
  operation=operation_term_id,
106
105
  animal=animal_term_id)
107
- prev[id] = prev.get(id, 0) + (value * coefficient)
106
+ prev[emission_term_id] = prev.get(emission_term_id, 0) + (value * coefficient)
108
107
  return prev
109
108
  return add
110
109
 
111
110
 
112
- def _get_input_mappings(cycle: dict, input: dict):
113
- term = input.get('term', {})
114
- term_id = term.get('@id')
115
- value = get_lookup_value(term, 'ecoinventMapping', model=MODEL, term=term_id)
116
- mappings = non_empty_list(value.split(';')) if value else []
117
- logRequirements(cycle, model=MODEL, term=term_id,
118
- mappings=';'.join(mappings))
119
- return [(m.split(':')[0], float(m.split(':')[1])) for m in mappings]
120
-
121
-
122
111
  def _run_input(cycle: dict):
123
112
  def run(inputs: list):
124
113
  input = inputs[0]
114
+ input_term_id = input.get('term', {}).get('@id')
125
115
  input_value = list_sum(flatten(input.get('value', []) for input in inputs))
126
- mappings = _get_input_mappings(cycle, input)
127
- term_id = input.get('term', {}).get('@id')
128
- should_run = len(mappings) > 0
129
- logShouldRun(cycle, MODEL, term_id, should_run, methodTier=TIER)
130
- grouped_emissions = reduce(_add_emission(cycle, input), mappings, {}) if should_run else {}
131
- return [_emission(term_id, value * input_value, input) for term_id, value in grouped_emissions.items()]
132
- return run
116
+ mappings = get_input_mappings(MODEL, cycle, input)
117
+ has_mappings = len(mappings) > 0
133
118
 
119
+ logRequirements(cycle, model=MODEL, term=input_term_id,
120
+ has_ecoinvent_mappings=has_mappings,
121
+ ecoinvent_mappings=';'.join([v[0] for v in mappings]))
134
122
 
135
- def _should_run_input(products: list):
136
- def should_run(input: dict):
137
- return all([
138
- list_sum(input.get('value', [])) > 0,
139
- # make sure Input is not a Product as well or we might double-count emissions
140
- find_term_match(products, input.get('term', {}).get('@id'), None) is None,
141
- # ignore inputs which are flagged as Product of the Cycle
142
- not input.get('fromCycle', False),
143
- not input.get('producedInCycle', False)
144
- ])
145
- return should_run
146
-
147
-
148
- def _animal_inputs(animal: dict):
149
- inputs = animal.get('inputs', [])
150
- return [(input | {'animal': animal.get('term', {})}) for input in inputs]
123
+ should_run = all([has_mappings])
124
+ logShouldRun(cycle, MODEL, input_term_id, should_run, methodTier=TIER)
125
+ grouped_emissions = reduce(_add_emission(cycle, input), mappings, {}) if should_run else {}
126
+ return [
127
+ _emission(term_id, value * input_value, input)
128
+ for term_id, value in grouped_emissions.items()
129
+ ]
130
+ return run
151
131
 
152
132
 
153
133
  def run(_, cycle: dict):
154
- # add all the properties of some Term that inlcude others with the mapping
155
- inputs = flatten(
156
- cycle.get('inputs', []) +
157
- list(map(_animal_inputs, cycle.get('animals', []))) +
158
- get_pesticides_from_inputs(cycle) +
159
- get_fertilisers_from_inputs(cycle)
160
- )
161
- inputs = list(filter(_should_run_input(cycle.get('products', [])), inputs))
162
- # group inputs with same id/operation to avoid adding emissions twice
163
- inputs = reduce(group_by_keys(['term', 'operation', 'animal']), inputs, {})
164
- return flatten(map(_run_input(cycle), inputs.values()))
134
+ extra_inputs = get_pesticides_from_inputs(cycle) + get_fertilisers_from_inputs(cycle)
135
+ inputs = get_background_inputs(cycle, extra_inputs=extra_inputs)
136
+ grouped_inputs = reduce(group_by_keys(['term', 'operation', 'animal']), inputs, {})
137
+ return flatten(map(_run_input(cycle), grouped_inputs.values()))
@@ -0,0 +1,40 @@
1
+ from hestia_earth.utils.model import find_term_match
2
+ from hestia_earth.utils.tools import list_sum, flatten, non_empty_list
3
+
4
+ from hestia_earth.models.utils.term import get_lookup_value
5
+
6
+
7
+ def _animal_inputs(animal: dict):
8
+ inputs = animal.get('inputs', [])
9
+ return [(input | {'animal': animal.get('term', {})}) for input in inputs]
10
+
11
+
12
+ def _should_run_input(products: list):
13
+ def should_run(input: dict):
14
+ return all([
15
+ list_sum(input.get('value', [])) > 0,
16
+ # make sure Input is not a Product as well or we might double-count emissions
17
+ find_term_match(products, input.get('term', {}).get('@id'), None) is None,
18
+ # ignore inputs which are flagged as Product of the Cycle
19
+ not input.get('fromCycle', False),
20
+ not input.get('producedInCycle', False)
21
+ ])
22
+ return should_run
23
+
24
+
25
+ def get_background_inputs(cycle: dict, extra_inputs: list = []):
26
+ # add all the properties of some Term that inlcude others with the mapping
27
+ inputs = flatten(
28
+ cycle.get('inputs', []) +
29
+ list(map(_animal_inputs, cycle.get('animals', []))) +
30
+ extra_inputs
31
+ )
32
+ return list(filter(_should_run_input(cycle.get('products', [])), inputs))
33
+
34
+
35
+ def get_input_mappings(model: str, cycle: dict, input: dict):
36
+ term = input.get('term', {})
37
+ term_id = term.get('@id')
38
+ value = get_lookup_value(term, 'ecoinventMapping', model=model, term=term_id)
39
+ mappings = non_empty_list(value.split(';')) if value else []
40
+ return [(m.split(':')[0], float(m.split(':')[1])) for m in mappings]
@@ -1,25 +1,30 @@
1
1
  """
2
2
  ecoinvent v3 and Ember Climate
3
3
 
4
- All emissions to air for the cycle.
4
+ This model calculates background emissions related to the production of Inputs from the ecoinvent database, version 3,
5
+ and using the percentage of production for each energy source from
6
+ [Ember](https://ember-climate.org/data-catalogue/yearly-electricity-data/).
7
+
8
+ Note: to use the `ecoinventV3AndEmberClimate` model locally or in the
9
+ [Hestia Community Edition](https://gitlab.com/hestia-earth/hestia-community-edition) you need a valid ecoinvent license.
10
+ Please contact us at community@hestia.earth for instructions to download the required file to run the model.
5
11
  """
6
12
  from functools import reduce
7
- from typing import Tuple
8
-
9
13
  from hestia_earth.utils.tools import list_sum, flatten
10
- from hestia_earth.utils.model import linked_node
11
14
  from hestia_earth.schema import EmissionMethodTier
12
15
 
13
- from hestia_earth.models.log import logShouldRun, logRequirements, log_blank_nodes_id
16
+ from hestia_earth.models.log import logShouldRun, logRequirements, debugValues
14
17
  from hestia_earth.models.utils.emission import _new_emission
15
18
  from hestia_earth.models.utils.blank_node import group_by_keys
16
19
  from hestia_earth.models.utils.completeness import _is_term_type_complete
17
20
  from hestia_earth.models.utils.term import get_electricity_grid_mix_terms
18
- from hestia_earth.models.utils.cycle import cycle_end_year
19
- from .utils import get_emission, get_all_emission_terms
21
+ from hestia_earth.models.data.ecoinventV3 import ecoinventV3_emissions
22
+ from .utils import get_input_coefficient
23
+ from ..ecoinventV3.utils import get_background_inputs, get_input_mappings
20
24
 
21
25
  REQUIREMENTS = {
22
26
  "Cycle": {
27
+ "completeness.electricityFuel": "True",
23
28
  "site": {
24
29
  "@type": "Site",
25
30
  "country": {"@type": "Term", "termType": "region"}
@@ -27,9 +32,26 @@ REQUIREMENTS = {
27
32
  "inputs": [{
28
33
  "@type": "Input",
29
34
  "term.@id": ["electricityGridMarketMix", "electricityGridRenewableMix"],
30
- "value": ""
35
+ "value": "> 0",
36
+ "none": {
37
+ "fromCycle": "True",
38
+ "producedInCycle": "True"
39
+ }
31
40
  }],
32
- "completeness.electricityFuel": "True"
41
+ "optional": {
42
+ "animals": [{
43
+ "@type": "Animal",
44
+ "inputs": [{
45
+ "@type": "Input",
46
+ "term.@id": ["electricityGridMarketMix", "electricityGridRenewableMix"],
47
+ "value": "> 0",
48
+ "none": {
49
+ "fromCycle": "True",
50
+ "producedInCycle": "True"
51
+ }
52
+ }]
53
+ }]
54
+ }
33
55
  }
34
56
  }
35
57
  RETURNS = {
@@ -41,6 +63,7 @@ RETURNS = {
41
63
  }]
42
64
  }
43
65
  LOOKUPS = {
66
+ "electricity": "ecoinventMapping",
44
67
  "region-ember-energySources": "using `country`",
45
68
  "ember-ecoinvent-mapping": ["ember", "ecoinventId", "ecoinventName"]
46
69
  }
@@ -50,94 +73,72 @@ MODEL_KEY = 'impactAssessment' # keep to generate entry in "model-links.json"
50
73
  TIER = EmissionMethodTier.BACKGROUND.value
51
74
 
52
75
 
53
- def _emission(value: float, term_id: str, inputs: list, operation: dict) -> dict:
76
+ def _emission(term_id: str, value: float, input: dict):
54
77
  emission = _new_emission(term_id, MODEL)
55
78
  emission['value'] = [value]
56
79
  emission['methodTier'] = TIER
57
- emission['inputs'] = list(map(linked_node, inputs))
58
- if operation:
59
- emission["operation"] = operation
80
+ emission['inputs'] = [input.get('term')]
81
+ if input.get('operation'):
82
+ emission['operation'] = input.get('operation')
83
+ if input.get('animal'):
84
+ emission['animals'] = [input.get('animal')]
60
85
  return emission
61
86
 
62
87
 
63
- def _grid_inputs(inputs: list, electricity_grid_terms: list):
64
- electricity_grid_term_ids = [v.get('@id') for v in electricity_grid_terms]
65
- return [
66
- i for i in inputs if i.get("term", {}).get("@id") in electricity_grid_term_ids
67
- ]
68
-
69
-
70
- def _run_input(cycle: dict, inputs: list, emission_term_id: str, electricity_grid_term: dict):
71
- electricity_grid_inputs = [electricity_grid_term]
72
- inputs = _grid_inputs(inputs, electricity_grid_inputs)
73
-
74
- def run(grouped_inputs: list):
75
- input = grouped_inputs[0]
76
- term_id = input.get('term', {}).get('@id')
77
- input_value = list_sum(flatten(input.get('value', []) for input in grouped_inputs))
78
- country_id = cycle.get("site", {}).get("country", {}).get("@id")
79
- year = cycle_end_year(cycle)
80
- value = get_emission(
81
- term_id=emission_term_id,
82
- country=country_id,
83
- energy=input_value,
84
- year=year,
85
- model=MODEL
86
- )
87
-
88
- logRequirements(cycle, model=MODEL, term=term_id,
89
- input_value=input_value,
90
- emission_value=value)
91
-
92
- should_run = all([input_value > 0, value])
93
- logShouldRun(cycle, MODEL, term_id, should_run, methodTier=TIER)
94
-
95
- return _emission(value, emission_term_id, electricity_grid_inputs, input.get("operation"))
96
-
97
- return list(map(run, _group_by_operation(inputs).values() if inputs else []))
98
-
99
-
100
- def _group_by_operation(inputs: list) -> dict:
101
- return reduce(group_by_keys(['operation']), inputs, {})
102
-
103
-
104
- def _run_emission(cycle: dict, electricity_grid_terms: list, inputs: list, emission_term_id: str) -> list:
105
- return flatten([
106
- _run_input(
107
- cycle=cycle,
108
- inputs=inputs,
109
- emission_term_id=emission_term_id,
110
- electricity_grid_term=electricity_grid_term
111
- ) for electricity_grid_term in electricity_grid_terms
112
- ])
113
-
114
-
115
- def _should_run_emission(cycle: dict, electricity_grid_terms: list, term_id: str) -> Tuple[bool, list]:
116
- term_type_complete = _is_term_type_complete(cycle, 'electricityFuel')
117
- inputs = _grid_inputs(cycle.get('inputs', []), electricity_grid_terms)
118
- has_relevant_inputs = bool(inputs)
119
- has_country = bool(cycle.get("site", {}).get("country", {}))
120
- has_end_date = bool(cycle.get("endDate"))
121
-
122
- logRequirements(cycle, model=MODEL, term=term_id,
123
- term_type_electricityFuel_complete=term_type_complete,
124
- input_ids=log_blank_nodes_id(inputs),
125
- has_relevant_inputs=has_relevant_inputs,
126
- has_country=has_country,
127
- has_end_date=has_end_date)
128
-
129
- should_run = all([term_type_complete, has_relevant_inputs, has_country, has_end_date])
130
- logShouldRun(cycle, MODEL, term_id, should_run, methodTier=TIER)
131
- return should_run, inputs
132
-
133
-
134
- def _run_emissions(cycle: dict, electricity_grid_terms: list):
135
- def run_emissions_for_term(term_id: str) -> list:
136
- should_run, inputs = _should_run_emission(cycle, electricity_grid_terms, term_id)
137
- return _run_emission(cycle, electricity_grid_terms, inputs, term_id) if should_run else []
138
- return run_emissions_for_term
88
+ def _add_emission(cycle: dict, input: dict):
89
+ input_term_id = input.get('term', {}).get('@id')
90
+ operation_term_id = input.get('operation', {}).get('@id')
91
+ animal_term_id = input.get('animal', {}).get('@id')
92
+ # TODO: as inputs are grouped, there could be more than 1 country
93
+ country_id = input.get('country', cycle.get('site', {}).get('country', {})).get('@id')
94
+
95
+ def add(prev: dict, mapping: tuple):
96
+ ecoinventName, _coefficient = mapping
97
+ # recalculate the coefficient using the country and year if it should be included
98
+ coefficient = get_input_coefficient(MODEL, cycle, country_id, ecoinventName) if _coefficient > 0 else 0
99
+ emissions = ecoinventV3_emissions(ecoinventName)
100
+ for emission_term_id, value in emissions:
101
+ # log run on each emission so we know it did run
102
+ debugValues(cycle, model=MODEL, term=emission_term_id,
103
+ value=value,
104
+ coefficient=coefficient,
105
+ input=input_term_id,
106
+ operation=operation_term_id,
107
+ animal=animal_term_id)
108
+ prev[emission_term_id] = prev.get(emission_term_id, 0) + (value * coefficient)
109
+ return prev
110
+ return add
111
+
112
+
113
+ def _run_input(cycle: dict):
114
+ electricity_complete = _is_term_type_complete(cycle, 'electricityFuel')
115
+
116
+ def run(inputs: list):
117
+ input = inputs[0]
118
+ input_value = list_sum(flatten(input.get('value', []) for input in inputs))
119
+ input_term_id = input.get('term', {}).get('@id')
120
+ mappings = get_input_mappings(MODEL, cycle, input)
121
+ has_mappings = len(mappings) > 0
122
+
123
+ logRequirements(cycle, model=MODEL, term=input_term_id,
124
+ has_ecoinvent_mappings=has_mappings,
125
+ ecoinvent_mappings=';'.join([v[0] for v in mappings]))
126
+
127
+ should_run = all([electricity_complete, has_mappings])
128
+ logShouldRun(cycle, MODEL, input_term_id, should_run, methodTier=TIER)
129
+
130
+ grouped_emissions = reduce(_add_emission(cycle, input), mappings, {}) if should_run else {}
131
+ return [
132
+ _emission(term_id, value * input_value, input)
133
+ for term_id, value in grouped_emissions.items()
134
+ ]
135
+ return run
139
136
 
140
137
 
141
138
  def run(_, cycle: dict):
142
- electricity_grid_terms = get_electricity_grid_mix_terms()
143
- return flatten(list(map(_run_emissions(cycle, electricity_grid_terms), get_all_emission_terms())))
139
+ terms = get_electricity_grid_mix_terms()
140
+ inputs = get_background_inputs(cycle)
141
+ # only keep the inputs matching the grid terms
142
+ inputs = [i for i in inputs if i.get('term', {}).get('@id') in terms]
143
+ grouped_inputs = reduce(group_by_keys(['term', 'operation', 'animal']), inputs, {})
144
+ return flatten(map(_run_input(cycle), grouped_inputs.values()))