Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 16 additions & 12 deletions esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def fix_data(self, cube):
Parameters
----------
cube: iris.cube.Cube
Input cube to fix.

Returns
-------
Expand All @@ -27,27 +28,30 @@ def fix_data(self, cube):
rlon = cube.coord('grid_longitude').points

# Transform grid latitude/longitude to array indices [0, 1, 2, ...]
rlat_to_idx = InterpolatedUnivariateSpline(
rlat, np.arange(len(rlat)), k=1)
rlon_to_idx = InterpolatedUnivariateSpline(
rlon, np.arange(len(rlon)), k=1)
rlat_to_idx = InterpolatedUnivariateSpline(rlat,
np.arange(len(rlat)),
k=1)
rlon_to_idx = InterpolatedUnivariateSpline(rlon,
np.arange(len(rlon)),
k=1)
rlat_idx_bnds = rlat_to_idx(cube.coord('grid_latitude').bounds)
rlon_idx_bnds = rlon_to_idx(cube.coord('grid_longitude').bounds)

# Calculate latitude/longitude vertices by interpolation
lat_vertices = []
lon_vertices = []
for (i, j) in [(0, 0), (0, 1), (1, 1), (1, 0)]:
(rlat_v, rlon_v) = np.meshgrid(
rlat_idx_bnds[:, i], rlon_idx_bnds[:, j], indexing='ij')
(rlat_v, rlon_v) = np.meshgrid(rlat_idx_bnds[:, i],
rlon_idx_bnds[:, j],
indexing='ij')
lat_vertices.append(
map_coordinates(
cube.coord('latitude').points, [rlat_v, rlon_v],
mode='nearest'))
map_coordinates(cube.coord('latitude').points,
[rlat_v, rlon_v],
mode='nearest'))
lon_vertices.append(
map_coordinates(
cube.coord('longitude').points, [rlat_v, rlon_v],
mode='wrap'))
map_coordinates(cube.coord('longitude').points,
[rlat_v, rlon_v],
mode='wrap'))
lat_vertices = np.array(lat_vertices)
lon_vertices = np.array(lon_vertices)
lat_vertices = np.moveaxis(lat_vertices, 0, -1)
Expand Down
59 changes: 2 additions & 57 deletions esmvalcore/cmor/_fixes/cmip5/bcc_csm1_1_m.py
Original file line number Diff line number Diff line change
@@ -1,61 +1,6 @@

"""Fixes for bcc-csm1-1-m."""
import numpy as np
from scipy.interpolate import InterpolatedUnivariateSpline
from scipy.ndimage import map_coordinates

from ..fix import Fix
from .bcc_csm1_1 import Tos as BaseTos


class Tos(Fix):
class Tos(BaseTos):
"""Fixes for tos."""

def fix_data(self, cube):
"""Fix data.

Calculate missing latitude/longitude boundaries using interpolation.

Parameters
----------
cube: iris.cube.Cube

Returns
-------
iris.cube.Cube

"""
rlat = cube.coord('grid_latitude').points
rlon = cube.coord('grid_longitude').points

# Transform grid latitude/longitude to array indices [0, 1, 2, ...]
rlat_to_idx = InterpolatedUnivariateSpline(
rlat, np.arange(len(rlat)), k=1)
rlon_to_idx = InterpolatedUnivariateSpline(
rlon, np.arange(len(rlon)), k=1)
rlat_idx_bnds = rlat_to_idx(cube.coord('grid_latitude').bounds)
rlon_idx_bnds = rlon_to_idx(cube.coord('grid_longitude').bounds)

# Calculate latitude/longitude vertices by interpolation
lat_vertices = []
lon_vertices = []
for (i, j) in [(0, 0), (0, 1), (1, 1), (1, 0)]:
(rlat_v, rlon_v) = np.meshgrid(
rlat_idx_bnds[:, i], rlon_idx_bnds[:, j], indexing='ij')
lat_vertices.append(
map_coordinates(
cube.coord('latitude').points, [rlat_v, rlon_v],
mode='nearest'))
lon_vertices.append(
map_coordinates(
cube.coord('longitude').points, [rlat_v, rlon_v],
mode='wrap'))
lat_vertices = np.array(lat_vertices)
lon_vertices = np.array(lon_vertices)
lat_vertices = np.moveaxis(lat_vertices, 0, -1)
lon_vertices = np.moveaxis(lon_vertices, 0, -1)

# Copy vertices to cube
cube.coord('latitude').bounds = lat_vertices
cube.coord('longitude').bounds = lon_vertices

