From fd2be7db5d12f1e08de36fc4b81d83bbc148444d Mon Sep 17 00:00:00 2001 From: Sam Evans <47793072+Sevans711@users.noreply.github.com> Date: Mon, 2 Nov 2026 17:28:18 -0500 Subject: [PATCH 1/4] test: scrip eager/dask already exactly agree (PR #1775 just didn't realize it.) --- test/io/test_scrip.py | 52 ++----------------------------------------- 1 file changed, 2 insertions(+), 50 deletions(-) diff --git a/test/io/test_scrip.py b/test/io/test_scrip.py index c15464632..f598e66d1 100644 --- a/test/io/test_scrip.py +++ b/test/io/test_scrip.py @@ -143,48 +143,15 @@ def test_scrip_dask_lazy_dedup_matches_eager(gridpath, chunks): """Opening a SCRIP grid with ``chunks=`` must produce the same mesh as the eager path, even though the underlying dedup algorithm differs (dask-native vs. Polars) once the corner arrays are dask-backed. - - The comparison is on corner coordinates *in winding order*, not sorted: - the two paths are free to number nodes differently, but the order of - corners within a face is what determines area sign and normal - direction, so it must be preserved exactly. """ grid_file = gridpath("scrip", "outCSne8", "outCSne8.nc") - grid_eager = ux.open_grid(grid_file) grid_lazy = ux.open_grid(grid_file, chunks=chunks) - assert isinstance(grid_lazy._ds["face_node_connectivity"].data, da.Array), ( f"chunks={chunks!r} did not produce a dask-backed connectivity, so this " "test would silently compare the eager path against itself" ) - - assert grid_eager.n_face == grid_lazy.n_face - assert grid_eager.n_node == grid_lazy.n_node - - fnc_eager = grid_eager.face_node_connectivity.values - fnc_lazy = grid_lazy.face_node_connectivity.values - - # Same set of unique nodes, independent of how each path numbered them. - nodes_eager = np.unique( - np.stack([grid_eager.node_lon.values, grid_eager.node_lat.values], 1), axis=0 - ) - nodes_lazy = np.unique( - np.stack([grid_lazy.node_lon.values, grid_lazy.node_lat.values], 1), axis=0 - ) - nt.assert_allclose(nodes_eager, nodes_lazy, atol=1e-10) - - # Same corners per face, in the same order. - nt.assert_allclose( - grid_eager.node_lon.values[fnc_eager], - grid_lazy.node_lon.values[fnc_lazy], - atol=1e-10, - ) - nt.assert_allclose( - grid_eager.node_lat.values[fnc_eager], - grid_lazy.node_lat.values[fnc_lazy], - atol=1e-10, - ) + assert grid_lazy == grid_eager def test_scrip_dask_lazy_dedup_matches_eager_radians(gridpath): @@ -192,25 +159,10 @@ def test_scrip_dask_lazy_dedup_matches_eager_radians(gridpath): radians, i.e. when ``_values_in_degrees`` converts a dask array rather than returning it untouched.""" grid_file = gridpath("scrip", "scrip_radians", "scrip_radians_grid.nc") - grid_eager = ux.open_grid(grid_file) grid_lazy = ux.open_grid(grid_file, chunks={"grid_size": 3}) - assert isinstance(grid_lazy._ds["face_node_connectivity"].data, da.Array) - assert grid_eager.n_node == grid_lazy.n_node - - fnc_eager = grid_eager.face_node_connectivity.values - fnc_lazy = grid_lazy.face_node_connectivity.values - nt.assert_allclose( - grid_eager.node_lon.values[fnc_eager], - grid_lazy.node_lon.values[fnc_lazy], - atol=1e-10, - ) - nt.assert_allclose( - grid_eager.node_lat.values[fnc_eager], - grid_lazy.node_lat.values[fnc_lazy], - atol=1e-10, - ) + assert grid_eager == grid_lazy def test_lookup_node_ids_preserves_input_order(): From 83313e20fa0008b1c7845cb4c691b0eff1dcedc7 Mon Sep 17 00:00:00 2001 From: Sam Evans <47793072+Sevans711@users.noreply.github.com> Date: Mon, 2 Nov 2026 17:28:59 -0500 Subject: [PATCH 2/4] test: scrip load is already deterministic (PR #1775 just didn't realize it.) --- test/io/test_scrip.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/io/test_scrip.py b/test/io/test_scrip.py index f598e66d1..9ca877f25 100644 --- a/test/io/test_scrip.py +++ b/test/io/test_scrip.py @@ -330,3 +330,18 @@ def test_open_multigrid_mask_active_value_per_grid_override(gridpath): assert grids["ocn"].n_face == expected_ocn assert grids["atm"].n_face == expected_atm + + +def test_scrip_load_is_deterministic(): + """Ensure loading the same SCRIP grid file repeatedly does not change results. + Regression test for issue #1738. + """ + examples = ["scrip-ne30pg2", "scrip-outCSne8"] + for example in examples: + for kw in (dict(), dict(chunks="auto")): + # load 3 times instead of 2, to drastically reduce the odds of + # non-deterministic results which "just happen" to align. + gridA = ux.tutorial.open_grid(example, **kw) + gridB = ux.tutorial.open_grid(example, **kw) + gridC = ux.tutorial.open_grid(example, **kw) + assert gridA == gridB == gridC From 1e14a7c5dafb29d4897dbd7f19bad7167b2aba05 Mon Sep 17 00:00:00 2001 From: Sam Evans <47793072+Sevans711@users.noreply.github.com> Date: Mon, 9 Nov 2026 12:36:40 -0500 Subject: [PATCH 3/4] test nontrivial chunks in deterministic scrip test --- test/io/test_scrip.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/io/test_scrip.py b/test/io/test_scrip.py index 9ca877f25..eca3f4e37 100644 --- a/test/io/test_scrip.py +++ b/test/io/test_scrip.py @@ -337,8 +337,9 @@ def test_scrip_load_is_deterministic(): Regression test for issue #1738. """ examples = ["scrip-ne30pg2", "scrip-outCSne8"] - for example in examples: - for kw in (dict(), dict(chunks="auto")): + n_nodes = [21600, 384] + for example, n_node in zip(examples, n_nodes): + for kw in (dict(), dict(chunks="auto"), dict(chunks={"grid_size": n_node//4})): # load 3 times instead of 2, to drastically reduce the odds of # non-deterministic results which "just happen" to align. gridA = ux.tutorial.open_grid(example, **kw) From 8601e878079da27db482cc1dea2f5a0ec64f7618 Mon Sep 17 00:00:00 2001 From: Sam Evans <47793072+Sevans711@users.noreply.github.com> Date: Mon, 9 Nov 2026 12:41:48 -0500 Subject: [PATCH 4/4] typo in previous commit --- test/io/test_scrip.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/io/test_scrip.py b/test/io/test_scrip.py index eca3f4e37..e1255066b 100644 --- a/test/io/test_scrip.py +++ b/test/io/test_scrip.py @@ -337,9 +337,9 @@ def test_scrip_load_is_deterministic(): Regression test for issue #1738. """ examples = ["scrip-ne30pg2", "scrip-outCSne8"] - n_nodes = [21600, 384] - for example, n_node in zip(examples, n_nodes): - for kw in (dict(), dict(chunks="auto"), dict(chunks={"grid_size": n_node//4})): + n_faces = [21600, 384] + for example, n_face in zip(examples, n_faces): + for kw in (dict(), dict(chunks="auto"), dict(chunks={"grid_size": n_face//4})): # load 3 times instead of 2, to drastically reduce the odds of # non-deterministic results which "just happen" to align. gridA = ux.tutorial.open_grid(example, **kw)