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.
- hestia_earth/models/agribalyse2016/fuelElectricity.py +1 -2
- hestia_earth/models/agribalyse2016/machineryInfrastructureDepreciatedAmountPerCycle.py +1 -7
- hestia_earth/models/akagiEtAl2011/utils.py +2 -6
- hestia_earth/models/aware/scarcityWeightedWaterUse.py +1 -4
- hestia_earth/models/aware2_0/scarcityWeightedWaterUse.py +1 -4
- hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsLandOccupation.py +1 -4
- hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsLandTransformation.py +1 -4
- hestia_earth/models/chaudharyBrooks2018/damageToTerrestrialEcosystemsTotalLandUseEffects.py +1 -4
- hestia_earth/models/cml2001Baseline/abioticResourceDepletionFossilFuels.py +1 -4
- hestia_earth/models/cml2001Baseline/abioticResourceDepletionMineralsAndMetals.py +1 -4
- hestia_earth/models/cml2001Baseline/eutrophicationPotentialExcludingFate.py +1 -4
- hestia_earth/models/cml2001Baseline/resourceUseEnergyDepletionDuringCycle.py +12 -10
- hestia_earth/models/cml2001Baseline/resourceUseMineralsAndMetalsDuringCycle.py +5 -5
- hestia_earth/models/cml2001Baseline/terrestrialAcidificationPotentialIncludingFateAverageEurope.py +1 -7
- hestia_earth/models/cml2001NonBaseline/eutrophicationPotentialIncludingFateAverageEurope.py +1 -7
- hestia_earth/models/cml2001NonBaseline/terrestrialAcidificationPotentialExcludingFate.py +1 -7
- hestia_earth/models/config/ImpactAssessment.json +12 -6
- hestia_earth/models/config/Site.json +16 -0
- hestia_earth/models/cycle/aboveGroundCropResidueTotal.py +1 -6
- hestia_earth/models/cycle/practice/landCover.py +9 -9
- hestia_earth/models/dammgen2009/noxToAirExcreta.py +1 -2
- hestia_earth/models/deRuijterEtAl2010/nh3ToAirCropResidueDecomposition.py +1 -2
- hestia_earth/models/ecoalimV9/cycle.py +1 -2
- hestia_earth/models/ecoalimV9/impact_assessment.py +10 -3
- hestia_earth/models/ecoinventV3/cycle.py +1 -2
- hestia_earth/models/ecoinventV3AndEmberClimate/cycle.py +1 -2
- hestia_earth/models/edip2003/ozoneDepletionPotential.py +1 -7
- hestia_earth/models/emepEea2019/utils.py +1 -2
- hestia_earth/models/emissionNotRelevant/__init__.py +2 -2
- hestia_earth/models/environmentalFootprintV3_1/environmentalFootprintSingleOverallScore.py +1 -4
- hestia_earth/models/environmentalFootprintV3_1/freshwaterEcotoxicityPotentialCtue.py +1 -7
- hestia_earth/models/environmentalFootprintV3_1/marineEutrophicationPotential.py +1 -7
- hestia_earth/models/environmentalFootprintV3_1/photochemicalOzoneCreationPotentialHumanHealthNmvocEq.py +1 -7
- hestia_earth/models/environmentalFootprintV3_1/scarcityWeightedWaterUse.py +1 -7
- hestia_earth/models/environmentalFootprintV3_1/soilQualityIndexLandOccupation.py +2 -7
- hestia_earth/models/environmentalFootprintV3_1/soilQualityIndexLandTransformation.py +2 -7
- hestia_earth/models/environmentalFootprintV3_1/soilQualityIndexTotalLandUseEffects.py +2 -7
- hestia_earth/models/epa2014/no3ToGroundwaterExcreta.py +1 -2
- hestia_earth/models/fantkeEtAl2016/damageToHumanHealthParticulateMatterFormation.py +2 -11
- hestia_earth/models/faostat2018/liveweightPerHead.py +11 -7
- hestia_earth/models/faostat2018/product/price.py +9 -5
- hestia_earth/models/faostat2018/seed.py +5 -12
- hestia_earth/models/faostat2018/utils.py +14 -6
- hestia_earth/models/frischknechtEtAl2000/ionisingRadiationKbqU235Eq.py +4 -8
- hestia_earth/models/geospatialDatabase/altitude.py +1 -2
- hestia_earth/models/geospatialDatabase/clayContent.py +1 -2
- hestia_earth/models/geospatialDatabase/croppingIntensity.py +1 -3
- hestia_earth/models/geospatialDatabase/drainageClass.py +1 -2
- hestia_earth/models/geospatialDatabase/ecoClimateZone.py +1 -2
- hestia_earth/models/geospatialDatabase/erodibility.py +1 -2
- hestia_earth/models/geospatialDatabase/heavyWinterPrecipitation.py +1 -2
- hestia_earth/models/geospatialDatabase/histosol.py +1 -2
- hestia_earth/models/geospatialDatabase/longFallowRatio.py +1 -3
- hestia_earth/models/geospatialDatabase/nutrientLossToAquaticEnvironment.py +1 -2
- hestia_earth/models/geospatialDatabase/organicCarbonPerKgSoil.py +1 -2
- hestia_earth/models/geospatialDatabase/potentialEvapotranspirationAnnual.py +1 -2
- hestia_earth/models/geospatialDatabase/potentialEvapotranspirationLongTermAnnualMean.py +1 -2
- hestia_earth/models/geospatialDatabase/potentialEvapotranspirationMonthly.py +1 -2
- hestia_earth/models/geospatialDatabase/precipitationAnnual.py +1 -2
- hestia_earth/models/geospatialDatabase/precipitationLongTermAnnualMean.py +1 -2
- hestia_earth/models/geospatialDatabase/precipitationMonthly.py +1 -2
- hestia_earth/models/geospatialDatabase/sandContent.py +1 -2
- hestia_earth/models/geospatialDatabase/siltContent.py +1 -2
- hestia_earth/models/geospatialDatabase/slope.py +1 -2
- hestia_earth/models/geospatialDatabase/slopeLength.py +1 -2
- hestia_earth/models/geospatialDatabase/soilPh.py +1 -2
- hestia_earth/models/geospatialDatabase/temperatureAnnual.py +1 -2
- hestia_earth/models/geospatialDatabase/temperatureLongTermAnnualMean.py +1 -2
- hestia_earth/models/geospatialDatabase/temperatureMonthly.py +1 -2
- hestia_earth/models/geospatialDatabase/totalNitrogenPerKgSoil.py +1 -2
- hestia_earth/models/geospatialDatabase/totalPhosphorusPerKgSoil.py +1 -2
- hestia_earth/models/geospatialDatabase/waterDepth.py +1 -2
- hestia_earth/models/hestia/aboveGroundCropResidue.py +1 -2
- hestia_earth/models/hestia/aboveGroundCropResidueTotal.py +1 -6
- hestia_earth/models/hestia/brackishWater.py +1 -2
- hestia_earth/models/hestia/cationExchangeCapacityPerKgSoil.py +1 -2
- hestia_earth/models/hestia/cropResidueManagement.py +1 -2
- hestia_earth/models/hestia/croppingIntensity.py +1 -7
- hestia_earth/models/hestia/default_emissions.py +3 -5
- hestia_earth/models/hestia/default_resourceUse.py +4 -11
- hestia_earth/models/hestia/excretaKgMass.py +9 -6
- hestia_earth/models/hestia/excretaKgN.py +8 -7
- hestia_earth/models/hestia/excretaKgVs.py +8 -7
- hestia_earth/models/hestia/feedConversionRatio/__init__.py +5 -7
- hestia_earth/models/hestia/flowingWater.py +1 -2
- hestia_earth/models/hestia/freshWater.py +1 -2
- hestia_earth/models/hestia/histosol.py +1 -2
- hestia_earth/models/hestia/inorganicFertiliser.py +2 -14
- hestia_earth/models/hestia/irrigatedTypeUnspecified.py +1 -7
- hestia_earth/models/hestia/landCover.py +192 -911
- hestia_earth/models/hestia/landCover_utils.py +769 -0
- hestia_earth/models/hestia/landOccupationDuringCycle.py +1 -2
- hestia_earth/models/hestia/liveAnimal.py +1 -3
- hestia_earth/models/hestia/longFallowRatio.py +1 -7
- hestia_earth/models/hestia/management.py +14 -8
- hestia_earth/models/hestia/materialAndSubstrate.py +1 -2
- hestia_earth/models/hestia/milkYield.py +2 -12
- hestia_earth/models/hestia/netPrimaryProduction.py +1 -2
- hestia_earth/models/hestia/organicCarbonPerHa.py +1 -2
- hestia_earth/models/hestia/pToSurfaceWaterAquacultureSystems.py +2 -4
- hestia_earth/models/hestia/pastureGrass.py +3 -4
- hestia_earth/models/hestia/pastureSystem.py +1 -7
- hestia_earth/models/hestia/potentialEvapotranspirationAnnual.py +1 -2
- hestia_earth/models/hestia/potentialEvapotranspirationMonthly.py +1 -2
- hestia_earth/models/hestia/precipitationAnnual.py +1 -2
- hestia_earth/models/hestia/precipitationMonthly.py +1 -2
- hestia_earth/models/hestia/rainfallAnnual.py +1 -2
- hestia_earth/models/hestia/rainfallMonthly.py +1 -2
- hestia_earth/models/hestia/residueBurnt.py +1 -7
- hestia_earth/models/hestia/residueIncorporated.py +1 -7
- hestia_earth/models/hestia/residueLeftOnField.py +1 -7
- hestia_earth/models/hestia/residueRemoved.py +3 -8
- hestia_earth/models/hestia/resourceUse_utils.py +4 -20
- hestia_earth/models/hestia/salineWater.py +1 -2
- hestia_earth/models/hestia/seed_emissions.py +5 -6
- hestia_earth/models/hestia/slope.py +44 -0
- hestia_earth/models/hestia/slopeLength.py +44 -0
- hestia_earth/models/hestia/soilMeasurement.py +1 -2
- hestia_earth/models/hestia/stockingDensityAnimalHousingAverage.py +1 -7
- hestia_earth/models/hestia/temperatureAnnual.py +1 -2
- hestia_earth/models/hestia/temperatureMonthly.py +1 -2
- hestia_earth/models/hestia/totalNitrogenPerKgSoil.py +1 -2
- hestia_earth/models/hestia/unknownPreSeasonWaterRegime.py +1 -7
- hestia_earth/models/hestia/utils.py +5 -5
- hestia_earth/models/hestia/waterDepth.py +1 -2
- hestia_earth/models/hestia/waterSalinity.py +1 -2
- hestia_earth/models/impact_assessment/emissions.py +8 -9
- hestia_earth/models/impact_assessment/post_checks/cycle.py +1 -1
- hestia_earth/models/impact_assessment/pre_checks/cycle.py +1 -1
- hestia_earth/models/ipcc2006/aboveGroundCropResidueRemoved.py +1 -3
- hestia_earth/models/ipcc2006/aboveGroundCropResidueTotal.py +1 -3
- hestia_earth/models/ipcc2006/belowGroundCropResidue.py +1 -3
- hestia_earth/models/ipcc2006/n2OToAirCropResidueDecompositionDirect.py +2 -6
- hestia_earth/models/ipcc2006/n2OToAirCropResidueDecompositionIndirect.py +1 -2
- hestia_earth/models/ipcc2006/n2OToAirExcretaDirect.py +1 -2
- hestia_earth/models/ipcc2006/n2OToAirExcretaIndirect.py +1 -2
- hestia_earth/models/ipcc2006/n2OToAirInorganicFertiliserDirect.py +1 -2
- hestia_earth/models/ipcc2006/n2OToAirInorganicFertiliserIndirect.py +1 -2
- hestia_earth/models/ipcc2006/n2OToAirOrganicFertiliserDirect.py +1 -2
- hestia_earth/models/ipcc2006/n2OToAirOrganicFertiliserIndirect.py +1 -2
- hestia_earth/models/ipcc2013ExcludingFeedbacks/gwp100.py +1 -7
- hestia_earth/models/ipcc2013IncludingFeedbacks/gwp100.py +1 -7
- hestia_earth/models/ipcc2019/aboveGroundBiomass.py +1 -1
- hestia_earth/models/ipcc2019/aboveGroundCropResidueTotal.py +1 -3
- hestia_earth/models/ipcc2019/animal/milkYieldPerAnimal.py +3 -7
- hestia_earth/models/ipcc2019/animal/pastureGrass.py +1 -2
- hestia_earth/models/ipcc2019/belowGroundBiomass.py +1 -1
- hestia_earth/models/ipcc2019/belowGroundCropResidue.py +1 -3
- hestia_earth/models/ipcc2019/biocharOrganicCarbonPerHa.py +1 -1
- hestia_earth/models/ipcc2019/ch4ToAirAquacultureSystems.py +2 -6
- hestia_earth/models/ipcc2019/ch4ToAirEntericFermentation.py +2 -12
- hestia_earth/models/ipcc2019/ch4ToAirExcreta.py +1 -2
- hestia_earth/models/ipcc2019/ch4ToAirFloodedRice.py +2 -10
- hestia_earth/models/ipcc2019/ch4ToAirOrganicSoilCultivation.py +1 -1
- hestia_earth/models/ipcc2019/co2ToAirAboveGroundBiomassStockChange.py +1 -1
- hestia_earth/models/ipcc2019/co2ToAirBelowGroundBiomassStockChange.py +1 -1
- hestia_earth/models/ipcc2019/co2ToAirBiocharStockChange.py +1 -1
- hestia_earth/models/ipcc2019/co2ToAirLimeHydrolysis.py +1 -2
- hestia_earth/models/ipcc2019/co2ToAirOrganicSoilCultivation.py +1 -1
- hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChange.py +1 -1
- hestia_earth/models/ipcc2019/co2ToAirUreaHydrolysis.py +1 -2
- hestia_earth/models/ipcc2019/croppingDuration.py +1 -2
- hestia_earth/models/ipcc2019/n2OToAirCropResidueBurningDirect.py +1 -2
- hestia_earth/models/ipcc2019/n2OToAirCropResidueDecompositionDirect.py +2 -7
- hestia_earth/models/ipcc2019/n2OToAirExcretaDirect.py +1 -2
- hestia_earth/models/ipcc2019/n2OToAirInorganicFertiliserDirect.py +2 -7
- hestia_earth/models/ipcc2019/n2OToAirOrganicFertiliserDirect.py +2 -7
- hestia_earth/models/ipcc2019/n2OToAirOrganicSoilCultivationDirect.py +2 -7
- hestia_earth/models/ipcc2019/n2OToAir_indirect_emissions_utils.py +3 -9
- hestia_earth/models/ipcc2019/nh3ToAirInorganicFertiliser.py +2 -6
- hestia_earth/models/ipcc2019/nh3ToAirOrganicFertiliser.py +2 -6
- hestia_earth/models/ipcc2019/no3ToGroundwaterCropResidueDecomposition.py +2 -7
- hestia_earth/models/ipcc2019/no3ToGroundwaterExcreta.py +2 -8
- hestia_earth/models/ipcc2019/no3ToGroundwaterInorganicFertiliser.py +2 -7
- hestia_earth/models/ipcc2019/no3ToGroundwaterOrganicFertiliser.py +2 -7
- hestia_earth/models/ipcc2019/nonCo2EmissionsToAirNaturalVegetationBurning.py +1 -1
- hestia_earth/models/ipcc2019/noxToAirInorganicFertiliser.py +2 -6
- hestia_earth/models/ipcc2019/noxToAirOrganicFertiliser.py +2 -6
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1.py +1 -1
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2.py +1 -1
- hestia_earth/models/ipcc2019/pastureGrass.py +1 -2
- hestia_earth/models/ipcc2021/gwp100.py +2 -11
- hestia_earth/models/jarvisAndPain1994/n2ToAirExcreta.py +1 -2
- hestia_earth/models/koble2014/utils.py +1 -3
- hestia_earth/models/lcImpactAllEffects100Years/damageToFreshwaterEcosystemsClimateChange.py +1 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToFreshwaterEcosystemsFreshwaterEcotoxicity.py +1 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToFreshwaterEcosystemsFreshwaterEutrophication.py +1 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToFreshwaterEcosystemsPdfYear.py +1 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToFreshwaterEcosystemsWaterStress.py +1 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealth.py +1 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthClimateChange.py +1 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthHumanToxicityCancerogenic.py +1 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthHumanToxicityNonCancerogenic.py +1 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthParticulateMatterFormation.py +1 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthPhotochemicalOzoneFormation.py +1 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthStratosphericOzoneDepletion.py +1 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToHumanHealthWaterStress.py +1 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToMarineEcosystemsMarineEcotoxicity.py +1 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToMarineEcosystemsMarineEutrophication.py +1 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToMarineEcosystemsPdfYear.py +1 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToTerrestrialEcosystemsClimateChange.py +1 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToTerrestrialEcosystemsPdfYear.py +1 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToTerrestrialEcosystemsPhotochemicalOzoneFormation.py +1 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToTerrestrialEcosystemsTerrestrialAcidification.py +1 -7
- hestia_earth/models/lcImpactAllEffects100Years/damageToTerrestrialEcosystemsTerrestrialEcotoxicity.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToFreshwaterEcosystemsClimateChange.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToFreshwaterEcosystemsFreshwaterEcotoxicity.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToFreshwaterEcosystemsFreshwaterEutrophication.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToFreshwaterEcosystemsPdfYear.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToFreshwaterEcosystemsWaterStress.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealth.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthClimateChange.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthHumanToxicityCancerogenic.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthHumanToxicityNonCancerogenic.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthParticulateMatterFormation.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthPhotochemicalOzoneFormation.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthStratosphericOzoneDepletion.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToHumanHealthWaterStress.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToMarineEcosystemsMarineEcotoxicity.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToMarineEcosystemsMarineEutrophication.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToMarineEcosystemsPdfYear.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToTerrestrialEcosystemsClimateChange.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToTerrestrialEcosystemsPdfYear.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToTerrestrialEcosystemsPhotochemicalOzoneFormation.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToTerrestrialEcosystemsTerrestrialAcidification.py +1 -7
- hestia_earth/models/lcImpactAllEffectsInfinite/damageToTerrestrialEcosystemsTerrestrialEcotoxicity.py +1 -7
- hestia_earth/models/lcImpactCertainEffects100Years/damageToFreshwaterEcosystemsFreshwaterEcotoxicity.py +1 -7
- hestia_earth/models/lcImpactCertainEffects100Years/damageToFreshwaterEcosystemsFreshwaterEutrophication.py +1 -7
- hestia_earth/models/lcImpactCertainEffects100Years/damageToFreshwaterEcosystemsPdfYear.py +1 -7
- hestia_earth/models/lcImpactCertainEffects100Years/damageToFreshwaterEcosystemsWaterStress.py +1 -7
- hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealth.py +1 -7
- hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthClimateChange.py +1 -7
- hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthHumanToxicityCancerogenic.py +1 -7
- hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthHumanToxicityNonCancerogenic.py +1 -7
- hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthParticulateMatterFormation.py +1 -7
- hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthPhotochemicalOzoneFormation.py +1 -7
- hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthStratosphericOzoneDepletion.py +1 -7
- hestia_earth/models/lcImpactCertainEffects100Years/damageToHumanHealthWaterStress.py +1 -7
- hestia_earth/models/lcImpactCertainEffects100Years/damageToMarineEcosystemsMarineEcotoxicity.py +1 -7
- hestia_earth/models/lcImpactCertainEffects100Years/damageToMarineEcosystemsMarineEutrophication.py +1 -7
- hestia_earth/models/lcImpactCertainEffects100Years/damageToMarineEcosystemsPdfYear.py +1 -7
- hestia_earth/models/lcImpactCertainEffects100Years/damageToTerrestrialEcosystemsClimateChange.py +1 -7
- hestia_earth/models/lcImpactCertainEffects100Years/damageToTerrestrialEcosystemsPdfYear.py +1 -7
- hestia_earth/models/lcImpactCertainEffects100Years/damageToTerrestrialEcosystemsPhotochemicalOzoneFormation.py +1 -7
- hestia_earth/models/lcImpactCertainEffects100Years/damageToTerrestrialEcosystemsTerrestrialAcidification.py +1 -7
- hestia_earth/models/lcImpactCertainEffects100Years/damageToTerrestrialEcosystemsTerrestrialEcotoxicity.py +1 -7
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToFreshwaterEcosystemsFreshwaterEcotoxicity.py +1 -7
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToFreshwaterEcosystemsFreshwaterEutrophication.py +1 -7
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToFreshwaterEcosystemsPdfYear.py +1 -7
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToFreshwaterEcosystemsWaterStress.py +1 -7
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealth.py +1 -7
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthClimateChange.py +1 -7
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthHumanToxicityCancerogenic.py +1 -7
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthHumanToxicityNonCancerogenic.py +1 -7
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthParticulateMatterFormation.py +1 -7
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthPhotochemicalOzoneFormation.py +1 -7
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthStratosphericOzoneDepletion.py +1 -7
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToHumanHealthWaterStress.py +1 -7
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToMarineEcosystemsMarineEcotoxicity.py +1 -7
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToMarineEcosystemsMarineEutrophication.py +1 -7
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToMarineEcosystemsPdfYear.py +1 -7
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToTerrestrialEcosystemsClimateChange.py +1 -7
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToTerrestrialEcosystemsPdfYear.py +1 -7
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToTerrestrialEcosystemsPhotochemicalOzoneFormation.py +1 -7
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToTerrestrialEcosystemsTerrestrialAcidification.py +1 -7
- hestia_earth/models/lcImpactCertainEffectsInfinite/damageToTerrestrialEcosystemsTerrestrialEcotoxicity.py +1 -7
- hestia_earth/models/linkedImpactAssessment/emissions.py +4 -6
- hestia_earth/models/linkedImpactAssessment/utils.py +4 -6
- hestia_earth/models/mocking/search-results.json +1 -1
- hestia_earth/models/pooreNemecek2018/aboveGroundCropResidueTotal.py +1 -3
- hestia_earth/models/pooreNemecek2018/belowGroundCropResidue.py +1 -3
- hestia_earth/models/pooreNemecek2018/ch4ToAirAquacultureSystems.py +1 -2
- hestia_earth/models/pooreNemecek2018/excretaKgN.py +3 -4
- hestia_earth/models/pooreNemecek2018/excretaKgVs.py +4 -4
- hestia_earth/models/pooreNemecek2018/freshwaterWithdrawalsDuringCycle.py +3 -9
- hestia_earth/models/pooreNemecek2018/landOccupationDuringCycle.py +3 -7
- hestia_earth/models/pooreNemecek2018/longFallowDuration.py +1 -7
- hestia_earth/models/pooreNemecek2018/n2OToAirAquacultureSystemsDirect.py +1 -2
- hestia_earth/models/pooreNemecek2018/n2ToAirAquacultureSystems.py +1 -2
- hestia_earth/models/pooreNemecek2018/nh3ToAirAquacultureSystems.py +1 -2
- hestia_earth/models/pooreNemecek2018/no3ToGroundwaterCropResidueDecomposition.py +1 -2
- hestia_earth/models/pooreNemecek2018/no3ToGroundwaterExcreta.py +1 -2
- hestia_earth/models/pooreNemecek2018/no3ToGroundwaterInorganicFertiliser.py +1 -2
- hestia_earth/models/pooreNemecek2018/no3ToGroundwaterOrganicFertiliser.py +1 -2
- hestia_earth/models/pooreNemecek2018/no3ToGroundwaterSoilFlux_utils.py +1 -2
- hestia_earth/models/pooreNemecek2018/noxToAirAquacultureSystems.py +1 -2
- hestia_earth/models/pooreNemecek2018/nurseryDensity.py +1 -7
- hestia_earth/models/pooreNemecek2018/nurseryDuration.py +1 -7
- hestia_earth/models/pooreNemecek2018/plantationDensity.py +1 -7
- hestia_earth/models/pooreNemecek2018/plantationLifespan.py +1 -7
- hestia_earth/models/pooreNemecek2018/plantationProductiveLifespan.py +1 -7
- hestia_earth/models/pooreNemecek2018/rotationDuration.py +1 -7
- hestia_earth/models/pooreNemecek2018/saplingsDepreciatedAmountPerCycle.py +1 -7
- hestia_earth/models/poschEtAl2008/terrestrialAcidificationPotentialAccumulatedExceedance.py +1 -7
- hestia_earth/models/poschEtAl2008/terrestrialEutrophicationPotentialAccumulatedExceedance.py +1 -7
- hestia_earth/models/recipe2016Egalitarian/damageToFreshwaterEcosystemsSpeciesYear.py +1 -7
- hestia_earth/models/recipe2016Egalitarian/damageToHumanHealth.py +1 -7
- hestia_earth/models/recipe2016Egalitarian/damageToMarineEcosystemsSpeciesYear.py +1 -7
- hestia_earth/models/recipe2016Egalitarian/damageToResourceAvailability.py +1 -7
- hestia_earth/models/recipe2016Egalitarian/damageToTerrestrialEcosystemsSpeciesYear.py +1 -7
- hestia_earth/models/recipe2016Egalitarian/ecosystemDamageOzoneFormation.py +1 -7
- hestia_earth/models/recipe2016Egalitarian/fossilResourceScarcity.py +1 -7
- hestia_earth/models/recipe2016Egalitarian/freshwaterAquaticEcotoxicityPotential14Dcbeq.py +1 -7
- hestia_earth/models/recipe2016Egalitarian/freshwaterEutrophicationPotential.py +1 -7
- hestia_earth/models/recipe2016Egalitarian/humanCarcinogenicToxicity.py +1 -7
- hestia_earth/models/recipe2016Egalitarian/humanDamageOzoneFormation.py +2 -10
- hestia_earth/models/recipe2016Egalitarian/humanNonCarcinogenicToxicity.py +1 -7
- hestia_earth/models/recipe2016Egalitarian/marineAquaticEcotoxicityPotential14Dcbeq.py +1 -7
- hestia_earth/models/recipe2016Egalitarian/marineEutrophicationPotential.py +1 -7
- hestia_earth/models/recipe2016Egalitarian/ozoneDepletionPotential.py +1 -7
- hestia_earth/models/recipe2016Egalitarian/terrestrialAcidificationPotential.py +1 -7
- hestia_earth/models/recipe2016Egalitarian/terrestrialEcotoxicityPotential14Dcbeq.py +1 -7
- hestia_earth/models/recipe2016Hierarchist/damageToFreshwaterEcosystemsSpeciesYear.py +1 -7
- hestia_earth/models/recipe2016Hierarchist/damageToHumanHealth.py +1 -7
- hestia_earth/models/recipe2016Hierarchist/damageToMarineEcosystemsSpeciesYear.py +1 -7
- hestia_earth/models/recipe2016Hierarchist/damageToResourceAvailability.py +1 -7
- hestia_earth/models/recipe2016Hierarchist/damageToTerrestrialEcosystemsSpeciesYear.py +1 -7
- hestia_earth/models/recipe2016Hierarchist/ecosystemDamageOzoneFormation.py +2 -10
- hestia_earth/models/recipe2016Hierarchist/fossilResourceScarcity.py +1 -7
- hestia_earth/models/recipe2016Hierarchist/freshwaterAquaticEcotoxicityPotential14Dcbeq.py +1 -7
- hestia_earth/models/recipe2016Hierarchist/freshwaterEutrophicationPotential.py +1 -7
- hestia_earth/models/recipe2016Hierarchist/humanCarcinogenicToxicity.py +1 -7
- hestia_earth/models/recipe2016Hierarchist/humanDamageOzoneFormation.py +2 -10
- hestia_earth/models/recipe2016Hierarchist/humanNonCarcinogenicToxicity.py +1 -7
- hestia_earth/models/recipe2016Hierarchist/marineAquaticEcotoxicityPotential14Dcbeq.py +1 -7
- hestia_earth/models/recipe2016Hierarchist/marineEutrophicationPotential.py +1 -7
- hestia_earth/models/recipe2016Hierarchist/ozoneDepletionPotential.py +1 -7
- hestia_earth/models/recipe2016Hierarchist/terrestrialAcidificationPotential.py +1 -7
- hestia_earth/models/recipe2016Hierarchist/terrestrialEcotoxicityPotential14Dcbeq.py +1 -7
- hestia_earth/models/recipe2016Individualist/damageToFreshwaterEcosystemsSpeciesYear.py +1 -7
- hestia_earth/models/recipe2016Individualist/damageToHumanHealth.py +1 -7
- hestia_earth/models/recipe2016Individualist/damageToMarineEcosystemsSpeciesYear.py +1 -7
- hestia_earth/models/recipe2016Individualist/damageToResourceAvailability.py +1 -7
- hestia_earth/models/recipe2016Individualist/damageToTerrestrialEcosystemsSpeciesYear.py +1 -7
- hestia_earth/models/recipe2016Individualist/ecosystemDamageOzoneFormation.py +2 -10
- hestia_earth/models/recipe2016Individualist/fossilResourceScarcity.py +1 -7
- hestia_earth/models/recipe2016Individualist/freshwaterAquaticEcotoxicityPotential14Dcbeq.py +1 -7
- hestia_earth/models/recipe2016Individualist/freshwaterEutrophicationPotential.py +1 -7
- hestia_earth/models/recipe2016Individualist/humanCarcinogenicToxicity.py +1 -7
- hestia_earth/models/recipe2016Individualist/humanDamageOzoneFormation.py +2 -10
- hestia_earth/models/recipe2016Individualist/humanNonCarcinogenicToxicity.py +1 -7
- hestia_earth/models/recipe2016Individualist/marineAquaticEcotoxicityPotential14Dcbeq.py +1 -7
- hestia_earth/models/recipe2016Individualist/marineEutrophicationPotential.py +1 -7
- hestia_earth/models/recipe2016Individualist/ozoneDepletionPotential.py +1 -7
- hestia_earth/models/recipe2016Individualist/terrestrialAcidificationPotential.py +1 -7
- hestia_earth/models/recipe2016Individualist/terrestrialEcotoxicityPotential14Dcbeq.py +1 -7
- hestia_earth/models/resourceUseNotRelevant/__init__.py +2 -8
- hestia_earth/models/schererPfister2015/nErosionSoilFlux.py +1 -2
- hestia_earth/models/schererPfister2015/pErosionSoilFlux.py +1 -2
- hestia_earth/models/schererPfister2015/pToDrainageWaterSoilFlux.py +1 -2
- hestia_earth/models/schererPfister2015/pToGroundwaterSoilFlux.py +1 -2
- hestia_earth/models/schererPfister2015/pToSurfaceWaterSoilFlux.py +1 -2
- hestia_earth/models/schmidt2007/ch4ToAirWasteTreatment.py +1 -2
- hestia_earth/models/schmidt2007/h2SToAirWasteTreatment.py +1 -2
- hestia_earth/models/schmidt2007/n2OToAirWasteTreatmentDirect.py +1 -2
- hestia_earth/models/schmidt2007/nh3ToAirWasteTreatment.py +1 -2
- hestia_earth/models/site/grouped_measurement.py +2 -3
- hestia_earth/models/stehfestBouwman2006/n2OToAirCropResidueDecompositionDirect.py +1 -2
- hestia_earth/models/stehfestBouwman2006/n2OToAirExcretaDirect.py +1 -2
- hestia_earth/models/stehfestBouwman2006/n2OToAirInorganicFertiliserDirect.py +1 -2
- hestia_earth/models/stehfestBouwman2006/n2OToAirOrganicFertiliserDirect.py +1 -2
- hestia_earth/models/stehfestBouwman2006/n2OToAirSoilFlux_utils.py +1 -2
- hestia_earth/models/stehfestBouwman2006/noxToAirCropResidueDecomposition.py +1 -2
- hestia_earth/models/stehfestBouwman2006/noxToAirExcreta.py +1 -2
- hestia_earth/models/stehfestBouwman2006/noxToAirInorganicFertiliser.py +1 -2
- hestia_earth/models/stehfestBouwman2006/noxToAirOrganicFertiliser.py +1 -2
- hestia_earth/models/stehfestBouwman2006/noxToAirSoilFlux_utils.py +1 -2
- hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirCropResidueDecomposition.py +1 -2
- hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirExcreta.py +1 -2
- hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirInorganicFertiliser.py +1 -2
- hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirOrganicFertiliser.py +1 -2
- hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirSoilFlux_utils.py +1 -2
- hestia_earth/models/transformation/input/excreta.py +7 -5
- hestia_earth/models/transformation/product/excreta.py +4 -4
- hestia_earth/models/usetoxV2/freshwaterEcotoxicityPotentialCtue.py +1 -7
- hestia_earth/models/utils/__init__.py +26 -7
- hestia_earth/models/utils/background_emissions.py +3 -3
- hestia_earth/models/utils/blank_node.py +31 -12
- hestia_earth/models/utils/emission.py +24 -10
- hestia_earth/models/utils/indicator.py +28 -13
- hestia_earth/models/utils/input.py +22 -5
- hestia_earth/models/utils/management.py +18 -4
- hestia_earth/models/utils/measurement.py +14 -6
- hestia_earth/models/utils/practice.py +24 -4
- hestia_earth/models/utils/product.py +32 -14
- hestia_earth/models/version.py +1 -1
- hestia_earth/models/webbEtAl2012AndSintermannEtAl2012/nh3ToAirOrganicFertiliser.py +1 -2
- hestia_earth/orchestrator/models/__init__.py +22 -11
- {hestia_earth_models-0.74.10.dist-info → hestia_earth_models-0.74.12.dist-info}/METADATA +2 -2
- {hestia_earth_models-0.74.10.dist-info → hestia_earth_models-0.74.12.dist-info}/RECORD +410 -404
- tests/models/cml2001Baseline/test_abioticResourceDepletionFossilFuels.py +4 -9
- tests/models/cml2001Baseline/test_abioticResourceDepletionMineralsAndMetals.py +4 -9
- tests/models/environmentalFootprintV3_1/test_environmentalFootprintSingleOverallScore.py +4 -9
- tests/models/environmentalFootprintV3_1/test_marineEutrophicationPotential.py +1 -7
- tests/models/environmentalFootprintV3_1/test_scarcityWeightedWaterUse.py +2 -8
- tests/models/environmentalFootprintV3_1/test_soilQualityIndexLandTransformation.py +3 -13
- tests/models/hestia/test_default_emissions.py +2 -2
- tests/models/hestia/test_default_resourceUse.py +3 -3
- tests/models/hestia/test_landCover.py +42 -295
- tests/models/hestia/test_landCover_utils.py +233 -0
- tests/models/hestia/test_slope.py +23 -0
- tests/models/hestia/test_slopeLength.py +23 -0
- tests/models/ipcc2019/test_no3ToGroundwaterExcreta.py +1 -0
- tests/models/poschEtAl2008/test_terrestrialAcidificationPotentialAccumulatedExceedance.py +4 -9
- tests/models/poschEtAl2008/test_terrestrialEutrophicationPotentialAccumulatedExceedance.py +4 -9
- tests/models/utils/test_indicator.py +6 -4
- tests/models/utils/test_measurement.py +7 -5
- {hestia_earth_models-0.74.10.dist-info → hestia_earth_models-0.74.12.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.74.10.dist-info → hestia_earth_models-0.74.12.dist-info}/WHEEL +0 -0
- {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
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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,
|
|
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(
|
|
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
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
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
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
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
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
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,
|
|
1044
|
-
return
|
|
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 []
|