From 0916f97d8a32d725ee7c544ba123710d084b04ee Mon Sep 17 00:00:00 2001 From: Axel Lauer Date: Fri, 5 Dec 2025 14:34:06 +0100 Subject: [PATCH 1/7] Add O3 fix to cnrm_esm2_1.py --- esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py b/esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py index 8eb9aafe6c..01cde76fff 100644 --- a/esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py +++ b/esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py @@ -4,6 +4,7 @@ from .cnrm_cm6_1 import Clcalipso as BaseClcalipso from .cnrm_cm6_1 import Cli as BaseCli from .cnrm_cm6_1 import Clw as BaseClw +from .cnrm_cm6_1 import O3 as BaseO3 from .cnrm_cm6_1 import Omon as BaseOmon Cl = BaseCl @@ -18,4 +19,7 @@ Clw = BaseClw +O3 = BaseO3 + + Omon = BaseOmon From 97c2b9f620bb5bfb2c48cdfdfdc1159f171dada3 Mon Sep 17 00:00:00 2001 From: Axel Lauer Date: Fri, 5 Dec 2025 14:35:09 +0100 Subject: [PATCH 2/7] Add O3 fix to cnrm_cm6_1.py --- esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py b/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py index f2815d9ea5..be28f3470a 100644 --- a/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py +++ b/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py @@ -105,3 +105,5 @@ def fix_metadata(self, cubes): Cli = Cl Clw = Cl + +O3 = Cl From 4e9e14fd5de36c8934b72233efea76acd04e594d Mon Sep 17 00:00:00 2001 From: Axel Lauer Date: Mon, 8 Dec 2025 15:00:56 +0100 Subject: [PATCH 3/7] skip ruff error n811 --- esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py b/esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py index 01cde76fff..80082da49c 100644 --- a/esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py +++ b/esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py @@ -1,10 +1,10 @@ """Fixes for CNRM-ESM2-1 model.""" +from .cnrm_cm6_1 import O3 as BaseO3 # noqa: N811 from .cnrm_cm6_1 import Cl as BaseCl from .cnrm_cm6_1 import Clcalipso as BaseClcalipso from .cnrm_cm6_1 import Cli as BaseCli from .cnrm_cm6_1 import Clw as BaseClw -from .cnrm_cm6_1 import O3 as BaseO3 from .cnrm_cm6_1 import Omon as BaseOmon Cl = BaseCl From 30fa6104d6504fe2ce22666309e28035e7e1be40 Mon Sep 17 00:00:00 2001 From: Axel Lauer Date: Tue, 9 Dec 2025 11:16:23 +0100 Subject: [PATCH 4/7] apply O3 fix to AERmon only --- esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py b/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py index be28f3470a..6b41e5a277 100644 --- a/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py +++ b/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py @@ -106,4 +106,11 @@ def fix_metadata(self, cubes): Clw = Cl -O3 = Cl + +class O3(Cl): + """Fixes of ozone variable (mip: AERmon only).""" + + def fix_metadata(self, cubes): + if cubes[0].attributes["table_id"] == "AERmon": + cubes = Cl.fix_metadata(self, cubes) + return cubes From c7625d1ada07fcdeb7c7ebb48d0e706a27b4866f Mon Sep 17 00:00:00 2001 From: Axel Lauer Date: Tue, 9 Dec 2025 13:58:10 +0100 Subject: [PATCH 5/7] added test for O3 CNRM-CM6-1 --- tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1.py b/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1.py index 50785ef4e8..cef4ae82fd 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1.py +++ b/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1.py @@ -5,6 +5,7 @@ import pytest from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1 import ( + O3, Cl, Clcalipso, Cli, @@ -154,3 +155,9 @@ def test_get_thetao_fix(): """Test getting of fix.""" fix = Fix.get_fixes("CMIP6", "CNRM-CM6-1", "Omon", "thetao") assert fix == [Omon(None), GenericFix(None)] + + +def test_o3_fix(): + """Test fix for ``o3`` from mip: AERmon.""" + fix = Fix.get_fixes("CMIP6", "CNRM-CM6-1", "AERmon", "o3") + assert fix == [O3(None), GenericFix(None)] From 06621aebe559ea64cc318ca11159068ec29d8571 Mon Sep 17 00:00:00 2001 From: Axel Lauer Date: Tue, 9 Dec 2025 15:57:56 +0100 Subject: [PATCH 6/7] fixed test for O3 CNRM-CM6-1 --- esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py | 7 ++++-- .../cmor/_fixes/cmip6/test_cnrm_cm6_1.py | 22 ++++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py b/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py index 6b41e5a277..2e740b508a 100644 --- a/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py +++ b/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py @@ -104,6 +104,7 @@ def fix_metadata(self, cubes): Cli = Cl + Clw = Cl @@ -111,6 +112,8 @@ class O3(Cl): """Fixes of ozone variable (mip: AERmon only).""" def fix_metadata(self, cubes): - if cubes[0].attributes["table_id"] == "AERmon": - cubes = Cl.fix_metadata(self, cubes) + for cube in cubes: + if "table_id" in cube.attributes: + if cube.attributes["table_id"] == "AERmon": + cubes = Cl.fix_metadata(self, cubes) return cubes diff --git a/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1.py b/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1.py index cef4ae82fd..849a5140cf 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1.py +++ b/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1.py @@ -157,7 +157,23 @@ def test_get_thetao_fix(): assert fix == [Omon(None), GenericFix(None)] -def test_o3_fix(): +def test_o3_fix_metadata(test_data_path): """Test fix for ``o3`` from mip: AERmon.""" - fix = Fix.get_fixes("CMIP6", "CNRM-CM6-1", "AERmon", "o3") - assert fix == [O3(None), GenericFix(None)] + nc_path = test_data_path / "cnrm_cm6_1_cl.nc" + cubes = iris.load(str(nc_path)) + # change cl cube from test data to an o3 cube + # (reusing vertical hybrid coordinates for testing) + o3_cube = cubes.extract_cube("cloud_area_fraction_in_atmosphere_layer") + o3_cube.long_name = "Mole Fraction of O3" + o3_cube.standard_name = "mole_fraction_of_ozone_in_air" + o3_cube.units = "mol mol-1" + o3_cube.attributes["table_id"] = "AERmon" + o3_cube.var_name = "o3" + # test o3 fix_metadata + vardef = get_var_info("CMIP6", "AERmon", "o3") + fix = O3(vardef) + fixed_cubes = fix.fix_metadata(cubes) + # make sure the fixed cube has a coordinate "air_pressure" + cube = fixed_cubes.extract_cube("mole_fraction_of_ozone_in_air") + air_pressure_coord = cube.coord("air_pressure") + assert air_pressure_coord.points is not None From 76e847fd238084f128d2b29da4de54007489737b Mon Sep 17 00:00:00 2001 From: Axel Lauer Date: Tue, 9 Dec 2025 16:19:48 +0100 Subject: [PATCH 7/7] update cnrm_cm6_1.py --- esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py | 1 + 1 file changed, 1 insertion(+) diff --git a/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py b/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py index 2e740b508a..4d634ef820 100644 --- a/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py +++ b/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py @@ -116,4 +116,5 @@ def fix_metadata(self, cubes): if "table_id" in cube.attributes: if cube.attributes["table_id"] == "AERmon": cubes = Cl.fix_metadata(self, cubes) + break return cubes