From a96e132b279313be5c4d8dcebbb43e65563b3bb6 Mon Sep 17 00:00:00 2001 From: ghosh97 Date: Mon, 1 Jun 2026 16:51:59 +0200 Subject: [PATCH 01/23] Add CORDEX domain target grids for regridding --- esmvalcore/_recipe/recipe.py | 3 +- esmvalcore/preprocessor/_regrid.py | 111 +++++++++++++++++++++++++---- 2 files changed, 100 insertions(+), 14 deletions(-) diff --git a/esmvalcore/_recipe/recipe.py b/esmvalcore/_recipe/recipe.py index 626c3e05b5..4c231315af 100644 --- a/esmvalcore/_recipe/recipe.py +++ b/esmvalcore/_recipe/recipe.py @@ -45,6 +45,7 @@ _spec_to_latlonvals, get_cmor_levels, get_reference_levels, + is_cordex_domain, parse_cell_spec, ) from esmvalcore.preprocessor._shared import _group_products @@ -183,7 +184,7 @@ def _update_target_grid( else: # Check that MxN grid spec is correct target_grid = settings["regrid"]["target_grid"] - if isinstance(target_grid, str): + if isinstance(target_grid, str) and not is_cordex_domain(target_grid): parse_cell_spec(target_grid) # Check that cdo spec is correct elif isinstance(target_grid, dict): diff --git a/esmvalcore/preprocessor/_regrid.py b/esmvalcore/preprocessor/_regrid.py index 461e75002a..2c303b5750 100644 --- a/esmvalcore/preprocessor/_regrid.py +++ b/esmvalcore/preprocessor/_regrid.py @@ -14,17 +14,20 @@ from pathlib import Path from typing import TYPE_CHECKING, Any, Literal +import cordex as cx import dask.array as da import iris import iris.coords import numpy as np import stratify +from cf_units import Unit from geopy.geocoders import Nominatim from iris.analysis import ( AreaWeighted, Linear, Nearest, ) +from iris.coord_systems import RotatedGeogCS from iris.cube import Cube from iris.util import broadcast_to_shape @@ -196,6 +199,85 @@ def parse_cell_spec(spec: str) -> tuple[float, float]: return dlon, dlat +def is_cordex_domain(spec: str) -> bool: + """Return ``True`` if ``spec`` is a known CORDEX domain name. + + Parameters + ---------- + spec: + Candidate CORDEX domain identifier (e.g. ``EUR-11``). + + Returns + ------- + bool + Whether ``spec`` is recognised by :mod:`cordex`. + """ + try: + cx.domain_info(spec) + except KeyError: + return False + return True + + +@functools.lru_cache +def _cordex_stock_cube(domain_name: str) -> Cube: + """Create a stock cube for a CORDEX domain target grid. + + Parameters + ---------- + domain_name: + CORDEX domain identifier (e.g. ``EUR-11``). + + Returns + ------- + iris.cube.Cube + Dummy cube with rotated-pole dimension coordinates and geographical + auxiliary coordinates matching the official domain specification. + """ + domain = cx.cordex_domain(domain_name, bounds=True) + domain_info = cx.domain_info(domain_name) + coord_system = RotatedGeogCS( + grid_north_pole_latitude=domain_info["pollat"], + grid_north_pole_longitude=domain_info["pollon"], + ) + + coords_spec = [] + for dim_index, dim_coord in enumerate(["rlat", "rlon"]): + var = domain[dim_coord] + coord = iris.coords.DimCoord( + var.data, + var_name=dim_coord, + standard_name=var.attrs.get("standard_name"), + long_name=var.attrs.get("long_name"), + units=Unit("degrees"), + coord_system=coord_system, + ) + coord.guess_bounds() + coords_spec.append((coord, dim_index)) + + shape = (domain.sizes["rlat"], domain.sizes["rlon"]) + dummy = np.empty(shape, dtype=np.int32) + cube = Cube(dummy, dim_coords_and_dims=coords_spec) + + aux_dims = cube.coord_dims(cube.coord(var_name="rlat")) + cube.coord_dims( + cube.coord(var_name="rlon"), + ) + for aux_coord in ["lat", "lon"]: + var = domain[aux_coord] + bounds_var = domain[f"{aux_coord}_vertices"] + coord = iris.coords.AuxCoord( + var.data, + var_name=aux_coord, + standard_name=var.attrs.get("standard_name"), + long_name=var.attrs.get("long_name"), + units=Unit(var.attrs.get("units", "degrees")), + bounds=bounds_var.data, + ) + cube.add_aux_coord(coord, aux_dims) + + return cube + + def _generate_cube_from_dimcoords( latdata: np.ndarray | da.Array, londata: np.ndarray | da.Array, @@ -608,19 +690,22 @@ def _get_target_grid_cube( elif isinstance(target_grid, (str, Path)) and os.path.isfile(target_grid): target_grid_cube = iris.load_cube(target_grid) elif isinstance(target_grid, str): - # Generate a target grid from the provided cell-specification - target_grid_cube = _global_stock_cube( - target_grid, - lat_offset, - lon_offset, - ) - # Align the target grid coordinate system to the source - # coordinate system. - src_cs = cube.coord_system() - xcoord = target_grid_cube.coord(axis="x", dim_coords=True) - ycoord = target_grid_cube.coord(axis="y", dim_coords=True) - xcoord.coord_system = src_cs - ycoord.coord_system = src_cs + if is_cordex_domain(target_grid): + target_grid_cube = _cordex_stock_cube(target_grid) + else: + # Generate a target grid from the provided cell-specification + target_grid_cube = _global_stock_cube( + target_grid, + lat_offset, + lon_offset, + ) + # Align the target grid coordinate system to the source + # coordinate system. + src_cs = cube.coord_system() + xcoord = target_grid_cube.coord(axis="x", dim_coords=True) + ycoord = target_grid_cube.coord(axis="y", dim_coords=True) + xcoord.coord_system = src_cs + ycoord.coord_system = src_cs elif isinstance(target_grid, dict): # Generate a target grid from the provided specification, target_grid_cube = _regional_stock_cube(target_grid) From 8e401c250a706c6adf6b3259352aa2c210ae3685 Mon Sep 17 00:00:00 2001 From: ghosh97 Date: Thu, 4 Jun 2026 15:22:48 +0200 Subject: [PATCH 02/23] Add unit tests for CORDEX domain target grids in regridding --- .../_regrid/test_cordex_target_grid.py | 133 ++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 tests/unit/preprocessor/_regrid/test_cordex_target_grid.py diff --git a/tests/unit/preprocessor/_regrid/test_cordex_target_grid.py b/tests/unit/preprocessor/_regrid/test_cordex_target_grid.py new file mode 100644 index 0000000000..788da15394 --- /dev/null +++ b/tests/unit/preprocessor/_regrid/test_cordex_target_grid.py @@ -0,0 +1,133 @@ +"""Unit tests for CORDEX domain target grids in regridding.""" + +import cordex as cx +import iris +import iris.coords +import numpy as np +import pytest + +from esmvalcore._recipe import recipe as recipe_module +from esmvalcore.dataset import Dataset +from esmvalcore.preprocessor._regrid import ( + _cordex_stock_cube, + _get_target_grid_cube, + _global_stock_cube, + is_cordex_domain, + parse_cell_spec, +) + + +@pytest.fixture(autouse=True) +def clear_lru_cache(): + """Clear LRU caches for stock cube helpers.""" + yield + _global_stock_cube.cache_clear() + _cordex_stock_cube.cache_clear() + + +@pytest.mark.parametrize( + ("spec", "expected"), + [ + ("EUR-11", True), + ("EUR-44", True), + ("not-a-grid", False), + ("1x1", False), + ("RCA4", False), + ], +) +def test_is_cordex_domain(spec, expected): + """Test CORDEX domain name detection.""" + assert is_cordex_domain(spec) is expected + + +def test_parse_cell_spec_rejects_cordex_domain(): + """CORDEX domains must not be parsed as MxN cell specifications.""" + with pytest.raises(ValueError, match="Invalid MxN cell specification"): + parse_cell_spec("EUR-11") + + +def test_cordex_stock_cube_eur11(): + """Test stock cube for EUR-11 matches the official domain grid.""" + domain = cx.cordex_domain("EUR-11", bounds=True) + cube = _cordex_stock_cube("EUR-11") + + np.testing.assert_array_equal( + cube.coord(var_name="rlat").points, + domain["rlat"].data, + ) + np.testing.assert_array_equal( + cube.coord(var_name="rlon").points, + domain["rlon"].data, + ) + np.testing.assert_array_equal( + cube.coord(var_name="lat").points, + domain["lat"].data, + ) + np.testing.assert_array_equal( + cube.coord(var_name="lon").points, + domain["lon"].data, + ) + assert cube.coord(var_name="rlat").has_bounds() + assert cube.coord(var_name="rlon").has_bounds() + assert cube.coord(var_name="lat").has_bounds() + assert cube.coord(var_name="lon").has_bounds() + + +@pytest.fixture +def global_cube(): + """Simple regular global cube for target-grid construction tests.""" + lat_coord = iris.coords.DimCoord( + np.linspace(-85, 85, 18), + standard_name="latitude", + units="degrees", + ) + lon_coord = iris.coords.DimCoord( + np.linspace(5, 355, 36), + standard_name="longitude", + units="degrees", + ) + lat_coord.guess_bounds() + lon_coord.guess_bounds() + return iris.cube.Cube( + np.zeros((18, 36), dtype=np.float32), + dim_coords_and_dims=[(lat_coord, 0), (lon_coord, 1)], + ) + + +def test_get_target_grid_cube_cordex_domain(global_cube): + """Test target grid cube construction for a CORDEX domain.""" + target = _get_target_grid_cube(global_cube, "EUR-11") + assert target.coord(var_name="rlat") is not None + assert target.coord(var_name="rlon") is not None + + +def test_update_target_grid_accepts_cordex_domain(): + """Test recipe preprocessing accepts CORDEX domain target grids.""" + dataset = Dataset( + dataset="RCA4", + project="CORDEX", + domain="EUR-11", + diagnostic="bias", + variable_group="ts", + preprocessor="ts_pp", + ) + settings = {"regrid": {"target_grid": "EUR-11", "scheme": "linear"}} + + recipe_module._update_target_grid(dataset, [dataset], settings) + + assert settings["regrid"]["target_grid"] == "EUR-11" + + +def test_update_target_grid_still_validates_mxn(): + """Test invalid MxN target grids are still rejected.""" + dataset = Dataset( + dataset="RCA4", + project="CORDEX", + diagnostic="bias", + variable_group="ts", + preprocessor="ts_pp", + ) + settings = {"regrid": {"target_grid": "EUR-11x", "scheme": "linear"}} + + with pytest.raises(ValueError, match="Invalid MxN cell specification"): + recipe_module._update_target_grid(dataset, [dataset], settings) From ef948223e463ccada77eb32c68839f2ba37382ff Mon Sep 17 00:00:00 2001 From: ghosh97 Date: Mon, 8 Jun 2026 13:04:38 +0200 Subject: [PATCH 03/23] Refactor _cordex_stock_cube function to utilize xarray for grid creation and streamline coordinate handling --- esmvalcore/preprocessor/_regrid.py | 60 +++++++++++++----------------- 1 file changed, 25 insertions(+), 35 deletions(-) diff --git a/esmvalcore/preprocessor/_regrid.py b/esmvalcore/preprocessor/_regrid.py index 2c303b5750..293eb6eb68 100644 --- a/esmvalcore/preprocessor/_regrid.py +++ b/esmvalcore/preprocessor/_regrid.py @@ -18,9 +18,10 @@ import dask.array as da import iris import iris.coords +import ncdata.iris_xarray import numpy as np import stratify -from cf_units import Unit +import xarray as xr from geopy.geocoders import Nominatim from iris.analysis import ( AreaWeighted, @@ -236,44 +237,33 @@ def _cordex_stock_cube(domain_name: str) -> Cube: """ domain = cx.cordex_domain(domain_name, bounds=True) domain_info = cx.domain_info(domain_name) + + data = xr.DataArray( + np.zeros((domain.sizes["rlat"], domain.sizes["rlon"]), dtype=np.int32), + dims=["rlat", "rlon"], + coords={ + "rlat": domain["rlat"], + "rlon": domain["rlon"], + "lat": domain["lat"], + "lon": domain["lon"], + }, + name="grid", + ) + (cube,) = ncdata.iris_xarray.cubes_from_xarray(data.to_dataset()) + coord_system = RotatedGeogCS( grid_north_pole_latitude=domain_info["pollat"], grid_north_pole_longitude=domain_info["pollon"], ) - - coords_spec = [] - for dim_index, dim_coord in enumerate(["rlat", "rlon"]): - var = domain[dim_coord] - coord = iris.coords.DimCoord( - var.data, - var_name=dim_coord, - standard_name=var.attrs.get("standard_name"), - long_name=var.attrs.get("long_name"), - units=Unit("degrees"), - coord_system=coord_system, - ) - coord.guess_bounds() - coords_spec.append((coord, dim_index)) - - shape = (domain.sizes["rlat"], domain.sizes["rlon"]) - dummy = np.empty(shape, dtype=np.int32) - cube = Cube(dummy, dim_coords_and_dims=coords_spec) - - aux_dims = cube.coord_dims(cube.coord(var_name="rlat")) + cube.coord_dims( - cube.coord(var_name="rlon"), - ) - for aux_coord in ["lat", "lon"]: - var = domain[aux_coord] - bounds_var = domain[f"{aux_coord}_vertices"] - coord = iris.coords.AuxCoord( - var.data, - var_name=aux_coord, - standard_name=var.attrs.get("standard_name"), - long_name=var.attrs.get("long_name"), - units=Unit(var.attrs.get("units", "degrees")), - bounds=bounds_var.data, - ) - cube.add_aux_coord(coord, aux_dims) + for dim_coord in ("rlat", "rlon"): + coord = cube.coord(var_name=dim_coord) + coord.coord_system = coord_system + if not coord.has_bounds(): + coord.guess_bounds() + + for aux_coord in ("lat", "lon"): + coord = cube.coord(var_name=aux_coord) + coord.bounds = domain[f"{aux_coord}_vertices"].data return cube From 0bd0814ae9b3af21761533141f86a388bc3da6b9 Mon Sep 17 00:00:00 2001 From: ghosh97 Date: Mon, 8 Jun 2026 15:31:23 +0200 Subject: [PATCH 04/23] Update docstring in global_cube fixture for clarity in target-grid construction tests --- tests/unit/preprocessor/_regrid/test_cordex_target_grid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/preprocessor/_regrid/test_cordex_target_grid.py b/tests/unit/preprocessor/_regrid/test_cordex_target_grid.py index 788da15394..d3d0c8fb6e 100644 --- a/tests/unit/preprocessor/_regrid/test_cordex_target_grid.py +++ b/tests/unit/preprocessor/_regrid/test_cordex_target_grid.py @@ -75,7 +75,7 @@ def test_cordex_stock_cube_eur11(): @pytest.fixture def global_cube(): - """Simple regular global cube for target-grid construction tests.""" + """Return a simple regular global cube for target-grid construction tests.""" lat_coord = iris.coords.DimCoord( np.linspace(-85, 85, 18), standard_name="latitude", From 1b6e678601c5627c02ce84892baeca14ba85c05d Mon Sep 17 00:00:00 2001 From: ghosh97 Date: Mon, 8 Jun 2026 15:56:25 +0200 Subject: [PATCH 05/23] Add documentation for regridding on CORDEX domain grids This update introduces a new section in the preprocessor documentation detailing how to regrid to standard CORDEX domains using the `target_grid` parameter. An example configuration for the `EUR-11` domain is provided, clarifying the use of domain names recognized by the `cordex` package. --- doc/recipe/preprocessor.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/doc/recipe/preprocessor.rst b/doc/recipe/preprocessor.rst index 7918f49bf8..a03d7d9502 100644 --- a/doc/recipe/preprocessor.rst +++ b/doc/recipe/preprocessor.rst @@ -803,6 +803,26 @@ or ana4MIPs datasets can be used); in this case the `scheme` is target_grid: ERA-Interim scheme: linear +Regridding on a CORDEX domain grid +---------------------------------- + +It is also possible to regrid to a standard CORDEX domain by using the +CORDEX domain name as ``target_grid``. For example, to regrid to the +``EUR-11`` domain: + +.. code-block:: yaml + + preprocessors: + regrid_preprocessor: + regrid: + target_grid: EUR-11 + scheme: linear + +Any domain name recognized by the ``cordex`` package can be used, for example +``EUR-11``. This creates the target grid from the official CORDEX +domain definition instead of interpreting the value as an ``MxN`` grid +specification. + Regridding on an ``MxN`` grid specification ------------------------------------------- From cdc90f161caaa5336ca0483155449ab1c1eccb5b Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Fri, 26 Jun 2026 17:18:10 +0200 Subject: [PATCH 06/23] More cordex fixes --- .../cosmo_crclim_v1_1.py | 20 +++++++++++++++++++ .../cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py | 4 +--- .../ichec_ec_earth/cosmo_crclim_v1_1.py | 20 +++++++++++++++++++ .../cordex/ichec_ec_earth/hadrem3_ga7_05.py | 4 +--- .../mohc_hadgem2_es/cosmo_crclim_v1_1.py | 20 +++++++++++++++++++ .../cordex/mohc_hadgem2_es/hadrem3_ga7_05.py | 4 +--- .../mpi_m_mpi_esm_lr/cosmo_crclim_v1_1.py | 20 +++++++++++++++++++ .../cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py | 4 +--- .../cordex/ncc_noresm1_m/cosmo_crclim_v1_1.py | 20 +++++++++++++++++++ .../cordex/ncc_noresm1_m/hadrem3_ga7_05.py | 4 +--- .../defaults/extra_facets_cordex.yml | 12 +++++++++++ 11 files changed, 117 insertions(+), 15 deletions(-) create mode 100644 esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/cosmo_crclim_v1_1.py create mode 100644 esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/cosmo_crclim_v1_1.py create mode 100644 esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/cosmo_crclim_v1_1.py create mode 100644 esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/cosmo_crclim_v1_1.py create mode 100644 esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/cosmo_crclim_v1_1.py diff --git a/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/cosmo_crclim_v1_1.py b/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/cosmo_crclim_v1_1.py new file mode 100644 index 0000000000..8dd6cbf562 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/cosmo_crclim_v1_1.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from esmvalcore.cmor.fix import Fix + +if TYPE_CHECKING: + from collections.abc import Sequence + + from iris.cube import Cube + + +class Snw(Fix): + """Fixes for snw.""" + + def fix_metadata(self, cubes: Sequence[Cube]) -> Sequence[Cube]: + cube = self.get_cube_from_list(cubes) + cube = cube.copy() + cube.remove_coord("height") + return [cube] diff --git a/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py index ad169ebb0b..68ee6f8046 100644 --- a/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py @@ -4,6 +4,4 @@ MOHCHadREM3GA705 as BaseFix, ) -Tas = BaseFix - -Pr = BaseFix +AllVars = BaseFix diff --git a/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/cosmo_crclim_v1_1.py b/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/cosmo_crclim_v1_1.py new file mode 100644 index 0000000000..8dd6cbf562 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/cosmo_crclim_v1_1.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from esmvalcore.cmor.fix import Fix + +if TYPE_CHECKING: + from collections.abc import Sequence + + from iris.cube import Cube + + +class Snw(Fix): + """Fixes for snw.""" + + def fix_metadata(self, cubes: Sequence[Cube]) -> Sequence[Cube]: + cube = self.get_cube_from_list(cubes) + cube = cube.copy() + cube.remove_coord("height") + return [cube] diff --git a/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/hadrem3_ga7_05.py index fd9743e0af..cfbf5466cd 100644 --- a/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/hadrem3_ga7_05.py @@ -4,6 +4,4 @@ MOHCHadREM3GA705 as BaseFix, ) -Tas = BaseFix - -Pr = BaseFix +AllVars = BaseFix diff --git a/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/cosmo_crclim_v1_1.py b/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/cosmo_crclim_v1_1.py new file mode 100644 index 0000000000..8dd6cbf562 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/cosmo_crclim_v1_1.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from esmvalcore.cmor.fix import Fix + +if TYPE_CHECKING: + from collections.abc import Sequence + + from iris.cube import Cube + + +class Snw(Fix): + """Fixes for snw.""" + + def fix_metadata(self, cubes: Sequence[Cube]) -> Sequence[Cube]: + cube = self.get_cube_from_list(cubes) + cube = cube.copy() + cube.remove_coord("height") + return [cube] diff --git a/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/hadrem3_ga7_05.py index 2852e95492..6811ae2fe8 100644 --- a/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/hadrem3_ga7_05.py @@ -4,6 +4,4 @@ MOHCHadREM3GA705 as BaseFix, ) -Tas = BaseFix - -Pr = BaseFix +AllVars = BaseFix diff --git a/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/cosmo_crclim_v1_1.py b/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/cosmo_crclim_v1_1.py new file mode 100644 index 0000000000..8dd6cbf562 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/cosmo_crclim_v1_1.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from esmvalcore.cmor.fix import Fix + +if TYPE_CHECKING: + from collections.abc import Sequence + + from iris.cube import Cube + + +class Snw(Fix): + """Fixes for snw.""" + + def fix_metadata(self, cubes: Sequence[Cube]) -> Sequence[Cube]: + cube = self.get_cube_from_list(cubes) + cube = cube.copy() + cube.remove_coord("height") + return [cube] diff --git a/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py index c2d52052e9..4ae51a1216 100644 --- a/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py @@ -4,6 +4,4 @@ MOHCHadREM3GA705 as BaseFix, ) -Tas = BaseFix - -Pr = BaseFix +AllVars = BaseFix diff --git a/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/cosmo_crclim_v1_1.py b/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/cosmo_crclim_v1_1.py new file mode 100644 index 0000000000..8dd6cbf562 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/cosmo_crclim_v1_1.py @@ -0,0 +1,20 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +from esmvalcore.cmor.fix import Fix + +if TYPE_CHECKING: + from collections.abc import Sequence + + from iris.cube import Cube + + +class Snw(Fix): + """Fixes for snw.""" + + def fix_metadata(self, cubes: Sequence[Cube]) -> Sequence[Cube]: + cube = self.get_cube_from_list(cubes) + cube = cube.copy() + cube.remove_coord("height") + return [cube] diff --git a/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py index 97cac1a896..ad72e3a449 100644 --- a/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py @@ -4,6 +4,4 @@ MOHCHadREM3GA705 as BaseFix, ) -Tas = BaseFix - -Pr = BaseFix +AllVars = BaseFix diff --git a/esmvalcore/config/configurations/defaults/extra_facets_cordex.yml b/esmvalcore/config/configurations/defaults/extra_facets_cordex.yml index 62f502a3f7..a8a042f24f 100644 --- a/esmvalcore/config/configurations/defaults/extra_facets_cordex.yml +++ b/esmvalcore/config/configurations/defaults/extra_facets_cordex.yml @@ -11,7 +11,19 @@ projects: '*': '*': use_standard_grid: true + COSMO-crCLIM-v1-1: + '*': + '*': + use_standard_grid: true # lat/lon coords missing bounds HIRHAM5: '*': '*': use_standard_grid: true + RACMO22E: + '*': + '*': + use_standard_grid: true # lat/lon coords missing bounds + RCA4: + '*': + '*': + use_standard_grid: true # lat/lon coords missing bounds From b57e930cc857e24c864529e775564a6e788a70fe Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Mon, 29 Jun 2026 18:00:15 +0200 Subject: [PATCH 07/23] Fix sftlf long name of HadREM3-GA7-05 --- .../cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py | 22 +++++++++++++++++++ .../ichec_ec_earth/cosmo_crclim_v1_1.py | 21 ++++-------------- .../cordex/ichec_ec_earth/hadrem3_ga7_05.py | 7 ++++++ .../mohc_hadgem2_es/cosmo_crclim_v1_1.py | 21 ++++-------------- .../cordex/mohc_hadgem2_es/hadrem3_ga7_05.py | 7 ++++++ .../mpi_m_mpi_esm_lr/cosmo_crclim_v1_1.py | 21 ++++-------------- .../cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py | 7 ++++++ .../cordex/ncc_noresm1_m/cosmo_crclim_v1_1.py | 21 ++++-------------- .../cordex/ncc_noresm1_m/hadrem3_ga7_05.py | 7 ++++++ 9 files changed, 66 insertions(+), 68 deletions(-) diff --git a/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py index 68ee6f8046..9cbed78315 100644 --- a/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py @@ -1,7 +1,29 @@ """Fixes for rcm HadREM3-GA7-05 driven by CNRM-CERFACS-CNRM-CM5.""" +from __future__ import annotations + +from typing import TYPE_CHECKING + from esmvalcore.cmor._fixes.cordex.cordex_fixes import ( MOHCHadREM3GA705 as BaseFix, ) +from esmvalcore.cmor._fixes.fix import Fix + +if TYPE_CHECKING: + from collections.abc import Sequence + + from iris.cube import Cube + AllVars = BaseFix + + +class Sftlf(Fix): + """Fixes for sftlf.""" + + def fix_metadata(self, cubes: Sequence[Cube]) -> Sequence[Cube]: + """Fix metadata.""" + cube = self.get_cube_from_list(cubes, "sftlf") + cube = cube.copy() + cube.long_name = self.vardef.long_name + return [cube] diff --git a/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/cosmo_crclim_v1_1.py b/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/cosmo_crclim_v1_1.py index 8dd6cbf562..e9d8f26514 100644 --- a/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/cosmo_crclim_v1_1.py +++ b/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/cosmo_crclim_v1_1.py @@ -1,20 +1,7 @@ -from __future__ import annotations +from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.cosmo_crclim_v1_1 import ( + Snw as BaseSnw, +) -from typing import TYPE_CHECKING -from esmvalcore.cmor.fix import Fix - -if TYPE_CHECKING: - from collections.abc import Sequence - - from iris.cube import Cube - - -class Snw(Fix): +class Snw(BaseSnw): """Fixes for snw.""" - - def fix_metadata(self, cubes: Sequence[Cube]) -> Sequence[Cube]: - cube = self.get_cube_from_list(cubes) - cube = cube.copy() - cube.remove_coord("height") - return [cube] diff --git a/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/hadrem3_ga7_05.py index cfbf5466cd..24372a44eb 100644 --- a/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/hadrem3_ga7_05.py @@ -1,7 +1,14 @@ """Fixes for rcm HadREM3-GA7-05 driven by ICHEC-EC-EARTH.""" +from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hadrem3_ga7_05 import ( + Sftlf as BaseSftlf, +) from esmvalcore.cmor._fixes.cordex.cordex_fixes import ( MOHCHadREM3GA705 as BaseFix, ) AllVars = BaseFix + + +class Sftlf(BaseSftlf): + """Fixes for sftlf.""" diff --git a/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/cosmo_crclim_v1_1.py b/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/cosmo_crclim_v1_1.py index 8dd6cbf562..e9d8f26514 100644 --- a/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/cosmo_crclim_v1_1.py +++ b/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/cosmo_crclim_v1_1.py @@ -1,20 +1,7 @@ -from __future__ import annotations +from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.cosmo_crclim_v1_1 import ( + Snw as BaseSnw, +) -from typing import TYPE_CHECKING -from esmvalcore.cmor.fix import Fix - -if TYPE_CHECKING: - from collections.abc import Sequence - - from iris.cube import Cube - - -class Snw(Fix): +class Snw(BaseSnw): """Fixes for snw.""" - - def fix_metadata(self, cubes: Sequence[Cube]) -> Sequence[Cube]: - cube = self.get_cube_from_list(cubes) - cube = cube.copy() - cube.remove_coord("height") - return [cube] diff --git a/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/hadrem3_ga7_05.py index 6811ae2fe8..622aeef00b 100644 --- a/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/hadrem3_ga7_05.py @@ -1,7 +1,14 @@ """Fixes for rcm HadREM3-GA7-05 driven by MOHC-HadGEM2-ES.""" +from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hadrem3_ga7_05 import ( + Sftlf as BaseSftlf, +) from esmvalcore.cmor._fixes.cordex.cordex_fixes import ( MOHCHadREM3GA705 as BaseFix, ) AllVars = BaseFix + + +class Sftlf(BaseSftlf): + """Fixes for sftlf.""" diff --git a/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/cosmo_crclim_v1_1.py b/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/cosmo_crclim_v1_1.py index 8dd6cbf562..e9d8f26514 100644 --- a/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/cosmo_crclim_v1_1.py +++ b/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/cosmo_crclim_v1_1.py @@ -1,20 +1,7 @@ -from __future__ import annotations +from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.cosmo_crclim_v1_1 import ( + Snw as BaseSnw, +) -from typing import TYPE_CHECKING -from esmvalcore.cmor.fix import Fix - -if TYPE_CHECKING: - from collections.abc import Sequence - - from iris.cube import Cube - - -class Snw(Fix): +class Snw(BaseSnw): """Fixes for snw.""" - - def fix_metadata(self, cubes: Sequence[Cube]) -> Sequence[Cube]: - cube = self.get_cube_from_list(cubes) - cube = cube.copy() - cube.remove_coord("height") - return [cube] diff --git a/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py index 4ae51a1216..58fb2a9b1b 100644 --- a/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py @@ -1,7 +1,14 @@ """Fixes for rcm HadREM3-GA7-05 driven by MPI-M-MPI-ESM-LR.""" +from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hadrem3_ga7_05 import ( + Sftlf as BaseSftlf, +) from esmvalcore.cmor._fixes.cordex.cordex_fixes import ( MOHCHadREM3GA705 as BaseFix, ) AllVars = BaseFix + + +class Sftlf(BaseSftlf): + """Fixes for sftlf.""" diff --git a/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/cosmo_crclim_v1_1.py b/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/cosmo_crclim_v1_1.py index 8dd6cbf562..e9d8f26514 100644 --- a/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/cosmo_crclim_v1_1.py +++ b/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/cosmo_crclim_v1_1.py @@ -1,20 +1,7 @@ -from __future__ import annotations +from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.cosmo_crclim_v1_1 import ( + Snw as BaseSnw, +) -from typing import TYPE_CHECKING -from esmvalcore.cmor.fix import Fix - -if TYPE_CHECKING: - from collections.abc import Sequence - - from iris.cube import Cube - - -class Snw(Fix): +class Snw(BaseSnw): """Fixes for snw.""" - - def fix_metadata(self, cubes: Sequence[Cube]) -> Sequence[Cube]: - cube = self.get_cube_from_list(cubes) - cube = cube.copy() - cube.remove_coord("height") - return [cube] diff --git a/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py index ad72e3a449..13280cf9b2 100644 --- a/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py @@ -1,7 +1,14 @@ """Fixes for rcm HadREM3-GA7-05 driven by NCC-NorESM1-M.""" +from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hadrem3_ga7_05 import ( + Sftlf as BaseSftlf, +) from esmvalcore.cmor._fixes.cordex.cordex_fixes import ( MOHCHadREM3GA705 as BaseFix, ) AllVars = BaseFix + + +class Sftlf(BaseSftlf): + """Fixes for sftlf.""" From 369c96c54c92f4569f442a497d74d5ac23a825f5 Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Wed, 1 Jul 2026 17:57:07 +0200 Subject: [PATCH 08/23] Always apply standard long_name for HadREM3-GA7-05 --- .../cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py | 20 ------------------- esmvalcore/cmor/_fixes/cordex/cordex_fixes.py | 1 + .../cordex/ichec_ec_earth/hadrem3_ga7_05.py | 7 ------- .../cordex/mohc_hadgem2_es/hadrem3_ga7_05.py | 7 ------- .../cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py | 7 ------- .../cordex/ncc_noresm1_m/hadrem3_ga7_05.py | 7 ------- 6 files changed, 1 insertion(+), 48 deletions(-) diff --git a/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py index 9cbed78315..b6cbf1defb 100644 --- a/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py @@ -2,28 +2,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING - from esmvalcore.cmor._fixes.cordex.cordex_fixes import ( MOHCHadREM3GA705 as BaseFix, ) -from esmvalcore.cmor._fixes.fix import Fix - -if TYPE_CHECKING: - from collections.abc import Sequence - - from iris.cube import Cube - AllVars = BaseFix - - -class Sftlf(Fix): - """Fixes for sftlf.""" - - def fix_metadata(self, cubes: Sequence[Cube]) -> Sequence[Cube]: - """Fix metadata.""" - cube = self.get_cube_from_list(cubes, "sftlf") - cube = cube.copy() - cube.long_name = self.vardef.long_name - return [cube] diff --git a/esmvalcore/cmor/_fixes/cordex/cordex_fixes.py b/esmvalcore/cmor/_fixes/cordex/cordex_fixes.py index c3e5556687..7982c278e3 100644 --- a/esmvalcore/cmor/_fixes/cordex/cordex_fixes.py +++ b/esmvalcore/cmor/_fixes/cordex/cordex_fixes.py @@ -71,6 +71,7 @@ def fix_metadata(self, cubes): iris.cube.CubeList """ for cube in cubes: + cube.long_name = self.vardef.long_name cube.coord("latitude").var_name = "lat" cube.coord("longitude").var_name = "lon" for coord in cube.coords("time"): diff --git a/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/hadrem3_ga7_05.py index 24372a44eb..cfbf5466cd 100644 --- a/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/hadrem3_ga7_05.py @@ -1,14 +1,7 @@ """Fixes for rcm HadREM3-GA7-05 driven by ICHEC-EC-EARTH.""" -from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hadrem3_ga7_05 import ( - Sftlf as BaseSftlf, -) from esmvalcore.cmor._fixes.cordex.cordex_fixes import ( MOHCHadREM3GA705 as BaseFix, ) AllVars = BaseFix - - -class Sftlf(BaseSftlf): - """Fixes for sftlf.""" diff --git a/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/hadrem3_ga7_05.py index 622aeef00b..6811ae2fe8 100644 --- a/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/hadrem3_ga7_05.py @@ -1,14 +1,7 @@ """Fixes for rcm HadREM3-GA7-05 driven by MOHC-HadGEM2-ES.""" -from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hadrem3_ga7_05 import ( - Sftlf as BaseSftlf, -) from esmvalcore.cmor._fixes.cordex.cordex_fixes import ( MOHCHadREM3GA705 as BaseFix, ) AllVars = BaseFix - - -class Sftlf(BaseSftlf): - """Fixes for sftlf.""" diff --git a/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py index 58fb2a9b1b..4ae51a1216 100644 --- a/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py @@ -1,14 +1,7 @@ """Fixes for rcm HadREM3-GA7-05 driven by MPI-M-MPI-ESM-LR.""" -from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hadrem3_ga7_05 import ( - Sftlf as BaseSftlf, -) from esmvalcore.cmor._fixes.cordex.cordex_fixes import ( MOHCHadREM3GA705 as BaseFix, ) AllVars = BaseFix - - -class Sftlf(BaseSftlf): - """Fixes for sftlf.""" diff --git a/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py index 13280cf9b2..ad72e3a449 100644 --- a/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py @@ -1,14 +1,7 @@ """Fixes for rcm HadREM3-GA7-05 driven by NCC-NorESM1-M.""" -from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hadrem3_ga7_05 import ( - Sftlf as BaseSftlf, -) from esmvalcore.cmor._fixes.cordex.cordex_fixes import ( MOHCHadREM3GA705 as BaseFix, ) AllVars = BaseFix - - -class Sftlf(BaseSftlf): - """Fixes for sftlf.""" From 1711bb88cc1323d984e654f10adf406063a763db Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Wed, 1 Jul 2026 20:50:36 +0200 Subject: [PATCH 09/23] Add more facets to alias --- esmvalcore/_recipe/to_datasets.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/esmvalcore/_recipe/to_datasets.py b/esmvalcore/_recipe/to_datasets.py index c5bf1e59bc..02e0fcfd74 100644 --- a/esmvalcore/_recipe/to_datasets.py +++ b/esmvalcore/_recipe/to_datasets.py @@ -33,12 +33,19 @@ _ALIAS_INFO_KEYS: tuple[str, ...] = ( "project", - "activity", - "driver", + "mip", + "short_name", + "branding_suffix", "dataset", + "rcm_version", + "driver", + "ensemble", "exp", "sub_experiment", - "ensemble", + "frequency", + "domain", + "region", + "grid", "version", ) """List of keys to be used to compose the alias, ordered by priority.""" From 4773c70d91da823acd38305c0c244da497130737 Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Wed, 1 Jul 2026 21:16:09 +0200 Subject: [PATCH 10/23] Avoid variable related facets --- esmvalcore/_recipe/to_datasets.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/esmvalcore/_recipe/to_datasets.py b/esmvalcore/_recipe/to_datasets.py index 02e0fcfd74..903447f3fe 100644 --- a/esmvalcore/_recipe/to_datasets.py +++ b/esmvalcore/_recipe/to_datasets.py @@ -33,9 +33,6 @@ _ALIAS_INFO_KEYS: tuple[str, ...] = ( "project", - "mip", - "short_name", - "branding_suffix", "dataset", "rcm_version", "driver", From 5b22c7ac1d991935acb6ff98ebdcbbba2f64d682 Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Wed, 1 Jul 2026 21:33:31 +0200 Subject: [PATCH 11/23] Also correct standard name --- esmvalcore/cmor/_fixes/cordex/cordex_fixes.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esmvalcore/cmor/_fixes/cordex/cordex_fixes.py b/esmvalcore/cmor/_fixes/cordex/cordex_fixes.py index 7982c278e3..7bc2173f81 100644 --- a/esmvalcore/cmor/_fixes/cordex/cordex_fixes.py +++ b/esmvalcore/cmor/_fixes/cordex/cordex_fixes.py @@ -71,6 +71,8 @@ def fix_metadata(self, cubes): iris.cube.CubeList """ for cube in cubes: + if self.vardef.standard_name: + cube.standard_name = self.vardef.standard_name cube.long_name = self.vardef.long_name cube.coord("latitude").var_name = "lat" cube.coord("longitude").var_name = "lon" From 683767c7cd1e7eb26dd002799955b6f3b9b50bd8 Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Thu, 2 Jul 2026 09:06:27 +0200 Subject: [PATCH 12/23] Add fewer facets --- esmvalcore/_recipe/to_datasets.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/esmvalcore/_recipe/to_datasets.py b/esmvalcore/_recipe/to_datasets.py index 903447f3fe..7a5dcdcc81 100644 --- a/esmvalcore/_recipe/to_datasets.py +++ b/esmvalcore/_recipe/to_datasets.py @@ -39,9 +39,6 @@ "ensemble", "exp", "sub_experiment", - "frequency", - "domain", - "region", "grid", "version", ) From f469e53f94777fcc9fd2d8744cf49bb155817198 Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Thu, 2 Jul 2026 09:11:31 +0200 Subject: [PATCH 13/23] More updates to facets used for alias and grouping --- esmvalcore/_recipe/to_datasets.py | 2 +- esmvalcore/preprocessor/_multimodel.py | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/esmvalcore/_recipe/to_datasets.py b/esmvalcore/_recipe/to_datasets.py index 7a5dcdcc81..9c391f4507 100644 --- a/esmvalcore/_recipe/to_datasets.py +++ b/esmvalcore/_recipe/to_datasets.py @@ -33,11 +33,11 @@ _ALIAS_INFO_KEYS: tuple[str, ...] = ( "project", + "exp", "dataset", "rcm_version", "driver", "ensemble", - "exp", "sub_experiment", "grid", "version", diff --git a/esmvalcore/preprocessor/_multimodel.py b/esmvalcore/preprocessor/_multimodel.py index d8ce839ff7..e929f23670 100644 --- a/esmvalcore/preprocessor/_multimodel.py +++ b/esmvalcore/preprocessor/_multimodel.py @@ -889,7 +889,15 @@ def ensemble_statistics( :func:`esmvalcore.preprocessor.multi_model_statistics` for the full description of the core statistics function. """ - ensemble_grouping = ("project", "dataset", "exp", "sub_experiment") + ensemble_grouping = ( + "project", + "exp", + "dataset", + "rcm_version", + "driver", + "grid", + "sub_experiment", + ) return multi_model_statistics( products=products, span=span, From 84742a106ef89f4920e9c685739ca5b562e341db Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Thu, 2 Jul 2026 09:49:39 +0200 Subject: [PATCH 14/23] Update tests --- .../cmor/_fixes/cordex/test_cordex_fixes.py | 20 ++++++++++++++----- tests/integration/recipe/test_recipe.py | 16 +++++++-------- .../_multimodel/test_multimodel.py | 6 +++--- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/tests/integration/cmor/_fixes/cordex/test_cordex_fixes.py b/tests/integration/cmor/_fixes/cordex/test_cordex_fixes.py index 590b61e7ca..3bf2feea82 100644 --- a/tests/integration/cmor/_fixes/cordex/test_cordex_fixes.py +++ b/tests/integration/cmor/_fixes/cordex/test_cordex_fixes.py @@ -18,13 +18,14 @@ TimeLongName, ) from esmvalcore.cmor.fix import Fix +from esmvalcore.cmor.table import get_var_info if TYPE_CHECKING: import pytest_mock @pytest.fixture -def cubes(): +def cubes() -> iris.cube.CubeList: correct_time_coord = iris.coords.DimCoord( [0.0], var_name="time", @@ -83,7 +84,7 @@ def cubes(): @pytest.fixture -def cordex_cubes(): +def cordex_cubes() -> iris.cube.CubeList: coord_system = iris.coord_systems.RotatedGeogCS( grid_north_pole_latitude=39.25, grid_north_pole_longitude=-162, @@ -134,8 +135,15 @@ def cordex_cubes(): ("longitude", "lon", "longitude"), ], ) -def test_mohchadrem3ga705_fix_metadata(cubes, coord, var_name, long_name): - fix = MOHCHadREM3GA705(None) +def test_mohchadrem3ga705_fix_metadata( + cubes: iris.cube.CubeList, + coord: str, + var_name: str, + long_name: str, +) -> None: + vardef = get_var_info("CORDEX", "day", "ts") + assert vardef is not None + fix = MOHCHadREM3GA705(vardef) out_cubes = fix.fix_metadata(cubes) assert cubes is out_cubes for cube in out_cubes: @@ -148,7 +156,9 @@ def test_mohchadrem3ga705_fix_metadata_no_time_coord( ) -> None: for cube in cubes: cube.remove_coord("time") - fix = MOHCHadREM3GA705(None) # type: ignore[arg-type] + vardef = get_var_info("CORDEX", "day", "ts") + assert vardef is not None + fix = MOHCHadREM3GA705(vardef) out_cubes = fix.fix_metadata(cubes) assert cubes is out_cubes for cube in out_cubes: diff --git a/tests/integration/recipe/test_recipe.py b/tests/integration/recipe/test_recipe.py index 17a0521120..72931db8b0 100644 --- a/tests/integration/recipe/test_recipe.py +++ b/tests/integration/recipe/test_recipe.py @@ -1730,10 +1730,10 @@ def test_alias_generation(tmp_path, patched_datafinder, session): # noqa: C901, - {dataset: FGOALS-g3, sub_experiment: s1961, ensemble: r1, institute: CAS} - {project: OBS, dataset: ERA-Interim, version: 1} - {project: OBS, dataset: ERA-Interim, version: 2} - - {project: CMIP6, activity: CMP, dataset: GF3, ensemble: r1, institute: fake} - - {project: CMIP6, activity: CMP, dataset: GF2, ensemble: r1, institute: fake} - - {project: CMIP6, activity: HRMP, dataset: EC, ensemble: r1, institute: fake} - - {project: CMIP6, activity: HRMP, dataset: HA, ensemble: r1, institute: fake} + - {project: CMIP6, activity: CMP, exp: a, dataset: GF3, ensemble: r1, institute: fake} + - {project: CMIP6, activity: CMP, exp: a, dataset: GF2, ensemble: r1, institute: fake} + - {project: CMIP6, activity: HRMP, exp: b, dataset: EC, ensemble: r1, institute: fake} + - {project: CMIP6, activity: HRMP, exp: b, dataset: HA, ensemble: r1, institute: fake} - {project: CORDEX, driver: ICHEC-EC-EARTH, dataset: RCA4, ensemble: r1, mip: mon, institute: SMHI} - {project: CORDEX, driver: MIROC-MIROC5, dataset: RCA4, ensemble: r1, mip: mon, institute: SMHI} scripts: null @@ -1758,13 +1758,13 @@ def test_alias_generation(tmp_path, patched_datafinder, session): # noqa: C901, assert dataset["alias"] == "my_alias" elif dataset["project"] == "CMIP6": if dataset["dataset"] == "GF3": - assert dataset["alias"] == "CMIP6_CMP_GF3" + assert dataset["alias"] == "CMIP6_a_GF3" elif dataset["dataset"] == "GF2": - assert dataset["alias"] == "CMIP6_CMP_GF2" + assert dataset["alias"] == "CMIP6_a_GF2" elif dataset["dataset"] == "EC": - assert dataset["alias"] == "CMIP6_HRMP_EC" + assert dataset["alias"] == "CMIP6_b_EC" else: - assert dataset["alias"] == "CMIP6_HRMP_HA" + assert dataset["alias"] == "CMIP6_b_HA" elif dataset["project"] == "CORDEX": if dataset["driver"] == "ICHEC-EC-EARTH": assert dataset["alias"] == "CORDEX_ICHEC-EC-EARTH" diff --git a/tests/unit/preprocessor/_multimodel/test_multimodel.py b/tests/unit/preprocessor/_multimodel/test_multimodel.py index 223ff454c4..e919740398 100644 --- a/tests/unit/preprocessor/_multimodel/test_multimodel.py +++ b/tests/unit/preprocessor/_multimodel/test_multimodel.py @@ -968,8 +968,8 @@ def test_ensemble_products(): output1 = PreprocessorFile() output2 = PreprocessorFile() output_products = { - "project_dataset_exp": {"mean": output1}, - "project_dataset2_exp": {"mean": output2}, + "project_exp_dataset": {"mean": output1}, + "project_exp_dataset2": {"mean": output2}, } kwargs = { @@ -1625,7 +1625,7 @@ def test_single_input_ensemble_statistics(products, stat): } products = {PreprocessorFile(cube, attributes=attributes)} output = PreprocessorFile() - output_products = {"project_dataset_exp": {stat_id: output}} + output_products = {"project_exp_dataset": {stat_id: output}} kwargs = { "statistics": [stat], "output_products": output_products, From 0704d4e523e0c54fceadba40a976bbafd0655d1d Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Thu, 2 Jul 2026 11:33:29 +0200 Subject: [PATCH 15/23] Add more tests --- .../cordex/test_cnrm_cerfacs_cnrm_cm5.py | 32 ++++++++++++++++++- .../cmor/_fixes/cordex/test_ichec_ec_earth.py | 18 +++++++++-- .../_fixes/cordex/test_ipsl_ipsl_cm5a_mr.py | 2 +- .../_fixes/cordex/test_mohc_hadgem2_es.py | 19 +++++++++-- .../_fixes/cordex/test_mpi_m_mpi_esm_lr.py | 12 +++++++ .../cmor/_fixes/cordex/test_ncc_noresm1_m.py | 18 +++++++++-- 6 files changed, 93 insertions(+), 8 deletions(-) diff --git a/tests/integration/cmor/_fixes/cordex/test_cnrm_cerfacs_cnrm_cm5.py b/tests/integration/cmor/_fixes/cordex/test_cnrm_cerfacs_cnrm_cm5.py index 12707c8835..a9a6f92d2e 100644 --- a/tests/integration/cmor/_fixes/cordex/test_cnrm_cerfacs_cnrm_cm5.py +++ b/tests/integration/cmor/_fixes/cordex/test_cnrm_cerfacs_cnrm_cm5.py @@ -137,7 +137,7 @@ def test_wrf381p_height_fix(): var_name="tas", dim_coords_and_dims=[(time_coord, 0)], ) - vardef = get_var_info("CMIP6", "Amon", "tas") + vardef = get_var_info("CORDEX", "day", "tas") fix = wrf381p.Tas(vardef) out_cubes = fix.fix_metadata([cube]) assert out_cubes[0].coord("height").points == 2.0 @@ -152,3 +152,33 @@ def test_get_cclm4_8_17fix() -> None: extra_facets={"driver": "CNRM-CERFACS-CNRM-CM5"}, ) assert any(isinstance(fix, CLMcomCCLM4817) for fix in fixes) + + +def test_cosmo_crclim_v1_1_snw_drop_height_coord(): + height_coord = iris.coords.AuxCoord( + [2.0], + var_name="height", + standard_name="height", + long_name="height", + ) + cube = iris.cube.Cube( + [10.0], + var_name="snw", + aux_coords_and_dims=[(height_coord, 0)], + ) + fixes = Fix.get_fixes( + "CORDEX", + "COSMO-crCLIM-v1-1", + "day", + "snw", + extra_facets={ + "driver": "CNRM-CERFACS-CNRM-CM5", + "domain": "EUR-11", + }, + ) + cubes = [cube] + for fix in fixes: + cubes = fix.fix_metadata(cubes) + assert len(cubes) == 1 + assert cubes[0].var_name == "snw" + assert not cubes[0].coords("height") diff --git a/tests/integration/cmor/_fixes/cordex/test_ichec_ec_earth.py b/tests/integration/cmor/_fixes/cordex/test_ichec_ec_earth.py index f921f1b93f..7509bcb486 100644 --- a/tests/integration/cmor/_fixes/cordex/test_ichec_ec_earth.py +++ b/tests/integration/cmor/_fixes/cordex/test_ichec_ec_earth.py @@ -3,7 +3,10 @@ import iris import pytest -from esmvalcore.cmor._fixes.cordex.ichec_ec_earth import wrf381p +from esmvalcore.cmor._fixes.cordex.ichec_ec_earth import ( + cosmo_crclim_v1_1, + wrf381p, +) from esmvalcore.cmor.fix import Fix from esmvalcore.cmor.table import get_var_info @@ -81,7 +84,18 @@ def test_wrf381p_height_fix(): var_name="tas", dim_coords_and_dims=[(time_coord, 0)], ) - vardef = get_var_info("CMIP6", "Amon", "tas") + vardef = get_var_info("CORDEX", "day", "tas") fix = wrf381p.Tas(vardef) out_cubes = fix.fix_metadata([cube]) assert out_cubes[0].coord("height").points == 2.0 + + +def test_get_cosmo_crclim_v1_1_fix() -> None: + fixes = Fix.get_fixes( + "CORDEX", + "COSMO-crCLIM-v1-1", + "day", + "snw", + extra_facets={"driver": "ICHEC-EC-Earth"}, + ) + assert any(isinstance(fix, cosmo_crclim_v1_1.Snw) for fix in fixes) diff --git a/tests/integration/cmor/_fixes/cordex/test_ipsl_ipsl_cm5a_mr.py b/tests/integration/cmor/_fixes/cordex/test_ipsl_ipsl_cm5a_mr.py index 86c85abfd3..be00e84c39 100644 --- a/tests/integration/cmor/_fixes/cordex/test_ipsl_ipsl_cm5a_mr.py +++ b/tests/integration/cmor/_fixes/cordex/test_ipsl_ipsl_cm5a_mr.py @@ -35,7 +35,7 @@ def test_wrf381p_height_fix(): var_name="tas", dim_coords_and_dims=[(time_coord, 0)], ) - vardef = get_var_info("CMIP6", "Amon", "tas") + vardef = get_var_info("CORDEX", "day", "tas") fix = wrf381p.Tas(vardef) out_cubes = fix.fix_metadata([cube]) assert out_cubes[0].coord("height").points == 2.0 diff --git a/tests/integration/cmor/_fixes/cordex/test_mohc_hadgem2_es.py b/tests/integration/cmor/_fixes/cordex/test_mohc_hadgem2_es.py index fd85412cc4..90995a5d26 100644 --- a/tests/integration/cmor/_fixes/cordex/test_mohc_hadgem2_es.py +++ b/tests/integration/cmor/_fixes/cordex/test_mohc_hadgem2_es.py @@ -4,7 +4,11 @@ import pytest from esmvalcore.cmor._fixes.cordex.cordex_fixes import CLMcomCCLM4817 -from esmvalcore.cmor._fixes.cordex.mohc_hadgem2_es import hirham5, wrf381p +from esmvalcore.cmor._fixes.cordex.mohc_hadgem2_es import ( + cosmo_crclim_v1_1, + hirham5, + wrf381p, +) from esmvalcore.cmor.fix import Fix from esmvalcore.cmor.table import get_var_info @@ -153,7 +157,7 @@ def test_wrf381p_height_fix(): var_name="tas", dim_coords_and_dims=[(time_coord, 0)], ) - vardef = get_var_info("CMIP6", "Amon", "tas") + vardef = get_var_info("CORDEX", "day", "tas") fix = wrf381p.Tas(vardef) out_cubes = fix.fix_metadata([cube]) assert out_cubes[0].coord("height").points == 2.0 @@ -168,3 +172,14 @@ def test_get_cclm4_8_17fix() -> None: extra_facets={"driver": "MOHC-HadGEM2-ES"}, ) assert any(isinstance(fix, CLMcomCCLM4817) for fix in fixes) + + +def test_get_cosmo_crclim_v1_1_fix() -> None: + fixes = Fix.get_fixes( + "CORDEX", + "COSMO-crCLIM-v1-1", + "day", + "snw", + extra_facets={"driver": "MOHC-HadGEM2-ES"}, + ) + assert any(isinstance(fix, cosmo_crclim_v1_1.Snw) for fix in fixes) diff --git a/tests/integration/cmor/_fixes/cordex/test_mpi_m_mpi_esm_lr.py b/tests/integration/cmor/_fixes/cordex/test_mpi_m_mpi_esm_lr.py index e68301660c..2ad4c21daf 100644 --- a/tests/integration/cmor/_fixes/cordex/test_mpi_m_mpi_esm_lr.py +++ b/tests/integration/cmor/_fixes/cordex/test_mpi_m_mpi_esm_lr.py @@ -3,6 +3,7 @@ import pytest from esmvalcore.cmor._fixes.cordex.cordex_fixes import CLMcomCCLM4817 +from esmvalcore.cmor._fixes.cordex.mpi_m_mpi_esm_lr import cosmo_crclim_v1_1 from esmvalcore.cmor.fix import Fix @@ -50,3 +51,14 @@ def test_get_cclm4_8_17fix() -> None: extra_facets={"driver": "MPI-M-MPI-ESM-LR"}, ) assert any(isinstance(fix, CLMcomCCLM4817) for fix in fixes) + + +def test_get_cosmo_crclim_v1_1_fix() -> None: + fixes = Fix.get_fixes( + "CORDEX", + "COSMO-crCLIM-v1-1", + "day", + "snw", + extra_facets={"driver": "MPI-M-MPI-ESM-LR"}, + ) + assert any(isinstance(fix, cosmo_crclim_v1_1.Snw) for fix in fixes) diff --git a/tests/integration/cmor/_fixes/cordex/test_ncc_noresm1_m.py b/tests/integration/cmor/_fixes/cordex/test_ncc_noresm1_m.py index 99291139c8..db401cba49 100644 --- a/tests/integration/cmor/_fixes/cordex/test_ncc_noresm1_m.py +++ b/tests/integration/cmor/_fixes/cordex/test_ncc_noresm1_m.py @@ -3,7 +3,10 @@ import iris import pytest -from esmvalcore.cmor._fixes.cordex.ncc_noresm1_m import wrf381p +from esmvalcore.cmor._fixes.cordex.ncc_noresm1_m import ( + cosmo_crclim_v1_1, + wrf381p, +) from esmvalcore.cmor.fix import Fix from esmvalcore.cmor.table import get_var_info @@ -81,7 +84,18 @@ def test_wrf381p_height_fix(): var_name="tas", dim_coords_and_dims=[(time_coord, 0)], ) - vardef = get_var_info("CMIP6", "Amon", "tas") + vardef = get_var_info("CORDEX", "day", "tas") fix = wrf381p.Tas(vardef) out_cubes = fix.fix_metadata([cube]) assert out_cubes[0].coord("height").points == 2.0 + + +def test_get_cosmo_crclim_v1_1_fix() -> None: + fixes = Fix.get_fixes( + "CORDEX", + "COSMO-crCLIM-v1-1", + "day", + "snw", + extra_facets={"driver": "NCC-NorESM1-M"}, + ) + assert any(isinstance(fix, cosmo_crclim_v1_1.Snw) for fix in fixes) From 78ec4d52029cfbb7f5225d046d34f4f3c27a2e90 Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Thu, 2 Jul 2026 22:28:58 +0200 Subject: [PATCH 16/23] Fix merge error --- esmvalcore/preprocessor/_regrid.py | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/esmvalcore/preprocessor/_regrid.py b/esmvalcore/preprocessor/_regrid.py index 2a5ddde35b..3e7ecf7fd8 100644 --- a/esmvalcore/preprocessor/_regrid.py +++ b/esmvalcore/preprocessor/_regrid.py @@ -667,22 +667,19 @@ def _get_target_grid_cube( elif is_cordex_domain(target_grid): target_grid_cube = _cordex_stock_cube(target_grid) elif isinstance(target_grid, str): - if is_cordex_domain(target_grid): - target_grid_cube = _cordex_stock_cube(target_grid) - else: - # Generate a target grid from the provided cell-specification - target_grid_cube = _global_stock_cube( - target_grid, - lat_offset, - lon_offset, - ) - # Align the target grid coordinate system to the source - # coordinate system. - src_cs = cube.coord_system() - xcoord = target_grid_cube.coord(axis="x", dim_coords=True) - ycoord = target_grid_cube.coord(axis="y", dim_coords=True) - xcoord.coord_system = src_cs - ycoord.coord_system = src_cs + # Generate a target grid from the provided cell-specification + target_grid_cube = _global_stock_cube( + target_grid, + lat_offset, + lon_offset, + ) + # Align the target grid coordinate system to the source + # coordinate system. + src_cs = cube.coord_system() + xcoord = target_grid_cube.coord(axis="x", dim_coords=True) + ycoord = target_grid_cube.coord(axis="y", dim_coords=True) + xcoord.coord_system = src_cs + ycoord.coord_system = src_cs elif isinstance(target_grid, dict): # Generate a target grid from the provided specification, target_grid_cube = _regional_stock_cube(target_grid) From b12463372a1db4cb33b547b4a2e23bd0b54f8c91 Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Thu, 2 Jul 2026 22:29:53 +0200 Subject: [PATCH 17/23] Simplify --- .../cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py index b6cbf1defb..68ee6f8046 100644 --- a/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py @@ -1,7 +1,5 @@ """Fixes for rcm HadREM3-GA7-05 driven by CNRM-CERFACS-CNRM-CM5.""" -from __future__ import annotations - from esmvalcore.cmor._fixes.cordex.cordex_fixes import ( MOHCHadREM3GA705 as BaseFix, ) From 082a12130f18d2ba5e4fcb54262cf36a5cca1f55 Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Fri, 3 Jul 2026 12:14:09 +0200 Subject: [PATCH 18/23] Enable standard grid for WRF381P --- .../config/configurations/defaults/extra_facets_cordex.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esmvalcore/config/configurations/defaults/extra_facets_cordex.yml b/esmvalcore/config/configurations/defaults/extra_facets_cordex.yml index a8a042f24f..27af7282f6 100644 --- a/esmvalcore/config/configurations/defaults/extra_facets_cordex.yml +++ b/esmvalcore/config/configurations/defaults/extra_facets_cordex.yml @@ -27,3 +27,7 @@ projects: '*': '*': use_standard_grid: true # lat/lon coords missing bounds + WRF381P: + '*': + '*': + use_standard_grid: true # https://github.com/ESMValGroup/ESMValCore/issues/3145 From 160fc432104090d7201bf30159c208f6996674c5 Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Fri, 3 Jul 2026 12:27:37 +0200 Subject: [PATCH 19/23] Revert "Enable standard grid for WRF381P" This reverts commit 082a12130f18d2ba5e4fcb54262cf36a5cca1f55. --- .../config/configurations/defaults/extra_facets_cordex.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/esmvalcore/config/configurations/defaults/extra_facets_cordex.yml b/esmvalcore/config/configurations/defaults/extra_facets_cordex.yml index 27af7282f6..a8a042f24f 100644 --- a/esmvalcore/config/configurations/defaults/extra_facets_cordex.yml +++ b/esmvalcore/config/configurations/defaults/extra_facets_cordex.yml @@ -27,7 +27,3 @@ projects: '*': '*': use_standard_grid: true # lat/lon coords missing bounds - WRF381P: - '*': - '*': - use_standard_grid: true # https://github.com/ESMValGroup/ESMValCore/issues/3145 From 3e2d6981999228033b4bd8cb5c9231b58251c2cb Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Fri, 3 Jul 2026 14:56:53 +0200 Subject: [PATCH 20/23] Replace grid of WRF381P with driver MPI-M-MPI-ESM-LR with the standard grid --- .../_fixes/cordex/mpi_m_mpi_esm_lr/wrf381p.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/wrf381p.py diff --git a/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/wrf381p.py b/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/wrf381p.py new file mode 100644 index 0000000000..ccb830df1f --- /dev/null +++ b/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/wrf381p.py @@ -0,0 +1,41 @@ +"""Fixes for rcm WRF381P driven by MPI-M-MPI-ESM-LR.""" + +from esmvalcore.cmor.fix import Fix +from esmvalcore.preprocessor._regrid import _cordex_stock_cube + + +class AllVars(Fix): + """Fixes for all vars.""" + + def fix_metadata(self, cubes): + cube = self.get_cube_from_list(cubes).copy() + if cube.coords("projection_x_coordinate"): + # WRF381P datasets with the MPI-M-MPI-ESM-LR driver have bad + # projection coordinates containing all zeros, but the latitude + # and longitude points match those of the standard grid. Therefore + # we replace the bad coordinates with the standard rotated pole + # coordinates. + # https://github.com/ESMValGroup/ESMValCore/issues/3145 + standard_grid = _cordex_stock_cube(self.extra_facets["domain"]) + x_dim = cube.coord_dims("projection_x_coordinate") + y_dim = cube.coord_dims("projection_y_coordinate") + + # Remove the bad coordinates. + cube.remove_coord("projection_x_coordinate") + cube.remove_coord("projection_y_coordinate") + cube.remove_coord("longitude") + cube.remove_coord("latitude") + + # Add the standard rotated pole coordinates. + cube.add_dim_coord( + standard_grid.coord("grid_longitude"), + x_dim, + ) + cube.add_dim_coord( + standard_grid.coord("grid_latitude"), + y_dim, + ) + cube.add_aux_coord(standard_grid.coord("longitude"), y_dim + x_dim) + cube.add_aux_coord(standard_grid.coord("latitude"), y_dim + x_dim) + + return [cube] From 6c439054074f936275ab18418a0cbdc88360170d Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Fri, 3 Jul 2026 16:06:11 +0200 Subject: [PATCH 21/23] Add test and correct units of cordex standard grid --- esmvalcore/preprocessor/_regrid.py | 3 + .../_fixes/cordex/test_mpi_m_mpi_esm_lr.py | 102 +++++++++++++++++- .../_regrid/test_cordex_target_grid.py | 4 + 3 files changed, 108 insertions(+), 1 deletion(-) diff --git a/esmvalcore/preprocessor/_regrid.py b/esmvalcore/preprocessor/_regrid.py index 3e7ecf7fd8..b826f7bd3c 100644 --- a/esmvalcore/preprocessor/_regrid.py +++ b/esmvalcore/preprocessor/_regrid.py @@ -249,6 +249,9 @@ def _cordex_stock_cube(domain_name: str) -> Cube: (cube,) = ncdata.iris_xarray.cubes_from_xarray(domain) cube.coord("grid_latitude").guess_bounds() cube.coord("grid_longitude").guess_bounds() + # Restore the units that iris changed to degrees. + cube.coord("latitude").units = domain.lat.attrs["units"] + cube.coord("longitude").units = domain.lon.attrs["units"] return cube diff --git a/tests/integration/cmor/_fixes/cordex/test_mpi_m_mpi_esm_lr.py b/tests/integration/cmor/_fixes/cordex/test_mpi_m_mpi_esm_lr.py index 2ad4c21daf..cc4480c88b 100644 --- a/tests/integration/cmor/_fixes/cordex/test_mpi_m_mpi_esm_lr.py +++ b/tests/integration/cmor/_fixes/cordex/test_mpi_m_mpi_esm_lr.py @@ -1,9 +1,17 @@ """Tests for the fixes of driver MPI-M-MPI-ESM-LR.""" +import cordex as cx +import dask.array as da +import iris +import iris.coords +import iris.cube +import numpy as np import pytest from esmvalcore.cmor._fixes.cordex.cordex_fixes import CLMcomCCLM4817 -from esmvalcore.cmor._fixes.cordex.mpi_m_mpi_esm_lr import cosmo_crclim_v1_1 +from esmvalcore.cmor._fixes.cordex.mpi_m_mpi_esm_lr import ( + cosmo_crclim_v1_1, +) from esmvalcore.cmor.fix import Fix @@ -62,3 +70,95 @@ def test_get_cosmo_crclim_v1_1_fix() -> None: extra_facets={"driver": "MPI-M-MPI-ESM-LR"}, ) assert any(isinstance(fix, cosmo_crclim_v1_1.Snw) for fix in fixes) + + +def test_wrf381p_rlut_standard_grid() -> None: + + standard_grid = cx.domain("EUR-11") + cube = iris.cube.Cube( + da.empty((1, 412, 424), dtype="float32"), + var_name="rlut", + standard_name="toa_outgoing_longwave_flux", + long_name="TOA Outgoing Longwave Radiation", + units="W m-2", + dim_coords_and_dims=[ + ( + iris.coords.DimCoord( + [0], + bounds=[[-0.5, 0.5]], + var_name="time", + standard_name="time", + units="days since 2000-01-01", + ), + 0, + ), + ], + aux_coords_and_dims=[ + ( + iris.coords.AuxCoord( + np.zeros(412), + var_name="y", + standard_name="projection_y_coordinate", + units="m", + ), + (1,), + ), + ( + iris.coords.AuxCoord( + np.zeros(424), + var_name="x", + standard_name="projection_x_coordinate", + units="m", + ), + (2,), + ), + ( + iris.coords.AuxCoord( + standard_grid.lat.data + 1e-7, + var_name="lat", + standard_name="latitude", + units="degrees_north", + ), + (1, 2), + ), + ( + iris.coords.AuxCoord( + standard_grid.lon.data + 1e-7, + var_name="lon", + standard_name="longitude", + units="degrees_east", + ), + (1, 2), + ), + ], + ) + fixes = Fix.get_fixes( + "CORDEX", + "WRF381P", + "day", + "rlut", + extra_facets={ + "driver": "MPI-M-MPI-ESM-LR", + "domain": "EUR-11", + }, + ) + cubes = [cube] + for fix in fixes: + cubes = fix.fix_metadata(cubes) + assert len(cubes) == 1 + result = cubes[0] + for standard_name, var_name in [ + ("grid_latitude", "rlat"), + ("grid_longitude", "rlon"), + ("latitude", "lat"), + ("longitude", "lon"), + ]: + print("Checking coordinate:", standard_name) + coord = result.coord(standard_name) + var = standard_grid[var_name] + assert coord.units == var.attrs["units"] + expected_points = ( + var.data % 360 if standard_name == "longitude" else var.data + ) + np.testing.assert_almost_equal(coord.points, expected_points) + assert coord.has_bounds() diff --git a/tests/unit/preprocessor/_regrid/test_cordex_target_grid.py b/tests/unit/preprocessor/_regrid/test_cordex_target_grid.py index d357dfccc1..cc6a8dbb6e 100644 --- a/tests/unit/preprocessor/_regrid/test_cordex_target_grid.py +++ b/tests/unit/preprocessor/_regrid/test_cordex_target_grid.py @@ -75,6 +75,10 @@ def test_cordex_stock_cube_eur11(): assert cube.coord(var_name="rlon").has_bounds() assert cube.coord(var_name="lat").has_bounds() assert cube.coord(var_name="lon").has_bounds() + assert cube.coord(var_name="rlat").units == "degrees" + assert cube.coord(var_name="rlon").units == "degrees" + assert cube.coord(var_name="lat").units == "degrees_north" + assert cube.coord(var_name="lon").units == "degrees_east" @pytest.fixture From a0c9c3671a2bea86d011fbec9b1ed840da3f3b29 Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Fri, 3 Jul 2026 18:02:34 +0200 Subject: [PATCH 22/23] Correct clivi units --- .../cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py | 23 ++++++++++++++++ .../cordex/cnrm_cerfacs_cnrm_cm5/hirham5.py | 26 +++++++++++++++++++ .../cordex/ichec_ec_earth/hadrem3_ga7_05.py | 7 +++++ .../_fixes/cordex/ichec_ec_earth/hirham5.py | 9 +++++++ .../cordex/ipsl_ipsl_cm5a_mr/hirham5.py | 9 +++++++ .../cordex/mohc_hadgem2_es/cclm4_8_17.py | 24 +++++++++++++++++ .../_fixes/cordex/mohc_hadgem2_es/hirham5.py | 9 ++++++- .../cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py | 7 +++++ .../_fixes/cordex/mpi_m_mpi_esm_lr/hirham5.py | 9 +++++++ .../cordex/ncc_noresm1_m/hadrem3_ga7_05.py | 7 +++++ .../_fixes/cordex/ncc_noresm1_m/hirham5.py | 9 +++++++ 11 files changed, 138 insertions(+), 1 deletion(-) create mode 100644 esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hirham5.py create mode 100644 esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/hirham5.py create mode 100644 esmvalcore/cmor/_fixes/cordex/ipsl_ipsl_cm5a_mr/hirham5.py create mode 100644 esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hirham5.py create mode 100644 esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hirham5.py diff --git a/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py index 68ee6f8046..375efbd8e9 100644 --- a/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py @@ -1,7 +1,30 @@ """Fixes for rcm HadREM3-GA7-05 driven by CNRM-CERFACS-CNRM-CM5.""" +from __future__ import annotations + +from typing import TYPE_CHECKING + +import iris +import iris.cube + from esmvalcore.cmor._fixes.cordex.cordex_fixes import ( MOHCHadREM3GA705 as BaseFix, ) +from esmvalcore.cmor.fix import Fix + +if TYPE_CHECKING: + from collections.abc import Sequence AllVars = BaseFix + + +class Clivi(Fix): + """Fixes for variable clivi.""" + + def fix_metadata( + self, + cubes: Sequence[iris.cube.Cube], + ) -> Sequence[iris.cube.Cube]: + cube = self.get_cube_from_list(iris.cube.CubeList(cubes)).copy() + cube.data = cube.core_data() * 0.1 + return [cube] diff --git a/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hirham5.py b/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hirham5.py new file mode 100644 index 0000000000..d9fac70604 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hirham5.py @@ -0,0 +1,26 @@ +"""Fixes for rcm HIRHAM5 driven by CNRM-CERFACS-CNRM-CM5.""" + +from __future__ import annotations + +from typing import TYPE_CHECKING + +import iris +import iris.cube + +from esmvalcore.cmor.fix import Fix + +if TYPE_CHECKING: + from collections.abc import Sequence + + +class Clivi(Fix): + """Fixes for variable clivi.""" + + def fix_metadata( + self, + cubes: Sequence[iris.cube.Cube], + ) -> Sequence[iris.cube.Cube]: + cube = self.get_cube_from_list(iris.cube.CubeList(cubes)).copy() + cube.units = "g m-2" + cube.convert_units("kg m-2") + return [cube] diff --git a/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/hadrem3_ga7_05.py index cfbf5466cd..c1928f398d 100644 --- a/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/hadrem3_ga7_05.py @@ -1,7 +1,14 @@ """Fixes for rcm HadREM3-GA7-05 driven by ICHEC-EC-EARTH.""" +from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hadrem3_ga7_05 import ( + Clivi as BaseClivi, +) from esmvalcore.cmor._fixes.cordex.cordex_fixes import ( MOHCHadREM3GA705 as BaseFix, ) AllVars = BaseFix + + +class Clivi(BaseClivi): + """Fixes for variable clivi.""" diff --git a/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/hirham5.py b/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/hirham5.py new file mode 100644 index 0000000000..538582f940 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cordex/ichec_ec_earth/hirham5.py @@ -0,0 +1,9 @@ +"""Fixes for rcm HIRHAM5 driven by ICHEC-EC-EARTH.""" + +from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hirham5 import ( + Clivi as BaseClivi, +) + + +class Clivi(BaseClivi): + """Fixes for variable clivi.""" diff --git a/esmvalcore/cmor/_fixes/cordex/ipsl_ipsl_cm5a_mr/hirham5.py b/esmvalcore/cmor/_fixes/cordex/ipsl_ipsl_cm5a_mr/hirham5.py new file mode 100644 index 0000000000..86755cfb53 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cordex/ipsl_ipsl_cm5a_mr/hirham5.py @@ -0,0 +1,9 @@ +"""Fixes for rcm HIRHAM5 driven by IPSL-IPSL-CM5A-MR.""" + +from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hirham5 import ( + Clivi as BaseClivi, +) + + +class Clivi(BaseClivi): + """Fixes for variable clivi.""" diff --git a/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/cclm4_8_17.py b/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/cclm4_8_17.py index ebd246f5ce..7a46f9d04c 100644 --- a/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/cclm4_8_17.py +++ b/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/cclm4_8_17.py @@ -1,7 +1,31 @@ """Fixes for rcm CCLM4-8-17 driven by MOHC-HadGEM2-ES.""" +from __future__ import annotations + +from typing import TYPE_CHECKING + +import iris +import iris.cube + from esmvalcore.cmor._fixes.cordex.cordex_fixes import ( CLMcomCCLM4817 as BaseFix, ) +from esmvalcore.cmor.fix import Fix + +if TYPE_CHECKING: + from collections.abc import Sequence AllVars = BaseFix + + +class Clivi(Fix): + """Fixes for variable clivi.""" + + def fix_metadata( + self, + cubes: Sequence[iris.cube.Cube], + ) -> Sequence[iris.cube.Cube]: + cube = self.get_cube_from_list(iris.cube.CubeList(cubes)).copy() + cube.units = "Mg m-2" + cube.convert_units("kg m-2") + return [cube] diff --git a/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/hirham5.py b/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/hirham5.py index 8a7e659c5a..1b0ad95caa 100644 --- a/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/hirham5.py +++ b/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/hirham5.py @@ -1,5 +1,8 @@ -"""Fixes for rcm HIRHAM driven by MOHC-HadGEM2.""" +"""Fixes for rcm HIRHAM5 driven by MOHC-HadGEM2.""" +from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hirham5 import ( + Clivi as BaseClivi, +) from esmvalcore.cmor.fix import Fix @@ -24,3 +27,7 @@ def fix_metadata(self, cubes): cube.coord("longitude").attributes = {} return cubes + + +class Clivi(BaseClivi): + """Fixes for variable clivi.""" diff --git a/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py index 4ae51a1216..06ecda399c 100644 --- a/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py @@ -1,7 +1,14 @@ """Fixes for rcm HadREM3-GA7-05 driven by MPI-M-MPI-ESM-LR.""" +from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hadrem3_ga7_05 import ( + Clivi as BaseClivi, +) from esmvalcore.cmor._fixes.cordex.cordex_fixes import ( MOHCHadREM3GA705 as BaseFix, ) AllVars = BaseFix + + +class Clivi(BaseClivi): + """Fixes for variable clivi.""" diff --git a/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hirham5.py b/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hirham5.py new file mode 100644 index 0000000000..538582f940 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hirham5.py @@ -0,0 +1,9 @@ +"""Fixes for rcm HIRHAM5 driven by ICHEC-EC-EARTH.""" + +from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hirham5 import ( + Clivi as BaseClivi, +) + + +class Clivi(BaseClivi): + """Fixes for variable clivi.""" diff --git a/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py index ad72e3a449..65fd929cde 100644 --- a/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py @@ -1,7 +1,14 @@ """Fixes for rcm HadREM3-GA7-05 driven by NCC-NorESM1-M.""" +from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hadrem3_ga7_05 import ( + Clivi as BaseClivi, +) from esmvalcore.cmor._fixes.cordex.cordex_fixes import ( MOHCHadREM3GA705 as BaseFix, ) AllVars = BaseFix + + +class Clivi(BaseClivi): + """Fixes for variable clivi.""" diff --git a/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hirham5.py b/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hirham5.py new file mode 100644 index 0000000000..538582f940 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hirham5.py @@ -0,0 +1,9 @@ +"""Fixes for rcm HIRHAM5 driven by ICHEC-EC-EARTH.""" + +from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hirham5 import ( + Clivi as BaseClivi, +) + + +class Clivi(BaseClivi): + """Fixes for variable clivi.""" From c5c22092051908d701a4dcb394b2753073d167a7 Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Fri, 3 Jul 2026 22:28:34 +0200 Subject: [PATCH 23/23] Add fixes for sic and prw --- .../cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py | 13 +++++++++++++ .../_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hirham5.py | 12 ++++++++++++ .../_fixes/cordex/mohc_hadgem2_es/cclm4_8_17.py | 13 +++++++++++++ .../_fixes/cordex/mohc_hadgem2_es/hadrem3_ga7_05.py | 7 +++++++ .../cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py | 7 +++++++ .../_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py | 7 +++++++ 6 files changed, 59 insertions(+) diff --git a/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py index 375efbd8e9..6ed60b4e06 100644 --- a/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hadrem3_ga7_05.py @@ -28,3 +28,16 @@ def fix_metadata( cube = self.get_cube_from_list(iris.cube.CubeList(cubes)).copy() cube.data = cube.core_data() * 0.1 return [cube] + + +class Sic(Fix): + """Fixes for variable sic.""" + + def fix_metadata( + self, + cubes: Sequence[iris.cube.Cube], + ) -> Sequence[iris.cube.Cube]: + cube = self.get_cube_from_list(iris.cube.CubeList(cubes)).copy() + cube.units = 1 + cube.convert_units("%") + return [cube] diff --git a/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hirham5.py b/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hirham5.py index d9fac70604..93297c9d42 100644 --- a/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hirham5.py +++ b/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/hirham5.py @@ -24,3 +24,15 @@ def fix_metadata( cube.units = "g m-2" cube.convert_units("kg m-2") return [cube] + + +class Prw(Fix): + """Fixes for variable prw.""" + + def fix_metadata( + self, + cubes: Sequence[iris.cube.Cube], + ) -> Sequence[iris.cube.Cube]: + cube = self.get_cube_from_list(iris.cube.CubeList(cubes)).copy() + cube.data = cube.core_data() / 200.0 + return [cube] diff --git a/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/cclm4_8_17.py b/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/cclm4_8_17.py index 7a46f9d04c..743320502c 100644 --- a/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/cclm4_8_17.py +++ b/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/cclm4_8_17.py @@ -29,3 +29,16 @@ def fix_metadata( cube.units = "Mg m-2" cube.convert_units("kg m-2") return [cube] + + +class Prw(Fix): + """Fixes for variable prw.""" + + def fix_metadata( + self, + cubes: Sequence[iris.cube.Cube], + ) -> Sequence[iris.cube.Cube]: + cube = self.get_cube_from_list(iris.cube.CubeList(cubes)).copy() + cube.units = "Mg m-2" + cube.convert_units("kg m-2") + return [cube] diff --git a/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/hadrem3_ga7_05.py index 6811ae2fe8..b96d7c561b 100644 --- a/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/mohc_hadgem2_es/hadrem3_ga7_05.py @@ -1,7 +1,14 @@ """Fixes for rcm HadREM3-GA7-05 driven by MOHC-HadGEM2-ES.""" +from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hadrem3_ga7_05 import ( + Sic as BaseSic, +) from esmvalcore.cmor._fixes.cordex.cordex_fixes import ( MOHCHadREM3GA705 as BaseFix, ) AllVars = BaseFix + + +class Sic(BaseSic): + """Fixes for variable sic.""" diff --git a/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py index 06ecda399c..64949560fc 100644 --- a/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/mpi_m_mpi_esm_lr/hadrem3_ga7_05.py @@ -3,6 +3,9 @@ from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hadrem3_ga7_05 import ( Clivi as BaseClivi, ) +from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hadrem3_ga7_05 import ( + Sic as BaseSic, +) from esmvalcore.cmor._fixes.cordex.cordex_fixes import ( MOHCHadREM3GA705 as BaseFix, ) @@ -12,3 +15,7 @@ class Clivi(BaseClivi): """Fixes for variable clivi.""" + + +class Sic(BaseSic): + """Fixes for variable sic.""" diff --git a/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py b/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py index 65fd929cde..bb7a5094ec 100644 --- a/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py +++ b/esmvalcore/cmor/_fixes/cordex/ncc_noresm1_m/hadrem3_ga7_05.py @@ -3,6 +3,9 @@ from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hadrem3_ga7_05 import ( Clivi as BaseClivi, ) +from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.hadrem3_ga7_05 import ( + Sic as BaseSic, +) from esmvalcore.cmor._fixes.cordex.cordex_fixes import ( MOHCHadREM3GA705 as BaseFix, ) @@ -12,3 +15,7 @@ class Clivi(BaseClivi): """Fixes for variable clivi.""" + + +class Sic(BaseSic): + """Fixes for variable sic."""