hestia-earth-models 0.72.2__py3-none-any.whl → 0.73.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 (130) 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 +2 -2
  9. hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsTotalLandUseEffects.py +1 -1
  10. hestia_earth/models/chaudharyBrooks2018/utils.py +1 -1
  11. hestia_earth/models/config/Cycle.json +48 -7
  12. hestia_earth/models/config/ImpactAssessment.json +22 -0
  13. hestia_earth/models/cycle/completeness/soilAmendment.py +1 -1
  14. hestia_earth/models/cycle/product/economicValueShare.py +3 -1
  15. hestia_earth/models/cycle/product/price.py +35 -10
  16. hestia_earth/models/cycle/product/revenue.py +5 -2
  17. hestia_earth/models/dammgen2009/noxToAirExcreta.py +14 -18
  18. hestia_earth/models/ecoinventV3/__init__.py +11 -6
  19. hestia_earth/models/ecoinventV3AndEmberClimate/utils.py +1 -1
  20. hestia_earth/models/emepEea2019/utils.py +2 -1
  21. hestia_earth/models/faostat2018/liveweightPerHead.py +1 -1
  22. hestia_earth/models/faostat2018/product/price.py +2 -2
  23. hestia_earth/models/faostat2018/seed.py +3 -2
  24. hestia_earth/models/faostat2018/utils.py +6 -5
  25. hestia_earth/models/geospatialDatabase/altitude.py +2 -1
  26. hestia_earth/models/geospatialDatabase/drainageClass.py +2 -1
  27. hestia_earth/models/geospatialDatabase/organicCarbonPerKgSoil.py +2 -1
  28. hestia_earth/models/geospatialDatabase/totalNitrogenPerKgSoil.py +2 -1
  29. hestia_earth/models/geospatialDatabase/totalPhosphorusPerKgSoil.py +2 -1
  30. hestia_earth/models/geospatialDatabase/utils.py +1 -1
  31. hestia_earth/models/globalCropWaterModel2008/rootingDepth.py +1 -1
  32. hestia_earth/models/hestia/brackishWater.py +1 -1
  33. hestia_earth/models/hestia/default_emissions.py +105 -0
  34. hestia_earth/models/hestia/default_resourceUse.py +110 -0
  35. hestia_earth/models/hestia/freshWater.py +1 -1
  36. hestia_earth/models/hestia/inorganicFertiliser.py +12 -12
  37. hestia_earth/models/hestia/landCover.py +7 -5
  38. hestia_earth/models/hestia/landTransformation100YearAverageDuringCycle.py +3 -0
  39. hestia_earth/models/hestia/landTransformation20YearAverageDuringCycle.py +3 -0
  40. hestia_earth/models/hestia/liveAnimal.py +1 -1
  41. hestia_earth/models/hestia/management.py +1 -1
  42. hestia_earth/models/hestia/netPrimaryProduction.py +1 -1
  43. hestia_earth/models/hestia/organicCarbonPerHa.py +2 -2
  44. hestia_earth/models/hestia/salineWater.py +1 -1
  45. hestia_earth/models/hestia/seed_emissions.py +34 -20
  46. hestia_earth/models/hestia/totalNitrogenPerKgSoil.py +1 -1
  47. hestia_earth/models/hestia/waterSalinity.py +2 -1
  48. hestia_earth/models/ipcc2006/aboveGroundCropResidueRemoved.py +2 -2
  49. hestia_earth/models/ipcc2006/aboveGroundCropResidueTotal.py +10 -6
  50. hestia_earth/models/ipcc2006/belowGroundCropResidue.py +12 -8
  51. hestia_earth/models/ipcc2019/animal/utils.py +1 -1
  52. hestia_earth/models/ipcc2019/belowGroundCropResidue.py +1 -1
  53. hestia_earth/models/ipcc2019/carbonContent.py +1 -1
  54. hestia_earth/models/ipcc2019/ch4ToAirAquacultureSystems.py +17 -9
  55. hestia_earth/models/ipcc2019/ch4ToAirEntericFermentation.py +6 -6
  56. hestia_earth/models/ipcc2019/ch4ToAirExcreta.py +4 -2
  57. hestia_earth/models/ipcc2019/co2ToAirUreaHydrolysis.py +1 -1
  58. hestia_earth/models/ipcc2019/croppingDuration.py +4 -2
  59. hestia_earth/models/ipcc2019/ligninContent.py +1 -1
  60. hestia_earth/models/{akagiEtAl2011AndIpcc2006 → ipcc2019}/n2OToAirCropResidueBurningDirect.py +8 -4
  61. hestia_earth/models/ipcc2019/nitrogenContent.py +1 -1
  62. hestia_earth/models/ipcc2019/nonCo2EmissionsToAirNaturalVegetationBurning.py +6 -2
  63. hestia_earth/models/ipcc2019/pastureGrass_utils.py +13 -12
  64. hestia_earth/models/ipcc2019/utils.py +6 -2
  65. hestia_earth/models/koble2014/residueBurnt.py +6 -3
  66. hestia_earth/models/koble2014/residueRemoved.py +1 -1
  67. hestia_earth/models/mocking/search-results.json +1577 -1573
  68. hestia_earth/models/pooreNemecek2018/aboveGroundCropResidueTotal.py +1 -1
  69. hestia_earth/models/pooreNemecek2018/belowGroundCropResidue.py +1 -1
  70. hestia_earth/models/pooreNemecek2018/excretaKgVs.py +1 -1
  71. hestia_earth/models/pooreNemecek2018/freshwaterWithdrawalsDuringCycle.py +1 -1
  72. hestia_earth/models/pooreNemecek2018/longFallowDuration.py +1 -1
  73. hestia_earth/models/pooreNemecek2018/nurseryDensity.py +1 -1
  74. hestia_earth/models/pooreNemecek2018/nurseryDuration.py +1 -1
  75. hestia_earth/models/pooreNemecek2018/plantationDensity.py +1 -1
  76. hestia_earth/models/pooreNemecek2018/plantationLifespan.py +1 -1
  77. hestia_earth/models/pooreNemecek2018/plantationProductiveLifespan.py +3 -1
  78. hestia_earth/models/pooreNemecek2018/saplingsDepreciatedAmountPerCycle.py +1 -1
  79. hestia_earth/models/resourceUseNotRelevant/__init__.py +65 -0
  80. hestia_earth/models/schererPfister2015/nErosionSoilFlux.py +5 -3
  81. hestia_earth/models/schererPfister2015/pErosionSoilFlux.py +5 -3
  82. hestia_earth/models/schererPfister2015/utils.py +5 -4
  83. hestia_earth/models/stehfestBouwman2006/n2OToAirSoilFlux_utils.py +1 -1
  84. hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirSoilFlux_utils.py +3 -3
  85. hestia_earth/models/utils/background_emissions.py +14 -10
  86. hestia_earth/models/utils/blank_node.py +6 -4
  87. hestia_earth/models/utils/crop.py +1 -1
  88. hestia_earth/models/utils/cropResidue.py +16 -0
  89. hestia_earth/models/utils/cycle.py +1 -1
  90. hestia_earth/models/utils/ecoClimateZone.py +2 -2
  91. hestia_earth/models/utils/excretaManagement.py +1 -1
  92. hestia_earth/models/utils/feedipedia.py +3 -3
  93. hestia_earth/models/utils/fertiliser.py +7 -1
  94. hestia_earth/models/utils/inorganicFertiliser.py +2 -2
  95. hestia_earth/models/utils/input.py +34 -1
  96. hestia_earth/models/utils/liveAnimal.py +2 -2
  97. hestia_earth/models/utils/lookup.py +1 -1
  98. hestia_earth/models/utils/measurement.py +5 -4
  99. hestia_earth/models/utils/productivity.py +1 -1
  100. hestia_earth/models/utils/property.py +4 -2
  101. hestia_earth/models/utils/site.py +2 -1
  102. hestia_earth/models/version.py +1 -1
  103. {hestia_earth_models-0.72.2.dist-info → hestia_earth_models-0.73.0.dist-info}/METADATA +1 -1
  104. {hestia_earth_models-0.72.2.dist-info → hestia_earth_models-0.73.0.dist-info}/RECORD +124 -113
  105. tests/models/akagiEtAl2011/test_ch4ToAirCropResidueBurning.py +33 -0
  106. tests/models/akagiEtAl2011/test_nh3ToAirCropResidueBurning.py +33 -0
  107. tests/models/{akagiEtAl2011AndIpcc2006 → akagiEtAl2011}/test_noxToAirCropResidueBurning.py +5 -17
  108. tests/models/akagiEtAl2011/test_pm25ToAirCropResidueBurning.py +33 -0
  109. tests/models/akagiEtAl2011/test_so2ToAirCropResidueBurning.py +33 -0
  110. tests/models/akagiEtAl2011/test_utils.py +18 -0
  111. tests/models/cycle/product/test_price.py +1 -11
  112. tests/models/dammgen2009/test_noxToAirExcreta.py +30 -10
  113. tests/models/geospatialDatabase/test_utils.py +2 -1
  114. tests/models/hestia/test_default_emissions.py +25 -0
  115. tests/models/hestia/test_default_resourceUse.py +26 -0
  116. tests/models/hestia/test_landCover.py +2 -2
  117. tests/models/ipcc2019/test_ch4ToAirAquacultureSystems.py +2 -2
  118. tests/models/{akagiEtAl2011AndIpcc2006/test_nh3ToAirCropResidueBurning.py → ipcc2019/test_n2OToAirCropResidueBurningDirect.py} +2 -2
  119. tests/models/test_resourceUseNotRelevant.py +27 -0
  120. tests/models/{akagiEtAl2011AndIpcc2006/test_utils.py → utils/test_cropResidue.py} +6 -6
  121. hestia_earth/models/akagiEtAl2011AndIpcc2006/ch4ToAirCropResidueBurning.py +0 -57
  122. hestia_earth/models/akagiEtAl2011AndIpcc2006/nh3ToAirCropResidueBurning.py +0 -57
  123. hestia_earth/models/akagiEtAl2011AndIpcc2006/noxToAirCropResidueBurning.py +0 -57
  124. hestia_earth/models/akagiEtAl2011AndIpcc2006/utils.py +0 -15
  125. tests/models/akagiEtAl2011AndIpcc2006/test_ch4ToAirCropResidueBurning.py +0 -45
  126. tests/models/akagiEtAl2011AndIpcc2006/test_n2OToAirCropResidueBurningDirect.py +0 -46
  127. {hestia_earth_models-0.72.2.dist-info → hestia_earth_models-0.73.0.dist-info}/LICENSE +0 -0
  128. {hestia_earth_models-0.72.2.dist-info → hestia_earth_models-0.73.0.dist-info}/WHEEL +0 -0
  129. {hestia_earth_models-0.72.2.dist-info → hestia_earth_models-0.73.0.dist-info}/top_level.txt +0 -0
  130. /tests/models/{akagiEtAl2011AndIpcc2006 → akagiEtAl2011}/__init__.py +0 -0
