Skip to content

Commit

Permalink
Cleanup and simplification from review
Browse files Browse the repository at this point in the history
  • Loading branch information
blimlim committed Aug 30, 2024
1 parent 171b7ff commit 05f35f2
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 31 deletions.
65 changes: 36 additions & 29 deletions test/test_um2netcdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,13 @@ def __init__(self, item_code, var_name=None, attributes=None, units=None):
# tests. Would cause a KeyError anyway?
# self.coord = {}


def coord(self):
raise NotImplementedError(
"coord() method not implemented on DummyCube"
)


def name(self):
# mimic iris API
return self.var_name
Expand Down Expand Up @@ -677,15 +684,17 @@ def DimCoord_maker():
@pytest.fixture
@to_iris_DimCoord
def lat_river_coord():
# iris DimCoord imitating UM V7.3s river grid. Must have length 180.
# iris DimCoord imitating UM V7.3s 1x1 degree river grid.
# Must have length 180.
lat_river_points = np.arange(-90., 90, dtype="float32") + 0.5
return lat_river_points, um2nc.LATITUDE


@pytest.fixture
@to_iris_DimCoord
def lon_river_coord():
# iris DimCoord imitating UM V7.3s river grid. Must have length 90.
# iris DimCoord imitating UM V7.3s 1x1 degree river grid.
# Must have length 360.
lon_river_points = np.arange(0., 360., dtype="float32") + 0.5
return lon_river_points, um2nc.LONGITUDE

Expand All @@ -695,6 +704,8 @@ def lon_river_coord():
def lat_v_nd_coord():
# iris DimCoord imitating the real
# lat_v grid from ESM1.5 (which uses the New Dynamics grid).
# This grid is offset half a grid cell compared to the standard
# New Dynamics latitude grid.
lat_v_points = np.arange(-90.+0.5*D_LAT_N96, 90,
D_LAT_N96, dtype="float32")
return lat_v_points, um2nc.LATITUDE
Expand All @@ -705,6 +716,8 @@ def lat_v_nd_coord():
def lon_u_nd_coord():
# iris DimCoord imitating the real
# lon_u grid from ESM1.5 (which uses the New Dynamics grid).
# This grid is offset half a grid cell compared to the standard
# New Dynamics longitude grid.
lon_u_points = np.arange(0.5*D_LON_N96, 360, D_LON_N96, dtype="float32")
return lon_u_points, um2nc.LONGITUDE

Expand All @@ -714,6 +727,8 @@ def lon_u_nd_coord():
def lat_v_eg_coord():
# iris DimCoord imitating the real
# lat_v grid from CM2 (which uses the End Game grid).
# This grid is offset half a grid cell compared to the standard
# End Game latitude grid.
lat_v_points = np.arange(-90., 91, D_LAT_N96, dtype="float32")
return lat_v_points, um2nc.LATITUDE

Expand All @@ -723,6 +738,8 @@ def lat_v_eg_coord():
def lon_u_eg_coord():
# iris DimCoord imitating the real
# lon_v grid from CM2 (which uses the End Game grid).
# This grid is offset half a grid cell compared to the standard
# New Dynamics longitude grid.
lon_u_points = np.arange(0, 360, D_LON_N96, dtype="float32")
return lon_u_points, um2nc.LONGITUDE

Expand All @@ -732,11 +749,7 @@ def lon_u_eg_coord():
def lat_standard_nd_coord():
# iris DimCoord imitating the standard latitude
# grid from ESM1.5 (which uses the New Dynamics grid).
lat_points_middle = np.arange(-88.75, 89., D_LAT_N96)
lat_points = np.concatenate(([-90],
lat_points_middle,
[90.]
), dtype="float32")
lat_points = np.arange(-90, 91, D_LAT_N96, dtype="float32")
return lat_points, um2nc.LATITUDE


