@@ -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 )
@@ -639,12 +638,82 @@ def _load_generic_scheme(scheme: dict):
639638 return loaded_scheme
640639
641640
641+ _CACHED_REGRIDDERS : dict [tuple , dict ] = {}
642+
643+
644+ def _get_regridder (
645+ src_cube : Cube ,
646+ tgt_cube : Cube ,
647+ scheme : str | dict ,
648+ cache_weights : bool ,
649+ ):
650+ """Get regridder to actually perform regridding.
651+
652+ Note
653+ ----
654+ If possible, this uses an existing regridder to reduce runtime (see also
655+ https://scitools-iris.readthedocs.io/en/latest/userguide/
656+ interpolation_and_regridding.html#caching-a-regridder.)
657+
658+ """
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 )
686+
687+ return regridder
688+
689+
690+ def _get_coord_key (src_cube : Cube , tgt_cube : Cube ) -> tuple :
691+ """Get dict key from coordinates."""
692+ src_lat = src_cube .coord ('latitude' )
693+ src_lon = src_cube .coord ('longitude' )
694+ tgt_lat = tgt_cube .coord ('latitude' )
695+ tgt_lon = tgt_cube .coord ('longitude' )
696+ return (src_lat , src_lon , tgt_lat , tgt_lon )
697+
698+
699+ def _get_name_and_shape_key (
700+ src_cube : Cube ,
701+ tgt_cube : Cube ,
702+ scheme : str | dict ,
703+ ) -> tuple :
704+ """Get dict key from scheme name and coordinate shapes."""
705+ name = str (scheme )
706+ shapes = [c .shape for c in _get_coord_key (src_cube , tgt_cube )]
707+ return (name , * shapes )
708+
709+
642710def regrid (
643711 cube : Cube ,
644712 target_grid : Cube | Dataset | Path | str | dict ,
645713 scheme : str | dict ,
646714 lat_offset : bool = True ,
647715 lon_offset : bool = True ,
716+ cache_weights : bool = False ,
648717) -> Cube :
649718 """Perform horizontal regridding.
650719
@@ -691,6 +760,14 @@ def regrid(
691760 Offset the grid centers of the longitude coordinate w.r.t. Greenwich
692761 meridian by half a grid step. This argument is ignored if
693762 `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. More details on this are given in the section
769+ on :ref:`caching_regridding_weights`. To clear the cache, use
770+ :func:`esmvalcore.preprocessor.regrid.cache_clear`.
694771
695772 Returns
696773 -------
@@ -757,16 +834,26 @@ def regrid(
757834 )
758835 return cube
759836
760- # Load scheme, rechunk and regrid
837+ # Load scheme and reuse existing regridder if possible
761838 if isinstance (scheme , str ):
762839 scheme = scheme .lower ()
763- loaded_scheme = _load_scheme (cube , scheme )
840+ regridder = _get_regridder (cube , target_grid_cube , scheme , cache_weights )
841+
842+ # Rechunk and actually perform the regridding
764843 cube = _rechunk (cube , target_grid_cube )
765- cube = cube . regrid ( target_grid_cube , loaded_scheme )
844+ cube = regridder ( cube )
766845
767846 return cube
768847
769848
849+ def _cache_clear ():
850+ """Clear regridding weights cache."""
851+ _CACHED_REGRIDDERS .clear ()
852+
853+
854+ regrid .cache_clear = _cache_clear # type: ignore
855+
856+
770857def _rechunk (cube : Cube , target_grid : Cube ) -> Cube :
771858 """Re-chunk cube with optimal chunk sizes for target grid."""
772859 if not cube .has_lazy_data () or cube .ndim < 3 :
0 commit comments