Repository: xgcm/xgcm (python)

Issue:
Check if values passed in `coords={..}` are valid dimensions.
Currently xgcm does not check if the values passed in `Grid(..., coords={...})` are valid dimensions upon the creation of the grid object. 

I recently came across a situation like this:
```python
import xarray as xr
import numpy as np
data = np.random.rand(4,5)
coord = np.random.rand(4,5)
ds = xr.DataArray(data, dims=['x','y'], coords={'c':(['x', 'y'], coord)}).to_dataset(name='data')
```

Should it be possible to assign a coordinate instead of a dimension like this:
```python
from xgcm import Grid
grid = Grid(ds, coords={'X':{'center':'c'}})
```
this currently does not raise an error/warning.

Only when trying to compute something we get:
```python
grid.diff(ds.data,'X')
```

```
...
KeyError: "None of the DataArray's dims ('x', 'y') were found in axis coords."
```

I think it might be easier for new users to get a warning like ` c is not a dataset dimension. xgcm can only accept 1D dimensions in coords` or similar. Should be very cheap test too, so not weigh the grid object creation down too much.

cc @abigalew


Pull request description:
Update constructor docstring and input checks
<!-- Feel free to remove check-list items aren't relevant to your change -->

 - [x] Closes #208
 - [x] Tests added
 - [x] Passes `pre-commit run --all-files`
 - [x] User visible changes (including notable bug fixes) are documented in `whats-new.rst`


Patch:
diff --git a/doc/whats-new.rst b/doc/whats-new.rst
index 5ebddb6..da8e3f3 100644
--- a/doc/whats-new.rst
+++ b/doc/whats-new.rst
@@ -15,11 +15,13 @@ New Features
 - :py:meth:`~xgcm.grid.Grid.set_metrics` now enables adding metrics to a grid object (:pull:`336`, :issue:`199`).
   By `Dianne Deauna <https://github.com/jdldeauna>`_ under the `SIParCS internship <https://www2.cisl.ucar.edu/siparcs-2021-projects#8>`_.
 
+- Better input checking when creating a grid object avoids creating grid positions on dataset coordinates which are not 1D (:issue:`208`, :pull:`358`).
+  By `Julius Busecke <https://github.com/jbusecke>`_.
+
 Breaking Changes
 ~~~~~~~~~~~~~~~~
 - Drop support for Python 3.6 (:issue:`360`, :pull:`361`). By `Julius Busecke <https://github.com/jbusecke>`_.
 
-
 v0.5.2 (2021/5/27)
 -------------------
 
diff --git a/xgcm/grid.py b/xgcm/grid.py
index fbfffc5..4b36943 100644
--- a/xgcm/grid.py
+++ b/xgcm/grid.py
@@ -1080,13 +1080,22 @@ class Grid:
         face_connections : dict
             Grid topology
         coords : dict, optional
