diff --git a/esmvalcore/preprocessor/_regrid.py b/esmvalcore/preprocessor/_regrid.py index 3cf0e057de..ea505fbbdf 100644 --- a/esmvalcore/preprocessor/_regrid.py +++ b/esmvalcore/preprocessor/_regrid.py @@ -47,7 +47,7 @@ _LON_RANGE = _LON_MAX - _LON_MIN # A cached stock of standard horizontal target grids. -_CACHE: Dict[str, iris.cube.Cube] = dict() +_CACHE: Dict[str, iris.cube.Cube] = {} # Supported point interpolation schemes. POINT_INTERPOLATION_SCHEMES = { @@ -598,7 +598,7 @@ def regrid(cube, target_grid, scheme, lat_offset=True, lon_offset=True): target_grid = _regional_stock_cube(target_grid) if not isinstance(target_grid, iris.cube.Cube): - raise ValueError('Expecting a cube, got {}.'.format(target_grid)) + raise ValueError(f'Expecting a cube, got {target_grid}.') # Unstructured regridding requires x2 2d spatial coordinates, # so ensure to purge any 1d native spatial dimension coordinates @@ -1008,12 +1008,13 @@ def get_cmor_levels(cmor_table, coordinate): """ if cmor_table not in CMOR_TABLES: raise ValueError( - "Level definition cmor_table '{}' not available".format( - cmor_table)) + f"Level definition cmor_table '{cmor_table}' not available" + ) if coordinate not in CMOR_TABLES[cmor_table].coords: - raise ValueError('Coordinate {} not available for {}'.format( - coordinate, cmor_table)) + raise ValueError( + f'Coordinate {coordinate} not available for {cmor_table}' + ) cmor = CMOR_TABLES[cmor_table].coords[coordinate] @@ -1023,8 +1024,9 @@ def get_cmor_levels(cmor_table, coordinate): return [float(cmor.value)] raise ValueError( - 'Coordinate {} in {} does not have requested values'.format( - coordinate, cmor_table)) + f'Coordinate {coordinate} in {cmor_table} does not have requested ' + f'values' + ) def get_reference_levels(filename, project, dataset, short_name, mip, @@ -1096,7 +1098,7 @@ def extract_coordinate_points(cube, definition, scheme): ---------- cube : cube The source cube to extract a point from. - defintion : dict(str, float or array of float) + definition : dict(str, float or array of float) The coordinate - values pairs to extract scheme : str The interpolation scheme. 'linear' or 'nearest'. No default. @@ -1114,7 +1116,6 @@ def extract_coordinate_points(cube, definition, scheme): ValueError: If the interpolation scheme is not provided or is not recognised. """ - msg = f"Unknown interpolation scheme, got {scheme!r}." scheme = POINT_INTERPOLATION_SCHEMES.get(scheme.lower()) if not scheme: diff --git a/esmvalcore/preprocessor/_regrid_esmpy.py b/esmvalcore/preprocessor/_regrid_esmpy.py index 511a055261..47b672bcaa 100755 --- a/esmvalcore/preprocessor/_regrid_esmpy.py +++ b/esmvalcore/preprocessor/_regrid_esmpy.py @@ -7,7 +7,6 @@ from ._mapping import get_empty_data, map_slices, ref_to_dims_index - ESMF_MANAGER = ESMF.Manager(debug=False) ESMF_LON, ESMF_LAT = 0, 1 @@ -73,8 +72,9 @@ def coords_iris_to_esmpy(lat, lon, circular): esmpy_lat_corners = cf_2d_bounds_to_esmpy_corners(lat.bounds, circular) esmpy_lon_corners = cf_2d_bounds_to_esmpy_corners(lon.bounds, circular) else: - raise NotImplementedError('Coord dimension is {}. Expected 1 or 2.' - ''.format(dim)) + raise NotImplementedError( + f'Coord dimension is {dim}. Expected 1 or 2.' + ) return esmpy_lat, esmpy_lon, esmpy_lat_corners, esmpy_lon_corners @@ -202,7 +202,6 @@ def regridder(src): def build_regridder_3d(src_rep, dst_rep, regrid_method, mask_threshold): - # pylint: disable=too-many-locals # The necessary refactoring will be done for the full 3d regridding. """Build regridder for 2.5d regridding.""" esmf_regridders = [] @@ -295,6 +294,11 @@ def get_grid_representants(src, dst): if src_rep.ndim == 3: dims = [dim + 1 for dim in dims] aux_coords_and_dims.append((coord, dims)) + + # Add scalar dimensions of source cube to target + for scalar_coord in src_rep.coords(dimensions=()): + aux_coords_and_dims.append((scalar_coord, ())) + dst_rep = iris.cube.Cube( data=get_empty_data(dst_shape, src.dtype), standard_name=src.standard_name, @@ -317,12 +321,12 @@ def regrid(src, dst, method='linear'): Parameters ---------- - src_cube: :class:`iris.cube.Cube` + src: :class:`iris.cube.Cube` Source data. Must have latitude and longitude coords. These can be 1d or 2d and should have bounds. - dst_cube: :class:`iris.cube.Cube` + dst: :class:`iris.cube.Cube` Defines the target grid. - regrid_method: + method: Selects the regridding method. Can be 'linear', 'area_weighted', or 'nearest'. See ESMPy_. diff --git a/tests/unit/preprocessor/_regrid_esmpy/test_regrid_esmpy.py b/tests/unit/preprocessor/_regrid_esmpy/test_regrid_esmpy.py index b7b04b1713..e8b7c0d1e7 100644 --- a/tests/unit/preprocessor/_regrid_esmpy/test_regrid_esmpy.py +++ b/tests/unit/preprocessor/_regrid_esmpy/test_regrid_esmpy.py @@ -196,6 +196,10 @@ def setUp(self): points=depth_points, bounds=depth_bounds, has_bounds=mock.Mock(return_value=True)) + self.scalar_coord = mock.Mock(iris.coords.AuxCoord, + long_name='scalar_coord', + ndim=1, + shape=(1,)) data_shape = lon_2d_points.shape raw_data = np.arange(np.prod(data_shape)).reshape(data_shape) mask = np.zeros(data_shape) @@ -225,13 +229,15 @@ def setUp(self): self.coords = { 'latitude': self.lat_2d, 'longitude': self.lon_2d, - 'depth': self.depth + 'depth': self.depth, + 'scalar_coord': self.scalar_coord, } self.coord_dims = { 'latitude': (0, 1), 'longitude': (0, 1), self.lat_2d: (0, 1), self.lon_2d: (0, 1), + 'scalar_coord': (), } def coord(name=None, axis=None): @@ -240,10 +246,12 @@ def coord(name=None, axis=None): raise CoordinateNotFoundError() return self.coords[name] - def coords(dim_coords=None): + def coords(dim_coords=None, dimensions=None): """Return coordinates for mock cube.""" if dim_coords: return [] + if dimensions == (): + return [self.scalar_coord] return list(self.coords.values()) self.cube = mock.Mock( @@ -280,6 +288,12 @@ def coord_3d(name=None, dimensions=None, dim_coords=None, axis=None): return self.coords['depth'] return self.coords[name] + def coords_3d(dimensions=None): + """Return coordinates for mock cube.""" + if dimensions == (): + return [] + return [self.lat_2d, self.lon_2d, self.depth] + self.cube_3d = mock.Mock( spec=iris.cube.Cube, dtype=np.float32, @@ -294,6 +308,7 @@ def coord_3d(name=None, dimensions=None, dim_coords=None, axis=None): data=self.data_3d, coord=coord_3d, coord_dims=lambda name: self.coord_dims_3d[name], + coords=coords_3d, ) self.cube.__getitem__ = mock.Mock(return_value=self.cube) @@ -636,7 +651,7 @@ def test_get_grid_representants_2d_src(self, mock_cube, attributes=src.attributes, cell_methods=src.cell_methods, dim_coords_and_dims=[], - aux_coords_and_dims=[], + aux_coords_and_dims=[(self.scalar_coord, ())], ) @mock.patch('esmvalcore.preprocessor._regrid_esmpy.map_slices')