diff --git a/esmvalcore/cmor/check.py b/esmvalcore/cmor/check.py index cbab6d420b..52c5d19806 100644 --- a/esmvalcore/cmor/check.py +++ b/esmvalcore/cmor/check.py @@ -332,9 +332,10 @@ def _check_multiple_coords_same_stdname(self): def _check_dim_names(self): """Check dimension names.""" - for (_, coordinate) in self._cmor_var.coordinates.items(): + cmor_var_coordinates = self._cmor_var.coordinates.copy() + for (key, coordinate) in cmor_var_coordinates.items(): if coordinate.generic_level: - continue + self._check_generic_level_dim_names(key, coordinate) else: try: cube_coord = self._cube.coord(var_name=coordinate.out_name) @@ -383,11 +384,59 @@ def _check_dim_names(self): self.report_error( self._does_msg, coordinate.name, 'exist') + def _check_generic_level_dim_names(self, key, coordinate): + if coordinate.generic_lev_coords: + for coord in coordinate.generic_lev_coords.values(): + try: + cube_coord = self._cube.coord( + var_name=coord.out_name + ) + coordinate.out_name = coord.out_name + if cube_coord.standard_name == coord.standard_name: + coordinate.standard_name = coord.standard_name + coordinate.name = coord.name + except iris.exceptions.CoordinateNotFoundError: + try: + cube_coord = self._cube.coord( + var_name=coord.standard_name + ) + coordinate.standard_name = coord.standard_name + coordinate.name = coord.name + except iris.exceptions.CoordinateNotFoundError: + pass + if coordinate.standard_name: + if not coordinate.out_name: + self.report_error( + f'Coordinate {coordinate.name} ' + 'has wrong var_name.', + ) + level = coordinate.generic_lev_coords[coordinate.name] + level.generic_level = True + level.generic_lev_coords = self._cmor_var.coordinates[ + key].generic_lev_coords + self._cmor_var.coordinates[key] = level + self.report_debug_message( + f'Generic level coordinate {key} ' + 'will be checked against ' + f'{coordinate.name} coordinate information' + ) + else: + if coordinate.out_name: + self.report_critical( + f'Coordinate {coordinate.name} ' + 'has wrong standard_name ' + 'or is not set.', + ) + else: + self.report_critical( + self._does_msg, coordinate.name, 'exist' + ) + def _check_coords(self): """Check coordinates.""" for coordinate in self._cmor_var.coordinates.values(): # Cannot check generic_level coords as no CMOR information - if coordinate.generic_level: + if coordinate.generic_level and not coordinate.out_name: continue var_name = coordinate.out_name diff --git a/esmvalcore/cmor/table.py b/esmvalcore/cmor/table.py index bba016f4d0..ca77c01d1e 100644 --- a/esmvalcore/cmor/table.py +++ b/esmvalcore/cmor/table.py @@ -325,7 +325,10 @@ def _assign_dimensions(self, var, generic_levels): if dimension in generic_levels: coord = CoordinateInfo(dimension) coord.generic_level = True - coord.axis = 'Z' + for name in self.coords: + generic_level = self.coords[name].generic_lev_name + if dimension in [generic_level]: + coord.generic_lev_coords[name] = self.coords[name] else: try: coord = self.coords[dimension] @@ -577,6 +580,7 @@ def __init__(self, name): super(CoordinateInfo, self).__init__() self.name = name self.generic_level = False + self.generic_lev_coords = {} self.axis = "" """Axis""" @@ -606,6 +610,8 @@ def __init__(self, name): """Maximum allowed value""" self.must_have_bounds = "" """Whether bounds are required on this dimension""" + self.generic_lev_name = "" + """Generic level name""" def read_json(self, json_data): """ @@ -634,6 +640,7 @@ def read_json(self, json_data): self.valid_max = self._read_json_variable('valid_max') self.requested = self._read_json_list_variable('requested') self.must_have_bounds = self._read_json_variable('must_have_bounds') + self.generic_lev_name = self._read_json_variable('generic_level_name') class CMIP5Info(InfoBase): diff --git a/tests/unit/cmor/test_cmor_check.py b/tests/unit/cmor/test_cmor_check.py index 63006a7782..045fc5c22d 100644 --- a/tests/unit/cmor/test_cmor_check.py +++ b/tests/unit/cmor/test_cmor_check.py @@ -73,6 +73,8 @@ def __init__(self, name): self.stored_direction = "increasing" self.must_have_bounds = "yes" self.requested = [] + self.generic_lev_coords = {} + self.generic_lev_name = "" valid_limits = {'lat': ('-90', '90'), 'lon': ('0', '360')} if name in valid_limits: @@ -292,6 +294,20 @@ def test_rank_unstructured_grid(self): self.cube.add_aux_coord(new_lat, 1) self._check_cube() + def test_bad_generic_level(self): + """Test check fails in metadata if generic level coord + has wrong var_name.""" + depth_coord = CoordinateInfoMock('depth') + depth_coord.axis = 'Z' + depth_coord.generic_lev_name = 'olevel' + depth_coord.out_name = 'lev' + depth_coord.name = 'depth_coord' + depth_coord.long_name = 'ocean depth coordinate' + self.var_info.coordinates['depth'].generic_lev_coords = { + 'depth_coord': depth_coord} + self.var_info.coordinates['depth'].out_name = "" + self._check_fails_in_metadata() + def test_check_bad_var_standard_name_strict_flag(self): """Test check fails for a bad variable standard_name with --cmor-check strict."""