|
4 | 4 |
|
5 | 5 | from pathlib import Path |
6 | 6 |
|
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 |
11 | 15 | import xarray as xr |
12 | 16 |
|
| 17 | +from tests.conftest import RGRID_FILES, SGRID_FILES, UGRID_FILES |
13 | 18 | from xarray_subset_grid.grids.regular_grid import RegularGrid |
14 | 19 |
|
15 | | -TEST_DATA = Path(__file__).parent.parent / "example_data" |
| 20 | +EXAMPLE_DATA = Path(__file__).parent.parent / "example_data" |
16 | 21 |
|
17 | | -TEST_FILE1 = TEST_DATA / "AMSEAS-subset.nc" |
18 | 22 |
|
19 | 23 | # NGOFS2_RGRID.nc is a small subset of the regridded NGOFS2 model. |
20 | 24 |
|
21 | 25 | # It was created by the "OFS subsetter" |
22 | 26 |
|
23 | | - |
24 | | -def test_recognise(): |
| 27 | +@pytest.mark.parametrize("test_file", RGRID_FILES) |
| 28 | +def test_recognize(test_file): |
25 | 29 | """ |
26 | 30 | works for at least one file ... |
27 | 31 | """ |
28 | | - ds = xr.open_dataset(TEST_FILE1) |
| 32 | + ds = xr.open_dataset(test_file) |
29 | 33 |
|
30 | 34 | assert RegularGrid.recognize(ds) |
31 | 35 |
|
32 | 36 |
|
33 | | -def test_recognise_not(): |
| 37 | +@pytest.mark.parametrize("test_file", UGRID_FILES + SGRID_FILES) |
| 38 | +def test_recognize_not(test_file): |
34 | 39 | """ |
35 | | - should not recognise an SGrid |
| 40 | + should not recognize an SGrid |
36 | 41 | """ |
37 | | - ds = xr.open_dataset(TEST_DATA / "arakawa_c_test_grid.nc") |
| 42 | + ds = xr.open_dataset(test_file) |
38 | 43 |
|
39 | 44 | assert not RegularGrid.recognize(ds) |
40 | 45 |
|
41 | 46 |
|
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. |
45 | 50 |
|
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 | + """ |
51 | 53 |
|
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) |
53 | 59 |
|
54 | | -# grid_vars = ds.xsg.grid_vars |
| 60 | + data = np.random.rand(21, 21) |
55 | 61 |
|
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"} |
58 | 76 |
|
| 77 | + return ds |
59 | 78 |
|
60 | | -# def test_data_vars(): |
61 | | -# """ |
62 | | -# Check if the grid vars are defined properly |
63 | 79 |
|
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) |
68 | 80 |
|
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 | | -# ) |
85 | 81 |
|
| 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") |
86 | 87 |
|
87 | | -# def test_extra_vars(): |
88 | | -# """ |
89 | | -# Check if the extra vars are defined properly |
| 88 | + grid_vars = ds.xsg.grid_vars |
90 | 89 |
|
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'} |
95 | 92 |
|
96 | | -# extra_vars = ds.xsg.extra_vars |
97 | 93 |
|
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 |
106 | 97 |
|
| 98 | + This is not currently working correctly! |
107 | 99 |
|
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) |
111 | 274 |
|
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 | | -# ) |
128 | 275 |
|
129 | 276 |
|
130 | 277 | # def test_vertical_levels(): |
|
0 commit comments