hestia-earth-models 0.67.1__py3-none-any.whl → 0.68.0__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 (147) hide show
  1. hestia_earth/models/aware/scarcityWeightedWaterUse.py +5 -6
  2. hestia_earth/models/blonkConsultants2016/ch4ToAirNaturalVegetationBurning.py +1 -1
  3. hestia_earth/models/blonkConsultants2016/co2ToAirAboveGroundBiomassStockChangeLandUseChange.py +1 -1
  4. hestia_earth/models/blonkConsultants2016/n2OToAirNaturalVegetationBurningDirect.py +1 -1
  5. hestia_earth/models/blonkConsultants2016/utils.py +9 -9
  6. hestia_earth/models/cache_sites.py +26 -14
  7. hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsLandOccupation.py +2 -2
  8. hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsLandTransformation.py +2 -2
  9. hestia_earth/models/chaudharyBrooks2018/utils.py +13 -8
  10. hestia_earth/models/cml2001Baseline/abioticResourceDepletionFossilFuels.py +2 -3
  11. hestia_earth/models/cml2001Baseline/abioticResourceDepletionMineralsAndMetals.py +1 -1
  12. hestia_earth/models/config/Cycle.json +15 -0
  13. hestia_earth/models/config/ImpactAssessment.json +14 -1
  14. hestia_earth/models/config/Site.json +8 -0
  15. hestia_earth/models/cycle/excretaKgMass.py +2 -2
  16. hestia_earth/models/cycle/materialAndSubstrate.py +3 -2
  17. hestia_earth/models/cycle/pastureGrass.py +3 -3
  18. hestia_earth/models/dammgen2009/noxToAirExcreta.py +1 -1
  19. hestia_earth/models/ecoinventV3AndEmberClimate/__init__.py +1 -1
  20. hestia_earth/models/ecoinventV3AndEmberClimate/utils.py +2 -6
  21. hestia_earth/models/emissionNotRelevant/__init__.py +4 -4
  22. hestia_earth/models/environmentalFootprintV3_1/environmentalFootprintSingleOverallScore.py +30 -21
  23. hestia_earth/models/environmentalFootprintV3_1/photochemicalOzoneCreationPotentialHumanHealthNmvocEq.py +36 -0
  24. hestia_earth/models/environmentalFootprintV3_1/scarcityWeightedWaterUse.py +2 -2
  25. hestia_earth/models/environmentalFootprintV3_1/soilQualityIndexLandOccupation.py +9 -8
  26. hestia_earth/models/environmentalFootprintV3_1/soilQualityIndexLandTransformation.py +25 -22
  27. hestia_earth/models/environmentalFootprintV3_1/soilQualityIndexTotalLandUseEffects.py +7 -6
  28. hestia_earth/models/faostat2018/coldCarcassWeightPerHead.py +2 -2
  29. hestia_earth/models/faostat2018/coldDressedCarcassWeightPerHead.py +2 -2
  30. hestia_earth/models/faostat2018/liveweightPerHead.py +7 -8
  31. hestia_earth/models/faostat2018/product/price.py +34 -28
  32. hestia_earth/models/faostat2018/readyToCookWeightPerHead.py +2 -2
  33. hestia_earth/models/faostat2018/utils.py +15 -27
  34. hestia_earth/models/frischknechtEtAl2000/ionisingRadiationKbqU235Eq.py +16 -9
  35. hestia_earth/models/geospatialDatabase/altitude.py +60 -0
  36. hestia_earth/models/geospatialDatabase/croppingIntensity.py +1 -1
  37. hestia_earth/models/geospatialDatabase/ecoClimateZone.py +2 -2
  38. hestia_earth/models/geospatialDatabase/longFallowRatio.py +1 -1
  39. hestia_earth/models/geospatialDatabase/utils.py +4 -1
  40. hestia_earth/models/globalCropWaterModel2008/rootingDepth.py +2 -3
  41. hestia_earth/models/haversineFormula/transport/distance.py +3 -3
  42. hestia_earth/models/hestia/landCover.py +72 -45
  43. hestia_earth/models/hestia/seed_emissions.py +11 -7
  44. hestia_earth/models/impact_assessment/__init__.py +3 -3
  45. hestia_earth/models/ipcc2019/animal/fatContent.py +1 -1
  46. hestia_earth/models/ipcc2019/animal/hoursWorkedPerDay.py +1 -1
  47. hestia_earth/models/ipcc2019/animal/liveweightGain.py +1 -1
  48. hestia_earth/models/ipcc2019/animal/liveweightPerHead.py +1 -1
  49. hestia_earth/models/ipcc2019/animal/milkYieldPerAnimal.py +1 -1
  50. hestia_earth/models/ipcc2019/animal/pastureGrass.py +1 -1
  51. hestia_earth/models/ipcc2019/animal/pregnancyRateTotal.py +1 -1
  52. hestia_earth/models/ipcc2019/animal/trueProteinContent.py +1 -1
  53. hestia_earth/models/ipcc2019/animal/utils.py +5 -7
  54. hestia_earth/models/ipcc2019/animal/weightAtMaturity.py +1 -1
  55. hestia_earth/models/ipcc2019/ch4ToAirEntericFermentation.py +2 -2
  56. hestia_earth/models/ipcc2019/ch4ToAirExcreta.py +6 -7
  57. hestia_earth/models/ipcc2019/ch4ToAirFloodedRice.py +5 -3
  58. hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +1 -1
  59. hestia_earth/models/ipcc2019/croppingDuration.py +3 -6
  60. hestia_earth/models/ipcc2019/nonCo2EmissionsToAirNaturalVegetationBurning.py +947 -0
  61. hestia_earth/models/ipcc2019/pastureGrass.py +1 -1
  62. hestia_earth/models/koble2014/residueBurnt.py +5 -7
  63. hestia_earth/models/koble2014/residueRemoved.py +5 -7
  64. hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthWaterStress.py +2 -2
  65. hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthWaterStress.py +2 -2
  66. hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthWaterStress.py +2 -2
  67. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthWaterStress.py +2 -2
  68. hestia_earth/models/log.py +1 -1
  69. hestia_earth/models/mocking/search-results.json +3413 -1113
  70. hestia_earth/models/site/management.py +1 -1
  71. hestia_earth/models/site/post_checks/__init__.py +3 -2
  72. hestia_earth/models/site/post_checks/country.py +9 -0
  73. hestia_earth/models/site/pre_checks/__init__.py +3 -2
  74. hestia_earth/models/site/pre_checks/country.py +9 -0
  75. hestia_earth/models/utils/__init__.py +1 -16
  76. hestia_earth/models/utils/blank_node.py +25 -25
  77. hestia_earth/models/utils/completeness.py +3 -2
  78. hestia_earth/models/utils/cycle.py +5 -4
  79. hestia_earth/models/utils/emission.py +5 -5
  80. hestia_earth/models/utils/feedipedia.py +6 -6
  81. hestia_earth/models/utils/impact_assessment.py +1 -2
  82. hestia_earth/models/utils/indicator.py +9 -7
  83. hestia_earth/models/utils/inorganicFertiliser.py +4 -6
  84. hestia_earth/models/utils/input.py +6 -5
  85. hestia_earth/models/utils/lookup.py +32 -100
  86. hestia_earth/models/utils/management.py +4 -4
  87. hestia_earth/models/utils/measurement.py +6 -7
  88. hestia_earth/models/utils/method.py +20 -0
  89. hestia_earth/models/utils/practice.py +4 -5
  90. hestia_earth/models/utils/product.py +4 -5
  91. hestia_earth/models/utils/property.py +12 -22
  92. hestia_earth/models/utils/site.py +14 -8
  93. hestia_earth/models/utils/term.py +27 -1
  94. hestia_earth/models/version.py +1 -1
  95. hestia_earth/orchestrator/log.py +0 -11
  96. hestia_earth/orchestrator/models/__init__.py +17 -4
  97. hestia_earth/orchestrator/strategies/run/add_blank_node_if_missing.py +2 -20
  98. {hestia_earth_models-0.67.1.dist-info → hestia_earth_models-0.68.0.dist-info}/METADATA +2 -2
  99. {hestia_earth_models-0.67.1.dist-info → hestia_earth_models-0.68.0.dist-info}/RECORD +145 -137
  100. tests/models/cml2001Baseline/test_abioticResourceDepletionFossilFuels.py +3 -3
  101. tests/models/cml2001Baseline/test_resourceUseEnergyDepletionDuringCycle.py +1 -1
  102. tests/models/cycle/test_coldCarcassWeightPerHead.py +1 -1
  103. tests/models/cycle/test_coldDressedCarcassWeightPerHead.py +1 -1
  104. tests/models/cycle/test_concentrateFeed.py +1 -1
  105. tests/models/cycle/test_energyContentLowerHeatingValue.py +1 -1
  106. tests/models/cycle/test_excretaKgMass.py +1 -1
  107. tests/models/cycle/test_feedConversionRatio.py +3 -3
  108. tests/models/cycle/test_pastureGrass.py +1 -1
  109. tests/models/cycle/test_readyToCookWeightPerHead.py +1 -1
  110. tests/models/environmentalFootprintV3_1/test_photochemicalOzoneCreationPotentialHumanHealthNmvocEq.py +30 -0
  111. tests/models/environmentalFootprintV3_1/test_soilQualityIndexTotalLandUseEffects.py +30 -7
  112. tests/models/faostat2018/product/test_price.py +27 -14
  113. tests/models/faostat2018/test_faostat_utils.py +4 -24
  114. tests/models/faostat2018/test_liveweightPerHead.py +9 -9
  115. tests/models/globalCropWaterModel2008/test_rootingDepth.py +7 -3
  116. tests/models/haversineFormula/transport/test_distance.py +1 -1
  117. tests/models/hestia/test_landCover.py +53 -5
  118. tests/models/ipcc2019/animal/test_pastureGrass.py +5 -3
  119. tests/models/ipcc2019/test_aboveGroundCropResidueTotal.py +4 -4
  120. tests/models/ipcc2019/test_belowGroundCropResidue.py +4 -4
  121. tests/models/ipcc2019/test_ch4ToAirEntericFermentation.py +10 -10
  122. tests/models/ipcc2019/test_croppingDuration.py +1 -1
  123. tests/models/ipcc2019/test_nonCo2EmissionsToAirNaturalVegetationBurning.py +83 -0
  124. tests/models/ipcc2019/test_organicCarbonPerHa.py +12 -12
  125. tests/models/ipcc2019/test_pastureGrass.py +5 -3
  126. tests/models/pooreNemecek2018/test_excretaKgN.py +5 -5
  127. tests/models/pooreNemecek2018/test_excretaKgVs.py +2 -2
  128. tests/models/site/post_checks/test_country.py +6 -0
  129. tests/models/site/pre_checks/test_cache_geospatialDatabase.py +1 -1
  130. tests/models/site/pre_checks/test_country.py +12 -0
  131. tests/models/test_ecoinventV3.py +7 -3
  132. tests/models/utils/test_blank_node.py +4 -12
  133. tests/models/utils/test_dataCompleteness.py +5 -5
  134. tests/models/utils/test_emission.py +2 -2
  135. tests/models/utils/test_indicator.py +2 -2
  136. tests/models/utils/test_input.py +2 -2
  137. tests/models/utils/test_measurement.py +2 -4
  138. tests/models/utils/test_practice.py +4 -2
  139. tests/models/utils/test_product.py +2 -2
  140. tests/models/utils/test_property.py +4 -2
  141. tests/models/utils/test_site.py +7 -0
  142. tests/orchestrator/strategies/run/test_add_blank_node_if_missing.py +4 -9
  143. hestia_earth/models/environmentalFootprintV3_1/utils.py +0 -17
  144. tests/models/utils/test_lookup.py +0 -10
  145. {hestia_earth_models-0.67.1.dist-info → hestia_earth_models-0.68.0.dist-info}/LICENSE +0 -0
  146. {hestia_earth_models-0.67.1.dist-info → hestia_earth_models-0.68.0.dist-info}/WHEEL +0 -0
  147. {hestia_earth_models-0.67.1.dist-info → hestia_earth_models-0.68.0.dist-info}/top_level.txt +0 -0