@@ -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
 
@@ -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
 
@@ -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):
@@ -111,7 +111,7 @@ def _is_below_max_size(term: str, site: dict) -> bool:
111
111
  current_size = _cached_value(site, CACHE_AREA_SIZE) or get_area_size(site)
112
112
  if current_size is not None:
113
113
  logRequirements(site, model=MODEL, term=term,
114
- current_size=int(current_size),
114
+ current_size=round(float(current_size), 5),
115
115
  max_area_size=MAX_AREA_SIZE)
116
116
  return current_size <= MAX_AREA_SIZE
117
117
  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.blank_node 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 []
@@ -0,0 +1,110 @@
1
+ from hestia_earth.schema import IndicatorMethodTier, TermTermType
2
+ from hestia_earth.utils.tools import flatten, safe_parse_float
3
+ from hestia_earth.utils.lookup import download_lookup, lookup_term_ids
4
+
5
+ from hestia_earth.models.log import logRequirements, logShouldRun
6
+ from hestia_earth.models.utils import _omit
7
+ from hestia_earth.models.utils.indicator import _new_indicator
8
+ from hestia_earth.models.utils.background_emissions import no_gap_filled_background_emissions
9
+ from hestia_earth.models.utils.blank_node import get_lookup_value
10
+ from hestia_earth.models.utils.input import unique_background_inputs
11
+ from . import MODEL
12
+
13
+ REQUIREMENTS = {
14
+ "ImpactAssessment": {
15
+ "cycle": {
16
+ "@type": "Cycle",
17
+ "inputs": [{
18
+ "@type": "Input",
19
+ "value": "> 0",
20
+ "none": {
21
+ "impactAssessment": {"@type": "ImpactAssessment"},
22
+ "fromCycle": "True",
23
+ "producedInCycle": "True"
24
+ }
25
+ }]
26
+ }
27
+ }
28
+ }
29
+ RETURNS = {
30
+ "Indicator": [{
31
+ "value": "",
32
+ "inputs": "",
33
+ "methodTier": "background"
34
+ }]
35
+ }
36
+ LOOKUPS = {
37
+ "organicFertiliser": "backgroundEmissionsResourceUseDefaultValue"
38
+ }
39
+ MODEL_KEY = 'default_resourceUse'
40
+ TIER = IndicatorMethodTier.BACKGROUND.value
41
+
42
+
43
+ def _indicator(term_id: str, value: float, input: dict):
44
+ indicator = _new_indicator(term_id, MODEL)
45
+ indicator['value'] = value
46
+ indicator['inputs'] = [input]
47
+ indicator['methodTier'] = TIER
48
+ return indicator
49
+
50
+
51
+ def _default_value(input: dict):
52
+ return safe_parse_float(get_lookup_value(input.get('term', {}), LOOKUPS['organicFertiliser']), default=None)
53
+
54
+
55
+ def _run_input(impact: dict):
56
+ required_resourceUse_term_ids = [
57
+ id for id in lookup_term_ids(download_lookup(f"{TermTermType.RESOURCEUSE.value}.csv"))
58
+ if id.endswith('InputsProduction')
59
+ ]
60
+
61
+ def run(input: dict):
62
+ input_term = input.get('input').get('term')
63
+ term_id = input_term.get('@id')
64
+ value = input.get('default-value-from-lookup')
65
+
66
+ for emission_id in required_resourceUse_term_ids:
67
+ logShouldRun(impact, MODEL, term_id, True, methodTier=TIER, model_key=MODEL_KEY, emission_id=emission_id)
68
+
69
+ return [
70
+ _indicator(term_id, value, input_term) for term_id in required_resourceUse_term_ids
71
+ ]
72
+
73
+ return run
74
+
75
+
76
+ def _should_run(impact: dict):
77
+ no_gap_filled_background_emissions_func = no_gap_filled_background_emissions(
78
+ node=impact, list_key='emissionsResourceUse', term_type=TermTermType.RESOURCEUSE
79
+ )
80
+
81
+ inputs = [
82
+ input | {
83
+ 'default-value-from-lookup': _default_value(input['input']),
84
+ 'no-gap-filled-background-emissions': no_gap_filled_background_emissions_func(input['input'])
85
+ }
86
+ for input in unique_background_inputs(impact.get('cycle', {}))
87
+ ]
88
+ valid_inputs = [
89
+ input for input in inputs
90
+ if all([
91
+ input.get('default-value-from-lookup') is not None,
92
+ input.get('no-gap-filled-background-emissions')
93
+ ])
94
+ ]
95
+
96
+ should_run = all([bool(valid_inputs)])
97
+
98
+ for input in inputs:
99
+ term_id = input.get('input').get('term', {}).get('@id')
100
+
101
+ logRequirements(impact, model=MODEL, term=term_id, model_key=MODEL_KEY,
102
+ **_omit(input, ['input', 'input-value']))
103
+ logShouldRun(impact, MODEL, term_id, should_run, methodTier=TIER, model_key=MODEL_KEY)
104
+
105
+ return should_run, valid_inputs
106
+
107
+
108
+ def run(impact: dict):
109
+ should_run, grouped_inputs = _should_run(impact)
110
+ return flatten(map(_run_input(impact), grouped_inputs)) if should_run else []
@@ -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([0 < waterSalinity < 500])
37
+ should_run = all([0 < (waterSalinity or 0) < 500])
38
38
  logShouldRun(site, MODEL, TERM_ID, should_run)
