Skip to content

Commit 16897c5

Browse files
authored
Merge branch 'main' into gradient
2 parents 7a02f7e + c9a4ead commit 16897c5

File tree

5 files changed

+729
-1
lines changed

5 files changed

+729
-1
lines changed

test/test_accessors.py

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
"""
2+
Test module for UxArray accessor functionality (groupby, resample, rolling, etc.).
3+
4+
This module tests that accessor methods properly preserve uxgrid attributes
5+
and return UxDataArray/UxDataset objects.
6+
"""
7+
8+
import os
9+
from pathlib import Path
10+
import numpy as np
11+
import pandas as pd
12+
import xarray as xr
13+
import uxarray as ux
14+
from uxarray import UxDataset
15+
import pytest
16+
17+
18+
current_path = Path(os.path.dirname(os.path.realpath(__file__)))
19+
20+
gridfile_ne30 = current_path / "meshfiles" / "ugrid" / "outCSne30" / "outCSne30.ug"
21+
dsfile_var2_ne30 = current_path / "meshfiles" / "ugrid" / "outCSne30" / "outCSne30_var2.nc"
22+
gridfile_geoflow = current_path / "meshfiles" / "ugrid" / "geoflow-small" / "grid.nc"
23+
dsfile_v1_geoflow = current_path / "meshfiles" / "ugrid" / "geoflow-small" / "v1.nc"
24+
mpas_ds_path = current_path / 'meshfiles' / "mpas" / "QU" / 'mesh.QU.1920km.151026.nc'
25+
26+
27+
def test_groupby_preserves_uxgrid():
28+
"""Test that groupby operations preserve the uxgrid attribute."""
29+
# Create a dataset from a file
30+
uxds = ux.open_dataset(mpas_ds_path, mpas_ds_path)
31+
original_grid = uxds.uxgrid
32+
33+
# Create bins from latitude values (extract data explicitly)
34+
lat_bins = (uxds.latCell > 0).astype(int).values
35+
36+
# Add the bins as a coordinate
37+
uxds = uxds.assign_coords({"lat_bins": ("n_face", lat_bins)})
38+
39+
# Test DataArray groupby preserves uxgrid
40+
da_result = uxds.latCell.groupby(uxds.lat_bins).mean()
41+
assert hasattr(da_result, "uxgrid")
42+
assert da_result.uxgrid is not None
43+
44+
# Test Dataset groupby preserves uxgrid
45+
ds_result = uxds.groupby(uxds.lat_bins).mean()
46+
assert hasattr(ds_result, "uxgrid")
47+
assert ds_result.uxgrid is not None
48+
assert ds_result.uxgrid == original_grid
49+
50+
def test_groupby_bins_preserves_uxgrid():
51+
"""Test that groupby_bins operations preserve the uxgrid attribute."""
52+
# Create a dataset from a file
53+
uxds = ux.open_dataset(mpas_ds_path, mpas_ds_path)
54+
original_grid = uxds.uxgrid
55+
56+
# Create bins from latitude values (extract data explicitly)
57+
lat_bins = [-90, -45, 0, 45, 90]
58+
59+
# Test DataArray groupby_bins preserves uxgrid
60+
da_result = uxds.latCell.groupby_bins(uxds.latCell, bins=lat_bins).mean()
61+
assert hasattr(da_result, "uxgrid")
62+
assert da_result.uxgrid is not None
63+
64+
# Test Dataset groupby_bins preserves uxgrid
65+
ds_result = uxds.groupby_bins(uxds.latCell, bins=lat_bins).mean()
66+
assert hasattr(ds_result, "uxgrid")
67+
assert ds_result.uxgrid is not None
68+
assert ds_result.uxgrid == original_grid
69+
70+
71+
72+
def test_resample_preserves_uxgrid_and_reduces_time():
73+
"""Test that resample operations preserve uxgrid and reduce time dimension."""
74+
75+
# Create a simple test with only time dimension
76+
times = pd.date_range("2000-01-01", periods=12, freq="D")
77+
temp_data = np.random.rand(12)
78+
79+
# Create a simple xarray Dataset
80+
xr_ds = xr.Dataset(
81+
{"temperature": ("time", temp_data)},
82+
coords={"time": times}
83+
)
84+
85+
# Open the minimal dataset with a real grid
86+
# Use existing test file we know works
87+
uxgrid = ux.open_grid(gridfile_ne30)
88+
89+
# Create a UxDataset with this grid
90+
uxds = ux.UxDataset(xr_ds, uxgrid=uxgrid)
91+
92+
# Test the resample method directly
93+
result = uxds.temperature.resample(time="1W").mean()
94+
95+
# Test assertions
96+
assert hasattr(result, "uxgrid"), "uxgrid not preserved on resample"
97+
assert result.uxgrid == uxds.uxgrid, "uxgrid not equal after resample"
98+
assert len(result.time) < len(uxds.time), "time dimension not reduced"
99+
100+
def test_resample_preserves_uxgrid():
101+
"""Test that resample preserves the uxgrid attribute."""
102+
103+
# Create a simple dataset with a time dimension
104+
times = pd.date_range("2000-01-01", periods=12, freq="D")
105+
data = np.random.rand(12)
106+
107+
# Create a simple xarray Dataset
108+
ds = xr.Dataset(
109+
{"temperature": ("time", data)},
110+
coords={"time": times}
111+
)
112+
113+
# Create a UxDataset with a real grid
114+
uxds = ux.open_dataset(gridfile_ne30, gridfile_ne30)
115+
original_uxgrid = uxds.uxgrid
116+
117+
# Create a new UxDataset with our time data and the real grid
118+
uxds_time = ux.UxDataset(ds, uxgrid=original_uxgrid)
119+
120+
# Test DataArray resample preserves uxgrid
121+
da_result = uxds_time.temperature.resample(time="1W").mean()
122+
assert hasattr(da_result, "uxgrid"), "uxgrid not preserved on DataArray resample"
123+
assert da_result.uxgrid is original_uxgrid, "uxgrid not identical after DataArray resample"
124+
125+
# Test Dataset resample preserves uxgrid
126+
ds_result = uxds_time.resample(time="1W").mean()
127+
assert hasattr(ds_result, "uxgrid"), "uxgrid not preserved on Dataset resample"
128+
assert ds_result.uxgrid is original_uxgrid, "uxgrid not identical after Dataset resample"
129+
130+
131+
def test_resample_reduces_time_dimension():
132+
"""Test that resample properly reduces the time dimension."""
133+
134+
# Create dataset with daily data for a year
135+
times = pd.date_range("2000-01-01", periods=365, freq="D")
136+
data = np.random.rand(365)
137+
138+
# Create a simple xarray Dataset
139+
ds = xr.Dataset(
140+
{"temperature": ("time", data)},
141+
coords={"time": times}
142+
)
143+
144+
# Create a UxDataset
145+
uxds = ux.UxDataset(ds, uxgrid=ux.open_grid(gridfile_ne30))
146+
147+
# Test monthly resampling reduces from 365 days to 12 months
148+
monthly = uxds.resample(time="1M").mean()
149+
assert "time" in monthly.dims, "time dimension missing after resample"
150+
assert monthly.dims["time"] < uxds.dims["time"], "time dimension not reduced"
151+
assert monthly.dims["time"] <= 12, "monthly resampling should give 12 or fewer time points"
152+
153+
154+
def test_resample_with_cftime():
155+
"""Test that resample works with cftime objects."""
156+
157+
try:
158+
import cftime
159+
except ImportError:
160+
pytest.skip("cftime package not available")
161+
162+
# Create a dataset with cftime DatetimeNoLeap objects
163+
times = [cftime.DatetimeNoLeap(2000, month, 15) for month in range(1, 13)]
164+
data = np.random.rand(12)
165+
166+
# Create a simple xarray Dataset with cftime
167+
ds = xr.Dataset(
168+
{"temperature": ("time", data)},
169+
coords={"time": times}
170+
)
171+
172+
# Create a UxDataset
173+
uxds = ux.UxDataset(ds, uxgrid=ux.open_grid(gridfile_ne30))
174+
175+
# Test that quarterly resampling works with cftime
176+
quarterly = uxds.resample(time="Q").mean()
177+
assert hasattr(quarterly, "uxgrid"), "uxgrid not preserved with cftime resampling"
178+
assert "time" in quarterly.dims, "time dimension missing after cftime resample"
179+
assert quarterly.dims["time"] < uxds.dims["time"], "time dimension not reduced with cftime"
180+
181+
182+
def test_rolling_preserves_uxgrid():
183+
"""Test that rolling operations preserve the uxgrid attribute."""
184+
185+
# Create a dataset with time dimension
186+
times = pd.date_range("2000-01-01", periods=30, freq="D")
187+
data = np.random.rand(30)
188+
189+
# Create a simple xarray Dataset
190+
ds = xr.Dataset(
191+
{"temperature": ("time", data)},
192+
coords={"time": times}
193+
)
194+
195+
# Create a UxDataset with a real grid
196+
uxds = ux.UxDataset(ds, uxgrid=ux.open_grid(gridfile_ne30))
197+
original_uxgrid = uxds.uxgrid
198+
199+
# Test DataArray rolling preserves uxgrid
200+
da_rolling = uxds.temperature.rolling(time=7)
201+
da_result = da_rolling.mean()
202+
assert hasattr(da_result, "uxgrid"), "uxgrid not preserved on DataArray rolling"
203+
assert da_result.uxgrid is original_uxgrid, "uxgrid not identical after DataArray rolling"
204+
205+
# Test Dataset rolling preserves uxgrid
206+
ds_rolling = uxds.rolling(time=7)
207+
ds_result = ds_rolling.mean()
208+
assert hasattr(ds_result, "uxgrid"), "uxgrid not preserved on Dataset rolling"
209+
assert ds_result.uxgrid is original_uxgrid, "uxgrid not identical after Dataset rolling"
210+
211+
# Test that rolling window operations work correctly
212+
assert len(da_result.time) == len(uxds.time), "rolling should preserve time dimension length"
213+
assert not np.isnan(da_result.values[6:]).any(), "rolling mean should have valid values after window size"
214+
215+
216+
def test_coarsen_preserves_uxgrid():
217+
"""Test that coarsen operations preserve the uxgrid attribute."""
218+
219+
# Create a dataset with time dimension (multiple of coarsen factor)
220+
times = pd.date_range("2000-01-01", periods=24, freq="D")
221+
data = np.random.rand(24)
222+
223+
# Create a simple xarray Dataset
224+
ds = xr.Dataset(
225+
{"temperature": ("time", data)},
226+
coords={"time": times}
227+
)
228+
229+
# Create a UxDataset with a real grid
230+
uxds = ux.UxDataset(ds, uxgrid=ux.open_grid(gridfile_ne30))
231+
original_uxgrid = uxds.uxgrid
232+
233+
# Test DataArray coarsen preserves uxgrid
234+
da_coarsen = uxds.temperature.coarsen(time=3)
235+
da_result = da_coarsen.mean()
236+
assert hasattr(da_result, "uxgrid"), "uxgrid not preserved on DataArray coarsen"
237+
assert da_result.uxgrid is original_uxgrid, "uxgrid not identical after DataArray coarsen"
238+
239+
# Test Dataset coarsen preserves uxgrid
240+
ds_coarsen = uxds.coarsen(time=3)
241+
ds_result = ds_coarsen.mean()
242+
assert hasattr(ds_result, "uxgrid"), "uxgrid not preserved on Dataset coarsen"
243+
assert ds_result.uxgrid is original_uxgrid, "uxgrid not identical after Dataset coarsen"
244+
245+
# Test that coarsen reduces dimension correctly
246+
assert len(da_result.time) == 8, "coarsen by 3 should reduce 24 points to 8"
247+
assert ds_result.dims["time"] == 8, "coarsen should reduce time dimension"
248+
249+
250+
def test_weighted_preserves_uxgrid():
251+
"""Test that weighted operations preserve the uxgrid attribute."""
252+
253+
# Create a dataset with time and face dimensions
254+
times = pd.date_range("2000-01-01", periods=10, freq="D")
255+
256+
# Open a real dataset to get face dimension
257+
uxds_base = ux.open_dataset(gridfile_ne30, dsfile_var2_ne30)
258+
n_face = uxds_base.dims["n_face"]
259+
260+
# Create data with time and face dimensions
261+
temp_data = np.random.rand(10, n_face)
262+
weights_data = np.random.rand(10) # weights along time
263+
264+
# Create a Dataset with both variables
265+
ds = xr.Dataset(
266+
{
267+
"temperature": (["time", "n_face"], temp_data),
268+
"weights": ("time", weights_data)
269+
},
270+
coords={"time": times}
271+
)
272+
273+
# Create a UxDataset
274+
uxds = ux.UxDataset(ds, uxgrid=uxds_base.uxgrid)
275+
original_uxgrid = uxds.uxgrid
276+
277+
# Test DataArray weighted preserves uxgrid
278+
da_weighted = uxds.temperature.weighted(uxds.weights)
279+
da_result = da_weighted.mean("time")
280+
assert hasattr(da_result, "uxgrid"), "uxgrid not preserved on DataArray weighted"
281+
assert da_result.uxgrid is original_uxgrid, "uxgrid not identical after DataArray weighted"
282+
283+
# Test Dataset weighted preserves uxgrid
284+
ds_weighted = uxds.weighted(uxds.weights)
285+
ds_result = ds_weighted.mean("time")
286+
assert hasattr(ds_result, "uxgrid"), "uxgrid not preserved on Dataset weighted"
287+
assert ds_result.uxgrid is original_uxgrid, "uxgrid not identical after Dataset weighted"
288+
289+
# Test that weighted operations reduce dimensions correctly
290+
assert "time" not in da_result.dims, "weighted mean over time should remove time dimension"
291+
assert "n_face" in da_result.dims, "face dimension should be preserved"
292+
assert da_result.shape == (n_face,), "result should only have face dimension"
293+
294+
295+
def test_cumulative_preserves_uxgrid():
296+
"""Test that cumulative operations preserve the uxgrid attribute."""
297+
298+
# Create a dataset with time dimension
299+
times = pd.date_range("2000-01-01", periods=10, freq="D")
300+
data = np.random.rand(10)
301+
302+
# Create a simple xarray Dataset
303+
ds = xr.Dataset(
304+
{"temperature": ("time", data)},
305+
coords={"time": times}
306+
)
307+
308+
# Create a UxDataset with a real grid
309+
uxds = ux.UxDataset(ds, uxgrid=ux.open_grid(gridfile_ne30))
310+
original_uxgrid = uxds.uxgrid
311+
312+
# Test DataArray cumulative preserves uxgrid
313+
da_cumulative = uxds.temperature.cumulative("time")
314+
da_result = da_cumulative.sum()
315+
assert hasattr(da_result, "uxgrid"), "uxgrid not preserved on DataArray cumulative"
316+
assert da_result.uxgrid is original_uxgrid, "uxgrid not identical after DataArray cumulative"
317+
318+
# Test Dataset cumulative preserves uxgrid
319+
ds_cumulative = uxds.cumulative("time")
320+
ds_result = ds_cumulative.sum()
321+
assert hasattr(ds_result, "uxgrid"), "uxgrid not preserved on Dataset cumulative"
322+
assert ds_result.uxgrid is original_uxgrid, "uxgrid not identical after Dataset cumulative"
323+
324+
# Test that cumulative preserves dimension length
325+
assert len(da_result.time) == len(uxds.time), "cumulative should preserve time dimension length"

0 commit comments

Comments
 (0)