Repository: pangeo-data/xESMF

Issue:
Hardcoded bounds dimension name.
https://github.com/pangeo-data/xESMF/blob/0bae6dcda466d19092cf7d48115491a21ed42269/xesmf/frontend.py#L75-L76

The lines above hardcode the name of the "bounds" dimension to `'bounds'`. This is my fault, from xarray-contrib/cf-xarray#108 and #49... It seems like CF conventions have no prescribed name for that dimension, so I'm not sure if this is a problem to be fixed here or in cf_xarray?

The current code raises an error if the input dataset uses anything else, like "bnds" or "nv".

Discussion:

Patch:
diff --git a/xesmf/frontend.py b/xesmf/frontend.py
index f41a794..e504a5c 100644
--- a/xesmf/frontend.py
+++ b/xesmf/frontend.py
@@ -66,14 +66,14 @@ def _get_lon_lat_bounds(ds):
             raise KeyError('lon_b')
         lon_name = ds.cf['longitude'].name
         lat_name = ds.cf['latitude'].name
-        ds2 = ds.cf.add_bounds([lon_name, lat_name])
-        lon_bnds = ds2.cf.get_bounds('longitude')
-        lat_bnds = ds2.cf.get_bounds('latitude')
+        ds = ds.cf.add_bounds([lon_name, lat_name])
+        lon_bnds = ds.cf.get_bounds('longitude')
+        lat_bnds = ds.cf.get_bounds('latitude')
 
     # Convert from CF bounds to xESMF bounds.
     # order=None is because we don't want to assume the dimension order for 2D bounds.
-    lon_b = cfxr.bounds_to_vertices(lon_bnds, 'bounds', order=None)
-    lat_b = cfxr.bounds_to_vertices(lat_bnds, 'bounds', order=None)
+    lon_b = cfxr.bounds_to_vertices(lon_bnds, ds.cf.get_bounds_dim_name('longitude'), order=None)
+    lat_b = cfxr.bounds_to_vertices(lat_bnds, ds.cf.get_bounds_dim_name('latitude'), order=None)
     return lon_b, lat_b
 
 


Test patch:
diff --git a/xesmf/tests/test_frontend.py b/xesmf/tests/test_frontend.py
index 993501e..2807507 100644
--- a/xesmf/tests/test_frontend.py
+++ b/xesmf/tests/test_frontend.py
@@ -1,7 +1,7 @@
 import os
 import warnings
 
-import cf_xarray  # noqa
+import cf_xarray as cfxr
 import dask
 import numpy as np
 import pytest
@@ -17,6 +17,8 @@ dask_schedulers = ['threaded_scheduler', 'processes_scheduler', 'distributed_sch
 
 # same test data as test_backend.py, but here we can use xarray DataSet
 ds_in = xe.util.grid_global(20, 12)
+ds_in.lat.attrs['standard_name'] = 'latitude'
+ds_in.lon.attrs['standard_name'] = 'longitude'
 ds_out = xe.util.grid_global(15, 9)
 
 horiz_shape_in = ds_in['lon'].shape
@@ -259,6 +261,21 @@ def test_regrid_with_1d_grid_infer_bounds():
     assert_allclose(dr_out, dr_exp)
 
 
+def test_regrid_cfbounds():
+    # Test regridding when bounds are given in cf format with a custom "bounds" name.
+    ds = ds_in.copy().drop_vars(['lat_b', 'lon_b'])
+    ds['lon_bounds'] = cfxr.vertices_to_bounds(ds_in.lon_b, ('bnds', 'y', 'x'))
+    ds['lat_bounds'] = cfxr.vertices_to_bounds(ds_in.lat_b, ('bnds', 'y', 'x'))
+    ds.lat.attrs['bounds'] = 'lat_bounds'
+    ds.lon.attrs['bounds'] = 'lon_bounds'
+
+    regridder = xe.Regridder(ds, ds_out, 'conservative', periodic=True)
+    dr_out = regridder(ds['data'])
+    # compare with provided-bounds solution
+    dr_exp = xe.Regridder(ds_in, ds_out, 'conservative', periodic=True)(ds_in['data'])
+    assert_allclose(dr_out, dr_exp)
+
+
 # TODO: consolidate (regrid method, input data types) combination
 # using pytest fixtures and parameterization