hestia-earth-models 0.59.1__py3-none-any.whl → 0.59.3__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/irrigatedTypeUnspecified.py +6 -7
- hestia_earth/models/impact_assessment/irrigated.py +4 -1
- hestia_earth/models/ipcc2006/n2OToAirCropResidueDecompositionDirect.py +83 -0
- hestia_earth/models/ipcc2006/n2OToAirCropResidueDecompositionIndirect.py +3 -3
- hestia_earth/models/ipcc2006/n2OToAirExcretaIndirect.py +3 -3
- hestia_earth/models/ipcc2006/n2OToAirInorganicFertiliserIndirect.py +5 -4
- hestia_earth/models/ipcc2006/n2OToAirOrganicFertiliserIndirect.py +5 -4
- hestia_earth/models/ipcc2006/utils.py +12 -16
- hestia_earth/models/ipcc2019/n2OToAirCropResidueDecompositionDirect.py +8 -7
- hestia_earth/models/ipcc2019/n2OToAirCropResidueDecompositionIndirect.py +100 -0
- hestia_earth/models/ipcc2019/n2OToAirExcretaIndirect.py +100 -0
- hestia_earth/models/ipcc2019/n2OToAirInorganicFertiliserIndirect.py +54 -61
- hestia_earth/models/ipcc2019/n2OToAirOrganicFertiliserIndirect.py +58 -66
- hestia_earth/models/ipcc2019/nh3ToAirInorganicFertiliser.py +112 -0
- hestia_earth/models/ipcc2019/nh3ToAirOrganicFertiliser.py +107 -0
- hestia_earth/models/ipcc2019/noxToAirInorganicFertiliser.py +112 -0
- hestia_earth/models/ipcc2019/noxToAirOrganicFertiliser.py +107 -0
- hestia_earth/models/ipcc2019/organicCarbonPerHa.py +67 -21
- hestia_earth/models/ipcc2019/utils.py +28 -16
- hestia_earth/models/site/soilMeasurement.py +197 -0
- hestia_earth/models/utils/cycle.py +7 -6
- hestia_earth/models/utils/emission.py +15 -0
- hestia_earth/models/version.py +1 -1
- {hestia_earth_models-0.59.1.dist-info → hestia_earth_models-0.59.3.dist-info}/METADATA +1 -1
- {hestia_earth_models-0.59.1.dist-info → hestia_earth_models-0.59.3.dist-info}/RECORD +40 -24
- tests/models/ipcc2006/test_n2OToAirCropResidueDecompositionDirect.py +50 -0
- tests/models/ipcc2019/test_n2OToAirCropResidueDecompositionIndirect.py +71 -0
- tests/models/ipcc2019/test_n2OToAirExcretaIndirect.py +71 -0
- tests/models/ipcc2019/test_n2OToAirInorganicFertiliserIndirect.py +36 -13
- tests/models/ipcc2019/test_n2OToAirOrganicFertiliserIndirect.py +36 -13
- tests/models/ipcc2019/test_nh3ToAirInorganicFertiliser.py +47 -0
- tests/models/ipcc2019/test_nh3ToAirOrganicFertiliser.py +35 -0
- tests/models/ipcc2019/test_noxToAirInorganicFertiliser.py +47 -0
- tests/models/ipcc2019/test_noxToAirOrganicFertiliser.py +35 -0
- tests/models/ipcc2019/test_organicCarbonPerHa.py +51 -5
- tests/models/site/test_soilMeasurement.py +159 -0
- tests/models/utils/test_blank_node.py +5 -5
- {hestia_earth_models-0.59.1.dist-info → hestia_earth_models-0.59.3.dist-info}/LICENSE +0 -0
- {hestia_earth_models-0.59.1.dist-info → hestia_earth_models-0.59.3.dist-info}/WHEEL +0 -0
- {hestia_earth_models-0.59.1.dist-info → hestia_earth_models-0.59.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from unittest.mock import patch
|
|
2
|
+
import json
|
|
3
|
+
from tests.utils import fixtures_path, fake_new_emission
|
|
4
|
+
|
|
5
|
+
from hestia_earth.models.ipcc2019.noxToAirOrganicFertiliser import MODEL, TERM_ID, run, _should_run
|
|
6
|
+
|
|
7
|
+
class_path = f"hestia_earth.models.{MODEL}.{TERM_ID}"
|
|
8
|
+
fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@patch(f"{class_path}._is_term_type_complete", return_value=False)
|
|
12
|
+
@patch(f"{class_path}.get_organic_fertiliser_N_total", return_value=0)
|
|
13
|
+
def test_should_run(mock_N_total, mock_complete, *args):
|
|
14
|
+
# no N => no run
|
|
15
|
+
assert not _should_run({})
|
|
16
|
+
|
|
17
|
+
# with N => no run
|
|
18
|
+
mock_N_total.return_value = 10
|
|
19
|
+
assert not _should_run({})
|
|
20
|
+
|
|
21
|
+
# is complete => run
|
|
22
|
+
mock_complete.return_value = True
|
|
23
|
+
assert _should_run({}) is True
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@patch(f"{class_path}._new_emission", side_effect=fake_new_emission)
|
|
27
|
+
def test_run(*args):
|
|
28
|
+
with open(f"{fixtures_folder}/cycle.jsonld", encoding='utf-8') as f:
|
|
29
|
+
cycle = json.load(f)
|
|
30
|
+
|
|
31
|
+
with open(f"{fixtures_folder}/result.jsonld", encoding='utf-8') as f:
|
|
32
|
+
expected = json.load(f)
|
|
33
|
+
|
|
34
|
+
value = run(cycle)
|
|
35
|
+
assert value == expected
|
|
@@ -16,6 +16,7 @@ from hestia_earth.models.ipcc2019.organicCarbonPerHa import (
|
|
|
16
16
|
_calc_temperature_factor,
|
|
17
17
|
_calc_tier_1_soc_stocks,
|
|
18
18
|
_calc_water_factor,
|
|
19
|
+
_calculated_cycles,
|
|
19
20
|
_check_cropland_low_category,
|
|
20
21
|
_check_cropland_medium_category,
|
|
21
22
|
_get_carbon_input_kwargs,
|
|
@@ -85,6 +86,25 @@ UPLAND_RICE_CROP_TERM_IDS = [
|
|
|
85
86
|
"riceGrainInHuskUpland"
|
|
86
87
|
]
|
|
87
88
|
|
|
89
|
+
DEFAULT_PROPERTIES = {
|
|
90
|
+
"manureDryKgMass": {
|
|
91
|
+
"carbonContent": {
|
|
92
|
+
"value": 38.4
|
|
93
|
+
},
|
|
94
|
+
"nitrogenContent": {
|
|
95
|
+
"value": 2.65
|
|
96
|
+
},
|
|
97
|
+
"ligninContent": {
|
|
98
|
+
"value": 9.67
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def find_term_property_side_effect(term: dict, property: str, *_):
|
|
105
|
+
term_id = term.get('@id', None)
|
|
106
|
+
return DEFAULT_PROPERTIES.get(term_id, {}).get(property, {})
|
|
107
|
+
|
|
88
108
|
|
|
89
109
|
# --- TIER 1 & TIER 2 TESTS ---
|
|
90
110
|
|
|
@@ -96,11 +116,13 @@ RUN_SUBFOLDERS = [
|
|
|
96
116
|
("tier-2/with-incomplete-climate-data", True), # Closes issue 599
|
|
97
117
|
("tier-2/with-initial-soc", True),
|
|
98
118
|
("tier-2/with-multi-year-cycles", True),
|
|
119
|
+
("tier-2/with-multi-year-cycles-and-missing-properties", True), # Closes issue 734
|
|
99
120
|
("tier-2/without-any-measurements", True), # Closes issue 594
|
|
100
121
|
("tier-2/without-initial-soc", True),
|
|
101
122
|
("tier-2/with-irrigation", True),
|
|
102
123
|
("tier-2/with-irrigation-dates", True),
|
|
103
124
|
("tier-2/with-paddy-rice", True),
|
|
125
|
+
("tier-2/with-sand-without-date", True), # Closes issue 739
|
|
104
126
|
("tier-2/with-irrigated-upland-rice", True),
|
|
105
127
|
("tier-1/cropland-depth-as-float", False),
|
|
106
128
|
("tier-1/cropland-with-measured-soc", False),
|
|
@@ -124,9 +146,11 @@ RUN_SUBFOLDERS = [
|
|
|
124
146
|
@patch(f"{class_path}.get_residue_removed_or_burnt_terms", return_value=RESIDUE_REMOVED_OR_BURNT_TERM_IDS)
|
|
125
147
|
@patch(f"{class_path}.get_upland_rice_land_cover_terms", return_value=UPLAND_RICE_LAND_COVER_TERM_IDS)
|
|
126
148
|
@patch(f"{class_path}.get_upland_rice_crop_terms", return_value=UPLAND_RICE_CROP_TERM_IDS)
|
|
127
|
-
@patch(f"{class_path}.
|
|
149
|
+
@patch(f"{class_path}._calculated_cycles")
|
|
150
|
+
@patch("hestia_earth.models.utils.property.find_term_property")
|
|
128
151
|
def test_run(
|
|
129
|
-
|
|
152
|
+
mock_find_term_property,
|
|
153
|
+
mock_calculated_cycles,
|
|
130
154
|
_mock_get_upland_rice_crop_terms,
|
|
131
155
|
_mock_get_upland_rice_land_cover_terms,
|
|
132
156
|
_mock_get_residue_removed_or_burnt_terms,
|
|
@@ -144,7 +168,8 @@ def test_run(
|
|
|
144
168
|
with open(f"{folder}/cycles.jsonld", encoding='utf-8') as f:
|
|
145
169
|
return json.load(f)
|
|
146
170
|
|
|
147
|
-
|
|
171
|
+
mock_find_term_property.side_effect = find_term_property_side_effect
|
|
172
|
+
mock_calculated_cycles.return_value = load_cycles_from_file() if load_cycles else []
|
|
148
173
|
|
|
149
174
|
with open(f"{folder}/site.jsonld", encoding='utf-8') as f:
|
|
150
175
|
site = json.load(f)
|
|
@@ -164,7 +189,7 @@ def test_run(
|
|
|
164
189
|
@patch(f"{class_path}.get_residue_removed_or_burnt_terms", return_value=RESIDUE_REMOVED_OR_BURNT_TERM_IDS)
|
|
165
190
|
@patch(f"{class_path}.get_upland_rice_land_cover_terms", return_value=UPLAND_RICE_LAND_COVER_TERM_IDS)
|
|
166
191
|
@patch(f"{class_path}.get_upland_rice_crop_terms", return_value=UPLAND_RICE_CROP_TERM_IDS)
|
|
167
|
-
@patch(f"{class_path}.
|
|
192
|
+
@patch(f"{class_path}._calculated_cycles", return_value=[])
|
|
168
193
|
def test_run_no_data(*args):
|
|
169
194
|
SITE = {}
|
|
170
195
|
EXPECTED = []
|
|
@@ -209,6 +234,22 @@ def test_calc_water_factor():
|
|
|
209
234
|
assert _calc_water_factor(1, 1) == _calc_water_factor(1000, 1000)
|
|
210
235
|
|
|
211
236
|
|
|
237
|
+
@patch(f"{class_path}.find_related")
|
|
238
|
+
@patch("hestia_earth.models.utils.download_hestia")
|
|
239
|
+
def test_calculated_cycles(mock_download_hestia, mock_find_related):
|
|
240
|
+
CYCLES = [{"@id": "cycle"}]
|
|
241
|
+
EXPECTED = [{"@id": "cycle", "@type": "Cycle"}]
|
|
242
|
+
|
|
243
|
+
mock_download_hestia.side_effect = lambda node_id, node_type, **_: {
|
|
244
|
+
"@type": node_type.value,
|
|
245
|
+
"@id": node_id
|
|
246
|
+
}
|
|
247
|
+
mock_find_related.return_value = CYCLES
|
|
248
|
+
|
|
249
|
+
result = _calculated_cycles({})
|
|
250
|
+
assert EXPECTED == result
|
|
251
|
+
|
|
252
|
+
|
|
212
253
|
# --- IPCC SOIL CATEGORY TESTS ---
|
|
213
254
|
|
|
214
255
|
|
|
@@ -397,7 +438,12 @@ CARBON_INPUT_CATEGORY_PARAMS = [
|
|
|
397
438
|
IpccCarbonInputCategory.CROPLAND_HIGH_WITH_MANURE
|
|
398
439
|
),
|
|
399
440
|
(
|
|
400
|
-
"cropland-high-without-manure",
|
|
441
|
+
"cropland-high-without-manure/organic-fertiliser", # Closes issue 743
|
|
442
|
+
IpccManagementCategory.FULL_TILLAGE,
|
|
443
|
+
IpccCarbonInputCategory.CROPLAND_HIGH_WITHOUT_MANURE
|
|
444
|
+
),
|
|
445
|
+
(
|
|
446
|
+
"cropland-high-without-manure/soil-amendment", # Closes issue 743
|
|
401
447
|
IpccManagementCategory.FULL_TILLAGE,
|
|
402
448
|
IpccCarbonInputCategory.CROPLAND_HIGH_WITHOUT_MANURE
|
|
403
449
|
),
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
from unittest.mock import patch
|
|
2
|
+
import json
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from tests.utils import fixtures_path, fake_new_measurement
|
|
6
|
+
|
|
7
|
+
from hestia_earth.models.site.soilMeasurement import (
|
|
8
|
+
MODEL,
|
|
9
|
+
MODEL_KEY,
|
|
10
|
+
_get_overlap,
|
|
11
|
+
_harmonise_measurements,
|
|
12
|
+
_should_run, run
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
class_path = f"hestia_earth.models.{MODEL}.{MODEL_KEY}"
|
|
16
|
+
fixtures_folder = f"{fixtures_path}/site/soilMeasurement"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.mark.parametrize(
|
|
20
|
+
"data,expected",
|
|
21
|
+
[
|
|
22
|
+
((1, 10, 2, 150), 8),
|
|
23
|
+
((1, 10, 5, 15), 5),
|
|
24
|
+
((1, 10, 10, 15), 0),
|
|
25
|
+
((10, 10, 0, 150), 0),
|
|
26
|
+
((150, 155, 1, 150), 0),
|
|
27
|
+
((20, 40, 0, 30), 10),
|
|
28
|
+
((0, 20, 0, 50), 20),
|
|
29
|
+
((20, 60, 0, 50), 30),
|
|
30
|
+
((10, 20, 40, 50), 0),
|
|
31
|
+
((10, 20, 19, 50), 1),
|
|
32
|
+
]
|
|
33
|
+
)
|
|
34
|
+
def test_get_overlap(data, expected):
|
|
35
|
+
result = _get_overlap(
|
|
36
|
+
data[0], data[1], data[2], data[3]
|
|
37
|
+
)
|
|
38
|
+
assert result == expected
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@pytest.mark.parametrize(
|
|
42
|
+
"measurements_list,returns_dict,expected_value",
|
|
43
|
+
[
|
|
44
|
+
(
|
|
45
|
+
[
|
|
46
|
+
{"value": [7.5], "depthUpper": 0, "depthLower": 20},
|
|
47
|
+
{"value": [6], "depthUpper": 20, "depthLower": 40},
|
|
48
|
+
],
|
|
49
|
+
{"depthUpper": 0, "depthLower": 30},
|
|
50
|
+
7
|
|
51
|
+
),
|
|
52
|
+
(
|
|
53
|
+
[
|
|
54
|
+
{"value": [7.5], "depthUpper": 0, "depthLower": 20},
|
|
55
|
+
{"value": [6], "depthUpper": 20, "depthLower": 40},
|
|
56
|
+
],
|
|
57
|
+
{"depthUpper": 0, "depthLower": 50},
|
|
58
|
+
6.75
|
|
59
|
+
),
|
|
60
|
+
]
|
|
61
|
+
)
|
|
62
|
+
def test_harmonise_measurements(measurements_list, returns_dict, expected_value):
|
|
63
|
+
actual_value = _harmonise_measurements(
|
|
64
|
+
measurements_list=measurements_list,
|
|
65
|
+
standard_depth_upper=returns_dict["depthUpper"],
|
|
66
|
+
standard_depth_lower=returns_dict["depthLower"],
|
|
67
|
+
)
|
|
68
|
+
assert actual_value == expected_value
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@pytest.mark.parametrize(
|
|
72
|
+
"test_name,site,expected_should_run",
|
|
73
|
+
[
|
|
74
|
+
(
|
|
75
|
+
"no measurement => no run",
|
|
76
|
+
{"measurements": []},
|
|
77
|
+
False
|
|
78
|
+
),
|
|
79
|
+
(
|
|
80
|
+
"missing dates => run",
|
|
81
|
+
{
|
|
82
|
+
"measurements":
|
|
83
|
+
[
|
|
84
|
+
{
|
|
85
|
+
"term": {"@id": "clayContent"},
|
|
86
|
+
"depthUpper": 0,
|
|
87
|
+
"depthLower": 20
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
True
|
|
92
|
+
),
|
|
93
|
+
(
|
|
94
|
+
"no depthUpper => no run",
|
|
95
|
+
{
|
|
96
|
+
"measurements":
|
|
97
|
+
[
|
|
98
|
+
{
|
|
99
|
+
"term": {"@id": "clayContent"},
|
|
100
|
+
"dates": ["2022-01-02"],
|
|
101
|
+
"depthLower": 20
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
},
|
|
105
|
+
False
|
|
106
|
+
),
|
|
107
|
+
(
|
|
108
|
+
"all fields => run",
|
|
109
|
+
{
|
|
110
|
+
"measurements":
|
|
111
|
+
[
|
|
112
|
+
{
|
|
113
|
+
"term": {"@id": "clayContent"},
|
|
114
|
+
"dates": ["2022-01-02"],
|
|
115
|
+
"depthUpper": 0,
|
|
116
|
+
"depthLower": 20
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
True
|
|
121
|
+
)
|
|
122
|
+
]
|
|
123
|
+
)
|
|
124
|
+
@patch(f"{class_path}.get_lookup_value")
|
|
125
|
+
def test_should_run(mock_get_lookup, test_name, site, expected_should_run):
|
|
126
|
+
mock_get_lookup.return_value = True
|
|
127
|
+
model_key = "clayContent"
|
|
128
|
+
should_run, *args = _should_run(site=site, model_key=model_key)
|
|
129
|
+
assert should_run == expected_should_run, test_name
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def lookup_side_effect(*args, **kwargs):
|
|
133
|
+
_ = kwargs
|
|
134
|
+
if args[0]["@id"] == "soilPh" and args[1] == "depthSensitive":
|
|
135
|
+
return False
|
|
136
|
+
elif args[0]["@id"] in {"baseSaturation", "soilDepth", "rainfallHourly"}:
|
|
137
|
+
return False
|
|
138
|
+
return True
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@pytest.mark.parametrize(
|
|
142
|
+
"test_name",
|
|
143
|
+
[
|
|
144
|
+
"missingDepth", "simpleSoilPh", "clayContent", "missingDepth", "nonUniqueMeasurements", "arrays"
|
|
145
|
+
]
|
|
146
|
+
)
|
|
147
|
+
@patch(f"{class_path}._new_measurement", side_effect=fake_new_measurement)
|
|
148
|
+
@patch(f"{class_path}.get_lookup_value")
|
|
149
|
+
def test_run(mock_lookup, mock_new_measurement, test_name):
|
|
150
|
+
mock_lookup.side_effect = lookup_side_effect
|
|
151
|
+
|
|
152
|
+
with open(f"{fixtures_folder}/{test_name}/site.jsonld", encoding='utf-8') as f:
|
|
153
|
+
site = json.load(f)
|
|
154
|
+
|
|
155
|
+
with open(f"{fixtures_folder}/{test_name}/result.jsonld", encoding='utf-8') as f:
|
|
156
|
+
result = json.load(f)
|
|
157
|
+
|
|
158
|
+
value = run(site)
|
|
159
|
+
assert value == result
|
|
@@ -815,13 +815,13 @@ def test_group_nodes_by_year_scenario_d():
|
|
|
815
815
|
"value": 100
|
|
816
816
|
},
|
|
817
817
|
{
|
|
818
|
-
"term.@id": "
|
|
818
|
+
"term.@id": "organicFertiliserUsed",
|
|
819
819
|
"endDate": "2000-11",
|
|
820
820
|
"startDate": "2000-09",
|
|
821
821
|
"value": True
|
|
822
822
|
},
|
|
823
823
|
{
|
|
824
|
-
"term.@id": "
|
|
824
|
+
"term.@id": "organicFertiliserUsed",
|
|
825
825
|
"endDate": "2002-06",
|
|
826
826
|
"startDate": "2001-07",
|
|
827
827
|
"value": True
|
|
@@ -838,7 +838,7 @@ def test_group_nodes_by_year_scenario_d():
|
|
|
838
838
|
"fraction_of_node_duration": 0.9153005464480874
|
|
839
839
|
},
|
|
840
840
|
{
|
|
841
|
-
"term.@id": "
|
|
841
|
+
"term.@id": "organicFertiliserUsed",
|
|
842
842
|
"endDate": "2000-11",
|
|
843
843
|
"startDate": "2000-09",
|
|
844
844
|
"value": True,
|
|
@@ -872,7 +872,7 @@ def test_group_nodes_by_year_scenario_d():
|
|
|
872
872
|
"fraction_of_node_duration": 0.8315217391304348
|
|
873
873
|
},
|
|
874
874
|
{
|
|
875
|
-
"term.@id": "
|
|
875
|
+
"term.@id": "organicFertiliserUsed",
|
|
876
876
|
"endDate": "2002-06",
|
|
877
877
|
"startDate": "2001-07",
|
|
878
878
|
"value": True,
|
|
@@ -882,7 +882,7 @@ def test_group_nodes_by_year_scenario_d():
|
|
|
882
882
|
],
|
|
883
883
|
2002: [
|
|
884
884
|
{
|
|
885
|
-
"term.@id": "
|
|
885
|
+
"term.@id": "organicFertiliserUsed",
|
|
886
886
|
"endDate": "2002-06",
|
|
887
887
|
"startDate": "2001-07",
|
|
888
888
|
"value": True,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|