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
@@ -31,7 +31,7 @@ def _product(value: float):
31
31
 
32
32
  def _get_lookup_value(product: dict):
33
33
  term_id = product.get('term', {}).get('@id', '')
34
- return safe_parse_float(get_crop_lookup_value(MODEL, TERM_ID, term_id, LOOKUPS['crop']), None)
34
+ return safe_parse_float(get_crop_lookup_value(MODEL, TERM_ID, term_id, LOOKUPS['crop']), default=None)
35
35
 
36
36
 
37
37
  def _run(product: dict):
@@ -31,7 +31,7 @@ def _product(value: float):
31
31
 
32
32
  def _get_lookup_value(product: dict):
33
33
  term_id = product.get('term', {}).get('@id', '')
34
- return safe_parse_float(get_crop_lookup_value(MODEL, TERM_ID, term_id, LOOKUPS['crop']), None)
34
+ return safe_parse_float(get_crop_lookup_value(MODEL, TERM_ID, term_id, LOOKUPS['crop']), default=None)
35
35
 
36
36
 
37
37
  def _run(product: dict):
@@ -131,7 +131,7 @@ def _run(excreta_vs_products: list, excreta_n_products: list, mass_balance_items
131
131
  def _get_carbonContent(cycle: dict):
132
132
  primary_prod = find_primary_product(cycle) or {}
133
133
  return safe_parse_float(
134
- get_lookup_value(primary_prod.get('term', {}), 'carbonContent', model=MODEL, model_key=MODEL_KEY)
134
+ get_lookup_value(primary_prod.get('term', {}), 'carbonContent', model=MODEL, model_key=MODEL_KEY), default=0
135
135
  ) / 100
136
136
 
137
137
 
@@ -58,7 +58,7 @@ def _get_conveyancing_efficiency(impact_assessment: dict, product: dict):
58
58
  debugValues(impact_assessment, model=MODEL, term=TERM_ID,
59
59
  grouping=grouping,
60
60
  conveyancing_efficiency=value)
61
- return safe_parse_float(value, 1)
61
+ return safe_parse_float(value, default=1)
62
62
 
63
63
 
64
64
  def _run(impact_assessment: dict, product: dict, irrigation: float):
@@ -29,7 +29,7 @@ def _practice(value: float):
29
29
 
30
30
  def _get_value(product: dict):
31
31
  term_id = product.get('term', {}).get('@id', '')
32
- return safe_parse_float(get_crop_lookup_value(MODEL, TERM_ID, term_id, LOOKUPS['crop']), None)
32
+ return safe_parse_float(get_crop_lookup_value(MODEL, TERM_ID, term_id, LOOKUPS['crop']), default=None)
33
33
 
34
34
 
35
35
  def _run(product: dict):
@@ -23,7 +23,7 @@ TERM_ID = 'nurseryDensity'
23
23
 
24
24
  def _get_value(product: dict):
25
25
  term_id = product.get('term', {}).get('@id', '')
26
- return safe_parse_float(get_crop_lookup_value(MODEL, TERM_ID, term_id, LOOKUPS['crop']), None)
26
+ return safe_parse_float(get_crop_lookup_value(MODEL, TERM_ID, term_id, LOOKUPS['crop']), default=None)
27
27
 
28
28
 
29
29
  def _practice(value: float):
@@ -29,7 +29,7 @@ def _practice(value: float):
29
29
 
30
30
  def _get_value(product: dict):
31
31
  term_id = product.get('term', {}).get('@id', '')
32
- return safe_parse_float(get_crop_lookup_value(MODEL, TERM_ID, term_id, LOOKUPS['crop']), None)
32
+ return safe_parse_float(get_crop_lookup_value(MODEL, TERM_ID, term_id, LOOKUPS['crop']), default=None)
33
33
 
34
34
 
35
35
  def run(cycle: dict):
@@ -29,7 +29,7 @@ def _practice(value: float):
29
29
 
30
30
  def _get_value(product: dict):
31
31
  term_id = product.get('term', {}).get('@id', '')
32
- return safe_parse_float(get_crop_lookup_value(MODEL, TERM_ID, term_id, LOOKUPS['crop']), None)
32
+ return safe_parse_float(get_crop_lookup_value(MODEL, TERM_ID, term_id, LOOKUPS['crop']), default=None)
33
33
 
34
34
 
35
35
  def run(cycle: dict):
@@ -23,7 +23,7 @@ TERM_ID = 'plantationLifespan'
23
23
 
24
24
  def _get_value(product: dict):
25
25
  term_id = product.get('term', {}).get('@id', '')
26
- return safe_parse_float(get_crop_lookup_value(MODEL, TERM_ID, term_id, LOOKUPS['crop']), None)
26
+ return safe_parse_float(get_crop_lookup_value(MODEL, TERM_ID, term_id, LOOKUPS['crop']), default=None)
27
27
 
28
28
 
29
29
  def _practice(value: float):
@@ -33,7 +33,9 @@ def _get_value(cycle: dict):
33
33
  def get(product: dict):
34
34
  term_id = product.get('term', {}).get('@id', '')
35
35
  plantationLifespan = get_plantationLifespan(product)
36
- nonProductiveLifespan = safe_parse_float(get_crop_lookup_value(MODEL, TERM_ID, term_id, LOOKUPS['crop']), None)
36
+ nonProductiveLifespan = safe_parse_float(
37
+ get_crop_lookup_value(MODEL, TERM_ID, term_id, LOOKUPS['crop']), default=None
38
+ )
37
39
  product_id = product.get('term').get('@id')
38
40
  product_id_logs = log_as_table({
39
41
  'plantationLifespan': plantationLifespan,
@@ -35,7 +35,7 @@ def _input(value: float):
35
35
 
36
36
  def _get_value(product: dict):
37
37
  term_id = product.get('term', {}).get('@id', '')
38
- return safe_parse_float(get_crop_lookup_value(MODEL, TERM_ID, term_id, LOOKUPS['crop']), None)
38
+ return safe_parse_float(get_crop_lookup_value(MODEL, TERM_ID, term_id, LOOKUPS['crop']), default=None)
39
39
 
40
40
 
41
41
  def _run(product: dict, plantationLifespan: float, cycleDuration: float):
@@ -0,0 +1,65 @@
1
+ from hestia_earth.schema import IndicatorMethodTier, TermTermType
2
+ from hestia_earth.utils.lookup import download_lookup, lookup_term_ids
3
+ from hestia_earth.utils.lookup_utils import is_in_system_boundary
4
+
5
+ from hestia_earth.models.log import logRequirements, logShouldRun
6
+ from hestia_earth.models.utils.indicator import _new_indicator
7
+ from hestia_earth.models.utils.blank_node import _run_required
8
+ from hestia_earth.models.utils.impact_assessment import get_site
9
+ from hestia_earth.models.utils.site import get_land_cover_term_id
10
+
11
+ REQUIREMENTS = {
12
+ "ImpactAssessment": {
13
+ "emissionsResourceUse": [{"@type": "Indicator"}]
14
+ }
15
+ }
16
+ RETURNS = {
17
+ "Indicator": [{
18
+ "value": "0",
19
+ "methodTier": "not relevant"
20
+ }]
21
+ }
22
+ LOOKUPS = {
23
+ "resourceUse": [
24
+ "term.id",
25
+ "inHestiaDefaultSystemBoundary",
26
+ "siteTypesAllowed"
27
+ ]
28
+ }
29
+ MODEL = 'resourceUseNotRelevant'
30
+ TIER = IndicatorMethodTier.NOT_RELEVANT.value
31
+
32
+
33
+ def _indicator(term_id: str, land_cover_id: str):
34
+ indicator = _new_indicator(term_id, MODEL, land_cover_id)
35
+ indicator['value'] = 0
36
+ indicator['methodTier'] = TIER
37
+ return indicator
38
+
39
+
40
+ def _resourceUse_ids():
41
+ return lookup_term_ids(download_lookup(f"{TermTermType.RESOURCEUSE.value}.csv"))
42
+
43
+
44
+ def _should_run_resourceUse(impact: dict):
45
+ def run(term_id: str):
46
+ is_not_relevant = not _run_required(MODEL, term_id, impact.get('cycle', {}))
47
+ in_system_boundary = is_in_system_boundary(term_id)
48
+
49
+ should_run = all([is_not_relevant, in_system_boundary])
50
+ if should_run:
51
+ # no need to show the model failed
52
+ logRequirements(impact, model=MODEL, term=term_id,
53
+ is_not_relevant=is_not_relevant,
54
+ in_system_boundary=in_system_boundary,
55
+ run_required=False)
56
+ logShouldRun(impact, MODEL, term_id, should_run)
57
+ return should_run
58
+ return run
59
+
60
+
61
+ def run(_, impact: dict):
62
+ term_ids = _resourceUse_ids()
63
+ term_ids = list(filter(_should_run_resourceUse(impact), term_ids))
64
+ land_cover_term_id = get_land_cover_term_id(get_site(impact).get('siteType'))
65
+ return [_indicator(term_id, land_cover_term_id) for term_id in term_ids]
@@ -80,7 +80,7 @@ def _should_run(cycle: dict):
80
80
  measurements = site.get('measurements', [])
81
81
 
82
82
  def _get_measurement_content(term_id: str):
83
- return most_relevant_measurement_value(measurements, term_id, end_date)
83
+ return most_relevant_measurement_value(measurements, term_id, end_date, default=None)
84
84
 
85
85
  nla_environment = _get_measurement_content('nutrientLossToAquaticEnvironment')
86
86
  soil_nitrogen_content = _get_measurement_content('totalNitrogenPerKgSoil')
@@ -91,6 +91,7 @@ def _should_run(cycle: dict):
91
91
 
92
92
  precipitation = _get_measurement_content('precipitationAnnual')
93
93
  inputs_water = get_water_input(cycle)
94
+ total_water = list_sum([(inputs_water or 0)/10, precipitation or 0])
94
95
 
95
96
  practice_factor = get_practice_factor(TERM_ID, site)
96
97
  pcorr = get_pcorr(slope / 100) if slope is not None else None
@@ -100,7 +101,7 @@ def _should_run(cycle: dict):
100
101
  list_of_contents_for_A = [
101
102
  practice_factor, erodibility, slope_length,
102
103
  pcorr, p_ef_c1, ef_p_c2]
103
- list_of_contents_for_R = [heavy_winter_precipitation, list_sum([(inputs_water or 0)/10, precipitation or 0])]
104
+ list_of_contents_for_R = [heavy_winter_precipitation, total_water]
104
105
  list_of_contents_for_value = [nla_environment, soil_nitrogen_content]
105
106
 
106
107
  logRequirements(cycle, model=MODEL, term=TERM_ID,
@@ -118,8 +119,9 @@ def _should_run(cycle: dict):
118
119
  soil_nitrogen_content=soil_nitrogen_content)
119
120
 
120
121
  should_run = all([
122
+ heavy_winter_precipitation is not None,
123
+ total_water > 0,
121
124
  all(list_of_contents_for_A),
122
- all(list_of_contents_for_R),
123
125
  all(list_of_contents_for_value)
124
126
  ])
125
127
  logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
@@ -80,7 +80,7 @@ def _should_run(cycle: dict):
80
80
  measurements = site.get('measurements', [])
81
81
 
82
82
  def _get_measurement_content(term_id: str):
83
- return most_relevant_measurement_value(measurements, term_id, end_date)
83
+ return most_relevant_measurement_value(measurements, term_id, end_date, default=None)
84
84
 
85
85
  nla_environment = _get_measurement_content('nutrientLossToAquaticEnvironment')
86
86
  soil_phosphorus_content = _get_measurement_content('totalPhosphorusPerKgSoil')
@@ -91,6 +91,7 @@ def _should_run(cycle: dict):
91
91
 
92
92
  precipitation = _get_measurement_content('precipitationAnnual')
93
93
  inputs_water = get_water_input(cycle)
94
+ total_water = list_sum([(inputs_water or 0)/10, precipitation or 0])
94
95
 
95
96
  practice_factor = get_practice_factor(TERM_ID, site)
96
97
  pcorr = get_pcorr(slope / 100) if slope is not None else None
@@ -100,7 +101,7 @@ def _should_run(cycle: dict):
100
101
  list_of_contents_for_A = [
101
102
  practice_factor, erodibility, slope_length,
102
103
  pcorr, p_ef_c1, ef_p_c2]
103
- list_of_contents_for_R = [heavy_winter_precipitation, list_sum([(inputs_water or 0)/10, precipitation or 0])]
104
+ list_of_contents_for_R = [heavy_winter_precipitation, total_water]
104
105
  list_of_contents_for_value = [nla_environment, soil_phosphorus_content]
105
106
 
106
107
  logRequirements(cycle, model=MODEL, term=TERM_ID,
@@ -118,8 +119,9 @@ def _should_run(cycle: dict):
118
119
  soil_phosphorus_content=soil_phosphorus_content)
119
120
 
120
121
  should_run = all([
122
+ heavy_winter_precipitation is not None,
123
+ total_water > 0,
121
124
  all(list_of_contents_for_A),
122
- all(list_of_contents_for_R),
123
125
  all(list_of_contents_for_value)
124
126
  ])
125
127
  logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
@@ -43,25 +43,26 @@ def get_pcorr(slope: float):
43
43
 
44
44
 
45
45
  def _get_C2_factor(term_id: str, term: dict):
46
- return safe_parse_float(get_lookup_value(term, 'C2_FACTORS', model=MODEL, term=term_id), None)
46
+ return safe_parse_float(get_lookup_value(term, 'C2_FACTORS', model=MODEL, term=term_id), default=None)
47
47
 
48
48
 
49
49
  def get_practice_factor(term_id: str, site: dict):
50
50
  return safe_parse_float(
51
- get_lookup_value(site.get('country', {}), 'Practice_Factor', model=MODEL, term=term_id), None)
51
+ get_lookup_value(site.get('country', {}), 'Practice_Factor', model=MODEL, term=term_id), default=None)
52
52
 
53
53
 
54
54
  def get_p_ef_c1(term_id: str, cycle: dict):
55
55
  primary_product = find_primary_product(cycle) or {}
56
56
  return safe_parse_float(
57
- get_lookup_value(primary_product.get('term', {}), 'P_EF_C1', model=MODEL, term=term_id), None)
57
+ get_lookup_value(primary_product.get('term', {}), 'P_EF_C1', model=MODEL, term=term_id), default=None)
58
58
 
59
59
 
60
60
  def get_ef_p_c2(term_id: str, cycle: dict):
61
61
  tillage = _get_tillage(cycle)
62
62
  country = cycle.get('site', {}).get('country', {})
63
63
  return _get_C2_factor(term_id, tillage) if tillage else safe_parse_float(
64
- get_lookup_value(country, 'EF_P_C2', model=MODEL, term=term_id), None)
64
+ get_lookup_value(country, 'EF_P_C2', model=MODEL, term=term_id), default=None
65
+ )
65
66
 
