Skip to content

Commit b958f0d

Browse files
author
Wyatt Sieminski
committed
Parameterized struc/unstruc tests and added unstructured advection test
1 parent f0d3189 commit b958f0d

1 file changed

Lines changed: 40 additions & 29 deletions

File tree

tests/test_windowed_array.py

Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,14 @@ def test_windowed_isel_backward_clock_loads_once_and_evicts():
7171
assert win.loads == ntime # each time level read exactly once, going backward
7272
assert max_cache <= 2 # only the bracketing levels resident
7373

74-
def test_structured_windowed_arrays_wraps_dask_but_not_numpy():
75-
ds = simple_UV_dataset(mesh="flat")
76-
fset_np = FieldSet.from_sgrid_conventions(ds, mesh="flat")
77-
fset_dk = FieldSet.from_sgrid_conventions(ds.chunk({"time": 1}), mesh="flat")
74+
@pytest.mark.parametrize("fset_convention, ds",
75+
[(FieldSet.from_sgrid_conventions, simple_UV_dataset(mesh="flat")),
76+
(FieldSet.from_ugrid_conventions, _ux_constant_flow_face_centered_2D())],
77+
ids=["structured", "unstructured"]
78+
)
79+
def test_windowed_arrays_wraps_dask_but_not_numpy(fset_convention: callable, ds: xr.Dataset):
80+
fset_np = fset_convention(ds, mesh="flat")
81+
fset_dk = fset_convention(ds.chunk({"time": 1}), mesh="flat")
7882

7983
# construction is never windowing -- it is opt-in via the fieldset method
8084
assert not isinstance(fset_np.U.data, WindowedArray)
@@ -91,29 +95,13 @@ def test_structured_windowed_arrays_wraps_dask_but_not_numpy():
9195
assert fset_dk.U.data.dims == fset_np.U.data.dims
9296
assert fset_dk.U.data.shape == fset_np.U.data.shape
9397

94-
def test_unstructured_windowed_arrays_wraps_dask_but_not_numpy():
95-
ds = _ux_constant_flow_face_centered_2D()
96-
fset_np = FieldSet.from_ugrid_conventions(ds, mesh="flat")
97-
fset_dk = FieldSet.from_ugrid_conventions(ds.chunk({"time": 1}), mesh="flat")
98-
99-
# construction is never windowing -- it is opt-in via the fieldset method
100-
assert not isinstance(fset_np.U.data, WindowedArray)
101-
assert not isinstance(fset_dk.U.data, WindowedArray)
102-
assert isinstance(fset_dk.U.data.data, da.Array) # chunked input stays lazy (dask-backed)
103-
104-
assert fset_np.to_windowed_arrays() is fset_np # chainable
105-
fset_dk.to_windowed_arrays()
106-
107-
# numpy-backed field is left eager; dask-backed field gets wrapped
108-
assert not isinstance(fset_np.U.data, WindowedArray)
109-
assert isinstance(fset_dk.U.data, WindowedArray)
110-
# transparency: forwarded attributes still behave like the DataArray
111-
assert fset_dk.U.data.dims == fset_np.U.data.dims
112-
assert fset_dk.U.data.shape == fset_np.U.data.shape
113-
114-
def test_to_windowed_arrays_is_idempotent_and_forwards_max_levels():
115-
ds = simple_UV_dataset(mesh="flat")
116-
fs = FieldSet.from_sgrid_conventions(ds.chunk({"time": 1}), mesh="flat")
98+
@pytest.mark.parametrize("fset_convention, ds",
99+
[(FieldSet.from_sgrid_conventions, simple_UV_dataset(mesh="flat")),
100+
(FieldSet.from_ugrid_conventions, _ux_constant_flow_face_centered_2D())],
101+
ids=["structured", "unstructured"]
102+
)
103+
def test_to_windowed_arrays_is_idempotent_and_forwards_max_levels(fset_convention: callable, ds: xr.Dataset):
104+
fs = fset_convention(ds.chunk({"time": 1}), mesh="flat")
117105

118106
fs.to_windowed_arrays(max_levels=3)
119107
first = fs.U.data
@@ -160,10 +148,10 @@ def test_maybe_windowed_passthrough_for_non_time_leading():
160148

161149
@pytest.mark.parametrize("mesh", ["flat", "spherical"])
162150
@pytest.mark.parametrize("dt_minutes", [15, -15], ids=["forward", "backward"])
163-
def test_dask_advection_matches_numpy(mesh, dt_minutes):
151+
def test_dask_advection_matches_numpy_on_structured_grids(mesh, dt_minutes):
164152
"""An identical advection must give identical trajectories whether the field
165153
is numpy-backed or dask-backed (windowed) -- for both forward (dt > 0) and
166-
backward (dt < 0) integration.
154+
backward (dt < 0) integration on structured grids.
167155
"""
168156
ds = simple_UV_dataset(mesh=mesh)
169157
ds["U"].data[:] = 1.0 # steady zonal flow -> in-bounds, deterministic
@@ -181,3 +169,26 @@ def run(chunked):
181169
x_dk, y_dk = run(True)
182170
np.testing.assert_allclose(x_dk, x_np, atol=1e-9)
183171
np.testing.assert_allclose(y_dk, y_np, atol=1e-9)
172+
173+
@pytest.mark.parametrize("mesh", ["flat", "spherical"])
174+
@pytest.mark.parametrize("dt_minutes", [15, -15], ids=["forward", "backward"])
175+
def test_dask_advection_matches_numpy_on_unstructured_grids(mesh, dt_minutes):
176+
"""An identical advection must give identical trajectories whether the field
177+
is numpy-backed or dask-backed (windowed) -- for both forward (dt > 0) and
178+
backward (dt < 0) integration on unstructured grids.
179+
"""
180+
ds = _ux_constant_flow_face_centered_2D()
181+
182+
def run(chunked):
183+
d = ds.chunk({"time": 1}) if chunked else ds
184+
fs = FieldSet.from_ugrid_conventions(d, mesh=mesh)
185+
if chunked:
186+
fs.to_windowed_arrays()
187+
pset = ParticleSet(fs, x=10*np.ones(10), y=np.linspace(5, 15, 10))
188+
pset.execute(AdvectionRK2, runtime=3600, dt=np.timedelta64(dt_minutes, "m"))
189+
return np.array(pset.x), np.array(pset.y)
190+
191+
x_np, y_np = run(False)
192+
x_dk, y_dk = run(True)
193+
np.testing.assert_allclose(x_dk, x_np, atol=1e-9)
194+
np.testing.assert_allclose(y_dk, y_np, atol=1e-9)

0 commit comments

Comments
 (0)