39
39
  return should_run
40
40
 
@@ -88,15 +88,15 @@ def _include_term_ids(term_id: str):
88
88
  def _run_input(cycle: dict, input: dict):
89
89
  term_id = input.get('term', {}).get('@id')
90
90
  input_term_ids = _include_term_ids(term_id)
91
- nitrogenContent = safe_parse_float(get_term_lookup(term_id, 'nitrogenContent'), 0)
92
- nitrogenContent_min = safe_parse_float(get_term_lookup(term_id, 'nitrogenContent-min'), None)
93
- nitrogenContent_max = safe_parse_float(get_term_lookup(term_id, 'nitrogenContent-max'), None)
94
- phosphateContentAsP2O5 = safe_parse_float(get_term_lookup(term_id, 'phosphateContentAsP2O5'), 0)
95
- phosphateContentAsP2O5_min = safe_parse_float(get_term_lookup(term_id, 'phosphateContentAsP2O5-min'), None)
96
- phosphateContentAsP2O5_max = safe_parse_float(get_term_lookup(term_id, 'phosphateContentAsP2O5-max'), None)
97
- potassiumContentAsK2O = safe_parse_float(get_term_lookup(term_id, 'potassiumContentAsK2O'), 0)
98
- potassiumContentAsK2O_min = safe_parse_float(get_term_lookup(term_id, 'potassiumContentAsK2O-min'), None)
99
- potassiumContentAsK2O_max = safe_parse_float(get_term_lookup(term_id, 'potassiumContentAsK2O-max'), None)
91
+ nitrogenContent = safe_parse_float(get_term_lookup(term_id, 'nitrogenContent'), default=0)
92
+ nitrogenContent_min = safe_parse_float(get_term_lookup(term_id, 'nitrogenContent-min'), default=None)
93
+ nitrogenContent_max = safe_parse_float(get_term_lookup(term_id, 'nitrogenContent-max'), default=None)
94
+ phosphateContentAsP2O5 = safe_parse_float(get_term_lookup(term_id, 'phosphateContentAsP2O5'), default=0)
95
+ phosphateContentAsP2O5_min = safe_parse_float(get_term_lookup(term_id, 'phosphateContentAsP2O5-min'), default=None)
96
+ phosphateContentAsP2O5_max = safe_parse_float(get_term_lookup(term_id, 'phosphateContentAsP2O5-max'), default=None)
97
+ potassiumContentAsK2O = safe_parse_float(get_term_lookup(term_id, 'potassiumContentAsK2O'), default=0)
98
+ potassiumContentAsK2O_min = safe_parse_float(get_term_lookup(term_id, 'potassiumContentAsK2O-min'), default=None)
99
+ potassiumContentAsK2O_max = safe_parse_float(get_term_lookup(term_id, 'potassiumContentAsK2O-max'), default=None)
100
100
 