@@ -79,7 +79,7 @@ def _run_default(site: dict):
79
79
  region_factor=region_factor)
80
80
 
81
81
  should_run = all([region_factor])
82
- logShouldRun(site, MODEL, TERM_ID, should_run)
82
+ logShouldRun(site, MODEL, TERM_ID, should_run, run_by='Lookup')
83
83
  return [_measurement(site, round(region_factor))] if should_run else []
84
84
 
85
85
 
@@ -92,7 +92,7 @@ def _should_run(site: dict):
92
92
  below_max_area_size=below_max_area_size)
93
93
 
94
94
  should_run = all([contains_geospatial_data, below_max_area_size])
95
- logShouldRun(site, MODEL, TERM_ID, should_run)
95
+ logShouldRun(site, MODEL, TERM_ID, should_run, run_by='Earth Engine')
96
96
  return should_run
97
97
 
98
98
 
@@ -65,7 +65,7 @@ def _should_run(cycle: dict):
65
65
 
66
66
 
67
67
  def run(cycle: dict):
68
- sites = get_allowed_sites(MODEL, TERM_ID, TermTermType.LANDUSEMANAGEMENT, cycle) if _should_run(cycle) else []
68
+ sites = get_allowed_sites(MODEL, TERM_ID, cycle) if _should_run(cycle) else []
69
69
  sites = [
70
70
  (site, get_region_factor(TERM_ID, site, TermTermType.LANDUSEMANAGEMENT)) for site in sites
71
71
  ]