66
67
 
67
68
  def get_water_input(cycle: dict):
@@ -150,7 +150,7 @@ def _get_value(cycle: dict, content_list_of_items: list, N_total: float, term=TE
150
150
  sand_factor = _sand_factor(sand, clay)
151
151
  eco_factor = get_ecoClimateZone_lookup_value(ecoClimateZone, 'STEHFEST_BOUWMAN_2006_N2O-N_FACTOR')
152
152
  crop_grouping_factor = N2O_FACTORS_BY_CROP[crop_grouping]
153
- sum_factors = sum([carbon_factor, soil_factor, sand_factor, eco_factor, crop_grouping_factor])
153
+ sum_factors = sum([carbon_factor, soil_factor, sand_factor, eco_factor or 0, crop_grouping_factor])
154
154
  conversion_unit = get_atomic_conversion(Units.KG_N2O, Units.TO_N)
155
155
 
156
156
  try:
@@ -85,10 +85,10 @@ def _should_run(cycle: dict, term=TERM_ID, tier=TIER):
85
85
 
86
86
 
87
87
  def _get_value(cycle: dict, country: dict, N_total: float, term=TERM_ID):
88
- noxToAirSoilFlux = safe_parse_float(get_lookup_value(country, LOOKUPS['region'], model=MODEL, term=TERM_ID))
88
+ EF_NOX = safe_parse_float(get_lookup_value(country, LOOKUPS['region'], model=MODEL, term=TERM_ID), default=0)
89
89
  debugValues(cycle, model=MODEL, term=term,
90
- noxToAirSoilFlux=noxToAirSoilFlux)
91
- return noxToAirSoilFlux * N_total
90
+ EF_NOX=EF_NOX)
91
+ return EF_NOX * N_total
92
92
 
