From b9e9fdadbc11b992f14ab1da604826c0198bda63 Mon Sep 17 00:00:00 2001 From: Manuel Schlund Date: Mon, 9 Mar 2020 12:05:44 +0100 Subject: [PATCH 1/4] Added fixes for sigma pressure coordinate of cl --- esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1.py | 56 +++++ esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1_m.py | 5 + esmvalcore/cmor/_fixes/cmip5/canesm2.py | 7 +- esmvalcore/cmor/_fixes/cmip5/ccsm4.py | 15 +- esmvalcore/cmor/_fixes/cmip5/csiro_mk3_6_0.py | 6 + esmvalcore/cmor/_fixes/cmip5/giss_e2_h.py | 6 + esmvalcore/cmor/_fixes/cmip5/giss_e2_r.py | 6 + esmvalcore/cmor/_fixes/cmip5/inmcm4.py | 12 +- esmvalcore/cmor/_fixes/cmip5/ipsl_cm5a_lr.py | 6 + esmvalcore/cmor/_fixes/cmip5/ipsl_cm5a_mr.py | 6 + esmvalcore/cmor/_fixes/cmip5/ipsl_cm5b_lr.py | 6 + esmvalcore/cmor/_fixes/cmip5/miroc5.py | 10 + esmvalcore/cmor/_fixes/cmip5/miroc_esm.py | 14 +- esmvalcore/cmor/_fixes/cmip5/mpi_esm_lr.py | 9 +- esmvalcore/cmor/_fixes/cmip5/mpi_esm_mr.py | 6 + esmvalcore/cmor/_fixes/cmip5/mpi_esm_p.py | 6 + esmvalcore/cmor/_fixes/cmip5/mri_cgcm3.py | 8 +- esmvalcore/cmor/_fixes/cmip5/noresm1_m.py | 6 + esmvalcore/cmor/_fixes/cmip6/bcc_csm2_mr.py | 5 + esmvalcore/cmor/_fixes/cmip6/bcc_esm1.py | 5 + esmvalcore/cmor/_fixes/cmip6/cams_csm1_0.py | 6 + esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py | 50 +++++ esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1_hr.py | 6 + esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py | 6 + esmvalcore/cmor/_fixes/cmip6/gfdl_cm4.py | 44 +++- esmvalcore/cmor/_fixes/cmip6/giss_e2_1_g.py | 6 + esmvalcore/cmor/_fixes/cmip6/giss_e2_1_h.py | 6 + esmvalcore/cmor/_fixes/cmip6/miroc6.py | 25 +++ esmvalcore/cmor/_fixes/cmip6/miroc_es2l.py | 6 + esmvalcore/cmor/_fixes/cmip6/mpi_esm1_2_hr.py | 19 +- esmvalcore/cmor/_fixes/cmip6/mri_esm2_0.py | 6 + esmvalcore/cmor/_fixes/cmip6/nesm3.py | 6 + esmvalcore/cmor/_fixes/cmip6/sam0_unicon.py | 6 + esmvalcore/cmor/_fixes/shared.py | 65 +++++- esmvalcore/preprocessor/_derive/__init__.py | 4 +- esmvalcore/preprocessor/_derive/_shared.py | 2 +- esmvalcore/preprocessor/_derive/alb.py | 6 +- esmvalcore/preprocessor/_derive/lwp.py | 6 +- .../cmor/_fixes/cmip5/test_bcc_csm1_1.py | 204 +++++++++++++++++- .../cmor/_fixes/cmip5/test_bcc_csm1_1_m.py | 20 +- .../cmor/_fixes/cmip5/test_canesm2.py | 23 +- .../cmor/_fixes/cmip5/test_ccsm4.py | 27 ++- .../cmor/_fixes/cmip5/test_csiro_mk3_6_0.py | 21 ++ .../cmor/_fixes/cmip5/test_giss_e2_h.py | 21 ++ .../cmor/_fixes/cmip5/test_giss_e2_r.py | 21 ++ .../cmor/_fixes/cmip5/test_inmcm4.py | 29 ++- .../cmor/_fixes/cmip5/test_ipsl_cm5a_lr.py | 21 ++ .../cmor/_fixes/cmip5/test_ipsl_cm5a_mr.py | 21 ++ .../cmor/_fixes/cmip5/test_ipsl_cm5b_lr.py | 21 ++ .../cmor/_fixes/cmip5/test_miroc5.py | 24 ++- .../cmor/_fixes/cmip5/test_miroc_esm.py | 27 ++- .../cmor/_fixes/cmip5/test_mpi_esm_lr.py | 21 +- .../cmor/_fixes/cmip5/test_mpi_esm_mr.py | 21 ++ .../cmor/_fixes/cmip5/test_mpi_esm_p.py | 21 ++ .../cmor/_fixes/cmip5/test_mri_cgcm3.py | 26 ++- .../cmor/_fixes/cmip5/test_noresm1_m.py | 21 ++ .../cmor/_fixes/cmip6/test_bcc_csm2_mr.py | 18 +- .../cmor/_fixes/cmip6/test_bcc_esm1.py | 18 +- .../cmor/_fixes/cmip6/test_cams_csm1_0.py | 21 ++ .../cmor/_fixes/cmip6/test_cnrm_cm6_1.py | 147 +++++++++++++ .../cmor/_fixes/cmip6/test_cnrm_cm6_1_hr.py | 21 ++ .../cmor/_fixes/cmip6/test_cnrm_esm2_1.py | 21 ++ .../cmor/_fixes/cmip6/test_gfdl_cm4.py | 137 ++++++++++++ .../cmor/_fixes/cmip6/test_giss_e2_1_g.py | 21 ++ .../cmor/_fixes/cmip6/test_giss_e2_1_h.py | 21 ++ .../cmor/_fixes/cmip6/test_miroc6.py | 56 +++++ .../cmor/_fixes/cmip6/test_miroc_es2l.py | 21 ++ .../cmor/_fixes/cmip6/test_mpi_esm1_2_hr.py | 21 ++ .../cmor/_fixes/cmip6/test_mri_esm2_0.py | 21 ++ .../cmor/_fixes/cmip6/test_nesm3.py | 21 ++ .../cmor/_fixes/cmip6/test_sam0_unicon.py | 21 ++ tests/integration/cmor/_fixes/test_shared.py | 169 +++++++++++++-- 72 files changed, 1720 insertions(+), 100 deletions(-) create mode 100644 esmvalcore/cmor/_fixes/cmip5/csiro_mk3_6_0.py create mode 100644 esmvalcore/cmor/_fixes/cmip5/giss_e2_h.py create mode 100644 esmvalcore/cmor/_fixes/cmip5/giss_e2_r.py create mode 100644 esmvalcore/cmor/_fixes/cmip5/ipsl_cm5a_lr.py create mode 100644 esmvalcore/cmor/_fixes/cmip5/ipsl_cm5a_mr.py create mode 100644 esmvalcore/cmor/_fixes/cmip5/ipsl_cm5b_lr.py create mode 100644 esmvalcore/cmor/_fixes/cmip5/mpi_esm_mr.py create mode 100644 esmvalcore/cmor/_fixes/cmip5/mpi_esm_p.py create mode 100644 esmvalcore/cmor/_fixes/cmip5/noresm1_m.py create mode 100644 esmvalcore/cmor/_fixes/cmip6/cams_csm1_0.py create mode 100644 esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py create mode 100644 esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1_hr.py create mode 100644 esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py create mode 100644 esmvalcore/cmor/_fixes/cmip6/giss_e2_1_g.py create mode 100644 esmvalcore/cmor/_fixes/cmip6/giss_e2_1_h.py create mode 100644 esmvalcore/cmor/_fixes/cmip6/miroc6.py create mode 100644 esmvalcore/cmor/_fixes/cmip6/miroc_es2l.py create mode 100644 esmvalcore/cmor/_fixes/cmip6/mri_esm2_0.py create mode 100644 esmvalcore/cmor/_fixes/cmip6/nesm3.py create mode 100644 esmvalcore/cmor/_fixes/cmip6/sam0_unicon.py create mode 100644 tests/integration/cmor/_fixes/cmip5/test_csiro_mk3_6_0.py create mode 100644 tests/integration/cmor/_fixes/cmip5/test_giss_e2_h.py create mode 100644 tests/integration/cmor/_fixes/cmip5/test_giss_e2_r.py create mode 100644 tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_lr.py create mode 100644 tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_mr.py create mode 100644 tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5b_lr.py create mode 100644 tests/integration/cmor/_fixes/cmip5/test_mpi_esm_mr.py create mode 100644 tests/integration/cmor/_fixes/cmip5/test_mpi_esm_p.py create mode 100644 tests/integration/cmor/_fixes/cmip5/test_noresm1_m.py create mode 100644 tests/integration/cmor/_fixes/cmip6/test_cams_csm1_0.py create mode 100644 tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1.py create mode 100644 tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1_hr.py create mode 100644 tests/integration/cmor/_fixes/cmip6/test_cnrm_esm2_1.py create mode 100644 tests/integration/cmor/_fixes/cmip6/test_gfdl_cm4.py create mode 100644 tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_g.py create mode 100644 tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_h.py create mode 100644 tests/integration/cmor/_fixes/cmip6/test_miroc6.py create mode 100644 tests/integration/cmor/_fixes/cmip6/test_miroc_es2l.py create mode 100644 tests/integration/cmor/_fixes/cmip6/test_mpi_esm1_2_hr.py create mode 100644 tests/integration/cmor/_fixes/cmip6/test_mri_esm2_0.py create mode 100644 tests/integration/cmor/_fixes/cmip6/test_nesm3.py create mode 100644 tests/integration/cmor/_fixes/cmip6/test_sam0_unicon.py diff --git a/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1.py b/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1.py index 0f1baa2f24..1ed206fd84 100644 --- a/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1.py +++ b/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1.py @@ -1,9 +1,65 @@ """Fixes for bcc-csm1-1.""" +import iris import numpy as np from scipy.interpolate import InterpolatedUnivariateSpline from scipy.ndimage import map_coordinates from ..fix import Fix +from ..shared import fix_bounds + + +class Cl(Fix): + """Fixes for cl.""" + + def fix_metadata(self, cubes): + """Fix hybrid sigma pressure coordinate. + + Parameters + ---------- + cubes : iris.cube.CubeList + Input cubes which need to be fixed. + + Returns + ------- + iris.cube.CubeList + + """ + cl_cube = self.get_cube_from_list(cubes) + + # Remove all existing aux_factories + for aux_factory in cl_cube.aux_factories: + cl_cube.remove_aux_factory(aux_factory) + + # Fix bounds + coords_to_fix = ['b'] + try: + cl_cube.coord(var_name='a') + coords_to_fix.append('a') + except iris.exceptions.CoordinateNotFoundError: + coords_to_fix.append('ap') + fix_bounds(cl_cube, cubes, coords_to_fix) + + # Fix bounds for ap if only a is given in original file + ap_coord = cl_cube.coord(var_name='ap') + if ap_coord.bounds is None: + cl_cube.remove_coord(ap_coord) + a_coord = cl_cube.coord(var_name='a') + p0_coord = cl_cube.coord(var_name='p0') + ap_coord = a_coord * p0_coord.points[0] + ap_coord.units = a_coord.units * p0_coord.units + ap_coord.rename('vertical pressure') + ap_coord.var_name = 'ap' + cl_cube.add_aux_coord(ap_coord, cl_cube.coord_dims(a_coord)) + + # Add aux_factory again + pressure_coord_factory = iris.aux_factory.HybridPressureFactory( + delta=ap_coord, + sigma=cl_cube.coord(var_name='b'), + surface_air_pressure=cl_cube.coord(var_name='ps'), + ) + cl_cube.add_aux_factory(pressure_coord_factory) + + return iris.cube.CubeList([cl_cube]) class Tos(Fix): diff --git a/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1_m.py b/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1_m.py index 11956558fe..9d19e68a7b 100644 --- a/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1_m.py +++ b/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1_m.py @@ -1,6 +1,11 @@ """Fixes for bcc-csm1-1-m.""" +from .bcc_csm1_1 import Cl as BaseCl from .bcc_csm1_1 import Tos as BaseTos +class Cl(BaseCl): + """Fixes for cl.""" + + class Tos(BaseTos): """Fixes for tos.""" diff --git a/esmvalcore/cmor/_fixes/cmip5/canesm2.py b/esmvalcore/cmor/_fixes/cmip5/canesm2.py index e30578bb5f..fa8293dbb3 100644 --- a/esmvalcore/cmor/_fixes/cmip5/canesm2.py +++ b/esmvalcore/cmor/_fixes/cmip5/canesm2.py @@ -1,6 +1,10 @@ - """Fixes for CanESM2 model.""" from ..fix import Fix +from .bcc_csm1_1 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for cl.""" class FgCo2(Fix): @@ -15,6 +19,7 @@ def fix_data(self, cube): Parameters ---------- cube: iris.cube.Cube + Input cube to fix. Returns ------- diff --git a/esmvalcore/cmor/_fixes/cmip5/ccsm4.py b/esmvalcore/cmor/_fixes/cmip5/ccsm4.py index 60b8c901f1..29528e5652 100644 --- a/esmvalcore/cmor/_fixes/cmip5/ccsm4.py +++ b/esmvalcore/cmor/_fixes/cmip5/ccsm4.py @@ -1,8 +1,13 @@ """Fixes for CCSM4 model.""" +from .bcc_csm1_1 import Cl as BaseCl from ..fix import Fix from ..shared import round_coordinates +class Cl(BaseCl): + """Fixes for cl.""" + + class Rlut(Fix): """Fixes for rlut.""" @@ -15,11 +20,12 @@ def fix_metadata(self, cubes): Parameters ---------- - cube: iris.cube.CubeList + cubes : iris.cube.CubeList + Input cubes. Returns ------- - iris.cube.Cube + iris.cube.CubeList """ return round_coordinates(cubes, 3) @@ -80,11 +86,12 @@ def fix_metadata(self, cubes): Parameters ---------- - cube: iris.cube.CubeList + cubes : iris.cube.CubeList + Input cubes. Returns ------- - iris.cube.Cube + iris.cube.CubeList """ self.get_cube_from_list(cubes).units = '1e3' diff --git a/esmvalcore/cmor/_fixes/cmip5/csiro_mk3_6_0.py b/esmvalcore/cmor/_fixes/cmip5/csiro_mk3_6_0.py new file mode 100644 index 0000000000..67e6804609 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cmip5/csiro_mk3_6_0.py @@ -0,0 +1,6 @@ +"""Fixes for CSIRO-Mk3-6-0 model.""" +from ..cmip6.miroc6 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for ``cl``.""" diff --git a/esmvalcore/cmor/_fixes/cmip5/giss_e2_h.py b/esmvalcore/cmor/_fixes/cmip5/giss_e2_h.py new file mode 100644 index 0000000000..968dfd32e1 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cmip5/giss_e2_h.py @@ -0,0 +1,6 @@ +"""Fixes for GISS-E2-H.""" +from .bcc_csm1_1 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for cl.""" diff --git a/esmvalcore/cmor/_fixes/cmip5/giss_e2_r.py b/esmvalcore/cmor/_fixes/cmip5/giss_e2_r.py new file mode 100644 index 0000000000..a356bcff54 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cmip5/giss_e2_r.py @@ -0,0 +1,6 @@ +"""Fixes for GISS-E2-R.""" +from .bcc_csm1_1 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for cl.""" diff --git a/esmvalcore/cmor/_fixes/cmip5/inmcm4.py b/esmvalcore/cmor/_fixes/cmip5/inmcm4.py index 8f07eb1904..eb5bb622d2 100644 --- a/esmvalcore/cmor/_fixes/cmip5/inmcm4.py +++ b/esmvalcore/cmor/_fixes/cmip5/inmcm4.py @@ -1,10 +1,14 @@ - """Fixes for inmcm4 model.""" import iris +from .bcc_csm1_1 import Cl as BaseCl from ..fix import Fix +class Cl(BaseCl): + """Fixes for cl.""" + + class Gpp(Fix): """Fixes for gpp.""" @@ -16,7 +20,8 @@ def fix_data(self, cube): Parameters ---------- - cube: iris.cube.Cube + cube : iris.cube.Cube + Input cube. Returns ------- @@ -40,7 +45,8 @@ def fix_data(self, cube): Parameters ---------- - cube: iris.cube.Cube + cube : iris.cube.Cube + Input cube. Returns ------- diff --git a/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5a_lr.py b/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5a_lr.py new file mode 100644 index 0000000000..ed01586f49 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5a_lr.py @@ -0,0 +1,6 @@ +"""Fixes for IPSL-CM5A-LR model.""" +from .bcc_csm1_1 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for cl.""" diff --git a/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5a_mr.py b/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5a_mr.py new file mode 100644 index 0000000000..f5977a1c61 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5a_mr.py @@ -0,0 +1,6 @@ +"""Fixes for IPSL-CM5A-MR model.""" +from .bcc_csm1_1 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for cl.""" diff --git a/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5b_lr.py b/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5b_lr.py new file mode 100644 index 0000000000..30ff79a4ba --- /dev/null +++ b/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5b_lr.py @@ -0,0 +1,6 @@ +"""Fixes for IPSL-CM5B-LR model.""" +from .bcc_csm1_1 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for cl.""" diff --git a/esmvalcore/cmor/_fixes/cmip5/miroc5.py b/esmvalcore/cmor/_fixes/cmip5/miroc5.py index fadb625999..da49992fdb 100644 --- a/esmvalcore/cmor/_fixes/cmip5/miroc5.py +++ b/esmvalcore/cmor/_fixes/cmip5/miroc5.py @@ -1,10 +1,15 @@ """Fixes for MIROC5 model.""" from dask import array as da +from ..cmip6.miroc6 import Cl as BaseCl from ..fix import Fix from ..shared import round_coordinates +class Cl(BaseCl): + """Fixes for ``cl``.""" + + class Sftof(Fix): """Fixes for sftof.""" @@ -17,6 +22,7 @@ def fix_data(self, cube): Parameters ---------- cube: iris.cube.Cube + Input cube. Returns ------- @@ -41,6 +47,7 @@ def fix_data(self, cube): Parameters ---------- cube: iris.cube.Cube + Input cube. Returns ------- @@ -98,6 +105,7 @@ def fix_data(self, cube): Parameters ---------- cube: iris.cube.Cube + Input cube. Returns ------- @@ -121,6 +129,7 @@ def fix_metadata(self, cubes): Parameters ---------- cubes: iris.cube.CubeList + Input cubes. Returns ------- @@ -146,6 +155,7 @@ def fix_data(self, cube): Parameters ---------- cube: iris.cube.Cube + Input cube. Returns ------- diff --git a/esmvalcore/cmor/_fixes/cmip5/miroc_esm.py b/esmvalcore/cmor/_fixes/cmip5/miroc_esm.py index 192ccb3996..e333b97584 100644 --- a/esmvalcore/cmor/_fixes/cmip5/miroc_esm.py +++ b/esmvalcore/cmor/_fixes/cmip5/miroc_esm.py @@ -3,9 +3,14 @@ from iris.coords import DimCoord from iris.exceptions import CoordinateNotFoundError +from .bcc_csm1_1 import Cl as BaseCl from ..fix import Fix +class Cl(BaseCl): + """Fixes for ``cl``.""" + + class Tro3(Fix): """Fixes for tro3.""" @@ -18,6 +23,7 @@ def fix_data(self, cube): Parameters ---------- cube: iris.cube.Cube + Input cube. Returns ------- @@ -41,11 +47,12 @@ def fix_metadata(self, cubes): Parameters ---------- - cube: iris.cube.CubeList + cubes : iris.cube.CubeList + Input cubes. Returns ------- - iris.cube.Cube + iris.cube.CubeList """ self.get_cube_from_list(cubes).units = '1.0e-6' @@ -63,7 +70,8 @@ def fix_metadata(self, cubes): Parameters ---------- - cube: iris.cube.CubeList + cubes : iris.cube.CubeList + Input cubes. Returns ------- diff --git a/esmvalcore/cmor/_fixes/cmip5/mpi_esm_lr.py b/esmvalcore/cmor/_fixes/cmip5/mpi_esm_lr.py index 39a21a5f8f..0d4d1e2656 100644 --- a/esmvalcore/cmor/_fixes/cmip5/mpi_esm_lr.py +++ b/esmvalcore/cmor/_fixes/cmip5/mpi_esm_lr.py @@ -1,6 +1,10 @@ - -"""Fixes for MPI ESM LR model.""" +"""Fixes for MPI-ESM-LR model.""" from ..fix import Fix +from .bcc_csm1_1 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for cl.""" class Pctisccp(Fix): @@ -15,6 +19,7 @@ def fix_data(self, cube): Parameters ---------- cube: iris.cube.Cube + Input cube. Returns ------- diff --git a/esmvalcore/cmor/_fixes/cmip5/mpi_esm_mr.py b/esmvalcore/cmor/_fixes/cmip5/mpi_esm_mr.py new file mode 100644 index 0000000000..a311de5ef7 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cmip5/mpi_esm_mr.py @@ -0,0 +1,6 @@ +"""Fixes for MPI-ESM-MR model.""" +from .bcc_csm1_1 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for cl.""" diff --git a/esmvalcore/cmor/_fixes/cmip5/mpi_esm_p.py b/esmvalcore/cmor/_fixes/cmip5/mpi_esm_p.py new file mode 100644 index 0000000000..6ed3c44827 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cmip5/mpi_esm_p.py @@ -0,0 +1,6 @@ +"""Fixes for MPI-ESM-P model.""" +from .bcc_csm1_1 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for cl.""" diff --git a/esmvalcore/cmor/_fixes/cmip5/mri_cgcm3.py b/esmvalcore/cmor/_fixes/cmip5/mri_cgcm3.py index a2ca092a0d..8da1e66977 100644 --- a/esmvalcore/cmor/_fixes/cmip5/mri_cgcm3.py +++ b/esmvalcore/cmor/_fixes/cmip5/mri_cgcm3.py @@ -1,10 +1,14 @@ - """Fixes for MRI-CGCM3 model.""" from dask import array as da +from .bcc_csm1_1 import Cl as BaseCl from ..fix import Fix +class Cl(BaseCl): + """Fixes for ``cl``.""" + + class Msftmyz(Fix): """Fixes for msftmyz.""" @@ -17,6 +21,7 @@ def fix_data(self, cube): Parameters ---------- cube: iris.cube.Cube + Input cube. Returns ------- @@ -39,6 +44,7 @@ def fix_data(self, cube): Parameters ---------- cube: iris.cube.Cube + Input cube. Returns ------- diff --git a/esmvalcore/cmor/_fixes/cmip5/noresm1_m.py b/esmvalcore/cmor/_fixes/cmip5/noresm1_m.py new file mode 100644 index 0000000000..6748e14456 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cmip5/noresm1_m.py @@ -0,0 +1,6 @@ +"""Fixes for NorESM1-M.""" +from .bcc_csm1_1 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for ``cl``.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/bcc_csm2_mr.py b/esmvalcore/cmor/_fixes/cmip6/bcc_csm2_mr.py index b0313e1eb9..3f89888afa 100644 --- a/esmvalcore/cmor/_fixes/cmip6/bcc_csm2_mr.py +++ b/esmvalcore/cmor/_fixes/cmip6/bcc_csm2_mr.py @@ -1,7 +1,12 @@ """Fixes for BCC-CSM2-MR model.""" +from ..cmip5.bcc_csm1_1 import Cl as BaseCl from ..cmip5.bcc_csm1_1 import Tos as BaseTos +class Cl(BaseCl): + """Fixes for ``cl``.""" + + class Tos(BaseTos): """Fixes for tos.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/bcc_esm1.py b/esmvalcore/cmor/_fixes/cmip6/bcc_esm1.py index e05c4940a1..3af096a886 100644 --- a/esmvalcore/cmor/_fixes/cmip6/bcc_esm1.py +++ b/esmvalcore/cmor/_fixes/cmip6/bcc_esm1.py @@ -1,6 +1,11 @@ """Fixes for BCC-ESM1 model.""" +from ..cmip5.bcc_csm1_1 import Cl as BaseCl from .bcc_csm2_mr import Tos as BaseTos +class Cl(BaseCl): + """Fixes for ``cl``.""" + + class Tos(BaseTos): """Fixes for tos.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/cams_csm1_0.py b/esmvalcore/cmor/_fixes/cmip6/cams_csm1_0.py new file mode 100644 index 0000000000..532cdf312a --- /dev/null +++ b/esmvalcore/cmor/_fixes/cmip6/cams_csm1_0.py @@ -0,0 +1,6 @@ +"""Fixes for CAMS-CSM1-0 model.""" +from ..cmip5.bcc_csm1_1 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for ``cl``.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py b/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py new file mode 100644 index 0000000000..8fefc82d4d --- /dev/null +++ b/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py @@ -0,0 +1,50 @@ +"""Fixes for CNRM-CM6-1 model.""" +import iris + +from ..cmip5.bcc_csm1_1 import Cl as BaseCl +from ..shared import add_aux_coords_from_cubes, get_bounds_cube + + +class Cl(BaseCl): + """Fixes for ``cl``.""" + + def fix_metadata(self, cubes): + """Fix vertical hybrid sigma coordinate (incl. bounds). + + Parameters + ---------- + cubes : iris.cube.CubeList + Input cubes. + + Returns + ------- + iris.cube.Cube + + """ + cube = self.get_cube_from_list(cubes) + + # Add auxiliary coordinate from list of cubes + coords_to_add = { + 'ap': 1, + 'b': 1, + 'ps': (0, 2, 3), + } + add_aux_coords_from_cubes(cube, cubes, coords_to_add) + cube.coord(var_name='ap').units = 'Pa' + + # Fix vertical coordinate bounds + for coord_name in ('ap', 'b'): + bounds_cube = get_bounds_cube(cubes, coord_name) + bounds = bounds_cube.data.reshape(-1, 2) + new_bounds_cube = iris.cube.Cube(bounds, + **bounds_cube.metadata._asdict()) + cubes.remove(bounds_cube) + cubes.append(new_bounds_cube) + + # Fix hybrid sigma pressure coordinate + cubes = super().fix_metadata(cubes) + + # Fix horizontal coordinates bounds + for coord_name in ('latitude', 'longitude'): + cube.coord(coord_name).guess_bounds() + return cubes diff --git a/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1_hr.py b/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1_hr.py new file mode 100644 index 0000000000..73a0d8fafb --- /dev/null +++ b/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1_hr.py @@ -0,0 +1,6 @@ +"""Fixes for CNRM-CM6-1-HR model.""" +from .cnrm_cm6_1 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for ``cl``.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py b/esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py new file mode 100644 index 0000000000..c11df2657f --- /dev/null +++ b/esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py @@ -0,0 +1,6 @@ +"""Fixes for CNRM-ESM2-1 model.""" +from .cnrm_cm6_1 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for ``cl``.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/gfdl_cm4.py b/esmvalcore/cmor/_fixes/cmip6/gfdl_cm4.py index b16eb6bb12..faac0927be 100644 --- a/esmvalcore/cmor/_fixes/cmip6/gfdl_cm4.py +++ b/esmvalcore/cmor/_fixes/cmip6/gfdl_cm4.py @@ -1,8 +1,35 @@ """Fixes for GFDL-CM4 model.""" import iris +from ..cmip5.bcc_csm1_1 import Cl as BaseCl from ..fix import Fix -from ..shared import add_scalar_height_coord +from ..shared import add_aux_coords_from_cubes, add_scalar_height_coord + + +class Cl(BaseCl): + """Fixes for ``cl``.""" + + def fix_metadata(self, cubes): + """Fix hybrid sigma pressure coordinate. + + Parameters + ---------- + cubes : iris.cube.CubeList + Input cubes which need to be fixed. + + Returns + ------- + iris.cube.CubeList + + """ + cube = self.get_cube_from_list(cubes) + coords_to_add = { + 'ap': 1, + 'b': 1, + 'ps': (0, 2, 3), + } + add_aux_coords_from_cubes(cube, cubes, coords_to_add) + return super().fix_metadata(cubes) class Tas(Fix): @@ -14,11 +41,12 @@ def fix_metadata(self, cubes): Parameters ---------- - cube : iris.cube.CubeList + cubes : iris.cube.CubeList + Input cubes. Returns ------- - iris.cube.Cube + iris.cube.CubeList """ cube = self.get_cube_from_list(cubes) @@ -38,11 +66,12 @@ def fix_metadata(self, cubes): Parameters ---------- - cube : iris.cube.CubeList + cubes : iris.cube.CubeList + Input cubes. Returns ------- - iris.cube.Cube + iris.cube.CubeList """ cube = self.get_cube_from_list(cubes) @@ -59,11 +88,12 @@ def fix_metadata(self, cubes): Parameters ---------- - cube : iris.cube.CubeList + cubes : iris.cube.CubeList + Input cubes. Returns ------- - iris.cube.Cube + iris.cube.CubeList """ cube = self.get_cube_from_list(cubes) diff --git a/esmvalcore/cmor/_fixes/cmip6/giss_e2_1_g.py b/esmvalcore/cmor/_fixes/cmip6/giss_e2_1_g.py new file mode 100644 index 0000000000..8e2a2b09ed --- /dev/null +++ b/esmvalcore/cmor/_fixes/cmip6/giss_e2_1_g.py @@ -0,0 +1,6 @@ +"""Fixes for GISS-E2-1-G model.""" +from ..cmip5.bcc_csm1_1 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for ``cl``.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/giss_e2_1_h.py b/esmvalcore/cmor/_fixes/cmip6/giss_e2_1_h.py new file mode 100644 index 0000000000..b6e5a23c3a --- /dev/null +++ b/esmvalcore/cmor/_fixes/cmip6/giss_e2_1_h.py @@ -0,0 +1,6 @@ +"""Fixes for GISS-E2-1-H model.""" +from ..cmip5.bcc_csm1_1 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for ``cl``.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/miroc6.py b/esmvalcore/cmor/_fixes/cmip6/miroc6.py new file mode 100644 index 0000000000..8a1ad27217 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cmip6/miroc6.py @@ -0,0 +1,25 @@ +"""Fixes for MIROC6 model.""" +from ..cmip5.bcc_csm1_1 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for ``cl``.""" + + def fix_metadata(self, cubes): + """Remove attributes from ``Surface Air Pressure`` coordinate. + + Parameters + ---------- + cubes : iris.cube.CubeList + Input cubes. + + Returns + ------- + iris.cube.CubeList + + """ + cubes = super().fix_metadata(cubes) + cube = self.get_cube_from_list(cubes) + coord = cube.coord(long_name='Surface Air Pressure') + coord.attributes = {} + return cubes diff --git a/esmvalcore/cmor/_fixes/cmip6/miroc_es2l.py b/esmvalcore/cmor/_fixes/cmip6/miroc_es2l.py new file mode 100644 index 0000000000..d467d83796 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cmip6/miroc_es2l.py @@ -0,0 +1,6 @@ +"""Fixes for MIROC-ES2L model.""" +from ..cmip5.bcc_csm1_1 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for ``cl``.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/mpi_esm1_2_hr.py b/esmvalcore/cmor/_fixes/cmip6/mpi_esm1_2_hr.py index 21648fc1dc..fcf3d9dd78 100644 --- a/esmvalcore/cmor/_fixes/cmip6/mpi_esm1_2_hr.py +++ b/esmvalcore/cmor/_fixes/cmip6/mpi_esm1_2_hr.py @@ -1,19 +1,24 @@ """Fixes for MPI-ESM1-2-HR model.""" - +from ..cmip5.bcc_csm1_1 import Cl as BaseCl from ..fix import Fix from ..shared import add_scalar_height_coord +class Cl(BaseCl): + """Fixes for ``cl``.""" + + class Tas(Fix): """Fixes for tas.""" def fix_metadata(self, cubes): """ - Adds missing height2m coordinate. + Add missing height2m coordinate. Parameters ---------- - cube: iris.cube.CubeList + cubes : iris.cube.CubeList + Input cubes. Returns ------- @@ -35,7 +40,8 @@ def fix_metadata(self, cubes): Parameters ---------- - cube: iris.cube.CubeList + cubes : iris.cube.CubeList + Input cubes. Returns ------- @@ -66,11 +72,12 @@ class SfcWind(Fix): def fix_metadata(self, cubes): """ - Adds missing height10m coordinate. + Add missing height10m coordinate. Parameters ---------- - cube: iris.cube.CubeList + cubes : iris.cube.CubeList + Input cubes. Returns ------- diff --git a/esmvalcore/cmor/_fixes/cmip6/mri_esm2_0.py b/esmvalcore/cmor/_fixes/cmip6/mri_esm2_0.py new file mode 100644 index 0000000000..af5126d5f4 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cmip6/mri_esm2_0.py @@ -0,0 +1,6 @@ +"""Fixes for MRI-ESM2-0 model.""" +from ..cmip5.bcc_csm1_1 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for ``cl``.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/nesm3.py b/esmvalcore/cmor/_fixes/cmip6/nesm3.py new file mode 100644 index 0000000000..9ded3e8fc2 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cmip6/nesm3.py @@ -0,0 +1,6 @@ +"""Fixes for NESM3 model.""" +from ..cmip5.bcc_csm1_1 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for ``cl``.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/sam0_unicon.py b/esmvalcore/cmor/_fixes/cmip6/sam0_unicon.py new file mode 100644 index 0000000000..0eee658088 --- /dev/null +++ b/esmvalcore/cmor/_fixes/cmip6/sam0_unicon.py @@ -0,0 +1,6 @@ +"""Fixes for SAM0-UNICON model.""" +from ..cmip5.bcc_csm1_1 import Cl as BaseCl + + +class Cl(BaseCl): + """Fixes for ``cl``.""" diff --git a/esmvalcore/cmor/_fixes/shared.py b/esmvalcore/cmor/_fixes/shared.py index b6eb609943..53b1ca023f 100644 --- a/esmvalcore/cmor/_fixes/shared.py +++ b/esmvalcore/cmor/_fixes/shared.py @@ -6,6 +6,8 @@ import iris from cf_units import Unit +from esmvalcore.preprocessor._derive._shared import var_name_constraint + logger = logging.getLogger(__name__) @@ -173,6 +175,20 @@ def add_sigma_factory(cube): "coordinate not available") +def add_aux_coords_from_cubes(cube, cubes, coord_dict): + """Add auxiliary coordinate to cube from another cube in list of cubes.""" + for (coord_name, coord_dims) in coord_dict.items(): + coord_cube = cubes.extract(var_name_constraint(coord_name)) + if len(coord_cube) != 1: + raise ValueError( + f"Expected exactly one coordinate cube '{coord_name}' in " + f"list of cubes {cubes}, got {len(coord_cube):d}") + coord_cube = coord_cube[0] + aux_coord = cube_to_aux_coord(coord_cube) + cube.add_aux_coord(aux_coord, coord_dims) + cubes.remove(coord_cube) + + def add_scalar_depth_coord(cube, depth=0.0): """Add scalar coordinate 'depth' with value of `depth`m.""" logger.debug("Adding depth coordinate (%sm)", depth) @@ -236,7 +252,7 @@ def add_scalar_typesea_coord(cube, value='default'): def cube_to_aux_coord(cube): - """Convert cube to iris AuxCoord""" + """Convert cube to iris AuxCoord.""" return iris.coords.AuxCoord( points=cube.core_data(), var_name=cube.var_name, @@ -246,8 +262,34 @@ def cube_to_aux_coord(cube): ) +def get_bounds_cube(cubes, coord_var_name): + """Find bound cube for a given variable in a list of cubes.""" + for bounds in ('bnds', 'bounds'): + bound_var = f'{coord_var_name}_{bounds}' + cube = cubes.extract(var_name_constraint(bound_var)) + if len(cube) == 1: + return cube[0] + if len(cube) > 1: + raise ValueError( + f"Multiple cubes with var_name '{bound_var}' found") + raise ValueError( + f"No bounds for coordinate variable '{coord_var_name}' available in " + f"cubes\n{cubes}") + + +def fix_bounds(cube, cubes, coord_var_names): + """Fix bounds for cube that could not be read correctly by :mod:`iris`.""" + for coord_var_name in coord_var_names: + coord = cube.coord(var_name=coord_var_name) + if coord.bounds is not None: + continue + bounds_cube = get_bounds_cube(cubes, coord_var_name) + cube.coord(var_name=coord_var_name).bounds = bounds_cube.core_data() + logger.debug("Fixed bounds of coordinate '%s'", coord_var_name) + + def round_coordinates(cubes, decimals=5, coord_names=None): - """Round all dimensional coordinates of all cubes in place + """Round all dimensional coordinates of all cubes in place. Cubes can be a list of Iris cubes, or an Iris `CubeList`. @@ -256,22 +298,23 @@ def round_coordinates(cubes, decimals=5, coord_names=None): Parameters ---------- - - cubes: iris.cube.CubeList (or a list of iris.cube.Cube). + cubes : iris.cube.CubeList or list of iris.cube.Cube + Cubes which are modified in place. - - decimals: number of decimals to round to. + decimals : int + Number of decimals to round to. - - coord_names: list of strings, or None. - If None (or a falsey value), all dimensional coordinates will - be rounded. - Otherwise, only coordinates given by the names in - `coord_names` are rounded. + coord_names : list of str or None + If ``None`` (or a falsey value), all dimensional coordinates will be + rounded. Otherwise, only coordinates given by the names in + ``coord_names`` are rounded. Returns ------- - The modified input `cubes` + iris.cube.CubeList or list of iris.cube.Cube + The modified input ``cubes``. """ - for cube in cubes: if not coord_names: coords = cube.coords(dim_coords=True) diff --git a/esmvalcore/preprocessor/_derive/__init__.py b/esmvalcore/preprocessor/_derive/__init__.py index 11e06de1bc..db4d2c169c 100644 --- a/esmvalcore/preprocessor/_derive/__init__.py +++ b/esmvalcore/preprocessor/_derive/__init__.py @@ -96,9 +96,7 @@ def derive(cubes, short_name, long_name, units, standard_name=None): except Exception as exc: msg = (f"Derivation of variable '{short_name}' failed. If you used " f"the option '--skip-nonexistent' for running your recipe, " - f"this might be caused by missing input data for derivation " - f"('{short_name}' needs the variables " - f"{DerivedVariable().required}).") + f"this might be caused by missing input data for derivation") raise ValueError(msg) from exc # Set standard attributes diff --git a/esmvalcore/preprocessor/_derive/_shared.py b/esmvalcore/preprocessor/_derive/_shared.py index 7f8abaf2d5..74083b4d71 100644 --- a/esmvalcore/preprocessor/_derive/_shared.py +++ b/esmvalcore/preprocessor/_derive/_shared.py @@ -8,7 +8,7 @@ logger = logging.getLogger(__name__) -def _var_name_constraint(var_name): +def var_name_constraint(var_name): """:mod:`iris.Constraint` using `var_name` of a :mod:`iris.cube.Cube`.""" return Constraint(cube_func=lambda c: c.var_name == var_name) diff --git a/esmvalcore/preprocessor/_derive/alb.py b/esmvalcore/preprocessor/_derive/alb.py index 6b6fd4b930..300353e857 100644 --- a/esmvalcore/preprocessor/_derive/alb.py +++ b/esmvalcore/preprocessor/_derive/alb.py @@ -6,7 +6,7 @@ """ from ._baseclass import DerivedVariableBase -from ._shared import _var_name_constraint +from ._shared import var_name_constraint class DerivedVariable(DerivedVariableBase): @@ -28,8 +28,8 @@ def required(project): @staticmethod def calculate(cubes): """Compute surface albedo.""" - rsdscs_cube = cubes.extract_strict(_var_name_constraint('rsdscs')) - rsuscs_cube = cubes.extract_strict(_var_name_constraint('rsuscs')) + rsdscs_cube = cubes.extract_strict(var_name_constraint('rsdscs')) + rsuscs_cube = cubes.extract_strict(var_name_constraint('rsuscs')) rsnscs_cube = rsuscs_cube / rsdscs_cube diff --git a/esmvalcore/preprocessor/_derive/lwp.py b/esmvalcore/preprocessor/_derive/lwp.py index 0208fc9d1f..fb9b8622c1 100644 --- a/esmvalcore/preprocessor/_derive/lwp.py +++ b/esmvalcore/preprocessor/_derive/lwp.py @@ -3,7 +3,7 @@ import logging from ._baseclass import DerivedVariableBase -from ._shared import _var_name_constraint +from ._shared import var_name_constraint logger = logging.getLogger(__name__) @@ -36,8 +36,8 @@ def calculate(cubes): """ # CMIP5 and CMIP6 names are slightly different, so use # variable name instead to extract cubes - clwvi_cube = cubes.extract_strict(_var_name_constraint('clwvi')) - clivi_cube = cubes.extract_strict(_var_name_constraint('clivi')) + clwvi_cube = cubes.extract_strict(var_name_constraint('clwvi')) + clivi_cube = cubes.extract_strict(var_name_constraint('clivi')) # CMIP5 and CMIP6 have different global attributes that we use # to determine model name and project name: diff --git a/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1.py b/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1.py index 28c838f3de..d7b2c0bbeb 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1.py +++ b/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1.py @@ -1,11 +1,211 @@ -"""Test Access1-0 fixes.""" +"""Test bcc-csm1-1 fixes.""" +import os import unittest import iris import numpy as np +import pytest +from netCDF4 import Dataset +from esmvalcore.cmor._fixes.cmip5.bcc_csm1_1 import Cl, Tos from esmvalcore.cmor.fix import Fix -from esmvalcore.cmor._fixes.cmip5.bcc_csm1_1 import Tos + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP5', 'bcc-csm1-1', 'Amon', 'cl') + assert fix == [Cl(None)] + + +def create_cl_file_without_ap(dataset): + """Create dataset without vertical auxiliary coordinate ``ap``.""" + dataset.createDimension('time', size=1) + dataset.createDimension('lev', size=2) + dataset.createDimension('lat', size=3) + dataset.createDimension('lon', size=4) + dataset.createDimension('bnds', size=2) + + # Dimensional variables + dataset.createVariable('time', np.float64, dimensions=('time',)) + dataset.createVariable('lev', np.float64, dimensions=('lev',)) + dataset.createVariable('lev_bnds', np.float64, dimensions=('lev', 'bnds')) + dataset.createVariable('lat', np.float64, dimensions=('lat',)) + dataset.createVariable('lon', np.float64, dimensions=('lon',)) + dataset.variables['time'][:] = [0.0] + dataset.variables['time'].standard_name = 'time' + dataset.variables['time'].units = 'days since 6543-2-1' + dataset.variables['lev'][:] = [1.0, 2.0] + dataset.variables['lev'].bounds = 'lev_bnds' + dataset.variables['lev'].standard_name = ( + 'atmosphere_hybrid_sigma_pressure_coordinate') + dataset.variables['lev'].units = '1' + dataset.variables['lev_bnds'][:] = [[0.5, 1.5], [1.5, 3.0]] + dataset.variables['lev_bnds'].standard_name = ( + 'atmosphere_hybrid_sigma_pressure_coordinate') + dataset.variables['lev_bnds'].units = '1' + dataset.variables['lat'][:] = [-30.0, 0.0, 30.0] + dataset.variables['lat'].standard_name = 'latitude' + dataset.variables['lat'].units = 'degrees_north' + dataset.variables['lon'][:] = [30.0, 60.0, 90.0, 120.0] + dataset.variables['lon'].standard_name = 'longitude' + dataset.variables['lon'].units = 'degrees_east' + + # Coordinates for derivation of pressure coordinate + dataset.createVariable('b', np.float64, dimensions=('lev',)) + dataset.createVariable('b_bnds', np.float64, dimensions=('lev', 'bnds')) + dataset.createVariable('ps', np.float64, + dimensions=('time', 'lat', 'lon')) + dataset.variables['b'][:] = [0.0, 1.0] + dataset.variables['b_bnds'][:] = [[-1.0, 0.5], [0.5, 2.0]] + dataset.variables['ps'][:] = np.arange(1 * 3 * 4).reshape(1, 3, 4) + dataset.variables['ps'].standard_name = 'surface_air_pressure' + dataset.variables['ps'].units = 'Pa' + + # Cl variable + dataset.createVariable('cl', np.float32, + dimensions=('time', 'lev', 'lat', 'lon')) + dataset.variables['cl'][:] = np.full((1, 2, 3, 4), 0.0, dtype=np.float32) + dataset.variables['cl'].standard_name = ( + 'cloud_area_fraction_in_atmosphere_layer') + dataset.variables['cl'].units = '%' + + +@pytest.fixture +def cl_file_with_a(tmp_path): + """Create netcdf file with similar issues as ``cl``.""" + nc_path = os.path.join(tmp_path, 'bcc_csm_1_1_cl_a.nc') + dataset = Dataset(nc_path, mode='w') + create_cl_file_without_ap(dataset) + dataset.createVariable('a', np.float64, dimensions=('lev',)) + dataset.createVariable('a_bnds', np.float64, dimensions=('lev', 'bnds')) + dataset.createVariable('p0', np.float64, dimensions=()) + dataset.variables['a'][:] = [1.0, 2.0] + dataset.variables['a_bnds'][:] = [[0.0, 1.5], [1.5, 3.0]] + dataset.variables['p0'][:] = 1.0 + dataset.variables['p0'].units = 'Pa' + dataset.variables['lev'].formula_terms = 'p0: p0 a: a b: b ps: ps' + dataset.variables['lev_bnds'].formula_terms = ( + 'p0: p0 a: a_bnds b: b_bnds ps: ps') + dataset.close() + return nc_path + + +@pytest.fixture +def cl_file_with_ap(tmp_path): + """Create netcdf file with similar issues as ``cl``.""" + nc_path = os.path.join(tmp_path, 'bcc_csm_1_1_cl_ap.nc') + dataset = Dataset(nc_path, mode='w') + create_cl_file_without_ap(dataset) + dataset.createVariable('ap', np.float64, dimensions=('lev',)) + dataset.createVariable('ap_bnds', np.float64, dimensions=('lev', 'bnds')) + dataset.variables['ap'][:] = [1.0, 2.0] + dataset.variables['ap_bnds'][:] = [[0.0, 1.5], [1.5, 3.0]] + dataset.variables['ap'].units = 'Pa' + dataset.variables['lev'].formula_terms = 'ap: ap b: b ps: ps' + dataset.variables['lev_bnds'].formula_terms = ( + 'ap: ap_bnds b: b_bnds ps: ps') + dataset.close() + return nc_path + + +AIR_PRESSURE_POINTS = np.array([[[[1.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0]], + [[2.0, 3.0, 4.0, 5.0], + [6.0, 7.0, 8.0, 9.0], + [10.0, 11.0, 12.0, 13.0]]]]) +AIR_PRESSURE_BOUNDS = np.array([[[[[0.0, 1.5], + [-1.0, 2.0], + [-2.0, 2.5], + [-3.0, 3.0]], + [[-4.0, 3.5], + [-5.0, 4.0], + [-6.0, 4.5], + [-7.0, 5.0]], + [[-8.0, 5.5], + [-9.0, 6.0], + [-10.0, 6.5], + [-11.0, 7.0]]], + [[[1.5, 3.0], + [2.0, 5.0], + [2.5, 7.0], + [3.0, 9.0]], + [[3.5, 11.0], + [4.0, 13.0], + [4.5, 15.0], + [5.0, 17.0]], + [[5.5, 19.0], + [6.0, 21.0], + [6.5, 23.0], + [7.0, 25.0]]]]]) + + +def test_cl_fix_metadata_with_a(cl_file_with_a): + """Test ``fix_metadata`` for ``cl``.""" + cubes = iris.load(cl_file_with_a) + + # Raw cubes + assert len(cubes) == 4 + var_names = [cube.var_name for cube in cubes] + assert 'cl' in var_names + assert 'ps' in var_names + assert 'a_bnds' in var_names + assert 'b_bnds' in var_names + + # Raw cl cube + cl_cube = cubes.extract_strict('cloud_area_fraction_in_atmosphere_layer') + air_pressure_coord = cl_cube.coord('air_pressure') + assert air_pressure_coord.points is not None + assert air_pressure_coord.bounds is None + np.testing.assert_allclose(air_pressure_coord.points, AIR_PRESSURE_POINTS) + + # Apply fix + fix = Cl(None) + fixed_cubes = fix.fix_metadata(cubes) + assert len(fixed_cubes) == 1 + fixed_cl_cube = fixed_cubes.extract_strict( + 'cloud_area_fraction_in_atmosphere_layer') + fixed_air_pressure_coord = fixed_cl_cube.coord('air_pressure') + assert fixed_air_pressure_coord.points is not None + assert fixed_air_pressure_coord.bounds is not None + np.testing.assert_allclose(fixed_air_pressure_coord.points, + AIR_PRESSURE_POINTS) + np.testing.assert_allclose(fixed_air_pressure_coord.bounds, + AIR_PRESSURE_BOUNDS) + + +def test_cl_fix_metadata_with_ap(cl_file_with_ap): + """Test ``fix_metadata`` for ``cl``.""" + cubes = iris.load(cl_file_with_ap) + + # Raw cubes + assert len(cubes) == 4 + var_names = [cube.var_name for cube in cubes] + assert 'cl' in var_names + assert 'ps' in var_names + assert 'ap_bnds' in var_names + assert 'b_bnds' in var_names + + # Raw cl cube + cl_cube = cubes.extract_strict('cloud_area_fraction_in_atmosphere_layer') + air_pressure_coord = cl_cube.coord('air_pressure') + assert air_pressure_coord.points is not None + assert air_pressure_coord.bounds is None + np.testing.assert_allclose(air_pressure_coord.points, AIR_PRESSURE_POINTS) + + # Apply fix + fix = Cl(None) + fixed_cubes = fix.fix_metadata(cubes) + assert len(fixed_cubes) == 1 + fixed_cl_cube = fixed_cubes.extract_strict( + 'cloud_area_fraction_in_atmosphere_layer') + fixed_air_pressure_coord = fixed_cl_cube.coord('air_pressure') + assert fixed_air_pressure_coord.points is not None + assert fixed_air_pressure_coord.bounds is not None + np.testing.assert_allclose(fixed_air_pressure_coord.points, + AIR_PRESSURE_POINTS) + np.testing.assert_allclose(fixed_air_pressure_coord.bounds, + AIR_PRESSURE_BOUNDS) class TestTos(unittest.TestCase): diff --git a/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1_m.py b/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1_m.py index efcec0b79c..ebbdce78f9 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1_m.py +++ b/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1_m.py @@ -1,8 +1,24 @@ -"""Test Access1-0 fixes.""" +"""Test fixes for bcc-csm1-1-m.""" import unittest +from esmvalcore.cmor._fixes.cmip5.bcc_csm1_1_m import Cl, Tos from esmvalcore.cmor._fixes.fix import Fix -from esmvalcore.cmor._fixes.cmip5.bcc_csm1_1_m import Tos + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP5', 'bcc-csm1-1-m', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip5.bcc_csm1_1_m.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') class TestTos(unittest.TestCase): diff --git a/tests/integration/cmor/_fixes/cmip5/test_canesm2.py b/tests/integration/cmor/_fixes/cmip5/test_canesm2.py index ff9d75082e..f3d9f024f5 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_canesm2.py +++ b/tests/integration/cmor/_fixes/cmip5/test_canesm2.py @@ -1,22 +1,39 @@ -"""Test CANESM2 fixes.""" +"""Test CanESM2 fixes.""" import unittest from cf_units import Unit from iris.cube import Cube +from esmvalcore.cmor._fixes.cmip5.canesm2 import Cl, FgCo2 from esmvalcore.cmor.fix import Fix -from esmvalcore.cmor._fixes.cmip5.canesm2 import FgCo2 + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP5', 'CanESM2', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip5.canesm2.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') class TestCanESM2Fgco2(unittest.TestCase): """Test fgc02 fixes.""" + def setUp(self): """Prepare tests.""" self.cube = Cube([1.0], var_name='fgco2', units='J') self.fix = FgCo2(None) def test_get(self): - """Test fix get""" + """Test fix get.""" self.assertListEqual( Fix.get_fixes('CMIP5', 'CANESM2', 'Amon', 'fgco2'), [FgCo2(None)]) diff --git a/tests/integration/cmor/_fixes/cmip5/test_ccsm4.py b/tests/integration/cmor/_fixes/cmip5/test_ccsm4.py index 1d326c5a03..0d46d5e596 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_ccsm4.py +++ b/tests/integration/cmor/_fixes/cmip5/test_ccsm4.py @@ -5,12 +5,29 @@ from iris.coords import DimCoord from iris.cube import Cube +from esmvalcore.cmor._fixes.cmip5.ccsm4 import Cl, Rlut, Rlutcs, So from esmvalcore.cmor.fix import Fix -from esmvalcore.cmor._fixes.cmip5.ccsm4 import Rlut, Rlutcs, So + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP5', 'CCSM4', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip5.ccsm4.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') class TestsRlut(unittest.TestCase): """Test for rlut fixes.""" + def setUp(self): """Prepare tests.""" self.cube = Cube([1.0, 2.0], var_name='rlut') @@ -24,7 +41,7 @@ def setUp(self): self.fix = Rlut(None) def test_get(self): - """Test fix get""" + """Test fix get.""" self.assertListEqual(Fix.get_fixes('CMIP5', 'CCSM4', 'Amon', 'rlut'), [Rlut(None)]) @@ -41,6 +58,7 @@ def test_fix_metadata(self): class TestsRlutcs(unittest.TestCase): """Test for rlutcs fixes.""" + def setUp(self): """Prepare tests.""" self.cube = Cube([1.0, 2.0], var_name='rlutcs') @@ -54,7 +72,7 @@ def setUp(self): self.fix = Rlutcs(None) def test_get(self): - """Test fix get""" + """Test fix get.""" self.assertListEqual(Fix.get_fixes('CMIP5', 'CCSM4', 'Amon', 'rlutcs'), [Rlutcs(None)]) @@ -71,13 +89,14 @@ def test_fix_metadata(self): class TestSo(unittest.TestCase): """Tests for so fixes.""" + def setUp(self): """Prepare tests.""" self.cube = Cube([1.0, 2.0], var_name='so', units='1.0') self.fix = So(None) def test_get(self): - """Test fix get""" + """Test fix get.""" self.assertListEqual(Fix.get_fixes('CMIP5', 'CCSM4', 'Amon', 'so'), [So(None)]) diff --git a/tests/integration/cmor/_fixes/cmip5/test_csiro_mk3_6_0.py b/tests/integration/cmor/_fixes/cmip5/test_csiro_mk3_6_0.py new file mode 100644 index 0000000000..e070c65220 --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip5/test_csiro_mk3_6_0.py @@ -0,0 +1,21 @@ +"""Test fixes for CSIRO-Mk3-6-0.""" +import unittest + +from esmvalcore.cmor._fixes.cmip5.csiro_mk3_6_0 import Cl +from esmvalcore.cmor._fixes.fix import Fix + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP5', 'CSIRO-Mk3-6-0', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip5.csiro_mk3_6_0.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip5/test_giss_e2_h.py b/tests/integration/cmor/_fixes/cmip5/test_giss_e2_h.py new file mode 100644 index 0000000000..0f32a68ca0 --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip5/test_giss_e2_h.py @@ -0,0 +1,21 @@ +"""Test fixes for GISS-E2-H.""" +import unittest + +from esmvalcore.cmor._fixes.cmip5.giss_e2_h import Cl +from esmvalcore.cmor._fixes.fix import Fix + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP5', 'GISS-E2-H', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip5.giss_e2_h.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip5/test_giss_e2_r.py b/tests/integration/cmor/_fixes/cmip5/test_giss_e2_r.py new file mode 100644 index 0000000000..05610eaf03 --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip5/test_giss_e2_r.py @@ -0,0 +1,21 @@ +"""Test fixes for GISS-E2-R.""" +import unittest + +from esmvalcore.cmor._fixes.cmip5.giss_e2_r import Cl +from esmvalcore.cmor._fixes.fix import Fix + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP5', 'GISS-E2-R', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip5.giss_e2_r.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip5/test_inmcm4.py b/tests/integration/cmor/_fixes/cmip5/test_inmcm4.py index 9da7f16103..322968cf5f 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_inmcm4.py +++ b/tests/integration/cmor/_fixes/cmip5/test_inmcm4.py @@ -1,22 +1,39 @@ """Tests for inmcm4 fixes.""" import unittest -from iris.cube import Cube from cf_units import Unit +from iris.cube import Cube +from esmvalcore.cmor._fixes.cmip5.inmcm4 import Cl, Gpp, Lai, Nbp from esmvalcore.cmor.fix import Fix -from esmvalcore.cmor._fixes.cmip5.inmcm4 import Gpp, Lai, Nbp + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP5', 'inmcm4', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip5.inmcm4.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') class TestGpp(unittest.TestCase): """Test gpp fixes.""" + def setUp(self): """Prepare tests.""" self.cube = Cube([1.0], var_name='gpp', units='J') self.fix = Gpp(None) def test_get(self): - """Test fix get""" + """Test fix get.""" self.assertListEqual(Fix.get_fixes('CMIP5', 'INMCM4', 'Amon', 'gpp'), [Gpp(None)]) @@ -29,13 +46,14 @@ def test_fix_data(self): class TestLai(unittest.TestCase): """Test lai fixes.""" + def setUp(self): """Prepare tests.""" self.cube = Cube([1.0], var_name='lai', units='J') self.fix = Lai(None) def test_get(self): - """Test fix get""" + """Test fix get.""" self.assertListEqual(Fix.get_fixes('CMIP5', 'INMCM4', 'Amon', 'lai'), [Lai(None)]) @@ -48,13 +66,14 @@ def test_fix_data(self): class TestNbp(unittest.TestCase): """Tests for nbp.""" + def setUp(self): """Prepare tests.""" self.cube = Cube([1.0], var_name='nbp') self.fix = Nbp(None) def test_get(self): - """Test fix get""" + """Test fix get.""" self.assertListEqual(Fix.get_fixes('CMIP5', 'INMCM4', 'Amon', 'nbp'), [Nbp(None)]) diff --git a/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_lr.py b/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_lr.py new file mode 100644 index 0000000000..0bcf3bd201 --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_lr.py @@ -0,0 +1,21 @@ +"""Test fixes for IPSL-CM5A-LR.""" +import unittest + +from esmvalcore.cmor._fixes.cmip5.ipsl_cm5a_lr import Cl +from esmvalcore.cmor._fixes.fix import Fix + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP5', 'IPSL-CM5A-LR', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip5.ipsl_cm5a_lr.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_mr.py b/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_mr.py new file mode 100644 index 0000000000..48b364c2ec --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_mr.py @@ -0,0 +1,21 @@ +"""Test fixes for IPSL-CM5A-MR.""" +import unittest + +from esmvalcore.cmor._fixes.cmip5.ipsl_cm5a_mr import Cl +from esmvalcore.cmor._fixes.fix import Fix + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP5', 'IPSL-CM5A-MR', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip5.ipsl_cm5a_mr.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5b_lr.py b/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5b_lr.py new file mode 100644 index 0000000000..18dd1ac578 --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5b_lr.py @@ -0,0 +1,21 @@ +"""Test fixes for IPSL-CM5B-LR.""" +import unittest + +from esmvalcore.cmor._fixes.cmip5.ipsl_cm5b_lr import Cl +from esmvalcore.cmor._fixes.fix import Fix + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP5', 'IPSL-CM5B-LR', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip5.ipsl_cm5b_lr.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip5/test_miroc5.py b/tests/integration/cmor/_fixes/cmip5/test_miroc5.py index e9980a36cc..10195f5bfe 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_miroc5.py +++ b/tests/integration/cmor/_fixes/cmip5/test_miroc5.py @@ -5,10 +5,26 @@ from cf_units import Unit from iris.cube import Cube -from esmvalcore.cmor._fixes.cmip5.miroc5 import Hur, Sftof, Tas +from esmvalcore.cmor._fixes.cmip5.miroc5 import Cl, Hur, Sftof, Tas from esmvalcore.cmor.fix import Fix +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP5', 'MIROC5', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip5.miroc5.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + def test_get_hur_fix(): """Test getting of fix.""" fix = Fix.get_fixes('CMIP5', 'MIROC5', 'Amon', 'hur') @@ -27,13 +43,14 @@ def test_hur_fix_metadata(mock_base_fix_metadata): class TestSftof(unittest.TestCase): """Test sftof fixes.""" + def setUp(self): """Prepare tests.""" self.cube = Cube([1.0], var_name='sftof', units='J') self.fix = Sftof(None) def test_get(self): - """Test fix get""" + """Test fix get.""" self.assertListEqual(Fix.get_fixes('CMIP5', 'MIROC5', 'Amon', 'sftof'), [Sftof(None)]) @@ -46,6 +63,7 @@ def test_fix_data(self): class TestTas(unittest.TestCase): """Test tas fixes.""" + def setUp(self): """Prepare tests.""" self.coord_name = 'latitude' @@ -56,7 +74,7 @@ def setUp(self): self.fix = Tas(None) def test_get(self): - """Test fix get""" + """Test fix get.""" self.assertListEqual(Fix.get_fixes('CMIP5', 'MIROC5', 'Amon', 'tas'), [Tas(None)]) diff --git a/tests/integration/cmor/_fixes/cmip5/test_miroc_esm.py b/tests/integration/cmor/_fixes/cmip5/test_miroc_esm.py index 51d887c597..2f49a6bbb0 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_miroc_esm.py +++ b/tests/integration/cmor/_fixes/cmip5/test_miroc_esm.py @@ -6,19 +6,36 @@ from iris.cube import Cube from iris.exceptions import CoordinateNotFoundError +from esmvalcore.cmor._fixes.cmip5.miroc_esm import AllVars, Cl, Co2, Tro3 from esmvalcore.cmor.fix import Fix -from esmvalcore.cmor._fixes.cmip5.miroc_esm import AllVars, Co2, Tro3 + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP5', 'MIROC-ESM', 'Amon', 'cl') + assert fix == [Cl(None), AllVars(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip5.miroc_esm.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') class TestCo2(unittest.TestCase): """Test c02 fixes.""" + def setUp(self): """Prepare tests.""" self.cube = Cube([1.0], var_name='co2', units='J') self.fix = Co2(None) def test_get(self): - """Test fix get""" + """Test fix get.""" self.assertListEqual( Fix.get_fixes('CMIP5', 'MIROC-ESM', 'Amon', 'co2'), [Co2(None), AllVars(None)]) @@ -32,13 +49,14 @@ def test_fix_metadata(self): class TestTro3(unittest.TestCase): """Test tro3 fixes.""" + def setUp(self): """Prepare tests.""" self.cube = Cube([1.0], var_name='tro3', units='J') self.fix = Tro3(None) def test_get(self): - """Test fix get""" + """Test fix get.""" self.assertListEqual( Fix.get_fixes('CMIP5', 'MIROC-ESM', 'Amon', 'tro3'), [Tro3(None), AllVars(None)]) @@ -52,6 +70,7 @@ def test_fix_data(self): class TestAll(unittest.TestCase): """Test fixes for allvars.""" + def setUp(self): """Prepare tests.""" self.cube = Cube([[1.0, 2.0], [3.0, 4.0]], var_name='co2', units='J') @@ -65,7 +84,7 @@ def setUp(self): self.fix = AllVars(None) def test_get(self): - """Test fix get""" + """Test fix get.""" self.assertListEqual( Fix.get_fixes('CMIP5', 'MIROC-ESM', 'Amon', 'tos'), [AllVars(None)]) diff --git a/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_lr.py b/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_lr.py index f8c90ec3e2..fa07e930d7 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_lr.py +++ b/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_lr.py @@ -4,19 +4,36 @@ from cf_units import Unit from iris.cube import Cube +from esmvalcore.cmor._fixes.cmip5.mpi_esm_lr import Cl, Pctisccp from esmvalcore.cmor.fix import Fix -from esmvalcore.cmor._fixes.cmip5.mpi_esm_lr import Pctisccp + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP5', 'MPI-ESM-LR', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip5.mpi_esm_lr.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') class TestPctisccp2(unittest.TestCase): """Test Pctisccp2 fixes.""" + def setUp(self): """Prepare tests.""" self.cube = Cube([1.0], var_name='pctisccp', units='J') self.fix = Pctisccp(None) def test_get(self): - """Test fix get""" + """Test fix get.""" self.assertListEqual( Fix.get_fixes('CMIP5', 'MPI-ESM-LR', 'Amon', 'pctisccp'), [Pctisccp(None)]) diff --git a/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_mr.py b/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_mr.py new file mode 100644 index 0000000000..0bdeafab3b --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_mr.py @@ -0,0 +1,21 @@ +"""Test fixes for MPI-ESM-MR.""" +import unittest + +from esmvalcore.cmor._fixes.cmip5.mpi_esm_mr import Cl +from esmvalcore.cmor._fixes.fix import Fix + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP5', 'MPI-ESM-MR', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip5.mpi_esm_mr.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_p.py b/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_p.py new file mode 100644 index 0000000000..852070264a --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_p.py @@ -0,0 +1,21 @@ +"""Test fixes for MPI-ESM-P.""" +import unittest + +from esmvalcore.cmor._fixes.cmip5.mpi_esm_p import Cl +from esmvalcore.cmor._fixes.fix import Fix + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP5', 'MPI-ESM-P', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip5.mpi_esm_p.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip5/test_mri_cgcm3.py b/tests/integration/cmor/_fixes/cmip5/test_mri_cgcm3.py index 1b93fb3004..4825150b31 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_mri_cgcm3.py +++ b/tests/integration/cmor/_fixes/cmip5/test_mri_cgcm3.py @@ -1,14 +1,31 @@ -"""Test MRI-GCM3 fixes.""" +"""Test MRI-CGCM3 fixes.""" import unittest +from esmvalcore.cmor._fixes.cmip5.mri_cgcm3 import Cl, Msftmyz, ThetaO from esmvalcore.cmor.fix import Fix -from esmvalcore.cmor._fixes.cmip5.mri_cgcm3 import Msftmyz, ThetaO + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP5', 'MRI-CGCM3', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip5.mri_cgcm3.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') class TestMsftmyz(unittest.TestCase): """Test msftmyz fixes.""" + def test_get(self): - """Test fix get""" + """Test fix get.""" self.assertListEqual( Fix.get_fixes('CMIP5', 'MRI-CGCM3', 'Amon', 'msftmyz'), [Msftmyz(None)]) @@ -16,8 +33,9 @@ def test_get(self): class TestThetao(unittest.TestCase): """Test thetao fixes.""" + def test_get(self): - """Test fix get""" + """Test fix get.""" self.assertListEqual( Fix.get_fixes('CMIP5', 'MRI-CGCM3', 'Amon', 'thetao'), [ThetaO(None)]) diff --git a/tests/integration/cmor/_fixes/cmip5/test_noresm1_m.py b/tests/integration/cmor/_fixes/cmip5/test_noresm1_m.py new file mode 100644 index 0000000000..4e740a44d0 --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip5/test_noresm1_m.py @@ -0,0 +1,21 @@ +"""Test fixes for NorESM1-M.""" +import unittest + +from esmvalcore.cmor._fixes.cmip5.noresm1_m import Cl +from esmvalcore.cmor._fixes.fix import Fix + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP5', 'NorESM1-M', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip5.noresm1_m.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py b/tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py index c29e84cdb2..4635e0a9b4 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py +++ b/tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py @@ -3,10 +3,26 @@ import iris -from esmvalcore.cmor._fixes.cmip6.bcc_csm2_mr import Tos +from esmvalcore.cmor._fixes.cmip6.bcc_csm2_mr import Cl, Tos from esmvalcore.cmor._fixes.fix import Fix +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'BCC-CSM2-MR', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.bcc_csm2_mr.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + def test_get_tos_fix(): """Test getting of fix.""" fix = Fix.get_fixes('CMIP6', 'BCC-CSM2-MR', 'Omon', 'tos') diff --git a/tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py b/tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py index c3479b0d45..1c7e0c04d6 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py +++ b/tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py @@ -1,10 +1,26 @@ """Test fixes for BCC-ESM1.""" import unittest -from esmvalcore.cmor._fixes.cmip6.bcc_esm1 import Tos +from esmvalcore.cmor._fixes.cmip6.bcc_esm1 import Cl, Tos from esmvalcore.cmor._fixes.fix import Fix +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'BCC-ESM1', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.bcc_esm1.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + def test_get_tos_fix(): """Test getting of fix.""" fix = Fix.get_fixes('CMIP6', 'BCC-ESM1', 'Omon', 'tos') diff --git a/tests/integration/cmor/_fixes/cmip6/test_cams_csm1_0.py b/tests/integration/cmor/_fixes/cmip6/test_cams_csm1_0.py new file mode 100644 index 0000000000..d5e023b6f3 --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip6/test_cams_csm1_0.py @@ -0,0 +1,21 @@ +"""Test fixes for CAMS-CSM1-0.""" +import unittest + +from esmvalcore.cmor._fixes.cmip6.cams_csm1_0 import Cl +from esmvalcore.cmor._fixes.fix import Fix + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'CAMS-CSM1-0', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.cams_csm1_0.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, '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 new file mode 100644 index 0000000000..29e8080498 --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1.py @@ -0,0 +1,147 @@ +"""Tests for the fixes of CNRM-CM6-1.""" +import os + +import iris +import numpy as np +import pytest +from netCDF4 import Dataset + +from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1 import Cl +from esmvalcore.cmor.fix import Fix + + +@pytest.fixture +def cl_file(tmp_path): + """Create netcdf file with similar issues as ``cl``.""" + nc_path = os.path.join(tmp_path, 'cnrm_cm6_1_cl.nc') + dataset = Dataset(nc_path, mode='w') + dataset.createDimension('time', size=1) + dataset.createDimension('lev', size=3) + dataset.createDimension('lat', size=2) + dataset.createDimension('lon', size=2) + dataset.createDimension('bnds', size=2) + + # Dimensional variables + dataset.createVariable('time', np.float64, dimensions=('time',)) + dataset.createVariable('lev', np.float64, dimensions=('lev',)) + dataset.createVariable('lev_bnds', np.float64, dimensions=('lev', 'bnds')) + dataset.createVariable('lat', np.float64, dimensions=('lat',)) + dataset.createVariable('lon', np.float64, dimensions=('lon',)) + dataset.variables['time'][:] = [0.0] + dataset.variables['time'].standard_name = 'time' + dataset.variables['time'].units = 'days since 6543-2-1' + dataset.variables['lev'][:] = [1.0, 2.0, 4.0] + dataset.variables['lev'].standard_name = ( + 'atmosphere_hybrid_sigma_pressure_coordinate') + dataset.variables['lev'].bounds = 'lev_bnds' + dataset.variables['lev'].units = '1' + dataset.variables['lev'].formula_term = ( + 'ap: ap b: b ps: ps') # Error in attribute intended + dataset.variables['lev_bnds'][:] = [[0.5, 1.5], [1.5, 3.0], [3.0, 5.0]] + dataset.variables['lev_bnds'].standard_name = ( + 'atmosphere_hybrid_sigma_pressure_coordinate') + dataset.variables['lev_bnds'].units = '1' + dataset.variables['lev_bnds'].formula_term = ( + 'ap: ap b: b ps: ps') # Error in attribute intended + dataset.variables['lat'][:] = [-30.0, 0.0] + dataset.variables['lat'].standard_name = 'latitude' + dataset.variables['lat'].units = 'degrees_north' + dataset.variables['lon'][:] = [30.0, 60.0] + dataset.variables['lon'].standard_name = 'longitude' + dataset.variables['lon'].units = 'degrees_east' + + # Coordinates for derivation of pressure coordinate + # Wrong shape of bounds is intended + dataset.createVariable('ap', np.float64, dimensions=('lev',)) + dataset.createVariable('ap_bnds', np.float64, dimensions=('bnds', 'lev')) + dataset.createVariable('b', np.float64, dimensions=('lev',)) + dataset.createVariable('b_bnds', np.float64, dimensions=('bnds', 'lev')) + dataset.createVariable('ps', np.float64, + dimensions=('time', 'lat', 'lon')) + dataset.variables['ap'][:] = [1.0, 2.0, 5.0] + dataset.variables['ap_bnds'][:] = [[0.0, 1.5, 1.5], [3.0, 3.0, 6.0]] + dataset.variables['b'][:] = [0.0, 1.0, 3.0] + dataset.variables['b_bnds'][:] = [[-1.0, 0.5, 0.5], [2.0, 2.0, 5.0]] + dataset.variables['ps'][:] = np.arange(1 * 2 * 2).reshape(1, 2, 2) + dataset.variables['ps'].standard_name = 'surface_air_pressure' + dataset.variables['ps'].units = 'Pa' + + # Cl variable + dataset.createVariable('cl', np.float32, + dimensions=('time', 'lev', 'lat', 'lon')) + dataset.variables['cl'][:] = np.full((1, 3, 2, 2), 0.0, dtype=np.float32) + dataset.variables['cl'].standard_name = ( + 'cloud_area_fraction_in_atmosphere_layer') + dataset.variables['cl'].units = '%' + + dataset.close() + return nc_path + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'CNRM-CM6-1', 'Amon', 'cl') + assert fix == [Cl(None)] + + +AIR_PRESSURE_POINTS = np.array([[[[1.0, 1.0], + [1.0, 1.0]], + [[2.0, 3.0], + [4.0, 5.0]], + [[5.0, 8.0], + [11.0, 14.0]]]]) +AIR_PRESSURE_BOUNDS = np.array([[[[[0.0, 1.5], + [-1.0, 2.0]], + [[-2.0, 2.5], + [-3.0, 3.0]]], + [[[1.5, 3.0], + [2.0, 5.0]], + [[2.5, 7.0], + [3.0, 9.0]]], + [[[3.0, 6.0], + [5.0, 11.0]], + [[7.0, 16.0], + [9.0, 21.0]]]]]) + + +def test_cl_fix_metadata(cl_file): + """Test ``fix_metadata`` for ``cl``.""" + cubes = iris.load(cl_file) + + # Raw cubes + assert len(cubes) == 6 + var_names = [cube.var_name for cube in cubes] + assert 'cl' in var_names + assert 'ap' in var_names + assert 'ap_bnds' in var_names + assert 'b' in var_names + assert 'b_bnds' in var_names + assert 'ps' in var_names + + # Raw cl cube + cl_cube = cubes.extract_strict('cloud_area_fraction_in_atmosphere_layer') + assert not cl_cube.coords('air_pressure') + + # Apply fix + fix = Cl(None) + fixed_cubes = fix.fix_metadata(cubes) + assert len(fixed_cubes) == 1 + fixed_cl_cube = fixed_cubes.extract_strict( + 'cloud_area_fraction_in_atmosphere_layer') + fixed_air_pressure_coord = fixed_cl_cube.coord('air_pressure') + assert fixed_air_pressure_coord.points is not None + assert fixed_air_pressure_coord.bounds is not None + assert fixed_air_pressure_coord.points.shape == (1, 3, 2, 2) + assert fixed_air_pressure_coord.bounds.shape == (1, 3, 2, 2, 2) + np.testing.assert_allclose(fixed_air_pressure_coord.points, + AIR_PRESSURE_POINTS) + np.testing.assert_allclose(fixed_air_pressure_coord.bounds, + AIR_PRESSURE_BOUNDS) + lat_coord = fixed_cl_cube.coord('latitude') + lon_coord = fixed_cl_cube.coord('longitude') + assert lat_coord.bounds is not None + assert lon_coord.bounds is not None + np.testing.assert_allclose(lat_coord.bounds, + [[-45.0, -15.0], [-15.0, 15.0]]) + np.testing.assert_allclose(lon_coord.bounds, + [[15.0, 45.0], [45.0, 75.0]]) diff --git a/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1_hr.py b/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1_hr.py new file mode 100644 index 0000000000..8e7f0560ee --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1_hr.py @@ -0,0 +1,21 @@ +"""Test fixes for CNRM-CM6-1-HR.""" +import unittest + +from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1_hr import Cl +from esmvalcore.cmor._fixes.fix import Fix + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'CNRM-CM6-1-HR', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1_hr.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip6/test_cnrm_esm2_1.py b/tests/integration/cmor/_fixes/cmip6/test_cnrm_esm2_1.py new file mode 100644 index 0000000000..d0d57b3d1b --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip6/test_cnrm_esm2_1.py @@ -0,0 +1,21 @@ +"""Test fixes for CNRM-ESM2-1.""" +import unittest + +from esmvalcore.cmor._fixes.cmip6.cnrm_esm2_1 import Cl +from esmvalcore.cmor._fixes.fix import Fix + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'CNRM-ESM2-1', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.cnrm_esm2_1.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip6/test_gfdl_cm4.py b/tests/integration/cmor/_fixes/cmip6/test_gfdl_cm4.py new file mode 100644 index 0000000000..3667e1d61e --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip6/test_gfdl_cm4.py @@ -0,0 +1,137 @@ +"""Tests for the fixes of GFDL-CM4.""" +import os + +import iris +import numpy as np +import pytest +from netCDF4 import Dataset + +from esmvalcore.cmor._fixes.cmip6.gfdl_cm4 import Cl +from esmvalcore.cmor.fix import Fix + + +@pytest.fixture +def cl_file(tmp_path): + """Create netcdf file with similar issues as ``cl``.""" + nc_path = os.path.join(tmp_path, 'gfdl_cm4_cl.nc') + dataset = Dataset(nc_path, mode='w') + dataset.createDimension('time', size=1) + dataset.createDimension('lev', size=3) + dataset.createDimension('lat', size=2) + dataset.createDimension('lon', size=2) + dataset.createDimension('bnds', size=2) + + # Dimensional variables + dataset.createVariable('time', np.float64, dimensions=('time',)) + dataset.createVariable('lev', np.float64, dimensions=('lev',)) + dataset.createVariable('lev_bnds', np.float64, dimensions=('lev', 'bnds')) + dataset.createVariable('lat', np.float64, dimensions=('lat',)) + dataset.createVariable('lon', np.float64, dimensions=('lon',)) + dataset.variables['time'][:] = [0.0] + dataset.variables['time'].standard_name = 'time' + dataset.variables['time'].units = 'days since 6543-2-1' + dataset.variables['lev'][:] = [1.0, 2.0, 4.0] + dataset.variables['lev'].standard_name = ( + 'atmosphere_hybrid_sigma_pressure_coordinate') + dataset.variables['lev'].bounds = 'lev_bnds' + dataset.variables['lev'].units = '1' + dataset.variables['lev'].formula_term = ( + 'ap: ap b: b ps: ps') # Error in attribute intended + dataset.variables['lev_bnds'][:] = [[0.5, 1.5], [1.5, 3.0], [3.0, 5.0]] + dataset.variables['lev_bnds'].standard_name = ( + 'atmosphere_hybrid_sigma_pressure_coordinate') + dataset.variables['lev_bnds'].units = '1' + dataset.variables['lev_bnds'].formula_term = ( + 'ap: ap_bnds b: b_bnds ps: ps') # Error in attribute intended + dataset.variables['lat'][:] = [-30.0, 0.0] + dataset.variables['lat'].standard_name = 'latitude' + dataset.variables['lat'].units = 'degrees_north' + dataset.variables['lon'][:] = [30.0, 60.0] + dataset.variables['lon'].standard_name = 'longitude' + dataset.variables['lon'].units = 'degrees_east' + + # Coordinates for derivation of pressure coordinate + dataset.createVariable('ap', np.float64, dimensions=('lev',)) + dataset.createVariable('ap_bnds', np.float64, dimensions=('lev', 'bnds')) + dataset.createVariable('b', np.float64, dimensions=('lev',)) + dataset.createVariable('b_bnds', np.float64, dimensions=('lev', 'bnds')) + dataset.createVariable('ps', np.float64, + dimensions=('time', 'lat', 'lon')) + dataset.variables['ap'][:] = [1.0, 2.0, 5.0] + dataset.variables['ap'].units = 'Pa' + dataset.variables['ap_bnds'][:] = [[0.0, 1.5], [1.5, 3.0], [3.0, 6.0]] + dataset.variables['b'][:] = [0.0, 1.0, 3.0] + dataset.variables['b_bnds'][:] = [[-1.0, 0.5], [0.5, 2.0], [2.0, 5.0]] + dataset.variables['ps'][:] = np.arange(1 * 2 * 2).reshape(1, 2, 2) + dataset.variables['ps'].standard_name = 'surface_air_pressure' + dataset.variables['ps'].units = 'Pa' + + # Cl variable + dataset.createVariable('cl', np.float32, + dimensions=('time', 'lev', 'lat', 'lon')) + dataset.variables['cl'][:] = np.full((1, 3, 2, 2), 0.0, dtype=np.float32) + dataset.variables['cl'].standard_name = ( + 'cloud_area_fraction_in_atmosphere_layer') + dataset.variables['cl'].units = '%' + + dataset.close() + return nc_path + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'GFDL-CM4', 'Amon', 'cl') + assert fix == [Cl(None)] + + +AIR_PRESSURE_POINTS = np.array([[[[1.0, 1.0], + [1.0, 1.0]], + [[2.0, 3.0], + [4.0, 5.0]], + [[5.0, 8.0], + [11.0, 14.0]]]]) +AIR_PRESSURE_BOUNDS = np.array([[[[[0.0, 1.5], + [-1.0, 2.0]], + [[-2.0, 2.5], + [-3.0, 3.0]]], + [[[1.5, 3.0], + [2.0, 5.0]], + [[2.5, 7.0], + [3.0, 9.0]]], + [[[3.0, 6.0], + [5.0, 11.0]], + [[7.0, 16.0], + [9.0, 21.0]]]]]) + + +def test_cl_fix_metadata(cl_file): + """Test ``fix_metadata`` for ``cl``.""" + cubes = iris.load(cl_file) + + # Raw cubes + assert len(cubes) == 6 + var_names = [cube.var_name for cube in cubes] + assert 'cl' in var_names + assert 'ap' in var_names + assert 'ap_bnds' in var_names + assert 'b' in var_names + assert 'b_bnds' in var_names + assert 'ps' in var_names + + # Raw cl cube + cl_cube = cubes.extract_strict('cloud_area_fraction_in_atmosphere_layer') + assert not cl_cube.coords('air_pressure') + + # Apply fix + fix = Cl(None) + fixed_cubes = fix.fix_metadata(cubes) + assert len(fixed_cubes) == 1 + fixed_cl_cube = fixed_cubes.extract_strict( + 'cloud_area_fraction_in_atmosphere_layer') + fixed_air_pressure_coord = fixed_cl_cube.coord('air_pressure') + assert fixed_air_pressure_coord.points is not None + assert fixed_air_pressure_coord.bounds is not None + np.testing.assert_allclose(fixed_air_pressure_coord.points, + AIR_PRESSURE_POINTS) + np.testing.assert_allclose(fixed_air_pressure_coord.bounds, + AIR_PRESSURE_BOUNDS) diff --git a/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_g.py b/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_g.py new file mode 100644 index 0000000000..733c97513a --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_g.py @@ -0,0 +1,21 @@ +"""Test fixes for GISS-E2-1-G.""" +import unittest + +from esmvalcore.cmor._fixes.cmip6.giss_e2_1_g import Cl +from esmvalcore.cmor._fixes.fix import Fix + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'GISS-E2-1-G', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.giss_e2_1_g.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_h.py b/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_h.py new file mode 100644 index 0000000000..cf161d4bb2 --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_h.py @@ -0,0 +1,21 @@ +"""Test fixes for GISS-E2-1-H.""" +import unittest + +from esmvalcore.cmor._fixes.cmip6.giss_e2_1_h import Cl +from esmvalcore.cmor._fixes.fix import Fix + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'GISS-E2-1-H', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.giss_e2_1_h.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip6/test_miroc6.py b/tests/integration/cmor/_fixes/cmip6/test_miroc6.py new file mode 100644 index 0000000000..fd945cbccf --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip6/test_miroc6.py @@ -0,0 +1,56 @@ +"""Test fixes for MIROC6.""" +import unittest + +import pytest +from iris.coords import AuxCoord +from iris.cube import Cube, CubeList + +from esmvalcore.cmor._fixes.cmip6.miroc6 import Cl +from esmvalcore.cmor._fixes.fix import Fix + + +@pytest.fixture +def cl_cubes(): + """Cubes for ``cl.``.""" + ps_coord = AuxCoord( + [[1.0]], + var_name='ps', + long_name='Surface Air Pressure', + attributes={'a': 1, 'b': '2'}, + ) + cl_cube = Cube( + [[0.0]], + var_name='cl', + standard_name='cloud_area_fraction_in_atmosphere_layer', + aux_coords_and_dims=[(ps_coord.copy(), (0, 1))], + ) + x_cube = Cube([[0.0]], + long_name='x', + aux_coords_and_dims=[(ps_coord.copy(), (0, 1))]) + cubes = CubeList([cl_cube, x_cube]) + return cubes + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'MIROC6', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.miroc6.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata, cl_cubes): + """Test ``fix_metadata`` for ``cl``.""" + mock_base_fix_metadata.return_value = cl_cubes + fix = Cl(None) + fixed_cubes = fix.fix_metadata(cl_cubes) + mock_base_fix_metadata.assert_called_once_with(fix, cl_cubes) + assert len(fixed_cubes) == 2 + cl_cube = fixed_cubes.extract_strict( + 'cloud_area_fraction_in_atmosphere_layer') + ps_coord_cl = cl_cube.coord('Surface Air Pressure') + assert not ps_coord_cl.attributes + x_cube = fixed_cubes.extract_strict('x') + ps_coord_x = x_cube.coord('Surface Air Pressure') + assert ps_coord_x.attributes == {'a': 1, 'b': '2'} diff --git a/tests/integration/cmor/_fixes/cmip6/test_miroc_es2l.py b/tests/integration/cmor/_fixes/cmip6/test_miroc_es2l.py new file mode 100644 index 0000000000..044faec07f --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip6/test_miroc_es2l.py @@ -0,0 +1,21 @@ +"""Test fixes for MIROC-ES2L.""" +import unittest + +from esmvalcore.cmor._fixes.cmip6.miroc_es2l import Cl +from esmvalcore.cmor._fixes.fix import Fix + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'MIROC-ES2L', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.miroc_es2l.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip6/test_mpi_esm1_2_hr.py b/tests/integration/cmor/_fixes/cmip6/test_mpi_esm1_2_hr.py new file mode 100644 index 0000000000..2f6f2c6237 --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip6/test_mpi_esm1_2_hr.py @@ -0,0 +1,21 @@ +"""Test fixes for MPI-ESM1-2-HR.""" +import unittest + +from esmvalcore.cmor._fixes.cmip6.mpi_esm1_2_hr import Cl +from esmvalcore.cmor._fixes.fix import Fix + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'MPI-ESM1-2-HR', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.mpi_esm1_2_hr.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip6/test_mri_esm2_0.py b/tests/integration/cmor/_fixes/cmip6/test_mri_esm2_0.py new file mode 100644 index 0000000000..362e647940 --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip6/test_mri_esm2_0.py @@ -0,0 +1,21 @@ +"""Test fixes for MRI-ESM2-0.""" +import unittest + +from esmvalcore.cmor._fixes.cmip6.mri_esm2_0 import Cl +from esmvalcore.cmor._fixes.fix import Fix + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'MRI-ESM2-0', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.mri_esm2_0.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip6/test_nesm3.py b/tests/integration/cmor/_fixes/cmip6/test_nesm3.py new file mode 100644 index 0000000000..36e3c1b3d9 --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip6/test_nesm3.py @@ -0,0 +1,21 @@ +"""Test fixes for NESM3.""" +import unittest + +from esmvalcore.cmor._fixes.cmip6.nesm3 import Cl +from esmvalcore.cmor._fixes.fix import Fix + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'NESM3', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.nesm3.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip6/test_sam0_unicon.py b/tests/integration/cmor/_fixes/cmip6/test_sam0_unicon.py new file mode 100644 index 0000000000..fe296c221f --- /dev/null +++ b/tests/integration/cmor/_fixes/cmip6/test_sam0_unicon.py @@ -0,0 +1,21 @@ +"""Test fixes for SAM0-UNICON.""" +import unittest + +from esmvalcore.cmor._fixes.cmip6.sam0_unicon import Cl +from esmvalcore.cmor._fixes.fix import Fix + + +def test_get_cl_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'SAM0-UNICON', 'Amon', 'cl') + assert fix == [Cl(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.sam0_unicon.BaseCl.fix_metadata', + autospec=True) +def test_cl_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cl``.""" + fix = Cl(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/test_shared.py b/tests/integration/cmor/_fixes/test_shared.py index 17f4af4800..2158f3c27f 100644 --- a/tests/integration/cmor/_fixes/test_shared.py +++ b/tests/integration/cmor/_fixes/test_shared.py @@ -1,16 +1,66 @@ """Tests for shared functions for fixes.""" -import numpy as np import iris +import numpy as np import pytest from cf_units import Unit -from esmvalcore.cmor._fixes.shared import (add_scalar_depth_coord, +from esmvalcore.cmor._fixes.shared import (add_aux_coords_from_cubes, + add_scalar_depth_coord, add_scalar_height_coord, add_scalar_typeland_coord, add_scalar_typesea_coord, add_sigma_factory, - round_coordinates, - cube_to_aux_coord) + cube_to_aux_coord, fix_bounds, + get_bounds_cube, round_coordinates) +from esmvalcore.preprocessor._derive._shared import var_name_constraint + + +TEST_ADD_AUX_COORDS_FROM_CUBES = [ + ({}, 1), + ({'x': ()}, 0), + ({'x': 1, 'a': ()}, 0), + ({'a': ()}, 1), + ({'a': (), 'b': 1}, 1), + ({'a': (), 'b': 1}, 1), + ({'c': 1}, 2), + ({'a': (), 'b': 1, 'c': 1}, 2), + ({'d': (0, 1)}, 1), + ({'a': (), 'b': 1, 'd': (0, 1)}, 1), +] + + +@pytest.mark.parametrize('coord_dict,output', TEST_ADD_AUX_COORDS_FROM_CUBES) +def test_add_aux_coords_from_cubes(coord_dict, output): + """Test extraction of auxiliary coordinates from cubes.""" + cube = iris.cube.Cube([[0.0]]) + cubes = iris.cube.CubeList([ + iris.cube.Cube(0.0, var_name='a'), + iris.cube.Cube([0.0], var_name='b'), + iris.cube.Cube([0.0], var_name='c'), + iris.cube.Cube([0.0], var_name='c'), + iris.cube.Cube([[0.0]], var_name='d'), + ]) + if output == 1: + add_aux_coords_from_cubes(cube, cubes, coord_dict) + for (coord_name, coord_dims) in coord_dict.items(): + coord = cube.coord(var_name=coord_name) + if len(cube.coord_dims(coord)) == 1: + assert cube.coord_dims(coord)[0] == coord_dims + else: + assert cube.coord_dims(coord) == coord_dims + points = np.full(coord.shape, 0.0) + assert coord.points == points + assert not cubes.extract(var_name_constraint(coord_name)) + assert len(cubes) == 5 - len(coord_dict) + return + with pytest.raises(ValueError) as err: + add_aux_coords_from_cubes(cube, cubes, coord_dict) + if output == 0: + assert "Expected exactly one coordinate cube 'x'" in str(err.value) + assert "got 0" in str(err.value) + else: + assert "Expected exactly one coordinate cube 'c'" in str(err.value) + assert "got 2" in str(err.value) DIM_COORD = iris.coords.DimCoord([3.141592], @@ -28,6 +78,7 @@ @pytest.mark.parametrize('cube_in,depth', TEST_ADD_SCALAR_COORD) def test_add_scalar_depth_coord(cube_in, depth): + """Test adding of scalar depth coordinate.""" cube_in = cube_in.copy() if depth is None: depth = 0.0 @@ -54,6 +105,7 @@ def test_add_scalar_depth_coord(cube_in, depth): @pytest.mark.parametrize('cube_in,height', TEST_ADD_SCALAR_COORD) def test_add_scalar_height_coord(cube_in, height): + """Test adding of scalar height coordinate.""" cube_in = cube_in.copy() if height is None: height = 2.0 @@ -80,6 +132,7 @@ def test_add_scalar_height_coord(cube_in, height): @pytest.mark.parametrize('cube_in,typeland', TEST_ADD_SCALAR_COORD) def test_add_scalar_typeland_coord(cube_in, typeland): + """Test adding of scalar typeland coordinate.""" cube_in = cube_in.copy() if typeland is None: typeland = 'default' @@ -105,6 +158,7 @@ def test_add_scalar_typeland_coord(cube_in, typeland): @pytest.mark.parametrize('cube_in,typesea', TEST_ADD_SCALAR_COORD) def test_add_scalar_typesea_coord(cube_in, typesea): + """Test adding of scalar typesea coordinate.""" cube_in = cube_in.copy() if typesea is None: typesea = 'default' @@ -164,6 +218,94 @@ def test_add_sigma_factory(cube, output): assert air_pressure_coord == output +def test_cube_to_aux_coord(): + """Test converting cube to auxiliary coordinate.""" + cube = iris.cube.Cube( + np.ones((2, 2)), + standard_name='longitude', + long_name='longitude', + var_name='lon', + units='degrees_north', + ) + coord = cube_to_aux_coord(cube) + assert coord.var_name == cube.var_name + assert coord.standard_name == cube.standard_name + assert coord.long_name == cube.long_name + assert coord.units == cube.units + assert np.all(coord.points == cube.data) + + +TEST_GET_BOUNDS_CUBE = [ + ('x', None), + ('a', iris.cube.Cube(0.0, var_name='a_bnds')), + ('b', iris.cube.Cube([0.0], var_name='b_bounds')), + ('c', False), + ('d', iris.cube.Cube([[0.0]], var_name='d_bnds')), + ('e', False), +] + + +@pytest.mark.parametrize('coord_name,output', TEST_GET_BOUNDS_CUBE) +def test_get_bounds_cube(coord_name, output): + """Test retrieving of bounds cube from list of cubes.""" + cubes = iris.cube.CubeList([ + iris.cube.Cube(0.0, var_name='a_bnds'), + iris.cube.Cube([0.0], var_name='b_bounds'), + iris.cube.Cube([0.0], var_name='c_bnds'), + iris.cube.Cube([0.0], var_name='c_bnds'), + iris.cube.Cube([[0.0]], var_name='d_bnds'), + iris.cube.Cube([[0.0]], var_name='d_bounds'), + iris.cube.Cube([[0.0]], var_name='e_bounds'), + iris.cube.Cube([[0.0]], var_name='e_bounds'), + ]) + if output is None: + with pytest.raises(ValueError) as err: + get_bounds_cube(cubes, coord_name) + msg = "No bounds for coordinate variable 'x' available in" + assert msg in str(err.value) + return + if not isinstance(output, iris.cube.Cube): + with pytest.raises(ValueError) as err: + get_bounds_cube(cubes, coord_name) + msg = f"Multiple cubes with var_name '{coord_name}" + assert msg in str(err.value) + return + bounds_cube = get_bounds_cube(cubes, coord_name) + assert bounds_cube == output + + +TEST_FIX_BOUNDS = [ + ([], [None, [[-3.0, 4.0]]]), + (['a'], [[[1.0, 2.0]], [[-3.0, 4.0]]]), + (['b'], [None, [[-3.0, 4.0]]]), + (['a', 'b'], [[[1.0, 2.0]], [[-3.0, 4.0]]]), +] + + +@pytest.mark.parametrize('var_names,output', TEST_FIX_BOUNDS) +def test_fix_bounds(var_names, output): + """Test retrieving of bounds cube from list of cubes.""" + a_coord = iris.coords.AuxCoord(1.5, var_name='a') + b_coord = iris.coords.AuxCoord(1.5, bounds=[-3.0, 4.0], var_name='b') + cube = iris.cube.Cube( + 0.0, + aux_coords_and_dims=[(a_coord, ()), (b_coord, ())], + var_name='x', + ) + cubes = iris.cube.CubeList([ + iris.cube.Cube([1.0, 2.0], var_name='a_bnds'), + iris.cube.Cube([1.0, 2.0], var_name='b_bounds'), + iris.cube.Cube([1000.0, 2000.0], var_name='c_bounds'), + ]) + assert cube.coord(var_name='a').bounds is None + fix_bounds(cube, cubes, var_names) + if output[0] is None: + assert cube.coord(var_name='a').bounds is None + else: + np.testing.assert_allclose(cube.coord(var_name='a').bounds, output[0]) + np.testing.assert_allclose(cube.coord(var_name='b').bounds, output[1]) + + DIM_COORD_NB = iris.coords.DimCoord([3.1415], standard_name='latitude') CUBE_3 = iris.cube.Cube([5.0], dim_coords_and_dims=[(DIM_COORD_NB, 0)]) COORD_3_DEC = DIM_COORD.copy([3.142], [[1.23, 4.568]]) @@ -180,6 +322,7 @@ def test_add_sigma_factory(cube, output): @pytest.mark.parametrize('cubes_in,decimals,out', TEST_ROUND) def test_round_coordinate(cubes_in, decimals, out): + """Test rounding of coordinates.""" kwargs = {} if decimals is None else {'decimals': decimals} cubes_out = round_coordinates(cubes_in, **kwargs) assert cubes_out is cubes_in @@ -191,24 +334,8 @@ def test_round_coordinate(cubes_in, decimals, out): assert coords[0] == out[idx] -def test_cube_to_aux_coord(): - cube = iris.cube.Cube( - np.ones((2, 2)), - standard_name='longitude', - long_name='longitude', - var_name='lon', - units='degrees_north', - ) - coord = cube_to_aux_coord(cube) - assert coord.var_name == cube.var_name - assert coord.standard_name == cube.standard_name - assert coord.long_name == cube.long_name - assert coord.units == cube.units - assert np.all(coord.points == cube.data) - - def test_round_coordinates_single_coord(): - """Test rounding of specified coordinate""" + """Test rounding of specified coordinate.""" coords, bounds = [10.0001], [[9.0001, 11.0001]] latcoord = iris.coords.DimCoord(coords.copy(), bounds=bounds.copy(), standard_name='latitude') From 716fdcc289b996cac506ac28663f867a22d86cd8 Mon Sep 17 00:00:00 2001 From: Manuel Schlund Date: Mon, 9 Mar 2020 13:32:14 +0100 Subject: [PATCH 2/4] Added fixes for sigma pressure coordinate of cli, clw and clcalipso --- esmvalcore/cmor/_fixes/cmip6/bcc_csm2_mr.py | 8 +++ esmvalcore/cmor/_fixes/cmip6/bcc_esm1.py | 8 +++ esmvalcore/cmor/_fixes/cmip6/cams_csm1_0.py | 8 +++ esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py | 31 ++++++++++ esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1_hr.py | 8 +++ esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py | 13 ++++ esmvalcore/cmor/_fixes/cmip6/gfdl_cm4.py | 8 +++ esmvalcore/cmor/_fixes/cmip6/giss_e2_1_g.py | 8 +++ esmvalcore/cmor/_fixes/cmip6/ipsl_cm6a_lr.py | 24 +++++++ esmvalcore/cmor/_fixes/cmip6/miroc6.py | 8 +++ esmvalcore/cmor/_fixes/cmip6/miroc_es2l.py | 8 +++ esmvalcore/cmor/_fixes/cmip6/mpi_esm1_2_hr.py | 8 +++ esmvalcore/cmor/_fixes/cmip6/mri_esm2_0.py | 8 +++ esmvalcore/cmor/_fixes/cmip6/nesm3.py | 8 +++ esmvalcore/cmor/_fixes/cmip6/sam0_unicon.py | 8 +++ .../cmor/_fixes/cmip6/test_bcc_csm2_mr.py | 34 +++++++++- .../cmor/_fixes/cmip6/test_bcc_esm1.py | 34 +++++++++- .../cmor/_fixes/cmip6/test_cams_csm1_0.py | 34 +++++++++- .../cmor/_fixes/cmip6/test_cnrm_cm6_1.py | 62 ++++++++++++++++++- .../cmor/_fixes/cmip6/test_cnrm_cm6_1_hr.py | 34 +++++++++- .../cmor/_fixes/cmip6/test_cnrm_esm2_1.py | 50 ++++++++++++++- .../cmor/_fixes/cmip6/test_gfdl_cm4.py | 35 ++++++++++- .../cmor/_fixes/cmip6/test_giss_e2_1_g.py | 34 +++++++++- .../cmor/_fixes/cmip6/test_ipsl_cm6a_lr.py | 40 +++++++++++- .../cmor/_fixes/cmip6/test_miroc6.py | 34 +++++++++- .../cmor/_fixes/cmip6/test_miroc_es2l.py | 34 +++++++++- .../cmor/_fixes/cmip6/test_mpi_esm1_2_hr.py | 34 +++++++++- .../cmor/_fixes/cmip6/test_mri_esm2_0.py | 34 +++++++++- .../cmor/_fixes/cmip6/test_nesm3.py | 34 +++++++++- .../cmor/_fixes/cmip6/test_sam0_unicon.py | 34 +++++++++- 30 files changed, 710 insertions(+), 15 deletions(-) diff --git a/esmvalcore/cmor/_fixes/cmip6/bcc_csm2_mr.py b/esmvalcore/cmor/_fixes/cmip6/bcc_csm2_mr.py index 3f89888afa..f16c188261 100644 --- a/esmvalcore/cmor/_fixes/cmip6/bcc_csm2_mr.py +++ b/esmvalcore/cmor/_fixes/cmip6/bcc_csm2_mr.py @@ -7,6 +7,14 @@ class Cl(BaseCl): """Fixes for ``cl``.""" +class Cli(Cl): + """Fixes for ``cli``.""" + + +class Clw(Cl): + """Fixes for ``clw``.""" + + class Tos(BaseTos): """Fixes for tos.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/bcc_esm1.py b/esmvalcore/cmor/_fixes/cmip6/bcc_esm1.py index 3af096a886..ab2c1849ca 100644 --- a/esmvalcore/cmor/_fixes/cmip6/bcc_esm1.py +++ b/esmvalcore/cmor/_fixes/cmip6/bcc_esm1.py @@ -7,5 +7,13 @@ class Cl(BaseCl): """Fixes for ``cl``.""" +class Cli(Cl): + """Fixes for ``cli``.""" + + +class Clw(Cl): + """Fixes for ``clw``.""" + + class Tos(BaseTos): """Fixes for tos.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/cams_csm1_0.py b/esmvalcore/cmor/_fixes/cmip6/cams_csm1_0.py index 532cdf312a..5a4bbb0cd8 100644 --- a/esmvalcore/cmor/_fixes/cmip6/cams_csm1_0.py +++ b/esmvalcore/cmor/_fixes/cmip6/cams_csm1_0.py @@ -4,3 +4,11 @@ class Cl(BaseCl): """Fixes for ``cl``.""" + + +class Cli(Cl): + """Fixes for ``cli``.""" + + +class Clw(Cl): + """Fixes for ``clw``.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py b/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py index 8fefc82d4d..812f40e849 100644 --- a/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py +++ b/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py @@ -2,6 +2,7 @@ import iris from ..cmip5.bcc_csm1_1 import Cl as BaseCl +from ..fix import Fix from ..shared import add_aux_coords_from_cubes, get_bounds_cube @@ -48,3 +49,33 @@ def fix_metadata(self, cubes): for coord_name in ('latitude', 'longitude'): cube.coord(coord_name).guess_bounds() return cubes + + +class Clcalipso(Fix): + """Fixes for ``clcalipso``.""" + + def fix_metadata(self, cubes): + """Fix ``alt40`` coordinate. + + Parameters + ---------- + cubes : iris.cube.CubeList + Input cubes + + Returns + ------- + iris.cube.CubeList + + """ + cube = self.get_cube_from_list(cubes) + alt_40_coord = cube.coord('alt40') + alt_40_coord.standard_name = 'altitude' + return iris.cube.CubeList([cube]) + + +class Cli(Cl): + """Fixes for ``cli``.""" + + +class Clw(Cl): + """Fixes for ``clw``.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1_hr.py b/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1_hr.py index 73a0d8fafb..8338207e2c 100644 --- a/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1_hr.py +++ b/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1_hr.py @@ -4,3 +4,11 @@ class Cl(BaseCl): """Fixes for ``cl``.""" + + +class Cli(Cl): + """Fixes for ``cli``.""" + + +class Clw(Cl): + """Fixes for ``clw``.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py b/esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py index c11df2657f..5a3ffbce27 100644 --- a/esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py +++ b/esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py @@ -1,6 +1,19 @@ """Fixes for CNRM-ESM2-1 model.""" from .cnrm_cm6_1 import Cl as BaseCl +from .cnrm_cm6_1 import Clcalipso as BaseClcalipso class Cl(BaseCl): """Fixes for ``cl``.""" + + +class Clcalipso(BaseClcalipso): + """Fixes for ``clcalipso``.""" + + +class Cli(Cl): + """Fixes for ``cli``.""" + + +class Clw(Cl): + """Fixes for ``clw``.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/gfdl_cm4.py b/esmvalcore/cmor/_fixes/cmip6/gfdl_cm4.py index faac0927be..ea3d28c6f5 100644 --- a/esmvalcore/cmor/_fixes/cmip6/gfdl_cm4.py +++ b/esmvalcore/cmor/_fixes/cmip6/gfdl_cm4.py @@ -32,6 +32,14 @@ def fix_metadata(self, cubes): return super().fix_metadata(cubes) +class Cli(Cl): + """Fixes for ``cli``.""" + + +class Clw(Cl): + """Fixes for ``clw``.""" + + class Tas(Fix): """Fixes for tas.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/giss_e2_1_g.py b/esmvalcore/cmor/_fixes/cmip6/giss_e2_1_g.py index 8e2a2b09ed..5dcf7298fa 100644 --- a/esmvalcore/cmor/_fixes/cmip6/giss_e2_1_g.py +++ b/esmvalcore/cmor/_fixes/cmip6/giss_e2_1_g.py @@ -4,3 +4,11 @@ class Cl(BaseCl): """Fixes for ``cl``.""" + + +class Cli(Cl): + """Fixes for ``cli``.""" + + +class Clw(Cl): + """Fixes for ``clw``.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/ipsl_cm6a_lr.py b/esmvalcore/cmor/_fixes/cmip6/ipsl_cm6a_lr.py index 214a222d84..dbaf4902e5 100644 --- a/esmvalcore/cmor/_fixes/cmip6/ipsl_cm6a_lr.py +++ b/esmvalcore/cmor/_fixes/cmip6/ipsl_cm6a_lr.py @@ -44,3 +44,27 @@ def fix_metadata(self, cubes): cube.coord('longitude').var_name = 'lon' new_list.append(cube) return CubeList(new_list) + + +class Clcalipso(Fix): + """Fixes for ``clcalipso``.""" + + def fix_metadata(self, cubes): + """Fix ``alt40`` coordinate. + + Parameters + ---------- + cubes : iris.cube.CubeList + Input cubes + + Returns + ------- + iris.cube.CubeList + + """ + cube = self.get_cube_from_list(cubes) + alt_40_coord = cube.coord('height') + alt_40_coord.long_name = 'altitude' + alt_40_coord.standard_name = 'altitude' + alt_40_coord.var_name = 'alt40' + return CubeList([cube]) diff --git a/esmvalcore/cmor/_fixes/cmip6/miroc6.py b/esmvalcore/cmor/_fixes/cmip6/miroc6.py index 8a1ad27217..9ffd9bb17b 100644 --- a/esmvalcore/cmor/_fixes/cmip6/miroc6.py +++ b/esmvalcore/cmor/_fixes/cmip6/miroc6.py @@ -23,3 +23,11 @@ def fix_metadata(self, cubes): coord = cube.coord(long_name='Surface Air Pressure') coord.attributes = {} return cubes + + +class Cli(Cl): + """Fixes for ``cli``.""" + + +class Clw(Cl): + """Fixes for ``clw``.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/miroc_es2l.py b/esmvalcore/cmor/_fixes/cmip6/miroc_es2l.py index d467d83796..7d9b0fe4f5 100644 --- a/esmvalcore/cmor/_fixes/cmip6/miroc_es2l.py +++ b/esmvalcore/cmor/_fixes/cmip6/miroc_es2l.py @@ -4,3 +4,11 @@ class Cl(BaseCl): """Fixes for ``cl``.""" + + +class Cli(Cl): + """Fixes for ``cli``.""" + + +class Clw(Cl): + """Fixes for ``clw``.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/mpi_esm1_2_hr.py b/esmvalcore/cmor/_fixes/cmip6/mpi_esm1_2_hr.py index fcf3d9dd78..651b6adaf0 100644 --- a/esmvalcore/cmor/_fixes/cmip6/mpi_esm1_2_hr.py +++ b/esmvalcore/cmor/_fixes/cmip6/mpi_esm1_2_hr.py @@ -8,6 +8,14 @@ class Cl(BaseCl): """Fixes for ``cl``.""" +class Cli(Cl): + """Fixes for ``cli``.""" + + +class Clw(Cl): + """Fixes for ``clw``.""" + + class Tas(Fix): """Fixes for tas.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/mri_esm2_0.py b/esmvalcore/cmor/_fixes/cmip6/mri_esm2_0.py index af5126d5f4..fca1c45b79 100644 --- a/esmvalcore/cmor/_fixes/cmip6/mri_esm2_0.py +++ b/esmvalcore/cmor/_fixes/cmip6/mri_esm2_0.py @@ -4,3 +4,11 @@ class Cl(BaseCl): """Fixes for ``cl``.""" + + +class Cli(Cl): + """Fixes for ``cli``.""" + + +class Clw(Cl): + """Fixes for ``clw``.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/nesm3.py b/esmvalcore/cmor/_fixes/cmip6/nesm3.py index 9ded3e8fc2..778313fca9 100644 --- a/esmvalcore/cmor/_fixes/cmip6/nesm3.py +++ b/esmvalcore/cmor/_fixes/cmip6/nesm3.py @@ -4,3 +4,11 @@ class Cl(BaseCl): """Fixes for ``cl``.""" + + +class Cli(Cl): + """Fixes for ``cli``.""" + + +class Clw(Cl): + """Fixes for ``clw``.""" diff --git a/esmvalcore/cmor/_fixes/cmip6/sam0_unicon.py b/esmvalcore/cmor/_fixes/cmip6/sam0_unicon.py index 0eee658088..37e2c27c94 100644 --- a/esmvalcore/cmor/_fixes/cmip6/sam0_unicon.py +++ b/esmvalcore/cmor/_fixes/cmip6/sam0_unicon.py @@ -4,3 +4,11 @@ class Cl(BaseCl): """Fixes for ``cl``.""" + + +class Cli(Cl): + """Fixes for ``cli``.""" + + +class Clw(Cl): + """Fixes for ``clw``.""" diff --git a/tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py b/tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py index 4635e0a9b4..7394096423 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py +++ b/tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py @@ -3,7 +3,7 @@ import iris -from esmvalcore.cmor._fixes.cmip6.bcc_csm2_mr import Cl, Tos +from esmvalcore.cmor._fixes.cmip6.bcc_csm2_mr import Cl, Cli, Clw, Tos from esmvalcore.cmor._fixes.fix import Fix @@ -23,6 +23,38 @@ def test_cl_fix_metadata(mock_base_fix_metadata): mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_get_cli_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'BCC-CSM2-MR', 'Amon', 'cli') + assert fix == [Cli(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.bcc_csm2_mr.Cl.fix_metadata', + autospec=True) +def test_cli_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cli``.""" + fix = Cli(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_clw_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'BCC-CSM2-MR', 'Amon', 'clw') + assert fix == [Clw(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.bcc_csm2_mr.Cl.fix_metadata', + autospec=True) +def test_clw_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``clw``.""" + fix = Clw(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + def test_get_tos_fix(): """Test getting of fix.""" fix = Fix.get_fixes('CMIP6', 'BCC-CSM2-MR', 'Omon', 'tos') diff --git a/tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py b/tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py index 1c7e0c04d6..861cef0881 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py +++ b/tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py @@ -1,7 +1,7 @@ """Test fixes for BCC-ESM1.""" import unittest -from esmvalcore.cmor._fixes.cmip6.bcc_esm1 import Cl, Tos +from esmvalcore.cmor._fixes.cmip6.bcc_esm1 import Cl, Cli, Clw, Tos from esmvalcore.cmor._fixes.fix import Fix @@ -21,6 +21,38 @@ def test_cl_fix_metadata(mock_base_fix_metadata): mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_get_cli_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'BCC-ESM1', 'Amon', 'cli') + assert fix == [Cli(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.bcc_esm1.Cl.fix_metadata', + autospec=True) +def test_cli_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cli``.""" + fix = Cli(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_clw_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'BCC-ESM1', 'Amon', 'clw') + assert fix == [Clw(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.bcc_esm1.Cl.fix_metadata', + autospec=True) +def test_clw_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``clw``.""" + fix = Clw(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + def test_get_tos_fix(): """Test getting of fix.""" fix = Fix.get_fixes('CMIP6', 'BCC-ESM1', 'Omon', 'tos') diff --git a/tests/integration/cmor/_fixes/cmip6/test_cams_csm1_0.py b/tests/integration/cmor/_fixes/cmip6/test_cams_csm1_0.py index d5e023b6f3..339aa538b1 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_cams_csm1_0.py +++ b/tests/integration/cmor/_fixes/cmip6/test_cams_csm1_0.py @@ -1,7 +1,7 @@ """Test fixes for CAMS-CSM1-0.""" import unittest -from esmvalcore.cmor._fixes.cmip6.cams_csm1_0 import Cl +from esmvalcore.cmor._fixes.cmip6.cams_csm1_0 import Cl, Cli, Clw from esmvalcore.cmor._fixes.fix import Fix @@ -19,3 +19,35 @@ def test_cl_fix_metadata(mock_base_fix_metadata): fix = Cl(None) fix.fix_metadata('cubes') mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_cli_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'CAMS-CSM1-0', 'Amon', 'cli') + assert fix == [Cli(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.cams_csm1_0.Cl.fix_metadata', + autospec=True) +def test_cli_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cli``.""" + fix = Cli(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_clw_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'CAMS-CSM1-0', 'Amon', 'clw') + assert fix == [Clw(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.cams_csm1_0.Cl.fix_metadata', + autospec=True) +def test_clw_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``clw``.""" + fix = Clw(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, '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 29e8080498..5460ab8a91 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1.py +++ b/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1.py @@ -1,12 +1,13 @@ """Tests for the fixes of CNRM-CM6-1.""" import os +import unittest import iris import numpy as np import pytest from netCDF4 import Dataset -from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1 import Cl +from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1 import Cl, Clcalipso, Cli, Clw from esmvalcore.cmor.fix import Fix @@ -145,3 +146,62 @@ def test_cl_fix_metadata(cl_file): [[-45.0, -15.0], [-15.0, 15.0]]) np.testing.assert_allclose(lon_coord.bounds, [[15.0, 45.0], [45.0, 75.0]]) + + +def test_get_clcalipso_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'CNRM-CM6-1', 'Amon', 'clcalipso') + assert fix == [Clcalipso(None)] + + +@pytest.fixture +def clcalipso_cubes(): + """Cubes to test fix for ``clcalipso``.""" + alt_40_coord = iris.coords.DimCoord([0.0], var_name='alt40') + cube = iris.cube.Cube([0.0], var_name='clcalipso', + dim_coords_and_dims=[(alt_40_coord.copy(), 0)]) + x_cube = iris.cube.Cube([0.0], var_name='x', + dim_coords_and_dims=[(alt_40_coord.copy(), 0)]) + return iris.cube.CubeList([cube, x_cube]) + + +def test_clcalipso_fix_metadata(clcalipso_cubes): + """Test ``fix_metadata`` for ``clcalipso``.""" + fix = Clcalipso(None) + cubes = fix.fix_metadata(clcalipso_cubes) + assert len(cubes) == 1 + cube = cubes[0] + coord = cube.coord('altitude') + assert coord.standard_name == 'altitude' + + +def test_get_cli_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'CNRM-CM6-1', 'Amon', 'cli') + assert fix == [Cli(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1.Cl.fix_metadata', + autospec=True) +def test_cli_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cli``.""" + fix = Cli(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_clw_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'CNRM-CM6-1', 'Amon', 'clw') + assert fix == [Clw(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1.Cl.fix_metadata', + autospec=True) +def test_clw_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``clw``.""" + fix = Clw(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1_hr.py b/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1_hr.py index 8e7f0560ee..436f4f071e 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1_hr.py +++ b/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1_hr.py @@ -1,7 +1,7 @@ """Test fixes for CNRM-CM6-1-HR.""" import unittest -from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1_hr import Cl +from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1_hr import Cl, Cli, Clw from esmvalcore.cmor._fixes.fix import Fix @@ -19,3 +19,35 @@ def test_cl_fix_metadata(mock_base_fix_metadata): fix = Cl(None) fix.fix_metadata('cubes') mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_cli_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'CNRM-CM6-1-HR', 'Amon', 'cli') + assert fix == [Cli(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1_hr.Cl.fix_metadata', + autospec=True) +def test_cli_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cli``.""" + fix = Cli(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_clw_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'CNRM-CM6-1-HR', 'Amon', 'clw') + assert fix == [Clw(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1_hr.Cl.fix_metadata', + autospec=True) +def test_clw_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``clw``.""" + fix = Clw(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip6/test_cnrm_esm2_1.py b/tests/integration/cmor/_fixes/cmip6/test_cnrm_esm2_1.py index d0d57b3d1b..85458d3e4d 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_cnrm_esm2_1.py +++ b/tests/integration/cmor/_fixes/cmip6/test_cnrm_esm2_1.py @@ -1,7 +1,7 @@ """Test fixes for CNRM-ESM2-1.""" import unittest -from esmvalcore.cmor._fixes.cmip6.cnrm_esm2_1 import Cl +from esmvalcore.cmor._fixes.cmip6.cnrm_esm2_1 import Cl, Clcalipso, Cli, Clw from esmvalcore.cmor._fixes.fix import Fix @@ -19,3 +19,51 @@ def test_cl_fix_metadata(mock_base_fix_metadata): fix = Cl(None) fix.fix_metadata('cubes') mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_clcalipso_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'CNRM-ESM2-1', 'Amon', 'clcalipso') + assert fix == [Clcalipso(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.cnrm_esm2_1.BaseClcalipso.fix_metadata', + autospec=True) +def test_clcalipso_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``clcalipso`.""" + fix = Clcalipso(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_cli_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'CNRM-ESM2-1', 'Amon', 'cli') + assert fix == [Cli(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.cnrm_esm2_1.Cl.fix_metadata', + autospec=True) +def test_cli_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cli``.""" + fix = Cli(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_clw_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'CNRM-ESM2-1', 'Amon', 'clw') + assert fix == [Clw(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.cnrm_esm2_1.Cl.fix_metadata', + autospec=True) +def test_clw_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``clw``.""" + fix = Clw(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip6/test_gfdl_cm4.py b/tests/integration/cmor/_fixes/cmip6/test_gfdl_cm4.py index 3667e1d61e..a5b16e3536 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_gfdl_cm4.py +++ b/tests/integration/cmor/_fixes/cmip6/test_gfdl_cm4.py @@ -1,12 +1,13 @@ """Tests for the fixes of GFDL-CM4.""" import os +import unittest import iris import numpy as np import pytest from netCDF4 import Dataset -from esmvalcore.cmor._fixes.cmip6.gfdl_cm4 import Cl +from esmvalcore.cmor._fixes.cmip6.gfdl_cm4 import Cl, Cli, Clw from esmvalcore.cmor.fix import Fix @@ -135,3 +136,35 @@ def test_cl_fix_metadata(cl_file): AIR_PRESSURE_POINTS) np.testing.assert_allclose(fixed_air_pressure_coord.bounds, AIR_PRESSURE_BOUNDS) + + +def test_get_cli_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'GFDL-CM4', 'Amon', 'cli') + assert fix == [Cli(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.gfdl_cm4.Cl.fix_metadata', + autospec=True) +def test_cli_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cli``.""" + fix = Cli(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_clw_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'GFDL-CM4', 'Amon', 'clw') + assert fix == [Clw(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.gfdl_cm4.Cl.fix_metadata', + autospec=True) +def test_clw_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``clw``.""" + fix = Clw(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_g.py b/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_g.py index 733c97513a..659cbf80ce 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_g.py +++ b/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_g.py @@ -1,7 +1,7 @@ """Test fixes for GISS-E2-1-G.""" import unittest -from esmvalcore.cmor._fixes.cmip6.giss_e2_1_g import Cl +from esmvalcore.cmor._fixes.cmip6.giss_e2_1_g import Cl, Cli, Clw from esmvalcore.cmor._fixes.fix import Fix @@ -19,3 +19,35 @@ def test_cl_fix_metadata(mock_base_fix_metadata): fix = Cl(None) fix.fix_metadata('cubes') mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_cli_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'GISS-E2-1-G', 'Amon', 'cli') + assert fix == [Cli(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.giss_e2_1_g.Cl.fix_metadata', + autospec=True) +def test_cli_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cli``.""" + fix = Cli(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_clw_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'GISS-E2-1-G', 'Amon', 'clw') + assert fix == [Clw(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.giss_e2_1_g.Cl.fix_metadata', + autospec=True) +def test_clw_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``clw``.""" + fix = Clw(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip6/test_ipsl_cm6a_lr.py b/tests/integration/cmor/_fixes/cmip6/test_ipsl_cm6a_lr.py index 5edc8b8554..2622fe3120 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_ipsl_cm6a_lr.py +++ b/tests/integration/cmor/_fixes/cmip6/test_ipsl_cm6a_lr.py @@ -1,16 +1,23 @@ +"""Tests for the fixes of IPSL-CM6A-LR.""" import unittest +import iris import numpy as np +import pytest from iris.cube import Cube, CubeList from iris.coords import AuxCoord from iris.exceptions import CoordinateNotFoundError -from esmvalcore.cmor._fixes.cmip6.ipsl_cm6a_lr import AllVars +from esmvalcore.cmor._fixes.cmip6.ipsl_cm6a_lr import AllVars, Clcalipso +from esmvalcore.cmor._fixes.fix import Fix class TestAllVars(unittest.TestCase): + """Tests for fixes of all variables.""" + def setUp(self): + """Set up tests.""" self.fix = AllVars(None) self.cube = Cube(np.random.rand(2, 2, 2), var_name='ch4') self.cube.add_aux_coord( @@ -23,6 +30,7 @@ def setUp(self): standard_name='longitude'), (1, 2)) def test_fix_metadata_ocean_var(self): + """Test ``fix_metadata`` for ocean variables.""" cell_area = Cube(np.random.rand(2, 2), standard_name='cell_area') cubes = self.fix.fix_metadata(CubeList([self.cube, cell_area])) @@ -33,6 +41,7 @@ def test_fix_metadata_ocean_var(self): self.cube.coord('cell_area') def test_fix_data_other_var(self): + """Test ``fix_metadata`` for other variables.""" cubes = self.fix.fix_metadata(CubeList([self.cube])) self.assertEqual(len(cubes), 1) @@ -41,3 +50,32 @@ def test_fix_data_other_var(self): self.assertEqual(cube.coord('longitude').var_name, 'nav_lon') with self.assertRaises(CoordinateNotFoundError): self.cube.coord('cell_area') + + +def test_get_clcalipso_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'IPSL-CM6A-LR', 'Amon', 'clcalipso') + assert fix == [Clcalipso(None), AllVars(None)] + + +@pytest.fixture +def clcalipso_cubes(): + """Cubes to test fix for ``clcalipso``.""" + alt_40_coord = iris.coords.DimCoord([0.0], var_name='height') + cube = iris.cube.Cube([0.0], var_name='clcalipso', + dim_coords_and_dims=[(alt_40_coord.copy(), 0)]) + x_cube = iris.cube.Cube([0.0], var_name='x', + dim_coords_and_dims=[(alt_40_coord.copy(), 0)]) + return iris.cube.CubeList([cube, x_cube]) + + +def test_clcalipso_fix_metadata(clcalipso_cubes): + """Test ``fix_metadata`` for ``clcalipso``.""" + fix = Clcalipso(None) + cubes = fix.fix_metadata(clcalipso_cubes) + assert len(cubes) == 1 + cube = cubes[0] + coord = cube.coord('altitude') + assert coord.long_name == 'altitude' + assert coord.standard_name == 'altitude' + assert coord.var_name == 'alt40' diff --git a/tests/integration/cmor/_fixes/cmip6/test_miroc6.py b/tests/integration/cmor/_fixes/cmip6/test_miroc6.py index fd945cbccf..643985e6c2 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_miroc6.py +++ b/tests/integration/cmor/_fixes/cmip6/test_miroc6.py @@ -5,7 +5,7 @@ from iris.coords import AuxCoord from iris.cube import Cube, CubeList -from esmvalcore.cmor._fixes.cmip6.miroc6 import Cl +from esmvalcore.cmor._fixes.cmip6.miroc6 import Cl, Cli, Clw from esmvalcore.cmor._fixes.fix import Fix @@ -54,3 +54,35 @@ def test_cl_fix_metadata(mock_base_fix_metadata, cl_cubes): x_cube = fixed_cubes.extract_strict('x') ps_coord_x = x_cube.coord('Surface Air Pressure') assert ps_coord_x.attributes == {'a': 1, 'b': '2'} + + +def test_get_cli_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'MIROC6', 'Amon', 'cli') + assert fix == [Cli(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.miroc6.Cl.fix_metadata', + autospec=True) +def test_cli_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cli``.""" + fix = Cli(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_clw_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'MIROC6', 'Amon', 'clw') + assert fix == [Clw(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.miroc6.Cl.fix_metadata', + autospec=True) +def test_clw_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``clw``.""" + fix = Clw(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip6/test_miroc_es2l.py b/tests/integration/cmor/_fixes/cmip6/test_miroc_es2l.py index 044faec07f..f2ff7dd31b 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_miroc_es2l.py +++ b/tests/integration/cmor/_fixes/cmip6/test_miroc_es2l.py @@ -1,7 +1,7 @@ """Test fixes for MIROC-ES2L.""" import unittest -from esmvalcore.cmor._fixes.cmip6.miroc_es2l import Cl +from esmvalcore.cmor._fixes.cmip6.miroc_es2l import Cl, Cli, Clw from esmvalcore.cmor._fixes.fix import Fix @@ -19,3 +19,35 @@ def test_cl_fix_metadata(mock_base_fix_metadata): fix = Cl(None) fix.fix_metadata('cubes') mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_cli_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'MIROC-ES2L', 'Amon', 'cli') + assert fix == [Cli(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.miroc_es2l.Cl.fix_metadata', + autospec=True) +def test_cli_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cli``.""" + fix = Cli(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_clw_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'MIROC-ES2L', 'Amon', 'clw') + assert fix == [Clw(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.miroc_es2l.Cl.fix_metadata', + autospec=True) +def test_clw_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``clw``.""" + fix = Clw(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip6/test_mpi_esm1_2_hr.py b/tests/integration/cmor/_fixes/cmip6/test_mpi_esm1_2_hr.py index 2f6f2c6237..943d719fcb 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_mpi_esm1_2_hr.py +++ b/tests/integration/cmor/_fixes/cmip6/test_mpi_esm1_2_hr.py @@ -1,7 +1,7 @@ """Test fixes for MPI-ESM1-2-HR.""" import unittest -from esmvalcore.cmor._fixes.cmip6.mpi_esm1_2_hr import Cl +from esmvalcore.cmor._fixes.cmip6.mpi_esm1_2_hr import Cl, Cli, Clw from esmvalcore.cmor._fixes.fix import Fix @@ -19,3 +19,35 @@ def test_cl_fix_metadata(mock_base_fix_metadata): fix = Cl(None) fix.fix_metadata('cubes') mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_cli_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'MPI-ESM1-2-HR', 'Amon', 'cli') + assert fix == [Cli(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.mpi_esm1_2_hr.Cl.fix_metadata', + autospec=True) +def test_cli_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cli``.""" + fix = Cli(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_clw_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'MPI-ESM1-2-HR', 'Amon', 'clw') + assert fix == [Clw(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.mpi_esm1_2_hr.Cl.fix_metadata', + autospec=True) +def test_clw_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``clw``.""" + fix = Clw(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip6/test_mri_esm2_0.py b/tests/integration/cmor/_fixes/cmip6/test_mri_esm2_0.py index 362e647940..8d3ab68718 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_mri_esm2_0.py +++ b/tests/integration/cmor/_fixes/cmip6/test_mri_esm2_0.py @@ -1,7 +1,7 @@ """Test fixes for MRI-ESM2-0.""" import unittest -from esmvalcore.cmor._fixes.cmip6.mri_esm2_0 import Cl +from esmvalcore.cmor._fixes.cmip6.mri_esm2_0 import Cl, Cli, Clw from esmvalcore.cmor._fixes.fix import Fix @@ -19,3 +19,35 @@ def test_cl_fix_metadata(mock_base_fix_metadata): fix = Cl(None) fix.fix_metadata('cubes') mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_cli_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'MRI-ESM2-0', 'Amon', 'cli') + assert fix == [Cli(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.mri_esm2_0.Cl.fix_metadata', + autospec=True) +def test_cli_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cli``.""" + fix = Cli(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_clw_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'MRI-ESM2-0', 'Amon', 'clw') + assert fix == [Clw(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.mri_esm2_0.Cl.fix_metadata', + autospec=True) +def test_clw_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``clw``.""" + fix = Clw(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip6/test_nesm3.py b/tests/integration/cmor/_fixes/cmip6/test_nesm3.py index 36e3c1b3d9..23099e5ac0 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_nesm3.py +++ b/tests/integration/cmor/_fixes/cmip6/test_nesm3.py @@ -1,7 +1,7 @@ """Test fixes for NESM3.""" import unittest -from esmvalcore.cmor._fixes.cmip6.nesm3 import Cl +from esmvalcore.cmor._fixes.cmip6.nesm3 import Cl, Cli, Clw from esmvalcore.cmor._fixes.fix import Fix @@ -19,3 +19,35 @@ def test_cl_fix_metadata(mock_base_fix_metadata): fix = Cl(None) fix.fix_metadata('cubes') mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_cli_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'NESM3', 'Amon', 'cli') + assert fix == [Cli(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.nesm3.Cl.fix_metadata', + autospec=True) +def test_cli_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cli``.""" + fix = Cli(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_clw_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'NESM3', 'Amon', 'clw') + assert fix == [Clw(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.nesm3.Cl.fix_metadata', + autospec=True) +def test_clw_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``clw``.""" + fix = Clw(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') diff --git a/tests/integration/cmor/_fixes/cmip6/test_sam0_unicon.py b/tests/integration/cmor/_fixes/cmip6/test_sam0_unicon.py index fe296c221f..60a99426f6 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_sam0_unicon.py +++ b/tests/integration/cmor/_fixes/cmip6/test_sam0_unicon.py @@ -1,7 +1,7 @@ """Test fixes for SAM0-UNICON.""" import unittest -from esmvalcore.cmor._fixes.cmip6.sam0_unicon import Cl +from esmvalcore.cmor._fixes.cmip6.sam0_unicon import Cl, Cli, Clw from esmvalcore.cmor._fixes.fix import Fix @@ -19,3 +19,35 @@ def test_cl_fix_metadata(mock_base_fix_metadata): fix = Cl(None) fix.fix_metadata('cubes') mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_cli_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'SAM0-UNICON', 'Amon', 'cli') + assert fix == [Cli(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.sam0_unicon.Cl.fix_metadata', + autospec=True) +def test_cli_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``cli``.""" + fix = Cli(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') + + +def test_get_clw_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'SAM0-UNICON', 'Amon', 'clw') + assert fix == [Clw(None)] + + +@unittest.mock.patch( + 'esmvalcore.cmor._fixes.cmip6.sam0_unicon.Cl.fix_metadata', + autospec=True) +def test_clw_fix_metadata(mock_base_fix_metadata): + """Test ``fix_metadata`` for ``clw``.""" + fix = Clw(None) + fix.fix_metadata('cubes') + mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') From 8b10380deb3b94d45b58cac7ce9d9b94f4cfcc2e Mon Sep 17 00:00:00 2001 From: Manuel Schlund Date: Thu, 12 Mar 2020 12:52:21 +0100 Subject: [PATCH 3/4] Added suggestions from @zklaus --- esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1.py | 56 +---- esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1_m.py | 5 +- esmvalcore/cmor/_fixes/cmip5/canesm2.py | 5 +- esmvalcore/cmor/_fixes/cmip5/ccsm4.py | 5 +- esmvalcore/cmor/_fixes/cmip5/csiro_mk3_6_0.py | 5 +- esmvalcore/cmor/_fixes/cmip5/giss_e2_h.py | 5 +- esmvalcore/cmor/_fixes/cmip5/giss_e2_r.py | 5 +- esmvalcore/cmor/_fixes/cmip5/inmcm4.py | 5 +- esmvalcore/cmor/_fixes/cmip5/ipsl_cm5a_lr.py | 5 +- esmvalcore/cmor/_fixes/cmip5/ipsl_cm5a_mr.py | 5 +- esmvalcore/cmor/_fixes/cmip5/ipsl_cm5b_lr.py | 5 +- esmvalcore/cmor/_fixes/cmip5/miroc5.py | 5 +- esmvalcore/cmor/_fixes/cmip5/miroc_esm.py | 7 +- esmvalcore/cmor/_fixes/cmip5/mpi_esm_lr.py | 5 +- esmvalcore/cmor/_fixes/cmip5/mpi_esm_mr.py | 5 +- esmvalcore/cmor/_fixes/cmip5/mpi_esm_p.py | 5 +- esmvalcore/cmor/_fixes/cmip5/mri_cgcm3.py | 5 +- esmvalcore/cmor/_fixes/cmip5/noresm1_m.py | 5 +- esmvalcore/cmor/_fixes/cmip6/bcc_csm2_mr.py | 11 +- esmvalcore/cmor/_fixes/cmip6/bcc_esm1.py | 11 +- esmvalcore/cmor/_fixes/cmip6/cams_csm1_0.py | 11 +- esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py | 10 +- esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1_hr.py | 9 +- esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py | 12 +- esmvalcore/cmor/_fixes/cmip6/gfdl_cm4.py | 10 +- esmvalcore/cmor/_fixes/cmip6/giss_e2_1_g.py | 11 +- esmvalcore/cmor/_fixes/cmip6/giss_e2_1_h.py | 11 +- esmvalcore/cmor/_fixes/cmip6/miroc6.py | 30 +-- esmvalcore/cmor/_fixes/cmip6/miroc_es2l.py | 11 +- esmvalcore/cmor/_fixes/cmip6/mpi_esm1_2_hr.py | 11 +- esmvalcore/cmor/_fixes/cmip6/mri_esm2_0.py | 11 +- esmvalcore/cmor/_fixes/cmip6/nesm3.py | 11 +- esmvalcore/cmor/_fixes/cmip6/sam0_unicon.py | 11 +- esmvalcore/cmor/_fixes/common.py | 64 ++++++ .../cmor/_fixes/cmip5/test_bcc_csm1_1.py | 196 +--------------- .../cmor/_fixes/cmip5/test_bcc_csm1_1_m.py | 12 +- .../cmor/_fixes/cmip5/test_canesm2.py | 12 +- .../cmor/_fixes/cmip5/test_ccsm4.py | 12 +- .../cmor/_fixes/cmip5/test_csiro_mk3_6_0.py | 14 +- .../cmor/_fixes/cmip5/test_giss_e2_h.py | 14 +- .../cmor/_fixes/cmip5/test_giss_e2_r.py | 14 +- .../cmor/_fixes/cmip5/test_inmcm4.py | 12 +- .../cmor/_fixes/cmip5/test_ipsl_cm5a_lr.py | 14 +- .../cmor/_fixes/cmip5/test_ipsl_cm5a_mr.py | 14 +- .../cmor/_fixes/cmip5/test_ipsl_cm5b_lr.py | 14 +- .../cmor/_fixes/cmip5/test_miroc5.py | 12 +- .../cmor/_fixes/cmip5/test_miroc_esm.py | 12 +- .../cmor/_fixes/cmip5/test_mpi_esm_lr.py | 12 +- .../cmor/_fixes/cmip5/test_mpi_esm_mr.py | 14 +- .../cmor/_fixes/cmip5/test_mpi_esm_p.py | 14 +- .../cmor/_fixes/cmip5/test_mri_cgcm3.py | 12 +- .../cmor/_fixes/cmip5/test_noresm1_m.py | 14 +- .../cmor/_fixes/cmip6/test_bcc_csm2_mr.py | 34 +-- .../cmor/_fixes/cmip6/test_bcc_esm1.py | 34 +-- .../cmor/_fixes/cmip6/test_cams_csm1_0.py | 36 +-- .../cmor/_fixes/cmip6/test_cnrm_cm6_1.py | 23 +- .../cmor/_fixes/cmip6/test_cnrm_cm6_1_hr.py | 36 +-- .../cmor/_fixes/cmip6/test_cnrm_esm2_1.py | 48 ++-- .../cmor/_fixes/cmip6/test_gfdl_cm4.py | 23 +- .../cmor/_fixes/cmip6/test_giss_e2_1_g.py | 36 +-- .../cmor/_fixes/cmip6/test_giss_e2_1_h.py | 38 +++- .../cmor/_fixes/cmip6/test_miroc6.py | 71 +----- .../cmor/_fixes/cmip6/test_miroc_es2l.py | 36 +-- .../cmor/_fixes/cmip6/test_mpi_esm1_2_hr.py | 36 +-- .../cmor/_fixes/cmip6/test_mri_esm2_0.py | 36 +-- .../cmor/_fixes/cmip6/test_nesm3.py | 36 +-- .../cmor/_fixes/cmip6/test_sam0_unicon.py | 36 +-- tests/integration/cmor/_fixes/test_common.py | 213 ++++++++++++++++++ 68 files changed, 612 insertions(+), 966 deletions(-) create mode 100644 esmvalcore/cmor/_fixes/common.py create mode 100644 tests/integration/cmor/_fixes/test_common.py diff --git a/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1.py b/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1.py index 1ed206fd84..c61eeaa192 100644 --- a/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1.py +++ b/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1.py @@ -1,65 +1,13 @@ """Fixes for bcc-csm1-1.""" -import iris import numpy as np from scipy.interpolate import InterpolatedUnivariateSpline from scipy.ndimage import map_coordinates +from ..common import ClFixHybridPressureCoord from ..fix import Fix -from ..shared import fix_bounds -class Cl(Fix): - """Fixes for cl.""" - - def fix_metadata(self, cubes): - """Fix hybrid sigma pressure coordinate. - - Parameters - ---------- - cubes : iris.cube.CubeList - Input cubes which need to be fixed. - - Returns - ------- - iris.cube.CubeList - - """ - cl_cube = self.get_cube_from_list(cubes) - - # Remove all existing aux_factories - for aux_factory in cl_cube.aux_factories: - cl_cube.remove_aux_factory(aux_factory) - - # Fix bounds - coords_to_fix = ['b'] - try: - cl_cube.coord(var_name='a') - coords_to_fix.append('a') - except iris.exceptions.CoordinateNotFoundError: - coords_to_fix.append('ap') - fix_bounds(cl_cube, cubes, coords_to_fix) - - # Fix bounds for ap if only a is given in original file - ap_coord = cl_cube.coord(var_name='ap') - if ap_coord.bounds is None: - cl_cube.remove_coord(ap_coord) - a_coord = cl_cube.coord(var_name='a') - p0_coord = cl_cube.coord(var_name='p0') - ap_coord = a_coord * p0_coord.points[0] - ap_coord.units = a_coord.units * p0_coord.units - ap_coord.rename('vertical pressure') - ap_coord.var_name = 'ap' - cl_cube.add_aux_coord(ap_coord, cl_cube.coord_dims(a_coord)) - - # Add aux_factory again - pressure_coord_factory = iris.aux_factory.HybridPressureFactory( - delta=ap_coord, - sigma=cl_cube.coord(var_name='b'), - surface_air_pressure=cl_cube.coord(var_name='ps'), - ) - cl_cube.add_aux_factory(pressure_coord_factory) - - return iris.cube.CubeList([cl_cube]) +Cl = ClFixHybridPressureCoord class Tos(Fix): diff --git a/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1_m.py b/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1_m.py index 9d19e68a7b..4507ac2141 100644 --- a/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1_m.py +++ b/esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1_m.py @@ -1,10 +1,9 @@ """Fixes for bcc-csm1-1-m.""" -from .bcc_csm1_1 import Cl as BaseCl from .bcc_csm1_1 import Tos as BaseTos +from ..common import ClFixHybridPressureCoord -class Cl(BaseCl): - """Fixes for cl.""" +Cl = ClFixHybridPressureCoord class Tos(BaseTos): diff --git a/esmvalcore/cmor/_fixes/cmip5/canesm2.py b/esmvalcore/cmor/_fixes/cmip5/canesm2.py index fa8293dbb3..b445a4dc3a 100644 --- a/esmvalcore/cmor/_fixes/cmip5/canesm2.py +++ b/esmvalcore/cmor/_fixes/cmip5/canesm2.py @@ -1,10 +1,9 @@ """Fixes for CanESM2 model.""" +from ..common import ClFixHybridPressureCoord from ..fix import Fix -from .bcc_csm1_1 import Cl as BaseCl -class Cl(BaseCl): - """Fixes for cl.""" +Cl = ClFixHybridPressureCoord class FgCo2(Fix): diff --git a/esmvalcore/cmor/_fixes/cmip5/ccsm4.py b/esmvalcore/cmor/_fixes/cmip5/ccsm4.py index 29528e5652..cc14dddc02 100644 --- a/esmvalcore/cmor/_fixes/cmip5/ccsm4.py +++ b/esmvalcore/cmor/_fixes/cmip5/ccsm4.py @@ -1,11 +1,10 @@ """Fixes for CCSM4 model.""" -from .bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord from ..fix import Fix from ..shared import round_coordinates -class Cl(BaseCl): - """Fixes for cl.""" +Cl = ClFixHybridPressureCoord class Rlut(Fix): diff --git a/esmvalcore/cmor/_fixes/cmip5/csiro_mk3_6_0.py b/esmvalcore/cmor/_fixes/cmip5/csiro_mk3_6_0.py index 67e6804609..4c0a5a7564 100644 --- a/esmvalcore/cmor/_fixes/cmip5/csiro_mk3_6_0.py +++ b/esmvalcore/cmor/_fixes/cmip5/csiro_mk3_6_0.py @@ -1,6 +1,5 @@ """Fixes for CSIRO-Mk3-6-0 model.""" -from ..cmip6.miroc6 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord -class Cl(BaseCl): - """Fixes for ``cl``.""" +Cl = ClFixHybridPressureCoord diff --git a/esmvalcore/cmor/_fixes/cmip5/giss_e2_h.py b/esmvalcore/cmor/_fixes/cmip5/giss_e2_h.py index 968dfd32e1..5ee8c8aff1 100644 --- a/esmvalcore/cmor/_fixes/cmip5/giss_e2_h.py +++ b/esmvalcore/cmor/_fixes/cmip5/giss_e2_h.py @@ -1,6 +1,5 @@ """Fixes for GISS-E2-H.""" -from .bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord -class Cl(BaseCl): - """Fixes for cl.""" +Cl = ClFixHybridPressureCoord diff --git a/esmvalcore/cmor/_fixes/cmip5/giss_e2_r.py b/esmvalcore/cmor/_fixes/cmip5/giss_e2_r.py index a356bcff54..0d538e7960 100644 --- a/esmvalcore/cmor/_fixes/cmip5/giss_e2_r.py +++ b/esmvalcore/cmor/_fixes/cmip5/giss_e2_r.py @@ -1,6 +1,5 @@ """Fixes for GISS-E2-R.""" -from .bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord -class Cl(BaseCl): - """Fixes for cl.""" +Cl = ClFixHybridPressureCoord diff --git a/esmvalcore/cmor/_fixes/cmip5/inmcm4.py b/esmvalcore/cmor/_fixes/cmip5/inmcm4.py index eb5bb622d2..e89cea019d 100644 --- a/esmvalcore/cmor/_fixes/cmip5/inmcm4.py +++ b/esmvalcore/cmor/_fixes/cmip5/inmcm4.py @@ -1,12 +1,11 @@ """Fixes for inmcm4 model.""" import iris -from .bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord from ..fix import Fix -class Cl(BaseCl): - """Fixes for cl.""" +Cl = ClFixHybridPressureCoord class Gpp(Fix): diff --git a/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5a_lr.py b/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5a_lr.py index ed01586f49..894af45317 100644 --- a/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5a_lr.py +++ b/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5a_lr.py @@ -1,6 +1,5 @@ """Fixes for IPSL-CM5A-LR model.""" -from .bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord -class Cl(BaseCl): - """Fixes for cl.""" +Cl = ClFixHybridPressureCoord diff --git a/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5a_mr.py b/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5a_mr.py index f5977a1c61..893b9779c2 100644 --- a/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5a_mr.py +++ b/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5a_mr.py @@ -1,6 +1,5 @@ """Fixes for IPSL-CM5A-MR model.""" -from .bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord -class Cl(BaseCl): - """Fixes for cl.""" +Cl = ClFixHybridPressureCoord diff --git a/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5b_lr.py b/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5b_lr.py index 30ff79a4ba..03bed332d6 100644 --- a/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5b_lr.py +++ b/esmvalcore/cmor/_fixes/cmip5/ipsl_cm5b_lr.py @@ -1,6 +1,5 @@ """Fixes for IPSL-CM5B-LR model.""" -from .bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord -class Cl(BaseCl): - """Fixes for cl.""" +Cl = ClFixHybridPressureCoord diff --git a/esmvalcore/cmor/_fixes/cmip5/miroc5.py b/esmvalcore/cmor/_fixes/cmip5/miroc5.py index da49992fdb..ae5ec6ee16 100644 --- a/esmvalcore/cmor/_fixes/cmip5/miroc5.py +++ b/esmvalcore/cmor/_fixes/cmip5/miroc5.py @@ -1,13 +1,12 @@ """Fixes for MIROC5 model.""" from dask import array as da -from ..cmip6.miroc6 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord from ..fix import Fix from ..shared import round_coordinates -class Cl(BaseCl): - """Fixes for ``cl``.""" +Cl = ClFixHybridPressureCoord class Sftof(Fix): diff --git a/esmvalcore/cmor/_fixes/cmip5/miroc_esm.py b/esmvalcore/cmor/_fixes/cmip5/miroc_esm.py index e333b97584..eacb7398c1 100644 --- a/esmvalcore/cmor/_fixes/cmip5/miroc_esm.py +++ b/esmvalcore/cmor/_fixes/cmip5/miroc_esm.py @@ -1,14 +1,13 @@ -"""Fixes for MIROC ESM model.""" +"""Fixes for MIROC-ESM model.""" from iris.coords import DimCoord from iris.exceptions import CoordinateNotFoundError -from .bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord from ..fix import Fix -class Cl(BaseCl): - """Fixes for ``cl``.""" +Cl = ClFixHybridPressureCoord class Tro3(Fix): diff --git a/esmvalcore/cmor/_fixes/cmip5/mpi_esm_lr.py b/esmvalcore/cmor/_fixes/cmip5/mpi_esm_lr.py index 0d4d1e2656..a24aebd499 100644 --- a/esmvalcore/cmor/_fixes/cmip5/mpi_esm_lr.py +++ b/esmvalcore/cmor/_fixes/cmip5/mpi_esm_lr.py @@ -1,10 +1,9 @@ """Fixes for MPI-ESM-LR model.""" +from ..common import ClFixHybridPressureCoord from ..fix import Fix -from .bcc_csm1_1 import Cl as BaseCl -class Cl(BaseCl): - """Fixes for cl.""" +Cl = ClFixHybridPressureCoord class Pctisccp(Fix): diff --git a/esmvalcore/cmor/_fixes/cmip5/mpi_esm_mr.py b/esmvalcore/cmor/_fixes/cmip5/mpi_esm_mr.py index a311de5ef7..92b1a6a06b 100644 --- a/esmvalcore/cmor/_fixes/cmip5/mpi_esm_mr.py +++ b/esmvalcore/cmor/_fixes/cmip5/mpi_esm_mr.py @@ -1,6 +1,5 @@ """Fixes for MPI-ESM-MR model.""" -from .bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord -class Cl(BaseCl): - """Fixes for cl.""" +Cl = ClFixHybridPressureCoord diff --git a/esmvalcore/cmor/_fixes/cmip5/mpi_esm_p.py b/esmvalcore/cmor/_fixes/cmip5/mpi_esm_p.py index 6ed3c44827..303e1b0fd1 100644 --- a/esmvalcore/cmor/_fixes/cmip5/mpi_esm_p.py +++ b/esmvalcore/cmor/_fixes/cmip5/mpi_esm_p.py @@ -1,6 +1,5 @@ """Fixes for MPI-ESM-P model.""" -from .bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord -class Cl(BaseCl): - """Fixes for cl.""" +Cl = ClFixHybridPressureCoord diff --git a/esmvalcore/cmor/_fixes/cmip5/mri_cgcm3.py b/esmvalcore/cmor/_fixes/cmip5/mri_cgcm3.py index 8da1e66977..a21b82eb7f 100644 --- a/esmvalcore/cmor/_fixes/cmip5/mri_cgcm3.py +++ b/esmvalcore/cmor/_fixes/cmip5/mri_cgcm3.py @@ -1,12 +1,11 @@ """Fixes for MRI-CGCM3 model.""" from dask import array as da -from .bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord from ..fix import Fix -class Cl(BaseCl): - """Fixes for ``cl``.""" +Cl = ClFixHybridPressureCoord class Msftmyz(Fix): diff --git a/esmvalcore/cmor/_fixes/cmip5/noresm1_m.py b/esmvalcore/cmor/_fixes/cmip5/noresm1_m.py index 6748e14456..703ef054d7 100644 --- a/esmvalcore/cmor/_fixes/cmip5/noresm1_m.py +++ b/esmvalcore/cmor/_fixes/cmip5/noresm1_m.py @@ -1,6 +1,5 @@ """Fixes for NorESM1-M.""" -from .bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord -class Cl(BaseCl): - """Fixes for ``cl``.""" +Cl = ClFixHybridPressureCoord diff --git a/esmvalcore/cmor/_fixes/cmip6/bcc_csm2_mr.py b/esmvalcore/cmor/_fixes/cmip6/bcc_csm2_mr.py index f16c188261..2787e282e4 100644 --- a/esmvalcore/cmor/_fixes/cmip6/bcc_csm2_mr.py +++ b/esmvalcore/cmor/_fixes/cmip6/bcc_csm2_mr.py @@ -1,18 +1,15 @@ """Fixes for BCC-CSM2-MR model.""" -from ..cmip5.bcc_csm1_1 import Cl as BaseCl from ..cmip5.bcc_csm1_1 import Tos as BaseTos +from ..common import ClFixHybridPressureCoord -class Cl(BaseCl): - """Fixes for ``cl``.""" +Cl = ClFixHybridPressureCoord -class Cli(Cl): - """Fixes for ``cli``.""" +Cli = ClFixHybridPressureCoord -class Clw(Cl): - """Fixes for ``clw``.""" +Clw = ClFixHybridPressureCoord class Tos(BaseTos): diff --git a/esmvalcore/cmor/_fixes/cmip6/bcc_esm1.py b/esmvalcore/cmor/_fixes/cmip6/bcc_esm1.py index ab2c1849ca..c42746bb92 100644 --- a/esmvalcore/cmor/_fixes/cmip6/bcc_esm1.py +++ b/esmvalcore/cmor/_fixes/cmip6/bcc_esm1.py @@ -1,18 +1,15 @@ """Fixes for BCC-ESM1 model.""" -from ..cmip5.bcc_csm1_1 import Cl as BaseCl from .bcc_csm2_mr import Tos as BaseTos +from ..common import ClFixHybridPressureCoord -class Cl(BaseCl): - """Fixes for ``cl``.""" +Cl = ClFixHybridPressureCoord -class Cli(Cl): - """Fixes for ``cli``.""" +Cli = ClFixHybridPressureCoord -class Clw(Cl): - """Fixes for ``clw``.""" +Clw = ClFixHybridPressureCoord class Tos(BaseTos): diff --git a/esmvalcore/cmor/_fixes/cmip6/cams_csm1_0.py b/esmvalcore/cmor/_fixes/cmip6/cams_csm1_0.py index 5a4bbb0cd8..59f09bcd28 100644 --- a/esmvalcore/cmor/_fixes/cmip6/cams_csm1_0.py +++ b/esmvalcore/cmor/_fixes/cmip6/cams_csm1_0.py @@ -1,14 +1,11 @@ """Fixes for CAMS-CSM1-0 model.""" -from ..cmip5.bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord -class Cl(BaseCl): - """Fixes for ``cl``.""" +Cl = ClFixHybridPressureCoord -class Cli(Cl): - """Fixes for ``cli``.""" +Cli = ClFixHybridPressureCoord -class Clw(Cl): - """Fixes for ``clw``.""" +Clw = ClFixHybridPressureCoord diff --git a/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py b/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py index 812f40e849..1e2762bdd4 100644 --- a/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py +++ b/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1.py @@ -1,12 +1,12 @@ """Fixes for CNRM-CM6-1 model.""" import iris -from ..cmip5.bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord from ..fix import Fix from ..shared import add_aux_coords_from_cubes, get_bounds_cube -class Cl(BaseCl): +class Cl(ClFixHybridPressureCoord): """Fixes for ``cl``.""" def fix_metadata(self, cubes): @@ -73,9 +73,7 @@ def fix_metadata(self, cubes): return iris.cube.CubeList([cube]) -class Cli(Cl): - """Fixes for ``cli``.""" +Cli = Cl -class Clw(Cl): - """Fixes for ``clw``.""" +Clw = Cl diff --git a/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1_hr.py b/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1_hr.py index 8338207e2c..1039b10b7d 100644 --- a/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1_hr.py +++ b/esmvalcore/cmor/_fixes/cmip6/cnrm_cm6_1_hr.py @@ -2,13 +2,10 @@ from .cnrm_cm6_1 import Cl as BaseCl -class Cl(BaseCl): - """Fixes for ``cl``.""" +Cl = BaseCl -class Cli(Cl): - """Fixes for ``cli``.""" +Cli = BaseCl -class Clw(Cl): - """Fixes for ``clw``.""" +Clw = BaseCl diff --git a/esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py b/esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py index 5a3ffbce27..a449b642ef 100644 --- a/esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py +++ b/esmvalcore/cmor/_fixes/cmip6/cnrm_esm2_1.py @@ -3,17 +3,13 @@ from .cnrm_cm6_1 import Clcalipso as BaseClcalipso -class Cl(BaseCl): - """Fixes for ``cl``.""" +Cl = BaseCl -class Clcalipso(BaseClcalipso): - """Fixes for ``clcalipso``.""" +Clcalipso = BaseClcalipso -class Cli(Cl): - """Fixes for ``cli``.""" +Cli = BaseCl -class Clw(Cl): - """Fixes for ``clw``.""" +Clw = BaseCl diff --git a/esmvalcore/cmor/_fixes/cmip6/gfdl_cm4.py b/esmvalcore/cmor/_fixes/cmip6/gfdl_cm4.py index ea3d28c6f5..ce61e7fab6 100644 --- a/esmvalcore/cmor/_fixes/cmip6/gfdl_cm4.py +++ b/esmvalcore/cmor/_fixes/cmip6/gfdl_cm4.py @@ -1,12 +1,12 @@ """Fixes for GFDL-CM4 model.""" import iris -from ..cmip5.bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord from ..fix import Fix from ..shared import add_aux_coords_from_cubes, add_scalar_height_coord -class Cl(BaseCl): +class Cl(ClFixHybridPressureCoord): """Fixes for ``cl``.""" def fix_metadata(self, cubes): @@ -32,12 +32,10 @@ def fix_metadata(self, cubes): return super().fix_metadata(cubes) -class Cli(Cl): - """Fixes for ``cli``.""" +Cli = Cl -class Clw(Cl): - """Fixes for ``clw``.""" +Clw = Cl class Tas(Fix): diff --git a/esmvalcore/cmor/_fixes/cmip6/giss_e2_1_g.py b/esmvalcore/cmor/_fixes/cmip6/giss_e2_1_g.py index 5dcf7298fa..fbab1684bf 100644 --- a/esmvalcore/cmor/_fixes/cmip6/giss_e2_1_g.py +++ b/esmvalcore/cmor/_fixes/cmip6/giss_e2_1_g.py @@ -1,14 +1,11 @@ """Fixes for GISS-E2-1-G model.""" -from ..cmip5.bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord -class Cl(BaseCl): - """Fixes for ``cl``.""" +Cl = ClFixHybridPressureCoord -class Cli(Cl): - """Fixes for ``cli``.""" +Cli = ClFixHybridPressureCoord -class Clw(Cl): - """Fixes for ``clw``.""" +Clw = ClFixHybridPressureCoord diff --git a/esmvalcore/cmor/_fixes/cmip6/giss_e2_1_h.py b/esmvalcore/cmor/_fixes/cmip6/giss_e2_1_h.py index b6e5a23c3a..054a42255f 100644 --- a/esmvalcore/cmor/_fixes/cmip6/giss_e2_1_h.py +++ b/esmvalcore/cmor/_fixes/cmip6/giss_e2_1_h.py @@ -1,6 +1,11 @@ """Fixes for GISS-E2-1-H model.""" -from ..cmip5.bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord -class Cl(BaseCl): - """Fixes for ``cl``.""" +Cl = ClFixHybridPressureCoord + + +Cli = ClFixHybridPressureCoord + + +Clw = ClFixHybridPressureCoord diff --git a/esmvalcore/cmor/_fixes/cmip6/miroc6.py b/esmvalcore/cmor/_fixes/cmip6/miroc6.py index 9ffd9bb17b..cd29695fe3 100644 --- a/esmvalcore/cmor/_fixes/cmip6/miroc6.py +++ b/esmvalcore/cmor/_fixes/cmip6/miroc6.py @@ -1,33 +1,11 @@ """Fixes for MIROC6 model.""" -from ..cmip5.bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord -class Cl(BaseCl): - """Fixes for ``cl``.""" +Cl = ClFixHybridPressureCoord - def fix_metadata(self, cubes): - """Remove attributes from ``Surface Air Pressure`` coordinate. - Parameters - ---------- - cubes : iris.cube.CubeList - Input cubes. +Cli = ClFixHybridPressureCoord - Returns - ------- - iris.cube.CubeList - """ - cubes = super().fix_metadata(cubes) - cube = self.get_cube_from_list(cubes) - coord = cube.coord(long_name='Surface Air Pressure') - coord.attributes = {} - return cubes - - -class Cli(Cl): - """Fixes for ``cli``.""" - - -class Clw(Cl): - """Fixes for ``clw``.""" +Clw = ClFixHybridPressureCoord diff --git a/esmvalcore/cmor/_fixes/cmip6/miroc_es2l.py b/esmvalcore/cmor/_fixes/cmip6/miroc_es2l.py index 7d9b0fe4f5..aa28ae13cc 100644 --- a/esmvalcore/cmor/_fixes/cmip6/miroc_es2l.py +++ b/esmvalcore/cmor/_fixes/cmip6/miroc_es2l.py @@ -1,14 +1,11 @@ """Fixes for MIROC-ES2L model.""" -from ..cmip5.bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord -class Cl(BaseCl): - """Fixes for ``cl``.""" +Cl = ClFixHybridPressureCoord -class Cli(Cl): - """Fixes for ``cli``.""" +Cli = ClFixHybridPressureCoord -class Clw(Cl): - """Fixes for ``clw``.""" +Clw = ClFixHybridPressureCoord diff --git a/esmvalcore/cmor/_fixes/cmip6/mpi_esm1_2_hr.py b/esmvalcore/cmor/_fixes/cmip6/mpi_esm1_2_hr.py index 651b6adaf0..72ac9cd7ae 100644 --- a/esmvalcore/cmor/_fixes/cmip6/mpi_esm1_2_hr.py +++ b/esmvalcore/cmor/_fixes/cmip6/mpi_esm1_2_hr.py @@ -1,19 +1,16 @@ """Fixes for MPI-ESM1-2-HR model.""" -from ..cmip5.bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord from ..fix import Fix from ..shared import add_scalar_height_coord -class Cl(BaseCl): - """Fixes for ``cl``.""" +Cl = ClFixHybridPressureCoord -class Cli(Cl): - """Fixes for ``cli``.""" +Cli = ClFixHybridPressureCoord -class Clw(Cl): - """Fixes for ``clw``.""" +Clw = ClFixHybridPressureCoord class Tas(Fix): diff --git a/esmvalcore/cmor/_fixes/cmip6/mri_esm2_0.py b/esmvalcore/cmor/_fixes/cmip6/mri_esm2_0.py index fca1c45b79..720670b4d5 100644 --- a/esmvalcore/cmor/_fixes/cmip6/mri_esm2_0.py +++ b/esmvalcore/cmor/_fixes/cmip6/mri_esm2_0.py @@ -1,14 +1,11 @@ """Fixes for MRI-ESM2-0 model.""" -from ..cmip5.bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord -class Cl(BaseCl): - """Fixes for ``cl``.""" +Cl = ClFixHybridPressureCoord -class Cli(Cl): - """Fixes for ``cli``.""" +Cli = ClFixHybridPressureCoord -class Clw(Cl): - """Fixes for ``clw``.""" +Clw = ClFixHybridPressureCoord diff --git a/esmvalcore/cmor/_fixes/cmip6/nesm3.py b/esmvalcore/cmor/_fixes/cmip6/nesm3.py index 778313fca9..a7e0a71442 100644 --- a/esmvalcore/cmor/_fixes/cmip6/nesm3.py +++ b/esmvalcore/cmor/_fixes/cmip6/nesm3.py @@ -1,14 +1,11 @@ """Fixes for NESM3 model.""" -from ..cmip5.bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord -class Cl(BaseCl): - """Fixes for ``cl``.""" +Cl = ClFixHybridPressureCoord -class Cli(Cl): - """Fixes for ``cli``.""" +Cli = ClFixHybridPressureCoord -class Clw(Cl): - """Fixes for ``clw``.""" +Clw = ClFixHybridPressureCoord diff --git a/esmvalcore/cmor/_fixes/cmip6/sam0_unicon.py b/esmvalcore/cmor/_fixes/cmip6/sam0_unicon.py index 37e2c27c94..696574b9a4 100644 --- a/esmvalcore/cmor/_fixes/cmip6/sam0_unicon.py +++ b/esmvalcore/cmor/_fixes/cmip6/sam0_unicon.py @@ -1,14 +1,11 @@ """Fixes for SAM0-UNICON model.""" -from ..cmip5.bcc_csm1_1 import Cl as BaseCl +from ..common import ClFixHybridPressureCoord -class Cl(BaseCl): - """Fixes for ``cl``.""" +Cl = ClFixHybridPressureCoord -class Cli(Cl): - """Fixes for ``cli``.""" +Cli = ClFixHybridPressureCoord -class Clw(Cl): - """Fixes for ``clw``.""" +Clw = ClFixHybridPressureCoord diff --git a/esmvalcore/cmor/_fixes/common.py b/esmvalcore/cmor/_fixes/common.py new file mode 100644 index 0000000000..7988395bed --- /dev/null +++ b/esmvalcore/cmor/_fixes/common.py @@ -0,0 +1,64 @@ +"""Common fixes used for multiple datasets.""" +import iris + +from .fix import Fix +from .shared import fix_bounds + + +class ClFixHybridPressureCoord(Fix): + """Fixes for ``cl`` regarding hybrid sigma pressure coordinates.""" + + def fix_metadata(self, cubes): + """Fix hybrid sigma pressure coordinate. + + Parameters + ---------- + cubes : iris.cube.CubeList + Input cubes which need to be fixed. + + Returns + ------- + iris.cube.CubeList + + """ + cl_cube = self.get_cube_from_list(cubes, short_name='cl') + + # Remove all existing aux_factories + for aux_factory in cl_cube.aux_factories: + cl_cube.remove_aux_factory(aux_factory) + + # Fix bounds + coords_to_fix = ['b'] + try: + cl_cube.coord(var_name='a') + coords_to_fix.append('a') + except iris.exceptions.CoordinateNotFoundError: + coords_to_fix.append('ap') + fix_bounds(cl_cube, cubes, coords_to_fix) + + # Fix bounds for ap if only a is given in original file + # This was originally done by iris, but it has to be repeated since + # a has bounds now + ap_coord = cl_cube.coord(var_name='ap') + if ap_coord.bounds is None: + cl_cube.remove_coord(ap_coord) + a_coord = cl_cube.coord(var_name='a') + p0_coord = cl_cube.coord(var_name='p0') + ap_coord = a_coord * p0_coord.points[0] + ap_coord.units = a_coord.units * p0_coord.units + ap_coord.rename('vertical pressure') + ap_coord.var_name = 'ap' + cl_cube.add_aux_coord(ap_coord, cl_cube.coord_dims(a_coord)) + + # Add aux_factory again + pressure_coord_factory = iris.aux_factory.HybridPressureFactory( + delta=ap_coord, + sigma=cl_cube.coord(var_name='b'), + surface_air_pressure=cl_cube.coord(var_name='ps'), + ) + cl_cube.add_aux_factory(pressure_coord_factory) + + # Remove attributes from Surface Air Pressure coordinate + cl_cube.coord(var_name='ps').attributes = {} + + return iris.cube.CubeList([cl_cube]) diff --git a/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1.py b/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1.py index d7b2c0bbeb..93e9912918 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1.py +++ b/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1.py @@ -1,13 +1,11 @@ """Test bcc-csm1-1 fixes.""" -import os import unittest import iris import numpy as np -import pytest -from netCDF4 import Dataset from esmvalcore.cmor._fixes.cmip5.bcc_csm1_1 import Cl, Tos +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor.fix import Fix @@ -17,195 +15,9 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -def create_cl_file_without_ap(dataset): - """Create dataset without vertical auxiliary coordinate ``ap``.""" - dataset.createDimension('time', size=1) - dataset.createDimension('lev', size=2) - dataset.createDimension('lat', size=3) - dataset.createDimension('lon', size=4) - dataset.createDimension('bnds', size=2) - - # Dimensional variables - dataset.createVariable('time', np.float64, dimensions=('time',)) - dataset.createVariable('lev', np.float64, dimensions=('lev',)) - dataset.createVariable('lev_bnds', np.float64, dimensions=('lev', 'bnds')) - dataset.createVariable('lat', np.float64, dimensions=('lat',)) - dataset.createVariable('lon', np.float64, dimensions=('lon',)) - dataset.variables['time'][:] = [0.0] - dataset.variables['time'].standard_name = 'time' - dataset.variables['time'].units = 'days since 6543-2-1' - dataset.variables['lev'][:] = [1.0, 2.0] - dataset.variables['lev'].bounds = 'lev_bnds' - dataset.variables['lev'].standard_name = ( - 'atmosphere_hybrid_sigma_pressure_coordinate') - dataset.variables['lev'].units = '1' - dataset.variables['lev_bnds'][:] = [[0.5, 1.5], [1.5, 3.0]] - dataset.variables['lev_bnds'].standard_name = ( - 'atmosphere_hybrid_sigma_pressure_coordinate') - dataset.variables['lev_bnds'].units = '1' - dataset.variables['lat'][:] = [-30.0, 0.0, 30.0] - dataset.variables['lat'].standard_name = 'latitude' - dataset.variables['lat'].units = 'degrees_north' - dataset.variables['lon'][:] = [30.0, 60.0, 90.0, 120.0] - dataset.variables['lon'].standard_name = 'longitude' - dataset.variables['lon'].units = 'degrees_east' - - # Coordinates for derivation of pressure coordinate - dataset.createVariable('b', np.float64, dimensions=('lev',)) - dataset.createVariable('b_bnds', np.float64, dimensions=('lev', 'bnds')) - dataset.createVariable('ps', np.float64, - dimensions=('time', 'lat', 'lon')) - dataset.variables['b'][:] = [0.0, 1.0] - dataset.variables['b_bnds'][:] = [[-1.0, 0.5], [0.5, 2.0]] - dataset.variables['ps'][:] = np.arange(1 * 3 * 4).reshape(1, 3, 4) - dataset.variables['ps'].standard_name = 'surface_air_pressure' - dataset.variables['ps'].units = 'Pa' - - # Cl variable - dataset.createVariable('cl', np.float32, - dimensions=('time', 'lev', 'lat', 'lon')) - dataset.variables['cl'][:] = np.full((1, 2, 3, 4), 0.0, dtype=np.float32) - dataset.variables['cl'].standard_name = ( - 'cloud_area_fraction_in_atmosphere_layer') - dataset.variables['cl'].units = '%' - - -@pytest.fixture -def cl_file_with_a(tmp_path): - """Create netcdf file with similar issues as ``cl``.""" - nc_path = os.path.join(tmp_path, 'bcc_csm_1_1_cl_a.nc') - dataset = Dataset(nc_path, mode='w') - create_cl_file_without_ap(dataset) - dataset.createVariable('a', np.float64, dimensions=('lev',)) - dataset.createVariable('a_bnds', np.float64, dimensions=('lev', 'bnds')) - dataset.createVariable('p0', np.float64, dimensions=()) - dataset.variables['a'][:] = [1.0, 2.0] - dataset.variables['a_bnds'][:] = [[0.0, 1.5], [1.5, 3.0]] - dataset.variables['p0'][:] = 1.0 - dataset.variables['p0'].units = 'Pa' - dataset.variables['lev'].formula_terms = 'p0: p0 a: a b: b ps: ps' - dataset.variables['lev_bnds'].formula_terms = ( - 'p0: p0 a: a_bnds b: b_bnds ps: ps') - dataset.close() - return nc_path - - -@pytest.fixture -def cl_file_with_ap(tmp_path): - """Create netcdf file with similar issues as ``cl``.""" - nc_path = os.path.join(tmp_path, 'bcc_csm_1_1_cl_ap.nc') - dataset = Dataset(nc_path, mode='w') - create_cl_file_without_ap(dataset) - dataset.createVariable('ap', np.float64, dimensions=('lev',)) - dataset.createVariable('ap_bnds', np.float64, dimensions=('lev', 'bnds')) - dataset.variables['ap'][:] = [1.0, 2.0] - dataset.variables['ap_bnds'][:] = [[0.0, 1.5], [1.5, 3.0]] - dataset.variables['ap'].units = 'Pa' - dataset.variables['lev'].formula_terms = 'ap: ap b: b ps: ps' - dataset.variables['lev_bnds'].formula_terms = ( - 'ap: ap_bnds b: b_bnds ps: ps') - dataset.close() - return nc_path - - -AIR_PRESSURE_POINTS = np.array([[[[1.0, 1.0, 1.0, 1.0], - [1.0, 1.0, 1.0, 1.0], - [1.0, 1.0, 1.0, 1.0]], - [[2.0, 3.0, 4.0, 5.0], - [6.0, 7.0, 8.0, 9.0], - [10.0, 11.0, 12.0, 13.0]]]]) -AIR_PRESSURE_BOUNDS = np.array([[[[[0.0, 1.5], - [-1.0, 2.0], - [-2.0, 2.5], - [-3.0, 3.0]], - [[-4.0, 3.5], - [-5.0, 4.0], - [-6.0, 4.5], - [-7.0, 5.0]], - [[-8.0, 5.5], - [-9.0, 6.0], - [-10.0, 6.5], - [-11.0, 7.0]]], - [[[1.5, 3.0], - [2.0, 5.0], - [2.5, 7.0], - [3.0, 9.0]], - [[3.5, 11.0], - [4.0, 13.0], - [4.5, 15.0], - [5.0, 17.0]], - [[5.5, 19.0], - [6.0, 21.0], - [6.5, 23.0], - [7.0, 25.0]]]]]) - - -def test_cl_fix_metadata_with_a(cl_file_with_a): - """Test ``fix_metadata`` for ``cl``.""" - cubes = iris.load(cl_file_with_a) - - # Raw cubes - assert len(cubes) == 4 - var_names = [cube.var_name for cube in cubes] - assert 'cl' in var_names - assert 'ps' in var_names - assert 'a_bnds' in var_names - assert 'b_bnds' in var_names - - # Raw cl cube - cl_cube = cubes.extract_strict('cloud_area_fraction_in_atmosphere_layer') - air_pressure_coord = cl_cube.coord('air_pressure') - assert air_pressure_coord.points is not None - assert air_pressure_coord.bounds is None - np.testing.assert_allclose(air_pressure_coord.points, AIR_PRESSURE_POINTS) - - # Apply fix - fix = Cl(None) - fixed_cubes = fix.fix_metadata(cubes) - assert len(fixed_cubes) == 1 - fixed_cl_cube = fixed_cubes.extract_strict( - 'cloud_area_fraction_in_atmosphere_layer') - fixed_air_pressure_coord = fixed_cl_cube.coord('air_pressure') - assert fixed_air_pressure_coord.points is not None - assert fixed_air_pressure_coord.bounds is not None - np.testing.assert_allclose(fixed_air_pressure_coord.points, - AIR_PRESSURE_POINTS) - np.testing.assert_allclose(fixed_air_pressure_coord.bounds, - AIR_PRESSURE_BOUNDS) - - -def test_cl_fix_metadata_with_ap(cl_file_with_ap): - """Test ``fix_metadata`` for ``cl``.""" - cubes = iris.load(cl_file_with_ap) - - # Raw cubes - assert len(cubes) == 4 - var_names = [cube.var_name for cube in cubes] - assert 'cl' in var_names - assert 'ps' in var_names - assert 'ap_bnds' in var_names - assert 'b_bnds' in var_names - - # Raw cl cube - cl_cube = cubes.extract_strict('cloud_area_fraction_in_atmosphere_layer') - air_pressure_coord = cl_cube.coord('air_pressure') - assert air_pressure_coord.points is not None - assert air_pressure_coord.bounds is None - np.testing.assert_allclose(air_pressure_coord.points, AIR_PRESSURE_POINTS) - - # Apply fix - fix = Cl(None) - fixed_cubes = fix.fix_metadata(cubes) - assert len(fixed_cubes) == 1 - fixed_cl_cube = fixed_cubes.extract_strict( - 'cloud_area_fraction_in_atmosphere_layer') - fixed_air_pressure_coord = fixed_cl_cube.coord('air_pressure') - assert fixed_air_pressure_coord.points is not None - assert fixed_air_pressure_coord.bounds is not None - np.testing.assert_allclose(fixed_air_pressure_coord.points, - AIR_PRESSURE_POINTS) - np.testing.assert_allclose(fixed_air_pressure_coord.bounds, - AIR_PRESSURE_BOUNDS) +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) class TestTos(unittest.TestCase): diff --git a/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1_m.py b/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1_m.py index ebbdce78f9..e25d62709f 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1_m.py +++ b/tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1_m.py @@ -2,6 +2,7 @@ import unittest from esmvalcore.cmor._fixes.cmip5.bcc_csm1_1_m import Cl, Tos +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix @@ -11,14 +12,9 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip5.bcc_csm1_1_m.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) class TestTos(unittest.TestCase): diff --git a/tests/integration/cmor/_fixes/cmip5/test_canesm2.py b/tests/integration/cmor/_fixes/cmip5/test_canesm2.py index f3d9f024f5..e63ff987b8 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_canesm2.py +++ b/tests/integration/cmor/_fixes/cmip5/test_canesm2.py @@ -5,6 +5,7 @@ from iris.cube import Cube from esmvalcore.cmor._fixes.cmip5.canesm2 import Cl, FgCo2 +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor.fix import Fix @@ -14,14 +15,9 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip5.canesm2.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) class TestCanESM2Fgco2(unittest.TestCase): diff --git a/tests/integration/cmor/_fixes/cmip5/test_ccsm4.py b/tests/integration/cmor/_fixes/cmip5/test_ccsm4.py index 0d46d5e596..853367b7a5 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_ccsm4.py +++ b/tests/integration/cmor/_fixes/cmip5/test_ccsm4.py @@ -6,6 +6,7 @@ from iris.cube import Cube from esmvalcore.cmor._fixes.cmip5.ccsm4 import Cl, Rlut, Rlutcs, So +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor.fix import Fix @@ -15,14 +16,9 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip5.ccsm4.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) class TestsRlut(unittest.TestCase): diff --git a/tests/integration/cmor/_fixes/cmip5/test_csiro_mk3_6_0.py b/tests/integration/cmor/_fixes/cmip5/test_csiro_mk3_6_0.py index e070c65220..7b89ba4ec9 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_csiro_mk3_6_0.py +++ b/tests/integration/cmor/_fixes/cmip5/test_csiro_mk3_6_0.py @@ -1,7 +1,6 @@ """Test fixes for CSIRO-Mk3-6-0.""" -import unittest - from esmvalcore.cmor._fixes.cmip5.csiro_mk3_6_0 import Cl +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix @@ -11,11 +10,6 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip5.csiro_mk3_6_0.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) diff --git a/tests/integration/cmor/_fixes/cmip5/test_giss_e2_h.py b/tests/integration/cmor/_fixes/cmip5/test_giss_e2_h.py index 0f32a68ca0..f667219a6b 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_giss_e2_h.py +++ b/tests/integration/cmor/_fixes/cmip5/test_giss_e2_h.py @@ -1,7 +1,6 @@ """Test fixes for GISS-E2-H.""" -import unittest - from esmvalcore.cmor._fixes.cmip5.giss_e2_h import Cl +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix @@ -11,11 +10,6 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip5.giss_e2_h.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) diff --git a/tests/integration/cmor/_fixes/cmip5/test_giss_e2_r.py b/tests/integration/cmor/_fixes/cmip5/test_giss_e2_r.py index 05610eaf03..f42f98da5c 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_giss_e2_r.py +++ b/tests/integration/cmor/_fixes/cmip5/test_giss_e2_r.py @@ -1,7 +1,6 @@ """Test fixes for GISS-E2-R.""" -import unittest - from esmvalcore.cmor._fixes.cmip5.giss_e2_r import Cl +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix @@ -11,11 +10,6 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip5.giss_e2_r.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) diff --git a/tests/integration/cmor/_fixes/cmip5/test_inmcm4.py b/tests/integration/cmor/_fixes/cmip5/test_inmcm4.py index 322968cf5f..a86c6decad 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_inmcm4.py +++ b/tests/integration/cmor/_fixes/cmip5/test_inmcm4.py @@ -5,6 +5,7 @@ from iris.cube import Cube from esmvalcore.cmor._fixes.cmip5.inmcm4 import Cl, Gpp, Lai, Nbp +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor.fix import Fix @@ -14,14 +15,9 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip5.inmcm4.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) class TestGpp(unittest.TestCase): diff --git a/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_lr.py b/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_lr.py index 0bcf3bd201..c6e9f02b1a 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_lr.py +++ b/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_lr.py @@ -1,7 +1,6 @@ """Test fixes for IPSL-CM5A-LR.""" -import unittest - from esmvalcore.cmor._fixes.cmip5.ipsl_cm5a_lr import Cl +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix @@ -11,11 +10,6 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip5.ipsl_cm5a_lr.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) diff --git a/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_mr.py b/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_mr.py index 48b364c2ec..08ddaec198 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_mr.py +++ b/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5a_mr.py @@ -1,7 +1,6 @@ """Test fixes for IPSL-CM5A-MR.""" -import unittest - from esmvalcore.cmor._fixes.cmip5.ipsl_cm5a_mr import Cl +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix @@ -11,11 +10,6 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip5.ipsl_cm5a_mr.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) diff --git a/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5b_lr.py b/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5b_lr.py index 18dd1ac578..746a83335c 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5b_lr.py +++ b/tests/integration/cmor/_fixes/cmip5/test_ipsl_cm5b_lr.py @@ -1,7 +1,6 @@ """Test fixes for IPSL-CM5B-LR.""" -import unittest - from esmvalcore.cmor._fixes.cmip5.ipsl_cm5b_lr import Cl +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix @@ -11,11 +10,6 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip5.ipsl_cm5b_lr.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) diff --git a/tests/integration/cmor/_fixes/cmip5/test_miroc5.py b/tests/integration/cmor/_fixes/cmip5/test_miroc5.py index 10195f5bfe..8cbf02fad8 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_miroc5.py +++ b/tests/integration/cmor/_fixes/cmip5/test_miroc5.py @@ -6,6 +6,7 @@ from iris.cube import Cube from esmvalcore.cmor._fixes.cmip5.miroc5 import Cl, Hur, Sftof, Tas +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor.fix import Fix @@ -15,14 +16,9 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip5.miroc5.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) def test_get_hur_fix(): diff --git a/tests/integration/cmor/_fixes/cmip5/test_miroc_esm.py b/tests/integration/cmor/_fixes/cmip5/test_miroc_esm.py index 2f49a6bbb0..9a034be48f 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_miroc_esm.py +++ b/tests/integration/cmor/_fixes/cmip5/test_miroc_esm.py @@ -7,6 +7,7 @@ from iris.exceptions import CoordinateNotFoundError from esmvalcore.cmor._fixes.cmip5.miroc_esm import AllVars, Cl, Co2, Tro3 +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor.fix import Fix @@ -16,14 +17,9 @@ def test_get_cl_fix(): assert fix == [Cl(None), AllVars(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip5.miroc_esm.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) class TestCo2(unittest.TestCase): diff --git a/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_lr.py b/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_lr.py index fa07e930d7..93fb6c0e2d 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_lr.py +++ b/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_lr.py @@ -5,6 +5,7 @@ from iris.cube import Cube from esmvalcore.cmor._fixes.cmip5.mpi_esm_lr import Cl, Pctisccp +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor.fix import Fix @@ -14,14 +15,9 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip5.mpi_esm_lr.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) class TestPctisccp2(unittest.TestCase): diff --git a/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_mr.py b/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_mr.py index 0bdeafab3b..715cfcab42 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_mr.py +++ b/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_mr.py @@ -1,7 +1,6 @@ """Test fixes for MPI-ESM-MR.""" -import unittest - from esmvalcore.cmor._fixes.cmip5.mpi_esm_mr import Cl +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix @@ -11,11 +10,6 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip5.mpi_esm_mr.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) diff --git a/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_p.py b/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_p.py index 852070264a..b88f032a43 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_p.py +++ b/tests/integration/cmor/_fixes/cmip5/test_mpi_esm_p.py @@ -1,7 +1,6 @@ """Test fixes for MPI-ESM-P.""" -import unittest - from esmvalcore.cmor._fixes.cmip5.mpi_esm_p import Cl +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix @@ -11,11 +10,6 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip5.mpi_esm_p.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) diff --git a/tests/integration/cmor/_fixes/cmip5/test_mri_cgcm3.py b/tests/integration/cmor/_fixes/cmip5/test_mri_cgcm3.py index 4825150b31..598ceee0db 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_mri_cgcm3.py +++ b/tests/integration/cmor/_fixes/cmip5/test_mri_cgcm3.py @@ -2,6 +2,7 @@ import unittest from esmvalcore.cmor._fixes.cmip5.mri_cgcm3 import Cl, Msftmyz, ThetaO +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor.fix import Fix @@ -11,14 +12,9 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip5.mri_cgcm3.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) class TestMsftmyz(unittest.TestCase): diff --git a/tests/integration/cmor/_fixes/cmip5/test_noresm1_m.py b/tests/integration/cmor/_fixes/cmip5/test_noresm1_m.py index 4e740a44d0..dc1c25b49c 100644 --- a/tests/integration/cmor/_fixes/cmip5/test_noresm1_m.py +++ b/tests/integration/cmor/_fixes/cmip5/test_noresm1_m.py @@ -1,7 +1,6 @@ """Test fixes for NorESM1-M.""" -import unittest - from esmvalcore.cmor._fixes.cmip5.noresm1_m import Cl +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix @@ -11,11 +10,6 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip5.noresm1_m.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) diff --git a/tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py b/tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py index 7394096423..3ce07f65a9 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py +++ b/tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py @@ -4,6 +4,7 @@ import iris from esmvalcore.cmor._fixes.cmip6.bcc_csm2_mr import Cl, Cli, Clw, Tos +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix @@ -13,14 +14,9 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.bcc_csm2_mr.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) def test_get_cli_fix(): @@ -29,14 +25,9 @@ def test_get_cli_fix(): assert fix == [Cli(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.bcc_csm2_mr.Cl.fix_metadata', - autospec=True) -def test_cli_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cli``.""" - fix = Cli(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cli_fix(): + """Test fix for ``cli``.""" + assert Cli(None) == ClFixHybridPressureCoord(None) def test_get_clw_fix(): @@ -45,14 +36,9 @@ def test_get_clw_fix(): assert fix == [Clw(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.bcc_csm2_mr.Cl.fix_metadata', - autospec=True) -def test_clw_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``clw``.""" - fix = Clw(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_clw_fix(): + """Test fix for ``clw``.""" + assert Clw(None) == ClFixHybridPressureCoord(None) def test_get_tos_fix(): diff --git a/tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py b/tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py index 861cef0881..ec2d3475f1 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py +++ b/tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py @@ -2,6 +2,7 @@ import unittest from esmvalcore.cmor._fixes.cmip6.bcc_esm1 import Cl, Cli, Clw, Tos +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix @@ -11,14 +12,9 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.bcc_esm1.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) def test_get_cli_fix(): @@ -27,14 +23,9 @@ def test_get_cli_fix(): assert fix == [Cli(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.bcc_esm1.Cl.fix_metadata', - autospec=True) -def test_cli_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cli``.""" - fix = Cli(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cli_fix(): + """Test fix for ``cli``.""" + assert Cli(None) == ClFixHybridPressureCoord(None) def test_get_clw_fix(): @@ -43,14 +34,9 @@ def test_get_clw_fix(): assert fix == [Clw(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.bcc_esm1.Cl.fix_metadata', - autospec=True) -def test_clw_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``clw``.""" - fix = Clw(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_clw_fix(): + """Test fix for ``clw``.""" + assert Clw(None) == ClFixHybridPressureCoord(None) def test_get_tos_fix(): diff --git a/tests/integration/cmor/_fixes/cmip6/test_cams_csm1_0.py b/tests/integration/cmor/_fixes/cmip6/test_cams_csm1_0.py index 339aa538b1..9bc0f83fec 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_cams_csm1_0.py +++ b/tests/integration/cmor/_fixes/cmip6/test_cams_csm1_0.py @@ -1,7 +1,6 @@ """Test fixes for CAMS-CSM1-0.""" -import unittest - from esmvalcore.cmor._fixes.cmip6.cams_csm1_0 import Cl, Cli, Clw +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix @@ -11,14 +10,9 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.cams_csm1_0.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) def test_get_cli_fix(): @@ -27,14 +21,9 @@ def test_get_cli_fix(): assert fix == [Cli(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.cams_csm1_0.Cl.fix_metadata', - autospec=True) -def test_cli_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cli``.""" - fix = Cli(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cli_fix(): + """Test fix for ``cli``.""" + assert Cli(None) == ClFixHybridPressureCoord(None) def test_get_clw_fix(): @@ -43,11 +32,6 @@ def test_get_clw_fix(): assert fix == [Clw(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.cams_csm1_0.Cl.fix_metadata', - autospec=True) -def test_clw_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``clw``.""" - fix = Clw(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_clw_fix(): + """Test fix for ``clw``.""" + assert Clw(None) == ClFixHybridPressureCoord(None) 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 5460ab8a91..fe14974f7d 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1.py +++ b/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1.py @@ -1,6 +1,5 @@ """Tests for the fixes of CNRM-CM6-1.""" import os -import unittest import iris import numpy as np @@ -181,14 +180,9 @@ def test_get_cli_fix(): assert fix == [Cli(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1.Cl.fix_metadata', - autospec=True) -def test_cli_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cli``.""" - fix = Cli(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cli_fix(): + """Test fix for ``cli``.""" + assert Cli(None) == Cl(None) def test_get_clw_fix(): @@ -197,11 +191,6 @@ def test_get_clw_fix(): assert fix == [Clw(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1.Cl.fix_metadata', - autospec=True) -def test_clw_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``clw``.""" - fix = Clw(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_clw_fix(): + """Test fix for ``clw``.""" + assert Clw(None) == Cl(None) diff --git a/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1_hr.py b/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1_hr.py index 436f4f071e..0ce697764c 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1_hr.py +++ b/tests/integration/cmor/_fixes/cmip6/test_cnrm_cm6_1_hr.py @@ -1,6 +1,5 @@ """Test fixes for CNRM-CM6-1-HR.""" -import unittest - +from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1 import Cl as BaseCl from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1_hr import Cl, Cli, Clw from esmvalcore.cmor._fixes.fix import Fix @@ -11,14 +10,9 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1_hr.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == BaseCl(None) def test_get_cli_fix(): @@ -27,14 +21,9 @@ def test_get_cli_fix(): assert fix == [Cli(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1_hr.Cl.fix_metadata', - autospec=True) -def test_cli_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cli``.""" - fix = Cli(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cli_fix(): + """Test fix for ``cli``.""" + assert Cli(None) == BaseCl(None) def test_get_clw_fix(): @@ -43,11 +32,6 @@ def test_get_clw_fix(): assert fix == [Clw(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1_hr.Cl.fix_metadata', - autospec=True) -def test_clw_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``clw``.""" - fix = Clw(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_clw_fix(): + """Test fix for ``clw``.""" + assert Clw(None) == BaseCl(None) diff --git a/tests/integration/cmor/_fixes/cmip6/test_cnrm_esm2_1.py b/tests/integration/cmor/_fixes/cmip6/test_cnrm_esm2_1.py index 85458d3e4d..592bb508c3 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_cnrm_esm2_1.py +++ b/tests/integration/cmor/_fixes/cmip6/test_cnrm_esm2_1.py @@ -1,6 +1,6 @@ """Test fixes for CNRM-ESM2-1.""" -import unittest - +from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1 import Cl as BaseCl +from esmvalcore.cmor._fixes.cmip6.cnrm_cm6_1 import Clcalipso as BaseClcalipso from esmvalcore.cmor._fixes.cmip6.cnrm_esm2_1 import Cl, Clcalipso, Cli, Clw from esmvalcore.cmor._fixes.fix import Fix @@ -11,14 +11,9 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.cnrm_esm2_1.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == BaseCl(None) def test_get_clcalipso_fix(): @@ -27,14 +22,9 @@ def test_get_clcalipso_fix(): assert fix == [Clcalipso(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.cnrm_esm2_1.BaseClcalipso.fix_metadata', - autospec=True) -def test_clcalipso_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``clcalipso`.""" - fix = Clcalipso(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_clcalipso_fix(): + """Test fix for ``cl``.""" + assert Clcalipso(None) == BaseClcalipso(None) def test_get_cli_fix(): @@ -43,14 +33,9 @@ def test_get_cli_fix(): assert fix == [Cli(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.cnrm_esm2_1.Cl.fix_metadata', - autospec=True) -def test_cli_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cli``.""" - fix = Cli(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cli_fix(): + """Test fix for ``cli``.""" + assert Cli(None) == BaseCl(None) def test_get_clw_fix(): @@ -59,11 +44,6 @@ def test_get_clw_fix(): assert fix == [Clw(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.cnrm_esm2_1.Cl.fix_metadata', - autospec=True) -def test_clw_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``clw``.""" - fix = Clw(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_clw_fix(): + """Test fix for ``clw``.""" + assert Clw(None) == BaseCl(None) diff --git a/tests/integration/cmor/_fixes/cmip6/test_gfdl_cm4.py b/tests/integration/cmor/_fixes/cmip6/test_gfdl_cm4.py index a5b16e3536..2b359df924 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_gfdl_cm4.py +++ b/tests/integration/cmor/_fixes/cmip6/test_gfdl_cm4.py @@ -1,6 +1,5 @@ """Tests for the fixes of GFDL-CM4.""" import os -import unittest import iris import numpy as np @@ -144,14 +143,9 @@ def test_get_cli_fix(): assert fix == [Cli(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.gfdl_cm4.Cl.fix_metadata', - autospec=True) -def test_cli_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cli``.""" - fix = Cli(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cli_fix(): + """Test fix for ``cli``.""" + assert Cli(None) == Cl(None) def test_get_clw_fix(): @@ -160,11 +154,6 @@ def test_get_clw_fix(): assert fix == [Clw(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.gfdl_cm4.Cl.fix_metadata', - autospec=True) -def test_clw_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``clw``.""" - fix = Clw(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_clw_fix(): + """Test fix for ``clw``.""" + assert Clw(None) == Cl(None) diff --git a/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_g.py b/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_g.py index 659cbf80ce..7be6b91faf 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_g.py +++ b/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_g.py @@ -1,7 +1,6 @@ """Test fixes for GISS-E2-1-G.""" -import unittest - from esmvalcore.cmor._fixes.cmip6.giss_e2_1_g import Cl, Cli, Clw +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix @@ -11,14 +10,9 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.giss_e2_1_g.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) def test_get_cli_fix(): @@ -27,14 +21,9 @@ def test_get_cli_fix(): assert fix == [Cli(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.giss_e2_1_g.Cl.fix_metadata', - autospec=True) -def test_cli_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cli``.""" - fix = Cli(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cli_fix(): + """Test fix for ``cli``.""" + assert Cli(None) == ClFixHybridPressureCoord(None) def test_get_clw_fix(): @@ -43,11 +32,6 @@ def test_get_clw_fix(): assert fix == [Clw(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.giss_e2_1_g.Cl.fix_metadata', - autospec=True) -def test_clw_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``clw``.""" - fix = Clw(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_clw_fix(): + """Test fix for ``clw``.""" + assert Clw(None) == ClFixHybridPressureCoord(None) diff --git a/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_h.py b/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_h.py index cf161d4bb2..533ed4658c 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_h.py +++ b/tests/integration/cmor/_fixes/cmip6/test_giss_e2_1_h.py @@ -1,7 +1,6 @@ """Test fixes for GISS-E2-1-H.""" -import unittest - -from esmvalcore.cmor._fixes.cmip6.giss_e2_1_h import Cl +from esmvalcore.cmor._fixes.cmip6.giss_e2_1_h import Cl, Cli, Clw +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix @@ -11,11 +10,28 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.giss_e2_1_h.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) + + +def test_get_cli_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'GISS-E2-1-H', 'Amon', 'cli') + assert fix == [Cli(None)] + + +def test_cli_fix(): + """Test fix for ``cli``.""" + assert Cli(None) == ClFixHybridPressureCoord(None) + + +def test_get_clw_fix(): + """Test getting of fix.""" + fix = Fix.get_fixes('CMIP6', 'GISS-E2-1-H', 'Amon', 'clw') + assert fix == [Clw(None)] + + +def test_clw_fix(): + """Test fix for ``clw``.""" + assert Clw(None) == ClFixHybridPressureCoord(None) diff --git a/tests/integration/cmor/_fixes/cmip6/test_miroc6.py b/tests/integration/cmor/_fixes/cmip6/test_miroc6.py index 643985e6c2..5b52817bbe 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_miroc6.py +++ b/tests/integration/cmor/_fixes/cmip6/test_miroc6.py @@ -1,59 +1,18 @@ """Test fixes for MIROC6.""" -import unittest - -import pytest -from iris.coords import AuxCoord -from iris.cube import Cube, CubeList - from esmvalcore.cmor._fixes.cmip6.miroc6 import Cl, Cli, Clw +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix -@pytest.fixture -def cl_cubes(): - """Cubes for ``cl.``.""" - ps_coord = AuxCoord( - [[1.0]], - var_name='ps', - long_name='Surface Air Pressure', - attributes={'a': 1, 'b': '2'}, - ) - cl_cube = Cube( - [[0.0]], - var_name='cl', - standard_name='cloud_area_fraction_in_atmosphere_layer', - aux_coords_and_dims=[(ps_coord.copy(), (0, 1))], - ) - x_cube = Cube([[0.0]], - long_name='x', - aux_coords_and_dims=[(ps_coord.copy(), (0, 1))]) - cubes = CubeList([cl_cube, x_cube]) - return cubes - - def test_get_cl_fix(): """Test getting of fix.""" fix = Fix.get_fixes('CMIP6', 'MIROC6', 'Amon', 'cl') assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.miroc6.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata, cl_cubes): - """Test ``fix_metadata`` for ``cl``.""" - mock_base_fix_metadata.return_value = cl_cubes - fix = Cl(None) - fixed_cubes = fix.fix_metadata(cl_cubes) - mock_base_fix_metadata.assert_called_once_with(fix, cl_cubes) - assert len(fixed_cubes) == 2 - cl_cube = fixed_cubes.extract_strict( - 'cloud_area_fraction_in_atmosphere_layer') - ps_coord_cl = cl_cube.coord('Surface Air Pressure') - assert not ps_coord_cl.attributes - x_cube = fixed_cubes.extract_strict('x') - ps_coord_x = x_cube.coord('Surface Air Pressure') - assert ps_coord_x.attributes == {'a': 1, 'b': '2'} +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) def test_get_cli_fix(): @@ -62,14 +21,9 @@ def test_get_cli_fix(): assert fix == [Cli(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.miroc6.Cl.fix_metadata', - autospec=True) -def test_cli_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cli``.""" - fix = Cli(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cli_fix(): + """Test fix for ``cli``.""" + assert Cli(None) == ClFixHybridPressureCoord(None) def test_get_clw_fix(): @@ -78,11 +32,6 @@ def test_get_clw_fix(): assert fix == [Clw(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.miroc6.Cl.fix_metadata', - autospec=True) -def test_clw_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``clw``.""" - fix = Clw(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_clw_fix(): + """Test fix for ``clw``.""" + assert Clw(None) == ClFixHybridPressureCoord(None) diff --git a/tests/integration/cmor/_fixes/cmip6/test_miroc_es2l.py b/tests/integration/cmor/_fixes/cmip6/test_miroc_es2l.py index f2ff7dd31b..354ea5d5b5 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_miroc_es2l.py +++ b/tests/integration/cmor/_fixes/cmip6/test_miroc_es2l.py @@ -1,7 +1,6 @@ """Test fixes for MIROC-ES2L.""" -import unittest - from esmvalcore.cmor._fixes.cmip6.miroc_es2l import Cl, Cli, Clw +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix @@ -11,14 +10,9 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.miroc_es2l.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) def test_get_cli_fix(): @@ -27,14 +21,9 @@ def test_get_cli_fix(): assert fix == [Cli(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.miroc_es2l.Cl.fix_metadata', - autospec=True) -def test_cli_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cli``.""" - fix = Cli(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cli_fix(): + """Test fix for ``cli``.""" + assert Cli(None) == ClFixHybridPressureCoord(None) def test_get_clw_fix(): @@ -43,11 +32,6 @@ def test_get_clw_fix(): assert fix == [Clw(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.miroc_es2l.Cl.fix_metadata', - autospec=True) -def test_clw_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``clw``.""" - fix = Clw(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_clw_fix(): + """Test fix for ``clw``.""" + assert Clw(None) == ClFixHybridPressureCoord(None) diff --git a/tests/integration/cmor/_fixes/cmip6/test_mpi_esm1_2_hr.py b/tests/integration/cmor/_fixes/cmip6/test_mpi_esm1_2_hr.py index 943d719fcb..ec0cc5daa0 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_mpi_esm1_2_hr.py +++ b/tests/integration/cmor/_fixes/cmip6/test_mpi_esm1_2_hr.py @@ -1,7 +1,6 @@ """Test fixes for MPI-ESM1-2-HR.""" -import unittest - from esmvalcore.cmor._fixes.cmip6.mpi_esm1_2_hr import Cl, Cli, Clw +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix @@ -11,14 +10,9 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.mpi_esm1_2_hr.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) def test_get_cli_fix(): @@ -27,14 +21,9 @@ def test_get_cli_fix(): assert fix == [Cli(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.mpi_esm1_2_hr.Cl.fix_metadata', - autospec=True) -def test_cli_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cli``.""" - fix = Cli(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cli_fix(): + """Test fix for ``cli``.""" + assert Cli(None) == ClFixHybridPressureCoord(None) def test_get_clw_fix(): @@ -43,11 +32,6 @@ def test_get_clw_fix(): assert fix == [Clw(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.mpi_esm1_2_hr.Cl.fix_metadata', - autospec=True) -def test_clw_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``clw``.""" - fix = Clw(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_clw_fix(): + """Test fix for ``clw``.""" + assert Clw(None) == ClFixHybridPressureCoord(None) diff --git a/tests/integration/cmor/_fixes/cmip6/test_mri_esm2_0.py b/tests/integration/cmor/_fixes/cmip6/test_mri_esm2_0.py index 8d3ab68718..201620fab0 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_mri_esm2_0.py +++ b/tests/integration/cmor/_fixes/cmip6/test_mri_esm2_0.py @@ -1,7 +1,6 @@ """Test fixes for MRI-ESM2-0.""" -import unittest - from esmvalcore.cmor._fixes.cmip6.mri_esm2_0 import Cl, Cli, Clw +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix @@ -11,14 +10,9 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.mri_esm2_0.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) def test_get_cli_fix(): @@ -27,14 +21,9 @@ def test_get_cli_fix(): assert fix == [Cli(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.mri_esm2_0.Cl.fix_metadata', - autospec=True) -def test_cli_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cli``.""" - fix = Cli(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cli_fix(): + """Test fix for ``cli``.""" + assert Cli(None) == ClFixHybridPressureCoord(None) def test_get_clw_fix(): @@ -43,11 +32,6 @@ def test_get_clw_fix(): assert fix == [Clw(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.mri_esm2_0.Cl.fix_metadata', - autospec=True) -def test_clw_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``clw``.""" - fix = Clw(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_clw_fix(): + """Test fix for ``clw``.""" + assert Clw(None) == ClFixHybridPressureCoord(None) diff --git a/tests/integration/cmor/_fixes/cmip6/test_nesm3.py b/tests/integration/cmor/_fixes/cmip6/test_nesm3.py index 23099e5ac0..040dabc9c6 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_nesm3.py +++ b/tests/integration/cmor/_fixes/cmip6/test_nesm3.py @@ -1,7 +1,6 @@ """Test fixes for NESM3.""" -import unittest - from esmvalcore.cmor._fixes.cmip6.nesm3 import Cl, Cli, Clw +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix @@ -11,14 +10,9 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.nesm3.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) def test_get_cli_fix(): @@ -27,14 +21,9 @@ def test_get_cli_fix(): assert fix == [Cli(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.nesm3.Cl.fix_metadata', - autospec=True) -def test_cli_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cli``.""" - fix = Cli(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cli_fix(): + """Test fix for ``cli``.""" + assert Cli(None) == ClFixHybridPressureCoord(None) def test_get_clw_fix(): @@ -43,11 +32,6 @@ def test_get_clw_fix(): assert fix == [Clw(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.nesm3.Cl.fix_metadata', - autospec=True) -def test_clw_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``clw``.""" - fix = Clw(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_clw_fix(): + """Test fix for ``clw``.""" + assert Clw(None) == ClFixHybridPressureCoord(None) diff --git a/tests/integration/cmor/_fixes/cmip6/test_sam0_unicon.py b/tests/integration/cmor/_fixes/cmip6/test_sam0_unicon.py index 60a99426f6..2e2d79df66 100644 --- a/tests/integration/cmor/_fixes/cmip6/test_sam0_unicon.py +++ b/tests/integration/cmor/_fixes/cmip6/test_sam0_unicon.py @@ -1,7 +1,6 @@ """Test fixes for SAM0-UNICON.""" -import unittest - from esmvalcore.cmor._fixes.cmip6.sam0_unicon import Cl, Cli, Clw +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord from esmvalcore.cmor._fixes.fix import Fix @@ -11,14 +10,9 @@ def test_get_cl_fix(): assert fix == [Cl(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.sam0_unicon.BaseCl.fix_metadata', - autospec=True) -def test_cl_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cl``.""" - fix = Cl(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cl_fix(): + """Test fix for ``cl``.""" + assert Cl(None) == ClFixHybridPressureCoord(None) def test_get_cli_fix(): @@ -27,14 +21,9 @@ def test_get_cli_fix(): assert fix == [Cli(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.sam0_unicon.Cl.fix_metadata', - autospec=True) -def test_cli_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``cli``.""" - fix = Cli(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_cli_fix(): + """Test fix for ``cli``.""" + assert Cli(None) == ClFixHybridPressureCoord(None) def test_get_clw_fix(): @@ -43,11 +32,6 @@ def test_get_clw_fix(): assert fix == [Clw(None)] -@unittest.mock.patch( - 'esmvalcore.cmor._fixes.cmip6.sam0_unicon.Cl.fix_metadata', - autospec=True) -def test_clw_fix_metadata(mock_base_fix_metadata): - """Test ``fix_metadata`` for ``clw``.""" - fix = Clw(None) - fix.fix_metadata('cubes') - mock_base_fix_metadata.assert_called_once_with(fix, 'cubes') +def test_clw_fix(): + """Test fix for ``clw``.""" + assert Clw(None) == ClFixHybridPressureCoord(None) diff --git a/tests/integration/cmor/_fixes/test_common.py b/tests/integration/cmor/_fixes/test_common.py new file mode 100644 index 0000000000..115030b538 --- /dev/null +++ b/tests/integration/cmor/_fixes/test_common.py @@ -0,0 +1,213 @@ +"""Test for common fixes used for multiple datasets.""" +import os + +import iris +import numpy as np +import pytest +from netCDF4 import Dataset + +from esmvalcore.cmor._fixes.common import ClFixHybridPressureCoord + + +def create_cl_file_without_ap(dataset): + """Create dataset without vertical auxiliary coordinate ``ap``.""" + dataset.createDimension('time', size=1) + dataset.createDimension('lev', size=2) + dataset.createDimension('lat', size=3) + dataset.createDimension('lon', size=4) + dataset.createDimension('bnds', size=2) + + # Dimensional variables + dataset.createVariable('time', np.float64, dimensions=('time',)) + dataset.createVariable('lev', np.float64, dimensions=('lev',)) + dataset.createVariable('lev_bnds', np.float64, dimensions=('lev', 'bnds')) + dataset.createVariable('lat', np.float64, dimensions=('lat',)) + dataset.createVariable('lon', np.float64, dimensions=('lon',)) + dataset.variables['time'][:] = [0.0] + dataset.variables['time'].standard_name = 'time' + dataset.variables['time'].units = 'days since 6543-2-1' + dataset.variables['lev'][:] = [1.0, 2.0] + dataset.variables['lev'].bounds = 'lev_bnds' + dataset.variables['lev'].standard_name = ( + 'atmosphere_hybrid_sigma_pressure_coordinate') + dataset.variables['lev'].units = '1' + dataset.variables['lev_bnds'][:] = [[0.5, 1.5], [1.5, 3.0]] + dataset.variables['lev_bnds'].standard_name = ( + 'atmosphere_hybrid_sigma_pressure_coordinate') + dataset.variables['lev_bnds'].units = '1' + dataset.variables['lat'][:] = [-30.0, 0.0, 30.0] + dataset.variables['lat'].standard_name = 'latitude' + dataset.variables['lat'].units = 'degrees_north' + dataset.variables['lon'][:] = [30.0, 60.0, 90.0, 120.0] + dataset.variables['lon'].standard_name = 'longitude' + dataset.variables['lon'].units = 'degrees_east' + + # Coordinates for derivation of pressure coordinate + dataset.createVariable('b', np.float64, dimensions=('lev',)) + dataset.createVariable('b_bnds', np.float64, dimensions=('lev', 'bnds')) + dataset.createVariable('ps', np.float64, + dimensions=('time', 'lat', 'lon')) + dataset.variables['b'][:] = [0.0, 1.0] + dataset.variables['b_bnds'][:] = [[-1.0, 0.5], [0.5, 2.0]] + dataset.variables['ps'][:] = np.arange(1 * 3 * 4).reshape(1, 3, 4) + dataset.variables['ps'].standard_name = 'surface_air_pressure' + dataset.variables['ps'].units = 'Pa' + dataset.variables['ps'].additional_attribute = 'xyz' + + # Cl variable + dataset.createVariable('cl', np.float32, + dimensions=('time', 'lev', 'lat', 'lon')) + dataset.variables['cl'][:] = np.full((1, 2, 3, 4), 0.0, dtype=np.float32) + dataset.variables['cl'].standard_name = ( + 'cloud_area_fraction_in_atmosphere_layer') + dataset.variables['cl'].units = '%' + + +@pytest.fixture +def cl_file_with_a(tmp_path): + """Create netcdf file with similar issues as ``cl``.""" + nc_path = os.path.join(tmp_path, 'bcc_csm_1_1_cl_a.nc') + dataset = Dataset(nc_path, mode='w') + create_cl_file_without_ap(dataset) + dataset.createVariable('a', np.float64, dimensions=('lev',)) + dataset.createVariable('a_bnds', np.float64, dimensions=('lev', 'bnds')) + dataset.createVariable('p0', np.float64, dimensions=()) + dataset.variables['a'][:] = [1.0, 2.0] + dataset.variables['a_bnds'][:] = [[0.0, 1.5], [1.5, 3.0]] + dataset.variables['p0'][:] = 1.0 + dataset.variables['p0'].units = 'Pa' + dataset.variables['lev'].formula_terms = 'p0: p0 a: a b: b ps: ps' + dataset.variables['lev_bnds'].formula_terms = ( + 'p0: p0 a: a_bnds b: b_bnds ps: ps') + dataset.close() + return nc_path + + +@pytest.fixture +def cl_file_with_ap(tmp_path): + """Create netcdf file with similar issues as ``cl``.""" + nc_path = os.path.join(tmp_path, 'bcc_csm_1_1_cl_ap.nc') + dataset = Dataset(nc_path, mode='w') + create_cl_file_without_ap(dataset) + dataset.createVariable('ap', np.float64, dimensions=('lev',)) + dataset.createVariable('ap_bnds', np.float64, dimensions=('lev', 'bnds')) + dataset.variables['ap'][:] = [1.0, 2.0] + dataset.variables['ap_bnds'][:] = [[0.0, 1.5], [1.5, 3.0]] + dataset.variables['ap'].units = 'Pa' + dataset.variables['lev'].formula_terms = 'ap: ap b: b ps: ps' + dataset.variables['lev_bnds'].formula_terms = ( + 'ap: ap_bnds b: b_bnds ps: ps') + dataset.close() + return nc_path + + +AIR_PRESSURE_POINTS = np.array([[[[1.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0], + [1.0, 1.0, 1.0, 1.0]], + [[2.0, 3.0, 4.0, 5.0], + [6.0, 7.0, 8.0, 9.0], + [10.0, 11.0, 12.0, 13.0]]]]) +AIR_PRESSURE_BOUNDS = np.array([[[[[0.0, 1.5], + [-1.0, 2.0], + [-2.0, 2.5], + [-3.0, 3.0]], + [[-4.0, 3.5], + [-5.0, 4.0], + [-6.0, 4.5], + [-7.0, 5.0]], + [[-8.0, 5.5], + [-9.0, 6.0], + [-10.0, 6.5], + [-11.0, 7.0]]], + [[[1.5, 3.0], + [2.0, 5.0], + [2.5, 7.0], + [3.0, 9.0]], + [[3.5, 11.0], + [4.0, 13.0], + [4.5, 15.0], + [5.0, 17.0]], + [[5.5, 19.0], + [6.0, 21.0], + [6.5, 23.0], + [7.0, 25.0]]]]]) + + +def test_cl_fix_hybrid_pressure_coord_fix_metadata_with_a(cl_file_with_a): + """Test ``fix_metadata`` for ``cl``.""" + cubes = iris.load(cl_file_with_a) + + # Raw cubes + assert len(cubes) == 4 + var_names = [cube.var_name for cube in cubes] + assert 'cl' in var_names + assert 'ps' in var_names + assert 'a_bnds' in var_names + assert 'b_bnds' in var_names + + # Raw cl cube + cl_cube = cubes.extract_strict('cloud_area_fraction_in_atmosphere_layer') + air_pressure_coord = cl_cube.coord('air_pressure') + assert air_pressure_coord.points is not None + assert air_pressure_coord.bounds is None + np.testing.assert_allclose(air_pressure_coord.points, AIR_PRESSURE_POINTS) + + # Raw ps cube + ps_cube = cubes.extract_strict('surface_air_pressure') + assert ps_cube.attributes == {'additional_attribute': 'xyz'} + + # Apply fix + fix = ClFixHybridPressureCoord(None) + fixed_cubes = fix.fix_metadata(cubes) + assert len(fixed_cubes) == 1 + fixed_cl_cube = fixed_cubes.extract_strict( + 'cloud_area_fraction_in_atmosphere_layer') + fixed_air_pressure_coord = fixed_cl_cube.coord('air_pressure') + assert fixed_air_pressure_coord.points is not None + assert fixed_air_pressure_coord.bounds is not None + np.testing.assert_allclose(fixed_air_pressure_coord.points, + AIR_PRESSURE_POINTS) + np.testing.assert_allclose(fixed_air_pressure_coord.bounds, + AIR_PRESSURE_BOUNDS) + surface_pressure_coord = fixed_cl_cube.coord(var_name='ps') + assert surface_pressure_coord.attributes == {} + + +def test_cl_fix_hybrid_pressure_coord_fix_metadata_with_ap(cl_file_with_ap): + """Test ``fix_metadata`` for ``cl``.""" + cubes = iris.load(cl_file_with_ap) + + # Raw cubes + assert len(cubes) == 4 + var_names = [cube.var_name for cube in cubes] + assert 'cl' in var_names + assert 'ps' in var_names + assert 'ap_bnds' in var_names + assert 'b_bnds' in var_names + + # Raw cl cube + cl_cube = cubes.extract_strict('cloud_area_fraction_in_atmosphere_layer') + air_pressure_coord = cl_cube.coord('air_pressure') + assert air_pressure_coord.points is not None + assert air_pressure_coord.bounds is None + np.testing.assert_allclose(air_pressure_coord.points, AIR_PRESSURE_POINTS) + + # Raw ps cube + ps_cube = cubes.extract_strict('surface_air_pressure') + assert ps_cube.attributes == {'additional_attribute': 'xyz'} + + # Apply fix + fix = ClFixHybridPressureCoord(None) + fixed_cubes = fix.fix_metadata(cubes) + assert len(fixed_cubes) == 1 + fixed_cl_cube = fixed_cubes.extract_strict( + 'cloud_area_fraction_in_atmosphere_layer') + fixed_air_pressure_coord = fixed_cl_cube.coord('air_pressure') + assert fixed_air_pressure_coord.points is not None + assert fixed_air_pressure_coord.bounds is not None + np.testing.assert_allclose(fixed_air_pressure_coord.points, + AIR_PRESSURE_POINTS) + np.testing.assert_allclose(fixed_air_pressure_coord.bounds, + AIR_PRESSURE_BOUNDS) + surface_pressure_coord = fixed_cl_cube.coord(var_name='ps') + assert surface_pressure_coord.attributes == {} From 2980eea26c2eaa5954e85e2e519de1720fa3585d Mon Sep 17 00:00:00 2001 From: Bouwe Andela Date: Thu, 12 Mar 2020 15:54:12 +0100 Subject: [PATCH 4/4] Avoid broken pytest-html plugin --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3850b6f12b..2a68f5c6ef 100755 --- a/setup.py +++ b/setup.py @@ -51,7 +51,7 @@ 'pytest-cov', 'pytest-env', 'pytest-flake8', - 'pytest-html', + 'pytest-html!=2.1.0', 'pytest-metadata>=1.5.1', ], # Development dependencies