hestia-earth-models 0.59.6__py3-none-any.whl → 0.60.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/cache_sites.py +8 -8
- hestia_earth/models/cycle/siteDuration.py +1 -1
- hestia_earth/models/emepEea2019/nh3ToAirInorganicFertiliser.py +9 -6
- hestia_earth/models/faostat2018/product/price.py +14 -3
- hestia_earth/models/geospatialDatabase/utils.py +0 -1
- hestia_earth/models/ipcc2019/animal/__init__.py +0 -0
- hestia_earth/models/ipcc2019/animal/pastureGrass.py +298 -0
- hestia_earth/models/ipcc2019/co2ToAirLimeHydrolysis.py +1 -1
- hestia_earth/models/ipcc2019/{co2ToAirSoilCarbonStockChangeManagementChange.py → co2ToAirSoilOrganicCarbonStockChangeManagementChange.py} +2 -2
- hestia_earth/models/ipcc2019/co2ToAirUreaHydrolysis.py +2 -7
- hestia_earth/models/ipcc2019/organicCarbonPerHa.py +7 -2
- hestia_earth/models/ipcc2019/pastureGrass.py +73 -447
- hestia_earth/models/ipcc2019/pastureGrass_utils.py +415 -0
- hestia_earth/models/mocking/search-results.json +215 -207
- hestia_earth/models/site/pre_checks/cache_geospatialDatabase.py +14 -2
- hestia_earth/models/utils/completeness.py +17 -14
- hestia_earth/models/utils/feedipedia.py +23 -23
- hestia_earth/models/utils/property.py +4 -1
- hestia_earth/models/utils/site.py +7 -4
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.59.6.dist-info → hestia_earth_models-0.60.0.dist-info}/LICENSE +1 -1
- {hestia_earth_models-0.59.6.dist-info → hestia_earth_models-0.60.0.dist-info}/METADATA +1 -1
- {hestia_earth_models-0.59.6.dist-info → hestia_earth_models-0.60.0.dist-info}/RECORD +49 -44
- tests/models/cycle/animal/input/test_properties.py +3 -1
- tests/models/cycle/animal/test_properties.py +4 -2
- tests/models/cycle/input/test_properties.py +3 -1
- tests/models/cycle/product/test_properties.py +2 -1
- tests/models/cycle/test_coldCarcassWeightPerHead.py +1 -0
- tests/models/cycle/test_coldDressedCarcassWeightPerHead.py +1 -0
- tests/models/cycle/test_energyContentLowerHeatingValue.py +1 -0
- tests/models/cycle/test_feedConversionRatio.py +10 -0
- tests/models/cycle/test_readyToCookWeightPerHead.py +1 -0
- tests/models/faostat2018/product/test_price.py +15 -3
- tests/models/ipcc2006/test_n2OToAirCropResidueDecompositionDirect.py +4 -1
- tests/models/ipcc2019/animal/__init__.py +0 -0
- tests/models/ipcc2019/animal/test_pastureGrass.py +45 -0
- tests/models/ipcc2019/test_ch4ToAirEntericFermentation.py +32 -8
- tests/models/ipcc2019/{test_co2ToAirSoilCarbonStockChangeManagementChange.py → test_co2ToAirSoilOrganicCarbonStockChangeManagementChange.py} +1 -1
- tests/models/ipcc2019/test_n2OToAirCropResidueDecompositionDirect.py +6 -1
- tests/models/ipcc2019/test_n2OToAirInorganicFertiliserDirect.py +6 -1
- tests/models/ipcc2019/test_n2OToAirOrganicFertiliserDirect.py +6 -1
- tests/models/ipcc2019/test_organicCarbonPerHa.py +95 -40
- tests/models/ipcc2019/test_pastureGrass.py +32 -8
- tests/models/pooreNemecek2018/test_excretaKgN.py +5 -0
- tests/models/pooreNemecek2018/test_excretaKgVs.py +5 -0
- tests/models/pooreNemecek2018/test_no3ToGroundwaterSoilFlux.py +1 -0
- tests/models/test_cache_sites.py +22 -7
- {hestia_earth_models-0.59.6.dist-info → hestia_earth_models-0.60.0.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.59.6.dist-info → hestia_earth_models-0.60.0.dist-info}/top_level.txt +0 -0
|
@@ -34,7 +34,7 @@ def _cache_results(results: list, collections: list, index: int):
|
|
|
34
34
|
return cache_site_results(results[start:end], collections)
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
def _run_values(sites: list, param_type: ParamType, rasters: list = [], vectors: list = [], years: list =
|
|
37
|
+
def _run_values(sites: list, param_type: ParamType, rasters: list = [], vectors: list = [], years: list = None):
|
|
38
38
|
param_values = list(map(_VALUE_AS_PARAM.get(param_type), sites))
|
|
39
39
|
# unique list
|
|
40
40
|
param_values = list(set(param_values)) if param_type == ParamType.GADM_IDS else list({
|
|
@@ -66,11 +66,9 @@ def _run_values(sites: list, param_type: ParamType, rasters: list = [], vectors:
|
|
|
66
66
|
} | ({CACHE_AREA_SIZE: area_size} if area_size is not None else {})
|
|
67
67
|
return {
|
|
68
68
|
**site,
|
|
69
|
-
CACHE_KEY:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
CACHE_GEOSPATIAL_KEY: cached_data
|
|
73
|
-
}
|
|
69
|
+
CACHE_KEY: (
|
|
70
|
+
cached_value(site) | {CACHE_GEOSPATIAL_KEY: cached_data}
|
|
71
|
+
) | ({CACHE_YEARS_KEY: years} if years else {})
|
|
74
72
|
}
|
|
75
73
|
|
|
76
74
|
return reduce(lambda prev, curr: prev + [_process_site(curr)], sites, [])
|
|
@@ -114,7 +112,7 @@ def _group_sites(sites: dict):
|
|
|
114
112
|
}
|
|
115
113
|
|
|
116
114
|
|
|
117
|
-
def run(sites: list, years: list, include_region: bool = False):
|
|
115
|
+
def run(sites: list, years: list = None, include_region: bool = False, years_only: bool = False):
|
|
118
116
|
"""
|
|
119
117
|
Run all queries at once for the list of provided Sites.
|
|
120
118
|
Note: Earth Engine needs to be initiliased with `init_gee()` before running this function.
|
|
@@ -128,8 +126,10 @@ def run(sites: list, years: list, include_region: bool = False):
|
|
|
128
126
|
include_region : bool
|
|
129
127
|
Prefecth region IDs.
|
|
130
128
|
This will cache region-level data and will make the request slower. Only use if needed.
|
|
129
|
+
years_only : bool
|
|
130
|
+
Run only the collections that depend on the years (if provided).
|
|
131
131
|
"""
|
|
132
|
-
rasters, vectors = list_collections(years, include_region
|
|
132
|
+
rasters, vectors = list_collections(years, include_region, years_only)
|
|
133
133
|
|
|
134
134
|
filtered_data = _group_sites(sites)
|
|
135
135
|
|
|
@@ -17,14 +17,17 @@ from . import MODEL
|
|
|
17
17
|
|
|
18
18
|
REQUIREMENTS = {
|
|
19
19
|
"Cycle": {
|
|
20
|
-
"
|
|
21
|
-
|
|
20
|
+
"completeness.fertiliser": "True",
|
|
21
|
+
"inputs": [
|
|
22
|
+
{
|
|
22
23
|
"@type": "Input",
|
|
23
24
|
"value": "",
|
|
24
|
-
"term.termType":
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
"term.termType": "inorganicFertiliser",
|
|
26
|
+
"optional": {
|
|
27
|
+
"properties": [{"@type": "Property", "value": "", "term.@id": "nitrogenContent"}]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
],
|
|
28
31
|
"site": {
|
|
29
32
|
"@type": "Site",
|
|
30
33
|
"country": {"@type": "Term", "termType": "region"},
|
|
@@ -41,7 +41,8 @@ LOOKUPS = {
|
|
|
41
41
|
"liveAnimal": ["primaryMeatProductFaoPriceTermId"],
|
|
42
42
|
"animalProduct": ["animalProductGroupingFAOEquivalent", "animalProductGroupingFAO"],
|
|
43
43
|
"region-animalProduct-animalProductGroupingFAO-price": "use value from above",
|
|
44
|
-
"region-animalProduct-animalProductGroupingFAO-averageColdCarcassWeight": "use value from above"
|
|
44
|
+
"region-animalProduct-animalProductGroupingFAO-averageColdCarcassWeight": "use value from above",
|
|
45
|
+
"region-animalProduct-animalProductGroupingFAO-weightPerItem": "use value from above"
|
|
45
46
|
}
|
|
46
47
|
MODEL_KEY = 'price'
|
|
47
48
|
LOOKUP_NAME = {
|
|
@@ -52,6 +53,9 @@ LOOKUP_GROUPING = {
|
|
|
52
53
|
TermTermType.CROP.value: get_crop_grouping_faostat_production,
|
|
53
54
|
TermTermType.ANIMALPRODUCT.value: get_animalProduct_grouping_fao
|
|
54
55
|
}
|
|
56
|
+
LOOKUP_UNITS_NUMBER = {
|
|
57
|
+
TermTermType.ANIMALPRODUCT.value: f"region-{TermTermType.ANIMALPRODUCT.value}-{FAO_LOOKUP_COLUMN}-weightPerItem.csv"
|
|
58
|
+
}
|
|
55
59
|
|
|
56
60
|
|
|
57
61
|
def _term_grouping(term: dict): return LOOKUP_GROUPING.get(term.get('termType'), lambda *_: None)(MODEL, term)
|
|
@@ -123,6 +127,7 @@ def _run_by_country(cycle: dict, product: dict, country_id: str, year: int = Non
|
|
|
123
127
|
product_term = product.get('term', {})
|
|
124
128
|
term_id = product_term.get('@id')
|
|
125
129
|
term_type = product_term.get('termType')
|
|
130
|
+
term_units = product_term.get('units')
|
|
126
131
|
|
|
127
132
|
has_yield = len(product.get('value', [])) > 0
|
|
128
133
|
not_already_set = MODEL_KEY not in product.keys()
|
|
@@ -133,17 +138,23 @@ def _run_by_country(cycle: dict, product: dict, country_id: str, year: int = Non
|
|
|
133
138
|
should_run = all([not_already_set, has_yield, grouping])
|
|
134
139
|
value = _lookup_data(term_id, grouping, country_id, year, term_type=term_type) if should_run else None
|
|
135
140
|
|
|
141
|
+
# if units is number instead of kg, need to convert to number first
|
|
142
|
+
conversion_to_number = safe_parse_float(get_table_value(
|
|
143
|
+
download_lookup(LOOKUP_UNITS_NUMBER.get(term_type)), 'termid', country_id, column_name(grouping)
|
|
144
|
+
), 1) if term_units == Units.NUMBER.value else 1
|
|
145
|
+
|
|
136
146
|
logRequirements(cycle, model=MODEL, term=term_id, key=MODEL_KEY, by='country',
|
|
137
147
|
has_yield=has_yield,
|
|
138
148
|
not_already_set=not_already_set,
|
|
139
149
|
year=year,
|
|
140
150
|
price_per_ton=value,
|
|
141
|
-
groupingFAO=f"'{grouping}'"
|
|
151
|
+
groupingFAO=f"'{grouping}'",
|
|
152
|
+
conversion_to_number=conversion_to_number)
|
|
142
153
|
|
|
143
154
|
logShouldRun(cycle, MODEL, term_id, should_run, key=MODEL_KEY, by='country')
|
|
144
155
|
|
|
145
156
|
# divide by 1000 to convert price per tonne to kg
|
|
146
|
-
return _product(product, value / 1000) if value is not None else None
|
|
157
|
+
return _product(product, value / 1000 * conversion_to_number) if value is not None else None
|
|
147
158
|
|
|
148
159
|
|
|
149
160
|
def _should_run_product(product: dict):
|
|
File without changes
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Full Grass Consumption
|
|
3
|
+
|
|
4
|
+
This model estimates the energetic requirements of ruminants and can be used to estimate the amount of grass they graze.
|
|
5
|
+
Source:
|
|
6
|
+
[IPCC 2019, Vol.4, Chapter 10](https://www.ipcc-nggip.iges.or.jp/public/2019rf/pdf/4_Volume4/19R_V4_Ch10_Livestock.pdf).
|
|
7
|
+
"""
|
|
8
|
+
from hestia_earth.schema import TermTermType
|
|
9
|
+
from hestia_earth.utils.model import filter_list_term_type
|
|
10
|
+
from hestia_earth.utils.tools import list_sum
|
|
11
|
+
|
|
12
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, debugValues
|
|
13
|
+
from hestia_earth.models.utils.input import _new_input
|
|
14
|
+
from hestia_earth.models.utils.term import get_wool_terms, get_lookup_value
|
|
15
|
+
from hestia_earth.models.utils.completeness import _is_term_type_complete, _is_term_type_incomplete
|
|
16
|
+
from hestia_earth.models.utils.property import get_node_property
|
|
17
|
+
from .. import MODEL
|
|
18
|
+
from ..pastureGrass_utils import (
|
|
19
|
+
practice_input_id,
|
|
20
|
+
should_run_practice,
|
|
21
|
+
calculate_meanDE,
|
|
22
|
+
calculate_meanECHHV,
|
|
23
|
+
calculate_REM,
|
|
24
|
+
calculate_REG,
|
|
25
|
+
calculate_NEfeed,
|
|
26
|
+
product_wool_energy,
|
|
27
|
+
get_animals,
|
|
28
|
+
get_animal_values
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
REQUIREMENTS = {
|
|
32
|
+
"Cycle": {
|
|
33
|
+
"completeness.animalFeed": "True",
|
|
34
|
+
"completeness.animalPopulation": "True",
|
|
35
|
+
"completeness.freshForage": "False",
|
|
36
|
+
"site": {
|
|
37
|
+
"@type": "Site",
|
|
38
|
+
"siteType": "permanent pasture"
|
|
39
|
+
},
|
|
40
|
+
"practices": [{
|
|
41
|
+
"@type": "Practice",
|
|
42
|
+
"value": "",
|
|
43
|
+
"term.@id": "pastureGrass",
|
|
44
|
+
"key": {
|
|
45
|
+
"@type": "Term",
|
|
46
|
+
"term.termType": "landCover"
|
|
47
|
+
}
|
|
48
|
+
}],
|
|
49
|
+
"animals": [{
|
|
50
|
+
"@type": "Animal",
|
|
51
|
+
"value": "> 0",
|
|
52
|
+
"term.termType": "liveAnimal",
|
|
53
|
+
"referencePeriod": "average",
|
|
54
|
+
"properties": [{
|
|
55
|
+
"@type": "Property",
|
|
56
|
+
"value": "",
|
|
57
|
+
"term.@id": [
|
|
58
|
+
"liveweightPerHead",
|
|
59
|
+
"weightAtMaturity"
|
|
60
|
+
]
|
|
61
|
+
}],
|
|
62
|
+
"optional": {
|
|
63
|
+
"properties": [{
|
|
64
|
+
"@type": "Property",
|
|
65
|
+
"value": "",
|
|
66
|
+
"term.@id": [
|
|
67
|
+
"hoursWorkedPerDay",
|
|
68
|
+
"pregnancyRateTotal",
|
|
69
|
+
"animalsPerBirth"
|
|
70
|
+
]
|
|
71
|
+
}],
|
|
72
|
+
"inputs": [{
|
|
73
|
+
"@type": "Input",
|
|
74
|
+
"term.units": "kg",
|
|
75
|
+
"value": "> 0",
|
|
76
|
+
"optional": {
|
|
77
|
+
"properties": [{
|
|
78
|
+
"@type": "Property",
|
|
79
|
+
"value": "",
|
|
80
|
+
"term.@id": ["neutralDetergentFibreContent", "energyContentHigherHeatingValue"]
|
|
81
|
+
}]
|
|
82
|
+
}
|
|
83
|
+
}],
|
|
84
|
+
"practices": [{
|
|
85
|
+
"@type": "Practice",
|
|
86
|
+
"value": "",
|
|
87
|
+
"term.termType": "animalManagement",
|
|
88
|
+
"properties": [{
|
|
89
|
+
"@type": "Property",
|
|
90
|
+
"value": "",
|
|
91
|
+
"term.@id": "fatContent"
|
|
92
|
+
}]
|
|
93
|
+
}]
|
|
94
|
+
}
|
|
95
|
+
}],
|
|
96
|
+
"none": {
|
|
97
|
+
"inputs": [{
|
|
98
|
+
"@type": "Input",
|
|
99
|
+
"term.units": "kg",
|
|
100
|
+
"value": "> 0",
|
|
101
|
+
"isAnimalFeed": "True"
|
|
102
|
+
}]
|
|
103
|
+
},
|
|
104
|
+
"optional": {
|
|
105
|
+
"products": [{
|
|
106
|
+
"@type": "Product",
|
|
107
|
+
"value": "",
|
|
108
|
+
"term.@id": "animalProduct"
|
|
109
|
+
}]
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
LOOKUPS = {
|
|
114
|
+
"animalManagement": [
|
|
115
|
+
"mjKgEvMilkIpcc2019"
|
|
116
|
+
],
|
|
117
|
+
"animalProduct": ["mjKgEvWoolNetEnergyWoolIpcc2019", "allowedLiveAnimalTermIds"],
|
|
118
|
+
"liveAnimal": [
|
|
119
|
+
"ipcc2019AnimalTypeGrouping",
|
|
120
|
+
"mjDayKgCfiNetEnergyMaintenanceIpcc2019",
|
|
121
|
+
"ratioCPregnancyNetEnergyPregnancyIpcc2019",
|
|
122
|
+
"ratioCNetEnergyGrowthCattleBuffaloIpcc2019",
|
|
123
|
+
"mjKgABNetEnergyGrowthSheepGoatsIpcc2019",
|
|
124
|
+
"isWoolProducingAnimal"
|
|
125
|
+
],
|
|
126
|
+
"system-liveAnimal-activityCoefficient-ipcc2019": "using animal term @id",
|
|
127
|
+
"crop-property": ["energyDigestibilityRuminants", "energyContentHigherHeatingValue"],
|
|
128
|
+
"crop": "grazedPastureGrassInputId",
|
|
129
|
+
"forage-property": ["energyDigestibilityRuminants", "energyContentHigherHeatingValue"],
|
|
130
|
+
"landCover": "grazedPastureGrassInputId"
|
|
131
|
+
}
|
|
132
|
+
RETURNS = {
|
|
133
|
+
"Animal": [{
|
|
134
|
+
"Input": [{
|
|
135
|
+
"@type": "Input",
|
|
136
|
+
"term.termType": ["crop", "forage"],
|
|
137
|
+
"value": ""
|
|
138
|
+
}]
|
|
139
|
+
}]
|
|
140
|
+
}
|
|
141
|
+
MODEL_KEY = 'animal/pastureGrass'
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _input(term_id: str, value: float):
|
|
145
|
+
node = _new_input(term_id, MODEL)
|
|
146
|
+
node['value'] = [value]
|
|
147
|
+
return node
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _sum_liveWeightPerHead(animals: list):
|
|
151
|
+
return list_sum([
|
|
152
|
+
animal.get('value', 0) * get_node_property(animal, 'liveweightPerHead', False).get('value', 0)
|
|
153
|
+
for animal in animals
|
|
154
|
+
])
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _isNEwool_animal(animal: dict):
|
|
158
|
+
value = get_lookup_value(animal.get('term', {}), 'isWoolProducingAnimal', model=MODEL, model_key=MODEL_KEY)
|
|
159
|
+
return not (not value)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _is_NEwool_product(product: dict, animal: dict):
|
|
163
|
+
animal_term_ids = get_lookup_value(product, 'allowedLiveAnimalTermIds', model=MODEL, model_key=MODEL_KEY).split(';')
|
|
164
|
+
return animal.get('term', {}).get('@id') in animal_term_ids
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def calculate_NEwool(cycle: dict, animal: dict, products: list, total_weight: float) -> float:
|
|
168
|
+
term_id = animal.get('term', {}).get('@id')
|
|
169
|
+
|
|
170
|
+
wool_products = [p for p in products if _is_NEwool_product(p.get('term', {}), animal)]
|
|
171
|
+
total_energy = list_sum([
|
|
172
|
+
list_sum(product.get('value', [])) * product_wool_energy(product) for product in wool_products
|
|
173
|
+
])
|
|
174
|
+
animal_weight = _sum_liveWeightPerHead([animal])
|
|
175
|
+
|
|
176
|
+
debugValues(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
177
|
+
total_energy=total_energy,
|
|
178
|
+
animal_liveWeightPerHead=animal_weight,
|
|
179
|
+
total_liveWeightPerHead=total_weight)
|
|
180
|
+
|
|
181
|
+
return total_energy * animal_weight/total_weight
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _calculate_GE(
|
|
185
|
+
cycle: dict, animal: dict, REM: float, REG: float, NEwool: float, system: dict
|
|
186
|
+
) -> float:
|
|
187
|
+
term_id = animal.get('term', {}).get('@id')
|
|
188
|
+
|
|
189
|
+
NEm, NEa, NEl, NEwork, NEp, NEg = get_animal_values(cycle, animal, system)
|
|
190
|
+
|
|
191
|
+
NEm_feed, NEg_feed = calculate_NEfeed(animal)
|
|
192
|
+
debugValues(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
193
|
+
NEm_feed=NEm_feed,
|
|
194
|
+
NEg_feed=NEg_feed)
|
|
195
|
+
|
|
196
|
+
return (NEm + NEa + NEl + NEwork + NEp - NEm_feed)/REM + (NEg + NEwool - NEg_feed)/REG
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _run_practice(cycle: dict, GE: float, meanECHHV: float):
|
|
200
|
+
def run(practice: dict):
|
|
201
|
+
key = practice.get('key', {})
|
|
202
|
+
key_id = key.get('@id')
|
|
203
|
+
input_term_id = practice_input_id(practice)
|
|
204
|
+
value = (GE / meanECHHV) * (list_sum(practice.get('value', [0])) / 100)
|
|
205
|
+
|
|
206
|
+
logRequirements(cycle, model=MODEL, term=input_term_id, model_key=MODEL_KEY,
|
|
207
|
+
GE=GE,
|
|
208
|
+
meanECHHV=meanECHHV,
|
|
209
|
+
key_id=key_id)
|
|
210
|
+
|
|
211
|
+
logShouldRun(cycle, MODEL, input_term_id, True, model_key=MODEL_KEY)
|
|
212
|
+
|
|
213
|
+
return _input(input_term_id, value)
|
|
214
|
+
|
|
215
|
+
return run
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _run_animal(cycle: dict, meanDE: float, meanECHHV: float, system: dict, practices: list):
|
|
219
|
+
REM = calculate_REM(meanDE)
|
|
220
|
+
REG = calculate_REG(meanDE)
|
|
221
|
+
|
|
222
|
+
wool_term_ids = get_wool_terms()
|
|
223
|
+
# list of animal product
|
|
224
|
+
wool_products = [p for p in cycle.get('products', []) if p.get('term', {}).get('@id') in wool_term_ids]
|
|
225
|
+
animals = list(filter(_isNEwool_animal, cycle.get('animals', [])))
|
|
226
|
+
total_liveWeightPerHead = _sum_liveWeightPerHead(animals)
|
|
227
|
+
|
|
228
|
+
def run(animal: dict):
|
|
229
|
+
term_id = animal.get('term', {}).get('@id')
|
|
230
|
+
|
|
231
|
+
NEwool = calculate_NEwool(cycle, animal, wool_products, total_liveWeightPerHead) if (
|
|
232
|
+
total_liveWeightPerHead > 0
|
|
233
|
+
) else 0
|
|
234
|
+
GE = (_calculate_GE(cycle, animal, REM, REG, NEwool, system) / (meanDE/100)) if all([REM, REG]) else 0
|
|
235
|
+
|
|
236
|
+
debugValues(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
237
|
+
REM=REM,
|
|
238
|
+
REG=REG,
|
|
239
|
+
NEwool=NEwool,
|
|
240
|
+
GE=GE,
|
|
241
|
+
meanDE=meanDE)
|
|
242
|
+
|
|
243
|
+
inputs = list(map(_run_practice(cycle, GE, meanECHHV), practices))
|
|
244
|
+
return animal | {
|
|
245
|
+
'inputs': animal.get('inputs', []) + inputs
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return run
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def _run(cycle: dict, meanDE: float, meanECHHV: float, system: dict, practices: list):
|
|
252
|
+
animals = get_animals(cycle)
|
|
253
|
+
return list(map(_run_animal(cycle, meanDE, meanECHHV, system, practices), animals))
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def _should_run(cycle: dict, practices: dict):
|
|
257
|
+
systems = filter_list_term_type(cycle.get('practices', []), TermTermType.SYSTEM)
|
|
258
|
+
animalFeed_complete = _is_term_type_complete(cycle, 'animalFeed')
|
|
259
|
+
animalPopulation_complete = _is_term_type_complete(cycle, 'animalPopulation')
|
|
260
|
+
freshForage_incomplete = _is_term_type_incomplete(cycle, 'freshForage')
|
|
261
|
+
all_animals_have_value = all([a.get('value', 0) > 0 for a in cycle.get('animals', [])])
|
|
262
|
+
|
|
263
|
+
no_cycle_inputs_feed = all([not input.get('isAnimalFeed', False) for input in cycle.get('inputs', [])])
|
|
264
|
+
|
|
265
|
+
meanDE = calculate_meanDE(practices)
|
|
266
|
+
meanECHHV = calculate_meanECHHV(practices)
|
|
267
|
+
|
|
268
|
+
should_run = all([
|
|
269
|
+
animalFeed_complete,
|
|
270
|
+
animalPopulation_complete,
|
|
271
|
+
freshForage_incomplete,
|
|
272
|
+
no_cycle_inputs_feed,
|
|
273
|
+
all_animals_have_value,
|
|
274
|
+
len(systems) > 0,
|
|
275
|
+
len(practices) > 0,
|
|
276
|
+
meanDE > 0,
|
|
277
|
+
meanECHHV > 0
|
|
278
|
+
])
|
|
279
|
+
|
|
280
|
+
for term_id in [MODEL_KEY] + [practice_input_id(p) for p in practices]:
|
|
281
|
+
logRequirements(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
282
|
+
term_type_animalFeed_complete=animalFeed_complete,
|
|
283
|
+
term_type_animalPopulation_complete=animalPopulation_complete,
|
|
284
|
+
term_type_freshForage_incomplete=freshForage_incomplete,
|
|
285
|
+
no_cycle_inputs_feed=no_cycle_inputs_feed,
|
|
286
|
+
all_animals_have_value=all_animals_have_value,
|
|
287
|
+
meanDE=meanDE,
|
|
288
|
+
meanECHHV=meanECHHV)
|
|
289
|
+
|
|
290
|
+
logShouldRun(cycle, MODEL, term_id, should_run, model_key=MODEL_KEY)
|
|
291
|
+
|
|
292
|
+
return should_run, meanDE, meanECHHV, systems[0] if systems else None
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def run(cycle: dict):
|
|
296
|
+
practices = list(filter(should_run_practice(cycle), cycle.get('practices', [])))
|
|
297
|
+
should_run, meanDE, meanECHHV, system = _should_run(cycle, practices)
|
|
298
|
+
return _run(cycle, meanDE, meanECHHV, system, practices) if should_run else []
|
|
@@ -50,7 +50,7 @@ RETURNS = {
|
|
|
50
50
|
"depth": "30"
|
|
51
51
|
}]
|
|
52
52
|
}
|
|
53
|
-
TERM_ID = '
|
|
53
|
+
TERM_ID = 'co2ToAirSoilOrganicCarbonStockChangeManagementChange'
|
|
54
54
|
|
|
55
55
|
DEPTH_UPPER = 0
|
|
56
56
|
DEPTH_LOWER = 30
|
|
@@ -101,7 +101,7 @@ MEASUREMENT_METHOD_RANKING = [
|
|
|
101
101
|
]
|
|
102
102
|
"""
|
|
103
103
|
A ranking of `MeasurementMethodClassification`s from weakest to strongest used to determine the `EmissionMethodTier` of
|
|
104
|
-
the `
|
|
104
|
+
the `co2ToAirSoilOrganicCarbonStockChangeManagementChange` output.
|
|
105
105
|
|
|
106
106
|
The `EmissionMethodTier` should be based on the weakest `MeasurementMethodClassification` between the current SOC and
|
|
107
107
|
previous SOC.
|
|
@@ -11,13 +11,8 @@ from . import MODEL
|
|
|
11
11
|
|
|
12
12
|
REQUIREMENTS = {
|
|
13
13
|
"Cycle": {
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
"inputs": [{"@type": "Input", "value": "", "term.termType": "inorganicFertiliser"}]
|
|
17
|
-
},
|
|
18
|
-
"optional": {
|
|
19
|
-
"inputs": [{"@type": "Input", "value": "", "term.@id": "inorganicNitrogenFertiliserUnspecifiedKgN"}]
|
|
20
|
-
}
|
|
14
|
+
"completeness.fertiliser": "",
|
|
15
|
+
"inputs": [{"@type": "Input", "value": "", "term.termType": "inorganicFertiliser"}]
|
|
21
16
|
}
|
|
22
17
|
}
|
|
23
18
|
RETURNS = {
|
|
@@ -259,6 +259,11 @@ The number of years required for soil organic carbon to reach equilibrium after
|
|
|
259
259
|
a change in land use, management regime or carbon input regime.
|
|
260
260
|
"""
|
|
261
261
|
|
|
262
|
+
EXCLUDED_ECO_CLIMATE_ZONES_TIER_1 = {
|
|
263
|
+
5, # Polar Moist
|
|
264
|
+
6 # Polar Dry
|
|
265
|
+
}
|
|
266
|
+
|
|
262
267
|
VALID_SITE_TYPES_TIER_1 = [
|
|
263
268
|
SiteSiteType.CROPLAND.value,
|
|
264
269
|
SiteSiteType.FOREST.value,
|
|
@@ -287,7 +292,7 @@ def _measurement(year: int, value: float, method_classification: str) -> dict:
|
|
|
287
292
|
dict
|
|
288
293
|
A valid Hestia `Measurement` node, see: https://www.hestia.earth/schema/Measurement.
|
|
289
294
|
"""
|
|
290
|
-
measurement = _new_measurement(TERM_ID
|
|
295
|
+
measurement = _new_measurement(TERM_ID)
|
|
291
296
|
measurement["value"] = [value]
|
|
292
297
|
measurement["dates"] = [f"{year}-12-31"]
|
|
293
298
|
measurement["depthUpper"] = DEPTH_UPPER
|
|
@@ -3578,7 +3583,7 @@ def _should_run_tier_1(
|
|
|
3578
3583
|
Determines whether there is sufficient data in the inventory and keyword args to run the tier 1 model.
|
|
3579
3584
|
"""
|
|
3580
3585
|
return all([
|
|
3581
|
-
eco_climate_zone and eco_climate_zone
|
|
3586
|
+
eco_climate_zone and eco_climate_zone not in EXCLUDED_ECO_CLIMATE_ZONES_TIER_1,
|
|
3582
3587
|
soc_ref and soc_ref > 0,
|
|
3583
3588
|
any(year for year, group in inventory.items() if group.get(_InventoryKey.SHOULD_RUN_TIER_1))
|
|
3584
3589
|
])
|