Skip to content

Commit 1d71a72

Browse files
i.evapo.mh: add test file (#5519)
* add test file * modify docstrings and make edits * modify existing test to fail because of bug * changed the stats after bug fix
1 parent aa45c58 commit 1d71a72

File tree

1 file changed

+193
-0
lines changed

1 file changed

+193
-0
lines changed
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import grass.script as gs
2+
from grass.gunittest.case import TestCase
3+
from grass.gunittest.main import test
4+
import math
5+
6+
7+
class TestEvapotranspirationMH(TestCase):
8+
"""Regression tests for i.evapo.mh module"""
9+
10+
tmp_rasters = []
11+
netradiation_diurnal = "netrad"
12+
average_temperature = "avg_temp"
13+
minimum_temperature = "min_temp"
14+
maximum_temperature = "max_temp"
15+
precipitation = "precipitation"
16+
output_map = "et_output"
17+
18+
@classmethod
19+
def setUpClass(cls):
20+
"""Setup input rasters and configure test environment."""
21+
seed = 43
22+
cls.use_temp_region()
23+
gs.run_command("g.region", n=10, s=0, e=10, w=0, rows=10, cols=10)
24+
cls.runModule(
25+
"r.mapcalc",
26+
expression=f"{cls.netradiation_diurnal} = rand(0, 160)",
27+
seed=seed,
28+
overwrite=True,
29+
)
30+
cls.runModule(
31+
"r.mapcalc",
32+
expression=f"{cls.minimum_temperature} = rand(0, 15)",
33+
seed=seed,
34+
overwrite=True,
35+
)
36+
cls.runModule(
37+
"r.mapcalc",
38+
expression=f"{cls.maximum_temperature} = rand(25, 40)",
39+
seed=seed,
40+
overwrite=True,
41+
)
42+
cls.runModule(
43+
"r.mapcalc",
44+
expression=f"{cls.average_temperature} = ({cls.minimum_temperature}+{cls.maximum_temperature})/2",
45+
overwrite=True,
46+
)
47+
cls.runModule(
48+
"r.mapcalc",
49+
expression=f"{cls.precipitation} = rand(50, 130)",
50+
seed=seed,
51+
overwrite=True,
52+
)
53+
cls.tmp_rasters.extend(
54+
[
55+
cls.netradiation_diurnal,
56+
cls.average_temperature,
57+
cls.minimum_temperature,
58+
cls.maximum_temperature,
59+
cls.precipitation,
60+
]
61+
)
62+
63+
@classmethod
64+
def tearDownClass(cls):
65+
"""Remove generated rasters and reset test environment."""
66+
gs.run_command("g.remove", type="raster", name=cls.tmp_rasters, flags="f")
67+
cls.del_temp_region()
68+
69+
def create_raster(self, name, expression, seed=None):
70+
"""Create temporary raster map and register it for cleanup."""
71+
self.runModule(
72+
"r.mapcalc",
73+
expression=f"{name} = {expression}",
74+
seed=seed,
75+
overwrite=True,
76+
)
77+
self.tmp_rasters.append(name)
78+
79+
def run_evapo(self, output, flags="", overwrite=True, **kwargs):
80+
"""Execute the i.evapo.mh module with standardized parameters."""
81+
self.assertModule(
82+
"i.evapo.mh",
83+
flags=flags,
84+
output=output,
85+
overwrite=overwrite,
86+
**kwargs,
87+
)
88+
self.tmp_rasters.append(output)
89+
90+
def test_z_flag_negative_values(self):
91+
"""Verify -z flag clamps negative output values to zero."""
92+
self.create_raster("netrad_z", "10")
93+
self.create_raster("min_temp_z", "0")
94+
self.create_raster("max_temp_z", "10")
95+
self.create_raster("avg_temp_z", "(min_temp_z + max_temp_z)/2")
96+
97+
self.run_evapo(
98+
flags="z",
99+
netradiation_diurnal="netrad_z",
100+
average_temperature="avg_temp_z",
101+
minimum_temperature="min_temp_z",
102+
maximum_temperature="max_temp_z",
103+
precipitation=self.precipitation,
104+
output=self.output_map,
105+
)
106+
stats = gs.parse_command(
107+
"r.univar", map=self.output_map, flags="g", format="json"
108+
)[0]
109+
self.assertGreaterEqual(stats["min"], 0)
110+
111+
def test_h_flag_precipitation_ignored(self):
112+
"""Confirm -h flag makes precipitation input irrelevant."""
113+
self.run_evapo(
114+
flags="h",
115+
netradiation_diurnal=self.netradiation_diurnal,
116+
average_temperature=self.average_temperature,
117+
minimum_temperature=self.minimum_temperature,
118+
maximum_temperature=self.maximum_temperature,
119+
precipitation=self.precipitation,
120+
output=f"{self.output_map}_h",
121+
)
122+
self.run_evapo(
123+
flags="h",
124+
netradiation_diurnal=self.netradiation_diurnal,
125+
average_temperature=self.average_temperature,
126+
minimum_temperature=self.minimum_temperature,
127+
maximum_temperature=self.maximum_temperature,
128+
output=f"{self.output_map}_h_noprecip",
129+
)
130+
self.assertRastersNoDifference(
131+
f"{self.output_map}_h",
132+
f"{self.output_map}_h_noprecip",
133+
precision=1e-6,
134+
)
135+
136+
def test_s_flag_different_output(self):
137+
"""Check -s flag produces different results from default method."""
138+
self.run_evapo(
139+
netradiation_diurnal=self.netradiation_diurnal,
140+
average_temperature=self.average_temperature,
141+
minimum_temperature=self.minimum_temperature,
142+
maximum_temperature=self.maximum_temperature,
143+
precipitation=self.precipitation,
144+
output=f"{self.output_map}_default",
145+
)
146+
self.run_evapo(
147+
flags="s",
148+
netradiation_diurnal=self.netradiation_diurnal,
149+
average_temperature=self.average_temperature,
150+
minimum_temperature=self.minimum_temperature,
151+
maximum_temperature=self.maximum_temperature,
152+
precipitation=self.precipitation,
153+
output=f"{self.output_map}_s",
154+
)
155+
self.create_raster(
156+
"diff_map", f"{self.output_map}_default - {self.output_map}_s"
157+
)
158+
stats = {"min": 0, "max": 2.276227, "mean": 0.985087}
159+
self.assertRasterFitsUnivar(raster="diff_map", reference=stats, precision=1e-6)
160+
161+
def test_hargreaves_formula(self):
162+
"""Validate Hargreaves formula implementation matches reference."""
163+
self.create_raster("netrad_test", "0.1446759")
164+
self.create_raster("tmin_test", "10")
165+
self.create_raster("tmax_test", "30")
166+
self.create_raster("tavg_test", "20")
167+
168+
ra_input_w = 0.1446759
169+
tmin = 10
170+
tmax = 30
171+
tavg = 20
172+
173+
ra_mj = ra_input_w * 0.0864
174+
td = tmax - tmin
175+
176+
expected_et = 0.0023 * 0.408 * ra_mj * (tavg + 17.8) * math.sqrt(td)
177+
178+
self.run_evapo(
179+
flags="h",
180+
netradiation_diurnal="netrad_test",
181+
average_temperature="tavg_test",
182+
minimum_temperature="tmin_test",
183+
maximum_temperature="tmax_test",
184+
output="et_const",
185+
)
186+
stats = gs.parse_command("r.univar", map="et_const", flags="g", format="json")[
187+
0
188+
]
189+
self.assertAlmostEqual(stats["mean"], expected_et)
190+
191+
192+
if __name__ == "__main__":
193+
test()

0 commit comments

Comments
 (0)