return cube
33 changes: 33 additions & 0 deletions esmvalcore/cmor/_fixes/cmip6/bcc_csm2_mr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""Fixes for BCC-CSM2-MR model."""
from ..cmip5.bcc_csm1_1 import Tos as BaseTos


class Tos(BaseTos):
"""Fixes for tos."""

def fix_metadata(self, cubes):
"""Rename ``var_name`` of 1D-``latitude`` and 1D-``longitude``.

Parameters
----------
cubes : iris.cube.CubeList
Input cubes.

Returns
-------
iris.cube.CubeList

"""
cube = self.get_cube_from_list(cubes)
lat_coord = cube.coord('latitude', dimensions=(1, ))
lon_coord = cube.coord('longitude', dimensions=(2, ))
lat_coord.standard_name = None
lat_coord.long_name = 'grid_latitude'
lat_coord.var_name = 'i'
lat_coord.units = '1'
lon_coord.standard_name = None
lon_coord.long_name = 'grid_longitude'
lon_coord.var_name = 'j'
lon_coord.units = '1'
lon_coord.circular = False
return cubes
6 changes: 6 additions & 0 deletions esmvalcore/cmor/_fixes/cmip6/bcc_esm1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""Fixes for BCC-ESM1 model."""
from .bcc_csm2_mr import Tos as BaseTos


class Tos(BaseTos):
"""Fixes for tos."""
68 changes: 67 additions & 1 deletion tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,79 @@
"""Test Access1-0 fixes."""
import unittest

import iris
import numpy as np

from esmvalcore.cmor.fix import Fix
from esmvalcore.cmor._fixes.cmip5.bcc_csm1_1 import Tos


class TestTos(unittest.TestCase):
"""Test tos fixes."""

def test_get(self):
"""Test fix get"""
"""Test fix get."""
self.assertListEqual(
Fix.get_fixes('CMIP5', 'BCC-CSM1-1', 'Amon', 'tos'), [Tos(None)])


def test_tos_fix_data():
"""Test ``fix_data`` for ``tos``."""
grid_lat = iris.coords.DimCoord(
[20.0, 40.0],
bounds=[[10.0, 30.0], [30.0, 50.0]],
var_name='rlat',
standard_name='grid_latitude',
)
grid_lon = iris.coords.DimCoord(
[10.0, 20.0, 30.0],
bounds=[[5.0, 15.0], [15.0, 25.0], [25.0, 35.0]],
var_name='rlon',
standard_name='grid_longitude',
)
latitude = iris.coords.AuxCoord(
[[-40.0, -20.0, 0.0], [-20.0, 0.0, 20.0]],
var_name='lat',
standard_name='latitude',
units='degrees_north',
)
longitude = iris.coords.AuxCoord(
[[100.0, 140.0, 180.0], [80.0, 100.0, 120.0]],
var_name='lon',
standard_name='longitude',
units='degrees_east',
)

# Create cube without bounds
cube = iris.cube.Cube(
np.full((2, 3), 300.0),
var_name='tos',
units='K',
dim_coords_and_dims=[(grid_lat, 0), (grid_lon, 1)],
aux_coords_and_dims=[(latitude, (0, 1)), (longitude, (0, 1))],
)
assert cube.coord('latitude').bounds is None
assert cube.coord('longitude').bounds is None

# Apply fix
fix = Tos(None)
fixed_cube = fix.fix_data(cube)
assert fixed_cube is cube
assert fixed_cube.coord('latitude').bounds is not None
assert fixed_cube.coord('longitude').bounds is not None
latitude_bounds = np.array([[[-40, -33.75, -23.75, -30.0],
[-33.75, -6.25, 3.75, -23.75],
[-6.25, -1.02418074021670e-14, 10.0, 3.75]],
[[-30.0, -23.75, -13.75, -20.0],
[-23.75, 3.75, 13.75, -13.75],
[3.75, 10.0, 20.0, 13.75]]])
np.testing.assert_allclose(fixed_cube.coord('latitude').bounds,
latitude_bounds)
longitude_bounds = np.array([[[140.625, 99.375, 99.375, 140.625],
[99.375, 140.625, 140.625, 99.375],
[140.625, 99.375, 99.375, 140.625]],
[[140.625, 99.375, 99.375, 140.625],
[99.375, 140.625, 140.625, 99.375],
[140.625, 99.375, 99.375, 140.625]]])
np.testing.assert_allclose(fixed_cube.coord('longitude').bounds,
longitude_bounds)
15 changes: 13 additions & 2 deletions tests/integration/cmor/_fixes/cmip5/test_bcc_csm1_1_m.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,18 @@

class TestTos(unittest.TestCase):
"""Test tos fixes."""