93
93
 
94
94
  def _run(cycle: dict, country: dict, N_total: float):
@@ -1,5 +1,5 @@
1
1
  from hestia_earth.schema import TermTermType
2
- from hestia_earth.utils.model import find_term_match
2
+ from hestia_earth.utils.model import find_term_match, filter_list_term_type
3
3
  from hestia_earth.utils.tools import flatten
4
4
  from hestia_earth.utils.emission import cycle_emissions_in_system_boundary
5
5
 
@@ -35,8 +35,10 @@ def get_background_inputs(cycle: dict, extra_inputs: list = []):
35
35
  return list(filter(_should_run_input(cycle.get('products', [])), inputs))
36
36
 
37
37
 
38
- def no_gap_filled_background_emissions(cycle: dict):
39
- emissions = cycle.get('emissions', [])
38
+ def no_gap_filled_background_emissions(
39
+ node: dict, list_key: str = 'emissions', term_type: TermTermType = TermTermType.EMISSION
40
+ ):
41
+ blank_nodes = filter_list_term_type(node.get(list_key, []), term_type)
40
42
 
41
43
  def check_input(input: dict):
42
44
  input_term_id = input.get('term', {}).get('@id')
@@ -44,12 +46,12 @@ def no_gap_filled_background_emissions(cycle: dict):
44
46
  animal_term_id = input.get('animal', {}).get('@id')
