hestia-earth-models 0.69.1__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/cache_sites.py +3 -2
- hestia_earth/models/cml2001Baseline/abioticResourceDepletionFossilFuels.py +2 -1
- hestia_earth/models/cml2001Baseline/abioticResourceDepletionMineralsAndMetals.py +3 -2
- hestia_earth/models/config/Cycle.json +82 -60
- hestia_earth/models/config/ImpactAssessment.json +12 -4
- hestia_earth/models/config/Site.json +33 -22
- hestia_earth/models/cycle/animal/input/hestiaAggregatedData.py +1 -1
- hestia_earth/models/cycle/animal/input/properties.py +1 -1
- hestia_earth/models/cycle/cycleDuration.py +2 -2
- hestia_earth/models/cycle/input/hestiaAggregatedData.py +12 -14
- hestia_earth/models/cycle/input/properties.py +1 -1
- hestia_earth/models/cycle/siteDuration.py +3 -3
- 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/frischknechtEtAl2000/ionisingRadiationKbqU235Eq.py +1 -1
- hestia_earth/models/geospatialDatabase/croppingIntensity.py +4 -4
- hestia_earth/models/geospatialDatabase/longFallowRatio.py +4 -4
- hestia_earth/models/geospatialDatabase/region.py +3 -2
- hestia_earth/models/geospatialDatabase/utils.py +6 -5
- hestia_earth/models/haversineFormula/transport/distance.py +5 -4
- hestia_earth/models/{koble2014 → hestia}/aboveGroundCropResidue.py +4 -5
- hestia_earth/models/{cycle → hestia}/aboveGroundCropResidueTotal.py +2 -2
- 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 +2 -2
- 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 +31 -27
- hestia_earth/models/hestia/landTransformation100YearAverageDuringCycle.py +2 -1
- hestia_earth/models/hestia/landTransformation20YearAverageDuringCycle.py +2 -1
- 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 +5 -3
- 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/hestia/seed_emissions.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/impact_assessment/emissions.py +1 -1
- hestia_earth/models/impact_assessment/product/economicValueShare.py +1 -1
- hestia_earth/models/impact_assessment/product/value.py +1 -1
- hestia_earth/models/ipcc2019/aboveGroundBiomass.py +1 -1
- hestia_earth/models/ipcc2019/animal/fatContent.py +2 -2
- hestia_earth/models/ipcc2019/animal/milkYieldPerAnimal.py +2 -2
- hestia_earth/models/ipcc2019/animal/trueProteinContent.py +2 -2
- hestia_earth/models/ipcc2019/belowGroundBiomass.py +1 -1
- hestia_earth/models/ipcc2019/biomass_utils.py +2 -4
- hestia_earth/models/ipcc2019/ch4ToAirEntericFermentation.py +7 -2
- hestia_earth/models/ipcc2019/ch4ToAirFloodedRice.py +163 -78
- hestia_earth/models/ipcc2019/co2ToAirAboveGroundBiomassStockChange.py +1 -0
- hestia_earth/models/ipcc2019/co2ToAirBelowGroundBiomassStockChange.py +2 -1
- hestia_earth/models/ipcc2019/co2ToAirCarbonStockChange_utils.py +31 -20
- hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChange.py +2 -1
- hestia_earth/models/ipcc2019/co2ToAirUreaHydrolysis.py +16 -9
- hestia_earth/models/ipcc2019/nonCo2EmissionsToAirNaturalVegetationBurning.py +36 -47
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1.py +94 -9
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2.py +167 -13
- hestia_earth/models/ipcc2019/organicCarbonPerHa_utils.py +7 -5
- hestia_earth/models/koble2014/cropResidueManagement.py +1 -1
- hestia_earth/models/koble2014/residueBurnt.py +1 -1
- hestia_earth/models/koble2014/residueRemoved.py +1 -1
- hestia_earth/models/koble2014/utils.py +3 -3
- hestia_earth/models/mocking/search-results.json +1263 -1229
- hestia_earth/models/pooreNemecek2018/excretaKgN.py +1 -1
- hestia_earth/models/pooreNemecek2018/freshwaterWithdrawalsDuringCycle.py +1 -1
- hestia_earth/models/pooreNemecek2018/utils.py +12 -3
- hestia_earth/models/schmidt2007/ch4ToAirWasteTreatment.py +1 -6
- hestia_earth/models/schmidt2007/h2SToAirWasteTreatment.py +1 -6
- hestia_earth/models/schmidt2007/n2OToAirWasteTreatmentDirect.py +1 -6
- hestia_earth/models/schmidt2007/nh3ToAirWasteTreatment.py +1 -6
- hestia_earth/models/site/pre_checks/country.py +4 -2
- hestia_earth/models/transformation/input/excreta.py +1 -1
- hestia_earth/models/utils/aggregated.py +12 -15
- hestia_earth/models/utils/background_emissions.py +52 -0
- hestia_earth/models/utils/blank_node.py +24 -6
- 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 +1 -1
- hestia_earth/models/utils/source.py +2 -1
- hestia_earth/models/utils/term.py +26 -1
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.69.1.dist-info → hestia_earth_models-0.70.1.dist-info}/METADATA +2 -2
- {hestia_earth_models-0.69.1.dist-info → hestia_earth_models-0.70.1.dist-info}/RECORD +214 -209
- tests/models/aware/test_scarcityWeightedWaterUse.py +1 -12
- tests/models/cycle/input/test_hestiaAggregatedData.py +18 -16
- 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/geospatialDatabase/test_region.py +1 -1
- tests/models/geospatialDatabase/test_utils.py +1 -1
- tests/models/haversineFormula/transport/test_distance.py +2 -2
- tests/models/{koble2014 → hestia}/test_aboveGroundCropResidue.py +3 -3
- 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_ch4ToAirEntericFermentation.py +11 -0
- tests/models/ipcc2019/test_ch4ToAirFloodedRice.py +10 -42
- tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChange.py +2 -1
- tests/models/ipcc2019/test_nonCo2EmissionsToAirNaturalVegetationBurning.py +22 -8
- tests/models/ipcc2019/test_organicCarbonPerHa.py +4 -2
- tests/models/pooreNemecek2018/test_landOccupationDuringCycle.py +3 -0
- tests/models/site/pre_checks/test_country.py +4 -3
- tests/models/test_ecoinventV3.py +2 -2
- tests/models/test_ecoinventV3AndEmberClimate.py +2 -2
- tests/models/test_emissionNotRelevant.py +0 -8
- tests/models/utils/test_measurement.py +1 -1
- tests/models/utils/test_source.py +15 -5
- tests/orchestrator/test_models.py +1 -0
- 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.69.1.dist-info → hestia_earth_models-0.70.1.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.69.1.dist-info → hestia_earth_models-0.70.1.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.69.1.dist-info → hestia_earth_models-0.70.1.dist-info}/top_level.txt +0 -0
@@ -25,7 +25,7 @@ SITE_TYPE_TO_DEPTH = {
|
|
25
25
|
|
26
26
|
|
27
27
|
def _measurement(site: dict, value: float):
|
28
|
-
data = _new_measurement(TERM_ID)
|
28
|
+
data = _new_measurement(TERM_ID, MODEL)
|
29
29
|
data['value'] = [value]
|
30
30
|
data['methodClassification'] = MeasurementMethodClassification.MODELLED_USING_OTHER_MEASUREMENTS.value
|
31
31
|
return data | get_source(site, BIBLIO_TITLE)
|
@@ -0,0 +1,78 @@
|
|
1
|
+
from hestia_earth.schema import MeasurementMethodClassification, TermTermType
|
2
|
+
from hestia_earth.utils.model import filter_list_term_type
|
3
|
+
from hestia_earth.utils.tools import safe_parse_float
|
4
|
+
|
5
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, log_as_table
|
6
|
+
from hestia_earth.models.utils.measurement import _new_measurement
|
7
|
+
from hestia_earth.models.utils.site import related_cycles
|
8
|
+
from hestia_earth.models.utils.blank_node import get_lookup_value
|
9
|
+
from . import MODEL
|
10
|
+
|
11
|
+
REQUIREMENTS = {
|
12
|
+
"Site": {
|
13
|
+
"related": {
|
14
|
+
"Cycle": {
|
15
|
+
"@type": "Cycle",
|
16
|
+
"products": [{
|
17
|
+
"@type": "Product",
|
18
|
+
"primary": "True",
|
19
|
+
"term.termType": "liveAquaticSpecies"
|
20
|
+
}]
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
25
|
+
RETURNS = {
|
26
|
+
"Measurement": [{
|
27
|
+
"value": "",
|
28
|
+
"startDate": "",
|
29
|
+
"endDate": "",
|
30
|
+
"methodClassification": "expert opinion"
|
31
|
+
}]
|
32
|
+
}
|
33
|
+
LOOKUPS = {
|
34
|
+
"liveAquaticSpecies": "defaultSalinity"
|
35
|
+
}
|
36
|
+
TERM_ID = 'waterSalinity'
|
37
|
+
|
38
|
+
|
39
|
+
def _measurement(value: float, start_date: str = None, end_date: str = None):
|
40
|
+
data = _new_measurement(TERM_ID, MODEL)
|
41
|
+
data['value'] = [value]
|
42
|
+
data['endDate'] = end_date
|
43
|
+
if start_date:
|
44
|
+
data['startDate'] = start_date
|
45
|
+
data['methodClassification'] = MeasurementMethodClassification.EXPERT_OPINION.value
|
46
|
+
return data
|
47
|
+
|
48
|
+
|
49
|
+
def _should_run(site: dict):
|
50
|
+
cycles = related_cycles(site)
|
51
|
+
relevant_products = [
|
52
|
+
{
|
53
|
+
'product-id': product.get('term', {}).get('@id'),
|
54
|
+
'lookup-value': safe_parse_float(
|
55
|
+
get_lookup_value(product.get('term', {}), LOOKUPS['liveAquaticSpecies']), default=None
|
56
|
+
),
|
57
|
+
'start-date': product.get('startDate') or cycle.get('startDate'),
|
58
|
+
'end-date': product.get('endDate') or cycle.get('endDate')
|
59
|
+
}
|
60
|
+
for cycle in cycles
|
61
|
+
for product in filter_list_term_type(cycle.get('products', []), TermTermType.LIVEAQUATICSPECIES)
|
62
|
+
]
|
63
|
+
has_valid_products = any([product.get('lookup-value') for product in relevant_products])
|
64
|
+
|
65
|
+
logRequirements(site, model=MODEL, term=TERM_ID,
|
66
|
+
live_aquatic_products=log_as_table(relevant_products))
|
67
|
+
|
68
|
+
should_run = all([has_valid_products])
|
69
|
+
logShouldRun(site, MODEL, TERM_ID, should_run)
|
70
|
+
return should_run, relevant_products
|
71
|
+
|
72
|
+
|
73
|
+
def run(site: dict):
|
74
|
+
should_run, values = _should_run(site)
|
75
|
+
return [
|
76
|
+
_measurement(value.get('lookup-value'), value.get('start-date'), value.get('end-date'))
|
77
|
+
for value in values if value.get('lookup-value') is not None
|
78
|
+
] if should_run else []
|
@@ -4,7 +4,7 @@ from .. import MODEL
|
|
4
4
|
|
5
5
|
REQUIREMENTS = {
|
6
6
|
"ImpactAssessment": {
|
7
|
-
"product": {"@type": "Product", "
|
7
|
+
"product": {"@type": "Product", "none": {"economicValueShare": ""}},
|
8
8
|
"cycle": {
|
9
9
|
"@type": "Cycle",
|
10
10
|
"products": [{"@type": "Product", "economicValueShare": ""}]
|
@@ -4,7 +4,7 @@ from .. import MODEL
|
|
4
4
|
|
5
5
|
REQUIREMENTS = {
|
6
6
|
"ImpactAssessment": {
|
7
|
-
"product": {"@type": "Product", "
|
7
|
+
"product": {"@type": "Product", "none": {"value": ""}},
|
8
8
|
"cycle": {
|
9
9
|
"@type": "Cycle",
|
10
10
|
"products": [{"@type": "Product", "value": ""}]
|
@@ -568,7 +568,7 @@ def _measurement(
|
|
568
568
|
"dates": [f"{year}-12-31" for year in timestamps],
|
569
569
|
"methodClassification": _METHOD_CLASSIFICATION
|
570
570
|
}
|
571
|
-
measurement = _new_measurement(TERM_ID) | {
|
571
|
+
measurement = _new_measurement(TERM_ID, MODEL) | {
|
572
572
|
key: value for key, value in update_dict.items() if value
|
573
573
|
}
|
574
574
|
return measurement
|
@@ -551,7 +551,7 @@ def _measurement(
|
|
551
551
|
"dates": [f"{year}-12-31" for year in timestamps],
|
552
552
|
"methodClassification": _METHOD_CLASSIFICATION
|
553
553
|
}
|
554
|
-
measurement = _new_measurement(TERM_ID) | {
|
554
|
+
measurement = _new_measurement(TERM_ID, MODEL) | {
|
555
555
|
key: value for key, value in update_dict.items() if value
|
556
556
|
}
|
557
557
|
measurement["depthUpper"] = _DEPTH_UPPER
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from enum import Enum
|
2
2
|
from functools import reduce
|
3
3
|
from math import isclose
|
4
|
-
from numpy import random
|
4
|
+
from numpy import inf, random
|
5
5
|
from numpy.typing import NDArray
|
6
6
|
from typing import Callable, Optional, Union
|
7
7
|
|
@@ -393,9 +393,7 @@ def sample_plus_minus_error(
|
|
393
393
|
) -> NDArray:
|
394
394
|
"""Randomly sample a model parameter with a truncated normal distribution described using plus/minus error."""
|
395
395
|
sd = value * (error / 200)
|
396
|
-
|
397
|
-
high = value + (value * (error / 100))
|
398
|
-
return truncated_normal_1d(shape=(1, iterations), mu=value, sigma=sd, low=low, high=high, seed=seed)
|
396
|
+
return truncated_normal_1d(shape=(1, iterations), mu=value, sigma=sd, low=0, high=inf, seed=seed)
|
399
397
|
|
400
398
|
|
401
399
|
def sample_constant(*, iterations: int, value: float, **_) -> NDArray:
|
@@ -5,6 +5,7 @@ from hestia_earth.utils.tools import list_sum, safe_parse_float
|
|
5
5
|
|
6
6
|
from hestia_earth.models.log import debugMissingLookup, debugValues, logRequirements, logShouldRun, log_as_table
|
7
7
|
from hestia_earth.models.utils.blank_node import get_total_value_converted_with_min_ratio
|
8
|
+
from hestia_earth.models.utils.term import get_ionophore_terms
|
8
9
|
from hestia_earth.models.utils.input import get_feed_inputs
|
9
10
|
from hestia_earth.models.utils.emission import _new_emission
|
10
11
|
from hestia_earth.models.utils.liveAnimal import get_default_digestibility
|
@@ -48,7 +49,10 @@ REQUIREMENTS = {
|
|
48
49
|
}
|
49
50
|
}]
|
50
51
|
}
|
51
|
-
]
|
52
|
+
],
|
53
|
+
"optional": {
|
54
|
+
"inputs": [{"@type": "Input", "term.@id": ["ionophores", "ionophoreAntibiotics"]}]
|
55
|
+
}
|
52
56
|
}
|
53
57
|
}
|
54
58
|
LOOKUPS = {
|
@@ -192,7 +196,8 @@ def _get_DE_type(lookup, term_id: str, term_type: str):
|
|
192
196
|
|
193
197
|
def _is_ionophore(cycle: dict, total_feed: float):
|
194
198
|
inputs = cycle.get('inputs', [])
|
195
|
-
|
199
|
+
ionophore_terms = get_ionophore_terms()
|
200
|
+
has_input = any([find_term_match(inputs, term_id, None) is not None for term_id in ionophore_terms])
|
196
201
|
maize_input = find_term_match(inputs, 'maizeSteamFlaked')
|
197
202
|
maize_feed = get_total_value_converted_with_min_ratio(MODEL, None, blank_nodes=[maize_input]) if maize_input else 0
|
198
203
|
maize_feed_ratio = maize_feed / total_feed if all([maize_feed, total_feed]) else 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 []
|
@@ -11,6 +11,7 @@ from . import MODEL
|
|
11
11
|
REQUIREMENTS = {
|
12
12
|
"Cycle": {
|
13
13
|
"site": {
|
14
|
+
"@type": "Site",
|
14
15
|
"measurements": [
|
15
16
|
{
|
16
17
|
"@type": "Measurement",
|
@@ -38,7 +39,7 @@ RETURNS = {
|
|
38
39
|
"statsDefinition": "simulated",
|
39
40
|
"observations": "",
|
40
41
|
"methodTier": "",
|
41
|
-
"depth":
|
42
|
+
"depth": 30
|
42
43
|
}]
|
43
44
|
}
|
44
45
|
TERM_ID = 'co2ToAirBelowGroundBiomassStockChangeLandUseChange,co2ToAirBelowGroundBiomassStockChangeManagementChange'
|
@@ -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
|