hestia-earth-models 0.72.2__py3-none-any.whl → 0.73.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.
Files changed (144) hide show
  1. hestia_earth/models/{akagiEtAl2011AndIpcc2006 → akagiEtAl2011}/__init__.py +1 -1
  2. hestia_earth/models/akagiEtAl2011/ch4ToAirCropResidueBurning.py +32 -0
  3. hestia_earth/models/akagiEtAl2011/nh3ToAirCropResidueBurning.py +32 -0
  4. hestia_earth/models/akagiEtAl2011/noxToAirCropResidueBurning.py +32 -0
  5. hestia_earth/models/akagiEtAl2011/pm25ToAirCropResidueBurning.py +32 -0
  6. hestia_earth/models/akagiEtAl2011/so2ToAirCropResidueBurning.py +32 -0
  7. hestia_earth/models/akagiEtAl2011/utils.py +45 -0
  8. hestia_earth/models/aware/scarcityWeightedWaterUse.py +10 -9
  9. hestia_earth/models/cache_sites.py +8 -4
  10. hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsLandTransformation.py +14 -11
  11. hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsTotalLandUseEffects.py +1 -1
  12. hestia_earth/models/chaudharyBrooks2018/utils.py +5 -3
  13. hestia_earth/models/config/Cycle.json +48 -7
  14. hestia_earth/models/config/ImpactAssessment.json +22 -0
  15. hestia_earth/models/cycle/completeness/electricityFuel.py +1 -1
  16. hestia_earth/models/cycle/completeness/freshForage.py +1 -1
  17. hestia_earth/models/cycle/completeness/soilAmendment.py +1 -1
  18. hestia_earth/models/cycle/input/hestiaAggregatedData.py +1 -2
  19. hestia_earth/models/cycle/product/economicValueShare.py +4 -2
  20. hestia_earth/models/cycle/product/price.py +35 -10
  21. hestia_earth/models/cycle/product/revenue.py +5 -2
  22. hestia_earth/models/dammgen2009/noxToAirExcreta.py +14 -18
  23. hestia_earth/models/ecoinventV3/__init__.py +11 -6
  24. hestia_earth/models/ecoinventV3AndEmberClimate/utils.py +1 -1
  25. hestia_earth/models/emepEea2019/utils.py +2 -1
  26. hestia_earth/models/environmentalFootprintV3_1/environmentalFootprintSingleOverallScore.py +1 -1
  27. hestia_earth/models/environmentalFootprintV3_1/soilQualityIndexLandOccupation.py +11 -11
  28. hestia_earth/models/environmentalFootprintV3_1/soilQualityIndexLandTransformation.py +14 -11
  29. hestia_earth/models/faostat2018/liveweightPerHead.py +1 -1
  30. hestia_earth/models/faostat2018/product/price.py +2 -2
  31. hestia_earth/models/faostat2018/seed.py +3 -2
  32. hestia_earth/models/faostat2018/utils.py +32 -18
  33. hestia_earth/models/geospatialDatabase/altitude.py +2 -1
  34. hestia_earth/models/geospatialDatabase/drainageClass.py +2 -1
  35. hestia_earth/models/geospatialDatabase/organicCarbonPerKgSoil.py +2 -1
  36. hestia_earth/models/geospatialDatabase/totalNitrogenPerKgSoil.py +2 -1
  37. hestia_earth/models/geospatialDatabase/totalPhosphorusPerKgSoil.py +2 -1
  38. hestia_earth/models/geospatialDatabase/utils.py +7 -5
  39. hestia_earth/models/globalCropWaterModel2008/rootingDepth.py +1 -1
  40. hestia_earth/models/hestia/brackishWater.py +1 -1
  41. hestia_earth/models/hestia/default_emissions.py +105 -0
  42. hestia_earth/models/hestia/default_resourceUse.py +110 -0
  43. hestia_earth/models/hestia/excretaKgMass.py +7 -9
  44. hestia_earth/models/hestia/freshWater.py +1 -1
  45. hestia_earth/models/hestia/inorganicFertiliser.py +12 -12
  46. hestia_earth/models/hestia/landCover.py +7 -5
  47. hestia_earth/models/hestia/landTransformation100YearAverageDuringCycle.py +3 -0
  48. hestia_earth/models/hestia/landTransformation20YearAverageDuringCycle.py +3 -0
  49. hestia_earth/models/hestia/liveAnimal.py +1 -1
  50. hestia_earth/models/hestia/management.py +1 -1
  51. hestia_earth/models/hestia/netPrimaryProduction.py +1 -1
  52. hestia_earth/models/hestia/organicCarbonPerHa.py +2 -2
  53. hestia_earth/models/hestia/salineWater.py +1 -1
  54. hestia_earth/models/hestia/seed_emissions.py +35 -21
  55. hestia_earth/models/hestia/stockingDensityAnimalHousingAverage.py +1 -1
  56. hestia_earth/models/hestia/totalNitrogenPerKgSoil.py +1 -1
  57. hestia_earth/models/hestia/waterSalinity.py +3 -2
  58. hestia_earth/models/ipcc2006/aboveGroundCropResidueRemoved.py +2 -2
  59. hestia_earth/models/ipcc2006/aboveGroundCropResidueTotal.py +10 -6
  60. hestia_earth/models/ipcc2006/belowGroundCropResidue.py +12 -8
  61. hestia_earth/models/ipcc2019/animal/utils.py +1 -1
  62. hestia_earth/models/ipcc2019/belowGroundCropResidue.py +1 -1
  63. hestia_earth/models/ipcc2019/carbonContent.py +1 -1
  64. hestia_earth/models/ipcc2019/ch4ToAirAquacultureSystems.py +18 -10
  65. hestia_earth/models/ipcc2019/ch4ToAirEntericFermentation.py +6 -6
  66. hestia_earth/models/ipcc2019/ch4ToAirExcreta.py +4 -2
  67. hestia_earth/models/ipcc2019/co2ToAirUreaHydrolysis.py +1 -1
  68. hestia_earth/models/ipcc2019/croppingDuration.py +4 -2
  69. hestia_earth/models/ipcc2019/ligninContent.py +1 -1
  70. hestia_earth/models/{akagiEtAl2011AndIpcc2006 → ipcc2019}/n2OToAirCropResidueBurningDirect.py +8 -4
  71. hestia_earth/models/ipcc2019/nitrogenContent.py +1 -1
  72. hestia_earth/models/ipcc2019/nonCo2EmissionsToAirNaturalVegetationBurning.py +6 -2
  73. hestia_earth/models/ipcc2019/pastureGrass_utils.py +13 -12
  74. hestia_earth/models/ipcc2019/utils.py +6 -2
  75. hestia_earth/models/koble2014/residueBurnt.py +6 -3
  76. hestia_earth/models/koble2014/residueRemoved.py +1 -1
  77. hestia_earth/models/mocking/search-results.json +1614 -1610
  78. hestia_earth/models/pooreNemecek2018/aboveGroundCropResidueTotal.py +1 -1
  79. hestia_earth/models/pooreNemecek2018/belowGroundCropResidue.py +1 -1
  80. hestia_earth/models/pooreNemecek2018/excretaKgVs.py +1 -1
  81. hestia_earth/models/pooreNemecek2018/freshwaterWithdrawalsDuringCycle.py +44 -6
  82. hestia_earth/models/pooreNemecek2018/longFallowDuration.py +1 -1
  83. hestia_earth/models/pooreNemecek2018/nurseryDensity.py +1 -1
  84. hestia_earth/models/pooreNemecek2018/nurseryDuration.py +1 -1
  85. hestia_earth/models/pooreNemecek2018/plantationDensity.py +1 -1
  86. hestia_earth/models/pooreNemecek2018/plantationLifespan.py +1 -1
  87. hestia_earth/models/pooreNemecek2018/plantationProductiveLifespan.py +3 -1
  88. hestia_earth/models/pooreNemecek2018/saplingsDepreciatedAmountPerCycle.py +1 -1
  89. hestia_earth/models/resourceUseNotRelevant/__init__.py +65 -0
  90. hestia_earth/models/schererPfister2015/nErosionSoilFlux.py +5 -3
  91. hestia_earth/models/schererPfister2015/pErosionSoilFlux.py +5 -3
  92. hestia_earth/models/schererPfister2015/utils.py +5 -4
  93. hestia_earth/models/site/grouped_measurement.py +1 -1
  94. hestia_earth/models/stehfestBouwman2006/n2OToAirSoilFlux_utils.py +1 -1
  95. hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirSoilFlux_utils.py +3 -3
  96. hestia_earth/models/utils/background_emissions.py +15 -11
  97. hestia_earth/models/utils/blank_node.py +6 -4
  98. hestia_earth/models/utils/crop.py +1 -1
  99. hestia_earth/models/utils/cropResidue.py +16 -0
  100. hestia_earth/models/utils/cycle.py +1 -1
  101. hestia_earth/models/utils/ecoClimateZone.py +2 -2
  102. hestia_earth/models/utils/excretaManagement.py +1 -1
  103. hestia_earth/models/utils/feedipedia.py +3 -3
  104. hestia_earth/models/utils/fertiliser.py +7 -1
  105. hestia_earth/models/utils/impact_assessment.py +29 -14
  106. hestia_earth/models/utils/inorganicFertiliser.py +2 -2
  107. hestia_earth/models/utils/input.py +34 -1
  108. hestia_earth/models/utils/liveAnimal.py +2 -2
  109. hestia_earth/models/utils/lookup.py +6 -2
  110. hestia_earth/models/utils/measurement.py +4 -4
  111. hestia_earth/models/utils/productivity.py +1 -1
  112. hestia_earth/models/utils/property.py +4 -2
  113. hestia_earth/models/utils/site.py +2 -1
  114. hestia_earth/models/version.py +1 -1
  115. {hestia_earth_models-0.72.2.dist-info → hestia_earth_models-0.73.1.dist-info}/METADATA +1 -1
  116. {hestia_earth_models-0.72.2.dist-info → hestia_earth_models-0.73.1.dist-info}/RECORD +138 -127
  117. tests/models/akagiEtAl2011/test_ch4ToAirCropResidueBurning.py +33 -0
  118. tests/models/akagiEtAl2011/test_nh3ToAirCropResidueBurning.py +33 -0
  119. tests/models/{akagiEtAl2011AndIpcc2006 → akagiEtAl2011}/test_noxToAirCropResidueBurning.py +5 -17
  120. tests/models/akagiEtAl2011/test_pm25ToAirCropResidueBurning.py +33 -0
  121. tests/models/akagiEtAl2011/test_so2ToAirCropResidueBurning.py +33 -0
  122. tests/models/akagiEtAl2011/test_utils.py +18 -0
  123. tests/models/cycle/product/test_price.py +1 -11
  124. tests/models/dammgen2009/test_noxToAirExcreta.py +30 -10
  125. tests/models/geospatialDatabase/test_utils.py +2 -1
  126. tests/models/hestia/test_default_emissions.py +25 -0
  127. tests/models/hestia/test_default_resourceUse.py +26 -0
  128. tests/models/hestia/test_landCover.py +2 -2
  129. tests/models/ipcc2019/test_ch4ToAirAquacultureSystems.py +2 -2
  130. tests/models/{akagiEtAl2011AndIpcc2006/test_nh3ToAirCropResidueBurning.py → ipcc2019/test_n2OToAirCropResidueBurningDirect.py} +2 -2
  131. tests/models/pooreNemecek2018/test_freshwaterWithdrawalsDuringCycle.py +12 -0
  132. tests/models/test_resourceUseNotRelevant.py +27 -0
  133. tests/models/{akagiEtAl2011AndIpcc2006/test_utils.py → utils/test_cropResidue.py} +6 -6
  134. tests/models/utils/test_impact_assessment.py +29 -13
  135. hestia_earth/models/akagiEtAl2011AndIpcc2006/ch4ToAirCropResidueBurning.py +0 -57
  136. hestia_earth/models/akagiEtAl2011AndIpcc2006/nh3ToAirCropResidueBurning.py +0 -57
  137. hestia_earth/models/akagiEtAl2011AndIpcc2006/noxToAirCropResidueBurning.py +0 -57
  138. hestia_earth/models/akagiEtAl2011AndIpcc2006/utils.py +0 -15
  139. tests/models/akagiEtAl2011AndIpcc2006/test_ch4ToAirCropResidueBurning.py +0 -45
  140. tests/models/akagiEtAl2011AndIpcc2006/test_n2OToAirCropResidueBurningDirect.py +0 -46
  141. {hestia_earth_models-0.72.2.dist-info → hestia_earth_models-0.73.1.dist-info}/LICENSE +0 -0
  142. {hestia_earth_models-0.72.2.dist-info → hestia_earth_models-0.73.1.dist-info}/WHEEL +0 -0
  143. {hestia_earth_models-0.72.2.dist-info → hestia_earth_models-0.73.1.dist-info}/top_level.txt +0 -0
  144. /tests/models/{akagiEtAl2011AndIpcc2006 → akagiEtAl2011}/__init__.py +0 -0