101
101
  from_units = input.get('term', {}).get('units')
102
102
  input_value = list_sum(input.get('value'))
@@ -166,9 +166,9 @@ def _run_input(cycle: dict, input: dict):
166
166
  def _should_run_input(cycle: dict, input: dict):
167
167
  term_id = input.get('term', {}).get('@id')
168
168
  has_value = list_sum(input.get('value', [])) > 0
169
- nitrogenContent = safe_parse_float(get_term_lookup(term_id, 'nitrogenContent'), None)
170
- phosphateContentAsP2O5 = safe_parse_float(get_term_lookup(term_id, 'phosphateContentAsP2O5'), None)
171
- potassiumContentAsK2O = safe_parse_float(get_term_lookup(term_id, 'potassiumContentAsK2O'), None)
169
+ nitrogenContent = safe_parse_float(get_term_lookup(term_id, 'nitrogenContent'), default=None)
170
+ phosphateContentAsP2O5 = safe_parse_float(get_term_lookup(term_id, 'phosphateContentAsP2O5'), default=None)
171
+ potassiumContentAsK2O = safe_parse_float(get_term_lookup(term_id, 'potassiumContentAsK2O'), default=None)
172
172
 
173
173
  # skip inputs that already have all the inlcuded term with a value
174
174
  inputs = cycle.get('inputs', [])
