Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ._copy method and _bounds attr to DataArrayBoundsAccessor #98

Merged
merged 1 commit into from
Aug 25, 2021
Merged
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
31 changes: 0 additions & 31 deletions tests/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,37 +144,6 @@
dims=["time", "lat", "lon"],
)

# Special cases, after copying bounds from parent Dataset to data variable
# using xCDAT.
ts_with_bnds_from_parent_cf = xr.DataArray(
name="ts",
data=np.ones((2, 12, 4, 4)),
coords={
"bnds": np.array([0, 1]),
"time": time_cf.assign_attrs(bounds="time_bnds"),
"lat": lat.assign_attrs(bounds="lat_bnds"),
"lon": lon.assign_attrs(bounds="lon_bnds"),
"lat_bnds": lat_bnds.copy(),
"lon_bnds": lon_bnds.copy(),
"time_bnds": time_bnds.copy(),
},
dims=["bnds", "time", "lat", "lon"],
)
ts_with_bnds_from_parent_non_cf = xr.DataArray(
name="ts",
data=np.ones((2, 12, 4, 4)),
coords={
"bnds": np.array([0, 1]),
"time": time_cf.copy(),
"lat": lat.copy(),
"lon": lon.copy(),
"lat_bnds": lat_bnds,
"lon_bnds": lon_bnds,
"time_bnds": time_bnds_non_cf,
},
dims=["bnds", "time", "lat", "lon"],
)


