hestia-earth-models 0.61.7__py3-none-any.whl → 0.62.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/completeness/electricityFuel.py +60 -0
- hestia_earth/models/cycle/product/economicValueShare.py +47 -31
- hestia_earth/models/emepEea2019/nh3ToAirInorganicFertiliser.py +44 -59
- hestia_earth/models/geospatialDatabase/histosol.py +4 -0
- hestia_earth/models/ipcc2006/co2ToAirOrganicSoilCultivation.py +4 -2
- hestia_earth/models/ipcc2006/n2OToAirOrganicSoilCultivationDirect.py +1 -1
- hestia_earth/models/ipcc2019/aboveGroundCropResidueTotal.py +1 -1
- hestia_earth/models/ipcc2019/animal/pastureGrass.py +30 -24
- hestia_earth/models/ipcc2019/belowGroundCropResidue.py +1 -1
- hestia_earth/models/ipcc2019/ch4ToAirExcreta.py +1 -1
- hestia_earth/models/ipcc2019/co2ToAirSoilOrganicCarbonStockChangeManagementChange.py +511 -458
- hestia_earth/models/ipcc2019/co2ToAirUreaHydrolysis.py +5 -1
- hestia_earth/models/ipcc2019/organicCarbonPerHa.py +116 -3882
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_1_utils.py +2060 -0
- hestia_earth/models/ipcc2019/organicCarbonPerHa_tier_2_utils.py +1630 -0
- hestia_earth/models/ipcc2019/organicCarbonPerHa_utils.py +324 -0
- hestia_earth/models/ipcc2019/pastureGrass.py +37 -19
- hestia_earth/models/ipcc2019/pastureGrass_utils.py +4 -21
- hestia_earth/models/mocking/search-results.json +293 -289
- hestia_earth/models/site/organicCarbonPerHa.py +58 -44
- hestia_earth/models/site/soilMeasurement.py +18 -13
- hestia_earth/models/utils/__init__.py +28 -0
- hestia_earth/models/utils/array_builders.py +578 -0
- hestia_earth/models/utils/blank_node.py +55 -39
- hestia_earth/models/utils/descriptive_stats.py +285 -0
- hestia_earth/models/utils/emission.py +73 -2
- hestia_earth/models/utils/inorganicFertiliser.py +2 -2
- hestia_earth/models/utils/measurement.py +118 -4
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.61.7.dist-info → hestia_earth_models-0.62.0.dist-info}/METADATA +2 -2
- {hestia_earth_models-0.61.7.dist-info → hestia_earth_models-0.62.0.dist-info}/RECORD +51 -39
- tests/models/cycle/completeness/test_electricityFuel.py +21 -0
- tests/models/cycle/product/test_economicValueShare.py +8 -0
- tests/models/emepEea2019/test_nh3ToAirInorganicFertiliser.py +2 -2
- tests/models/ipcc2019/animal/test_pastureGrass.py +2 -2
- tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChangeManagementChange.py +55 -165
- tests/models/ipcc2019/test_organicCarbonPerHa.py +219 -460
- tests/models/ipcc2019/test_organicCarbonPerHa_tier_1_utils.py +471 -0
- tests/models/ipcc2019/test_organicCarbonPerHa_tier_2_utils.py +208 -0
- tests/models/ipcc2019/test_organicCarbonPerHa_utils.py +75 -0
- tests/models/ipcc2019/test_pastureGrass.py +0 -16
- tests/models/site/test_organicCarbonPerHa.py +3 -12
- tests/models/site/test_soilMeasurement.py +3 -18
- tests/models/utils/test_array_builders.py +253 -0
- tests/models/utils/test_blank_node.py +154 -15
- tests/models/utils/test_descriptive_stats.py +134 -0
- tests/models/utils/test_emission.py +51 -1
- tests/models/utils/test_measurement.py +54 -2
- {hestia_earth_models-0.61.7.dist-info → hestia_earth_models-0.62.0.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.61.7.dist-info → hestia_earth_models-0.62.0.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.61.7.dist-info → hestia_earth_models-0.62.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from functools import lru_cache
|
|
3
|
+
from numpy.typing import NDArray
|
|
4
|
+
from typing import NamedTuple, Optional
|
|
5
|
+
|
|
6
|
+
from hestia_earth.schema import MeasurementStatsDefinition, SiteSiteType
|
|
7
|
+
|
|
8
|
+
from hestia_earth.models.utils.array_builders import (
|
|
9
|
+
repeat_single, plus_minus_uncertainty_to_normal_1d, truncated_normal_1d
|
|
10
|
+
)
|
|
11
|
+
from hestia_earth.models.utils.blank_node import cumulative_nodes_term_match
|
|
12
|
+
from hestia_earth.models.utils.term import (
|
|
13
|
+
get_cover_crop_property_terms, get_crop_residue_incorporated_or_left_on_field_terms, get_irrigated_terms,
|
|
14
|
+
get_residue_removed_or_burnt_terms, get_upland_rice_crop_terms, get_upland_rice_land_cover_terms
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
STATS_DEFINITION = MeasurementStatsDefinition.SIMULATED.value
|
|
18
|
+
DEPTH_UPPER = 0
|
|
19
|
+
DEPTH_LOWER = 30
|
|
20
|
+
|
|
21
|
+
MIN_AREA_THRESHOLD = 30 # 30% as per IPCC guidelines
|
|
22
|
+
SUPER_MAJORITY_AREA_THRESHOLD = 100 - MIN_AREA_THRESHOLD
|
|
23
|
+
MIN_YIELD_THRESHOLD = 1
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_cover_crop_property_terms_with_cache():
|
|
27
|
+
return lru_cache()(get_cover_crop_property_terms)()
|
|
28
|
+
|
|
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
|
+
def get_irrigated_terms_with_cache():
|
|
35
|
+
return lru_cache()(get_irrigated_terms)()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_residue_removed_or_burnt_terms_with_cache():
|
|
39
|
+
return lru_cache()(get_residue_removed_or_burnt_terms)()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def get_upland_rice_crop_terms_with_cache():
|
|
43
|
+
return lru_cache()(get_upland_rice_crop_terms)()
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def get_upland_rice_land_cover_terms_with_cache():
|
|
47
|
+
return lru_cache()(get_upland_rice_land_cover_terms)()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class EcoClimateZone(Enum):
|
|
51
|
+
"""
|
|
52
|
+
Enum representing eco-climate zones. The value of each member of the Enum correctly corresponds with the values of
|
|
53
|
+
`ecoClimateZone` term and the `ecoClimateZone-lookup.csv`.
|
|
54
|
+
"""
|
|
55
|
+
WARM_TEMPERATE_MOIST = 1
|
|
56
|
+
WARM_TEMPERATE_DRY = 2
|
|
57
|
+
COOL_TEMPERATE_MOIST = 3
|
|
58
|
+
COOL_TEMPERATE_DRY = 4
|
|
59
|
+
POLAR_MOIST = 5
|
|
60
|
+
POLAR_DRY = 6
|
|
61
|
+
BOREAL_MOIST = 7
|
|
62
|
+
BOREAL_DRY = 8
|
|
63
|
+
TROPICAL_MONTANE = 9
|
|
64
|
+
TROPICAL_WET = 10
|
|
65
|
+
TROPICAL_MOIST = 11
|
|
66
|
+
TROPICAL_DRY = 12
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class IpccSoilCategory(Enum):
|
|
70
|
+
"""
|
|
71
|
+
Enum representing IPCC Soil Categories.
|
|
72
|
+
|
|
73
|
+
See [IPCC (2019) Vol 4, Ch. 2 and 3](https://www.ipcc-nggip.iges.or.jp/public/2019rf/vol4.html) for more
|
|
74
|
+
information.
|
|
75
|
+
"""
|
|
76
|
+
ORGANIC_SOILS = "organic soils"
|
|
77
|
+
SANDY_SOILS = "sandy soils"
|
|
78
|
+
WETLAND_SOILS = "wetland soils"
|
|
79
|
+
VOLCANIC_SOILS = "volcanic soils"
|
|
80
|
+
SPODIC_SOILS = "spodic soils"
|
|
81
|
+
HIGH_ACTIVITY_CLAY_SOILS = "high-activity clay soils"
|
|
82
|
+
LOW_ACTIVITY_CLAY_SOILS = "low-activity clay soils"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
IPCC_SOIL_CATEGORY_TO_SOIL_TYPE_LOOKUP_VALUE = {
|
|
86
|
+
IpccSoilCategory.ORGANIC_SOILS: "Organic soils",
|
|
87
|
+
IpccSoilCategory.SANDY_SOILS: "Sandy soils",
|
|
88
|
+
IpccSoilCategory.WETLAND_SOILS: "Wetland soils",
|
|
89
|
+
IpccSoilCategory.VOLCANIC_SOILS: "Volcanic soils",
|
|
90
|
+
IpccSoilCategory.SPODIC_SOILS: "Spodic soils",
|
|
91
|
+
IpccSoilCategory.HIGH_ACTIVITY_CLAY_SOILS: "High-activity clay soils",
|
|
92
|
+
IpccSoilCategory.LOW_ACTIVITY_CLAY_SOILS: "Low-activity clay soils",
|
|
93
|
+
}
|
|
94
|
+
"""
|
|
95
|
+
A dictionary mapping IPCC soil categories to corresponding soil type and USDA soil type lookup values in the
|
|
96
|
+
`"IPCC_SOIL_CATEGORY"` column.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class IpccLandUseCategory(Enum):
|
|
101
|
+
"""
|
|
102
|
+
Enum representing IPCC Land Use Categories.
|
|
103
|
+
|
|
104
|
+
See [IPCC (2019) Vol 4](https://www.ipcc-nggip.iges.or.jp/public/2019rf/vol4.html) for more information.
|
|
105
|
+
"""
|
|
106
|
+
GRASSLAND = "grassland"
|
|
107
|
+
PERENNIAL_CROPS = "perennial crops"
|
|
108
|
+
PADDY_RICE_CULTIVATION = "paddy rice cultivation"
|
|
109
|
+
ANNUAL_CROPS_WET = "annual crops (wet)"
|
|
110
|
+
ANNUAL_CROPS = "annual crops"
|
|
111
|
+
SET_ASIDE = "set aside"
|
|
112
|
+
FOREST = "forest"
|
|
113
|
+
NATIVE = "native"
|
|
114
|
+
OTHER = "other"
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
SITE_TYPE_TO_IPCC_LAND_USE_CATEGORY = {
|
|
118
|
+
SiteSiteType.PERMANENT_PASTURE.value: IpccLandUseCategory.GRASSLAND,
|
|
119
|
+
SiteSiteType.FOREST.value: IpccLandUseCategory.FOREST,
|
|
120
|
+
SiteSiteType.OTHER_NATURAL_VEGETATION.value: IpccLandUseCategory.NATIVE
|
|
121
|
+
}
|
|
122
|
+
"""
|
|
123
|
+
A dictionary mapping site types to corresponding IPCC land use categories.
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
IPCC_LAND_USE_CATEGORY_TO_LAND_COVER_LOOKUP_VALUE = {
|
|
127
|
+
IpccLandUseCategory.GRASSLAND: "Grassland",
|
|
128
|
+
IpccLandUseCategory.PERENNIAL_CROPS: "Perennial crops",
|
|
129
|
+
IpccLandUseCategory.PADDY_RICE_CULTIVATION: "Paddy rice cultivation",
|
|
130
|
+
IpccLandUseCategory.ANNUAL_CROPS_WET: "Annual crops",
|
|
131
|
+
IpccLandUseCategory.ANNUAL_CROPS: "Annual crops",
|
|
132
|
+
IpccLandUseCategory.SET_ASIDE: [
|
|
133
|
+
"Annual crops", "Paddy rice cultivation", "Perennial crops", "Set aside"
|
|
134
|
+
],
|
|
135
|
+
IpccLandUseCategory.FOREST: "Forest",
|
|
136
|
+
IpccLandUseCategory.NATIVE: "Native"
|
|
137
|
+
}
|
|
138
|
+
"""
|
|
139
|
+
A dictionary mapping IPCC land use categories to corresponding land cover lookup values in the
|
|
140
|
+
`"IPCC_LAND_USE_CATEGORY"` column.
|
|
141
|
+
"""
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class IpccManagementCategory(Enum):
|
|
145
|
+
"""
|
|
146
|
+
Enum representing IPCC Management Categories for grasslands and annual croplands.
|
|
147
|
+
|
|
148
|
+
See [IPCC (2019) Vol. 4, Ch. 5 and 6](https://www.ipcc-nggip.iges.or.jp/public/2019rf/vol4.html) for more
|
|
149
|
+
information.
|
|
150
|
+
"""
|
|
151
|
+
SEVERELY_DEGRADED = "severely degraded"
|
|
152
|
+
IMPROVED_GRASSLAND = "improved grassland"
|
|
153
|
+
HIGH_INTENSITY_GRAZING = "high-intensity grazing"
|
|
154
|
+
NOMINALLY_MANAGED = "nominally managed"
|
|
155
|
+
FULL_TILLAGE = "full tillage"
|
|
156
|
+
REDUCED_TILLAGE = "reduced tillage"
|
|
157
|
+
NO_TILLAGE = "no tillage"
|
|
158
|
+
OTHER = "other"
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
IPCC_MANAGEMENT_CATEGORY_TO_GRASSLAND_MANAGEMENT_TERM_ID = {
|
|
162
|
+
IpccManagementCategory.SEVERELY_DEGRADED: "severelyDegradedPasture",
|
|
163
|
+
IpccManagementCategory.IMPROVED_GRASSLAND: "improvedPasture",
|
|
164
|
+
IpccManagementCategory.HIGH_INTENSITY_GRAZING: "highIntensityGrazingPasture",
|
|
165
|
+
IpccManagementCategory.NOMINALLY_MANAGED: "nominallyManagedPasture",
|
|
166
|
+
IpccManagementCategory.OTHER: "nativePasture"
|
|
167
|
+
}
|
|
168
|
+
"""
|
|
169
|
+
A dictionary mapping IPCC management categories to corresponding grassland management term IDs from the land cover
|
|
170
|
+
glossary.
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
IPCC_MANAGEMENT_CATEGORY_TO_TILLAGE_MANAGEMENT_LOOKUP_VALUE = {
|
|
175
|
+
IpccManagementCategory.FULL_TILLAGE: "Full tillage",
|
|
176
|
+
IpccManagementCategory.REDUCED_TILLAGE: "Reduced tillage",
|
|
177
|
+
IpccManagementCategory.NO_TILLAGE: "No tillage"
|
|
178
|
+
}
|
|
179
|
+
"""
|
|
180
|
+
A dictionary mapping IPCC management categories to corresponding tillage lookup values in the
|
|
181
|
+
`"IPCC_TILLAGE_MANAGEMENT_CATEGORY" column`.
|
|
182
|
+
"""
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class IpccCarbonInputCategory(Enum):
|
|
186
|
+
"""
|
|
187
|
+
Enum representing IPCC Carbon Input Categories for improved grasslands and annual croplands.
|
|
188
|
+
|
|
189
|
+
See [IPCC (2019) Vol. 4, Ch. 4, 5 and 6](https://www.ipcc-nggip.iges.or.jp/public/2019rf/vol4.html) for more
|
|
190
|
+
information.
|
|
191
|
+
"""
|
|
192
|
+
GRASSLAND_HIGH = "grassland high"
|
|
193
|
+
GRASSLAND_MEDIUM = "grassland medium"
|
|
194
|
+
CROPLAND_HIGH_WITH_MANURE = "cropland high (with manure)"
|
|
195
|
+
CROPLAND_HIGH_WITHOUT_MANURE = "cropland high (without manure)"
|
|
196
|
+
CROPLAND_MEDIUM = "cropland medium"
|
|
197
|
+
CROPLAND_LOW = "cropland low"
|
|
198
|
+
OTHER = "other"
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
CarbonSource = NamedTuple(
|
|
202
|
+
"CarbonSource",
|
|
203
|
+
[
|
|
204
|
+
("mass", float),
|
|
205
|
+
("carbon_content", float),
|
|
206
|
+
("nitrogen_content", float),
|
|
207
|
+
("lignin_content", float),
|
|
208
|
+
]
|
|
209
|
+
)
|
|
210
|
+
"""
|
|
211
|
+
A single carbon source (e.g. crop residues or organic amendment).
|
|
212
|
+
|
|
213
|
+
Attributes
|
|
214
|
+
-----------
|
|
215
|
+
mass : float
|
|
216
|
+
The dry-matter mass of the carbon source, kg ha-1
|
|
217
|
+
carbon_content : float
|
|
218
|
+
The carbon content of the carbon source, decimal proportion, kg C (kg d.m.)-1.
|
|
219
|
+
nitrogen_content : float
|
|
220
|
+
The nitrogen content of the carbon source, decimal_proportion, kg N (kg d.m.)-1.
|
|
221
|
+
lignin_content : float
|
|
222
|
+
The lignin content of the carbon source, decimal_proportion, kg lignin (kg d.m.)-1.
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def check_consecutive(ints: list[int]) -> bool:
|
|
227
|
+
"""
|
|
228
|
+
Checks whether a list of integers are consecutive.
|
|
229
|
+
|
|
230
|
+
Used to determine whether annualised data is complete from every year from beggining to end.
|
|
231
|
+
|
|
232
|
+
Parameters
|
|
233
|
+
----------
|
|
234
|
+
ints : list[int]
|
|
235
|
+
A list of integer values.
|
|
236
|
+
|
|
237
|
+
Returns
|
|
238
|
+
-------
|
|
239
|
+
bool
|
|
240
|
+
Whether or not the list of integers is consecutive.
|
|
241
|
+
"""
|
|
242
|
+
range_list = list(range(min(ints), max(ints)+1)) if ints else []
|
|
243
|
+
return all(a == b for a, b in zip(ints, range_list))
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def check_irrigation(water_regime_nodes: list[dict]) -> bool:
|
|
247
|
+
"""
|
|
248
|
+
Check if irrigation is present in the water regime nodes.
|
|
249
|
+
|
|
250
|
+
Parameters
|
|
251
|
+
----------
|
|
252
|
+
water_regime_nodes : list[dict]
|
|
253
|
+
List of water regime nodes to be checked.
|
|
254
|
+
|
|
255
|
+
Returns
|
|
256
|
+
-------
|
|
257
|
+
bool
|
|
258
|
+
`True` if irrigation is present, `False` otherwise.
|
|
259
|
+
"""
|
|
260
|
+
return cumulative_nodes_term_match(
|
|
261
|
+
water_regime_nodes,
|
|
262
|
+
target_term_ids=get_irrigated_terms_with_cache(),
|
|
263
|
+
cumulative_threshold=MIN_AREA_THRESHOLD
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def sample_truncated_normal(
|
|
268
|
+
*, iterations: int, value: float, sd: float, min: float, max: float, seed: Optional[int] = None, **_
|
|
269
|
+
) -> NDArray:
|
|
270
|
+
"""Randomly sample a model parameter with a truncated normal distribution."""
|
|
271
|
+
return truncated_normal_1d(shape=(1, iterations), mu=value, sigma=sd, low=min, high=max, seed=seed)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def sample_plus_minus_uncertainty(
|
|
275
|
+
*, iterations: int, value: float, uncertainty: float, seed: Optional[int] = None, **_
|
|
276
|
+
) -> NDArray:
|
|
277
|
+
"""Randomly sample a model parameter with a plus/minus uncertainty distribution."""
|
|
278
|
+
return plus_minus_uncertainty_to_normal_1d(shape=(1, iterations), value=value, uncertainty=uncertainty, seed=seed)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def sample_plus_minus_error(
|
|
282
|
+
*, iterations: int, value: float, error: float, seed: Optional[int] = None, **_
|
|
283
|
+
) -> NDArray:
|
|
284
|
+
"""Randomly sample a model parameter with a truncated normal distribution described using plus/minus error."""
|
|
285
|
+
sd = value * (error / 200)
|
|
286
|
+
low = value - (value * (error / 100))
|
|
287
|
+
high = value + (value * (error / 100))
|
|
288
|
+
return truncated_normal_1d(shape=(1, iterations), mu=value, sigma=sd, low=low, high=high, seed=seed)
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def sample_constant(*, iterations: int, value: float, **_) -> NDArray:
|
|
292
|
+
"""Sample a constant model parameter."""
|
|
293
|
+
return repeat_single(shape=(1, iterations), value=value)
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def format_bool(value: Optional[bool]) -> str:
|
|
297
|
+
"""Format a bool for logging in a table."""
|
|
298
|
+
return str(bool(value))
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def format_number(value: Optional[float]) -> str:
|
|
302
|
+
"""Format a float for logging in a table."""
|
|
303
|
+
return f"{value:.1f}" if isinstance(value, (float, int)) else "None"
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def format_enum(value: Optional[Enum]) -> str:
|
|
307
|
+
"""Format an enum for logging in a table."""
|
|
308
|
+
return value.value if isinstance(value, Enum) else "None"
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def format_bool_list(values: Optional[list[bool]]) -> str:
|
|
312
|
+
"""Format a list of bools for logging in a table."""
|
|
313
|
+
return (
|
|
314
|
+
" ".join(format_bool(value) for value in values) or "None" if isinstance(values, list)
|
|
315
|
+
else "None"
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def format_number_list(values: Optional[list[float]]) -> str:
|
|
320
|
+
"""Format a list of floats for logging in a table."""
|
|
321
|
+
return (
|
|
322
|
+
" ".join(format_number(value) for value in values) or "None"if isinstance(values, list)
|
|
323
|
+
else "None"
|
|
324
|
+
)
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Cycle Pasture Grass
|
|
3
3
|
|
|
4
4
|
This model estimates the energetic requirements of ruminants and can be used to estimate the amount of grass they graze.
|
|
5
5
|
Source:
|
|
6
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
|
+
This version of the model will run at the Cycle level, if at least one Cycle Input is given as feed
|
|
9
|
+
(see https://www.hestia.earth/schema/Input#isAnimalFeed).
|
|
7
10
|
"""
|
|
8
11
|
from hestia_earth.schema import TermTermType
|
|
9
12
|
from hestia_earth.utils.model import filter_list_term_type
|
|
10
13
|
from hestia_earth.utils.tools import list_sum
|
|
11
14
|
|
|
12
|
-
from hestia_earth.models.log import logRequirements, logShouldRun
|
|
15
|
+
from hestia_earth.models.log import logRequirements, logShouldRun, debugValues
|
|
13
16
|
from hestia_earth.models.utils.input import _new_input
|
|
14
17
|
from hestia_earth.models.utils.term import get_wool_terms
|
|
15
18
|
from hestia_earth.models.utils.completeness import _is_term_type_complete, _is_term_type_incomplete
|
|
@@ -30,7 +33,7 @@ from .pastureGrass_utils import (
|
|
|
30
33
|
REQUIREMENTS = {
|
|
31
34
|
"Cycle": {
|
|
32
35
|
"completeness.animalFeed": "True",
|
|
33
|
-
"completeness.animalPopulation": "
|
|
36
|
+
"completeness.animalPopulation": "True",
|
|
34
37
|
"completeness.freshForage": "False",
|
|
35
38
|
"site": {
|
|
36
39
|
"@type": "Site",
|
|
@@ -45,6 +48,19 @@ REQUIREMENTS = {
|
|
|
45
48
|
"term.termType": "landCover"
|
|
46
49
|
}
|
|
47
50
|
}],
|
|
51
|
+
"inputs": [{
|
|
52
|
+
"@type": "Input",
|
|
53
|
+
"term.units": "kg",
|
|
54
|
+
"value": "> 0",
|
|
55
|
+
"isAnimalFeed": "True",
|
|
56
|
+
"optional": {
|
|
57
|
+
"properties": [{
|
|
58
|
+
"@type": "Property",
|
|
59
|
+
"value": "",
|
|
60
|
+
"term.@id": ["neutralDetergentFibreContent", "energyContentHigherHeatingValue"]
|
|
61
|
+
}]
|
|
62
|
+
}
|
|
63
|
+
}],
|
|
48
64
|
"animals": [{
|
|
49
65
|
"@type": "Animal",
|
|
50
66
|
"value": "> 0",
|
|
@@ -93,19 +109,6 @@ REQUIREMENTS = {
|
|
|
93
109
|
}
|
|
94
110
|
}],
|
|
95
111
|
"optional": {
|
|
96
|
-
"inputs": [{
|
|
97
|
-
"@type": "Input",
|
|
98
|
-
"term.units": "kg",
|
|
99
|
-
"value": "> 0",
|
|
100
|
-
"isAnimalFeed": "True",
|
|
101
|
-
"optional": {
|
|
102
|
-
"properties": [{
|
|
103
|
-
"@type": "Property",
|
|
104
|
-
"value": "",
|
|
105
|
-
"term.@id": ["neutralDetergentFibreContent", "energyContentHigherHeatingValue"]
|
|
106
|
-
}]
|
|
107
|
-
}
|
|
108
|
-
}],
|
|
109
112
|
"products": [{
|
|
110
113
|
"@type": "Product",
|
|
111
114
|
"value": "",
|
|
@@ -169,6 +172,17 @@ def _calculate_GE(
|
|
|
169
172
|
NEwork = _sum_values(values, 3)
|
|
170
173
|
NEp = _sum_values(values, 4)
|
|
171
174
|
NEg = _sum_values(values, 5)
|
|
175
|
+
|
|
176
|
+
debugValues(cycle, model=MODEL, term=MODEL_KEY, model_key=MODEL_KEY,
|
|
177
|
+
NEm=NEm,
|
|
178
|
+
NEa=NEa,
|
|
179
|
+
NEl=NEl,
|
|
180
|
+
NEwork=NEwork,
|
|
181
|
+
NEp=NEp,
|
|
182
|
+
NEg=NEg,
|
|
183
|
+
NEm_feed=NEm_feed,
|
|
184
|
+
NEg_feed=NEg_feed)
|
|
185
|
+
|
|
172
186
|
return (NEm + NEa + NEl + NEwork + NEp - NEm_feed)/REM + (NEg + NEwool - NEg_feed)/REG
|
|
173
187
|
|
|
174
188
|
|
|
@@ -207,17 +221,20 @@ def _run_practice(cycle: dict, meanDE: float, meanECHHV: float, system: dict):
|
|
|
207
221
|
def _should_run(cycle: dict, practices: dict):
|
|
208
222
|
systems = filter_list_term_type(cycle.get('practices', []), TermTermType.SYSTEM)
|
|
209
223
|
animalFeed_complete = _is_term_type_complete(cycle, 'animalFeed')
|
|
210
|
-
|
|
224
|
+
animalPopulation_complete = _is_term_type_complete(cycle, 'animalPopulation')
|
|
211
225
|
freshForage_incomplete = _is_term_type_incomplete(cycle, 'freshForage')
|
|
212
226
|
all_animals_have_value = all([a.get('value', 0) > 0 for a in cycle.get('animals', [])])
|
|
213
227
|
|
|
228
|
+
has_cycle_inputs_feed = any([i.get('isAnimalFeed', False) for i in cycle.get('inputs', [])])
|
|
229
|
+
|
|
214
230
|
meanDE = calculate_meanDE(practices)
|
|
215
231
|
meanECHHV = calculate_meanECHHV(practices)
|
|
216
232
|
|
|
217
233
|
should_run = all([
|
|
218
234
|
animalFeed_complete,
|
|
219
|
-
|
|
235
|
+
animalPopulation_complete,
|
|
220
236
|
freshForage_incomplete,
|
|
237
|
+
has_cycle_inputs_feed,
|
|
221
238
|
all_animals_have_value,
|
|
222
239
|
len(systems) > 0,
|
|
223
240
|
len(practices) > 0,
|
|
@@ -228,8 +245,9 @@ def _should_run(cycle: dict, practices: dict):
|
|
|
228
245
|
for term_id in [MODEL_KEY] + [practice_input_id(p) for p in practices]:
|
|
229
246
|
logRequirements(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
230
247
|
term_type_animalFeed_complete=animalFeed_complete,
|
|
231
|
-
|
|
248
|
+
term_type_animalPopulation_complete=animalPopulation_complete,
|
|
232
249
|
term_type_freshForage_incomplete=freshForage_incomplete,
|
|
250
|
+
has_cycle_inputs_feed=has_cycle_inputs_feed,
|
|
233
251
|
all_animals_have_value=all_animals_have_value,
|
|
234
252
|
meanDE=meanDE,
|
|
235
253
|
meanECHHV=meanECHHV)
|
|
@@ -3,7 +3,7 @@ from hestia_earth.utils.api import download_hestia
|
|
|
3
3
|
from hestia_earth.utils.lookup import download_lookup, get_table_value, column_name, extract_grouped_data
|
|
4
4
|
from hestia_earth.utils.tools import list_sum, safe_parse_float
|
|
5
5
|
|
|
6
|
-
from hestia_earth.models.log import debugValues
|
|
6
|
+
from hestia_earth.models.log import debugValues
|
|
7
7
|
from hestia_earth.models.utils.input import get_feed_inputs
|
|
8
8
|
from hestia_earth.models.utils.term import get_lookup_value
|
|
9
9
|
from hestia_earth.models.utils.property import get_node_property, get_node_property_value, node_property_lookup_value
|
|
@@ -98,7 +98,7 @@ _NEa_BY_GROUPING = {
|
|
|
98
98
|
|
|
99
99
|
def _calculate_NEa(cycle: dict, animal: dict, system: dict, NEm: float) -> float:
|
|
100
100
|
grouping = _get_grouping(animal)
|
|
101
|
-
return _NEa_BY_GROUPING.get(grouping, lambda *args:
|
|
101
|
+
return _NEa_BY_GROUPING.get(grouping, lambda *args: 0)(cycle, animal, system, NEm)
|
|
102
102
|
|
|
103
103
|
|
|
104
104
|
def _calculate_NEl_cattleAndBuffalo(cycle: dict, animal: dict) -> float:
|
|
@@ -156,7 +156,7 @@ _NEl_BY_GROUPING = {
|
|
|
156
156
|
|
|
157
157
|
def _calculate_NEl(cycle: dict, animal: dict) -> float:
|
|
158
158
|
grouping = _get_grouping(animal)
|
|
159
|
-
return _NEl_BY_GROUPING.get(grouping, lambda *args:
|
|
159
|
+
return _NEl_BY_GROUPING.get(grouping, lambda *args: 0)(cycle, animal)
|
|
160
160
|
|
|
161
161
|
|
|
162
162
|
def _calculate_NEwork(cycle: dict, animal: dict, NEm: float) -> float:
|
|
@@ -347,8 +347,6 @@ def calculate_NEfeed(node: dict) -> tuple:
|
|
|
347
347
|
|
|
348
348
|
|
|
349
349
|
def get_animal_values(cycle: dict, animal: dict, system: dict):
|
|
350
|
-
term_id = animal.get('term', {}).get('@id')
|
|
351
|
-
|
|
352
350
|
NEm = _calculate_NEm(cycle, animal)
|
|
353
351
|
NEa = _calculate_NEa(cycle, animal, system, NEm)
|
|
354
352
|
NEl = _calculate_NEl(cycle, animal)
|
|
@@ -356,14 +354,6 @@ def get_animal_values(cycle: dict, animal: dict, system: dict):
|
|
|
356
354
|
NEp = _calculate_NEp(cycle, animal, NEm)
|
|
357
355
|
NEg = _calculate_NEg(cycle, animal)
|
|
358
356
|
|
|
359
|
-
logRequirements(cycle, model=MODEL, term=term_id, model_key=MODEL_KEY,
|
|
360
|
-
NEm=NEm,
|
|
361
|
-
NEa=NEa,
|
|
362
|
-
NEl=NEl,
|
|
363
|
-
NEwork=NEwork,
|
|
364
|
-
NEp=NEp,
|
|
365
|
-
NEg=NEg)
|
|
366
|
-
|
|
367
357
|
return (NEm, NEa, NEl, NEwork, NEp, NEg)
|
|
368
358
|
|
|
369
359
|
|
|
@@ -394,14 +384,7 @@ def should_run_practice(cycle: dict):
|
|
|
394
384
|
term_id = practice.get('term', {}).get('@id')
|
|
395
385
|
key_term_type = practice.get('key', {}).get('termType')
|
|
396
386
|
value = practice.get('value', [])
|
|
397
|
-
|
|
398
|
-
logRequirements(cycle, model=MODEL, term=term_id,
|
|
399
|
-
practice_value=list_sum(value),
|
|
400
|
-
practice_key_term_type=key_term_type)
|
|
401
|
-
|
|
402
|
-
should_run = all([len(value) > 0, term_id == MODEL_KEY, key_term_type in KEY_TERM_TYPES])
|
|
403
|
-
logShouldRun(cycle, MODEL, term_id, should_run)
|
|
404
|
-
return should_run
|
|
387
|
+
return all([len(value) > 0, term_id == MODEL_KEY, key_term_type in KEY_TERM_TYPES])
|
|
405
388
|
|
|
406
389
|
return should_run
|
|
407
390
|
|