Skip to content

Commit 4103064

Browse files
committed
Fix unstructured mesh handling
1 parent d756912 commit 4103064

2 files changed

Lines changed: 105 additions & 0 deletions

File tree

esmvalcore/cmor/_fixes/cmip6/icon_esm_lr.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,76 @@
11
"""Fixes for ICON-ESM-LR model."""
22

3+
from iris.mesh import MeshXY
4+
35
from esmvalcore.cmor._fixes.fix import Fix
6+
from esmvalcore.iris_helpers import has_unstructured_grid
47

58

69
class AllVars(Fix):
710
"""Fixes for all variables."""
811

12+
@staticmethod
13+
def _add_mesh_from_vertices(cube, lat_vertices, lon_vertices):
14+
"""Add Iris mesh from cell vertices if this is an unstructured grid."""
15+
# skip if the mesh topology is already provided
16+
if cube.mesh is not None:
17+
return False
18+
# only create meshes for 1D unstructured grids.
19+
if not has_unstructured_grid(cube):
20+
return False
21+
22+
lat = cube.coord("latitude")
23+
lon = cube.coord("longitude")
24+
lat_dims = cube.coord_dims(lat)
25+
lon_dims = cube.coord_dims(lon)
26+
27+
# check the dims
28+
if lat_dims != lon_dims or len(lat_dims) != 1:
29+
return False
30+
31+
# at least triangular cell bounds
32+
if (
33+
lat_vertices.shape != lon_vertices.shape
34+
or lat_vertices.ndim != 2
35+
or lat_vertices.shape[0] != lat.shape[0]
36+
or lat_vertices.shape[1] < 3
37+
):
38+
return False
39+
40+
lat.bounds = lat_vertices
41+
lon.bounds = lon_vertices
42+
43+
mesh = MeshXY.from_coords(lon, lat)
44+
# replace aux coords with mesh aware ones
45+
cube.remove_coord("latitude")
46+
cube.remove_coord("longitude")
47+
for mesh_coord in mesh.to_MeshCoords("face"):
48+
cube.add_aux_coord(mesh_coord, lat_dims)
49+
50+
return True
51+
52+
@classmethod
53+
def _add_mesh_from_coord_bounds(cls, cube):
54+
"""Add Iris mesh from existing coordinate bounds if possible."""
55+
if not cube.coords("latitude") or not cube.coords("longitude"):
56+
return False
57+
58+
lat = cube.coord("latitude")
59+
lon = cube.coord("longitude")
60+
if not lat.has_bounds() or not lon.has_bounds():
61+
return False
62+
63+
return cls._add_mesh_from_vertices(
64+
cube,
65+
lat.core_bounds(),
66+
lon.core_bounds(),
67+
)
68+
69+
def fix_data(self, cube):
70+
"""Add Iris mesh for unstructured data if possible."""
71+
self._add_mesh_from_coord_bounds(cube)
72+
return cube
73+
974
def fix_metadata(self, cubes):
1075
"""Rename ``var_name`` of latitude and longitude.
1176

tests/integration/cmor/_fixes/cmip6/test_icon_esm_lr.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,43 @@ def test_allvars_fix_metadata_no_lat_lon(cubes):
9595
fix = AllVars(None)
9696
out_cubes = fix.fix_metadata(cubes)
9797
assert cubes is out_cubes
98+
99+
100+
def test_allvars_fix_data_adds_mesh_from_bounds():
101+
"""Test ``fix_metadata`` adds a mesh for unstructured ICON data."""
102+
lat_coord = AuxCoord(
103+
[0.0, 1.0],
104+
bounds=[[0.0, 0.0, 1.0], [0.0, 1.0, 1.0]],
105+
var_name="latitude",
106+
standard_name="latitude",
107+
units="degrees_north",
108+
)
109+
lon_coord = AuxCoord(
110+
[0.0, 1.0],
111+
bounds=[[0.0, 1.0, 0.0], [1.0, 1.0, 2.0]],
112+
var_name="longitude",
113+
standard_name="longitude",
114+
units="degrees_east",
115+
)
116+
cube = Cube(
117+
[10.0, 20.0],
118+
var_name="tas",
119+
aux_coords_and_dims=[(lat_coord, 0), (lon_coord, 0)],
120+
attributes={"grid_type": "unstructured"},
121+
)
122+
cubes = CubeList([cube])
123+
fix = AllVars(None)
124+
125+
out_cubes = fix.fix_metadata(cubes)
126+
127+
assert cubes is out_cubes
128+
assert cube.mesh is None
129+
assert cube.coord("latitude").var_name == "lat"
130+
assert cube.coord("longitude").var_name == "lon"
131+
132+
out_cube = fix.fix_data(cube)
133+
134+
assert out_cube is cube
135+
assert cube.mesh is not None
136+
assert cube.coord("latitude").mesh is cube.mesh
137+
assert cube.coord("longitude").mesh is cube.mesh

0 commit comments

Comments
 (0)