Skip to content

Commit 92e20fd

Browse files
authored
Set use_src_mask parameter in regridding of datasets with grid discontinuities (#2070)
1 parent 83650df commit 92e20fd

File tree

3 files changed

+98
-8
lines changed

3 files changed

+98
-8
lines changed

doc/recipe/preprocessor.rst

+7
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,13 @@ scheme available in :doc:`iris-esmf-regrid:index`:
10151015
reference: esmf_regrid.schemes:regrid_rectilinear_to_rectilinear
10161016
mdtol: 0.7
10171017
1018+
Since version 0.6 of :doc:`iris-esmf-regrid:index`, the regridder is able
1019+
to ignore discontinuities in the source grids if the data defined in the
1020+
discontiguous points is masked.
1021+
The :func:`~esmvalcore.preprocessor.regrid` preprocessor automatically detects
1022+
if discontinuities are present, and configures the regridding scheme in order to take
1023+
into account the mask of the source grid to ignore them.
1024+
10181025
.. _ensemble statistics:
10191026

10201027
Ensemble statistics

esmvalcore/preprocessor/_regrid.py

+24
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,21 @@ def extract_location(cube, location, scheme):
384384
scheme)
385385

386386

387+
def _check_grid_discontiguities(cube, scheme):
388+
"""Check if there are grid discontiguities and set use_src_mask to True."""
389+
scheme = dict(scheme)
390+
try:
391+
discontiguities = iris.util.find_discontiguities(cube)
392+
except NotImplementedError:
393+
pass
394+
else:
395+
if discontiguities.any():
396+
scheme['use_src_mask'] = True
397+
logger.debug('Grid discontinuities were found in the source grid. '
398+
'Setting scheme argument `use_src_mask` to True.')
399+
return scheme
400+
401+
387402
def extract_point(cube, latitude, longitude, scheme):
388403
"""Extract a point, with interpolation.
389404
@@ -561,6 +576,13 @@ def regrid(cube, target_grid, scheme, lat_offset=True, lon_offset=True):
561576
target: 1x1
562577
scheme:
563578
reference: esmf_regrid.schemes:ESMFAreaWeighted
579+
580+
Since version 0.6 of :doc:`iris-esmf-regrid:index`, the regridder is able
581+
to ignore discontinuities in the source grids if the data defined in the
582+
discontiguous points is masked.
583+
The preprocessor automatically detects if discontinuities are present,
584+
and configures the regridding scheme in order to take into account
585+
the mask of the source grid to ignore them.
564586
"""
565587
if is_dataset(target_grid):
566588
target_grid = target_grid.copy()
@@ -615,6 +637,8 @@ def regrid(cube, target_grid, scheme, lat_offset=True, lon_offset=True):
615637
scheme['src_cube'] = cube
616638
if 'grid_cube' in scheme_args:
617639
scheme['grid_cube'] = target_grid
640+
if 'use_src_mask' in scheme_args:
641+
scheme = _check_grid_discontiguities(cube, scheme)
618642

619643
loaded_scheme = obj(**scheme)
620644
else:

tests/unit/preprocessor/_regrid/test_regrid.py

+67-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Unit tests for the :func:`esmvalcore.preprocessor.regrid.regrid`
22
function."""
33

4+
import logging
45
import unittest
56
from unittest import mock
67

@@ -15,12 +16,14 @@
1516
from esmvalcore.preprocessor._regrid import (
1617
_CACHE,
1718
HORIZONTAL_SCHEMES,
19+
_check_grid_discontiguities,
1820
_horizontal_grid_is_close,
1921
_rechunk,
2022
)
2123

2224

2325
class Test(tests.Test):
26+
2427
def _check(self, tgt_grid, scheme, spec=False):
2528
expected_scheme = HORIZONTAL_SCHEMES[scheme]
2629

@@ -70,8 +73,8 @@ def setUp(self):
7073
)
7174
self.src_cube.ndim = 1
7275
self.tgt_grid_coord = mock.Mock()
73-
self.tgt_grid = mock.Mock(
74-
spec=iris.cube.Cube, coord=self.tgt_grid_coord)
76+
self.tgt_grid = mock.Mock(spec=iris.cube.Cube,
77+
coord=self.tgt_grid_coord)
7578
self.regrid_schemes = [
7679
'linear', 'linear_extrapolate', 'nearest', 'area_weighted',
7780
'unstructured_nearest'
@@ -111,8 +114,8 @@ def test_invalid_scheme__unknown(self):
111114
regrid(self.src_cube, self.src_cube, 'wibble')
112115

113116
def test_horizontal_schemes(self):
114-
self.assertEqual(
115-
set(HORIZONTAL_SCHEMES.keys()), set(self.regrid_schemes))
117+
self.assertEqual(set(HORIZONTAL_SCHEMES.keys()),
118+
set(self.regrid_schemes))
116119

117120
def test_regrid__horizontal_schemes(self):
118121
for scheme in self.regrid_schemes:
@@ -154,12 +157,12 @@ def test_regrid_generic_regridding(self):
154157
third_party_regridder = mock.Mock()
155158

156159
def test_regrid_generic_third_party(self):
157-
regrid(self.src_cube, '1x1',
158-
{"reference":
159-
"tests.unit.preprocessor._regrid.test_regrid:"
160+
regrid(
161+
self.src_cube, '1x1', {
162+
"reference": "tests.unit.preprocessor._regrid.test_regrid:"
160163
"Test.third_party_regridder",
161164
"method": "good",
162-
})
165+
})
163166
self.third_party_regridder.assert_called_once_with(method="good")
164167

165168

@@ -266,6 +269,62 @@ def test_regrid_is_skipped_if_grids_are_the_same():
266269
assert expected_different_cube is not cube
267270

268271

272+
def test_no_discontiguities_in_coords():
273+
"""Test that no mask is used if there are no discontinuities in coords."""
274+
cube = _make_cube(lat=LAT_SPEC1, lon=LON_SPEC1)
275+
scheme = {}
276+
scheme = _check_grid_discontiguities(cube, scheme)
277+
assert scheme == {}
278+
279+
280+
def test_use_mask_if_discontiguities_in_coords(caplog):
281+
"""Test use_src_mask is added to the scheme."""
282+
lat_bounds = np.array(
283+
[[[-43.48076211, -34.01923789, -22.00961894, -31.47114317],
284+
[-34.01923789, -10.0, 2.00961894, -22.00961894],
285+
[-10.0, -0.53847577, 11.47114317, 2.00961894]],
286+
[[-31.47114317, -22.00961894, -10.0, -19.46152423],
287+
[-22.00961894, 2.00961894, 14.01923789, -10.0],
288+
[2.00961894, 11.47114317, 23.48076211, 14.01923789]]])
289+
lat_coord = iris.coords.AuxCoord(
290+
[[-40.0, -20.0, 0.0], [-20.0, 0.0, 20.0]],
291+
var_name='lat',
292+
standard_name='latitude',
293+
units='degrees_north',
294+
bounds=lat_bounds,
295+
)
296+
lon_bounds = np.array([[[140.625, 99.375, 99.375, 140.625],
297+
[99.375, 140.625, 140.625, 99.375],
298+
[140.625, 99.375, 99.375, 140.625]],
299+
[[140., 99.375, 99.375, 140.],
300+
[99.375, 140.625, 140.625, 99.375],
301+
[140., 99.375, 99.375, 140.]]])
302+
lon_coord = iris.coords.AuxCoord(
303+
[[100.0, 140.0, 180.0], [80.0, 100.0, 120.0]],
304+
var_name='lon',
305+
standard_name='longitude',
306+
units='degrees_east',
307+
bounds=lon_bounds,
308+
)
309+
data = np.ma.array(
310+
[[-40.0, -20.0, 0.0], [-20.0, 0.0, 20.0]],
311+
mask=[[True, False, True], [False, True, False]],
312+
)
313+
cube = iris.cube.Cube(
314+
data,
315+
aux_coords_and_dims=[(lat_coord, (0, 1)), (lon_coord, (0, 1))],
316+
)
317+
318+
scheme = {}
319+
with caplog.at_level(logging.DEBUG):
320+
scheme = _check_grid_discontiguities(cube, scheme)
321+
assert scheme == {'use_src_mask': True}
322+
323+
msg = ('Grid discontinuities were found in the source grid. '
324+
'Setting scheme argument `use_src_mask` to True.')
325+
assert msg in caplog.text
326+
327+
269328
def test_rechunk_on_increased_grid():
270329
"""Test that an increase in grid size rechunks."""
271330
with dask.config.set({'array.chunk-size': '128 M'}):

0 commit comments

Comments
 (0)