hestia-earth-models 0.74.10__py3-none-any.whl → 0.74.12__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.

Potentially problematic release.


This version of hestia-earth-models might be problematic. Click here for more details.

Files changed (410) hide show
  1. hestia_earth/models/agribalyse2016/fuelElectricity.py +1 -2
  2. hestia_earth/models/agribalyse2016/machineryInfrastructureDepreciatedAmountPerCycle.py +1 -7
  3. hestia_earth/models/akagiEtAl2011/utils.py +2 -6
  4. hestia_earth/models/aware/scarcityWeightedWaterUse.py +1 -4
  5. hestia_earth/models/aware2_0/scarcityWeightedWaterUse.py +1 -4
  6. hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsLandOccupation.py +1 -4
  7. hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsLandTransformation.py +1 -4
  8. hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsTotalLandUseEffects.py +1 -4
  9. hestia_earth/models/cml2001Baseline/abioticResourceDepletionFossilFuels.py +1 -4
  10. hestia_earth/models/cml2001Baseline/abioticResourceDepletionMineralsAndMetals.py +1 -4
  11. hestia_earth/models/cml2001Baseline/eutrophicationPotentialExcludingFate.py +1 -4
  12. hestia_earth/models/cml2001Baseline/resourceUseEnergyDepletionDuringCycle.py +12 -10
  13. hestia_earth/models/cml2001Baseline/resourceUseMineralsAndMetalsDuringCycle.py +5 -5
  14. hestia_earth/models/cml2001Baseline/terrestrialAcidificationPotentialIncludingFateAverageEurope.py +1 -7
  15. hestia_earth/models/cml2001NonBaseline/eutrophicationPotentialIncludingFateAverageEurope.py +1 -7
  16. hestia_earth/models/cml2001NonBaseline/terrestrialAcidificationPotentialExcludingFate.py +1 -7
  17. hestia_earth/models/config/ImpactAssessment.json +12 -6
  18. hestia_earth/models/config/Site.json +16 -0
  19. hestia_earth/models/cycle/aboveGroundCropResidueTotal.py +1 -6
  20. hestia_earth/models/cycle/practice/landCover.py +9 -9
  21. hestia_earth/models/dammgen2009/noxToAirExcreta.py +1 -2
  22. hestia_earth/models/deRuijterEtAl2010/nh3ToAirCropResidueDecomposition.py +1 -2
  23. hestia_earth/models/ecoalimV9/cycle.py +1 -2
  24. hestia_earth/models/ecoalimV9/impact_assessment.py +10 -3
  25. hestia_earth/models/ecoinventV3/cycle.py +1 -2
  26. hestia_earth/models/ecoinventV3AndEmberClimate/cycle.py +1 -2
  27. hestia_earth/models/edip2003/ozoneDepletionPotential.py +1 -7
  28. hestia_earth/models/emepEea2019/utils.py +1 -2
  29. hestia_earth/models/emissionNotRelevant/__init__.py +2 -2
  30. hestia_earth/models/environmentalFootprintV3_1/environmentalFootprintSingleOverallScore.py +1 -4
  31. hestia_earth/models/environmentalFootprintV3_1/freshwaterEcotoxicityPotentialCtue.py +1 -7
  32. hestia_earth/models/environmentalFootprintV3_1/marineEutrophicationPotential.py +1 -7
  33. hestia_earth/models/environmentalFootprintV3_1/photochemicalOzoneCreationPotentialHumanHealthNmvocEq.py +1 -7
  34. hestia_earth/models/environmentalFootprintV3_1/scarcityWeightedWaterUse.py +1 -7
  35. hestia_earth/models/environmentalFootprintV3_1/soilQualityIndexLandOccupation.py +2 -7
  36. hestia_earth/models/environmentalFootprintV3_1/soilQualityIndexLandTransformation.py +2 -7
  37. hestia_earth/models/environmentalFootprintV3_1/soilQualityIndexTotalLandUseEffects.py +2 -7
  38. hestia_earth/models/epa2014/no3ToGroundwaterExcreta.py +1 -2
  39. hestia_earth/models/fantkeEtAl2016/damageToHumanHealthParticulateMatterFormation.py +2 -11
  40. hestia_earth/models/faostat2018/liveweightPerHead.py +11 -7
  41. hestia_earth/models/faostat2018/product/price.py +9 -5
  42. hestia_earth/models/faostat2018/seed.py +5 -12
  43. hestia_earth/models/faostat2018/utils.py +14 -6
  44. hestia_earth/models/frischknechtEtAl2000/ionisingRadiationKbqU235Eq.py +4 -8
  45. hestia_earth/models/geospatialDatabase/altitude.py +1 -2
  46. hestia_earth/models/geospatialDatabase/clayContent.py +1 -2
  47. hestia_earth/models/geospatialDatabase/croppingIntensity.py +1 -3
  48. hestia_earth/models/geospatialDatabase/drainageClass.py +1 -2
  49. hestia_earth/models/geospatialDatabase/ecoClimateZone.py +1 -2
  50. hestia_earth/models/geospatialDatabase/erodibility.py +1 -2
  51. hestia_earth/models/geospatialDatabase/heavyWinterPrecipitation.py +1 -2
  52. hestia_earth/models/geospatialDatabase/histosol.py +1 -2
  53. hestia_earth/models/geospatialDatabase/longFallowRatio.py +1 -3
  54. hestia_earth/models/geospatialDatabase/nutrientLossToAquaticEnvironment.py +1 -2
  55. hestia_earth/models/geospatialDatabase/organicCarbonPerKgSoil.py +1 -2
  56. hestia_earth/models/geospatialDatabase/potentialEvapotranspirationAnnual.py +1 -2
  57. hestia_earth/models/geospatialDatabase/potentialEvapotranspirationLongTermAnnualMean.py +1 -2
  58. hestia_earth/models/geospatialDatabase/potentialEvapotranspirationMonthly.py +1 -2
  59. hestia_earth/models/geospatialDatabase/precipitationAnnual.py +1 -2
  60. hestia_earth/models/geospatialDatabase/precipitationLongTermAnnualMean.py +1 -2
  61. hestia_earth/models/geospatialDatabase/precipitationMonthly.py +1 -2
  62. hestia_earth/models/geospatialDatabase/sandContent.py +1 -2
  63. hestia_earth/models/geospatialDatabase/siltContent.py +1 -2
  64. hestia_earth/models/geospatialDatabase/slope.py +1 -2
  65. hestia_earth/models/geospatialDatabase/slopeLength.py +1 -2
  66. hestia_earth/models/geospatialDatabase/soilPh.py +1 -2
  67. hestia_earth/models/geospatialDatabase/temperatureAnnual.py +1 -2
  68. hestia_earth/models/geospatialDatabase/temperatureLongTermAnnualMean.py +1 -2
  69. hestia_earth/models/geospatialDatabase/temperatureMonthly.py +1 -2
  70. hestia_earth/models/geospatialDatabase/totalNitrogenPerKgSoil.py +1 -2
  71. hestia_earth/models/geospatialDatabase/totalPhosphorusPerKgSoil.py +1 -2
  72. hestia_earth/models/geospatialDatabase/waterDepth.py +1 -2
  73. hestia_earth/models/hestia/aboveGroundCropResidue.py +1 -2
  74. hestia_earth/models/hestia/aboveGroundCropResidueTotal.py +1 -6
  75. hestia_earth/models/hestia/brackishWater.py +1 -2
  76. hestia_earth/models/hestia/cationExchangeCapacityPerKgSoil.py +1 -2
  77. hestia_earth/models/hestia/cropResidueManagement.py +1 -2
  78. hestia_earth/models/hestia/croppingIntensity.py +1 -7
  79. hestia_earth/models/hestia/default_emissions.py +3 -5
  80. hestia_earth/models/hestia/default_resourceUse.py +4 -11
  81. hestia_earth/models/hestia/excretaKgMass.py +9 -6
  82. hestia_earth/models/hestia/excretaKgN.py +8 -7
  83. hestia_earth/models/hestia/excretaKgVs.py +8 -7
  84. hestia_earth/models/hestia/feedConversionRatio/__init__.py +5 -7
  85. hestia_earth/models/hestia/flowingWater.py +1 -2
  86. hestia_earth/models/hestia/freshWater.py +1 -2
  87. hestia_earth/models/hestia/histosol.py +1 -2
  88. hestia_earth/models/hestia/inorganicFertiliser.py +2 -14
  89. hestia_earth/models/hestia/irrigatedTypeUnspecified.py +1 -7
  90. hestia_earth/models/hestia/landCover.py +192 -911
  91. hestia_earth/models/hestia/landCover_utils.py +769 -0
  92. hestia_earth/models/hestia/landOccupationDuringCycle.py +1 -2
  93. hestia_earth/models/hestia/liveAnimal.py +1 -3
  94. hestia_earth/models/hestia/longFallowRatio.py +1 -7
  95. hestia_earth/models/hestia/management.py +14 -8
  96. hestia_earth/models/hestia/materialAndSubstrate.py +1 -2
  97. hestia_earth/models/hestia/milkYield.py +2 -12
  98. hestia_earth/models/hestia/netPrimaryProduction.py +1 -2
  99. hestia_earth/models/hestia/organicCarbonPerHa.py +1 -2
  100. hestia_earth/models/hestia/pToSurfaceWaterAquacultureSystems.py +2 -4
  101. hestia_earth/models/hestia/pastureGrass.py +3 -4
  102. hestia_earth/models/hestia/pastureSystem.py +1 -7
  103. hestia_earth/models/hestia/potentialEvapotranspirationAnnual.py +1 -2
  104. hestia_earth/models/hestia/potentialEvapotranspirationMonthly.py +1 -2
  105. hestia_earth/models/hestia/precipitationAnnual.py +1 -2
  106. hestia_earth/models/hestia/precipitationMonthly.py +1 -2
  107. hestia_earth/models/hestia/rainfallAnnual.py +1 -2
  108. hestia_earth/models/hestia/rainfallMonthly.py +1 -2
  109. hestia_earth/models/hestia/residueBurnt.py +1 -7
  110. hestia_earth/models/hestia/residueIncorporated.py +1 -7
  111. hestia_earth/models/hestia/residueLeftOnField.py +1 -7
  112. hestia_earth/models/hestia/residueRemoved.py +3 -8
  113. hestia_earth/models/hestia/resourceUse_utils.py +4 -20
  114. hestia_earth/models/hestia/salineWater.py +1 -2
  115. hestia_earth/models/hestia/seed_emissions.py +5 -6
  116. hestia_earth/models/hestia/slope.py +44 -0
  117. hestia_earth/models/hestia/slopeLength.py +44 -0
  118. hestia_earth/models/hestia/soilMeasurement.py +1 -2
  119. hestia_earth/models/hestia/stockingDensityAnimalHousingAverage.py +1 -7
  120. hestia_earth/models/hestia/temperatureAnnual.py +1 -2
  121. hestia_earth/models/hestia/temperatureMonthly.py +1 -2
  122. hestia_earth/models/hestia/totalNitrogenPerKgSoil.py +1 -2
  123. hestia_earth/models/hestia/unknownPreSeasonWaterRegime.py +1 -7
  124. hestia_earth/models/hestia/utils.py +5 -5
  125. hestia_earth/models/hestia/waterDepth.py +1 -2
  126. hestia_earth/models/hestia/waterSalinity.py +1 -2
  127. hestia_earth/models/impact_assessment/emissions.py +8 -9
  128. hestia_earth/models/impact_assessment/post_checks/cycle.py +1 -1
  129. hestia_earth/models/impact_assessment/pre_checks/cycle.py +1 -1
  130. hestia_earth/models/ipcc2006/aboveGroundCropResidueRemoved.py +1 -3
  131. hestia_earth/models/ipcc2006/aboveGroundCropResidueTotal.py +1 -3
  132. hestia_earth/models/ipcc2006/belowGroundCropResidue.py +1 -3
  133. hestia_earth/models/ipcc2006/n2OToAirCropResidueDecompositionDirect.py +2 -6
  134. hestia_earth/models/ipcc2006/n2OToAirCropResidueDecompositionIndirect.py +1 -2
  135. hestia_earth/models/ipcc2006/n2OToAirExcretaDirect.py +1 -2
  136. hestia_earth/models/ipcc2006/n2OToAirExcretaIndirect.py +1 -2
  137. hestia_earth/models/ipcc2006/n2OToAirInorganicFertiliserDirect.py +1 -2
  138. hestia_earth/models/ipcc2006/n2OToAirInorganicFertiliserIndirect.py +1 -2
  139. hestia_earth/models/ipcc2006/n2OToAirOrganicFertiliserDirect.py +1 -2
  140. hestia_earth/models/ipcc2006/n2OToAirOrganicFertiliserIndirect.py +1 -2
  141. hestia_earth/models/ipcc2013ExcludingFeedbacks/gwp100.py +1 -7
  142. hestia_earth/models/ipcc2013IncludingFeedbacks/gwp100.py +1 -7
  143. hestia_earth/models/ipcc2019/aboveGroundBiomass.py +1 -1
  144. hestia_earth/models/ipcc2019/aboveGroundCropResidueTotal.py +1 -3
  145. hestia_earth/models/ipcc2019/animal/milkYieldPerAnimal.py +3 -7
  146. hestia_earth/models/ipcc2019/animal/pastureGrass.py +1 -2
  147. hestia_earth/models/ipcc2019/belowGroundBiomass.py +1 -1
  148. hestia_earth/models/ipcc2019/belowGroundCropResidue.py +1 -3
  149. hestia_earth/models/ipcc2019/biocharOrganicCarbonPerHa.py +1 -1
  150. hestia_earth/models/ipcc2019/ch4ToAirAquacultureSystems.py +2 -6
  151. hestia_earth/models/ipcc2019/ch4ToAirEntericFermentation.py +2 -12
  152. hestia_earth/models/ipcc2019/ch4ToAirExcreta.py +1 -2
  153. hestia_earth/models/ipcc2019/ch4ToAirFloodedRice.py +2 -10
  154. hestia_earth/models/ipcc2019/ch4ToAirOrganicSoilCultivation.py +1 -1
  155. hestia_earth/models/ipcc2019/co2ToAirAboveGroundBiomassStockChange.py +1 -1
  156. hestia_earth/models/ipcc2019/co2ToAirBelowGroundBiomassStockChange.py +1 -1
  157. hestia_earth/models/ipcc2019/co2ToAirBiocharStockChange.py +1 -1
  158. hestia_earth/models/ipcc2019/co2ToAirLimeHydrolysis.py +1 -2
  159. hestia_earth/models/ipcc2019/co2ToAirOrganicSoilCultivation.py +1 -1
  160. hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChange.py +1 -1
  161. hestia_earth/models/ipcc2019/co2ToAirUreaHydrolysis.py +1 -2
  162. hestia_earth/models/ipcc2019/croppingDuration.py +1 -2
  163. hestia_earth/models/ipcc2019/n2OToAirCropResidueBurningDirect.py +1 -2
  164. hestia_earth/models/ipcc2019/n2OToAirCropResidueDecompositionDirect.py +2 -7
  165. hestia_earth/models/ipcc2019/n2OToAirExcretaDirect.py +1 -2
  166. hestia_earth/models/ipcc2019/n2OToAirInorganicFertiliserDirect.py +2 -7
  167. hestia_earth/models/ipcc2019/n2OToAirOrganicFertiliserDirect.py +2 -7
  168. hestia_earth/models/ipcc2019/n2OToAirOrganicSoilCultivationDirect.py +2 -7
  169. hestia_earth/models/ipcc2019/n2OToAir_indirect_emissions_utils.py +3 -9
  170. hestia_earth/models/ipcc2019/nh3ToAirInorganicFertiliser.py +2 -6
  171. hestia_earth/models/ipcc2019/nh3ToAirOrganicFertiliser.py +2 -6
  172. hestia_earth/models/ipcc2019/no3ToGroundwaterCropResidueDecomposition.py +2 -7
  173. hestia_earth/models/ipcc2019/no3ToGroundwaterExcreta.py +2 -8
  174. hestia_earth/models/ipcc2019/no3ToGroundwaterInorganicFertiliser.py +2 -7
  175. hestia_earth/models/ipcc2019/no3ToGroundwaterOrganicFertiliser.py +2 -7
  176. hestia_earth/models/ipcc2019/nonCo2EmissionsToAirNaturalVegetationBurning.py +1 -1
  177. hestia_earth/models/ipcc2019/noxToAirInorganicFertiliser.py +2 -6
  178. hestia_earth/models/ipcc2019/noxToAirOrganicFertiliser.py +2 -6
  179. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1.py +1 -1
  180. hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2.py +1 -1
  181. hestia_earth/models/ipcc2019/pastureGrass.py +1 -2
  182. hestia_earth/models/ipcc2021/gwp100.py +2 -11
  183. hestia_earth/models/jarvisAndPain1994/n2ToAirExcreta.py +1 -2
  184. hestia_earth/models/koble2014/utils.py +1 -3
  185. hestia_earth/models/lcImpactAllEffects100Years/damageToFreshwaterEcosystemsClimateChange.py +1 -7
  186. hestia_earth/models/lcImpactAllEffects100Years/damageToFreshwaterEcosystemsFreshwaterEcotoxicity.py +1 -7
  187. hestia_earth/models/lcImpactAllEffects100Years/damageToFreshwaterEcosystemsFreshwaterEutrophication.py +1 -7
  188. hestia_earth/models/lcImpactAllEffects100Years/damageToFreshwaterEcosystemsPdfYear.py +1 -7
  189. hestia_earth/models/lcImpactAllEffects100Years/damageToFreshwaterEcosystemsWaterStress.py +1 -7
  190. hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealth.py +1 -7
  191. hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthClimateChange.py +1 -7
  192. hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthHumanToxicityCancerogenic.py +1 -7
  193. hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthHumanToxicityNonCancerogenic.py +1 -7
  194. hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthParticulateMatterFormation.py +1 -7
  195. hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthPhotochemicalOzoneFormation.py +1 -7
  196. hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthStratosphericOzoneDepletion.py +1 -7
  197. hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthWaterStress.py +1 -7
  198. hestia_earth/models/lcImpactAllEffects100Years/damageToMarineEcosystemsMarineEcotoxicity.py +1 -7
  199. hestia_earth/models/lcImpactAllEffects100Years/damageToMarineEcosystemsMarineEutrophication.py +1 -7
  200. hestia_earth/models/lcImpactAllEffects100Years/damageToMarineEcosystemsPdfYear.py +1 -7
  201. hestia_earth/models/lcImpactAllEffects100Years/damageToTerrestrialEcosystemsClimateChange.py +1 -7
  202. hestia_earth/models/lcImpactAllEffects100Years/damageToTerrestrialEcosystemsPdfYear.py +1 -7
  203. hestia_earth/models/lcImpactAllEffects100Years/damageToTerrestrialEcosystemsPhotochemicalOzoneFormation.py +1 -7
  204. hestia_earth/models/lcImpactAllEffects100Years/damageToTerrestrialEcosystemsTerrestrialAcidification.py +1 -7
  205. hestia_earth/models/lcImpactAllEffects100Years/damageToTerrestrialEcosystemsTerrestrialEcotoxicity.py +1 -7
  206. hestia_earth/models/lcImpactAllEffectsInfinite/damageToFreshwaterEcosystemsClimateChange.py +1 -7
  207. hestia_earth/models/lcImpactAllEffectsInfinite/damageToFreshwaterEcosystemsFreshwaterEcotoxicity.py +1 -7
  208. hestia_earth/models/lcImpactAllEffectsInfinite/damageToFreshwaterEcosystemsFreshwaterEutrophication.py +1 -7
  209. hestia_earth/models/lcImpactAllEffectsInfinite/damageToFreshwaterEcosystemsPdfYear.py +1 -7
  210. hestia_earth/models/lcImpactAllEffectsInfinite/damageToFreshwaterEcosystemsWaterStress.py +1 -7
  211. hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealth.py +1 -7
  212. hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthClimateChange.py +1 -7
  213. hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthHumanToxicityCancerogenic.py +1 -7
  214. hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthHumanToxicityNonCancerogenic.py +1 -7
  215. hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthParticulateMatterFormation.py +1 -7
  216. hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthPhotochemicalOzoneFormation.py +1 -7
  217. hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthStratosphericOzoneDepletion.py +1 -7
  218. hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthWaterStress.py +1 -7
  219. hestia_earth/models/lcImpactAllEffectsInfinite/damageToMarineEcosystemsMarineEcotoxicity.py +1 -7
  220. hestia_earth/models/lcImpactAllEffectsInfinite/damageToMarineEcosystemsMarineEutrophication.py +1 -7
  221. hestia_earth/models/lcImpactAllEffectsInfinite/damageToMarineEcosystemsPdfYear.py +1 -7
  222. hestia_earth/models/lcImpactAllEffectsInfinite/damageToTerrestrialEcosystemsClimateChange.py +1 -7
  223. hestia_earth/models/lcImpactAllEffectsInfinite/damageToTerrestrialEcosystemsPdfYear.py +1 -7
  224. hestia_earth/models/lcImpactAllEffectsInfinite/damageToTerrestrialEcosystemsPhotochemicalOzoneFormation.py +1 -7
  225. hestia_earth/models/lcImpactAllEffectsInfinite/damageToTerrestrialEcosystemsTerrestrialAcidification.py +1 -7
  226. hestia_earth/models/lcImpactAllEffectsInfinite/damageToTerrestrialEcosystemsTerrestrialEcotoxicity.py +1 -7
  227. hestia_earth/models/lcImpactCertainEffects100Years/damageToFreshwaterEcosystemsFreshwaterEcotoxicity.py +1 -7
  228. hestia_earth/models/lcImpactCertainEffects100Years/damageToFreshwaterEcosystemsFreshwaterEutrophication.py +1 -7
  229. hestia_earth/models/lcImpactCertainEffects100Years/damageToFreshwaterEcosystemsPdfYear.py +1 -7
  230. hestia_earth/models/lcImpactCertainEffects100Years/damageToFreshwaterEcosystemsWaterStress.py +1 -7
  231. hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealth.py +1 -7
  232. hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthClimateChange.py +1 -7
  233. hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthHumanToxicityCancerogenic.py +1 -7
  234. hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthHumanToxicityNonCancerogenic.py +1 -7
  235. hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthParticulateMatterFormation.py +1 -7
  236. hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthPhotochemicalOzoneFormation.py +1 -7
  237. hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthStratosphericOzoneDepletion.py +1 -7
  238. hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthWaterStress.py +1 -7
  239. hestia_earth/models/lcImpactCertainEffects100Years/damageToMarineEcosystemsMarineEcotoxicity.py +1 -7
  240. hestia_earth/models/lcImpactCertainEffects100Years/damageToMarineEcosystemsMarineEutrophication.py +1 -7
  241. hestia_earth/models/lcImpactCertainEffects100Years/damageToMarineEcosystemsPdfYear.py +1 -7
  242. hestia_earth/models/lcImpactCertainEffects100Years/damageToTerrestrialEcosystemsClimateChange.py +1 -7
  243. hestia_earth/models/lcImpactCertainEffects100Years/damageToTerrestrialEcosystemsPdfYear.py +1 -7
  244. hestia_earth/models/lcImpactCertainEffects100Years/damageToTerrestrialEcosystemsPhotochemicalOzoneFormation.py +1 -7
  245. hestia_earth/models/lcImpactCertainEffects100Years/damageToTerrestrialEcosystemsTerrestrialAcidification.py +1 -7
  246. hestia_earth/models/lcImpactCertainEffects100Years/damageToTerrestrialEcosystemsTerrestrialEcotoxicity.py +1 -7
  247. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToFreshwaterEcosystemsFreshwaterEcotoxicity.py +1 -7
  248. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToFreshwaterEcosystemsFreshwaterEutrophication.py +1 -7
  249. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToFreshwaterEcosystemsPdfYear.py +1 -7
  250. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToFreshwaterEcosystemsWaterStress.py +1 -7
  251. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealth.py +1 -7
  252. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthClimateChange.py +1 -7
  253. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthHumanToxicityCancerogenic.py +1 -7
  254. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthHumanToxicityNonCancerogenic.py +1 -7
  255. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthParticulateMatterFormation.py +1 -7
  256. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthPhotochemicalOzoneFormation.py +1 -7
  257. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthStratosphericOzoneDepletion.py +1 -7
  258. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthWaterStress.py +1 -7
  259. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToMarineEcosystemsMarineEcotoxicity.py +1 -7
  260. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToMarineEcosystemsMarineEutrophication.py +1 -7
  261. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToMarineEcosystemsPdfYear.py +1 -7
  262. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToTerrestrialEcosystemsClimateChange.py +1 -7
  263. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToTerrestrialEcosystemsPdfYear.py +1 -7
  264. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToTerrestrialEcosystemsPhotochemicalOzoneFormation.py +1 -7
  265. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToTerrestrialEcosystemsTerrestrialAcidification.py +1 -7
  266. hestia_earth/models/lcImpactCertainEffectsInfinite/damageToTerrestrialEcosystemsTerrestrialEcotoxicity.py +1 -7
  267. hestia_earth/models/linkedImpactAssessment/emissions.py +4 -6
  268. hestia_earth/models/linkedImpactAssessment/utils.py +4 -6
  269. hestia_earth/models/mocking/search-results.json +1 -1
  270. hestia_earth/models/pooreNemecek2018/aboveGroundCropResidueTotal.py +1 -3
  271. hestia_earth/models/pooreNemecek2018/belowGroundCropResidue.py +1 -3
  272. hestia_earth/models/pooreNemecek2018/ch4ToAirAquacultureSystems.py +1 -2
  273. hestia_earth/models/pooreNemecek2018/excretaKgN.py +3 -4
  274. hestia_earth/models/pooreNemecek2018/excretaKgVs.py +4 -4
  275. hestia_earth/models/pooreNemecek2018/freshwaterWithdrawalsDuringCycle.py +3 -9
  276. hestia_earth/models/pooreNemecek2018/landOccupationDuringCycle.py +3 -7
  277. hestia_earth/models/pooreNemecek2018/longFallowDuration.py +1 -7
  278. hestia_earth/models/pooreNemecek2018/n2OToAirAquacultureSystemsDirect.py +1 -2
  279. hestia_earth/models/pooreNemecek2018/n2ToAirAquacultureSystems.py +1 -2
  280. hestia_earth/models/pooreNemecek2018/nh3ToAirAquacultureSystems.py +1 -2
  281. hestia_earth/models/pooreNemecek2018/no3ToGroundwaterCropResidueDecomposition.py +1 -2
  282. hestia_earth/models/pooreNemecek2018/no3ToGroundwaterExcreta.py +1 -2
  283. hestia_earth/models/pooreNemecek2018/no3ToGroundwaterInorganicFertiliser.py +1 -2
  284. hestia_earth/models/pooreNemecek2018/no3ToGroundwaterOrganicFertiliser.py +1 -2
  285. hestia_earth/models/pooreNemecek2018/no3ToGroundwaterSoilFlux_utils.py +1 -2
  286. hestia_earth/models/pooreNemecek2018/noxToAirAquacultureSystems.py +1 -2
  287. hestia_earth/models/pooreNemecek2018/nurseryDensity.py +1 -7
  288. hestia_earth/models/pooreNemecek2018/nurseryDuration.py +1 -7
  289. hestia_earth/models/pooreNemecek2018/plantationDensity.py +1 -7
  290. hestia_earth/models/pooreNemecek2018/plantationLifespan.py +1 -7
  291. hestia_earth/models/pooreNemecek2018/plantationProductiveLifespan.py +1 -7
  292. hestia_earth/models/pooreNemecek2018/rotationDuration.py +1 -7
  293. hestia_earth/models/pooreNemecek2018/saplingsDepreciatedAmountPerCycle.py +1 -7
  294. hestia_earth/models/poschEtAl2008/terrestrialAcidificationPotentialAccumulatedExceedance.py +1 -7
  295. hestia_earth/models/poschEtAl2008/terrestrialEutrophicationPotentialAccumulatedExceedance.py +1 -7
  296. hestia_earth/models/recipe2016Egalitarian/damageToFreshwaterEcosystemsSpeciesYear.py +1 -7
  297. hestia_earth/models/recipe2016Egalitarian/damageToHumanHealth.py +1 -7
  298. hestia_earth/models/recipe2016Egalitarian/damageToMarineEcosystemsSpeciesYear.py +1 -7
  299. hestia_earth/models/recipe2016Egalitarian/damageToResourceAvailability.py +1 -7
  300. hestia_earth/models/recipe2016Egalitarian/damageToTerrestrialEcosystemsSpeciesYear.py +1 -7
  301. hestia_earth/models/recipe2016Egalitarian/ecosystemDamageOzoneFormation.py +1 -7
  302. hestia_earth/models/recipe2016Egalitarian/fossilResourceScarcity.py +1 -7
  303. hestia_earth/models/recipe2016Egalitarian/freshwaterAquaticEcotoxicityPotential14Dcbeq.py +1 -7
  304. hestia_earth/models/recipe2016Egalitarian/freshwaterEutrophicationPotential.py +1 -7
  305. hestia_earth/models/recipe2016Egalitarian/humanCarcinogenicToxicity.py +1 -7
  306. hestia_earth/models/recipe2016Egalitarian/humanDamageOzoneFormation.py +2 -10
  307. hestia_earth/models/recipe2016Egalitarian/humanNonCarcinogenicToxicity.py +1 -7
  308. hestia_earth/models/recipe2016Egalitarian/marineAquaticEcotoxicityPotential14Dcbeq.py +1 -7
  309. hestia_earth/models/recipe2016Egalitarian/marineEutrophicationPotential.py +1 -7
  310. hestia_earth/models/recipe2016Egalitarian/ozoneDepletionPotential.py +1 -7
  311. hestia_earth/models/recipe2016Egalitarian/terrestrialAcidificationPotential.py +1 -7
  312. hestia_earth/models/recipe2016Egalitarian/terrestrialEcotoxicityPotential14Dcbeq.py +1 -7
  313. hestia_earth/models/recipe2016Hierarchist/damageToFreshwaterEcosystemsSpeciesYear.py +1 -7
  314. hestia_earth/models/recipe2016Hierarchist/damageToHumanHealth.py +1 -7
  315. hestia_earth/models/recipe2016Hierarchist/damageToMarineEcosystemsSpeciesYear.py +1 -7
  316. hestia_earth/models/recipe2016Hierarchist/damageToResourceAvailability.py +1 -7
  317. hestia_earth/models/recipe2016Hierarchist/damageToTerrestrialEcosystemsSpeciesYear.py +1 -7
  318. hestia_earth/models/recipe2016Hierarchist/ecosystemDamageOzoneFormation.py +2 -10
  319. hestia_earth/models/recipe2016Hierarchist/fossilResourceScarcity.py +1 -7
  320. hestia_earth/models/recipe2016Hierarchist/freshwaterAquaticEcotoxicityPotential14Dcbeq.py +1 -7
  321. hestia_earth/models/recipe2016Hierarchist/freshwaterEutrophicationPotential.py +1 -7
  322. hestia_earth/models/recipe2016Hierarchist/humanCarcinogenicToxicity.py +1 -7
  323. hestia_earth/models/recipe2016Hierarchist/humanDamageOzoneFormation.py +2 -10
  324. hestia_earth/models/recipe2016Hierarchist/humanNonCarcinogenicToxicity.py +1 -7
  325. hestia_earth/models/recipe2016Hierarchist/marineAquaticEcotoxicityPotential14Dcbeq.py +1 -7
  326. hestia_earth/models/recipe2016Hierarchist/marineEutrophicationPotential.py +1 -7
  327. hestia_earth/models/recipe2016Hierarchist/ozoneDepletionPotential.py +1 -7
  328. hestia_earth/models/recipe2016Hierarchist/terrestrialAcidificationPotential.py +1 -7
  329. hestia_earth/models/recipe2016Hierarchist/terrestrialEcotoxicityPotential14Dcbeq.py +1 -7
  330. hestia_earth/models/recipe2016Individualist/damageToFreshwaterEcosystemsSpeciesYear.py +1 -7
  331. hestia_earth/models/recipe2016Individualist/damageToHumanHealth.py +1 -7
  332. hestia_earth/models/recipe2016Individualist/damageToMarineEcosystemsSpeciesYear.py +1 -7
  333. hestia_earth/models/recipe2016Individualist/damageToResourceAvailability.py +1 -7
  334. hestia_earth/models/recipe2016Individualist/damageToTerrestrialEcosystemsSpeciesYear.py +1 -7
  335. hestia_earth/models/recipe2016Individualist/ecosystemDamageOzoneFormation.py +2 -10
  336. hestia_earth/models/recipe2016Individualist/fossilResourceScarcity.py +1 -7
  337. hestia_earth/models/recipe2016Individualist/freshwaterAquaticEcotoxicityPotential14Dcbeq.py +1 -7
  338. hestia_earth/models/recipe2016Individualist/freshwaterEutrophicationPotential.py +1 -7
  339. hestia_earth/models/recipe2016Individualist/humanCarcinogenicToxicity.py +1 -7
  340. hestia_earth/models/recipe2016Individualist/humanDamageOzoneFormation.py +2 -10
  341. hestia_earth/models/recipe2016Individualist/humanNonCarcinogenicToxicity.py +1 -7
  342. hestia_earth/models/recipe2016Individualist/marineAquaticEcotoxicityPotential14Dcbeq.py +1 -7
  343. hestia_earth/models/recipe2016Individualist/marineEutrophicationPotential.py +1 -7
  344. hestia_earth/models/recipe2016Individualist/ozoneDepletionPotential.py +1 -7
  345. hestia_earth/models/recipe2016Individualist/terrestrialAcidificationPotential.py +1 -7
  346. hestia_earth/models/recipe2016Individualist/terrestrialEcotoxicityPotential14Dcbeq.py +1 -7
  347. hestia_earth/models/resourceUseNotRelevant/__init__.py +2 -8
  348. hestia_earth/models/schererPfister2015/nErosionSoilFlux.py +1 -2
  349. hestia_earth/models/schererPfister2015/pErosionSoilFlux.py +1 -2
  350. hestia_earth/models/schererPfister2015/pToDrainageWaterSoilFlux.py +1 -2
  351. hestia_earth/models/schererPfister2015/pToGroundwaterSoilFlux.py +1 -2
  352. hestia_earth/models/schererPfister2015/pToSurfaceWaterSoilFlux.py +1 -2
  353. hestia_earth/models/schmidt2007/ch4ToAirWasteTreatment.py +1 -2
  354. hestia_earth/models/schmidt2007/h2SToAirWasteTreatment.py +1 -2
  355. hestia_earth/models/schmidt2007/n2OToAirWasteTreatmentDirect.py +1 -2
  356. hestia_earth/models/schmidt2007/nh3ToAirWasteTreatment.py +1 -2
  357. hestia_earth/models/site/grouped_measurement.py +2 -3
  358. hestia_earth/models/stehfestBouwman2006/n2OToAirCropResidueDecompositionDirect.py +1 -2
  359. hestia_earth/models/stehfestBouwman2006/n2OToAirExcretaDirect.py +1 -2
  360. hestia_earth/models/stehfestBouwman2006/n2OToAirInorganicFertiliserDirect.py +1 -2
  361. hestia_earth/models/stehfestBouwman2006/n2OToAirOrganicFertiliserDirect.py +1 -2
  362. hestia_earth/models/stehfestBouwman2006/n2OToAirSoilFlux_utils.py +1 -2
  363. hestia_earth/models/stehfestBouwman2006/noxToAirCropResidueDecomposition.py +1 -2
  364. hestia_earth/models/stehfestBouwman2006/noxToAirExcreta.py +1 -2
  365. hestia_earth/models/stehfestBouwman2006/noxToAirInorganicFertiliser.py +1 -2
  366. hestia_earth/models/stehfestBouwman2006/noxToAirOrganicFertiliser.py +1 -2
  367. hestia_earth/models/stehfestBouwman2006/noxToAirSoilFlux_utils.py +1 -2
  368. hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirCropResidueDecomposition.py +1 -2
  369. hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirExcreta.py +1 -2
  370. hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirInorganicFertiliser.py +1 -2
  371. hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirOrganicFertiliser.py +1 -2
  372. hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirSoilFlux_utils.py +1 -2
  373. hestia_earth/models/transformation/input/excreta.py +7 -5
  374. hestia_earth/models/transformation/product/excreta.py +4 -4
  375. hestia_earth/models/usetoxV2/freshwaterEcotoxicityPotentialCtue.py +1 -7
  376. hestia_earth/models/utils/__init__.py +26 -7
  377. hestia_earth/models/utils/background_emissions.py +3 -3
  378. hestia_earth/models/utils/blank_node.py +31 -12
  379. hestia_earth/models/utils/emission.py +24 -10
  380. hestia_earth/models/utils/indicator.py +28 -13
  381. hestia_earth/models/utils/input.py +22 -5
  382. hestia_earth/models/utils/management.py +18 -4
  383. hestia_earth/models/utils/measurement.py +14 -6
  384. hestia_earth/models/utils/practice.py +24 -4
  385. hestia_earth/models/utils/product.py +32 -14
  386. hestia_earth/models/version.py +1 -1
  387. hestia_earth/models/webbEtAl2012AndSintermannEtAl2012/nh3ToAirOrganicFertiliser.py +1 -2
  388. hestia_earth/orchestrator/models/__init__.py +22 -11
  389. {hestia_earth_models-0.74.10.dist-info → hestia_earth_models-0.74.12.dist-info}/METADATA +2 -2
  390. {hestia_earth_models-0.74.10.dist-info → hestia_earth_models-0.74.12.dist-info}/RECORD +410 -404
  391. tests/models/cml2001Baseline/test_abioticResourceDepletionFossilFuels.py +4 -9
  392. tests/models/cml2001Baseline/test_abioticResourceDepletionMineralsAndMetals.py +4 -9
  393. tests/models/environmentalFootprintV3_1/test_environmentalFootprintSingleOverallScore.py +4 -9
  394. tests/models/environmentalFootprintV3_1/test_marineEutrophicationPotential.py +1 -7
  395. tests/models/environmentalFootprintV3_1/test_scarcityWeightedWaterUse.py +2 -8
  396. tests/models/environmentalFootprintV3_1/test_soilQualityIndexLandTransformation.py +3 -13
  397. tests/models/hestia/test_default_emissions.py +2 -2
  398. tests/models/hestia/test_default_resourceUse.py +3 -3
  399. tests/models/hestia/test_landCover.py +42 -295
  400. tests/models/hestia/test_landCover_utils.py +233 -0
  401. tests/models/hestia/test_slope.py +23 -0
  402. tests/models/hestia/test_slopeLength.py +23 -0
  403. tests/models/ipcc2019/test_no3ToGroundwaterExcreta.py +1 -0
  404. tests/models/poschEtAl2008/test_terrestrialAcidificationPotentialAccumulatedExceedance.py +4 -9
  405. tests/models/poschEtAl2008/test_terrestrialEutrophicationPotentialAccumulatedExceedance.py +4 -9
  406. tests/models/utils/test_indicator.py +6 -4
  407. tests/models/utils/test_measurement.py +7 -5
  408. {hestia_earth_models-0.74.10.dist-info → hestia_earth_models-0.74.12.dist-info}/LICENSE +0 -0
  409. {hestia_earth_models-0.74.10.dist-info → hestia_earth_models-0.74.12.dist-info}/WHEEL +0 -0
  410. {hestia_earth_models-0.74.10.dist-info → hestia_earth_models-0.74.12.dist-info}/top_level.txt +0 -0