@@ -9,9 +9,12 @@ REQUIREMENTS = {
9
9
  "Cycle": {
10
10
  "products": [{
11
11
  "@type": "Product",
12
- "price": "",
13
12
  "optional": {
14
- "value": ""
13
+ "value": "",
14
+ "price": ""
15
+ },
16
+ "none": {
17
+ "revenue": ""
15
18
  }
16
19
  }]
17
20
  }
@@ -1,22 +1,19 @@
1
1
  from hestia_earth.schema import EmissionMethodTier
2
+ from hestia_earth.utils.model import find_term_match
3
+ from hestia_earth.utils.tools import list_sum
2
4
 
3
5
  from hestia_earth.models.log import logRequirements, logShouldRun
4
6
  from hestia_earth.models.utils.constant import Units, get_atomic_conversion
5
7
  from hestia_earth.models.utils.emission import _new_emission
6
- from hestia_earth.models.utils.input import total_excreta
7
- from hestia_earth.models.utils.excretaManagement import get_lookup_factor
8
8
  from . import MODEL
9
9
 
10
10
  REQUIREMENTS = {
11
11
  "Cycle": {
12
- "practices": [
13
- {"@type": "Practice", "value": "", "term.termType": "excretaManagement"}
12
+ "emissions": [
13
+ {"@type": "Emission", "value": "", "term.@id": "n2OToAirExcretaDirect"}
14
14
  ]
15
15
  }
16
16
  }
17
- LOOKUPS = {
18
- "excretaManagement": "EF_NO-N"
19
- }
20
17
  RETURNS = {
21
18
  "Emission": [{
22
19
  "value": "",
@@ -25,6 +22,7 @@ RETURNS = {
25
22
  }
26
23
  TERM_ID = 'noxToAirExcreta'
27
24
  TIER = EmissionMethodTier.TIER_1.value
25
+ N2O_TERM_ID = 'n2OToAirExcretaDirect'
28
26
 
29
27
 
30
28
  def _emission(value: float):
@@ -34,24 +32,22 @@ def _emission(value: float):
34
32
  return emission
35
33
 
36
34
 
37
- def _run(excretaKgN: float, NO_N_EF: float):
38
- value = (NO_N_EF or 0) * (excretaKgN or 0) * get_atomic_conversion(Units.KG_NOX, Units.TO_N)
35
+ def _run(n2o: dict):
36
+ value = 0.1 * list_sum(n2o.get("value", [])) / get_atomic_conversion(Units.KG_N2O, Units.TO_N)
37
+ value = value * get_atomic_conversion(Units.KG_NOX, Units.TO_N)
39
38
  return [_emission(value)]
40
39
 
41
40
 
42
41
  def _should_run(cycle: dict):
43
- excretaKgN = total_excreta(cycle.get('inputs', []))
44
- NO_N_EF = get_lookup_factor(cycle.get('practices', []), LOOKUPS['excretaManagement'])
42
+ n2o = find_term_match(cycle.get('emissions', []), N2O_TERM_ID)
45
43
 
46
- logRequirements(cycle, model=MODEL, term=TERM_ID,
47
- excretaKgN=excretaKgN,
48
- NO_N_EF=NO_N_EF)
44
+ logRequirements(cycle, model=MODEL, term=TERM_ID, has_n2o=n2o is not None)
49
45
 
50
- should_run = all([excretaKgN is not None, NO_N_EF is not None])
46
+ should_run = all([n2o])
51
47
  logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
52
- return should_run, excretaKgN, NO_N_EF
48
+ return should_run, n2o
53
49
 
54
50
 
55
51
  def run(cycle: dict):
56
- should_run, excretaKgN, NO_N_EF = _should_run(cycle)
57
- return _run(excretaKgN, NO_N_EF) if should_run else []
52
+ should_run, n2o = _should_run(cycle)
53
+ return _run(n2o) if should_run else []
@@ -79,7 +79,7 @@ def _emission(term_id: str, value: float, input: dict):
79
79
  return emission
80
80
 
81
81
 
82
- def _add_emission(cycle: dict, input: dict):
82
+ def _add_emission(cycle: dict, input: dict, **extra_logs):
83
83
  input_term_id = input.get('term', {}).get('@id')
84
84
  operation_term_id = input.get('operation', {}).get('@id')
85
85
  animal_term_id = input.get('animal', {}).get('@id')
@@ -89,7 +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
+ logShouldRun(cycle, MODEL, input_term_id, True, methodTier=TIER, emission_id=emission_term_id, **extra_logs)
93
93
  debugValues(cycle, model=MODEL, term=emission_term_id,
94
94
  value=value,
95
95
  coefficient=coefficient,
@@ -112,6 +112,10 @@ def _run_input(cycle: dict):
112
112
  mappings = get_input_mappings(MODEL, cycle, input)
113
113
  has_mappings = len(mappings) > 0
114
114
 
115
+ # grouping the inputs together in the logs
116
+ input_parent_term_id = input.get('parent', {}).get('@id')
117
+ extra_logs = {'input_group_id': input_parent_term_id} if input_parent_term_id else {}
118
+
115
119
  # skip input that has background emissions we have already gap-filled (model run before)
116
120
  has_no_gap_filled_background_emissions = no_gap_filled_background_emissions_func(input)
117
121
 
@@ -119,13 +123,14 @@ def _run_input(cycle: dict):
119
123
  has_ecoinvent_mappings=has_mappings,
120
124
  ecoinvent_mappings=';'.join([v[0] for v in mappings]),
121
125
  has_no_gap_filled_background_emissions=has_no_gap_filled_background_emissions,
122
- input_value=input_value)
126
+ input_value=input_value,
127
+ **extra_logs)
123
128
 
124
129
  should_run = all([has_mappings, has_no_gap_filled_background_emissions, input_value])
125
- logShouldRun(cycle, MODEL, input_term_id, should_run, methodTier=TIER)
130
+ logShouldRun(cycle, MODEL, input_term_id, should_run, methodTier=TIER, **extra_logs)
126
131
 
127
- grouped_emissions = reduce(_add_emission(cycle, input), mappings, {}) if should_run else {}
128
- log_missing_emissions_func(input_term_id, list(grouped_emissions.keys()))
132
+ grouped_emissions = reduce(_add_emission(cycle, input, **extra_logs), mappings, {}) if should_run else {}
133
+ log_missing_emissions_func(input_term_id, list(grouped_emissions.keys()), **extra_logs)
129
134
  return [
130
135
  _emission(term_id, value * input_value, input)
131
136
  for term_id, value in grouped_emissions.items()
@@ -19,4 +19,4 @@ def get_input_coefficient(model: str, cycle: dict, country_id: str, ecoinventNam
19
19
  # find the ratio for the country / year
20
20
  data = get_region_lookup_value(REGION_EMBER_SOURCES_LOOKUP_NAME, country_id, source_name, model=model)
21
21
  percentage = extract_grouped_data(data, str(year))
22
- return safe_parse_float(percentage, 0) / 100
22
+ return safe_parse_float(percentage, default=0) / 100
@@ -95,7 +95,8 @@ def group_fuel_inputs(inputs: list):
95
95
 
96
96
  def _get_emissions_factor(animal: dict, lookup_col: str) -> float:
97
97
  return safe_parse_float(
98
- get_lookup_value(animal.get("term", {}), lookup_col, model=MODEL, term=animal.get("term", ""))
98
+ get_lookup_value(animal.get("term", {}), lookup_col, model=MODEL, term=animal.get("term", "")),
99
+ default=None
99
100
  )
100
101
 
101
102
 
@@ -4,7 +4,7 @@ from hestia_earth.utils.tools import list_sum
4
4
  from typing import List, Optional, Tuple
5
5
 
6
6
  from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table, debugValues
7
- from hestia_earth.models.utils.blank_node import get_lookup_value
7
+ from hestia_earth.models.utils.term import get_lookup_value
8
8
  from hestia_earth.models.utils.indicator import _new_indicator
9
9
  from hestia_earth.models.utils.lookup import _node_value
10
10
  from . import MODEL
@@ -60,17 +60,17 @@ def _should_run(impact_assessment: dict) -> Tuple[bool, list]:
60
60
  ]
61
61
 
62
62
  found_land_occupation_indicators = [{
63
- 'area-by-year': _node_value(land_occupation_indicator),
64
- 'area-unit': land_occupation_indicator.get('term', {}).get("units"),
65
- 'land-cover-id': land_occupation_indicator.get('landCover', {}).get("@id"),
66
- 'country-id': get_country_id(impact_assessment),
67
- 'area-by-year-is-valid': _node_value(land_occupation_indicator) is not None and _node_value(
68
- land_occupation_indicator) > 0,
69
- 'area-unit-is-valid': land_occupation_indicator.get('term', {}).get("units") == "m2*year",
70
- 'used-country': fallback_country(get_country_id(impact_assessment), [LOOKUP]),
71
- 'pef-grouping': get_pef_grouping(land_occupation_indicator.get('landCover', {}).get("@id"))
72
-
73
- } for land_occupation_indicator in land_occupation_indicators]
63
+ 'area-by-year': _node_value(indicator),
64
+ 'area-unit': indicator.get('term', {}).get("units"),
65
+ 'land-cover-id': indicator.get('landCover', {}).get("@id"),
66
+ 'country-id': get_country_id(impact_assessment, blank_node=indicator),
67
+ 'area-by-year-is-valid': _node_value(indicator) is not None and _node_value(
68
+ indicator) > 0,
69
+ 'area-unit-is-valid': indicator.get('term', {}).get("units") == "m2*year",
70
+ 'used-country': fallback_country(get_country_id(impact_assessment, blank_node=indicator), [LOOKUP]),
71
+ 'pef-grouping': get_pef_grouping(indicator.get('landCover', {}).get("@id"))
72
+
73
+ } for indicator in land_occupation_indicators]
74
74
 
75
75
  found_indicators_with_coefficient = [
76
76
  indicator | {
@@ -92,21 +92,24 @@ def _should_run(impact_assessment: dict) -> Tuple[bool, list]:
92
92
 
93
93
  found_transformations = [
94
94
  {
95
- 'value': _node_value(transformation_indicator),
96
- 'land-cover-id-from': transformation_indicator.get('previousLandCover', {}).get("@id"),
97
- 'land-cover-id-to': transformation_indicator.get('landCover', {}).get("@id"),
98
- 'indicator-id': transformation_indicator.get('term', {}).get('@id', ''),
95
+ 'value': _node_value(indicator),
96
+ 'land-cover-id-from': indicator.get('previousLandCover', {}).get("@id"),
97
+ 'land-cover-id-to': indicator.get('landCover', {}).get("@id"),
98
+ 'indicator-id': indicator.get('term', {}).get('@id', ''),
99
99
  'good-land-cover-term': all([
100
- bool(transformation_indicator.get('landCover')),
101
- bool(transformation_indicator.get('previousLandCover'))
100
+ bool(indicator.get('landCover')),
101
+ bool(indicator.get('previousLandCover'))
102
102
  ]),
103
- 'country-id': get_country_id(impact_assessment),
103
+ 'country-id': get_country_id(impact_assessment, blank_node=indicator),
104
104
  'value-is-valid': (
105
- _node_value(transformation_indicator) is not None and
106
- _node_value(transformation_indicator) >= 0
105
+ _node_value(indicator) is not None and
106
+ _node_value(indicator) >= 0
107
107
  ),
108
- 'lookup-country': fallback_country(get_country_id(impact_assessment), [from_lookup_file, to_lookup_file]),
109
- } for transformation_indicator in resource_uses
108
+ 'lookup-country': fallback_country(
109
+ get_country_id(impact_assessment, blank_node=indicator),
110
+ [from_lookup_file, to_lookup_file]
111
+ ),
112
+ } for indicator in resource_uses
110
113
  ]
111
114
 
112
115
  found_transformations_with_coefficient = [
@@ -55,7 +55,7 @@ def _product_value(product: dict, year: int, country_id: str):
55
55
  groupingFAO = get_animalProduct_lookup_value(MODEL, product_id, FAO_LOOKUP_COLUMN)
56
56
 
57
57
  data = get_region_lookup_value(LOOKUP_WEIGHT, country_id, groupingFAO, model=MODEL, term=TERM_ID)
58
- average_carcass_weight = safe_parse_float(extract_grouped_data_closest_date(data, year), None)
58
+ average_carcass_weight = safe_parse_float(extract_grouped_data_closest_date(data, year), default=None)
59
59
  # average_carcass_weight is in hg, divide by 10 to go back to kg
60
60
  kg_carcass_weight = average_carcass_weight / 10 if average_carcass_weight else None
61
61
 
@@ -65,7 +65,7 @@ def _lookup_data(
65
65
  def get_data(country_id):
66
66
  data = get_region_lookup_value(lookup_name, country_id, grouping, model=MODEL, term=term_id, key=MODEL_KEY)
67
67
  price = extract_grouped_data(data, str(year)) or extract_grouped_data(data, 'Average_price_per_tonne')
68
- return safe_parse_float(price, None)
68
+ return safe_parse_float(price, default=None)
69
69
 
70
70
  # try get country data first, falls back to region data
71
71
  country_id = country.get('@id')
@@ -141,7 +141,7 @@ def _run_by_country(cycle: dict, product: dict, country: dict, year: int = None)
141
141
 
142
142
  # if units is number instead of kg, need to convert to number first
143
143
  conversion_to_number = safe_parse_float(
144
- get_region_lookup_value(LOOKUP_UNITS_NUMBER.get(term_type), country.get('@id'), grouping), 1
144
+ get_region_lookup_value(LOOKUP_UNITS_NUMBER.get(term_type), country.get('@id'), grouping), default=1
145
145
  ) if term_units == Units.NUMBER.value else 1
146
146
 
147
147
  logRequirements(cycle, model=MODEL, term=term_id, key=MODEL_KEY, by='country',
@@ -43,8 +43,9 @@ def _input(value: float, sd: float):
43
43
  def _run_product(product: dict):
44
44
  term = product.get('term', {})
45
45
  product_value = list_sum(product.get('value', []))
46
- value = safe_parse_float(get_lookup_value(term, LOOKUPS['crop'][0], model=MODEL, term=TERM_ID)) * product_value
47
- sd = safe_parse_float(get_lookup_value(term, LOOKUPS['crop'][1], model=MODEL, term=TERM_ID))
46
+ value = safe_parse_float(
47
+ get_lookup_value(term, LOOKUPS['crop'][0], model=MODEL, term=TERM_ID), default=0) * product_value
48
+ sd = safe_parse_float(get_lookup_value(term, LOOKUPS['crop'][1], model=MODEL, term=TERM_ID), default=0)
48
49
  return value, sd
49
50
 
50
51
 
@@ -1,9 +1,9 @@
1
1
  from hestia_earth.schema import TermTermType
2
2
  from hestia_earth.utils.api import download_hestia
3
3
  from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name, extract_grouped_data_closest_date
4
- from hestia_earth.utils.tools import safe_parse_float, flatten
4
+ from hestia_earth.utils.tools import safe_parse_float, flatten, non_empty_list
5
5
 
6
- from hestia_earth.models.log import logger, debugMissingLookup, logRequirements, logShouldRun, debugValues
6
+ from hestia_earth.models.log import logger, debugMissingLookup, logRequirements, logShouldRun, debugValues, log_as_table
7
7
  from hestia_earth.models.utils.animalProduct import (
8
8
  FAO_LOOKUP_COLUMN, FAO_EQUIVALENT_LOOKUP_COLUMN, get_animalProduct_lookup_value
9
9
  )
@@ -34,10 +34,10 @@ def product_equivalent_value(product: dict, year: int, country: str):
34
34
  return None
35
35
 
36
36
  quantity_values = get_region_lookup_value(f"{LOOKUP_PREFIX}-productionQuantity.csv", country, grouping)
37
- quantity = safe_parse_float(extract_grouped_data_closest_date(quantity_values, year))
37
+ quantity = safe_parse_float(extract_grouped_data_closest_date(quantity_values, year), default=0)
38
38
 
39
39
  head_values = get_region_lookup_value(f"{LOOKUP_PREFIX}-head.csv", country, grouping)
40
- head = safe_parse_float(extract_grouped_data_closest_date(head_values, year))
40
+ head = safe_parse_float(extract_grouped_data_closest_date(head_values, year), default=0)
41
41
 
42
42
  # quantity is in Tonnes
43
43
  value = quantity * 1000 / head if head > 0 else 0
@@ -58,7 +58,7 @@ def product_equivalent_value(product: dict, year: int, country: str):
58
58
  def _split_delta(table_value: str, start_year: int, end_year: int):
59
59
  start_value = extract_grouped_data_closest_date(table_value, start_year)
60
60
  end_value = extract_grouped_data_closest_date(table_value, end_year)
61
- return safe_parse_float(end_value) - safe_parse_float(start_value) if all([
61
+ return safe_parse_float(end_value, default=None) - safe_parse_float(start_value, default=None) if all([
62
62
  start_value is not None, end_value is not None
63
63
  ]) else None
64
64
 
@@ -69,7 +69,8 @@ def get_sum_of_columns(country: str, year: int, columns_list: list) -> float:
69
69
  extract_grouped_data_closest_date(
70
70
  data=get_region_lookup_value(FAOSTAT_AREA_LOOKUP, country, col, model=MODEL),
71
71
  year=year
72
- )
72
+ ),
73
+ default=0
73
74
  ) for col in columns_list]
74
75
  )
75
76
 
@@ -128,7 +129,7 @@ def get_change_in_harvested_area_for_crop(country_id: str, crop_name: str, start
128
129
  lookup_name = 'region-crop-cropGroupingFaostatProduction-areaHarvested.csv'
129
130
  value = get_region_lookup_value(lookup_name, country_id, crop_name)
130
131
  return safe_parse_float(
131
- extract_grouped_data_closest_date(value, start_year)
132
+ extract_grouped_data_closest_date(value, start_year), default=0
132
133
  ) if end_year == 0 or end_year == start_year else _split_delta(value, start_year, end_year)
133
134
 
134
135
 
@@ -151,24 +152,37 @@ def should_run_landTransformationFromCropland(term_id: str, impact: dict):
151
152
  return should_run, indicators
152
153
 
153
154
 
155
+ def _map_indicator_value(impact: dict, start_year: int, end_year: int):
156
+ def mapper(indicator: dict):
157
+ country_id = get_country_id(impact, blank_node=indicator)
158
+ total, permanent, temporary = get_cropland_ratio(country_id, start_year, end_year)
159
+ return {
160
+ 'landCover-id': indicator.get('landCover', {}).get('@id'),
161
+ 'value': indicator.get('value'),
162
+ 'country-id': country_id,
163
+ 'diff-total-area': total,
164
+ 'diff-temporary-area': temporary,
165
+ 'diff-permanent-area': permanent
166
+ } if total is not None else None
167
+ return mapper
168
+
169
+
154
170
  def run_landTransformationFromCropland(term_id: str, impact: dict, indicators: list, years: int):
155
- country_id = get_country_id(impact)
156
171
  end_year = impact_end_year(impact)
157
- total, permanent, temporary = get_cropland_ratio(country_id, end_year - years, end_year)
172
+
173
+ values = non_empty_list(map(_map_indicator_value(impact, end_year - years, end_year), indicators))
158
174
 
159
175
  debugValues(impact, model=MODEL, term_id=term_id,
160
- diff_temporary_area=temporary,
161
- diff_permanent_area=permanent,
162
- diff_total_area=total)
176
+ indicators_used=log_as_table(values))
163
177
 
164
178
  return flatten([
165
179
  [
166
- _new_indicator(term_id, MODEL, indicator.get('landCover', {}).get('@id'), 'annualCropland') | {
167
- 'value': indicator.get('value') * temporary / total
180
+ _new_indicator(term_id, MODEL, value.get('landCover-id'), 'annualCropland') | {
181
+ 'value': value.get('value') * value.get('diff-temporary-area') / value.get('diff-total-area')
168
182
  },
169
- _new_indicator(term_id, MODEL, indicator.get('landCover', {}).get('@id'), 'permanentCropland') | {
170
- 'value': indicator.get('value') * permanent / total
183
+ _new_indicator(term_id, MODEL, value.get('landCover-id'), 'permanentCropland') | {
184
+ 'value': value.get('value') * value.get('diff-permanent-area') / value.get('diff-total-area')
171
185
  }
172
186
  ]
173
- for indicator in indicators
174
- ]) if total is not None else []
187
+ for value in values
188
+ ])
@@ -42,7 +42,8 @@ def _measurement(site: dict, value: float):
42
42
 
43
43
  def _run(site: dict):
44
44
  value = download(TERM_ID, site, EE_PARAMS)
45
- return [_measurement(site, safe_parse_float(value))] if value is not None else []
45
+ value = safe_parse_float(value, default=None)
46
+ return [_measurement(site, value)] if value is not None else []
46
47
 
47
48
 
48
49
  def _should_run(site: dict):
@@ -40,7 +40,8 @@ def _measurement(site: dict, value: float):
40
40
 
41
41
  def _run(site: dict):
42
42
  value = download(TERM_ID, site, EE_PARAMS)
43
- return [_measurement(site, safe_parse_float(value))] if value is not None else []
43
+ value = safe_parse_float(value, default=None)
44
+ return [_measurement(site, value)] if value is not None else []
44
45
 
45
46
 
46
47
  def _should_run(site: dict):
@@ -69,8 +69,9 @@ def _measurement(site: dict, value: int, depthUpper: int, depthLower: int):
69
69
 
70
70
  def _run_depths(site: dict, params: dict):
71
71
  value = download(TERM_ID, site, params)
72
+ value = safe_parse_float(value, default=None)
72
73
  return None if value is None else (
73
- _measurement(site, safe_parse_float(value) * 10, params.get('depthUpper'), params.get('depthLower'))
74
+ _measurement(site, value * 10, params.get('depthUpper'), params.get('depthLower'))
74
75
  )
75
76
 
76
77
 
@@ -69,8 +69,9 @@ def _measurement(site: dict, value: int, depthUpper: int, depthLower: int):
69
69
 
70
70
  def _run_depths(site: dict, params: dict):
71
71
  value = download(TERM_ID, site, params)
72
+ value = safe_parse_float(value, default=None)
72
73
  return None if value is None else (
73
- _measurement(site, safe_parse_float(value), params.get('depthUpper'), params.get('depthLower'))
74
+ _measurement(site, value, params.get('depthUpper'), params.get('depthLower'))
74
75
  )
75
76
 
76
77
 
@@ -44,7 +44,8 @@ def _measurement(site: dict, value: float):
44
44
 
45
45
  def _run(site: dict):
46
46
  value = download(TERM_ID, site, EE_PARAMS)
47
- return [_measurement(site, safe_parse_float(value))] if value is not None else []
47
+ value = safe_parse_float(value, default=None)
48
+ return [_measurement(site, value)] if value is not None else []
48
49
 
49
50
 
50
51
  def _should_run(site: dict):
@@ -101,17 +101,19 @@ def _get_region_area_size(site: dict):
101
101
 
102
102
 
103
103
  def get_area_size(site: dict):
104
- return None if has_coordinates(site) else (
105
- # fallback if `boundary` provided but no `boundaryArea` was computed
106
- site.get('boundaryArea') or _get_boundary_area_size(site.get('boundary'))
107
- ) if has_boundary(site) else _get_region_area_size(site)
104
+ return _cached_value(site, CACHE_AREA_SIZE) or (
105
+ None if has_coordinates(site) else (
106
+ # fallback if `boundary` provided but no `boundaryArea` was computed
107
+ site.get('boundaryArea') or _get_boundary_area_size(site.get('boundary'))
108
+ ) if has_boundary(site) else _get_region_area_size(site)
109
+ )
108
110
 
109
111
 
110
112
  def _is_below_max_size(term: str, site: dict) -> bool:
111
113
  current_size = _cached_value(site, CACHE_AREA_SIZE) or get_area_size(site)
112
114
  if current_size is not None:
113
115
  logRequirements(site, model=MODEL, term=term,
114
- current_size=int(current_size),
116
+ current_size=round(float(current_size), 5),
115
117
  max_area_size=MAX_AREA_SIZE)
116
118
  return current_size <= MAX_AREA_SIZE
117
119
  return True
@@ -60,7 +60,7 @@ def _get_value(cycle: dict, term: dict, irrigation_ids: list):
60
60
  else:
61
61
  column = 'Rooting_depth_average_m'
62
62
 
63
- return safe_parse_float(get_crop_lookup_value(MODEL, TERM_ID, term_id, column), None)
63
+ return safe_parse_float(get_crop_lookup_value(MODEL, TERM_ID, term_id, column), default=None)
64
64
 
65
65
 
66
66
  def _should_run_product(cycle: dict):
@@ -34,7 +34,7 @@ def _should_run(site: dict):
34
34
  logRequirements(site, model=MODEL, term=TERM_ID,
35
35
  waterSalinity=waterSalinity)
36
36
 
37
- should_run = all([500 <= waterSalinity <= 18000])
37
+ should_run = all([500 <= (waterSalinity or 0) <= 18000])
38
38
  logShouldRun(site, MODEL, TERM_ID, should_run)
39
39
  return should_run
40
40
 
@@ -0,0 +1,105 @@
1
+ from hestia_earth.schema import EmissionMethodTier
2
+ from hestia_earth.utils.tools import flatten, safe_parse_float
3
+ from hestia_earth.utils.emission import cycle_emissions_in_system_boundary
4
+
5
+ from hestia_earth.models.log import logRequirements, logShouldRun
6
+ from hestia_earth.models.utils import _omit
7
+ from hestia_earth.models.utils.emission import _new_emission
8
+ from hestia_earth.models.utils.background_emissions import no_gap_filled_background_emissions
9
+ from hestia_earth.models.utils.term import get_lookup_value
10
+ from hestia_earth.models.utils.input import unique_background_inputs
11
+ from . import MODEL
12
+
13
+ REQUIREMENTS = {
14
+ "Cycle": {
15
+ "inputs": [{
16
+ "@type": "Input",
17
+ "value": "> 0",
18
+ "none": {
19
+ "impactAssessment": {"@type": "ImpactAssessment"},
20
+ "fromCycle": "True",
21
+ "producedInCycle": "True"
22
+ }
23
+ }]
24
+ }
25
+ }
26
+ RETURNS = {
27
+ "Emission": [{
28
+ "value": "",
29
+ "inputs": "",
30
+ "methodTier": "background"
31
+ }]
32
+ }
33
+ LOOKUPS = {
34
+ "organicFertiliser": "backgroundEmissionsResourceUseDefaultValue"
35
+ }
36
+ MODEL_KEY = 'default_emissions'
37
+ TIER = EmissionMethodTier.BACKGROUND.value
38
+
39
+
40
+ def _emission(term_id: str, value: float, input: dict):
41
+ emission = _new_emission(term_id, MODEL)
42
+ emission['value'] = [value]
43
+ emission['inputs'] = [input]
44
+ emission['methodTier'] = TIER
45
+ return emission
46
+
47
+
48
+ def _default_value(input: dict):
49
+ return safe_parse_float(get_lookup_value(input.get('term', {}), LOOKUPS['organicFertiliser']), default=None)
50
+
51
+
52
+ def _run_input(cycle: dict):
53
+ required_emission_term_ids = [
54
+ id for id in cycle_emissions_in_system_boundary(cycle)
55
+ if id.endswith('InputsProduction')
56
+ ]
57
+
58
+ def run(input: dict):
59
+ input_term = input.get('input').get('term')
60
+ term_id = input_term.get('@id')
61
+ value = input.get('default-value-from-lookup')
62
+
63
+ for emission_id in required_emission_term_ids:
64
+ logShouldRun(cycle, MODEL, term_id, True, methodTier=TIER, model_key=MODEL_KEY, emission_id=emission_id)
65
+
66
+ return [
67
+ _emission(term_id, value, input_term) for term_id in required_emission_term_ids
68
+ ]
69
+
70
+ return run
71
+
72
+
73
+ def _should_run(cycle: dict):
74
+ no_gap_filled_background_emissions_func = no_gap_filled_background_emissions(cycle)
75
+
76
+ inputs = [
77
+ input | {
78
+ 'default-value-from-lookup': _default_value(input['input']),
79
+ 'no-gap-filled-background-emissions': no_gap_filled_background_emissions_func(input['input'])
80
+ }
81
+ for input in unique_background_inputs(cycle)
82
+ ]
83
+ valid_inputs = [
84
+ input for input in inputs
85
+ if all([
86
+ input.get('default-value-from-lookup') is not None,
87
+ input.get('no-gap-filled-background-emissions')
88
+ ])
89
+ ]
90
+
91
+ should_run = all([bool(valid_inputs)])
92
+
93
+ for input in inputs:
94
+ term_id = input.get('input').get('term', {}).get('@id')
95
+
96
+ logRequirements(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
97
+ **_omit(input, ['input', 'input-value']))
98
+ logShouldRun(cycle, MODEL, term_id, should_run, methodTier=TIER, model_key=MODEL_KEY)
99
+
100
+ return should_run, valid_inputs
101
+
102
+
103
+ def run(cycle: dict):
104
+ should_run, grouped_inputs = _should_run(cycle)
105
+ return flatten(map(_run_input(cycle), grouped_inputs)) if should_run else []