@@ -196,4 +196,7 @@ def download(term: str, site: dict, data: dict, only_coordinates=False) -> dict:
196
196
  def get_region_factor(term_id: str, site: dict, termType: TermTermType = TermTermType.MEASUREMENT):
197
197
  region_id = region_level_1_id(site.get('region', {}).get('@id'))
198
198
  country_id = site.get('country', {}).get('@id')
199
- return region_factor(MODEL, region_id, term_id, termType) or region_factor(MODEL, country_id, term_id, termType)
199
+ return (
200
+ # `region-measurement` only exists for countries
201
+ region_factor(MODEL, region_id, term_id, termType) if termType != TermTermType.MEASUREMENT else None
202
+ ) or region_factor(MODEL, country_id, term_id, termType)
@@ -1,11 +1,10 @@
1
1
  from hestia_earth.schema import CycleFunctionalUnit, TermTermType
2
- from hestia_earth.utils.api import download_hestia
3
2
  from hestia_earth.utils.model import find_term_match, filter_list_term_type
4
3
  from hestia_earth.utils.tools import list_sum, non_empty_list, safe_parse_float
5
4
 
6
5
  from hestia_earth.models.log import logRequirements, logShouldRun
7
6
  from hestia_earth.models.utils.property import _new_property, node_has_no_property
8
- from hestia_earth.models.utils.term import get_irrigation_terms
7
+ from hestia_earth.models.utils.term import get_irrigation_terms, download_term
9
8
  from hestia_earth.models.utils.crop import get_crop_lookup_value
10
9
  from hestia_earth.models.utils.completeness import _is_term_type_complete
11
10
  from . import MODEL
@@ -81,7 +80,7 @@ def _should_run_product(cycle: dict):
81
80
 
82
81
 
83
82
  def _run_cycle(products: list):
84
- term = download_hestia(TERM_ID)
83
+ term = download_term(TERM_ID, TermTermType.PROPERTY)
85
84
 
86
85
  def run_product(values: tuple):
87
86
  product, value = values
@@ -10,7 +10,7 @@ from hestia_earth.utils.api import download_hestia
10
10
  from hestia_earth.utils.tools import non_empty_list
11
11
 
12
12
  from hestia_earth.models.log import logRequirements, logShouldRun, debugValues
13
- from hestia_earth.models.utils import _include_methodModel
13
+ from hestia_earth.models.utils.method import include_methodModel
14
14
  from .. import MODEL
15
15
 
