Skip to content

Commit b6eaade

Browse files
committed
Add option to enable/disable regridder weights caching
1 parent 3c10fc2 commit b6eaade

File tree

3 files changed

+94
-44
lines changed

3 files changed

+94
-44
lines changed

doc/recipe/preprocessor.rst

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -881,6 +881,8 @@ The arguments are defined below:
881881
Otherwise, it cuts off at the previous value.
882882
* ``step_longitude``: Longitude distance between the centers of two neighbouring cells.
883883

884+
.. _regridding_schemes:
885+
884886
Regridding (interpolation, extrapolation) schemes
885887
-------------------------------------------------
886888

@@ -889,10 +891,26 @@ ESMValCore has a number of built-in regridding schemes, which are presented in
889891
third party regridding schemes designed for use with :doc:`Iris
890892
<iris:index>`. This is explained in :ref:`generic regridding schemes`.
891893

892-
Whenever possible, regridding weights are cached to reduce run times (see `here
894+
If desired, regridding weights can be cached to reduce run times (see `here
893895
<https://scitools-iris.readthedocs.io/en/latest/userguide/interpolation_and_regridding.html#caching-a-regridder>`__
894896
for technical details on this).
895-
An overview of regridding schemes that support weights caching is given `here
897+
This can speed up the regridding of different datasets with similar source and
898+
target grids massively, but may take up a lot of memory for extremely
899+
high-resolution data.
900+
By default, this feature is disabled; to enable it, use the option
901+
``cache_weights: true`` in the preprocessor definition:
902+
903+
.. code-block:: yaml
904+
905+
preprocessors:
906+
regrid_preprocessor:
907+
regrid:
908+
target_grid: 0.1x0.1
909+
scheme: linear
910+
cache_weights: true
911+
912+
Not all regridding schemes support weights caching. An overview of those that
913+
do is given `here
896914
<https://scitools-iris.readthedocs.io/en/latest/further_topics/which_regridder_to_use.html#which-regridder-to-use>`__
897915
and in the docstrings :ref:`here <regridding_schemes>`.
898916

esmvalcore/preprocessor/_regrid.py

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -500,8 +500,7 @@ def _get_target_grid_cube(
500500
elif isinstance(target_grid, (str, Path)) and os.path.isfile(target_grid):
501501
target_grid_cube = iris.load_cube(target_grid)
502502
elif isinstance(target_grid, str):
503-
# Generate a target grid from the provided cell-specification,
504-
# and cache the resulting stock cube for later use.
503+
# Generate a target grid from the provided cell-specification
505504
target_grid_cube = _global_stock_cube(
506505
target_grid, lat_offset, lon_offset
507506
)
@@ -642,7 +641,12 @@ def _load_generic_scheme(scheme: dict):
642641
_CACHED_REGRIDDERS: dict[tuple, dict] = {}
643642

644643

645-
def _get_regridder(src_cube: Cube, tgt_cube: Cube, scheme: str | dict):
644+
def _get_regridder(
645+
src_cube: Cube,
646+
tgt_cube: Cube,
647+
scheme: str | dict,
648+
cache_weights: bool,
649+
):
646650
"""Get regridder to actually perform regridding.
647651
648652
Note
@@ -652,25 +656,33 @@ def _get_regridder(src_cube: Cube, tgt_cube: Cube, scheme: str | dict):
652656
interpolation_and_regridding.html#caching-a-regridder.)
653657
654658
"""
655-
# Use existing regridder if possible. This first checks the regridding
656-
# scheme name and shapes of source and target coordinates, and only if
657-
# these match check coordinates themselves (this is much more expensive).
658-
coord_key = _get_coord_key(src_cube, tgt_cube)
659-
name_shape_key = _get_name_and_shape_key(src_cube, tgt_cube, scheme)
660-
if name_shape_key in _CACHED_REGRIDDERS:
661-
# We cannot simply do a test for `coord_key in
662-
# _CACHED_REGRIDDERS[shape_key]` below since the hash() of a coordinate
663-
# is simply its id() (thus, coordinates loaded from two different files
664-
# would never be considered equal)
665-
for (key, regridder) in _CACHED_REGRIDDERS[name_shape_key].items():
666-
if key == coord_key:
667-
return regridder
668-
669-
# If regridder is not cached yet, return a new one and cache it
670-
loaded_scheme = _load_scheme(src_cube, scheme)
671-
regridder = loaded_scheme.regridder(src_cube, tgt_cube)
672-
_CACHED_REGRIDDERS.setdefault(name_shape_key, {})
673-
_CACHED_REGRIDDERS[name_shape_key][coord_key] = regridder
659+
# (1) Weights caching enabled
660+
if cache_weights:
661+
# To search for a matching regridder in the cache, first check the
662+
# regridding scheme name and shapes of source and target coordinates.
663+
# Only if these match, check coordinates themselves (this is much more
664+
# expensive).
665+
coord_key = _get_coord_key(src_cube, tgt_cube)
666+
name_shape_key = _get_name_and_shape_key(src_cube, tgt_cube, scheme)
667+
if name_shape_key in _CACHED_REGRIDDERS:
668+
# We cannot simply do a test for `coord_key in
669+
# _CACHED_REGRIDDERS[shape_key]` below since the hash() of a
670+
# coordinate is simply its id() (thus, coordinates loaded from two
671+
# different files would never be considered equal)
672+
for (key, regridder) in _CACHED_REGRIDDERS[name_shape_key].items():
673+
if key == coord_key:
674+
return regridder
675+
676+
# Regridder is not in cached -> return a new one and cache it
677+
loaded_scheme = _load_scheme(src_cube, scheme)
678+
regridder = loaded_scheme.regridder(src_cube, tgt_cube)
679+
_CACHED_REGRIDDERS.setdefault(name_shape_key, {})
680+
_CACHED_REGRIDDERS[name_shape_key][coord_key] = regridder
681+
682+
# (2) Weights caching disabled
683+
else:
684+
loaded_scheme = _load_scheme(src_cube, scheme)
685+
regridder = loaded_scheme.regridder(src_cube, tgt_cube)
674686

675687
return regridder
676688

@@ -701,6 +713,7 @@ def regrid(
701713
scheme: str | dict,
702714
lat_offset: bool = True,
703715
lon_offset: bool = True,
716+
cache_weights: bool = False,
704717
) -> Cube:
705718
"""Perform horizontal regridding.
706719
@@ -747,6 +760,13 @@ def regrid(
747760
Offset the grid centers of the longitude coordinate w.r.t. Greenwich
748761
meridian by half a grid step. This argument is ignored if
749762
`target_grid` is a cube or file.
763+
cache_weights:
764+
If ``True``, cache regridding weights for later usage. This can speed
765+
up the regridding of different datasets with similar source and target
766+
grids massively, but may take up a lot of memory for extremely
767+
high-resolution data. This option is ignored for schemes that do not
768+
support weights caching. A list of supported schemes is available in
769+
the section on :ref:`regridding_schemes`.
750770
751771
Returns
752772
-------
@@ -816,7 +836,7 @@ def regrid(
816836
# Load scheme and reuse existing regridder if possible
817837
if isinstance(scheme, str):
818838
scheme = scheme.lower()
819-
regridder = _get_regridder(cube, target_grid_cube, scheme)
839+
regridder = _get_regridder(cube, target_grid_cube, scheme, cache_weights)
820840

821841
# Rechunk and actually perform the regridding
822842
cube = _rechunk(cube, target_grid_cube)

tests/unit/preprocessor/_regrid/test_regrid.py

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -79,22 +79,26 @@ def cube_30x30():
7979
SCHEMES = ['area_weighted', 'linear', 'nearest']
8080

8181

82+
@pytest.mark.parametrize('cache_weights', [True, False])
8283
@pytest.mark.parametrize('scheme', SCHEMES)
83-
def test_builtin_regridding(scheme, cube_10x10, cube_30x30):
84+
def test_builtin_regridding(scheme, cache_weights, cube_10x10, cube_30x30):
8485
"""Test `regrid.`"""
8586
_cached_regridders = esmvalcore.preprocessor._regrid._CACHED_REGRIDDERS
8687
assert _cached_regridders == {}
8788

88-
res = regrid(cube_10x10, cube_30x30, scheme)
89+
res = regrid(cube_10x10, cube_30x30, scheme, cache_weights=cache_weights)
8990

9091
assert res.coord('latitude') == cube_30x30.coord('latitude')
9192
assert res.coord('longitude') == cube_30x30.coord('longitude')
9293
assert res.dtype == np.float32
9394
assert np.allclose(res.data, 0.0)
9495

95-
assert len(_cached_regridders) == 1
96-
key = (scheme, (18,), (36,), (30,), (30,))
97-
assert key in _cached_regridders
96+
if cache_weights:
97+
assert len(_cached_regridders) == 1
98+
key = (scheme, (18,), (36,), (30,), (30,))
99+
assert key in _cached_regridders
100+
else:
101+
assert not _cached_regridders
98102

99103

100104
@pytest.mark.parametrize('scheme', SCHEMES)
@@ -127,7 +131,8 @@ def test_regrid_generic_invalid_reference(cube_10x10, cube_30x30):
127131
regrid(cube_10x10, cube_30x30, {'reference': 'this.does:not.exist'})
128132

129133

130-
def test_regrid_generic_regridding(cube_10x10, cube_30x30):
134+
@pytest.mark.parametrize('cache_weights', [True, False])
135+
def test_regrid_generic_regridding(cache_weights, cube_10x10, cube_30x30):
131136
"""Test `regrid.`"""
132137
_cached_regridders = esmvalcore.preprocessor._regrid._CACHED_REGRIDDERS
133138
assert _cached_regridders == {}
@@ -139,23 +144,30 @@ def test_regrid_generic_regridding(cube_10x10, cube_30x30):
139144
'reference': 'iris.analysis:Linear',
140145
'extrapolation_mode': 'mask',
141146
},
147+
cache_weights=cache_weights,
148+
)
149+
cube_lin = regrid(
150+
cube_10x10, cube_30x30, 'linear', cache_weights=cache_weights
142151
)
143-
cube_lin = regrid(cube_10x10, cube_30x30, 'linear')
144152
assert cube_gen.dtype == np.float32
145153
assert cube_lin.dtype == np.float32
146154
assert cube_gen == cube_lin
147155

148-
assert len(_cached_regridders) == 2
149-
key_1 = (
150-
"{'reference': 'iris.analysis:Linear', 'extrapolation_mode': 'mask'}",
151-
(18,),
152-
(36,),
153-
(30,),
154-
(30,),
155-
)
156-
key_2 = ('linear', (18,), (36,), (30,), (30,))
157-
assert key_1 in _cached_regridders
158-
assert key_2 in _cached_regridders
156+
if cache_weights:
157+
assert len(_cached_regridders) == 2
158+
key_1 = (
159+
"{'reference': 'iris.analysis:Linear', 'extrapolation_mode': "
160+
"'mask'}",
161+
(18,),
162+
(36,),
163+
(30,),
164+
(30,),
165+
)
166+
key_2 = ('linear', (18,), (36,), (30,), (30,))
167+
assert key_1 in _cached_regridders
168+
assert key_2 in _cached_regridders
169+
else:
170+
assert not _cached_regridders
159171

160172

161173
@pytest.mark.parametrize(
@@ -360,7 +372,7 @@ def test_regridding_weights_use_cache(scheme, cube_10x10, cube_30x30, mocker):
360372
esmvalcore.preprocessor._regrid, '_load_scheme', autospec=True
361373
)
362374

363-
reg = _get_regridder(cube_10x10, cube_30x30, scheme)
375+
reg = _get_regridder(cube_10x10, cube_30x30, scheme, cache_weights=True)
364376

365377
assert reg == mocker.sentinel.regridder
366378

0 commit comments

Comments
 (0)