hestia-earth-models 0.58.0__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/{irrigated.py → irrigatedTypeUnspecified.py} +4 -4
- hestia_earth/models/cycle/residueIncorporated.py +1 -1
- hestia_earth/models/emepEea2019/nh3ToAirInorganicFertiliser.py +2 -2
- hestia_earth/models/geospatialDatabase/clayContent.py +17 -4
- hestia_earth/models/geospatialDatabase/sandContent.py +17 -4
- hestia_earth/models/impact_assessment/irrigated.py +0 -3
- hestia_earth/models/ipcc2019/co2ToAirSoilCarbonStockChangeManagementChange.py +10 -9
- hestia_earth/models/ipcc2019/n2OToAirCropResidueDecompositionDirect.py +4 -51
- hestia_earth/models/ipcc2019/n2OToAirInorganicFertiliserDirect.py +104 -0
- hestia_earth/models/ipcc2019/n2OToAirOrganicFertiliserDirect.py +105 -0
- hestia_earth/models/ipcc2019/organicCarbonPerHa.py +1059 -1220
- hestia_earth/models/ipcc2019/utils.py +82 -1
- hestia_earth/models/mocking/search-results.json +161 -87
- hestia_earth/models/site/management.py +12 -9
- hestia_earth/models/site/organicCarbonPerHa.py +251 -89
- hestia_earth/models/utils/blank_node.py +157 -34
- hestia_earth/models/utils/cycle.py +6 -3
- hestia_earth/models/utils/measurement.py +1 -1
- hestia_earth/models/utils/term.py +46 -1
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.58.0.dist-info → hestia_earth_models-0.59.0.dist-info}/METADATA +4 -8
- {hestia_earth_models-0.58.0.dist-info → hestia_earth_models-0.59.0.dist-info}/RECORD +34 -30
- tests/models/cycle/{test_irrigated.py → test_irrigatedTypeUnspecified.py} +1 -1
- tests/models/geospatialDatabase/test_clayContent.py +9 -3
- tests/models/geospatialDatabase/test_sandContent.py +9 -3
- 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/site/test_organicCarbonPerHa.py +51 -5
- tests/models/utils/test_blank_node.py +102 -42
- tests/models/utils/test_term.py +17 -3
- {hestia_earth_models-0.58.0.dist-info → hestia_earth_models-0.59.0.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.58.0.dist-info → hestia_earth_models-0.59.0.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.58.0.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,8 +47,10 @@ 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
|
|
|
59
56
|
from .utils import check_consecutive
|
|
@@ -116,6 +113,7 @@ REQUIREMENTS = {
|
|
|
116
113
|
}
|
|
117
114
|
}
|
|
118
115
|
LOOKUPS = {
|
|
116
|
+
"crop": "IPCC_LAND_USE_CATEGORY",
|
|
119
117
|
"ecoClimateZone": [
|
|
120
118
|
"IPCC_2019_SOC_REF_KG_C_HECTARE_SAN",
|
|
121
119
|
"IPCC_2019_SOC_REF_KG_C_HECTARE_WET",
|
|
@@ -169,6 +167,7 @@ TERM_ID = 'organicCarbonPerHa'
|
|
|
169
167
|
|
|
170
168
|
MIN_AREA_THRESHOLD = 30 # 30% as per IPCC guidelines
|
|
171
169
|
SUPER_MAJORITY_AREA_THRESHOLD = 100 - MIN_AREA_THRESHOLD
|
|
170
|
+
MIN_YIELD_THRESHOLD = 1
|
|
172
171
|
DEPTH_UPPER = 0
|
|
173
172
|
DEPTH_LOWER = 30
|
|
174
173
|
|
|
@@ -222,6 +221,14 @@ DEFAULT_PARAMS = {
|
|
|
222
221
|
"default_lignin_content": 0.073
|
|
223
222
|
}
|
|
224
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
|
+
|
|
225
232
|
# --- TIER 1 CONSTANTS ---
|
|
226
233
|
|
|
227
234
|
CLAY_CONTENT_TERM_ID = "clayContent"
|
|
@@ -232,8 +239,6 @@ ANIMAL_MANURE_USED_TERM_ID = "animalManureUsed"
|
|
|
232
239
|
INORGANIC_NITROGEN_FERTILISER_USED_TERM_ID = "inorganicNitrogenFertiliserUsed"
|
|
233
240
|
ORGANIC_FERTILISER_USED_TERM_ID = "organicFertiliserOrSoilCarbonIncreasingAmendmentUsed"
|
|
234
241
|
|
|
235
|
-
DEFAULT_NODE_VALUE = 100
|
|
236
|
-
|
|
237
242
|
CLAY_CONTENT_MAX = 8
|
|
238
243
|
SAND_CONTENT_MIN = 70
|
|
239
244
|
|
|
@@ -243,6 +248,13 @@ The number of years required for soil organic carbon to reach equilibrium after
|
|
|
243
248
|
a change in land use, management regime or carbon input regime.
|
|
244
249
|
"""
|
|
245
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
|
+
|
|
246
258
|
# --- SHARED TIER 1 & TIER 2 FORMAT MEASUREMENT OUTPUT ---
|
|
247
259
|
|
|
248
260
|
|
|
@@ -275,112 +287,115 @@ def _measurement(year: int, value: float, method_classification: str) -> dict:
|
|
|
275
287
|
|
|
276
288
|
# --- SHARED TIER 1 & TIER 2 ENUMS ---
|
|
277
289
|
|
|
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
|
-
|
|
319
|
-
|
|
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
|
|
320
334
|
]
|
|
321
335
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
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
|
|
328
346
|
]
|
|
329
347
|
|
|
330
348
|
|
|
331
349
|
# --- TIER 1 ENUMS ---
|
|
332
350
|
|
|
333
351
|
|
|
334
|
-
IpccSoilCategory
|
|
335
|
-
"
|
|
336
|
-
|
|
337
|
-
"WETLAND_SOILS",
|
|
338
|
-
"VOLCANIC_SOILS",
|
|
339
|
-
"SPODIC_SOILS",
|
|
340
|
-
"HIGH_ACTIVITY_CLAY_SOILS",
|
|
341
|
-
"LOW_ACTIVITY_CLAY_SOILS",
|
|
342
|
-
])
|
|
343
|
-
"""
|
|
344
|
-
Enum representing IPCC Soil Categories.
|
|
352
|
+
class IpccSoilCategory(Enum):
|
|
353
|
+
"""
|
|
354
|
+
Enum representing IPCC Soil Categories.
|
|
345
355
|
|
|
346
|
-
See [IPCC (2019) Vol 4, Ch. 2 and 3](https://www.ipcc-nggip.iges.or.jp/public/2019rf/vol4.html) for more
|
|
347
|
-
|
|
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"
|
|
348
366
|
|
|
349
367
|
|
|
350
|
-
IpccLandUseCategory
|
|
351
|
-
"
|
|
352
|
-
|
|
353
|
-
"PADDY_RICE_CULTIVATION",
|
|
354
|
-
"ANNUAL_CROPS_WET",
|
|
355
|
-
"ANNUAL_CROPS",
|
|
356
|
-
"SET_ASIDE",
|
|
357
|
-
"FOREST",
|
|
358
|
-
"NATIVE",
|
|
359
|
-
"OTHER"
|
|
360
|
-
])
|
|
361
|
-
"""
|
|
362
|
-
Enum representing IPCC Land Use Categories.
|
|
368
|
+
class IpccLandUseCategory(Enum):
|
|
369
|
+
"""
|
|
370
|
+
Enum representing IPCC Land Use Categories.
|
|
363
371
|
|
|
364
|
-
See [IPCC (2019) Vol 4](https://www.ipcc-nggip.iges.or.jp/public/2019rf/vol4.html) for more information.
|
|
365
|
-
"""
|
|
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"
|
|
366
383
|
|
|
367
384
|
|
|
368
|
-
IpccCarbonInputCategory
|
|
369
|
-
"
|
|
370
|
-
|
|
371
|
-
"CROPLAND_HIGH_WITH_MANURE",
|
|
372
|
-
"CROPLAND_HIGH_WITHOUT_MANURE",
|
|
373
|
-
"CROPLAND_MEDIUM",
|
|
374
|
-
"CROPLAND_LOW",
|
|
375
|
-
"OTHER"
|
|
376
|
-
])
|
|
377
|
-
"""
|
|
378
|
-
Enum representing IPCC Carbon Input Categories for improved grasslands
|
|
379
|
-
and annual croplands.
|
|
385
|
+
class IpccCarbonInputCategory(Enum):
|
|
386
|
+
"""
|
|
387
|
+
Enum representing IPCC Carbon Input Categories for improved grasslands and annual croplands.
|
|
380
388
|
|
|
381
|
-
See [IPCC (2019) Vol. 4, Ch. 4, 5 and 6](https://www.ipcc-nggip.iges.or.jp/public/2019rf/vol4.html)
|
|
382
|
-
|
|
383
|
-
"""
|
|
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"
|
|
384
399
|
|
|
385
400
|
|
|
386
401
|
# --- TIER 2 NAMED TUPLES FOR CARBON SOURCES AND MODEL RESULTS ---
|
|
@@ -449,33 +464,6 @@ annual_water_factors : list[float]
|
|
|
449
464
|
"""
|
|
450
465
|
|
|
451
466
|
|
|
452
|
-
CarbonInputResult = NamedTuple(
|
|
453
|
-
"CarbonInputResult",
|
|
454
|
-
[
|
|
455
|
-
("timestamps", list[float]),
|
|
456
|
-
("organic_carbon_inputs", list[float]),
|
|
457
|
-
("average_nitrogen_contents", list[float]),
|
|
458
|
-
("average_lignin_contents", list[float]),
|
|
459
|
-
]
|
|
460
|
-
)
|
|
461
|
-
"""
|
|
462
|
-
A named tuple to hold the result of `_run_annual_organic_carbon_inputs`.
|
|
463
|
-
|
|
464
|
-
Attributes
|
|
465
|
-
----------
|
|
466
|
-
timestamps : list[int]
|
|
467
|
-
A list of integer timestamps (e.g. `[1995, 1996]`) for each year in the inventory.
|
|
468
|
-
organic_carbon_inputs : list[float]
|
|
469
|
-
A list of organic carbon inputs to the soil for each year in the inventory, kg C ha-1.
|
|
470
|
-
average_nitrogen_contents : list[float]
|
|
471
|
-
A list of the average nitrogen contents of the carbon sources for each year in the inventory, decimal_proportion,
|
|
472
|
-
kg N (kg d.m.)-1.
|
|
473
|
-
average_lignin_contents : list[float]
|
|
474
|
-
A list of the average lignin contents of the carbon sources for each year in the inventory, decimal_proportion,
|
|
475
|
-
kg lignin (kg d.m.)-1.
|
|
476
|
-
"""
|
|
477
|
-
|
|
478
|
-
|
|
479
467
|
Tier2SocResult = NamedTuple(
|
|
480
468
|
"Tier2SocResult",
|
|
481
469
|
[
|
|
@@ -523,45 +511,6 @@ carbon_input_factor : float
|
|
|
523
511
|
The stock change factor for mineral soil organic C for the input of organic amendments, dimensionless.
|
|
524
512
|
"""
|
|
525
513
|
|
|
526
|
-
CarbonInputArgs = NamedTuple("CarbonInputArgs", [
|
|
527
|
-
("num_grassland_improvements", int),
|
|
528
|
-
("has_irrigation", bool),
|
|
529
|
-
("has_residue_removed_or_burnt", bool),
|
|
530
|
-
("has_low_residue_producing_crops", bool),
|
|
531
|
-
("has_bare_fallow", bool),
|
|
532
|
-
("has_n_fixing_crop_or_inorganic_n_fertiliser_used", bool),
|
|
533
|
-
("has_practice_increasing_c_input", bool),
|
|
534
|
-
("has_cover_crop", bool),
|
|
535
|
-
("has_organic_fertiliser_or_soil_amendment_used", bool),
|
|
536
|
-
("has_animal_manure_used", bool)
|
|
537
|
-
])
|
|
538
|
-
"""
|
|
539
|
-
Named tuple representing the arguments for the functions assigning `IpccCarbonInputCategories` to inventory years.
|
|
540
|
-
|
|
541
|
-
Attributes
|
|
542
|
-
----------
|
|
543
|
-
num_grassland_improvements : int
|
|
544
|
-
The number of grassland improvements.
|
|
545
|
-
has_irrigation : bool
|
|
546
|
-
Indicates whether irrigation is applied to more than 30% of the site.
|
|
547
|
-
has_residue_removed_or_burnt : bool
|
|
548
|
-
Indicates whether residues are removed or burnt on more than 30% of the site.
|
|
549
|
-
has_low_residue_producing_crops : bool
|
|
550
|
-
Indicates whether low residue-producing crops are present on more than 70% of the site.
|
|
551
|
-
has_bare_fallow : bool
|
|
552
|
-
Indicates whether bare fallow is present on more than 30% of the site.
|
|
553
|
-
has_n_fixing_crop_or_inorganic_n_fertiliser_used : bool
|
|
554
|
-
Indicates whether a nitrogen-fixing crop or inorganic nitrogen fertiliser is used on more than 30% of the site.
|
|
555
|
-
has_practice_increasing_c_input : bool
|
|
556
|
-
Indicates whether practices increasing carbon input are present on more than 30% of the site.
|
|
557
|
-
has_cover_crop : bool
|
|
558
|
-
Indicates whether cover crops are present on more than 30% of the site.
|
|
559
|
-
has_organic_fertiliser_or_soil_amendment_used : bool
|
|
560
|
-
Indicates whether organic fertiliser or soil amendments are used on more than 30% of the site.
|
|
561
|
-
has_animal_manure_used : bool
|
|
562
|
-
Indicates whether animal manure is used on more than 30% of the site.
|
|
563
|
-
"""
|
|
564
|
-
|
|
565
514
|
|
|
566
515
|
# --- SHARED TIER 1 & TIER 2 MAPPING DICTS ---
|
|
567
516
|
|
|
@@ -651,8 +600,8 @@ IPCC_SOIL_CATEGORY_TO_SOIL_TYPE_LOOKUP_VALUE = {
|
|
|
651
600
|
IpccSoilCategory.LOW_ACTIVITY_CLAY_SOILS: "Low-activity clay soils",
|
|
652
601
|
}
|
|
653
602
|
"""
|
|
654
|
-
A dictionary mapping IPCC soil categories to corresponding soil type and USDA soil type lookup values
|
|
655
|
-
|
|
603
|
+
A dictionary mapping IPCC soil categories to corresponding soil type and USDA soil type lookup values in the
|
|
604
|
+
`"IPCC_SOIL_CATEGORY"` column.
|
|
656
605
|
"""
|
|
657
606
|
|
|
658
607
|
IPCC_LAND_USE_CATEGORY_TO_SITE_TYPE = {
|
|
@@ -676,8 +625,8 @@ IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE = {
|
|
|
676
625
|
IpccLandUseCategory.ANNUAL_CROPS: "Annual crops"
|
|
677
626
|
}
|
|
678
627
|
"""
|
|
679
|
-
A dictionary mapping IPCC land use categories to corresponding land cover lookup values
|
|
680
|
-
|
|
628
|
+
A dictionary mapping IPCC land use categories to corresponding land cover lookup values in the
|
|
629
|
+
`"IPCC_LAND_USE_CATEGORY"` column.
|
|
681
630
|
"""
|
|
682
631
|
|
|
683
632
|
IPCC_MANAGEMENT_CATEGORY_TO_GRASSLAND_MANAGEMENT_TERM_ID = {
|
|
@@ -688,8 +637,8 @@ IPCC_MANAGEMENT_CATEGORY_TO_GRASSLAND_MANAGEMENT_TERM_ID = {
|
|
|
688
637
|
IpccManagementCategory.OTHER: "nativePasture"
|
|
689
638
|
}
|
|
690
639
|
"""
|
|
691
|
-
A dictionary mapping IPCC management categories to corresponding grassland management term IDs from the
|
|
692
|
-
|
|
640
|
+
A dictionary mapping IPCC management categories to corresponding grassland management term IDs from the land cover
|
|
641
|
+
glossary.
|
|
693
642
|
"""
|
|
694
643
|
|
|
695
644
|
|
|
@@ -746,8 +695,7 @@ def _check_cycle_tillage_management_category(
|
|
|
746
695
|
tillage_nodes,
|
|
747
696
|
lookup=LOOKUP,
|
|
748
697
|
target_lookup_values=target_lookup_values,
|
|
749
|
-
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
750
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
698
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
751
699
|
) and (
|
|
752
700
|
key is not IpccManagementCategory.NO_TILLAGE
|
|
753
701
|
or _check_zero_tillages(tillage_nodes)
|
|
@@ -1516,18 +1464,16 @@ def _run_soc_stocks(
|
|
|
1516
1464
|
annual_temperature_factors: list[float],
|
|
1517
1465
|
annual_water_factors: list[float],
|
|
1518
1466
|
annual_organic_carbon_inputs: list[float],
|
|
1519
|
-
|
|
1520
|
-
|
|
1467
|
+
annual_n_contents: list[float],
|
|
1468
|
+
annual_lignin_contents: list[float],
|
|
1521
1469
|
annual_tillage_categories: Union[list[IpccManagementCategory], None] = None,
|
|
1522
1470
|
sand_content: float = 0.33,
|
|
1523
1471
|
run_in_period: int = 5,
|
|
1524
|
-
initial_soc_stock: Union[float, None] = None,
|
|
1525
1472
|
params: Union[dict[str, float], None] = None,
|
|
1526
1473
|
) -> Tier2SocResult:
|
|
1527
1474
|
"""
|
|
1528
1475
|
Run the IPCC Tier 2 SOC model with precomputed `annual_temperature_factors`, `annual_water_factors`,
|
|
1529
|
-
`annual_organic_carbon_inputs`, `
|
|
1530
|
-
`annual_average_lignin_contents_of_organic_carbon_sources`.
|
|
1476
|
+
`annual_organic_carbon_inputs`, `annual_n_contents`, `annual_lignin_contents`.
|
|
1531
1477
|
|
|
1532
1478
|
Parameters
|
|
1533
1479
|
----------
|
|
@@ -1538,22 +1484,20 @@ def _run_soc_stocks(
|
|
|
1538
1484
|
annual_water_factors : list[float]
|
|
1539
1485
|
A list of water factors for each year in the inventory, dimensionless (see Equation 5.0F).
|
|
1540
1486
|
annual_organic_carbon_inputs : list[float]
|
|
1541
|
-
A list of organic carbon inputs to the soil for each year in the inventory, kg C ha-1 year-1
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
A list of the average nitrogen contents of the organic carbon sources for each year in the inventory,
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
A list of the average lignin contents of the organic carbon sources for each year in the inventory,
|
|
1548
|
-
|
|
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.
|
|
1549
1495
|
annual_tillage_categories : list[IpccManagementCategory] | None
|
|
1550
1496
|
A list of the site"s `IpccManagementCategory`s for each year in the inventory.
|
|
1551
1497
|
sand_content : float
|
|
1552
1498
|
The sand content of the site, decimal proportion, default value: `0.33`.
|
|
1553
1499
|
run_in_period : int
|
|
1554
1500
|
The length of the run-in period in years, must be greater than or equal to 1, default value: `5`.
|
|
1555
|
-
initial_soc_stock : float | None
|
|
1556
|
-
The measured or pre-computed initial SOC stock at the end of the run-in period, kg C ha-1.
|
|
1557
1501
|
params : dict[str: float] | None
|
|
1558
1502
|
Overrides for the model parameters. If `None` only default parameters will be used.
|
|
1559
1503
|
|
|
@@ -1598,29 +1542,16 @@ def _run_soc_stocks(
|
|
|
1598
1542
|
|
|
1599
1543
|
# --- SPLIT ANNUAL DATA INTO RUN-IN AND INVENTORY PERIODS ---
|
|
1600
1544
|
|
|
1601
|
-
inventory_temperature_factors = timeseries_to_inventory(
|
|
1602
|
-
|
|
1603
|
-
)
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
)
|
|
1607
|
-
inventory_carbon_inputs = timeseries_to_inventory(
|
|
1608
|
-
annual_organic_carbon_inputs, run_in_period
|
|
1609
|
-
)
|
|
1610
|
-
inventory_nitrogen_contents = timeseries_to_inventory(
|
|
1611
|
-
annual_average_nitrogen_contents_of_organic_carbon_sources, run_in_period
|
|
1612
|
-
)
|
|
1613
|
-
inventory_lignin_contents = timeseries_to_inventory(
|
|
1614
|
-
annual_average_lignin_contents_of_organic_carbon_sources, run_in_period
|
|
1615
|
-
)
|
|
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)
|
|
1616
1550
|
inventory_f_2s = timeseries_to_inventory(annual_f_2s, run_in_period)
|
|
1617
|
-
inventory_tillage_factors = timeseries_to_inventory(
|
|
1618
|
-
annual_tillage_factors, run_in_period
|
|
1619
|
-
)
|
|
1551
|
+
inventory_tillage_factors = timeseries_to_inventory(annual_tillage_factors, run_in_period)
|
|
1620
1552
|
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
] # 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:]
|
|
1624
1555
|
|
|
1625
1556
|
# --- CALCULATE THE ACTIVE ACTIVE POOL STEADY STATES ---
|
|
1626
1557
|
|
|
@@ -1642,7 +1573,7 @@ def _run_soc_stocks(
|
|
|
1642
1573
|
inventory_carbon_inputs,
|
|
1643
1574
|
inventory_f_2s,
|
|
1644
1575
|
inventory_lignin_contents,
|
|
1645
|
-
|
|
1576
|
+
inventory_n_contents,
|
|
1646
1577
|
)
|
|
1647
1578
|
]
|
|
1648
1579
|
|
|
@@ -1732,39 +1663,9 @@ def _run_soc_stocks(
|
|
|
1732
1663
|
|
|
1733
1664
|
# --- CALCULATE THE ACTIVE, SLOW AND PASSIVE SOC STOCKS ---
|
|
1734
1665
|
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
)
|
|
1739
|
-
|
|
1740
|
-
init_active_frac = inventory_active_pool_steady_states[0]/init_total_steady_state
|
|
1741
|
-
init_slow_frac = inventory_slow_pool_steady_states[0]/init_total_steady_state
|
|
1742
|
-
init_passive_frac = 1 - (init_active_frac + init_slow_frac)
|
|
1743
|
-
|
|
1744
|
-
inventory_active_pool_soc_stocks = []
|
|
1745
|
-
inventory_slow_pool_soc_stocks = []
|
|
1746
|
-
inventory_passive_pool_soc_stocks = []
|
|
1747
|
-
|
|
1748
|
-
should_calc_run_in = initial_soc_stock is None
|
|
1749
|
-
|
|
1750
|
-
inventory_active_pool_soc_stocks.insert(
|
|
1751
|
-
0,
|
|
1752
|
-
inventory_active_pool_steady_states[0]
|
|
1753
|
-
if should_calc_run_in
|
|
1754
|
-
else init_active_frac * initial_soc_stock,
|
|
1755
|
-
)
|
|
1756
|
-
inventory_slow_pool_soc_stocks.insert(
|
|
1757
|
-
0,
|
|
1758
|
-
inventory_slow_pool_steady_states[0]
|
|
1759
|
-
if should_calc_run_in
|
|
1760
|
-
else init_slow_frac * initial_soc_stock,
|
|
1761
|
-
)
|
|
1762
|
-
inventory_passive_pool_soc_stocks.insert(
|
|
1763
|
-
0,
|
|
1764
|
-
inventory_passive_pool_steady_states[0]
|
|
1765
|
-
if should_calc_run_in
|
|
1766
|
-
else init_passive_frac * initial_soc_stock,
|
|
1767
|
-
)
|
|
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]
|
|
1768
1669
|
|
|
1769
1670
|
for index in range(1, len(inventory_timestamps), 1):
|
|
1770
1671
|
inventory_active_pool_soc_stocks.insert(
|
|
@@ -1829,45 +1730,6 @@ def _check_12_months(inner_dict: dict, keys: set[Any]):
|
|
|
1829
1730
|
# --- SUB-MODEL ANNUAL TEMPERATURE FACTORS ---
|
|
1830
1731
|
|
|
1831
1732
|
|
|
1832
|
-
def _should_run_annual_temperature_factors(
|
|
1833
|
-
site: dict
|
|
1834
|
-
) -> tuple[bool, dict]:
|
|
1835
|
-
"""
|
|
1836
|
-
Extracts, formats and checks data from the site node to determine whether or not to run the annual temperature
|
|
1837
|
-
factors model on a specific Hestia `Site`.
|
|
1838
|
-
|
|
1839
|
-
Parameters
|
|
1840
|
-
----------
|
|
1841
|
-
site : dict
|
|
1842
|
-
A Hestia `Site` node, see: https://www.hestia.earth/schema/Site.
|
|
1843
|
-
|
|
1844
|
-
Returns
|
|
1845
|
-
-------
|
|
1846
|
-
tuple[bool, dict]
|
|
1847
|
-
`(should_run, grouped_data)`.
|
|
1848
|
-
"""
|
|
1849
|
-
measurements = site.get("measurements", [])
|
|
1850
|
-
temperature_monthly = find_term_match(measurements, TEMPERATURE_MONTHLY_TERM_ID, {})
|
|
1851
|
-
|
|
1852
|
-
grouped_data = group_measurement_values_by_year(
|
|
1853
|
-
temperature_monthly,
|
|
1854
|
-
inner_key=_InnerKey.TEMPERATURES,
|
|
1855
|
-
complete_years_only=True
|
|
1856
|
-
)
|
|
1857
|
-
|
|
1858
|
-
should_run = all([
|
|
1859
|
-
all(
|
|
1860
|
-
_check_12_months(inner, {_InnerKey.TEMPERATURES})
|
|
1861
|
-
for inner in grouped_data.values()
|
|
1862
|
-
),
|
|
1863
|
-
len(grouped_data.keys()) >= MIN_RUN_IN_PERIOD,
|
|
1864
|
-
check_consecutive(grouped_data.keys())
|
|
1865
|
-
])
|
|
1866
|
-
|
|
1867
|
-
logShouldRun(site, MODEL, TERM_ID, should_run, sub_model="_run_annual_temperature_factors")
|
|
1868
|
-
return should_run, grouped_data
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
1733
|
def _run_annual_temperature_factors(
|
|
1872
1734
|
timestamps: list[int],
|
|
1873
1735
|
temperatures: list[list[float]],
|
|
@@ -1907,77 +1769,6 @@ def _run_annual_temperature_factors(
|
|
|
1907
1769
|
# --- TIER 2 SUB-MODEL: ANNUAL WATER FACTORS ---
|
|
1908
1770
|
|
|
1909
1771
|
|
|
1910
|
-
def _should_run_annual_water_factors(
|
|
1911
|
-
site: dict,
|
|
1912
|
-
cycles: list[dict]
|
|
1913
|
-
) -> tuple[bool, bool, dict]:
|
|
1914
|
-
"""
|
|
1915
|
-
Extracts, formats and checks data from the site and cycle nodes to determine determine whether or not to run the
|
|
1916
|
-
annual water factors model on a specific Hestia `Site` and `Cycle`s.
|
|
1917
|
-
|
|
1918
|
-
TODO: Implement checks for monthly is_irrigateds from cycles.
|
|
1919
|
-
|
|
1920
|
-
Parameters
|
|
1921
|
-
----------
|
|
1922
|
-
site : dict
|
|
1923
|
-
A Hestia `Site` node, see: https://www.hestia.earth/schema/Site.
|
|
1924
|
-
cycles : list[dict]
|
|
1925
|
-
A list of Hestia `Cycle` nodes, see: https://www.hestia.earth/schema/Cycle.
|
|
1926
|
-
|
|
1927
|
-
Returns
|
|
1928
|
-
-------
|
|
1929
|
-
tuple[bool, bool, dict]
|
|
1930
|
-
`(should_run, run_with_irrigation, grouped_data)`.
|
|
1931
|
-
"""
|
|
1932
|
-
measurements = site.get("measurements", [])
|
|
1933
|
-
precipitation_monthly = find_term_match(measurements, PRECIPITATION_MONTHLY_TERM_ID, {})
|
|
1934
|
-
potential_evapotranspiration_monthly = find_term_match(measurements, PET_MONTHLY_TERM_ID, {})
|
|
1935
|
-
|
|
1936
|
-
grouped_precipitations = group_measurement_values_by_year(
|
|
1937
|
-
precipitation_monthly,
|
|
1938
|
-
inner_key=_InnerKey.PRECIPITATIONS,
|
|
1939
|
-
complete_years_only=True
|
|
1940
|
-
)
|
|
1941
|
-
grouped_pets = group_measurement_values_by_year(
|
|
1942
|
-
potential_evapotranspiration_monthly,
|
|
1943
|
-
inner_key=_InnerKey.PETS,
|
|
1944
|
-
complete_years_only=True
|
|
1945
|
-
)
|
|
1946
|
-
|
|
1947
|
-
is_irrigateds = None # TODO: Implement is_irrigateds check.
|
|
1948
|
-
run_with_irrigation = bool(is_irrigateds)
|
|
1949
|
-
|
|
1950
|
-
grouped_data = (
|
|
1951
|
-
merge(grouped_precipitations, grouped_pets) if is_irrigateds is None else reduce(
|
|
1952
|
-
merge, [grouped_precipitations, grouped_pets, is_irrigateds]
|
|
1953
|
-
)
|
|
1954
|
-
)
|
|
1955
|
-
|
|
1956
|
-
should_run = all([
|
|
1957
|
-
all(
|
|
1958
|
-
_check_12_months(inner, {_InnerKey.PRECIPITATIONS, _InnerKey.PETS})
|
|
1959
|
-
for inner in grouped_data.values()
|
|
1960
|
-
),
|
|
1961
|
-
not run_with_irrigation or all(
|
|
1962
|
-
_check_12_months(inner, {_InnerKey.IS_IRRIGATEDS})
|
|
1963
|
-
for inner in grouped_data.values()
|
|
1964
|
-
),
|
|
1965
|
-
len(grouped_data.keys()) >= MIN_RUN_IN_PERIOD,
|
|
1966
|
-
check_consecutive(grouped_data.keys()),
|
|
1967
|
-
check_cycle_site_ids_identical(cycles)
|
|
1968
|
-
])
|
|
1969
|
-
|
|
1970
|
-
logShouldRun(
|
|
1971
|
-
site,
|
|
1972
|
-
MODEL,
|
|
1973
|
-
TERM_ID,
|
|
1974
|
-
should_run,
|
|
1975
|
-
sub_model="_run_annual_water_factors",
|
|
1976
|
-
run_with_irrigation=run_with_irrigation
|
|
1977
|
-
)
|
|
1978
|
-
return should_run, run_with_irrigation, grouped_data
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
1772
|
def _run_annual_water_factors(
|
|
1982
1773
|
timestamps: list[int],
|
|
1983
1774
|
precipitations: list[list[float]],
|
|
@@ -2090,267 +1881,51 @@ def _get_carbon_sources_from_cycles(cycles: dict) -> list[CarbonSource]:
|
|
|
2090
1881
|
return non_empty_list([_iterate_carbon_source(node) for node in inputs_and_products])
|
|
2091
1882
|
|
|
2092
1883
|
|
|
2093
|
-
def _should_run_annual_organic_carbon_inputs(
|
|
2094
|
-
site: dict,
|
|
2095
|
-
cycles: list[dict]
|
|
2096
|
-
) -> tuple[bool, dict]:
|
|
2097
|
-
"""
|
|
2098
|
-
Extracts, formats and checks data from the site node to determine whether or not to run the annual organic carbon
|
|
2099
|
-
inputs model on a specific set of Hestia `Cycle`s.
|
|
2100
|
-
|
|
2101
|
-
Parameters
|
|
2102
|
-
----------
|
|
2103
|
-
cycles : list[dict]
|
|
2104
|
-
A list of Hestia `Cycle` nodes, see: https://www.hestia.earth/schema/Cycle.
|
|
2105
|
-
|
|
2106
|
-
Returns
|
|
2107
|
-
-------
|
|
2108
|
-
tuple[bool, dict]
|
|
2109
|
-
`(should_run, grouped_data)`.
|
|
2110
|
-
"""
|
|
2111
|
-
grouped_cycles = group_nodes_by_year(cycles)
|
|
2112
|
-
|
|
2113
|
-
grouped_data = {
|
|
2114
|
-
year: {
|
|
2115
|
-
_InnerKey.CARBON_SOURCES: _get_carbon_sources_from_cycles(_cycles)
|
|
2116
|
-
} for year, _cycles in grouped_cycles.items()
|
|
2117
|
-
}
|
|
2118
|
-
|
|
2119
|
-
should_run = all([
|
|
2120
|
-
len(grouped_data.keys()) >= MIN_RUN_IN_PERIOD,
|
|
2121
|
-
check_consecutive(grouped_data.keys()),
|
|
2122
|
-
check_cycle_site_ids_identical(cycles)
|
|
2123
|
-
])
|
|
2124
|
-
|
|
2125
|
-
logShouldRun(site, MODEL, TERM_ID, should_run, sub_model="_run_annual_organic_carbon_inputs")
|
|
2126
|
-
return should_run, grouped_data
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
def _run_annual_organic_carbon_inputs(
|
|
2130
|
-
timestamps: list[int],
|
|
2131
|
-
annual_carbon_sources: list[list[CarbonSource]],
|
|
2132
|
-
default_carbon_content: float = 0.42,
|
|
2133
|
-
default_nitrogen_content: float = 0.0085,
|
|
2134
|
-
default_lignin_content: float = 0.073,
|
|
2135
|
-
):
|
|
2136
|
-
"""
|
|
2137
|
-
Calculate the organic carbon input, average nitrogen content of carbon sources and average lignin content of carbon
|
|
2138
|
-
sources for each year of the inventory.
|
|
2139
|
-
|
|
2140
|
-
`timestamps` and `annual_carbon_sources` must have the same length.
|
|
2141
|
-
|
|
2142
|
-
Parameters
|
|
2143
|
-
----------
|
|
2144
|
-
timestamps : list[int]
|
|
2145
|
-
A list of integer timestamps (e.g. `[1995, 1996]`) for each year in the inventory.
|
|
2146
|
-
annual_carbon_sources : list[list[CarbonSource]]
|
|
2147
|
-
A list of carbon sources for each year of the inventory, where each carbon source is a named tupled with the
|
|
2148
|
-
format `(mass: float, carbon_content: float, nitrogen_content: float, lignin_content: float)`
|
|
2149
|
-
default_carbon_content : float
|
|
2150
|
-
The default carbon content of a carbon source, decimal proportion, kg C (kg d.m.)-1, default value: `0.42`.
|
|
2151
|
-
default_nitrogen_content : float
|
|
2152
|
-
The default nitrogen content of a carbon source, decimal proportion, kg N (kg d.m.)-1, default value: `0.0085`.
|
|
2153
|
-
default_lignin_content : float)
|
|
2154
|
-
The default lignin content of a carbon source, decimal proportion, kg lignin (kg d.m.)-1,
|
|
2155
|
-
default value: `0.073`.
|
|
2156
|
-
|
|
2157
|
-
Returns
|
|
2158
|
-
-------
|
|
2159
|
-
CarbonInputResult
|
|
2160
|
-
An inventory of annual carbon input data as a named tuple with the format
|
|
2161
|
-
`(timestamps: list[int], organic_carbon_inputs: list[float], average_nitrogen_contents: list[float],
|
|
2162
|
-
average_lignin_contents: list[float])`
|
|
2163
|
-
"""
|
|
2164
|
-
return CarbonInputResult(
|
|
2165
|
-
timestamps=timestamps,
|
|
2166
|
-
organic_carbon_inputs=[
|
|
2167
|
-
_calc_total_organic_carbon_input(sources, default_carbon_content=default_carbon_content)
|
|
2168
|
-
for sources in annual_carbon_sources
|
|
2169
|
-
],
|
|
2170
|
-
average_nitrogen_contents=[
|
|
2171
|
-
_calc_average_nitrogen_content_of_organic_carbon_sources(
|
|
2172
|
-
sources, default_nitrogen_content=default_nitrogen_content)
|
|
2173
|
-
for sources in annual_carbon_sources
|
|
2174
|
-
],
|
|
2175
|
-
average_lignin_contents=[
|
|
2176
|
-
_calc_average_lignin_content_of_organic_carbon_sources(
|
|
2177
|
-
sources, default_lignin_content=default_lignin_content)
|
|
2178
|
-
for sources in annual_carbon_sources
|
|
2179
|
-
],
|
|
2180
|
-
)
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
1884
|
# --- TIER 2 SOC MODEL ---
|
|
2184
1885
|
|
|
2185
1886
|
|
|
2186
|
-
def _should_run_tier_2(
|
|
2187
|
-
site: dict
|
|
2188
|
-
) -> tuple:
|
|
2189
|
-
"""
|
|
2190
|
-
Extracts, formats and checks data from the site and cycle nodes to determine
|
|
2191
|
-
determine whether or not to run the Tier 2 SOC model.
|
|
2192
|
-
|
|
2193
|
-
Parameters
|
|
2194
|
-
----------
|
|
2195
|
-
site : dict
|
|
2196
|
-
A Hestia `Site` node, see: https://www.hestia.earth/schema/Site.
|
|
2197
|
-
cycles : list[dict]
|
|
2198
|
-
A list of Hestia `Cycle` nodes, see: https://www.hestia.earth/schema/Cycle.
|
|
2199
|
-
|
|
2200
|
-
Returns
|
|
2201
|
-
-------
|
|
2202
|
-
tuple
|
|
2203
|
-
`(should_run, timestamps, temperatures, precipitations, pets, carbon_sources, tillage_categories, sand_content,
|
|
2204
|
-
is_irrigateds, run_in_period, initial_soc_stock)`
|
|
2205
|
-
"""
|
|
2206
|
-
cycles = related_cycles(site.get("@id"))
|
|
2207
|
-
|
|
2208
|
-
should_run_t, grouped_temperature_data = _should_run_annual_temperature_factors(site)
|
|
2209
|
-
should_run_w, run_with_irrigation, grouped_water_data = _should_run_annual_water_factors(site, cycles)
|
|
2210
|
-
should_run_c, grouped_carbon_sources_data = _should_run_annual_organic_carbon_inputs(site, cycles)
|
|
2211
|
-
|
|
2212
|
-
grouped_cycles = group_nodes_by_year(cycles)
|
|
2213
|
-
|
|
2214
|
-
grouped_tillage_categories = {
|
|
2215
|
-
year: {
|
|
2216
|
-
_InnerKey.TILLAGE_CATEGORY: _assign_tier_2_ipcc_tillage_management_category(_cycles)
|
|
2217
|
-
} for year, _cycles in grouped_cycles.items()
|
|
2218
|
-
}
|
|
2219
|
-
|
|
2220
|
-
# Combine all the grouped data into one dictionary
|
|
2221
|
-
grouped_data = reduce(merge, [grouped_temperature_data, grouped_water_data,
|
|
2222
|
-
grouped_carbon_sources_data, grouped_tillage_categories])
|
|
2223
|
-
|
|
2224
|
-
# Select the correct keys for data completeness based on `run_with_irrigation`
|
|
2225
|
-
keys = INNER_KEYS_RUN_WITH_IRRIGATION if run_with_irrigation else INNER_KEYS_RUN_WITHOUT_IRRIGATION
|
|
2226
|
-
|
|
2227
|
-
# Filter out any incomplete years
|
|
2228
|
-
complete_data = dict(filter(
|
|
2229
|
-
lambda item: all([key in item[1].keys() for key in keys]),
|
|
2230
|
-
grouped_data.items()
|
|
2231
|
-
))
|
|
2232
|
-
|
|
2233
|
-
timestamps = list(complete_data)
|
|
2234
|
-
start_year = timestamps[0] if timestamps else 0
|
|
2235
|
-
end_year = timestamps[-1] if timestamps else 0
|
|
2236
|
-
|
|
2237
|
-
measurements = site.get("measurements", [])
|
|
2238
|
-
|
|
2239
|
-
sand_content_value, _ = most_relevant_measurement_value_by_depth_and_date(
|
|
2240
|
-
measurements,
|
|
2241
|
-
SAND_CONTENT_TERM_ID,
|
|
2242
|
-
f"{start_year}-12-31",
|
|
2243
|
-
DEPTH_UPPER,
|
|
2244
|
-
DEPTH_LOWER,
|
|
2245
|
-
depth_strict=False
|
|
2246
|
-
) if timestamps else (None, None)
|
|
2247
|
-
sand_content = sand_content_value/100 if sand_content_value else None
|
|
2248
|
-
|
|
2249
|
-
initial_soc_stock_value, initial_soc_stock_date = most_relevant_measurement_value_by_depth_and_date(
|
|
2250
|
-
measurements,
|
|
2251
|
-
TERM_ID,
|
|
2252
|
-
f"{end_year}-12-31",
|
|
2253
|
-
DEPTH_UPPER,
|
|
2254
|
-
DEPTH_LOWER,
|
|
2255
|
-
depth_strict=True
|
|
2256
|
-
) if timestamps else (None, None)
|
|
2257
|
-
|
|
2258
|
-
run_with_initial_soc_stock = bool(initial_soc_stock_value and initial_soc_stock_date)
|
|
2259
|
-
|
|
2260
|
-
run_in_period = (
|
|
2261
|
-
int(abs(diff_in_years(f"{start_year}-12-31", initial_soc_stock_date)) + 1)
|
|
2262
|
-
if run_with_initial_soc_stock else MIN_RUN_IN_PERIOD
|
|
2263
|
-
) if timestamps else 0
|
|
2264
|
-
|
|
2265
|
-
timestamps = list(complete_data.keys())
|
|
2266
|
-
temperatures = [complete_data[year][_InnerKey.TEMPERATURES] for year in timestamps]
|
|
2267
|
-
precipitations = [complete_data[year][_InnerKey.PRECIPITATIONS] for year in timestamps]
|
|
2268
|
-
pets = [complete_data[year][_InnerKey.PETS] for year in timestamps]
|
|
2269
|
-
annual_carbon_sources = [complete_data[year][_InnerKey.CARBON_SOURCES] for year in timestamps]
|
|
2270
|
-
annual_tillage_categories = [complete_data[year][_InnerKey.TILLAGE_CATEGORY] for year in timestamps]
|
|
2271
|
-
is_irrigateds = (
|
|
2272
|
-
[complete_data[year][_InnerKey.IS_IRRIGATEDS] for year in timestamps] if run_with_irrigation else None
|
|
2273
|
-
)
|
|
2274
|
-
|
|
2275
|
-
should_run = all([
|
|
2276
|
-
should_run_t,
|
|
2277
|
-
should_run_w,
|
|
2278
|
-
should_run_c,
|
|
2279
|
-
sand_content is not None and 0 < sand_content <= 1,
|
|
2280
|
-
run_in_period >= MIN_RUN_IN_PERIOD,
|
|
2281
|
-
len(timestamps) >= run_in_period,
|
|
2282
|
-
check_cycle_site_ids_identical(cycles),
|
|
2283
|
-
check_consecutive(timestamps)
|
|
2284
|
-
])
|
|
2285
|
-
|
|
2286
|
-
logShouldRun(
|
|
2287
|
-
site,
|
|
2288
|
-
MODEL,
|
|
2289
|
-
TERM_ID,
|
|
2290
|
-
should_run,
|
|
2291
|
-
sub_model="_run_tier_2",
|
|
2292
|
-
run_with_irrigation=run_with_irrigation,
|
|
2293
|
-
run_with_initial_soc_stock=run_with_initial_soc_stock,
|
|
2294
|
-
run_in_period=run_in_period
|
|
2295
|
-
)
|
|
2296
|
-
|
|
2297
|
-
return (
|
|
2298
|
-
should_run,
|
|
2299
|
-
timestamps,
|
|
2300
|
-
temperatures,
|
|
2301
|
-
precipitations,
|
|
2302
|
-
pets,
|
|
2303
|
-
annual_carbon_sources,
|
|
2304
|
-
annual_tillage_categories,
|
|
2305
|
-
sand_content,
|
|
2306
|
-
is_irrigateds,
|
|
2307
|
-
run_in_period,
|
|
2308
|
-
initial_soc_stock_value
|
|
2309
|
-
)
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
1887
|
def _run_tier_2(
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
precipitations: list[list[float]],
|
|
2316
|
-
pets: list[list[float]],
|
|
2317
|
-
annual_carbon_sources: list[list[CarbonSource]],
|
|
2318
|
-
annual_tillage_categories: list[IpccManagementCategory],
|
|
2319
|
-
sand_content: float = 0.33,
|
|
2320
|
-
is_irrigateds: Union[list[list[bool]], None] = None,
|
|
1888
|
+
inventory: dict[int: dict[_InventoryKey: any]],
|
|
1889
|
+
*,
|
|
2321
1890
|
run_in_period: int = 5,
|
|
2322
|
-
|
|
1891
|
+
run_with_irrigation: bool = True,
|
|
1892
|
+
sand_content: float = 0.33,
|
|
2323
1893
|
params: Union[dict[str, float], None] = None,
|
|
1894
|
+
**_
|
|
2324
1895
|
) -> list[dict]:
|
|
2325
1896
|
"""
|
|
2326
1897
|
Run the IPCC Tier 2 SOC model on a time series of annual data about a site and the mangagement activities taking
|
|
2327
|
-
place on it.
|
|
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
|
+
...
|
|
1915
|
+
}
|
|
1916
|
+
```
|
|
1917
|
+
|
|
1918
|
+
TODO: interpolate between `sandContent` measurements for different years of the inventory
|
|
2328
1919
|
|
|
2329
1920
|
Parameters
|
|
2330
1921
|
----------
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
A list of monthly average temperatures for each year in the inventory.
|
|
2335
|
-
precipitations : list[list[float]]
|
|
2336
|
-
A list of monthly sum precipitations for each year in the inventory.
|
|
2337
|
-
pets : list[list[float]]
|
|
2338
|
-
A list of monthly sum potential evapotransiprations for each year in the inventory.
|
|
2339
|
-
annual_carbon_sources : list[list[CarbonSource]]
|
|
2340
|
-
A list of carbon sources for each year of the inventory, where each carbon source is a named tupled with the
|
|
2341
|
-
format `(mass: float, carbon_content: float, nitrogen_content: float, lignin_content: float)`
|
|
2342
|
-
annual_tillage_categories : list[IpccManagementCategory)
|
|
2343
|
-
A list of the site"s IpccManagementCategory for each year in the inventory.
|
|
2344
|
-
sand_content : float
|
|
2345
|
-
The sand content of the site, decimal proportion, default value: `0.33`.
|
|
2346
|
-
is_irrigateds : list[list[bool]] | None
|
|
2347
|
-
A list of monthly booleans that describe whether irrigation is used in a particular calendar month for each
|
|
2348
|
-
year in the inventory.
|
|
2349
|
-
run_in_period : int
|
|
1922
|
+
inventory : dict
|
|
1923
|
+
The inventory built by the `_should_run` function.
|
|
1924
|
+
run_in_period : int, optional
|
|
2350
1925
|
The length of the run-in period in years, must be greater than or equal to 1, default value: `5`.
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
params : dict | None
|
|
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
|
|
2354
1929
|
Overrides for the model parameters. If `None` only default parameters will be used.
|
|
2355
1930
|
|
|
2356
1931
|
Returns
|
|
@@ -2358,6 +1933,28 @@ def _run_tier_2(
|
|
|
2358
1933
|
list[dict]
|
|
2359
1934
|
A list of Hestia `Measurement` nodes containing the calculated SOC stocks and additional relevant data.
|
|
2360
1935
|
"""
|
|
1936
|
+
valid_inventory = {
|
|
1937
|
+
year: group for year, group in inventory.items() if group.get(_InventoryKey.SHOULD_RUN_TIER_2)
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
timestamps = [year for year in valid_inventory.keys()]
|
|
1941
|
+
|
|
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()]
|
|
1945
|
+
|
|
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
|
+
)
|
|
1953
|
+
|
|
1954
|
+
sand_content = next(
|
|
1955
|
+
group[_InventoryKey.SAND_CONTENT]/100 for group in valid_inventory.values()
|
|
1956
|
+
if _InventoryKey.SAND_CONTENT in group
|
|
1957
|
+
)
|
|
2361
1958
|
|
|
2362
1959
|
# --- MERGE ANY USER-SET PARAMETERS WITH THE IPCC DEFAULTS ---
|
|
2363
1960
|
|
|
@@ -2367,45 +1964,31 @@ def _run_tier_2(
|
|
|
2367
1964
|
|
|
2368
1965
|
_, annual_temperature_factors = _run_annual_temperature_factors(
|
|
2369
1966
|
timestamps,
|
|
2370
|
-
|
|
1967
|
+
annual_temperature_monthlys,
|
|
2371
1968
|
maximum_temperature=params.get("maximum_temperature"),
|
|
2372
1969
|
optimum_temperature=params.get("optimum_temperature")
|
|
2373
1970
|
)
|
|
2374
1971
|
|
|
2375
1972
|
_, annual_water_factors = _run_annual_water_factors(
|
|
2376
1973
|
timestamps,
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
1974
|
+
annual_precipitation_monthlys,
|
|
1975
|
+
annual_pet_monthlys,
|
|
1976
|
+
annual_irrigated_monthly,
|
|
2380
1977
|
water_factor_slope=params.get("water_factor_slope")
|
|
2381
1978
|
)
|
|
2382
1979
|
|
|
2383
|
-
(
|
|
2384
|
-
_,
|
|
2385
|
-
annual_organic_carbon_inputs,
|
|
2386
|
-
annual_nitrogen_contents,
|
|
2387
|
-
annual_lignin_contents
|
|
2388
|
-
) = _run_annual_organic_carbon_inputs(
|
|
2389
|
-
timestamps,
|
|
2390
|
-
annual_carbon_sources,
|
|
2391
|
-
default_carbon_content=params.get("default_carbon_content"),
|
|
2392
|
-
default_nitrogen_content=params.get("default_nitrogen_content"),
|
|
2393
|
-
default_lignin_content=params.get("default_lignin_content")
|
|
2394
|
-
)
|
|
2395
|
-
|
|
2396
1980
|
# --- RUN THE MODEL ---
|
|
2397
1981
|
|
|
2398
1982
|
result = _run_soc_stocks(
|
|
2399
1983
|
timestamps=timestamps,
|
|
2400
1984
|
annual_temperature_factors=annual_temperature_factors,
|
|
2401
1985
|
annual_water_factors=annual_water_factors,
|
|
2402
|
-
annual_organic_carbon_inputs=
|
|
2403
|
-
|
|
2404
|
-
|
|
1986
|
+
annual_organic_carbon_inputs=annual_carbon_inputs,
|
|
1987
|
+
annual_n_contents=annual_n_contents,
|
|
1988
|
+
annual_lignin_contents=annual_lignin_contents,
|
|
2405
1989
|
annual_tillage_categories=annual_tillage_categories,
|
|
2406
1990
|
sand_content=sand_content,
|
|
2407
1991
|
run_in_period=run_in_period,
|
|
2408
|
-
initial_soc_stock=initial_soc_stock,
|
|
2409
1992
|
params=params
|
|
2410
1993
|
)
|
|
2411
1994
|
|
|
@@ -2438,39 +2021,13 @@ def _run_tier_2(
|
|
|
2438
2021
|
# --- TIER 1 FUNCTIONS ---
|
|
2439
2022
|
|
|
2440
2023
|
|
|
2441
|
-
def
|
|
2442
|
-
|
|
2443
|
-
|
|
2024
|
+
def _retrieve_soc_ref(
|
|
2025
|
+
eco_climate_zone: int,
|
|
2026
|
+
ipcc_soil_category: IpccSoilCategory
|
|
2027
|
+
) -> float:
|
|
2444
2028
|
"""
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
Parameters
|
|
2448
|
-
----------
|
|
2449
|
-
lst : iterable[int | float]
|
|
2450
|
-
The list of integers.
|
|
2451
|
-
target : int | float
|
|
2452
|
-
The target value.
|
|
2453
|
-
|
|
2454
|
-
Returns
|
|
2455
|
-
-------
|
|
2456
|
-
int
|
|
2457
|
-
The index of the closest value.
|
|
2458
|
-
"""
|
|
2459
|
-
should_run = all([
|
|
2460
|
-
lst,
|
|
2461
|
-
all(isinstance(element, (int, float)) for element in lst),
|
|
2462
|
-
isinstance(target_value, (int, float))
|
|
2463
|
-
])
|
|
2464
|
-
return min(range(len(lst)), key=lambda i: abs(lst[i] - target_value)) if should_run else None
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
def _retrieve_soc_ref(
|
|
2468
|
-
eco_climate_zone: int,
|
|
2469
|
-
ipcc_soil_category: IpccSoilCategory
|
|
2470
|
-
) -> float:
|
|
2471
|
-
"""
|
|
2472
|
-
Retrieve the soil organic carbon (SOC) reference value for a given combination of eco-climate zone
|
|
2473
|
-
and IPCC soil category.
|
|
2029
|
+
Retrieve the soil organic carbon (SOC) reference value for a given combination of eco-climate zone
|
|
2030
|
+
and IPCC soil category.
|
|
2474
2031
|
|
|
2475
2032
|
See [IPCC (2019) Vol. 4, Ch. 2, Table 2.3](https://www.ipcc-nggip.iges.or.jp/public/2019rf/vol4.html)
|
|
2476
2033
|
for more information.
|
|
@@ -2500,9 +2057,8 @@ def _retrieve_soc_stock_factors(
|
|
|
2500
2057
|
ipcc_carbon_input_category: IpccCarbonInputCategory
|
|
2501
2058
|
) -> StockChangeFactors:
|
|
2502
2059
|
"""
|
|
2503
|
-
Retrieve the stock change factors for soil organic carbon (SOC)
|
|
2504
|
-
|
|
2505
|
-
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.
|
|
2506
2062
|
|
|
2507
2063
|
Parameters
|
|
2508
2064
|
----------
|
|
@@ -2562,8 +2118,8 @@ def _calc_soc_equilibrium(
|
|
|
2562
2118
|
"""
|
|
2563
2119
|
Calculate the soil organic carbon (SOC) equilibrium based on reference SOC and factors.
|
|
2564
2120
|
|
|
2565
|
-
In the tier 1 model, SOC equilibriums are considered to be reached after 20 years of
|
|
2566
|
-
|
|
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.
|
|
2567
2123
|
|
|
2568
2124
|
Parameters
|
|
2569
2125
|
----------
|
|
@@ -2629,8 +2185,8 @@ def _iterate_soc_equilibriums(
|
|
|
2629
2185
|
timestamps: list[int], soc_equilibriums: list[float]
|
|
2630
2186
|
) -> tuple[list[int], list[float]]:
|
|
2631
2187
|
"""
|
|
2632
|
-
Iterate over SOC equilibriums, inserting timestamps and soc_equilibriums for any
|
|
2633
|
-
|
|
2188
|
+
Iterate over SOC equilibriums, inserting timestamps and soc_equilibriums for any missing years where SOC would have
|
|
2189
|
+
reached equilibrium.
|
|
2634
2190
|
|
|
2635
2191
|
Parameters
|
|
2636
2192
|
----------
|
|
@@ -2670,7 +2226,6 @@ def _iterate_soc_equilibriums(
|
|
|
2670
2226
|
)
|
|
2671
2227
|
|
|
2672
2228
|
for index, (timestamp, soc_equilibrium) in enumerate(zip(timestamps, soc_equilibriums)):
|
|
2673
|
-
|
|
2674
2229
|
equilibrium_reached_timestamp = calc_equilibrium_reached_timestamp(index)
|
|
2675
2230
|
|
|
2676
2231
|
if is_missing_equilibrium_year(timestamp, equilibrium_reached_timestamp):
|
|
@@ -2691,8 +2246,8 @@ def _run_soc_equilibriums(
|
|
|
2691
2246
|
"""
|
|
2692
2247
|
Run the soil organic carbon (SOC) equilibriums calculation for each year in the inventory.
|
|
2693
2248
|
|
|
2694
|
-
Missing years where SOC equilibrium would be reached are inserted to allow for annual
|
|
2695
|
-
|
|
2249
|
+
Missing years where SOC equilibrium would be reached are inserted to allow for annual SOC change to be calculated
|
|
2250
|
+
correctly.
|
|
2696
2251
|
|
|
2697
2252
|
Parameters
|
|
2698
2253
|
----------
|
|
@@ -2714,8 +2269,8 @@ def _run_soc_equilibriums(
|
|
|
2714
2269
|
Returns
|
|
2715
2270
|
-------
|
|
2716
2271
|
tuple[list[int], list[float]]
|
|
2717
|
-
`timestamps` and `soc_equilibriums` for each year in the inventory, including any
|
|
2718
|
-
|
|
2272
|
+
`timestamps` and `soc_equilibriums` for each year in the inventory, including any missing years where SOC
|
|
2273
|
+
equilibrium would have been reached.
|
|
2719
2274
|
"""
|
|
2720
2275
|
|
|
2721
2276
|
# Calculate SOC equilibriums for each year
|
|
@@ -2748,8 +2303,7 @@ def _calc_tier_1_soc_stocks(
|
|
|
2748
2303
|
soc_equilibriums: list[float],
|
|
2749
2304
|
) -> list[float]:
|
|
2750
2305
|
"""
|
|
2751
|
-
Calculate soil organic carbon (SOC) stocks (kg C ha-1) in the 0-30cm depth interval for each year in
|
|
2752
|
-
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.
|
|
2753
2307
|
|
|
2754
2308
|
Parameters
|
|
2755
2309
|
----------
|
|
@@ -2790,62 +2344,6 @@ def _calc_tier_1_soc_stocks(
|
|
|
2790
2344
|
return soc_stocks
|
|
2791
2345
|
|
|
2792
2346
|
|
|
2793
|
-
def _calc_measurement_scaling_factor(
|
|
2794
|
-
measured_soc: float, calculated_soc: float
|
|
2795
|
-
) -> float:
|
|
2796
|
-
"""
|
|
2797
|
-
Calculate the scaling factor soil organic carbon (SOC) values based
|
|
2798
|
-
on the ratio between measured and calculated values.
|
|
2799
|
-
|
|
2800
|
-
Parameters
|
|
2801
|
-
----------
|
|
2802
|
-
measured_soc : float
|
|
2803
|
-
The measured SOC value.
|
|
2804
|
-
calculated_soc : float
|
|
2805
|
-
The calculated SOC value.
|
|
2806
|
-
|
|
2807
|
-
Returns
|
|
2808
|
-
-------
|
|
2809
|
-
float
|
|
2810
|
-
The scaling factor.
|
|
2811
|
-
"""
|
|
2812
|
-
return measured_soc / calculated_soc
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
def _scale_soc_stocks(
|
|
2816
|
-
soc_stocks: list[float],
|
|
2817
|
-
soc_measurement_value: Union[float, None] = None,
|
|
2818
|
-
soc_measurement_index: Union[int, None] = None
|
|
2819
|
-
) -> list[float]:
|
|
2820
|
-
"""
|
|
2821
|
-
Scale soil organic carbon (SOC) stocks based on a measurement value and index.
|
|
2822
|
-
|
|
2823
|
-
Parameters
|
|
2824
|
-
----------
|
|
2825
|
-
soc_stocks : list[float]
|
|
2826
|
-
The list of SOC stocks to be scaled.
|
|
2827
|
-
soc_measurement_value : float | None, optional
|
|
2828
|
-
The measured SOC value for scaling. If None, no scaling is applied.
|
|
2829
|
-
soc_measurement_index : int | None, optional
|
|
2830
|
-
The index of the calculated SOC stock to compare against the SOC measurement.
|
|
2831
|
-
|
|
2832
|
-
Returns
|
|
2833
|
-
-------
|
|
2834
|
-
list[float]
|
|
2835
|
-
The scaled SOC stocks.
|
|
2836
|
-
"""
|
|
2837
|
-
|
|
2838
|
-
measurement_scaling_factor = (
|
|
2839
|
-
_calc_measurement_scaling_factor(
|
|
2840
|
-
soc_measurement_value,
|
|
2841
|
-
soc_stocks[soc_measurement_index]
|
|
2842
|
-
) if soc_measurement_value and soc_measurement_index is not None
|
|
2843
|
-
else 1
|
|
2844
|
-
)
|
|
2845
|
-
|
|
2846
|
-
return [value * measurement_scaling_factor for value in soc_stocks]
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
2347
|
# --- GET THE ECO-CLIMATE ZONE FROM THE MEASUREMENTS ---
|
|
2850
2348
|
|
|
2851
2349
|
|
|
@@ -2864,7 +2362,6 @@ def _get_eco_climate_zone(measurements: list[dict]) -> Optional[int]:
|
|
|
2864
2362
|
The eco-climate zone value if found, otherwise None.
|
|
2865
2363
|
"""
|
|
2866
2364
|
eco_climate_zone = find_term_match(measurements, "ecoClimateZone")
|
|
2867
|
-
# return measurement_value(eco_climate_zone) or None
|
|
2868
2365
|
return get_node_value(eco_climate_zone) or None
|
|
2869
2366
|
|
|
2870
2367
|
|
|
@@ -2872,10 +2369,10 @@ def _get_eco_climate_zone(measurements: list[dict]) -> Optional[int]:
|
|
|
2872
2369
|
|
|
2873
2370
|
|
|
2874
2371
|
def _check_soil_category(
|
|
2875
|
-
soil_types: list[dict],
|
|
2876
|
-
usda_soil_types: list[dict],
|
|
2877
2372
|
*,
|
|
2878
2373
|
key: IpccSoilCategory,
|
|
2374
|
+
soil_types: list[dict],
|
|
2375
|
+
usda_soil_types: list[dict],
|
|
2879
2376
|
**_
|
|
2880
2377
|
) -> bool:
|
|
2881
2378
|
"""
|
|
@@ -2883,12 +2380,12 @@ def _check_soil_category(
|
|
|
2883
2380
|
|
|
2884
2381
|
Parameters
|
|
2885
2382
|
----------
|
|
2383
|
+
key : IpccSoilCategory
|
|
2384
|
+
The IPCC soil category to check.
|
|
2886
2385
|
soil_types : list[dict]
|
|
2887
2386
|
List of soil type measurement nodes.
|
|
2888
2387
|
usda_soil_types : list[dict]
|
|
2889
2388
|
List of USDA soil type measurement nodes
|
|
2890
|
-
key : IpccSoilCategory
|
|
2891
|
-
The IPCC soil category to check.
|
|
2892
2389
|
|
|
2893
2390
|
Returns
|
|
2894
2391
|
-------
|
|
@@ -2904,26 +2401,25 @@ def _check_soil_category(
|
|
|
2904
2401
|
soil_types,
|
|
2905
2402
|
lookup=SOIL_TYPE_LOOKUP,
|
|
2906
2403
|
target_lookup_values=target_lookup_values,
|
|
2907
|
-
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
2908
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
2404
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
2909
2405
|
)
|
|
2910
2406
|
|
|
2911
2407
|
is_usda_soil_type_match = cumulative_nodes_lookup_match(
|
|
2912
2408
|
usda_soil_types,
|
|
2913
2409
|
lookup=USDA_SOIL_TYPE_LOOKUP,
|
|
2914
2410
|
target_lookup_values=target_lookup_values,
|
|
2915
|
-
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
2916
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
2411
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
2917
2412
|
)
|
|
2918
2413
|
|
|
2919
2414
|
return is_soil_type_match or is_usda_soil_type_match
|
|
2920
2415
|
|
|
2921
2416
|
|
|
2922
|
-
def
|
|
2417
|
+
def _check_sandy_soil_category(
|
|
2418
|
+
*,
|
|
2419
|
+
key: IpccSoilCategory,
|
|
2923
2420
|
soil_types: list[dict],
|
|
2924
2421
|
usda_soil_types: list[dict],
|
|
2925
|
-
|
|
2926
|
-
is_sandy: bool,
|
|
2422
|
+
has_sandy_soil: bool,
|
|
2927
2423
|
**_
|
|
2928
2424
|
) -> bool:
|
|
2929
2425
|
"""
|
|
@@ -2933,11 +2429,13 @@ def _check_sandy_soils(
|
|
|
2933
2429
|
|
|
2934
2430
|
Parameters
|
|
2935
2431
|
----------
|
|
2432
|
+
key : IpccSoilCategory
|
|
2433
|
+
The IPCC soil category to check.
|
|
2936
2434
|
soil_types : list[dict]
|
|
2937
2435
|
List of soil type measurement nodes.
|
|
2938
2436
|
usda_soil_types : list[dict]
|
|
2939
2437
|
List of USDA soil type measurement nodes
|
|
2940
|
-
|
|
2438
|
+
has_sandy_soil : bool
|
|
2941
2439
|
True if the soils are sandy, False otherwise.
|
|
2942
2440
|
|
|
2943
2441
|
Returns
|
|
@@ -2945,13 +2443,12 @@ def _check_sandy_soils(
|
|
|
2945
2443
|
bool
|
|
2946
2444
|
`True` if the soil category matches, `False` otherwise.
|
|
2947
2445
|
"""
|
|
2948
|
-
|
|
2949
|
-
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
|
|
2950
2447
|
|
|
2951
2448
|
|
|
2952
2449
|
SOIL_CATEGORY_DECISION_TREE = {
|
|
2953
2450
|
IpccSoilCategory.ORGANIC_SOILS: _check_soil_category,
|
|
2954
|
-
IpccSoilCategory.SANDY_SOILS:
|
|
2451
|
+
IpccSoilCategory.SANDY_SOILS: _check_sandy_soil_category,
|
|
2955
2452
|
IpccSoilCategory.WETLAND_SOILS: _check_soil_category,
|
|
2956
2453
|
IpccSoilCategory.VOLCANIC_SOILS: _check_soil_category,
|
|
2957
2454
|
IpccSoilCategory.SPODIC_SOILS: _check_soil_category,
|
|
@@ -2990,19 +2487,17 @@ def _assign_ipcc_soil_category(
|
|
|
2990
2487
|
|
|
2991
2488
|
clay_content = get_node_value(find_term_match(measurements, CLAY_CONTENT_TERM_ID))
|
|
2992
2489
|
sand_content = get_node_value(find_term_match(measurements, SAND_CONTENT_TERM_ID))
|
|
2993
|
-
# clay_content = measurement_value(find_term_match(measurements, CLAY_CONTENT_TERM_ID))
|
|
2994
|
-
# sand_content = measurement_value(find_term_match(measurements, SAND_CONTENT_TERM_ID))
|
|
2995
2490
|
|
|
2996
|
-
|
|
2491
|
+
has_sandy_soil = clay_content < CLAY_CONTENT_MAX and sand_content > SAND_CONTENT_MIN
|
|
2997
2492
|
|
|
2998
2493
|
return next(
|
|
2999
2494
|
(
|
|
3000
2495
|
key for key in SOIL_CATEGORY_DECISION_TREE
|
|
3001
2496
|
if SOIL_CATEGORY_DECISION_TREE[key](
|
|
3002
|
-
soil_types,
|
|
3003
|
-
usda_soil_types,
|
|
3004
2497
|
key=key,
|
|
3005
|
-
|
|
2498
|
+
soil_types=soil_types,
|
|
2499
|
+
usda_soil_types=usda_soil_types,
|
|
2500
|
+
has_sandy_soil=has_sandy_soil
|
|
3006
2501
|
)
|
|
3007
2502
|
),
|
|
3008
2503
|
default
|
|
@@ -3029,182 +2524,174 @@ def _has_irrigation(water_regime_nodes: list[dict]) -> bool:
|
|
|
3029
2524
|
return cumulative_nodes_term_match(
|
|
3030
2525
|
water_regime_nodes,
|
|
3031
2526
|
target_term_ids=get_irrigated_terms(),
|
|
3032
|
-
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3033
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
2527
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3034
2528
|
)
|
|
3035
2529
|
|
|
3036
2530
|
|
|
3037
|
-
def
|
|
3038
|
-
site_type: str,
|
|
3039
|
-
*,
|
|
3040
|
-
key: IpccLandUseCategory,
|
|
3041
|
-
**_
|
|
3042
|
-
):
|
|
2531
|
+
def _has_long_fallow(land_cover_nodes: list[dict]) -> bool:
|
|
3043
2532
|
"""
|
|
3044
|
-
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.
|
|
3045
2536
|
|
|
3046
2537
|
Parameters
|
|
3047
2538
|
----------
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
key : IpccLandUseCategory
|
|
3051
|
-
The IPCC land use category to check.
|
|
2539
|
+
land_cover_nodes : List[dict]
|
|
2540
|
+
List of land cover nodes to be checked.
|
|
3052
2541
|
|
|
3053
2542
|
Returns
|
|
3054
2543
|
-------
|
|
3055
2544
|
bool
|
|
3056
|
-
`True` if
|
|
3057
|
-
|
|
2545
|
+
`True` if long fallow is present, `False` otherwise.
|
|
3058
2546
|
"""
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
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
|
+
)
|
|
3062
2556
|
|
|
3063
2557
|
|
|
3064
|
-
def
|
|
3065
|
-
site_type: str,
|
|
3066
|
-
*,
|
|
3067
|
-
key: IpccLandUseCategory,
|
|
3068
|
-
land_cover_nodes: list[dict],
|
|
3069
|
-
**_
|
|
3070
|
-
) -> bool:
|
|
2558
|
+
def _has_upland_rice(land_cover_nodes: list[dict]) -> bool:
|
|
3071
2559
|
"""
|
|
3072
|
-
Check if
|
|
3073
|
-
|
|
3074
|
-
This function is special case of `_check_cropland_land_use_category`.
|
|
2560
|
+
Check if upland rice is present in the land cover nodes.
|
|
3075
2561
|
|
|
3076
2562
|
Parameters
|
|
3077
2563
|
----------
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
key : IpccLandUseCategory
|
|
3081
|
-
The IPCC land use category to check.
|
|
3082
|
-
land_cover_nodes : list[dict]
|
|
3083
|
-
List of land cover nodes.
|
|
2564
|
+
land_cover_nodes : List[dict]
|
|
2565
|
+
List of land cover nodes to be checked.
|
|
3084
2566
|
|
|
3085
2567
|
Returns
|
|
3086
2568
|
-------
|
|
3087
2569
|
bool
|
|
3088
|
-
`True` if
|
|
2570
|
+
`True` if upland rice is present, `False` otherwise.
|
|
3089
2571
|
"""
|
|
3090
|
-
|
|
3091
|
-
target_lookup_values = IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE.get(key, None)
|
|
3092
|
-
|
|
3093
|
-
return _check_ipcc_land_use_category(site_type, key=key) and cumulative_nodes_lookup_match(
|
|
2572
|
+
return cumulative_nodes_term_match(
|
|
3094
2573
|
land_cover_nodes,
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
cumulative_threshold=MIN_AREA_THRESHOLD,
|
|
3098
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
2574
|
+
target_term_ids=get_upland_rice_land_cover_terms(),
|
|
2575
|
+
cumulative_threshold=SUPER_MAJORITY_AREA_THRESHOLD
|
|
3099
2576
|
)
|
|
3100
2577
|
|
|
3101
2578
|
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
"""
|
|
3110
|
-
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
|
+
"""
|
|
3111
2586
|
|
|
3112
|
-
|
|
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.
|
|
3113
2591
|
|
|
3114
2592
|
Parameters
|
|
3115
2593
|
----------
|
|
2594
|
+
key : IpccLandUseCategory
|
|
2595
|
+
The IPCC land use category to check.
|
|
3116
2596
|
site_type : str
|
|
3117
2597
|
The site type to check.
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
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.
|
|
3122
2605
|
|
|
3123
2606
|
Returns
|
|
3124
2607
|
-------
|
|
3125
2608
|
bool
|
|
3126
2609
|
`True` if the conditions match the specified land use category, `False` otherwise.
|
|
2610
|
+
|
|
3127
2611
|
"""
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
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
|
|
3132
2616
|
|
|
3133
2617
|
|
|
3134
|
-
def
|
|
3135
|
-
site_type: str,
|
|
3136
|
-
*,
|
|
3137
|
-
land_cover_nodes: list[dict],
|
|
3138
|
-
has_long_fallow: bool,
|
|
3139
|
-
**_
|
|
2618
|
+
def _check_cropland_land_use_category(
|
|
2619
|
+
*, key: IpccLandUseCategory, site_type: str, land_cover_nodes: list[dict], **kwargs
|
|
3140
2620
|
) -> bool:
|
|
3141
2621
|
"""
|
|
3142
|
-
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.
|
|
3143
2623
|
|
|
3144
|
-
This function is special case of `
|
|
2624
|
+
This function is special case of `_check_ipcc_land_use_category`.
|
|
3145
2625
|
|
|
3146
2626
|
Parameters
|
|
3147
2627
|
----------
|
|
2628
|
+
key : IpccLandUseCategory
|
|
2629
|
+
The IPCC land use category to check.
|
|
3148
2630
|
site_type : str
|
|
3149
2631
|
The site type to check.
|
|
3150
|
-
|
|
3151
|
-
|
|
2632
|
+
|
|
2633
|
+
Keyword Args
|
|
2634
|
+
------------
|
|
3152
2635
|
has_long_fallow : bool
|
|
3153
|
-
|
|
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.
|
|
3154
2639
|
|
|
3155
2640
|
Returns
|
|
3156
2641
|
-------
|
|
3157
2642
|
bool
|
|
3158
2643
|
`True` if the conditions match the specified land use category, `False` otherwise.
|
|
3159
2644
|
"""
|
|
3160
|
-
|
|
3161
|
-
|
|
3162
|
-
|
|
3163
|
-
|
|
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
|
|
3164
2654
|
|
|
3165
2655
|
|
|
3166
|
-
def
|
|
3167
|
-
site_type: str,
|
|
3168
|
-
|
|
3169
|
-
land_cover_nodes: list[dict],
|
|
3170
|
-
has_wetland_soils: bool,
|
|
3171
|
-
has_long_fallow: bool,
|
|
3172
|
-
**_
|
|
3173
|
-
):
|
|
2656
|
+
def _check_paddy_rice_cultivation_land_use_category(
|
|
2657
|
+
*, key: IpccLandUseCategory, site_type: str, has_irrigated_upland_rice: bool, **kwargs
|
|
2658
|
+
) -> bool:
|
|
3174
2659
|
"""
|
|
3175
|
-
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.
|
|
3176
2661
|
|
|
3177
2662
|
This function is special case of `_check_cropland_land_use_category`.
|
|
3178
2663
|
|
|
3179
2664
|
Parameters
|
|
3180
2665
|
----------
|
|
2666
|
+
key : IpccLandUseCategory
|
|
2667
|
+
The IPCC land use category to check.
|
|
3181
2668
|
site_type : str
|
|
3182
2669
|
The site type to check.
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
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.
|
|
3187
2675
|
has_long_fallow : bool
|
|
3188
|
-
|
|
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.
|
|
3189
2679
|
|
|
3190
2680
|
Returns
|
|
3191
2681
|
-------
|
|
3192
2682
|
bool
|
|
3193
2683
|
`True` if the conditions match the specified land use category, `False` otherwise.
|
|
3194
2684
|
"""
|
|
3195
|
-
|
|
3196
|
-
return _check_cropland_land_use_category(
|
|
3197
|
-
site_type, key=KEY, land_cover_nodes=land_cover_nodes
|
|
3198
|
-
) 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
|
|
3199
2686
|
|
|
3200
2687
|
|
|
3201
2688
|
LAND_USE_CATEGORY_DECISION_TREE = {
|
|
3202
2689
|
IpccLandUseCategory.GRASSLAND: _check_ipcc_land_use_category,
|
|
3203
|
-
IpccLandUseCategory.PERENNIAL_CROPS: _check_cropland_land_use_category,
|
|
3204
|
-
IpccLandUseCategory.PADDY_RICE_CULTIVATION: _check_paddy_rice_cultivation_category,
|
|
3205
|
-
IpccLandUseCategory.ANNUAL_CROPS_WET: _check_annual_crops_wet_category,
|
|
3206
|
-
IpccLandUseCategory.ANNUAL_CROPS: _check_annual_crops_category,
|
|
3207
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,
|
|
3208
2695
|
IpccLandUseCategory.FOREST: _check_ipcc_land_use_category,
|
|
3209
2696
|
IpccLandUseCategory.NATIVE: _check_ipcc_land_use_category,
|
|
3210
2697
|
IpccLandUseCategory.OTHER: _check_ipcc_land_use_category
|
|
@@ -3219,9 +2706,7 @@ and land cover nodes.
|
|
|
3219
2706
|
|
|
3220
2707
|
|
|
3221
2708
|
def _assign_ipcc_land_use_category(
|
|
3222
|
-
site_type: str,
|
|
3223
|
-
management_nodes: list[dict],
|
|
3224
|
-
ipcc_soil_category: IpccSoilCategory
|
|
2709
|
+
site_type: str, management_nodes: list[dict], ipcc_soil_category: IpccSoilCategory
|
|
3225
2710
|
) -> IpccLandUseCategory:
|
|
3226
2711
|
"""
|
|
3227
2712
|
Assigns IPCC land use category based on site type, management nodes, and soil category.
|
|
@@ -3243,33 +2728,13 @@ def _assign_ipcc_land_use_category(
|
|
|
3243
2728
|
DECISION_TREE = LAND_USE_CATEGORY_DECISION_TREE
|
|
3244
2729
|
DEFAULT = IpccLandUseCategory.OTHER
|
|
3245
2730
|
|
|
3246
|
-
land_cover_nodes = filter_list_term_type(
|
|
3247
|
-
|
|
3248
|
-
)
|
|
3249
|
-
|
|
3250
|
-
water_regime_nodes = filter_list_term_type(
|
|
3251
|
-
management_nodes, [TermTermType.WATERREGIME]
|
|
3252
|
-
)
|
|
2731
|
+
land_cover_nodes = filter_list_term_type(management_nodes, [TermTermType.LANDCOVER])
|
|
2732
|
+
water_regime_nodes = filter_list_term_type(management_nodes, [TermTermType.WATERREGIME])
|
|
3253
2733
|
|
|
3254
2734
|
has_irrigation = _has_irrigation(water_regime_nodes)
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
target_term_ids=get_rice_plant_upland_terms(),
|
|
3259
|
-
cumulative_threshold=SUPER_MAJORITY_AREA_THRESHOLD,
|
|
3260
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
3261
|
-
)
|
|
3262
|
-
|
|
3263
|
-
is_irrigated_upland_rice = is_upland_rice and has_irrigation
|
|
3264
|
-
|
|
3265
|
-
# SUPER_MAJORITY_AREA_THRESHOLD
|
|
3266
|
-
has_long_fallow = cumulative_nodes_match(
|
|
3267
|
-
lambda node: get_node_property(node, LONG_FALLOW_CROP_TERM_ID, False).get("value", 0),
|
|
3268
|
-
land_cover_nodes,
|
|
3269
|
-
cumulative_threshold=SUPER_MAJORITY_AREA_THRESHOLD,
|
|
3270
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
3271
|
-
)
|
|
3272
|
-
|
|
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)
|
|
3273
2738
|
has_wetland_soils = ipcc_soil_category is IpccSoilCategory.WETLAND_SOILS
|
|
3274
2739
|
|
|
3275
2740
|
should_run = bool(site_type)
|
|
@@ -3278,11 +2743,11 @@ def _assign_ipcc_land_use_category(
|
|
|
3278
2743
|
(
|
|
3279
2744
|
key for key in DECISION_TREE
|
|
3280
2745
|
if DECISION_TREE[key](
|
|
3281
|
-
site_type,
|
|
3282
2746
|
key=key,
|
|
2747
|
+
site_type=site_type,
|
|
3283
2748
|
land_cover_nodes=land_cover_nodes,
|
|
3284
|
-
is_irrigated_upland_rice=is_irrigated_upland_rice,
|
|
3285
2749
|
has_long_fallow=has_long_fallow,
|
|
2750
|
+
has_irrigated_upland_rice=has_irrigated_upland_rice,
|
|
3286
2751
|
has_wetland_soils=has_wetland_soils
|
|
3287
2752
|
)
|
|
3288
2753
|
),
|
|
@@ -3294,20 +2759,17 @@ def _assign_ipcc_land_use_category(
|
|
|
3294
2759
|
|
|
3295
2760
|
|
|
3296
2761
|
def _check_grassland_ipcc_management_category(
|
|
3297
|
-
*,
|
|
3298
|
-
land_cover_nodes: list[dict],
|
|
3299
|
-
key: IpccManagementCategory,
|
|
3300
|
-
**_
|
|
2762
|
+
*, key: IpccManagementCategory, land_cover_nodes: list[dict], **_
|
|
3301
2763
|
) -> bool:
|
|
3302
2764
|
"""
|
|
3303
2765
|
Check if the land cover nodes match the target conditions for a grassland IpccManagementCategory.
|
|
3304
2766
|
|
|
3305
2767
|
Parameters
|
|
3306
2768
|
----------
|
|
3307
|
-
land_cover_nodes : List[dict]
|
|
3308
|
-
List of land cover nodes to be checked.
|
|
3309
2769
|
key : IpccManagementCategory
|
|
3310
2770
|
The IPCC management category to check.
|
|
2771
|
+
land_cover_nodes : List[dict]
|
|
2772
|
+
List of land cover nodes to be checked.
|
|
3311
2773
|
|
|
3312
2774
|
Returns
|
|
3313
2775
|
-------
|
|
@@ -3315,25 +2777,25 @@ def _check_grassland_ipcc_management_category(
|
|
|
3315
2777
|
`True` if the conditions match the specified management category, `False` otherwise.
|
|
3316
2778
|
"""
|
|
3317
2779
|
target_term_id = IPCC_MANAGEMENT_CATEGORY_TO_GRASSLAND_MANAGEMENT_TERM_ID.get(key, None)
|
|
3318
|
-
|
|
3319
2780
|
return cumulative_nodes_term_match(
|
|
3320
2781
|
land_cover_nodes,
|
|
3321
2782
|
target_term_ids=target_term_id,
|
|
3322
|
-
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3323
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
2783
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3324
2784
|
)
|
|
3325
2785
|
|
|
3326
2786
|
|
|
3327
|
-
def _check_tillage_ipcc_management_category(
|
|
2787
|
+
def _check_tillage_ipcc_management_category(
|
|
2788
|
+
*, key: IpccManagementCategory, tillage_nodes: list[dict], **_
|
|
2789
|
+
) -> bool:
|
|
3328
2790
|
"""
|
|
3329
2791
|
Check if the tillage nodes match the target conditions for a tillage IpccManagementCategory.
|
|
3330
2792
|
|
|
3331
2793
|
Parameters
|
|
3332
2794
|
----------
|
|
3333
|
-
tillage_nodes : List[dict]
|
|
3334
|
-
List of tillage nodes to be checked.
|
|
3335
2795
|
key : IpccManagementCategory
|
|
3336
2796
|
The IPCC management category to check.
|
|
2797
|
+
tillage_nodes : List[dict]
|
|
2798
|
+
List of tillage nodes to be checked.
|
|
3337
2799
|
|
|
3338
2800
|
Returns
|
|
3339
2801
|
-------
|
|
@@ -3342,13 +2804,11 @@ def _check_tillage_ipcc_management_category(*, tillage_nodes: list[dict], key: I
|
|
|
3342
2804
|
"""
|
|
3343
2805
|
LOOKUP = LOOKUPS["tillage"]
|
|
3344
2806
|
target_lookup_values = IPCC_MANAGEMENT_CATEGORY_TO_TILLAGE_MANAGEMENT_LOOKUP_VALUE.get(key, None)
|
|
3345
|
-
|
|
3346
2807
|
return cumulative_nodes_lookup_match(
|
|
3347
2808
|
tillage_nodes,
|
|
3348
2809
|
lookup=LOOKUP,
|
|
3349
2810
|
target_lookup_values=target_lookup_values,
|
|
3350
|
-
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3351
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
2811
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3352
2812
|
)
|
|
3353
2813
|
|
|
3354
2814
|
|
|
@@ -3404,8 +2864,7 @@ Value: Default IPCC management category for the given land use category.
|
|
|
3404
2864
|
|
|
3405
2865
|
|
|
3406
2866
|
def _assign_ipcc_management_category(
|
|
3407
|
-
management_nodes: list[dict],
|
|
3408
|
-
ipcc_land_use_category: IpccLandUseCategory
|
|
2867
|
+
management_nodes: list[dict], ipcc_land_use_category: IpccLandUseCategory
|
|
3409
2868
|
) -> IpccManagementCategory:
|
|
3410
2869
|
"""
|
|
3411
2870
|
Assign an IPCC Management Category based on the given management nodes and IPCC Land Use Category.
|
|
@@ -3422,32 +2881,26 @@ def _assign_ipcc_management_category(
|
|
|
3422
2881
|
IpccManagementCategory
|
|
3423
2882
|
The assigned IPCC Management Category.
|
|
3424
2883
|
"""
|
|
3425
|
-
decision_tree = IPCC_LAND_USE_CATEGORY_TO_DECISION_TREE.get(
|
|
3426
|
-
ipcc_land_use_category, {}
|
|
3427
|
-
)
|
|
2884
|
+
decision_tree = IPCC_LAND_USE_CATEGORY_TO_DECISION_TREE.get(ipcc_land_use_category, {})
|
|
3428
2885
|
default = IPCC_LAND_USE_CATEGORY_TO_DEFAULT_IPCC_MANAGEMENT_CATEGORY.get(
|
|
3429
2886
|
ipcc_land_use_category, IpccManagementCategory.OTHER
|
|
3430
2887
|
)
|
|
3431
2888
|
|
|
3432
|
-
land_cover_nodes = filter_list_term_type(
|
|
3433
|
-
|
|
3434
|
-
)
|
|
3435
|
-
tillage_nodes = filter_list_term_type(
|
|
3436
|
-
management_nodes, [TermTermType.TILLAGE]
|
|
3437
|
-
)
|
|
2889
|
+
land_cover_nodes = filter_list_term_type(management_nodes, [TermTermType.LANDCOVER])
|
|
2890
|
+
tillage_nodes = filter_list_term_type(management_nodes, [TermTermType.TILLAGE])
|
|
3438
2891
|
|
|
3439
|
-
should_run = (
|
|
3440
|
-
len(land_cover_nodes) > 0
|
|
3441
|
-
|
|
3442
|
-
)
|
|
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
|
+
])
|
|
3443
2896
|
|
|
3444
2897
|
return next(
|
|
3445
2898
|
(
|
|
3446
2899
|
key for key in decision_tree
|
|
3447
2900
|
if decision_tree[key](
|
|
2901
|
+
key=key,
|
|
3448
2902
|
land_cover_nodes=land_cover_nodes,
|
|
3449
2903
|
tillage_nodes=tillage_nodes,
|
|
3450
|
-
key=key
|
|
3451
2904
|
)
|
|
3452
2905
|
),
|
|
3453
2906
|
default
|
|
@@ -3470,9 +2923,7 @@ Value: Minimum number of improvements required for the corresponding Grassland C
|
|
|
3470
2923
|
|
|
3471
2924
|
|
|
3472
2925
|
def _check_grassland_ipcc_carbon_input_category(
|
|
3473
|
-
|
|
3474
|
-
*,
|
|
3475
|
-
key: IpccCarbonInputCategory
|
|
2926
|
+
*, key: IpccCarbonInputCategory, num_grassland_improvements: int, **_,
|
|
3476
2927
|
) -> bool:
|
|
3477
2928
|
"""
|
|
3478
2929
|
Checks if the given carbon input arguments satisfy the conditions for a specific
|
|
@@ -3480,24 +2931,26 @@ def _check_grassland_ipcc_carbon_input_category(
|
|
|
3480
2931
|
|
|
3481
2932
|
Parameters
|
|
3482
2933
|
----------
|
|
3483
|
-
carbon_input_args : CarbonInputArgs
|
|
3484
|
-
The carbon input arguments.
|
|
3485
2934
|
key : IpccCarbonInputCategory
|
|
3486
2935
|
The grassland IPCC Carbon Input Category to check.
|
|
2936
|
+
num_grassland_improvements : int
|
|
2937
|
+
The number of grassland improvements.
|
|
3487
2938
|
|
|
3488
2939
|
Returns
|
|
3489
2940
|
-------
|
|
3490
2941
|
bool
|
|
3491
2942
|
`True` if the conditions for the specified category are met; otherwise, `False`.
|
|
3492
2943
|
"""
|
|
3493
|
-
|
|
3494
|
-
GRASSLAND_IPCC_CARBON_INPUT_CATEGORY_TO_MIN_NUM_IMPROVEMENTS[key]
|
|
3495
|
-
)
|
|
3496
|
-
return carbon_input_args.num_grassland_improvements >= min_improvements
|
|
2944
|
+
return num_grassland_improvements >= GRASSLAND_IPCC_CARBON_INPUT_CATEGORY_TO_MIN_NUM_IMPROVEMENTS[key]
|
|
3497
2945
|
|
|
3498
2946
|
|
|
3499
2947
|
def _check_cropland_high_with_manure_category(
|
|
3500
|
-
|
|
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,
|
|
3501
2954
|
**_
|
|
3502
2955
|
) -> Optional[int]:
|
|
3503
2956
|
"""
|
|
@@ -3505,8 +2958,16 @@ def _check_cropland_high_with_manure_category(
|
|
|
3505
2958
|
|
|
3506
2959
|
Parameters
|
|
3507
2960
|
----------
|
|
3508
|
-
|
|
3509
|
-
|
|
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.
|
|
3510
2971
|
|
|
3511
2972
|
Returns
|
|
3512
2973
|
-------
|
|
@@ -3515,11 +2976,11 @@ def _check_cropland_high_with_manure_category(
|
|
|
3515
2976
|
"""
|
|
3516
2977
|
conditions = {
|
|
3517
2978
|
1: all([
|
|
3518
|
-
not
|
|
3519
|
-
not
|
|
3520
|
-
not
|
|
3521
|
-
|
|
3522
|
-
|
|
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
|
|
3523
2984
|
])
|
|
3524
2985
|
}
|
|
3525
2986
|
|
|
@@ -3529,15 +2990,41 @@ def _check_cropland_high_with_manure_category(
|
|
|
3529
2990
|
|
|
3530
2991
|
|
|
3531
2992
|
def _check_cropland_high_without_manure_category(
|
|
3532
|
-
|
|
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
|
+
**_
|
|
3533
3004
|
) -> Optional[int]:
|
|
3534
3005
|
"""
|
|
3535
3006
|
Checks the Cropland High without Manure IPCC Carbon Input Category based on the given carbon input arguments.
|
|
3536
3007
|
|
|
3537
3008
|
Parameters
|
|
3538
3009
|
----------
|
|
3539
|
-
|
|
3540
|
-
|
|
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.
|
|
3541
3028
|
|
|
3542
3029
|
Returns
|
|
3543
3030
|
-------
|
|
@@ -3546,17 +3033,17 @@ def _check_cropland_high_without_manure_category(
|
|
|
3546
3033
|
"""
|
|
3547
3034
|
conditions = {
|
|
3548
3035
|
1: all([
|
|
3549
|
-
not
|
|
3550
|
-
not
|
|
3551
|
-
not
|
|
3552
|
-
|
|
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,
|
|
3553
3040
|
any([
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3041
|
+
has_irrigation,
|
|
3042
|
+
has_practice_increasing_c_input,
|
|
3043
|
+
has_cover_crop,
|
|
3044
|
+
has_organic_fertiliser_or_soil_amendment_used
|
|
3558
3045
|
]),
|
|
3559
|
-
not
|
|
3046
|
+
not has_animal_manure_used
|
|
3560
3047
|
])
|
|
3561
3048
|
}
|
|
3562
3049
|
|
|
@@ -3566,7 +3053,16 @@ def _check_cropland_high_without_manure_category(
|
|
|
3566
3053
|
|
|
3567
3054
|
|
|
3568
3055
|
def _check_cropland_medium_category(
|
|
3569
|
-
|
|
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,
|
|
3570
3066
|
**_
|
|
3571
3067
|
) -> Optional[int]:
|
|
3572
3068
|
"""
|
|
@@ -3574,8 +3070,24 @@ def _check_cropland_medium_category(
|
|
|
3574
3070
|
|
|
3575
3071
|
Parameters
|
|
3576
3072
|
----------
|
|
3577
|
-
|
|
3578
|
-
|
|
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.
|
|
3579
3091
|
|
|
3580
3092
|
Returns
|
|
3581
3093
|
-------
|
|
@@ -3584,43 +3096,43 @@ def _check_cropland_medium_category(
|
|
|
3584
3096
|
"""
|
|
3585
3097
|
conditions = {
|
|
3586
3098
|
1: all([
|
|
3587
|
-
|
|
3588
|
-
|
|
3099
|
+
has_residue_removed_or_burnt,
|
|
3100
|
+
has_animal_manure_used
|
|
3589
3101
|
]),
|
|
3590
3102
|
2: all([
|
|
3591
|
-
not
|
|
3103
|
+
not has_residue_removed_or_burnt,
|
|
3592
3104
|
any([
|
|
3593
|
-
|
|
3594
|
-
|
|
3105
|
+
has_low_residue_producing_crops,
|
|
3106
|
+
has_bare_fallow
|
|
3595
3107
|
]),
|
|
3596
3108
|
any([
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3109
|
+
has_irrigation,
|
|
3110
|
+
has_practice_increasing_c_input,
|
|
3111
|
+
has_cover_crop,
|
|
3112
|
+
has_organic_fertiliser_or_soil_amendment_used,
|
|
3601
3113
|
])
|
|
3602
3114
|
]),
|
|
3603
3115
|
3: all([
|
|
3604
|
-
not
|
|
3605
|
-
not
|
|
3606
|
-
not
|
|
3607
|
-
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,
|
|
3608
3120
|
any([
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3121
|
+
has_irrigation,
|
|
3122
|
+
has_practice_increasing_c_input,
|
|
3123
|
+
has_cover_crop,
|
|
3124
|
+
has_organic_fertiliser_or_soil_amendment_used
|
|
3613
3125
|
])
|
|
3614
3126
|
]),
|
|
3615
3127
|
4: all([
|
|
3616
|
-
not
|
|
3617
|
-
not
|
|
3618
|
-
not
|
|
3619
|
-
|
|
3620
|
-
not
|
|
3621
|
-
not
|
|
3622
|
-
not
|
|
3623
|
-
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
|
|
3624
3136
|
])
|
|
3625
3137
|
}
|
|
3626
3138
|
|
|
@@ -3630,7 +3142,16 @@ def _check_cropland_medium_category(
|
|
|
3630
3142
|
|
|
3631
3143
|
|
|
3632
3144
|
def _check_cropland_low_category(
|
|
3633
|
-
|
|
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,
|
|
3634
3155
|
**_
|
|
3635
3156
|
) -> Optional[int]:
|
|
3636
3157
|
"""
|
|
@@ -3638,8 +3159,24 @@ def _check_cropland_low_category(
|
|
|
3638
3159
|
|
|
3639
3160
|
Parameters
|
|
3640
3161
|
----------
|
|
3641
|
-
|
|
3642
|
-
|
|
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.
|
|
3643
3180
|
|
|
3644
3181
|
Returns
|
|
3645
3182
|
-------
|
|
@@ -3648,29 +3185,29 @@ def _check_cropland_low_category(
|
|
|
3648
3185
|
"""
|
|
3649
3186
|
conditions = {
|
|
3650
3187
|
1: all([
|
|
3651
|
-
|
|
3652
|
-
not
|
|
3188
|
+
has_residue_removed_or_burnt,
|
|
3189
|
+
not has_animal_manure_used
|
|
3653
3190
|
]),
|
|
3654
3191
|
2: all([
|
|
3655
|
-
not
|
|
3192
|
+
not has_residue_removed_or_burnt,
|
|
3656
3193
|
any([
|
|
3657
|
-
|
|
3658
|
-
|
|
3194
|
+
has_low_residue_producing_crops,
|
|
3195
|
+
has_bare_fallow
|
|
3659
3196
|
]),
|
|
3660
|
-
not
|
|
3661
|
-
not
|
|
3662
|
-
not
|
|
3663
|
-
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
|
|
3664
3201
|
]),
|
|
3665
3202
|
3: all([
|
|
3666
|
-
not
|
|
3667
|
-
not
|
|
3668
|
-
not
|
|
3669
|
-
not
|
|
3670
|
-
not
|
|
3671
|
-
not
|
|
3672
|
-
not
|
|
3673
|
-
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
|
|
3674
3211
|
])
|
|
3675
3212
|
}
|
|
3676
3213
|
|
|
@@ -3679,9 +3216,9 @@ def _check_cropland_low_category(
|
|
|
3679
3216
|
)
|
|
3680
3217
|
|
|
3681
3218
|
|
|
3682
|
-
def
|
|
3219
|
+
def _get_carbon_input_kwargs(
|
|
3683
3220
|
management_nodes: list[dict]
|
|
3684
|
-
) ->
|
|
3221
|
+
) -> dict:
|
|
3685
3222
|
"""
|
|
3686
3223
|
Creates CarbonInputArgs based on the provided list of management nodes.
|
|
3687
3224
|
|
|
@@ -3692,8 +3229,8 @@ def _make_carbon_input_args(
|
|
|
3692
3229
|
|
|
3693
3230
|
Returns
|
|
3694
3231
|
-------
|
|
3695
|
-
|
|
3696
|
-
The carbon input arguments.
|
|
3232
|
+
dict
|
|
3233
|
+
The carbon input keyword arguments.
|
|
3697
3234
|
"""
|
|
3698
3235
|
|
|
3699
3236
|
PRACTICE_INCREASING_C_INPUT_LOOKUP = LOOKUPS["landUseManagement"]
|
|
@@ -3708,62 +3245,56 @@ def _make_carbon_input_args(
|
|
|
3708
3245
|
ORGANIC_FERTILISER_USED_TERM_ID
|
|
3709
3246
|
}
|
|
3710
3247
|
|
|
3711
|
-
crop_residue_management_nodes = filter_list_term_type(
|
|
3712
|
-
|
|
3713
|
-
)
|
|
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])
|
|
3714
3252
|
|
|
3715
|
-
|
|
3716
|
-
|
|
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)
|
|
3717
3255
|
)
|
|
3718
3256
|
|
|
3719
|
-
|
|
3720
|
-
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
water_regime_nodes = filter_list_term_type(
|
|
3724
|
-
management_nodes, [TermTermType.WATERREGIME]
|
|
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
|
|
3725
3261
|
)
|
|
3726
3262
|
|
|
3727
|
-
|
|
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
|
|
3269
|
+
)
|
|
3728
3270
|
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
cumulative_threshold=MIN_AREA_THRESHOLD,
|
|
3733
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
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)
|
|
3734
3274
|
)
|
|
3735
3275
|
|
|
3276
|
+
has_irrigation = _has_irrigation(water_regime_nodes)
|
|
3277
|
+
|
|
3736
3278
|
# SUPER_MAJORITY_AREA_THRESHOLD
|
|
3737
3279
|
has_low_residue_producing_crops = cumulative_nodes_lookup_match(
|
|
3738
3280
|
land_cover_nodes,
|
|
3739
3281
|
lookup=LOW_RESIDUE_PRODUCING_CROP_LOOKUP,
|
|
3740
3282
|
target_lookup_values=True,
|
|
3741
|
-
cumulative_threshold=SUPER_MAJORITY_AREA_THRESHOLD
|
|
3742
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
3743
|
-
)
|
|
3744
|
-
|
|
3745
|
-
has_bare_fallow = cumulative_nodes_term_match(
|
|
3746
|
-
land_use_management_nodes,
|
|
3747
|
-
target_term_ids=SHORT_BARE_FALLOW_TERM_ID,
|
|
3748
|
-
cumulative_threshold=MIN_AREA_THRESHOLD,
|
|
3749
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
3283
|
+
cumulative_threshold=SUPER_MAJORITY_AREA_THRESHOLD
|
|
3750
3284
|
)
|
|
3751
3285
|
|
|
3752
3286
|
has_n_fixing_crop = cumulative_nodes_lookup_match(
|
|
3753
3287
|
land_cover_nodes,
|
|
3754
3288
|
lookup=N_FIXING_CROP_LOOKUP,
|
|
3755
3289
|
target_lookup_values=True,
|
|
3756
|
-
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3757
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
3290
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3758
3291
|
)
|
|
3759
3292
|
|
|
3760
|
-
|
|
3761
|
-
get_node_value(node) for node in land_use_management_nodes
|
|
3762
|
-
if node_term_match(node, INORGANIC_NITROGEN_FERTILISER_USED_TERM_ID)
|
|
3763
|
-
)
|
|
3293
|
+
has_n_fixing_crop_or_inorganic_n_fertiliser_used = has_n_fixing_crop or has_inorganic_n_fertiliser_used
|
|
3764
3294
|
|
|
3765
|
-
|
|
3766
|
-
|
|
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)
|
|
3767
3298
|
)
|
|
3768
3299
|
|
|
3769
3300
|
has_practice_increasing_c_input = cumulative_nodes_match(
|
|
@@ -3772,27 +3303,13 @@ def _make_carbon_input_args(
|
|
|
3772
3303
|
and not node_term_match(node, EXCLUDED_PRACTICE_TERM_IDS)
|
|
3773
3304
|
),
|
|
3774
3305
|
land_use_management_nodes,
|
|
3775
|
-
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3776
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
3777
|
-
)
|
|
3778
|
-
|
|
3779
|
-
has_cover_crop = cumulative_nodes_match(
|
|
3780
|
-
lambda node: any(
|
|
3781
|
-
get_node_property(node, term_id, False).get("value", False) for term_id in get_cover_crop_property_terms()
|
|
3782
|
-
),
|
|
3783
|
-
land_cover_nodes,
|
|
3784
|
-
cumulative_threshold=MIN_AREA_THRESHOLD,
|
|
3785
|
-
default_node_value=DEFAULT_NODE_VALUE
|
|
3786
|
-
)
|
|
3787
|
-
|
|
3788
|
-
has_organic_fertiliser_or_soil_amendment_used = any(
|
|
3789
|
-
get_node_value(node) for node in land_use_management_nodes
|
|
3790
|
-
if node_term_match(node, ORGANIC_FERTILISER_USED_TERM_ID)
|
|
3306
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
3791
3307
|
)
|
|
3792
3308
|
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
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
|
|
3796
3313
|
)
|
|
3797
3314
|
|
|
3798
3315
|
num_grassland_improvements = [
|
|
@@ -3802,18 +3319,18 @@ def _make_carbon_input_args(
|
|
|
3802
3319
|
has_organic_fertiliser_or_soil_amendment_used
|
|
3803
3320
|
].count(True)
|
|
3804
3321
|
|
|
3805
|
-
return
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
has_n_fixing_crop_or_inorganic_n_fertiliser_used
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
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
|
+
}
|
|
3817
3334
|
|
|
3818
3335
|
|
|
3819
3336
|
GRASSLAND_IPCC_CARBON_INPUT_CATEGORY_DECISION_TREE = {
|
|
@@ -3889,14 +3406,12 @@ def _assign_ipcc_carbon_input_category(
|
|
|
3889
3406
|
decision_tree = DECISION_TREE_FROM_IPCC_MANAGEMENT_CATEGORY.get(ipcc_management_category, {})
|
|
3890
3407
|
default = DEFAULT_CARBON_INPUT_CATEGORY.get(ipcc_management_category, IpccCarbonInputCategory.OTHER)
|
|
3891
3408
|
|
|
3892
|
-
carbon_input_args = _make_carbon_input_args(management_nodes)
|
|
3893
|
-
|
|
3894
3409
|
should_run = len(management_nodes) > 0
|
|
3895
3410
|
|
|
3896
3411
|
return next(
|
|
3897
3412
|
(key for key in decision_tree if decision_tree[key](
|
|
3898
|
-
carbon_input_args,
|
|
3899
3413
|
key=key,
|
|
3414
|
+
**_get_carbon_input_kwargs(management_nodes)
|
|
3900
3415
|
)),
|
|
3901
3416
|
default
|
|
3902
3417
|
) if should_run else default
|
|
@@ -3905,50 +3420,477 @@ def _assign_ipcc_carbon_input_category(
|
|
|
3905
3420
|
# --- TIER 1 SOC MODEL ---
|
|
3906
3421
|
|
|
3907
3422
|
|
|
3908
|
-
def
|
|
3423
|
+
def _run_tier_1(
|
|
3424
|
+
inventory: dict,
|
|
3425
|
+
*,
|
|
3426
|
+
eco_climate_zone: int,
|
|
3427
|
+
soc_ref: float,
|
|
3428
|
+
**_
|
|
3429
|
+
) -> list[dict]:
|
|
3909
3430
|
"""
|
|
3910
|
-
|
|
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.
|
|
3911
3434
|
|
|
3912
|
-
|
|
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
|
+
```
|
|
3913
3449
|
|
|
3914
3450
|
Parameters
|
|
3915
3451
|
----------
|
|
3916
|
-
|
|
3917
|
-
|
|
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.
|
|
3918
3460
|
|
|
3919
3461
|
Returns
|
|
3920
3462
|
-------
|
|
3921
|
-
|
|
3922
|
-
|
|
3463
|
+
list[dict]
|
|
3464
|
+
A list of Hestia `Measurement` nodes containing the calculated SOC stocks and additional relevant data.
|
|
3923
3465
|
"""
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
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
|
|
3483
|
+
)
|
|
3484
|
+
|
|
3485
|
+
soc_stocks = _calc_tier_1_soc_stocks(iterated_timestamps, iterated_soc_equilibriums)
|
|
3486
|
+
|
|
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
|
+
```
|
|
3538
|
+
"""
|
|
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)
|
|
3928
3591
|
)
|
|
3929
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
|
|
3930
3597
|
|
|
3931
|
-
|
|
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.
|
|
3932
3608
|
"""
|
|
3933
|
-
|
|
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.
|
|
3934
3732
|
|
|
3935
3733
|
Parameters
|
|
3936
3734
|
----------
|
|
3937
|
-
|
|
3938
|
-
|
|
3735
|
+
group : dict
|
|
3736
|
+
Dictionary containing information for a specific inventory year.
|
|
3939
3737
|
|
|
3940
3738
|
Returns
|
|
3941
3739
|
-------
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
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
|
+
)
|
|
3947
3752
|
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
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
|
+
])
|
|
3758
|
+
|
|
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 ---
|
|
3951
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
|
+
"""
|
|
3952
3894
|
eco_climate_zone = _get_eco_climate_zone(measurement_nodes)
|
|
3953
3895
|
ipcc_soil_category = _assign_ipcc_soil_category(measurement_nodes)
|
|
3954
3896
|
soc_ref = _retrieve_soc_ref(eco_climate_zone, ipcc_soil_category)
|
|
@@ -3957,7 +3899,7 @@ def _should_run_tier_1(site: dict) -> tuple:
|
|
|
3957
3899
|
|
|
3958
3900
|
grouped_land_use_categories = {
|
|
3959
3901
|
year: {
|
|
3960
|
-
|
|
3902
|
+
_InventoryKey.LU_CATEGORY: _assign_ipcc_land_use_category(
|
|
3961
3903
|
site_type,
|
|
3962
3904
|
nodes,
|
|
3963
3905
|
ipcc_soil_category
|
|
@@ -3967,171 +3909,72 @@ def _should_run_tier_1(site: dict) -> tuple:
|
|
|
3967
3909
|
|
|
3968
3910
|
grouped_management_categories = {
|
|
3969
3911
|
year: {
|
|
3970
|
-
|
|
3912
|
+
_InventoryKey.MG_CATEGORY: _assign_ipcc_management_category(
|
|
3971
3913
|
nodes,
|
|
3972
|
-
grouped_land_use_categories[year][
|
|
3914
|
+
grouped_land_use_categories[year][_InventoryKey.LU_CATEGORY]
|
|
3973
3915
|
)
|
|
3974
3916
|
} for year, nodes in grouped_management.items()
|
|
3975
3917
|
}
|
|
3976
3918
|
|
|
3977
3919
|
grouped_carbon_input_categories = {
|
|
3978
3920
|
year: {
|
|
3979
|
-
|
|
3921
|
+
_InventoryKey.CI_CATEGORY: _assign_ipcc_carbon_input_category(
|
|
3980
3922
|
nodes,
|
|
3981
|
-
grouped_management_categories[year][
|
|
3923
|
+
grouped_management_categories[year][_InventoryKey.MG_CATEGORY]
|
|
3982
3924
|
)
|
|
3983
3925
|
} for year, nodes in grouped_management.items()
|
|
3984
3926
|
}
|
|
3985
3927
|
|
|
3986
|
-
grouped_data =
|
|
3928
|
+
grouped_data = merge(
|
|
3987
3929
|
grouped_land_use_categories,
|
|
3988
3930
|
grouped_management_categories,
|
|
3989
3931
|
grouped_carbon_input_categories
|
|
3990
|
-
|
|
3932
|
+
)
|
|
3991
3933
|
|
|
3992
|
-
|
|
3993
|
-
year:
|
|
3994
|
-
|
|
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()
|
|
3995
3937
|
}
|
|
3996
3938
|
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
TERM_ID,
|
|
4004
|
-
f"{end_year}-12-31", # Last year in inventory
|
|
4005
|
-
DEPTH_UPPER,
|
|
4006
|
-
DEPTH_LOWER,
|
|
4007
|
-
depth_strict=True
|
|
4008
|
-
) if end_year else (None, None)
|
|
4009
|
-
|
|
4010
|
-
soc_measurement_datetime = safe_parse_date(soc_measurement_date)
|
|
4011
|
-
soc_measurement_year = (
|
|
4012
|
-
soc_measurement_datetime.year if soc_measurement_datetime else None
|
|
4013
|
-
)
|
|
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
|
+
}
|
|
4014
3945
|
|
|
4015
|
-
|
|
4016
|
-
ipcc_management_categories = [complete_data[year][IpccManagementCategory] for year in timestamps]
|
|
4017
|
-
ipcc_carbon_input_categories = [complete_data[year][IpccCarbonInputCategory] for year in timestamps]
|
|
3946
|
+
return inventory, kwargs
|
|
4018
3947
|
|
|
4019
|
-
should_run = all([
|
|
4020
|
-
soc_ref > 0,
|
|
4021
|
-
isinstance(eco_climate_zone, int) and eco_climate_zone > 0,
|
|
4022
|
-
len(timestamps) > 0,
|
|
4023
|
-
(
|
|
4024
|
-
start_year <= soc_measurement_year < end_year
|
|
4025
|
-
) if soc_measurement_value and soc_measurement_year else True
|
|
4026
|
-
])
|
|
4027
3948
|
|
|
4028
|
-
|
|
4029
|
-
site, MODEL, TERM_ID, should_run,
|
|
4030
|
-
timestamps=log_as_table(set(timestamps)),
|
|
4031
|
-
ipcc_land_use_categories=log_as_table(set(ipcc_land_use_categories)),
|
|
4032
|
-
ipcc_management_categories=log_as_table(set(ipcc_management_categories)),
|
|
4033
|
-
ipcc_carbon_input_categories=log_as_table(set(ipcc_carbon_input_categories)),
|
|
4034
|
-
eco_climate_zone=eco_climate_zone,
|
|
4035
|
-
soc_ref=soc_ref,
|
|
4036
|
-
run_with_soc_measurement=(soc_measurement_value and soc_measurement_year),
|
|
4037
|
-
soc_measurement_value=soc_measurement_value,
|
|
4038
|
-
soc_measurement_year=soc_measurement_year
|
|
4039
|
-
)
|
|
4040
|
-
|
|
4041
|
-
return (
|
|
4042
|
-
should_run,
|
|
4043
|
-
timestamps,
|
|
4044
|
-
ipcc_land_use_categories,
|
|
4045
|
-
ipcc_management_categories,
|
|
4046
|
-
ipcc_carbon_input_categories,
|
|
4047
|
-
eco_climate_zone,
|
|
4048
|
-
soc_ref,
|
|
4049
|
-
soc_measurement_value,
|
|
4050
|
-
soc_measurement_year
|
|
4051
|
-
)
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
def _run_tier_1(
|
|
4055
|
-
timestamps: list[int],
|
|
4056
|
-
ipcc_land_use_categories: list[IpccLandUseCategory],
|
|
4057
|
-
ipcc_management_categories: list[IpccManagementCategory],
|
|
4058
|
-
ipcc_carbon_input_categories: list[IpccCarbonInputCategory],
|
|
4059
|
-
eco_climate_zone: int,
|
|
4060
|
-
soc_ref: float,
|
|
4061
|
-
soc_measurement_value: Optional[float] = None,
|
|
4062
|
-
soc_measurement_year: Optional[int] = None
|
|
4063
|
-
) -> list[dict]:
|
|
3949
|
+
def _should_run_inventory_year_tier_1(group: dict) -> bool:
|
|
4064
3950
|
"""
|
|
4065
|
-
|
|
4066
|
-
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.
|
|
4067
3952
|
|
|
4068
|
-
|
|
3953
|
+
1. Check if the land use category is not "OTHER"
|
|
3954
|
+
2. Check if all required keys are present.
|
|
4069
3955
|
|
|
4070
3956
|
Parameters
|
|
4071
3957
|
----------
|
|
4072
|
-
|
|
4073
|
-
|
|
4074
|
-
ipcc_land_use_categories : list[IpccLandUseCategory]
|
|
4075
|
-
A list of IPCC land use categories for each year in the inventory.
|
|
4076
|
-
ipcc_management_categories : list[IpccManagementCategory]
|
|
4077
|
-
A list of IPCC management categories for each year in the inventory.
|
|
4078
|
-
ipcc_carbon_input_categories : list[IpccCarbonInputCategory]
|
|
4079
|
-
A list of IPCC carbon input categories for each year in the inventory.
|
|
4080
|
-
eco_climate_zone : int
|
|
4081
|
-
The eco-climate zone identifier for the site corresponding to a row in the
|
|
4082
|
-
[ecoClimateZone](https://gitlab.com/hestia-earth/hestia-glossary/-/blob/develop/Measurements/ecoClimateZone-lookup.csv)
|
|
4083
|
-
lookup table.
|
|
4084
|
-
ipcc_soil_category : IpccSoilCategory
|
|
4085
|
-
The reference condition SOC stock in the 0-30cm depth interval, kg C ha-1.
|
|
4086
|
-
soc_measurement_value : float | None, optional
|
|
4087
|
-
Measured SOC stock, if provided.
|
|
4088
|
-
soc_measurement_year : int | None, optional
|
|
4089
|
-
The year the SOC measurement took place, if provided.
|
|
3958
|
+
group : dict
|
|
3959
|
+
Dictionary containing information for a specific inventory year.
|
|
4090
3960
|
|
|
4091
3961
|
Returns
|
|
4092
3962
|
-------
|
|
4093
|
-
|
|
3963
|
+
bool
|
|
3964
|
+
True if the inventory year is valid, False otherwise.
|
|
4094
3965
|
"""
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
ipcc_management_categories,
|
|
4100
|
-
ipcc_carbon_input_categories,
|
|
4101
|
-
eco_climate_zone,
|
|
4102
|
-
soc_ref
|
|
4103
|
-
)
|
|
4104
|
-
|
|
4105
|
-
soc_stocks = _calc_tier_1_soc_stocks(iterated_timestamps, iterated_soc_equilibriums)
|
|
4106
|
-
|
|
4107
|
-
soc_measurement_index = _find_closest_value_index(iterated_timestamps, soc_measurement_year)
|
|
4108
|
-
|
|
4109
|
-
scaled_soc_stocks = _scale_soc_stocks(
|
|
4110
|
-
soc_stocks, soc_measurement_value, soc_measurement_index
|
|
4111
|
-
)
|
|
4112
|
-
|
|
4113
|
-
slice_index = soc_measurement_index or 0
|
|
4114
|
-
result_timestamps = iterated_timestamps[slice_index:]
|
|
4115
|
-
result_soc_stocks = scaled_soc_stocks[slice_index:]
|
|
4116
|
-
|
|
4117
|
-
return [
|
|
4118
|
-
_measurement(
|
|
4119
|
-
year,
|
|
4120
|
-
soc_stock,
|
|
4121
|
-
MeasurementMethodClassification.TIER_1_MODEL.value
|
|
4122
|
-
) for year, soc_stock in zip(
|
|
4123
|
-
result_timestamps,
|
|
4124
|
-
result_soc_stocks
|
|
4125
|
-
)
|
|
4126
|
-
]
|
|
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
|
+
])
|
|
4127
3970
|
|
|
4128
3971
|
|
|
4129
|
-
# ---
|
|
3972
|
+
# --- RUN ---
|
|
4130
3973
|
|
|
4131
3974
|
|
|
4132
3975
|
def run(site: dict) -> list[dict]:
|
|
4133
3976
|
"""
|
|
4134
|
-
Check which
|
|
3977
|
+
Check which Tiers of IPCC SOC model to run, run it and return the formatted output.
|
|
4135
3978
|
|
|
4136
3979
|
Parameters
|
|
4137
3980
|
----------
|
|
@@ -4143,13 +3986,9 @@ def run(site: dict) -> list[dict]:
|
|
|
4143
3986
|
list[dict]
|
|
4144
3987
|
A list of Hestia `Measurement` nodes containing the calculated SOC stocks and additional relevant data.
|
|
4145
3988
|
"""
|
|
4146
|
-
should_run_tier_2,
|
|
4147
|
-
should_run_tier_1, *tier_1_args = (
|
|
4148
|
-
_should_run_tier_1(site) if not should_run_tier_2
|
|
4149
|
-
else (False, None)
|
|
4150
|
-
)
|
|
3989
|
+
should_run_tier_1, should_run_tier_2, inventory, kwargs = _should_run(site)
|
|
4151
3990
|
return (
|
|
4152
|
-
_run_tier_2(
|
|
4153
|
-
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
|
|
4154
3993
|
else []
|
|
4155
3994
|
)
|