hestia-earth-models 0.62.1__py3-none-any.whl → 0.62.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of hestia-earth-models might be problematic. Click here for more details.
- hestia_earth/models/cycle/coldCarcassWeightPerHead.py +4 -2
- hestia_earth/models/cycle/coldDressedCarcassWeightPerHead.py +2 -2
- hestia_earth/models/cycle/concentrateFeed.py +3 -3
- hestia_earth/models/cycle/feedConversionRatio/feedConversionRatioNitrogen.py +2 -1
- hestia_earth/models/cycle/readyToCookWeightPerHead.py +2 -2
- hestia_earth/models/ipcc2019/animal/pastureGrass.py +50 -40
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2_utils.py +208 -39
- hestia_earth/models/ipcc2019/organicCarbonPerHa_utils.py +2 -6
- hestia_earth/models/ipcc2019/pastureGrass.py +43 -41
- hestia_earth/models/ipcc2019/pastureGrass_utils.py +63 -109
- hestia_earth/models/mocking/search-results.json +393 -249
- hestia_earth/models/site/management.py +52 -29
- hestia_earth/models/utils/blank_node.py +38 -0
- hestia_earth/models/utils/property.py +6 -6
- hestia_earth/models/utils/site.py +7 -0
- hestia_earth/models/utils/term.py +21 -1
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.2.dist-info}/METADATA +1 -1
- {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.2.dist-info}/RECORD +25 -25
- tests/models/ipcc2019/test_organicCarbonPerHa.py +9 -20
- tests/models/ipcc2019/test_organicCarbonPerHa_tier_2_utils.py +0 -8
- tests/models/site/test_management.py +31 -3
- {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.2.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.2.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.62.1.dist-info → hestia_earth_models-0.62.2.dist-info}/top_level.txt +0 -0
|
@@ -42,8 +42,10 @@ def _property(value: float):
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
def _run(product: dict):
|
|
45
|
-
liveweightPerHead = get_node_property_value(MODEL, product, 'liveweightPerHead')
|
|
46
|
-
processingConversion = get_node_property_value(
|
|
45
|
+
liveweightPerHead = get_node_property_value(MODEL, product, 'liveweightPerHead', term=TERM_ID)
|
|
46
|
+
processingConversion = get_node_property_value(
|
|
47
|
+
MODEL, product, 'processingConversionLiveweightToColdCarcassWeight', term=TERM_ID
|
|
48
|
+
)
|
|
47
49
|
value = liveweightPerHead * processingConversion if all([liveweightPerHead, processingConversion]) else None
|
|
48
50
|
prop = _property(value) if value else None
|
|
49
51
|
return {**product, 'properties': product.get('properties', []) + [prop]} if prop else None
|
|
@@ -46,9 +46,9 @@ def _property(value: float):
|
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
def _run(product: dict):
|
|
49
|
-
liveweightPerHead = get_node_property_value(MODEL, product, 'liveweightPerHead')
|
|
49
|
+
liveweightPerHead = get_node_property_value(MODEL, product, 'liveweightPerHead', term=TERM_ID)
|
|
50
50
|
processingConversion = get_node_property_value(
|
|
51
|
-
MODEL, product, 'processingConversionLiveweightToColdDressedCarcassWeight'
|
|
51
|
+
MODEL, product, 'processingConversionLiveweightToColdDressedCarcassWeight', term=TERM_ID
|
|
52
52
|
)
|
|
53
53
|
value = liveweightPerHead * processingConversion if all([liveweightPerHead, processingConversion]) else None
|
|
54
54
|
prop = _property(value) if value else None
|
|
@@ -79,7 +79,7 @@ def _calculate_value(cycle: dict, product: dict, inputs: list, property_id: str,
|
|
|
79
79
|
def _calculate_default_value(cycle: dict, product: dict, inputs: list, property_id: str):
|
|
80
80
|
values = [(
|
|
81
81
|
i.get('term', {}).get('@id'),
|
|
82
|
-
get_node_property_value(MODEL, i, property_id),
|
|
82
|
+
get_node_property_value(MODEL, i, property_id, term=TERM_ID),
|
|
83
83
|
list_sum(i.get('value', []))
|
|
84
84
|
) for i in inputs]
|
|
85
85
|
return _calculate_value(cycle, product, inputs, property_id, values)
|
|
@@ -88,8 +88,8 @@ def _calculate_default_value(cycle: dict, product: dict, inputs: list, property_
|
|
|
88
88
|
def _calculate_N_value(cycle: dict, product: dict, inputs: list, property_id: str):
|
|
89
89
|
values = [(
|
|
90
90
|
i.get('term', {}).get('@id'),
|
|
91
|
-
get_node_property_value(MODEL, i, property_id) or
|
|
92
|
-
get_node_property_value(MODEL, i, 'crudeProteinContent', default=0) * 0.16,
|
|
91
|
+
get_node_property_value(MODEL, i, property_id, term=TERM_ID) or
|
|
92
|
+
get_node_property_value(MODEL, i, 'crudeProteinContent', default=0, term=TERM_ID) * 0.16,
|
|
93
93
|
list_sum(i.get('value', []))
|
|
94
94
|
) for i in inputs]
|
|
95
95
|
return _calculate_value(cycle, product, inputs, property_id, values)
|
|
@@ -72,5 +72,6 @@ TERM_ID = 'feedConversionRatioNitrogen'
|
|
|
72
72
|
def run(cycle: dict, feed: float):
|
|
73
73
|
inputs = get_feed_inputs(cycle)
|
|
74
74
|
return list_sum(non_empty_list([
|
|
75
|
-
get_node_property_value_converted(MODEL, input, 'crudeProteinContent', default=0) / 6.25
|
|
75
|
+
get_node_property_value_converted(MODEL, input, 'crudeProteinContent', default=0, term=TERM_ID) / 6.25
|
|
76
|
+
for input in inputs
|
|
76
77
|
]))
|
|
@@ -42,9 +42,9 @@ def _property(value: float):
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
def _run(product: dict):
|
|
45
|
-
liveweightPerHead = get_node_property_value(MODEL, product, 'liveweightPerHead')
|
|
45
|
+
liveweightPerHead = get_node_property_value(MODEL, product, 'liveweightPerHead', term=TERM_ID)
|
|
46
46
|
processingConversion = get_node_property_value(
|
|
47
|
-
MODEL, product, 'processingConversionLiveweightToReadyToCookWeight'
|
|
47
|
+
MODEL, product, 'processingConversionLiveweightToReadyToCookWeight', term=TERM_ID
|
|
48
48
|
)
|
|
49
49
|
value = liveweightPerHead * processingConversion if all([liveweightPerHead, processingConversion]) else None
|
|
50
50
|
prop = _property(value) if value else None
|
|
@@ -12,7 +12,8 @@ from hestia_earth.schema import TermTermType
|
|
|
12
12
|
from hestia_earth.utils.model import filter_list_term_type
|
|
13
13
|
from hestia_earth.utils.tools import list_sum
|
|
14
14
|
|
|
15
|
-
from hestia_earth.models.log import logRequirements, logShouldRun, debugValues
|
|
15
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, debugValues, log_as_table
|
|
16
|
+
from hestia_earth.models.utils.blank_node import lookups_logs, properties_logs
|
|
16
17
|
from hestia_earth.models.utils.input import _new_input
|
|
17
18
|
from hestia_earth.models.utils.term import get_wool_terms, get_lookup_value
|
|
18
19
|
from hestia_earth.models.utils.completeness import _is_term_type_complete, _is_term_type_incomplete
|
|
@@ -26,6 +27,7 @@ from ..pastureGrass_utils import (
|
|
|
26
27
|
calculate_REM,
|
|
27
28
|
calculate_REG,
|
|
28
29
|
calculate_NEfeed,
|
|
30
|
+
calculate_GE,
|
|
29
31
|
product_wool_energy,
|
|
30
32
|
get_animals,
|
|
31
33
|
get_animal_values
|
|
@@ -115,7 +117,8 @@ REQUIREMENTS = {
|
|
|
115
117
|
}
|
|
116
118
|
LOOKUPS = {
|
|
117
119
|
"animalManagement": [
|
|
118
|
-
"mjKgEvMilkIpcc2019"
|
|
120
|
+
"mjKgEvMilkIpcc2019",
|
|
121
|
+
"defaultFatContentEvMilkIpcc2019"
|
|
119
122
|
],
|
|
120
123
|
"animalProduct": ["mjKgEvWoolNetEnergyWoolIpcc2019", "allowedLiveAnimalTermIds"],
|
|
121
124
|
"liveAnimal": [
|
|
@@ -184,38 +187,50 @@ def calculate_NEwool(cycle: dict, animal: dict, products: list, total_weight: fl
|
|
|
184
187
|
return total_energy * animal_weight/total_weight
|
|
185
188
|
|
|
186
189
|
|
|
187
|
-
def
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
NEm, NEa, NEl, NEwork, NEp, NEg = get_animal_values(cycle, animal, system, log_node=animal)
|
|
193
|
-
|
|
194
|
-
NEm_feed, NEg_feed = calculate_NEfeed(animal)
|
|
195
|
-
debugValues(animal, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
196
|
-
NEm=NEm,
|
|
197
|
-
NEa=NEa,
|
|
198
|
-
NEl=NEl,
|
|
199
|
-
NEwork=NEwork,
|
|
200
|
-
NEp=NEp,
|
|
201
|
-
NEg=NEg,
|
|
202
|
-
NEm_feed=NEm_feed,
|
|
203
|
-
NEg_feed=NEg_feed)
|
|
204
|
-
|
|
205
|
-
return (NEm + NEa + NEl + NEwork + NEp - NEm_feed)/REM + (NEg + NEwool - NEg_feed)/REG
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
def _run_practice(animal: dict, GE: float, meanECHHV: float):
|
|
190
|
+
def _run_practice(
|
|
191
|
+
animal: dict, values: dict, meanDE: float, meanECHHV: float, REM: float, REG: float,
|
|
192
|
+
NEwool: float, NEm_feed: float, NEg_feed: float
|
|
193
|
+
):
|
|
209
194
|
def run(practice: dict):
|
|
210
195
|
key = practice.get('key', {})
|
|
211
196
|
key_id = key.get('@id')
|
|
212
197
|
input_term_id = practice_input_id(practice)
|
|
198
|
+
|
|
199
|
+
GE = (
|
|
200
|
+
calculate_GE([values], REM, REG, NEwool, NEm_feed, NEg_feed) / (meanDE/100)
|
|
201
|
+
) if meanDE else 0
|
|
202
|
+
|
|
213
203
|
value = (GE / meanECHHV) * (list_sum(practice.get('value', [0])) / 100)
|
|
214
204
|
|
|
205
|
+
logs = log_as_table(values | {
|
|
206
|
+
'animalId': animal.get('term', {}).get('@id'),
|
|
207
|
+
'practiceKeyId': key_id,
|
|
208
|
+
'GE': GE,
|
|
209
|
+
'NEmFeed': NEm_feed,
|
|
210
|
+
'NEgFeed': NEg_feed,
|
|
211
|
+
'REM': REM,
|
|
212
|
+
'REG': REG,
|
|
213
|
+
'NEwool': NEwool,
|
|
214
|
+
'meanECHHV': meanECHHV,
|
|
215
|
+
'meanDE': meanDE
|
|
216
|
+
})
|
|
217
|
+
animal_lookups = lookups_logs(MODEL, [animal], LOOKUPS, model_key=MODEL_KEY, term=input_term_id)
|
|
218
|
+
animal_properties = properties_logs([animal], properties=[
|
|
219
|
+
'liveweightPerHead',
|
|
220
|
+
'hoursWorkedPerDay',
|
|
221
|
+
'animalsPerBirth',
|
|
222
|
+
'pregnancyRateTotal',
|
|
223
|
+
'weightAtMaturity',
|
|
224
|
+
'liveweightGain',
|
|
225
|
+
'weightAtWeaning',
|
|
226
|
+
'weightAtOneYear',
|
|
227
|
+
'weightAtSlaughter'
|
|
228
|
+
])
|
|
229
|
+
|
|
215
230
|
logRequirements(animal, model=MODEL, term=input_term_id, model_key=MODEL_KEY,
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
231
|
+
animal_logs=logs,
|
|
232
|
+
animal_lookups=animal_lookups,
|
|
233
|
+
animal_properties=animal_properties)
|
|
219
234
|
|
|
220
235
|
logShouldRun(animal, MODEL, input_term_id, True, model_key=MODEL_KEY)
|
|
221
236
|
|
|
@@ -235,21 +250,16 @@ def _run_animal(cycle: dict, meanDE: float, meanECHHV: float, system: dict, prac
|
|
|
235
250
|
total_liveWeightPerHead = _sum_liveWeightPerHead(animals)
|
|
236
251
|
|
|
237
252
|
def run(animal: dict):
|
|
238
|
-
term_id = animal.get('term', {}).get('@id')
|
|
239
|
-
|
|
240
253
|
NEwool = calculate_NEwool(cycle, animal, wool_products, total_liveWeightPerHead) if (
|
|
241
254
|
total_liveWeightPerHead > 0
|
|
242
255
|
) else 0
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
debugValues(animal, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
246
|
-
REM=REM,
|
|
247
|
-
REG=REG,
|
|
248
|
-
NEwool=NEwool,
|
|
249
|
-
GE=GE,
|
|
250
|
-
meanDE=meanDE)
|
|
256
|
+
NEm_feed, NEg_feed = calculate_NEfeed(animal)
|
|
257
|
+
animal_values = get_animal_values(cycle, animal, system)
|
|
251
258
|
|
|
252
|
-
inputs = list(map(
|
|
259
|
+
inputs = list(map(
|
|
260
|
+
_run_practice(animal, animal_values, meanDE, meanECHHV, REM, REG, NEwool, NEm_feed, NEg_feed),
|
|
261
|
+
practices
|
|
262
|
+
))
|
|
253
263
|
return animal | {
|
|
254
264
|
'inputs': animal.get('inputs', []) + inputs
|
|
255
265
|
}
|
|
@@ -289,8 +299,8 @@ def _should_run(cycle: dict, animals: list, practices: dict):
|
|
|
289
299
|
term_type_freshForage_incomplete=freshForage_incomplete,
|
|
290
300
|
no_cycle_inputs_feed=no_cycle_inputs_feed,
|
|
291
301
|
all_animals_have_value=all_animals_have_value,
|
|
292
|
-
meanDE=
|
|
293
|
-
meanECHHV=
|
|
302
|
+
meanDE=calculate_meanDE(practices, term=term_id),
|
|
303
|
+
meanECHHV=calculate_meanECHHV(practices, term=term_id))
|
|
294
304
|
|
|
295
305
|
logShouldRun(animal, MODEL, term_id, should_run, model_key=MODEL_KEY)
|
|
296
306
|
|
|
@@ -26,7 +26,7 @@ from hestia_earth.models.utils.array_builders import (
|
|
|
26
26
|
)
|
|
27
27
|
from hestia_earth.models.utils.blank_node import (
|
|
28
28
|
cumulative_nodes_lookup_match, cumulative_nodes_term_match, group_nodes_by_year,
|
|
29
|
-
group_nodes_by_year_and_month, GroupNodesByYearMode, node_term_match
|
|
29
|
+
group_nodes_by_year_and_month, GroupNodesByYearMode, node_lookup_match, node_term_match
|
|
30
30
|
)
|
|
31
31
|
from hestia_earth.models.utils.cycle import check_cycle_site_ids_identical
|
|
32
32
|
from hestia_earth.models.utils.descriptive_stats import calc_descriptive_stats
|
|
@@ -36,16 +36,16 @@ from hestia_earth.models.utils.site import related_cycles
|
|
|
36
36
|
|
|
37
37
|
from .organicCarbonPerHa_utils import (
|
|
38
38
|
CarbonSource, check_consecutive, DEPTH_LOWER, DEPTH_UPPER, check_irrigation,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
sample_plus_minus_uncertainty, sample_truncated_normal, STATS_DEFINITION
|
|
39
|
+
get_cover_crop_property_terms_with_cache, get_upland_rice_crop_terms_with_cache,
|
|
40
|
+
get_upland_rice_land_cover_terms_with_cache, IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE,
|
|
41
|
+
IPCC_MANAGEMENT_CATEGORY_TO_TILLAGE_MANAGEMENT_LOOKUP_VALUE, IpccLandUseCategory, IpccManagementCategory,
|
|
42
|
+
MIN_AREA_THRESHOLD, MIN_YIELD_THRESHOLD, sample_constant, sample_plus_minus_uncertainty, sample_truncated_normal,
|
|
43
|
+
STATS_DEFINITION
|
|
45
44
|
)
|
|
46
45
|
|
|
47
46
|
_LOOKUPS = {
|
|
48
47
|
"crop": "IPCC_LAND_USE_CATEGORY",
|
|
48
|
+
"landCover": "IPCC_LAND_USE_CATEGORY",
|
|
49
49
|
"tillage": "IPCC_TILLAGE_MANAGEMENT_CATEGORY"
|
|
50
50
|
}
|
|
51
51
|
|
|
@@ -59,10 +59,27 @@ _NUMBER_OF_TILLAGES_TERM_ID = "numberOfTillages"
|
|
|
59
59
|
_TEMPERATURE_MONTHLY_TERM_ID = "temperatureMonthly"
|
|
60
60
|
_PRECIPITATION_MONTHLY_TERM_ID = "precipitationMonthly"
|
|
61
61
|
_PET_MONTHLY_TERM_ID = "potentialEvapotranspirationMonthly"
|
|
62
|
+
_ABOVE_GROUND_CROP_RESIDUE_TOTAL_TERM_ID = "aboveGroundCropResidueTotal"
|
|
62
63
|
_CARBON_CONTENT_TERM_ID = "carbonContent"
|
|
63
64
|
_NITROGEN_CONTENT_TERM_ID = "nitrogenContent"
|
|
64
65
|
_LIGNIN_CONTENT_TERM_ID = "ligninContent"
|
|
65
66
|
|
|
67
|
+
_CROP_RESIDUE_MANAGEMENT_TERM_IDS = [
|
|
68
|
+
"residueIncorporated",
|
|
69
|
+
"residueIncorporatedLessThan30DaysBeforeCultivation",
|
|
70
|
+
"residueIncorporatedMoreThan30DaysBeforeCultivation",
|
|
71
|
+
"residueLeftOnField"
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
_CARBON_SOURCE_TERM_IDS = [
|
|
75
|
+
"discardedCropIncorporated",
|
|
76
|
+
"discardedCropLeftOnField",
|
|
77
|
+
"belowGroundCropResidue",
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
_MIN_RESIDUE_LEFT_ON_FIELD = 20 # TODO: Confirm assumption
|
|
81
|
+
_DEFAULT_COVER_CROP_BIOMASS = 4000 # TODO: Confirm assumption, Source PAS 2050-1:2012
|
|
82
|
+
|
|
66
83
|
_CARBON_INPUT_PROPERTY_TERM_IDS = [
|
|
67
84
|
_CARBON_CONTENT_TERM_ID,
|
|
68
85
|
_NITROGEN_CONTENT_TERM_ID,
|
|
@@ -1284,8 +1301,8 @@ def _get_grouped_sand_content_measurements(grouped_measurements: dict) -> dict:
|
|
|
1284
1301
|
|
|
1285
1302
|
def _get_grouped_carbon_input_data(grouped_cycles: dict) -> dict:
|
|
1286
1303
|
grouped_carbon_sources = {
|
|
1287
|
-
year:
|
|
1288
|
-
for year,
|
|
1304
|
+
year: flatten([_get_carbon_sources(cycle) for cycle in cycles])
|
|
1305
|
+
for year, cycles in grouped_cycles.items()
|
|
1289
1306
|
}
|
|
1290
1307
|
|
|
1291
1308
|
return {
|
|
@@ -1297,67 +1314,219 @@ def _get_grouped_carbon_input_data(grouped_cycles: dict) -> dict:
|
|
|
1297
1314
|
}
|
|
1298
1315
|
|
|
1299
1316
|
|
|
1300
|
-
def
|
|
1317
|
+
def _get_carbon_sources(cycle: dict) -> list[CarbonSource]:
|
|
1301
1318
|
"""
|
|
1302
|
-
|
|
1319
|
+
Extract and format the carbon source data from a cycle's inputs and products.
|
|
1303
1320
|
|
|
1304
1321
|
Carbon sources can be either a Hestia `Product` node (e.g. crop residue) or `Input` node (e.g. organic amendment).
|
|
1305
1322
|
|
|
1306
1323
|
Parameters
|
|
1307
1324
|
----------
|
|
1308
|
-
|
|
1309
|
-
A
|
|
1325
|
+
cycle : list[dict]
|
|
1326
|
+
A Hestia `Cycle` node, see: https://www.hestia.earth/schema/Cycle.
|
|
1310
1327
|
|
|
1311
1328
|
Returns
|
|
1312
1329
|
-------
|
|
1313
1330
|
list[CarbonSource]
|
|
1314
|
-
A formatted list of `CarbonSource`s
|
|
1331
|
+
A formatted list of `CarbonSource`s.
|
|
1315
1332
|
"""
|
|
1316
|
-
inputs_and_products =
|
|
1317
|
-
[cycle.get("inputs", []) + cycle.get("products", []) for cycle in cycles]
|
|
1318
|
-
))
|
|
1319
|
-
|
|
1333
|
+
inputs_and_products = cycle.get("inputs", []) + cycle.get("products", [])
|
|
1320
1334
|
return non_empty_list([
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1335
|
+
next(
|
|
1336
|
+
(_func(node, cycle) for validator, _func in _CARBON_SOURCE_DECISION_TREE.items() if validator(node)),
|
|
1337
|
+
None
|
|
1338
|
+
) for node in inputs_and_products
|
|
1339
|
+
])
|
|
1340
|
+
|
|
1341
|
+
|
|
1342
|
+
def _should_run_carbon_source_ag_residue(node: dict) -> bool:
|
|
1343
|
+
"""
|
|
1344
|
+
Determine whether an input or product is a valid above-ground biomass carbon source.
|
|
1345
|
+
|
|
1346
|
+
Parameters
|
|
1347
|
+
----------
|
|
1348
|
+
node : dict
|
|
1349
|
+
A Hestia [Input](https://www.hestia.earth/schema/Input) or [Product](https://www.hestia.earth/schema/Product)
|
|
1350
|
+
node.
|
|
1351
|
+
|
|
1352
|
+
Returns
|
|
1353
|
+
-------
|
|
1354
|
+
bool
|
|
1355
|
+
Whether the node satisfies the critera.
|
|
1356
|
+
"""
|
|
1357
|
+
return node.get("term", {}).get("@id") == _ABOVE_GROUND_CROP_RESIDUE_TOTAL_TERM_ID
|
|
1358
|
+
|
|
1359
|
+
|
|
1360
|
+
def _calc_carbon_source_ag_crop_residue(node: dict, cycle: dict) -> Union[CarbonSource, None]:
|
|
1361
|
+
"""
|
|
1362
|
+
Extract and format the carbon source data for above-ground crop residues.
|
|
1363
|
+
|
|
1364
|
+
n.b., We assume that even if a cycle's residue management states that 100% of above-ground crop residues are
|
|
1365
|
+
removed or burnt, a minimal amount of crop residues do remain on site to become a carbon source (see
|
|
1366
|
+
`_MIN_RESIDUE_LEFT_ON_FIELD` variable).
|
|
1367
|
+
|
|
1368
|
+
Parameters
|
|
1369
|
+
----------
|
|
1370
|
+
node : dict
|
|
1371
|
+
A Hestia [Product](https://www.hestia.earth/schema/Product) node with `term.termType` == `landCover`.
|
|
1372
|
+
|
|
1373
|
+
Returns
|
|
1374
|
+
-------
|
|
1375
|
+
CarbonSource | None
|
|
1376
|
+
The carbon source data of the above-ground residues, or `None` if carbon source data incomplete.
|
|
1377
|
+
"""
|
|
1378
|
+
value = get_node_value(node)
|
|
1379
|
+
residue_left_on_field = list_sum([
|
|
1380
|
+
get_node_value(practice) for practice in cycle.get("practices", [])
|
|
1381
|
+
if node_term_match(practice, _CROP_RESIDUE_MANAGEMENT_TERM_IDS)
|
|
1382
|
+
])
|
|
1383
|
+
mass = value * max(residue_left_on_field, _MIN_RESIDUE_LEFT_ON_FIELD) / 100
|
|
1384
|
+
|
|
1385
|
+
carbon_source = CarbonSource(
|
|
1386
|
+
mass,
|
|
1387
|
+
*_retrieve_carbon_source_properties(node)
|
|
1388
|
+
)
|
|
1389
|
+
|
|
1390
|
+
return carbon_source if _validate_carbon_source(carbon_source) else None
|
|
1391
|
+
|
|
1392
|
+
|
|
1393
|
+
def _should_run_carbon_source_cover_crop(node: dict) -> bool:
|
|
1394
|
+
"""
|
|
1395
|
+
Determine whether a product is a valid above cover crop carbon source.
|
|
1396
|
+
|
|
1397
|
+
Parameters
|
|
1398
|
+
----------
|
|
1399
|
+
node : dict
|
|
1400
|
+
A Hestia [Input](https://www.hestia.earth/schema/Input) or [Product](https://www.hestia.earth/schema/Product)
|
|
1401
|
+
node.
|
|
1402
|
+
|
|
1403
|
+
Returns
|
|
1404
|
+
-------
|
|
1405
|
+
bool
|
|
1406
|
+
Whether the node satisfies the critera.
|
|
1407
|
+
"""
|
|
1408
|
+
LOOKUP = _LOOKUPS["landCover"]
|
|
1409
|
+
return all([
|
|
1410
|
+
node.get("term", {}).get("termType") in [TermTermType.LANDCOVER.value],
|
|
1411
|
+
node_lookup_match(
|
|
1412
|
+
node, LOOKUP, IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE[IpccLandUseCategory.ANNUAL_CROPS]
|
|
1413
|
+
),
|
|
1414
|
+
any(
|
|
1415
|
+
get_node_property(node, term_id, False).get("value", False)
|
|
1416
|
+
for term_id in get_cover_crop_property_terms_with_cache()
|
|
1417
|
+
)
|
|
1326
1418
|
])
|
|
1327
1419
|
|
|
1328
1420
|
|
|
1329
|
-
def
|
|
1421
|
+
def _calc_carbon_source_cover_crop(node: dict, *_) -> Union[CarbonSource, None]:
|
|
1330
1422
|
"""
|
|
1331
|
-
|
|
1423
|
+
Extract and format the carbon source data for an annual cover crop.
|
|
1424
|
+
|
|
1425
|
+
n.b., We make the assumption that the entirety of the cover crop's biomass remains on site.
|
|
1332
1426
|
|
|
1333
1427
|
Parameters
|
|
1334
1428
|
----------
|
|
1335
1429
|
node : dict
|
|
1336
|
-
A Hestia
|
|
1337
|
-
or https://www.hestia.earth/schema/Input.
|
|
1430
|
+
A Hestia [Product](https://www.hestia.earth/schema/Product) node with `term.termType` == `landCover`.
|
|
1338
1431
|
|
|
1339
1432
|
Returns
|
|
1340
1433
|
-------
|
|
1341
1434
|
CarbonSource | None
|
|
1342
|
-
|
|
1435
|
+
The carbon source data of the cover crop, or `None` if carbon source data incomplete.
|
|
1436
|
+
"""
|
|
1437
|
+
value = get_node_value(node)
|
|
1438
|
+
carbon_source = CarbonSource(
|
|
1439
|
+
_DEFAULT_COVER_CROP_BIOMASS * value / 100,
|
|
1440
|
+
_Parameter.DEFAULT_CARBON_CONTENT.value.get("value"),
|
|
1441
|
+
_Parameter.DEFAULT_NITROGEN_CONTENT.value.get("value"),
|
|
1442
|
+
_Parameter.DEFAULT_NITROGEN_CONTENT.value.get("value")
|
|
1443
|
+
)
|
|
1444
|
+
return carbon_source
|
|
1445
|
+
|
|
1446
|
+
|
|
1447
|
+
def _should_run_carbon_source(node: dict) -> bool:
|
|
1448
|
+
"""
|
|
1449
|
+
Determine whether an input or product is a valid carbon source.
|
|
1450
|
+
|
|
1451
|
+
Parameters
|
|
1452
|
+
----------
|
|
1453
|
+
node : dict
|
|
1454
|
+
A Hestia [Input](https://www.hestia.earth/schema/Input) or [Product](https://www.hestia.earth/schema/Product)
|
|
1455
|
+
node.
|
|
1456
|
+
|
|
1457
|
+
Returns
|
|
1458
|
+
-------
|
|
1459
|
+
bool
|
|
1460
|
+
Whether the node satisfies the critera.
|
|
1461
|
+
"""
|
|
1462
|
+
return any([
|
|
1463
|
+
node.get("term", {}).get("@id") in _CARBON_SOURCE_TERM_IDS,
|
|
1464
|
+
node.get("term", {}).get("termType") in _CARBON_SOURCE_TERM_TYPES
|
|
1465
|
+
])
|
|
1466
|
+
|
|
1467
|
+
|
|
1468
|
+
def _calc_carbon_source(node: dict, *_) -> Union[CarbonSource, None]:
|
|
1469
|
+
"""
|
|
1470
|
+
Extract and format the carbon source data for an input or product.
|
|
1471
|
+
|
|
1472
|
+
Parameters
|
|
1473
|
+
----------
|
|
1474
|
+
node : dict
|
|
1475
|
+
A Hestia [Input](https://www.hestia.earth/schema/Input) or [Product](https://www.hestia.earth/schema/Product)
|
|
1476
|
+
node.
|
|
1477
|
+
|
|
1478
|
+
Returns
|
|
1479
|
+
-------
|
|
1480
|
+
CarbonSource | None
|
|
1481
|
+
The carbon source data of the cover crop, or `None` if carbon source data incomplete.
|
|
1482
|
+
"""
|
|
1483
|
+
carbon_source = CarbonSource(
|
|
1484
|
+
get_node_value(node),
|
|
1485
|
+
*_retrieve_carbon_source_properties(node)
|
|
1486
|
+
)
|
|
1487
|
+
|
|
1488
|
+
return carbon_source if _validate_carbon_source(carbon_source) else None
|
|
1489
|
+
|
|
1490
|
+
|
|
1491
|
+
def _retrieve_carbon_source_properties(node: dict) -> tuple[float, float, float]:
|
|
1492
|
+
"""
|
|
1493
|
+
Extract the carbon source properties from an input or product node or, if required, retrieve them from default
|
|
1494
|
+
properties.
|
|
1495
|
+
|
|
1496
|
+
Parameters
|
|
1497
|
+
----------
|
|
1498
|
+
node : dict
|
|
1499
|
+
A Hestia [Input](https://www.hestia.earth/schema/Input) or [Product](https://www.hestia.earth/schema/Product)
|
|
1500
|
+
node.
|
|
1501
|
+
|
|
1502
|
+
Returns
|
|
1503
|
+
-------
|
|
1504
|
+
tuple[float, float, float]
|
|
1505
|
+
`(carbon_content, nitrogen_content, lignin_content)`
|
|
1343
1506
|
"""
|
|
1344
|
-
mass = list_sum(node.get("value", []))
|
|
1345
1507
|
carbon_content, nitrogen_content, lignin_content = (
|
|
1346
1508
|
get_node_property(node, term_id).get("value", 0)/100 for term_id in _CARBON_INPUT_PROPERTY_TERM_IDS
|
|
1347
1509
|
)
|
|
1510
|
+
return carbon_content, nitrogen_content, lignin_content
|
|
1348
1511
|
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1512
|
+
|
|
1513
|
+
def _validate_carbon_source(carbon_source: CarbonSource) -> bool:
|
|
1514
|
+
"""
|
|
1515
|
+
Validate that a `CarbonSource` named tuple is data complete.
|
|
1516
|
+
"""
|
|
1517
|
+
return all([
|
|
1518
|
+
carbon_source.mass > 0,
|
|
1519
|
+
0 < carbon_source.carbon_content <= 1,
|
|
1520
|
+
0 < carbon_source.nitrogen_content <= 1,
|
|
1521
|
+
0 < carbon_source.lignin_content <= 1
|
|
1354
1522
|
])
|
|
1355
1523
|
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1524
|
+
|
|
1525
|
+
_CARBON_SOURCE_DECISION_TREE = {
|
|
1526
|
+
_should_run_carbon_source_ag_residue: _calc_carbon_source_ag_crop_residue,
|
|
1527
|
+
_should_run_carbon_source_cover_crop: _calc_carbon_source_cover_crop,
|
|
1528
|
+
_should_run_carbon_source: _calc_carbon_source,
|
|
1529
|
+
}
|
|
1361
1530
|
|
|
1362
1531
|
|
|
1363
1532
|
def _calc_total_organic_carbon_input(
|
|
@@ -10,8 +10,8 @@ from hestia_earth.models.utils.array_builders import (
|
|
|
10
10
|
)
|
|
11
11
|
from hestia_earth.models.utils.blank_node import cumulative_nodes_term_match
|
|
12
12
|
from hestia_earth.models.utils.term import (
|
|
13
|
-
get_cover_crop_property_terms,
|
|
14
|
-
|
|
13
|
+
get_cover_crop_property_terms, get_irrigated_terms, get_residue_removed_or_burnt_terms, get_upland_rice_crop_terms,
|
|
14
|
+
get_upland_rice_land_cover_terms
|
|
15
15
|
)
|
|
16
16
|
|
|
17
17
|
STATS_DEFINITION = MeasurementStatsDefinition.SIMULATED.value
|
|
@@ -27,10 +27,6 @@ def get_cover_crop_property_terms_with_cache():
|
|
|
27
27
|
return lru_cache()(get_cover_crop_property_terms)()
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
def get_crop_residue_inc_or_left_terms_with_cache():
|
|
31
|
-
return lru_cache()(get_crop_residue_incorporated_or_left_on_field_terms)()
|
|
32
|
-
|
|
33
|
-
|
|
34
30
|
def get_irrigated_terms_with_cache():
|
|
35
31
|
return lru_cache()(get_irrigated_terms)()
|
|
36
32
|
|