Repository: xgcm/xgcm
Pull request #354 (2021-07-12 18:31:02)

Integrate interp_like with get_metric
- [x] Enable interpolation for metrics using get_metric 
- [x] Tests added to test_metrics.py with different inputs (test_get_metric_with_conditions)
- [x] Updated test_metric_ops_multi_axis
- [x] Rename _get_axis_coord to _get_position_name



jbusecke: Ok so I was actually curious myself whether interpolating or combining lower order metrics is 'better'. So I made this little [notebook](https://nbviewer.jupyter.org/gist/jbusecke/f8bad77e7957ab011d4c83fac37dc8e2), where I check how close we are getting when reconstructing the area on the t cell using various methods. This is only one model, and really just a quick look, but the take home message is here: 
![image](https://user-images.githubusercontent.com/14314623/125501782-9581a13d-39f8-49c8-92b8-50d03ec724fb.png)
This shows the global distribution of relative error for a few ways to combine the metric. It turns out that combining from existing dx/dy on the tracer point performs the best (which is your current first choice in the code). Interpolation is close and first interpolating distances from the u/v points and then combining leads to larger errors (maybe not surprising). The good take home is that all these seem to have a relative error well below 1%!!!  
dcherian: Another high-level question is whether we should make the user opt-in to this metric interpolation. For e.g. `grid.autoguess_metrics = True`. If `False` then we could raise an error instead of interpolating.
jdldeauna: > The good take home is that all these seem to have a relative error well below 1%!!!

Great!


jdldeauna: > Another high-level question is whether we should make the user opt-in to this metric interpolation. For e.g. `grid.autoguess_metrics = True`. If `False` then we could raise an error instead of interpolating.

Is`autoguess_metrics` a new keyword in `class Grid` or `get_metric`?
If I understand correctly, the error will appear before any metric interpolation steps, so after `Line 1370` and `Line 1392`?

Patch:
diff --git a/xgcm/grid.py b/xgcm/grid.py
index b4e446e..fbfffc5 100644
--- a/xgcm/grid.py
+++ b/xgcm/grid.py
@@ -274,7 +274,7 @@ class Axis:
         da_i : xarray.DataArray
             The differenced data
         """
-        position_from, dim = self._get_axis_coord(da)
+        position_from, dim = self._get_position_name(da)
         if to is None:
             to = self._default_shifts[position_from]
 
@@ -340,7 +340,7 @@ class Axis:
         boundary conditions.
         """
 
-        position, this_dim = self._get_axis_coord(da)
+        position, this_dim = self._get_position_name(da)
         this_axis_num = da.get_axis_num(this_dim)
 
         def face_edge_data(fnum, face_axis, count=1):
@@ -508,7 +508,7 @@ class Axis:
         position_check=True,
     ):
 
-        position_from, dim = self._get_axis_coord(da)
+        position_from, dim = self._get_position_name(da)
         axis_num = da.get_axis_num(dim)
 
         boundary_kwargs = dict(
@@ -664,7 +664,7 @@ class Axis:
             The cumsummed data
         """
 
-        pos, dim = self._get_axis_coord(da)
+        pos, dim = self._get_position_name(da)
 
         if to is None:
             to = self._default_shifts[pos]
@@ -930,7 +930,7 @@ class Axis:
             _check_other_dims(target_data)
             return target, target_dim, target_data
 
-        _, dim = self._get_axis_coord(da)
+        _, dim = self._get_position_name(da)
         if method == "linear":
             target, target_dim, target_data = _parse_target(
                 target, target_dim, dim, target_data
@@ -972,7 +972,7 @@ class Axis:
                 # Rechunk to keep xr.apply_func from complaining.
                 # TODO: This should be made obsolete, when the internals are refactored using numba
                 target_data = target_data.chunk(
-                    {self._get_axis_coord(target_data)[1]: -1}
+                    {self._get_position_name(target_data)[1]: -1}
                 )
 
             out = conservative_interpolation(
@@ -991,7 +991,7 @@ class Axis:
         Take the base coords from da, the data from data_new, and return
         a new DataArray with a coordinate on position_to.
         """
-        position_from, old_dim = self._get_axis_coord(da)
+        position_from, old_dim = self._get_position_name(da)
         try:
             new_dim = self.coords[position_to]
         except KeyError:
@@ -1023,8 +1023,8 @@ class Axis:
 
         return xr.DataArray(data_new, dims=dims, coords=coords)
 
-    def _get_axis_coord(self, da):
-        """Return the position and name of the axis coordiante in a DataArray."""
+    def _get_position_name(self, da):
+        """Return the position and name of the axis coordinate in a DataArray."""
         for position, coord_name in self.coords.items():
             # TODO: should we have more careful checking of alignment here?
             if coord_name in da.dims:
@@ -1037,7 +1037,7 @@ class Axis:
 
     def _get_axis_dim_num(self, da):
         """Return the dimension number of the axis coordinate in a DataArray."""
-        _, coord_name = self._get_axis_coord(da)
+        _, coord_name = self._get_position_name(da)
         return da.get_axis_num(coord_name)
 
 
@@ -1305,16 +1305,19 @@ class Grid:
 
     def _get_dims_from_axis(self, da, axis):
         dim = []
+        axis = _maybe_promote_str_to_list(axis)
         for ax in axis:
-            all_dim = self.axes[ax].coords.values()
-            matching_dim = [di for di in all_dim if di in da.dims]
-            if len(matching_dim) == 1:
-                dim.append(matching_dim[0])
+            if ax in self.axes:
+                all_dim = self.axes[ax].coords.values()
+                matching_dim = [di for di in all_dim if di in da.dims]
+                if len(matching_dim) == 1:
+                    dim.append(matching_dim[0])
+                else:
+                    raise ValueError(
+                        f"Did not find single matching dimension {da.dims} from {da.name} corresponding to axis {ax}, got {matching_dim}."
+                    )
             else:
-                raise ValueError(
-                    "Did not find single matching dimension corresponding to axis %s. Got (%s)"
-                    % (ax, matching_dim)
-                )
+                raise KeyError(f"Did not find axis {ax} from data array {da.name}")
         return dim
 
     def get_metric(self, array, axes):
@@ -1325,8 +1328,7 @@ class Grid:
         Parameters
         ----------
         array : xarray.DataArray
-            The array for which we are looking for a metric. Only its
-            dimensions are considered.
+            The array for which we are looking for a metric. Only its dimensions are considered.
         axes : iterable
             A list of axes for which to find the metric.
 
@@ -1339,6 +1341,10 @@ class Grid:
         metric_vars = None
         array_dims = set(array.dims)
 
+        # Will raise a Value Error if array doesn't have a dimension corresponding to metric axes specified
+        # See _get_dims_from_axis
+        self._get_dims_from_axis(array, frozenset(axes))
+
         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)
@@ -1352,11 +1358,13 @@ class Grid:
                 if metric_dims.issubset(array_dims):
                     metric_vars = mv
                     break
-            # if metric_vars is None:
-            #     # Condition 4: interpolate metric with matching axis to desired dimensions
-            #     metric_vars = self.interp_like(mv, array)
+            if metric_vars is None:
+                # Condition 2: interpolate metric with matching axis to desired dimensions
+                warnings.warn(
+                    f"Metric at {array.dims} being interpolated from metrics at dimensions {mv.dims}. Boundary value set to 'extend'."
+                )
+                metric_vars = self.interp_like(mv, array, "extend", None)
         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
@@ -1370,15 +1378,19 @@ class Grid:
                             [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
+                            # Condition 3: use provided metrics with matching dimensions to calculate for required metric
                             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
-                        #     )
+                        else:
+                            # Condition 4: metrics in the wrong position (must interpolate before multiplying)
+                            possible_dims = [pc.dims for pc in possible_combinations]
+                            warnings.warn(
+                                f"Metric at {array.dims} being interpolated from metrics at dimensions {possible_dims}. Boundary value set to 'extend'."
+                            )
+                            metric_vars = tuple(
+                                self.interp_like(pc, array, "extend", None)
+                                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)
@@ -1426,8 +1438,8 @@ class Grid:
         interp_axes = []
         for axname, axis in self.axes.items():
             try:
-                position_array, _ = axis._get_axis_coord(array)
-                position_like, _ = axis._get_axis_coord(like)
+                position_array, _ = axis._get_position_name(array)
+                position_like, _ = axis._get_position_name(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.
@@ -1687,7 +1699,7 @@ class Grid:
             )
         for axis_name, component in vector.items():
             axis = self.axes[axis_name]
-            position, coord = axis._get_axis_coord(component)
+            position, coord = axis._get_position_name(component)
             if position == "center":
                 raise NotImplementedError(
                     "Only vector interpolation to cell "
@@ -1784,8 +1796,7 @@ class Grid:
         weighted = da * weight
         # TODO: We should integrate xarray.weighted once available.
 
-        # get dimension(s) corresponding
-        # to `da` and `axis` input
+        # get dimension(s) corresponding to `da` and `axis` input
         dim = self._get_dims_from_axis(da, axis)
 
         return weighted.sum(dim, **kwargs)
@@ -1832,11 +1843,11 @@ class Grid:
         da_i : xarray.DataArray
             The averaged data
         """
+
         weight = self.get_metric(da, axis)
         weighted = da.weighted(weight)
 
-        # get dimension(s) corresponding
-        # to `da` and `axis` input
+        # get dimension(s) corresponding to `da` and `axis` input
         dim = self._get_dims_from_axis(da, axis)
         return weighted.mean(dim, **kwargs)
 


Test patch:
diff --git a/xgcm/test/test_grid.py b/xgcm/test/test_grid.py
index a015764..6ca5e64 100644
--- a/xgcm/test/test_grid.py
+++ b/xgcm/test/test_grid.py
@@ -164,14 +164,14 @@ def test_axis_repr(all_datasets):
     # TODO: make this more complete
 
 
-def test_get_axis_coord(all_datasets):
+def test_get_position_name(all_datasets):
     ds, periodic, expected = all_datasets
     axis_objs = _get_axes(ds)
     for ax_name, axis in axis_objs.items():
         # create a dataarray with each axis coordinate
         for position, coord in axis.coords.items():
             da = 1 * ds[coord]
-            assert axis._get_axis_coord(da) == (position, coord)
+            assert axis._get_position_name(da) == (position, coord)
 
 
 def test_axis_wrap_and_replace_2d(periodic_2d):
@@ -762,11 +762,11 @@ def test_keep_coords(funcname, gridtype):
             assert set(result.coords) == set(base_coords + augmented_coords)
         else:
             assert set(result.coords) == set(base_coords)
-        #
+
         if funcname not in ["integrate", "average"]:
             result = func(ds.tracer, axis_name, keep_coords=False)
             assert set(result.coords) == set(base_coords)
-            #
+
             result = func(ds.tracer, axis_name, keep_coords=True)
             assert set(result.coords) == set(base_coords + augmented_coords)
 
diff --git a/xgcm/test/test_metric_ops_multi_axis.py b/xgcm/test/test_metric_ops_multi_axis.py
index 6c20e5e..07a0669 100644
--- a/xgcm/test/test_metric_ops_multi_axis.py
+++ b/xgcm/test/test_metric_ops_multi_axis.py
@@ -14,7 +14,7 @@ def _expected_result(da, metric, grid, dim, axes, funcname, boundary=None):
     elif funcname == "cumint":
         expected = grid.cumsum(da * metric, axes, boundary=boundary)
     else:
-        raise ValueError("funcname (`%s`) not recognized" % funcname)
+        raise ValueError(f"funcname {funcname} not recognized")
     return expected
 
 
@@ -32,10 +32,10 @@ class TestParametrized:
         grid = Grid(ds, coords=coords, metrics=metrics, periodic=periodic)
 
         if funcname == "cumint":
-            # cumint needs a boundary...
+            # cumint needs a boundary
             kwargs = dict(boundary=boundary)
         else:
-            # integrate and average do use the boundary input
+            # integrate and average don't use the boundary input
             kwargs = dict()
 
         func = getattr(grid, funcname)
@@ -85,21 +85,22 @@ class TestParametrized:
         ds, coords, metrics = datasets_grid_metric("C")
         grid = Grid(ds, coords=coords, metrics=metrics, periodic=periodic)
 
-        func = getattr(grid, funcname)
-
         if funcname == "cumint":
-            # cumint needs a boundary...
+            # cumint needs a boundary
             kwargs = dict(boundary=boundary)
         else:
-            # integrate and average do use the boundary input
+            # integrate and average don't use the boundary input
             kwargs = dict()
 
+        func = getattr(grid, funcname)
+
         # test tracer position
         for axis, metric_name, dim in zip(
             ["X", "Y", "Z", ["X", "Y"], ["X", "Y", "Z"]],
             ["dx_t", "dy_t", "dz_t", "area_t", "volume_t"],
             ["xt", "yt", "zt", ["xt", "yt"], ["xt", "yt", "zt"]],
         ):
+
             new = func(ds.tracer, axis, **kwargs)
             expected = _expected_result(
                 ds.tracer, ds[metric_name], grid, dim, axis, funcname, **kwargs
@@ -136,8 +137,7 @@ class TestParametrized:
 
     @pytest.mark.parametrize("axis", ["X", "Y", "Z"])
     def test_missingaxis(self, axis, funcname, periodic, boundary):
-        # Error should be raised if application axes
-        # include dimension not in datasets
+        # Error should be raised if application axes include dimension not in datasets
 
         ds, coords, metrics = datasets_grid_metric("C")
 
@@ -152,21 +152,14 @@ class TestParametrized:
         func = getattr(grid, funcname)
 
         if funcname == "cumint":
-            # cumint needs a boundary...
+            # cumint needs a boundary
             kwargs = dict(boundary=boundary)
         else:
             kwargs = dict()
 
-        match_message = (
-            "Unable to find any combinations of metrics for array dims.*%s.*" % axis
-        )
-
-        with pytest.raises(KeyError, match=match_message):
+        with pytest.raises(KeyError, match="Did not find axis"):
             func(ds.tracer, ["X", "Y", "Z"], **kwargs)
 
-        with pytest.raises(KeyError, match=match_message):
-            func(ds, axis, **kwargs)
-
         if axis == "Y":
             # test two missing axes at the same time
             del coords["X"]
@@ -179,41 +172,31 @@ class TestParametrized:
             func = getattr(grid, funcname)
 
             if funcname == "cumint":
-                # cumint needs a boundary...
+                # cumint needs a boundary
                 kwargs = dict(boundary="fill")
             else:
                 kwargs = dict()
 
-            match_message = (
-                "Unable to find any combinations of metrics for array dims.*X.*Y.*Z.*"
-            )
-            with pytest.raises(KeyError, match=match_message):
-                func(ds, ["X", "Y", "Z"], **kwargs)
+            with pytest.raises(KeyError, match="Did not find axis"):
+                func(ds.tracer, ["X", "Y", "Z"], **kwargs)
 
-            match_message = (
-                "Unable to find any combinations of metrics for array dims.*X.*Y.*"
-            )
-            with pytest.raises(KeyError, match=match_message):
-                func(ds, ("X", "Y"), **kwargs)
+            with pytest.raises(KeyError, match="Did not find axis"):
+                func(ds.tracer, ("X", "Y"), **kwargs)
 
-    def test_missingdim(self, funcname, periodic, boundary):
+    def test_metric_axes_missing_from_array(self, funcname, periodic, boundary):
         ds, coords, metrics = datasets_grid_metric("C")
         grid = Grid(ds, coords=coords, metrics=metrics, periodic=periodic)
 
-        func = getattr(grid, funcname)
-
         if funcname == "cumint":
-            # cumint needs a boundary...
-            kwargs = dict(boundary=boundary)
+            # cumint needs a boundary
+            kwargs = dict(boundary="fill")
         else:
             kwargs = dict()
 
-        match_message = "Unable to find any combinations of metrics for array dims.*X.*"
-        with pytest.raises(KeyError, match=match_message):
+        func = getattr(grid, funcname)
+
+        with pytest.raises(ValueError, match="Did not find single matching dimension"):
             func(ds.tracer.mean("xt"), "X", **kwargs)
 
-        match_message = (
-            "Unable to find any combinations of metrics for array dims.*X.*Y.*Z.*"
-        )
-        with pytest.raises(KeyError, match=match_message):
+        with pytest.raises(ValueError, match="Did not find single matching dimension"):
             func(ds.tracer.mean("xt"), ["X", "Y", "Z"], **kwargs)
diff --git a/xgcm/test/test_metrics.py b/xgcm/test/test_metrics.py
index c6e5636..cafc1ec 100644
--- a/xgcm/test/test_metrics.py
+++ b/xgcm/test/test_metrics.py
@@ -140,25 +140,23 @@ def test_iterate_axis_combinations(axes, expected):
 
 
 @pytest.mark.parametrize(
-    "axes, data_var, drop_vars, metric_expected_list, expected_error",
+    "axes, data_var, drop_vars, metric_expected_list",
     [
-        ("X", "tracer", None, ["dx_t"], None),
-        (["X", "Y"], "tracer", None, ["area_t"], None),
+        ("X", "tracer", None, ["dx_t"]),
+        (["X", "Y"], "tracer", None, ["area_t"]),
         (
             ("X", "Y"),
             "tracer",
             None,
             ["area_t"],
-            None,
-        ),  # 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),
+        ),
+        # should we be able to pass a tuple as well as a list?
+        (["X", "Y", "Z"], "tracer", None, ["volume_t"]),
+        (["X"], "u", None, ["dx_e"]),
+        (["X", "Y"], "u", None, ["area_e"]),
     ],
 )
-def test_get_metric_orig(
-    axes, data_var, drop_vars, metric_expected_list, expected_error
-):
+def test_get_metric_orig(axes, data_var, drop_vars, metric_expected_list):
     ds, coords, metrics = datasets_grid_metric("C")
     # drop metrics according to drop_vars input, and remove from metrics input
     if drop_vars:
@@ -167,16 +165,10 @@ def test_get_metric_orig(
         metrics = {k: [a for a in v if a not in drop_vars] for k, v in metrics.items()}
 
     grid = Grid(ds, coords=coords, metrics=metrics)
-    if expected_error:
-        with pytest.raises(expected_error):
-            metric = grid.get_metric(ds[data_var], axes)
-    else:
-        metric = grid.get_metric(ds[data_var], axes)
-        expected_metrics = [
-            ds[me].reset_coords(drop=True) for me in metric_expected_list
-        ]
-        expected = functools.reduce(operator.mul, expected_metrics, 1)
-        assert metric.equals(expected)
+    metric = grid.get_metric(ds[data_var], axes)
+    expected_metrics = [ds[me].reset_coords(drop=True) for me in metric_expected_list]
+    expected = functools.reduce(operator.mul, expected_metrics, 1)
+    assert metric.equals(expected)
 
 
 def test_get_metric_with_conditions_01():
@@ -190,72 +182,71 @@ def test_get_metric_with_conditions_01():
     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")
+@pytest.mark.parametrize("periodic", [True, False])
+def test_get_metric_with_conditions_02a(periodic):
+    # Condition 2, case a: interpolate metric with matching axis to desired dimensions
+    ds, coords, _ = datasets_grid_metric("C")
+    grid = Grid(ds, coords=coords, periodic=periodic, boundary="extend")
+    grid.set_metrics(("X", "Y"), "area_e")
 
     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)
+    expected_metric = grid.interp(ds["area_e"], ("X", "Y"))
 
     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")
+def test_get_metric_with_conditions_02b():
+    # Condition 2, 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_t")
-    grid.set_metrics(("Z"), "dz_t")
+    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.tracer, ("X", "Y", "Z"))
+    get_metric = grid.get_metric(ds.v, ("X", "Y"))
 
-    metric_var_1 = ds.area_t
-    metric_var_2 = ds.dz_t
-    expected_metric = (metric_var_1 * metric_var_2).reset_coords(drop=True)
+    expected_metric = grid.interp(ds["area_e"], ("X", "Y"))
 
     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")
+def test_get_metric_with_conditions_03a():
+    # Condition 3: use provided metrics with matching dimensions to calculate for required metric
+    ds, coords, metrics = 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"), "dy_n")
 
     get_metric = grid.get_metric(ds.v, ("X", "Y"))
 
-    expected_metric = grid.interp(ds["area_e"], ("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)
 
 
-@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")
+def test_get_metric_with_conditions_03b():
+    # Condition 3: use provided metrics with matching dimensions to calculate for required metric
+    ds, coords, metrics = 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")
+    grid.set_metrics(("X", "Y"), "area_t")
+    grid.set_metrics(("Z"), "dz_t")
 
-    get_metric = grid.get_metric(ds.v, ("X", "Y"))
+    get_metric = grid.get_metric(ds.tracer, ("X", "Y", "Z"))
 
-    expected_metric = grid.interp(ds["area_e"], ("X", "Y"))
+    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_04a():
-    # Condition 5, case a: 1 metric on the wrong position (must interpolate before multiplying)
+    # Condition 4, 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")
@@ -269,9 +260,8 @@ def test_get_metric_with_conditions_04a():
     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)
+    # Condition 4, case b: 2 metrics in the wrong position (must interpolate both before multiplying)
     ds, coords, _ = datasets_grid_metric("C")
     grid = Grid(ds, coords=coords)
     grid.set_metrics(("X"), "dx_t")