@@ -1,35 +1,28 @@
1
- import functools
2
- import math
3
- from collections import defaultdict
4
1
  from datetime import datetime, timedelta
5
2
  from hestia_earth.schema import SiteSiteType, TermTermType
6
- from hestia_earth.utils.lookup import (
7
- download_lookup, get_table_value, column_name, _is_missing_value, extract_grouped_data, lookup_columns
8
- )
9
3
  from hestia_earth.utils.model import filter_list_term_type
10
- from hestia_earth.utils.tools import safe_parse_float, to_precision
4
+ from hestia_earth.utils.tools import to_precision, omit, pick, non_empty_list
5
+ from hestia_earth.utils.blank_node import group_by_keys
11
6
 
12
7
  from hestia_earth.models.log import logRequirements, log_as_table, logShouldRun
13
- from hestia_earth.models.utils import _omit, clamp
14
8
  from hestia_earth.models.utils.constant import DAYS_IN_YEAR
15
9
  from hestia_earth.models.utils.management import _new_management
16
- from hestia_earth.models.utils.term import get_lookup_value
17
- from hestia_earth.models.utils.lookup import get_region_lookup, get_region_lookup_value
18
10
  from hestia_earth.models.utils.blank_node import DatestrFormat, _gapfill_datestr, DatestrGapfillMode
