Skip to content

Commit 66dd7bd

Browse files
authored
Fix erroneous use of grid_latitude and grid_longitude and cleaned ocean grid fixes (#1092)
* Fixed ocean grid fix * Adapted points and bounds of index coordinates * Generalized retrieving of coordinate dimensions for 1D index coordinates * Added warning if data for fix is not 3D and added comment with link to CF conventions * Extended comment in OceanFixGrid
1 parent 4e9eb33 commit 66dd7bd

12 files changed

+429
-355
lines changed
+2-63
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,7 @@
11
"""Fixes for bcc-csm1-1."""
2-
import numpy as np
3-
from scipy.interpolate import InterpolatedUnivariateSpline
4-
from scipy.ndimage import map_coordinates
5-
6-
from ..common import ClFixHybridPressureCoord
7-
from ..fix import Fix
8-
2+
from ..common import ClFixHybridPressureCoord, OceanFixGrid
93

104
Cl = ClFixHybridPressureCoord
115

126

13-
class Tos(Fix):
14-
"""Fixes for tos."""
15-
16-
def fix_data(self, cube):
17-
"""Fix data.
18-
19-
Calculate missing latitude/longitude boundaries using interpolation.
20-
21-
Parameters
22-
----------
23-
cube: iris.cube.Cube
24-
Input cube to fix.
25-
26-
Returns
27-
-------
28-
iris.cube.Cube
29-
30-
"""
31-
rlat = cube.coord('grid_latitude').points
32-
rlon = cube.coord('grid_longitude').points
33-
34-
# Transform grid latitude/longitude to array indices [0, 1, 2, ...]
35-
rlat_to_idx = InterpolatedUnivariateSpline(rlat,
36-
np.arange(len(rlat)),
37-
k=1)
38-
rlon_to_idx = InterpolatedUnivariateSpline(rlon,
39-
np.arange(len(rlon)),
40-
k=1)
41-
rlat_idx_bnds = rlat_to_idx(cube.coord('grid_latitude').bounds)
42-
rlon_idx_bnds = rlon_to_idx(cube.coord('grid_longitude').bounds)
43-
44-
# Calculate latitude/longitude vertices by interpolation
45-
lat_vertices = []
46-
lon_vertices = []
47-
for (i, j) in [(0, 0), (0, 1), (1, 1), (1, 0)]:
48-
(rlat_v, rlon_v) = np.meshgrid(rlat_idx_bnds[:, i],
49-
rlon_idx_bnds[:, j],
50-
indexing='ij')
51-
lat_vertices.append(
52-
map_coordinates(cube.coord('latitude').points,
53-
[rlat_v, rlon_v],
54-
mode='nearest'))
55-
lon_vertices.append(
56-
map_coordinates(cube.coord('longitude').points,
57-
[rlat_v, rlon_v],
58-
mode='wrap'))
59-
lat_vertices = np.array(lat_vertices)
60-
lon_vertices = np.array(lon_vertices)
61-
lat_vertices = np.moveaxis(lat_vertices, 0, -1)
62-
lon_vertices = np.moveaxis(lon_vertices, 0, -1)
63-
64-
# Copy vertices to cube
65-
cube.coord('latitude').bounds = lat_vertices
66-
cube.coord('longitude').bounds = lon_vertices
67-
68-
return cube
7+
Tos = OceanFixGrid
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
"""Fixes for bcc-csm1-1-m."""
2-
from ..common import ClFixHybridPressureCoord
3-
from .bcc_csm1_1 import Tos as BaseTos
4-
2+
from ..common import ClFixHybridPressureCoord, OceanFixGrid
53

64
Cl = ClFixHybridPressureCoord
75

86

9-
Tos = BaseTos
7+
Tos = OceanFixGrid
+3-55
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
"""Fixes for BCC-CSM2-MR model."""
2-
from ..cmip5.bcc_csm1_1 import Tos as BaseTos
3-
from ..common import ClFixHybridPressureCoord
4-
2+
from ..common import ClFixHybridPressureCoord, OceanFixGrid
53

64
Cl = ClFixHybridPressureCoord
75

@@ -12,57 +10,7 @@
1210
Clw = ClFixHybridPressureCoord
1311

1412

15-
class Tos(BaseTos):
16-
"""Fixes for tos."""
17-
18-
def fix_metadata(self, cubes):
19-
"""Rename ``var_name`` of 1D-``latitude`` and 1D-``longitude``.
20-
Parameters
21-
----------
22-
cubes : iris.cube.CubeList
23-
Input cubes.
24-
Returns
25-
-------
26-
iris.cube.CubeList
27-
"""
28-
cube = self.get_cube_from_list(cubes)
29-
lat_coord = cube.coord('latitude', dimensions=(1, ))
30-
lon_coord = cube.coord('longitude', dimensions=(2, ))
31-
lat_coord.standard_name = None
32-
lat_coord.long_name = 'grid_latitude'
33-
lat_coord.var_name = 'i'
34-
lat_coord.units = '1'
35-
lon_coord.standard_name = None
36-
lon_coord.long_name = 'grid_longitude'
37-
lon_coord.var_name = 'j'
38-
lon_coord.units = '1'
39-
lon_coord.circular = False
40-
return cubes
41-
13+
Tos = OceanFixGrid
4214

43-
class Siconc(BaseTos):
44-
"""Fixes for siconc."""
4515

46-
def fix_metadata(self, cubes):
47-
"""Rename ``var_name`` of 1D-``latitude`` and 1D-``longitude``.
48-
Parameters
49-
----------
50-
cubes : iris.cube.CubeList
51-
Input cubes.
52-
Returns
53-
-------
54-
iris.cube.CubeList
55-
"""
56-
cube = self.get_cube_from_list(cubes)
57-
lat_coord = cube.coord('latitude', dimensions=(1, ))
58-
lon_coord = cube.coord('longitude', dimensions=(2, ))
59-
lat_coord.standard_name = None
60-
lat_coord.long_name = 'grid_latitude'
61-
lat_coord.var_name = 'i'
62-
lat_coord.units = '1'
63-
lon_coord.standard_name = None
64-
lon_coord.long_name = 'grid_longitude'
65-
lon_coord.var_name = 'j'
66-
lon_coord.units = '1'
67-
lon_coord.circular = False
68-
return cubes
16+
Siconc = OceanFixGrid
+2-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
"""Fixes for BCC-ESM1 model."""
2-
from ..common import ClFixHybridPressureCoord
3-
from .bcc_csm2_mr import Tos as BaseTos
4-
2+
from ..common import ClFixHybridPressureCoord, OceanFixGrid
53

64
Cl = ClFixHybridPressureCoord
75

@@ -12,4 +10,4 @@
1210
Clw = ClFixHybridPressureCoord
1311

1412

15-
Tos = BaseTos
13+
Tos = OceanFixGrid

esmvalcore/cmor/_fixes/cmip6/fgoals_g3.py

+26-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from ..cmip5.fgoals_g2 import Cl as BaseCl
33
from ..common import OceanFixGrid
44

5-
65
Cl = BaseCl
76

87

@@ -12,7 +11,31 @@
1211
Clw = BaseCl
1312

1413

15-
Tos = OceanFixGrid
14+
class Tos(OceanFixGrid):
15+
"""Fixes for tos."""
16+
17+
def fix_metadata(self, cubes):
18+
"""Fix metadata.
19+
20+
FGOALS-g3 data contain latitude and longitude data set to >1e30 in some
21+
places.
22+
23+
Parameters
24+
----------
25+
cubes : iris.cube.CubeList
26+
Input cubes.
27+
28+
Returns
29+
-------
30+
iris.cube.CubeList
31+
32+
"""
33+
cube = self.get_cube_from_list(cubes)
34+
cube.coord('latitude').points[
35+
cube.coord('latitude').points > 1000.0] = 0.0
36+
cube.coord('longitude').points[
37+
cube.coord('longitude').points > 1000.0] = 0.0
38+
return super().fix_metadata(cubes)
1639

1740

18-
Siconc = OceanFixGrid
41+
Siconc = Tos

esmvalcore/cmor/_fixes/common.py

+59-64
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
"""Common fixes used for multiple datasets."""
2+
import logging
3+
24
import iris
35
import numpy as np
46
from scipy.ndimage import map_coordinates
57

68
from .fix import Fix
79
from .shared import add_plev_from_altitude, fix_bounds
810

11+
logger = logging.getLogger(__name__)
12+
913

1014
class ClFixHybridHeightCoord(Fix):
1115
"""Fixes for ``cl`` regarding hybrid sigma height coordinates."""
@@ -108,49 +112,74 @@ def fix_metadata(self, cubes):
108112
class OceanFixGrid(Fix):
109113
"""Fixes for tos, siconc in FGOALS-g3."""
110114

111-
def fix_data(self, cube):
112-
"""
113-
Fix data.
114-
115-
Calculate missing latitude/longitude boundaries using interpolation.
116-
Based on a similar fix for BCC-CSM2-MR.
115+
def fix_metadata(self, cubes):
116+
"""Fix ``latitude`` and ``longitude`` (metadata and bounds).
117117
118118
Parameters
119119
----------
120-
cube: iris.cube.Cube
121-
Input cube to fix.
120+
cubes : iris.cube.CubeList
121+
Input cubes.
122122
123123
Returns
124124
-------
125-
iris.cube.Cube
125+
iris.cube.CubeList
126+
126127
"""
127-
rlat = cube.coord('grid_latitude').points
128-
rlon = cube.coord('grid_longitude').points
129-
130-
# Guess coordinate bounds in rlat, rlon (following BCC-CSM2-MR-1).
131-
rlat_idx_bnds = np.zeros((len(rlat), 2))
132-
rlat_idx_bnds[:, 0] = np.arange(len(rlat)) - 0.5
133-
rlat_idx_bnds[:, 1] = np.arange(len(rlat)) + 0.5
134-
rlat_idx_bnds[0, 0] = 0.
135-
rlat_idx_bnds[len(rlat) - 1, 1] = len(rlat)
136-
rlon_idx_bnds = np.zeros((len(rlon), 2))
137-
rlon_idx_bnds[:, 0] = np.arange(len(rlon)) - 0.5
138-
rlon_idx_bnds[:, 1] = np.arange(len(rlon)) + 0.5
139-
140-
# Calculate latitude/longitude vertices by interpolation
128+
cube = self.get_cube_from_list(cubes)
129+
if cube.ndim != 3:
130+
logger.warning(
131+
"OceanFixGrid is designed to work on any data with an "
132+
"irregular ocean grid, but it was only tested on 3D (time, "
133+
"latitude, longitude) data so far; got %dD data", cube.ndim)
134+
135+
# Get dimensional coordinates. Note:
136+
# - First dimension i -> X-direction (= longitude)
137+
# - Second dimension j -> Y-direction (= latitude)
138+
(j_dim, i_dim) = sorted(set(
139+
cube.coord_dims(cube.coord('latitude', dim_coords=False)) +
140+
cube.coord_dims(cube.coord('longitude', dim_coords=False))
141+
))
142+
i_coord = cube.coord(dim_coords=True, dimensions=i_dim)
143+
j_coord = cube.coord(dim_coords=True, dimensions=j_dim)
144+
145+
# Fix metadata of coordinate i
146+
i_coord.var_name = 'i'
147+
i_coord.standard_name = None
148+
i_coord.long_name = 'cell index along first dimension'
149+
i_coord.units = '1'
150+
i_coord.circular = False
151+
152+
# Fix metadata of coordinate j
153+
j_coord.var_name = 'j'
154+
j_coord.standard_name = None
155+
j_coord.long_name = 'cell index along second dimension'
156+
j_coord.units = '1'
157+
158+
# Fix points and bounds of index coordinates i and j
159+
for idx_coord in (i_coord, j_coord):
160+
idx_coord.points = np.arange(len(idx_coord.points))
161+
idx_coord.bounds = None
162+
idx_coord.guess_bounds()
163+
164+
# Calculate latitude/longitude vertices by interpolation.
165+
# Following the CF conventions (see
166+
# cfconventions.org/cf-conventions/cf-conventions.html#cell-boundaries)
167+
# we go counter-clockwise around the cells and construct a grid of
168+
# index values which are in turn used to interpolate longitudes and
169+
# latitudes in the midpoints between the cell centers.
141170
lat_vertices = []
142171
lon_vertices = []
143-
for (i, j) in [(0, 0), (0, 1), (1, 1), (1, 0)]:
144-
(rlat_v, rlon_v) = np.meshgrid(rlat_idx_bnds[:, i],
145-
rlon_idx_bnds[:, j],
146-
indexing='ij')
172+
for (j, i) in [(0, 0), (0, 1), (1, 1), (1, 0)]:
173+
(j_v, i_v) = np.meshgrid(j_coord.bounds[:, j],
174+
i_coord.bounds[:, i],
175+
indexing='ij')
147176
lat_vertices.append(
148177
map_coordinates(cube.coord('latitude').points,
149-
[rlat_v, rlon_v],
178+
[j_v, i_v],
150179
mode='nearest'))
151180
lon_vertices.append(
152181
map_coordinates(cube.coord('longitude').points,
153-
[rlat_v, rlon_v],
182+
[j_v, i_v],
154183
mode='wrap'))
155184
lat_vertices = np.array(lat_vertices)
156185
lon_vertices = np.array(lon_vertices)
@@ -160,39 +189,5 @@ def fix_data(self, cube):
160189
# Copy vertices to cube
161190
cube.coord('latitude').bounds = lat_vertices
162191
cube.coord('longitude').bounds = lon_vertices
163-
return cube
164-
165-
def fix_metadata(self, cubes):
166-
"""
167-
Rename ``var_name`` of 1D-``latitude`` and 1D-``longitude``.
168-
169-
Parameters
170-
----------
171-
cubes : iris.cube.CubeList
172-
Input cubes.
173192

174-
Returns
175-
-------
176-
iris.cube.CubeList
177-
"""
178-
cube = self.get_cube_from_list(cubes)
179-
lat_coord = cube.coord('cell index along second dimension',
180-
dimensions=(1, ))
181-
lon_coord = cube.coord('cell index along first dimension',
182-
dimensions=(2, ))
183-
lat_coord.standard_name = None
184-
lat_coord.long_name = 'grid_latitude'
185-
lat_coord.var_name = 'i'
186-
lat_coord.units = '1'
187-
lon_coord.standard_name = None
188-
lon_coord.long_name = 'grid_longitude'
189-
lon_coord.var_name = 'j'
190-
lon_coord.units = '1'
191-
lon_coord.circular = False
192-
# FGOALS-g3 data contain latitude and longitude data set to
193-
# >1e30 in some places. Set to 0. to avoid problem in check.py.
194-
cube.coord('latitude').points[cube.coord('latitude').points > 1000.]\
195-
= 0.
196-
cube.coord('longitude').points[cube.coord('longitude').points > 1000.]\
197-
= 0.
198-
return cubes
193+
return iris.cube.CubeList([cube])

0 commit comments

Comments
 (0)