45
47
 
46
48
  return not any([
47
- is_from_model(emission)
48
- for emission in emissions
49
+ is_from_model(blank_node)
50
+ for blank_node in blank_nodes
49
51
  if all([
50
- any([i.get('@id') == input_term_id for i in emission.get('inputs', [])]),
51
- emission.get('operation', {}).get('@id') == operation_term_id,
52
- emission.get('animal', {}).get('@id') == animal_term_id
52
+ any([i.get('@id') == input_term_id for i in blank_node.get('inputs', [])]),
53
+ blank_node.get('operation', {}).get('@id') == operation_term_id,
54
+ blank_node.get('animal', {}).get('@id') == animal_term_id
53
55
  ])
54
56
  ])
55
57
 
@@ -67,10 +69,12 @@ def all_background_emission_term_ids(cycle: dict):
67
69
  def log_missing_emissions(cycle: dict, **log_args):
68
70
  all_emission_term_ids = all_background_emission_term_ids(cycle)
69
71
 
70
- def log_input(input_term_id: str, included_emission_term_ids: list):
72
+ def log_input(input_term_id: str, included_emission_term_ids: list, **extra_log_args):
71
73
  missing_emission_term_ids = [
72
74
  term_id for term_id in all_emission_term_ids if term_id not in included_emission_term_ids
73
75
  ]