19
11
  from .utils import (
20
- IPCC_LAND_USE_CATEGORY_ANNUAL,
21
- IPCC_LAND_USE_CATEGORY_PERENNIAL,
22
12
  LAND_USE_TERMS_FOR_TRANSFORMATION,
13
+ IPCC_LAND_USE_CATEGORY_PERENNIAL,
23
14
  ANNUAL_CROPLAND,
24
15
  PERMANENT_CROPLAND,
25
- FOREST_LAND,
26
16
  OTHER_LAND,
27
17
  PERMANENT_PASTURE,
28
18
  TOTAL_CROPLAND,
29
- TOTAL_AGRICULTURAL_CHANGE,
30
- ALL_LAND_USE_TERMS,
31
19
  crop_ipcc_land_use_category
32
20
  )
21
+ from .landCover_utils import (
22
+ _get_lookup_with_cache,
23
+ get_site_area_from_lookups,
24
+ compute_site_area
25
+ )
33
26
  from . import MODEL
34
27
 
35
28
  REQUIREMENTS = {
@@ -86,123 +79,21 @@ LOOKUPS = {
86
79
  }
87
80
  MODEL_KEY = 'landCover'
88
81
 
89
- _LAND_AREA = LOOKUPS["region-faostatArea"][3]
90
- _ALLOWED_SITE_TYPES = {
91
- SiteSiteType.CROPLAND.value,
92
- SiteSiteType.FOREST.value,
93
- SiteSiteType.OTHER_NATURAL_VEGETATION.value,
94
- SiteSiteType.PERMANENT_PASTURE.value
95
- }
96
82
  _BUILDING_SITE_TYPES = [
97
83
  SiteSiteType.AGRI_FOOD_PROCESSOR.value,
98
84
  SiteSiteType.ANIMAL_HOUSING.value,
99
85
  SiteSiteType.FOOD_RETAILER.value
100
86
  ]
87
+ _ALLOWED_SITE_TYPES = [
88
+ SiteSiteType.CROPLAND.value,
89
+ SiteSiteType.FOREST.value,
90
+ SiteSiteType.OTHER_NATURAL_VEGETATION.value,
91
+ SiteSiteType.PERMANENT_PASTURE.value
92
+ ]
101
93
  _DEFAULT_WINDOW_IN_YEARS = 20
102
- _DATE_TOLERANCE_IN_YEARS = 2
103
- _OUTPUT_SIGNIFICANT_DIGITS = 3
94
+ _DATE_TOLERANCE_IN_YEARS = 1
104
95
  _ALLOWED_LAND_USE_TYPES = [ANNUAL_CROPLAND, PERMANENT_CROPLAND, PERMANENT_PASTURE, TOTAL_CROPLAND]
105
- _TOP_LEVEL_LAND_USE_TYPE_IDS = {"annualCropland", "permanentCropland", "permanentPasture", "cropland"}
106
- _LOOKUP_EXPANSION = "region-crop-cropGroupingFaostatProduction-areaHarvestedUpTo20YearExpansion.csv"
107
- _COMPLETE_CHANGES_OTHER_LAND = {
108
- OTHER_LAND: 1,
109
- FOREST_LAND: 0,
110
- PERMANENT_PASTURE: 0,
111
- ANNUAL_CROPLAND: 0,
112
- PERMANENT_CROPLAND: 0
113
- }
114
-
115
-
116
- def _get_lookup_with_cache(lookup_term, column):
117
- """Wrapper to get_lookup_value which pulls out the immutable parts of the term to allow caching."""
118
- @functools.cache
119
- def _get_immutable_lookup(term_id: str, term_type: str, col: str):
120
- new_term = {"@id": term_id, "termType": term_type} if term_type and term_id else {}
121
- return get_lookup_value(
122
- lookup_term=new_term,
123
- column=col,
124
- skip_debug=False,
125
- model=MODEL,
126
- term=new_term
127
- )
128
-
129
- return _get_immutable_lookup(
130
- term_id=lookup_term.get("@id"),
131
- term_type=lookup_term.get("termType"),
132
- col=column
133
- )
134
-
135
-
136
- def _get_land_cover_lookup_suffix(land_type: str) -> str:
137
- return LAND_USE_TERMS_FOR_TRANSFORMATION[land_type][0]
138
-
139
-
140
- def get_landCover_lookups(country_id: str, end_year: int, product_name: str):
141
- """
142
- Attempts to get the pre-calculated values for the landCover model calculation.
143
- Returns: {"Arable land": <value>, "Forest land": <value>, "Other land": <value>,
144
- "Permanent crops": <value>, "Permanent meadows and pastures": <value>}
145
- Missing values are returned as None.
146
- """
147
- lookup_prefix = 'region-crop-cropGroupingFAOSTAT-landCover'
148
- return {
149
- # Divide by 100 to match site_area ratios
150
- land_type: value / 100 if value is not None else value
151
- for land_type, value in
152
- {
153
- land_type: safe_parse_float(
154
- value=extract_grouped_data(
155
- data=get_region_lookup_value(
156
- lookup_name=f"{lookup_prefix}-{_get_land_cover_lookup_suffix(land_type)}.csv",
157
- term_id=country_id,
158
- column=product_name,
159
- model=MODEL,
160
- key=MODEL_KEY
161
- ),
162
- key=str(end_year)
163
- ),
164
- default=None
165
- )
166
- for land_type in LAND_USE_TERMS_FOR_TRANSFORMATION.keys()
167
- }.items()
168
- }
169
-
170
-
171
- def _management(term_id: str, value: float, start_date: str, end_date: str):
172
- node = _new_management(term_id, MODEL)
173
- node['value'] = value
174
- node['startDate'] = start_date
175
- node['endDate'] = end_date
176
- return node
177
-
178
-
179
- def _is_missing_or_none(value) -> bool:
180
- return value is None or _is_missing_value(value)
181
-
182
-
183
- def _safe_divide(numerator, denominator, default=0) -> float:
184
- return default if not denominator else numerator / denominator
185
-
186
-
187
- def scale_values_to_one(dictionary: dict) -> dict:
188
- """
189
- Takes a dictionary with numeric values.
190
- Scales each value so that the sum of them all is one.
191
- """
192
- # Does not handle negative values.
193
- sum_of_values = sum(dictionary.values())
194
- return {key: value / sum_of_values for key, value in dictionary.items()} if sum_of_values != 0 else dictionary
195
-
196
-
197
- def cap_values(dictionary: dict, lower_limit: float = 0, upper_limit: float = 1) -> dict:
198
- return {key: min([upper_limit, max([lower_limit, value])]) for key, value in dictionary.items()}
199
-
200
-
201
- def site_area_sum_to_100(dict_of_percentages: dict):
202
- return False if dict_of_percentages == {} else (
203
- math.isclose(sum(dict_of_percentages.values()), 1.0, rel_tol=0.05) or
204
- math.isclose(sum(dict_of_percentages.values()), 0.0, rel_tol=0.01)
205
- )
96
+ _COMPLETE_CHANGES_OTHER_LAND = {k: 0 for k in LAND_USE_TERMS_FOR_TRANSFORMATION.keys()} | {OTHER_LAND: 1}
206
97
 
207
98
 
208
99
  def _should_group_landCover(management_node: dict):
@@ -215,732 +106,6 @@ def _should_group_landCover(management_node: dict):
215
106
  )
