From 826c873faa87388bfdec633aeb29e3861497bc0d Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Mon, 15 Jun 2026 16:05:51 +0200 Subject: [PATCH 1/4] Fix ALADIN53 tas coordinates --- .../cordex/cnrm_cerfacs_cnrm_cm5/aladin53.py | 24 +++++++++++++++++++ esmvalcore/cmor/_fixes/cordex/cordex_fixes.py | 13 ++++++++++ 2 files changed, 37 insertions(+) diff --git a/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/aladin53.py b/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/aladin53.py index abb96c21f6..d9c543704c 100644 --- a/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/aladin53.py +++ b/esmvalcore/cmor/_fixes/cordex/cnrm_cerfacs_cnrm_cm5/aladin53.py @@ -4,6 +4,8 @@ from typing import TYPE_CHECKING +import iris.coord_systems + from esmvalcore.cmor.fix import Fix if TYPE_CHECKING: @@ -30,3 +32,25 @@ def fix_metadata(self, cubes: Sequence[Cube]) -> Sequence[Cube]: cube.units = "deg_C" cube.convert_units(self.vardef.units) return cubes + + +class AllVars(Fix): + """Fixes for all variables.""" + + def fix_metadata(self, cubes: Sequence[Cube]) -> Sequence[Cube]: + for cube in cubes: + if self.extra_facets.get("domain") == "EUR-11": + # Set false_easting and false_northing to 0.0, as odd values + # have been observed in some files. + coord_system = iris.coord_systems.LambertConformal( + central_lat=49.5, + central_lon=10.5, + secant_latitudes=(49.5,), + ) + for coord_name in [ + "projection_x_coordinate", + "projection_y_coordinate", + ]: + if cube.coords(coord_name): + cube.coord(coord_name).coord_system = coord_system + return cubes diff --git a/esmvalcore/cmor/_fixes/cordex/cordex_fixes.py b/esmvalcore/cmor/_fixes/cordex/cordex_fixes.py index c3e5556687..820bc42a18 100644 --- a/esmvalcore/cmor/_fixes/cordex/cordex_fixes.py +++ b/esmvalcore/cmor/_fixes/cordex/cordex_fixes.py @@ -12,6 +12,7 @@ import iris.coords import iris.cube import iris.exceptions +import iris.util import numpy as np import pyproj from cf_units import Unit @@ -293,6 +294,18 @@ def _use_standard_lambert_conformal_grid( ) y_coord.guess_bounds() + # If the original coordinate was not monotonic, it has been downgraded + # to an auxiliary coordinate, so promote it back to a dimension + # coordinate. + iris.util.promote_aux_coord_to_dim_coord( + cube, + "projection_x_coordinate", + ) + iris.util.promote_aux_coord_to_dim_coord( + cube, + "projection_y_coordinate", + ) + # Define the transformation from projection coordinates to # geographic coordinates. transformer = pyproj.Transformer.from_crs( From 038ce1214c7e1517c738fe46c0b33a8cb6bd5ea7 Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Mon, 15 Jun 2026 16:17:57 +0200 Subject: [PATCH 2/4] Fix test --- tests/integration/cmor/_fixes/test_fix.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/integration/cmor/_fixes/test_fix.py b/tests/integration/cmor/_fixes/test_fix.py index f1ebf6c32e..d7d1aecfbc 100644 --- a/tests/integration/cmor/_fixes/test_fix.py +++ b/tests/integration/cmor/_fixes/test_fix.py @@ -10,6 +10,9 @@ from esmvalcore.cmor._fixes.cmip5.canesm2 import FgCo2 from esmvalcore.cmor._fixes.cmip5.cesm1_bgc import Gpp from esmvalcore.cmor._fixes.cmip6.cesm2 import Omon, Tos +from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.aladin53 import ( + AllVars as Aladin53AllVars, +) from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5.aladin63 import ( Tas, ) @@ -53,7 +56,7 @@ def test_get_grid_fix_cordex(): "tas", extra_facets={"driver": "CNRM-CERFACS-CNRM-CM5"}, ) - assert fix == [AllVars(None), GenericFix(None)] + assert fix == [Aladin53AllVars(None), AllVars(None), GenericFix(None)] def test_get_fixes_with_replace(): From 0fe9c86df78764a9462cbb7606eddccdb1dcd963 Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Mon, 15 Jun 2026 16:33:27 +0200 Subject: [PATCH 3/4] Add test --- .../cordex/test_cnrm_cerfacs_cnrm_cm5.py | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) 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..e60006b6df 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 @@ -1,8 +1,10 @@ """Tests for the fixes for driver CNRM-CERFACS-CNRM-CM5.""" import iris +import iris.coord_systems import iris.coords import iris.cube +import numpy as np import pytest from esmvalcore.cmor._fixes.cordex.cnrm_cerfacs_cnrm_cm5 import ( @@ -90,6 +92,60 @@ def test_fix_aladin53_ts() -> None: assert result.units == "K" +def test_fix_aladin53_tas() -> None: + fixes = Fix.get_fixes( + "CORDEX", + "ALADIN53", + "day", + "tas", + extra_facets={ + "driver": "CNRM-CERFACS-CNRM-CM5", + "domain": "EUR-11", + }, + ) + assert isinstance(fixes[0], aladin53.AllVars) + wrong_coord_system = iris.coord_systems.LambertConformal( + central_lat=49.5, + central_lon=10.5, + secant_latitudes=(49.5,), + false_easting=400000.0, + false_northing=-100000.0, + ) + cube = iris.cube.Cube( + np.array([0, 1.0]).reshape(1, 2), + var_name="tas", + units="K", + aux_coords_and_dims=[ + ( + iris.coords.AuxCoord( + [0.0], + standard_name="projection_y_coordinate", + units="m", + coord_system=wrong_coord_system, + ), + (0,), + ), + ( + iris.coords.AuxCoord( + [0.0, 1.0], + standard_name="projection_x_coordinate", + units="m", + coord_system=wrong_coord_system, + ), + (1,), + ), + ], + ) + (result,) = fixes[0].fix_metadata([cube]) + assert result.coord_system() == iris.coord_systems.LambertConformal( + central_lat=49.5, + central_lon=10.5, + secant_latitudes=(49.5,), + false_easting=0, + false_northing=0, + ) + + @pytest.mark.parametrize("short_name", ["pr", "tas"]) def test_get_aladin63_fix(short_name): fix = Fix.get_fixes( From 7373fbd33881229b947ce0d50fb864f22e38d18f Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Mon, 15 Jun 2026 16:49:07 +0200 Subject: [PATCH 4/4] Add test that auxcoords are promoted --- .../cmor/_fixes/cordex/test_cordex_fixes.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 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..2b61d04bec 100644 --- a/tests/integration/cmor/_fixes/cordex/test_cordex_fixes.py +++ b/tests/integration/cmor/_fixes/cordex/test_cordex_fixes.py @@ -298,8 +298,12 @@ def test_lambert_conformal_grid_fix(use_standard_grid: bool) -> None: ), 1, ), + ], + aux_coords_and_dims=[ + # If the x coordinate is not monotonic, it may be demoted to an + # auxiliary coordinate on load. ( - iris.coords.DimCoord( + iris.coords.AuxCoord( np.arange(0, 453), var_name="projection_x_coordinate", standard_name="projection_x_coordinate", @@ -308,8 +312,6 @@ def test_lambert_conformal_grid_fix(use_standard_grid: bool) -> None: ), 2, ), - ], - aux_coords_and_dims=[ ( iris.coords.AuxCoord( np.ones((453, 453)), @@ -344,8 +346,8 @@ def test_lambert_conformal_grid_fix(use_standard_grid: bool) -> None: ]: assert len(result.coords(coord_name)) == 1 - x_coord = result.coord("projection_x_coordinate") - y_coord = result.coord("projection_y_coordinate") + x_coord = result.coord("projection_x_coordinate", dim_coords=True) + y_coord = result.coord("projection_y_coordinate", dim_coords=True) assert x_coord.units == "m" assert y_coord.units == "m" assert x_coord.points.dtype == np.float64