@@ -163,7 +163,7 @@ def _should_group_landCover(management_node: dict):
163
163
  )
164
164
 
165
165
 
166
- def get_changes(country_id: str, end_year: int) -> tuple[dict, bool]:
166
+ def _get_changes(country_id: str, end_year: int) -> tuple[dict, bool]:
167
167
  """
168
168
  For each entry in ALL_LAND_USE_TERMS, creates a key: value in output dictionary, also TOTAL
169
169
  """
@@ -178,7 +178,7 @@ def get_changes(country_id: str, end_year: int) -> tuple[dict, bool]:
178
178
  )
179
179
  for land_use_term in ALL_LAND_USE_TERMS + [LAND_AREA]
180
180
  }
181
- missing_changes = any(val is None for val in changes_dict.values())
181
+ missing_changes = [k for k, v in changes_dict.items() if v is None]
182
182
  changes_dict = {k: v if v is not None else 0 for k, v in changes_dict.items()}
183
183
  changes_dict[TOTAL_AGRICULTURAL_CHANGE] = (
184
184
  float(changes_dict.get(TOTAL_CROPLAND, 0)) + float(changes_dict.get(PERMANENT_PASTURE, 0))
@@ -609,7 +609,7 @@ def _should_run_historical_land_use_change_single_crop(
609
609
  ) -> tuple[bool, dict]:
610
610
  """Calculate land use change percentages for a single management node/crop."""
611
611
  # (C-H).
612
- changes, missing_changes = get_changes(country_id=country_id, end_year=end_year)
612
+ changes, missing_changes = _get_changes(country_id=country_id, end_year=end_year)
613
613
 
614
614
  # (L). Estimate maximum forest loss
615
615
  forest_loss = _estimate_maximum_forest_change(
@@ -749,12 +749,13 @@ def _should_run_historical_land_use_change_single_crop(
749
749
  land_use_type=land_use_type,
750
750
  country_id=country_id,
751
751
  changes=log_as_table(changes),
752
+ missing_changes=log_as_table(missing_changes),
752
753
  site_area=log_as_table(capped_site_area),
753
754
  sum_of_site_areas_is_100=sum_of_site_areas_is_100,
754
755
  site_type_allowed=site_type_allowed
755
756
  )
756
757
 
757
- should_run = all([not missing_changes, country_id, site_type_allowed, sum_of_site_areas_is_100])
758
+ should_run = all([len(missing_changes) == 0, country_id, site_type_allowed, sum_of_site_areas_is_100])
758
759
  logShouldRun(site, MODEL, term.get("@id"), should_run, model_key=MODEL_KEY)
759
760
 
760
761
  return should_run, capped_site_area
@@ -831,7 +832,8 @@ def _should_run(site: dict) -> tuple[bool, list, dict]:
831
832
  land_use_type=land_use_type,
832
833
  allowed_land_use_types=';'.join(ALLOWED_LAND_USE_TYPES),
833
834
  has_no_prior_land_cover_data=has_no_prior_land_cover_data,
834
- management_nodes=log_as_table([_omit(n, ['term']) for n in relevant_nodes]))
835
+ management_nodes=log_as_table([_omit(n, ['term']) for n in relevant_nodes]),
836
+ should_run_nodes=should_run_nodes)
835
837
 
