From 57eaae08dd111f41cbc51826b04bd7d9f808ac9e Mon Sep 17 00:00:00 2001 From: Valyrian-Code Date: Mon, 8 Jun 2026 01:34:47 +0530 Subject: [PATCH 1/2] r.sim.water: replace flaky parallel test with a synthetic pytest The parallel (nprocs=2) check in test_nodxdy compared depth cell-wise against a reference. Because r.sim.water is stochastic and thread scheduling changes the random walker update order (and results also vary by CPU and compiler), that comparison was flaky and intermittently failed CI on unrelated PRs. Remove that parallel cell-wise assertion (the deterministic serial check stays) and add a synthetic-data pytest that checks scheduling-independent properties for both serial and parallel runs: non-negative depth, every cell has a value, water is present, and more water accumulates downhill. Approach approved in #7506. --- .../r.sim.water/tests/r_sim_water_test.py | 31 +++++++++++++++++++ .../r.sim.water/testsuite/test_r_sim_water.py | 16 ---------- 2 files changed, 31 insertions(+), 16 deletions(-) create mode 100644 raster/r.sim/r.sim.water/tests/r_sim_water_test.py diff --git a/raster/r.sim/r.sim.water/tests/r_sim_water_test.py b/raster/r.sim/r.sim.water/tests/r_sim_water_test.py new file mode 100644 index 00000000000..1db4e78456c --- /dev/null +++ b/raster/r.sim/r.sim.water/tests/r_sim_water_test.py @@ -0,0 +1,31 @@ +"""Tests of r.sim.water""" + +import pytest + + +@pytest.mark.parametrize("nprocs", [1, 2]) +def test_water_is_physical_and_flows_downhill(session_tools, nprocs): + """Water is physical and accumulates downhill for serial and parallel runs. + + Uses a small synthetic surface that slopes down toward the south with a + gentle valley. The checks are properties that hold regardless of parallel + walker scheduling (which makes exact per-cell values nondeterministic): + depth is never negative, every cell has a value, water is present, and more + water accumulates in the lower (south) third than in the upper (north) + third. + """ + tools = session_tools + tools.g_region(s=0, n=100, w=0, e=100, res=1) + tools.r_mapcalc(expression="elev = 0.05 * y() + 0.0006 * (x() - 50.0)^2") + tools.r_sim_water(elevation="elev", depth="depth", random_seed=1, nprocs=nprocs) + + depth = tools.r_univar(map="depth", format="json").json + assert depth["min"] >= 0 + assert depth["null_cells"] == 0 + assert depth["sum"] > 0 + + tools.r_mapcalc(expression="south = if(y() < 33, depth, null())") + tools.r_mapcalc(expression="north = if(y() > 67, depth, null())") + south = tools.r_univar(map="south", format="json").json + north = tools.r_univar(map="north", format="json").json + assert south["mean"] > north["mean"] diff --git a/raster/r.sim/r.sim.water/testsuite/test_r_sim_water.py b/raster/r.sim/r.sim.water/testsuite/test_r_sim_water.py index 764e1e65135..42b233a5240 100644 --- a/raster/r.sim/r.sim.water/testsuite/test_r_sim_water.py +++ b/raster/r.sim/r.sim.water/testsuite/test_r_sim_water.py @@ -13,7 +13,6 @@ class TestRSimWater(TestCase): dx = "tmp_dx" dy = "tmp_dy" depth = "tmp_depth" - depth2 = "tmp_depth2" discharge = "tmp_discharge" diff_depth = "tmp_diff_depth" diff_discharge = "tmp_diff_discharge" @@ -160,21 +159,6 @@ def test_nodxdy(self): stats = tools.r_univar(map=self.diff_discharge, format="json") self.assertAlmostEqual(stats["sum"], 0, delta=1e-3) - # test parallelized dxdy - # lower precision because parallelization affects random number generator - self.assertModule( - "r.sim.water", - elevation=self.elevation, - depth=self.depth2, - random_seed=1, - nprocs=2, - ) - self.assertRastersEqual( - self.depth2, - reference=self.reference_depth_default, - precision="0.02", - ) - def test_complex(self): """Test r.sim.water execution with more complex inputs""" # Run the r.sim.water simulation From 9f79926d6b78acf7a71eb721f281bc0bda9b2479 Mon Sep 17 00:00:00 2001 From: Valyrian-Code Date: Tue, 9 Jun 2026 00:25:17 +0530 Subject: [PATCH 2/2] r.sim.water: drop new pytest per review, keep only flaky test removal --- .../r.sim.water/tests/r_sim_water_test.py | 31 ------------------- 1 file changed, 31 deletions(-) delete mode 100644 raster/r.sim/r.sim.water/tests/r_sim_water_test.py diff --git a/raster/r.sim/r.sim.water/tests/r_sim_water_test.py b/raster/r.sim/r.sim.water/tests/r_sim_water_test.py deleted file mode 100644 index 1db4e78456c..00000000000 --- a/raster/r.sim/r.sim.water/tests/r_sim_water_test.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Tests of r.sim.water""" - -import pytest - - -@pytest.mark.parametrize("nprocs", [1, 2]) -def test_water_is_physical_and_flows_downhill(session_tools, nprocs): - """Water is physical and accumulates downhill for serial and parallel runs. - - Uses a small synthetic surface that slopes down toward the south with a - gentle valley. The checks are properties that hold regardless of parallel - walker scheduling (which makes exact per-cell values nondeterministic): - depth is never negative, every cell has a value, water is present, and more - water accumulates in the lower (south) third than in the upper (north) - third. - """ - tools = session_tools - tools.g_region(s=0, n=100, w=0, e=100, res=1) - tools.r_mapcalc(expression="elev = 0.05 * y() + 0.0006 * (x() - 50.0)^2") - tools.r_sim_water(elevation="elev", depth="depth", random_seed=1, nprocs=nprocs) - - depth = tools.r_univar(map="depth", format="json").json - assert depth["min"] >= 0 - assert depth["null_cells"] == 0 - assert depth["sum"] > 0 - - tools.r_mapcalc(expression="south = if(y() < 33, depth, null())") - tools.r_mapcalc(expression="north = if(y() > 67, depth, null())") - south = tools.r_univar(map="south", format="json").json - north = tools.r_univar(map="north", format="json").json - assert south["mean"] > north["mean"]