Expand Down Expand Up @@ -771,7 +784,7 @@ def lon_standard_eg_coord():
class DummyCubeWithCoords(DummyCube):
# DummyCube with coordinates, which can be filled with
# iris.DimCoord objects for testing.
def __init__(self, dummy_cube, coords=[]):
def __init__(self, dummy_cube, coords=None):
super().__init__(dummy_cube.item_code,
dummy_cube.var_name,
dummy_cube.attributes,
Expand All @@ -781,15 +794,18 @@ def __init__(self, dummy_cube, coords=[]):
# given by the coordinate names. This ensures that the key used to
# access a coordinate via the coord method matches the
# coordinate's name.
self.coordinate_dict = {}
for coord in coords:
self.coordinate_dict[coord.name()] = coord
if coords is not None:
self.coordinate_dict = {
coord.name(): coord for coord in coords
}
else:
self.coordinate_dict = {}

def coord(self, coordinate_name):
return self.coordinate_dict[coordinate_name]


def assert_unmodified_coordinates(lat_coord, lon_coord):
def assert_coordinates_are_unmodified(lat_coord, lon_coord):
"""
Helper function to check that a coordinate's attributes match
those expected for a coordinate that has not yet been modified
Expand Down Expand Up @@ -826,17 +842,13 @@ def test_fix_latlon_coords_river(ua_plev_cube,
coords=[lat_river_coord, lon_river_coord]
)

# Supplementary grid information used by fix_latlon_coords.
# Grid type doesn't matter for river grid, but must be set.
grid_type = um2nc.GRID_END_GAME

cube_lat_coord = cube_with_river_coords.coord(um2nc.LATITUDE)
cube_lon_coord = cube_with_river_coords.coord(um2nc.LONGITUDE)

# Checks prior to modifications.
assert_unmodified_coordinates(cube_lat_coord, cube_lon_coord)
assert_coordinates_are_unmodified(cube_lat_coord, cube_lon_coord)

um2nc.fix_latlon_coords(cube_with_river_coords, grid_type,
um2nc.fix_latlon_coords(cube_with_river_coords, um2nc.GRID_END_GAME,
D_LAT_N96, D_LON_N96)

# Checks post modifications.
Expand Down Expand Up @@ -870,7 +882,7 @@ def test_fix_latlon_coords_uv(ua_plev_cube,
)

# Checks prior to modifications.
assert_unmodified_coordinates(lat_coordinate, lon_coordinate)
assert_coordinates_are_unmodified(lat_coordinate, lon_coordinate)

um2nc.fix_latlon_coords(cube_with_uv_coords, grid_type,
D_LAT_N96, D_LON_N96)
Expand Down Expand Up @@ -913,7 +925,7 @@ def test_fix_latlon_coords_standard(ua_plev_cube,
)

# Checks prior to modifications.
assert_unmodified_coordinates(lat_coordinate, lon_coordinate)
assert_coordinates_are_unmodified(lat_coordinate, lon_coordinate)

um2nc.fix_latlon_coords(cube_with_uv_coords, grid_type,
D_LAT_N96, D_LON_N96)
Expand All @@ -930,7 +942,6 @@ def test_fix_latlon_coords_single_point(ua_plev_cube):
Test that single point longitude and latitude coordinates
are provided with global bounds.
"""
grid_type = um2nc.GRID_NEW_DYNAMICS

# Expected values after modification
expected_lat_bounds = um2nc.GLOBAL_COORD_BOUNDS[um2nc.LATITUDE]
Expand All @@ -949,7 +960,7 @@ def test_fix_latlon_coords_single_point(ua_plev_cube):
assert not lat_coord_single.has_bounds()
assert not lon_coord_single.has_bounds()

um2nc.fix_latlon_coords(cube_with_uv_coords, grid_type,
um2nc.fix_latlon_coords(cube_with_uv_coords, um2nc.GRID_NEW_DYNAMICS,
D_LAT_N96, D_LON_N96)

assert_has_bounds(lat_coord_single, lon_coord_single)
Expand All @@ -962,8 +973,6 @@ def test_fix_latlon_coords_has_bounds(ua_plev_cube):
Test that existing coordinate bounds are not modified by
fix_latlon_coords.
"""
# Grid information required to run fix_latlon_coords6
grid_type = um2nc.GRID_NEW_DYNAMICS

# Expected values after modification
lon_bounds = np.array([[0, 1]])
Expand All @@ -982,7 +991,7 @@ def test_fix_latlon_coords_has_bounds(ua_plev_cube):
)
assert_has_bounds(lat_coord, lon_coord)

um2nc.fix_latlon_coords(cube_with_uv_coords, grid_type,
um2nc.fix_latlon_coords(cube_with_uv_coords, um2nc.GRID_NEW_DYNAMICS,
D_LAT_N96, D_LON_N96)

assert np.array_equal(lat_coord.bounds, lat_bounds)
Expand All @@ -999,16 +1008,14 @@ def _raise_CoordinateNotFoundError(coord_name):
# Iris cube's ".coord" method
raise iris.exceptions.CoordinateNotFoundError(coord_name)

# Following values don't matter for test. Just needed as arguments
grid_type = um2nc.GRID_NEW_DYNAMICS

# Replace coord method to raise UnsupportedTimeSeriesError
ua_plev_cube.coord = _raise_CoordinateNotFoundError

with (
pytest.raises(um2nc.UnsupportedTimeSeriesError)
):
um2nc.fix_latlon_coords(ua_plev_cube, grid_type, D_LAT_N96, D_LON_N96)
um2nc.fix_latlon_coords(ua_plev_cube, um2nc.GRID_NEW_DYNAMICS,
D_LAT_N96, D_LON_N96)


def test_fix_cell_methods_drop_hours():
Expand Down
4 changes: 2 additions & 2 deletions umpost/um2netcdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,12 +297,12 @@ def fix_latlon_coords(cube, grid_type, dlat, dlon):
try:
latitude_coordinate = cube.coord(LATITUDE)
longitude_coordinate = cube.coord(LONGITUDE)
except iris.exceptions.CoordinateNotFoundError:
except iris.exceptions.CoordinateNotFoundError as CoordError:
msg = (
"Missing latitude or longitude coordinate for variable (possible timeseries?): \n"
f"{cube}\n"
)
raise UnsupportedTimeSeriesError(msg)
raise UnsupportedTimeSeriesError(msg) from CoordError

# Force to double for consistency with CMOR
latitude_coordinate.points = latitude_coordinate.points.astype(np.float64)
Expand Down

0 comments on commit 05f35f2

Please sign in to comment.