Skip to content

Commit

Permalink
Added scalar coords of input cubes to output of esmpy_regrid (#1811)
Browse files Browse the repository at this point in the history
  • Loading branch information
schlunma authored Nov 23, 2022
1 parent bb58405 commit 5644203
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 20 deletions.
21 changes: 11 additions & 10 deletions esmvalcore/preprocessor/_regrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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]

Expand All @@ -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,
Expand Down Expand Up @@ -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.
Expand All @@ -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:
Expand Down
18 changes: 11 additions & 7 deletions esmvalcore/preprocessor/_regrid_esmpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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


Expand Down Expand Up @@ -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 = []
Expand Down Expand Up @@ -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,
Expand All @@ -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_.
Expand Down
21 changes: 18 additions & 3 deletions tests/unit/preprocessor/_regrid_esmpy/test_regrid_esmpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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):
Expand All @@ -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(
Expand Down Expand Up @@ -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,
Expand All @@ -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)

Expand Down Expand Up @@ -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')
Expand Down

0 comments on commit 5644203

Please sign in to comment.