216
107
 
217
108
 
218
- def _get_changes(country_id: str, end_year: int) -> tuple[dict, list]:
219
- """
220
- For each entry in ALL_LAND_USE_TERMS, creates a key: value in output dictionary, also TOTAL
221
- """
222
- lookup_name = "region-faostatArea-UpTo20YearExpansion.csv"
223
- changes_dict = {
224
- land_use_term: safe_parse_float(
225
- extract_grouped_data(
226
- get_region_lookup_value(lookup_name, country_id, land_use_term, model=MODEL, key=MODEL_KEY),
227
- str(end_year)
228
- ),
229
- default=None
230
- )
231
- for land_use_term in ALL_LAND_USE_TERMS + [_LAND_AREA]
232
- }
233
- missing_changes = [k for k, v in changes_dict.items() if v is None]
234
- changes_dict = {k: v if v is not None else 0 for k, v in changes_dict.items()}
235
- changes_dict[TOTAL_AGRICULTURAL_CHANGE] = (
236
- float(changes_dict.get(TOTAL_CROPLAND, 0)) + float(changes_dict.get(PERMANENT_PASTURE, 0))
237
- )
238
-
239
- return changes_dict, missing_changes
240
-
241
-
242
- def _get_ratio_start_and_end_values(
243
- expansion: float,
244
- fao_name: str,
245
- country_id: str,
246
- end_year: int
247
- ) -> float:
248
- # expansion over twenty years / current area
249
- table_value = get_region_lookup_value('region-faostatArea.csv', country_id, fao_name, model=MODEL, key=MODEL_KEY)
250
- end_value = safe_parse_float(value=extract_grouped_data(table_value, str(end_year)), default=None)
251
- return max(0.0, _safe_divide(numerator=expansion, denominator=end_value))
252
-
253
-
254
- def _get_ratio_between_land_use_types(
255
- country_id: str,
256
- end_year: int,
257
- first_land_use_term: str,
258
- second_land_use_term: str
259
- ) -> tuple:
260
- """Returns a tuple of the values of the two land use terms for the same country and year."""
261
- return tuple([
262
- safe_parse_float(value=extract_grouped_data(
263
- get_region_lookup_value(
264
- 'region-faostatArea.csv', country_id, land_use_term, model=MODEL, key=MODEL_KEY
265
- ),
266
- str(end_year)),
267
- default=None
268
- )
269
- for land_use_term in [first_land_use_term, second_land_use_term]
270
- ])
271
-
272
-
273
- def _estimate_maximum_forest_change(
274
- forest_change: float, total_cropland_change: float, pasture_change: float, total_agricultural_change: float
275
- ):
276
- """
277
- (L): Estimate maximum forest loss
278
- Gives a negative number representing forest loss. Does not currently handle forest gain.
279
- """
280
- positive_change = pasture_change > 0 and total_cropland_change > 0
281
- return _negative_agricultural_land_change(
282
- forest_change=forest_change,
283
- pasture_change=pasture_change,
284
- total_cropland_change=total_cropland_change
285
- ) if not positive_change else (
286
- -total_agricultural_change
287
- if -min(forest_change, 0) > total_agricultural_change else
288
- min(forest_change, 0)
289
- )
290
-
291
-
292
- def _negative_agricultural_land_change(forest_change, pasture_change, total_cropland_change):
293
- return (
294
- -pasture_change if 0 < pasture_change < -min(forest_change, 0)
295
- else min(forest_change, 0) if pasture_change > 0
296
- else -total_cropland_change if 0 < total_cropland_change < -min(forest_change, 0)
297
- else min(forest_change, 0) if 0 < total_cropland_change
298
- else 0
299
- )
300
-
301
-
302
- def _allocate_forest_loss(forest_loss: float, changes: dict):
303
- """Allocate forest loss between agricultural categories for the specific country"""
304
- return {
305
- TOTAL_CROPLAND: forest_loss * _safe_divide(
306
- numerator=max(changes[TOTAL_CROPLAND], 0),
307
- denominator=max(changes[TOTAL_CROPLAND], 0) + max(changes[PERMANENT_PASTURE], 0)
308
- ),
309
- PERMANENT_PASTURE: forest_loss * _safe_divide(
310
- numerator=max(changes[PERMANENT_PASTURE], 0),
311
- denominator=max(changes[TOTAL_CROPLAND], 0) + max(changes[PERMANENT_PASTURE], 0)
312
- )
313
- }
314
-
315
-
316
- def _additional_allocation(changes, max_forest_loss_to_cropland, max_forest_loss_to_permanent_pasture):
317
- """Determine how much area still needs to be assigned"""
318
- return {
319
- TOTAL_CROPLAND: max(changes[TOTAL_CROPLAND], 0) + max_forest_loss_to_cropland,
320
- PERMANENT_PASTURE: max(changes[PERMANENT_PASTURE], 0) + max_forest_loss_to_permanent_pasture
321
- }
322
-
323
-
324
- def _allocate_cropland_loss_to_pasture(changes: dict, land_required_for_permanent_pasture: float):
325
- """Allocate changes between Permanent pasture and cropland"""
326
- return (
327
- max(-land_required_for_permanent_pasture, changes[TOTAL_CROPLAND])
328
- if changes[TOTAL_CROPLAND] < 0 else 0
329
- )
330
-
331
-
332
- def _allocate_pasture_loss_to_cropland(changes: dict, land_required_for_cropland: float):
333
- """Allocate changes between Permanent pasture and cropland"""
334
- return (
335
- max(-land_required_for_cropland, changes[PERMANENT_PASTURE])
336
- if changes[PERMANENT_PASTURE] < 0 else 0
337
- )
338
-
339
-
340
- def _allocate_other_land(
341
- changes: dict, max_forest_loss_to: dict, pasture_loss_to_cropland: float, cropland_loss_to_pasture: float
342
- ) -> dict:
343
- """Allocate changes between Other land and cropland"""
344
- other_land_loss_to_cropland = (
345
- -(max(changes[TOTAL_CROPLAND], 0) + max_forest_loss_to[TOTAL_CROPLAND]
346
- + pasture_loss_to_cropland)
347
- )
348
- other_land_loss_to_pasture = (
349
- -(max(changes[PERMANENT_PASTURE], 0) + max_forest_loss_to[PERMANENT_PASTURE]
350
- + cropland_loss_to_pasture)
351
- )
352
- return {
353
- TOTAL_CROPLAND: other_land_loss_to_cropland,
354
- PERMANENT_PASTURE: other_land_loss_to_pasture,
355
- TOTAL_AGRICULTURAL_CHANGE: other_land_loss_to_cropland + other_land_loss_to_pasture
356
- }
357
-
358
-
359
- def _allocate_annual_permanent_cropland_losses(changes: dict) -> tuple:
360
- """
361
- (Z, AA): Allocate changes between Annual cropland and Permanent cropland
362
- Returns: annual_cropland_loss_to_permanent_cropland, permanent_cropland_loss_to_annual_cropland
363
- """
364
- return (
365
- -min(-changes[ANNUAL_CROPLAND], changes[PERMANENT_CROPLAND])
366
- if (changes[ANNUAL_CROPLAND] < 0 and changes[PERMANENT_CROPLAND] > 0) else 0,
367
- -min(changes[ANNUAL_CROPLAND], -changes[PERMANENT_CROPLAND])
368
- if (changes[ANNUAL_CROPLAND] > 0 and changes[PERMANENT_CROPLAND] < 0) else 0
369
- )
370
-
371
-
372
- def _estimate_conversions_to_annual_cropland(
373
- changes: dict,
374
- pasture_loss_to_crops: float,
375
- forest_loss_to_cropland: float,
376
- other_land_loss_to_annual_cropland: float,
377
- permanent_to_annual_cropland: float
378
- ) -> dict:
379
- """(AC-AG): Estimate percentage of land sources when converted to: Annual cropland"""
380
-
381
- # -> percent_annual_cropland_was[]
382
- def conversion_to_annual_cropland(factor: float):
383
- return factor * _safe_divide(
384
- numerator=_safe_divide(
385
- numerator=max(changes[ANNUAL_CROPLAND], 0),
386
- denominator=max(changes[ANNUAL_CROPLAND], 0) + max(changes[PERMANENT_CROPLAND], 0)
387
- ),
388
- denominator=-changes[ANNUAL_CROPLAND]
389
- )
390
-
391
- percentages = {
392
- FOREST_LAND: conversion_to_annual_cropland(forest_loss_to_cropland),
393
- OTHER_LAND: conversion_to_annual_cropland(other_land_loss_to_annual_cropland),
394
- PERMANENT_PASTURE: conversion_to_annual_cropland(pasture_loss_to_crops),
395
- PERMANENT_CROPLAND: _safe_divide(numerator=permanent_to_annual_cropland, denominator=-changes[ANNUAL_CROPLAND])
396
- }
397
- return percentages
398
-
399
-
400
- def _estimate_conversions_to_permanent_cropland(
401
- changes: dict,
402
- annual_loss_to_permanent_cropland: float,
403
- pasture_loss_to_cropland: float,
404
- forest_loss_to_cropland: float,
405
- other_land_loss_to_annual_cropland: float
406
- ) -> dict:
407
- """Estimate percentage of land sources when converted to: Permanent cropland"""
408
-
409
- def conversion_to_permanent_cropland(factor: float):
410
- return _safe_divide(
411
- numerator=_safe_divide(
412
- numerator=factor * max(changes[PERMANENT_CROPLAND], 0),
413
- denominator=max(changes[ANNUAL_CROPLAND], 0) + max(changes[PERMANENT_CROPLAND], 0)
414
- ),
415
- denominator=-changes[PERMANENT_CROPLAND]
416
- )
417
-
418
- percentages = {
419
- FOREST_LAND: conversion_to_permanent_cropland(forest_loss_to_cropland),
420
- OTHER_LAND: conversion_to_permanent_cropland(other_land_loss_to_annual_cropland),
421
- PERMANENT_PASTURE: conversion_to_permanent_cropland(pasture_loss_to_cropland),
422
- ANNUAL_CROPLAND: conversion_to_permanent_cropland(annual_loss_to_permanent_cropland)
423
- }
424
- return percentages
425
-
426
-
427
- def _estimate_conversions_to_pasture(
428
- changes: dict,
429
- forest_loss_to_pasture: float,
430
- total_cropland_loss_to_pasture: float,
431
- other_land_loss_to_pasture: float
432
- ) -> dict:
433
- """Estimate percentage of land sources when converted to: Permanent pasture"""
434
- percentages = {
435
- FOREST_LAND: _safe_divide(
436
- numerator=forest_loss_to_pasture,
437
- denominator=-changes[PERMANENT_PASTURE],
438
- ),
439
- OTHER_LAND: _safe_divide(
440
- numerator=other_land_loss_to_pasture,
441
- denominator=-changes[PERMANENT_PASTURE]
442
- ),
443
- # AT
444
- ANNUAL_CROPLAND: _safe_divide(
445
- numerator=total_cropland_loss_to_pasture * _safe_divide(
446
- numerator=min(changes[ANNUAL_CROPLAND], 0),
447
- denominator=(min(changes[ANNUAL_CROPLAND], 0) + min(changes[PERMANENT_CROPLAND], 0))
448
- ),
449
- denominator=-changes[PERMANENT_PASTURE]
450
- ),
451
- PERMANENT_CROPLAND: _safe_divide(
452
- numerator=total_cropland_loss_to_pasture * _safe_divide(
453
- numerator=min(changes[PERMANENT_CROPLAND], 0),
454
- denominator=(min(changes[ANNUAL_CROPLAND], 0) + min(changes[PERMANENT_CROPLAND], 0))
455
- ),
456
- denominator=-changes[PERMANENT_PASTURE]
457
- )
458
- }
459
- return percentages
460
-
461
-
462
- def _get_shares_of_expansion(
463
- land_use_type: str,
464
- percent_annual_cropland_was: dict,
465
- percent_permanent_cropland_was: dict,
466
- percent_pasture_was: dict
467
- ) -> dict:
468
- expansion_for_type = {
469
- ANNUAL_CROPLAND: percent_annual_cropland_was,
470
- PERMANENT_CROPLAND: percent_permanent_cropland_was,
471
- PERMANENT_PASTURE: percent_pasture_was
472
- }
473
- return scale_values_to_one({
474
- k: expansion_for_type[land_use_type].get(k, 0)
475
- for k in LAND_USE_TERMS_FOR_TRANSFORMATION.keys()
476
- })
477
-
478
-
479
- def _get_faostat_name(term: dict) -> str:
480
- """For landCover terms, find the cropGroupingFaostatArea name for the landCover id."""
481
- return _get_lookup_with_cache(term, "cropGroupingFaostatArea")
482
-
483
-
484
- def _get_most_common_or_alphabetically_first(crop_terms: list) -> str:
485
- histogram = {term: crop_terms.count(term) for term in crop_terms}
486
- max_freq = max(histogram.values())
487
- # Sorted; to be deterministic
488
- return sorted([term for term, freq in histogram.items() if freq == max_freq])[0]
489
-
490
-
491
- def _get_complete_faostat_to_crop_mapping() -> dict:
492
- """Returns mapping in the format: {faostat_name: IPCC_LAND_USE_CATEGORY, ...}"""
493
- lookup = download_lookup("crop.csv")
494
- mappings = defaultdict(list)
495
- for crop_term_id in [row[0] for row in lookup]:
496
- key = column_name(
497
- get_table_value(lookup, 'termid', crop_term_id, column_name("cropGroupingFaostatArea"))
498
- )
499
- if key:
500
- mappings[key].append(crop_ipcc_land_use_category(crop_term_id=crop_term_id, lookup_term_type="crop"))
501
- return {
502
- fao_name: _get_most_common_or_alphabetically_first(crop_terms)
503
- for fao_name, crop_terms in mappings.items()
504
- }
505
-
506
-
507
- def _get_harvested_area(country_id: str, year: int, faostat_name: str) -> float:
508
- """
509
- Returns a dictionary of harvested areas for the country & year, indexed by landCover term (crop)
510
- """
511
- lookup_name = "region-crop-cropGroupingFaostatProduction-areaHarvested.csv"
512
-
513
- return safe_parse_float(
514
- value=extract_grouped_data(
515
- data=get_region_lookup_value(lookup_name, country_id, faostat_name, model=MODEL, key=MODEL_KEY),
516
- key=str(year)
517
- ),
518
- default=None
519
- )
520
-
521
-
522
- def _get_term_id_for_crop(nodes: set, land_type: str) -> str:
523
- """Use the original crop term id for permanent/perennial crops and the land use id for other types."""
524
- result = next(
525
- (node for node in nodes if crop_ipcc_land_use_category(node[0]) == IPCC_LAND_USE_CATEGORY_PERENNIAL), None
526
- )
527
- return (
528
- # Take first perennial crop - not multi-cropping
529
- result[0] if land_type == PERMANENT_CROPLAND and result else
530
- LAND_USE_TERMS_FOR_TRANSFORMATION[land_type][0]
531
- )
532
-
533
-
534
- def _run(site: dict, existing_nodes: list, percentage_transformed_from: dict):
535
- start_year = _get_year_from_landCover(existing_nodes[0]) - _DEFAULT_WINDOW_IN_YEARS
536
-
537
- """Creates a list of new management nodes, excluding any dates matching existing ones."""
538
- existing_nodes_set = {
539
- (node.get("term", {}).get("@id", ""), node.get("startDate"), node.get("endDate"))
540
- for node in existing_nodes
541
- }
542
- values = [
543
- {
544
- "land_management_key": (
545
- LAND_USE_TERMS_FOR_TRANSFORMATION[land_type], f"{start_year}-01-01", f"{start_year}-12-31"
546
- ),
547
- "land_type": land_type,
548
- "percentage": 0 if ratio == -0.0 else to_precision(
549
- number=ratio * 100,
550
- digits=_OUTPUT_SIGNIFICANT_DIGITS
551
- ),
552
- "term_id": _get_term_id_for_crop(existing_nodes_set, land_type=land_type)
553
- }
554
- for land_type, ratio in percentage_transformed_from.items()
555
- ]
556
- values = [v for v in values if v.get("land_management_key") not in existing_nodes_set]
557
-
558
- for value in values:
559
- logShouldRun(site, MODEL, value.get("term_id"), True, model_key=MODEL_KEY)
560
-
561
- return [
562
- _management(
563
- term_id=value.get("term_id"),
564
- value=value.get("percentage"),
565
- start_date=value.get("land_management_key")[1],
566
- end_date=value.get("land_management_key")[2]
567
- )
568
- for value in values
569
- ]
570
-
571
-
572
- def get_ratio_of_expanded_area(country_id: str, fao_name: str, end_year: int) -> float:
573
- table_value = get_region_lookup_value(_LOOKUP_EXPANSION, country_id, fao_name, model=MODEL, key=MODEL_KEY)
574
- expansion = safe_parse_float(value=extract_grouped_data(table_value, str(end_year)), default=None)
575
- end_value = _get_harvested_area(
576
- country_id=country_id,
577
- year=end_year,
578
- faostat_name=fao_name
579
- )
580
- return 0.0 if any([expansion is None, end_value is None]) else max(
581
- 0.0, _safe_divide(numerator=expansion, denominator=end_value)
582
- )
583
-
584
-
585
- def _get_sum_for_land_category(
586
- values: dict,
587
- year: int,
588
- ipcc_land_use_category,
589
- fao_stat_to_ipcc_type: dict,
590
- include_negatives: bool = True
591
- ) -> float:
592
- return sum([
593
- safe_parse_float(value=extract_grouped_data(table_value, str(year)), default=None)
594
- for fao_name, table_value in values.items()
595
- if not _is_missing_or_none(extract_grouped_data(table_value, str(year))) and
596
- fao_stat_to_ipcc_type[fao_name] == ipcc_land_use_category and
597
- (
598
- include_negatives or
599
- safe_parse_float(value=extract_grouped_data(table_value, str(year)), default=None) > 0.0
600
- )
601
- ])
602
-
603
-
604
- def _get_sums_of_crop_expansion(country_id: str, year: int, include_negatives: bool = True) -> tuple[float, float]:
605
- """
606
- Sum net expansion for all annual and permanent crops, returned as two values.
607
- Returns a tuple of (expansion of annual crops, expansion of permanent crops)
608
- """
609
- lookup = get_region_lookup(lookup_name=_LOOKUP_EXPANSION, term_id=country_id)
610
- columns = lookup_columns(lookup)
611
- values = {
612
- name: get_table_value(lookup, 'termid', country_id, column_name(name))
613
- for name in columns if name != "termid"
614
- }
615
-
616
- fao_stat_to_ipcc_type = _get_complete_faostat_to_crop_mapping()
617
-
618
- annual_sum_of_expansion = _get_sum_for_land_category(
619
- values=values,
620
- year=year,
621
- ipcc_land_use_category=IPCC_LAND_USE_CATEGORY_ANNUAL,
622
- fao_stat_to_ipcc_type=fao_stat_to_ipcc_type,
623
- include_negatives=include_negatives
624
- )
625
- permanent_sum_of_expansion = _get_sum_for_land_category(
626
- values=values,
627
- year=year,
628
- ipcc_land_use_category=IPCC_LAND_USE_CATEGORY_PERENNIAL,
629
- fao_stat_to_ipcc_type=fao_stat_to_ipcc_type,
630
- include_negatives=include_negatives
631
- )
632
-
633
- return annual_sum_of_expansion, permanent_sum_of_expansion
634
-
635
-
636
- def _get_net_expansion_cultivated_vs_harvested(
637
- annual_crops_net_expansion, changes, land_use_type, permanent_crops_net_expansion
638
- ):
639
- return (
640
- _safe_divide(
641
- numerator=max(0, changes[ANNUAL_CROPLAND]),
642
- denominator=(annual_crops_net_expansion / 1000)
643
- ) if land_use_type == ANNUAL_CROPLAND else
644
- _safe_divide(
645
- numerator=max(0, changes[PERMANENT_CROPLAND]),
646
- denominator=(permanent_crops_net_expansion / 1000)
647
- ) if land_use_type == PERMANENT_CROPLAND else
648
- 1
649
- )
650
-
651
-
652
- def _get_year_from_landCover(node: dict):
653
- date = node.get('startDate') or node.get('endDate')
654
- return int(date[:4])
655
-
656
-
657
- def _scale_site_area_errors(site_area: dict) -> dict:
658
- """Redistribute the result of any rounding error in proportion to the other land use types."""
659
- # Positive errors would not have been capped, so won't be missing.
660
- negative_errors = [v for v in site_area.values() if v < 0.0]
661
- return {k: v + negative_errors[0] * v for k, v in site_area.items()} \
662
- if negative_errors and abs(negative_errors[0]) < 1 and all([v < 1 for v in site_area.values()]) else site_area
663
-
664
-
665
- def _new_landCover_term(new_land_use_term) -> dict:
666
- return {
667
- "@id": LAND_USE_TERMS_FOR_TRANSFORMATION[new_land_use_term][0],
668
- "name": LAND_USE_TERMS_FOR_TRANSFORMATION[new_land_use_term][1],
669
- "@type": "Term",
670
- "termType": TermTermType.LANDCOVER.value
671
- }
672
-
673
-
674
- def _scaled_value(
675
- permanent_crops_value: float,
676
- annual_crops_value: float,
677
- permanent_crops_factor: float,
678
- annual_crops_factor: float,
679
- ):
680
- total_area = permanent_crops_factor + annual_crops_factor
681
- permanent_crops_scaled = permanent_crops_value * permanent_crops_factor / total_area
682
- annual_crops_scaled = annual_crops_value * annual_crops_factor / total_area
683
- return annual_crops_scaled + permanent_crops_scaled
684
-
685
-
686
- def _scale_from_annual_and_permanent_results(
687
- annual_cropland_results: dict,
688
- permanent_cropland_results: dict,
689
- annual_cropland_factor: float,
690
- permanent_cropland_factor: float
691
- ) -> dict:
692
- return {
693
- land_key: _scaled_value(
694
- permanent_crops_value=permanent_cropland_results[land_key],
695
- annual_crops_value=land_value,
696
- permanent_crops_factor=permanent_cropland_factor,
697
- annual_crops_factor=annual_cropland_factor
698
- )
699
- for land_key, land_value in annual_cropland_results.items()
700
- }
701
-
702
-
703
- def _should_run_historical_land_use_change(site: dict, nodes: list, land_use_type: str) -> tuple[bool, dict]:
704
- # Assume a single management node for single-cropping.
705
- return (
706
- _should_run_historical_land_use_change_total_cropland(site, nodes) if land_use_type == TOTAL_CROPLAND else
707
- _should_run_historical_land_use_change_single_crop(
708
- site=site,
709
- term=nodes[0].get("term", {}),
710
- country_id=site.get("country", {}).get("@id"),
711
- end_year=_get_year_from_landCover(nodes[0]),
712
- land_use_type=land_use_type
713
- )
714
- )
715
-
716
-
717
- def _should_run_historical_land_use_change_total_cropland(site: dict, nodes: list) -> tuple[bool, dict]:
718
- end_year = _get_year_from_landCover(nodes[0])
719
- country_id = site.get("country", {}).get("@id")
720
-
721
- # Run _should_run_historical_land_use_change_single_crop for annual and permanent
722
- should_run_annual, areas_for_annual_cropland = _should_run_historical_land_use_change_single_crop(
723
- site=site,
724
- term=_new_landCover_term(ANNUAL_CROPLAND),
725
- country_id=country_id,
726
- end_year=end_year,
727
- land_use_type=ANNUAL_CROPLAND
728
- )
729
- should_run_permanent, areas_for_permanent_cropland = _should_run_historical_land_use_change_single_crop(
730
- site=site,
731
- term=_new_landCover_term(PERMANENT_CROPLAND),
732
- country_id=country_id,
733
- end_year=end_year,
734
- land_use_type=PERMANENT_CROPLAND
735
- )
736
- # Get current ratios ("Arable land" vs "Permanent crops")
737
- annual_cropland_factor, permanent_crops_factor = _get_ratio_between_land_use_types(
738
- country_id=country_id,
739
- end_year=end_year,
740
- first_land_use_term=ANNUAL_CROPLAND,
741
- second_land_use_term=PERMANENT_CROPLAND
742
- ) if should_run_annual and should_run_permanent else tuple([0, 0])
743
- scaled_results = _scale_from_annual_and_permanent_results(
744
- annual_cropland_results=areas_for_annual_cropland,
745
- permanent_cropland_results=areas_for_permanent_cropland,
746
- annual_cropland_factor=annual_cropland_factor,
747
- permanent_cropland_factor=permanent_crops_factor
748
- ) if should_run_annual and should_run_permanent else {}
749
- # Scale results according to current ratios.
750
- return all([should_run_annual, should_run_permanent]), scaled_results
751
-
752
-
753
- def _log_all_terms(site: dict, land_use_type: str, country_id: str, changes: dict, missing_changes: list):
754
- for land_use_term, _ in LAND_USE_TERMS_FOR_TRANSFORMATION.values():
755
- logRequirements(
756
- log_node=site,
757
- model=MODEL,
758
- term=land_use_term,
759
- model_key=MODEL_KEY,
760
- land_use_type=land_use_type,
761
- country_id=country_id,
762
- changes=log_as_table(changes),
763
- missing_changes=log_as_table(missing_changes)
764
- )
765
-
766
- logShouldRun(site, MODEL, land_use_term, False, model_key=MODEL_KEY)
767
-
768
-
769
- def _should_run_historical_land_use_change_single_crop(
770
- site: dict,
771
- term: dict,
772
- country_id: str,
773
- end_year: int,
774
- land_use_type: str
775
- ) -> tuple[bool, dict]:
776
- """Calculate land use change percentages for a single management node/crop."""
777
- # (C-H).
778
- changes, missing_changes = _get_changes(country_id=country_id, end_year=end_year)
779
-
780
- # (L). Estimate maximum forest loss
781
- forest_loss = _estimate_maximum_forest_change(
782
- forest_change=changes[FOREST_LAND],
783
- total_cropland_change=changes[TOTAL_CROPLAND],
784
- pasture_change=changes[PERMANENT_PASTURE],
785
- total_agricultural_change=changes[TOTAL_AGRICULTURAL_CHANGE]
786
- )
787
-
788
- # (M, N). Allocate forest loss between agricultural categories for the specific country
789
- forest_loss_to = _allocate_forest_loss(forest_loss=forest_loss, changes=changes)
790
-
791
- # (P, Q): Determine how much area still needs to be assigned
792
- land_required_for = _additional_allocation(
793
- changes=changes,
794
- max_forest_loss_to_cropland=forest_loss_to[TOTAL_CROPLAND],
795
- max_forest_loss_to_permanent_pasture=forest_loss_to[PERMANENT_PASTURE]
796
- )
797
-
798
- # (R): Allocate changes between Permanent pasture and cropland
799
- cropland_loss_to_pasture = _allocate_cropland_loss_to_pasture(
800
- changes=changes,
801
- land_required_for_permanent_pasture=land_required_for[PERMANENT_PASTURE]
802
- )
803
- # (S)
804
- pasture_loss_to_cropland = _allocate_pasture_loss_to_cropland(
805
- changes=changes,
806
- land_required_for_cropland=land_required_for[TOTAL_CROPLAND]
807
- )
808
-
809
- # (V): Allocate changes between Other land and cropland
810
- other_land_loss_to = _allocate_other_land(
811
- changes=changes,
812
- max_forest_loss_to=forest_loss_to,
813
- pasture_loss_to_cropland=pasture_loss_to_cropland,
814
- cropland_loss_to_pasture=cropland_loss_to_pasture
815
- )
816
-
817
- # (Z, AA): Allocate changes between Annual cropland and Permanent cropland
818
- annual_cropland_loss_to_permanent_cropland, permanent_cropland_loss_to_annual_cropland = (
819
- _allocate_annual_permanent_cropland_losses(changes)
820
- )
821
-
822
- # (AC-AG): Estimate percentage of land sources when converted to: Annual cropland
823
- # Note: All percentages are expressed as decimal fractions. 50% = 0.5
824
- percent_annual_cropland_was = _estimate_conversions_to_annual_cropland(
825
- changes=changes,
826
- pasture_loss_to_crops=pasture_loss_to_cropland,
827
- forest_loss_to_cropland=forest_loss_to[TOTAL_CROPLAND],
828
- other_land_loss_to_annual_cropland=other_land_loss_to[TOTAL_CROPLAND],
829
- permanent_to_annual_cropland=permanent_cropland_loss_to_annual_cropland,
830
- )
831
-
832
- # (AJ-AM): Estimate percentage of land sources when converted to: Permanent cropland
833
- percent_permanent_cropland_was = _estimate_conversions_to_permanent_cropland(
834
- changes=changes,
835
- annual_loss_to_permanent_cropland=annual_cropland_loss_to_permanent_cropland,
836
- pasture_loss_to_cropland=pasture_loss_to_cropland,
837
- forest_loss_to_cropland=forest_loss_to[TOTAL_CROPLAND],
838
- other_land_loss_to_annual_cropland=other_land_loss_to[TOTAL_CROPLAND]
839
- )
840
-
841
- # Estimate percentage of land sources when converted to: Permanent pasture
842
- percent_pasture_was = _estimate_conversions_to_pasture(
843
- changes=changes,
844
- forest_loss_to_pasture=forest_loss_to[PERMANENT_PASTURE],
845
- total_cropland_loss_to_pasture=cropland_loss_to_pasture,
846
- other_land_loss_to_pasture=other_land_loss_to[PERMANENT_PASTURE]
847
- )
848
-
849
- # C14-G14
850
- shares_of_expansion = _get_shares_of_expansion(
851
- land_use_type=land_use_type,
852
- percent_annual_cropland_was=percent_annual_cropland_was,
853
- percent_permanent_cropland_was=percent_permanent_cropland_was,
854
- percent_pasture_was=percent_pasture_was
855
- )
856
-
857
- fao_name = _get_faostat_name(term)
858
-
859
- # Cell E8
860
- expansion_factor = _get_ratio_start_and_end_values(
861
- expansion=changes[land_use_type],
862
- fao_name=land_use_type,
863
- country_id=country_id,
864
- end_year=end_year
865
- ) if term.get("@id") in _TOP_LEVEL_LAND_USE_TYPE_IDS or fao_name == "" else get_ratio_of_expanded_area(
866
- country_id=country_id,
867
- fao_name=fao_name,
868
- end_year=end_year
869
- )
870
-
871
- # E9
872
- annual_crops_net_expansion, permanent_crops_net_expansion = _get_sums_of_crop_expansion(
873
- country_id=country_id,
874
- year=end_year,
875
- include_negatives=True
876
- )
877
- annual_crops_gross_expansion, permanent_crops_gross_expansion = _get_sums_of_crop_expansion(
878
- country_id=country_id,
879
- year=end_year,
880
- include_negatives=False
881
- )
882
- e9_net_expansion = _safe_divide(
883
- numerator=permanent_crops_net_expansion,
884
- denominator=permanent_crops_gross_expansion
885
- ) if land_use_type == PERMANENT_CROPLAND else (
886
- _safe_divide(
887
- numerator=annual_crops_net_expansion,
888
- denominator=annual_crops_gross_expansion
889
- ) if land_use_type == ANNUAL_CROPLAND else 1
890
- )
891
-
892
- # E10: Compare changes to annual/permanent cropland from net expansion.
893
- net_expansion_cultivated_vs_harvested = _get_net_expansion_cultivated_vs_harvested(
894
- annual_crops_net_expansion=annual_crops_net_expansion,
895
- changes=changes,
896
- land_use_type=land_use_type,
897
- permanent_crops_net_expansion=permanent_crops_net_expansion
898
- )
899
- capped_expansion_factor = clamp(
900
- value=expansion_factor * e9_net_expansion * net_expansion_cultivated_vs_harvested,
901
- min_value=0,
902
- max_value=1
903
- )
904
-
905
- site_area = {
906
- land_type: (shares_of_expansion[land_type] * capped_expansion_factor)
907
- for land_type in LAND_USE_TERMS_FOR_TRANSFORMATION.keys()
908
- if land_type != land_use_type
909
- }
910
- site_area[land_use_type] = 1 - sum(site_area.values())
911
- capped_site_area = cap_values(dictionary=_scale_site_area_errors(site_area))
912
-
913
- sum_of_site_areas_is_100 = site_area_sum_to_100(capped_site_area)
914
- site_type_allowed = site.get("siteType") in _ALLOWED_SITE_TYPES
915
-
916
- logRequirements(
917
- log_node=site,
918
- model=MODEL,
919
- term=term.get("@id"),
920
- model_key=MODEL_KEY,
921
- land_use_type=land_use_type,
922
- country_id=country_id,
923
- changes=log_as_table(changes),
924
- missing_changes=log_as_table(missing_changes),
925
- site_area=log_as_table(capped_site_area),
926
- sum_of_site_areas_is_100=sum_of_site_areas_is_100,
927
- site_type_allowed=site_type_allowed
928
- )
929
-
930
- should_run = all([len(missing_changes) == 0, country_id, site_type_allowed, sum_of_site_areas_is_100])
931
- logShouldRun(site, MODEL, term.get("@id"), should_run, model_key=MODEL_KEY)
932
- if not should_run:
933
- _log_all_terms(
934
- site=site,
935
- land_use_type=land_use_type,
936
- country_id=country_id,
937
- changes=changes,
938
- missing_changes=missing_changes
939
- )
940
-
941
- return should_run, capped_site_area
942
-
943
-
944
109
  def _get_land_use_term_from_node(node: dict):
