hestia-earth-models 0.70.0__py3-none-any.whl → 0.70.1__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/config/Cycle.json +52 -64
- hestia_earth/models/config/ImpactAssessment.json +12 -4
- hestia_earth/models/config/Site.json +33 -22
- 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 +7 -2
- 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/{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/{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 +163 -78
- hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +31 -20
- hestia_earth/models/ipcc2019/co2ToAirUreaHydrolysis.py +16 -9
- 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/mocking/search-results.json +764 -772
- 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/utils/background_emissions.py +52 -0
- hestia_earth/models/utils/blank_node.py +9 -5
- 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/version.py +1 -1
- {hestia_earth_models-0.70.0.dist-info → hestia_earth_models-0.70.1.dist-info}/METADATA +2 -2
- {hestia_earth_models-0.70.0.dist-info → hestia_earth_models-0.70.1.dist-info}/RECORD +163 -158
- 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 +1 -1
- 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/{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 +2 -2
- 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_ch4ToAirFloodedRice.py +10 -42
- tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChange.py +2 -1
- tests/models/ipcc2019/test_nonCo2EmissionsToAirNaturalVegetationBurning.py +3 -2
- 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}/management.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.1.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.70.0.dist-info → hestia_earth_models-0.70.1.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.70.0.dist-info → hestia_earth_models-0.70.1.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,10 @@
|
|
1
|
+
from functools import reduce
|
1
2
|
from hestia_earth.schema import EmissionMethodTier, EmissionStatsDefinition, TermTermType
|
2
3
|
from hestia_earth.utils.model import filter_list_term_type, find_term_match
|
3
|
-
from hestia_earth.utils.tools import list_sum, safe_parse_float
|
4
|
+
from hestia_earth.utils.tools import list_sum, safe_parse_float, non_empty_list
|
4
5
|
|
5
|
-
from hestia_earth.models.log import
|
6
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table, debugValues
|
7
|
+
from hestia_earth.models.utils import multiply_values
|
6
8
|
from hestia_earth.models.utils.term import get_lookup_value
|
7
9
|
from hestia_earth.models.utils.emission import _new_emission
|
8
10
|
from hestia_earth.models.utils.product import has_flooded_rice
|
@@ -12,18 +14,17 @@ from . import MODEL
|
|
12
14
|
|
13
15
|
REQUIREMENTS = {
|
14
16
|
"Cycle": {
|
15
|
-
"practices": [
|
17
|
+
"practices": [
|
18
|
+
{"@type": "Practice", "value": "", "term.@id": "croppingDuration"},
|
19
|
+
{"@type": "Practice", "value": "", "term.termType": ["landUseManagement", "waterRegime"]}
|
20
|
+
],
|
16
21
|
"site": {
|
17
22
|
"@type": "Site",
|
18
23
|
"country": {"@type": "Term", "termType": "region"}
|
19
24
|
},
|
20
25
|
"optional": {
|
21
26
|
"inputs": [
|
22
|
-
{
|
23
|
-
"@type": "Input",
|
24
|
-
"value": "",
|
25
|
-
"term.termType": "organicFertiliser"
|
26
|
-
},
|
27
|
+
{"@type": "Input", "value": "", "term.termType": "organicFertiliser"},
|
27
28
|
{
|
28
29
|
"@type": "Input",
|
29
30
|
"value": "",
|
@@ -32,28 +33,35 @@ REQUIREMENTS = {
|
|
32
33
|
}
|
33
34
|
],
|
34
35
|
"products": [{"@type": "Product", "value": "", "term.@id": "aboveGroundCropResidueIncorporated"}],
|
35
|
-
"practices": [
|
36
|
-
{"@type": "Practice", "value": "", "term.termType": "cropResidueManagement"},
|
37
|
-
{"@type": "Practice", "value": "", "term.termType": "landUseManagement"},
|
38
|
-
{"@type": "Practice", "value": "", "term.termType": "waterRegime"}
|
39
|
-
]
|
36
|
+
"practices": [{"@type": "Practice", "value": "", "term.termType": "cropResidueManagement"}]
|
40
37
|
}
|
41
38
|
}
|
42
39
|
}
|
43
40
|
LOOKUPS = {
|
44
41
|
"landUseManagement": [
|
45
|
-
"
|
46
|
-
"
|
47
|
-
"IPCC_2019_CH4_rice_SFp
|
42
|
+
"IPCC_2019_CH4_rice_SFp",
|
43
|
+
"IPCC_2019_CH4_rice_SFp-min",
|
44
|
+
"IPCC_2019_CH4_rice_SFp-max",
|
48
45
|
"IPCC_2019_CH4_rice_SFp-sd"
|
49
46
|
],
|
50
47
|
"waterRegime": [
|
51
|
-
"IPCC_2019_CH4_rice_SFw",
|
52
|
-
"IPCC_2019_CH4_rice_SFw-
|
53
|
-
"
|
54
|
-
"
|
48
|
+
"IPCC_2019_CH4_rice_SFw",
|
49
|
+
"IPCC_2019_CH4_rice_SFw-min",
|
50
|
+
"IPCC_2019_CH4_rice_SFw-max",
|
51
|
+
"IPCC_2019_CH4_rice_SFw-sd"
|
52
|
+
],
|
53
|
+
"organicFertiliser": [
|
54
|
+
"IPCC_2019_CH4_rice_CFOA_kg_fresh_weight",
|
55
|
+
"IPCC_2019_CH4_rice_CFOA_kg_fresh_weight_min",
|
56
|
+
"IPCC_2019_CH4_rice_CFOA_kg_fresh_weight_max",
|
57
|
+
"IPCC_2019_CH4_rice_CFOA_kg_fresh_weight_sd"
|
58
|
+
],
|
59
|
+
"cropResidueManagement": [
|
60
|
+
"IPCC_2019_CH4_rice_CFOA_kg_dry_weight",
|
61
|
+
"IPCC_2019_CH4_rice_CFOA_kg_dry_weight_min",
|
62
|
+
"IPCC_2019_CH4_rice_CFOA_kg_dry_weight_max",
|
63
|
+
"IPCC_2019_CH4_rice_CFOA_kg_dry_weight_sd"
|
55
64
|
],
|
56
|
-
"organicFertiliser": ["IPCC_2019_CH4_rice_CFOA_kg_fresh_weight", "IPCC_2019_CH4_rice_CFOA_kg_dry_weight"],
|
57
65
|
"region-ch4ef-IPCC2019": ["CH4_ef", "CH4_ef_min", "CH4_ef_max", "CH4_ef_sd"]
|
58
66
|
}
|
59
67
|
RETURNS = {
|
@@ -68,92 +76,138 @@ RETURNS = {
|
|
68
76
|
}
|
69
77
|
TERM_ID = 'ch4ToAirFloodedRice'
|
70
78
|
TIER = EmissionMethodTier.TIER_1.value
|
79
|
+
_STATS = ['value', 'min', 'max', 'sd']
|
71
80
|
|
72
81
|
|
73
82
|
def _emission(value: float, min: float, max: float, sd: float):
|
74
83
|
emission = _new_emission(TERM_ID, MODEL)
|
75
84
|
emission['value'] = [value]
|
76
|
-
|
77
|
-
|
78
|
-
|
85
|
+
if min is not None:
|
86
|
+
emission['min'] = [min]
|
87
|
+
if max is not None:
|
88
|
+
emission['max'] = [max]
|
89
|
+
if sd is not None:
|
90
|
+
emission['sd'] = [sd]
|
79
91
|
emission['methodTier'] = TIER
|
80
92
|
emission['statsDefinition'] = EmissionStatsDefinition.MODELLED.value
|
81
93
|
return emission
|
82
94
|
|
83
95
|
|
84
|
-
def _get_CH4_ef(country: str, suffix: str = ''):
|
96
|
+
def _get_CH4_ef(country: str, suffix: str = 'value'):
|
85
97
|
lookup_name = 'region-ch4ef-IPCC2019.csv'
|
98
|
+
lookup = 'CH4_ef'
|
99
|
+
lookup = '_'.join([lookup, suffix]) if suffix != 'value' else lookup
|
86
100
|
return safe_parse_float(
|
87
|
-
get_region_lookup_value(lookup_name, country,
|
101
|
+
get_region_lookup_value(lookup_name, country, lookup, model=MODEL, term=TERM_ID),
|
102
|
+
default=None
|
88
103
|
)
|
89
104
|
|
90
105
|
|
91
|
-
def
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
def _get_cropResidue_value(cycle: dict, suffix: str = ''):
|
106
|
+
def _get_cropResidue_value(cycle: dict, suffix: str = 'value'):
|
107
|
+
product_id = 'aboveGroundCropResidueIncorporated'
|
96
108
|
abgIncorporated = list_sum(
|
97
|
-
find_term_match(cycle.get('products', []),
|
109
|
+
find_term_match(cycle.get('products', []), product_id).get('value', []),
|
110
|
+
default=None
|
98
111
|
)
|
99
112
|
abgManagement = filter_list_term_type(cycle.get('practices', []), TermTermType.CROPRESIDUEMANAGEMENT)
|
100
113
|
term = abgManagement[0].get('term', {}) if len(abgManagement) > 0 else None
|
114
|
+
lookup = 'IPCC_2019_CH4_rice_CFOA_kg_dry_weight'
|
115
|
+
lookup = '_'.join([lookup, suffix]) if suffix != 'value' else lookup
|
101
116
|
factor = safe_parse_float(
|
102
|
-
get_lookup_value(term,
|
103
|
-
|
104
|
-
|
117
|
+
get_lookup_value(term, lookup, model=MODEL, term=TERM_ID),
|
118
|
+
default=None
|
119
|
+
) if term else None
|
105
120
|
|
121
|
+
debugValues(cycle, model=MODEL, term=TERM_ID,
|
122
|
+
**{'cropResidue_' + suffix: log_as_table({
|
123
|
+
'product-id': product_id,
|
124
|
+
'product-value': abgIncorporated,
|
125
|
+
'factor': factor
|
126
|
+
})})
|
106
127
|
|
107
|
-
|
128
|
+
return multiply_values([abgIncorporated, factor])
|
129
|
+
|
130
|
+
|
131
|
+
def _get_fertiliser_values(input: dict, suffix: str = 'value'):
|
108
132
|
term = input.get('term', {})
|
133
|
+
lookup = 'IPCC_2019_CH4_rice_CFOA_kg_fresh_weight'
|
134
|
+
lookup = '_'.join([lookup, suffix]) if suffix != 'value' else lookup
|
109
135
|
factor = safe_parse_float(
|
110
|
-
get_lookup_value(term,
|
136
|
+
get_lookup_value(term, lookup, model=MODEL, term=TERM_ID),
|
137
|
+
default=None
|
111
138
|
)
|
112
|
-
|
139
|
+
value = list_sum(input.get('value', []))
|
140
|
+
return {'input-id': term.get('@id'), 'input-value': value, 'factor': factor}
|
113
141
|
|
114
142
|
|
115
|
-
def
|
116
|
-
|
117
|
-
fertilisers = get_organicFertiliser_inputs(cycle)
|
118
|
-
fert_value = list_sum([_get_fertiliser_value(i, suffix) for i in fertilisers])
|
119
|
-
return (1 + (fert_value/1000) + (cropResidue/1000)) ** 0.59
|
143
|
+
def _get_fertiliser_value(cycle: dict, suffix: str = 'value'):
|
144
|
+
fertiliser_values = [_get_fertiliser_values(i, suffix) for i in get_organicFertiliser_inputs(cycle)]
|
120
145
|
|
146
|
+
debugValues(cycle, model=MODEL, term=TERM_ID,
|
147
|
+
**{'fertiliser_' + suffix: log_as_table(fertiliser_values)})
|
121
148
|
|
122
|
-
|
123
|
-
|
124
|
-
(
|
149
|
+
valid_fertiliser_values = [
|
150
|
+
value for value in fertiliser_values
|
151
|
+
if all([value.get('input-value') is not None, value.get('factor') is not None])
|
125
152
|
]
|
126
|
-
|
127
|
-
|
153
|
+
fert_value = list_sum([
|
154
|
+
value.get('input-value') * value.get('factor')
|
155
|
+
for value in valid_fertiliser_values
|
156
|
+
])
|
157
|
+
return fert_value
|
128
158
|
|
129
159
|
|
130
|
-
def
|
131
|
-
|
132
|
-
|
133
|
-
SFp = _calculate_SF_average(practices, 'IPCC_2019_CH4_rice_SFp' + suffix)
|
134
|
-
SFo = _calculate_SFo(cycle, suffix)
|
135
|
-
debugValues(cycle, model=MODEL, term=TERM_ID, **{
|
136
|
-
'CH4_ef' + suffix: CH4_ef,
|
137
|
-
'SFw' + suffix: SFw,
|
138
|
-
'SFp' + suffix: SFp,
|
139
|
-
'SFo' + suffix: SFo
|
140
|
-
})
|
141
|
-
return CH4_ef * (SFw if SFw > 0 else 1) * (SFp if SFp > 0 else 1) * SFo
|
160
|
+
def _calculate_SFo(cycle: dict, suffix: str = 'value'):
|
161
|
+
cropResidue = _get_cropResidue_value(cycle, suffix)
|
162
|
+
fertiliser = _get_fertiliser_value(cycle, suffix)
|
142
163
|
|
164
|
+
return (1 + (fertiliser/1000) + (cropResidue/1000)) ** 0.59
|
143
165
|
|
144
|
-
def _get_croppingDuration(croppingDuration: dict, key: str = 'value'):
|
145
|
-
return list_sum(croppingDuration.get(key, croppingDuration.get('value', [])))
|
146
166
|
|
167
|
+
def _get_practice_values(practice: dict, col: str, default=None):
|
168
|
+
term = practice.get('term', {})
|
169
|
+
factor = safe_parse_float(get_lookup_value(term, col, model=MODEL, term=TERM_ID), default)
|
170
|
+
return {
|
171
|
+
'practice-id': term.get('@id'),
|
172
|
+
'factor': factor,
|
173
|
+
'practice-value': list_sum(practice.get('value', []), default=default)
|
174
|
+
} if factor is not None else None
|
147
175
|
|
148
|
-
def _run(cycle: dict, croppingDuration: dict, country: str):
|
149
|
-
practices = filter_list_term_type(cycle.get('practices', []), [
|
150
|
-
TermTermType.WATERREGIME, TermTermType.LANDUSEMANAGEMENT
|
151
|
-
])
|
152
176
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
177
|
+
def _calculate_SF_total(cycle: dict, practices: list, lookup: str, suffix: str = 'value', default=None):
|
178
|
+
lookup_column = '-'.join([lookup, suffix]) if suffix != 'value' else lookup
|
179
|
+
values = non_empty_list([_get_practice_values(p, lookup_column) for p in practices])
|
180
|
+
|
181
|
+
debugValues(cycle, model=MODEL, term=TERM_ID,
|
182
|
+
**{lookup_column: log_as_table(values)})
|
183
|
+
|
184
|
+
used_values = [value for value in values if value.get('practice-value') is not None]
|
185
|
+
|
186
|
+
# sum only values that are numbers
|
187
|
+
return (
|
188
|
+
list_sum([
|
189
|
+
value.get('factor') * value.get('practice-value') for value in used_values
|
190
|
+
], default=None) / list_sum([
|
191
|
+
value.get('practice-value') for value in used_values
|
192
|
+
])
|
193
|
+
) if used_values else (
|
194
|
+
default if suffix == 'value' else None
|
195
|
+
)
|
196
|
+
|
197
|
+
|
198
|
+
def _value_from_factors(values: list, key: str = 'value'):
|
199
|
+
# get the value from all factors, and only run if all are provided
|
200
|
+
all_values = [value.get(key) for value in values]
|
201
|
+
return multiply_values(all_values) if all([v is not None for v in all_values]) else None
|
202
|
+
|
203
|
+
|
204
|
+
def _run(values: list):
|
205
|
+
value = _value_from_factors(values, 'value')
|
206
|
+
min = _value_from_factors(values, 'min')
|
207
|
+
max = _value_from_factors(values, 'max')
|
208
|
+
sd = _value_from_factors(values, 'sd')
|
209
|
+
|
210
|
+
sd = (max-min)/4 if all([max, min]) else None
|
157
211
|
|
158
212
|
return [_emission(value, min, max, sd)]
|
159
213
|
|
@@ -162,20 +216,51 @@ def _should_run(cycle: dict):
|
|
162
216
|
country = cycle.get('site', {}).get('country', {}).get('@id')
|
163
217
|
|
164
218
|
flooded_rice = has_flooded_rice(cycle.get('products', []))
|
219
|
+
practices = cycle.get('practices', [])
|
165
220
|
|
166
|
-
croppingDuration = find_term_match(
|
221
|
+
croppingDuration = find_term_match(practices, 'croppingDuration', None)
|
167
222
|
has_croppingDuration = croppingDuration is not None
|
223
|
+
croppingDuration = reduce(lambda p, key: p | {
|
224
|
+
key: list_sum(croppingDuration.get(key) or [], default=None)
|
225
|
+
}, _STATS, {}) if has_croppingDuration else {}
|
226
|
+
|
227
|
+
CH4_ef = reduce(lambda p, key: p | {key: _get_CH4_ef(country, key)}, _STATS, {})
|
228
|
+
SFo = reduce(lambda p, key: p | {key: _calculate_SFo(cycle, key)}, _STATS, {})
|
229
|
+
|
230
|
+
water_regime = filter_list_term_type(practices, TermTermType.WATERREGIME)
|
231
|
+
SFw = reduce(lambda p, key: p | {
|
232
|
+
key: _calculate_SF_total(cycle, water_regime, 'IPCC_2019_CH4_rice_SFw', key)
|
233
|
+
}, _STATS, {})
|
234
|
+
|
235
|
+
land_use_management = filter_list_term_type(practices, TermTermType.LANDUSEMANAGEMENT)
|
236
|
+
SFp = reduce(lambda p, key: p | {
|
237
|
+
key: _calculate_SF_total(cycle, land_use_management, 'IPCC_2019_CH4_rice_SFp', key, default=1)
|
238
|
+
}, _STATS, {})
|
168
239
|
|
169
240
|
logRequirements(cycle, model=MODEL, term=TERM_ID,
|
170
241
|
has_flooded_rice=flooded_rice,
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
242
|
+
country=country,
|
243
|
+
values=log_as_table([
|
244
|
+
{'name': 'croppingDuration'} | croppingDuration,
|
245
|
+
{'name': 'CH4-ef'} | CH4_ef,
|
246
|
+
{'name': 'SFo'} | SFo,
|
247
|
+
{'name': 'SFw'} | SFw,
|
248
|
+
{'name': 'SFp'} | SFp,
|
249
|
+
]))
|
250
|
+
|
251
|
+
should_run = all([
|
252
|
+
flooded_rice,
|
253
|
+
has_croppingDuration,
|
254
|
+
country,
|
255
|
+
CH4_ef.get('value') is not None,
|
256
|
+
SFo.get('value') is not None,
|
257
|
+
SFw.get('value') is not None,
|
258
|
+
SFp.get('value') is not None,
|
259
|
+
])
|
175
260
|
logShouldRun(cycle, MODEL, TERM_ID, should_run, methodTier=TIER)
|
176
|
-
return should_run, croppingDuration,
|
261
|
+
return should_run, [croppingDuration, CH4_ef, SFo, SFw, SFp]
|
177
262
|
|
178
263
|
|
179
264
|
def run(cycle: dict):
|
180
|
-
should_run,
|
181
|
-
return _run(
|
265
|
+
should_run, values = _should_run(cycle)
|
266
|
+
return _run(values) if should_run else []
|
@@ -796,26 +796,8 @@ def _preprocess_carbon_stocks(
|
|
796
796
|
list[CarbonStock]
|
797
797
|
A list of carbon stocks sorted by date.
|
798
798
|
"""
|
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
|
799
|
+
dates, values, sds, methods = _extract_node_data(
|
800
|
+
flatten([split_node_by_dates(m) for m in carbon_stock_measurements])
|
819
801
|
)
|
820
802
|
|
821
803
|
correlation_matrix = compute_time_series_correlation_matrix(
|
@@ -842,6 +824,35 @@ def _preprocess_carbon_stocks(
|
|
842
824
|
]
|
843
825
|
|
844
826
|
|
827
|
+
def _extract_node_data(nodes: list[dict]) -> list[dict]:
|
828
|
+
|
829
|
+
def group_node(result, node) -> dict[str, dict]:
|
830
|
+
date = _gapfill_datestr(node["dates"][0], DatestrGapfillMode.END)
|
831
|
+
result[date] = result.get(date, []) + [node]
|
832
|
+
return result
|
833
|
+
|
834
|
+
grouped_nodes = reduce(group_node, nodes, dict())
|
835
|
+
|
836
|
+
def get_values(date):
|
837
|
+
return flatten(node.get("value", []) for node in grouped_nodes[date])
|
838
|
+
|
839
|
+
def get_sds(date):
|
840
|
+
return flatten(
|
841
|
+
node.get("sd", []) or [_calc_nominal_sd(v, _NOMINAL_ERROR) for v in node.get("value", [])]
|
842
|
+
for node in grouped_nodes[date]
|
843
|
+
)
|
844
|
+
|
845
|
+
def get_methods(date):
|
846
|
+
return flatten(node.get("methodClassification", []) for node in grouped_nodes[date])
|
847
|
+
|
848
|
+
dates = sorted(grouped_nodes.keys())
|
849
|
+
values = [mean(get_values(date)) for date in dates]
|
850
|
+
sds = [mean(get_sds(date)) for date in dates]
|
851
|
+
methods = [min_measurement_method_classification(get_methods(date)) for date in dates]
|
852
|
+
|
853
|
+
return dates, values, sds, methods
|
854
|
+
|
855
|
+
|
845
856
|
def _calc_nominal_sd(value: float, error: float) -> float:
|
846
857
|
"""
|
847
858
|
Calculate a nominal SD for a carbon stock measurement. Can be used to gap fill SD when information not present in
|
@@ -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,
|
@@ -156,8 +156,6 @@ _Inventory = dict[int, _InventoryYear]
|
|
156
156
|
{year (int): data (_InventoryYear)}
|
157
157
|
"""
|
158
158
|
|
159
|
-
_EmissionInventory = dict[_EmissionTermId, npt.NDArray]
|
160
|
-
|
161
159
|
|
162
160
|
_BIOMASS_CATEGORY_TO_FUEL_CATEGORY = {
|
163
161
|
BiomassCategory.FOREST: {
|
@@ -518,9 +516,9 @@ def _sum_cycle_emissions(term_id: _EmissionTermId, cycle_id: str, inventory: _In
|
|
518
516
|
return reduce(add_cycle_emissions, inventory.keys(), np.array(0))
|
519
517
|
|
520
518
|
|
521
|
-
def
|
519
|
+
def _compile_inventory(
|
522
520
|
cycle: dict, site: dict, land_cover_nodes: list[dict], eco_climate_zone: EcoClimateZone
|
523
|
-
)
|
521
|
+
):
|
524
522
|
"""
|
525
523
|
Compile the run data for the model, collating data from `site.management` and related cycles. An annualised
|
526
524
|
inventory of land cover change and natural vegetation burning events is constructed. Emissions from burning events
|
@@ -539,18 +537,12 @@ def _compile_run_data(
|
|
539
537
|
|
540
538
|
Returns
|
541
539
|
-------
|
542
|
-
|
543
|
-
|
544
|
-
```
|
545
|
-
{
|
546
|
-
emission_term_id (str): value (NDArray),
|
547
|
-
...
|
548
|
-
}
|
549
|
-
```
|
540
|
+
should_run : bool
|
541
|
+
Whether the model should be run.
|
550
542
|
inventory : _Inventory
|
551
|
-
An inventory of model data
|
543
|
+
An inventory of model data.
|
552
544
|
logs : dict
|
553
|
-
Data
|
545
|
+
Data about the inventory compilation to be logged.
|
554
546
|
"""
|
555
547
|
cycle_id = cycle.get("@id")
|
556
548
|
related_cycles_ = related_cycles(site, cycles_mapping={cycle_id: cycle})
|
@@ -586,9 +578,15 @@ def _compile_run_data(
|
|
586
578
|
Returns
|
587
579
|
-------
|
588
580
|
inventory : dict
|
589
|
-
An inventory of model data, updated to include the
|
581
|
+
An inventory of model data, updated to include the input year.
|
590
582
|
"""
|
591
|
-
land_cover_nodes =
|
583
|
+
land_cover_nodes = land_cover_grouped.get(
|
584
|
+
next(
|
585
|
+
(k for k in sorted(land_cover_grouped) if k >= year), # backfill if possible
|
586
|
+
min(land_cover_grouped, key=lambda k: abs(k - year)) # else forward-fill
|
587
|
+
),
|
588
|
+
[]
|
589
|
+
)
|
592
590
|
|
593
591
|
biomass_category_summary = summarise_land_cover_nodes(land_cover_nodes)
|
594
592
|
prev_biomass_category_summary = inventory.get(year-1, {}).get("biomass_category_summary", {})
|
@@ -656,17 +654,17 @@ def _compile_run_data(
|
|
656
654
|
|
657
655
|
inventory = reduce(build_inventory_year, range(min_year, max_year+1), dict())
|
658
656
|
|
659
|
-
|
660
|
-
term_id: value for term_id in EMISSION_TERM_IDS
|
661
|
-
if np.all((value := _sum_cycle_emissions(term_id, cycle_id, inventory)) > 0)
|
662
|
-
}
|
657
|
+
n_land_cover_years = len(land_cover_grouped)
|
663
658
|
|
664
659
|
logs = {
|
660
|
+
"n_land_cover_years": n_land_cover_years,
|
665
661
|
"percent_burned": percent_burned,
|
666
662
|
"seed": seed,
|
667
663
|
}
|
668
664
|
|
669
|
-
|
665
|
+
should_run = bool(inventory and n_land_cover_years > 1)
|
666
|
+
|
667
|
+
return should_run, inventory, logs
|
670
668
|
|
671
669
|
|
672
670
|
def _format_bool(value: Optional[bool]) -> str:
|
@@ -831,23 +829,16 @@ def _format_inventory(term_id: _EmissionTermId, cycle_id: str, inventory: dict)
|
|
831
829
|
) if inventory else "None"
|
832
830
|
|
833
831
|
|
834
|
-
def
|
835
|
-
term_id: _EmissionTermId, cycle: dict, emission_inventory: _EmissionInventory, inventory: dict, logs: dict
|
836
|
-
):
|
832
|
+
def _log_emission_data(should_run: bool, term_id: _EmissionTermId, cycle: dict, inventory: dict, logs: dict):
|
837
833
|
"""
|
838
|
-
|
839
|
-
the model logs and inventory.
|
834
|
+
Format and log the model logs and inventory.
|
840
835
|
"""
|
841
|
-
should_run = term_id in emission_inventory
|
842
|
-
|
843
836
|
formatted_logs = _format_logs(logs)
|
844
837
|
formatted_inventory = _format_inventory(term_id, cycle.get("@id"), inventory)
|
845
838
|
|
846
839
|
logRequirements(cycle, model=MODEL, term=term_id, **formatted_logs, inventory=formatted_inventory)
|
847
840
|
logShouldRun(cycle, MODEL, term_id, should_run)
|
848
841
|
|
849
|
-
return should_run
|
850
|
-
|
851
842
|
|
852
843
|
def _should_run(cycle: dict):
|
853
844
|
"""
|
@@ -861,8 +852,8 @@ def _should_run(cycle: dict):
|
|
861
852
|
|
862
853
|
Returns
|
863
854
|
-------
|
864
|
-
tuple[bool,
|
865
|
-
should_run,
|
855
|
+
tuple[bool, _Inventory]
|
856
|
+
should_run, inventory
|
866
857
|
"""
|
867
858
|
site = _get_site(cycle)
|
868
859
|
|
@@ -873,7 +864,7 @@ def _should_run(cycle: dict):
|
|
873
864
|
|
874
865
|
has_valid_site_type = all([site_type, site_type not in _EXCLUDED_SITE_TYPES])
|
875
866
|
has_valid_eco_climate_zone = all([eco_climate_zone, eco_climate_zone not in _EXCLUDED_ECO_CLIMATE_ZONES])
|
876
|
-
has_land_cover_nodes = len(land_cover_nodes) >
|
867
|
+
has_land_cover_nodes = len(land_cover_nodes) > 1
|
877
868
|
|
878
869
|
should_compile_inventory = all([
|
879
870
|
has_valid_site_type,
|
@@ -881,9 +872,9 @@ def _should_run(cycle: dict):
|
|
881
872
|
has_land_cover_nodes
|
882
873
|
])
|
883
874
|
|
884
|
-
|
885
|
-
|
886
|
-
if should_compile_inventory else (
|
875
|
+
should_run, inventory, compilation_logs = (
|
876
|
+
_compile_inventory(cycle, site, land_cover_nodes, eco_climate_zone)
|
877
|
+
if should_compile_inventory else (False, {}, {})
|
887
878
|
)
|
888
879
|
|
889
880
|
logs = {
|
@@ -897,21 +888,18 @@ def _should_run(cycle: dict):
|
|
897
888
|
**compilation_logs
|
898
889
|
}
|
899
890
|
|
900
|
-
|
901
|
-
|
902
|
-
_should_run_emission(term_id, cycle, emission_inventory, inventory, logs) for term_id in EMISSION_TERM_IDS
|
903
|
-
])
|
904
|
-
])
|
891
|
+
for term_id in EMISSION_TERM_IDS:
|
892
|
+
_log_emission_data(should_run, term_id, cycle, inventory, logs)
|
905
893
|
|
906
|
-
return should_run,
|
894
|
+
return should_run, inventory
|
907
895
|
|
908
896
|
|
909
|
-
def _run_emission(term_id: _EmissionTermId,
|
897
|
+
def _run_emission(term_id: _EmissionTermId, cycle_id: str, inventory: _Inventory) -> list[dict]:
|
910
898
|
"""
|
911
|
-
Retrieve the
|
899
|
+
Retrieve the sum relevant emissions and format them as a HESTIA
|
912
900
|
[Emission node](https://www.hestia.earth/schema/Emission).
|
913
901
|
"""
|
914
|
-
emission =
|
902
|
+
emission = _sum_cycle_emissions(term_id, cycle_id, inventory)
|
915
903
|
descriptive_stats = calc_descriptive_stats(emission, STATS_DEFINITION, decimals=3)
|
916
904
|
return _emission(term_id, **descriptive_stats)
|
917
905
|
|
@@ -932,5 +920,5 @@ def run(cycle: dict):
|
|
932
920
|
`ch4ToAirNaturalVegetationBurning` **OR** `coToAirNaturalVegetationBurning` **OR**
|
933
921
|
`n2OToAirNaturalVegetationBurningDirect` **OR** `noxToAirNaturalVegetationBurning`.
|
934
922
|
"""
|
935
|
-
should_run,
|
936
|
-
return [_run_emission(term_id,
|
923
|
+
should_run, inventory = _should_run(cycle)
|
924
|
+
return [_run_emission(term_id, cycle.get("@id"), inventory) for term_id in EMISSION_TERM_IDS] if should_run else []
|