836
838
  should_run = all([land_use_type, has_no_prior_land_cover_data, should_run_nodes])
837
839
  logShouldRun(site, MODEL, None, should_run, model_key=MODEL_KEY)
@@ -27,6 +27,9 @@ RETURNS = {
27
27
  "previousLandCover": ""
28
28
  }]
29
29
  }
30
+ LOOKUPS = {
31
+ "crop": "IPCC_LAND_USE_CATEGORY"
32
+ }
30
33
  TERM_ID = 'landTransformation100YearAverageDuringCycle'
31
34
  _HISTORIC_DATE_OFFSET = 100
32
35
 
@@ -27,6 +27,9 @@ RETURNS = {
27
27
  "previousLandCover": ""
28
28
  }]
29
29
  }
30
+ LOOKUPS = {
31
+ "crop": "IPCC_LAND_USE_CATEGORY"
32
+ }
30
33
  TERM_ID = 'landTransformation20YearAverageDuringCycle'
31
34
  _HISTORIC_DATE_OFFSET = 20
32
35
 
@@ -66,7 +66,7 @@ def _should_run(cycle: dict):
66
66
  ) or next(
67
67
  (p for p in product.get('properties', []) if p.get('term', {}).get('@id').endswith('PerHead')), {}
68
68
  )