945
110
  return _get_lookup_with_cache(lookup_term=node.get("term", {}), column=LOOKUPS.get("landCover")[1])
946
111
 
@@ -948,28 +113,16 @@ def _get_land_use_term_from_node(node: dict):
948
113
  def _date_strip(date: str): return date[:10] if date else None
949
114
 
950
115
 
951
- def _collect_land_use_types(nodes: list) -> list:
952
- """Look up the land use type from management nodes."""
953
- return [
954
- {
955
- "value": node.get("value", ""),
956
- "term": node.get("term", {}),
957
- "id": node.get("term", {}).get("@id"),
958
- "land-use-type": _get_land_use_term_from_node(node),
959
- "endDate": _date_strip(_gapfill_datestr(datestr=node.get("endDate"), mode=DatestrGapfillMode.END)),
960
- "startDate": _date_strip(_gapfill_datestr(datestr=node.get("startDate"), mode=DatestrGapfillMode.START))
961
- } for node in nodes
962
- ]
116
+ def _date_year(date: str): return int(date[:4]) if date else None
963
117
 
964
118
 
965
- def _no_prior_land_cover_data(nodes: list, target_node: dict) -> bool:
119
+ def _no_prior_land_cover_data(nodes: list, reference_date: str) -> bool:
966
120
  """
967
121
  Returns true if there are no nodes whose start & end dates the target_node falls within,
968
122
  including a tolerance.
969
123
  """
