hestia-earth-models 0.70.0__py3-none-any.whl → 0.70.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 (192) hide show
  1. hestia_earth/models/aware/scarcityWeightedWaterUse.py +8 -16
  2. hestia_earth/models/cml2001Baseline/resourceUseMineralsAndMetalsDuringCycle.py +2 -1
  3. hestia_earth/models/config/Cycle.json +98 -50
  4. hestia_earth/models/config/ImpactAssessment.json +12 -4
  5. hestia_earth/models/config/Site.json +40 -21
  6. hestia_earth/models/cycle/transformation.py +1 -1
  7. hestia_earth/models/cycle/utils.py +0 -6
  8. hestia_earth/models/data/ecoinventV3/__init__.py +15 -13
  9. hestia_earth/models/ecoalimV9/__init__.py +13 -0
  10. hestia_earth/models/ecoalimV9/cycle.py +128 -0
  11. hestia_earth/models/ecoalimV9/impact_assessment.py +125 -0
  12. hestia_earth/models/ecoalimV9/utils.py +31 -0
  13. hestia_earth/models/ecoinventV3/__init__.py +6 -14
  14. hestia_earth/models/ecoinventV3/utils.py +1 -29
  15. hestia_earth/models/ecoinventV3AndEmberClimate/__init__.py +8 -2
  16. hestia_earth/models/emissionNotRelevant/__init__.py +33 -8
  17. hestia_earth/models/{cycle → hestia}/aboveGroundCropResidue.py +4 -3
  18. hestia_earth/models/{cycle → hestia}/aboveGroundCropResidueTotal.py +1 -1
  19. hestia_earth/models/{site → hestia}/brackishWater.py +1 -1
  20. hestia_earth/models/{site → hestia}/cationExchangeCapacityPerKgSoil.py +1 -1
  21. hestia_earth/models/{cycle → hestia}/coldCarcassWeightPerHead.py +1 -1
  22. hestia_earth/models/{cycle → hestia}/coldDressedCarcassWeightPerHead.py +1 -1
  23. hestia_earth/models/{cycle → hestia}/concentrateFeed.py +1 -1
  24. hestia_earth/models/{cycle → hestia}/cropResidueManagement.py +1 -1
  25. hestia_earth/models/{cycle → hestia}/croppingIntensity.py +1 -1
  26. hestia_earth/models/{cycle → hestia}/energyContentLowerHeatingValue.py +1 -1
  27. hestia_earth/models/{cycle → hestia}/excretaKgMass.py +8 -3
  28. hestia_earth/models/{cycle → hestia}/excretaKgN.py +1 -1
  29. hestia_earth/models/{cycle → hestia}/excretaKgVs.py +1 -1
  30. hestia_earth/models/{cycle → hestia}/feedConversionRatio/__init__.py +1 -1
  31. hestia_earth/models/{site → hestia}/flowingWater.py +1 -1
  32. hestia_earth/models/{site → hestia}/freshWater.py +1 -1
  33. hestia_earth/models/{cycle → hestia}/inorganicFertiliser.py +1 -1
  34. hestia_earth/models/{cycle → hestia}/irrigatedTypeUnspecified.py +14 -19
  35. hestia_earth/models/hestia/landCover.py +30 -22
  36. hestia_earth/models/{cycle → hestia}/liveAnimal.py +1 -1
  37. hestia_earth/models/{cycle → hestia}/longFallowRatio.py +1 -1
  38. hestia_earth/models/{site → hestia}/management.py +4 -6
  39. hestia_earth/models/{cycle → hestia}/materialAndSubstrate.py +1 -1
  40. hestia_earth/models/{cycle → hestia}/milkYield.py +1 -1
  41. hestia_earth/models/{site → hestia}/netPrimaryProduction.py +1 -1
  42. hestia_earth/models/{site → hestia}/organicCarbonPerHa.py +1 -1
  43. hestia_earth/models/hestia/pToSurfaceWaterAquacultureSystems.py +148 -0
  44. hestia_earth/models/{cycle → hestia}/pastureGrass.py +1 -1
  45. hestia_earth/models/{cycle → hestia}/pastureSystem.py +1 -1
  46. hestia_earth/models/{site → hestia}/potentialEvapotranspirationAnnual.py +3 -3
  47. hestia_earth/models/{site → hestia}/potentialEvapotranspirationMonthly.py +3 -3
  48. hestia_earth/models/{site → hestia}/precipitationAnnual.py +3 -3
  49. hestia_earth/models/{site → hestia}/precipitationMonthly.py +3 -3
  50. hestia_earth/models/{site → hestia}/rainfallAnnual.py +3 -3
  51. hestia_earth/models/{site → hestia}/rainfallMonthly.py +3 -3
  52. hestia_earth/models/{cycle → hestia}/readyToCookWeightPerHead.py +1 -1
  53. hestia_earth/models/{cycle → hestia}/residueBurnt.py +1 -1
  54. hestia_earth/models/{cycle → hestia}/residueIncorporated.py +1 -1
  55. hestia_earth/models/{cycle → hestia}/residueLeftOnField.py +1 -1
  56. hestia_earth/models/hestia/residueRemoved.py +65 -13
  57. hestia_earth/models/{site → hestia}/salineWater.py +1 -1
  58. hestia_earth/models/{site → hestia}/soilMeasurement.py +1 -1
  59. hestia_earth/models/{cycle → hestia}/stockingDensityAnimalHousingAverage.py +1 -1
  60. hestia_earth/models/{site → hestia}/temperatureAnnual.py +3 -3
  61. hestia_earth/models/{site → hestia}/temperatureMonthly.py +3 -3
  62. hestia_earth/models/{site → hestia}/totalNitrogenPerKgSoil.py +1 -1
  63. hestia_earth/models/{cycle → hestia}/unknownPreSeasonWaterRegime.py +1 -1
  64. hestia_earth/models/hestia/utils.py +93 -0
  65. hestia_earth/models/{site → hestia}/waterDepth.py +1 -1
  66. hestia_earth/models/hestia/waterSalinity.py +78 -0
  67. hestia_earth/models/ipcc2019/aboveGroundBiomass.py +1 -1
  68. hestia_earth/models/ipcc2019/belowGroundBiomass.py +1 -1
  69. hestia_earth/models/ipcc2019/biomass_utils.py +2 -4
  70. hestia_earth/models/ipcc2019/ch4ToAirFloodedRice.py +166 -79
  71. hestia_earth/models/ipcc2019/ch4ToAirOrganicSoilCultivation.py +270 -0
  72. hestia_earth/models/ipcc2019/co2ToAirAboveGroundBiomassStockChange.py +0 -3
  73. hestia_earth/models/ipcc2019/co2ToAirBelowGroundBiomassStockChange.py +0 -3
  74. hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +88 -63
  75. hestia_earth/models/ipcc2019/co2ToAirLimeHydrolysis.py +7 -5
  76. hestia_earth/models/ipcc2019/co2ToAirOrganicSoilCultivation.py +215 -0
  77. hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChange.py +0 -3
  78. hestia_earth/models/ipcc2019/co2ToAirUreaHydrolysis.py +16 -9
  79. hestia_earth/models/ipcc2019/n2OToAirOrganicSoilCultivationDirect.py +161 -0
  80. hestia_earth/models/ipcc2019/nonCo2EmissionsToAirNaturalVegetationBurning.py +35 -47
  81. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1.py +86 -1
  82. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2.py +127 -1
  83. hestia_earth/models/ipcc2019/organicCarbonPerHa_utils.py +7 -5
  84. hestia_earth/models/ipcc2019/organicSoilCultivation_utils.py +159 -0
  85. hestia_earth/models/mocking/search-results.json +1113 -1113
  86. hestia_earth/models/pooreNemecek2018/utils.py +8 -2
  87. hestia_earth/models/schmidt2007/ch4ToAirWasteTreatment.py +1 -4
  88. hestia_earth/models/schmidt2007/h2SToAirWasteTreatment.py +1 -4
  89. hestia_earth/models/schmidt2007/n2OToAirWasteTreatmentDirect.py +1 -4
  90. hestia_earth/models/schmidt2007/nh3ToAirWasteTreatment.py +1 -4
  91. hestia_earth/models/site/grouped_measurement.py +132 -0
  92. hestia_earth/models/utils/__init__.py +4 -3
  93. hestia_earth/models/utils/background_emissions.py +52 -0
  94. hestia_earth/models/utils/blank_node.py +47 -14
  95. hestia_earth/models/utils/constant.py +26 -20
  96. hestia_earth/models/utils/impact_assessment.py +26 -17
  97. hestia_earth/models/utils/lookup.py +48 -39
  98. hestia_earth/models/utils/measurement.py +3 -3
  99. hestia_earth/models/utils/product.py +39 -1
  100. hestia_earth/models/utils/property.py +14 -6
  101. hestia_earth/models/version.py +1 -1
  102. {hestia_earth_models-0.70.0.dist-info → hestia_earth_models-0.70.2.dist-info}/METADATA +2 -2
  103. {hestia_earth_models-0.70.0.dist-info → hestia_earth_models-0.70.2.dist-info}/RECORD +187 -171
  104. tests/models/aware/test_scarcityWeightedWaterUse.py +1 -12
  105. tests/models/ecoalimV9/__init__.py +0 -0
  106. tests/models/ecoalimV9/test_cycle.py +21 -0
  107. tests/models/ecoalimV9/test_impact_assessment.py +24 -0
  108. tests/models/environmentalFootprintV3_1/test_scarcityWeightedWaterUse.py +4 -2
  109. tests/models/{cycle → hestia}/test_aboveGroundCropResidue.py +1 -1
  110. tests/models/{cycle → hestia}/test_aboveGroundCropResidueTotal.py +1 -1
  111. tests/models/{site → hestia}/test_brackishWater.py +1 -1
  112. tests/models/{site → hestia}/test_cationExchangeCapacityPerKgSoil.py +1 -1
  113. tests/models/{cycle → hestia}/test_coldCarcassWeightPerHead.py +1 -1
  114. tests/models/{cycle → hestia}/test_coldDressedCarcassWeightPerHead.py +1 -1
  115. tests/models/{cycle → hestia}/test_concentrateFeed.py +1 -1
  116. tests/models/{cycle → hestia}/test_cropResidueManagement.py +1 -1
  117. tests/models/{cycle → hestia}/test_croppingIntensity.py +1 -1
  118. tests/models/{cycle → hestia}/test_energyContentLowerHeatingValue.py +5 -3
  119. tests/models/{cycle → hestia}/test_excretaKgMass.py +1 -1
  120. tests/models/{cycle → hestia}/test_excretaKgN.py +1 -1
  121. tests/models/{cycle → hestia}/test_excretaKgVs.py +1 -1
  122. tests/models/{cycle → hestia}/test_feedConversionRatio.py +3 -4
  123. tests/models/{site → hestia}/test_flowingWater.py +1 -1
  124. tests/models/{site → hestia}/test_freshWater.py +1 -1
  125. tests/models/{cycle → hestia}/test_inorganicFertiliser.py +1 -1
  126. tests/models/{cycle → hestia}/test_irrigatedTypeUnspecified.py +2 -5
  127. tests/models/hestia/test_landCover.py +4 -34
  128. tests/models/{cycle → hestia}/test_liveAnimal.py +1 -1
  129. tests/models/{cycle → hestia}/test_longFallowRatio.py +1 -1
  130. tests/models/{site → hestia}/test_management.py +1 -1
  131. tests/models/{cycle → hestia}/test_materialsAndSubstrate.py +1 -1
  132. tests/models/{cycle → hestia}/test_milkYield.py +1 -1
  133. tests/models/{site → hestia}/test_netPrimaryProduction.py +1 -1
  134. tests/models/{site → hestia}/test_organicCarbonPerHa.py +1 -1
  135. tests/models/{site → hestia}/test_organicCarbonPerKgSoil.py +1 -1
  136. tests/models/{site → hestia}/test_organicCarbonPerM3Soil.py +1 -1
  137. tests/models/{site → hestia}/test_organicMatterPerKgSoil.py +1 -1
  138. tests/models/{site → hestia}/test_organicMatterPerM3Soil.py +1 -1
  139. tests/models/hestia/test_pToSurfaceWaterAquacultureSystems.py +56 -0
  140. tests/models/{cycle → hestia}/test_pastureGrass.py +1 -1
  141. tests/models/{cycle → hestia}/test_pastureSystem.py +1 -1
  142. tests/models/{site → hestia}/test_potentialEvapotranspirationAnnual.py +1 -1
  143. tests/models/{site → hestia}/test_potentialEvapotranspirationMonthly.py +1 -1
  144. tests/models/{site → hestia}/test_precipitationAnnual.py +1 -1
  145. tests/models/{site → hestia}/test_precipitationMonthly.py +1 -1
  146. tests/models/{site → hestia}/test_rainfallAnnual.py +1 -1
  147. tests/models/{site → hestia}/test_rainfallMonthly.py +1 -1
  148. tests/models/{cycle → hestia}/test_readyToCookWeightPerHead.py +1 -1
  149. tests/models/{cycle → hestia}/test_residueBurnt.py +1 -1
  150. tests/models/{cycle → hestia}/test_residueIncorporated.py +1 -1
  151. tests/models/{cycle → hestia}/test_residueLeftOnField.py +1 -1
  152. tests/models/hestia/test_residueRemoved.py +15 -3
  153. tests/models/{site → hestia}/test_salineWater.py +1 -1
  154. tests/models/{site → hestia}/test_soilMeasurement.py +13 -21
  155. tests/models/{cycle → hestia}/test_stockingDensityAnimalHousingAverage.py +1 -1
  156. tests/models/{site → hestia}/test_temperatureAnnual.py +1 -1
  157. tests/models/{site → hestia}/test_temperatureMonthly.py +1 -1
  158. tests/models/{site → hestia}/test_totalNitrogenPerKgSoil.py +1 -1
  159. tests/models/{cycle → hestia}/test_unknownPreSeasonWaterRegime.py +1 -1
  160. tests/models/{site → hestia}/test_waterDepth.py +1 -1
  161. tests/models/hestia/test_waterSalinity.py +26 -0
  162. tests/models/ipcc2019/test_ch4ToAirEntericFermentation.py +2 -5
  163. tests/models/ipcc2019/test_ch4ToAirFloodedRice.py +10 -42
  164. tests/models/ipcc2019/test_ch4ToAirOrganicSoilCultivation.py +61 -0
  165. tests/models/ipcc2019/test_co2ToAirAboveGroundBiomassStockChange.py +11 -9
  166. tests/models/ipcc2019/test_co2ToAirBelowGroundBiomassStockChange.py +10 -8
  167. tests/models/ipcc2019/test_co2ToAirLimeHydrolysis.py +1 -1
  168. tests/models/ipcc2019/test_co2ToAirOrganicSoilCultivation.py +62 -0
  169. tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChange.py +11 -8
  170. tests/models/ipcc2019/test_n2OToAirOrganicSoilCultivationDirect.py +61 -0
  171. tests/models/ipcc2019/test_nonCo2EmissionsToAirNaturalVegetationBurning.py +3 -2
  172. tests/models/site/test_grouped_measurement.py +20 -0
  173. tests/models/test_ecoinventV3AndEmberClimate.py +2 -2
  174. tests/models/test_emissionNotRelevant.py +0 -8
  175. tests/models/utils/test_measurement.py +1 -1
  176. hestia_earth/models/cycle/residueRemoved.py +0 -54
  177. hestia_earth/models/hestia/nh3ToSurfaceWaterAquacultureSystems.py +0 -64
  178. hestia_earth/models/site/utils.py +0 -93
  179. tests/models/cycle/test_residueRemoved.py +0 -37
  180. tests/models/hestia/test_nh3ToSurfaceWaterAquacultureSystems.py +0 -51
  181. /hestia_earth/models/{cycle → hestia}/feedConversionRatio/feedConversionRatioCarbon.py +0 -0
  182. /hestia_earth/models/{cycle → hestia}/feedConversionRatio/feedConversionRatioDryMatter.py +0 -0
  183. /hestia_earth/models/{cycle → hestia}/feedConversionRatio/feedConversionRatioEnergy.py +0 -0
  184. /hestia_earth/models/{cycle → hestia}/feedConversionRatio/feedConversionRatioFedWeight.py +0 -0
  185. /hestia_earth/models/{cycle → hestia}/feedConversionRatio/feedConversionRatioNitrogen.py +0 -0
  186. /hestia_earth/models/{site → hestia}/organicCarbonPerKgSoil.py +0 -0
  187. /hestia_earth/models/{site → hestia}/organicCarbonPerM3Soil.py +0 -0
  188. /hestia_earth/models/{site → hestia}/organicMatterPerKgSoil.py +0 -0
  189. /hestia_earth/models/{site → hestia}/organicMatterPerM3Soil.py +0 -0
  190. {hestia_earth_models-0.70.0.dist-info → hestia_earth_models-0.70.2.dist-info}/LICENSE +0 -0
  191. {hestia_earth_models-0.70.0.dist-info → hestia_earth_models-0.70.2.dist-info}/WHEEL +0 -0
  192. {hestia_earth_models-0.70.0.dist-info → hestia_earth_models-0.70.2.dist-info}/top_level.txt +0 -0
