Skip to content

Commit b93b706

Browse files
authored
Expand ICON-XPP fix for new model version (#3014)
1 parent 6a382f6 commit b93b706

File tree

5 files changed

+107
-22
lines changed

5 files changed

+107
-22
lines changed

doc/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ Fixes for datasets
201201
- Fix ERA5 native6 fix to handle single monthly-averaged NetCDF files. (:pull:`2512`) by :user:`rbeucher`
202202
- Expand ICON extra facets (:pull:`2965`) by :user:`schlunma`
203203
- Copy fixes for obs4MIPs dataset SSMI RSSv07r00 to RSS-v7 (:pull:`2968`) by :user:`bouweandela`
204+
- Expand ICON-XPP fix for new model version (:pull:`3014`) by :user:`schlunma`
204205

205206
Installation
206207
~~~~~~~~~~~~

esmvalcore/cmor/_fixes/icon/_base_fixes.py

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -584,13 +584,8 @@ def fix_metadata(self, cubes: CubeList) -> CubeList:
584584
if cube.coords(long_name="depth_below_sea"):
585585
self._fix_depth(cube, cubes)
586586

587-
# Remove undesired lev coordinate with length 1
588-
lev_coord = DimCoord(0.0, var_name="lev")
589-
if cube.coords(lev_coord, dim_coords=True):
590-
slices = [slice(None)] * cube.ndim
591-
slices[cube.coord_dims(lev_coord)[0]] = 0
592-
cube = cube[tuple(slices)]
593-
cube.remove_coord(lev_coord)
587+
# Remove undesired coordinates of length 1
588+
cube = self._remove_undesired_coords(cube)
594589

595590
# Fix latitude
596591
if self.vardef.has_coord_with_standard_name("latitude"):
@@ -795,19 +790,33 @@ def _fix_height(self, cube: Cube, cubes: CubeList) -> Cube:
795790

796791
def _fix_depth(self, cube: Cube, cubes: CubeList) -> None:
797792
"""Fix ocean depth coordinate."""
798-
depth_coord = self.fix_depth_coord_metadata(cube)
793+
depth_coord = cube.coord(long_name="depth_below_sea")
794+
depth_var_name = depth_coord.var_name
795+
depth_coord = self.fix_depth_coord_metadata(cube, coord=depth_coord)
799796
if depth_coord.has_bounds():
800797
return
801798

802-
# Try to get bounds of depth coordinate from depth_2 coordinate that
803-
# might be present in other variables loaded from the same file
799+
# ICON ocean output usually consists of two different depth
800+
# coordinates, "depth" and "depth_2". One of them is the depth at the
801+
# cell centers, the other at the cell boundaries. The nomenclature is
802+
# not consistent on which coordinate is which. Thus, if possible, we
803+
# try to add the cell boundaries from the other depth coordinate here
804+
# (potentially available from other variables loaded from same file).
805+
if depth_var_name == "depth":
806+
other_depth_var_name = "depth_2"
807+
else:
808+
other_depth_var_name = "depth"
804809
for other_cube in cubes:
805-
if not other_cube.coords(var_name="depth_2"):
810+
if not other_cube.coords(var_name=other_depth_var_name):
806811
continue
807-
depth_2_coord = other_cube.coord(var_name="depth_2")
812+
depth_2_coord = other_cube.coord(var_name=other_depth_var_name)
808813
depth_2_coord.convert_units(depth_coord.units)
809814
bounds = depth_2_coord.core_points()
810-
depth_coord.bounds = np.stack((bounds[:-1], bounds[1:]), axis=-1)
815+
if bounds.shape == (depth_coord.shape[0] + 1,):
816+
depth_coord.bounds = np.stack(
817+
(bounds[:-1], bounds[1:]),
818+
axis=-1,
819+
)
811820

812821
def _fix_lat(self, cube: Cube) -> tuple[int, ...]:
813822
"""Fix latitude coordinate of cube."""
@@ -1121,6 +1130,20 @@ def _fix_invalid_time_units(time_coord: Coord) -> None:
11211130
time_coord.points = new_dt_points
11221131
time_coord.units = new_t_units
11231132

1133+
def _remove_undesired_coords(self, cube: Cube) -> Cube:
1134+
"""Remove undesired coordinates of length 1 from cube."""
1135+
coords_to_remove = [
1136+
DimCoord(0.0, var_name="lev"),
1137+
DimCoord(0.0, var_name="ice_class"),
1138+
]
1139+
for coord in coords_to_remove:
1140+
if cube.coords(coord, dim_coords=True):
1141+
slices: list[int | slice] = [slice(None)] * cube.ndim
1142+
slices[cube.coord_dims(coord)[0]] = 0
1143+
cube = cube[tuple(slices)]
1144+
cube.remove_coord(coord)
1145+
return cube
1146+
11241147

11251148
class NegateData(IconFix):
11261149
"""Base fix to negate data."""

esmvalcore/config/configurations/defaults/extra_facets_icon.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,11 +138,12 @@ projects:
138138
siconc: {raw_name: conc, var_type: oce_ice, raw_units: '1'}
139139
siconca: {raw_name: fr_seaice, var_type: atm_2d_ml}
140140
sithick: {raw_name: hi, var_type: oce_ice}
141+
sos: {var_type: oce_def, raw_units: "0.001"}
141142
tos: {raw_name: t_seasfc, var_type: atm_2d_ml}
142143
zos: {raw_name: zos, var_type: oce_dbg}
143144

144145
# 3D ocean variables
145-
so: {raw_name: so, var_type: oce_def, raw_units: "0.001"}
146+
so: {var_type: oce_def, raw_units: "0.001"}
146147
thetao: {raw_name: to, var_type: oce_def, raw_units: degC}
147148
uo: {raw_name: u, var_type: oce_def}
148149
vo: {raw_name: v, var_type: oce_def}

tests/integration/cmor/_fixes/icon/test_icon_xpp.py

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -929,7 +929,8 @@ def test_rtmt_fix(cubes_regular_grid):
929929
np.testing.assert_allclose(cube.data, [[[0.0, 2.0], [4.0, 6.0]]])
930930

931931

932-
# Test siconc (for extra_facets, removal of lev coord and typesi coordinate)
932+
# Test siconc (for extra_facets, removal of lev/ice_class coord and typesi
933+
# coordinate)
933934

934935

935936
def test_get_siconc_fix():
@@ -939,18 +940,19 @@ def test_get_siconc_fix():
939940

940941

941942
@pytest.mark.online
942-
def test_siconc_fix(cubes_ocean_3d, session):
943+
@pytest.mark.parametrize("z_coord_name", ["lev", "ice_class"])
944+
def test_siconc_fix(z_coord_name, cubes_ocean_3d, session):
943945
"""Test fix."""
944946
cubes = CubeList(
945947
[cubes_ocean_3d.extract_cube(NameConstraint(var_name="to")).copy()],
946948
)
947949
cubes[0].var_name = "conc"
948950
cubes[0].units = None
949951

950-
# Add lev coord to test removal of it
952+
# Add Z-coord to test removal of it
951953
cubes[0] = cubes[0][:, [0], :]
952954
cubes[0].remove_coord("depth")
953-
cubes[0].add_dim_coord(DimCoord(0.0, var_name="lev"), 1)
955+
cubes[0].add_dim_coord(DimCoord(0.0, var_name=z_coord_name), 1)
954956

955957
fix = get_allvars_fix("SImon", "siconc", session=session)
956958
fixed_cubes = fix.fix_metadata(cubes)
@@ -965,7 +967,7 @@ def test_siconc_fix(cubes_ocean_3d, session):
965967
check_typesi(cube)
966968

967969
assert cube.shape == (1, 8)
968-
assert not cube.coords(var_name="lev")
970+
assert not cube.coords(var_name=z_coord_name)
969971

970972
assert cube.dtype == np.float32
971973
np.testing.assert_allclose(
@@ -1109,6 +1111,34 @@ def test_thetao_fix(cubes_ocean_3d, session):
11091111
assert cube.shape == (1, 47, 8)
11101112

11111113

1114+
@pytest.mark.online
1115+
def test_thetao_fix_switched_depth_coords(cubes_ocean_3d, session):
1116+
"""Test fix."""
1117+
to_cube = cubes_ocean_3d.extract_cube(NameConstraint(var_name="to"))
1118+
w_cube = cubes_ocean_3d.extract_cube(NameConstraint(var_name="w"))
1119+
to_cube.coord("depth").var_name = "depth_2"
1120+
w_cube.coord("depth").var_name = "depth"
1121+
cubes = CubeList([to_cube, w_cube])
1122+
1123+
fix = get_allvars_fix("Omon", "thetao", session=session)
1124+
1125+
fixed_cubes = fix.fix_metadata(cubes)
1126+
1127+
assert len(fixed_cubes) == 1
1128+
cube = fixed_cubes[0]
1129+
assert cube.var_name == "thetao"
1130+
assert cube.standard_name == "sea_water_potential_temperature"
1131+
assert cube.long_name == "Sea Water Potential Temperature"
1132+
assert cube.units == "degC"
1133+
assert "positive" not in cube.attributes
1134+
1135+
depth_coord = cube.coord("depth")
1136+
assert depth_coord.has_bounds()
1137+
1138+
assert cube.dtype == np.float32
1139+
assert cube.shape == (1, 47, 8)
1140+
1141+
11121142
@pytest.mark.online
11131143
def test_thetao_fix_already_bounds(cubes_ocean_3d, session):
11141144
"""Test fix."""
@@ -1164,6 +1194,36 @@ def test_thetao_fix_no_bounds(cubes_ocean_3d, session):
11641194
assert cube.shape == (1, 47, 8)
11651195

11661196

1197+
@pytest.mark.online
1198+
def test_thetao_fix_no_bounds_invalid_depth_2(cubes_ocean_3d, session):
1199+
"""Test fix."""
1200+
to_cube = cubes_ocean_3d.extract_cube(NameConstraint(var_name="to"))
1201+
w_cube = cubes_ocean_3d.extract_cube(NameConstraint(var_name="w"))[
1202+
:,
1203+
:40,
1204+
:,
1205+
]
1206+
cubes = CubeList([to_cube, w_cube])
1207+
1208+
fix = get_allvars_fix("Omon", "thetao", session=session)
1209+
1210+
fixed_cubes = fix.fix_metadata(cubes)
1211+
1212+
assert len(fixed_cubes) == 1
1213+
cube = fixed_cubes[0]
1214+
assert cube.var_name == "thetao"
1215+
assert cube.standard_name == "sea_water_potential_temperature"
1216+
assert cube.long_name == "Sea Water Potential Temperature"
1217+
assert cube.units == "degC"
1218+
assert "positive" not in cube.attributes
1219+
1220+
depth_coord = cube.coord("depth")
1221+
assert not depth_coord.has_bounds()
1222+
1223+
assert cube.dtype == np.float32
1224+
assert cube.shape == (1, 47, 8)
1225+
1226+
11671227
# Test zg (for extra fix)
11681228

11691229

tests/unit/test_dataset.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2242,15 +2242,15 @@ def test_get_extra_facets_icon_xpp():
22422242
dataset = Dataset(
22432243
project="ICON",
22442244
mip="Omon",
2245-
short_name="so",
2245+
short_name="thetao",
22462246
dataset="ICON-XPP",
22472247
)
22482248

22492249
extra_facets = dataset._get_extra_facets()
22502250

22512251
assert extra_facets == {
2252-
"raw_name": "so",
2253-
"raw_units": "0.001",
2252+
"raw_name": "to",
2253+
"raw_units": "degC",
22542254
"var_type": "oce_def",
22552255
}
22562256

0 commit comments

Comments
 (0)