def test_get(self):
"""Test fix get"""
"""Test fix get."""
self.assertListEqual(
Fix.get_fixes('CMIP5', 'BCC-CSM1-1-M', 'Amon', 'tos'), [Tos(None)])
Fix.get_fixes('CMIP5', 'bcc-csm1-1-m', 'Amon', 'tos'), [Tos(None)])


@unittest.mock.patch(
'esmvalcore.cmor._fixes.cmip5.bcc_csm1_1_m.BaseTos.fix_data',
autospec=True)
def test_tos_fix_data(mock_base_fix_data):
"""Test ``fix_data`` for ``tos``."""
fix = Tos(None)
fix.fix_data('cubes')
mock_base_fix_data.assert_called_once_with(fix, 'cubes')
81 changes: 81 additions & 0 deletions tests/integration/cmor/_fixes/cmip6/test_bcc_csm2_mr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
"""Test fixes for BCC-CSM2-MR."""
import unittest

import iris

from esmvalcore.cmor._fixes.cmip6.bcc_csm2_mr import Tos
from esmvalcore.cmor._fixes.fix import Fix


def test_get_tos_fix():
"""Test getting of fix."""
fix = Fix.get_fixes('CMIP6', 'BCC-CSM2-MR', 'Omon', 'tos')
assert fix == [Tos(None)]


@unittest.mock.patch(
'esmvalcore.cmor._fixes.cmip6.bcc_csm2_mr.BaseTos.fix_data',
autospec=True)
def test_tos_fix_data(mock_base_fix_data):
"""Test ``fix_data`` for ``tos``."""
fix = Tos(None)
fix.fix_data('cubes')
mock_base_fix_data.assert_called_once_with(fix, 'cubes')


def test_tos_fix_metadata():
"""Test ``fix_metadata`` for ``tos``."""
grid_lat = iris.coords.DimCoord([1.0],
var_name='lat',
standard_name='latitude',
long_name='latitude',
units='degrees_north',
attributes={'1D': '1'})
grid_lon = iris.coords.DimCoord([1.0],
var_name='lon',
standard_name='longitude',
long_name='longitude',
units='degrees_east',
circular=True,
attributes={'1D': '1'})
latitude = iris.coords.AuxCoord([[0.0]],
var_name='lat',
standard_name='latitude',
long_name='latitude',
units='degrees_north')
longitude = iris.coords.AuxCoord([[0]],
var_name='lon',
standard_name='longitude',
long_name='longitude',
units='degrees_east')
cube = iris.cube.Cube(
[[[0.0]]],
var_name='tos',
long_name='sea_surface_temperature',
dim_coords_and_dims=[(grid_lat.copy(), 1), (grid_lon.copy(), 2)],
aux_coords_and_dims=[(latitude.copy(), (1, 2)),
(longitude.copy(), (1, 2))],
)
cubes = iris.cube.CubeList([cube, iris.cube.Cube(0.0)])
fix = Tos(None)
fixed_cubes = fix.fix_metadata(cubes)
tos_cube = fixed_cubes.extract_strict('sea_surface_temperature')

# No duplicates anymore
assert len(tos_cube.coords('latitude')) == 1
assert len(tos_cube.coords('longitude')) == 1

# Latitude
grid_lat = tos_cube.coord('grid_latitude')
assert grid_lat.var_name == 'i'
assert grid_lat.long_name == 'grid_latitude'
assert grid_lat.standard_name is None
assert grid_lat.units == '1'

# Longitude
grid_lon = tos_cube.coord('grid_longitude')
assert grid_lon.var_name == 'j'
assert grid_lon.long_name == 'grid_longitude'
assert grid_lon.standard_name is None
assert grid_lon.units == '1'
assert not grid_lon.circular
21 changes: 21 additions & 0 deletions tests/integration/cmor/_fixes/cmip6/test_bcc_esm1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""Test fixes for BCC-ESM1."""
import unittest

from esmvalcore.cmor._fixes.cmip6.bcc_esm1 import Tos
from esmvalcore.cmor._fixes.fix import Fix


def test_get_tos_fix():
"""Test getting of fix."""
fix = Fix.get_fixes('CMIP6', 'BCC-ESM1', 'Omon', 'tos')
assert fix == [Tos(None)]


@unittest.mock.patch(
'esmvalcore.cmor._fixes.cmip6.bcc_esm1.BaseTos.fix_metadata',
autospec=True)
def test_tos_fix_metadata(mock_base_fix_metadata):
"""Test ``fix_metadata`` for ``tos``."""
fix = Tos(None)
fix.fix_metadata('cubes')
mock_base_fix_metadata.assert_called_once_with(fix, 'cubes')