16
16
  REQUIREMENTS = {
@@ -39,8 +39,8 @@ MODEL_KEY = 'distance'
39
39
 
40
40
  def _run_transport(cycle: dict, distance_kms: float):
41
41
  def exec(transport: dict):
42
- return _include_methodModel({
43
- **transport, MODEL_KEY: distance_kms
42
+ return include_methodModel(transport | {
43
+ MODEL_KEY: distance_kms
44
44
  }, MODEL) if _should_run_transport(cycle, transport) else transport
45
45
  return exec
46
46
 
@@ -10,7 +10,7 @@ from datetime import datetime, timedelta
10
10
 
11
11
  from hestia_earth.schema import SiteSiteType, TermTermType
12
12
  from hestia_earth.utils.lookup import (
13
- download_lookup, get_table_value, column_name, _is_missing_value, extract_grouped_data
13
+ download_lookup, get_table_value, column_name, _is_missing_value, extract_grouped_data, lookup_columns
14
14
  )
15
15
  from hestia_earth.utils.model import filter_list_term_type
16
16
  from hestia_earth.utils.tools import safe_parse_float, to_precision
@@ -19,6 +19,7 @@ from hestia_earth.models.log import logRequirements, log_as_table, logShouldRun
19
19
  from hestia_earth.models.utils.constant import DAYS_IN_YEAR
20
20
  from hestia_earth.models.utils.management import _new_management
21
21
  from hestia_earth.models.utils.term import get_lookup_value
22
+ from hestia_earth.models.utils.lookup import get_region_lookup_value
22
23
  from hestia_earth.models.utils.blank_node import _node_date, DatestrFormat, _gapfill_datestr, DatestrGapfillMode
23
24
  from .utils import (
24
25
  IPCC_LAND_USE_CATEGORY_ANNUAL,
@@ -71,9 +72,9 @@ RETURNS = {
71
72
  }]
72
73
  }
73
74
  LOOKUPS = {
74
- "region-crop-cropGroupingFaostatProduction-areaHarvestedUpTo20YearExpansion": "All crops",
75
- "region-crop-cropGroupingFaostatProduction-areaHarvested": "All crops",
76
- "region-faostatArea-UpTo20YearExpansion": "All land uses",
75
+ "region-crop-cropGroupingFaostatProduction-areaHarvestedUpTo20YearExpansion": "",
76
+ "region-crop-cropGroupingFaostatProduction-areaHarvested": "",
77
+ "region-faostatArea-UpTo20YearExpansion": "",
77
78
  "region-faostatArea": [
78
79
  "Arable land",
79
80
  "Cropland",
@@ -99,6 +100,7 @@ SITE_TYPES = {
99
100
  DEFAULT_WINDOW_IN_YEARS = 20
100
101
  DATE_TOLERANCE_IN_YEARS = 2
101
102
  OUTPUT_SIGNIFICANT_DIGITS = 3
103
+ _LOOKUP_EXPANSION = "region-crop-cropGroupingFaostatProduction-areaHarvestedUpTo20YearExpansion.csv"
102
104
 
103
105
 
104
106
  def _management(term_id: str, value: float, start_date: str, end_date: str):
@@ -117,10 +119,25 @@ def _safe_divide(numerator, denominator, default=0) -> float:
117
119
  return default if not denominator else numerator / denominator
118
120
 
119
121
 
122
+ def scale_values_to_one(dictionary: dict) -> dict:
123
+ """
124
+ Takes a dictionary with numeric values.
125
+ Scales each value so that the sum of them all is one.
126
+ """
127
+ # Does not handle negative values.
128
+ sum_of_values = sum(dictionary.values())
129
+ return {key: value / sum_of_values for key, value in dictionary.items()} if sum_of_values != 0 else dictionary
130
+
131
+
132
+ def cap_values(dictionary: dict, lower_limit: float = 0, upper_limit: float = 1) -> dict:
133
+ return {key: min([upper_limit, max([lower_limit, value])]) for key, value in dictionary.items()}
134
+
135
+
120
136
  def site_area_sum_to_100(dict_of_percentages: dict):
121
- return False if dict_of_percentages == {} else \
122
- (math.isclose(sum(dict_of_percentages.values()), 1.0, rel_tol=0.01) or
123
- math.isclose(sum(dict_of_percentages.values()), 0.0, rel_tol=0.01))
137
+ return False if dict_of_percentages == {} else (
138
+ math.isclose(sum(dict_of_percentages.values()), 1.0, rel_tol=0.01) or
139
+ math.isclose(sum(dict_of_percentages.values()), 0.0, rel_tol=0.01)
140
+ )
124
141
 
125
142
 
126
143
  def _should_group_landCover(term: dict):
@@ -134,12 +151,13 @@ def get_changes(country_id: str, end_year: int) -> tuple[dict, bool]:
134
151
  """
135
152
  For each entry in ALL_LAND_USE_TERMS, creates a key: value in output dictionary, also TOTAL
136
153
  """
137
- lookup = download_lookup("region-faostatArea-UpTo20YearExpansion.csv")
154
+ lookup_name = "region-faostatArea-UpTo20YearExpansion.csv"
138
155
  changes_dict = {
139
156
  land_use_term: safe_parse_float(
140
157
  extract_grouped_data(
141
- get_table_value(lookup, 'termid', country_id, column_name(land_use_term)),
142
- str(end_year)),
158
+ get_region_lookup_value(lookup_name, country_id, land_use_term, model=MODEL, key=MODEL_KEY),
159
+ str(end_year)
160
+ ),
143
161
  default=None
144
162
  )
145
163
  for land_use_term in ALL_LAND_USE_TERMS + [LAND_AREA]
@@ -160,8 +178,7 @@ def _get_ratio_start_and_end_values(
160
178
  end_year: int
161
179
  ) -> float:
162
180
  # expansion over twenty years / current area
163
- lookup = download_lookup('region-faostatArea.csv')
164
- table_value = get_table_value(lookup, 'termid', country_id, column_name(fao_name))
181
+ table_value = get_region_lookup_value('region-faostatArea.csv', country_id, fao_name, model=MODEL, key=MODEL_KEY)
165
182
  end_value = safe_parse_float(value=extract_grouped_data(table_value, str(end_year)), default=None)
166
183
  return max(0.0, _safe_divide(numerator=expansion, denominator=end_value))
167
184
 
@@ -186,11 +203,13 @@ def _estimate_maximum_forest_change(
186
203
 
187
204
 
188
205
  def _negative_agricultural_land_change(forest_change, pasture_change, total_cropland_change):
189
- return -pasture_change if 0 < pasture_change < -min(forest_change, 0) \
190
- else min(forest_change, 0) if pasture_change > 0 \
191
- else -total_cropland_change if 0 < total_cropland_change < -min(forest_change, 0) \
192
- else min(forest_change, 0) if 0 < total_cropland_change \
206
+ return (
207
+ -pasture_change if 0 < pasture_change < -min(forest_change, 0)
208
+ else min(forest_change, 0) if pasture_change > 0
209
+ else -total_cropland_change if 0 < total_cropland_change < -min(forest_change, 0)
210
+ else min(forest_change, 0) if 0 < total_cropland_change
193
211
  else 0
212
+ )
194
213
 
195
214
 
196
215
  def _allocate_forest_loss(forest_loss: float, changes: dict):
@@ -271,12 +290,14 @@ def _estimate_conversions_to_annual_cropland(
271
290
  permanent_to_annual_cropland: float
272
291
  ) -> dict:
273
292
  """(AC-AG): Estimate percentage of land sources when converted to: Annual cropland"""
293
+
274
294
  # -> percent_annual_cropland_was[]
275
295
  def conversion_to_annual_cropland(factor: float):
276
296
  return factor * _safe_divide(
277
297
  numerator=_safe_divide(
278
298
  numerator=max(changes[ANNUAL_CROPLAND], 0),
279
- denominator=max(changes[ANNUAL_CROPLAND], 0) + max(changes[PERMANENT_CROPLAND], 0)),
299
+ denominator=max(changes[ANNUAL_CROPLAND], 0) + max(changes[PERMANENT_CROPLAND], 0)
300
+ ),
280
301
  denominator=-changes[ANNUAL_CROPLAND]
281
302
  )
282
303
 
@@ -297,11 +318,13 @@ def _estimate_conversions_to_permanent_cropland(
297
318
  other_land_loss_to_annual_cropland: float
298
319
  ) -> dict:
299
320
  """Estimate percentage of land sources when converted to: Annual cropland"""
321
+
300
322
  def conversion_to_permanent_cropland(factor: float):
301
323
  return _safe_divide(
302
324
  numerator=_safe_divide(
303
325
  numerator=factor * max(changes[PERMANENT_CROPLAND], 0),
304
- denominator=max(changes[ANNUAL_CROPLAND], 0) + max(changes[PERMANENT_CROPLAND], 0)),
326
+ denominator=max(changes[ANNUAL_CROPLAND], 0) + max(changes[PERMANENT_CROPLAND], 0)
327
+ ),
305
328
  denominator=-changes[PERMANENT_CROPLAND]
306
329
  )
307
330
 
@@ -360,10 +383,10 @@ def _get_shares_of_expansion(
360
383
  PERMANENT_CROPLAND: percent_permanent_cropland_was,
361
384
  PERMANENT_PASTURE: percent_pasture_was
362
385
  }
363
- return {
386
+ return scale_values_to_one({
364
387
  k: expansion_for_type[land_use_type].get(k, 0)
365
388
  for k in LAND_USE_TERMS_FOR_TRANSFORMATION.keys()
366
- }
389
+ })
367
390
 
368
391
 
369
392
  def _get_faostat_name(term: dict) -> str:
@@ -391,10 +414,11 @@ def _get_harvested_area(country_id: str, year: int, faostat_name: str) -> float:
391
414
  """
392
415
  Returns a dictionary of harvested areas for the country & year, indexed by landCover term (crop)
393
416
  """
394
- lookup = download_lookup("region-crop-cropGroupingFaostatProduction-areaHarvested.csv")
417
+ lookup_name = "region-crop-cropGroupingFaostatProduction-areaHarvested.csv"
418
+
395
419
  return safe_parse_float(
396
420
  value=extract_grouped_data(
397
- data=get_table_value(lookup, "termid", country_id, column_name(faostat_name)),
421
+ data=get_region_lookup_value(lookup_name, country_id, faostat_name, model=MODEL, key=MODEL_KEY),
398
422
  key=str(year)
399
423
  ),
400
424
  default=None
@@ -452,8 +476,7 @@ def _run(site: dict, existing_nodes: list, percentage_transformed_from: dict):
452
476
 
453
477
 
454
478
  def get_ratio_of_expanded_area(country_id: str, fao_name: str, end_year: int) -> float:
455
- lookup = download_lookup("region-crop-cropGroupingFaostatProduction-areaHarvestedUpTo20YearExpansion.csv")
456
- table_value = get_table_value(lookup, 'termid', country_id, column_name(fao_name))
479
+ table_value = get_region_lookup_value(_LOOKUP_EXPANSION, country_id, fao_name, model=MODEL, key=MODEL_KEY)
457
480
  expansion = safe_parse_float(value=extract_grouped_data(table_value, str(end_year)), default=None)
458
481
  end_value = _get_harvested_area(
459
482
  country_id=country_id,
@@ -472,16 +495,16 @@ def _get_sum_for_land_category(
472
495
  fao_stat_to_ipcc_type: dict,
473
496
  include_negatives: bool = True
474
497
  ) -> float:
475
- return sum(
476
- [
477
- safe_parse_float(value=extract_grouped_data(table_value, str(year)), default=None)
478
- for fao_name, table_value in values.items()
479
- if not _is_missing_or_none(extract_grouped_data(table_value, str(year))) and
480
- fao_stat_to_ipcc_type[fao_name] == ipcc_land_use_category and
481
- (include_negatives or
482
- safe_parse_float(value=extract_grouped_data(table_value, str(year)), default=None) > 0.0)
483
- ]
484
- )
498
+ return sum([
499
+ safe_parse_float(value=extract_grouped_data(table_value, str(year)), default=None)
500
+ for fao_name, table_value in values.items()
501
+ if not _is_missing_or_none(extract_grouped_data(table_value, str(year))) and
502
+ fao_stat_to_ipcc_type[fao_name] == ipcc_land_use_category and
503
+ (
504
+ include_negatives or
505
+ safe_parse_float(value=extract_grouped_data(table_value, str(year)), default=None) > 0.0
506
+ )
507
+ ])
485
508
 
486
509
 
487
510
  def _get_sums_of_crop_expansion(country_id: str, year: int, include_negatives: bool = True) -> tuple[float, float]:
@@ -489,9 +512,12 @@ def _get_sums_of_crop_expansion(country_id: str, year: int, include_negatives: b
489
512
  Sum net expansion for all annual and permanent crops, returned as two values.
490
513
  Returns a tuple of (expansion of annual crops, expansion of permanent crops)
491
514
  """
492
- lookup = download_lookup("region-crop-cropGroupingFaostatProduction-areaHarvestedUpTo20YearExpansion.csv")
493
- values = {name: get_table_value(lookup, 'termid', country_id, column_name(name))
494
- for name in list(lookup.dtype.names) if name != "termid"}
515
+ lookup = download_lookup(_LOOKUP_EXPANSION.replace('region', f"{country_id}")) or download_lookup(_LOOKUP_EXPANSION)
516
+ columns = lookup_columns(lookup)
517
+ values = {
518
+ name: get_table_value(lookup, 'termid', country_id, column_name(name))
519
+ for name in columns if name != "termid"
520
+ }
495
521
 
496
522
  fao_stat_to_ipcc_type = _get_complete_faostat_to_crop_mapping()
497
523
 
@@ -513,8 +539,9 @@ def _get_sums_of_crop_expansion(country_id: str, year: int, include_negatives: b
513
539
  return annual_sum_of_expansion, permanent_sum_of_expansion
514
540
 
515
541
 
516
- def _get_net_expansion_cultivated_vs_harvested(annual_crops_net_expansion, changes, land_use_type,
517
- permanent_crops_net_expansion):
542
+ def _get_net_expansion_cultivated_vs_harvested(
543
+ annual_crops_net_expansion, changes, land_use_type, permanent_crops_net_expansion
544
+ ):
518
545
  if land_use_type == ANNUAL_CROPLAND:
519
546
  net_expansion_cultivated_vs_harvested = _safe_divide(numerator=max(0, changes[ANNUAL_CROPLAND]),
520
547
  denominator=(annual_crops_net_expansion / 1000))
@@ -673,14 +700,14 @@ def _should_run_historical_land_use_change_single_crop(
673
700
 
674
701
  site_area = {
675
702
  land_type: (
676
- shares_of_expansion[land_type] * expansion_factor * e9_net_expansion * net_expansion_cultivated_vs_harvested
677
- )
678
- for land_type in LAND_USE_TERMS_FOR_TRANSFORMATION.keys()
703
+ shares_of_expansion[land_type] * expansion_factor * e9_net_expansion * net_expansion_cultivated_vs_harvested
704
+ ) for land_type in LAND_USE_TERMS_FOR_TRANSFORMATION.keys()
679
705
  if land_type != land_use_type
680
706
  }
681
707
  site_area[land_use_type] = 1 - sum(site_area.values())
708
+ capped_site_area = cap_values(dictionary=site_area, lower_limit=0, upper_limit=1)
682
709
 
683
- sum_of_site_areas_is_100 = site_area_sum_to_100(site_area)
710
+ sum_of_site_areas_is_100 = site_area_sum_to_100(capped_site_area)
684
711
  site_type_allowed = site.get("siteType") in SITE_TYPES
685
712
 
686
713
  logRequirements(
@@ -691,7 +718,7 @@ def _should_run_historical_land_use_change_single_crop(
691
718
  land_use_type=land_use_type,
692
719
  country_id=country_id,
693
720
  changes=log_as_table(changes),
694
- site_area=log_as_table(site_area),
721
+ site_area=log_as_table(capped_site_area),
695
722
  sum_of_site_areas_is_100=sum_of_site_areas_is_100,
696
723
  site_type_allowed=site_type_allowed
697
724
  )
@@ -699,7 +726,7 @@ def _should_run_historical_land_use_change_single_crop(
699
726
  should_run = all([not missing_changes, country_id, site_type_allowed, sum_of_site_areas_is_100])
700
727
  logShouldRun(site, MODEL, term.get("@id"), should_run, model_key=MODEL_KEY)
701
728
 
702
- return should_run, site_area
729
+ return should_run, capped_site_area
703
730
 
704
731
 
705
732
  def _get_land_use_term_from_node(node: dict) -> str:
@@ -13,7 +13,9 @@ These are the steps:
13
13
  """
14
14
  from functools import reduce
15
15
  from hestia_earth.schema import TermTermType, EmissionMethodTier, SiteSiteType
16
- from hestia_earth.utils.lookup import download_lookup, column_name, get_table_value, extract_grouped_data_closest_date
16
+ from hestia_earth.utils.lookup import (
17
+ download_lookup, column_name, extract_grouped_data_closest_date, find_term_ids_by
18
+ )
17
19
  from hestia_earth.utils.model import filter_list_term_type
18
20
  from hestia_earth.utils.tools import non_empty_list, flatten, list_sum, safe_parse_float
19
21
  from hestia_earth.utils.emission import cycle_emissions_in_system_boundary
@@ -26,6 +28,7 @@ from hestia_earth.models.utils.cycle import cycle_end_year
26
28
  from hestia_earth.models.utils.crop import get_crop_grouping_faostat_production, get_landCover_term_id
27
29
  from hestia_earth.models.utils.completeness import _is_term_type_complete
28
30
  from hestia_earth.models.utils.blank_node import get_lookup_value
31
+ from hestia_earth.models.utils.lookup import get_region_lookup_value
29
32
  from . import MODEL
30
33
 
31
34
  REQUIREMENTS = {
@@ -65,7 +68,7 @@ RETURNS = {
65
68
  }]
66
69
  }
67
70
  LOOKUPS = {
68
- "region-crop-cropGroupingFaostatProduction-yield": "value from cropGroupingFaostatProduction and country",
71
+ "region-crop-cropGroupingFaostatProduction-yield": "",
69
72
  "crop": [
70
73
  "correspondingSeedTermIds",
71
74
  "cropGroupingFaostatProduction",
@@ -133,7 +136,7 @@ def _filter_emissions(cycle: dict):
133
136
  'id': group_id,
134
137
  'emissions': list(filter(
135
138
  lambda id: id in required_emission_term_ids,
136
- set(list(lookup[lookup[column_name('inputProductionGroupId')] == group_id].termid))
139
+ find_term_ids_by(lookup, column_name('inputProductionGroupId'), group_id)
137
140
  ))
138
141
  }
139
142
  for group_id in group_ids
@@ -169,11 +172,12 @@ def _evs(product: dict):
169
172
 
170
173
  def _faostat_yield(country_id: str, end_year: int, product: dict):
171
174
  grouping = get_crop_grouping_faostat_production(MODEL, product.get('term', {}))
172
- return safe_parse_float(extract_grouped_data_closest_date(get_table_value(
173
- download_lookup('region-crop-cropGroupingFaostatProduction-yield.csv'),
174
- 'termid',
175
+ return safe_parse_float(extract_grouped_data_closest_date(get_region_lookup_value(
176
+ 'region-crop-cropGroupingFaostatProduction-yield.csv',
175
177
  country_id,
176
- column_name(grouping)
178
+ grouping,
179
+ model=MODEL,
180
+ model_key=MODEL_KEY
177
181
  ), end_year))
178
182
 
179
183
 
@@ -2,12 +2,12 @@ from os.path import dirname, abspath
2
2
  import sys
3
3
  from importlib import import_module
4
4
 
5
- from hestia_earth.models.utils.blank_node import run_if_required
6
-
7
5
  CURRENT_DIR = dirname(abspath(__file__)) + '/'
8
6
  sys.path.append(CURRENT_DIR)
9
7
  MODEL = 'impact_assessment'
10
8
  PKG = '.'.join(['hestia_earth', 'models', MODEL])
11
9
 
12
10
 
13
- def run(model: str, data): return run_if_required(MODEL, model, data, import_module(f".{model}", package=PKG))
11
+ def run(model: str, data):
12
+ run = getattr(import_module(f".{model}", package=PKG), 'run')
13
+ return run(data)
@@ -17,7 +17,7 @@ REQUIREMENTS = {
17
17
  }
18
18
  }
19
19
  LOOKUPS = {
20
- "region-liveAnimal-milkFatContent": "fat content",
20
+ "region-liveAnimal-milkFatContent": "",
21
21
  "liveAnimal": "milkYieldPracticeTermIds"
22
22
  }
23
23
  RETURNS = {
@@ -20,7 +20,7 @@ REQUIREMENTS = {
20
20
  }
21
21
  }
22
22
  LOOKUPS = {
23
- "region-liveAnimal-hoursWorkedPerDay": "hours worked per day"
23
+ "region-liveAnimal-hoursWorkedPerDay": ""
24
24
  }
25
25
  RETURNS = {
26
26
  "Animal": [{
@@ -20,7 +20,7 @@ REQUIREMENTS = {
20
20
  }
21
21
  }
22
22
  LOOKUPS = {
23
- "region-liveAnimal-liveweightGain": "liveweight gain"
23
+ "region-liveAnimal-liveweightGain": ""
24
24
  }
25
25
  RETURNS = {
26
26
  "Animal": [{
@@ -20,7 +20,7 @@ REQUIREMENTS = {
20
20
  }
21
21
  }
22
22
  LOOKUPS = {
23
- "region-liveAnimal-liveweightPerHead": "liveweight per head"
23
+ "region-liveAnimal-liveweightPerHead": ""
24
24
  }
25
25
  RETURNS = {
26
26
  "Animal": [{
@@ -27,7 +27,7 @@ REQUIREMENTS = {
27
27
  }
28
28
  }
29
29
  LOOKUPS = {
30
- "region-liveAnimal-milkYieldPerAnimal": "yield value",
30
+ "region-liveAnimal-milkYieldPerAnimal": "",
31
31
  "liveAnimal": ["milkYieldPracticeTermIds", "ipcc2019MilkYieldPerAnimalTermId"]
32
32
  }
33
33
  RETURNS = {
@@ -136,7 +136,7 @@ LOOKUPS = {
136
136
  "mjKgABNetEnergyGrowthSheepGoatsIpcc2019",
137
137
  "isWoolProducingAnimal"
138
138
  ],
139
- "system-liveAnimal-activityCoefficient-ipcc2019": "using animal term @id",
139
+ "system-liveAnimal-activityCoefficient-ipcc2019": "",
140
140
  "landCover": "grazedPastureGrassInputId",
141
141
  "crop-property": ["energyDigestibilityRuminants", "energyContentHigherHeatingValue"],
142
142
  "crop": "grazedPastureGrassInputId",
@@ -20,7 +20,7 @@ REQUIREMENTS = {
20
20
  }
21
21
  }
22
22
  LOOKUPS = {
23
- "region-liveAnimal-pregnancyRateTotal": "pregnancy rate"
23
+ "region-liveAnimal-pregnancyRateTotal": ""
24
24
  }
25
25
  RETURNS = {
26
26
  "Animal": [{
@@ -17,7 +17,7 @@ REQUIREMENTS = {
17
17
  }
18
18
  }
19
19
  LOOKUPS = {
20
- "region-liveAnimal-milkTrueProteinContent": "protein content",
20
+ "region-liveAnimal-milkTrueProteinContent": "",
21
21
  "liveAnimal": "milkYieldPracticeTermIds"
22
22
  }
23
23
  RETURNS = {
@@ -1,13 +1,14 @@
1
1
  from hestia_earth.schema import TermTermType
2
- from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name, extract_grouped_data
2
+ from hestia_earth.utils.lookup import extract_grouped_data
3
3
  from hestia_earth.utils.model import filter_list_term_type
4
4
  from hestia_earth.utils.tools import safe_parse_float, non_empty_list
5
5
 
6
- from hestia_earth.models.log import logRequirements, logShouldRun, debugMissingLookup
6
+ from hestia_earth.models.log import logRequirements, logShouldRun
7
7
  from hestia_earth.models.utils.blank_node import merge_blank_nodes
8
8
  from hestia_earth.models.utils.property import _new_property, node_has_no_property
9
9
  from hestia_earth.models.utils.productivity import PRODUCTIVITY, get_productivity
10
10
  from hestia_earth.models.utils.term import get_lookup_value
11
+ from hestia_earth.models.utils.lookup import get_region_lookup_value
11
12
  from .. import MODEL
12
13
 
13
14
 
@@ -24,11 +25,8 @@ def _get_practice(term_id: str, animal: dict, practice_column: str):
24
25
  def productivity_lookup_value(term_id: str, lookup: str, country: dict, animal: dict):
25
26
  country_id = country.get('@id')
26
27
  productivity_key = get_productivity(country)
27
- lookup_name = f"{lookup}.csv"
28
- lookup = download_lookup(lookup_name)
29
- column = column_name(animal.get('term').get('@id'))
30
- value = get_table_value(lookup, 'termid', country_id, column)
31
- debugMissingLookup(lookup_name, 'termid', country_id, column, value, model=MODEL, term=term_id)
28
+ column = animal.get('term').get('@id')
29
+ value = get_region_lookup_value(f"{lookup}.csv", country_id, column, model=MODEL, term=term_id)
32
30
  return safe_parse_float(
33
31
  extract_grouped_data(value, productivity_key.value) or
34
32
  extract_grouped_data(value, PRODUCTIVITY.HIGH.value), # defaults to high if low is not found
@@ -37,7 +37,7 @@ REQUIREMENTS = {
37
37
  }
38
38
  }
39
39
  LOOKUPS = {
40
- "region-liveAnimal-weightAtMaturity": "weight at maturity"
40
+ "region-liveAnimal-weightAtMaturity": ""
41
41
  }
42
42
  RETURNS = {
43
43
  "Animal": [{
@@ -195,10 +195,10 @@ def _is_ionophore(cycle: dict, total_feed: float):
195
195
  has_input = find_term_match(inputs, 'ionophores', None) is not None
196
196
  maize_input = find_term_match(inputs, 'maizeSteamFlaked')
197
197
  maize_feed = get_total_value_converted_with_min_ratio(MODEL, None, blank_nodes=[maize_input]) if maize_input else 0
198
- maize_feed_ratio = maize_feed / total_feed
198
+ maize_feed_ratio = maize_feed / total_feed if all([maize_feed, total_feed]) else 0
199
199
 
200
200
  debugValues(cycle, model=MODEL, term=TERM_ID,
201
- maize_feed=maize_feed,
201
+ maize_feed_in_MJ=maize_feed,
202
202
  maize_feed_ratio=maize_feed_ratio)
203
203
 
204
204
  return has_input and maize_feed_ratio >= 0.9
@@ -1,6 +1,6 @@
1
1
  from enum import Enum
2
2
  from hestia_earth.schema import EmissionMethodTier, TermTermType
3
- from hestia_earth.utils.lookup import column_name, download_lookup, get_table_value, extract_grouped_data
3
+ from hestia_earth.utils.lookup import download_lookup, get_table_value, extract_grouped_data
4
4
  from hestia_earth.utils.model import filter_list_term_type
5
5
  from hestia_earth.utils.tools import safe_parse_float, list_sum
6
6
 
@@ -12,6 +12,7 @@ from hestia_earth.models.utils.productivity import PRODUCTIVITY, get_productivit
12
12
  from hestia_earth.models.utils.emission import _new_emission
13
13
  from hestia_earth.models.utils.measurement import most_relevant_measurement_value
14
14
  from hestia_earth.models.utils.input import total_excreta
15
+ from hestia_earth.models.utils.lookup import get_region_lookup_value
15
16
  from . import MODEL
16
17
 
17
18
  REQUIREMENTS = {
@@ -29,8 +30,8 @@ REQUIREMENTS = {
29
30
  }
30
31
  LOOKUPS = {
31
32
  "region": "HDI",
32
- "region-excreta-excretaManagement-ch4B0": "use input `@id`",
33
- "excretaManagement-ecoClimateZone-CH4conv": "use `ecoClimateZone` from site measurements"
33
+ "region-excreta-excretaManagement-ch4B0": "",
34
+ "excretaManagement-ecoClimateZone-CH4conv": ""
34
35
  }
35
36
  RETURNS = {
36
37
  "Emission": [{
@@ -83,11 +84,9 @@ def _get_excreta_b0(country: dict, input: dict):
83
84
  # a high or low value is stored in the lookup as "high"
84
85
  # therefore this model defaults to "high" productivity in these cases to ascertain this value
85
86
  productivity_key = get_productivity(country)
86
- lookup_name = 'region-excreta-excretaManagement-ch4B0.csv'
87
- lookup = download_lookup(lookup_name)
88
87
  term_id = input.get('term', {}).get('@id')
89
- data_values = get_table_value(lookup, 'termid', country.get('@id'), column_name(term_id))
90
- debugMissingLookup(lookup_name, 'termid', country.get('@id'), term_id, data_values, model=MODEL, term=TERM_ID)
88
+ lookup_name = 'region-excreta-excretaManagement-ch4B0.csv'
89
+ data_values = get_region_lookup_value(lookup_name, country.get('@id'), term_id, model=MODEL, term=TERM_ID)
91
90
  return safe_parse_float(
92
91
  extract_grouped_data(data_values, productivity_key.value) or
93
92
  extract_grouped_data(data_values, PRODUCTIVITY.HIGH.value) # defaults to high if low is not found
@@ -1,6 +1,5 @@
1
1
  from hestia_earth.schema import EmissionMethodTier, EmissionStatsDefinition, TermTermType
2
2
  from hestia_earth.utils.model import filter_list_term_type, find_term_match
3
- from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name
4
3
  from hestia_earth.utils.tools import list_sum, safe_parse_float
5
4
 
6
5
  from hestia_earth.models.log import debugValues, logRequirements, logShouldRun
@@ -8,6 +7,7 @@ from hestia_earth.models.utils.term import get_lookup_value
8
7
  from hestia_earth.models.utils.emission import _new_emission
9
8
  from hestia_earth.models.utils.product import has_flooded_rice
10
9
  from hestia_earth.models.utils.organicFertiliser import get_cycle_inputs as get_organicFertiliser_inputs
10
+ from hestia_earth.models.utils.lookup import get_region_lookup_value
11
11
  from . import MODEL
12
12
 
13
13
  REQUIREMENTS = {
@@ -82,8 +82,10 @@ def _emission(value: float, min: float, max: float, sd: float):
82
82
 
83
83
 
84
84
  def _get_CH4_ef(country: str, suffix: str = ''):
85
- lookup = download_lookup('region-ch4ef-IPCC2019.csv')
86
- return safe_parse_float(get_table_value(lookup, 'termid', country, column_name('CH4_ef' + suffix)))
85
+ lookup_name = 'region-ch4ef-IPCC2019.csv'
86
+ return safe_parse_float(
87
+ get_region_lookup_value(lookup_name, country, 'CH4_ef' + suffix, model=MODEL, term=TERM_ID)
88
+ )
87
89
 
88
90
 
89
91
  def _get_practice_lookup(term: dict, col: str):
@@ -410,7 +410,7 @@ def create_should_run_function(
410
410
  cycle_end_date = cycle.get("endDate")
411
411
 
412
412
  site = _get_site(cycle)
413
- cycles = related_cycles(site)
413
+ cycles = related_cycles(site, cycles_mapping={cycle_id: cycle})
414
414
 
415
415
  carbon_stock_measurements = [
416
416
  node for node in site.get("measurements", [])