hestia-earth-models 0.57.2__py3-none-any.whl → 0.59.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of hestia-earth-models might be problematic. Click here for more details.
- hestia_earth/models/cycle/aboveGroundCropResidueTotal.py +17 -12
- hestia_earth/models/cycle/excretaKgMass.py +4 -5
- hestia_earth/models/cycle/excretaKgN.py +4 -5
- hestia_earth/models/cycle/excretaKgVs.py +4 -5
- hestia_earth/models/cycle/inorganicFertiliser.py +2 -2
- hestia_earth/models/cycle/{irrigated.py → irrigatedTypeUnspecified.py} +4 -4
- hestia_earth/models/cycle/liveAnimal.py +9 -11
- hestia_earth/models/cycle/milkYield.py +154 -0
- hestia_earth/models/cycle/residueIncorporated.py +1 -1
- hestia_earth/models/cycle/utils.py +6 -0
- hestia_earth/models/emepEea2019/nh3ToAirInorganicFertiliser.py +3 -3
- hestia_earth/models/faostat2018/seed.py +2 -3
- hestia_earth/models/geospatialDatabase/clayContent.py +17 -4
- hestia_earth/models/geospatialDatabase/sandContent.py +17 -4
- hestia_earth/models/geospatialDatabase/siltContent.py +2 -2
- hestia_earth/models/impact_assessment/irrigated.py +0 -3
- hestia_earth/models/ipcc2006/co2ToAirOrganicSoilCultivation.py +2 -2
- hestia_earth/models/ipcc2006/n2OToAirCropResidueDecompositionIndirect.py +2 -2
- hestia_earth/models/ipcc2006/n2OToAirExcretaDirect.py +1 -1
- hestia_earth/models/ipcc2006/n2OToAirExcretaIndirect.py +8 -4
- hestia_earth/models/ipcc2006/n2OToAirInorganicFertiliserDirect.py +4 -1
- hestia_earth/models/ipcc2006/n2OToAirInorganicFertiliserIndirect.py +1 -1
- hestia_earth/models/ipcc2006/n2OToAirOrganicFertiliserDirect.py +1 -1
- hestia_earth/models/ipcc2006/n2OToAirOrganicFertiliserIndirect.py +1 -1
- hestia_earth/models/ipcc2006/utils.py +11 -8
- hestia_earth/models/ipcc2019/ch4ToAirEntericFermentation.py +4 -4
- hestia_earth/models/ipcc2019/ch4ToAirFloodedRice.py +16 -7
- hestia_earth/models/ipcc2019/co2ToAirSoilCarbonStockChangeManagementChange.py +759 -0
- hestia_earth/models/ipcc2019/croppingDuration.py +12 -6
- hestia_earth/models/ipcc2019/n2OToAirCropResidueDecompositionDirect.py +5 -52
- hestia_earth/models/ipcc2019/n2OToAirInorganicFertiliserDirect.py +104 -0
- hestia_earth/models/ipcc2019/n2OToAirInorganicFertiliserIndirect.py +1 -1
- hestia_earth/models/ipcc2019/n2OToAirOrganicFertiliserDirect.py +105 -0
- hestia_earth/models/ipcc2019/n2OToAirOrganicFertiliserIndirect.py +1 -1
- hestia_earth/models/ipcc2019/no3ToGroundwaterCropResidueDecomposition.py +1 -1
- hestia_earth/models/ipcc2019/no3ToGroundwaterExcreta.py +1 -1
- hestia_earth/models/ipcc2019/no3ToGroundwaterInorganicFertiliser.py +1 -1
- hestia_earth/models/ipcc2019/no3ToGroundwaterOrganicFertiliser.py +1 -1
- hestia_earth/models/ipcc2019/organicCarbonPerHa.py +1088 -1268
- hestia_earth/models/ipcc2019/pastureGrass.py +4 -4
- hestia_earth/models/ipcc2019/utils.py +102 -1
- hestia_earth/models/koble2014/aboveGroundCropResidue.py +15 -17
- hestia_earth/models/koble2014/cropResidueManagement.py +2 -2
- hestia_earth/models/koble2014/utils.py +19 -3
- hestia_earth/models/linkedImpactAssessment/__init__.py +4 -2
- hestia_earth/models/log.py +15 -3
- hestia_earth/models/mocking/search-results.json +184 -118
- hestia_earth/models/pooreNemecek2018/excretaKgN.py +6 -7
- hestia_earth/models/pooreNemecek2018/excretaKgVs.py +7 -6
- hestia_earth/models/pooreNemecek2018/no3ToGroundwaterCropResidueDecomposition.py +3 -2
- hestia_earth/models/pooreNemecek2018/no3ToGroundwaterExcreta.py +3 -2
- hestia_earth/models/pooreNemecek2018/no3ToGroundwaterInorganicFertiliser.py +3 -2
- hestia_earth/models/pooreNemecek2018/saplings.py +0 -1
- hestia_earth/models/site/management.py +168 -0
- hestia_earth/models/site/organicCarbonPerHa.py +251 -89
- hestia_earth/models/stehfestBouwman2006/n2OToAirCropResidueDecompositionDirect.py +3 -2
- hestia_earth/models/stehfestBouwman2006/n2OToAirExcretaDirect.py +3 -2
- hestia_earth/models/stehfestBouwman2006/n2OToAirInorganicFertiliserDirect.py +3 -2
- hestia_earth/models/stehfestBouwman2006/n2OToAirOrganicFertiliserDirect.py +3 -2
- hestia_earth/models/stehfestBouwman2006/noxToAirCropResidueDecomposition.py +3 -2
- hestia_earth/models/stehfestBouwman2006/noxToAirExcreta.py +3 -2
- hestia_earth/models/stehfestBouwman2006/noxToAirInorganicFertiliser.py +3 -2
- hestia_earth/models/stehfestBouwman2006/noxToAirOrganicFertiliser.py +3 -2
- hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirCropResidueDecomposition.py +3 -2
- hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirExcreta.py +3 -2
- hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirInorganicFertiliser.py +3 -2
- hestia_earth/models/stehfestBouwman2006GisImplementation/noxToAirOrganicFertiliser.py +3 -2
- hestia_earth/models/utils/aggregated.py +1 -0
- hestia_earth/models/utils/blank_node.py +394 -72
- hestia_earth/models/utils/cropResidue.py +13 -0
- hestia_earth/models/utils/cycle.py +18 -9
- hestia_earth/models/utils/measurement.py +1 -1
- hestia_earth/models/utils/property.py +4 -4
- hestia_earth/models/utils/term.py +48 -3
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.57.2.dist-info → hestia_earth_models-0.59.0.dist-info}/METADATA +5 -9
- {hestia_earth_models-0.57.2.dist-info → hestia_earth_models-0.59.0.dist-info}/RECORD +109 -97
- {hestia_earth_models-0.57.2.dist-info → hestia_earth_models-0.59.0.dist-info}/WHEEL +1 -1
- tests/models/cycle/animal/input/test_hestiaAggregatedData.py +2 -14
- tests/models/cycle/input/test_hestiaAggregatedData.py +4 -16
- tests/models/cycle/test_coldCarcassWeightPerHead.py +1 -1
- tests/models/cycle/test_coldDressedCarcassWeightPerHead.py +1 -1
- tests/models/cycle/{test_irrigated.py → test_irrigatedTypeUnspecified.py} +1 -1
- tests/models/cycle/test_milkYield.py +58 -0
- tests/models/cycle/test_readyToCookWeightPerHead.py +1 -1
- tests/models/emepEea2019/test_nh3ToAirInorganicFertiliser.py +1 -1
- tests/models/geospatialDatabase/test_clayContent.py +9 -3
- tests/models/geospatialDatabase/test_sandContent.py +9 -3
- tests/models/ipcc2006/test_n2OToAirExcretaDirect.py +7 -2
- tests/models/ipcc2006/test_n2OToAirExcretaIndirect.py +1 -1
- tests/models/ipcc2006/test_n2OToAirInorganicFertiliserDirect.py +7 -2
- tests/models/ipcc2006/test_n2OToAirInorganicFertiliserIndirect.py +7 -2
- tests/models/ipcc2006/test_n2OToAirOrganicFertiliserDirect.py +7 -2
- tests/models/ipcc2006/test_n2OToAirOrganicFertiliserIndirect.py +7 -2
- tests/models/ipcc2019/test_ch4ToAirEntericFermentation.py +1 -1
- tests/models/ipcc2019/test_co2ToAirSoilCarbonStockChangeManagementChange.py +228 -0
- tests/models/ipcc2019/test_n2OToAirInorganicFertiliserDirect.py +74 -0
- tests/models/ipcc2019/test_n2OToAirOrganicFertiliserDirect.py +74 -0
- tests/models/ipcc2019/test_organicCarbonPerHa.py +303 -1044
- tests/models/koble2014/test_residueBurnt.py +1 -2
- tests/models/koble2014/test_residueLeftOnField.py +1 -2
- tests/models/koble2014/test_residueRemoved.py +1 -2
- tests/models/koble2014/test_utils.py +52 -0
- tests/models/site/test_management.py +117 -0
- tests/models/site/test_organicCarbonPerHa.py +51 -5
- tests/models/utils/test_blank_node.py +230 -34
- tests/models/utils/test_term.py +17 -3
- {hestia_earth_models-0.57.2.dist-info → hestia_earth_models-0.59.0.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.57.2.dist-info → hestia_earth_models-0.59.0.dist-info}/top_level.txt +0 -0
|
@@ -4,13 +4,8 @@ changes. This model combines the Tier 1 & Tier 2 methodologies. It first tries t
|
|
|
4
4
|
remaining croplands). If Tier 2 cannot run, it will try to run Tier 1 (for croplands remaining croplands and for
|
|
5
5
|
grasslands remaining grasslands). Source:
|
|
6
6
|
[IPCC 2019, Vol. 4, Chapter 10](https://www.ipcc-nggip.iges.or.jp/public/2019rf/pdf/4_Volume4/19R_V4_Ch05_Cropland.pdf).
|
|
7
|
-
|
|
8
|
-
Currently, the Tier 2 implementation does not take into account the irrigation of cycles when estimating soil organic
|
|
9
|
-
carbon stock changes.
|
|
10
7
|
"""
|
|
11
|
-
from collections.abc import Iterable
|
|
12
8
|
from enum import Enum
|
|
13
|
-
from functools import reduce
|
|
14
9
|
from numpy import exp
|
|
15
10
|
from pydash.objects import merge
|
|
16
11
|
from statistics import mean
|
|
@@ -21,21 +16,23 @@ from typing import (
|
|
|
21
16
|
Union
|
|
22
17
|
)
|
|
23
18
|
from hestia_earth.schema import (
|
|
19
|
+
CycleFunctionalUnit,
|
|
24
20
|
MeasurementMethodClassification,
|
|
25
21
|
SiteSiteType,
|
|
26
|
-
TermTermType
|
|
22
|
+
TermTermType,
|
|
27
23
|
)
|
|
28
|
-
from hestia_earth.utils.date import diff_in_years
|
|
29
24
|
from hestia_earth.utils.model import find_term_match, filter_list_term_type
|
|
30
|
-
from hestia_earth.utils.tools import flatten, list_sum, non_empty_list
|
|
25
|
+
from hestia_earth.utils.tools import flatten, list_sum, non_empty_list
|
|
31
26
|
|
|
32
|
-
from hestia_earth.models.log import
|
|
27
|
+
from hestia_earth.models.log import log_as_table, logRequirements, logShouldRun
|
|
33
28
|
from hestia_earth.models.utils.blank_node import (
|
|
34
29
|
cumulative_nodes_match,
|
|
35
30
|
cumulative_nodes_lookup_match,
|
|
36
31
|
cumulative_nodes_term_match,
|
|
37
32
|
get_node_value,
|
|
33
|
+
group_nodes_by_year_and_month,
|
|
38
34
|
group_nodes_by_year,
|
|
35
|
+
GroupNodesByYearMode,
|
|
39
36
|
node_lookup_match,
|
|
40
37
|
node_term_match
|
|
41
38
|
)
|
|
@@ -43,8 +40,6 @@ from hestia_earth.models.utils.cycle import check_cycle_site_ids_identical
|
|
|
43
40
|
from hestia_earth.models.utils.ecoClimateZone import get_ecoClimateZone_lookup_value
|
|
44
41
|
from hestia_earth.models.utils.measurement import (
|
|
45
42
|
_new_measurement,
|
|
46
|
-
group_measurement_values_by_year,
|
|
47
|
-
most_relevant_measurement_value_by_depth_and_date
|
|
48
43
|
)
|
|
49
44
|
from hestia_earth.models.utils.property import get_node_property
|
|
50
45
|
from hestia_earth.models.utils.site import related_cycles
|
|
@@ -52,10 +47,13 @@ from hestia_earth.models.utils.term import (
|
|
|
52
47
|
get_cover_crop_property_terms,
|
|
53
48
|
get_crop_residue_incorporated_or_left_on_field_terms,
|
|
54
49
|
get_irrigated_terms,
|
|
50
|
+
get_long_fallow_land_cover_terms,
|
|
55
51
|
get_residue_removed_or_burnt_terms,
|
|
56
|
-
|
|
52
|
+
get_upland_rice_crop_terms,
|
|
53
|
+
get_upland_rice_land_cover_terms
|
|
57
54
|
)
|
|
58
55
|
|
|
56
|
+
from .utils import check_consecutive
|
|
59
57
|
from . import MODEL
|
|
60
58
|
|
|
61
59
|
REQUIREMENTS = {
|
|
@@ -115,6 +113,7 @@ REQUIREMENTS = {
|
|
|
115
113
|
}
|
|
116
114
|
}
|
|
117
115
|
LOOKUPS = {
|
|
116
|
+
"crop": "IPCC_LAND_USE_CATEGORY",
|
|
118
117
|
"ecoClimateZone": [
|
|
119
118
|
"IPCC_2019_SOC_REF_KG_C_HECTARE_SAN",
|
|
120
119
|
"IPCC_2019_SOC_REF_KG_C_HECTARE_WET",
|
|
@@ -168,6 +167,7 @@ TERM_ID = 'organicCarbonPerHa'
|
|
|
168
167
|
|
|
169
168
|
MIN_AREA_THRESHOLD = 30 # 30% as per IPCC guidelines
|
|
170
169
|
SUPER_MAJORITY_AREA_THRESHOLD = 100 - MIN_AREA_THRESHOLD
|
|
170
|
+
MIN_YIELD_THRESHOLD = 1
|
|
171
171
|
DEPTH_UPPER = 0
|
|
172
172
|
DEPTH_LOWER = 30
|
|
173
173
|
|
|
@@ -221,6 +221,14 @@ DEFAULT_PARAMS = {
|
|
|
221
221
|
"default_lignin_content": 0.073
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
+
VALID_SITE_TYPES_TIER_2 = [
|
|
225
|
+
SiteSiteType.CROPLAND.value
|
|
226
|
+
]
|
|
227
|
+
|
|
228
|
+
VALID_FUNCTIONAL_UNITS_TIER_2 = [
|
|
229
|
+
CycleFunctionalUnit._1_HA.value
|
|
230
|
+
]
|
|
231
|
+
|
|
224
232
|
# --- TIER 1 CONSTANTS ---
|
|
225
233
|
|
|
226
234
|
CLAY_CONTENT_TERM_ID = "clayContent"
|
|
@@ -231,8 +239,6 @@ ANIMAL_MANURE_USED_TERM_ID = "animalManureUsed"
|
|
|
231
239
|
INORGANIC_NITROGEN_FERTILISER_USED_TERM_ID = "inorganicNitrogenFertiliserUsed"
|
|
232
240
|
ORGANIC_FERTILISER_USED_TERM_ID = "organicFertiliserOrSoilCarbonIncreasingAmendmentUsed"
|
|
233
241
|
|
|
234
|
-
DEFAULT_NODE_VALUE = 100
|
|
235
|
-
|
|
236
242
|
CLAY_CONTENT_MAX = 8
|
|
237
243
|
SAND_CONTENT_MIN = 70
|
|
238
244
|
|
|
@@ -242,6 +248,13 @@ The number of years required for soil organic carbon to reach equilibrium after
|
|
|
242
248
|
a change in land use, management regime or carbon input regime.
|
|
243
249
|
"""
|
|
244
250
|
|
|
251
|
+
VALID_SITE_TYPES_TIER_1 = [
|
|
252
|
+
SiteSiteType.CROPLAND.value,
|
|
253
|
+
SiteSiteType.FOREST.value,
|
|
254
|
+
SiteSiteType.OTHER_NATURAL_VEGETATION.value,
|
|
255
|
+
SiteSiteType.PERMANENT_PASTURE.value,
|
|
256
|
+
]
|
|
257
|
+
|
|
245
258
|
# --- SHARED TIER 1 & TIER 2 FORMAT MEASUREMENT OUTPUT ---
|
|
246
259
|
|
|
247
260
|
|
|
@@ -274,112 +287,115 @@ def _measurement(year: int, value: float, method_classification: str) -> dict:
|
|
|
274
287
|
|
|
275
288
|
# --- SHARED TIER 1 & TIER 2 ENUMS ---
|
|
276
289
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
"
|
|
284
|
-
"
|
|
285
|
-
"
|
|
286
|
-
"
|
|
287
|
-
|
|
288
|
-
""
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
290
|
+
class IpccManagementCategory(Enum):
|
|
291
|
+
"""
|
|
292
|
+
Enum representing IPCC Management Categories for grasslands and annual croplands.
|
|
293
|
+
|
|
294
|
+
See [IPCC (2019) Vol. 4, Ch. 5 and 6](https://www.ipcc-nggip.iges.or.jp/public/2019rf/vol4.html) for more
|
|
295
|
+
information.
|
|
296
|
+
"""
|
|
297
|
+
SEVERELY_DEGRADED = "severely degraded"
|
|
298
|
+
IMPROVED_GRASSLAND = "improved grassland"
|
|
299
|
+
HIGH_INTENSITY_GRAZING = "high-intensity grazing"
|
|
300
|
+
NOMINALLY_MANAGED = "nominally managed"
|
|
301
|
+
FULL_TILLAGE = "full tillage"
|
|
302
|
+
REDUCED_TILLAGE = "reduced tillage"
|
|
303
|
+
NO_TILLAGE = "no tillage"
|
|
304
|
+
OTHER = "other"
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class _InventoryKey(Enum):
|
|
308
|
+
"""
|
|
309
|
+
Enum representing the inner keys of the annual inventory is constructed from site and cycle data.
|
|
310
|
+
"""
|
|
311
|
+
# Tier 1
|
|
312
|
+
LU_CATEGORY = 'ipcc land use category'
|
|
313
|
+
MG_CATEGORY = 'ipcc management category'
|
|
314
|
+
CI_CATEGORY = 'ipcc carbon input category'
|
|
315
|
+
SHOULD_RUN_TIER_1 = 'should run tier 1'
|
|
316
|
+
# Tier 2
|
|
317
|
+
TEMP_MONTHLY = 'temperature monthly'
|
|
318
|
+
PRECIP_MONTHLY = 'precipitation monthly'
|
|
319
|
+
PET_MONTHLY = 'PET monthly'
|
|
320
|
+
IRRIGATED_MONTHLY = 'irrigated monthly'
|
|
321
|
+
CARBON_INPUT = 'carbon input'
|
|
322
|
+
N_CONTENT = 'nitrogen content'
|
|
323
|
+
LIGNIN_CONTENT = 'lignin content'
|
|
324
|
+
TILLAGE_CATEGORY = 'ipcc tillage category'
|
|
325
|
+
SAND_CONTENT = 'sand content'
|
|
326
|
+
IS_PADDY_RICE = 'is paddy rice'
|
|
327
|
+
SHOULD_RUN_TIER_2 = 'should run tier 2'
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
REQUIRED_KEYS_TIER_1 = [
|
|
331
|
+
_InventoryKey.LU_CATEGORY,
|
|
332
|
+
_InventoryKey.MG_CATEGORY,
|
|
333
|
+
_InventoryKey.CI_CATEGORY
|
|
319
334
|
]
|
|
320
335
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
336
|
+
|
|
337
|
+
REQUIRED_KEYS_TIER_2 = [
|
|
338
|
+
_InventoryKey.TEMP_MONTHLY,
|
|
339
|
+
_InventoryKey.PRECIP_MONTHLY,
|
|
340
|
+
_InventoryKey.PET_MONTHLY,
|
|
341
|
+
_InventoryKey.CARBON_INPUT,
|
|
342
|
+
_InventoryKey.N_CONTENT,
|
|
343
|
+
_InventoryKey.LIGNIN_CONTENT,
|
|
344
|
+
_InventoryKey.TILLAGE_CATEGORY,
|
|
345
|
+
_InventoryKey.IS_PADDY_RICE
|
|
327
346
|
]
|
|
328
347
|
|
|
329
348
|
|
|
330
349
|
# --- TIER 1 ENUMS ---
|
|
331
350
|
|
|
332
351
|
|
|
333
|
-
IpccSoilCategory
|
|
334
|
-
"
|
|
335
|
-
|
|
336
|
-
"WETLAND_SOILS",
|
|
337
|
-
"VOLCANIC_SOILS",
|
|
338
|
-
"SPODIC_SOILS",
|
|
339
|
-
"HIGH_ACTIVITY_CLAY_SOILS",
|
|
340
|
-
"LOW_ACTIVITY_CLAY_SOILS",
|
|
341
|
-
])
|
|
342
|
-
"""
|
|
343
|
-
Enum representing IPCC Soil Categories.
|
|
352
|
+
class IpccSoilCategory(Enum):
|
|
353
|
+
"""
|
|
354
|
+
Enum representing IPCC Soil Categories.
|
|
344
355
|
|
|
345
|
-
See [IPCC (2019) Vol 4, Ch. 2 and 3](https://www.ipcc-nggip.iges.or.jp/public/2019rf/vol4.html) for more
|
|
346
|
-
|
|
356
|
+
See [IPCC (2019) Vol 4, Ch. 2 and 3](https://www.ipcc-nggip.iges.or.jp/public/2019rf/vol4.html) for more
|
|
357
|
+
information.
|
|
358
|
+
"""
|
|
359
|
+
ORGANIC_SOILS = "organic soils"
|
|
360
|
+
SANDY_SOILS = "sandy soils"
|
|
361
|
+
WETLAND_SOILS = "wetland soils"
|
|
362
|
+
VOLCANIC_SOILS = "volcanic soils"
|
|
363
|
+
SPODIC_SOILS = "spodic soils"
|
|
364
|
+
HIGH_ACTIVITY_CLAY_SOILS = "high-activity clay soils"
|
|
365
|
+
LOW_ACTIVITY_CLAY_SOILS = "low-activity clay soils"
|
|
347
366
|
|
|
348
367
|
|
|
349
|
-
IpccLandUseCategory
|
|
350
|
-
"
|
|
351
|
-
|
|
352
|
-
"PADDY_RICE_CULTIVATION",
|
|
353
|
-
"ANNUAL_CROPS_WET",
|
|
354
|
-
"ANNUAL_CROPS",
|
|
355
|
-
"SET_ASIDE",
|
|
356
|
-
"FOREST",
|
|
357
|
-
"NATIVE",
|
|
358
|
-
"OTHER"
|
|
359
|
-
])
|
|
360
|
-
"""
|
|
361
|
-
Enum representing IPCC Land Use Categories.
|
|
368
|
+
class IpccLandUseCategory(Enum):
|
|
369
|
+
"""
|
|
370
|
+
Enum representing IPCC Land Use Categories.
|
|
362
371
|
|
|
363
|
-
See [IPCC (2019) Vol 4](https://www.ipcc-nggip.iges.or.jp/public/2019rf/vol4.html) for more information.
|
|
364
|
-
"""
|
|
372
|
+
See [IPCC (2019) Vol 4](https://www.ipcc-nggip.iges.or.jp/public/2019rf/vol4.html) for more information.
|
|
373
|
+
"""
|
|
374
|
+
GRASSLAND = "grassland"
|
|
375
|
+
PERENNIAL_CROPS = "perennial crops"
|
|
376
|
+
PADDY_RICE_CULTIVATION = "paddy rice cultivation"
|
|
377
|
+
ANNUAL_CROPS_WET = "annual crops (wet)"
|
|
378
|
+
ANNUAL_CROPS = "annual crops"
|
|
379
|
+
SET_ASIDE = "set aside"
|
|
380
|
+
FOREST = "forest"
|
|
381
|
+
NATIVE = "native"
|
|
382
|
+
OTHER = "other"
|
|
365
383
|
|
|
366
384
|
|
|
367
|
-
IpccCarbonInputCategory
|
|
368
|
-
"
|
|
369
|
-
|
|
370
|
-
"CROPLAND_HIGH_WITH_MANURE",
|
|
371
|
-
"CROPLAND_HIGH_WITHOUT_MANURE",
|
|
372
|
-
"CROPLAND_MEDIUM",
|
|
373
|
-
"CROPLAND_LOW",
|
|
374
|
-
"OTHER"
|
|
375
|
-
])
|
|
376
|
-
"""
|
|
377
|
-
Enum representing IPCC Carbon Input Categories for improved grasslands
|
|
378
|
-
and annual croplands.
|
|
385
|
+
class IpccCarbonInputCategory(Enum):
|
|
386
|
+
"""
|
|
387
|
+
Enum representing IPCC Carbon Input Categories for improved grasslands and annual croplands.
|
|
379
388
|
|
|
380
|
-
See [IPCC (2019) Vol. 4, Ch. 4, 5 and 6](https://www.ipcc-nggip.iges.or.jp/public/2019rf/vol4.html)
|
|
381
|
-
|
|
382
|
-
"""
|
|
389
|
+
See [IPCC (2019) Vol. 4, Ch. 4, 5 and 6](https://www.ipcc-nggip.iges.or.jp/public/2019rf/vol4.html) for more
|
|
390
|
+
information.
|
|
391
|
+
"""
|
|
392
|
+
GRASSLAND_HIGH = "grassland high"
|
|
393
|
+
GRASSLAND_MEDIUM = "grassland medium"
|
|
394
|
+
CROPLAND_HIGH_WITH_MANURE = "cropland high (with manure)"
|
|
395
|
+
CROPLAND_HIGH_WITHOUT_MANURE = "cropland high (without manure)"
|
|
396
|
+
CROPLAND_MEDIUM = "cropland medium"
|
|
397
|
+
CROPLAND_LOW = "cropland low"
|
|
398
|
+
OTHER = "other"
|
|
383
399
|
|
|
384
400
|
|
|
385
401
|
# --- TIER 2 NAMED TUPLES FOR CARBON SOURCES AND MODEL RESULTS ---
|
|
@@ -448,33 +464,6 @@ annual_water_factors : list[float]
|
|
|
448
464
|
"""
|
|
449
465
|
|
|
450
466
|
|
|
451
|
-
CarbonInputResult = NamedTuple(
|
|
452
|
-
"CarbonInputResult",
|
|
453
|
-
[
|
|
454
|
-
("timestamps", list[float]),
|
|
455
|
-
("organic_carbon_inputs", list[float]),
|
|
456
|
-
("average_nitrogen_contents", list[float]),
|
|
457
|
-
("average_lignin_contents", list[float]),
|
|
458
|
-
]
|
|
459
|
-
)
|
|
460
|
-
"""
|
|
461
|
-
A named tuple to hold the result of `_run_annual_organic_carbon_inputs`.
|
|
462
|
-
|
|
463
|
-
Attributes
|
|
464
|
-
----------
|
|
465
|
-
timestamps : list[int]
|
|
466
|
-
A list of integer timestamps (e.g. `[1995, 1996]`) for each year in the inventory.
|
|
467
|
-
organic_carbon_inputs : list[float]
|
|
468
|
-
A list of organic carbon inputs to the soil for each year in the inventory, kg C ha-1.
|
|
469
|
-
average_nitrogen_contents : list[float]
|
|
470
|
-
A list of the average nitrogen contents of the carbon sources for each year in the inventory, decimal_proportion,
|
|
471
|
-
kg N (kg d.m.)-1.
|
|
472
|
-
average_lignin_contents : list[float]
|
|
473
|
-
A list of the average lignin contents of the carbon sources for each year in the inventory, decimal_proportion,
|
|
474
|
-
kg lignin (kg d.m.)-1.
|
|
475
|
-
"""
|
|
476
|
-
|
|
477
|
-
|
|
478
467
|
Tier2SocResult = NamedTuple(
|
|
479
468
|
"Tier2SocResult",
|
|
480
469
|
[
|
|
@@ -522,45 +511,6 @@ carbon_input_factor : float
|
|
|
522
511
|
The stock change factor for mineral soil organic C for the input of organic amendments, dimensionless.
|
|
523
512
|
"""
|
|
524
513
|
|
|
525
|
-
CarbonInputArgs = NamedTuple("CarbonInputArgs", [
|
|
526
|
-
("num_grassland_improvements", int),
|
|
527
|
-
("has_irrigation", bool),
|
|
528
|
-
("has_residue_removed_or_burnt", bool),
|
|
529
|
-
("has_low_residue_producing_crops", bool),
|
|
530
|
-
("has_bare_fallow", bool),
|
|
531
|
-
("has_n_fixing_crop_or_inorganic_n_fertiliser_used", bool),
|
|
532
|
-
("has_practice_increasing_c_input", bool),
|
|
533
|
-
("has_cover_crop", bool),
|
|
534
|
-
("has_organic_fertiliser_or_soil_amendment_used", bool),
|
|
535
|
-
("has_animal_manure_used", bool)
|
|
536
|
-
])
|
|
537
|
-
"""
|
|
538
|
-
Named tuple representing the arguments for the functions assigning `IpccCarbonInputCategories` to inventory years.
|
|
539
|
-
|
|
540
|
-
Attributes
|
|
541
|
-
----------
|
|
542
|
-
num_grassland_improvements : int
|
|
543
|
-
The number of grassland improvements.
|
|
544
|
-
has_irrigation : bool
|
|
545
|
-
Indicates whether irrigation is applied to more than 30% of the site.
|
|
546
|
-
has_residue_removed_or_burnt : bool
|
|
547
|
-
Indicates whether residues are removed or burnt on more than 30% of the site.
|
|
548
|
-
has_low_residue_producing_crops : bool
|
|
549
|
-
Indicates whether low residue-producing crops are present on more than 70% of the site.
|
|
550
|
-
has_bare_fallow : bool
|
|
551
|
-
Indicates whether bare fallow is present on more than 30% of the site.
|
|
552
|
-
has_n_fixing_crop_or_inorganic_n_fertiliser_used : bool
|
|
553
|
-
Indicates whether a nitrogen-fixing crop or inorganic nitrogen fertiliser is used on more than 30% of the site.
|
|
554
|
-
has_practice_increasing_c_input : bool
|
|
555
|
-
Indicates whether practices increasing carbon input are present on more than 30% of the site.
|
|
556
|
-
has_cover_crop : bool
|
|
557
|
-
Indicates whether cover crops are present on more than 30% of the site.
|
|
558
|
-
has_organic_fertiliser_or_soil_amendment_used : bool
|
|
559
|
-
Indicates whether organic fertiliser or soil amendments are used on more than 30% of the site.
|
|
560
|
-
has_animal_manure_used : bool
|
|
561
|
-
Indicates whether animal manure is used on more than 30% of the site.
|
|
562
|
-
"""
|
|
563
|
-
|
|
564
514
|
|
|
565
515
|
# --- SHARED TIER 1 & TIER 2 MAPPING DICTS ---
|
|
566
516
|
|
|
@@ -650,8 +600,8 @@ IPCC_SOIL_CATEGORY_TO_SOIL_TYPE_LOOKUP_VALUE = {
|
|
|
650
600
|
IpccSoilCategory.LOW_ACTIVITY_CLAY_SOILS: "Low-activity clay soils",
|
|
651
601
|
}
|
|
652
602
|
"""
|
|
653
|
-
A dictionary mapping IPCC soil categories to corresponding soil type and USDA soil type lookup values
|
|
654
|
-
|
|
603
|
+
A dictionary mapping IPCC soil categories to corresponding soil type and USDA soil type lookup values in the
|
|
604
|
+
`"IPCC_SOIL_CATEGORY"` column.
|
|
655
605
|
"""
|
|
656
606
|
|
|
657
607
|
IPCC_LAND_USE_CATEGORY_TO_SITE_TYPE = {
|
|
@@ -675,8 +625,8 @@ IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE = {
|
|
|
675
625
|
IpccLandUseCategory.ANNUAL_CROPS: "Annual crops"
|
|
676
626
|
}
|
|
677
627
|
"""
|
|
678
|
-
A dictionary mapping IPCC land use categories to corresponding land cover lookup values
|
|
679
|
-
|
|
628
|
+
A dictionary mapping IPCC land use categories to corresponding land cover lookup values in the
|
|
629
|
+
`"IPCC_LAND_USE_CATEGORY"` column.
|
|
680
630
|
"""
|
|
681
631
|
|
|
682
632
|
IPCC_MANAGEMENT_CATEGORY_TO_GRASSLAND_MANAGEMENT_TERM_ID = {
|
|
@@ -687,8 +637,8 @@ IPCC_MANAGEMENT_CATEGORY_TO_GRASSLAND_MANAGEMENT_TERM_ID = {
|
|
|
687
637
|
IpccManagementCategory.OTHER: "nativePasture"
|
|
688
638
|
}
|
|
689
639
|
"""
|
|
690
|
-
A dictionary mapping IPCC management categories to corresponding grassland management term IDs from the
|
|
691
|
-
|
|
640
|
+
A dictionary mapping IPCC management categories to corresponding grassland management term IDs from the land cover
|
|
641
|
+
glossary.
|
|
692
642
|
"""
|
|
693
643
|
|
|
694
644
|
|
|
@@ -745,8 +695,7 @@ def _check_cycle_tillage_management_category(
|
|
|
745
695
|
tillage_nodes,
|
|
746
696
|
lookup=LOOKUP,
|
|
747
697
|
target_lookup_values=target_lookup_values,
|
|
748
|
-
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
749
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
698
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
750
699
|
) and (
|
|
751
700
|
key is not IpccManagementCategory.NO_TILLAGE
|
|
752
701
|
or _check_zero_tillages(tillage_nodes)
|
|
@@ -1515,18 +1464,16 @@ def _run_soc_stocks(
|
|
|
1515
1464
|
annual_temperature_factors: list[float],
|
|
1516
1465
|
annual_water_factors: list[float],
|
|
1517
1466
|
annual_organic_carbon_inputs: list[float],
|
|
1518
|
-
|
|
1519
|
-
|
|
1467
|
+
annual_n_contents: list[float],
|
|
1468
|
+
annual_lignin_contents: list[float],
|
|
1520
1469
|
annual_tillage_categories: Union[list[IpccManagementCategory], None] = None,
|
|
1521
1470
|
sand_content: float = 0.33,
|
|
1522
1471
|
run_in_period: int = 5,
|
|
1523
|
-
initial_soc_stock: Union[float, None] = None,
|
|
1524
1472
|
params: Union[dict[str, float], None] = None,
|
|
1525
1473
|
) -> Tier2SocResult:
|
|
1526
1474
|
"""
|
|
1527
1475
|
Run the IPCC Tier 2 SOC model with precomputed `annual_temperature_factors`, `annual_water_factors`,
|
|
1528
|
-
`annual_organic_carbon_inputs`, `
|
|
1529
|
-
`annual_average_lignin_contents_of_organic_carbon_sources`.
|
|
1476
|
+
`annual_organic_carbon_inputs`, `annual_n_contents`, `annual_lignin_contents`.
|
|
1530
1477
|
|
|
1531
1478
|
Parameters
|
|
1532
1479
|
----------
|
|
@@ -1537,22 +1484,20 @@ def _run_soc_stocks(
|
|
|
1537
1484
|
annual_water_factors : list[float]
|
|
1538
1485
|
A list of water factors for each year in the inventory, dimensionless (see Equation 5.0F).
|
|
1539
1486
|
annual_organic_carbon_inputs : list[float]
|
|
1540
|
-
A list of organic carbon inputs to the soil for each year in the inventory, kg C ha-1 year-1
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
A list of the average nitrogen contents of the organic carbon sources for each year in the inventory,
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
A list of the average lignin contents of the organic carbon sources for each year in the inventory,
|
|
1547
|
-
|
|
1487
|
+
A list of organic carbon inputs to the soil for each year in the inventory, kg C ha-1 year-1 (see Equation
|
|
1488
|
+
5.0H).
|
|
1489
|
+
annual_n_contents : list[float]
|
|
1490
|
+
A list of the average nitrogen contents of the organic carbon sources for each year in the inventory, decimal
|
|
1491
|
+
proportion.
|
|
1492
|
+
annual_lignin_contents : list[float]
|
|
1493
|
+
A list of the average lignin contents of the organic carbon sources for each year in the inventory, decimal
|
|
1494
|
+
proportion.
|
|
1548
1495
|
annual_tillage_categories : list[IpccManagementCategory] | None
|
|
1549
1496
|
A list of the site"s `IpccManagementCategory`s for each year in the inventory.
|
|
1550
1497
|
sand_content : float
|
|
1551
1498
|
The sand content of the site, decimal proportion, default value: `0.33`.
|
|
1552
1499
|
run_in_period : int
|
|
1553
1500
|
The length of the run-in period in years, must be greater than or equal to 1, default value: `5`.
|
|
1554
|
-
initial_soc_stock : float | None
|
|
1555
|
-
The measured or pre-computed initial SOC stock at the end of the run-in period, kg C ha-1.
|
|
1556
1501
|
params : dict[str: float] | None
|
|
1557
1502
|
Overrides for the model parameters. If `None` only default parameters will be used.
|
|
1558
1503
|
|
|
@@ -1597,29 +1542,16 @@ def _run_soc_stocks(
|
|
|
1597
1542
|
|
|
1598
1543
|
# --- SPLIT ANNUAL DATA INTO RUN-IN AND INVENTORY PERIODS ---
|
|
1599
1544
|
|
|
1600
|
-
inventory_temperature_factors = timeseries_to_inventory(
|
|
1601
|
-
|
|
1602
|
-
)
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
)
|
|
1606
|
-
inventory_carbon_inputs = timeseries_to_inventory(
|
|
1607
|
-
annual_organic_carbon_inputs, run_in_period
|
|
1608
|
-
)
|
|
1609
|
-
inventory_nitrogen_contents = timeseries_to_inventory(
|
|
1610
|
-
annual_average_nitrogen_contents_of_organic_carbon_sources, run_in_period
|
|
1611
|
-
)
|
|
1612
|
-
inventory_lignin_contents = timeseries_to_inventory(
|
|
1613
|
-
annual_average_lignin_contents_of_organic_carbon_sources, run_in_period
|
|
1614
|
-
)
|
|
1545
|
+
inventory_temperature_factors = timeseries_to_inventory(annual_temperature_factors, run_in_period)
|
|
1546
|
+
inventory_water_factors = timeseries_to_inventory(annual_water_factors, run_in_period)
|
|
1547
|
+
inventory_carbon_inputs = timeseries_to_inventory(annual_organic_carbon_inputs, run_in_period)
|
|
1548
|
+
inventory_n_contents = timeseries_to_inventory(annual_n_contents, run_in_period)
|
|
1549
|
+
inventory_lignin_contents = timeseries_to_inventory(annual_lignin_contents, run_in_period)
|
|
1615
1550
|
inventory_f_2s = timeseries_to_inventory(annual_f_2s, run_in_period)
|
|
1616
|
-
inventory_tillage_factors = timeseries_to_inventory(
|
|
1617
|
-
annual_tillage_factors, run_in_period
|
|
1618
|
-
)
|
|
1551
|
+
inventory_tillage_factors = timeseries_to_inventory(annual_tillage_factors, run_in_period)
|
|
1619
1552
|
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
] # The last year of the run-in should be the first year of the inventory
|
|
1553
|
+
# The last year of the run-in should be the first year of the inventory
|
|
1554
|
+
inventory_timestamps = timestamps[run_in_period - 1:]
|
|
1623
1555
|
|
|
1624
1556
|
# --- CALCULATE THE ACTIVE ACTIVE POOL STEADY STATES ---
|
|
1625
1557
|
|
|
@@ -1641,7 +1573,7 @@ def _run_soc_stocks(
|
|
|
1641
1573
|
inventory_carbon_inputs,
|
|
1642
1574
|
inventory_f_2s,
|
|
1643
1575
|
inventory_lignin_contents,
|
|
1644
|
-
|
|
1576
|
+
inventory_n_contents,
|
|
1645
1577
|
)
|
|
1646
1578
|
]
|
|
1647
1579
|
|
|
@@ -1731,39 +1663,9 @@ def _run_soc_stocks(
|
|
|
1731
1663
|
|
|
1732
1664
|
# --- CALCULATE THE ACTIVE, SLOW AND PASSIVE SOC STOCKS ---
|
|
1733
1665
|
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
)
|
|
1738
|
-
|
|
1739
|
-
init_active_frac = inventory_active_pool_steady_states[0]/init_total_steady_state
|
|
1740
|
-
init_slow_frac = inventory_slow_pool_steady_states[0]/init_total_steady_state
|
|
1741
|
-
init_passive_frac = 1 - (init_active_frac + init_slow_frac)
|
|
1742
|
-
|
|
1743
|
-
inventory_active_pool_soc_stocks = []
|
|
1744
|
-
inventory_slow_pool_soc_stocks = []
|
|
1745
|
-
inventory_passive_pool_soc_stocks = []
|
|
1746
|
-
|
|
1747
|
-
should_calc_run_in = initial_soc_stock is None
|
|
1748
|
-
|
|
1749
|
-
inventory_active_pool_soc_stocks.insert(
|
|
1750
|
-
0,
|
|
1751
|
-
inventory_active_pool_steady_states[0]
|
|
1752
|
-
if should_calc_run_in
|
|
1753
|
-
else init_active_frac * initial_soc_stock,
|
|
1754
|
-
)
|
|
1755
|
-
inventory_slow_pool_soc_stocks.insert(
|
|
1756
|
-
0,
|
|
1757
|
-
inventory_slow_pool_steady_states[0]
|
|
1758
|
-
if should_calc_run_in
|
|
1759
|
-
else init_slow_frac * initial_soc_stock,
|
|
1760
|
-
)
|
|
1761
|
-
inventory_passive_pool_soc_stocks.insert(
|
|
1762
|
-
0,
|
|
1763
|
-
inventory_passive_pool_steady_states[0]
|
|
1764
|
-
if should_calc_run_in
|
|
1765
|
-
else init_passive_frac * initial_soc_stock,
|
|
1766
|
-
)
|
|
1666
|
+
inventory_active_pool_soc_stocks = inventory_active_pool_steady_states[:1]
|
|
1667
|
+
inventory_slow_pool_soc_stocks = inventory_slow_pool_steady_states[:1]
|
|
1668
|
+
inventory_passive_pool_soc_stocks = inventory_passive_pool_steady_states[:1]
|
|
1767
1669
|
|
|
1768
1670
|
for index in range(1, len(inventory_timestamps), 1):
|
|
1769
1671
|
inventory_active_pool_soc_stocks.insert(
|
|
@@ -1804,26 +1706,6 @@ def _run_soc_stocks(
|
|
|
1804
1706
|
# --- TIER 2 SUB-MODEL: ANNUAL TEMPERATURE FACTORS ---
|
|
1805
1707
|
|
|
1806
1708
|
|
|
1807
|
-
def _check_consecutive(ints: list[int]) -> bool:
|
|
1808
|
-
"""
|
|
1809
|
-
Checks whether a list of integers are consecutive.
|
|
1810
|
-
|
|
1811
|
-
Used to determine whether annualised data is complete from every year from beggining to end.
|
|
1812
|
-
|
|
1813
|
-
Parameters
|
|
1814
|
-
----------
|
|
1815
|
-
ints : list[int]
|
|
1816
|
-
A list of integer values.
|
|
1817
|
-
|
|
1818
|
-
Returns
|
|
1819
|
-
-------
|
|
1820
|
-
bool
|
|
1821
|
-
Whether or not the list of integers is consecutive.
|
|
1822
|
-
"""
|
|
1823
|
-
range_list = list(range(min(ints), max(ints)+1)) if ints else []
|
|
1824
|
-
return all(a == b for a, b in zip(ints, range_list))
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
1709
|
def _check_12_months(inner_dict: dict, keys: set[Any]):
|
|
1828
1710
|
"""
|
|
1829
1711
|
Checks whether an inner dict has 12 months of data for each of the required inner keys.
|
|
@@ -1848,45 +1730,6 @@ def _check_12_months(inner_dict: dict, keys: set[Any]):
|
|
|
1848
1730
|
# --- SUB-MODEL ANNUAL TEMPERATURE FACTORS ---
|
|
1849
1731
|
|
|
1850
1732
|
|
|
1851
|
-
def _should_run_annual_temperature_factors(
|
|
1852
|
-
site: dict
|
|
1853
|
-
) -> tuple[bool, dict]:
|
|
1854
|
-
"""
|
|
1855
|
-
Extracts, formats and checks data from the site node to determine whether or not to run the annual temperature
|
|
1856
|
-
factors model on a specific Hestia `Site`.
|
|
1857
|
-
|
|
1858
|
-
Parameters
|
|
1859
|
-
----------
|
|
1860
|
-
site : dict
|
|
1861
|
-
A Hestia `Site` node, see: https://www.hestia.earth/schema/Site.
|
|
1862
|
-
|
|
1863
|
-
Returns
|
|
1864
|
-
-------
|
|
1865
|
-
tuple[bool, dict]
|
|
1866
|
-
`(should_run, grouped_data)`.
|
|
1867
|
-
"""
|
|
1868
|
-
measurements = site.get("measurements", [])
|
|
1869
|
-
temperature_monthly = find_term_match(measurements, TEMPERATURE_MONTHLY_TERM_ID, {})
|
|
1870
|
-
|
|
1871
|
-
grouped_data = group_measurement_values_by_year(
|
|
1872
|
-
temperature_monthly,
|
|
1873
|
-
inner_key=_InnerKey.TEMPERATURES,
|
|
1874
|
-
complete_years_only=True
|
|
1875
|
-
)
|
|
1876
|
-
|
|
1877
|
-
should_run = all([
|
|
1878
|
-
all(
|
|
1879
|
-
_check_12_months(inner, {_InnerKey.TEMPERATURES})
|
|
1880
|
-
for inner in grouped_data.values()
|
|
1881
|
-
),
|
|
1882
|
-
len(grouped_data.keys()) >= MIN_RUN_IN_PERIOD,
|
|
1883
|
-
_check_consecutive(grouped_data.keys())
|
|
1884
|
-
])
|
|
1885
|
-
|
|
1886
|
-
logShouldRun(site, MODEL, TERM_ID, should_run, sub_model="_run_annual_temperature_factors")
|
|
1887
|
-
return should_run, grouped_data
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
1733
|
def _run_annual_temperature_factors(
|
|
1891
1734
|
timestamps: list[int],
|
|
1892
1735
|
temperatures: list[list[float]],
|
|
@@ -1926,77 +1769,6 @@ def _run_annual_temperature_factors(
|
|
|
1926
1769
|
# --- TIER 2 SUB-MODEL: ANNUAL WATER FACTORS ---
|
|
1927
1770
|
|
|
1928
1771
|
|
|
1929
|
-
def _should_run_annual_water_factors(
|
|
1930
|
-
site: dict,
|
|
1931
|
-
cycles: list[dict]
|
|
1932
|
-
) -> tuple[bool, bool, dict]:
|
|
1933
|
-
"""
|
|
1934
|
-
Extracts, formats and checks data from the site and cycle nodes to determine determine whether or not to run the
|
|
1935
|
-
annual water factors model on a specific Hestia `Site` and `Cycle`s.
|
|
1936
|
-
|
|
1937
|
-
TODO: Implement checks for monthly is_irrigateds from cycles.
|
|
1938
|
-
|
|
1939
|
-
Parameters
|
|
1940
|
-
----------
|
|
1941
|
-
site : dict
|
|
1942
|
-
A Hestia `Site` node, see: https://www.hestia.earth/schema/Site.
|
|
1943
|
-
cycles : list[dict]
|
|
1944
|
-
A list of Hestia `Cycle` nodes, see: https://www.hestia.earth/schema/Cycle.
|
|
1945
|
-
|
|
1946
|
-
Returns
|
|
1947
|
-
-------
|
|
1948
|
-
tuple[bool, bool, dict]
|
|
1949
|
-
`(should_run, run_with_irrigation, grouped_data)`.
|
|
1950
|
-
"""
|
|
1951
|
-
measurements = site.get("measurements", [])
|
|
1952
|
-
precipitation_monthly = find_term_match(measurements, PRECIPITATION_MONTHLY_TERM_ID, {})
|
|
1953
|
-
potential_evapotranspiration_monthly = find_term_match(measurements, PET_MONTHLY_TERM_ID, {})
|
|
1954
|
-
|
|
1955
|
-
grouped_precipitations = group_measurement_values_by_year(
|
|
1956
|
-
precipitation_monthly,
|
|
1957
|
-
inner_key=_InnerKey.PRECIPITATIONS,
|
|
1958
|
-
complete_years_only=True
|
|
1959
|
-
)
|
|
1960
|
-
grouped_pets = group_measurement_values_by_year(
|
|
1961
|
-
potential_evapotranspiration_monthly,
|
|
1962
|
-
inner_key=_InnerKey.PETS,
|
|
1963
|
-
complete_years_only=True
|
|
1964
|
-
)
|
|
1965
|
-
|
|
1966
|
-
is_irrigateds = None # TODO: Implement is_irrigateds check.
|
|
1967
|
-
run_with_irrigation = bool(is_irrigateds)
|
|
1968
|
-
|
|
1969
|
-
grouped_data = (
|
|
1970
|
-
merge(grouped_precipitations, grouped_pets) if is_irrigateds is None else reduce(
|
|
1971
|
-
merge, [grouped_precipitations, grouped_pets, is_irrigateds]
|
|
1972
|
-
)
|
|
1973
|
-
)
|
|
1974
|
-
|
|
1975
|
-
should_run = all([
|
|
1976
|
-
all(
|
|
1977
|
-
_check_12_months(inner, {_InnerKey.PRECIPITATIONS, _InnerKey.PETS})
|
|
1978
|
-
for inner in grouped_data.values()
|
|
1979
|
-
),
|
|
1980
|
-
not run_with_irrigation or all(
|
|
1981
|
-
_check_12_months(inner, {_InnerKey.IS_IRRIGATEDS})
|
|
1982
|
-
for inner in grouped_data.values()
|
|
1983
|
-
),
|
|
1984
|
-
len(grouped_data.keys()) >= MIN_RUN_IN_PERIOD,
|
|
1985
|
-
_check_consecutive(grouped_data.keys()),
|
|
1986
|
-
check_cycle_site_ids_identical(cycles)
|
|
1987
|
-
])
|
|
1988
|
-
|
|
1989
|
-
logShouldRun(
|
|
1990
|
-
site,
|
|
1991
|
-
MODEL,
|
|
1992
|
-
TERM_ID,
|
|
1993
|
-
should_run,
|
|
1994
|
-
sub_model="_run_annual_water_factors",
|
|
1995
|
-
run_with_irrigation=run_with_irrigation
|
|
1996
|
-
)
|
|
1997
|
-
return should_run, run_with_irrigation, grouped_data
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
1772
|
def _run_annual_water_factors(
|
|
2001
1773
|
timestamps: list[int],
|
|
2002
1774
|
precipitations: list[list[float]],
|
|
@@ -2109,338 +1881,130 @@ def _get_carbon_sources_from_cycles(cycles: dict) -> list[CarbonSource]:
|
|
|
2109
1881
|
return non_empty_list([_iterate_carbon_source(node) for node in inputs_and_products])
|
|
2110
1882
|
|
|
2111
1883
|
|
|
2112
|
-
|
|
2113
|
-
site: dict,
|
|
2114
|
-
cycles: list[dict]
|
|
2115
|
-
) -> tuple[bool, dict]:
|
|
2116
|
-
"""
|
|
2117
|
-
Extracts, formats and checks data from the site node to determine whether or not to run the annual organic carbon
|
|
2118
|
-
inputs model on a specific set of Hestia `Cycle`s.
|
|
1884
|
+
# --- TIER 2 SOC MODEL ---
|
|
2119
1885
|
|
|
2120
|
-
Parameters
|
|
2121
|
-
----------
|
|
2122
|
-
cycles : list[dict]
|
|
2123
|
-
A list of Hestia `Cycle` nodes, see: https://www.hestia.earth/schema/Cycle.
|
|
2124
1886
|
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
1887
|
+
def _run_tier_2(
|
|
1888
|
+
inventory: dict[int: dict[_InventoryKey: any]],
|
|
1889
|
+
*,
|
|
1890
|
+
run_in_period: int = 5,
|
|
1891
|
+
run_with_irrigation: bool = True,
|
|
1892
|
+
sand_content: float = 0.33,
|
|
1893
|
+
params: Union[dict[str, float], None] = None,
|
|
1894
|
+
**_
|
|
1895
|
+
) -> list[dict]:
|
|
2129
1896
|
"""
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
1897
|
+
Run the IPCC Tier 2 SOC model on a time series of annual data about a site and the mangagement activities taking
|
|
1898
|
+
place on it. To avoid any errors, the `inventory` parameter must be pre-validated by the `should_run` function.
|
|
1899
|
+
|
|
1900
|
+
The inventory should be in the following shape:
|
|
1901
|
+
```
|
|
1902
|
+
{
|
|
1903
|
+
year (int): {
|
|
1904
|
+
_InventoryKey.SHOULD_RUN_TIER_2: bool,
|
|
1905
|
+
_InventoryKey.TEMP_MONTHLY: list[float],
|
|
1906
|
+
_InventoryKey.PRECIP_MONTHLY: list[float],
|
|
1907
|
+
_InventoryKey.PET_MONTHLY: list[float],
|
|
1908
|
+
_InventoryKey.IRRIGATED_MONTHLY: list[bool]
|
|
1909
|
+
_InventoryKey.CARBON_INPUT: float,
|
|
1910
|
+
_InventoryKey.N_CONTENT: float,
|
|
1911
|
+
_InventoryKey.TILLAGE_CATEGORY: IpccManagementCategory,
|
|
1912
|
+
_InventoryKey.SAND_CONTENT: float
|
|
1913
|
+
},
|
|
1914
|
+
...
|
|
2136
1915
|
}
|
|
1916
|
+
```
|
|
2137
1917
|
|
|
2138
|
-
|
|
2139
|
-
len(grouped_data.keys()) >= MIN_RUN_IN_PERIOD,
|
|
2140
|
-
_check_consecutive(grouped_data.keys()),
|
|
2141
|
-
check_cycle_site_ids_identical(cycles)
|
|
2142
|
-
])
|
|
2143
|
-
|
|
2144
|
-
logShouldRun(site, MODEL, TERM_ID, should_run, sub_model="_run_annual_organic_carbon_inputs")
|
|
2145
|
-
return should_run, grouped_data
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
def _run_annual_organic_carbon_inputs(
|
|
2149
|
-
timestamps: list[int],
|
|
2150
|
-
annual_carbon_sources: list[list[CarbonSource]],
|
|
2151
|
-
default_carbon_content: float = 0.42,
|
|
2152
|
-
default_nitrogen_content: float = 0.0085,
|
|
2153
|
-
default_lignin_content: float = 0.073,
|
|
2154
|
-
):
|
|
2155
|
-
"""
|
|
2156
|
-
Calculate the organic carbon input, average nitrogen content of carbon sources and average lignin content of carbon
|
|
2157
|
-
sources for each year of the inventory.
|
|
2158
|
-
|
|
2159
|
-
`timestamps` and `annual_carbon_sources` must have the same length.
|
|
1918
|
+
TODO: interpolate between `sandContent` measurements for different years of the inventory
|
|
2160
1919
|
|
|
2161
1920
|
Parameters
|
|
2162
1921
|
----------
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
The default nitrogen content of a carbon source, decimal proportion, kg N (kg d.m.)-1, default value: `0.0085`.
|
|
2172
|
-
default_lignin_content : float)
|
|
2173
|
-
The default lignin content of a carbon source, decimal proportion, kg lignin (kg d.m.)-1,
|
|
2174
|
-
default value: `0.073`.
|
|
1922
|
+
inventory : dict
|
|
1923
|
+
The inventory built by the `_should_run` function.
|
|
1924
|
+
run_in_period : int, optional
|
|
1925
|
+
The length of the run-in period in years, must be greater than or equal to 1, default value: `5`.
|
|
1926
|
+
run_with_irrigation : bool, optional
|
|
1927
|
+
`True` if the model should run while taking into account irrigation, `False` if not.
|
|
1928
|
+
params : dict | None, optional
|
|
1929
|
+
Overrides for the model parameters. If `None` only default parameters will be used.
|
|
2175
1930
|
|
|
2176
1931
|
Returns
|
|
2177
1932
|
-------
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
`(timestamps: list[int], organic_carbon_inputs: list[float], average_nitrogen_contents: list[float],
|
|
2181
|
-
average_lignin_contents: list[float])`
|
|
2182
|
-
"""
|
|
2183
|
-
return CarbonInputResult(
|
|
2184
|
-
timestamps=timestamps,
|
|
2185
|
-
organic_carbon_inputs=[
|
|
2186
|
-
_calc_total_organic_carbon_input(sources, default_carbon_content=default_carbon_content)
|
|
2187
|
-
for sources in annual_carbon_sources
|
|
2188
|
-
],
|
|
2189
|
-
average_nitrogen_contents=[
|
|
2190
|
-
_calc_average_nitrogen_content_of_organic_carbon_sources(
|
|
2191
|
-
sources, default_nitrogen_content=default_nitrogen_content)
|
|
2192
|
-
for sources in annual_carbon_sources
|
|
2193
|
-
],
|
|
2194
|
-
average_lignin_contents=[
|
|
2195
|
-
_calc_average_lignin_content_of_organic_carbon_sources(
|
|
2196
|
-
sources, default_lignin_content=default_lignin_content)
|
|
2197
|
-
for sources in annual_carbon_sources
|
|
2198
|
-
],
|
|
2199
|
-
)
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
# --- TIER 2 SOC MODEL ---
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
def _should_run_tier_2(
|
|
2206
|
-
site: dict
|
|
2207
|
-
) -> tuple:
|
|
1933
|
+
list[dict]
|
|
1934
|
+
A list of Hestia `Measurement` nodes containing the calculated SOC stocks and additional relevant data.
|
|
2208
1935
|
"""
|
|
2209
|
-
|
|
2210
|
-
|
|
1936
|
+
valid_inventory = {
|
|
1937
|
+
year: group for year, group in inventory.items() if group.get(_InventoryKey.SHOULD_RUN_TIER_2)
|
|
1938
|
+
}
|
|
2211
1939
|
|
|
2212
|
-
|
|
2213
|
-
----------
|
|
2214
|
-
site : dict
|
|
2215
|
-
A Hestia `Site` node, see: https://www.hestia.earth/schema/Site.
|
|
2216
|
-
cycles : list[dict]
|
|
2217
|
-
A list of Hestia `Cycle` nodes, see: https://www.hestia.earth/schema/Cycle.
|
|
1940
|
+
timestamps = [year for year in valid_inventory.keys()]
|
|
2218
1941
|
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
`(should_run, timestamps, temperatures, precipitations, pets, carbon_sources, tillage_categories, sand_content,
|
|
2223
|
-
is_irrigateds, run_in_period, initial_soc_stock)`
|
|
2224
|
-
"""
|
|
2225
|
-
cycles = related_cycles(site.get("@id"))
|
|
1942
|
+
annual_temperature_monthlys = [group[_InventoryKey.TEMP_MONTHLY] for group in valid_inventory.values()]
|
|
1943
|
+
annual_precipitation_monthlys = [group[_InventoryKey.PRECIP_MONTHLY] for group in valid_inventory.values()]
|
|
1944
|
+
annual_pet_monthlys = [group[_InventoryKey.PET_MONTHLY] for group in valid_inventory.values()]
|
|
2226
1945
|
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
1946
|
+
annual_carbon_inputs = [group[_InventoryKey.CARBON_INPUT] for group in valid_inventory.values()]
|
|
1947
|
+
annual_n_contents = [group[_InventoryKey.N_CONTENT] for group in valid_inventory.values()]
|
|
1948
|
+
annual_lignin_contents = [group[_InventoryKey.LIGNIN_CONTENT] for group in valid_inventory.values()]
|
|
1949
|
+
annual_tillage_categories = [group[_InventoryKey.TILLAGE_CATEGORY] for group in valid_inventory.values()]
|
|
1950
|
+
annual_irrigated_monthly = (
|
|
1951
|
+
[group[_InventoryKey.IRRIGATED_MONTHLY] for group in valid_inventory.values()] if run_with_irrigation else None
|
|
1952
|
+
)
|
|
2230
1953
|
|
|
2231
|
-
|
|
1954
|
+
sand_content = next(
|
|
1955
|
+
group[_InventoryKey.SAND_CONTENT]/100 for group in valid_inventory.values()
|
|
1956
|
+
if _InventoryKey.SAND_CONTENT in group
|
|
1957
|
+
)
|
|
2232
1958
|
|
|
2233
|
-
|
|
2234
|
-
year: {
|
|
2235
|
-
_InnerKey.TILLAGE_CATEGORY: _assign_tier_2_ipcc_tillage_management_category(_cycles)
|
|
2236
|
-
} for year, _cycles in grouped_cycles.items()
|
|
2237
|
-
}
|
|
1959
|
+
# --- MERGE ANY USER-SET PARAMETERS WITH THE IPCC DEFAULTS ---
|
|
2238
1960
|
|
|
2239
|
-
|
|
2240
|
-
grouped_data = reduce(merge, [grouped_temperature_data, grouped_water_data,
|
|
2241
|
-
grouped_carbon_sources_data, grouped_tillage_categories])
|
|
1961
|
+
params = DEFAULT_PARAMS | (params or {})
|
|
2242
1962
|
|
|
2243
|
-
#
|
|
2244
|
-
keys = INNER_KEYS_RUN_WITH_IRRIGATION if run_with_irrigation else INNER_KEYS_RUN_WITHOUT_IRRIGATION
|
|
1963
|
+
# --- COMPUTE FACTORS AND CARBON INPUTS ---
|
|
2245
1964
|
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
1965
|
+
_, annual_temperature_factors = _run_annual_temperature_factors(
|
|
1966
|
+
timestamps,
|
|
1967
|
+
annual_temperature_monthlys,
|
|
1968
|
+
maximum_temperature=params.get("maximum_temperature"),
|
|
1969
|
+
optimum_temperature=params.get("optimum_temperature")
|
|
1970
|
+
)
|
|
2251
1971
|
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
sand_content_value, _ = most_relevant_measurement_value_by_depth_and_date(
|
|
2259
|
-
measurements,
|
|
2260
|
-
SAND_CONTENT_TERM_ID,
|
|
2261
|
-
f"{start_year}-12-31",
|
|
2262
|
-
DEPTH_UPPER,
|
|
2263
|
-
DEPTH_LOWER,
|
|
2264
|
-
depth_strict=False
|
|
2265
|
-
) if timestamps else (None, None)
|
|
2266
|
-
sand_content = sand_content_value/100 if sand_content_value else None
|
|
2267
|
-
|
|
2268
|
-
initial_soc_stock_value, initial_soc_stock_date = most_relevant_measurement_value_by_depth_and_date(
|
|
2269
|
-
measurements,
|
|
2270
|
-
TERM_ID,
|
|
2271
|
-
f"{end_year}-12-31",
|
|
2272
|
-
DEPTH_UPPER,
|
|
2273
|
-
DEPTH_LOWER,
|
|
2274
|
-
depth_strict=True
|
|
2275
|
-
) if timestamps else (None, None)
|
|
2276
|
-
|
|
2277
|
-
run_with_initial_soc_stock = bool(initial_soc_stock_value and initial_soc_stock_date)
|
|
2278
|
-
|
|
2279
|
-
run_in_period = (
|
|
2280
|
-
int(abs(diff_in_years(f"{start_year}-12-31", initial_soc_stock_date)) + 1)
|
|
2281
|
-
if run_with_initial_soc_stock else MIN_RUN_IN_PERIOD
|
|
2282
|
-
) if timestamps else 0
|
|
2283
|
-
|
|
2284
|
-
timestamps = list(complete_data.keys())
|
|
2285
|
-
temperatures = [complete_data[year][_InnerKey.TEMPERATURES] for year in timestamps]
|
|
2286
|
-
precipitations = [complete_data[year][_InnerKey.PRECIPITATIONS] for year in timestamps]
|
|
2287
|
-
pets = [complete_data[year][_InnerKey.PETS] for year in timestamps]
|
|
2288
|
-
annual_carbon_sources = [complete_data[year][_InnerKey.CARBON_SOURCES] for year in timestamps]
|
|
2289
|
-
annual_tillage_categories = [complete_data[year][_InnerKey.TILLAGE_CATEGORY] for year in timestamps]
|
|
2290
|
-
is_irrigateds = (
|
|
2291
|
-
[complete_data[year][_InnerKey.IS_IRRIGATEDS] for year in timestamps] if run_with_irrigation else None
|
|
1972
|
+
_, annual_water_factors = _run_annual_water_factors(
|
|
1973
|
+
timestamps,
|
|
1974
|
+
annual_precipitation_monthlys,
|
|
1975
|
+
annual_pet_monthlys,
|
|
1976
|
+
annual_irrigated_monthly,
|
|
1977
|
+
water_factor_slope=params.get("water_factor_slope")
|
|
2292
1978
|
)
|
|
2293
1979
|
|
|
2294
|
-
|
|
2295
|
-
should_run_t,
|
|
2296
|
-
should_run_w,
|
|
2297
|
-
should_run_c,
|
|
2298
|
-
sand_content is not None and 0 < sand_content <= 1,
|
|
2299
|
-
run_in_period >= MIN_RUN_IN_PERIOD,
|
|
2300
|
-
len(timestamps) >= run_in_period,
|
|
2301
|
-
check_cycle_site_ids_identical(cycles),
|
|
2302
|
-
_check_consecutive(timestamps)
|
|
2303
|
-
])
|
|
1980
|
+
# --- RUN THE MODEL ---
|
|
2304
1981
|
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
1982
|
+
result = _run_soc_stocks(
|
|
1983
|
+
timestamps=timestamps,
|
|
1984
|
+
annual_temperature_factors=annual_temperature_factors,
|
|
1985
|
+
annual_water_factors=annual_water_factors,
|
|
1986
|
+
annual_organic_carbon_inputs=annual_carbon_inputs,
|
|
1987
|
+
annual_n_contents=annual_n_contents,
|
|
1988
|
+
annual_lignin_contents=annual_lignin_contents,
|
|
1989
|
+
annual_tillage_categories=annual_tillage_categories,
|
|
1990
|
+
sand_content=sand_content,
|
|
1991
|
+
run_in_period=run_in_period,
|
|
1992
|
+
params=params
|
|
2314
1993
|
)
|
|
2315
1994
|
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
initial_soc_stock_value
|
|
2328
|
-
)
|
|
1995
|
+
values = [
|
|
1996
|
+
_calc_tier_2_soc_stock(
|
|
1997
|
+
active,
|
|
1998
|
+
slow,
|
|
1999
|
+
passive
|
|
2000
|
+
) for active, slow, passive in zip(
|
|
2001
|
+
result.active_pool_soc_stocks,
|
|
2002
|
+
result.slow_pool_soc_stocks,
|
|
2003
|
+
result.passive_pool_soc_stocks
|
|
2004
|
+
)
|
|
2005
|
+
]
|
|
2329
2006
|
|
|
2330
|
-
|
|
2331
|
-
def _run_tier_2(
|
|
2332
|
-
timestamps: list[int],
|
|
2333
|
-
temperatures: list[list[float]],
|
|
2334
|
-
precipitations: list[list[float]],
|
|
2335
|
-
pets: list[list[float]],
|
|
2336
|
-
annual_carbon_sources: list[list[CarbonSource]],
|
|
2337
|
-
annual_tillage_categories: list[IpccManagementCategory],
|
|
2338
|
-
sand_content: float = 0.33,
|
|
2339
|
-
is_irrigateds: Union[list[list[bool]], None] = None,
|
|
2340
|
-
run_in_period: int = 5,
|
|
2341
|
-
initial_soc_stock: Union[float, None] = None,
|
|
2342
|
-
params: Union[dict[str, float], None] = None,
|
|
2343
|
-
) -> list[dict]:
|
|
2344
|
-
"""
|
|
2345
|
-
Run the IPCC Tier 2 SOC model on a time series of annual data about a site and the mangagement activities taking
|
|
2346
|
-
place on it. `timestamps` and `annual_`... lists must be the same length.
|
|
2347
|
-
|
|
2348
|
-
Parameters
|
|
2349
|
-
----------
|
|
2350
|
-
timestamps : list[int]
|
|
2351
|
-
A list of integer timestamps (e.g. [1995, 1996...]) for each year in the inventory.
|
|
2352
|
-
temperatures : list[list[float]]
|
|
2353
|
-
A list of monthly average temperatures for each year in the inventory.
|
|
2354
|
-
precipitations : list[list[float]]
|
|
2355
|
-
A list of monthly sum precipitations for each year in the inventory.
|
|
2356
|
-
pets : list[list[float]]
|
|
2357
|
-
A list of monthly sum potential evapotransiprations for each year in the inventory.
|
|
2358
|
-
annual_carbon_sources : list[list[CarbonSource]]
|
|
2359
|
-
A list of carbon sources for each year of the inventory, where each carbon source is a named tupled with the
|
|
2360
|
-
format `(mass: float, carbon_content: float, nitrogen_content: float, lignin_content: float)`
|
|
2361
|
-
annual_tillage_categories : list[IpccManagementCategory)
|
|
2362
|
-
A list of the site"s IpccManagementCategory for each year in the inventory.
|
|
2363
|
-
sand_content : float
|
|
2364
|
-
The sand content of the site, decimal proportion, default value: `0.33`.
|
|
2365
|
-
is_irrigateds : list[list[bool]] | None
|
|
2366
|
-
A list of monthly booleans that describe whether irrigation is used in a particular calendar month for each
|
|
2367
|
-
year in the inventory.
|
|
2368
|
-
run_in_period : int
|
|
2369
|
-
The length of the run-in period in years, must be greater than or equal to 1, default value: `5`.
|
|
2370
|
-
initial_soc_stock : float | None]
|
|
2371
|
-
The measured or pre-computed initial SOC stock at the end of the run-in period, kg C ha-1.
|
|
2372
|
-
params : dict | None
|
|
2373
|
-
Overrides for the model parameters. If `None` only default parameters will be used.
|
|
2374
|
-
|
|
2375
|
-
Returns
|
|
2376
|
-
-------
|
|
2377
|
-
list[dict]
|
|
2378
|
-
A list of Hestia `Measurement` nodes containing the calculated SOC stocks and additional relevant data.
|
|
2379
|
-
"""
|
|
2380
|
-
|
|
2381
|
-
# --- MERGE ANY USER-SET PARAMETERS WITH THE IPCC DEFAULTS ---
|
|
2382
|
-
|
|
2383
|
-
params = DEFAULT_PARAMS | (params or {})
|
|
2384
|
-
|
|
2385
|
-
# --- COMPUTE FACTORS AND CARBON INPUTS ---
|
|
2386
|
-
|
|
2387
|
-
_, annual_temperature_factors = _run_annual_temperature_factors(
|
|
2388
|
-
timestamps,
|
|
2389
|
-
temperatures,
|
|
2390
|
-
maximum_temperature=params.get("maximum_temperature"),
|
|
2391
|
-
optimum_temperature=params.get("optimum_temperature")
|
|
2392
|
-
)
|
|
2393
|
-
|
|
2394
|
-
_, annual_water_factors = _run_annual_water_factors(
|
|
2395
|
-
timestamps,
|
|
2396
|
-
precipitations,
|
|
2397
|
-
pets,
|
|
2398
|
-
is_irrigateds,
|
|
2399
|
-
water_factor_slope=params.get("water_factor_slope")
|
|
2400
|
-
)
|
|
2401
|
-
|
|
2402
|
-
(
|
|
2403
|
-
_,
|
|
2404
|
-
annual_organic_carbon_inputs,
|
|
2405
|
-
annual_nitrogen_contents,
|
|
2406
|
-
annual_lignin_contents
|
|
2407
|
-
) = _run_annual_organic_carbon_inputs(
|
|
2408
|
-
timestamps,
|
|
2409
|
-
annual_carbon_sources,
|
|
2410
|
-
default_carbon_content=params.get("default_carbon_content"),
|
|
2411
|
-
default_nitrogen_content=params.get("default_nitrogen_content"),
|
|
2412
|
-
default_lignin_content=params.get("default_lignin_content")
|
|
2413
|
-
)
|
|
2414
|
-
|
|
2415
|
-
# --- RUN THE MODEL ---
|
|
2416
|
-
|
|
2417
|
-
result = _run_soc_stocks(
|
|
2418
|
-
timestamps=timestamps,
|
|
2419
|
-
annual_temperature_factors=annual_temperature_factors,
|
|
2420
|
-
annual_water_factors=annual_water_factors,
|
|
2421
|
-
annual_organic_carbon_inputs=annual_organic_carbon_inputs,
|
|
2422
|
-
annual_average_nitrogen_contents_of_organic_carbon_sources=annual_nitrogen_contents,
|
|
2423
|
-
annual_average_lignin_contents_of_organic_carbon_sources=annual_lignin_contents,
|
|
2424
|
-
annual_tillage_categories=annual_tillage_categories,
|
|
2425
|
-
sand_content=sand_content,
|
|
2426
|
-
run_in_period=run_in_period,
|
|
2427
|
-
initial_soc_stock=initial_soc_stock,
|
|
2428
|
-
params=params
|
|
2429
|
-
)
|
|
2430
|
-
|
|
2431
|
-
values = [
|
|
2432
|
-
_calc_tier_2_soc_stock(
|
|
2433
|
-
active,
|
|
2434
|
-
slow,
|
|
2435
|
-
passive
|
|
2436
|
-
) for active, slow, passive in zip(
|
|
2437
|
-
result.active_pool_soc_stocks,
|
|
2438
|
-
result.slow_pool_soc_stocks,
|
|
2439
|
-
result.passive_pool_soc_stocks
|
|
2440
|
-
)
|
|
2441
|
-
]
|
|
2442
|
-
|
|
2443
|
-
# --- RETURN MEASUREMENT NODES ---
|
|
2007
|
+
# --- RETURN MEASUREMENT NODES ---
|
|
2444
2008
|
|
|
2445
2009
|
return [
|
|
2446
2010
|
_measurement(
|
|
@@ -2457,32 +2021,6 @@ def _run_tier_2(
|
|
|
2457
2021
|
# --- TIER 1 FUNCTIONS ---
|
|
2458
2022
|
|
|
2459
2023
|
|
|
2460
|
-
def _find_closest_value_index(
|
|
2461
|
-
lst: Iterable[Union[int, float]], target_value: Union[int, float]
|
|
2462
|
-
) -> Optional[int]:
|
|
2463
|
-
"""
|
|
2464
|
-
Find the index of the closest value in a list.
|
|
2465
|
-
|
|
2466
|
-
Parameters
|
|
2467
|
-
----------
|
|
2468
|
-
lst : iterable[int | float]
|
|
2469
|
-
The list of integers.
|
|
2470
|
-
target : int | float
|
|
2471
|
-
The target value.
|
|
2472
|
-
|
|
2473
|
-
Returns
|
|
2474
|
-
-------
|
|
2475
|
-
int
|
|
2476
|
-
The index of the closest value.
|
|
2477
|
-
"""
|
|
2478
|
-
should_run = all([
|
|
2479
|
-
lst,
|
|
2480
|
-
all(isinstance(element, (int, float)) for element in lst),
|
|
2481
|
-
isinstance(target_value, (int, float))
|
|
2482
|
-
])
|
|
2483
|
-
return min(range(len(lst)), key=lambda i: abs(lst[i] - target_value)) if should_run else None
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
2024
|
def _retrieve_soc_ref(
|
|
2487
2025
|
eco_climate_zone: int,
|
|
2488
2026
|
ipcc_soil_category: IpccSoilCategory
|
|
@@ -2519,9 +2057,8 @@ def _retrieve_soc_stock_factors(
|
|
|
2519
2057
|
ipcc_carbon_input_category: IpccCarbonInputCategory
|
|
2520
2058
|
) -> StockChangeFactors:
|
|
2521
2059
|
"""
|
|
2522
|
-
Retrieve the stock change factors for soil organic carbon (SOC)
|
|
2523
|
-
|
|
2524
|
-
input.
|
|
2060
|
+
Retrieve the stock change factors for soil organic carbon (SOC) based on a given combination of land use,
|
|
2061
|
+
management and carbon input.
|
|
2525
2062
|
|
|
2526
2063
|
Parameters
|
|
2527
2064
|
----------
|
|
@@ -2581,8 +2118,8 @@ def _calc_soc_equilibrium(
|
|
|
2581
2118
|
"""
|
|
2582
2119
|
Calculate the soil organic carbon (SOC) equilibrium based on reference SOC and factors.
|
|
2583
2120
|
|
|
2584
|
-
In the tier 1 model, SOC equilibriums are considered to be reached after 20 years of
|
|
2585
|
-
|
|
2121
|
+
In the tier 1 model, SOC equilibriums are considered to be reached after 20 years of consistant land use,
|
|
2122
|
+
management and carbon input.
|
|
2586
2123
|
|
|
2587
2124
|
Parameters
|
|
2588
2125
|
----------
|
|
@@ -2648,8 +2185,8 @@ def _iterate_soc_equilibriums(
|
|
|
2648
2185
|
timestamps: list[int], soc_equilibriums: list[float]
|
|
2649
2186
|
) -> tuple[list[int], list[float]]:
|
|
2650
2187
|
"""
|
|
2651
|
-
Iterate over SOC equilibriums, inserting timestamps and soc_equilibriums for any
|
|
2652
|
-
|
|
2188
|
+
Iterate over SOC equilibriums, inserting timestamps and soc_equilibriums for any missing years where SOC would have
|
|
2189
|
+
reached equilibrium.
|
|
2653
2190
|
|
|
2654
2191
|
Parameters
|
|
2655
2192
|
----------
|
|
@@ -2689,7 +2226,6 @@ def _iterate_soc_equilibriums(
|
|
|
2689
2226
|
)
|
|
2690
2227
|
|
|
2691
2228
|
for index, (timestamp, soc_equilibrium) in enumerate(zip(timestamps, soc_equilibriums)):
|
|
2692
|
-
|
|
2693
2229
|
equilibrium_reached_timestamp = calc_equilibrium_reached_timestamp(index)
|
|
2694
2230
|
|
|
2695
2231
|
if is_missing_equilibrium_year(timestamp, equilibrium_reached_timestamp):
|
|
@@ -2710,8 +2246,8 @@ def _run_soc_equilibriums(
|
|
|
2710
2246
|
"""
|
|
2711
2247
|
Run the soil organic carbon (SOC) equilibriums calculation for each year in the inventory.
|
|
2712
2248
|
|
|
2713
|
-
Missing years where SOC equilibrium would be reached are inserted to allow for annual
|
|
2714
|
-
|
|
2249
|
+
Missing years where SOC equilibrium would be reached are inserted to allow for annual SOC change to be calculated
|
|
2250
|
+
correctly.
|
|
2715
2251
|
|
|
2716
2252
|
Parameters
|
|
2717
2253
|
----------
|
|
@@ -2733,8 +2269,8 @@ def _run_soc_equilibriums(
|
|
|
2733
2269
|
Returns
|
|
2734
2270
|
-------
|
|
2735
2271
|
tuple[list[int], list[float]]
|
|
2736
|
-
`timestamps` and `soc_equilibriums` for each year in the inventory, including any
|
|
2737
|
-
|
|
2272
|
+
`timestamps` and `soc_equilibriums` for each year in the inventory, including any missing years where SOC
|
|
2273
|
+
equilibrium would have been reached.
|
|
2738
2274
|
"""
|
|
2739
2275
|
|
|
2740
2276
|
# Calculate SOC equilibriums for each year
|
|
@@ -2767,8 +2303,7 @@ def _calc_tier_1_soc_stocks(
|
|
|
2767
2303
|
soc_equilibriums: list[float],
|
|
2768
2304
|
) -> list[float]:
|
|
2769
2305
|
"""
|
|
2770
|
-
Calculate soil organic carbon (SOC) stocks (kg C ha-1) in the 0-30cm depth interval for each year in
|
|
2771
|
-
the inventory.
|
|
2306
|
+
Calculate soil organic carbon (SOC) stocks (kg C ha-1) in the 0-30cm depth interval for each year in the inventory.
|
|
2772
2307
|
|
|
2773
2308
|
Parameters
|
|
2774
2309
|
----------
|
|
@@ -2809,62 +2344,6 @@ def _calc_tier_1_soc_stocks(
|
|
|
2809
2344
|
return soc_stocks
|
|
2810
2345
|
|
|
2811
2346
|
|
|
2812
|
-
def _calc_measurement_scaling_factor(
|
|
2813
|
-
measured_soc: float, calculated_soc: float
|
|
2814
|
-
) -> float:
|
|
2815
|
-
"""
|
|
2816
|
-
Calculate the scaling factor soil organic carbon (SOC) values based
|
|
2817
|
-
on the ratio between measured and calculated values.
|
|
2818
|
-
|
|
2819
|
-
Parameters
|
|
2820
|
-
----------
|
|
2821
|
-
measured_soc : float
|
|
2822
|
-
The measured SOC value.
|
|
2823
|
-
calculated_soc : float
|
|
2824
|
-
The calculated SOC value.
|
|
2825
|
-
|
|
2826
|
-
Returns
|
|
2827
|
-
-------
|
|
2828
|
-
float
|
|
2829
|
-
The scaling factor.
|
|
2830
|
-
"""
|
|
2831
|
-
return measured_soc / calculated_soc
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
def _scale_soc_stocks(
|
|
2835
|
-
soc_stocks: list[float],
|
|
2836
|
-
soc_measurement_value: Union[float, None] = None,
|
|
2837
|
-
soc_measurement_index: Union[int, None] = None
|
|
2838
|
-
) -> list[float]:
|
|
2839
|
-
"""
|
|
2840
|
-
Scale soil organic carbon (SOC) stocks based on a measurement value and index.
|
|
2841
|
-
|
|
2842
|
-
Parameters
|
|
2843
|
-
----------
|
|
2844
|
-
soc_stocks : list[float]
|
|
2845
|
-
The list of SOC stocks to be scaled.
|
|
2846
|
-
soc_measurement_value : float | None, optional
|
|
2847
|
-
The measured SOC value for scaling. If None, no scaling is applied.
|
|
2848
|
-
soc_measurement_index : int | None, optional
|
|
2849
|
-
The index of the calculated SOC stock to compare against the SOC measurement.
|
|
2850
|
-
|
|
2851
|
-
Returns
|
|
2852
|
-
-------
|
|
2853
|
-
list[float]
|
|
2854
|
-
The scaled SOC stocks.
|
|
2855
|
-
"""
|
|
2856
|
-
|
|
2857
|
-
measurement_scaling_factor = (
|
|
2858
|
-
_calc_measurement_scaling_factor(
|
|
2859
|
-
soc_measurement_value,
|
|
2860
|
-
soc_stocks[soc_measurement_index]
|
|
2861
|
-
) if soc_measurement_value and soc_measurement_index is not None
|
|
2862
|
-
else 1
|
|
2863
|
-
)
|
|
2864
|
-
|
|
2865
|
-
return [value * measurement_scaling_factor for value in soc_stocks]
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
2347
|
# --- GET THE ECO-CLIMATE ZONE FROM THE MEASUREMENTS ---
|
|
2869
2348
|
|
|
2870
2349
|
|
|
@@ -2883,7 +2362,6 @@ def _get_eco_climate_zone(measurements: list[dict]) -> Optional[int]:
|
|
|
2883
2362
|
The eco-climate zone value if found, otherwise None.
|
|
2884
2363
|
"""
|
|
2885
2364
|
eco_climate_zone = find_term_match(measurements, "ecoClimateZone")
|
|
2886
|
-
# return measurement_value(eco_climate_zone) or None
|
|
2887
2365
|
return get_node_value(eco_climate_zone) or None
|
|
2888
2366
|
|
|
2889
2367
|
|
|
@@ -2891,10 +2369,10 @@ def _get_eco_climate_zone(measurements: list[dict]) -> Optional[int]:
|
|
|
2891
2369
|
|
|
2892
2370
|
|
|
2893
2371
|
def _check_soil_category(
|
|
2894
|
-
soil_types: list[dict],
|
|
2895
|
-
usda_soil_types: list[dict],
|
|
2896
2372
|
*,
|
|
2897
2373
|
key: IpccSoilCategory,
|
|
2374
|
+
soil_types: list[dict],
|
|
2375
|
+
usda_soil_types: list[dict],
|
|
2898
2376
|
**_
|
|
2899
2377
|
) -> bool:
|
|
2900
2378
|
"""
|
|
@@ -2902,12 +2380,12 @@ def _check_soil_category(
|
|
|
2902
2380
|
|
|
2903
2381
|
Parameters
|
|
2904
2382
|
----------
|
|
2383
|
+
key : IpccSoilCategory
|
|
2384
|
+
The IPCC soil category to check.
|
|
2905
2385
|
soil_types : list[dict]
|
|
2906
2386
|
List of soil type measurement nodes.
|
|
2907
2387
|
usda_soil_types : list[dict]
|
|
2908
2388
|
List of USDA soil type measurement nodes
|
|
2909
|
-
key : IpccSoilCategory
|
|
2910
|
-
The IPCC soil category to check.
|
|
2911
2389
|
|
|
2912
2390
|
Returns
|
|
2913
2391
|
-------
|
|
@@ -2923,26 +2401,25 @@ def _check_soil_category(
|
|
|
2923
2401
|
soil_types,
|
|
2924
2402
|
lookup=SOIL_TYPE_LOOKUP,
|
|
2925
2403
|
target_lookup_values=target_lookup_values,
|
|
2926
|
-
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
2927
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
2404
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
2928
2405
|
)
|
|
2929
2406
|
|
|
2930
2407
|
is_usda_soil_type_match = cumulative_nodes_lookup_match(
|
|
2931
2408
|
usda_soil_types,
|
|
2932
2409
|
lookup=USDA_SOIL_TYPE_LOOKUP,
|
|
2933
2410
|
target_lookup_values=target_lookup_values,
|
|
2934
|
-
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
2935
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
2411
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
2936
2412
|
)
|
|
2937
2413
|
|
|
2938
2414
|
return is_soil_type_match or is_usda_soil_type_match
|
|
2939
2415
|
|
|
2940
2416
|
|
|
2941
|
-
def
|
|
2417
|
+
def _check_sandy_soil_category(
|
|
2418
|
+
*,
|
|
2419
|
+
key: IpccSoilCategory,
|
|
2942
2420
|
soil_types: list[dict],
|
|
2943
2421
|
usda_soil_types: list[dict],
|
|
2944
|
-
|
|
2945
|
-
is_sandy: bool,
|
|
2422
|
+
has_sandy_soil: bool,
|
|
2946
2423
|
**_
|
|
2947
2424
|
) -> bool:
|
|
2948
2425
|
"""
|
|
@@ -2952,11 +2429,13 @@ def _check_sandy_soils(
|
|
|
2952
2429
|
|
|
2953
2430
|
Parameters
|
|
2954
2431
|
----------
|
|
2432
|
+
key : IpccSoilCategory
|
|
2433
|
+
The IPCC soil category to check.
|
|
2955
2434
|
soil_types : list[dict]
|
|
2956
2435
|
List of soil type measurement nodes.
|
|
2957
2436
|
usda_soil_types : list[dict]
|
|
2958
2437
|
List of USDA soil type measurement nodes
|
|
2959
|
-
|
|
2438
|
+
has_sandy_soil : bool
|
|
2960
2439
|
True if the soils are sandy, False otherwise.
|
|
2961
2440
|
|
|
2962
2441
|
Returns
|
|
@@ -2964,13 +2443,12 @@ def _check_sandy_soils(
|
|
|
2964
2443
|
bool
|
|
2965
2444
|
`True` if the soil category matches, `False` otherwise.
|
|
2966
2445
|
"""
|
|
2967
|
-
|
|
2968
|
-
return _check_soil_category(soil_types, usda_soil_types, key=KEY) or is_sandy
|
|
2446
|
+
return _check_soil_category(key=key, soil_types=soil_types, usda_soil_types=usda_soil_types) or has_sandy_soil
|
|
2969
2447
|
|
|
2970
2448
|
|
|
2971
2449
|
SOIL_CATEGORY_DECISION_TREE = {
|
|
2972
2450
|
IpccSoilCategory.ORGANIC_SOILS: _check_soil_category,
|
|
2973
|
-
IpccSoilCategory.SANDY_SOILS:
|
|
2451
|
+
IpccSoilCategory.SANDY_SOILS: _check_sandy_soil_category,
|
|
2974
2452
|
IpccSoilCategory.WETLAND_SOILS: _check_soil_category,
|
|
2975
2453
|
IpccSoilCategory.VOLCANIC_SOILS: _check_soil_category,
|
|
2976
2454
|
IpccSoilCategory.SPODIC_SOILS: _check_soil_category,
|
|
@@ -3009,19 +2487,17 @@ def _assign_ipcc_soil_category(
|
|
|
3009
2487
|
|
|
3010
2488
|
clay_content = get_node_value(find_term_match(measurements, CLAY_CONTENT_TERM_ID))
|
|
3011
2489
|
sand_content = get_node_value(find_term_match(measurements, SAND_CONTENT_TERM_ID))
|
|
3012
|
-
# clay_content = measurement_value(find_term_match(measurements, CLAY_CONTENT_TERM_ID))
|
|
3013
|
-
# sand_content = measurement_value(find_term_match(measurements, SAND_CONTENT_TERM_ID))
|
|
3014
2490
|
|
|
3015
|
-
|
|
2491
|
+
has_sandy_soil = clay_content < CLAY_CONTENT_MAX and sand_content > SAND_CONTENT_MIN
|
|
3016
2492
|
|
|
3017
2493
|
return next(
|
|
3018
2494
|
(
|
|
3019
2495
|
key for key in SOIL_CATEGORY_DECISION_TREE
|
|
3020
2496
|
if SOIL_CATEGORY_DECISION_TREE[key](
|
|
3021
|
-
soil_types,
|
|
3022
|
-
usda_soil_types,
|
|
3023
2497
|
key=key,
|
|
3024
|
-
|
|
2498
|
+
soil_types=soil_types,
|
|
2499
|
+
usda_soil_types=usda_soil_types,
|
|
2500
|
+
has_sandy_soil=has_sandy_soil
|
|
3025
2501
|
)
|
|
3026
2502
|
),
|
|
3027
2503
|
default
|
|
@@ -3048,182 +2524,174 @@ def _has_irrigation(water_regime_nodes: list[dict]) -> bool:
|
|
|
3048
2524
|
return cumulative_nodes_term_match(
|
|
3049
2525
|
water_regime_nodes,
|
|
3050
2526
|
target_term_ids=get_irrigated_terms(),
|
|
3051
|
-
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3052
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
2527
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3053
2528
|
)
|
|
3054
2529
|
|
|
3055
2530
|
|
|
3056
|
-
def
|
|
3057
|
-
site_type: str,
|
|
3058
|
-
*,
|
|
3059
|
-
key: IpccLandUseCategory,
|
|
3060
|
-
**_
|
|
3061
|
-
):
|
|
2531
|
+
def _has_long_fallow(land_cover_nodes: list[dict]) -> bool:
|
|
3062
2532
|
"""
|
|
3063
|
-
Check if
|
|
2533
|
+
Check if long fallow terms is present in the land cover nodes.
|
|
2534
|
+
|
|
2535
|
+
n.b., a super majority of the site area must be under long fallow for it to be classified as set aside.
|
|
3064
2536
|
|
|
3065
2537
|
Parameters
|
|
3066
2538
|
----------
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
key : IpccLandUseCategory
|
|
3070
|
-
The IPCC land use category to check.
|
|
2539
|
+
land_cover_nodes : List[dict]
|
|
2540
|
+
List of land cover nodes to be checked.
|
|
3071
2541
|
|
|
3072
2542
|
Returns
|
|
3073
2543
|
-------
|
|
3074
2544
|
bool
|
|
3075
|
-
`True` if
|
|
3076
|
-
|
|
2545
|
+
`True` if long fallow is present, `False` otherwise.
|
|
3077
2546
|
"""
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
2547
|
+
return cumulative_nodes_term_match(
|
|
2548
|
+
land_cover_nodes,
|
|
2549
|
+
target_term_ids=get_long_fallow_land_cover_terms(),
|
|
2550
|
+
cumulative_threshold=SUPER_MAJORITY_AREA_THRESHOLD
|
|
2551
|
+
) or cumulative_nodes_match(
|
|
2552
|
+
lambda node: get_node_property(node, LONG_FALLOW_CROP_TERM_ID, False).get("value", 0),
|
|
2553
|
+
land_cover_nodes,
|
|
2554
|
+
cumulative_threshold=SUPER_MAJORITY_AREA_THRESHOLD
|
|
2555
|
+
)
|
|
3081
2556
|
|
|
3082
2557
|
|
|
3083
|
-
def
|
|
3084
|
-
site_type: str,
|
|
3085
|
-
*,
|
|
3086
|
-
key: IpccLandUseCategory,
|
|
3087
|
-
land_cover_nodes: list[dict],
|
|
3088
|
-
**_
|
|
3089
|
-
) -> bool:
|
|
2558
|
+
def _has_upland_rice(land_cover_nodes: list[dict]) -> bool:
|
|
3090
2559
|
"""
|
|
3091
|
-
Check if
|
|
3092
|
-
|
|
3093
|
-
This function is special case of `_check_cropland_land_use_category`.
|
|
2560
|
+
Check if upland rice is present in the land cover nodes.
|
|
3094
2561
|
|
|
3095
2562
|
Parameters
|
|
3096
2563
|
----------
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
key : IpccLandUseCategory
|
|
3100
|
-
The IPCC land use category to check.
|
|
3101
|
-
land_cover_nodes : list[dict]
|
|
3102
|
-
List of land cover nodes.
|
|
2564
|
+
land_cover_nodes : List[dict]
|
|
2565
|
+
List of land cover nodes to be checked.
|
|
3103
2566
|
|
|
3104
2567
|
Returns
|
|
3105
2568
|
-------
|
|
3106
2569
|
bool
|
|
3107
|
-
`True` if
|
|
2570
|
+
`True` if upland rice is present, `False` otherwise.
|
|
3108
2571
|
"""
|
|
3109
|
-
|
|
3110
|
-
target_lookup_values = IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE.get(key, None)
|
|
3111
|
-
|
|
3112
|
-
return _check_ipcc_land_use_category(site_type, key=key) and cumulative_nodes_lookup_match(
|
|
2572
|
+
return cumulative_nodes_term_match(
|
|
3113
2573
|
land_cover_nodes,
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
cumulative_threshold=MIN_AREA_THRESHOLD,
|
|
3117
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
2574
|
+
target_term_ids=get_upland_rice_land_cover_terms(),
|
|
2575
|
+
cumulative_threshold=SUPER_MAJORITY_AREA_THRESHOLD
|
|
3118
2576
|
)
|
|
3119
2577
|
|
|
3120
2578
|
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
"""
|
|
3129
|
-
Check if the site type and land cover nodes match the target conditions for paddy rice cultivation.
|
|
2579
|
+
IPCC_LAND_USE_CATEGORY_TO_VALIDATION_KWARGS = {
|
|
2580
|
+
IpccLandUseCategory.SET_ASIDE: {"has_long_fallow"},
|
|
2581
|
+
IpccLandUseCategory.ANNUAL_CROPS_WET: {"has_wetland_soils"}
|
|
2582
|
+
}
|
|
2583
|
+
"""
|
|
2584
|
+
Keyword arguments that need to be checked for specific `IpccLandUseCategory`s.
|
|
2585
|
+
"""
|
|
3130
2586
|
|
|
3131
|
-
|
|
2587
|
+
|
|
2588
|
+
def _check_ipcc_land_use_category(*, key: IpccLandUseCategory, site_type: str, **kwargs) -> bool:
|
|
2589
|
+
"""
|
|
2590
|
+
Check if the site type matches the target site type for the given key.
|
|
3132
2591
|
|
|
3133
2592
|
Parameters
|
|
3134
2593
|
----------
|
|
2594
|
+
key : IpccLandUseCategory
|
|
2595
|
+
The IPCC land use category to check.
|
|
3135
2596
|
site_type : str
|
|
3136
2597
|
The site type to check.
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
2598
|
+
|
|
2599
|
+
Keyword Args
|
|
2600
|
+
------------
|
|
2601
|
+
has_long_fallow : bool
|
|
2602
|
+
Indicates whether long fallow is present on more than 30% of the site.
|
|
2603
|
+
has_wetland_soils : bool
|
|
2604
|
+
Indicates whether wetland soils are present to more than 30% of the site.
|
|
3141
2605
|
|
|
3142
2606
|
Returns
|
|
3143
2607
|
-------
|
|
3144
2608
|
bool
|
|
3145
2609
|
`True` if the conditions match the specified land use category, `False` otherwise.
|
|
2610
|
+
|
|
3146
2611
|
"""
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
2612
|
+
target_site_type = IPCC_LAND_USE_CATEGORY_TO_SITE_TYPE.get(key, None)
|
|
2613
|
+
validation_kwargs = IPCC_LAND_USE_CATEGORY_TO_VALIDATION_KWARGS.get(key, set())
|
|
2614
|
+
valid_kwargs = all(v for k, v in kwargs.items() if k in validation_kwargs)
|
|
2615
|
+
return site_type == target_site_type and valid_kwargs
|
|
3151
2616
|
|
|
3152
2617
|
|
|
3153
|
-
def
|
|
3154
|
-
site_type: str,
|
|
3155
|
-
*,
|
|
3156
|
-
land_cover_nodes: list[dict],
|
|
3157
|
-
has_long_fallow: bool,
|
|
3158
|
-
**_
|
|
2618
|
+
def _check_cropland_land_use_category(
|
|
2619
|
+
*, key: IpccLandUseCategory, site_type: str, land_cover_nodes: list[dict], **kwargs
|
|
3159
2620
|
) -> bool:
|
|
3160
2621
|
"""
|
|
3161
|
-
Check if the site type and land cover nodes match the target conditions for
|
|
2622
|
+
Check if the site type and land cover nodes match the target conditions for a cropland IpccLandUseCategory.
|
|
3162
2623
|
|
|
3163
|
-
This function is special case of `
|
|
2624
|
+
This function is special case of `_check_ipcc_land_use_category`.
|
|
3164
2625
|
|
|
3165
2626
|
Parameters
|
|
3166
2627
|
----------
|
|
2628
|
+
key : IpccLandUseCategory
|
|
2629
|
+
The IPCC land use category to check.
|
|
3167
2630
|
site_type : str
|
|
3168
2631
|
The site type to check.
|
|
3169
|
-
|
|
3170
|
-
|
|
2632
|
+
|
|
2633
|
+
Keyword Args
|
|
2634
|
+
------------
|
|
3171
2635
|
has_long_fallow : bool
|
|
3172
|
-
|
|
2636
|
+
Indicates whether long fallow is present on more than 30% of the site.
|
|
2637
|
+
has_wetland_soils : bool
|
|
2638
|
+
Indicates whether wetland soils are present to more than 30% of the site.
|
|
3173
2639
|
|
|
3174
2640
|
Returns
|
|
3175
2641
|
-------
|
|
3176
2642
|
bool
|
|
3177
2643
|
`True` if the conditions match the specified land use category, `False` otherwise.
|
|
3178
2644
|
"""
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
2645
|
+
LOOKUP = LOOKUPS["landCover"][0]
|
|
2646
|
+
target_lookup_values = IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE.get(key, None)
|
|
2647
|
+
valid_lookup = cumulative_nodes_lookup_match(
|
|
2648
|
+
land_cover_nodes,
|
|
2649
|
+
lookup=LOOKUP,
|
|
2650
|
+
target_lookup_values=target_lookup_values,
|
|
2651
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
2652
|
+
)
|
|
2653
|
+
return _check_ipcc_land_use_category(key=key, site_type=site_type, **kwargs) and valid_lookup
|
|
3183
2654
|
|
|
3184
2655
|
|
|
3185
|
-
def
|
|
3186
|
-
site_type: str,
|
|
3187
|
-
|
|
3188
|
-
land_cover_nodes: list[dict],
|
|
3189
|
-
has_wetland_soils: bool,
|
|
3190
|
-
has_long_fallow: bool,
|
|
3191
|
-
**_
|
|
3192
|
-
):
|
|
2656
|
+
def _check_paddy_rice_cultivation_land_use_category(
|
|
2657
|
+
*, key: IpccLandUseCategory, site_type: str, has_irrigated_upland_rice: bool, **kwargs
|
|
2658
|
+
) -> bool:
|
|
3193
2659
|
"""
|
|
3194
|
-
Check if the site type and land cover nodes match the target conditions for
|
|
2660
|
+
Check if the site type and land cover nodes match the target conditions for a cropland IpccLandUseCategory.
|
|
3195
2661
|
|
|
3196
2662
|
This function is special case of `_check_cropland_land_use_category`.
|
|
3197
2663
|
|
|
3198
2664
|
Parameters
|
|
3199
2665
|
----------
|
|
2666
|
+
key : IpccLandUseCategory
|
|
2667
|
+
The IPCC land use category to check.
|
|
3200
2668
|
site_type : str
|
|
3201
2669
|
The site type to check.
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
2670
|
+
|
|
2671
|
+
Keyword Args
|
|
2672
|
+
------------
|
|
2673
|
+
has_irrigated_upland_rice : bool
|
|
2674
|
+
Indicates whether irrigated upland rice is present on more than 30% of the site.
|
|
3206
2675
|
has_long_fallow : bool
|
|
3207
|
-
|
|
2676
|
+
Indicates whether long fallow is present on more than 30% of the site.
|
|
2677
|
+
has_wetland_soils : bool
|
|
2678
|
+
Indicates whether wetland soils are present to more than 30% of the site.
|
|
3208
2679
|
|
|
3209
2680
|
Returns
|
|
3210
2681
|
-------
|
|
3211
2682
|
bool
|
|
3212
2683
|
`True` if the conditions match the specified land use category, `False` otherwise.
|
|
3213
2684
|
"""
|
|
3214
|
-
|
|
3215
|
-
return _check_cropland_land_use_category(
|
|
3216
|
-
site_type, key=KEY, land_cover_nodes=land_cover_nodes
|
|
3217
|
-
) and has_wetland_soils and not has_long_fallow
|
|
2685
|
+
return _check_cropland_land_use_category(key=key, site_type=site_type, **kwargs) or has_irrigated_upland_rice
|
|
3218
2686
|
|
|
3219
2687
|
|
|
3220
2688
|
LAND_USE_CATEGORY_DECISION_TREE = {
|
|
3221
2689
|
IpccLandUseCategory.GRASSLAND: _check_ipcc_land_use_category,
|
|
3222
|
-
IpccLandUseCategory.PERENNIAL_CROPS: _check_cropland_land_use_category,
|
|
3223
|
-
IpccLandUseCategory.PADDY_RICE_CULTIVATION: _check_paddy_rice_cultivation_category,
|
|
3224
|
-
IpccLandUseCategory.ANNUAL_CROPS_WET: _check_annual_crops_wet_category,
|
|
3225
|
-
IpccLandUseCategory.ANNUAL_CROPS: _check_annual_crops_category,
|
|
3226
2690
|
IpccLandUseCategory.SET_ASIDE: _check_ipcc_land_use_category,
|
|
2691
|
+
IpccLandUseCategory.PERENNIAL_CROPS: _check_cropland_land_use_category,
|
|
2692
|
+
IpccLandUseCategory.PADDY_RICE_CULTIVATION: _check_paddy_rice_cultivation_land_use_category,
|
|
2693
|
+
IpccLandUseCategory.ANNUAL_CROPS_WET: _check_cropland_land_use_category,
|
|
2694
|
+
IpccLandUseCategory.ANNUAL_CROPS: _check_cropland_land_use_category,
|
|
3227
2695
|
IpccLandUseCategory.FOREST: _check_ipcc_land_use_category,
|
|
3228
2696
|
IpccLandUseCategory.NATIVE: _check_ipcc_land_use_category,
|
|
3229
2697
|
IpccLandUseCategory.OTHER: _check_ipcc_land_use_category
|
|
@@ -3238,9 +2706,7 @@ and land cover nodes.
|
|
|
3238
2706
|
|
|
3239
2707
|
|
|
3240
2708
|
def _assign_ipcc_land_use_category(
|
|
3241
|
-
site_type: str,
|
|
3242
|
-
management_nodes: list[dict],
|
|
3243
|
-
ipcc_soil_category: IpccSoilCategory
|
|
2709
|
+
site_type: str, management_nodes: list[dict], ipcc_soil_category: IpccSoilCategory
|
|
3244
2710
|
) -> IpccLandUseCategory:
|
|
3245
2711
|
"""
|
|
3246
2712
|
Assigns IPCC land use category based on site type, management nodes, and soil category.
|
|
@@ -3262,33 +2728,13 @@ def _assign_ipcc_land_use_category(
|
|
|
3262
2728
|
DECISION_TREE = LAND_USE_CATEGORY_DECISION_TREE
|
|
3263
2729
|
DEFAULT = IpccLandUseCategory.OTHER
|
|
3264
2730
|
|
|
3265
|
-
land_cover_nodes = filter_list_term_type(
|
|
3266
|
-
|
|
3267
|
-
)
|
|
3268
|
-
|
|
3269
|
-
water_regime_nodes = filter_list_term_type(
|
|
3270
|
-
management_nodes, [TermTermType.WATERREGIME]
|
|
3271
|
-
)
|
|
2731
|
+
land_cover_nodes = filter_list_term_type(management_nodes, [TermTermType.LANDCOVER])
|
|
2732
|
+
water_regime_nodes = filter_list_term_type(management_nodes, [TermTermType.WATERREGIME])
|
|
3272
2733
|
|
|
3273
2734
|
has_irrigation = _has_irrigation(water_regime_nodes)
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
target_term_ids=get_rice_plant_upland_terms(),
|
|
3278
|
-
cumulative_threshold=SUPER_MAJORITY_AREA_THRESHOLD,
|
|
3279
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
3280
|
-
)
|
|
3281
|
-
|
|
3282
|
-
is_irrigated_upland_rice = is_upland_rice and has_irrigation
|
|
3283
|
-
|
|
3284
|
-
# SUPER_MAJORITY_AREA_THRESHOLD
|
|
3285
|
-
has_long_fallow = cumulative_nodes_match(
|
|
3286
|
-
lambda node: get_node_property(node, LONG_FALLOW_CROP_TERM_ID, False).get("value", 0),
|
|
3287
|
-
land_cover_nodes,
|
|
3288
|
-
cumulative_threshold=SUPER_MAJORITY_AREA_THRESHOLD,
|
|
3289
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
3290
|
-
)
|
|
3291
|
-
|
|
2735
|
+
has_upland_rice = _has_upland_rice(land_cover_nodes)
|
|
2736
|
+
has_irrigated_upland_rice = has_upland_rice and has_irrigation
|
|
2737
|
+
has_long_fallow = _has_long_fallow(land_cover_nodes)
|
|
3292
2738
|
has_wetland_soils = ipcc_soil_category is IpccSoilCategory.WETLAND_SOILS
|
|
3293
2739
|
|
|
3294
2740
|
should_run = bool(site_type)
|
|
@@ -3297,11 +2743,11 @@ def _assign_ipcc_land_use_category(
|
|
|
3297
2743
|
(
|
|
3298
2744
|
key for key in DECISION_TREE
|
|
3299
2745
|
if DECISION_TREE[key](
|
|
3300
|
-
site_type,
|
|
3301
2746
|
key=key,
|
|
2747
|
+
site_type=site_type,
|
|
3302
2748
|
land_cover_nodes=land_cover_nodes,
|
|
3303
|
-
is_irrigated_upland_rice=is_irrigated_upland_rice,
|
|
3304
2749
|
has_long_fallow=has_long_fallow,
|
|
2750
|
+
has_irrigated_upland_rice=has_irrigated_upland_rice,
|
|
3305
2751
|
has_wetland_soils=has_wetland_soils
|
|
3306
2752
|
)
|
|
3307
2753
|
),
|
|
@@ -3313,20 +2759,17 @@ def _assign_ipcc_land_use_category(
|
|
|
3313
2759
|
|
|
3314
2760
|
|
|
3315
2761
|
def _check_grassland_ipcc_management_category(
|
|
3316
|
-
*,
|
|
3317
|
-
land_cover_nodes: list[dict],
|
|
3318
|
-
key: IpccManagementCategory,
|
|
3319
|
-
**_
|
|
2762
|
+
*, key: IpccManagementCategory, land_cover_nodes: list[dict], **_
|
|
3320
2763
|
) -> bool:
|
|
3321
2764
|
"""
|
|
3322
2765
|
Check if the land cover nodes match the target conditions for a grassland IpccManagementCategory.
|
|
3323
2766
|
|
|
3324
2767
|
Parameters
|
|
3325
2768
|
----------
|
|
3326
|
-
land_cover_nodes : List[dict]
|
|
3327
|
-
List of land cover nodes to be checked.
|
|
3328
2769
|
key : IpccManagementCategory
|
|
3329
2770
|
The IPCC management category to check.
|
|
2771
|
+
land_cover_nodes : List[dict]
|
|
2772
|
+
List of land cover nodes to be checked.
|
|
3330
2773
|
|
|
3331
2774
|
Returns
|
|
3332
2775
|
-------
|
|
@@ -3334,25 +2777,25 @@ def _check_grassland_ipcc_management_category(
|
|
|
3334
2777
|
`True` if the conditions match the specified management category, `False` otherwise.
|
|
3335
2778
|
"""
|
|
3336
2779
|
target_term_id = IPCC_MANAGEMENT_CATEGORY_TO_GRASSLAND_MANAGEMENT_TERM_ID.get(key, None)
|
|
3337
|
-
|
|
3338
2780
|
return cumulative_nodes_term_match(
|
|
3339
2781
|
land_cover_nodes,
|
|
3340
2782
|
target_term_ids=target_term_id,
|
|
3341
|
-
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3342
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
2783
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3343
2784
|
)
|
|
3344
2785
|
|
|
3345
2786
|
|
|
3346
|
-
def _check_tillage_ipcc_management_category(
|
|
2787
|
+
def _check_tillage_ipcc_management_category(
|
|
2788
|
+
*, key: IpccManagementCategory, tillage_nodes: list[dict], **_
|
|
2789
|
+
) -> bool:
|
|
3347
2790
|
"""
|
|
3348
2791
|
Check if the tillage nodes match the target conditions for a tillage IpccManagementCategory.
|
|
3349
2792
|
|
|
3350
2793
|
Parameters
|
|
3351
2794
|
----------
|
|
3352
|
-
tillage_nodes : List[dict]
|
|
3353
|
-
List of tillage nodes to be checked.
|
|
3354
2795
|
key : IpccManagementCategory
|
|
3355
2796
|
The IPCC management category to check.
|
|
2797
|
+
tillage_nodes : List[dict]
|
|
2798
|
+
List of tillage nodes to be checked.
|
|
3356
2799
|
|
|
3357
2800
|
Returns
|
|
3358
2801
|
-------
|
|
@@ -3361,13 +2804,11 @@ def _check_tillage_ipcc_management_category(*, tillage_nodes: list[dict], key: I
|
|
|
3361
2804
|
"""
|
|
3362
2805
|
LOOKUP = LOOKUPS["tillage"]
|
|
3363
2806
|
target_lookup_values = IPCC_MANAGEMENT_CATEGORY_TO_TILLAGE_MANAGEMENT_LOOKUP_VALUE.get(key, None)
|
|
3364
|
-
|
|
3365
2807
|
return cumulative_nodes_lookup_match(
|
|
3366
2808
|
tillage_nodes,
|
|
3367
2809
|
lookup=LOOKUP,
|
|
3368
2810
|
target_lookup_values=target_lookup_values,
|
|
3369
|
-
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3370
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
2811
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3371
2812
|
)
|
|
3372
2813
|
|
|
3373
2814
|
|
|
@@ -3423,8 +2864,7 @@ Value: Default IPCC management category for the given land use category.
|
|
|
3423
2864
|
|
|
3424
2865
|
|
|
3425
2866
|
def _assign_ipcc_management_category(
|
|
3426
|
-
management_nodes: list[dict],
|
|
3427
|
-
ipcc_land_use_category: IpccLandUseCategory
|
|
2867
|
+
management_nodes: list[dict], ipcc_land_use_category: IpccLandUseCategory
|
|
3428
2868
|
) -> IpccManagementCategory:
|
|
3429
2869
|
"""
|
|
3430
2870
|
Assign an IPCC Management Category based on the given management nodes and IPCC Land Use Category.
|
|
@@ -3441,32 +2881,26 @@ def _assign_ipcc_management_category(
|
|
|
3441
2881
|
IpccManagementCategory
|
|
3442
2882
|
The assigned IPCC Management Category.
|
|
3443
2883
|
"""
|
|
3444
|
-
decision_tree = IPCC_LAND_USE_CATEGORY_TO_DECISION_TREE.get(
|
|
3445
|
-
ipcc_land_use_category, {}
|
|
3446
|
-
)
|
|
2884
|
+
decision_tree = IPCC_LAND_USE_CATEGORY_TO_DECISION_TREE.get(ipcc_land_use_category, {})
|
|
3447
2885
|
default = IPCC_LAND_USE_CATEGORY_TO_DEFAULT_IPCC_MANAGEMENT_CATEGORY.get(
|
|
3448
2886
|
ipcc_land_use_category, IpccManagementCategory.OTHER
|
|
3449
2887
|
)
|
|
3450
2888
|
|
|
3451
|
-
land_cover_nodes = filter_list_term_type(
|
|
3452
|
-
|
|
3453
|
-
)
|
|
3454
|
-
tillage_nodes = filter_list_term_type(
|
|
3455
|
-
management_nodes, [TermTermType.TILLAGE]
|
|
3456
|
-
)
|
|
2889
|
+
land_cover_nodes = filter_list_term_type(management_nodes, [TermTermType.LANDCOVER])
|
|
2890
|
+
tillage_nodes = filter_list_term_type(management_nodes, [TermTermType.TILLAGE])
|
|
3457
2891
|
|
|
3458
|
-
should_run = (
|
|
3459
|
-
len(land_cover_nodes) > 0
|
|
3460
|
-
|
|
3461
|
-
)
|
|
2892
|
+
should_run = any([
|
|
2893
|
+
decision_tree == GRASSLAND_IPCC_MANAGEMENT_CATEGORY_DECISION_TREE and len(land_cover_nodes) > 0,
|
|
2894
|
+
decision_tree == TILLAGE_IPCC_MANAGEMENT_CATEGORY_DECISION_TREE and len(tillage_nodes) > 0
|
|
2895
|
+
])
|
|
3462
2896
|
|
|
3463
2897
|
return next(
|
|
3464
2898
|
(
|
|
3465
2899
|
key for key in decision_tree
|
|
3466
2900
|
if decision_tree[key](
|
|
2901
|
+
key=key,
|
|
3467
2902
|
land_cover_nodes=land_cover_nodes,
|
|
3468
2903
|
tillage_nodes=tillage_nodes,
|
|
3469
|
-
key=key
|
|
3470
2904
|
)
|
|
3471
2905
|
),
|
|
3472
2906
|
default
|
|
@@ -3489,9 +2923,7 @@ Value: Minimum number of improvements required for the corresponding Grassland C
|
|
|
3489
2923
|
|
|
3490
2924
|
|
|
3491
2925
|
def _check_grassland_ipcc_carbon_input_category(
|
|
3492
|
-
|
|
3493
|
-
*,
|
|
3494
|
-
key: IpccCarbonInputCategory
|
|
2926
|
+
*, key: IpccCarbonInputCategory, num_grassland_improvements: int, **_,
|
|
3495
2927
|
) -> bool:
|
|
3496
2928
|
"""
|
|
3497
2929
|
Checks if the given carbon input arguments satisfy the conditions for a specific
|
|
@@ -3499,24 +2931,26 @@ def _check_grassland_ipcc_carbon_input_category(
|
|
|
3499
2931
|
|
|
3500
2932
|
Parameters
|
|
3501
2933
|
----------
|
|
3502
|
-
carbon_input_args : CarbonInputArgs
|
|
3503
|
-
The carbon input arguments.
|
|
3504
2934
|
key : IpccCarbonInputCategory
|
|
3505
2935
|
The grassland IPCC Carbon Input Category to check.
|
|
2936
|
+
num_grassland_improvements : int
|
|
2937
|
+
The number of grassland improvements.
|
|
3506
2938
|
|
|
3507
2939
|
Returns
|
|
3508
2940
|
-------
|
|
3509
2941
|
bool
|
|
3510
2942
|
`True` if the conditions for the specified category are met; otherwise, `False`.
|
|
3511
2943
|
"""
|
|
3512
|
-
|
|
3513
|
-
GRASSLAND_IPCC_CARBON_INPUT_CATEGORY_TO_MIN_NUM_IMPROVEMENTS[key]
|
|
3514
|
-
)
|
|
3515
|
-
return carbon_input_args.num_grassland_improvements >= min_improvements
|
|
2944
|
+
return num_grassland_improvements >= GRASSLAND_IPCC_CARBON_INPUT_CATEGORY_TO_MIN_NUM_IMPROVEMENTS[key]
|
|
3516
2945
|
|
|
3517
2946
|
|
|
3518
2947
|
def _check_cropland_high_with_manure_category(
|
|
3519
|
-
|
|
2948
|
+
*,
|
|
2949
|
+
has_animal_manure_used: bool,
|
|
2950
|
+
has_bare_fallow: bool,
|
|
2951
|
+
has_low_residue_producing_crops: bool,
|
|
2952
|
+
has_n_fixing_crop_or_inorganic_n_fertiliser_used: bool,
|
|
2953
|
+
has_residue_removed_or_burnt: bool,
|
|
3520
2954
|
**_
|
|
3521
2955
|
) -> Optional[int]:
|
|
3522
2956
|
"""
|
|
@@ -3524,8 +2958,16 @@ def _check_cropland_high_with_manure_category(
|
|
|
3524
2958
|
|
|
3525
2959
|
Parameters
|
|
3526
2960
|
----------
|
|
3527
|
-
|
|
3528
|
-
|
|
2961
|
+
has_animal_manure_used : bool
|
|
2962
|
+
Indicates whether animal manure is used on more than 30% of the site.
|
|
2963
|
+
has_bare_fallow : bool
|
|
2964
|
+
Indicates whether bare fallow is present on more than 30% of the site.
|
|
2965
|
+
has_low_residue_producing_crops : bool
|
|
2966
|
+
Indicates whether low residue-producing crops are present on more than 70% of the site.
|
|
2967
|
+
has_n_fixing_crop_or_inorganic_n_fertiliser_used : bool
|
|
2968
|
+
Indicates whether a nitrogen-fixing crop or inorganic nitrogen fertiliser is used on more than 30% of the site.
|
|
2969
|
+
has_residue_removed_or_burnt : bool
|
|
2970
|
+
Indicates whether residues are removed or burnt on more than 30% of the site.
|
|
3529
2971
|
|
|
3530
2972
|
Returns
|
|
3531
2973
|
-------
|
|
@@ -3534,11 +2976,11 @@ def _check_cropland_high_with_manure_category(
|
|
|
3534
2976
|
"""
|
|
3535
2977
|
conditions = {
|
|
3536
2978
|
1: all([
|
|
3537
|
-
not
|
|
3538
|
-
not
|
|
3539
|
-
not
|
|
3540
|
-
|
|
3541
|
-
|
|
2979
|
+
not has_residue_removed_or_burnt,
|
|
2980
|
+
not has_low_residue_producing_crops,
|
|
2981
|
+
not has_bare_fallow,
|
|
2982
|
+
has_n_fixing_crop_or_inorganic_n_fertiliser_used,
|
|
2983
|
+
has_animal_manure_used
|
|
3542
2984
|
])
|
|
3543
2985
|
}
|
|
3544
2986
|
|
|
@@ -3548,15 +2990,41 @@ def _check_cropland_high_with_manure_category(
|
|
|
3548
2990
|
|
|
3549
2991
|
|
|
3550
2992
|
def _check_cropland_high_without_manure_category(
|
|
3551
|
-
|
|
2993
|
+
*,
|
|
2994
|
+
has_animal_manure_used: bool,
|
|
2995
|
+
has_bare_fallow: bool,
|
|
2996
|
+
has_cover_crop: bool,
|
|
2997
|
+
has_irrigation: bool,
|
|
2998
|
+
has_low_residue_producing_crops: bool,
|
|
2999
|
+
has_n_fixing_crop_or_inorganic_n_fertiliser_used: bool,
|
|
3000
|
+
has_organic_fertiliser_or_soil_amendment_used: bool,
|
|
3001
|
+
has_practice_increasing_c_input: bool,
|
|
3002
|
+
has_residue_removed_or_burnt: bool,
|
|
3003
|
+
**_
|
|
3552
3004
|
) -> Optional[int]:
|
|
3553
3005
|
"""
|
|
3554
3006
|
Checks the Cropland High without Manure IPCC Carbon Input Category based on the given carbon input arguments.
|
|
3555
3007
|
|
|
3556
3008
|
Parameters
|
|
3557
3009
|
----------
|
|
3558
|
-
|
|
3559
|
-
|
|
3010
|
+
has_animal_manure_used : bool
|
|
3011
|
+
Indicates whether animal manure is used on more than 30% of the site.
|
|
3012
|
+
has_bare_fallow : bool
|
|
3013
|
+
Indicates whether bare fallow is present on more than 30% of the site.
|
|
3014
|
+
has_cover_crop : bool
|
|
3015
|
+
Indicates whether cover crops are present on more than 30% of the site.
|
|
3016
|
+
has_irrigation : bool
|
|
3017
|
+
Indicates whether irrigation is applied to more than 30% of the site.
|
|
3018
|
+
has_low_residue_producing_crops : bool
|
|
3019
|
+
Indicates whether low residue-producing crops are present on more than 70% of the site.
|
|
3020
|
+
has_n_fixing_crop_or_inorganic_n_fertiliser_used : bool
|
|
3021
|
+
Indicates whether a nitrogen-fixing crop or inorganic nitrogen fertiliser is used on more than 30% of the site.
|
|
3022
|
+
has_organic_fertiliser_or_soil_amendment_used : bool
|
|
3023
|
+
Indicates whether organic fertiliser or soil amendments are used on more than 30% of the site.
|
|
3024
|
+
has_practice_increasing_c_input : bool
|
|
3025
|
+
Indicates whether practices increasing carbon input are present on more than 30% of the site.
|
|
3026
|
+
has_residue_removed_or_burnt : bool
|
|
3027
|
+
Indicates whether residues are removed or burnt on more than 30% of the site.
|
|
3560
3028
|
|
|
3561
3029
|
Returns
|
|
3562
3030
|
-------
|
|
@@ -3565,17 +3033,17 @@ def _check_cropland_high_without_manure_category(
|
|
|
3565
3033
|
"""
|
|
3566
3034
|
conditions = {
|
|
3567
3035
|
1: all([
|
|
3568
|
-
not
|
|
3569
|
-
not
|
|
3570
|
-
not
|
|
3571
|
-
|
|
3036
|
+
not has_residue_removed_or_burnt,
|
|
3037
|
+
not has_low_residue_producing_crops,
|
|
3038
|
+
not has_bare_fallow,
|
|
3039
|
+
has_n_fixing_crop_or_inorganic_n_fertiliser_used,
|
|
3572
3040
|
any([
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3041
|
+
has_irrigation,
|
|
3042
|
+
has_practice_increasing_c_input,
|
|
3043
|
+
has_cover_crop,
|
|
3044
|
+
has_organic_fertiliser_or_soil_amendment_used
|
|
3577
3045
|
]),
|
|
3578
|
-
not
|
|
3046
|
+
not has_animal_manure_used
|
|
3579
3047
|
])
|
|
3580
3048
|
}
|
|
3581
3049
|
|
|
@@ -3585,7 +3053,16 @@ def _check_cropland_high_without_manure_category(
|
|
|
3585
3053
|
|
|
3586
3054
|
|
|
3587
3055
|
def _check_cropland_medium_category(
|
|
3588
|
-
|
|
3056
|
+
*,
|
|
3057
|
+
has_animal_manure_used: bool,
|
|
3058
|
+
has_bare_fallow: bool,
|
|
3059
|
+
has_cover_crop: bool,
|
|
3060
|
+
has_irrigation: bool,
|
|
3061
|
+
has_low_residue_producing_crops: bool,
|
|
3062
|
+
has_n_fixing_crop_or_inorganic_n_fertiliser_used: bool,
|
|
3063
|
+
has_organic_fertiliser_or_soil_amendment_used: bool,
|
|
3064
|
+
has_practice_increasing_c_input: bool,
|
|
3065
|
+
has_residue_removed_or_burnt: bool,
|
|
3589
3066
|
**_
|
|
3590
3067
|
) -> Optional[int]:
|
|
3591
3068
|
"""
|
|
@@ -3593,8 +3070,24 @@ def _check_cropland_medium_category(
|
|
|
3593
3070
|
|
|
3594
3071
|
Parameters
|
|
3595
3072
|
----------
|
|
3596
|
-
|
|
3597
|
-
|
|
3073
|
+
has_animal_manure_used : bool
|
|
3074
|
+
Indicates whether animal manure is used on more than 30% of the site.
|
|
3075
|
+
has_bare_fallow : bool
|
|
3076
|
+
Indicates whether bare fallow is present on more than 30% of the site.
|
|
3077
|
+
has_cover_crop : bool
|
|
3078
|
+
Indicates whether cover crops are present on more than 30% of the site.
|
|
3079
|
+
has_irrigation : bool
|
|
3080
|
+
Indicates whether irrigation is applied to more than 30% of the site.
|
|
3081
|
+
has_low_residue_producing_crops : bool
|
|
3082
|
+
Indicates whether low residue-producing crops are present on more than 70% of the site.
|
|
3083
|
+
has_n_fixing_crop_or_inorganic_n_fertiliser_used : bool
|
|
3084
|
+
Indicates whether a nitrogen-fixing crop or inorganic nitrogen fertiliser is used on more than 30% of the site.
|
|
3085
|
+
has_organic_fertiliser_or_soil_amendment_used : bool
|
|
3086
|
+
Indicates whether organic fertiliser or soil amendments are used on more than 30% of the site.
|
|
3087
|
+
has_practice_increasing_c_input : bool
|
|
3088
|
+
Indicates whether practices increasing carbon input are present on more than 30% of the site.
|
|
3089
|
+
has_residue_removed_or_burnt : bool
|
|
3090
|
+
Indicates whether residues are removed or burnt on more than 30% of the site.
|
|
3598
3091
|
|
|
3599
3092
|
Returns
|
|
3600
3093
|
-------
|
|
@@ -3603,43 +3096,43 @@ def _check_cropland_medium_category(
|
|
|
3603
3096
|
"""
|
|
3604
3097
|
conditions = {
|
|
3605
3098
|
1: all([
|
|
3606
|
-
|
|
3607
|
-
|
|
3099
|
+
has_residue_removed_or_burnt,
|
|
3100
|
+
has_animal_manure_used
|
|
3608
3101
|
]),
|
|
3609
3102
|
2: all([
|
|
3610
|
-
not
|
|
3103
|
+
not has_residue_removed_or_burnt,
|
|
3611
3104
|
any([
|
|
3612
|
-
|
|
3613
|
-
|
|
3105
|
+
has_low_residue_producing_crops,
|
|
3106
|
+
has_bare_fallow
|
|
3614
3107
|
]),
|
|
3615
3108
|
any([
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3109
|
+
has_irrigation,
|
|
3110
|
+
has_practice_increasing_c_input,
|
|
3111
|
+
has_cover_crop,
|
|
3112
|
+
has_organic_fertiliser_or_soil_amendment_used,
|
|
3620
3113
|
])
|
|
3621
3114
|
]),
|
|
3622
3115
|
3: all([
|
|
3623
|
-
not
|
|
3624
|
-
not
|
|
3625
|
-
not
|
|
3626
|
-
not
|
|
3116
|
+
not has_residue_removed_or_burnt,
|
|
3117
|
+
not has_low_residue_producing_crops,
|
|
3118
|
+
not has_bare_fallow,
|
|
3119
|
+
not has_n_fixing_crop_or_inorganic_n_fertiliser_used,
|
|
3627
3120
|
any([
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3121
|
+
has_irrigation,
|
|
3122
|
+
has_practice_increasing_c_input,
|
|
3123
|
+
has_cover_crop,
|
|
3124
|
+
has_organic_fertiliser_or_soil_amendment_used
|
|
3632
3125
|
])
|
|
3633
3126
|
]),
|
|
3634
3127
|
4: all([
|
|
3635
|
-
not
|
|
3636
|
-
not
|
|
3637
|
-
not
|
|
3638
|
-
|
|
3639
|
-
not
|
|
3640
|
-
not
|
|
3641
|
-
not
|
|
3642
|
-
not
|
|
3128
|
+
not has_residue_removed_or_burnt,
|
|
3129
|
+
not has_low_residue_producing_crops,
|
|
3130
|
+
not has_bare_fallow,
|
|
3131
|
+
has_n_fixing_crop_or_inorganic_n_fertiliser_used,
|
|
3132
|
+
not has_irrigation,
|
|
3133
|
+
not has_organic_fertiliser_or_soil_amendment_used,
|
|
3134
|
+
not has_practice_increasing_c_input,
|
|
3135
|
+
not has_cover_crop
|
|
3643
3136
|
])
|
|
3644
3137
|
}
|
|
3645
3138
|
|
|
@@ -3649,7 +3142,16 @@ def _check_cropland_medium_category(
|
|
|
3649
3142
|
|
|
3650
3143
|
|
|
3651
3144
|
def _check_cropland_low_category(
|
|
3652
|
-
|
|
3145
|
+
*,
|
|
3146
|
+
has_animal_manure_used: bool,
|
|
3147
|
+
has_bare_fallow: bool,
|
|
3148
|
+
has_cover_crop: bool,
|
|
3149
|
+
has_irrigation: bool,
|
|
3150
|
+
has_low_residue_producing_crops: bool,
|
|
3151
|
+
has_n_fixing_crop_or_inorganic_n_fertiliser_used: bool,
|
|
3152
|
+
has_organic_fertiliser_or_soil_amendment_used: bool,
|
|
3153
|
+
has_practice_increasing_c_input: bool,
|
|
3154
|
+
has_residue_removed_or_burnt: bool,
|
|
3653
3155
|
**_
|
|
3654
3156
|
) -> Optional[int]:
|
|
3655
3157
|
"""
|
|
@@ -3657,8 +3159,24 @@ def _check_cropland_low_category(
|
|
|
3657
3159
|
|
|
3658
3160
|
Parameters
|
|
3659
3161
|
----------
|
|
3660
|
-
|
|
3661
|
-
|
|
3162
|
+
has_animal_manure_used : bool
|
|
3163
|
+
Indicates whether animal manure is used on more than 30% of the site.
|
|
3164
|
+
has_bare_fallow : bool
|
|
3165
|
+
Indicates whether bare fallow is present on more than 30% of the site.
|
|
3166
|
+
has_cover_crop : bool
|
|
3167
|
+
Indicates whether cover crops are present on more than 30% of the site.
|
|
3168
|
+
has_irrigation : bool
|
|
3169
|
+
Indicates whether irrigation is applied to more than 30% of the site.
|
|
3170
|
+
has_low_residue_producing_crops : bool
|
|
3171
|
+
Indicates whether low residue-producing crops are present on more than 70% of the site.
|
|
3172
|
+
has_n_fixing_crop_or_inorganic_n_fertiliser_used : bool
|
|
3173
|
+
Indicates whether a nitrogen-fixing crop or inorganic nitrogen fertiliser is used on more than 30% of the site.
|
|
3174
|
+
has_organic_fertiliser_or_soil_amendment_used : bool
|
|
3175
|
+
Indicates whether organic fertiliser or soil amendments are used on more than 30% of the site.
|
|
3176
|
+
has_practice_increasing_c_input : bool
|
|
3177
|
+
Indicates whether practices increasing carbon input are present on more than 30% of the site.
|
|
3178
|
+
has_residue_removed_or_burnt : bool
|
|
3179
|
+
Indicates whether residues are removed or burnt on more than 30% of the site.
|
|
3662
3180
|
|
|
3663
3181
|
Returns
|
|
3664
3182
|
-------
|
|
@@ -3667,29 +3185,29 @@ def _check_cropland_low_category(
|
|
|
3667
3185
|
"""
|
|
3668
3186
|
conditions = {
|
|
3669
3187
|
1: all([
|
|
3670
|
-
|
|
3671
|
-
not
|
|
3188
|
+
has_residue_removed_or_burnt,
|
|
3189
|
+
not has_animal_manure_used
|
|
3672
3190
|
]),
|
|
3673
3191
|
2: all([
|
|
3674
|
-
not
|
|
3192
|
+
not has_residue_removed_or_burnt,
|
|
3675
3193
|
any([
|
|
3676
|
-
|
|
3677
|
-
|
|
3194
|
+
has_low_residue_producing_crops,
|
|
3195
|
+
has_bare_fallow
|
|
3678
3196
|
]),
|
|
3679
|
-
not
|
|
3680
|
-
not
|
|
3681
|
-
not
|
|
3682
|
-
not
|
|
3197
|
+
not has_irrigation,
|
|
3198
|
+
not has_practice_increasing_c_input,
|
|
3199
|
+
not has_cover_crop,
|
|
3200
|
+
not has_organic_fertiliser_or_soil_amendment_used
|
|
3683
3201
|
]),
|
|
3684
3202
|
3: all([
|
|
3685
|
-
not
|
|
3686
|
-
not
|
|
3687
|
-
not
|
|
3688
|
-
not
|
|
3689
|
-
not
|
|
3690
|
-
not
|
|
3691
|
-
not
|
|
3692
|
-
not
|
|
3203
|
+
not has_residue_removed_or_burnt,
|
|
3204
|
+
not has_low_residue_producing_crops,
|
|
3205
|
+
not has_bare_fallow,
|
|
3206
|
+
not has_n_fixing_crop_or_inorganic_n_fertiliser_used,
|
|
3207
|
+
not has_irrigation,
|
|
3208
|
+
not has_organic_fertiliser_or_soil_amendment_used,
|
|
3209
|
+
not has_practice_increasing_c_input,
|
|
3210
|
+
not has_cover_crop
|
|
3693
3211
|
])
|
|
3694
3212
|
}
|
|
3695
3213
|
|
|
@@ -3698,9 +3216,9 @@ def _check_cropland_low_category(
|
|
|
3698
3216
|
)
|
|
3699
3217
|
|
|
3700
3218
|
|
|
3701
|
-
def
|
|
3219
|
+
def _get_carbon_input_kwargs(
|
|
3702
3220
|
management_nodes: list[dict]
|
|
3703
|
-
) ->
|
|
3221
|
+
) -> dict:
|
|
3704
3222
|
"""
|
|
3705
3223
|
Creates CarbonInputArgs based on the provided list of management nodes.
|
|
3706
3224
|
|
|
@@ -3711,8 +3229,8 @@ def _make_carbon_input_args(
|
|
|
3711
3229
|
|
|
3712
3230
|
Returns
|
|
3713
3231
|
-------
|
|
3714
|
-
|
|
3715
|
-
The carbon input arguments.
|
|
3232
|
+
dict
|
|
3233
|
+
The carbon input keyword arguments.
|
|
3716
3234
|
"""
|
|
3717
3235
|
|
|
3718
3236
|
PRACTICE_INCREASING_C_INPUT_LOOKUP = LOOKUPS["landUseManagement"]
|
|
@@ -3727,62 +3245,56 @@ def _make_carbon_input_args(
|
|
|
3727
3245
|
ORGANIC_FERTILISER_USED_TERM_ID
|
|
3728
3246
|
}
|
|
3729
3247
|
|
|
3730
|
-
crop_residue_management_nodes = filter_list_term_type(
|
|
3731
|
-
|
|
3248
|
+
crop_residue_management_nodes = filter_list_term_type(management_nodes, [TermTermType.CROPRESIDUEMANAGEMENT])
|
|
3249
|
+
land_cover_nodes = filter_list_term_type(management_nodes, [TermTermType.LANDCOVER])
|
|
3250
|
+
land_use_management_nodes = filter_list_term_type(management_nodes, [TermTermType.LANDUSEMANAGEMENT])
|
|
3251
|
+
water_regime_nodes = filter_list_term_type(management_nodes, [TermTermType.WATERREGIME])
|
|
3252
|
+
|
|
3253
|
+
has_animal_manure_used = any(
|
|
3254
|
+
get_node_value(node) for node in land_use_management_nodes if node_term_match(node, ANIMAL_MANURE_USED_TERM_ID)
|
|
3732
3255
|
)
|
|
3733
3256
|
|
|
3734
|
-
|
|
3735
|
-
|
|
3257
|
+
has_bare_fallow = cumulative_nodes_term_match(
|
|
3258
|
+
land_cover_nodes,
|
|
3259
|
+
target_term_ids=SHORT_BARE_FALLOW_TERM_ID,
|
|
3260
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3736
3261
|
)
|
|
3737
3262
|
|
|
3738
|
-
|
|
3739
|
-
|
|
3263
|
+
has_cover_crop = cumulative_nodes_match(
|
|
3264
|
+
lambda node: any(
|
|
3265
|
+
get_node_property(node, term_id, False).get("value", False) for term_id in get_cover_crop_property_terms()
|
|
3266
|
+
),
|
|
3267
|
+
land_cover_nodes,
|
|
3268
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3740
3269
|
)
|
|
3741
3270
|
|
|
3742
|
-
|
|
3743
|
-
|
|
3271
|
+
has_inorganic_n_fertiliser_used = any(
|
|
3272
|
+
get_node_value(node) for node in land_use_management_nodes
|
|
3273
|
+
if node_term_match(node, INORGANIC_NITROGEN_FERTILISER_USED_TERM_ID)
|
|
3744
3274
|
)
|
|
3745
3275
|
|
|
3746
3276
|
has_irrigation = _has_irrigation(water_regime_nodes)
|
|
3747
3277
|
|
|
3748
|
-
has_residue_removed_or_burnt = cumulative_nodes_term_match(
|
|
3749
|
-
crop_residue_management_nodes,
|
|
3750
|
-
target_term_ids=get_residue_removed_or_burnt_terms(),
|
|
3751
|
-
cumulative_threshold=MIN_AREA_THRESHOLD,
|
|
3752
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
3753
|
-
)
|
|
3754
|
-
|
|
3755
3278
|
# SUPER_MAJORITY_AREA_THRESHOLD
|
|
3756
3279
|
has_low_residue_producing_crops = cumulative_nodes_lookup_match(
|
|
3757
3280
|
land_cover_nodes,
|
|
3758
3281
|
lookup=LOW_RESIDUE_PRODUCING_CROP_LOOKUP,
|
|
3759
3282
|
target_lookup_values=True,
|
|
3760
|
-
cumulative_threshold=SUPER_MAJORITY_AREA_THRESHOLD
|
|
3761
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
3762
|
-
)
|
|
3763
|
-
|
|
3764
|
-
has_bare_fallow = cumulative_nodes_term_match(
|
|
3765
|
-
land_use_management_nodes,
|
|
3766
|
-
target_term_ids=SHORT_BARE_FALLOW_TERM_ID,
|
|
3767
|
-
cumulative_threshold=MIN_AREA_THRESHOLD,
|
|
3768
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
3283
|
+
cumulative_threshold=SUPER_MAJORITY_AREA_THRESHOLD
|
|
3769
3284
|
)
|
|
3770
3285
|
|
|
3771
3286
|
has_n_fixing_crop = cumulative_nodes_lookup_match(
|
|
3772
3287
|
land_cover_nodes,
|
|
3773
3288
|
lookup=N_FIXING_CROP_LOOKUP,
|
|
3774
3289
|
target_lookup_values=True,
|
|
3775
|
-
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3776
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
3290
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3777
3291
|
)
|
|
3778
3292
|
|
|
3779
|
-
|
|
3780
|
-
get_node_value(node) for node in land_use_management_nodes
|
|
3781
|
-
if node_term_match(node, INORGANIC_NITROGEN_FERTILISER_USED_TERM_ID)
|
|
3782
|
-
)
|
|
3293
|
+
has_n_fixing_crop_or_inorganic_n_fertiliser_used = has_n_fixing_crop or has_inorganic_n_fertiliser_used
|
|
3783
3294
|
|
|
3784
|
-
|
|
3785
|
-
|
|
3295
|
+
has_organic_fertiliser_or_soil_amendment_used = any(
|
|
3296
|
+
get_node_value(node) for node in land_use_management_nodes
|
|
3297
|
+
if node_term_match(node, ORGANIC_FERTILISER_USED_TERM_ID)
|
|
3786
3298
|
)
|
|
3787
3299
|
|
|
3788
3300
|
has_practice_increasing_c_input = cumulative_nodes_match(
|
|
@@ -3791,27 +3303,13 @@ def _make_carbon_input_args(
|
|
|
3791
3303
|
and not node_term_match(node, EXCLUDED_PRACTICE_TERM_IDS)
|
|
3792
3304
|
),
|
|
3793
3305
|
land_use_management_nodes,
|
|
3794
|
-
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3795
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
3306
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3796
3307
|
)
|
|
3797
3308
|
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
land_cover_nodes,
|
|
3803
|
-
cumulative_threshold=MIN_AREA_THRESHOLD,
|
|
3804
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
3805
|
-
)
|
|
3806
|
-
|
|
3807
|
-
has_organic_fertiliser_or_soil_amendment_used = any(
|
|
3808
|
-
get_node_value(node) for node in land_use_management_nodes
|
|
3809
|
-
if node_term_match(node, ORGANIC_FERTILISER_USED_TERM_ID)
|
|
3810
|
-
)
|
|
3811
|
-
|
|
3812
|
-
has_animal_manure_used = any(
|
|
3813
|
-
get_node_value(node) for node in land_use_management_nodes
|
|
3814
|
-
if node_term_match(node, ANIMAL_MANURE_USED_TERM_ID)
|
|
3309
|
+
has_residue_removed_or_burnt = cumulative_nodes_term_match(
|
|
3310
|
+
crop_residue_management_nodes,
|
|
3311
|
+
target_term_ids=get_residue_removed_or_burnt_terms(),
|
|
3312
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3815
3313
|
)
|
|
3816
3314
|
|
|
3817
3315
|
num_grassland_improvements = [
|
|
@@ -3821,18 +3319,18 @@ def _make_carbon_input_args(
|
|
|
3821
3319
|
has_organic_fertiliser_or_soil_amendment_used
|
|
3822
3320
|
].count(True)
|
|
3823
3321
|
|
|
3824
|
-
return
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
has_n_fixing_crop_or_inorganic_n_fertiliser_used
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3322
|
+
return {
|
|
3323
|
+
"has_animal_manure_used": has_animal_manure_used,
|
|
3324
|
+
"has_bare_fallow": has_bare_fallow,
|
|
3325
|
+
"has_cover_crop": has_cover_crop,
|
|
3326
|
+
"has_irrigation": has_irrigation,
|
|
3327
|
+
"has_low_residue_producing_crops": has_low_residue_producing_crops,
|
|
3328
|
+
"has_n_fixing_crop_or_inorganic_n_fertiliser_used": has_n_fixing_crop_or_inorganic_n_fertiliser_used,
|
|
3329
|
+
"has_organic_fertiliser_or_soil_amendment_used": has_organic_fertiliser_or_soil_amendment_used,
|
|
3330
|
+
"has_practice_increasing_c_input": has_practice_increasing_c_input,
|
|
3331
|
+
"has_residue_removed_or_burnt": has_residue_removed_or_burnt,
|
|
3332
|
+
"num_grassland_improvements": num_grassland_improvements
|
|
3333
|
+
}
|
|
3836
3334
|
|
|
3837
3335
|
|
|
3838
3336
|
GRASSLAND_IPCC_CARBON_INPUT_CATEGORY_DECISION_TREE = {
|
|
@@ -3908,14 +3406,12 @@ def _assign_ipcc_carbon_input_category(
|
|
|
3908
3406
|
decision_tree = DECISION_TREE_FROM_IPCC_MANAGEMENT_CATEGORY.get(ipcc_management_category, {})
|
|
3909
3407
|
default = DEFAULT_CARBON_INPUT_CATEGORY.get(ipcc_management_category, IpccCarbonInputCategory.OTHER)
|
|
3910
3408
|
|
|
3911
|
-
carbon_input_args = _make_carbon_input_args(management_nodes)
|
|
3912
|
-
|
|
3913
3409
|
should_run = len(management_nodes) > 0
|
|
3914
3410
|
|
|
3915
3411
|
return next(
|
|
3916
3412
|
(key for key in decision_tree if decision_tree[key](
|
|
3917
|
-
carbon_input_args,
|
|
3918
3413
|
key=key,
|
|
3414
|
+
**_get_carbon_input_kwargs(management_nodes)
|
|
3919
3415
|
)),
|
|
3920
3416
|
default
|
|
3921
3417
|
) if should_run else default
|
|
@@ -3924,50 +3420,477 @@ def _assign_ipcc_carbon_input_category(
|
|
|
3924
3420
|
# --- TIER 1 SOC MODEL ---
|
|
3925
3421
|
|
|
3926
3422
|
|
|
3927
|
-
def
|
|
3423
|
+
def _run_tier_1(
|
|
3424
|
+
inventory: dict,
|
|
3425
|
+
*,
|
|
3426
|
+
eco_climate_zone: int,
|
|
3427
|
+
soc_ref: float,
|
|
3428
|
+
**_
|
|
3429
|
+
) -> list[dict]:
|
|
3928
3430
|
"""
|
|
3929
|
-
|
|
3431
|
+
Run the IPCC (2019) Tier 1 methodology for calculating SOC stocks (in kg C ha-1) for each year in the inventory
|
|
3432
|
+
and wrap each of the calculated values in Hestia measurement nodes. To avoid any errors, the `inventory` parameter
|
|
3433
|
+
must be pre-validated by the `should_run` function.
|
|
3930
3434
|
|
|
3931
|
-
|
|
3435
|
+
See [IPCC (2019) Vol. 4, Ch. 2](https://www.ipcc-nggip.iges.or.jp/public/2019rf/vol4.html) for more information.
|
|
3436
|
+
|
|
3437
|
+
The inventory should be in the following shape:
|
|
3438
|
+
```
|
|
3439
|
+
{
|
|
3440
|
+
year (int): {
|
|
3441
|
+
_InventoryKey.SHOULD_RUN_TIER_1: bool,
|
|
3442
|
+
_InventoryKey.LU_CATEGORY: IpccLandUseCategory,
|
|
3443
|
+
_InventoryKey.MG_CATEGORY: IpccManagementCategory,
|
|
3444
|
+
_InventoryKey.CI_CATEGORY: IpccCarbonInputCategory
|
|
3445
|
+
},
|
|
3446
|
+
...
|
|
3447
|
+
}
|
|
3448
|
+
```
|
|
3932
3449
|
|
|
3933
3450
|
Parameters
|
|
3934
3451
|
----------
|
|
3935
|
-
|
|
3936
|
-
|
|
3452
|
+
inventory : dict
|
|
3453
|
+
The inventory built by the `_should_run` function.
|
|
3454
|
+
eco_climate_zone : int
|
|
3455
|
+
The eco-climate zone identifier for the site corresponding to a row in the
|
|
3456
|
+
[ecoClimateZone](https://gitlab.com/hestia-earth/hestia-glossary/-/blob/develop/Measurements/ecoClimateZone-lookup.csv)
|
|
3457
|
+
lookup table.
|
|
3458
|
+
ipcc_soil_category : IpccSoilCategory
|
|
3459
|
+
The reference condition SOC stock in the 0-30cm depth interval, kg C ha-1.
|
|
3937
3460
|
|
|
3938
3461
|
Returns
|
|
3939
3462
|
-------
|
|
3940
|
-
|
|
3941
|
-
|
|
3463
|
+
list[dict]
|
|
3464
|
+
A list of Hestia `Measurement` nodes containing the calculated SOC stocks and additional relevant data.
|
|
3942
3465
|
"""
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3466
|
+
|
|
3467
|
+
valid_inventory = {
|
|
3468
|
+
year: group for year, group in inventory.items() if group.get(_InventoryKey.SHOULD_RUN_TIER_1)
|
|
3469
|
+
}
|
|
3470
|
+
|
|
3471
|
+
timestamps = [year for year in valid_inventory.keys()]
|
|
3472
|
+
ipcc_land_use_categories = [group[_InventoryKey.LU_CATEGORY] for group in valid_inventory.values()]
|
|
3473
|
+
ipcc_management_categories = [group[_InventoryKey.MG_CATEGORY] for group in valid_inventory.values()]
|
|
3474
|
+
ipcc_carbon_input_categories = [group[_InventoryKey.CI_CATEGORY] for group in valid_inventory.values()]
|
|
3475
|
+
|
|
3476
|
+
iterated_timestamps, iterated_soc_equilibriums = _run_soc_equilibriums(
|
|
3477
|
+
timestamps,
|
|
3478
|
+
ipcc_land_use_categories,
|
|
3479
|
+
ipcc_management_categories,
|
|
3480
|
+
ipcc_carbon_input_categories,
|
|
3481
|
+
eco_climate_zone,
|
|
3482
|
+
soc_ref
|
|
3947
3483
|
)
|
|
3948
3484
|
|
|
3485
|
+
soc_stocks = _calc_tier_1_soc_stocks(iterated_timestamps, iterated_soc_equilibriums)
|
|
3949
3486
|
|
|
3950
|
-
|
|
3487
|
+
return [
|
|
3488
|
+
_measurement(
|
|
3489
|
+
year,
|
|
3490
|
+
soc_stock,
|
|
3491
|
+
MeasurementMethodClassification.TIER_1_MODEL.value
|
|
3492
|
+
) for year, soc_stock in zip(
|
|
3493
|
+
iterated_timestamps,
|
|
3494
|
+
soc_stocks
|
|
3495
|
+
)
|
|
3496
|
+
]
|
|
3497
|
+
|
|
3498
|
+
|
|
3499
|
+
# --- SHOULD RUN ---
|
|
3500
|
+
|
|
3501
|
+
|
|
3502
|
+
def _should_run(site: dict) -> tuple[bool, dict]:
|
|
3503
|
+
"""
|
|
3504
|
+
Extract data from site & related cycles, pre-process data and determine whether there is sufficient data to run the
|
|
3505
|
+
tier 1 and/or tier 2 model.
|
|
3506
|
+
|
|
3507
|
+
The inventory dict should be in the following shape:
|
|
3508
|
+
```
|
|
3509
|
+
{
|
|
3510
|
+
year (int): {
|
|
3511
|
+
_InventoryKey.SHOULD_RUN_TIER_2: bool,
|
|
3512
|
+
_InventoryKey.TEMP_MONTHLY: list[float],
|
|
3513
|
+
_InventoryKey.PRECIP_MONTHLY: list[float],
|
|
3514
|
+
_InventoryKey.PET_MONTHLY: list[float],
|
|
3515
|
+
_InventoryKey.IRRIGATED_MONTHLY: list[bool]
|
|
3516
|
+
_InventoryKey.CARBON_INPUT: float,
|
|
3517
|
+
_InventoryKey.N_CONTENT: float,
|
|
3518
|
+
_InventoryKey.TILLAGE_CATEGORY: IpccManagementCategory,
|
|
3519
|
+
_InventoryKey.SAND_CONTENT: float,
|
|
3520
|
+
_InventoryKey.SHOULD_RUN_TIER_1: bool,
|
|
3521
|
+
_InventoryKey.LU_CATEGORY: IpccLandUseCategory,
|
|
3522
|
+
_InventoryKey.MG_CATEGORY: IpccManagementCategory,
|
|
3523
|
+
_InventoryKey.CI_CATEGORY: IpccCarbonInputCategory
|
|
3524
|
+
},
|
|
3525
|
+
...
|
|
3526
|
+
}
|
|
3527
|
+
```
|
|
3528
|
+
|
|
3529
|
+
The kwargs dict should be in the following shape:
|
|
3530
|
+
```
|
|
3531
|
+
{
|
|
3532
|
+
"run_with_irrigation": bool,
|
|
3533
|
+
"eco_climate_zone": int,
|
|
3534
|
+
"ipcc_soil_category": IpccSoilCategory,
|
|
3535
|
+
"soc_ref": float
|
|
3536
|
+
}
|
|
3537
|
+
```
|
|
3951
3538
|
"""
|
|
3952
|
-
|
|
3539
|
+
site_type = site.get("siteType", "")
|
|
3540
|
+
management_nodes = site.get("management", [])
|
|
3541
|
+
measurement_nodes = site.get("measurements", [])
|
|
3542
|
+
cycles = related_cycles(site.get("@id"))
|
|
3543
|
+
|
|
3544
|
+
has_management = len(management_nodes) > 0
|
|
3545
|
+
has_measurements = len(measurement_nodes) > 0
|
|
3546
|
+
has_related_cycles = len(cycles) > 0
|
|
3547
|
+
has_functional_unit_1_ha = all(cycle.get("functionalUnit") in VALID_FUNCTIONAL_UNITS_TIER_2 for cycle in cycles)
|
|
3548
|
+
|
|
3549
|
+
should_build_inventory_tier_1 = all([
|
|
3550
|
+
site_type in VALID_SITE_TYPES_TIER_1,
|
|
3551
|
+
has_management,
|
|
3552
|
+
has_measurements
|
|
3553
|
+
])
|
|
3554
|
+
|
|
3555
|
+
should_build_inventory_tier_2 = all([
|
|
3556
|
+
site_type in VALID_SITE_TYPES_TIER_2,
|
|
3557
|
+
has_related_cycles,
|
|
3558
|
+
check_cycle_site_ids_identical(cycles),
|
|
3559
|
+
has_functional_unit_1_ha
|
|
3560
|
+
])
|
|
3561
|
+
|
|
3562
|
+
inventory_tier_1, kwargs_tier_1 = (
|
|
3563
|
+
_build_inventory_tier_1(site_type, management_nodes, measurement_nodes)
|
|
3564
|
+
if should_build_inventory_tier_1 else ({}, {})
|
|
3565
|
+
)
|
|
3566
|
+
|
|
3567
|
+
inventory_tier_2, kwargs_tier_2 = (
|
|
3568
|
+
_build_inventory_tier_2(cycles, measurement_nodes)
|
|
3569
|
+
if should_build_inventory_tier_2 else ({}, {})
|
|
3570
|
+
)
|
|
3571
|
+
|
|
3572
|
+
inventory = dict(sorted(merge(inventory_tier_1, inventory_tier_2).items()))
|
|
3573
|
+
kwargs = kwargs_tier_1 | kwargs_tier_2
|
|
3574
|
+
|
|
3575
|
+
should_run_tier_1 = _should_run_tier_1(inventory, **kwargs) if should_build_inventory_tier_1 else False
|
|
3576
|
+
should_run_tier_2 = _should_run_tier_2(inventory, **kwargs) if should_build_inventory_tier_2 else False
|
|
3577
|
+
|
|
3578
|
+
logRequirements(
|
|
3579
|
+
site, model=MODEL, term=TERM_ID,
|
|
3580
|
+
should_build_inventory_tier_1=should_build_inventory_tier_1,
|
|
3581
|
+
should_build_inventory_tier_2=should_build_inventory_tier_2,
|
|
3582
|
+
should_run_tier_1=should_run_tier_1,
|
|
3583
|
+
should_run_tier_2=should_run_tier_2,
|
|
3584
|
+
site_type=site_type,
|
|
3585
|
+
has_management=has_management,
|
|
3586
|
+
has_measurements=has_measurements,
|
|
3587
|
+
has_related_cycles=has_related_cycles,
|
|
3588
|
+
is_unit_hectare=has_functional_unit_1_ha,
|
|
3589
|
+
**kwargs,
|
|
3590
|
+
inventory=_log_inventory(inventory)
|
|
3591
|
+
)
|
|
3592
|
+
|
|
3593
|
+
should_run = should_run_tier_1 or should_run_tier_2
|
|
3594
|
+
logShouldRun(site, MODEL, TERM_ID, should_run)
|
|
3595
|
+
|
|
3596
|
+
return should_run_tier_1, should_run_tier_2, inventory, kwargs
|
|
3597
|
+
|
|
3598
|
+
|
|
3599
|
+
def _should_run_tier_1(
|
|
3600
|
+
inventory: dict,
|
|
3601
|
+
*,
|
|
3602
|
+
eco_climate_zone: int = None,
|
|
3603
|
+
soc_ref: float = None,
|
|
3604
|
+
**_
|
|
3605
|
+
) -> bool:
|
|
3606
|
+
"""
|
|
3607
|
+
Determines whether there is sufficient data in the inventory and keyword args to run the tier 1 model.
|
|
3608
|
+
"""
|
|
3609
|
+
return all([
|
|
3610
|
+
eco_climate_zone and eco_climate_zone > 0,
|
|
3611
|
+
soc_ref and soc_ref > 0,
|
|
3612
|
+
any(year for year, group in inventory.items() if group.get(_InventoryKey.SHOULD_RUN_TIER_1))
|
|
3613
|
+
])
|
|
3614
|
+
|
|
3615
|
+
|
|
3616
|
+
def _should_run_tier_2(
|
|
3617
|
+
inventory: dict,
|
|
3618
|
+
**_
|
|
3619
|
+
) -> bool:
|
|
3620
|
+
"""
|
|
3621
|
+
Determines whether there is sufficient data in the inventory and keyword args to run the tier 2 model.
|
|
3622
|
+
"""
|
|
3623
|
+
valid_years = [year for year, group in inventory.items() if group.get(_InventoryKey.SHOULD_RUN_TIER_2)]
|
|
3624
|
+
return all([
|
|
3625
|
+
len(valid_years) >= MIN_RUN_IN_PERIOD,
|
|
3626
|
+
check_consecutive(valid_years),
|
|
3627
|
+
any(inventory.get(year).get(_InventoryKey.SAND_CONTENT) for year in valid_years)
|
|
3628
|
+
])
|
|
3629
|
+
|
|
3630
|
+
|
|
3631
|
+
# --- LOGGING ---
|
|
3632
|
+
|
|
3633
|
+
|
|
3634
|
+
def _log_inventory(inventory: dict) -> str:
|
|
3635
|
+
"""
|
|
3636
|
+
Format the inventory data as a table for logging.
|
|
3637
|
+
"""
|
|
3638
|
+
log_table = log_as_table(
|
|
3639
|
+
{
|
|
3640
|
+
"year": year,
|
|
3641
|
+
"should-run-tier-1": group.get(_InventoryKey.SHOULD_RUN_TIER_1, False),
|
|
3642
|
+
"should-run-tier-2": group.get(_InventoryKey.SHOULD_RUN_TIER_2, False),
|
|
3643
|
+
"ipcc-land-use-category": (
|
|
3644
|
+
group.get(_InventoryKey.LU_CATEGORY).value if group.get(_InventoryKey.LU_CATEGORY) else None
|
|
3645
|
+
),
|
|
3646
|
+
"ipcc-management-category": (
|
|
3647
|
+
group.get(_InventoryKey.MG_CATEGORY).value if group.get(_InventoryKey.MG_CATEGORY) else None
|
|
3648
|
+
),
|
|
3649
|
+
"ipcc-carbon-input-category": (
|
|
3650
|
+
group.get(_InventoryKey.CI_CATEGORY).value if group.get(_InventoryKey.CI_CATEGORY) else None
|
|
3651
|
+
),
|
|
3652
|
+
"temperature-monthly": (
|
|
3653
|
+
" ".join(f"{val:.1f}" for val in group.get(_InventoryKey.TEMP_MONTHLY))
|
|
3654
|
+
if group.get(_InventoryKey.TEMP_MONTHLY) else None
|
|
3655
|
+
),
|
|
3656
|
+
"precipitation-monthly": (
|
|
3657
|
+
" ".join(f"{val:.1f}" for val in group.get(_InventoryKey.PRECIP_MONTHLY))
|
|
3658
|
+
if group.get(_InventoryKey.PRECIP_MONTHLY) else None
|
|
3659
|
+
),
|
|
3660
|
+
"pet-monthly": (
|
|
3661
|
+
" ".join(f"{val:.1f}" for val in group.get(_InventoryKey.PET_MONTHLY))
|
|
3662
|
+
if group.get(_InventoryKey.PET_MONTHLY) else None
|
|
3663
|
+
),
|
|
3664
|
+
"irrigated-monthly": (
|
|
3665
|
+
" ".join(str(val) for val in group.get(_InventoryKey.IRRIGATED_MONTHLY))
|
|
3666
|
+
if group.get(_InventoryKey.PET_MONTHLY) else None
|
|
3667
|
+
),
|
|
3668
|
+
"sand-content": group.get(_InventoryKey.SAND_CONTENT, None),
|
|
3669
|
+
"carbon-input": group.get(_InventoryKey.CARBON_INPUT, None),
|
|
3670
|
+
"n-content": group.get(_InventoryKey.N_CONTENT, None),
|
|
3671
|
+
"lignin-content": group.get(_InventoryKey.LIGNIN_CONTENT, None),
|
|
3672
|
+
"ipcc-tillage-category": (
|
|
3673
|
+
group.get(_InventoryKey.TILLAGE_CATEGORY).value if group.get(_InventoryKey.TILLAGE_CATEGORY) else None
|
|
3674
|
+
),
|
|
3675
|
+
"is-paddy-rice": group.get(_InventoryKey.IS_PADDY_RICE, None),
|
|
3676
|
+
} for year, group in inventory.items()
|
|
3677
|
+
)
|
|
3678
|
+
|
|
3679
|
+
return log_table or None
|
|
3680
|
+
|
|
3681
|
+
|
|
3682
|
+
# --- TIER 2 BUILD INVENTORY ---
|
|
3683
|
+
|
|
3684
|
+
|
|
3685
|
+
def _build_inventory_tier_2(
|
|
3686
|
+
cycles: list[dict], measurement_nodes: list[dict]
|
|
3687
|
+
) -> tuple[dict, dict]:
|
|
3688
|
+
"""
|
|
3689
|
+
Builds an annual inventory of data and a dictionary of keyword arguments for the tier 2 model.
|
|
3690
|
+
|
|
3691
|
+
TODO: implement long-term average climate data and annual climate data as back ups for monthly data
|
|
3692
|
+
"""
|
|
3693
|
+
grouped_cycles = group_nodes_by_year(cycles)
|
|
3694
|
+
grouped_measurements = group_nodes_by_year(measurement_nodes, mode=GroupNodesByYearMode.DATES)
|
|
3695
|
+
|
|
3696
|
+
grouped_climate_data = _get_grouped_climate_measurements(grouped_measurements)
|
|
3697
|
+
grouped_irrigated_monthly = _get_grouped_irrigated_monthly(grouped_cycles)
|
|
3698
|
+
grouped_sand_content_measurements = _get_grouped_sand_content_measurements(grouped_measurements)
|
|
3699
|
+
grouped_carbon_input_data = _get_grouped_carbon_input_data(grouped_cycles)
|
|
3700
|
+
grouped_tillage_categories = _get_grouped_tillage_categories(grouped_cycles)
|
|
3701
|
+
grouped_is_paddy_rice = _get_grouped_is_paddy_rice(grouped_cycles)
|
|
3702
|
+
|
|
3703
|
+
grouped_data = merge(
|
|
3704
|
+
grouped_climate_data,
|
|
3705
|
+
grouped_irrigated_monthly,
|
|
3706
|
+
grouped_sand_content_measurements,
|
|
3707
|
+
grouped_carbon_input_data,
|
|
3708
|
+
grouped_tillage_categories,
|
|
3709
|
+
grouped_is_paddy_rice
|
|
3710
|
+
)
|
|
3711
|
+
|
|
3712
|
+
grouped_should_run = {
|
|
3713
|
+
year: {_InventoryKey.SHOULD_RUN_TIER_2: _should_run_inventory_year_tier_2(group)}
|
|
3714
|
+
for year, group in grouped_data.items()
|
|
3715
|
+
}
|
|
3716
|
+
|
|
3717
|
+
inventory = merge(grouped_data, grouped_should_run)
|
|
3718
|
+
kwargs = {
|
|
3719
|
+
"run_with_irrigation": True
|
|
3720
|
+
}
|
|
3721
|
+
|
|
3722
|
+
return inventory, kwargs
|
|
3723
|
+
|
|
3724
|
+
|
|
3725
|
+
def _should_run_inventory_year_tier_2(group: dict) -> bool:
|
|
3726
|
+
"""
|
|
3727
|
+
Determines whether there is sufficient data in an inventory year to run the tier 2 model.
|
|
3728
|
+
|
|
3729
|
+
1. Check that the cycle is not for paddy rice.
|
|
3730
|
+
2. Check if monthly data has a value for each calendar month.
|
|
3731
|
+
3. Check if all required keys are present.
|
|
3953
3732
|
|
|
3954
3733
|
Parameters
|
|
3955
3734
|
----------
|
|
3956
|
-
|
|
3957
|
-
|
|
3735
|
+
group : dict
|
|
3736
|
+
Dictionary containing information for a specific inventory year.
|
|
3958
3737
|
|
|
3959
3738
|
Returns
|
|
3960
3739
|
-------
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3740
|
+
bool
|
|
3741
|
+
True if the inventory year is valid, False otherwise.
|
|
3742
|
+
"""
|
|
3743
|
+
monthly_data_complete = _check_12_months(
|
|
3744
|
+
group,
|
|
3745
|
+
{
|
|
3746
|
+
_InventoryKey.TEMP_MONTHLY,
|
|
3747
|
+
_InventoryKey.PRECIP_MONTHLY,
|
|
3748
|
+
_InventoryKey.PET_MONTHLY,
|
|
3749
|
+
_InventoryKey.IRRIGATED_MONTHLY
|
|
3750
|
+
}
|
|
3751
|
+
)
|
|
3752
|
+
|
|
3753
|
+
return all([
|
|
3754
|
+
not group.get(_InventoryKey.IS_PADDY_RICE),
|
|
3755
|
+
monthly_data_complete,
|
|
3756
|
+
all(key in group.keys() for key in REQUIRED_KEYS_TIER_2),
|
|
3757
|
+
])
|
|
3966
3758
|
|
|
3967
|
-
site_type = site.get("siteType", "")
|
|
3968
|
-
management_nodes = site.get("management", [])
|
|
3969
|
-
measurement_nodes = site.get("measurements", [])
|
|
3970
3759
|
|
|
3760
|
+
def _get_grouped_climate_measurements(grouped_measurements: dict) -> dict:
|
|
3761
|
+
return {
|
|
3762
|
+
year: {
|
|
3763
|
+
_InventoryKey.TEMP_MONTHLY: find_term_match(measurements, TEMPERATURE_MONTHLY_TERM_ID, {}).get("value", []),
|
|
3764
|
+
_InventoryKey.PRECIP_MONTHLY: (
|
|
3765
|
+
find_term_match(measurements, PRECIPITATION_MONTHLY_TERM_ID, {}).get("value", [])
|
|
3766
|
+
),
|
|
3767
|
+
_InventoryKey.PET_MONTHLY: find_term_match(measurements, PET_MONTHLY_TERM_ID, {}).get("value", [])
|
|
3768
|
+
} for year, measurements in grouped_measurements.items()
|
|
3769
|
+
}
|
|
3770
|
+
|
|
3771
|
+
|
|
3772
|
+
def _get_grouped_irrigated_monthly(grouped_cycles: dict) -> dict:
|
|
3773
|
+
return {
|
|
3774
|
+
year: {
|
|
3775
|
+
_InventoryKey.IRRIGATED_MONTHLY: _get_irrigated_monthly(year, cycles)
|
|
3776
|
+
} for year, cycles in grouped_cycles.items()
|
|
3777
|
+
}
|
|
3778
|
+
|
|
3779
|
+
|
|
3780
|
+
def _get_irrigated_monthly(year: int, cycles: list[dict]) -> list[bool]:
|
|
3781
|
+
# Get practice nodes and add "startDate" and "endDate" from cycle if missing.
|
|
3782
|
+
irrigation_nodes = non_empty_list(flatten([
|
|
3783
|
+
[
|
|
3784
|
+
{
|
|
3785
|
+
"startDate": cycle.get("startDate"),
|
|
3786
|
+
"endDate": cycle.get("endDate"),
|
|
3787
|
+
**node
|
|
3788
|
+
} for node in cycle.get("practices", [])
|
|
3789
|
+
] for cycle in cycles
|
|
3790
|
+
]))
|
|
3791
|
+
|
|
3792
|
+
grouped_nodes = group_nodes_by_year_and_month(irrigation_nodes)
|
|
3793
|
+
|
|
3794
|
+
# For each month (1 - 12) check if irrigation is present.
|
|
3795
|
+
return [
|
|
3796
|
+
cumulative_nodes_term_match(
|
|
3797
|
+
grouped_nodes.get(year, {}).get(month, []),
|
|
3798
|
+
target_term_ids=get_irrigated_terms(),
|
|
3799
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3800
|
+
) for month in range(1, 13)
|
|
3801
|
+
]
|
|
3802
|
+
|
|
3803
|
+
|
|
3804
|
+
def _get_grouped_sand_content_measurements(grouped_measurements: dict) -> dict:
|
|
3805
|
+
grouped_sand_content_measurements = {
|
|
3806
|
+
year: find_term_match(
|
|
3807
|
+
[m for m in measurements if m.get("depthUpper") == DEPTH_UPPER and m.get("depthLower") == DEPTH_LOWER],
|
|
3808
|
+
SAND_CONTENT_TERM_ID,
|
|
3809
|
+
{}
|
|
3810
|
+
) for year, measurements in grouped_measurements.items()
|
|
3811
|
+
}
|
|
3812
|
+
|
|
3813
|
+
return {
|
|
3814
|
+
year: {_InventoryKey.SAND_CONTENT: get_node_value(measurement)}
|
|
3815
|
+
for year, measurement in grouped_sand_content_measurements.items() if measurement
|
|
3816
|
+
}
|
|
3817
|
+
|
|
3818
|
+
|
|
3819
|
+
def _get_grouped_carbon_input_data(grouped_cycles: dict) -> dict:
|
|
3820
|
+
grouped_carbon_sources = {
|
|
3821
|
+
year: _get_carbon_sources_from_cycles(cycle)
|
|
3822
|
+
for year, cycle in grouped_cycles.items()
|
|
3823
|
+
}
|
|
3824
|
+
|
|
3825
|
+
return {
|
|
3826
|
+
year: {
|
|
3827
|
+
_InventoryKey.CARBON_INPUT: _calc_total_organic_carbon_input(carbon_sources),
|
|
3828
|
+
_InventoryKey.N_CONTENT: _calc_average_nitrogen_content_of_organic_carbon_sources(carbon_sources),
|
|
3829
|
+
_InventoryKey.LIGNIN_CONTENT: _calc_average_lignin_content_of_organic_carbon_sources(carbon_sources)
|
|
3830
|
+
} for year, carbon_sources in grouped_carbon_sources.items()
|
|
3831
|
+
}
|
|
3832
|
+
|
|
3833
|
+
|
|
3834
|
+
def _get_grouped_tillage_categories(grouped_cycles):
|
|
3835
|
+
return {
|
|
3836
|
+
year: {
|
|
3837
|
+
_InventoryKey.TILLAGE_CATEGORY: _assign_tier_2_ipcc_tillage_management_category(cycles)
|
|
3838
|
+
} for year, cycles in grouped_cycles.items()
|
|
3839
|
+
}
|
|
3840
|
+
|
|
3841
|
+
|
|
3842
|
+
def _get_grouped_is_paddy_rice(grouped_cycles: dict) -> dict:
|
|
3843
|
+
return {
|
|
3844
|
+
year: {
|
|
3845
|
+
_InventoryKey.IS_PADDY_RICE: _check_is_paddy_rice(cycles)
|
|
3846
|
+
} for year, cycles in grouped_cycles.items()
|
|
3847
|
+
}
|
|
3848
|
+
|
|
3849
|
+
|
|
3850
|
+
def _check_is_paddy_rice(cycles: list[dict]) -> bool:
|
|
3851
|
+
LOOKUP = LOOKUPS["crop"]
|
|
3852
|
+
TARGET_LOOKUP_VALUES = IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE.get(
|
|
3853
|
+
IpccLandUseCategory.PADDY_RICE_CULTIVATION, None
|
|
3854
|
+
)
|
|
3855
|
+
|
|
3856
|
+
has_paddy_rice_products = any(cumulative_nodes_lookup_match(
|
|
3857
|
+
filter_list_term_type(
|
|
3858
|
+
cycle.get("products", []) + cycle.get("practices", []),
|
|
3859
|
+
[TermTermType.CROP, TermTermType.FORAGE, TermTermType.LANDCOVER]
|
|
3860
|
+
),
|
|
3861
|
+
lookup=LOOKUP,
|
|
3862
|
+
target_lookup_values=TARGET_LOOKUP_VALUES,
|
|
3863
|
+
cumulative_threshold=MIN_YIELD_THRESHOLD,
|
|
3864
|
+
default_node_value=MIN_YIELD_THRESHOLD
|
|
3865
|
+
) for cycle in cycles)
|
|
3866
|
+
|
|
3867
|
+
has_upland_rice_products = any(cumulative_nodes_term_match(
|
|
3868
|
+
filter_list_term_type(
|
|
3869
|
+
cycle.get("products", []) + cycle.get("practices", []),
|
|
3870
|
+
[TermTermType.CROP, TermTermType.FORAGE, TermTermType.LANDCOVER]
|
|
3871
|
+
),
|
|
3872
|
+
target_term_ids=get_upland_rice_crop_terms() + get_upland_rice_land_cover_terms(),
|
|
3873
|
+
cumulative_threshold=MIN_YIELD_THRESHOLD,
|
|
3874
|
+
default_node_value=MIN_YIELD_THRESHOLD
|
|
3875
|
+
) for cycle in cycles)
|
|
3876
|
+
|
|
3877
|
+
has_irrigation = any(
|
|
3878
|
+
_has_irrigation(filter_list_term_type(cycle.get("practices", []), [TermTermType.WATERREGIME]))
|
|
3879
|
+
for cycle in cycles
|
|
3880
|
+
)
|
|
3881
|
+
|
|
3882
|
+
return has_paddy_rice_products or (has_upland_rice_products and has_irrigation)
|
|
3883
|
+
|
|
3884
|
+
|
|
3885
|
+
# --- TIER 1 BUILD INVENTORY ---
|
|
3886
|
+
|
|
3887
|
+
|
|
3888
|
+
def _build_inventory_tier_1(
|
|
3889
|
+
site_type: str, management_nodes: list[dict], measurement_nodes: list[dict]
|
|
3890
|
+
) -> tuple[dict, dict]:
|
|
3891
|
+
"""
|
|
3892
|
+
Builds an annual inventory of data and a dictionary of keyword arguments for the tier 2 model.
|
|
3893
|
+
"""
|
|
3971
3894
|
eco_climate_zone = _get_eco_climate_zone(measurement_nodes)
|
|
3972
3895
|
ipcc_soil_category = _assign_ipcc_soil_category(measurement_nodes)
|
|
3973
3896
|
soc_ref = _retrieve_soc_ref(eco_climate_zone, ipcc_soil_category)
|
|
@@ -3976,7 +3899,7 @@ def _should_run_tier_1(site: dict) -> tuple:
|
|
|
3976
3899
|
|
|
3977
3900
|
grouped_land_use_categories = {
|
|
3978
3901
|
year: {
|
|
3979
|
-
|
|
3902
|
+
_InventoryKey.LU_CATEGORY: _assign_ipcc_land_use_category(
|
|
3980
3903
|
site_type,
|
|
3981
3904
|
nodes,
|
|
3982
3905
|
ipcc_soil_category
|
|
@@ -3986,171 +3909,72 @@ def _should_run_tier_1(site: dict) -> tuple:
|
|
|
3986
3909
|
|
|
3987
3910
|
grouped_management_categories = {
|
|
3988
3911
|
year: {
|
|
3989
|
-
|
|
3912
|
+
_InventoryKey.MG_CATEGORY: _assign_ipcc_management_category(
|
|
3990
3913
|
nodes,
|
|
3991
|
-
grouped_land_use_categories[year][
|
|
3914
|
+
grouped_land_use_categories[year][_InventoryKey.LU_CATEGORY]
|
|
3992
3915
|
)
|
|
3993
3916
|
} for year, nodes in grouped_management.items()
|
|
3994
3917
|
}
|
|
3995
3918
|
|
|
3996
3919
|
grouped_carbon_input_categories = {
|
|
3997
3920
|
year: {
|
|
3998
|
-
|
|
3921
|
+
_InventoryKey.CI_CATEGORY: _assign_ipcc_carbon_input_category(
|
|
3999
3922
|
nodes,
|
|
4000
|
-
grouped_management_categories[year][
|
|
3923
|
+
grouped_management_categories[year][_InventoryKey.MG_CATEGORY]
|
|
4001
3924
|
)
|
|
4002
3925
|
} for year, nodes in grouped_management.items()
|
|
4003
3926
|
}
|
|
4004
3927
|
|
|
4005
|
-
grouped_data =
|
|
3928
|
+
grouped_data = merge(
|
|
4006
3929
|
grouped_land_use_categories,
|
|
4007
3930
|
grouped_management_categories,
|
|
4008
3931
|
grouped_carbon_input_categories
|
|
4009
|
-
])
|
|
4010
|
-
|
|
4011
|
-
complete_data = {
|
|
4012
|
-
year: inner_dict for year, inner_dict in grouped_data.items()
|
|
4013
|
-
if _should_run_inventory_year(inner_dict)
|
|
4014
|
-
}
|
|
4015
|
-
|
|
4016
|
-
timestamps = list(complete_data)
|
|
4017
|
-
start_year = timestamps[0] if timestamps else None
|
|
4018
|
-
end_year = timestamps[-1] if timestamps else None
|
|
4019
|
-
|
|
4020
|
-
soc_measurement_value, soc_measurement_date = most_relevant_measurement_value_by_depth_and_date(
|
|
4021
|
-
measurement_nodes,
|
|
4022
|
-
TERM_ID,
|
|
4023
|
-
f"{end_year}-12-31", # Last year in inventory
|
|
4024
|
-
DEPTH_UPPER,
|
|
4025
|
-
DEPTH_LOWER,
|
|
4026
|
-
depth_strict=True
|
|
4027
|
-
) if end_year else (None, None)
|
|
4028
|
-
|
|
4029
|
-
soc_measurement_datetime = safe_parse_date(soc_measurement_date)
|
|
4030
|
-
soc_measurement_year = (
|
|
4031
|
-
soc_measurement_datetime.year if soc_measurement_datetime else None
|
|
4032
3932
|
)
|
|
4033
3933
|
|
|
4034
|
-
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
should_run = all([
|
|
4039
|
-
soc_ref > 0,
|
|
4040
|
-
isinstance(eco_climate_zone, int) and eco_climate_zone > 0,
|
|
4041
|
-
len(timestamps) > 0,
|
|
4042
|
-
(
|
|
4043
|
-
start_year <= soc_measurement_year < end_year
|
|
4044
|
-
) if soc_measurement_value and soc_measurement_year else True
|
|
4045
|
-
])
|
|
3934
|
+
grouped_should_run = {
|
|
3935
|
+
year: {_InventoryKey.SHOULD_RUN_TIER_1: _should_run_inventory_year_tier_1(group)}
|
|
3936
|
+
for year, group in grouped_data.items()
|
|
3937
|
+
}
|
|
4046
3938
|
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
eco_climate_zone=eco_climate_zone,
|
|
4054
|
-
soc_ref=soc_ref,
|
|
4055
|
-
run_with_soc_measurement=(soc_measurement_value and soc_measurement_year),
|
|
4056
|
-
soc_measurement_value=soc_measurement_value,
|
|
4057
|
-
soc_measurement_year=soc_measurement_year
|
|
4058
|
-
)
|
|
3939
|
+
inventory = merge(grouped_data, grouped_should_run)
|
|
3940
|
+
kwargs = {
|
|
3941
|
+
"eco_climate_zone": eco_climate_zone,
|
|
3942
|
+
"ipcc_soil_category": ipcc_soil_category,
|
|
3943
|
+
"soc_ref": soc_ref,
|
|
3944
|
+
}
|
|
4059
3945
|
|
|
4060
|
-
return
|
|
4061
|
-
should_run,
|
|
4062
|
-
timestamps,
|
|
4063
|
-
ipcc_land_use_categories,
|
|
4064
|
-
ipcc_management_categories,
|
|
4065
|
-
ipcc_carbon_input_categories,
|
|
4066
|
-
eco_climate_zone,
|
|
4067
|
-
soc_ref,
|
|
4068
|
-
soc_measurement_value,
|
|
4069
|
-
soc_measurement_year
|
|
4070
|
-
)
|
|
3946
|
+
return inventory, kwargs
|
|
4071
3947
|
|
|
4072
3948
|
|
|
4073
|
-
def
|
|
4074
|
-
timestamps: list[int],
|
|
4075
|
-
ipcc_land_use_categories: list[IpccLandUseCategory],
|
|
4076
|
-
ipcc_management_categories: list[IpccManagementCategory],
|
|
4077
|
-
ipcc_carbon_input_categories: list[IpccCarbonInputCategory],
|
|
4078
|
-
eco_climate_zone: int,
|
|
4079
|
-
soc_ref: float,
|
|
4080
|
-
soc_measurement_value: Optional[float] = None,
|
|
4081
|
-
soc_measurement_year: Optional[int] = None
|
|
4082
|
-
) -> list[dict]:
|
|
3949
|
+
def _should_run_inventory_year_tier_1(group: dict) -> bool:
|
|
4083
3950
|
"""
|
|
4084
|
-
|
|
4085
|
-
and wrap each of the calculated values in Hestia measurement nodes.
|
|
3951
|
+
Determines whether there is sufficient data in an inventory year to run the tier 1 model.
|
|
4086
3952
|
|
|
4087
|
-
|
|
3953
|
+
1. Check if the land use category is not "OTHER"
|
|
3954
|
+
2. Check if all required keys are present.
|
|
4088
3955
|
|
|
4089
3956
|
Parameters
|
|
4090
3957
|
----------
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
ipcc_land_use_categories : list[IpccLandUseCategory]
|
|
4094
|
-
A list of IPCC land use categories for each year in the inventory.
|
|
4095
|
-
ipcc_management_categories : list[IpccManagementCategory]
|
|
4096
|
-
A list of IPCC management categories for each year in the inventory.
|
|
4097
|
-
ipcc_carbon_input_categories : list[IpccCarbonInputCategory]
|
|
4098
|
-
A list of IPCC carbon input categories for each year in the inventory.
|
|
4099
|
-
eco_climate_zone : int
|
|
4100
|
-
The eco-climate zone identifier for the site corresponding to a row in the
|
|
4101
|
-
[ecoClimateZone](https://gitlab.com/hestia-earth/hestia-glossary/-/blob/develop/Measurements/ecoClimateZone-lookup.csv)
|
|
4102
|
-
lookup table.
|
|
4103
|
-
ipcc_soil_category : IpccSoilCategory
|
|
4104
|
-
The reference condition SOC stock in the 0-30cm depth interval, kg C ha-1.
|
|
4105
|
-
soc_measurement_value : float | None, optional
|
|
4106
|
-
Measured SOC stock, if provided.
|
|
4107
|
-
soc_measurement_year : int | None, optional
|
|
4108
|
-
The year the SOC measurement took place, if provided.
|
|
3958
|
+
group : dict
|
|
3959
|
+
Dictionary containing information for a specific inventory year.
|
|
4109
3960
|
|
|
4110
3961
|
Returns
|
|
4111
3962
|
-------
|
|
4112
|
-
|
|
3963
|
+
bool
|
|
3964
|
+
True if the inventory year is valid, False otherwise.
|
|
4113
3965
|
"""
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
ipcc_management_categories,
|
|
4119
|
-
ipcc_carbon_input_categories,
|
|
4120
|
-
eco_climate_zone,
|
|
4121
|
-
soc_ref
|
|
4122
|
-
)
|
|
4123
|
-
|
|
4124
|
-
soc_stocks = _calc_tier_1_soc_stocks(iterated_timestamps, iterated_soc_equilibriums)
|
|
4125
|
-
|
|
4126
|
-
soc_measurement_index = _find_closest_value_index(iterated_timestamps, soc_measurement_year)
|
|
4127
|
-
|
|
4128
|
-
scaled_soc_stocks = _scale_soc_stocks(
|
|
4129
|
-
soc_stocks, soc_measurement_value, soc_measurement_index
|
|
4130
|
-
)
|
|
4131
|
-
|
|
4132
|
-
slice_index = soc_measurement_index or 0
|
|
4133
|
-
result_timestamps = iterated_timestamps[slice_index:]
|
|
4134
|
-
result_soc_stocks = scaled_soc_stocks[slice_index:]
|
|
4135
|
-
|
|
4136
|
-
return [
|
|
4137
|
-
_measurement(
|
|
4138
|
-
year,
|
|
4139
|
-
soc_stock,
|
|
4140
|
-
MeasurementMethodClassification.TIER_1_MODEL.value
|
|
4141
|
-
) for year, soc_stock in zip(
|
|
4142
|
-
result_timestamps,
|
|
4143
|
-
result_soc_stocks
|
|
4144
|
-
)
|
|
4145
|
-
]
|
|
3966
|
+
return all([
|
|
3967
|
+
group.get(_InventoryKey.LU_CATEGORY) != IpccLandUseCategory.OTHER,
|
|
3968
|
+
all(key in group.keys() for key in REQUIRED_KEYS_TIER_1),
|
|
3969
|
+
])
|
|
4146
3970
|
|
|
4147
3971
|
|
|
4148
|
-
# ---
|
|
3972
|
+
# --- RUN ---
|
|
4149
3973
|
|
|
4150
3974
|
|
|
4151
3975
|
def run(site: dict) -> list[dict]:
|
|
4152
3976
|
"""
|
|
4153
|
-
Check which
|
|
3977
|
+
Check which Tiers of IPCC SOC model to run, run it and return the formatted output.
|
|
4154
3978
|
|
|
4155
3979
|
Parameters
|
|
4156
3980
|
----------
|
|
@@ -4162,13 +3986,9 @@ def run(site: dict) -> list[dict]:
|
|
|
4162
3986
|
list[dict]
|
|
4163
3987
|
A list of Hestia `Measurement` nodes containing the calculated SOC stocks and additional relevant data.
|
|
4164
3988
|
"""
|
|
4165
|
-
should_run_tier_2,
|
|
4166
|
-
should_run_tier_1, *tier_1_args = (
|
|
4167
|
-
_should_run_tier_1(site) if not should_run_tier_2
|
|
4168
|
-
else (False, None)
|
|
4169
|
-
)
|
|
3989
|
+
should_run_tier_1, should_run_tier_2, inventory, kwargs = _should_run(site)
|
|
4170
3990
|
return (
|
|
4171
|
-
_run_tier_2(
|
|
4172
|
-
else _run_tier_1(
|
|
3991
|
+
_run_tier_2(inventory, **kwargs) if should_run_tier_2
|
|
3992
|
+
else _run_tier_1(inventory, **kwargs) if should_run_tier_1
|
|
4173
3993
|
else []
|
|
4174
3994
|
)
|