Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions tests/test_accessor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import numpy as np
import pytest
import xarray as xr

import xarray_subset_grid.accessor # noqa: F401 -- register accessor


def test_accessor_warns_when_no_grid_recognized():
ds = xr.Dataset()
with pytest.warns(UserWarning, match="no grid type"):
accessor = ds.xsg
assert accessor.grid is None


def test_subset_polygon_and_bbox_return_none_without_grid():
ds = xr.Dataset()
poly = np.array(
[
[-72.0, 41.0],
[-70.0, 41.0],
[-71.0, 39.0],
[-72.0, 41.0],
]
)
with pytest.warns(UserWarning, match="no grid type"):
assert ds.xsg.subset_polygon(poly) is None
assert ds.xsg.subset_bbox((-72, 39, -70, 41)) is None


def test_subset_vars_passthrough_without_grid():
ds = xr.Dataset({"a": (("x",), [1, 2, 3])})
with pytest.warns(UserWarning, match="no grid type"):
out = ds.xsg.subset_vars(["a"])
# Without a recognized grid, subset_vars returns the dataset unchanged.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm -- this might take a bit of thought -- I think it should simply raise an Error.

There is no use-case for subset_vars without a grid, and having it be do-nothing might be more surprising.

assert "a" in out.data_vars


def test_has_vertical_levels_false_without_grid():
ds = xr.Dataset()
with pytest.warns(UserWarning, match="no grid type"):
assert ds.xsg.has_vertical_levels is False
1 change: 1 addition & 0 deletions tests/test_grids/test_sgrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from xarray_subset_grid.grids.sgrid import _get_location_info_from_topology

# open dataset as zarr object using fsspec reference file system and xarray
zarr__version__ = 0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to check the status of zarr3 and FSpec AWS -- this may be a non_issue now.

try:
import fsspec
import zarr
Expand Down
77 changes: 77 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import os
from datetime import datetime

import cftime
import numpy as np
import pytest
import xarray as xr

from tests.conftest import EXAMPLE_DATA
from xarray_subset_grid import utils as xsg_utils
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should either import the whole utils module, or import a bunch of names, but not both.

from xarray_subset_grid.utils import (
asdatetime,
compute_2d_subset_mask,
format_bytes,
normalize_bbox_x_coords,
normalize_polygon_x_coords,
ray_tracing_numpy,
Expand Down Expand Up @@ -125,3 +133,72 @@ def test_ray_tracing_numpy():
result = ray_tracing_numpy(points[:, 0], points[:, 1], poly)

assert np.array_equal(result, [False, True, False])


@pytest.mark.parametrize(
"num, unit",
[
(512, "bytes"),
(2048, "KB"),
(3 * 1024**2, "MB"),
],
)
def test_format_bytes(num, unit):
assert unit in format_bytes(num)


def test_asdatetime_none():
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks! I actually copied that ober from another project and forgot to bring the tests over -- so this is nice.

assert asdatetime(None) is None


def test_asdatetime_datetime_passthrough():
dt = datetime(2020, 6, 15, 12, 30, 0)
assert asdatetime(dt) is dt


def test_asdatetime_cftime_passthrough():
dt = cftime.datetime(2020, 6, 15, 12)
assert asdatetime(dt) is dt


def test_asdatetime_parse_string():
dt = asdatetime("2020-06-15T12:30:00")
assert dt.year == 2020 and dt.month == 6 and dt.day == 15


def test_compute_2d_subset_mask_all_inside():
ny, nx = 5, 5
lat = np.linspace(40.0, 44.0, ny)
lon = np.linspace(-74.0, -70.0, nx)
lat2d, lon2d = np.meshgrid(lat, lon, indexing="ij")
lat_da = xr.DataArray(lat2d, dims=("y", "x"))
lon_da = xr.DataArray(lon2d, dims=("y", "x"))
poly = np.array([(-75.0, 39.0), (-69.0, 39.0), (-69.0, 45.0), (-75.0, 45.0)])
mask = compute_2d_subset_mask(lat_da, lon_da, poly)
assert mask.dims == ("y", "x")
assert bool(mask.all())
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you don't need the bool() call here -- .all() returns a numpy bool, which works fine. (I think assert probably calls bool() anyway).



def test_compute_2d_subset_mask_partial():
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be nice to have a simple test case that checks that the mask is actually correct.

the actual point-in-polygon code should be tested elswhere, but good to know it's being applied properly -- at least on one example.

ny, nx = 7, 7
lat = np.linspace(40.0, 46.0, ny)
lon = np.linspace(-74.0, -68.0, nx)
lat2d, lon2d = np.meshgrid(lat, lon, indexing="ij")
lat_da = xr.DataArray(lat2d, dims=("y", "x"))
lon_da = xr.DataArray(lon2d, dims=("y", "x"))
# Small polygon over the south-west corner only
poly = np.array([(-74.5, 40.0), (-73.0, 40.0), (-73.0, 41.0), (-74.5, 41.0)])
mask = compute_2d_subset_mask(lat_da, lon_da, poly)
assert mask.dims == ("y", "x")
assert bool(mask.any())
assert not bool(mask.all())


def test_assign_ugrid_topology_utils_deprecation_wrapper():
nc = EXAMPLE_DATA / "SFBOFS_subset1.nc"
if not nc.is_file():
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should either make sure it's there or not -- semi-silently skipping is not good.

pytest.skip("example NetCDF not present")
ds = xr.open_dataset(nc)
with pytest.warns(DeprecationWarning, match="assign_ugrid_topology"):
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, let's jsut get rid of that Depricated Function -- and make sure that none of the examples use it!

#129

ds2 = xsg_utils.assign_ugrid_topology(ds, face_node_connectivity="nv")
assert "mesh" in ds2.variables
9 changes: 6 additions & 3 deletions xarray_subset_grid/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ def normalize_polygon_x_coords(x, poly):
If the x coords are between 0 and 180 (i.e. both will work), the polygon
is not changed.

NOTE: polygon is normalized in place!
NOTE: ``poly`` is normalized in place when it is already an ndarray;
a copy is made when ``poly`` is a sequence.

Args:
x (np.array): x-coordinates of the vertices
poly (np.array): polygon vertices
"""
poly = np.asarray(poly)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, thanks!

x_min, x_max = x.min(), x.max()

poly_x = poly[:, 0]
Expand Down Expand Up @@ -97,10 +99,11 @@ def ray_tracing_numpy(x, y, poly):
# this placeholder for backwards compatibility for a brief period
def assign_ugrid_topology(*args, **kwargs):
warnings.warn(
DeprecationWarning,
"The function `assign_grid_topology` has been moved to the "
"The function `assign_ugrid_topology` has been moved to the "
"`grids.ugrid` module. It will not be able to be called from "
"the utils `module` in the future.",
DeprecationWarning,
stacklevel=2,
)
from .grids.ugrid import assign_ugrid_topology

Expand Down
Loading