def generate_dataset(cf_compliant: bool, has_bounds: bool) -> xr.Dataset:
"""Generates a dataset using coordinate and data variable fixtures.
Expand Down
114 changes: 60 additions & 54 deletions tests/test_bounds.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,7 @@
import pytest
import xarray as xr

from tests.fixtures import (
generate_dataset,
lat_bnds,
lon_bnds,
time_bnds,
ts_cf,
ts_with_bnds_from_parent_cf,
)
from tests.fixtures import generate_dataset, lat_bnds, lon_bnds, time_bnds, ts_cf
from xcdat.bounds import DataArrayBoundsAccessor, DatasetBoundsAccessor


Expand All @@ -29,19 +22,20 @@ def test_decorator_call(self):
def test_bounds_property_returns_map_of_coordinate_key_to_bounds_dataarray(self):
ds = self.ds_with_bnds.copy()
expected = {
"T": ds.time,
"X": ds.lon,
"Y": ds.lat,
"lat": ds.lat,
"latitude": ds.lat,
"lon": ds.lon,
"longitude": ds.lon,
"time": ds.time,
"T": ds.time_bnds,
"X": ds.lon_bnds,
"Y": ds.lat_bnds,
"lat": ds.lat_bnds,
"latitude": ds.lat_bnds,
"lon": ds.lon_bnds,
"longitude": ds.lon_bnds,
"time": ds.time_bnds,
}

result = ds.bounds.bounds

assert result == expected
for key in expected.keys():
assert result[key].identical(expected[key])

def test_fill_missing_returns_dataset_with_filled_bounds(self):
ds = self.ds_with_bnds.copy()
Expand Down Expand Up @@ -141,6 +135,7 @@ class TestDataArrayBoundsAccessor:
@pytest.fixture(autouse=True)
def setUp(self):
self.ds = generate_dataset(cf_compliant=True, has_bounds=True)
self.ts = ts_cf.copy()

def test__init__(self):
obj = DataArrayBoundsAccessor(self.ds.ts)
Expand All @@ -151,65 +146,76 @@ def test_decorator_call(self):
result = self.ds["ts"].bounds._dataarray
assert result.identical(expected)

def test__copy_from_parent_copies_bounds(self):
ts_expected = ts_with_bnds_from_parent_cf.copy()
ts_result = ts_cf.copy().bounds.copy_from_parent(self.ds)

assert ts_result.identical(ts_expected)
def test_copy_returns_exact_copy_with_attrs(self):
ds_bounds = self.ds.drop_vars("ts")
ts = self.ts.copy()

def test__set_bounds_dim_adds_bnds(self):
ts_expected = ts_cf.copy().expand_dims(bnds=np.array([0, 1]))
ts_result = ts_cf.copy().bounds._set_bounds_dim(self.ds)
ts_result = ts.bounds.copy_from_parent(self.ds)
assert ts_result.bounds._bounds.identical(ds_bounds)

assert ts_result.identical(ts_expected)
ts_result_copy = ts_result.bounds.copy()
assert ts_result_copy.bounds._dataarray.identical(ts_result.bounds._dataarray)
assert ts_result_copy.bounds._bounds.identical(ts_result.bounds._bounds)

def test__set_bounds_dim_adds_bounds(self):
ds = self.ds.swap_dims({"bnds": "bounds"}).copy()
def test__copy_from_parent_copies_bounds(self):
ds_bounds = self.ds.drop_vars("ts")
ts = self.ts.copy()

ts_expected = ts_cf.copy().expand_dims(bounds=np.array([0, 1]))
ts_result = ts_cf.copy().bounds._set_bounds_dim(ds)
assert ts_result.identical(ts_expected)
ts_result = ts.bounds.copy_from_parent(self.ds)
assert ts_result.bounds._bounds.identical(ds_bounds)

def test_bounds_property_returns_mapping_of_coordinate_keys_to_bounds_dataarrays(
def test_bounds_property_returns_mapping_of_keys_to_bounds_dataarrays(
self,
):
ts = ts_with_bnds_from_parent_cf.copy()
ds = self.ds
ts = self.ts.copy()
ts = ts.bounds.copy_from_parent(ds)

result = ts.bounds.bounds
expected = {"time": ts.time_bnds, "lat": ts.lat_bnds, "lon": ts.lon_bnds}
expected = {"time": ds.time_bnds, "lat": ds.lat_bnds, "lon": ds.lon_bnds}

for key in expected.keys():
assert result[key].identical(expected[key])

def test_bounds_names_property_returns_mapping_of_coordinate_keys_to_names_of_bounds(
self,
):
ts = ts_with_bnds_from_parent_cf.copy()
ts = ts_cf.copy()
ts = ts.bounds.copy_from_parent(self.ds)

result = ts.bounds.bounds_names
expected = {"time": "time_bnds", "lat": "lat_bnds", "lon": "lon_bnds"}
expected = {
"lat": ["lat_bnds"],
"Y": ["lat_bnds"],
"latitude": ["lat_bnds"],
"longitude": ["lon_bnds"],
"lon": ["lon_bnds"],
"time": ["time_bnds"],
"T": ["time_bnds"],
"X": ["lon_bnds"],
}
assert result == expected

def test__check_bounds_are_set_raises_error_if_not_set(
self,
):
ts = self.ts.copy()

with pytest.raises(ValueError):
ts.bounds.bounds

def test_get_bounds_returns_bounds_dataarray_for_coordinate_key(self):
ts = ts_with_bnds_from_parent_cf.copy()
ts = self.ts.copy()
ts = ts.bounds.copy_from_parent(self.ds)

result = ts.bounds.get_bounds("lat")
expected = ts.lat_bnds
expected = self.ds.lat_bnds

assert result.identical(expected)

def test_get_bounds_returns_keyerror_with_incorrect_key(self):
ts = ts_with_bnds_from_parent_cf.copy()
def test_get_bounds_raises_error_with_incorrect_key(self):
ts = self.ts.copy()
ts = ts.bounds.copy_from_parent(self.ds)

with pytest.raises(KeyError):
with pytest.raises(ValueError):
ts.bounds.get_bounds("something")

def test_get_bounds_dim_name_returns_dim_of_coordinate_bounds(self):
ts = ts_with_bnds_from_parent_cf.copy()
result = ts.bounds.get_bounds_dim_name("lat")
expected = "bnds"

assert result == expected

def test_get_bounds_dim_name_raises_keyerror_with_incorrect_key(self):
ts = ts_with_bnds_from_parent_cf.copy()

with pytest.raises(KeyError):
ts.bounds.get_bounds_dim_name("something")
57 changes: 43 additions & 14 deletions tests/test_variable.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest

from tests.fixtures import generate_dataset, ts_with_bnds_from_parent_cf
from xcdat.variable import open_variable
from tests.fixtures import generate_dataset
from xcdat.variable import copy_variable, open_variable


class TestOpenVariable:
Expand All @@ -11,12 +11,6 @@ def test_raises_error_if_variable_does_not_exist(self):
with pytest.raises(KeyError):
open_variable(ds, "invalid_var")

def test_raises_error_if_bounds_dim_is_missing(self):
ds = generate_dataset(cf_compliant=True, has_bounds=False)

with pytest.raises(KeyError):
open_variable(ds, "ts")

def test_raises_error_if_bounds_are_missing_for_coordinates(self):
ds = generate_dataset(cf_compliant=True, has_bounds=True)

Expand All @@ -31,11 +25,46 @@ def test_raises_error_if_bounds_are_missing_for_coordinates(self):

def test_returns_variable_with_bounds(self):
ds = generate_dataset(cf_compliant=True, has_bounds=True)
ds.lat.attrs["bounds"] = "lat_bnds"
ds.lon.attrs["bounds"] = "lon_bnds"
ds.time.attrs["bounds"] = "time_bnds"
expected = {
"T": ds.time_bnds,
"X": ds.lon_bnds,
"Y": ds.lat_bnds,
"lat": ds.lat_bnds,
"latitude": ds.lat_bnds,
"lon": ds.lon_bnds,
"longitude": ds.lon_bnds,
"time": ds.time_bnds,
}

ts = open_variable(ds, "ts")
result = ts.bounds.bounds

for key in expected.keys():
assert result[key].identical(expected[key])


class TestCopyVariable:
def test_returns_copy_of_variable_with_bounds(self):
ds = generate_dataset(cf_compliant=True, has_bounds=True)
expected = {
"T": ds.time_bnds,
"X": ds.lon_bnds,
"Y": ds.lat_bnds,
"lat": ds.lat_bnds,
"latitude": ds.lat_bnds,
"lon": ds.lon_bnds,
"longitude": ds.lon_bnds,
"time": ds.time_bnds,
}

ts = open_variable(ds, "ts")
ts_bounds = ts.bounds.bounds

for key in expected.keys():
assert ts_bounds[key].identical(expected[key])

ts_expected = ts_with_bnds_from_parent_cf.copy()
ts_copy = copy_variable(ts)
ts_copy_bounds = ts_copy.bounds.bounds

ts_result = open_variable(ds, "ts")
assert ts_result.identical(ts_expected)
for key in expected.keys():
assert ts_copy_bounds[key].identical(ts_bounds[key])
Loading