@@ -31,12 +31,18 @@ def run_products_average(cycle: dict, term_id: str, get_value_func):
31
31
  return list_average(values) if should_run else None
32
32
 
33
33
 
34
+ def _excreta_product_id(data: str, term_id):
35
+ value = extract_grouped_data(data, term_id) or ''
36
+ return value.split('|')[0] if '|' in value else value
37
+
38
+
34
39
  def get_excreta_product_with_ratio(cycle: dict, lookup: str, **log_args):
35
40
  product = find_primary_product(cycle) or {}
36
41
 
37
42
  data = get_lookup_value(product.get('term'), lookup, model=MODEL, **log_args)
38
43
 
39
- default_product_id = extract_grouped_data(data, 'default')
44
+ # TODO: handle multiple products delimited by `|`
45
+ default_product_id = _excreta_product_id(data, 'default')
40
46
 
41
47
  # find matching practices and assign a ratio for each
42
48
  # if no matches, use default id with 100% ratio
@@ -44,7 +50,7 @@ def get_excreta_product_with_ratio(cycle: dict, lookup: str, **log_args):
44
50
  practices = [
45
51
  {
46
52
  'id': practice.get('term', {}).get('@id'),
47
- 'product-id': extract_grouped_data(data, practice.get('term', {}).get('@id')) or default_product_id,
53
+ 'product-id': _excreta_product_id(data, practice.get('term', {}).get('@id')) or default_product_id,
48
54
  'value': list_sum(practice.get('value'))
49
55
  }
50
56
  for practice in practices
@@ -8,10 +8,7 @@ from . import MODEL
8
8
 
9
9
  REQUIREMENTS = {
10
10
  "Cycle": {
11
- "or": {
12
- "products": [{"@type": "Product", "value": "", "term.termType": "waste"}],
13
- "completeness.waste": ""
14
- }
11
+ "products": [{"@type": "Product", "value": ""}]
15
12
  }
16
13
  }
17
14
  RETURNS = {
@@ -8,10 +8,7 @@ from . import MODEL
8
8
 
9
9
  REQUIREMENTS = {
10
10
  "Cycle": {
11
- "or": {
12
- "products": [{"@type": "Product", "value": "", "term.termType": "waste"}],
13
- "completeness.waste": ""
14
- }
11
+ "products": [{"@type": "Product", "value": ""}]
15
12
  }
16
13
  }
17
14
  RETURNS = {
@@ -8,10 +8,7 @@ from . import MODEL
8
8
 
9
9
  REQUIREMENTS = {
10
10
  "Cycle": {
11
- "or": {
12
- "products": [{"@type": "Product", "value": "", "term.termType": "waste"}],
13
- "completeness.waste": ""
14
- }
11
+ "products": [{"@type": "Product", "value": ""}]
15
12
  }
16
13
  }
17
14
  RETURNS = {
@@ -8,10 +8,7 @@ from . import MODEL
8
8
 
9
9
  REQUIREMENTS = {
10
10
  "Cycle": {
11
- "or": {
12
- "products": [{"@type": "Product", "value": "", "term.termType": "waste"}],
13
- "completeness.waste": ""
14
- }
11
+ "products": [{"@type": "Product", "value": ""}]
15
12
  }
16
13
  }
17
14
  RETURNS = {
@@ -0,0 +1,132 @@
1
+ from functools import reduce
2
+ from hestia_earth.schema import MeasurementMethodClassification
3
+ from hestia_earth.utils.tools import non_empty_list, flatten, is_number, list_sum
4
+
5
+ from hestia_earth.models.log import logRequirements, logShouldRun
6
+ from hestia_earth.models.utils import _include
7
+ from hestia_earth.models.utils.measurement import _new_measurement
8
+ from hestia_earth.models.utils.blank_node import get_lookup_value
9
+ from . import MODEL
10
+
11
+ REQUIREMENTS = {
12
+ "Site": {
13
+ "measurements": [
14
+ {"@type": "Measurement", "depthUpper": "", "depthLower": "", "value": ""}
15
+ ]
16
+ }
17
+ }
18
+ RETURNS = {
19
+ "Measurement": [{
20
+ "value": "0",
21
+ "depthUpper": "",
22
+ "depthLower": "",
23
+ "dates": "",
24
+ "methodClassification": "modelled using other measurements"
25
+ }]
26
+ }
27
+ LOOKUPS = {
28
+ "measurement": ["sumIs100Group", "sumMax100Group"]
29
+ }
30
+ MODEL_KEY = 'grouped_measurement'
31
+
32
+
33
+ def _measurement(term_id: str, measurement: dict):
34
+ data = _new_measurement(term_id) | _include(measurement, [
35
+ 'depthUpper', 'depthLower', 'startDate', 'endDate', 'dates'
36
+ ])
37
+ data["value"] = [0]
38
+ data["methodClassification"] = MeasurementMethodClassification.MODELLED_USING_OTHER_MEASUREMENTS.value
39
+ return data
40
+
41
+
42
+ def _measurement_term_ids(measurements: list):
43
+ return list(set([m.get('term', {}).get('@id') for m in measurements]))
44
+
45
+
46
+ def _formatDepth(depth: str):
47
+ # handle float values
48
+ return str(int(depth)) if is_number(depth) else ''
49
+
50
+
51
+ def _group_by_depth(measurements: list):
52
+ def group_by(group: dict, measurement: dict):
53
+ keys = non_empty_list([
54
+ _formatDepth(measurement.get('depthUpper')),
55
+ _formatDepth(measurement.get('depthLower'))
56
+ ])
57
+ key = '-'.join(keys) if len(keys) > 0 else 'default'
58
+ group[key] = group.get(key, []) + [measurement]
59
+ return group
60
+
61
+ return reduce(group_by, measurements, {})
62
+
63
+
64
+ def _run_measurements(site: dict, all_term_ids: list, measurements: list):
65
+ term_ids = _measurement_term_ids(measurements)
66
+
67
+ # check the total value is 100%
68
+ total_value = list_sum([
69
+ list_sum(m.get('value')) for m in measurements
70
+ ])
71
+ is_total_100 = 99.5 <= total_value <= 100.5
72
+
73
+ should_run = all([is_total_100])
74
+
75
+ other_term_ids = [v for v in all_term_ids if v not in term_ids]
76
+ for term_id in other_term_ids:
77
+ logRequirements(site, model=MODEL, term=term_id, model_key=MODEL_KEY,
78
+ total_value=total_value)
79
+
80
+ logShouldRun(site, MODEL, term_id, should_run, model_key=MODEL_KEY)
81
+
82
+ return [
83
+ _measurement(term_id=term_id, measurement=measurements[0])
84
+ for term_id in other_term_ids
85
+ ] if should_run else []
86
+
87
+
88
+ def _run(site: dict, measurements: list):
89
+ term_ids = _measurement_term_ids(measurements)
90
+ grouped_measurements = _group_by_depth(measurements)
91
+
92
+ return flatten([
93
+ _run_measurements(site, term_ids, values)
94
+ for values in grouped_measurements.values()
95
+ ]) if len(grouped_measurements.keys()) > 1 else []
96
+
97
+
98
+ def _group_by_100group(measurements: list):
99
+ def group_by(group: dict, measurement: dict):
100
+ term = measurement.get('term', {})
101
+ sum_group_key = (
102
+ get_lookup_value(term, 'sumIs100Group', skip_debug=True, model=MODEL) or
103
+ get_lookup_value(term, 'sumMax100Group', skip_debug=True, model=MODEL)
104
+ )
105
+ keys = non_empty_list([
106
+ sum_group_key,
107
+ measurement.get('startDate'),
108
+ measurement.get('endDate'),
109
+ '-'.join(measurement.get('dates') or []),
110
+ ])
111
+ key = '-'.join(keys) if len(keys) > 0 else 'default'
112
+
113
+ if all([
114
+ sum_group_key,
115
+ measurement.get('value', []),
116
+ measurement.get('depthUpper') is not None,
117
+ measurement.get('depthLower') is not None
118
+ ]):
119
+ group[key] = group.get(key, []) + [measurement]
120
+
121
+ return group
122
+
123
+ return reduce(group_by, measurements, {})
124
+
125
+
126
+ def run(site: dict):
127
+ grouped_measurements = _group_by_100group(site.get('measurements', []))
128
+ return flatten([
129
+ _run(site, values)
130
+ for values in grouped_measurements.values()
131
+ if len(values) > 1
132
+ ])
@@ -8,7 +8,7 @@ import datetime
8
8
  from functools import reduce
9
9
  import operator
10
10
  from pydash.objects import get
11
- from typing import Any, Callable
11
+ from typing import Union, List, Callable
12
12
  from hestia_earth.schema import SchemaType
13
13
  from hestia_earth.utils.api import download_hestia
14
14
  from hestia_earth.utils.tools import flatten, non_empty_list
@@ -54,8 +54,9 @@ def _load_calculated_node(node, type: SchemaType, data_state='recalculated'):
54
54
  def _unit_str(unit) -> str: return unit if isinstance(unit, str) else unit.value
55
55
 
56
56
 
57
- def _filter_list_term_unit(values: list, unit: Any):
58
- units = list(map(_unit_str, unit)) if isinstance(unit, list) else [_unit_str(unit)]
57
+ def _filter_list_term_unit(values: list, unit: Union[str, List[str]]):
58
+ units = unit if isinstance(unit, list) else[unit]
59
+ units = list(map(_unit_str, units))
59
60
  return list(filter(lambda i: i.get('term', {}).get('units') in units, values))
60
61
 
61
62
 
@@ -0,0 +1,52 @@
1
+ from hestia_earth.utils.model import find_term_match
2
+ from hestia_earth.utils.tools import flatten
3
+
4
+ from . import is_from_model
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
+ # make sure Input is not a Product as well or we might double-count emissions
16
+ find_term_match(products, input.get('term', {}).get('@id'), None) is None,
17
+ # ignore inputs which are flagged as Product of the Cycle
18
+ not input.get('fromCycle', False),
19
+ not input.get('producedInCycle', False)
20
+ ])
21
+ return should_run
22
+
23
+
24
+ def get_background_inputs(cycle: dict, extra_inputs: list = []):
25
+ # add all the properties of some Term that inlcude others with the mapping
26
+ inputs = flatten(
27
+ cycle.get('inputs', []) +
28
+ list(map(_animal_inputs, cycle.get('animals', []))) +
29
+ extra_inputs
30
+ )
31
+ return list(filter(_should_run_input(cycle.get('products', [])), inputs))
32
+
33
+
34
+ def no_gap_filled_background_emissions(cycle: dict):
35
+ emissions = cycle.get('emissions', [])
36
+
37
+ def check_input(input: dict):
38
+ input_term_id = input.get('term', {}).get('@id')
39
+ operation_term_id = input.get('operation', {}).get('@id')
40
+ animal_term_id = input.get('animal', {}).get('@id')
41
+
42
+ return not any([
43
+ is_from_model(emission)
44
+ for emission in emissions
45
+ if all([
46
+ any([i.get('@id') == input_term_id for i in emission.get('inputs', [])]),
47
+ emission.get('operation', {}).get('@id') == operation_term_id,
48
+ emission.get('animal', {}).get('@id') == animal_term_id
49
+ ])
50
+ ])
51
+
52
+ return check_input
@@ -22,11 +22,11 @@ from hestia_earth.utils.tools import (
22
22
  list_sum,
23
23
  list_average,
24
24
  safe_parse_date,
25
- safe_parse_float,
26
25
  non_empty_list
27
26
  )
28
27
  from hestia_earth.utils.lookup_utils import (
29
28
  is_model_siteType_allowed,
29
+ is_model_product_id_allowed,
30
30
  is_siteType_allowed,
31
31
  is_product_id_allowed,
32
32
  is_product_termType_allowed,
@@ -124,13 +124,16 @@ def _module_term_id(term_id: str, module):
124
124
  return getattr(module, 'TERM_ID', term_id_str).split(',')[0]
125
125
 
126
126
 
127
- def _run_model_required(model: str, term_id: str, data: dict):
127
+ def _run_model_required(model: str, term_id: str, data: dict, skip_logs: bool = False):
128
128
  siteType_allowed = is_model_siteType_allowed(model, term_id, data)
129
+ product_id_allowed = is_model_product_id_allowed(model, term_id, data)
129
130
 
130
- run_required = all([siteType_allowed])
131
- debugValues(data, model=model, term=term_id,
132
- run_required=run_required,
133
- siteType_allowed=siteType_allowed)
131
+ run_required = all([siteType_allowed, product_id_allowed])
132
+ if not skip_logs:
133
+ debugValues(data, model=model, term=term_id,
134
+ run_required=run_required,
135
+ siteType_allowed=siteType_allowed,
136
+ product_id_allowed=product_id_allowed)
134
137
  return run_required
135
138
 
136
139
 
@@ -234,16 +237,21 @@ def get_total_value(nodes: list):
234
237
 
235
238
  def _value_as(term_id: str, convert_to_property=True):
236
239
  def get_value(node: dict):
237
- property = get_node_property(node, term_id)
240
+ factor = get_node_property_value(
241
+ None, node, term_id, default=0, handle_percents=False
242
+ ) or get_lookup_value(
243
+ lookup_term=node.get('term', {}),
244
+ column=term_id
245
+ ) or 0
238
246
  # ignore node value if property is not found
239
- factor = safe_parse_float(property.get('value', 0))
240
247
  value = list_sum(node.get('value', []))
248
+ property = get_node_property(node, term_id, find_default_property=False, download_from_hestia=True)
241
249
  ratio = factor / 100 if property.get('term', {}).get('units', '') == '%' else factor
242
250
  return 0 if ratio == 0 else (value * ratio if convert_to_property else value / ratio)
243
251
  return get_value
244
252
 
245
253
 
246
- def get_total_value_converted(nodes: list, conversion_property, convert_to_property=True):
254
+ def get_total_value_converted(nodes: list, conversion_property: Union[List[str], str], convert_to_property=True):
247
255
  """
248
256
  Get the total `value` of a list of Blank Nodes converted using a property of each Blank Node.
249
257
 
@@ -367,14 +375,39 @@ def get_KG_total(nodes: list) -> list:
367
375
  return get_total_value(kg_nodes) + get_total_value_converted(kg_N_nodes, 'nitrogenContent', False)
368
376
 
369
377
 
378
+ def get_P_total(nodes: list) -> list:
379
+ """
380
+ Get the total phosphorous content of a list of Blank Node.
381
+
382
+ The result contains the values of the following nodes:
383
+ 1. Every blank node specified in `kg P` will be used.
384
+ 1. Every blank node specified in `kg N` will be multiplied by the `phosphateContentAsP` property.
385
+ 2. Every blank node specified in `kg` will be multiplied by the `phosphateContentAsP` property.
386
+
387
+ Parameters
388
+ ----------
389
+ nodes : list
390
+ A list of Blank Node.
391
+
392
+ Returns
393
+ -------
394
+ list
395
+ The phosphorous values as a list of numbers.
396
+ """
397
+ kg_P_nodes = _filter_list_term_unit(nodes, Units.KG_P)
398
+ kg_N_nodes = _filter_list_term_unit(nodes, Units.KG_N)
399
+ kg_nodes = _filter_list_term_unit(nodes, Units.KG)
400
+ return get_total_value(kg_P_nodes) + get_total_value_converted(kg_N_nodes + kg_nodes, 'phosphateContentAsP')
401
+
402
+
370
403
  def get_P2O5_total(nodes: list) -> list:
371
404
  """
372
405
  Get the total phosphate content of a list of Blank Node.
373
406
 
374
407
  The result contains the values of the following nodes:
375
- 1. Every organic fertiliser specified in `kg P2O5` will be used.
376
- 1. Every organic fertiliser specified in `kg N` will be multiplied by the `phosphateContentAsP2O5` property.
377
- 2. Every organic fertiliser specified in `kg` will be multiplied by the `phosphateContentAsP2O5` property.
408
+ 1. Every blank node specified in `kg P2O5` will be used.
409
+ 1. Every blank node specified in `kg N` will be multiplied by the `phosphateContentAsP2O5` property.
410
+ 2. Every blank node specified in `kg` will be multiplied by the `phosphateContentAsP2O5` property.
378
411
 
379
412
  Parameters
380
413
  ----------
@@ -386,10 +419,10 @@ def get_P2O5_total(nodes: list) -> list:
386
419
  list
387
420
  The phosphate values as a list of numbers.
388
421
  """
389
- kg_P_nodes = _filter_list_term_unit(nodes, Units.KG_P2O5)
422
+ kg_P2O5_nodes = _filter_list_term_unit(nodes, Units.KG_P2O5)
390
423
  kg_N_nodes = _filter_list_term_unit(nodes, Units.KG_N)
391
424
  kg_nodes = _filter_list_term_unit(nodes, Units.KG)
392
- return get_total_value(kg_P_nodes) + get_total_value_converted(kg_N_nodes + kg_nodes, 'phosphateContentAsP2O5')
425
+ return get_total_value(kg_P2O5_nodes) + get_total_value_converted(kg_N_nodes + kg_nodes, 'phosphateContentAsP2O5')
393
426
 
394
427
 
395
428
  def convert_to_nitrogen(node: dict, model: str, blank_nodes: list, **log_args):
@@ -19,7 +19,7 @@ class Units(Enum):
19
19
  KG_CO2 = 'kg CO2'
20
20
  KG_K = 'kg K'
21
21
  KG_K2O = 'kg K2O'
22
- KG_MGCO3 = 'kg MgCO3'
22
+ KG_CAMGCO32 = 'kg CaMg(CO3)2'
23
23
  KG_N = 'kg N'
24
24
  KG_N2 = 'kg N2'
25
25
  KG_N2O = 'kg N2O'
@@ -48,58 +48,62 @@ C = 12.012
48
48
  CA = 40.078
49
49
  H = 1.008
50
50
  K = 39.098
51
+ MG = 24.305
51
52
  N = 14.007
52
53
  _O = 15.999
53
54
  P = 30.974
54
55
  ATOMIC_WEIGHT_CONVERSIONS = {
55
56
  Units.KG_P.value: {
56
- Units.KG_P2O5.value: (P*2) / (P*2 + _O*5), # Conv_Mol_P_P2O5
57
- Units.KG_PO43.value: P / ((P + _O*4)*3) # Conv_Mol_P_PO43-
57
+ Units.KG_P2O5.value: (P*2) / (P*2 + _O*5),
58
+ Units.KG_PO43.value: P / ((P + _O*4)*3)
58
59
  },
59
60
  Units.KG_PO43.value: {
60
- Units.KG_P2O5.value: ((P + _O*4)*3) / (P*2 + _O*5) # Conv_Mol_PO43-_P2O5
61
+ Units.KG_P2O5.value: ((P + _O*4)*3) / (P*2 + _O*5)
62
+ },
63
+ Units.KG_P2O5.value: {
64
+ Units.KG_P.value: (P*2 + _O*5) / (P*2)
61
65
  },
62
66
  Units.KG_K.value: {
63
- Units.KG_K2O.value: (K*2) / (K*2 + _O) # Conv_Mol_K_K2O
67
+ Units.KG_K2O.value: (K*2) / (K*2 + _O)
64
68
  },
65
69
  Units.KG_CA.value: {
66
- Units.KG_CAO.value: CA / (CA + _O) # Conv_Mol_Ca_CaO
70
+ Units.KG_CAO.value: CA / (CA + _O)
67
71
  },
68
72
  Units.KG_CAO.value: {
69
- Units.KG_CACO3.value: (CA + _O) / (CA + C + _O*3) # Conv_Mol_CaO_CaCO3
73
+ Units.KG_CACO3.value: (CA + _O) / (CA + C + _O*3)
70
74
  },
71
75
  Units.KG_CACO3.value: {
72
- Units.KG_CO2.value: 0.12
76
+ Units.KG_CO2.value: (CA + C + _O*3) / C
73
77
  },
74
- Units.KG_MGCO3.value: {
75
- Units.KG_CO2.value: 0.13
78
+ Units.KG_CAMGCO32.value: {
79
+ Units.KG_CO2.value: (CA + MG + (C + _O*3)*2) / (C*2)
76
80
  },
77
81
  Units.KG_CH4.value: {
78
- Units.TO_C.value: (C + H*4) / C # Conv_Mol_CH4C_CH4
82
+ Units.TO_C.value: (C + H*4) / C
79
83
  },
80
84
  Units.KG_CO2.value: {
81
- Units.TO_C.value: (C + _O*2) / C # Conv_Mol_CO2C_CO2
85
+ Units.TO_C.value: (C + _O*2) / C
82
86
  },
83
87
  Units.KG_NOX.value: {
84
- Units.TO_N.value: (N + _O) / N # Conv_Mol_NON_NO
88
+ Units.TO_N.value: (N + _O) / N
85
89
  },
86
90
  Units.KG_N2.value: {
87
91
  Units.TO_N.value: 1
88
92
  },
89
93
  Units.KG_N2O.value: {
90
- Units.TO_N.value: (N*2 + _O) / (N*2) # Conv_Mol_N2ON_N2O
94
+ Units.TO_N.value: (N*2 + _O) / (N*2)
91
95
  },
92
96
  Units.KG_NO2.value: {
93
- Units.TO_N.value: (N + _O*2) / N # Conv_Mol_NO2N_NO2
97
+ Units.TO_N.value: (N + _O*2) / N
94
98
  },
95
99
  Units.KG_NO3.value: {
96
- Units.TO_N.value: (N + _O*3) / N # Conv_Mol_NO3N_NO3
100
+ Units.TO_N.value: (N + _O*3) / N
97
101
  },
98
102
  Units.KG_NH3.value: {
99
- Units.TO_N.value: (N + H*3) / N # Conv_Mol_NH3N_NH3
103
+ Units.TO_N.value: (N + H*3) / N
100
104
  },
101
105
  Units.KG_NH4.value: {
102
- Units.TO_N.value: (N + H*4) / N # Conv_Mol_NH4N_NH4
106
+ Units.TO_N.value: (N + H*4) / N
103
107
  },
104
108
  Units.KW_H.value: {
105
109
  Units.MJ.value: 3.6
@@ -113,8 +117,10 @@ def get_atomic_conversion(src_unit: Units, dest_unit: Units, default_value=1):
113
117
  return ATOMIC_WEIGHT_CONVERSIONS.get(src_key, {}).get(dest_key, default_value)
114
118
 
115
119
 
116
- def convert_to_unit(node: dict, dest_unit: Units):
117
- return list_sum(node.get('value', [])) * get_atomic_conversion(node.get('term', {}).get('units'), dest_unit)
120
+ def convert_to_unit(node: dict, dest_unit: Units, default_value=None):
121
+ value = list_sum(node.get('value', []), default=None)
122
+ conversion = get_atomic_conversion(node.get('term', {}).get('units'), dest_unit, default_value=None)
123
+ return value / conversion if all([value is not None, conversion is not None]) else default_value
118
124
 
119
125
 
120
126
  def convert_to_N(node: dict):
@@ -3,7 +3,7 @@ from hestia_earth.schema import TermTermType
3
3
  from hestia_earth.utils.model import find_term_match, filter_list_term_type
4
4
  from hestia_earth.utils.tools import list_sum, safe_parse_date
5
5
 
6
- from hestia_earth.models.log import debugValues, log_as_table
6
+ from hestia_earth.models.log import debugValues
7
7
  from .lookup import all_factor_value, _term_factor_value, _aware_factor_value, fallback_country
8
8
  from .product import find_by_product
9
9
  from .site import region_level_1_id
@@ -157,22 +157,22 @@ def impact_country_value(
157
157
  The impact total value.
158
158
  """
159
159
  term_type = TermTermType.RESOURCEUSE.value if 'resourceUse' in lookup else TermTermType.EMISSION.value
160
- nodes = filter_list_term_type(impact.get('emissionsResourceUse', []), term_type)
160
+ blank_nodes = filter_list_term_type(impact.get('emissionsResourceUse', []), term_type)
161
161
 
162
162
  country_id = get_country_id(impact)
163
163
  country_id = fallback_country(country_id, [lookup]) if country_fallback else country_id
164
164
 
165
- values = list(map(_term_factor_value(model, term_id, lookup, country_id, group_key), nodes))
166
- debugValues(impact, model=model, term=term_id,
167
- values_used=log_as_table(values))
168
-
169
- has_values = len(values) > 0
170
- missing_values = set([v.get('id') for v in values if v.get('value') and v.get('coefficient') is None])
171
- all_with_factors = not missing_values
172
- values = [float((v.get('value') or 0) * (v.get('coefficient') or 0)) for v in values]
173
-
174
- # fail if some factors are missing
175
- return None if not all_with_factors else (list_sum(values) if has_values else default_no_values)
165
+ return all_factor_value(
166
+ model=model,
167
+ term_id=term_id,
168
+ node=impact,
169
+ lookup_name=lookup,
170
+ lookup_col=country_id,
171
+ blank_nodes=blank_nodes,
172
+ grouped_key=group_key,
173
+ default_no_values=None,
174
+ factor_value_func=_term_factor_value
175
+ )
176
176
 
177
177
 
178
178
  def impact_aware_value(model: str, term_id: str, impact: dict, lookup: str, group_key: str = None) -> float:
@@ -197,14 +197,23 @@ def impact_aware_value(model: str, term_id: str, impact: dict, lookup: str, grou
197
197
  int
198
198
  The impact total value.
199
199
  """
200
- nodes = impact.get('emissionsResourceUse', [])
200
+ blank_nodes = impact.get('emissionsResourceUse', [])
201
201
  site = get_site(impact)
202
202
  aware_id = site.get('awareWaterBasinId')
203
203
  if aware_id is None:
204
204
  return None
205
- factors = list(map(_aware_factor_value(model, term_id, lookup, aware_id, group_key), nodes))
206
- values = [value for value in factors if value is not None]
207
- return list_sum(values) if len(values) > 0 else None
205
+
206
+ return all_factor_value(
207
+ model=model,
208
+ term_id=term_id,
209
+ node=impact,
210
+ lookup_name=lookup,
211
+ lookup_col=aware_id,
212
+ blank_nodes=blank_nodes,
213
+ grouped_key=group_key,
214
+ default_no_values=None,
215
+ factor_value_func=_aware_factor_value
216
+ )
208
217
 
209
218
 
210
219
  def impact_endpoint_value(model: str, term_id: str, impact: dict, lookup_col: str) -> float: