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
|
@@ -1,38 +1,25 @@
|
|
|
1
|
-
from unittest.mock import patch
|
|
2
1
|
import json
|
|
3
|
-
from numpy.
|
|
2
|
+
from numpy.typing import NDArray
|
|
3
|
+
from os.path import isfile
|
|
4
4
|
from pytest import mark
|
|
5
|
-
import
|
|
6
|
-
|
|
7
|
-
from
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
from hestia_earth.models.ipcc2019.organicCarbonPerHa import (
|
|
12
|
-
_assign_ipcc_carbon_input_category,
|
|
13
|
-
_assign_ipcc_land_use_category,
|
|
14
|
-
_assign_ipcc_management_category,
|
|
15
|
-
_assign_ipcc_soil_category,
|
|
16
|
-
_calc_temperature_factor,
|
|
17
|
-
_calc_tier_1_soc_stocks,
|
|
18
|
-
_calc_water_factor,
|
|
19
|
-
_check_cropland_low_category,
|
|
20
|
-
_check_cropland_medium_category,
|
|
21
|
-
_get_carbon_input_kwargs,
|
|
22
|
-
_iterate_soc_equilibriums,
|
|
23
|
-
_should_run,
|
|
24
|
-
IpccCarbonInputCategory,
|
|
25
|
-
IpccLandUseCategory,
|
|
26
|
-
IpccManagementCategory,
|
|
27
|
-
IpccSoilCategory,
|
|
28
|
-
MODEL,
|
|
29
|
-
run,
|
|
30
|
-
TERM_ID
|
|
31
|
-
)
|
|
5
|
+
from unittest.mock import MagicMock, patch
|
|
6
|
+
|
|
7
|
+
from hestia_earth.models.ipcc2019.organicCarbonPerHa import _should_run, MODEL, run, TERM_ID
|
|
8
|
+
from hestia_earth.models.ipcc2019.organicCarbonPerHa_utils import sample_constant
|
|
9
|
+
|
|
10
|
+
from tests.utils import fake_new_measurement, fixtures_path
|
|
32
11
|
|
|
33
12
|
class_path = f"hestia_earth.models.{MODEL}.{TERM_ID}"
|
|
13
|
+
tier_1_path = f"hestia_earth.models.{MODEL}.{TERM_ID}_tier_1_utils"
|
|
14
|
+
tier_2_path = f"hestia_earth.models.{MODEL}.{TERM_ID}_tier_2_utils"
|
|
15
|
+
utils_path = f"hestia_earth.models.{MODEL}.{TERM_ID}_utils"
|
|
16
|
+
term_path = "hestia_earth.models.utils.term"
|
|
17
|
+
property_path = "hestia_earth.models.utils.property"
|
|
18
|
+
|
|
34
19
|
fixtures_folder = f"{fixtures_path}/{MODEL}/{TERM_ID}"
|
|
35
20
|
|
|
21
|
+
ITERATIONS = 1000
|
|
22
|
+
|
|
36
23
|
COVER_CROP_PROPERTY_TERM_IDS = [
|
|
37
24
|
"catchCrop",
|
|
38
25
|
"coverCrop",
|
|
@@ -73,14 +60,14 @@ RESIDUE_REMOVED_OR_BURNT_TERM_IDS = [
|
|
|
73
60
|
"residueRemoved"
|
|
74
61
|
]
|
|
75
62
|
|
|
76
|
-
UPLAND_RICE_LAND_COVER_TERM_IDS = [
|
|
77
|
-
"ricePlantUpland"
|
|
78
|
-
]
|
|
79
|
-
|
|
80
63
|
UPLAND_RICE_CROP_TERM_IDS = [
|
|
81
64
|
"riceGrainInHuskUpland"
|
|
82
65
|
]
|
|
83
66
|
|
|
67
|
+
UPLAND_RICE_LAND_COVER_TERM_IDS = [
|
|
68
|
+
"ricePlantUpland"
|
|
69
|
+
]
|
|
70
|
+
|
|
84
71
|
DEFAULT_PROPERTIES = {
|
|
85
72
|
"manureDryKgMass": {
|
|
86
73
|
"carbonContent": {
|
|
@@ -96,69 +83,76 @@ DEFAULT_PROPERTIES = {
|
|
|
96
83
|
}
|
|
97
84
|
|
|
98
85
|
|
|
99
|
-
def
|
|
86
|
+
def fake_calc_descriptive_stats(arr: NDArray, *_args, **_kwargs):
|
|
87
|
+
return {"value": [row[0] for row in arr]}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def fake_find_term_property(term: dict, property: str, *_):
|
|
100
91
|
term_id = term.get('@id', None)
|
|
101
92
|
return DEFAULT_PROPERTIES.get(term_id, {}).get(property, {})
|
|
102
93
|
|
|
103
94
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
("tier-2/
|
|
115
|
-
("tier-2/with-
|
|
116
|
-
("tier-
|
|
117
|
-
("tier-
|
|
118
|
-
("tier-
|
|
119
|
-
("tier-
|
|
120
|
-
("tier-
|
|
121
|
-
("tier-
|
|
122
|
-
("tier-
|
|
123
|
-
("tier-1/
|
|
124
|
-
("tier-1/cropland-
|
|
125
|
-
("tier-
|
|
126
|
-
("tier-
|
|
127
|
-
("tier-
|
|
128
|
-
("tier-
|
|
129
|
-
("tier-
|
|
130
|
-
("tier-
|
|
131
|
-
("tier-
|
|
95
|
+
def order_list(values: list[dict]) -> list[dict]:
|
|
96
|
+
return sorted(values, key=lambda node: (
|
|
97
|
+
node.get("term", {}).get('@id', ""), # sort by `term.@id`
|
|
98
|
+
node.get("methodClassification", ""), # then by `methodClassifaction`
|
|
99
|
+
node.get("dates", []) # then by `dates[0]`
|
|
100
|
+
))
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# subfolder, should_run
|
|
104
|
+
PARAMS_SHOULD_RUN = [
|
|
105
|
+
("tier-1-and-2/cropland", True),
|
|
106
|
+
("tier-1-and-2/with-zero-carbon-input", True), # Closes issue 777
|
|
107
|
+
("tier-1/cropland-depth-as-float", True),
|
|
108
|
+
("tier-1/cropland-with-measured-soc", True),
|
|
109
|
+
("tier-1/cropland-without-measured-soc", True),
|
|
110
|
+
("tier-1/permanent-pasture", True),
|
|
111
|
+
("tier-1/should-not-run", False),
|
|
112
|
+
("tier-1/without-management-with-measured-soc", False),
|
|
113
|
+
("tier-1/land-use-change", True), # Closes issue 755
|
|
114
|
+
("tier-1/run-with-site-type", True), # Closes issue 755
|
|
115
|
+
("tier-1/cropland-polar", False), # Closes issue 794
|
|
116
|
+
("tier-2/with-generalised-monthly-measurements", False), # Closes issue 600
|
|
117
|
+
("tier-2/with-incomplete-climate-data", False), # Closes issue 599
|
|
118
|
+
("tier-2/with-initial-soc", True),
|
|
119
|
+
("tier-2/with-multi-year-cycles", True),
|
|
120
|
+
("tier-2/with-multi-year-cycles-and-missing-properties", True), # Closes issue 734
|
|
121
|
+
("tier-2/without-any-measurements", False), # Closes issue 594
|
|
122
|
+
("tier-2/without-initial-soc", True),
|
|
123
|
+
("tier-2/with-irrigation", True), # Closes issue 716
|
|
124
|
+
("tier-2/with-irrigation-dates", True), # Closes issue 716
|
|
125
|
+
("tier-2/with-paddy-rice", False), # Closes issue 718
|
|
126
|
+
("tier-2/with-sand-without-date", True), # Closes issue 739
|
|
127
|
+
("tier-2/with-irrigated-upland-rice", False) # Closes issue 718
|
|
132
128
|
]
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
)
|
|
140
|
-
@patch(f"{
|
|
141
|
-
@patch(f"{
|
|
142
|
-
@patch(f"{
|
|
143
|
-
@patch(f"{
|
|
144
|
-
@patch(f"{
|
|
145
|
-
@patch(f"{
|
|
146
|
-
@patch(f"{
|
|
147
|
-
@patch(f"{class_path}.related_cycles")
|
|
148
|
-
@patch("hestia_earth.models.utils.property.find_term_property", side_effect=find_term_property_side_effect)
|
|
129
|
+
IDS_SHOULD_RUN = [p[0] for p in PARAMS_SHOULD_RUN]
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@mark.parametrize("subfolder, should_run", PARAMS_SHOULD_RUN, ids=IDS_SHOULD_RUN)
|
|
133
|
+
@patch(f"{term_path}.search")
|
|
134
|
+
@patch(f"{property_path}.download_hestia")
|
|
135
|
+
@patch(f"{property_path}.find_term_property", side_effect=fake_find_term_property)
|
|
136
|
+
@patch(f"{utils_path}.get_upland_rice_land_cover_terms", return_value=UPLAND_RICE_LAND_COVER_TERM_IDS)
|
|
137
|
+
@patch(f"{utils_path}.get_upland_rice_crop_terms", return_value=UPLAND_RICE_CROP_TERM_IDS)
|
|
138
|
+
@patch(f"{utils_path}.get_residue_removed_or_burnt_terms", return_value=RESIDUE_REMOVED_OR_BURNT_TERM_IDS)
|
|
139
|
+
@patch(f"{utils_path}.get_irrigated_terms", return_value=IRRIGATED_TERM_IDS)
|
|
140
|
+
@patch(f"{utils_path}.get_crop_residue_incorporated_or_left_on_field_terms", return_value=CROP_RESIDUE_INCORP_TERM_IDS)
|
|
141
|
+
@patch(f"{utils_path}.get_cover_crop_property_terms", return_value=COVER_CROP_PROPERTY_TERM_IDS)
|
|
142
|
+
@patch(f"{tier_2_path}.related_cycles")
|
|
149
143
|
def test_should_run(
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
should_run
|
|
144
|
+
mock_related_cycles: MagicMock,
|
|
145
|
+
mock_get_cover_crop_property_terms: MagicMock, # utils mocks
|
|
146
|
+
mock_get_crop_residue_incorporated_or_left_on_field_terms: MagicMock,
|
|
147
|
+
mock_get_irrigated_terms: MagicMock,
|
|
148
|
+
mock_get_residue_removed_or_burnt_terms: MagicMock,
|
|
149
|
+
mock_get_upland_rice_crop_terms: MagicMock,
|
|
150
|
+
mock_get_upland_rice_land_cover_terms: MagicMock,
|
|
151
|
+
_mock_find_term_property: MagicMock,
|
|
152
|
+
mock_download_hestia: MagicMock,
|
|
153
|
+
mock_search: MagicMock,
|
|
154
|
+
subfolder: str,
|
|
155
|
+
should_run: bool
|
|
162
156
|
):
|
|
163
157
|
folder = f"{fixtures_folder}/{subfolder}"
|
|
164
158
|
|
|
@@ -166,67 +160,86 @@ def test_should_run(
|
|
|
166
160
|
with open(f"{folder}/cycles.jsonld", encoding='utf-8') as f:
|
|
167
161
|
return json.load(f)
|
|
168
162
|
|
|
169
|
-
mock_related_cycles.return_value =
|
|
163
|
+
mock_related_cycles.return_value = (
|
|
164
|
+
load_cycles_from_file() if isfile(f"{folder}/cycles.jsonld") else []
|
|
165
|
+
)
|
|
170
166
|
|
|
171
167
|
with open(f"{folder}/site.jsonld", encoding='utf-8') as f:
|
|
172
168
|
site = json.load(f)
|
|
173
169
|
|
|
174
|
-
|
|
175
|
-
should_run_ = should_run_tier_1 or should_run_tier_2
|
|
170
|
+
result, _ = _should_run(site)
|
|
176
171
|
|
|
177
|
-
|
|
172
|
+
# Ensure that API calls to retrieve term IDs are properly cached.
|
|
173
|
+
mock_get_cover_crop_property_terms.call_count <= 1
|
|
174
|
+
mock_get_crop_residue_incorporated_or_left_on_field_terms.call_count <= 1
|
|
175
|
+
mock_get_irrigated_terms.call_count <= 1
|
|
176
|
+
mock_get_residue_removed_or_burnt_terms.call_count <= 1
|
|
177
|
+
mock_get_upland_rice_crop_terms.call_count <= 1
|
|
178
|
+
mock_get_upland_rice_land_cover_terms.call_count <= 1
|
|
178
179
|
|
|
180
|
+
# Ensure that the property and term utils are properly mocked.
|
|
181
|
+
mock_download_hestia.assert_not_called()
|
|
182
|
+
mock_search.assert_not_called()
|
|
179
183
|
|
|
180
|
-
|
|
181
|
-
@patch(f"{class_path}.get_crop_residue_incorporated_or_left_on_field_terms", return_value=CROP_RESIDUE_INCORP_TERM_IDS)
|
|
182
|
-
@patch(f"{class_path}.get_irrigated_terms", return_value=IRRIGATED_TERM_IDS)
|
|
183
|
-
@patch(f"{class_path}.get_residue_removed_or_burnt_terms", return_value=RESIDUE_REMOVED_OR_BURNT_TERM_IDS)
|
|
184
|
-
@patch(f"{class_path}.get_upland_rice_land_cover_terms", return_value=UPLAND_RICE_LAND_COVER_TERM_IDS)
|
|
185
|
-
@patch(f"{class_path}.get_upland_rice_crop_terms", return_value=UPLAND_RICE_CROP_TERM_IDS)
|
|
186
|
-
@patch(f"{class_path}.related_cycles", return_value=[])
|
|
187
|
-
@patch("hestia_earth.models.utils.property.find_term_property", side_effect=find_term_property_side_effect)
|
|
188
|
-
def test_should_run_no_data(*args):
|
|
189
|
-
SITE = {}
|
|
190
|
-
EXPECTED = False
|
|
184
|
+
assert result == should_run
|
|
191
185
|
|
|
192
|
-
should_run_tier_1, should_run_tier_2, *_ = _should_run(SITE)
|
|
193
|
-
should_run = should_run_tier_1 or should_run_tier_2
|
|
194
186
|
|
|
195
|
-
|
|
187
|
+
@patch(f"{term_path}.search")
|
|
188
|
+
@patch(f"{property_path}.download_hestia")
|
|
189
|
+
@patch(f"{tier_2_path}.related_cycles", return_value=[])
|
|
190
|
+
def test_should_run_no_data(
|
|
191
|
+
_mock_related_cycles: MagicMock,
|
|
192
|
+
mock_download_hestia: MagicMock,
|
|
193
|
+
mock_search: MagicMock
|
|
194
|
+
):
|
|
195
|
+
SITE = {}
|
|
196
|
+
EXPECTED = []
|
|
196
197
|
|
|
198
|
+
result = run(SITE)
|
|
197
199
|
|
|
198
|
-
|
|
199
|
-
(
|
|
200
|
-
|
|
201
|
-
]
|
|
200
|
+
mock_download_hestia.assert_not_called()
|
|
201
|
+
mock_search.assert_not_called()
|
|
202
|
+
assert result == EXPECTED
|
|
202
203
|
|
|
203
204
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
)
|
|
209
|
-
@patch(f"{
|
|
210
|
-
@patch(f"{
|
|
211
|
-
@patch(f"{
|
|
212
|
-
@patch(f"{
|
|
213
|
-
@patch(f"{
|
|
214
|
-
@patch(f"{
|
|
215
|
-
@patch(f"{
|
|
216
|
-
@patch(f"{
|
|
217
|
-
@patch("
|
|
205
|
+
PARAMS_RUN = [subfolder for subfolder, should_run in PARAMS_SHOULD_RUN if should_run]
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
@mark.parametrize("subfolder", PARAMS_RUN)
|
|
209
|
+
@patch(f"{term_path}.search")
|
|
210
|
+
@patch(f"{property_path}.download_hestia")
|
|
211
|
+
@patch(f"{property_path}.find_term_property", side_effect=fake_find_term_property)
|
|
212
|
+
@patch(f"{utils_path}.get_upland_rice_land_cover_terms", return_value=UPLAND_RICE_LAND_COVER_TERM_IDS)
|
|
213
|
+
@patch(f"{utils_path}.get_upland_rice_crop_terms", return_value=UPLAND_RICE_CROP_TERM_IDS)
|
|
214
|
+
@patch(f"{utils_path}.get_residue_removed_or_burnt_terms", return_value=RESIDUE_REMOVED_OR_BURNT_TERM_IDS)
|
|
215
|
+
@patch(f"{utils_path}.get_irrigated_terms", return_value=IRRIGATED_TERM_IDS)
|
|
216
|
+
@patch(f"{utils_path}.get_crop_residue_incorporated_or_left_on_field_terms", return_value=CROP_RESIDUE_INCORP_TERM_IDS)
|
|
217
|
+
@patch(f"{utils_path}.get_cover_crop_property_terms", return_value=COVER_CROP_PROPERTY_TERM_IDS)
|
|
218
|
+
@patch(f"{tier_2_path}.related_cycles")
|
|
219
|
+
@patch(f"{tier_2_path}.calc_descriptive_stats", side_effect=fake_calc_descriptive_stats)
|
|
220
|
+
@patch(f"{tier_2_path}._new_measurement", side_effect=fake_new_measurement)
|
|
221
|
+
@patch(f"{tier_2_path}._get_sample_func", return_value=sample_constant)
|
|
222
|
+
@patch(f"{tier_1_path}.calc_descriptive_stats", side_effect=fake_calc_descriptive_stats)
|
|
223
|
+
@patch(f"{tier_1_path}._new_measurement", side_effect=fake_new_measurement)
|
|
224
|
+
@patch(f"{tier_1_path}._get_sample_func", return_value=sample_constant)
|
|
218
225
|
def test_run(
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
226
|
+
_mock_get_sample_func_t1: MagicMock,
|
|
227
|
+
_mock_new_measurement_t1: MagicMock,
|
|
228
|
+
_mock_calc_descriptive_stats_t1: MagicMock,
|
|
229
|
+
_mock_get_sample_func_t2: MagicMock,
|
|
230
|
+
_mock_new_measurement_t2: MagicMock,
|
|
231
|
+
_mock_calc_descriptive_stats_t2: MagicMock,
|
|
232
|
+
mock_related_cycles: MagicMock,
|
|
233
|
+
mock_get_cover_crop_property_terms: MagicMock, # utils mocks
|
|
234
|
+
mock_get_crop_residue_incorporated_or_left_on_field_terms: MagicMock,
|
|
235
|
+
mock_get_irrigated_terms: MagicMock,
|
|
236
|
+
mock_get_residue_removed_or_burnt_terms: MagicMock,
|
|
237
|
+
mock_get_upland_rice_crop_terms: MagicMock,
|
|
238
|
+
mock_get_upland_rice_land_cover_terms: MagicMock,
|
|
239
|
+
_mock_find_term_property: MagicMock,
|
|
240
|
+
mock_download_hestia: MagicMock,
|
|
241
|
+
mock_search: MagicMock,
|
|
242
|
+
subfolder: str
|
|
230
243
|
):
|
|
231
244
|
folder = f"{fixtures_folder}/{subfolder}"
|
|
232
245
|
|
|
@@ -234,7 +247,9 @@ def test_run(
|
|
|
234
247
|
with open(f"{folder}/cycles.jsonld", encoding='utf-8') as f:
|
|
235
248
|
return json.load(f)
|
|
236
249
|
|
|
237
|
-
mock_related_cycles.return_value =
|
|
250
|
+
mock_related_cycles.return_value = (
|
|
251
|
+
load_cycles_from_file() if isfile(f"{folder}/cycles.jsonld") else []
|
|
252
|
+
)
|
|
238
253
|
|
|
239
254
|
with open(f"{folder}/site.jsonld", encoding='utf-8') as f:
|
|
240
255
|
site = json.load(f)
|
|
@@ -242,345 +257,89 @@ def test_run(
|
|
|
242
257
|
with open(f"{folder}/result.jsonld", encoding='utf-8') as f:
|
|
243
258
|
expected = json.load(f)
|
|
244
259
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
# --- TIER 2 TESTS: SUB-MODELS ---
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
def test_calc_temperature_factor():
|
|
253
|
-
NUM_RANDOM = 9999
|
|
254
|
-
MIN_T, MAX_T = -60, 60
|
|
255
|
-
MIN_FAC, MAX_FAC = 0, 1
|
|
256
|
-
|
|
257
|
-
temperatures = [random.uniform(MIN_T, MAX_T) for _ in range(0, NUM_RANDOM)]
|
|
258
|
-
results = [
|
|
259
|
-
_calc_temperature_factor(t) for t in temperatures
|
|
260
|
-
]
|
|
261
|
-
|
|
262
|
-
assert all(MIN_FAC <= result <= MAX_FAC for result in results)
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
def test_calc_water_factor():
|
|
266
|
-
NUM_RANDOM = 9999
|
|
267
|
-
MIN, MAX = 0, 9999
|
|
268
|
-
MIN_FAC, MAX_FAC = 0.2129, 1.5
|
|
269
|
-
IRR_FAC = 0.775
|
|
270
|
-
|
|
271
|
-
precipitations = [random.uniform(MIN, MAX) for _ in range(0, NUM_RANDOM)]
|
|
272
|
-
pets = [random.uniform(MIN, MAX) for _ in range(0, NUM_RANDOM)]
|
|
273
|
-
|
|
274
|
-
results = [
|
|
275
|
-
_calc_water_factor(pre, pet) for pre, pet in zip(precipitations, pets)
|
|
276
|
-
]
|
|
277
|
-
irr_results = [
|
|
278
|
-
_calc_water_factor(pre, pet, is_irrigated=True) for pre, pet in zip(precipitations, pets)
|
|
279
|
-
]
|
|
280
|
-
|
|
281
|
-
assert all(MIN_FAC <= result <= MAX_FAC for result in results)
|
|
282
|
-
assert all(result == IRR_FAC for result in irr_results)
|
|
283
|
-
assert _calc_water_factor(1, 1) == _calc_water_factor(1000, 1000)
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
def test_calc_water_factor_zero():
|
|
287
|
-
"""
|
|
288
|
-
Closes issue 771. Function should not raise 0 error and should return the maximum water factor.
|
|
289
|
-
"""
|
|
290
|
-
assert _calc_water_factor(0, 0) == 1.49961875
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
# --- IPCC SOIL CATEGORY TESTS ---
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
# subfolder, expected
|
|
297
|
-
SOIL_CATEGORY_PARAMS = [
|
|
298
|
-
("fractional", IpccSoilCategory.WETLAND_SOILS),
|
|
299
|
-
("no-measurements", IpccSoilCategory.LOW_ACTIVITY_CLAY_SOILS),
|
|
300
|
-
("sandy-override", IpccSoilCategory.SANDY_SOILS),
|
|
301
|
-
("soilType/hac", IpccSoilCategory.HIGH_ACTIVITY_CLAY_SOILS),
|
|
302
|
-
("soilType/lac", IpccSoilCategory.LOW_ACTIVITY_CLAY_SOILS),
|
|
303
|
-
("soilType/org", IpccSoilCategory.ORGANIC_SOILS),
|
|
304
|
-
("soilType/pod", IpccSoilCategory.SPODIC_SOILS),
|
|
305
|
-
("soilType/san", IpccSoilCategory.SANDY_SOILS),
|
|
306
|
-
("soilType/vol", IpccSoilCategory.VOLCANIC_SOILS),
|
|
307
|
-
("soilType/wet", IpccSoilCategory.WETLAND_SOILS),
|
|
308
|
-
("usdaSoilType/hac", IpccSoilCategory.HIGH_ACTIVITY_CLAY_SOILS),
|
|
309
|
-
("usdaSoilType/lac", IpccSoilCategory.LOW_ACTIVITY_CLAY_SOILS),
|
|
310
|
-
("usdaSoilType/org", IpccSoilCategory.ORGANIC_SOILS),
|
|
311
|
-
("usdaSoilType/pod", IpccSoilCategory.SPODIC_SOILS),
|
|
312
|
-
("usdaSoilType/san", IpccSoilCategory.SANDY_SOILS),
|
|
313
|
-
("usdaSoilType/vol", IpccSoilCategory.VOLCANIC_SOILS),
|
|
314
|
-
("usdaSoilType/wet", IpccSoilCategory.WETLAND_SOILS)
|
|
315
|
-
]
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
@mark.parametrize(
|
|
319
|
-
"subfolder, expected",
|
|
320
|
-
SOIL_CATEGORY_PARAMS,
|
|
321
|
-
ids=[params[0] for params in SOIL_CATEGORY_PARAMS]
|
|
322
|
-
)
|
|
323
|
-
def test_assign_ipcc_soil_category(subfolder, expected):
|
|
324
|
-
folder = f"{fixtures_folder}/IpccSoilCategory/{subfolder}"
|
|
325
|
-
|
|
326
|
-
with open(f"{folder}/site.jsonld", encoding='utf-8') as f:
|
|
327
|
-
site = json.load(f)
|
|
260
|
+
with patch(f"{class_path}.ITERATIONS", ITERATIONS):
|
|
261
|
+
result = run(site)
|
|
328
262
|
|
|
329
|
-
|
|
330
|
-
|
|
263
|
+
# Ensure that API calls to retrieve term IDs are properly cached.
|
|
264
|
+
mock_get_cover_crop_property_terms.call_count <= 1
|
|
265
|
+
mock_get_crop_residue_incorporated_or_left_on_field_terms.call_count <= 1
|
|
266
|
+
mock_get_irrigated_terms.call_count <= 1
|
|
267
|
+
mock_get_residue_removed_or_burnt_terms.call_count <= 1
|
|
268
|
+
mock_get_upland_rice_crop_terms.call_count <= 1
|
|
269
|
+
mock_get_upland_rice_land_cover_terms.call_count <= 1
|
|
331
270
|
|
|
271
|
+
# Ensure that the property and term utils are properly mocked.
|
|
272
|
+
mock_download_hestia.assert_not_called()
|
|
273
|
+
mock_search.assert_not_called()
|
|
332
274
|
|
|
333
|
-
|
|
275
|
+
assert order_list(result) == order_list(expected)
|
|
334
276
|
|
|
335
277
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
("fractional", IpccSoilCategory.LOW_ACTIVITY_CLAY_SOILS, IpccLandUseCategory.PERENNIAL_CROPS),
|
|
342
|
-
("grassland", IpccSoilCategory.LOW_ACTIVITY_CLAY_SOILS, IpccLandUseCategory.GRASSLAND),
|
|
343
|
-
("irrigated-upland-rice", IpccSoilCategory.LOW_ACTIVITY_CLAY_SOILS, IpccLandUseCategory.PADDY_RICE_CULTIVATION),
|
|
344
|
-
("native", IpccSoilCategory.LOW_ACTIVITY_CLAY_SOILS, IpccLandUseCategory.NATIVE),
|
|
345
|
-
("other", IpccSoilCategory.LOW_ACTIVITY_CLAY_SOILS, IpccLandUseCategory.OTHER),
|
|
346
|
-
("paddy-rice-cultivation", IpccSoilCategory.LOW_ACTIVITY_CLAY_SOILS, IpccLandUseCategory.PADDY_RICE_CULTIVATION),
|
|
347
|
-
("perennial-crops", IpccSoilCategory.LOW_ACTIVITY_CLAY_SOILS, IpccLandUseCategory.PERENNIAL_CROPS),
|
|
348
|
-
("set-aside", IpccSoilCategory.LOW_ACTIVITY_CLAY_SOILS, IpccLandUseCategory.SET_ASIDE),
|
|
349
|
-
("set-aside-override", IpccSoilCategory.LOW_ACTIVITY_CLAY_SOILS, IpccLandUseCategory.SET_ASIDE),
|
|
350
|
-
("upland-rice", IpccSoilCategory.LOW_ACTIVITY_CLAY_SOILS, IpccLandUseCategory.ANNUAL_CROPS),
|
|
278
|
+
PARAMS_RUN_WITH_STATS = [
|
|
279
|
+
"tier-1-and-2/with-stats", # Closes issue 753
|
|
280
|
+
"tier-1/cropland-with-stats", # Closes issue 753
|
|
281
|
+
"tier-1/land-use-change-with-stats", # Closes issue 753
|
|
282
|
+
"tier-2/with-stats" # Closes issue 753
|
|
351
283
|
]
|
|
352
284
|
|
|
353
285
|
|
|
354
|
-
@mark.parametrize(
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
)
|
|
359
|
-
@patch(f"{
|
|
360
|
-
@patch(f"{
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
MANAGEMENT_CATEGORY_PARAMS = [
|
|
382
|
-
("fractional-annual-crops", IpccLandUseCategory.ANNUAL_CROPS, IpccManagementCategory.REDUCED_TILLAGE),
|
|
383
|
-
("fractional-annual-crops-wet", IpccLandUseCategory.ANNUAL_CROPS_WET, IpccManagementCategory.REDUCED_TILLAGE),
|
|
384
|
-
("fractional-grassland", IpccLandUseCategory.GRASSLAND, IpccManagementCategory.IMPROVED_GRASSLAND),
|
|
385
|
-
("full-tillage", IpccLandUseCategory.ANNUAL_CROPS, IpccManagementCategory.FULL_TILLAGE),
|
|
386
|
-
("high-intensity-grazing", IpccLandUseCategory.GRASSLAND, IpccManagementCategory.HIGH_INTENSITY_GRAZING),
|
|
387
|
-
("improved-grassland", IpccLandUseCategory.GRASSLAND, IpccManagementCategory.IMPROVED_GRASSLAND),
|
|
388
|
-
("no-management/annual-crops", IpccLandUseCategory.ANNUAL_CROPS, IpccManagementCategory.FULL_TILLAGE),
|
|
389
|
-
("no-management/annual-crops-wet", IpccLandUseCategory.ANNUAL_CROPS_WET, IpccManagementCategory.FULL_TILLAGE),
|
|
390
|
-
("no-management/grassland", IpccLandUseCategory.GRASSLAND, IpccManagementCategory.NOMINALLY_MANAGED),
|
|
391
|
-
("no-tillage", IpccLandUseCategory.ANNUAL_CROPS, IpccManagementCategory.NO_TILLAGE),
|
|
392
|
-
("nominally-managed", IpccLandUseCategory.GRASSLAND, IpccManagementCategory.NOMINALLY_MANAGED),
|
|
393
|
-
("other", IpccLandUseCategory.OTHER, IpccManagementCategory.OTHER),
|
|
394
|
-
("reduced-tillage", IpccLandUseCategory.ANNUAL_CROPS, IpccManagementCategory.REDUCED_TILLAGE),
|
|
395
|
-
("severely-degraded", IpccLandUseCategory.GRASSLAND, IpccManagementCategory.SEVERELY_DEGRADED),
|
|
396
|
-
]
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
@mark.parametrize(
|
|
400
|
-
"subfolder, land_use_category, expected",
|
|
401
|
-
MANAGEMENT_CATEGORY_PARAMS,
|
|
402
|
-
ids=[params[0] for params in MANAGEMENT_CATEGORY_PARAMS]
|
|
403
|
-
)
|
|
404
|
-
def test_assign_ipcc_management_category(subfolder, land_use_category, expected):
|
|
405
|
-
folder = f"{fixtures_folder}/IpccManagementCategory/{subfolder}"
|
|
406
|
-
|
|
407
|
-
with open(f"{folder}/site.jsonld", encoding='utf-8') as f:
|
|
408
|
-
site = json.load(f)
|
|
409
|
-
|
|
410
|
-
result = _assign_ipcc_management_category(site.get("management", []), land_use_category)
|
|
411
|
-
assert result == expected
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
# --- IPCC CARBON INPUT CATEGORY TESTS ---
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
@mark.parametrize("key", [1, 2, 3, 4], ids=lambda key: f"scenario-{key}")
|
|
418
|
-
@patch(f"{class_path}.get_cover_crop_property_terms", return_value=COVER_CROP_PROPERTY_TERM_IDS)
|
|
419
|
-
@patch(f"{class_path}.get_crop_residue_incorporated_or_left_on_field_terms", return_value=CROP_RESIDUE_INCORP_TERM_IDS)
|
|
420
|
-
@patch(f"{class_path}.get_irrigated_terms", return_value=IRRIGATED_TERM_IDS)
|
|
421
|
-
@patch(f"{class_path}.get_residue_removed_or_burnt_terms", return_value=RESIDUE_REMOVED_OR_BURNT_TERM_IDS)
|
|
422
|
-
def test_check_cropland_medium_category(
|
|
423
|
-
_mock_get_residue_removed_or_burnt_terms,
|
|
424
|
-
_mock_get_irrigated_terms,
|
|
425
|
-
_mock_get_crop_residue_incorporated_or_left_on_field_terms,
|
|
426
|
-
_mock_get_cover_crop_property_terms,
|
|
427
|
-
key
|
|
286
|
+
@mark.parametrize("subfolder", PARAMS_RUN_WITH_STATS)
|
|
287
|
+
@patch(f"{term_path}.search")
|
|
288
|
+
@patch(f"{property_path}.download_hestia")
|
|
289
|
+
@patch(f"{property_path}.find_term_property", side_effect=fake_find_term_property)
|
|
290
|
+
@patch(f"{utils_path}.get_upland_rice_land_cover_terms", return_value=UPLAND_RICE_LAND_COVER_TERM_IDS)
|
|
291
|
+
@patch(f"{utils_path}.get_upland_rice_crop_terms", return_value=UPLAND_RICE_CROP_TERM_IDS)
|
|
292
|
+
@patch(f"{utils_path}.get_residue_removed_or_burnt_terms", return_value=RESIDUE_REMOVED_OR_BURNT_TERM_IDS)
|
|
293
|
+
@patch(f"{utils_path}.get_irrigated_terms", return_value=IRRIGATED_TERM_IDS)
|
|
294
|
+
@patch(f"{utils_path}.get_crop_residue_incorporated_or_left_on_field_terms", return_value=CROP_RESIDUE_INCORP_TERM_IDS)
|
|
295
|
+
@patch(f"{utils_path}.get_cover_crop_property_terms", return_value=COVER_CROP_PROPERTY_TERM_IDS)
|
|
296
|
+
@patch(f"{tier_2_path}.related_cycles")
|
|
297
|
+
@patch(f"{tier_2_path}._new_measurement", side_effect=fake_new_measurement)
|
|
298
|
+
@patch(f"{tier_1_path}._new_measurement", side_effect=fake_new_measurement)
|
|
299
|
+
def test_run_with_stats(
|
|
300
|
+
_mock_new_measurement_t1: MagicMock,
|
|
301
|
+
_mock_new_measurement_t2: MagicMock,
|
|
302
|
+
mock_related_cycles: MagicMock,
|
|
303
|
+
mock_get_cover_crop_property_terms: MagicMock, # utils mocks
|
|
304
|
+
mock_get_crop_residue_incorporated_or_left_on_field_terms: MagicMock,
|
|
305
|
+
mock_get_irrigated_terms: MagicMock,
|
|
306
|
+
mock_get_residue_removed_or_burnt_terms: MagicMock,
|
|
307
|
+
mock_get_upland_rice_crop_terms: MagicMock,
|
|
308
|
+
mock_get_upland_rice_land_cover_terms: MagicMock,
|
|
309
|
+
_mock_find_term_property: MagicMock,
|
|
310
|
+
mock_download_hestia: MagicMock,
|
|
311
|
+
mock_search: MagicMock,
|
|
312
|
+
subfolder: str
|
|
428
313
|
):
|
|
429
|
-
""
|
|
430
|
-
Tests each set of cropland medium conditions against a list of nodes that such satisfy it. The function returns the
|
|
431
|
-
key of the matching condition set, which should match the suffix of the fixtures subfolder.
|
|
432
|
-
"""
|
|
433
|
-
folder = f"{fixtures_folder}/IpccCarbonInputCategory/cropland-medium/scenario-{key}"
|
|
434
|
-
|
|
435
|
-
with open(f"{folder}/site.jsonld", encoding='utf-8') as f:
|
|
436
|
-
site = json.load(f)
|
|
437
|
-
|
|
438
|
-
result = _check_cropland_medium_category(**_get_carbon_input_kwargs(site.get("management", [])))
|
|
439
|
-
assert result == key
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
@mark.parametrize("key", [1, 2, 3], ids=lambda key: f"scenario-{key}")
|
|
443
|
-
@patch(f"{class_path}.get_cover_crop_property_terms", return_value=COVER_CROP_PROPERTY_TERM_IDS)
|
|
444
|
-
@patch(f"{class_path}.get_crop_residue_incorporated_or_left_on_field_terms", return_value=CROP_RESIDUE_INCORP_TERM_IDS)
|
|
445
|
-
@patch(f"{class_path}.get_irrigated_terms", return_value=IRRIGATED_TERM_IDS)
|
|
446
|
-
@patch(f"{class_path}.get_residue_removed_or_burnt_terms", return_value=RESIDUE_REMOVED_OR_BURNT_TERM_IDS)
|
|
447
|
-
def test_check_cropland_low_category(
|
|
448
|
-
_mock_get_residue_removed_or_burnt_terms,
|
|
449
|
-
_mock_get_irrigated_terms,
|
|
450
|
-
_mock_get_crop_residue_incorporated_or_left_on_field_terms,
|
|
451
|
-
_mock_get_cover_crop_property_terms,
|
|
452
|
-
key
|
|
453
|
-
):
|
|
454
|
-
"""
|
|
455
|
-
Tests each set of cropland low conditions against a list of nodes that such satisfy it. The function returns the
|
|
456
|
-
key of the matching condition set, which should match the suffix of the fixtures subfolder.
|
|
457
|
-
"""
|
|
458
|
-
folder = f"{fixtures_folder}/IpccCarbonInputCategory/cropland-low/scenario-{key}"
|
|
314
|
+
folder = f"{fixtures_folder}/{subfolder}"
|
|
459
315
|
|
|
460
|
-
|
|
461
|
-
|
|
316
|
+
def load_cycles_from_file():
|
|
317
|
+
with open(f"{folder}/cycles.jsonld", encoding='utf-8') as f:
|
|
318
|
+
return json.load(f)
|
|
462
319
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
# subfolder, management_category, expected
|
|
468
|
-
CARBON_INPUT_CATEGORY_PARAMS = [
|
|
469
|
-
(
|
|
470
|
-
"cropland-high-with-manure",
|
|
471
|
-
IpccManagementCategory.FULL_TILLAGE,
|
|
472
|
-
IpccCarbonInputCategory.CROPLAND_HIGH_WITH_MANURE
|
|
473
|
-
),
|
|
474
|
-
(
|
|
475
|
-
"cropland-high-without-manure/organic-fertiliser", # Closes issue 743
|
|
476
|
-
IpccManagementCategory.FULL_TILLAGE,
|
|
477
|
-
IpccCarbonInputCategory.CROPLAND_HIGH_WITHOUT_MANURE
|
|
478
|
-
),
|
|
479
|
-
(
|
|
480
|
-
"cropland-high-without-manure/soil-amendment", # Closes issue 743
|
|
481
|
-
IpccManagementCategory.FULL_TILLAGE,
|
|
482
|
-
IpccCarbonInputCategory.CROPLAND_HIGH_WITHOUT_MANURE
|
|
483
|
-
),
|
|
484
|
-
("cropland-low/scenario-1", IpccManagementCategory.FULL_TILLAGE, IpccCarbonInputCategory.CROPLAND_LOW),
|
|
485
|
-
("cropland-low/scenario-2", IpccManagementCategory.FULL_TILLAGE, IpccCarbonInputCategory.CROPLAND_LOW),
|
|
486
|
-
("cropland-low/scenario-3", IpccManagementCategory.FULL_TILLAGE, IpccCarbonInputCategory.CROPLAND_LOW),
|
|
487
|
-
("cropland-medium/scenario-1", IpccManagementCategory.FULL_TILLAGE, IpccCarbonInputCategory.CROPLAND_MEDIUM),
|
|
488
|
-
("cropland-medium/scenario-2", IpccManagementCategory.FULL_TILLAGE, IpccCarbonInputCategory.CROPLAND_MEDIUM),
|
|
489
|
-
("cropland-medium/scenario-3", IpccManagementCategory.FULL_TILLAGE, IpccCarbonInputCategory.CROPLAND_MEDIUM),
|
|
490
|
-
("cropland-medium/scenario-4", IpccManagementCategory.FULL_TILLAGE, IpccCarbonInputCategory.CROPLAND_MEDIUM),
|
|
491
|
-
("grassland-high", IpccManagementCategory.IMPROVED_GRASSLAND, IpccCarbonInputCategory.GRASSLAND_HIGH),
|
|
492
|
-
(
|
|
493
|
-
"grassland-medium/0-improvements",
|
|
494
|
-
IpccManagementCategory.IMPROVED_GRASSLAND,
|
|
495
|
-
IpccCarbonInputCategory.GRASSLAND_MEDIUM
|
|
496
|
-
),
|
|
497
|
-
(
|
|
498
|
-
"grassland-medium/1-improvements",
|
|
499
|
-
IpccManagementCategory.IMPROVED_GRASSLAND,
|
|
500
|
-
IpccCarbonInputCategory.GRASSLAND_MEDIUM
|
|
320
|
+
mock_related_cycles.return_value = (
|
|
321
|
+
load_cycles_from_file() if isfile(f"{folder}/cycles.jsonld") else []
|
|
501
322
|
)
|
|
502
|
-
]
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
@mark.parametrize(
|
|
506
|
-
"subfolder, management_category, expected",
|
|
507
|
-
CARBON_INPUT_CATEGORY_PARAMS,
|
|
508
|
-
ids=[params[0] for params in CARBON_INPUT_CATEGORY_PARAMS]
|
|
509
|
-
)
|
|
510
|
-
@patch(f"{class_path}.get_cover_crop_property_terms", return_value=COVER_CROP_PROPERTY_TERM_IDS)
|
|
511
|
-
@patch(f"{class_path}.get_crop_residue_incorporated_or_left_on_field_terms", return_value=CROP_RESIDUE_INCORP_TERM_IDS)
|
|
512
|
-
@patch(f"{class_path}.get_irrigated_terms", return_value=IRRIGATED_TERM_IDS)
|
|
513
|
-
@patch(f"{class_path}.get_residue_removed_or_burnt_terms", return_value=RESIDUE_REMOVED_OR_BURNT_TERM_IDS)
|
|
514
|
-
def test_assign_ipcc_carbon_input_category(
|
|
515
|
-
_mock_get_residue_removed_or_burnt_terms,
|
|
516
|
-
_mock_get_irrigated_terms,
|
|
517
|
-
_mock_get_crop_residue_incorporated_or_left_on_field_terms,
|
|
518
|
-
_mock_get_cover_crop_property_terms,
|
|
519
|
-
subfolder,
|
|
520
|
-
management_category,
|
|
521
|
-
expected
|
|
522
|
-
):
|
|
523
|
-
folder = f"{fixtures_folder}/IpccCarbonInputCategory/{subfolder}"
|
|
524
323
|
|
|
525
324
|
with open(f"{folder}/site.jsonld", encoding='utf-8') as f:
|
|
526
325
|
site = json.load(f)
|
|
527
326
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
# --- TIER 1 TESTS ---
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
@mark.parametrize(
|
|
536
|
-
"soc_equilibriums, expected",
|
|
537
|
-
[
|
|
538
|
-
(
|
|
539
|
-
[77.000, 70.840, 70.840, 70.840, 70.840, 70.840, 70.840],
|
|
540
|
-
[77.000, 75.460, 73.920, 72.380, 70.840, 70.840, 70.840]
|
|
541
|
-
),
|
|
542
|
-
(
|
|
543
|
-
[77.000, 70.840, 70.840, 70.840, 80.850, 80.850, 80.850],
|
|
544
|
-
[77.000, 75.460, 73.920, 72.380, 74.498, 76.615, 78.733]
|
|
545
|
-
),
|
|
546
|
-
(
|
|
547
|
-
[80.850, 70.840, 70.840, 70.840, 70.840, 80.850, 80.850],
|
|
548
|
-
[80.850, 78.348, 75.845, 73.343, 70.840, 73.343, 75.845]
|
|
549
|
-
),
|
|
550
|
-
(
|
|
551
|
-
[80.850, 80.850, 77.000, 77.000, 77.000, 77.000, 77.000],
|
|
552
|
-
[80.850, 80.850, 79.888, 78.925, 77.963, 77.000, 77.000]
|
|
553
|
-
),
|
|
554
|
-
(
|
|
555
|
-
[70.840, 70.840, 70.840, 70.840, 80.850, 80.850, 80.850],
|
|
556
|
-
[70.840, 70.840, 70.840, 70.840, 73.343, 75.845, 78.348]
|
|
557
|
-
),
|
|
558
|
-
(
|
|
559
|
-
[70.840, 70.840, 80.850, 80.850, 80.850, 70.840, 80.850],
|
|
560
|
-
[70.840, 70.840, 73.343, 75.845, 78.348, 76.471, 77.565]
|
|
561
|
-
),
|
|
562
|
-
],
|
|
563
|
-
ids=["land-unit-1", "land-unit-2", "land-unit-3", "land-unit-4", "land-unit-5", "land-unit-6"]
|
|
564
|
-
)
|
|
565
|
-
def test_run_tier_1_soc_stocks(soc_equilibriums, expected):
|
|
566
|
-
"""
|
|
567
|
-
Test the interpolation between SOC equilibriums using test data provided in IPCC (2019).
|
|
568
|
-
"""
|
|
569
|
-
TIMESTAMPS = [1990, 1995, 2000, 2005, 2010, 2015, 2020]
|
|
570
|
-
result = _calc_tier_1_soc_stocks(
|
|
571
|
-
TIMESTAMPS, soc_equilibriums
|
|
572
|
-
)
|
|
573
|
-
assert_almost_equal(result, expected, decimal=3)
|
|
327
|
+
with open(f"{folder}/result.jsonld", encoding='utf-8') as f:
|
|
328
|
+
expected = json.load(f)
|
|
574
329
|
|
|
330
|
+
with patch(f"{class_path}.ITERATIONS", ITERATIONS):
|
|
331
|
+
result = run(site)
|
|
575
332
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
333
|
+
# Ensure that API calls to retrieve term IDs are properly cached.
|
|
334
|
+
mock_get_cover_crop_property_terms.call_count <= 1
|
|
335
|
+
mock_get_crop_residue_incorporated_or_left_on_field_terms.call_count <= 1
|
|
336
|
+
mock_get_irrigated_terms.call_count <= 1
|
|
337
|
+
mock_get_residue_removed_or_burnt_terms.call_count <= 1
|
|
338
|
+
mock_get_upland_rice_crop_terms.call_count <= 1
|
|
339
|
+
mock_get_upland_rice_land_cover_terms.call_count <= 1
|
|
579
340
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
)
|
|
341
|
+
# Ensure that the property and term utils are properly mocked.
|
|
342
|
+
mock_download_hestia.assert_not_called()
|
|
343
|
+
mock_search.assert_not_called()
|
|
584
344
|
|
|
585
|
-
result
|
|
586
|
-
assert result == EXPECTED
|
|
345
|
+
assert order_list(result) == order_list(expected)
|