hestia-earth-models 0.70.0__py3-none-any.whl → 0.70.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. hestia_earth/models/aware/scarcityWeightedWaterUse.py +8 -16
  2. hestia_earth/models/cml2001Baseline/resourceUseMineralsAndMetalsDuringCycle.py +2 -1
  3. hestia_earth/models/config/Cycle.json +98 -50
  4. hestia_earth/models/config/ImpactAssessment.json +12 -4
  5. hestia_earth/models/config/Site.json +40 -21
  6. hestia_earth/models/cycle/transformation.py +1 -1
  7. hestia_earth/models/cycle/utils.py +0 -6
  8. hestia_earth/models/data/ecoinventV3/__init__.py +15 -13
  9. hestia_earth/models/ecoalimV9/__init__.py +13 -0
  10. hestia_earth/models/ecoalimV9/cycle.py +128 -0
  11. hestia_earth/models/ecoalimV9/impact_assessment.py +125 -0
  12. hestia_earth/models/ecoalimV9/utils.py +31 -0
  13. hestia_earth/models/ecoinventV3/__init__.py +6 -14
  14. hestia_earth/models/ecoinventV3/utils.py +1 -29
  15. hestia_earth/models/ecoinventV3AndEmberClimate/__init__.py +8 -2
  16. hestia_earth/models/emissionNotRelevant/__init__.py +33 -8
  17. hestia_earth/models/{cycle → hestia}/aboveGroundCropResidue.py +4 -3
  18. hestia_earth/models/{cycle → hestia}/aboveGroundCropResidueTotal.py +1 -1
  19. hestia_earth/models/{site → hestia}/brackishWater.py +1 -1
  20. hestia_earth/models/{site → hestia}/cationExchangeCapacityPerKgSoil.py +1 -1
  21. hestia_earth/models/{cycle → hestia}/coldCarcassWeightPerHead.py +1 -1
  22. hestia_earth/models/{cycle → hestia}/coldDressedCarcassWeightPerHead.py +1 -1
  23. hestia_earth/models/{cycle → hestia}/concentrateFeed.py +1 -1
  24. hestia_earth/models/{cycle → hestia}/cropResidueManagement.py +1 -1
  25. hestia_earth/models/{cycle → hestia}/croppingIntensity.py +1 -1
  26. hestia_earth/models/{cycle → hestia}/energyContentLowerHeatingValue.py +1 -1
  27. hestia_earth/models/{cycle → hestia}/excretaKgMass.py +8 -3
  28. hestia_earth/models/{cycle → hestia}/excretaKgN.py +1 -1
  29. hestia_earth/models/{cycle → hestia}/excretaKgVs.py +1 -1
  30. hestia_earth/models/{cycle → hestia}/feedConversionRatio/__init__.py +1 -1
  31. hestia_earth/models/{site → hestia}/flowingWater.py +1 -1
  32. hestia_earth/models/{site → hestia}/freshWater.py +1 -1
  33. hestia_earth/models/{cycle → hestia}/inorganicFertiliser.py +1 -1
  34. hestia_earth/models/{cycle → hestia}/irrigatedTypeUnspecified.py +14 -19
  35. hestia_earth/models/hestia/landCover.py +30 -22
  36. hestia_earth/models/{cycle → hestia}/liveAnimal.py +1 -1
  37. hestia_earth/models/{cycle → hestia}/longFallowRatio.py +1 -1
  38. hestia_earth/models/{site → hestia}/management.py +4 -6
  39. hestia_earth/models/{cycle → hestia}/materialAndSubstrate.py +1 -1
  40. hestia_earth/models/{cycle → hestia}/milkYield.py +1 -1
  41. hestia_earth/models/{site → hestia}/netPrimaryProduction.py +1 -1
  42. hestia_earth/models/{site → hestia}/organicCarbonPerHa.py +1 -1
  43. hestia_earth/models/hestia/pToSurfaceWaterAquacultureSystems.py +148 -0
  44. hestia_earth/models/{cycle → hestia}/pastureGrass.py +1 -1
  45. hestia_earth/models/{cycle → hestia}/pastureSystem.py +1 -1
  46. hestia_earth/models/{site → hestia}/potentialEvapotranspirationAnnual.py +3 -3
  47. hestia_earth/models/{site → hestia}/potentialEvapotranspirationMonthly.py +3 -3
  48. hestia_earth/models/{site → hestia}/precipitationAnnual.py +3 -3
  49. hestia_earth/models/{site → hestia}/precipitationMonthly.py +3 -3
  50. hestia_earth/models/{site → hestia}/rainfallAnnual.py +3 -3
  51. hestia_earth/models/{site → hestia}/rainfallMonthly.py +3 -3
  52. hestia_earth/models/{cycle → hestia}/readyToCookWeightPerHead.py +1 -1
  53. hestia_earth/models/{cycle → hestia}/residueBurnt.py +1 -1
  54. hestia_earth/models/{cycle → hestia}/residueIncorporated.py +1 -1
  55. hestia_earth/models/{cycle → hestia}/residueLeftOnField.py +1 -1
  56. hestia_earth/models/hestia/residueRemoved.py +65 -13
  57. hestia_earth/models/{site → hestia}/salineWater.py +1 -1
  58. hestia_earth/models/{site → hestia}/soilMeasurement.py +1 -1
  59. hestia_earth/models/{cycle → hestia}/stockingDensityAnimalHousingAverage.py +1 -1
  60. hestia_earth/models/{site → hestia}/temperatureAnnual.py +3 -3
  61. hestia_earth/models/{site → hestia}/temperatureMonthly.py +3 -3
  62. hestia_earth/models/{site → hestia}/totalNitrogenPerKgSoil.py +1 -1
  63. hestia_earth/models/{cycle → hestia}/unknownPreSeasonWaterRegime.py +1 -1
  64. hestia_earth/models/hestia/utils.py +93 -0
  65. hestia_earth/models/{site → hestia}/waterDepth.py +1 -1
  66. hestia_earth/models/hestia/waterSalinity.py +78 -0
  67. hestia_earth/models/ipcc2019/aboveGroundBiomass.py +1 -1
  68. hestia_earth/models/ipcc2019/belowGroundBiomass.py +1 -1
  69. hestia_earth/models/ipcc2019/biomass_utils.py +2 -4
  70. hestia_earth/models/ipcc2019/ch4ToAirFloodedRice.py +166 -79
  71. hestia_earth/models/ipcc2019/ch4ToAirOrganicSoilCultivation.py +270 -0
  72. hestia_earth/models/ipcc2019/co2ToAirAboveGroundBiomassStockChange.py +0 -3
  73. hestia_earth/models/ipcc2019/co2ToAirBelowGroundBiomassStockChange.py +0 -3
  74. hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +88 -63
  75. hestia_earth/models/ipcc2019/co2ToAirLimeHydrolysis.py +7 -5
  76. hestia_earth/models/ipcc2019/co2ToAirOrganicSoilCultivation.py +215 -0
  77. hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChange.py +0 -3
  78. hestia_earth/models/ipcc2019/co2ToAirUreaHydrolysis.py +16 -9
  79. hestia_earth/models/ipcc2019/n2OToAirOrganicSoilCultivationDirect.py +161 -0
  80. hestia_earth/models/ipcc2019/nonCo2EmissionsToAirNaturalVegetationBurning.py +35 -47
  81. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1.py +86 -1
  82. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2.py +127 -1
  83. hestia_earth/models/ipcc2019/organicCarbonPerHa_utils.py +7 -5
  84. hestia_earth/models/ipcc2019/organicSoilCultivation_utils.py +159 -0
  85. hestia_earth/models/mocking/search-results.json +1113 -1113
  86. hestia_earth/models/pooreNemecek2018/utils.py +8 -2
  87. hestia_earth/models/schmidt2007/ch4ToAirWasteTreatment.py +1 -4
  88. hestia_earth/models/schmidt2007/h2SToAirWasteTreatment.py +1 -4
  89. hestia_earth/models/schmidt2007/n2OToAirWasteTreatmentDirect.py +1 -4
  90. hestia_earth/models/schmidt2007/nh3ToAirWasteTreatment.py +1 -4
  91. hestia_earth/models/site/grouped_measurement.py +132 -0
  92. hestia_earth/models/utils/__init__.py +4 -3
  93. hestia_earth/models/utils/background_emissions.py +52 -0
  94. hestia_earth/models/utils/blank_node.py +47 -14
  95. hestia_earth/models/utils/constant.py +26 -20
  96. hestia_earth/models/utils/impact_assessment.py +26 -17
  97. hestia_earth/models/utils/lookup.py +48 -39
  98. hestia_earth/models/utils/measurement.py +3 -3
  99. hestia_earth/models/utils/product.py +39 -1
  100. hestia_earth/models/utils/property.py +14 -6
  101. hestia_earth/models/version.py +1 -1
  102. {hestia_earth_models-0.70.0.dist-info → hestia_earth_models-0.70.2.dist-info}/METADATA +2 -2
  103. {hestia_earth_models-0.70.0.dist-info → hestia_earth_models-0.70.2.dist-info}/RECORD +187 -171
  104. tests/models/aware/test_scarcityWeightedWaterUse.py +1 -12
  105. tests/models/ecoalimV9/__init__.py +0 -0
  106. tests/models/ecoalimV9/test_cycle.py +21 -0
  107. tests/models/ecoalimV9/test_impact_assessment.py +24 -0
  108. tests/models/environmentalFootprintV3_1/test_scarcityWeightedWaterUse.py +4 -2
  109. tests/models/{cycle → hestia}/test_aboveGroundCropResidue.py +1 -1
  110. tests/models/{cycle → hestia}/test_aboveGroundCropResidueTotal.py +1 -1
  111. tests/models/{site → hestia}/test_brackishWater.py +1 -1
  112. tests/models/{site → hestia}/test_cationExchangeCapacityPerKgSoil.py +1 -1
  113. tests/models/{cycle → hestia}/test_coldCarcassWeightPerHead.py +1 -1
  114. tests/models/{cycle → hestia}/test_coldDressedCarcassWeightPerHead.py +1 -1
  115. tests/models/{cycle → hestia}/test_concentrateFeed.py +1 -1
  116. tests/models/{cycle → hestia}/test_cropResidueManagement.py +1 -1
  117. tests/models/{cycle → hestia}/test_croppingIntensity.py +1 -1
  118. tests/models/{cycle → hestia}/test_energyContentLowerHeatingValue.py +5 -3
  119. tests/models/{cycle → hestia}/test_excretaKgMass.py +1 -1
  120. tests/models/{cycle → hestia}/test_excretaKgN.py +1 -1
  121. tests/models/{cycle → hestia}/test_excretaKgVs.py +1 -1
  122. tests/models/{cycle → hestia}/test_feedConversionRatio.py +3 -4
  123. tests/models/{site → hestia}/test_flowingWater.py +1 -1
  124. tests/models/{site → hestia}/test_freshWater.py +1 -1
  125. tests/models/{cycle → hestia}/test_inorganicFertiliser.py +1 -1
  126. tests/models/{cycle → hestia}/test_irrigatedTypeUnspecified.py +2 -5
  127. tests/models/hestia/test_landCover.py +4 -34
  128. tests/models/{cycle → hestia}/test_liveAnimal.py +1 -1
  129. tests/models/{cycle → hestia}/test_longFallowRatio.py +1 -1
  130. tests/models/{site → hestia}/test_management.py +1 -1
  131. tests/models/{cycle → hestia}/test_materialsAndSubstrate.py +1 -1
  132. tests/models/{cycle → hestia}/test_milkYield.py +1 -1
  133. tests/models/{site → hestia}/test_netPrimaryProduction.py +1 -1
  134. tests/models/{site → hestia}/test_organicCarbonPerHa.py +1 -1
  135. tests/models/{site → hestia}/test_organicCarbonPerKgSoil.py +1 -1
  136. tests/models/{site → hestia}/test_organicCarbonPerM3Soil.py +1 -1
  137. tests/models/{site → hestia}/test_organicMatterPerKgSoil.py +1 -1
  138. tests/models/{site → hestia}/test_organicMatterPerM3Soil.py +1 -1
  139. tests/models/hestia/test_pToSurfaceWaterAquacultureSystems.py +56 -0
  140. tests/models/{cycle → hestia}/test_pastureGrass.py +1 -1
  141. tests/models/{cycle → hestia}/test_pastureSystem.py +1 -1
  142. tests/models/{site → hestia}/test_potentialEvapotranspirationAnnual.py +1 -1
  143. tests/models/{site → hestia}/test_potentialEvapotranspirationMonthly.py +1 -1
  144. tests/models/{site → hestia}/test_precipitationAnnual.py +1 -1
  145. tests/models/{site → hestia}/test_precipitationMonthly.py +1 -1
  146. tests/models/{site → hestia}/test_rainfallAnnual.py +1 -1
  147. tests/models/{site → hestia}/test_rainfallMonthly.py +1 -1
  148. tests/models/{cycle → hestia}/test_readyToCookWeightPerHead.py +1 -1
  149. tests/models/{cycle → hestia}/test_residueBurnt.py +1 -1
  150. tests/models/{cycle → hestia}/test_residueIncorporated.py +1 -1
  151. tests/models/{cycle → hestia}/test_residueLeftOnField.py +1 -1
  152. tests/models/hestia/test_residueRemoved.py +15 -3
  153. tests/models/{site → hestia}/test_salineWater.py +1 -1
  154. tests/models/{site → hestia}/test_soilMeasurement.py +13 -21
  155. tests/models/{cycle → hestia}/test_stockingDensityAnimalHousingAverage.py +1 -1
  156. tests/models/{site → hestia}/test_temperatureAnnual.py +1 -1
  157. tests/models/{site → hestia}/test_temperatureMonthly.py +1 -1
  158. tests/models/{site → hestia}/test_totalNitrogenPerKgSoil.py +1 -1
  159. tests/models/{cycle → hestia}/test_unknownPreSeasonWaterRegime.py +1 -1
  160. tests/models/{site → hestia}/test_waterDepth.py +1 -1
  161. tests/models/hestia/test_waterSalinity.py +26 -0
  162. tests/models/ipcc2019/test_ch4ToAirEntericFermentation.py +2 -5
  163. tests/models/ipcc2019/test_ch4ToAirFloodedRice.py +10 -42
  164. tests/models/ipcc2019/test_ch4ToAirOrganicSoilCultivation.py +61 -0
  165. tests/models/ipcc2019/test_co2ToAirAboveGroundBiomassStockChange.py +11 -9
  166. tests/models/ipcc2019/test_co2ToAirBelowGroundBiomassStockChange.py +10 -8
  167. tests/models/ipcc2019/test_co2ToAirLimeHydrolysis.py +1 -1
  168. tests/models/ipcc2019/test_co2ToAirOrganicSoilCultivation.py +62 -0
  169. tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChange.py +11 -8
  170. tests/models/ipcc2019/test_n2OToAirOrganicSoilCultivationDirect.py +61 -0
  171. tests/models/ipcc2019/test_nonCo2EmissionsToAirNaturalVegetationBurning.py +3 -2
  172. tests/models/site/test_grouped_measurement.py +20 -0
  173. tests/models/test_ecoinventV3AndEmberClimate.py +2 -2
  174. tests/models/test_emissionNotRelevant.py +0 -8
  175. tests/models/utils/test_measurement.py +1 -1
  176. hestia_earth/models/cycle/residueRemoved.py +0 -54
  177. hestia_earth/models/hestia/nh3ToSurfaceWaterAquacultureSystems.py +0 -64
  178. hestia_earth/models/site/utils.py +0 -93
  179. tests/models/cycle/test_residueRemoved.py +0 -37
  180. tests/models/hestia/test_nh3ToSurfaceWaterAquacultureSystems.py +0 -51
  181. /hestia_earth/models/{cycle → hestia}/feedConversionRatio/feedConversionRatioCarbon.py +0 -0
  182. /hestia_earth/models/{cycle → hestia}/feedConversionRatio/feedConversionRatioDryMatter.py +0 -0
  183. /hestia_earth/models/{cycle → hestia}/feedConversionRatio/feedConversionRatioEnergy.py +0 -0
  184. /hestia_earth/models/{cycle → hestia}/feedConversionRatio/feedConversionRatioFedWeight.py +0 -0
  185. /hestia_earth/models/{cycle → hestia}/feedConversionRatio/feedConversionRatioNitrogen.py +0 -0
  186. /hestia_earth/models/{site → hestia}/organicCarbonPerKgSoil.py +0 -0
  187. /hestia_earth/models/{site → hestia}/organicCarbonPerM3Soil.py +0 -0
  188. /hestia_earth/models/{site → hestia}/organicMatterPerKgSoil.py +0 -0
  189. /hestia_earth/models/{site → hestia}/organicMatterPerM3Soil.py +0 -0
  190. {hestia_earth_models-0.70.0.dist-info → hestia_earth_models-0.70.2.dist-info}/LICENSE +0 -0
  191. {hestia_earth_models-0.70.0.dist-info → hestia_earth_models-0.70.2.dist-info}/WHEEL +0 -0
  192. {hestia_earth_models-0.70.0.dist-info → hestia_earth_models-0.70.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,161 @@
1
+ from hestia_earth.schema import EmissionMethodTier, EmissionStatsDefinition
2
+
3
+ from hestia_earth.models.log import logRequirements, logShouldRun
4
+ from hestia_earth.models.utils.cycle import land_occupation_per_ha
5
+ from hestia_earth.models.utils.ecoClimateZone import EcoClimateZone, get_eco_climate_zone_value
6
+ from hestia_earth.models.utils.emission import _new_emission
7
+ from hestia_earth.models.utils.measurement import most_relevant_measurement_value
8
+ from hestia_earth.models.utils.site import valid_site_type
9
+
10
+ from .organicSoilCultivation_utils import (
11
+ assign_organic_soil_category, calc_emission, get_emission_factor, OrganicSoilCategory, remap_categories,
12
+ valid_eco_climate_zone
13
+ )
14
+ from . import MODEL
15
+
16
+
17
+ REQUIREMENTS = {
18
+ "Cycle": {
19
+ "or": [
20
+ {
21
+ "cycleDuration": "",
22
+ "practices": [{"@type": "Practice", "value": "", "term.@id": "longFallowRatio"}]
23
+ },
24
+ {
25
+ "@doc": "for plantations, additional properties are required",
26
+ "practices": [
27
+ {"@type": "Practice", "value": "", "term.@id": "nurseryDensity"},
28
+ {"@type": "Practice", "value": "", "term.@id": "nurseryDuration"},
29
+ {"@type": "Practice", "value": "", "term.@id": "plantationProductiveLifespan"},
30
+ {"@type": "Practice", "value": "", "term.@id": "plantationDensity"},
31
+ {"@type": "Practice", "value": "", "term.@id": "plantationLifespan"},
32
+ {"@type": "Practice", "value": "", "term.@id": "rotationDuration"}
33
+ ]
34
+ }
35
+ ],
36
+ "site": {
37
+ "@type": "Site",
38
+ "measurements": [
39
+ {"@type": "Measurement", "value": "", "term.@id": "histosol"},
40
+ {"@type": "Measurement", "value": "", "term.@id": "ecoClimateZone"}
41
+ ]
42
+ },
43
+ "optional": {
44
+ "cycleDuration": ""
45
+ }
46
+ }
47
+ }
48
+ LOOKUPS = {
49
+ "crop": [
50
+ "isPlantation",
51
+ "IPCC_2013_ORGANIC_SOIL_CULTIVATION_CATEGORY"
52
+ ],
53
+ "forage": [
54
+ "isPlantation",
55
+ "IPCC_2013_ORGANIC_SOIL_CULTIVATION_CATEGORY"
56
+ ],
57
+ "ecoClimateZone": [
58
+ "IPCC_2013_ORGANIC_SOILS_TONNES_N2O-N_HECTARE_ANNUAL_CROPS",
59
+ "IPCC_2013_ORGANIC_SOILS_TONNES_N2O-N_HECTARE_PERENNIAL_CROPS",
60
+ "IPCC_2013_ORGANIC_SOILS_TONNES_N2O-N_HECTARE_ACACIA",
61
+ "IPCC_2013_ORGANIC_SOILS_TONNES_N2O-N_HECTARE_OIL_PALM",
62
+ "IPCC_2013_ORGANIC_SOILS_TONNES_N2O-N_HECTARE_SAGO_PALM",
63
+ "IPCC_2013_ORGANIC_SOILS_TONNES_N2O-N_HECTARE_PADDY_RICE_CULTIVATION",
64
+ "IPCC_2013_ORGANIC_SOILS_TONNES_N2O-N_HECTARE_GRASSLAND",
65
+ "IPCC_2013_ORGANIC_SOILS_TONNES_N2O-N_HECTARE_OTHER"
66
+ ]
67
+ }
68
+ RETURNS = {
69
+ "Emission": [{
70
+ "value": "",
71
+ "sd": "",
72
+ "statsDefinition": "modelled",
73
+ "methodTier": "tier 1"
74
+ }]
75
+ }
76
+ TERM_ID = 'n2OToAirOrganicSoilCultivationDirect'
77
+ TIER = EmissionMethodTier.TIER_1.value
78
+
79
+ _STATS_DEFINITION = EmissionStatsDefinition.MODELLED.value
80
+
81
+ _CATEGORY_REMAPPER = {
82
+ OrganicSoilCategory.ACACIA: OrganicSoilCategory.PERENNIAL_CROPS
83
+ }
84
+
85
+
86
+ def _emission(value: float, sd: float):
87
+ emission = _new_emission(TERM_ID, MODEL)
88
+ emission['value'] = [value]
89
+ emission['sd'] = [sd]
90
+ emission['statsDefinition'] = _STATS_DEFINITION
91
+ emission['methodTier'] = TIER
92
+ return emission
93
+
94
+
95
+ def sample_emission_factor(
96
+ eco_climate_zone: EcoClimateZone,
97
+ organic_soil_category: OrganicSoilCategory,
98
+ ) -> tuple[float, float]:
99
+ category = remap_categories(organic_soil_category, _CATEGORY_REMAPPER) # fewer categories than CO2 model
100
+ factor_data = get_emission_factor(TERM_ID, eco_climate_zone, category)
101
+ mean = factor_data.get("value", 0)
102
+ sd = factor_data.get("sd", 0)
103
+ return mean, sd
104
+
105
+
106
+ def _should_run(cycle: dict):
107
+ end_date = cycle.get('endDate')
108
+ site = cycle.get('site', {})
109
+ measurements = site.get('measurements', [])
110
+
111
+ def _get_measurement_content(term_id: str):
112
+ return most_relevant_measurement_value(measurements, term_id, end_date)
113
+
114
+ histosol = _get_measurement_content('histosol')
115
+ eco_climate_zone = get_eco_climate_zone_value(cycle, as_enum=True)
116
+ organic_soil_category = assign_organic_soil_category(cycle, log_id=TERM_ID)
117
+
118
+ emission_factor_mean, emission_factor_sd = (
119
+ sample_emission_factor(eco_climate_zone, organic_soil_category)
120
+ if eco_climate_zone
121
+ else (None, None)
122
+ )
123
+
124
+ land_occupation = land_occupation_per_ha(MODEL, TERM_ID, cycle)
125
+
126
+ logRequirements(
127
+ cycle, model=MODEL, term=TERM_ID,
128
+ eco_climate_zone=eco_climate_zone,
129
+ organic_soil_category=organic_soil_category,
130
+ emission_factor=f"{emission_factor_mean} ± {emission_factor_sd}",
131
+ land_occupation=land_occupation,
132
+ histosol=histosol
133
+ )
134
+
135
+ should_run = all([
136
+ valid_site_type(site),
137
+ valid_eco_climate_zone(eco_climate_zone),
138
+ all(
139
+ var is not None for var in [
140
+ emission_factor_mean,
141
+ emission_factor_sd,
142
+ land_occupation,
143
+ histosol
144
+ ]
145
+ )
146
+ ])
147
+
148
+ logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
149
+
150
+ return should_run, emission_factor_mean, emission_factor_sd, histosol, land_occupation
151
+
152
+
153
+ def _run(emission_factor_mean: float, emission_factor_sd: float, histosol: float, land_occupation: float):
154
+ value = calc_emission(TERM_ID, emission_factor_mean, histosol, land_occupation)
155
+ sd = calc_emission(TERM_ID, emission_factor_sd, histosol, land_occupation)
156
+ return [_emission(value, sd)]
157
+
158
+
159
+ def run(cycle: dict):
160
+ should_run, emission_factor_mean, emission_factor_sd, histosol, land_occupation = _should_run(cycle)
161
+ return _run(emission_factor_mean, emission_factor_sd, histosol, land_occupation) if should_run else []
@@ -156,8 +156,6 @@ _Inventory = dict[int, _InventoryYear]
156
156
  {year (int): data (_InventoryYear)}
157
157
  """
158
158
 
159
- _EmissionInventory = dict[_EmissionTermId, npt.NDArray]
160
-
161
159
 
162
160
  _BIOMASS_CATEGORY_TO_FUEL_CATEGORY = {
163
161
  BiomassCategory.FOREST: {
@@ -518,9 +516,9 @@ def _sum_cycle_emissions(term_id: _EmissionTermId, cycle_id: str, inventory: _In
518
516
  return reduce(add_cycle_emissions, inventory.keys(), np.array(0))
519
517
 
520
518
 
521
- def _compile_run_data(
519
+ def _compile_inventory(
522
520
  cycle: dict, site: dict, land_cover_nodes: list[dict], eco_climate_zone: EcoClimateZone
523
- ) -> tuple[_EmissionInventory, _Inventory, dict]:
521
+ ):
524
522
  """
525
523
  Compile the run data for the model, collating data from `site.management` and related cycles. An annualised
526
524
  inventory of land cover change and natural vegetation burning events is constructed. Emissions from burning events
@@ -539,18 +537,12 @@ def _compile_run_data(
539
537
 
540
538
  Returns
541
539
  -------
542
- emission_inventory : _EmissionInventory
543
- A dictionary of emissions relevant to the cycle the model is run on, in the format:
544
- ```
545
- {
546
- emission_term_id (str): value (NDArray),
547
- ...
548
- }
549
- ```
540
+ should_run : bool
541
+ Whether the model should be run.
550
542
  inventory : _Inventory
551
- An inventory of model data
543
+ An inventory of model data.
552
544
  logs : dict
553
- Data from the compilation process that should be logged.
545
+ Data about the inventory compilation to be logged.
554
546
  """
555
547
  cycle_id = cycle.get("@id")
556
548
  related_cycles_ = related_cycles(site, cycles_mapping={cycle_id: cycle})
@@ -586,9 +578,15 @@ def _compile_run_data(
586
578
  Returns
587
579
  -------
588
580
  inventory : dict
589
- An inventory of model data, updated to include the new model year.
581
+ An inventory of model data, updated to include the input year.
590
582
  """
591
- land_cover_nodes = next((nodes for year_, nodes in land_cover_grouped.items() if year_ >= year), []) # Backfill
583
+ land_cover_nodes = land_cover_grouped.get(
584
+ next(
585
+ (k for k in sorted(land_cover_grouped) if k >= year), # backfill if possible
586
+ min(land_cover_grouped, key=lambda k: abs(k - year)) # else forward-fill
587
+ ),
588
+ []
589
+ )
592
590
 
593
591
  biomass_category_summary = summarise_land_cover_nodes(land_cover_nodes)
594
592
  prev_biomass_category_summary = inventory.get(year-1, {}).get("biomass_category_summary", {})
@@ -656,17 +654,17 @@ def _compile_run_data(
656
654
 
657
655
  inventory = reduce(build_inventory_year, range(min_year, max_year+1), dict())
658
656
 
659
- emission_inventory = {
660
- term_id: value for term_id in EMISSION_TERM_IDS
661
- if np.all((value := _sum_cycle_emissions(term_id, cycle_id, inventory)) > 0)
662
- }
657
+ n_land_cover_years = len(land_cover_grouped)
663
658
 
664
659
  logs = {
660
+ "n_land_cover_years": n_land_cover_years,
665
661
  "percent_burned": percent_burned,
666
662
  "seed": seed,
667
663
  }
668
664
 
669
- return emission_inventory, inventory, logs
665
+ should_run = bool(inventory and n_land_cover_years > 1)
666
+
667
+ return should_run, inventory, logs
670
668
 
671
669
 
672
670
  def _format_bool(value: Optional[bool]) -> str:
@@ -831,23 +829,16 @@ def _format_inventory(term_id: _EmissionTermId, cycle_id: str, inventory: dict)
831
829
  ) if inventory else "None"
832
830
 
833
831
 
834
- def _should_run_emission(
835
- term_id: _EmissionTermId, cycle: dict, emission_inventory: _EmissionInventory, inventory: dict, logs: dict
836
- ):
832
+ def _log_emission_data(should_run: bool, term_id: _EmissionTermId, cycle: dict, inventory: dict, logs: dict):
837
833
  """
838
- Determine, based on the compiled data, whether the model should run for a specifc emission term id. Format and log
839
- the model logs and inventory.
834
+ Format and log the model logs and inventory.
840
835
  """
841
- should_run = term_id in emission_inventory
842
-
843
836
  formatted_logs = _format_logs(logs)
844
837
  formatted_inventory = _format_inventory(term_id, cycle.get("@id"), inventory)
845
838
 
846
839
  logRequirements(cycle, model=MODEL, term=term_id, **formatted_logs, inventory=formatted_inventory)
847
840
  logShouldRun(cycle, MODEL, term_id, should_run)
848
841
 
849
- return should_run
850
-
851
842
 
852
843
  def _should_run(cycle: dict):
853
844
  """
@@ -861,8 +852,8 @@ def _should_run(cycle: dict):
861
852
 
862
853
  Returns
863
854
  -------
864
- tuple[bool, dict]
865
- should_run, emission_inventory
855
+ tuple[bool, _Inventory]
856
+ should_run, inventory
866
857
  """
867
858
  site = _get_site(cycle)
868
859
 
@@ -873,7 +864,7 @@ def _should_run(cycle: dict):
873
864
 
874
865
  has_valid_site_type = all([site_type, site_type not in _EXCLUDED_SITE_TYPES])
875
866
  has_valid_eco_climate_zone = all([eco_climate_zone, eco_climate_zone not in _EXCLUDED_ECO_CLIMATE_ZONES])
876
- has_land_cover_nodes = len(land_cover_nodes) > 0
867
+ has_land_cover_nodes = len(land_cover_nodes) > 1
877
868
 
878
869
  should_compile_inventory = all([
879
870
  has_valid_site_type,
@@ -881,9 +872,9 @@ def _should_run(cycle: dict):
881
872
  has_land_cover_nodes
882
873
  ])
883
874
 
884
- emission_inventory, inventory, compilation_logs = (
885
- _compile_run_data(cycle, site, land_cover_nodes, eco_climate_zone)
886
- if should_compile_inventory else ({}, {}, {})
875
+ should_run, inventory, compilation_logs = (
876
+ _compile_inventory(cycle, site, land_cover_nodes, eco_climate_zone)
877
+ if should_compile_inventory else (False, {}, {})
887
878
  )
888
879
 
889
880
  logs = {
@@ -897,21 +888,18 @@ def _should_run(cycle: dict):
897
888
  **compilation_logs
898
889
  }
899
890
 
900
- should_run = all([
901
- any([
902
- _should_run_emission(term_id, cycle, emission_inventory, inventory, logs) for term_id in EMISSION_TERM_IDS
903
- ])
904
- ])
891
+ for term_id in EMISSION_TERM_IDS:
892
+ _log_emission_data(should_run, term_id, cycle, inventory, logs)
905
893
 
906
- return should_run, emission_inventory
894
+ return should_run, inventory
907
895
 
908
896
 
909
- def _run_emission(term_id: _EmissionTermId, emissions: dict[_EmissionTermId, npt.NDArray]) -> list[dict]:
897
+ def _run_emission(term_id: _EmissionTermId, cycle_id: str, inventory: _Inventory) -> list[dict]:
910
898
  """
911
- Retrieve the pre-computed emissions and format them as a HESTIA
899
+ Retrieve the sum relevant emissions and format them as a HESTIA
912
900
  [Emission node](https://www.hestia.earth/schema/Emission).
913
901
  """
914
- emission = emissions[term_id]
902
+ emission = _sum_cycle_emissions(term_id, cycle_id, inventory)
915
903
  descriptive_stats = calc_descriptive_stats(emission, STATS_DEFINITION, decimals=3)
916
904
  return _emission(term_id, **descriptive_stats)
917
905
 
@@ -932,5 +920,5 @@ def run(cycle: dict):
932
920
  `ch4ToAirNaturalVegetationBurning` **OR** `coToAirNaturalVegetationBurning` **OR**
933
921
  `n2OToAirNaturalVegetationBurningDirect` **OR** `noxToAirNaturalVegetationBurning`.
934
922
  """
935
- should_run, emission_inventory = _should_run(cycle)
936
- return [_run_emission(term_id, emission_inventory) for term_id in emission_inventory] if should_run else []
923
+ should_run, inventory = _should_run(cycle)
924
+ return [_run_emission(term_id, cycle.get("@id"), inventory) for term_id in EMISSION_TERM_IDS] if should_run else []
@@ -31,6 +31,77 @@ from .organicCarbonPerHa_utils import (
31
31
  )
32
32
  from . import MODEL
33
33
 
34
+ REQUIREMENTS = {
35
+ "Site": {
36
+ "management": [
37
+ {"@type": "Management", "value": "", "term.termType": "landCover"}
38
+ ],
39
+ "measurements": [
40
+ {
41
+ "@type": "Measurement",
42
+ "value": ["1", "2", "3", "4", "7", "8", "9", "10", "11", "12"],
43
+ "term.@id": "ecoClimateZone"
44
+ }
45
+ ],
46
+ "optional": {
47
+ "measurements": [
48
+ {"@type": "Measurement", "value": "", "term.termType": ["soilType", "usdaSoilType"]}
49
+ ],
50
+ "management": [
51
+ {
52
+ "@type": "Management",
53
+ "value": "",
54
+ "startDate": "",
55
+ "endDate": "",
56
+ "term.termType": "cropResidueManagement",
57
+ "name": ["burnt", "removed"]
58
+ },
59
+ {
60
+ "@type": "Management",
61
+ "value": "",
62
+ "startDate": "",
63
+ "endDate": "",
64
+ "term.termType": "landUseManagement"
65
+ },
66
+ {"@type": "Management", "value": "", "startDate": "", "endDate": "", "term.termType": "tillage"},
67
+ {
68
+ "@type": "Management",
69
+ "value": "",
70
+ "startDate": "",
71
+ "endDate": "",
72
+ "term.termType": "waterRegime",
73
+ "name": ["deep water", "irrigated"]
74
+ },
75
+ {
76
+ "@type": "Management",
77
+ "value": "",
78
+ "startDate": "",
79
+ "endDate": "",
80
+ "term.@id": "amendmentIncreasingSoilCarbonUsed"
81
+ },
82
+ {"@type": "Management", "value": "", "startDate": "", "endDate": "", "term.@id": "animalManureUsed"},
83
+ {
84
+ "@type": "Management",
85
+ "value": "",
86
+ "startDate": "",
87
+ "endDate": "",
88
+ "term.@id": "inorganicNitrogenFertiliserUsed"
89
+ },
90
+ {
91
+ "@type": "Management",
92
+ "value": "",
93
+ "startDate": "",
94
+ "endDate": "",
95
+ "term.@id": "organicFertiliserUsed"
96
+ },
97
+ {"@type": "Management", "value": "", "startDate": "", "endDate": "", "term.@id": "shortBareFallow"}
98
+ ]
99
+ },
100
+ "none": {
101
+ "siteType": ["glass or high accessible cover"]
102
+ }
103
+ }
104
+ }
34
105
  LOOKUPS = {
35
106
  "crop": "IPCC_LAND_USE_CATEGORY",
36
107
  "landCover": [
@@ -43,6 +114,20 @@ LOOKUPS = {
43
114
  "tillage": "IPCC_TILLAGE_MANAGEMENT_CATEGORY",
44
115
  "usdaSoilType": "IPCC_SOIL_CATEGORY"
45
116
  }
117
+ RETURNS = {
118
+ "Measurement": [{
119
+ "value": "",
120
+ "sd": "",
121
+ "min": "",
122
+ "max": "",
123
+ "statsDefinition": "simulated",
124
+ "observations": "",
125
+ "dates": "",
126
+ "depthUpper": "0",
127
+ "depthLower": "30",
128
+ "methodClassification": "tier 1 model"
129
+ }]
130
+ }
46
131
 
47
132
  TERM_ID = 'organicCarbonPerHa'
48
133
  _METHOD_CLASSIFICATION = MeasurementMethodClassification.TIER_1_MODEL.value
@@ -108,7 +193,7 @@ def _measurement(
108
193
  dict
109
194
  A valid HESTIA `Measurement` node, see: https://www.hestia.earth/schema/Measurement.
110
195
  """
111
- measurement = _new_measurement(TERM_ID) | descriptive_stats_dict
196
+ measurement = _new_measurement(TERM_ID, MODEL) | descriptive_stats_dict
112
197
  measurement["dates"] = [f"{year}-12-31" for year in timestamps]
113
198
  measurement["depthUpper"] = DEPTH_UPPER
114
199
  measurement["depthLower"] = DEPTH_LOWER
@@ -33,11 +33,137 @@ from .organicCarbonPerHa_utils import (
33
33
  )
34
34
  from . import MODEL
35
35
 
36
+ REQUIREMENTS = {
37
+ "Site": {
38
+ "siteType": "cropland",
39
+ "measurements": [
40
+ {
41
+ "@type": "Measurement",
42
+ "term.@id": "sandContent",
43
+ "value": "",
44
+ "depthUpper": "0",
45
+ "depthLower": "30",
46
+ "optional": {
47
+ "dates": ""
48
+ }
49
+ },
50
+ {
51
+ "@type": "Measurement",
52
+ "term.@id": "temperatureMonthly",
53
+ "value": "",
54
+ "dates": ""
55
+ },
56
+ {
57
+ "@type": "Measurement",
58
+ "term.@id": "precipitationMonthly",
59
+ "value": "",
60
+ "dates": ""
61
+ },
62
+ {
63
+ "@type": "Measurement",
64
+ "term.@id": "potentialEvapotranspirationMonthly",
65
+ "value": "",
66
+ "dates": ""
67
+ }
68
+ ],
69
+ "related": {
70
+ "Cycle": [{
71
+ "endDate": "",
72
+ "products": [
73
+ {
74
+ "@type": "Product",
75
+ "term.@id": [
76
+ "aboveGroundCropResidueLeftOnField",
77
+ "aboveGroundCropResidueIncorporated",
78
+ "belowGroundCropResidue",
79
+ "discardedCropLeftOnField",
80
+ "discardedCropIncorporated"
81
+ ],
82
+ "value": "",
83
+ "properties": [
84
+ {
85
+ "@type": "Property",
86
+ "term.@id": "carbonContent",
87
+ "value": ""
88
+ },
89
+ {
90
+ "@type": "Property",
91
+ "term.@id": "nitrogenContent",
92
+ "value": ""
93
+ },
94
+ {
95
+ "@type": "Property",
96
+ "term.@id": "ligninContent",
97
+ "value": ""
98
+ }
99
+ ]
100
+ }
101
+ ],
102
+ "inputs": [
103
+ {
104
+ "@type": "Input",
105
+ "term.termType": ["organicFertiliser", "soilAmendment"],
106
+ "value": "",
107
+ "properties": [
108
+ {
109
+ "@type": "Property",
110
+ "term.@id": "carbonContent",
111
+ "value": ""
112
+ },
113
+ {
114
+ "@type": "Property",
115
+ "term.@id": "nitrogenContent",
116
+ "value": ""
117
+ },
118
+ {
119
+ "@type": "Property",
120
+ "term.@id": "ligninContent",
121
+ "value": ""
122
+ }
123
+ ]
124
+ }
125
+ ],
126
+ "practices": [
127
+ {
128
+ "@type": "Practice",
129
+ "term.termType": "tillage",
130
+ "value": ""
131
+ },
132
+ {
133
+ "@type": "Practice",
134
+ "term.termType": "waterRegime",
135
+ "name": "irrigated",
136
+ "value": "",
137
+ "startDate": "",
138
+ "endDate": ""
139
+ }
140
+ ],
141
+ "optional": {
142
+ "startDate": ""
143
+ }
144
+ }]
145
+ }
146
+ }
147
+ }
36
148
  LOOKUPS = {
37
149
  "crop": "IPCC_LAND_USE_CATEGORY",
38
150
  "landCover": "IPCC_LAND_USE_CATEGORY",
39
151
  "tillage": "IPCC_TILLAGE_MANAGEMENT_CATEGORY"
40
152
  }
153
+ RETURNS = {
154
+ "Measurement": [{
155
+ "value": "",
156
+ "sd": "",
157
+ "min": "",
158
+ "max": "",
159
+ "statsDefinition": "simulated",
160
+ "observations": "",
161
+ "dates": "",
162
+ "depthUpper": "0",
163
+ "depthLower": "30",
164
+ "methodClassification": "tier 2 model"
165
+ }]
166
+ }
41
167
 
42
168
  TERM_ID = 'organicCarbonPerHa'
43
169
  _METHOD_CLASSIFICATION = MeasurementMethodClassification.TIER_2_MODEL.value
@@ -124,7 +250,7 @@ def _measurement(
124
250
  dict
125
251
  A valid HESTIA `Measurement` node, see: https://www.hestia.earth/schema/Measurement.
126
252
  """
127
- measurement = _new_measurement(TERM_ID) | descriptive_stats_dict
253
+ measurement = _new_measurement(TERM_ID, MODEL) | descriptive_stats_dict
128
254
  measurement["dates"] = [f"{year}-12-31" for year in timestamps]
129
255
  measurement["depthUpper"] = DEPTH_UPPER
130
256
  measurement["depthLower"] = DEPTH_LOWER
@@ -1,13 +1,15 @@
1
1
  from enum import Enum
2
+ from numpy import inf
2
3
  from numpy.typing import NDArray
3
4
  from typing import NamedTuple, Optional
4
5
 
5
6
  from hestia_earth.schema import MeasurementStatsDefinition, SiteSiteType
6
7
 
7
8
  from hestia_earth.models.utils.array_builders import (
8
- repeat_single, plus_minus_uncertainty_to_normal_1d, truncated_normal_1d
9
+ repeat_single, truncated_normal_1d
9
10
  )
10
11
  from hestia_earth.models.utils.blank_node import cumulative_nodes_term_match, node_term_match
12
+ from hestia_earth.models.utils.stats import calc_z_critical
11
13
  from hestia_earth.models.utils.term import get_cover_crop_property_terms, get_irrigated_terms
12
14
 
13
15
  STATS_DEFINITION = MeasurementStatsDefinition.SIMULATED.value
@@ -238,7 +240,9 @@ def sample_plus_minus_uncertainty(
238
240
  *, iterations: int, value: float, uncertainty: float, seed: Optional[int] = None, **_
239
241
  ) -> NDArray:
240
242
  """Randomly sample a model parameter with a plus/minus uncertainty distribution."""
241
- return plus_minus_uncertainty_to_normal_1d(shape=(1, iterations), value=value, uncertainty=uncertainty, seed=seed)
243
+ n_sds = calc_z_critical(95)
244
+ sigma = (value * (uncertainty / 100)) / n_sds
245
+ return truncated_normal_1d(shape=(1, iterations), mu=value, sigma=sigma, low=0, high=inf, seed=seed)
242
246
 
243
247
 
244
248
  def sample_plus_minus_error(
@@ -246,9 +250,7 @@ def sample_plus_minus_error(
246
250
  ) -> NDArray:
247
251
  """Randomly sample a model parameter with a truncated normal distribution described using plus/minus error."""
248
252
  sd = value * (error / 200)
249
- low = value - (value * (error / 100))
250
- high = value + (value * (error / 100))
251
- return truncated_normal_1d(shape=(1, iterations), mu=value, sigma=sd, low=low, high=high, seed=seed)
253
+ return truncated_normal_1d(shape=(1, iterations), mu=value, sigma=sd, low=0, high=inf, seed=seed)
252
254
 
253
255
 
254
256
  def sample_constant(*, iterations: int, value: float, **_) -> NDArray: