Skip to content

Commit d464bb3

Browse files
Regular grid fixes (#108)
* fixed data_vars bug in RegularGrid - removed RegularGrid2D -- doesn't appear to be different, and not used? * fix for bug with lat (or lon) not increasing ... * made test non-square * fixed polygon subsetting for regular grid. * added tests from gitHub PR from Nesar976 * added data_vars tests * Fixed bug with regular grid mis-identifying UGRIDS * added more tests for recognise()
1 parent 57c29f8 commit d464bb3

14 files changed

Lines changed: 424 additions & 147 deletions

tests/conftest.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# conftest: some configuration for the tests
22

3+
from pathlib import Path
4+
35
import pytest
46

57

@@ -35,3 +37,18 @@ def pytest_collection_modifyitems(config, items):
3537
# if envnames:
3638
# if item.config.getoption("-E") not in envnames:
3739
# pytest.skip(f"test requires env in {envnames!r}")
40+
41+
EXAMPLE_DATA = Path(__file__).parent / 'example_data'
42+
43+
UGRID_FILES = [EXAMPLE_DATA / 'SFBOFS_subset1.nc',
44+
EXAMPLE_DATA / 'small_ugrid_zero_based.nc',
45+
EXAMPLE_DATA / 'tris_and_bounds.nc',
46+
]
47+
48+
SGRID_FILES = [EXAMPLE_DATA / 'arakawa_c_test_grid.nc',
49+
]
50+
51+
RGRID_FILES = [EXAMPLE_DATA / '2D-rectangular_grid_wind.nc',
52+
EXAMPLE_DATA / 'rectangular_grid_decreasing.nc',
53+
EXAMPLE_DATA / 'AMSEAS-subset.nc',
54+
]
119 KB
Binary file not shown.
File renamed without changes.
119 KB
Binary file not shown.
File renamed without changes.
File renamed without changes.

tests/test_grids/test_regular_grid.py

Lines changed: 229 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -4,127 +4,274 @@
44

55
from pathlib import Path
66

7-
try:
8-
import fsspec
9-
except ImportError:
10-
fsspec = None
7+
import numpy as np
8+
import pytest
9+
10+
# only needed if you want to hit AWS servers.
11+
# try:
12+
# import fsspec
13+
# except ImportError:
14+
# fsspec = None
1115
import xarray as xr
1216

17+
from tests.conftest import RGRID_FILES, SGRID_FILES, UGRID_FILES
1318
from xarray_subset_grid.grids.regular_grid import RegularGrid
1419

15-
TEST_DATA = Path(__file__).parent.parent / "example_data"
20+
EXAMPLE_DATA = Path(__file__).parent.parent / "example_data"
1621

17-
TEST_FILE1 = TEST_DATA / "AMSEAS-subset.nc"
1822

1923
# NGOFS2_RGRID.nc is a small subset of the regridded NGOFS2 model.
2024

2125
# It was created by the "OFS subsetter"
2226

23-
24-
def test_recognise():
27+
@pytest.mark.parametrize("test_file", RGRID_FILES)
28+
def test_recognize(test_file):
2529
"""
2630
works for at least one file ...
2731
"""
28-
ds = xr.open_dataset(TEST_FILE1)
32+
ds = xr.open_dataset(test_file)
2933

3034
assert RegularGrid.recognize(ds)
3135

3236

33-
def test_recognise_not():
37+
@pytest.mark.parametrize("test_file", UGRID_FILES + SGRID_FILES)
38+
def test_recognize_not(test_file):
3439
"""
35-
should not recognise an SGrid
40+
should not recognize an SGrid
3641
"""
37-
ds = xr.open_dataset(TEST_DATA / "arakawa_c_test_grid.nc")
42+
ds = xr.open_dataset(test_file)
3843

3944
assert not RegularGrid.recognize(ds)
4045

4146

42-
#######
43-
# These from the ugrid tests -- need to be adapted
44-
#######
47+
def create_synthetic_rectangular_grid_dataset(decreasing=False):
48+
"""
49+
Create a synthetic dataset with regular grid.
4550
46-
# def test_grid_vars():
47-
# """
48-
# Check if the grid vars are defined properly
49-
# """
50-
# ds = xr.open_dataset(EXAMPLE_DATA / "SFBOFS_subset1.nc")
51+
Can be either decreasing or increasing in latitude
52+
"""
5153

52-
# ds = ugrid.assign_ugrid_topology(ds, **grid_topology)
54+
lon = np.linspace(-100, -80, 21)
55+
if decreasing:
56+
lat = np.linspace(50, 30, 21)
57+
else:
58+
lat = np.linspace(30, 50, 21)
5359

54-
# grid_vars = ds.xsg.grid_vars
60+
data = np.random.rand(21, 21)
5561

56-
# # ['mesh', 'nv', 'lon', 'lat', 'lonc', 'latc']
57-
# assert grid_vars == set(["mesh", "nv", "nbe", "lon", "lat", "lonc", "latc"])
62+
ds = xr.Dataset(
63+
data_vars={
64+
"temp": (("lat", "lon"), data),
65+
"salt": (("lat", "lon"), data),
66+
},
67+
coords={
68+
"lat": lat,
69+
"lon": lon,
70+
},
71+
)
72+
# Add cf attributes
73+
ds.lat.attrs = {"standard_name": "latitude", "units": "degrees_north"}
74+
ds.lon.attrs = {"standard_name": "longitude", "units": "degrees_east"}
75+
ds.temp.attrs = {"standard_name": "sea_water_temperature"}
5876

77+
return ds
5978

60-
# def test_data_vars():
61-
# """
62-
# Check if the grid vars are defined properly
6379

64-
# This is not currently working correctly!
65-
# """
66-
# ds = xr.open_dataset(EXAMPLE_DATA / "SFBOFS_subset1.nc")
67-
# ds = ugrid.assign_ugrid_topology(ds, **grid_topology)
6880

69-
# data_vars = ds.xsg.data_vars
70-
71-
# assert set(data_vars) == set(
72-
# [
73-
# "h",
74-
# "zeta",
75-
# "temp",
76-
# "salinity",
77-
# "u",
78-
# "v",
79-
# "uwind_speed",
80-
# "vwind_speed",
81-
# "wet_nodes",
82-
# "wet_cells",
83-
# ]
84-
# )
8581

82+
def test_grid_vars():
83+
"""
84+
Check if the grid vars are defined properly
85+
"""
86+
ds = xr.open_dataset(EXAMPLE_DATA / "AMSEAS-subset.nc")
8687

87-
# def test_extra_vars():
88-
# """
89-
# Check if the extra vars are defined properly
88+
grid_vars = ds.xsg.grid_vars
9089

91-
# This is not currently working correctly!
92-
# """
93-
# ds = xr.open_dataset(EXAMPLE_DATA / "SFBOFS_subset1.nc")
94-
# ds = ugrid.assign_ugrid_topology(ds, **grid_topology)
90+
# ['mesh', 'nv', 'lon', 'lat', 'lonc', 'latc']
91+
assert grid_vars == {'lat', 'lon'}
9592

96-
# extra_vars = ds.xsg.extra_vars
9793

98-
# print([*ds])
99-
# print(f"{extra_vars=}")
100-
# assert extra_vars == set(
101-
# [
102-
# "nf_type",
103-
# "Times",
104-
# ]
105-
# )
94+
def test_data_vars():
95+
"""
96+
Check if the data vars are defined properly
10697
98+
This is not currently working correctly!
10799
108-
# def test_coords():
109-
# ds = xr.open_dataset(EXAMPLE_DATA / "SFBOFS_subset1.nc")
110-
# ds = ugrid.assign_ugrid_topology(ds, **grid_topology)
100+
it finds extra stuff
101+
"""
102+
ds = xr.open_dataset(EXAMPLE_DATA / "AMSEAS-subset.nc")
103+
104+
data_vars = ds.xsg.data_vars
105+
106+
# the extra "time" variables are not using the grid
107+
# so they should not be listed as data_vars
108+
assert data_vars == {
109+
'water_w',
110+
'salinity',
111+
'surf_roughness',
112+
'surf_temp_flux',
113+
'water_v',
114+
# 'time_offset',
115+
'water_temp',
116+
'water_baro_v',
117+
'surf_atm_press',
118+
'surf_el',
119+
'surf_salt_flux',
120+
'water_u',
121+
'surf_wnd_stress_gridy',
122+
'water_baro_u',
123+
'watdep',
124+
'surf_solar_flux',
125+
# 'time1_run',
126+
'surf_wnd_stress_gridx',
127+
# 'time1_offset'
128+
}
129+
130+
# might not be needed if tested elsewhere.
131+
def test_data_vars2():
132+
"""
133+
redundant with above, by already written ...
134+
"""
135+
print("Testing data_vars error...")
136+
ds = create_synthetic_rectangular_grid_dataset()
137+
# Ensure it is recognized as a RegularGrid
138+
assert RegularGrid.recognize(ds)
139+
140+
# Access xsg accessor
141+
data_vars = ds.xsg.data_vars
142+
print(f"data_vars: {data_vars}")
143+
144+
assert data_vars == {'salt', 'temp'}
145+
146+
147+
def test_extra_vars():
148+
"""
149+
Check if the extra vars are defined properly
150+
"""
151+
ds = xr.open_dataset(EXAMPLE_DATA / "AMSEAS-subset.nc")
152+
153+
extra_vars = ds.xsg.extra_vars
154+
155+
# the extra "time" variables are not using the grid
156+
# so they should be listed as extra_vars
157+
assert extra_vars == {
158+
'time_offset',
159+
'time1_run',
160+
'time1_offset'
161+
}
162+
163+
def test_subset_to_bb():
164+
"""
165+
Not a complete test by any means, but the basics are there.
166+
167+
NOTE: it doesn't test if the variables got subset corectly ...
168+
169+
"""
170+
ds = xr.open_dataset(EXAMPLE_DATA / "2D-rectangular_grid_wind.nc")
171+
172+
print("initial bounds:", ds['lon'].data.min(),
173+
ds['lat'].data.min(),
174+
ds['lon'].data.max(),
175+
ds['lat'].data.max(),
176+
)
177+
178+
bbox = (-0.5, 0, 0.5, 0.5)
179+
180+
ds2 = ds.xsg.subset_bbox(bbox)
181+
182+
assert ds2['lat'].size == 15
183+
assert ds2['lon'].size == 29
184+
185+
new_bounds = (ds2['lon'].data.min(),
186+
ds2['lat'].data.min(),
187+
ds2['lon'].data.max(),
188+
ds2['lat'].data.max(),
189+
)
190+
print("new bounds:", new_bounds)
191+
assert new_bounds == bbox
192+
193+
def test_decreasing_latitude():
194+
"""
195+
Some datasets have the latitude or longitude decreasing: 10, 9, 8 etc.
196+
e.g the NOAA GFS met model
197+
198+
subsetting should still work
199+
200+
"""
201+
ds = xr.open_dataset(EXAMPLE_DATA / "rectangular_grid_decreasing.nc")
202+
203+
print("initial bounds:", ds['lon'].data.min(),
204+
ds['lat'].data.min(),
205+
ds['lon'].data.max(),
206+
ds['lat'].data.max(),
207+
)
208+
209+
bbox = (-0.5, 0, 0.5, 0.5)
210+
211+
ds2 = ds.xsg.subset_bbox(bbox)
212+
213+
assert ds2['lat'].size == 15
214+
assert ds2['lon'].size == 29
215+
216+
new_bounds = (ds2['lon'].data.min(),
217+
ds2['lat'].data.min(),
218+
ds2['lon'].data.max(),
219+
ds2['lat'].data.max(),
220+
)
221+
print("new bounds:", new_bounds)
222+
assert new_bounds == bbox
223+
224+
def test_decreasing_coords():
225+
"""
226+
Redundant with above, but already written ...
227+
"""
228+
print("\nTesting decreasing coordinates support...")
229+
ds = create_synthetic_rectangular_grid_dataset(decreasing=True)
230+
# assert RegularGrid.recognize(ds)
231+
232+
# bbox: (min_lon, min_lat, max_lon, max_lat)
233+
bbox = (-95, 35, -85, 45)
234+
235+
subset = ds.xsg.subset_bbox(bbox)
236+
print(f"Subset size: {subset.sizes}")
237+
238+
# Check if subset has data
239+
assert subset.sizes["lat"] > 0
240+
assert subset.sizes["lon"] > 0
241+
242+
def test_subset_polygon():
243+
"""
244+
Not a complete test by any means, but the basics are there.
245+
246+
NOTE: it doesn't test if the variables got subset corectly ...
247+
248+
"""
249+
ds = xr.open_dataset(EXAMPLE_DATA / "2D-rectangular_grid_wind.nc")
250+
251+
print("initial bounds:", ds['lon'].data.min(),
252+
ds['lat'].data.min(),
253+
ds['lon'].data.max(),
254+
ds['lat'].data.max(),
255+
)
256+
257+
poly = [(-0.5, 0.0), (0.0, 0.5), (0.5, 0.5), (0.5, 0.0), (0, 0.0)]
258+
# this poly has this bounding box:
259+
# bbox = (-0.5, 0, 0.5, 0.5)
260+
# so results should be the same as the bbox tests
261+
262+
ds2 = ds.xsg.subset_polygon(poly)
263+
264+
assert ds2['lat'].size == 15
265+
assert ds2['lon'].size == 29
266+
267+
new_bounds = (ds2['lon'].data.min(),
268+
ds2['lat'].data.min(),
269+
ds2['lon'].data.max(),
270+
ds2['lat'].data.max(),
271+
)
272+
print("new bounds:", new_bounds)
273+
assert new_bounds == (-0.5, 0, 0.5, 0.5)
111274

112-
# coords = ds.xsg.coords
113-
114-
# print(f"{coords=}")
115-
# print(f"{ds.coords=}")
116-
117-
# assert set(coords) == set(
118-
# [
119-
# "lon",
120-
# "lat",
121-
# "lonc",
122-
# "latc",
123-
# "time",
124-
# "siglay",
125-
# "siglev",
126-
# ]
127-
# )
128275

129276

130277
# def test_vertical_levels():

0 commit comments

Comments
 (0)