69
- propertyPerHead_value = safe_parse_float(propertyPerHead.get('value'), None)
69
+ propertyPerHead_value = safe_parse_float(propertyPerHead.get('value'), default=None)
70
70
 
71
71
  # make sure the `liveAnimal` Term is not already present as a Product or Input
72
72
  term_id = get_liveAnimal_term_id(product, model_key=MODEL_KEY)
@@ -95,7 +95,7 @@ _INPUT_RULES = {
95
95
  TermTermType.INORGANICFERTILISER.value: (
96
96
  (
97
97
  TermTermType.INORGANICFERTILISER.value, # Lookup column
98
- lambda x: safe_parse_float(x) > 0, # Condition
98
+ lambda x: safe_parse_float(x, default=0) > 0, # Condition
99
99
  _INORGANIC_NITROGEN_FERTILISER_USED_TERM_ID # New term.
100
100
  ),
101
101
  ),
@@ -45,7 +45,7 @@ def _should_run(site: dict):
45
45
  logRequirements(site, model=MODEL, term=TERM_ID,
46
46
  temperature=temperature)
47
47
 
48
- should_run = temperature > 0
48
+ should_run = all([(temperature or 0) > 0])
49
49
  logShouldRun(site, MODEL, TERM_ID, should_run)
50
50
  return should_run, temperature
51
51
 
@@ -189,8 +189,8 @@ def _run_calculation(site: dict, depth_key: str, measurement_nodes: list[dict])
189
189
  A list of `organicCarbonPerHa` [Measurement nodes](https://www.hestia.earth/schema/Measurement).
190
190
  """
191
191
  split = depth_key.split('_') # "a_to_b"
192
- depth_upper = safe_parse_float(split[0])
193
- depth_lower = safe_parse_float(split[2])
192
+ depth_upper = safe_parse_float(split[0], default=0)
193
+ depth_lower = safe_parse_float(split[2], default=0)
194
194
 
195
195
  soil_bulk_density_nodes = [
196
196
  node for node in measurement_nodes if node.get('term', {}).get('@id') == SOIL_BULK_DENSITY_TERM_ID
@@ -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([waterSalinity > 18000])
37
+ should_run = all([(waterSalinity or 0) > 18000])
38
38
  logShouldRun(site, MODEL, TERM_ID, should_run)
39
39
  return should_run
40
40