970
124
  target_date = (
971
- datetime.strptime(target_node.get('startDate') or target_node.get('endDate'),
972
- DatestrFormat.YEAR_MONTH_DAY.value)
125
+ datetime.strptime(reference_date, DatestrFormat.YEAR_MONTH_DAY.value)
973
126
  - timedelta(days=_DEFAULT_WINDOW_IN_YEARS * DAYS_IN_YEAR)
974
127
  )
975
128
  tolerance = timedelta(days=_DATE_TOLERANCE_IN_YEARS * DAYS_IN_YEAR)
@@ -982,63 +135,191 @@ def _no_prior_land_cover_data(nodes: list, target_node: dict) -> bool:
982
135
  return len(previous_nodes) == 0
983
136
 
984
137
 
138
+ def _collect_land_use_types(nodes: list) -> list:
139
+ """Look up the land use type from management nodes."""
140
+ return [
141
+ {
142
+ "term": node.get("term", {}),
143
+ "landCover-id": node.get("term", {}).get("@id"),
144
+ "landUseType": _get_land_use_term_from_node(node),
145
+ "endDate": _date_strip(_gapfill_datestr(datestr=node.get("endDate"), mode=DatestrGapfillMode.END)),
146
+ "startDate": _date_strip(_gapfill_datestr(datestr=node.get("startDate"), mode=DatestrGapfillMode.START))
147
+ } for node in nodes
148
+ ]
149
+
150
+
151
+ def _site_area_valid(site_area: dict): return site_area and all([v is not None for v in site_area.values()])
152
+
153
+
154
+ def _extend_site_area(site: dict, management_nodes: list, land_use_node: dict) -> list:
155
+ reference_year = land_use_node['year']
156
+
157
+ has_no_prior_land_cover_data = _no_prior_land_cover_data(
158
+ nodes=management_nodes,
159
+ reference_date=f"{land_use_node['year']}-06-01"
160
+ )
161
+
162
+ site_area_from_lookups = get_site_area_from_lookups(
163
+ country_id=site.get("country", {}).get("@id"),
164
+ reference_year=reference_year,
165
+ term=land_use_node['term']
166
+ )
167
+ has_siteArea_from_lookups = _site_area_valid(site_area_from_lookups)
168
+
169
+ site_area_computed, is_valid, extra_logs = compute_site_area(
170
+ site=site,
171
+ term=land_use_node['term'],
172
+ land_use_type=land_use_node['landUseType'],
173
+ reference_year=reference_year
174
+ ) if not has_siteArea_from_lookups else ({}, False, {})
175
+
176
+ is_perenial = crop_ipcc_land_use_category(land_use_node['landCover-id']) == IPCC_LAND_USE_CATEGORY_PERENNIAL
177
+
178
+ return land_use_node | {
179
+ 'hasNoPriorLandCoverData': has_no_prior_land_cover_data,
180
+ 'isPerenial': is_perenial,
181
+ 'isValid': has_siteArea_from_lookups or is_valid,
182
+ 'siteArea': site_area_from_lookups if has_siteArea_from_lookups else site_area_computed,
183
+ 'site-area-from-lookup': site_area_from_lookups
184
+ } | extra_logs
185
+
186
+
187
+ def _years_from_node(node: dict):
188
+ start_year = _date_year(node['startDate']) if node.get('startDate') else None
189
+ end_year = _date_year(node['endDate']) if node.get('endDate') else None
190
+ return list(range(start_year, end_year + 1)) if start_year and end_year else non_empty_list([start_year, end_year])
191
+
192
+
193
+ def _log_land_use_nodes(site: dict, land_use_nodes: list):
194
+ # group by term to show together in the logs
195
+ grouped_values = group_by_keys(land_use_nodes, ['term-id'])
196
+
197
+ for term_id, values in grouped_values.items():
198
+ # collect logs for every year
199
+ logRequirements(site, model=MODEL, term=term_id, model_key=MODEL_KEY,
200
+ values=log_as_table([
201
+ omit(v, ['term', 'term-id', 'land-type', 'missing-changes']) | {
202
+ key: v.get(key, {}).get(v['land-type'])
203
+ for key in [
204
+ 'changes',
205
+ 'site-area',
206
+ 'site-area-from-lookup',
207
+ 'annualCropland',
208
+ 'permanentCropland',
209
+ ] if key in v
210
+ }
211
+ for v in values
212
+ ]))
213
+
214
+ logShouldRun(site, MODEL, term_id, values[0]['isValid'], model_key=MODEL_KEY)
215
+
216
+
985
217
  def _should_run(site: dict) -> tuple[bool, list, dict]:
986
218
  is_site_building = site.get('siteType') in _BUILDING_SITE_TYPES
987
219
 
988
220
  allowed_land_use_types = _ALLOWED_LAND_USE_TYPES + ([OTHER_LAND] if is_site_building else [])
989
221
 
222
+ # get all Management `landCover` nodes from the site
990
223
  management_nodes = _collect_land_use_types([
991
224
  node for node in filter_list_term_type(site.get("management", []), TermTermType.LANDCOVER)
992
225
  if not _should_group_landCover(node)
993
226
  ])
994
- relevant_nodes = sorted(
995
- [
996
- node for node in management_nodes
997
- if node["land-use-type"] in allowed_land_use_types
998
- ],
999
- key=lambda n: n.get("startDate") or n.get("endDate")
1000
- )
1001
-
1002
- land_use_type = relevant_nodes[0].get("land-use-type") if relevant_nodes else None
1003
-
1004
- has_no_prior_land_cover_data = _no_prior_land_cover_data(
1005
- nodes=management_nodes,
1006
- target_node=relevant_nodes[0]
1007
- ) if relevant_nodes else None
1008
-
1009
- landCover_from_lookups = get_landCover_lookups(
1010
- country_id=site.get("country", {}).get("@id"),
1011
- end_year=_get_year_from_landCover(relevant_nodes[0]),
1012
- product_name=relevant_nodes[0].get("term", {}).get("name", "")
1013
- ) if relevant_nodes else {}
1014
227
 