74
76
  for emission_id in missing_emission_term_ids:
75
- logShouldRun(cycle, term=input_term_id, should_run=False, emission_id=emission_id, **log_args)
77
+ logShouldRun(cycle, term=input_term_id, should_run=False, emission_id=emission_id,
78
+ **log_args,
79
+ **extra_log_args)
76
80
  return log_input
@@ -159,7 +159,7 @@ def _run_required(model: str, term_id: str, data: dict):
159
159
  input_id_allowed=input_id_allowed,
160
160
  input_termType_allowed=input_termType_allowed)
161
161
  # logging this for the model would cause issues parsing statuses
162
- if model != 'emissionNotRelevant':
162
+ if not model.endswith('NotRelevant'):
163
163
  debugValues(data, model=model, term=term_id, run_required=run_required)
164
164
  return run_required
165
165
 
@@ -1326,16 +1326,18 @@ def get_inputs_from_properties(input: dict, term_types: Union[TermTermType, List
1326
1326
  dict
1327
1327
  A dictionary of nodes grouped by latest date, in the format `{date: list[node]}`.
1328
1328
  """
1329
+ term = input.get('term', {})
1329
1330
  input_value = list_sum(input.get('value', []))
1330
1331
  properties = (
1331
1332
  input.get('properties') or
1332
- input.get('term', {}).get('defaultProperties') or
1333
- download_term(input.get('term', {})).get('defaultProperties')
1333
+ term.get('defaultProperties') or
1334
+ download_term(term).get('defaultProperties')
1334
1335
  )
1335
1336
  inputs = non_empty_list([
1336
1337
  {
1337
1338
  'term': p.get('key'),
1338
- 'value': [(p.get('value') / 100) * (p.get('share', 100) / 100) * input_value]
1339
+ 'value': [(p.get('value') / 100) * (p.get('share', 100) / 100) * input_value],
1340
+ 'parent': term
1339
1341
  } for p in (properties or []) if all([p.get('key'), p.get('value')])
1340
1342
  ]) if input_value > 0 else []
1341
1343
  return filter_list_term_type(inputs, term_types)
@@ -30,7 +30,7 @@ def get_N2ON_fertiliser_coeff_from_primary_product(model: str, log_id: str, cycl
30
30
  product = find_primary_product(cycle)
31
31
  term_id = product.get('term', {}).get('@id') if product else None
32
32
  percent = get_crop_lookup_value(model, log_id, term_id, 'N2ON_FERT') if term_id else None
33
- return safe_parse_float(percent, 0.01)
33
+ return safe_parse_float(percent, default=0.01)
34
34
 
35
35
 
36
36
  def is_plantation(model: str, log_id: str, term_id: str):
@@ -1,3 +1,9 @@
1
+ from hestia_earth.schema import TermTermType
2
+ from hestia_earth.utils.model import find_term_match
3
+ from hestia_earth.utils.tools import flatten
4
+
5
+ from .completeness import _is_term_type_complete
6
+
1
7
  PRODUCT_ID_TO_PRACTICES_ID = [
2
8
  {'product': 'aboveGroundCropResidueRemoved', 'practices': ['residueRemoved']},
3
9
  {'product': 'aboveGroundCropResidueIncorporated', 'practices': [
@@ -11,3 +17,13 @@ PRODUCT_ID_TO_PRACTICES_ID = [
11
17
 
12
18
 
13
19
  def crop_residue_product_ids(): return [v.get('product') for v in PRODUCT_ID_TO_PRACTICES_ID]
20
+
21
+
22
+ def get_crop_residue_burnt_value(cycle: dict):
23
+ products = cycle.get('products', [])
24
+ value = flatten([
25
+ find_term_match(products, 'aboveGroundCropResidueBurnt').get('value', []),
26
+ find_term_match(products, 'discardedCropBurnt').get('value', [])
27
+ ])
28
+ data_complete = _is_term_type_complete(cycle, TermTermType.CROPRESIDUE)
29
+ return [0] if len(value) == 0 and data_complete else value
@@ -204,7 +204,7 @@ def get_inorganic_fertiliser_P_total(cycle: dict) -> float:
204
204
 
205
205
  def get_max_rooting_depth(cycle: dict) -> float:
206
206
  properties = list(map(lambda p: get_node_property(p, 'rootingDepth'), cycle.get('products', [])))
207
- values = [safe_parse_float(p.get('value')) for p in properties if p.get('value') is not None]
207
+ values = [safe_parse_float(p.get('value'), default=0) for p in properties if p.get('value') is not None]
208
208
  return max(values) if len(values) > 0 else None
209
209
 
210
210
 
@@ -87,7 +87,7 @@ def get_ecoClimateZone_lookup_value(eco_climate_zone: str, col_name: str, group_
87
87
  code = int(str(eco_climate_zone))
88
88
  data = _get_single_table_value(lookup, column_name('ecoClimateZone'), code, column_name(col_name))
89
89
  return safe_parse_float(
90
- data if group_name is None else extract_grouped_data(data, group_name)
90
+ data if group_name is None else extract_grouped_data(data, group_name), default=None
91
91
  )
92
92
  except Exception:
93
93
  return 0
@@ -118,7 +118,7 @@ def get_ecoClimateZone_lookup_grouped_value(
118
118
  code = int(str(eco_climate_zone))
119
119
  data = _get_single_table_value(lookup, column_name('ecoClimateZone'), code, column_name(col_name))
120
120
  grouped_data = reduce(
121
- lambda prev, curr: prev | {curr.split(':')[0]: safe_parse_float(curr.split(':')[1])},
121
+ lambda prev, curr: prev | {curr.split(':')[0]: safe_parse_float(curr.split(':')[1], default=None)},
122
122
  data.split(';'),
123
123
  {}
124
124
  ) if data is not None and isinstance(data, str) and len(data) > 1 else default
@@ -19,7 +19,7 @@ def _get_nh3_factor(lookup_name: str, term_id: str, input: dict, **log_args):
19
19
  input_term_id = input.get('term', {}).get('@id')
20
20
  value = get_table_value(download_lookup(lookup_name), 'termid', term_id, column_name(input_term_id))
21
21
  debugMissingLookup(lookup_name, 'termid', term_id, input_term_id, value, **log_args)
22
- return safe_parse_float(value, None)
22
+ return safe_parse_float(value, default=None)
23
23
 
24
24
 
25
25
  def get_excreta_inputs_with_factor(cycle: dict, lookup_name: str, excreta_conversion_func, **log_args):
@@ -27,9 +27,9 @@ def _should_rescale_by_dm(property_id: str):
27
27
  def _dm_property(term_id: str, property_values: dict, dm_property_values: dict, dry_matter_property: dict):
28
28
  blank_node_data = {}
29
29
  for property_key in property_values.keys():
30
- new_dm_value = safe_parse_float(dry_matter_property.get(property_key))
31
- old_dm_value = safe_parse_float(dm_property_values.get(property_key))
32
- old_property_value = safe_parse_float(property_values.get(property_key))
30
+ new_dm_value = safe_parse_float(dry_matter_property.get(property_key), default=None)
31
+ old_dm_value = safe_parse_float(dm_property_values.get(property_key), default=None)
32
+ old_property_value = safe_parse_float(property_values.get(property_key), default=None)
33
33
  if all([new_dm_value, old_dm_value, old_property_value]):
34
34
  new_value = round(
35
35
  old_property_value / old_dm_value * new_dm_value,
@@ -5,7 +5,13 @@ from hestia_earth.utils.tools import flatten
5
5
 
6
6
  from .blank_node import get_inputs_from_properties
7
7
 
8
- _TERM_TYPES = [TermTermType.INORGANICFERTILISER, TermTermType.ORGANICFERTILISER]
8
+ _TERM_TYPES = [
9
+ TermTermType.INORGANICFERTILISER,
10
+ TermTermType.ORGANICFERTILISER,
11
+ TermTermType.OTHERINORGANICCHEMICAL,
12
+ TermTermType.OTHERORGANICCHEMICAL,
13
+ TermTermType.PESTICIDEAI
14
+ ]
9
15
 
10
16
 
11
17
  def get_fertilisers_from_inputs(cycle: dict, term_types: Union[TermTermType, List[TermTermType]] = _TERM_TYPES):
@@ -31,12 +31,12 @@ def get_NH3_emission_factor(term_id: str, soilPh: float, temperature: float):
31
31
  soilPh_key = _get_soilPh_lookup_key(soilPh)
32
32
  temperature_key = _get_temperature_lookup_key(temperature)
33
33
  data = get_term_lookup(term_id, f"NH3_emissions_factor_{soilPh_key}")
34
- return safe_parse_float(extract_grouped_data(data, temperature_key), None)
34
+ return safe_parse_float(extract_grouped_data(data, temperature_key), default=None)
35
35
 
36
36
 
37
37
  def get_country_breakdown(model: str, term_id: str, country_id: str, col_name: str):
38
38
  value = get_region_lookup_value(BREAKDOWN_LOOKUP, country_id, col_name, model=model, term=term_id)
39
- return safe_parse_float(value, None)
39
+ return safe_parse_float(value, default=None)
40
40
 
41
41
 
42
42
  def get_cycle_inputs(cycle: dict):
@@ -4,7 +4,7 @@ from hestia_earth.utils.tools import list_sum, non_empty_list, list_average, fla
4
4
  from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name
5
5
 
6
6
  from hestia_earth.models.log import logger
7
- from . import _filter_list_term_unit, _load_calculated_node
7
+ from . import _filter_list_term_unit, _load_calculated_node, group_by
8
8
  from .constant import Units
9
9
  from .blank_node import get_total_value, get_total_value_converted, get_lookup_value
10
10
  from .term import download_term
@@ -148,3 +148,36 @@ def total_excreta(inputs: list, units=Units.KG_N):
148
148
  def get_total_irrigation_m3(cycle: dict):
149
149
  irrigation_inputs = filter_list_term_type(cycle.get('inputs', []), TermTermType.WATER)
150
150
  return sum([list_average(i.get('value')) for i in irrigation_inputs if len(i.get('value', [])) > 0])
151
+
152
+
153
+ def _group_inputs(inputs: list):
154
+ grouped_inputs = group_by(inputs, ['input.term.@id'])
155
+ return [
156
+ inputs[0] | {'value': list_sum(flatten([v.get('input-value') for v in inputs]))}
157
+ for inputs in grouped_inputs.values()
158
+ ]
159
+
160
+
161
+ def _input_data(input: dict):
162
+ return {
163
+ 'input': input,
164
+ 'input-value': list_sum(input.get('value'), default=None),
165
+ 'has-linked-impact-assessment': bool(input.get('impactAssessment')),
166
+ 'is-fromCycle': input.get('fromCycle', False),
167
+ 'is-producedInCycle': input.get('producedInCycle', False)
168
+ }
169
+
170
+
171
+ def unique_background_inputs(cycle: dict):
172
+ inputs = non_empty_list(map(_input_data, cycle.get('inputs', [])))
173
+
174
+ # sum up inputs with the same id
175
+ return _group_inputs([
176
+ v for v in inputs
177
+ if all([
178
+ v.get('input-value') or -1 > 0,
179
+ not v.get('has-linked-impact-assessment'),
180
+ not v.get('is-fromCycle'),
181
+ not v.get('is-producedInCycle')
182
+ ])
183
+ ])
@@ -33,8 +33,8 @@ def get_default_digestibility(model: str, term_id: str, cycle: dict):
33
33
  lookup_col = column_name(product_id)
34
34
  value = get_table_value(lookup, 'termid', system_id, lookup_col)
35
35
  debugMissingLookup(lookup_name, 'termid', term_id, lookup_col, value, model=model, term=term_id)
36
- min = safe_parse_float(extract_grouped_data(value, 'min'), None)
37
- max = safe_parse_float(extract_grouped_data(value, 'max'), None)
36
+ min = safe_parse_float(extract_grouped_data(value, 'min'), default=None)
37
+ max = safe_parse_float(extract_grouped_data(value, 'max'), default=None)
38
38
  if min and max:
39
39
  return mean([min, max])
40
40
 
@@ -28,7 +28,7 @@ def _factor_value(model: str, term_id: str, lookup_name: str, lookup_col: str, g
28
28
  coefficient = safe_parse_float(
29
29
  extract_grouped_data(coefficient, grouped_data_key),
30
30
  default=None
31
- ) if ':' in str(coefficient) else safe_parse_float(coefficient, None)
31
+ ) if ':' in str(coefficient) else safe_parse_float(coefficient, default=None)
32
32
  if value is not None and coefficient is not None:
33
33
  if model:
34
34
  debugValues(data, model=model, term=term_id,
@@ -32,17 +32,18 @@ def _new_measurement(term, method=None):
32
32
  return include_method(node, term_id=method)
33
33
 
34
34
 
35
- def measurement_value(measurement: dict, is_larger_unit: bool = False) -> float:
35
+ def measurement_value(measurement: dict, is_larger_unit: bool = False, default=None) -> float:
36
36
  term = measurement.get('term', {})
37
37
  reducer = get_lookup_value(term, 'arrayTreatmentLargerUnitOfTime' if is_larger_unit else 'arrayTreatment') or 'mean'
38
38
  value = non_empty_list(measurement.get('value', []))
39
39
  is_value_valid = value is not None and isinstance(value, list) and len(value) > 0
40
- return MEASUREMENT_REDUCE.get(reducer, lambda v: v[0])(value) if is_value_valid else 0
40
+ print(reducer, value, is_value_valid)
41
+ return MEASUREMENT_REDUCE.get(reducer, lambda v: v[0])(value) if is_value_valid else default
41
42
 
42
43
 
43
44
  def most_relevant_measurement_value(measurements: list, term_id: str, date: str, default=None):
44
45
  measurement = most_relevant_blank_node_by_id(measurements, term_id, date)
45
- return measurement_value(measurement) if measurement else default
46
+ return measurement_value(measurement, default=default) if measurement else default
46
47
 
47
48
 
48
49
  def _group_measurement_key(measurement: dict, include_dates: bool = True):
@@ -167,7 +168,7 @@ def most_relevant_measurement_value_by_depth_and_date(
167
168
 
168
169
  def depth_distance(depth_string):
169
170
  split = depth_string.split('_')
170
- upper, lower = safe_parse_float(split[0]), safe_parse_float(split[2]) # "a_to_b"
171
+ upper, lower = safe_parse_float(split[0], default=0), safe_parse_float(split[2], default=0) # "a_to_b"
171
172
  return abs(upper - depth_upper) + abs(lower - depth_lower)
172
173
 
173
174
  nearest_key = min(grouped_measurements.keys(), key=depth_distance)
@@ -17,5 +17,5 @@ PRODUCTIVITY_KEY = {
17
17
 
18
18
 
19
19
  def get_productivity(country: dict, default: PRODUCTIVITY = PRODUCTIVITY.HIGH):
20
- hdi = safe_parse_float(get_lookup_value(country, 'hdi'), None)
20
+ hdi = safe_parse_float(get_lookup_value(country, 'hdi'), default=None)
21
21
  return next((key for key in PRODUCTIVITY_KEY if hdi and PRODUCTIVITY_KEY[key](hdi)), default)
@@ -132,9 +132,11 @@ def get_node_property_value_converted(model: str, node: dict, prop_id: str, defa
132
132
 
133
133
  def _get_nitrogen_content(node: dict):
134
134
  return safe_parse_float(
135
- get_node_property(node, 'nitrogenContent').get('value', 0)) if node else 0
135
+ get_node_property(node, 'nitrogenContent').get('value', 0), default=0
136
+ ) if node else 0
136
137
 
137
138
 
138
139
  def _get_nitrogen_tan_content(node: dict):
139
140
  return safe_parse_float(
140
- get_node_property(node, 'totalAmmoniacalNitrogenContentAsN').get('value', 0)) if node else 0
141
+ get_node_property(node, 'totalAmmoniacalNitrogenContentAsN').get('value', 0), default=0
142
+ ) if node else 0