Repository: ESMValGroup/ESMValCore

Issue:
Erroneous use of `grid_latitude` and `grid_longitude`
In `esmvalcore.cmor._fixes.common.OceanFixGrid.fix_metadata` we introduce `grid_latitude` and `grid_longitude` as names for index coordinates. This is confusing because those names are used to identified rotated pole grids (see [CF-1.7](http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#_rotated_pole)). This leads to more confusion down the line in fixes to other ocean data.



Discussion:

Patch:
diff --git a/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1.py b/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1.py
index c61eeaa19..28593a9d2 100644
--- a/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1.py
+++ b/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1.py
@@ -1,68 +1,7 @@
 """Fixes for bcc-csm1-1."""
-import numpy as np
-from scipy.interpolate import InterpolatedUnivariateSpline
-from scipy.ndimage import map_coordinates
-
-from ..common import ClFixHybridPressureCoord
-from ..fix import Fix
-
+from ..common import ClFixHybridPressureCoord, OceanFixGrid
 
 Cl = ClFixHybridPressureCoord
 
 
-class Tos(Fix):
-    """Fixes for tos."""
-
-    def fix_data(self, cube):
-        """Fix data.
-
-        Calculate missing latitude/longitude boundaries using interpolation.
-
-        Parameters
-        ----------
-        cube: iris.cube.Cube
-            Input cube to fix.
-
-        Returns
-        -------
-        iris.cube.Cube
-
-        """
-        rlat = cube.coord('grid_latitude').points
-        rlon = cube.coord('grid_longitude').points
-
-        # Transform grid latitude/longitude to array indices [0, 1, 2, ...]
-        rlat_to_idx = InterpolatedUnivariateSpline(rlat,
-                                                   np.arange(len(rlat)),
-                                                   k=1)
-        rlon_to_idx = InterpolatedUnivariateSpline(rlon,
-                                                   np.arange(len(rlon)),
-                                                   k=1)
-        rlat_idx_bnds = rlat_to_idx(cube.coord('grid_latitude').bounds)
-        rlon_idx_bnds = rlon_to_idx(cube.coord('grid_longitude').bounds)
-
-        # Calculate latitude/longitude vertices by interpolation
-        lat_vertices = []
-        lon_vertices = []
-        for (i, j) in [(0, 0), (0, 1), (1, 1), (1, 0)]:
-            (rlat_v, rlon_v) = np.meshgrid(rlat_idx_bnds[:, i],
-                                           rlon_idx_bnds[:, j],
-                                           indexing='ij')
-            lat_vertices.append(
-                map_coordinates(cube.coord('latitude').points,
-                                [rlat_v, rlon_v],
-                                mode='nearest'))
-            lon_vertices.append(
-                map_coordinates(cube.coord('longitude').points,
-                                [rlat_v, rlon_v],
-                                mode='wrap'))
-        lat_vertices = np.array(lat_vertices)
-        lon_vertices = np.array(lon_vertices)
-        lat_vertices = np.moveaxis(lat_vertices, 0, -1)
-        lon_vertices = np.moveaxis(lon_vertices, 0, -1)
-
-        # Copy vertices to cube
-        cube.coord('latitude').bounds = lat_vertices
-        cube.coord('longitude').bounds = lon_vertices
-
-        return cube
+Tos = OceanFixGrid
diff --git a/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1_m.py b/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1_m.py
index 8a1ab262a..4794048f6 100644
--- a/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1_m.py
+++ b/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1_m.py
@@ -1,9 +1,7 @@
 """Fixes for bcc-csm1-1-m."""
-from ..common import ClFixHybridPressureCoord
-from .bcc_csm1_1 import Tos as BaseTos
-
+from ..common import ClFixHybridPressureCoord, OceanFixGrid
 
 Cl = ClFixHybridPressureCoord
 
 
-Tos = BaseTos
+Tos = OceanFixGrid
diff --git a/esmvalcore/cmor/_fixes/cmip6/bcc_csm2_mr.py b/esmvalcore/cmor/_fixes/cmip6/bcc_csm2_mr.py
index e084fbebb..79660aeb5 100644
--- a/esmvalcore/cmor/_fixes/cmip6/bcc_csm2_mr.py
+++ b/esmvalcore/cmor/_fixes/cmip6/bcc_csm2_mr.py
@@ -1,7 +1,5 @@
 """Fixes for BCC-CSM2-MR model."""
-from ..cmip5.bcc_csm1_1 import Tos as BaseTos
-from ..common import ClFixHybridPressureCoord
-
+from ..common import ClFixHybridPressureCoord, OceanFixGrid
 
 Cl = ClFixHybridPressureCoord
 
@@ -12,57 +10,7 @@ Cli = ClFixHybridPressureCoord
 Clw = ClFixHybridPressureCoord
 
 
-class Tos(BaseTos):
-    """Fixes for tos."""
-
-    def fix_metadata(self, cubes):
-        """Rename ``var_name`` of 1D-``latitude`` and 1D-``longitude``.
-        Parameters
-        ----------
-        cubes : iris.cube.CubeList
-            Input cubes.
-        Returns
-        -------
-        iris.cube.CubeList
-        """
-        cube = self.get_cube_from_list(cubes)
-        lat_coord = cube.coord('latitude', dimensions=(1, ))
-        lon_coord = cube.coord('longitude', dimensions=(2, ))
-        lat_coord.standard_name = None
-        lat_coord.long_name = 'grid_latitude'
-        lat_coord.var_name = 'i'
-        lat_coord.units = '1'
-        lon_coord.standard_name = None
-        lon_coord.long_name = 'grid_longitude'
-        lon_coord.var_name = 'j'
-        lon_coord.units = '1'
-        lon_coord.circular = False
-        return cubes
-
+Tos = OceanFixGrid
 
-class Siconc(BaseTos):
-    """Fixes for siconc."""
 
-    def fix_metadata(self, cubes):
-        """Rename ``var_name`` of 1D-``latitude`` and 1D-``longitude``.
-        Parameters
-        ----------
-        cubes : iris.cube.CubeList
-            Input cubes.
-        Returns
-        -------
-        iris.cube.CubeList
-        """
-        cube = self.get_cube_from_list(cubes)
-        lat_coord = cube.coord('latitude', dimensions=(1, ))
-        lon_coord = cube.coord('longitude', dimensions=(2, ))
-        lat_coord.standard_name = None
-        lat_coord.long_name = 'grid_latitude'
-        lat_coord.var_name = 'i'
-        lat_coord.units = '1'
-        lon_coord.standard_name = None
-        lon_coord.long_name = 'grid_longitude'
-        lon_coord.var_name = 'j'
-        lon_coord.units = '1'
-        lon_coord.circular = False
-        return cubes
+Siconc = OceanFixGrid
diff --git a/esmvalcore/cmor/_fixes/cmip6/bcc_esm1.py b/esmvalcore/cmor/_fixes/cmip6/bcc_esm1.py
index cae45b3f2..1a0603c81 100644
--- a/esmvalcore/cmor/_fixes/cmip6/bcc_esm1.py
+++ b/esmvalcore/cmor/_fixes/cmip6/bcc_esm1.py
@@ -1,7 +1,5 @@
 """Fixes for BCC-ESM1 model."""
-from ..common import ClFixHybridPressureCoord
-from .bcc_csm2_mr import Tos as BaseTos
-
+from ..common import ClFixHybridPressureCoord, OceanFixGrid
 
 Cl = ClFixHybridPressureCoord
 
@@ -12,4 +10,4 @@ Cli = ClFixHybridPressureCoord
 Clw = ClFixHybridPressureCoord
 
 
-Tos = BaseTos
+Tos = OceanFixGrid
diff --git a/esmvalcore/cmor/_fixes/cmip6/cesm2.py b/esmvalcore/cmor/_fixes/cmip6/cesm2.py
index 9b4bc499f..4ab1f0a4b 100644
--- a/esmvalcore/cmor/_fixes/cmip6/cesm2.py
+++ b/esmvalcore/cmor/_fixes/cmip6/cesm2.py
@@ -10,6 +10,7 @@ from ..shared import (
     add_scalar_height_coord,
     add_scalar_typeland_coord,
     add_scalar_typesea_coord,
+    fix_ocean_depth_coord,
 )
 from .gfdl_esm4 import Siconc as Addtypesi
 
@@ -243,3 +244,34 @@ class Tos(Fix):
 
 
 Siconc = Addtypesi
+
+
+class Omon(Fix):
+    """Fixes for ocean variables."""
+
+    def fix_metadata(self, cubes):
+        """Fix ocean depth coordinate.
+
+        Parameters
+        ----------
+        cubes: iris CubeList
+            List of cubes to fix
+
+        Returns
+        -------
+        iris.cube.CubeList
+
+        """
+        for cube in cubes:
+            if cube.coords(axis='Z'):
+                z_coord = cube.coord(axis='Z')
+
+                # Only points need to be fixed, not bounds
+                if z_coord.units == 'cm':
+                    z_coord.points = z_coord.core_points() / 100.0
+                    z_coord.units = 'm'
+
+                # Fix depth metadata
+                if z_coord.standard_name is None:
+                    fix_ocean_depth_coord(cube)
+        return cubes
diff --git a/esmvalcore/cmor/_fixes/cmip6/fgoals_g3.py b/esmvalcore/cmor/_fixes/cmip6/fgoals_g3.py
index c752ca207..69a6b6375 100644
--- a/esmvalcore/cmor/_fixes/cmip6/fgoals_g3.py
+++ b/esmvalcore/cmor/_fixes/cmip6/fgoals_g3.py
@@ -2,7 +2,6 @@
 from ..cmip5.fgoals_g2 import Cl as BaseCl
 from ..common import OceanFixGrid
 
-
 Cl = BaseCl
 
 
@@ -12,7 +11,31 @@ Cli = BaseCl
 Clw = BaseCl
 
 
-Tos = OceanFixGrid
+class Tos(OceanFixGrid):
+    """Fixes for tos."""
+
+    def fix_metadata(self, cubes):
+        """Fix metadata.
+
+        FGOALS-g3 data contain latitude and longitude data set to >1e30 in some
+        places.
+
+        Parameters
+        ----------
+        cubes : iris.cube.CubeList
+            Input cubes.
+
+        Returns
+        -------
+        iris.cube.CubeList
+
+        """
+        cube = self.get_cube_from_list(cubes)
+        cube.coord('latitude').points[
+            cube.coord('latitude').points > 1000.0] = 0.0
+        cube.coord('longitude').points[
+            cube.coord('longitude').points > 1000.0] = 0.0
+        return super().fix_metadata(cubes)
 
 
-Siconc = OceanFixGrid
+Siconc = Tos
diff --git a/esmvalcore/cmor/_fixes/common.py b/esmvalcore/cmor/_fixes/common.py
index 2e3165dc3..41c1d974a 100644
--- a/esmvalcore/cmor/_fixes/common.py
+++ b/esmvalcore/cmor/_fixes/common.py
@@ -1,4 +1,6 @@
 """Common fixes used for multiple datasets."""
+import logging
+
 import iris
 import numpy as np
 from scipy.ndimage import map_coordinates
@@ -6,6 +8,8 @@ from scipy.ndimage import map_coordinates
 from .fix import Fix
 from .shared import add_plev_from_altitude, fix_bounds
 
+logger = logging.getLogger(__name__)
+
 
 class ClFixHybridHeightCoord(Fix):
     """Fixes for ``cl`` regarding hybrid sigma height coordinates."""
@@ -108,49 +112,74 @@ class ClFixHybridPressureCoord(Fix):
 class OceanFixGrid(Fix):
     """Fixes for tos, siconc in FGOALS-g3."""
 
-    def fix_data(self, cube):
-        """
-        Fix data.
-
-        Calculate missing latitude/longitude boundaries using interpolation.
-        Based on a similar fix for BCC-CSM2-MR.
+    def fix_metadata(self, cubes):
+        """Fix ``latitude`` and ``longitude`` (metadata and bounds).
 
         Parameters
         ----------
-        cube: iris.cube.Cube
-            Input cube to fix.
+        cubes : iris.cube.CubeList
+            Input cubes.
 
         Returns
         -------
-        iris.cube.Cube
+        iris.cube.CubeList
+
         """
-        rlat = cube.coord('grid_latitude').points
-        rlon = cube.coord('grid_longitude').points
-
-        # Guess coordinate bounds in rlat, rlon (following BCC-CSM2-MR-1).
-        rlat_idx_bnds = np.zeros((len(rlat), 2))
-        rlat_idx_bnds[:, 0] = np.arange(len(rlat)) - 0.5
-        rlat_idx_bnds[:, 1] = np.arange(len(rlat)) + 0.5
-        rlat_idx_bnds[0, 0] = 0.
-        rlat_idx_bnds[len(rlat) - 1, 1] = len(rlat)
-        rlon_idx_bnds = np.zeros((len(rlon), 2))
-        rlon_idx_bnds[:, 0] = np.arange(len(rlon)) - 0.5
-        rlon_idx_bnds[:, 1] = np.arange(len(rlon)) + 0.5
-
-        # Calculate latitude/longitude vertices by interpolation
+        cube = self.get_cube_from_list(cubes)
+        if cube.ndim != 3:
+            logger.warning(
+                "OceanFixGrid is designed to work on any data with an "
+                "irregular ocean grid, but it was only tested on 3D (time, "
+                "latitude, longitude) data so far; got %dD data", cube.ndim)
+
+        # Get dimensional coordinates. Note:
+        # - First dimension i -> X-direction (= longitude)
+        # - Second dimension j -> Y-direction (= latitude)
+        (j_dim, i_dim) = sorted(set(
+            cube.coord_dims(cube.coord('latitude', dim_coords=False)) +
+            cube.coord_dims(cube.coord('longitude', dim_coords=False))
+        ))
+        i_coord = cube.coord(dim_coords=True, dimensions=i_dim)
+        j_coord = cube.coord(dim_coords=True, dimensions=j_dim)
+
+        # Fix metadata of coordinate i
+        i_coord.var_name = 'i'
+        i_coord.standard_name = None
+        i_coord.long_name = 'cell index along first dimension'
+        i_coord.units = '1'
+        i_coord.circular = False
+
+        # Fix metadata of coordinate j
+        j_coord.var_name = 'j'
+        j_coord.standard_name = None
+        j_coord.long_name = 'cell index along second dimension'
+        j_coord.units = '1'
+
+        # Fix points and bounds of index coordinates i and j
+        for idx_coord in (i_coord, j_coord):
+            idx_coord.points = np.arange(len(idx_coord.points))
+            idx_coord.bounds = None
+            idx_coord.guess_bounds()
+
+        # Calculate latitude/longitude vertices by interpolation.
+        # Following the CF conventions (see
+        # cfconventions.org/cf-conventions/cf-conventions.html#cell-boundaries)
+        # we go counter-clockwise around the cells and construct a grid of
+        # index values which are in turn used to interpolate longitudes and
+        # latitudes in the midpoints between the cell centers.
         lat_vertices = []
         lon_vertices = []
-        for (i, j) in [(0, 0), (0, 1), (1, 1), (1, 0)]:
-            (rlat_v, rlon_v) = np.meshgrid(rlat_idx_bnds[:, i],
-                                           rlon_idx_bnds[:, j],
-                                           indexing='ij')
+        for (j, i) in [(0, 0), (0, 1), (1, 1), (1, 0)]:
+            (j_v, i_v) = np.meshgrid(j_coord.bounds[:, j],
+                                     i_coord.bounds[:, i],
+                                     indexing='ij')
             lat_vertices.append(
                 map_coordinates(cube.coord('latitude').points,
-                                [rlat_v, rlon_v],
+                                [j_v, i_v],
                                 mode='nearest'))
             lon_vertices.append(
                 map_coordinates(cube.coord('longitude').points,
-                                [rlat_v, rlon_v],
+                                [j_v, i_v],
                                 mode='wrap'))
         lat_vertices = np.array(lat_vertices)
         lon_vertices = np.array(lon_vertices)
@@ -160,39 +189,5 @@ class OceanFixGrid(Fix):
         # Copy vertices to cube
         cube.coord('latitude').bounds = lat_vertices
         cube.coord('longitude').bounds = lon_vertices
-        return cube
-
-    def fix_metadata(self, cubes):
-        """
-        Rename ``var_name`` of 1D-``latitude`` and 1D-``longitude``.
-
-        Parameters
-        ----------
-        cubes : iris.cube.CubeList
-            Input cubes.
 
-        Returns
-        -------
-        iris.cube.CubeList
-        """
-        cube = self.get_cube_from_list(cubes)
-        lat_coord = cube.coord('cell index along second dimension',
-                               dimensions=(1, ))
-        lon_coord = cube.coord('cell index along first dimension',
-                               dimensions=(2, ))
-        lat_coord.standard_name = None
-        lat_coord.long_name = 'grid_latitude'
-        lat_coord.var_name = 'i'
-        lat_coord.units = '1'
-        lon_coord.standard_name = None
-        lon_coord.long_name = 'grid_longitude'
-        lon_coord.var_name = 'j'
-        lon_coord.units = '1'
-        lon_coord.circular = False
-        # FGOALS-g3 data contain latitude and longitude data set to
-        # >1e30 in some places. Set to 0. to avoid problem in check.py.
-        cube.coord('latitude').points[cube.coord('latitude').points > 1000.]\
-            = 0.
-        cube.coord('longitude').points[cube.coord('longitude').points > 1000.]\
-            = 0.
-        return cubes
+        return iris.cube.CubeList([cube])
diff --git a/esmvalcore/cmor/_fixes/fix.py b/esmvalcore/cmor/_fixes/fix.py
index 12aee7b9a..ba4175813 100644
--- a/esmvalcore/cmor/_fixes/fix.py
+++ b/esmvalcore/cmor/_fixes/fix.py
@@ -162,7 +162,7 @@ class Fix:
 
             classes = inspect.getmembers(fixes_module, inspect.isclass)
             classes = dict((name.lower(), value) for name, value in classes)
-            for fix_name in (short_name, 'allvars'):
+            for fix_name in (short_name, mip.lower(), 'allvars'):
                 try:
                     fixes.append(classes[fix_name](vardef))
                 except KeyError:
diff --git a/esmvalcore/cmor/_fixes/shared.py b/esmvalcore/cmor/_fixes/shared.py
index 62b9c76c8..de03d7885 100644
--- a/esmvalcore/cmor/_fixes/shared.py
+++ b/esmvalcore/cmor/_fixes/shared.py
@@ -517,3 +517,20 @@ def round_coordinates(cubes, decimals=5, coord_names=None):
                 coord.bounds = da.round(da.asarray(coord.core_bounds()),
                                         decimals)
     return cubes
+
+
+def fix_ocean_depth_coord(cube):
+    """Fix attributes of ocean vertical level coordinate.
+
+    Parameters
+    ----------
+    cube : iris.cube.Cube
+        Input cube.
+
+    """
+    depth_coord = cube.coord(axis='Z')
+    depth_coord.standard_name = 'depth'
+    depth_coord.var_name = 'lev'
+    depth_coord.units = 'm'
+    depth_coord.long_name = 'ocean depth coordinate'
+    depth_coord.attributes = {'positive': 'down'}


Test patch:
diff --git a/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1.py b/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1.py
index a55007923..dd38a6f36 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1.py
@@ -5,8 +5,12 @@ import iris
 import numpy as np
 
 from esmvalcore.cmor._fixes.cmip5.bcc_csm1_1 import Cl, Tos
-from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
+from esmvalcore.cmor._fixes.common import (
+    ClFixHybridPressureCoord,
+    OceanFixGrid,
+)
 from esmvalcore.cmor.fix import Fix
+from esmvalcore.cmor.table import get_var_info
 
 
 def test_get_cl_fix():
@@ -26,11 +30,16 @@ class TestTos(unittest.TestCase):
     def test_get(self):
         """Test fix get."""
         self.assertListEqual(
-            Fix.get_fixes('CMIP5', 'BCC-CSM1-1', 'Amon', 'tos'), [Tos(None)])
+            Fix.get_fixes('CMIP5', 'bcc-csm1-1', 'Amon', 'tos'), [Tos(None)])
 
 
-def test_tos_fix_data():
-    """Test ``fix_data`` for ``tos``."""
+def test_tos_fix():
+    """Test fix for ``tos``."""
+    assert Tos is OceanFixGrid
+
+
+def test_tos_fix_metadata():
+    """Test ``fix_metadata`` for ``tos``."""
     grid_lat = iris.coords.DimCoord(
         [20.0, 40.0],
         bounds=[[10.0, 30.0], [30.0, 50.0]],
@@ -55,22 +64,51 @@ def test_tos_fix_data():
         standard_name='longitude',
         units='degrees_east',
     )
+    time_coord = iris.coords.DimCoord(
+        1.0,
+        bounds=[0.0, 2.0],
+        var_name='time',
+        standard_name='time',
+        long_name='time',
+        units='days since 1950-01-01',
+    )
 
     # Create cube without bounds
     cube = iris.cube.Cube(
-        np.full((2, 3), 300.0),
+        np.full((1, 2, 3), 300.0),
         var_name='tos',
+        standard_name='sea_surface_temperature',
         units='K',
-        dim_coords_and_dims=[(grid_lat, 0), (grid_lon, 1)],
-        aux_coords_and_dims=[(latitude, (0, 1)), (longitude, (0, 1))],
+        dim_coords_and_dims=[(time_coord, 0), (grid_lat, 1), (grid_lon, 2)],
+        aux_coords_and_dims=[(latitude, (1, 2)), (longitude, (1, 2))],
     )
     assert cube.coord('latitude').bounds is None
     assert cube.coord('longitude').bounds is None
 
     # Apply fix
-    fix = Tos(None)
-    fixed_cube = fix.fix_data(cube)
+    vardef = get_var_info('CMIP6', 'Omon', 'tos')
+    fix = Tos(vardef)
+    cubes = iris.cube.CubeList([cube])
+    fixed_cubes = fix.fix_metadata(cubes)
+    assert len(fixed_cubes) == 1
+    fixed_cube = fixed_cubes.extract_cube('sea_surface_temperature')
     assert fixed_cube is cube
+    i_coord = fixed_cube.coord('cell index along first dimension')
+    j_coord = fixed_cube.coord('cell index along second dimension')
+    assert i_coord.var_name == 'i'
+    assert i_coord.standard_name is None
+    assert i_coord.long_name == 'cell index along first dimension'
+    assert i_coord.units == '1'
+    assert i_coord.circular is False
+    assert j_coord.var_name == 'j'
+    assert j_coord.standard_name is None
+    assert j_coord.long_name == 'cell index along second dimension'
+    assert j_coord.units == '1'
+    np.testing.assert_allclose(i_coord.points, [0, 1, 2])
+    np.testing.assert_allclose(i_coord.bounds,
+                               [[-0.5, 0.5], [0.5, 1.5], [1.5, 2.5]])
+    np.testing.assert_allclose(j_coord.points, [0, 1])
+    np.testing.assert_allclose(j_coord.bounds, [[-0.5, 0.5], [0.5, 1.5]])
     assert fixed_cube.coord('latitude').bounds is not None
     assert fixed_cube.coord('longitude').bounds is not None
     latitude_bounds = np.array([[[-40, -33.75, -23.75, -30.0],
diff --git a/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1_m.py b/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1_m.py
index ce8af0eeb..8473c84f4 100644
--- a/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1_m.py
+++ b/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1_m.py
@@ -1,9 +1,11 @@
 """Test fixes for bcc-csm1-1-m."""
 import unittest
 
-from esmvalcore.cmor._fixes.cmip5.bcc_csm1_1 import Tos as BaseTos
 from esmvalcore.cmor._fixes.cmip5.bcc_csm1_1_m import Cl, Tos
-from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
+from esmvalcore.cmor._fixes.common import (
+    ClFixHybridPressureCoord,
+    OceanFixGrid,
+)
 from esmvalcore.cmor._fixes.fix import Fix
 
 
@@ -29,4 +31,4 @@ class TestTos(unittest.TestCase):
 
 def test_tos_fix():
     """Test fix for ``tos``."""
-    assert Tos is BaseTos
+    assert Tos is OceanFixGrid
diff --git a/tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py b/tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py
index 21aa51dfc..7657b663d 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py
@@ -1,13 +1,10 @@
 """Test fixes for BCC-CSM2-MR."""
-import unittest.mock
-
-import iris
-
-from esmvalcore.cmor._fixes.cmip6.bcc_csm2_mr import (Cl, Cli,
-                                                      Clw, Tos, Siconc)
-from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
+from esmvalcore.cmor._fixes.cmip6.bcc_csm2_mr import Cl, Cli, Clw, Siconc, Tos
+from esmvalcore.cmor._fixes.common import (
+    ClFixHybridPressureCoord,
+    OceanFixGrid,
+)
 from esmvalcore.cmor._fixes.fix import Fix
-from esmvalcore.cmor.table import get_var_info
 
 
 def test_get_cl_fix():
@@ -49,146 +46,17 @@ def test_get_tos_fix():
     assert fix == [Tos(None)]
 
 
+def test_tos_fix():
+    """Test fix for ``tos``."""
+    assert Tos is OceanFixGrid
+
+
 def test_get_siconc_fix():
     """Test getting of fix."""
-    fix = Fix.get_fixes('CMIP6', 'BCC-CSM2-MR', 'Omon', 'siconc')
+    fix = Fix.get_fixes('CMIP6', 'BCC-CSM2-MR', 'SImon', 'siconc')
     assert fix == [Siconc(None)]
 
 
-@unittest.mock.patch(
-    'esmvalcore.cmor._fixes.cmip6.bcc_csm2_mr.BaseTos.fix_data',
-    autospec=True)
-def test_tos_fix_data(mock_base_fix_data):
-    """Test ``fix_data`` for ``tos``."""
-    fix = Tos(None)
-    fix.fix_data('cubes')
-    mock_base_fix_data.assert_called_once_with(fix, 'cubes')
-
-
-@unittest.mock.patch(
-    'esmvalcore.cmor._fixes.cmip6.bcc_csm2_mr.BaseTos.fix_data',
-    autospec=True)
-def test_siconc_fix_data(mock_base_fix_data):
-    """Test ``fix_data`` for ``siconc``."""
-    fix = Siconc(None)
-    fix.fix_data('cubes')
-    mock_base_fix_data.assert_called_once_with(fix, 'cubes')
-
-
-def test_tos_fix_metadata():
-    """Test ``fix_metadata`` for ``tos``."""
-    grid_lat = iris.coords.DimCoord([1.0],
-                                    var_name='lat',
-                                    standard_name='latitude',
-                                    long_name='latitude',
-                                    units='degrees_north',
-                                    attributes={'1D': '1'})
-    grid_lon = iris.coords.DimCoord([1.0],
-                                    var_name='lon',
-                                    standard_name='longitude',
-                                    long_name='longitude',
-                                    units='degrees_east',
-                                    circular=True,
-                                    attributes={'1D': '1'})
-    latitude = iris.coords.AuxCoord([[0.0]],
-                                    var_name='lat',
-                                    standard_name='latitude',
-                                    long_name='latitude',
-                                    units='degrees_north')
-    longitude = iris.coords.AuxCoord([[0]],
-                                     var_name='lon',
-                                     standard_name='longitude',
-                                     long_name='longitude',
-                                     units='degrees_east')
-    cube = iris.cube.Cube(
-        [[[0.0]]],
-        var_name='tos',
-        long_name='sea_surface_temperature',
-        dim_coords_and_dims=[(grid_lat.copy(), 1), (grid_lon.copy(), 2)],
-        aux_coords_and_dims=[(latitude.copy(), (1, 2)),
-                             (longitude.copy(), (1, 2))],
-    )
-    cubes = iris.cube.CubeList([cube, iris.cube.Cube(0.0)])
-    vardef = get_var_info('CMIP6', 'Omon', 'tos')
-    fix = Tos(vardef)
-    fixed_cubes = fix.fix_metadata(cubes)
-    tos_cube = fixed_cubes.extract_cube('sea_surface_temperature')
-
-    # No duplicates anymore
-    assert len(tos_cube.coords('latitude')) == 1
-    assert len(tos_cube.coords('longitude')) == 1
-
-    # Latitude
-    grid_lat = tos_cube.coord('grid_latitude')
-    assert grid_lat.var_name == 'i'
-    assert grid_lat.long_name == 'grid_latitude'
-    assert grid_lat.standard_name is None
-    assert grid_lat.units == '1'
-
-    # Longitude
-    grid_lon = tos_cube.coord('grid_longitude')
-    assert grid_lon.var_name == 'j'
-    assert grid_lon.long_name == 'grid_longitude'
-    assert grid_lon.standard_name is None
-    assert grid_lon.units == '1'
-    assert not grid_lon.circular
-
-
-def test_siconc_fix_metadata():
-    """Test ``fix_metadata`` for ``tos``."""
-    grid_lat = iris.coords.DimCoord([1.0],
-                                    var_name='lat',
-                                    standard_name='latitude',
-                                    long_name='latitude',
-                                    units='degrees_north',
-                                    attributes={'1D': '1'})
-    grid_lon = iris.coords.DimCoord([1.0],
-                                    var_name='lon',
-                                    standard_name='longitude',
-                                    long_name='longitude',
-                                    units='degrees_east',
-                                    circular=True,
-                                    attributes={'1D': '1'})
-    latitude = iris.coords.AuxCoord([[0.0]],
-                                    var_name='lat',
-                                    standard_name='latitude',
-                                    long_name='latitude',
-                                    units='degrees_north')
-    longitude = iris.coords.AuxCoord([[0]],
-                                     var_name='lon',
-                                     standard_name='longitude',
-                                     long_name='longitude',
-                                     units='degrees_east')
-
-    cube = iris.cube.Cube(
-        [[[0.0]]],
-        var_name='siconc',
-        long_name='sea_ice_area_fraction',
-        dim_coords_and_dims=[(grid_lat.copy(), 1), (grid_lon.copy(), 2)],
-        aux_coords_and_dims=[(latitude.copy(), (1, 2)),
-                             (longitude.copy(), (1, 2))],
-    )
-    cubes = iris.cube.CubeList([cube, iris.cube.Cube(0.0)])
-    vardef = get_var_info('CMIP6', 'SImon', 'siconc')
-    fix = Siconc(vardef)
-    fixed_cubes = fix.fix_metadata(cubes)
-    siconc_cube = fixed_cubes.extract_cube('sea_ice_area_fraction')
-
-    # No duplicates anymore
-    assert len(siconc_cube.coords('latitude')) == 1
-    assert len(siconc_cube.coords('longitude')) == 1
-
-    # Latitude
-    grid_lat = siconc_cube.coord('grid_latitude')
-    assert grid_lat.var_name == 'i'
-    assert grid_lat.long_name == 'grid_latitude'
-    assert grid_lat.standard_name is None
-    assert grid_lat.units == '1'
-
-    # Longitude
-    grid_lon = siconc_cube.coord('grid_longitude')
-    assert grid_lon.var_name == 'j'
-    assert grid_lon.long_name == 'grid_longitude'
-    assert grid_lon.standard_name is None
-    assert grid_lon.units == '1'
-    assert not grid_lon.circular
+def test_siconc_fix():
+    """Test fix for ``siconc``."""
+    assert Siconc is OceanFixGrid
diff --git a/tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py b/tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py
index c6ce25135..d4ceec1d8 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py
@@ -1,7 +1,9 @@
 """Test fixes for BCC-ESM1."""
-from esmvalcore.cmor._fixes.cmip6.bcc_csm2_mr import Tos as BaseTos
 from esmvalcore.cmor._fixes.cmip6.bcc_esm1 import Cl, Cli, Clw, Tos
-from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord
+from esmvalcore.cmor._fixes.common import (
+    ClFixHybridPressureCoord,
+    OceanFixGrid,
+)
 from esmvalcore.cmor._fixes.fix import Fix
 
 
@@ -46,4 +48,4 @@ def test_get_tos_fix():
 
 def test_tos_fix():
     """Test fix for ``tos``."""
-    assert Tos is BaseTos
+    assert Tos is OceanFixGrid
diff --git a/tests/integration/cmor/_fixes/cmip6/test_cesm2.py b/tests/integration/cmor/_fixes/cmip6/test_cesm2.py
index fec9c8c05..dcddc43f3 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_cesm2.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_cesm2.py
@@ -8,7 +8,7 @@ import numpy as np
 import pytest
 from cf_units import Unit
 
-from esmvalcore.cmor._fixes.cmip6.cesm2 import Cl, Cli, Clw, Tas, Tos
+from esmvalcore.cmor._fixes.cmip6.cesm2 import Cl, Cli, Clw, Omon, Tas, Tos
 from esmvalcore.cmor.fix import Fix
 from esmvalcore.cmor.table import get_var_info
 
@@ -225,6 +225,34 @@ def tos_cubes():
     return iris.cube.CubeList([tos_cube])
 
 
+@pytest.fixture
+def thetao_cubes():
+    """Cubes to test fixes for ``thetao``."""
+    time_coord = iris.coords.DimCoord(
+        [0.0004, 1.09776], var_name='time', standard_name='time',
+        units='days since 1850-01-01 00:00:00')
+    lat_coord = iris.coords.DimCoord(
+        [0.0, 1.0], var_name='lat', standard_name='latitude', units='degrees')
+    lon_coord = iris.coords.DimCoord(
+        [0.0, 1.0], var_name='lon', standard_name='longitude', units='degrees')
+    lev_coord = iris.coords.DimCoord(
+        [500.0, 1000.0], bounds=[[2.5, 7.5], [7.5, 12.5]],
+        var_name='lev', standard_name=None, units='cm',
+        attributes={'positive': 'up'})
+    coord_specs = [
+        (time_coord, 0),
+        (lev_coord, 1),
+        (lat_coord, 2),
+        (lon_coord, 3),
+    ]
+    thetao_cube = iris.cube.Cube(
+        np.ones((2, 2, 2, 2)),
+        var_name='thetao',
+        dim_coords_and_dims=coord_specs,
+    )
+    return iris.cube.CubeList([thetao_cube])
+
+
 def test_get_tas_fix():
     """Test getting of fix."""
     fix = Fix.get_fixes('CMIP6', 'CESM2', 'Amon', 'tas')
@@ -233,8 +261,14 @@ def test_get_tas_fix():
 
 def test_get_tos_fix():
     """Test getting of fix."""
-    fix = Fix.get_fixes('CMIP6', 'CESM2', 'Amon', 'tos')
-    assert fix == [Tos(None)]
+    fix = Fix.get_fixes('CMIP6', 'CESM2', 'Omon', 'tos')
+    assert fix == [Tos(None), Omon(None)]
+
+
+def test_get_thetao_fix():
+    """Test getting of fix."""
+    fix = Fix.get_fixes('CMIP6', 'CESM2', 'Omon', 'thetao')
+    assert fix == [Omon(None)]
 
 
 def test_tas_fix_metadata(tas_cubes):
@@ -271,3 +305,25 @@ def test_tos_fix_metadata(tos_cubes):
     assert out_cubes is tos_cubes
     for cube in out_cubes:
         np.testing.assert_equal(cube.coord("time").points, [0., 1.1])
+
+
+def test_thetao_fix_metadata(thetao_cubes):
+    """Test ``fix_metadata`` for ``thetao``."""
+    vardef = get_var_info('CMIP6', 'Omon', 'thetao')
+    fix = Omon(vardef)
+    out_cubes = fix.fix_metadata(thetao_cubes)
+    assert out_cubes is thetao_cubes
+    assert len(out_cubes) == 1
+    out_cube = out_cubes[0]
+
+    # Check metadata of depth coordinate
+    depth_coord = out_cube.coord('depth')
+    assert depth_coord.standard_name == 'depth'
+    assert depth_coord.var_name == 'lev'
+    assert depth_coord.long_name == 'ocean depth coordinate'
+    assert depth_coord.units == 'm'
+    assert depth_coord.attributes == {'positive': 'down'}
+
+    # Check values of depth coordinate
+    np.testing.assert_allclose(depth_coord.points, [5.0, 10.0])
+    np.testing.assert_allclose(depth_coord.bounds, [[2.5, 7.5], [7.5, 12.5]])
diff --git a/tests/integration/cmor/_fixes/cmip6/test_fgoals_g3.py b/tests/integration/cmor/_fixes/cmip6/test_fgoals_g3.py
index bb0f2060f..c134ae122 100644
--- a/tests/integration/cmor/_fixes/cmip6/test_fgoals_g3.py
+++ b/tests/integration/cmor/_fixes/cmip6/test_fgoals_g3.py
@@ -1,7 +1,14 @@
 """Tests for the fixes of FGOALS-g3."""
+from unittest import mock
+
+import iris
+import numpy as np
+
 from esmvalcore.cmor._fixes.cmip5.fgoals_g2 import Cl as BaseCl
-from esmvalcore.cmor._fixes.cmip6.fgoals_g3 import Cl, Cli, Clw
+from esmvalcore.cmor._fixes.cmip6.fgoals_g3 import Cl, Cli, Clw, Siconc, Tos
+from esmvalcore.cmor._fixes.common import OceanFixGrid
 from esmvalcore.cmor.fix import Fix
+from esmvalcore.cmor.table import get_var_info
 
 
 def test_get_cl_fix():
@@ -35,3 +42,56 @@ def test_get_clw_fix():
 def test_clw_fix():
     """Test fix for ``clw``."""
     assert Clw is BaseCl
+
+
+def test_get_tos_fix():
+    """Test getting of fix."""
+    fix = Fix.get_fixes('CMIP6', 'BCC-CSM2-MR', 'Omon', 'tos')
+    assert fix == [Tos(None)]
+
+
+def test_tos_fix():
+    """Test fix for ``tos``."""
+    assert issubclass(Tos, OceanFixGrid)
+
+
+@mock.patch('esmvalcore.cmor._fixes.cmip6.fgoals_g3.OceanFixGrid.fix_metadata',
+            autospec=True)
+def test_tos_fix_metadata(mock_base_fix_metadata):
+    """Test ``fix_metadata`` for ``tos``."""
+    mock_base_fix_metadata.side_effect = lambda x, y: y
+
+    # Create test cube
+    lat_coord = iris.coords.AuxCoord([3.14, 1200.0, 6.28],
+                                     var_name='lat',
+                                     standard_name='latitude')
+    lon_coord = iris.coords.AuxCoord([1.0, 2.0, 1e30],
+                                     var_name='lon',
+                                     standard_name='longitude')
+    cube = iris.cube.Cube([1.0, 2.0, 3.0], var_name='tos',
+                          standard_name='sea_surface_temperature',
+                          aux_coords_and_dims=[(lat_coord, 0), (lon_coord, 0)])
+    cubes = iris.cube.CubeList([cube])
+
+    # Apply fix
+    vardef = get_var_info('CMIP6', 'Omon', 'tos')
+    fix = Tos(vardef)
+    fixed_cubes = fix.fix_metadata(cubes)
+    assert len(fixed_cubes) == 1
+    fixed_cube = fixed_cubes[0]
+    np.testing.assert_allclose(fixed_cube.coord('latitude').points,
+                               [3.14, 0.0, 6.28])
+    np.testing.assert_allclose(fixed_cube.coord('longitude').points,
+                               [1.0, 2.0, 0.0])
+    mock_base_fix_metadata.assert_called_once_with(fix, cubes)
+
+
+def test_get_siconc_fix():
+    """Test getting of fix."""
+    fix = Fix.get_fixes('CMIP6', 'BCC-CSM2-MR', 'SImon', 'siconc')
+    assert fix == [Siconc(None)]
+
+
+def test_siconc_fix():
+    """Test fix for ``siconc``."""
+    assert Siconc is Tos
diff --git a/tests/integration/cmor/_fixes/test_common.py b/tests/integration/cmor/_fixes/test_common.py
index 70190c1fc..42966fd0b 100644
--- a/tests/integration/cmor/_fixes/test_common.py
+++ b/tests/integration/cmor/_fixes/test_common.py
@@ -6,6 +6,7 @@ import pytest
 from esmvalcore.cmor._fixes.common import (
     ClFixHybridHeightCoord,
     ClFixHybridPressureCoord,
+    OceanFixGrid,
 )
 from esmvalcore.cmor.table import get_var_info
 from esmvalcore.iris_helpers import var_name_constraint
@@ -166,3 +167,205 @@ def test_cl_hybrid_height_coord_fix_metadata(test_data_path):
     nc_path = test_data_path / 'common_cl_hybrid_height.nc'
     hybrid_height_coord_fix_metadata(nc_path, 'cl',
                                      ClFixHybridHeightCoord(vardef))
+
+
+def get_tos_cubes(wrong_ij_names=False, ij_bounds=False):
+    """Cubes containing tos variable."""
+    if wrong_ij_names:
+        j_var_name = 'lat'
+        j_long_name = 'latitude'
+        i_var_name = 'lon'
+        i_long_name = 'longitude'
+    else:
+        j_var_name = 'j'
+        j_long_name = 'cell index along second dimension'
+        i_var_name = 'i'
+        i_long_name = 'cell index along first dimension'
+    if ij_bounds:
+        j_bounds = [[10.0, 30.0], [30.0, 50.0]]
+        i_bounds = [[5.0, 15.0], [15.0, 25.0], [25.0, 35.0]]
+    else:
+        j_bounds = None
+        i_bounds = None
+    j_coord = iris.coords.DimCoord(
+        [20.0, 40.0],
+        bounds=j_bounds,
+        var_name=j_var_name,
+        long_name=j_long_name,
+    )
+    i_coord = iris.coords.DimCoord(
+        [10.0, 20.0, 30.0],
+        bounds=i_bounds,
+        var_name=i_var_name,
+        long_name=i_long_name,
+    )
+    lat_coord = iris.coords.AuxCoord(
+        [[-40.0, -20.0, 0.0], [-20.0, 0.0, 20.0]],
+        var_name='lat',
+        standard_name='latitude',
+        units='degrees_north',
+    )
+    lon_coord = iris.coords.AuxCoord(
+        [[100.0, 140.0, 180.0], [80.0, 100.0, 120.0]],
+        var_name='lon',
+        standard_name='longitude',
+        units='degrees_east',
+    )
+    time_coord = iris.coords.DimCoord(
+        1.0,
+        bounds=[0.0, 2.0],
+        var_name='time',
+        standard_name='time',
+        long_name='time',
+        units='days since 1950-01-01',
+    )
+
+    # Create tos variable cube
+    cube = iris.cube.Cube(
+        np.full((1, 2, 3), 300.0),
+        var_name='tos',
+        long_name='sea_surface_temperature',
+        units='K',
+        dim_coords_and_dims=[(time_coord, 0), (j_coord, 1), (i_coord, 2)],
+        aux_coords_and_dims=[(lat_coord, (1, 2)), (lon_coord, (1, 2))],
+    )
+
+    # Create empty (dummy) cube
+    empty_cube = iris.cube.Cube(0.0)
+    return iris.cube.CubeList([cube, empty_cube])
+
+
+@pytest.fixture
+def tos_cubes_wrong_ij_names():
+    """Cubes with wrong ij names."""
+    return get_tos_cubes(wrong_ij_names=True, ij_bounds=True)
+
+
+def test_ocean_fix_grid_wrong_ij_names(tos_cubes_wrong_ij_names):
+    """Test ``fix_metadata`` with cubes with wrong ij names."""
+    cube_in = tos_cubes_wrong_ij_names.extract_cube('sea_surface_temperature')
+    assert len(cube_in.coords('latitude')) == 2
+    assert len(cube_in.coords('longitude')) == 2
+    assert cube_in.coord('latitude', dimensions=1).bounds is not None
+    assert cube_in.coord('longitude', dimensions=2).bounds is not None
+    assert cube_in.coord('latitude', dimensions=(1, 2)).bounds is None
+    assert cube_in.coord('longitude', dimensions=(1, 2)).bounds is None
+
+    # Apply fix
+    vardef = get_var_info('CMIP6', 'Omon', 'tos')
+    fix = OceanFixGrid(vardef)
+    fixed_cubes = fix.fix_metadata(tos_cubes_wrong_ij_names)
+    assert len(fixed_cubes) == 1
+    fixed_cube = fixed_cubes.extract_cube('sea_surface_temperature')
+    assert fixed_cube is cube_in
+
+    # Check ij names
+    i_coord = fixed_cube.coord('cell index along first dimension')
+    j_coord = fixed_cube.coord('cell index along second dimension')
+    assert i_coord.var_name == 'i'
+    assert i_coord.standard_name is None
+    assert i_coord.long_name == 'cell index along first dimension'
+    assert i_coord.units == '1'
+    assert i_coord.circular is False
+    assert j_coord.var_name == 'j'
+    assert j_coord.standard_name is None
+    assert j_coord.long_name == 'cell index along second dimension'
+    assert j_coord.units == '1'
+
+    # Check ij points and bounds
+    np.testing.assert_allclose(i_coord.points, [0, 1, 2])
+    np.testing.assert_allclose(i_coord.bounds,
+                               [[-0.5, 0.5], [0.5, 1.5], [1.5, 2.5]])
+    np.testing.assert_allclose(j_coord.points, [0, 1])
+    np.testing.assert_allclose(j_coord.bounds, [[-0.5, 0.5], [0.5, 1.5]])
+
+    # Check bounds of latitude and longitude
+    assert len(fixed_cube.coords('latitude')) == 1
+    assert len(fixed_cube.coords('longitude')) == 1
+    assert fixed_cube.coord('latitude').bounds is not None
+    assert fixed_cube.coord('longitude').bounds is not None
+    latitude_bounds = np.array([[[-40, -33.75, -23.75, -30.0],
+                                 [-33.75, -6.25, 3.75, -23.75],
+                                 [-6.25, -1.02418074021670e-14, 10.0, 3.75]],
+                                [[-30.0, -23.75, -13.75, -20.0],
+                                 [-23.75, 3.75, 13.75, -13.75],
+                                 [3.75, 10.0, 20.0, 13.75]]])
+    np.testing.assert_allclose(fixed_cube.coord('latitude').bounds,
+                               latitude_bounds)
+    longitude_bounds = np.array([[[140.625, 99.375, 99.375, 140.625],
+                                  [99.375, 140.625, 140.625, 99.375],
+                                  [140.625, 99.375, 99.375, 140.625]],
+                                 [[140.625, 99.375, 99.375, 140.625],
+                                  [99.375, 140.625, 140.625, 99.375],
+                                  [140.625, 99.375, 99.375, 140.625]]])
+    np.testing.assert_allclose(fixed_cube.coord('longitude').bounds,
+                               longitude_bounds)
+
+
+@pytest.fixture
+def tos_cubes_no_ij_bounds():
+    """Cubes with no ij bounds."""
+    return get_tos_cubes(wrong_ij_names=False, ij_bounds=False)
+
+
+def test_ocean_fix_grid_no_ij_bounds(tos_cubes_no_ij_bounds):
+    """Test ``fix_metadata`` with cubes with no ij bounds."""
+    cube_in = tos_cubes_no_ij_bounds.extract_cube('sea_surface_temperature')
+    assert len(cube_in.coords('latitude')) == 1
+    assert len(cube_in.coords('longitude')) == 1
+    assert cube_in.coord('latitude').bounds is None
+    assert cube_in.coord('longitude').bounds is None
+    assert cube_in.coord('cell index along first dimension').var_name == 'i'
+    assert cube_in.coord('cell index along second dimension').var_name == 'j'
+    assert cube_in.coord('cell index along first dimension').bounds is None
+    assert cube_in.coord('cell index along second dimension').bounds is None
+
+    # Apply fix
+    vardef = get_var_info('CMIP6', 'Omon', 'tos')
+    fix = OceanFixGrid(vardef)
+    fixed_cubes = fix.fix_metadata(tos_cubes_no_ij_bounds)
+    assert len(fixed_cubes) == 1
+    fixed_cube = fixed_cubes.extract_cube('sea_surface_temperature')
+    assert fixed_cube is cube_in
+
+    # Check ij names
+    i_coord = fixed_cube.coord('cell index along first dimension')
+    j_coord = fixed_cube.coord('cell index along second dimension')
+    assert i_coord.var_name == 'i'
+    assert i_coord.standard_name is None
+    assert i_coord.long_name == 'cell index along first dimension'
+    assert i_coord.units == '1'
+    assert i_coord.circular is False
+    assert j_coord.var_name == 'j'
+    assert j_coord.standard_name is None
+    assert j_coord.long_name == 'cell index along second dimension'
+    assert j_coord.units == '1'
+
+    # Check ij points and bounds
+    np.testing.assert_allclose(i_coord.points, [0, 1, 2])
+    np.testing.assert_allclose(i_coord.bounds,
+                               [[-0.5, 0.5], [0.5, 1.5], [1.5, 2.5]])
+    np.testing.assert_allclose(j_coord.points, [0, 1])
+    np.testing.assert_allclose(j_coord.bounds, [[-0.5, 0.5], [0.5, 1.5]])
+
+    # Check bounds of latitude and longitude
+    assert len(fixed_cube.coords('latitude')) == 1
+    assert len(fixed_cube.coords('longitude')) == 1
+    assert fixed_cube.coord('latitude').bounds is not None
+    assert fixed_cube.coord('longitude').bounds is not None
+    latitude_bounds = np.array([[[-40, -33.75, -23.75, -30.0],
+                                 [-33.75, -6.25, 3.75, -23.75],
+                                 [-6.25, -1.02418074021670e-14, 10.0, 3.75]],
+                                [[-30.0, -23.75, -13.75, -20.0],
+                                 [-23.75, 3.75, 13.75, -13.75],
+                                 [3.75, 10.0, 20.0, 13.75]]])
+    np.testing.assert_allclose(fixed_cube.coord('latitude').bounds,
+                               latitude_bounds)
+    longitude_bounds = np.array([[[140.625, 99.375, 99.375, 140.625],
+                                  [99.375, 140.625, 140.625, 99.375],
+                                  [140.625, 99.375, 99.375, 140.625]],
+                                 [[140.625, 99.375, 99.375, 140.625],
+                                  [99.375, 140.625, 140.625, 99.375],
+                                  [140.625, 99.375, 99.375, 140.625]]])
+    np.testing.assert_allclose(fixed_cube.coord('longitude').bounds,
+                               longitude_bounds)
diff --git a/tests/integration/cmor/_fixes/test_fix.py b/tests/integration/cmor/_fixes/test_fix.py
index 019991150..5ffc4ca86 100644
--- a/tests/integration/cmor/_fixes/test_fix.py
+++ b/tests/integration/cmor/_fixes/test_fix.py
@@ -1,3 +1,5 @@
+"""Integration tests for fixes."""
+
 import os
 import shutil
 import tempfile
@@ -6,35 +8,35 @@ import unittest
 import pytest
 from iris.cube import Cube
 
+from esmvalcore.cmor._fixes.cmip5.bnu_esm import Ch4
+from esmvalcore.cmor._fixes.cmip5.canesm2 import FgCo2
+from esmvalcore.cmor._fixes.cmip5.cesm1_bgc import Gpp
+from esmvalcore.cmor._fixes.cmip6.cesm2 import Omon, Tos
 from esmvalcore.cmor.fix import Fix
 
 
 class TestFix(unittest.TestCase):
     def setUp(self):
-        """Set up temp folder"""
+        """Set up temp folder."""
         self.temp_folder = tempfile.mkdtemp()
 
     def tearDown(self):
-        """Remove temp folder"""
+        """Remove temp folder."""
         shutil.rmtree(self.temp_folder)
 
     def test_get_fix(self):
-        from esmvalcore.cmor._fixes.cmip5.canesm2 import FgCo2
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'CanESM2', 'Amon', 'fgco2'), [FgCo2(None)])
 
     def test_get_fix_case_insensitive(self):
-        from esmvalcore.cmor._fixes.cmip5.canesm2 import FgCo2
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'CanESM2', 'Amon', 'fgCo2'), [FgCo2(None)])
 
     def test_get_fixes_with_replace(self):
-        from esmvalcore.cmor._fixes.cmip5.bnu_esm import Ch4
         self.assertListEqual(Fix.get_fixes('CMIP5', 'BNU-ESM', 'Amon', 'ch4'),
                              [Ch4(None)])
 
     def test_get_fixes_with_generic(self):
-        from esmvalcore.cmor._fixes.cmip5.cesm1_bgc import Gpp
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'CESM1-BGC', 'Amon', 'gpp'), [Gpp(None)])
 
@@ -50,6 +52,19 @@ class TestFix(unittest.TestCase):
         self.assertListEqual(
             Fix.get_fixes('CMIP5', 'BNU-ESM', 'Amon', 'BAD_VAR'), [])
 
+    def test_get_fix_only_mip(self):
+        self.assertListEqual(
+            Fix.get_fixes('CMIP6', 'CESM2', 'Omon', 'thetao'), [Omon(None)])
+
+    def test_get_fix_only_mip_case_insensitive(self):
+        self.assertListEqual(
+            Fix.get_fixes('CMIP6', 'CESM2', 'omOn', 'thetao'), [Omon(None)])
+
+    def test_get_fix_mip_and_var(self):
+        self.assertListEqual(
+            Fix.get_fixes('CMIP6', 'CESM2', 'Omon', 'tos'),
+            [Tos(None), Omon(None)])
+
     def test_fix_metadata(self):
         cube = Cube([0])
         reference = Cube([0])
diff --git a/tests/integration/cmor/_fixes/test_shared.py b/tests/integration/cmor/_fixes/test_shared.py
index 687bfd4f1..b185c7c92 100644
--- a/tests/integration/cmor/_fixes/test_shared.py
+++ b/tests/integration/cmor/_fixes/test_shared.py
@@ -15,6 +15,7 @@ from esmvalcore.cmor._fixes.shared import (
     add_sigma_factory,
     cube_to_aux_coord,
     fix_bounds,
+    fix_ocean_depth_coord,
     get_altitude_to_pressure_func,
     get_bounds_cube,
     get_pressure_to_altitude_func,
@@ -500,3 +501,18 @@ def test_round_coordinates_single_coord():
     assert cubes[0].coord('longitude') is out[0].coord('longitude')
     np.testing.assert_allclose(out[0].coord('latitude').points, [10])
     np.testing.assert_allclose(out[0].coord('latitude').bounds, [[9, 11]])
+
+
+def test_fix_ocean_depth_coord():
+    """Test `fix_ocean_depth_coord`."""
+    z_coord = iris.coords.DimCoord(0.0, var_name='alt',
+                                   attributes={'positive': 'up'})
+    cube = iris.cube.Cube([0.0], var_name='x',
+                          dim_coords_and_dims=[(z_coord, 0)])
+    fix_ocean_depth_coord(cube)
+    depth_coord = cube.coord('depth')
+    assert depth_coord.standard_name == 'depth'
+    assert depth_coord.var_name == 'lev'
+    assert depth_coord.units == 'm'
+    assert depth_coord.long_name == 'ocean depth coordinate'
+    assert depth_coord.attributes == {'positive': 'down'}