-            Explicit specification of axis coordinates, e.g
+            Specifies positions of dimension names along axes X, Y, Z, e.g
             ``{'X': {'center': 'XC', 'left: 'XG'}}``.
-            Each key should be the name of an axis. The value should be
-            a dictionary mapping positions (e.g. ``'left'``) to names of
-            coordinates in ``ds``.
+            Each key should be an axis name (e.g., `X`, `Y`, or `Z`) and map
+            to a dictionary which maps positions (`center`, `left`, `right`,
+            `outer`, `inner`) to dimension names in the dataset
+            (in the example above, `XC` is at the `center` position and `XG`
+            at the `left` position along the `X` axis).
+            If the values are not present in ``ds`` or are not dimensions,
+            an error will be raised.
         metrics : dict, optional
-            Specification of grid metrics
+            Specification of grid metrics mapping axis names (X, Y, Z) to corresponding
+            metric variable names in the dataset
+            (e.g. {('X',):['dx_t'], ('X', 'Y'):['area_tracer', 'area_u']}
+            for the cell distance in the x-direction ``dx_t`` and the
+            horizontal cell areas ``area_tracer`` and ``area_u``, located at
+            different grid positions).
         boundary : {None, 'fill', 'extend', 'extrapolate', dict}, optional
             A flag indicating how to handle boundaries:
 
@@ -1120,6 +1129,18 @@ class Grid:
             all_axes = comodo.get_all_axes(ds)
             coords = {}
 
+        # check coords input validity
+        for axis, positions in coords.items():
+            for pos, dim in positions.items():
+                if not (dim in ds.variables or dim in ds.dims):
+                    raise ValueError(
+                        f"Could not find dimension `{dim}` (for the `{pos}` position on axis `{axis}`) in input dataset."
+                    )
+                if dim not in ds.dims:
+                    raise ValueError(
+                        f"Input `{dim}` (for the `{pos}` position on axis `{axis}`) is not a dimension in the input datasets `ds`."
+                    )
+
         self.axes = OrderedDict()
         for axis_name in all_axes:
             try:


Test patch:
diff --git a/xgcm/test/test_grid.py b/xgcm/test/test_grid.py
index 6ca5e64..5ffca92 100644
--- a/xgcm/test/test_grid.py
+++ b/xgcm/test/test_grid.py
@@ -623,9 +623,9 @@ def test_create_grid_no_comodo(all_datasets):
 
 
 def test_grid_no_coords(periodic_1d):
-
+    """Ensure that you can use xgcm with Xarray datasets that don't have dimension coordinates."""
     ds, periodic, expected = periodic_1d
-    ds_nocoords = ds.drop_dims(list(ds.dims.keys()))
+    ds_nocoords = ds.drop_vars(list(ds.dims.keys()))
 
     coords = expected["axes"]
     grid = Grid(ds_nocoords, periodic=periodic, coords=coords)
@@ -833,3 +833,25 @@ def test_interp_like(
     )
 
     xr.testing.assert_allclose(interp_metric, expected_metric)
+
+
+def test_input_not_dims():
+    data = np.random.rand(4, 5)
+    coord = np.random.rand(4, 5)
+    ds = xr.DataArray(
+        data, dims=["x", "y"], coords={"c": (["x", "y"], coord)}
+    ).to_dataset(name="data")
+    msg = r"is not a dimension in the input dataset"
+    with pytest.raises(ValueError, match=msg):
+        Grid(ds, coords={"X": {"center": "c"}})
+
+
+def test_input_dim_notfound():
+    data = np.random.rand(4, 5)
+    coord = np.random.rand(4, 5)
+    ds = xr.DataArray(
+        data, dims=["x", "y"], coords={"c": (["x", "y"], coord)}
+    ).to_dataset(name="data")
+    msg = r"Could not find dimension `other` \(for the `center` position on axis `X`\) in input dataset."
+    with pytest.raises(ValueError, match=msg):
+        Grid(ds, coords={"X": {"center": "other"}})
diff --git a/xgcm/test/test_transform.py b/xgcm/test/test_transform.py
index ca0eb93..fcf0239 100644
--- a/xgcm/test/test_transform.py
+++ b/xgcm/test/test_transform.py
@@ -119,7 +119,7 @@ cases = {
             "data",
             [1.0, 4.0, 6.0, 5.6, 4.0, 2.0, -0.272727, -0.818182],
         ),
-        "grid_kwargs": {"coords": {"Z": {"center": "depth", "outer": "depth_bnds"}}},
+        "grid_kwargs": {"coords": {"Z": {"center": "depth"}}},
         "transform_kwargs": {
             "method": "linear",
             "target_data": "dens",
@@ -139,7 +139,7 @@ cases = {
             "data",
             [1.0, 4.0, 6.0, 5.6, 4.0, 2.0, -0.272727, -0.818182],
         ),
-        "grid_kwargs": {"coords": {"Z": {"center": "depth", "outer": "depth_bnds"}}},
+        "grid_kwargs": {"coords": {"Z": {"center": "depth"}}},
         # "error": True,  # this currently fails but shouldnt
         "transform_kwargs": {
             "method": "linear",
@@ -160,7 +160,7 @@ cases = {
             "data",
             [1.0, 4.0, 6.0, 5.6, 4.0, 2.0],
         ),
-        "grid_kwargs": {"coords": {"Z": {"center": "depth", "outer": "depth_bnds"}}},
+        "grid_kwargs": {"coords": {"Z": {"center": "depth"}}},
         # "error": True,  # this currently fails but shouldnt
         "transform_kwargs": {
             "method": "linear",
@@ -180,7 +180,7 @@ cases = {
             "data",
             [1.0, 4.0, 6.0, 5.6, 4.0, 2.0, -0.272727, -0.818182],
         ),
-        "grid_kwargs": {"coords": {"Z": {"center": "depth", "outer": "depth_bnds"}}},
+        "grid_kwargs": {"coords": {"Z": {"center": "depth"}}},
         "transform_kwargs": {
             "method": "linear",
             "target_data": "dens",
@@ -201,7 +201,7 @@ cases = {
             "data",
             [np.nan, 4.0, 6.0, 5.6, 4.0, 2.0, -0.272727, -0.818182],
         ),
-        "grid_kwargs": {"coords": {"Z": {"center": "depth", "outer": "depth_bnds"}}},
+        "grid_kwargs": {"coords": {"Z": {"center": "depth"}}},
         "transform_kwargs": {
             "method": "linear",
             "target_data": "dens",
@@ -222,7 +222,7 @@ cases = {
             "data",
             [-0.818182, -0.272727, 2.0, 4.0, 5.6, 6.0, 4.0, 1.0],
         ),
-        "grid_kwargs": {"coords": {"Z": {"center": "depth", "outer": "depth_bnds"}}},
+        "grid_kwargs": {"coords": {"Z": {"center": "depth"}}},
         "transform_kwargs": {
             "method": "linear",
             "target_data": "dens",