Repository: xgcm/xgcm (python)

Issue:
Overeager stale bot?
I am finding myself to reopen most of the autoclosed issues here. Maybe we want to have some more time elapse between marking as stale and closing? 
I am also wondering if we should include `bug` in the exempt labels?
@andersy005

Pull request description:
Exempt bug issues from automatic closing.
<!-- Feel free to remove check-list items aren't relevant to your change -->

 - [x] Closes #347 


Patch:
diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml
index 1ff499b..1e0ee71 100644
--- a/.github/workflows/stale.yaml
+++ b/.github/workflows/stale.yaml
@@ -10,7 +10,7 @@ jobs:
     - uses: actions/stale@v3
       with:
         repo-token: ${{ secrets.GITHUB_TOKEN }}
-        exempt-issue-labels: 'keepOpen,In Progress'
+        exempt-issue-labels: 'keepOpen,In Progress,bug'
         exempt-pr-labels: 'keepOpen,In Progress'
         stale-issue-message: "This issue has been marked 'stale' due to lack of recent activity. If there is no further activity, the issue will be closed in another 30 days. Thank you for your contribution!"
         close-issue-message: "This issue has been closed due to inactivity. If you feel this is in error, please reopen the issue or file a new issue with the relevant details."
diff --git a/README.rst b/README.rst
index 9136c7d..fbd9182 100644
--- a/README.rst
+++ b/README.rst
@@ -50,7 +50,7 @@ To learn how to install and use xgcm for your dataset, visit the `xgcm documenta
 .. _Pangeo: http://pangeo.io
 .. _dask: http://dask.pydata.org
 .. _xarray: http://xarray.pydata.org
-.. _Arakawa Grids: https://en.wikipedia.org/wiki/Arakawa_grid
+.. _Arakawa Grids: https://en.wikipedia.org/wiki/Arakawa_grids
 .. _xgcm documentation: https://xgcm.readthedocs.io/
 
 .. |conda forge| image:: https://img.shields.io/conda/vn/conda-forge/xgcm
diff --git a/doc/conf.py b/doc/conf.py
index dd99b9a..6f9aaef 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -15,7 +15,6 @@
 
 import sys
 
-
 import xgcm
 
 # If extensions (or modules to document with autodoc) are in another directory,
diff --git a/doc/grid_metrics.ipynb b/doc/grid_metrics.ipynb
index 7cc1295..5c21d93 100644
--- a/doc/grid_metrics.ipynb
+++ b/doc/grid_metrics.ipynb
@@ -165,7 +165,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "## Grid aware integration"
+    "## Grid-aware (weighted) integration"
    ]
   },
   {
@@ -333,7 +333,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### Grid-aware (weighted) average\n",
+    "## Grid-aware (weighted) average\n",
     "\n",
     "xgcm can also calcualate the weighted average along each axis and combinations of axes. \n",
     "See for example the vertical average of salinity:\n",
@@ -453,7 +453,7 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "### Cumulative integration\n",
+    "## Cumulative integration\n",
     "\n",
     "Using the metric-aware cumulative integration `cumint`, we can calculate the [barotropic transport streamfunction](https://xgcm.readthedocs.io/en/latest/example_mitgcm.html#Barotropic-Transport-Streamfunction) even easier and more intuitive in one line:"
    ]
diff --git a/doc/whats-new.rst b/doc/whats-new.rst
index c9ce5ab..a925816 100644
--- a/doc/whats-new.rst
+++ b/doc/whats-new.rst
@@ -10,6 +10,8 @@ v0.6.0 (unreleased)
 
 New Features
 ~~~~~~~~~~~~
+- :py:meth:`~xgcm.grid.Grid.interp_like` enables users to interpolate arrays onto the grid positions of another array (:issue:`234` , :issue:`343`, :pull:`350`). By `Dianne Deauna <https://github.com/jdldeauna>`_ under the `SIParCS internship <https://www2.cisl.ucar.edu/siparcs-2021-projects#8>`_.
+
 - :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>`_.
 
diff --git a/xgcm/grid.py b/xgcm/grid.py
index 21f2b81..45ed989 100644
--- a/xgcm/grid.py
+++ b/xgcm/grid.py
@@ -10,6 +10,7 @@ import xarray as xr
 
 from . import comodo
 from .duck_array_ops import _apply_boundary_condition, _pad_array, concatenate
+from .metrics import iterate_axis_combinations
 
 try:
     import numba
@@ -107,7 +108,7 @@ class Axis:
               points nearest to the edge
             This sets the default value. It can be overriden by specifying the
             boundary kwarg when calling specific methods.
-        fill_value : {float}, optional
+        fill_value : float, optional
             The value to use in the boundary condition when `boundary='fill'`.
 
         REFERENCES
@@ -1259,7 +1260,6 @@ class Grid:
             self.axes[axis]._connections = axis_links
 
     def set_metrics(self, key, value):
-
         metric_axes = frozenset(_maybe_promote_str_to_list(key))
         axes_not_found = [ma for ma in metric_axes if ma not in self.axes]
 
@@ -1315,53 +1315,63 @@ class Grid:
             A metric which can broadcast against ``array``
         """
 
-        # a function to find the right combination of metrics
-        def iterate_axis_combinations(items):
-            items_set = frozenset(items)
-            yield (items_set,)
-            N = len(items)
-            for nleft in range(N - 1, 0, -1):
-                nright = N - nleft
-                for sub_loop, sub_items in itertools.product(
-                    range(min(nright, nleft), 0, -1),
-                    itertools.combinations(items_set, nleft),
-                ):
-                    these = frozenset(sub_items)
-                    those = items_set - these
-                    others = [
-                        frozenset(i) for i in itertools.combinations(those, sub_loop)
-                    ]
-                    yield (these,) + tuple(others)
-
         metric_vars = None
         array_dims = set(array.dims)
-        for axis_combinations in iterate_axis_combinations(axes):
-            try:
-                # will raise KeyError if the axis combination is not in metrics
-                possible_metric_vars = [self._metrics[ac] for ac in axis_combinations]
-                for possible_combinations in itertools.product(*possible_metric_vars):
-                    metric_dims = set(
-                        [d for mv in possible_combinations for d in mv.dims]
-                    )
-                    if metric_dims.issubset(array_dims):
-                        # we found a set of metrics with dimensions compatible
-                        # with the array
-                        metric_vars = possible_combinations
-                        break
-                if metric_vars is not None:
+
+        possible_metric_vars = set(tuple(k) for k in self._metrics.keys())
+        possible_combos = set(itertools.permutations(tuple(axes)))
+        overlap_metrics = possible_metric_vars.intersection(possible_combos)
+
+        if len(overlap_metrics) > 0:
+            # Condition 1: metric with matching axes and dimensions exist
+            overlap_metrics = frozenset(*overlap_metrics)
+            possible_metrics = self._metrics[overlap_metrics]
+            for mv in possible_metrics:
+                metric_dims = set(mv.dims)
+                if metric_dims.issubset(array_dims):
+                    metric_vars = mv
                     break
-            except KeyError:
-                pass
+            # if metric_vars is None:
+            #     # Condition 4: interpolate metric with matching axis to desired dimensions
+            #     metric_vars = self.interp_like(mv, array)
+        else:
+            # Condition 2: use provided metrics to calculate for required metric
+            for axis_combinations in iterate_axis_combinations(axes):
+                try:
+                    # will raise KeyError if the axis combination is not in metrics
+                    possible_metric_vars = [
+                        self._metrics[ac] for ac in axis_combinations
+                    ]
+                    for possible_combinations in itertools.product(
+                        *possible_metric_vars
+                    ):
+                        metric_dims = set(
+                            [d for mv in possible_combinations for d in mv.dims]
+                        )
+                        if metric_dims.issubset(array_dims):
+                            # we found a set of metrics with dimensions compatible with the array
+                            metric_vars = possible_combinations
+                            break
+                        # else:
+                        #     # Condition 5: metrics in the wrong position (must interpolate before multiplying)
+                        #     metric_vars = tuple(
+                        #         self.interp_like(pc, array)
+                        #         for pc in possible_combinations
+                        #     )
+                    if metric_vars is not None:
+                        # return the product of the metrics
+                        metric_vars = functools.reduce(operator.mul, metric_vars, 1)
+                        break
+                except KeyError:
+                    pass
         if metric_vars is None:
             raise KeyError(
                 f"Unable to find any combinations of metrics for array dims {array_dims!r} and axes {axes!r}"
             )
-
-        # return the product of the metrics
-        return functools.reduce(operator.mul, metric_vars, 1)
+        return metric_vars
 
     @docstrings.dedent
-    def interp_like(self, array, like):
+    def interp_like(self, array, like, boundary=None, fill_value=None):
         """Compares positions between two data arrays and interpolates array to the position of like if necessary
 
         Parameters
@@ -1370,6 +1380,21 @@ class Grid:
             DataArray to interpolate to the position of like
         like : DataArray
             DataArray with desired grid positions for source array
+        boundary : str or dict, optional,
+            boundary can either be one of {None, 'fill', 'extend', 'extrapolate'}
+            * None:  Do not apply any boundary conditions. Raise an error if
+              boundary conditions are required for the operation.
+            * 'fill':  Set values outside the array boundary to fill_value
+              (i.e. a Dirichlet boundary condition.)
+            * 'extend': Set values outside the array to the nearest array
+              value. (i.e. a limited form of Neumann boundary condition where
+              the difference at the boundary will be zero.)
+            * 'extrapolate': Set values by extrapolating linearly from the two
+              points nearest to the edge
+            This sets the default value. It can be overriden by specifying the
+            boundary kwarg when calling specific methods.
+        fill_value : float, optional
+            The value to use in the boundary condition when `boundary='fill'`.
 
         Returns
         -------
@@ -1377,20 +1402,30 @@ class Grid:
             Source data array with updated positions along axes matching with target array
         """
 
+        interp_axes = []
         for axname, axis in self.axes.items():
             try:
                 position_array, _ = axis._get_axis_coord(array)
                 position_like, _ = axis._get_axis_coord(like)
             # This will raise a KeyError if you have multiple axes contained in self,
             # since the for-loop will go through all axes, but the method is applied for only 1 axis at a time
+            # This is for cases where an axis is present in self that is not available for either array or like.
+            # For the axis you are interested in interpolating, there should be data for it in grid, array, and like.
             except KeyError:
                 continue
             if position_like != position_array:
-                array = self.interp(array, axname)
+                interp_axes.append(axname)
 
+        array = self.interp(
+            array,
+            interp_axes,
+            fill_value=fill_value,
+            boundary=boundary,
+        )
         return array
 
     def _interp_metric(self, da, axes):
+        # include boundary and fill_value as inputs
         metric_available = self._metrics.get(frozenset(axes), None)
         if metric_available is not None:
             # this function works with only one metric at a time
diff --git a/xgcm/metrics.py b/xgcm/metrics.py
new file mode 100644
index 0000000..20ed812
--- /dev/null
+++ b/xgcm/metrics.py
@@ -0,0 +1,30 @@
+import itertools
+
+
+def iterate_axis_combinations(items):
+    """A function to find the right combination of metrics
+
+
+    Parameters
+    ----------
+    items : list
+        List of axes needed for calculations to be performed on data array
+
+    Yields
+    -------
+    items_set : frozenset
+        Frozen set of axes for initial double checking with metric dimensions
+    """
+    items_set = frozenset(items)
+    yield (items_set,)
+    N = len(items)
+    for nleft in range(N - 1, 0, -1):
+        nright = N - nleft
+        for sub_loop, sub_items in itertools.product(
+            range(min(nright, nleft), 0, -1),
+            itertools.combinations(items_set, nleft),
+        ):
+            these = frozenset(sub_items)
+            those = items_set - these
+            others = [frozenset(i) for i in itertools.combinations(those, sub_loop)]
+            yield (these,) + tuple(others)


Test patch:
diff --git a/xgcm/test/test_grid.py b/xgcm/test/test_grid.py
index 1a6d0ce..a015764 100644
--- a/xgcm/test/test_grid.py
+++ b/xgcm/test/test_grid.py
@@ -4,16 +4,14 @@ import xarray as xr
 
 from xgcm.grid import Axis, Grid
 
-from .datasets import (  # noqa: F401
-    all_2d,
-    all_datasets,
-    datasets,
-    datasets_grid_metric,
-    nonperiodic_1d,
-    nonperiodic_2d,
-    periodic_1d,
-    periodic_2d,
-)
+from .datasets import all_2d  # noqa: F401
+from .datasets import all_datasets  # noqa: F401
+from .datasets import datasets  # noqa: F401
+from .datasets import datasets_grid_metric  # noqa: F401
+from .datasets import nonperiodic_1d  # noqa: F401
+from .datasets import nonperiodic_2d  # noqa: F401
+from .datasets import periodic_1d  # noqa: F401
+from .datasets import periodic_2d  # noqa: F401
 
 
 # helper function to produce axes from datasets
@@ -786,34 +784,52 @@ def test_boundary_kwarg_same_as_grid_constructor_kwarg():
 
 @pytest.mark.parametrize(
     "metric_axes,metric_name",
-    [("X", "dx_t"), ("Y", "dy_ne"), (["Y", "X"], "dy_n")],
+    [
+        (["Y", "X"], "area_n"),
+        ("X", "dx_t"),
+        ("Y", "dy_ne"),
+        (["Y", "X"], "dy_n"),
+        (["X"], "tracer"),
+    ],
 )
-def test_interp_like(metric_axes, metric_name):
-
-    ds, coords, _ = datasets_grid_metric("C")
-    grid = Grid(ds, coords=coords)
-    grid.set_metrics(metric_axes, metric_name)
-    metric_available = grid._metrics.get(frozenset(metric_axes), None)
-    metric_available = metric_available[0]
-    interp_metric = grid.interp_like(metric_available, ds.u)
-    test_metric = grid.interp(ds[metric_name], metric_axes)
-
-    xr.testing.assert_equal(interp_metric, test_metric)
-    xr.testing.assert_allclose(interp_metric, test_metric)
-
-
+@pytest.mark.parametrize("periodic", [True, False])
 @pytest.mark.parametrize(
-    "var_name,like_name,var_axes",
+    "boundary, boundary_expected",
     [
-        ("tracer", "u", "X"),
+        ({"X": "fill", "Y": "fill"}, {"X": "fill", "Y": "fill"}),
+        ({"X": "extend", "Y": "extend"}, {"X": "extend", "Y": "extend"}),
+        (
+            {"X": "extrapolate", "Y": "extrapolate"},
+            {"X": "extrapolate", "Y": "extrapolate"},
+        ),
+        ("fill", {"X": "fill", "Y": "fill"}),
+        ("extend", {"X": "extend", "Y": "extend"}),
+        ("extrapolate", {"X": "extrapolate", "Y": "extrapolate"}),
+        ({"X": "extend", "Y": "fill"}, {"X": "extend", "Y": "fill"}),
+        ({"X": "extrapolate", "Y": "fill"}, {"X": "extrapolate", "Y": "fill"}),
+        pytest.param(
+            "fill",
+            {"X": "fill", "Y": "extend"},
+            marks=pytest.mark.xfail,
+            id="boundary not equal to boundary_expected",
+        ),
     ],
 )
-def test_interp_like_var(var_name, like_name, var_axes):
+@pytest.mark.parametrize("fill_value", [None, 0.1])
+def test_interp_like(
+    metric_axes, metric_name, periodic, boundary, boundary_expected, fill_value
+):
 
-    ds, coords, metrics = datasets_grid_metric("C")
-    grid = Grid(ds, coords=coords, metrics=metrics)
-    interp_var = grid.interp_like(ds[var_name], ds[like_name])
-    test_var = grid.interp(ds[var_name], var_axes)
+    ds, coords, _ = datasets_grid_metric("C")
+    grid = Grid(ds, coords=coords, periodic=periodic)
+    grid.set_metrics(metric_axes, metric_name)
+    metric_available = grid._metrics.get(frozenset(metric_axes), None)
+    metric_available = metric_available[0]
+    interp_metric = grid.interp_like(
+        metric_available, ds.u, boundary=boundary, fill_value=fill_value
+    )
+    expected_metric = grid.interp(
+        ds[metric_name], metric_axes, boundary=boundary_expected, fill_value=fill_value
+    )
 
-    xr.testing.assert_equal(interp_var, test_var)
-    xr.testing.assert_allclose(interp_var, test_var)
+    xr.testing.assert_allclose(interp_metric, expected_metric)
diff --git a/xgcm/test/test_metrics.py b/xgcm/test/test_metrics.py
index f148feb..01d27bd 100644
--- a/xgcm/test/test_metrics.py
+++ b/xgcm/test/test_metrics.py
@@ -6,6 +6,7 @@ import pytest
 import xarray as xr
 
 from xgcm.grid import Grid
+from xgcm.metrics import iterate_axis_combinations
 from xgcm.test.datasets import datasets_grid_metric
 
 
@@ -96,6 +97,48 @@ def test_assign_metric(key, metric_vars):
     _ = Grid(ds, coords=coords, metrics={key: metric_vars})
 
 
+@pytest.mark.parametrize(
+    "axes, expected",
+    [
+        (
+            ("X", "Y"),
+            (
+                (frozenset({"X", "Y"}),),
+                (frozenset({"X"}), frozenset({"Y"})),
+                (frozenset({"Y"}), frozenset({"X"})),
+            ),
+        ),
+        (
+            ("X", "Y", "Z"),
+            (
+                (frozenset({"Y", "X", "Z"}),),
+                (frozenset({"Z"}), frozenset({"X"}), frozenset({"Y"})),
+                (frozenset({"X"}), frozenset({"Y"}), frozenset({"Z"})),
+                (frozenset({"Y"}), frozenset({"X"}), frozenset({"Z"})),
+                (frozenset({"Y", "Z"}), frozenset({"X"})),
+                (frozenset({"Y", "X"}), frozenset({"Z"})),
+                (frozenset({"X", "Z"}), frozenset({"Y"})),
+            ),
+        ),
+    ],
+)
+def test_iterate_axis_combinations(axes, expected):
+
+    actual = list(iterate_axis_combinations(axes))
+
+    expected = [set(e) for e in expected]
+    actual = [set(a) for a in actual]
+
+    # comparing these is a pain since output order is not guaranteed.
+    # instead we ensure actual and expected have the same number of elements
+    # and that every element in actual is present in expected and vice-versa
+    assert len(actual) == len(expected)
+    for a in actual:
+        assert set(a) in expected
+    for e in expected:
+        assert set(e) in actual
+
+
 @pytest.mark.parametrize(
     "axes, data_var, drop_vars, metric_expected_list, expected_error",
     [
@@ -107,17 +150,15 @@ def test_assign_metric(key, metric_vars):
             None,
             ["area_t"],
             None,
-        ),  # should we be able to pass a tuple aswell as a list?
+        ),  # should we be able to pass a tuple as well as a list?
         (["X", "Y", "Z"], "tracer", None, ["volume_t"], None),
         (["X"], "u", None, ["dx_e"], None),
         (["X", "Y"], "u", None, ["area_e"], None),
-        (("X", "Y", "Z"), "u", None, ["volume_t"], KeyError),  # This should error out
-        # reconstructed cases
-        (["X", "Y"], "tracer", ["area_t"], ["dx_t", "dy_t"], None),
-        (["X", "Y", "Z"], "tracer", ["volume_t"], ["area_t", "dz_t"], None),
     ],
 )
-def test_get_metric(axes, data_var, drop_vars, metric_expected_list, expected_error):
+def test_get_metric_orig(
+    axes, data_var, drop_vars, metric_expected_list, expected_error
+):
     ds, coords, metrics = datasets_grid_metric("C")
     # drop metrics according to drop_vars input, and remove from metrics input
     if drop_vars:
@@ -138,6 +179,117 @@ def test_get_metric(axes, data_var, drop_vars, metric_expected_list, expected_er
         assert metric.equals(expected)
 
 
+def test_get_metric_with_conditions_01():
+    # Condition 1: metric with matching axes and dimensions exist
+    ds, coords, metrics = datasets_grid_metric("C")
+    grid = Grid(ds, coords=coords, metrics=metrics)
+    get_metric = grid.get_metric(ds.v, ("X", "Y"))
+
+    expected_metric = ds["area_n"].reset_coords(drop=True)
+
+    xr.testing.assert_allclose(get_metric, expected_metric)
+
+
+def test_get_metric_with_conditions_02():
+    # Condition 2: use provided metrics to calculate for required metric
+    ds, coords, metrics = datasets_grid_metric("C")
+    grid = Grid(ds, coords=coords)
+    grid.set_metrics(("X"), "dx_n")
+    grid.set_metrics(("Y"), "dy_n")
+
+    get_metric = grid.get_metric(ds.v, ("X", "Y"))
+
+    metric_var_1 = ds.dx_n
+    metric_var_2 = ds.dy_n
+    expected_metric = (metric_var_1 * metric_var_2).reset_coords(drop=True)
+
+    xr.testing.assert_allclose(get_metric, expected_metric)
+
+
+def test_get_metric_with_conditions_03():
+    # Condition 3: Adapted from original version of test_get_metric
+    ds, coords, metrics = datasets_grid_metric("C")
+    grid = Grid(ds, coords=coords)
+    grid.set_metrics(("X", "Y"), "area_t")
+    grid.set_metrics(("Z"), "dz_t")
+
+    get_metric = grid.get_metric(ds.tracer, ("X", "Y", "Z"))
+
+    metric_var_1 = ds.area_t
+    metric_var_2 = ds.dz_t
+    expected_metric = (metric_var_1 * metric_var_2).reset_coords(drop=True)
+
+    xr.testing.assert_allclose(get_metric, expected_metric)
+
+
+@pytest.mark.skip(reason="For the next PR")
+def test_get_metric_with_conditions_02a():
+    # Condition 4, case a: interpolate metric with matching axis to desired dimensions
+    ds, coords, _ = datasets_grid_metric("C")
+    grid = Grid(ds, coords=coords)
+    grid.set_metrics(("X", "Y"), "area_e")
+
+    get_metric = grid.get_metric(ds.v, ("X", "Y"))
+
+    expected_metric = grid.interp(ds["area_e"], ("X", "Y"))
+
+    xr.testing.assert_equal(get_metric, expected_metric)
+    xr.testing.assert_allclose(get_metric, expected_metric)
+
+
+@pytest.mark.skip(reason="For the next PR")
+def test_get_metric_with_conditions_02b():
+    # Condition 4, case b: get_metric should select for the metric with matching axes and interpolate from there,
+    # even if other metrics in the desired positions are available
+    ds, coords, _ = datasets_grid_metric("C")
+    grid = Grid(ds, coords=coords)
+    grid.set_metrics(("X", "Y"), "area_e")
+    grid.set_metrics(("X"), "dx_n")
+    grid.set_metrics(("Y"), "dx_n")
+
+    get_metric = grid.get_metric(ds.v, ("X", "Y"))
+
+    expected_metric = grid.interp(ds["area_e"], ("X", "Y"))
+
+    xr.testing.assert_equal(get_metric, expected_metric)
+    xr.testing.assert_allclose(get_metric, expected_metric)
+
+
+@pytest.mark.skip(reason="For the next PR")
+def test_get_metric_with_conditions_04a():
+    # Condition 5, case a: 1 metric on the wrong position (must interpolate before multiplying)
+    ds, coords, _ = datasets_grid_metric("C")
+    grid = Grid(ds, coords=coords)
+    grid.set_metrics(("X"), "dx_t")
+    grid.set_metrics(("Y"), "dy_n")
+
+    get_metric = grid.get_metric(ds.v, ("X", "Y"))
+
+    interp_metric = grid.interp(ds.dx_t, "Y")
+    expected_metric = (interp_metric * ds.dy_n).reset_coords(drop=True)
+
+    xr.testing.assert_equal(get_metric, expected_metric)
+    xr.testing.assert_allclose(get_metric, expected_metric)
+
+
+@pytest.mark.skip(reason="For the next PR")
+def test_get_metric_with_conditions_04b():
+    # Condition 5, case b: 2 metrics in the wrong position (must interpolate before multiplying)
+    ds, coords, _ = datasets_grid_metric("C")
+    grid = Grid(ds, coords=coords)
+    grid.set_metrics(("X"), "dx_t")
+    grid.set_metrics(("Y"), "dy_t")
+
+    get_metric = grid.get_metric(ds.v, ("X", "Y"))
+
+    interp_metric_1 = grid.interp(ds.dx_t, "Y")
+    interp_metric_2 = grid.interp(ds.dy_t, "Y")
+    expected_metric = (interp_metric_1 * interp_metric_2).reset_coords(drop=True)
+
+    xr.testing.assert_equal(get_metric, expected_metric)
+    xr.testing.assert_allclose(get_metric, expected_metric)
+
+
 def test_set_metric():
 
     ds, coords, metrics = datasets_grid_metric("C")
@@ -165,6 +317,7 @@ def test_set_metric():
             xr.testing.assert_equal(metric_expected.reset_coords(drop=True), metric)
 
 
+@pytest.mark.skip(reason="For the next PR")
 @pytest.mark.parametrize(
     "metric_axes,metric_name",
     [
@@ -173,13 +326,13 @@ def test_set_metric():
     ],
 )
 def test_interp_metrics(metric_axes, metric_name):
-
-    ds, coords, _ = datasets_grid_metric("C")
+    # need to test with boundary and fill_value conditions
+    ds, coords, metrics = datasets_grid_metric("C")
     grid = Grid(ds, coords=coords)
     grid.set_metrics(metric_axes, metric_name)
     interp_metric = grid._interp_metric(ds.u, metric_axes)
 
-    test_metric = grid.interp(ds[metric_name], metric_axes)
+    expected_metric = grid.interp(ds[metric_name], metric_axes)
 
-    xr.testing.assert_equal(interp_metric, test_metric)
-    xr.testing.assert_allclose(interp_metric, test_metric)
+    if interp_metric.equals(expected_metric) is False:
+        xr.testing.assert_allclose(interp_metric, expected_metric)