1015
- should_run_nodes, site_area = (
1016
- (False, {}) if not all([land_use_type, has_no_prior_land_cover_data])
1017
- else (True, landCover_from_lookups) if landCover_from_lookups and all(landCover_from_lookups.values())
1018
- else (True, _COMPLETE_CHANGES_OTHER_LAND) if is_site_building
1019
- else _should_run_historical_land_use_change(
1020
- site=site,
1021
- nodes=relevant_nodes,
1022
- land_use_type=land_use_type
1023
- )
1024
- )
228
+ # get the Management `landCover` nodes that are "landUse" nodes
229
+ land_use_nodes = [
230
+ node for node in management_nodes
231
+ if node["landUseType"] in allowed_land_use_types
232
+ ]
1025
233
 
1026
- logRequirements(site, model=MODEL, model_key=MODEL_KEY,
1027
- has_management_nodes=bool(relevant_nodes),
1028
- land_use_type=land_use_type,
1029
- allowed_land_use_types=';'.join(allowed_land_use_types),
1030
- has_no_prior_land_cover_data=has_no_prior_land_cover_data,
1031
- management_nodes=log_as_table([_omit(n, ['term']) for n in relevant_nodes]),
1032
- landCover_from_lookups=log_as_table([
1033
- {land_type: value} for land_type, value in landCover_from_lookups.items()
1034
- ]),
1035
- should_run_nodes=should_run_nodes)
234
+ # get the nodes over all years of management
235
+ land_use_nodes = {
236
+ year: omit(node, ['startDate', 'endDate']) | {'year': year}
237
+ for node in land_use_nodes
238
+ for year in _years_from_node(node)
239
+ }.values()
240
+
241
+ # add metadata
242
+ land_use_nodes = sorted([
243
+ node if is_site_building else _extend_site_area(site, management_nodes, node)
244
+ for node in land_use_nodes
245
+ ], key=lambda n: n['year'])
246
+
247
+ # for buildings, add the landCover of the building 20years prior
248
+ land_use_nodes = [
249
+ land_use_nodes[0] | {
250
+ 'hasNoPriorLandCoverData': True,
251
+ 'isValid': True,
252
+ 'siteArea': _COMPLETE_CHANGES_OTHER_LAND,
253
+ 'site-area-from-lookup': {}
254
+ }
255
+ ] if is_site_building else land_use_nodes
256
+
257
+ # create list of land use nodes with all data
258
+ land_use_nodes = sorted([
259
+ omit(node, ['siteArea']) | {
260
+ "land-type": land_type,
261
+ "percentage": 0 if ratio == -0.0 else to_precision(ratio * 100),
262
+ "term-id": (
263
+ node['landCover-id'] if node.get('isPerenial') and land_type == PERMANENT_CROPLAND else
264
+ LAND_USE_TERMS_FOR_TRANSFORMATION[land_type][0]
265
+ )
266
+ }
267
+ for node in land_use_nodes
268
+ for land_type, ratio in node.get('siteArea').items()
269
+ ], key=lambda n: '-'.join([str(n['year']), n['term-id']]))
270
+
271
+ _log_land_use_nodes(site, land_use_nodes)
272
+
273
+ # filter valid values
274
+ valid_land_use_nodes = [
275
+ node for node in land_use_nodes if all([
276
+ node.get('hasNoPriorLandCoverData'),
277
+ node.get('isValid'),
278
+ ])
279
+ ]
1036
280
 
1037
- should_run = all([land_use_type, has_no_prior_land_cover_data, should_run_nodes])
1038
- logShouldRun(site, MODEL, None, should_run, model_key=MODEL_KEY)
1039
- return should_run_nodes, relevant_nodes, site_area
281
+ site_type_allowed = site.get("siteType") in (_ALLOWED_SITE_TYPES + _BUILDING_SITE_TYPES)
282
+
283
+ # group by term to show global logs for each Management node in the Site
284
+ grouped_values = group_by_keys(land_use_nodes, ['term'])
285
+
286
+ for term_id, values in grouped_values.items():
287
+ is_valid = any([v.get('isValid') for v in values])
288
+
289
+ logRequirements(site, model=MODEL, term=term_id, model_key=MODEL_KEY,
290
+ country_id=site.get("country", {}).get("@id"),
291
+ site_type_allowed=site_type_allowed,
292
+ allowed_land_use_types=';'.join(allowed_land_use_types),
293
+ land_use_type=values[0]['landUseType'],
294
+ values=log_as_table([
295
+ pick(v, ['year', 'term-id', 'isValid', 'percentage']) | {
296
+ key: v.get(key, {}).get(v['land-type'])
297
+ for key in [
298
+ 'changes',
299
+ 'site-area',
300
+ 'site-area-from-lookup',
301
+ 'annualCropland',
302
+ 'permanentCropland',
303
+ ] if key in v
304
+ } | {
305
+ 'missing-changes': '-'.join(v.get('missing-changes', []))
306
+ }
307
+ for v in values
308
+ ]))
309
+ logShouldRun(site, MODEL, term_id, is_valid, model_key=MODEL_KEY)
310
+
311
+ should_run = all([site_type_allowed, valid_land_use_nodes])
312
+ return should_run, valid_land_use_nodes
1040
313
 
1041
314
 
1042
315
  def run(site: dict) -> list:
1043
- should_run, relevant_nodes, site_area = _should_run(site=site)
1044
- return _run(site, relevant_nodes, site_area) if should_run else []
316
+ should_run, values = _should_run(site=site)
317
+ return [
318
+ _new_management(
319
+ term=value.get("term-id"),
320
+ value=value.get("percentage"),
321
+ model=MODEL,
322
+ start_date=f"{value['year'] - _DEFAULT_WINDOW_IN_YEARS}-01-01",
323
+ end_date=f"{value['year'] - _DEFAULT_WINDOW_IN_YEARS}-12-31",
324
+ ) for value in values
325
+ ] if should_run else []