hestia-earth-models 0.70.0__py3-none-any.whl → 0.70.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hestia_earth/models/aware/scarcityWeightedWaterUse.py +8 -16
- hestia_earth/models/cml2001Baseline/resourceUseMineralsAndMetalsDuringCycle.py +2 -1
- hestia_earth/models/config/Cycle.json +98 -50
- hestia_earth/models/config/ImpactAssessment.json +12 -4
- hestia_earth/models/config/Site.json +40 -21
- hestia_earth/models/cycle/transformation.py +1 -1
- hestia_earth/models/cycle/utils.py +0 -6
- hestia_earth/models/data/ecoinventV3/__init__.py +15 -13
- hestia_earth/models/ecoalimV9/__init__.py +13 -0
- hestia_earth/models/ecoalimV9/cycle.py +128 -0
- hestia_earth/models/ecoalimV9/impact_assessment.py +125 -0
- hestia_earth/models/ecoalimV9/utils.py +31 -0
- hestia_earth/models/ecoinventV3/__init__.py +6 -14
- hestia_earth/models/ecoinventV3/utils.py +1 -29
- hestia_earth/models/ecoinventV3AndEmberClimate/__init__.py +8 -2
- hestia_earth/models/emissionNotRelevant/__init__.py +33 -8
- hestia_earth/models/{cycle → hestia}/aboveGroundCropResidue.py +4 -3
- hestia_earth/models/{cycle → hestia}/aboveGroundCropResidueTotal.py +1 -1
- hestia_earth/models/{site → hestia}/brackishWater.py +1 -1
- hestia_earth/models/{site → hestia}/cationExchangeCapacityPerKgSoil.py +1 -1
- hestia_earth/models/{cycle → hestia}/coldCarcassWeightPerHead.py +1 -1
- hestia_earth/models/{cycle → hestia}/coldDressedCarcassWeightPerHead.py +1 -1
- hestia_earth/models/{cycle → hestia}/concentrateFeed.py +1 -1
- hestia_earth/models/{cycle → hestia}/cropResidueManagement.py +1 -1
- hestia_earth/models/{cycle → hestia}/croppingIntensity.py +1 -1
- hestia_earth/models/{cycle → hestia}/energyContentLowerHeatingValue.py +1 -1
- hestia_earth/models/{cycle → hestia}/excretaKgMass.py +8 -3
- hestia_earth/models/{cycle → hestia}/excretaKgN.py +1 -1
- hestia_earth/models/{cycle → hestia}/excretaKgVs.py +1 -1
- hestia_earth/models/{cycle → hestia}/feedConversionRatio/__init__.py +1 -1
- hestia_earth/models/{site → hestia}/flowingWater.py +1 -1
- hestia_earth/models/{site → hestia}/freshWater.py +1 -1
- hestia_earth/models/{cycle → hestia}/inorganicFertiliser.py +1 -1
- hestia_earth/models/{cycle → hestia}/irrigatedTypeUnspecified.py +14 -19
- hestia_earth/models/hestia/landCover.py +30 -22
- hestia_earth/models/{cycle → hestia}/liveAnimal.py +1 -1
- hestia_earth/models/{cycle → hestia}/longFallowRatio.py +1 -1
- hestia_earth/models/{site → hestia}/management.py +4 -6
- hestia_earth/models/{cycle → hestia}/materialAndSubstrate.py +1 -1
- hestia_earth/models/{cycle → hestia}/milkYield.py +1 -1
- hestia_earth/models/{site → hestia}/netPrimaryProduction.py +1 -1
- hestia_earth/models/{site → hestia}/organicCarbonPerHa.py +1 -1
- hestia_earth/models/hestia/pToSurfaceWaterAquacultureSystems.py +148 -0
- hestia_earth/models/{cycle → hestia}/pastureGrass.py +1 -1
- hestia_earth/models/{cycle → hestia}/pastureSystem.py +1 -1
- hestia_earth/models/{site → hestia}/potentialEvapotranspirationAnnual.py +3 -3
- hestia_earth/models/{site → hestia}/potentialEvapotranspirationMonthly.py +3 -3
- hestia_earth/models/{site → hestia}/precipitationAnnual.py +3 -3
- hestia_earth/models/{site → hestia}/precipitationMonthly.py +3 -3
- hestia_earth/models/{site → hestia}/rainfallAnnual.py +3 -3
- hestia_earth/models/{site → hestia}/rainfallMonthly.py +3 -3
- hestia_earth/models/{cycle → hestia}/readyToCookWeightPerHead.py +1 -1
- hestia_earth/models/{cycle → hestia}/residueBurnt.py +1 -1
- hestia_earth/models/{cycle → hestia}/residueIncorporated.py +1 -1
- hestia_earth/models/{cycle → hestia}/residueLeftOnField.py +1 -1
- hestia_earth/models/hestia/residueRemoved.py +65 -13
- hestia_earth/models/{site → hestia}/salineWater.py +1 -1
- hestia_earth/models/{site → hestia}/soilMeasurement.py +1 -1
- hestia_earth/models/{cycle → hestia}/stockingDensityAnimalHousingAverage.py +1 -1
- hestia_earth/models/{site → hestia}/temperatureAnnual.py +3 -3
- hestia_earth/models/{site → hestia}/temperatureMonthly.py +3 -3
- hestia_earth/models/{site → hestia}/totalNitrogenPerKgSoil.py +1 -1
- hestia_earth/models/{cycle → hestia}/unknownPreSeasonWaterRegime.py +1 -1
- hestia_earth/models/hestia/utils.py +93 -0
- hestia_earth/models/{site → hestia}/waterDepth.py +1 -1
- hestia_earth/models/hestia/waterSalinity.py +78 -0
- hestia_earth/models/ipcc2019/aboveGroundBiomass.py +1 -1
- hestia_earth/models/ipcc2019/belowGroundBiomass.py +1 -1
- hestia_earth/models/ipcc2019/biomass_utils.py +2 -4
- hestia_earth/models/ipcc2019/ch4ToAirFloodedRice.py +166 -79
- hestia_earth/models/ipcc2019/ch4ToAirOrganicSoilCultivation.py +270 -0
- hestia_earth/models/ipcc2019/co2ToAirAboveGroundBiomassStockChange.py +0 -3
- hestia_earth/models/ipcc2019/co2ToAirBelowGroundBiomassStockChange.py +0 -3
- hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +88 -63
- hestia_earth/models/ipcc2019/co2ToAirLimeHydrolysis.py +7 -5
- hestia_earth/models/ipcc2019/co2ToAirOrganicSoilCultivation.py +215 -0
- hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChange.py +0 -3
- hestia_earth/models/ipcc2019/co2ToAirUreaHydrolysis.py +16 -9
- hestia_earth/models/ipcc2019/n2OToAirOrganicSoilCultivationDirect.py +161 -0
- hestia_earth/models/ipcc2019/nonCo2EmissionsToAirNaturalVegetationBurning.py +35 -47
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1.py +86 -1
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2.py +127 -1
- hestia_earth/models/ipcc2019/organicCarbonPerHa_utils.py +7 -5
- hestia_earth/models/ipcc2019/organicSoilCultivation_utils.py +159 -0
- hestia_earth/models/mocking/search-results.json +1113 -1113
- hestia_earth/models/pooreNemecek2018/utils.py +8 -2
- hestia_earth/models/schmidt2007/ch4ToAirWasteTreatment.py +1 -4
- hestia_earth/models/schmidt2007/h2SToAirWasteTreatment.py +1 -4
- hestia_earth/models/schmidt2007/n2OToAirWasteTreatmentDirect.py +1 -4
- hestia_earth/models/schmidt2007/nh3ToAirWasteTreatment.py +1 -4
- hestia_earth/models/site/grouped_measurement.py +132 -0
- hestia_earth/models/utils/__init__.py +4 -3
- hestia_earth/models/utils/background_emissions.py +52 -0
- hestia_earth/models/utils/blank_node.py +47 -14
- hestia_earth/models/utils/constant.py +26 -20
- hestia_earth/models/utils/impact_assessment.py +26 -17
- hestia_earth/models/utils/lookup.py +48 -39
- hestia_earth/models/utils/measurement.py +3 -3
- hestia_earth/models/utils/product.py +39 -1
- hestia_earth/models/utils/property.py +14 -6
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.70.0.dist-info → hestia_earth_models-0.70.2.dist-info}/METADATA +2 -2
- {hestia_earth_models-0.70.0.dist-info → hestia_earth_models-0.70.2.dist-info}/RECORD +187 -171
- tests/models/aware/test_scarcityWeightedWaterUse.py +1 -12
- tests/models/ecoalimV9/__init__.py +0 -0
- tests/models/ecoalimV9/test_cycle.py +21 -0
- tests/models/ecoalimV9/test_impact_assessment.py +24 -0
- tests/models/environmentalFootprintV3_1/test_scarcityWeightedWaterUse.py +4 -2
- tests/models/{cycle → hestia}/test_aboveGroundCropResidue.py +1 -1
- tests/models/{cycle → hestia}/test_aboveGroundCropResidueTotal.py +1 -1
- tests/models/{site → hestia}/test_brackishWater.py +1 -1
- tests/models/{site → hestia}/test_cationExchangeCapacityPerKgSoil.py +1 -1
- tests/models/{cycle → hestia}/test_coldCarcassWeightPerHead.py +1 -1
- tests/models/{cycle → hestia}/test_coldDressedCarcassWeightPerHead.py +1 -1
- tests/models/{cycle → hestia}/test_concentrateFeed.py +1 -1
- tests/models/{cycle → hestia}/test_cropResidueManagement.py +1 -1
- tests/models/{cycle → hestia}/test_croppingIntensity.py +1 -1
- tests/models/{cycle → hestia}/test_energyContentLowerHeatingValue.py +5 -3
- tests/models/{cycle → hestia}/test_excretaKgMass.py +1 -1
- tests/models/{cycle → hestia}/test_excretaKgN.py +1 -1
- tests/models/{cycle → hestia}/test_excretaKgVs.py +1 -1
- tests/models/{cycle → hestia}/test_feedConversionRatio.py +3 -4
- tests/models/{site → hestia}/test_flowingWater.py +1 -1
- tests/models/{site → hestia}/test_freshWater.py +1 -1
- tests/models/{cycle → hestia}/test_inorganicFertiliser.py +1 -1
- tests/models/{cycle → hestia}/test_irrigatedTypeUnspecified.py +2 -5
- tests/models/hestia/test_landCover.py +4 -34
- tests/models/{cycle → hestia}/test_liveAnimal.py +1 -1
- tests/models/{cycle → hestia}/test_longFallowRatio.py +1 -1
- tests/models/{site → hestia}/test_management.py +1 -1
- tests/models/{cycle → hestia}/test_materialsAndSubstrate.py +1 -1
- tests/models/{cycle → hestia}/test_milkYield.py +1 -1
- tests/models/{site → hestia}/test_netPrimaryProduction.py +1 -1
- tests/models/{site → hestia}/test_organicCarbonPerHa.py +1 -1
- tests/models/{site → hestia}/test_organicCarbonPerKgSoil.py +1 -1
- tests/models/{site → hestia}/test_organicCarbonPerM3Soil.py +1 -1
- tests/models/{site → hestia}/test_organicMatterPerKgSoil.py +1 -1
- tests/models/{site → hestia}/test_organicMatterPerM3Soil.py +1 -1
- tests/models/hestia/test_pToSurfaceWaterAquacultureSystems.py +56 -0
- tests/models/{cycle → hestia}/test_pastureGrass.py +1 -1
- tests/models/{cycle → hestia}/test_pastureSystem.py +1 -1
- tests/models/{site → hestia}/test_potentialEvapotranspirationAnnual.py +1 -1
- tests/models/{site → hestia}/test_potentialEvapotranspirationMonthly.py +1 -1
- tests/models/{site → hestia}/test_precipitationAnnual.py +1 -1
- tests/models/{site → hestia}/test_precipitationMonthly.py +1 -1
- tests/models/{site → hestia}/test_rainfallAnnual.py +1 -1
- tests/models/{site → hestia}/test_rainfallMonthly.py +1 -1
- tests/models/{cycle → hestia}/test_readyToCookWeightPerHead.py +1 -1
- tests/models/{cycle → hestia}/test_residueBurnt.py +1 -1
- tests/models/{cycle → hestia}/test_residueIncorporated.py +1 -1
- tests/models/{cycle → hestia}/test_residueLeftOnField.py +1 -1
- tests/models/hestia/test_residueRemoved.py +15 -3
- tests/models/{site → hestia}/test_salineWater.py +1 -1
- tests/models/{site → hestia}/test_soilMeasurement.py +13 -21
- tests/models/{cycle → hestia}/test_stockingDensityAnimalHousingAverage.py +1 -1
- tests/models/{site → hestia}/test_temperatureAnnual.py +1 -1
- tests/models/{site → hestia}/test_temperatureMonthly.py +1 -1
- tests/models/{site → hestia}/test_totalNitrogenPerKgSoil.py +1 -1
- tests/models/{cycle → hestia}/test_unknownPreSeasonWaterRegime.py +1 -1
- tests/models/{site → hestia}/test_waterDepth.py +1 -1
- tests/models/hestia/test_waterSalinity.py +26 -0
- tests/models/ipcc2019/test_ch4ToAirEntericFermentation.py +2 -5
- tests/models/ipcc2019/test_ch4ToAirFloodedRice.py +10 -42
- tests/models/ipcc2019/test_ch4ToAirOrganicSoilCultivation.py +61 -0
- tests/models/ipcc2019/test_co2ToAirAboveGroundBiomassStockChange.py +11 -9
- tests/models/ipcc2019/test_co2ToAirBelowGroundBiomassStockChange.py +10 -8
- tests/models/ipcc2019/test_co2ToAirLimeHydrolysis.py +1 -1
- tests/models/ipcc2019/test_co2ToAirOrganicSoilCultivation.py +62 -0
- tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChange.py +11 -8
- tests/models/ipcc2019/test_n2OToAirOrganicSoilCultivationDirect.py +61 -0
- tests/models/ipcc2019/test_nonCo2EmissionsToAirNaturalVegetationBurning.py +3 -2
- tests/models/site/test_grouped_measurement.py +20 -0
- tests/models/test_ecoinventV3AndEmberClimate.py +2 -2
- tests/models/test_emissionNotRelevant.py +0 -8
- tests/models/utils/test_measurement.py +1 -1
- hestia_earth/models/cycle/residueRemoved.py +0 -54
- hestia_earth/models/hestia/nh3ToSurfaceWaterAquacultureSystems.py +0 -64
- hestia_earth/models/site/utils.py +0 -93
- tests/models/cycle/test_residueRemoved.py +0 -37
- tests/models/hestia/test_nh3ToSurfaceWaterAquacultureSystems.py +0 -51
- /hestia_earth/models/{cycle → hestia}/feedConversionRatio/feedConversionRatioCarbon.py +0 -0
- /hestia_earth/models/{cycle → hestia}/feedConversionRatio/feedConversionRatioDryMatter.py +0 -0
- /hestia_earth/models/{cycle → hestia}/feedConversionRatio/feedConversionRatioEnergy.py +0 -0
- /hestia_earth/models/{cycle → hestia}/feedConversionRatio/feedConversionRatioFedWeight.py +0 -0
- /hestia_earth/models/{cycle → hestia}/feedConversionRatio/feedConversionRatioNitrogen.py +0 -0
- /hestia_earth/models/{site → hestia}/organicCarbonPerKgSoil.py +0 -0
- /hestia_earth/models/{site → hestia}/organicCarbonPerM3Soil.py +0 -0
- /hestia_earth/models/{site → hestia}/organicMatterPerKgSoil.py +0 -0
- /hestia_earth/models/{site → hestia}/organicMatterPerM3Soil.py +0 -0
- {hestia_earth_models-0.70.0.dist-info → hestia_earth_models-0.70.2.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.70.0.dist-info → hestia_earth_models-0.70.2.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.70.0.dist-info → hestia_earth_models-0.70.2.dist-info}/top_level.txt +0 -0
@@ -319,7 +319,13 @@ def _add_carbon_stock_change_emissions(
|
|
319
319
|
value = emission_1.value + emission_2.value
|
320
320
|
start_date = min(emission_1.start_date, emission_2.start_date)
|
321
321
|
end_date = max(emission_1.end_date, emission_2.end_date)
|
322
|
-
|
322
|
+
|
323
|
+
methods = [
|
324
|
+
method for emission in (emission_1, emission_2)
|
325
|
+
if isinstance((method := emission.method), EmissionMethodTier)
|
326
|
+
]
|
327
|
+
|
328
|
+
method = min_emission_method_tier(*methods) if methods else None
|
323
329
|
|
324
330
|
return CarbonStockChangeEmission(value, start_date, end_date, method)
|
325
331
|
|
@@ -442,7 +448,6 @@ def create_should_run_function(
|
|
442
448
|
|
443
449
|
inventory, inventory_logs = (
|
444
450
|
compile_inventory_func(
|
445
|
-
cycle_id,
|
446
451
|
cycles,
|
447
452
|
carbon_stock_measurements,
|
448
453
|
land_cover_nodes
|
@@ -464,7 +469,8 @@ def create_should_run_function(
|
|
464
469
|
logs = should_compile_logs | inventory_logs | {
|
465
470
|
"seed": seed,
|
466
471
|
"has_valid_inventory": has_valid_inventory,
|
467
|
-
"has_consecutive_years": has_consecutive_years
|
472
|
+
"has_consecutive_years": has_consecutive_years,
|
473
|
+
"has_stock_measurements": bool(carbon_stock_measurements)
|
468
474
|
}
|
469
475
|
|
470
476
|
return should_run_, kwargs, logs
|
@@ -554,7 +560,6 @@ def _create_compile_inventory_function(
|
|
554
560
|
The `compile_inventory` function.
|
555
561
|
"""
|
556
562
|
def compile_inventory(
|
557
|
-
cycle_id: str,
|
558
563
|
cycles: list[dict],
|
559
564
|
carbon_stock_measurements: list[dict],
|
560
565
|
land_cover_nodes: list[dict]
|
@@ -796,26 +801,8 @@ def _preprocess_carbon_stocks(
|
|
796
801
|
list[CarbonStock]
|
797
802
|
A list of carbon stocks sorted by date.
|
798
803
|
"""
|
799
|
-
|
800
|
-
flatten([split_node_by_dates(m) for m in carbon_stock_measurements])
|
801
|
-
key=lambda node: _gapfill_datestr(node["dates"][0], DatestrGapfillMode.END)
|
802
|
-
)
|
803
|
-
|
804
|
-
values = flatten(node["value"] for node in sorted_measurements)
|
805
|
-
|
806
|
-
sds = flatten(
|
807
|
-
node.get("sd", []) or [_calc_nominal_sd(v, _NOMINAL_ERROR) for v in node["value"]]
|
808
|
-
for node in sorted_measurements
|
809
|
-
)
|
810
|
-
|
811
|
-
dates = flatten(
|
812
|
-
[_gapfill_datestr(datestr, DatestrGapfillMode.END) for datestr in node["dates"]]
|
813
|
-
for node in sorted_measurements
|
814
|
-
)
|
815
|
-
|
816
|
-
methods = flatten(
|
817
|
-
[MeasurementMethodClassification(node.get("methodClassification")) for _ in node["value"]]
|
818
|
-
for node in sorted_measurements
|
804
|
+
dates, values, sds, methods = _extract_node_data(
|
805
|
+
flatten([split_node_by_dates(m) for m in carbon_stock_measurements])
|
819
806
|
)
|
820
807
|
|
821
808
|
correlation_matrix = compute_time_series_correlation_matrix(
|
@@ -842,6 +829,35 @@ def _preprocess_carbon_stocks(
|
|
842
829
|
]
|
843
830
|
|
844
831
|
|
832
|
+
def _extract_node_data(nodes: list[dict]) -> list[dict]:
|
833
|
+
|
834
|
+
def group_node(result, node) -> dict[str, dict]:
|
835
|
+
date = _gapfill_datestr(node["dates"][0], DatestrGapfillMode.END)
|
836
|
+
result[date] = result.get(date, []) + [node]
|
837
|
+
return result
|
838
|
+
|
839
|
+
grouped_nodes = reduce(group_node, nodes, dict())
|
840
|
+
|
841
|
+
def get_values(date):
|
842
|
+
return flatten(node.get("value", []) for node in grouped_nodes[date])
|
843
|
+
|
844
|
+
def get_sds(date):
|
845
|
+
return flatten(
|
846
|
+
node.get("sd", []) or [_calc_nominal_sd(v, _NOMINAL_ERROR) for v in node.get("value", [])]
|
847
|
+
for node in grouped_nodes[date]
|
848
|
+
)
|
849
|
+
|
850
|
+
def get_methods(date):
|
851
|
+
return flatten(node.get("methodClassification", []) for node in grouped_nodes[date])
|
852
|
+
|
853
|
+
dates = sorted(grouped_nodes.keys())
|
854
|
+
values = [mean(get_values(date)) for date in dates]
|
855
|
+
sds = [mean(get_sds(date)) for date in dates]
|
856
|
+
methods = [min_measurement_method_classification(get_methods(date)) for date in dates]
|
857
|
+
|
858
|
+
return dates, values, sds, methods
|
859
|
+
|
860
|
+
|
845
861
|
def _calc_nominal_sd(value: float, error: float) -> float:
|
846
862
|
"""
|
847
863
|
Calculate a nominal SD for a carbon stock measurement. Can be used to gap fill SD when information not present in
|
@@ -1166,20 +1182,19 @@ def _squash_inventory(
|
|
1166
1182
|
return _InventoryKey.CO2_EMISSION in carbon_stock_inventory.get(method, {}).get(year, {}).keys()
|
1167
1183
|
|
1168
1184
|
def squash(result: dict, year: int) -> dict:
|
1169
|
-
|
1170
|
-
(
|
1171
|
-
|
1172
|
-
year: {
|
1173
|
-
**_get_land_use_change_data(year, land_use_inventory),
|
1174
|
-
**reduce(merge, [
|
1175
|
-
carbon_stock_inventory.get(method, {}).get(year, {}),
|
1176
|
-
cycle_inventory.get(year, {})
|
1177
|
-
], dict())
|
1178
|
-
}
|
1179
|
-
} for method in measurement_method_ranking if should_run_group(method, year)
|
1180
|
-
),
|
1181
|
-
{}
|
1185
|
+
method = next(
|
1186
|
+
(method for method in measurement_method_ranking if should_run_group(method, year)),
|
1187
|
+
None
|
1182
1188
|
)
|
1189
|
+
update_dict = {
|
1190
|
+
year: {
|
1191
|
+
**_get_land_use_change_data(year, land_use_inventory),
|
1192
|
+
**reduce(merge, [
|
1193
|
+
carbon_stock_inventory.get(method, {}).get(year, {}),
|
1194
|
+
cycle_inventory.get(year, {})
|
1195
|
+
], dict())
|
1196
|
+
}
|
1197
|
+
}
|
1183
1198
|
return result | update_dict
|
1184
1199
|
|
1185
1200
|
return reduce(squash, inventory_years, dict())
|
@@ -1415,36 +1430,31 @@ def create_run_function(
|
|
1415
1430
|
Assign emissions to either the land use or management change term ids and sum together.
|
1416
1431
|
"""
|
1417
1432
|
data = inventory[year]
|
1433
|
+
|
1418
1434
|
years_since_luc_event = data[_InventoryKey.YEARS_SINCE_LUC_EVENT]
|
1419
1435
|
years_since_inventory_start = data[_InventoryKey.YEARS_SINCE_INVENTORY_START]
|
1436
|
+
share_of_emission = data[_InventoryKey.SHARE_OF_EMISSION][cycle_id]
|
1420
1437
|
|
1438
|
+
co2_emission = data.get(_InventoryKey.CO2_EMISSION)
|
1439
|
+
|
1440
|
+
has_co2_emission = bool(co2_emission)
|
1421
1441
|
is_luc_emission = bool(years_since_luc_event) and years_since_luc_event <= _TRANSITION_PERIOD_YEARS
|
1422
1442
|
is_data_complete = bool(years_since_inventory_start) and years_since_inventory_start >= _TRANSITION_PERIOD_YEARS
|
1423
1443
|
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
zero_emission_term_id = management_change_emission_term_id
|
1428
|
-
elif is_data_complete:
|
1429
|
-
# If management emission && data complete allocate emissions to management change AND add corresponding
|
1430
|
-
# zero emission to management
|
1431
|
-
emission_term_id = management_change_emission_term_id
|
1432
|
-
zero_emission_term_id = land_use_change_emission_term_id
|
1433
|
-
else:
|
1434
|
-
# If management emission, but not data complete allocate emissions to management change only
|
1435
|
-
emission_term_id = management_change_emission_term_id
|
1436
|
-
zero_emission_term_id = None
|
1444
|
+
emission_term_id = (
|
1445
|
+
land_use_change_emission_term_id if is_luc_emission else management_change_emission_term_id
|
1446
|
+
) if has_co2_emission else None
|
1437
1447
|
|
1438
|
-
|
1439
|
-
|
1448
|
+
zero_emission_term_id = (
|
1449
|
+
management_change_emission_term_id if is_luc_emission else
|
1450
|
+
(land_use_change_emission_term_id if is_data_complete else None)
|
1440
1451
|
)
|
1441
1452
|
|
1442
|
-
|
1443
|
-
|
1444
|
-
|
1445
|
-
|
1446
|
-
|
1447
|
-
) if zero_emission_term_id else None
|
1453
|
+
rescaled_emission = _rescale_carbon_stock_change_emission(
|
1454
|
+
co2_emission, share_of_emission
|
1455
|
+
) if emission_term_id else None
|
1456
|
+
|
1457
|
+
zero_emission = get_zero_emission(year) if zero_emission_term_id else None
|
1448
1458
|
|
1449
1459
|
previous_emission = result.get(emission_term_id)
|
1450
1460
|
previous_zero_emission = result.get(zero_emission_term_id)
|
@@ -1454,7 +1464,7 @@ def create_run_function(
|
|
1454
1464
|
_add_carbon_stock_change_emissions(previous_emission, rescaled_emission) if previous_emission
|
1455
1465
|
else rescaled_emission
|
1456
1466
|
)
|
1457
|
-
}
|
1467
|
+
} if emission_term_id else {}
|
1458
1468
|
|
1459
1469
|
zero_emission_dict = {
|
1460
1470
|
zero_emission_term_id: (
|
@@ -1497,7 +1507,7 @@ def create_run_function(
|
|
1497
1507
|
return [
|
1498
1508
|
new_emission_func(
|
1499
1509
|
term_id=emission_term_id,
|
1500
|
-
method_tier=total_emission
|
1510
|
+
method_tier=_get_emission_method(total_emission),
|
1501
1511
|
start_date=_get_emission_start_date(total_emission, cycle_start_date),
|
1502
1512
|
end_date=_get_emission_end_date(total_emission, cycle_end_date),
|
1503
1513
|
**calc_descriptive_stats(
|
@@ -1506,18 +1516,33 @@ def create_run_function(
|
|
1506
1516
|
decimals=6
|
1507
1517
|
)
|
1508
1518
|
) for emission_term_id, total_emission in assigned_emissions.items()
|
1519
|
+
if isinstance(total_emission, CarbonStockChangeEmission)
|
1509
1520
|
]
|
1510
1521
|
|
1511
1522
|
return run
|
1512
1523
|
|
1513
1524
|
|
1525
|
+
def get_zero_emission(year):
|
1526
|
+
return CarbonStockChangeEmission(
|
1527
|
+
value=array(0),
|
1528
|
+
start_date=_gapfill_datestr(year),
|
1529
|
+
end_date=_gapfill_datestr(year, DatestrGapfillMode.END),
|
1530
|
+
method=None
|
1531
|
+
)
|
1532
|
+
|
1533
|
+
|
1534
|
+
def _get_emission_method(emission: CarbonStockChangeEmission):
|
1535
|
+
method = emission.method
|
1536
|
+
return method if isinstance(method, EmissionMethodTier) else EmissionMethodTier.TIER_1
|
1537
|
+
|
1538
|
+
|
1514
1539
|
def _get_emission_start_date(emission: CarbonStockChangeEmission, cycle_start_date: str) -> str:
|
1515
1540
|
cycle_datetime = safe_parse_date(_gapfill_datestr(cycle_start_date))
|
1516
1541
|
emission_datetime = safe_parse_date(emission.start_date)
|
1517
1542
|
|
1518
1543
|
should_run = (
|
1519
1544
|
cycle_datetime and emission_datetime
|
1520
|
-
and cycle_datetime
|
1545
|
+
and cycle_datetime < emission_datetime # If the cycle starts before the emission, add a `startDate`
|
1521
1546
|
)
|
1522
1547
|
|
1523
1548
|
return (
|
@@ -1527,12 +1552,12 @@ def _get_emission_start_date(emission: CarbonStockChangeEmission, cycle_start_da
|
|
1527
1552
|
|
1528
1553
|
|
1529
1554
|
def _get_emission_end_date(emission: CarbonStockChangeEmission, cycle_end_date: str) -> str:
|
1530
|
-
cycle_datetime = safe_parse_date(_gapfill_datestr(cycle_end_date))
|
1555
|
+
cycle_datetime = safe_parse_date(_gapfill_datestr(cycle_end_date, DatestrGapfillMode.END))
|
1531
1556
|
emission_datetime = safe_parse_date(emission.end_date)
|
1532
1557
|
|
1533
1558
|
should_run = (
|
1534
1559
|
cycle_datetime and emission_datetime
|
1535
|
-
and cycle_datetime
|
1560
|
+
and cycle_datetime > emission_datetime # If the cycle ends after the emission, add an `endDate`
|
1536
1561
|
)
|
1537
1562
|
|
1538
1563
|
return (
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from hestia_earth.schema import EmissionMethodTier, TermTermType
|
2
|
-
from hestia_earth.utils.tools import list_sum
|
2
|
+
from hestia_earth.utils.tools import list_sum, non_empty_list
|
3
3
|
from hestia_earth.utils.model import filter_list_term_type
|
4
4
|
|
5
5
|
from hestia_earth.models.log import logRequirements, logShouldRun
|
@@ -16,7 +16,7 @@ REQUIREMENTS = {
|
|
16
16
|
"@type": "Input",
|
17
17
|
"value": "",
|
18
18
|
"term.termType": "soilAmendment",
|
19
|
-
"term.units": ["kg CaCO3", "kg
|
19
|
+
"term.units": ["kg CaCO3", "kg CaMg(CO3)2"]
|
20
20
|
}]
|
21
21
|
}
|
22
22
|
}
|
@@ -38,8 +38,10 @@ def _emission(value: float):
|
|
38
38
|
|
39
39
|
|
40
40
|
def _get_lime_values(cycle: dict, inputs: list):
|
41
|
-
|
42
|
-
|
41
|
+
values = non_empty_list([
|
42
|
+
convert_to_unit(i, Units.KG_CO2)
|
43
|
+
for i in inputs if len(i.get('value', [])) > 0
|
44
|
+
])
|
43
45
|
return [0] if (
|
44
46
|
len(values) == 0 and _is_term_type_complete(cycle, TermTermType.SOILAMENDMENT)
|
45
47
|
) else values
|
@@ -56,7 +58,7 @@ def _run(CaCO3_values: list, MgCO3_values: list):
|
|
56
58
|
def _should_run(cycle: dict):
|
57
59
|
inputs = filter_list_term_type(cycle.get('inputs', []), TermTermType.SOILAMENDMENT)
|
58
60
|
CaCO3_values = _get_lime_values(cycle, _filter_list_term_unit(inputs, Units.KG_CACO3))
|
59
|
-
MgCO3_values = _get_lime_values(cycle, _filter_list_term_unit(inputs, Units.
|
61
|
+
MgCO3_values = _get_lime_values(cycle, _filter_list_term_unit(inputs, Units.KG_CAMGCO32))
|
60
62
|
|
61
63
|
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
62
64
|
CaCO3_values=len(CaCO3_values),
|
@@ -0,0 +1,215 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import numpy.typing as npt
|
3
|
+
from typing import Callable, Union
|
4
|
+
|
5
|
+
from hestia_earth.schema import EmissionMethodTier, EmissionStatsDefinition
|
6
|
+
|
7
|
+
from hestia_earth.models.log import logRequirements, logShouldRun
|
8
|
+
from hestia_earth.models.utils.array_builders import gen_seed, repeat_single, truncated_normal_1d
|
9
|
+
from hestia_earth.models.utils.cycle import land_occupation_per_ha
|
10
|
+
from hestia_earth.models.utils.descriptive_stats import calc_descriptive_stats
|
11
|
+
from hestia_earth.models.utils.ecoClimateZone import EcoClimateZone, get_eco_climate_zone_value
|
12
|
+
from hestia_earth.models.utils.emission import _new_emission
|
13
|
+
from hestia_earth.models.utils.measurement import most_relevant_measurement_value
|
14
|
+
from hestia_earth.models.utils.site import valid_site_type
|
15
|
+
|
16
|
+
from .organicSoilCultivation_utils import (
|
17
|
+
assign_organic_soil_category, calc_emission, get_emission_factor, OrganicSoilCategory, valid_eco_climate_zone
|
18
|
+
)
|
19
|
+
from . import MODEL
|
20
|
+
|
21
|
+
REQUIREMENTS = {
|
22
|
+
"Cycle": {
|
23
|
+
"or": [
|
24
|
+
{
|
25
|
+
"cycleDuration": "",
|
26
|
+
"practices": [{"@type": "Practice", "value": "", "term.@id": "longFallowRatio"}]
|
27
|
+
},
|
28
|
+
{
|
29
|
+
"@doc": "for plantations, additional properties are required",
|
30
|
+
"practices": [
|
31
|
+
{"@type": "Practice", "value": "", "term.@id": "nurseryDensity"},
|
32
|
+
{"@type": "Practice", "value": "", "term.@id": "nurseryDuration"},
|
33
|
+
{"@type": "Practice", "value": "", "term.@id": "plantationProductiveLifespan"},
|
34
|
+
{"@type": "Practice", "value": "", "term.@id": "plantationDensity"},
|
35
|
+
{"@type": "Practice", "value": "", "term.@id": "plantationLifespan"},
|
36
|
+
{"@type": "Practice", "value": "", "term.@id": "rotationDuration"}
|
37
|
+
]
|
38
|
+
}
|
39
|
+
],
|
40
|
+
"site": {
|
41
|
+
"@type": "Site",
|
42
|
+
"measurements": [
|
43
|
+
{"@type": "Measurement", "value": "", "term.@id": "histosol"},
|
44
|
+
{"@type": "Measurement", "value": "", "term.@id": "ecoClimateZone"}
|
45
|
+
]
|
46
|
+
},
|
47
|
+
"optional": {
|
48
|
+
"cycleDuration": ""
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
LOOKUPS = {
|
53
|
+
"crop": [
|
54
|
+
"isPlantation",
|
55
|
+
"IPCC_2013_ORGANIC_SOIL_CULTIVATION_CATEGORY"
|
56
|
+
],
|
57
|
+
"forage": [
|
58
|
+
"isPlantation",
|
59
|
+
"IPCC_2013_ORGANIC_SOIL_CULTIVATION_CATEGORY"
|
60
|
+
],
|
61
|
+
"ecoClimateZone": [
|
62
|
+
"IPCC_2013_ORGANIC_SOILS_TONNES_CO2-C_HECTARE_ANNUAL_CROPS",
|
63
|
+
"IPCC_2013_ORGANIC_SOILS_TONNES_CO2-C_HECTARE_PERENNIAL_CROPS",
|
64
|
+
"IPCC_2013_ORGANIC_SOILS_TONNES_CO2-C_HECTARE_ACACIA",
|
65
|
+
"IPCC_2013_ORGANIC_SOILS_TONNES_CO2-C_HECTARE_OIL_PALM",
|
66
|
+
"IPCC_2013_ORGANIC_SOILS_TONNES_CO2-C_HECTARE_SAGO_PALM",
|
67
|
+
"IPCC_2013_ORGANIC_SOILS_TONNES_CO2-C_HECTARE_PADDY_RICE_CULTIVATION",
|
68
|
+
"IPCC_2013_ORGANIC_SOILS_TONNES_CO2-C_HECTARE_GRASSLAND",
|
69
|
+
"IPCC_2013_ORGANIC_SOILS_TONNES_CO2-C_HECTARE_OTHER"
|
70
|
+
]
|
71
|
+
}
|
72
|
+
RETURNS = {
|
73
|
+
"Emission": [{
|
74
|
+
"value": "",
|
75
|
+
"sd": "",
|
76
|
+
"min": "",
|
77
|
+
"max": "",
|
78
|
+
"observations": "",
|
79
|
+
"statsDefinition": "simulated",
|
80
|
+
"methodTier": "tier 1"
|
81
|
+
}]
|
82
|
+
}
|
83
|
+
TERM_ID = 'co2ToAirOrganicSoilCultivation'
|
84
|
+
TIER = EmissionMethodTier.TIER_1.value
|
85
|
+
|
86
|
+
_STATS_DEFINITION = EmissionStatsDefinition.SIMULATED.value
|
87
|
+
_ITERATIONS = 100000
|
88
|
+
|
89
|
+
|
90
|
+
def _emission(descriptive_stats: dict):
|
91
|
+
emission = _new_emission(TERM_ID, MODEL) | descriptive_stats
|
92
|
+
emission['methodTier'] = TIER
|
93
|
+
return emission
|
94
|
+
|
95
|
+
|
96
|
+
def sample_emission_factor(
|
97
|
+
eco_climate_zone: EcoClimateZone,
|
98
|
+
organic_soil_category: OrganicSoilCategory,
|
99
|
+
seed: Union[int, np.random.Generator, None] = None,
|
100
|
+
) -> npt.NDArray:
|
101
|
+
factor_data = get_emission_factor(TERM_ID, eco_climate_zone, organic_soil_category)
|
102
|
+
sample_func = _get_sample_func(factor_data)
|
103
|
+
return sample_func(iterations=_ITERATIONS, seed=seed, **factor_data)
|
104
|
+
|
105
|
+
|
106
|
+
def _sample_truncated_normal(
|
107
|
+
*, iterations: int, value: float, sd: float, seed: Union[int, np.random.Generator, None] = None, **_
|
108
|
+
) -> npt.NDArray:
|
109
|
+
"""
|
110
|
+
Randomly sample a model parameter with a truncated normal distribution. Emission factors annot be below 0, so
|
111
|
+
truncated normal sampling used.
|
112
|
+
"""
|
113
|
+
return truncated_normal_1d(shape=(1, iterations), mu=value, sigma=sd, low=0, high=np.inf, seed=seed)
|
114
|
+
|
115
|
+
|
116
|
+
def _sample_constant(*, value: float, **_) -> npt.NDArray:
|
117
|
+
"""Sample a constant model parameter."""
|
118
|
+
return repeat_single(shape=(1, 1), value=value)
|
119
|
+
|
120
|
+
|
121
|
+
_KWARGS_TO_SAMPLE_FUNC = {
|
122
|
+
("value", "sd"): _sample_truncated_normal,
|
123
|
+
("value",): _sample_constant
|
124
|
+
}
|
125
|
+
"""
|
126
|
+
Mapping from available distribution data to sample function.
|
127
|
+
"""
|
128
|
+
|
129
|
+
|
130
|
+
def _get_sample_func(kwargs: dict) -> Callable:
|
131
|
+
"""
|
132
|
+
Select the correct sample function for a parameter based on the distribution data available. All possible
|
133
|
+
parameters for the model should have, at a minimum, a `value`, meaning that no default function needs to be
|
134
|
+
specified.
|
135
|
+
|
136
|
+
This function has been extracted into it's own method to allow for mocking of sample function.
|
137
|
+
|
138
|
+
Keyword Args
|
139
|
+
------------
|
140
|
+
value : float
|
141
|
+
The distribution mean.
|
142
|
+
sd : float
|
143
|
+
The standard deviation of the distribution.
|
144
|
+
se : float
|
145
|
+
The standard error of the distribution.
|
146
|
+
n : float
|
147
|
+
Sample size.
|
148
|
+
|
149
|
+
Returns
|
150
|
+
-------
|
151
|
+
Callable
|
152
|
+
The sample function for the distribution.
|
153
|
+
"""
|
154
|
+
return next(
|
155
|
+
sample_func for required_kwargs, sample_func in _KWARGS_TO_SAMPLE_FUNC.items()
|
156
|
+
if all(kwarg in kwargs.keys() for kwarg in required_kwargs)
|
157
|
+
)
|
158
|
+
|
159
|
+
|
160
|
+
def _should_run(cycle: dict):
|
161
|
+
end_date = cycle.get('endDate')
|
162
|
+
site = cycle.get('site', {})
|
163
|
+
measurements = site.get('measurements', [])
|
164
|
+
|
165
|
+
seed = gen_seed(cycle, MODEL, TERM_ID)
|
166
|
+
rng = np.random.default_rng(seed)
|
167
|
+
|
168
|
+
def _get_measurement_content(term_id: str):
|
169
|
+
return most_relevant_measurement_value(measurements, term_id, end_date)
|
170
|
+
|
171
|
+
histosol = _get_measurement_content('histosol')
|
172
|
+
eco_climate_zone = get_eco_climate_zone_value(cycle, as_enum=True)
|
173
|
+
organic_soil_category = assign_organic_soil_category(cycle, log_id=TERM_ID)
|
174
|
+
|
175
|
+
emission_factor = (
|
176
|
+
sample_emission_factor(eco_climate_zone, organic_soil_category, seed=rng) if eco_climate_zone
|
177
|
+
else None
|
178
|
+
)
|
179
|
+
land_occupation = land_occupation_per_ha(MODEL, TERM_ID, cycle)
|
180
|
+
|
181
|
+
logRequirements(
|
182
|
+
cycle, model=MODEL, term=TERM_ID,
|
183
|
+
eco_climate_zone=eco_climate_zone,
|
184
|
+
organic_soil_category=organic_soil_category,
|
185
|
+
emission_factor=f"{np.mean(emission_factor):.3f}",
|
186
|
+
land_occupation=land_occupation,
|
187
|
+
histosol=histosol
|
188
|
+
)
|
189
|
+
|
190
|
+
should_run = all([
|
191
|
+
valid_site_type(site),
|
192
|
+
valid_eco_climate_zone(eco_climate_zone),
|
193
|
+
all(
|
194
|
+
var is not None for var in [
|
195
|
+
emission_factor,
|
196
|
+
land_occupation,
|
197
|
+
histosol
|
198
|
+
]
|
199
|
+
)
|
200
|
+
])
|
201
|
+
|
202
|
+
logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
|
203
|
+
|
204
|
+
return should_run, emission_factor, histosol, land_occupation
|
205
|
+
|
206
|
+
|
207
|
+
def _run(emission_factor: npt.NDArray, histosol: float, land_occupation: float):
|
208
|
+
result = calc_emission(TERM_ID, emission_factor, histosol, land_occupation)
|
209
|
+
descriptive_stats = calc_descriptive_stats(result, _STATS_DEFINITION)
|
210
|
+
return [_emission(descriptive_stats)]
|
211
|
+
|
212
|
+
|
213
|
+
def run(cycle: dict):
|
214
|
+
should_run, emission_factor, histosol, land_occupation = _should_run(cycle)
|
215
|
+
return _run(emission_factor, histosol, land_occupation) if should_run else []
|
@@ -199,13 +199,11 @@ def _should_compile_inventory_func(
|
|
199
199
|
) for cycle in cycles
|
200
200
|
)
|
201
201
|
|
202
|
-
has_stock_measurements = len(carbon_stock_measurements) > 0
|
203
202
|
has_cycles = len(cycles) > 0
|
204
203
|
has_functional_unit_1_ha = all(cycle.get('functionalUnit') == CycleFunctionalUnit._1_HA.value for cycle in cycles)
|
205
204
|
|
206
205
|
should_run = all([
|
207
206
|
has_soil,
|
208
|
-
has_stock_measurements,
|
209
207
|
has_cycles,
|
210
208
|
has_functional_unit_1_ha
|
211
209
|
])
|
@@ -214,7 +212,6 @@ def _should_compile_inventory_func(
|
|
214
212
|
"site_type": site_type,
|
215
213
|
"has_soil": has_soil,
|
216
214
|
"carbon_stock_term": _CARBON_STOCK_TERM_ID,
|
217
|
-
"has_stock_measurements": has_stock_measurements,
|
218
215
|
"has_cycles": has_cycles,
|
219
216
|
"has_functional_unit_1_ha": has_functional_unit_1_ha,
|
220
217
|
}
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from hestia_earth.schema import EmissionMethodTier
|
2
|
-
from hestia_earth.utils.tools import list_sum, safe_parse_float
|
2
|
+
from hestia_earth.utils.tools import list_sum, safe_parse_float, non_empty_list
|
3
3
|
from hestia_earth.utils.model import find_term_match
|
4
4
|
|
5
5
|
from hestia_earth.models.log import debugValues, logRequirements, logShouldRun
|
@@ -68,14 +68,21 @@ def _should_run(cycle: dict):
|
|
68
68
|
uan_share = get_country_breakdown(MODEL, TERM_ID, country_id, LOOKUPS['inorganicFertiliser'][1])
|
69
69
|
urea_unspecified_as_n = list_sum(find_term_match(inputs, UNSPECIFIED_TERM_ID).get('value', []))
|
70
70
|
|
71
|
-
urea_values = [
|
72
|
-
{
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
71
|
+
urea_values = [
|
72
|
+
{
|
73
|
+
'id': id,
|
74
|
+
'values': _get_urea_values(cycle, inputs, id)
|
75
|
+
} for id in term_ids
|
76
|
+
] + non_empty_list([
|
77
|
+
{
|
78
|
+
'id': 'ureaKgN',
|
79
|
+
'values': [urea_unspecified_as_n * urea_share]
|
80
|
+
} if urea_share is not None else None,
|
81
|
+
{
|
82
|
+
'id': 'ureaAmmoniumNitrateKgN',
|
83
|
+
'values': [urea_unspecified_as_n * uan_share]
|
84
|
+
} if urea_share is not None else None
|
85
|
+
] if urea_unspecified_as_n > 0 else [])
|
79
86
|
has_urea_value = any([len(data.get('values')) > 0 for data in urea_values])
|
80
87
|
|
81
88
|
logRequirements(cycle, model=MODEL, term=TERM_ID,
|