hestia-earth-models 0.61.6__py3-none-any.whl → 0.61.8__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 +56 -0
- hestia_earth/models/cycle/input/hestiaAggregatedData.py +1 -1
- 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/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 +117 -3881
- 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/mocking/search-results.json +360 -260
- hestia_earth/models/schererPfister2015/pToDrainageWaterSoilFlux.py +1 -1
- hestia_earth/models/schererPfister2015/pToGroundwaterSoilFlux.py +1 -1
- hestia_earth/models/site/organicCarbonPerHa.py +58 -44
- hestia_earth/models/site/soilMeasurement.py +25 -38
- hestia_earth/models/utils/__init__.py +28 -0
- hestia_earth/models/utils/aquacultureManagement.py +2 -2
- hestia_earth/models/utils/array_builders.py +578 -0
- hestia_earth/models/utils/blank_node.py +2 -3
- hestia_earth/models/utils/crop.py +24 -1
- hestia_earth/models/utils/cycle.py +0 -23
- 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/lookup.py +6 -3
- hestia_earth/models/utils/measurement.py +118 -4
- hestia_earth/models/utils/site.py +25 -13
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.61.6.dist-info → hestia_earth_models-0.61.8.dist-info}/METADATA +1 -1
- {hestia_earth_models-0.61.6.dist-info → hestia_earth_models-0.61.8.dist-info}/RECORD +52 -40
- tests/models/cycle/completeness/test_electricityFuel.py +21 -0
- tests/models/emepEea2019/test_nh3ToAirInorganicFertiliser.py +2 -2
- tests/models/ipcc2019/test_co2ToAirSoilOrganicCarbonStockChangeManagementChange.py +54 -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/site/test_organicCarbonPerHa.py +3 -12
- tests/models/site/test_soilMeasurement.py +5 -19
- tests/models/utils/test_array_builders.py +253 -0
- tests/models/utils/{test_cycle.py → test_crop.py} +2 -2
- 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.6.dist-info → hestia_earth_models-0.61.8.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.61.6.dist-info → hestia_earth_models-0.61.8.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.61.6.dist-info → hestia_earth_models-0.61.8.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
|
+
)
|