Skip to content
Merged
3 changes: 3 additions & 0 deletions docs/src/whatsnew/latest.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ This document explains the changes made to Iris for this release
#. `@rcomer`_ enabled partial collapse of multi-dimensional string coordinates,
fixing :issue:`3653`. (:pull:`5955`)

#. `@ESadek-MO`_ has updated :mod:`iris.fileformats.pp_save_rules` to set
`pp.lbelv` of surface fields to 9999. (:issue:`3280`, :pull:`5734`)


💣 Incompatible Changes
=======================
Expand Down
17 changes: 11 additions & 6 deletions lib/iris/fileformats/pp.py
Original file line number Diff line number Diff line change
Expand Up @@ -2156,7 +2156,7 @@ def _load_cubes_variable_loader(
return result


def save(cube, target, append=False, field_coords=None):
def save(cube, target, append=False, field_coords=None, label_surface_fields=False):
"""Use the PP saving rules (and any user rules) to save a cube to a PP file.

Parameters
Expand Down Expand Up @@ -2185,11 +2185,11 @@ def save(cube, target, append=False, field_coords=None):
of cubes to be saved to a PP file.

"""
fields = as_fields(cube, field_coords)
fields = as_fields(cube, field_coords, label_surface_fields=label_surface_fields)
save_fields(fields, target, append=append)


def save_pairs_from_cube(cube, field_coords=None):
def save_pairs_from_cube(cube, field_coords=None, label_surface_fields=False):
"""Use the PP saving rules to generate (2D cube, PP field) pairs from a cube.

Parameters
Expand Down Expand Up @@ -2301,12 +2301,12 @@ def save_pairs_from_cube(cube, field_coords=None):

# Run the PP save rules on the slice2D, to fill the PPField,
# recording the rules that were used
pp_field = verify(slice2D, pp_field)
pp_field = verify(slice2D, pp_field, label_surface_fields=label_surface_fields)

yield (slice2D, pp_field)


def as_fields(cube, field_coords=None):
def as_fields(cube, field_coords=None, label_surface_fields=False):
"""Use the PP saving rules to convert a cube to an iterable of PP fields.

Use the PP saving rules (and any user rules) to convert a cube to
Expand All @@ -2322,7 +2322,12 @@ def as_fields(cube, field_coords=None):
If None, the final two dimensions are chosen for slicing.

"""
return (field for _, field in save_pairs_from_cube(cube, field_coords=field_coords))
return (
field
for _, field in save_pairs_from_cube(
cube, field_coords=field_coords, label_surface_fields=label_surface_fields
)
)


def save_fields(fields, target, append: bool = False):
Expand Down
22 changes: 19 additions & 3 deletions lib/iris/fileformats/pp_save_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,7 @@ def _lbproc_rules(cube, pp):
return pp


def _vertical_rules(cube, pp):
def _vertical_rules(cube, pp, label_surface_fields=False):
"""Rule for setting vertical levels for the PP field.

Parameters
Expand Down Expand Up @@ -773,6 +773,22 @@ def _vertical_rules(cube, pp):
pp.brsvd[0] = depth_coord.bounds[0, 0]
pp.brlev = depth_coord.bounds[0, 1]

# Surface field.
if (
height_coord is None
and depth_coord is None
and pressure_coord is None
and soil_mln_coord is None
and apt_coord is None
and air_pres_coord is None
and level_height_coord is None
and mln_coord is None
and sigma_coord is None
and label_surface_fields
):
pp.lbvc = 129
pp.lblev = 9999

# Single potential-temperature level.
if (
apt_coord is not None
Expand Down Expand Up @@ -883,7 +899,7 @@ def _all_other_rules(cube, pp):
return pp


def verify(cube, field):
def verify(cube, field, label_surface_fields=False):
# Rules functions.
field = _basic_coord_system_rules(cube, field)
field = _um_version_rules(cube, field)
Expand All @@ -893,7 +909,7 @@ def verify(cube, field):
field = _grid_and_pole_rules(cube, field)
field = _non_std_cross_section_rules(cube, field)
field = _lbproc_rules(cube, field)
field = _vertical_rules(cube, field)
field = _vertical_rules(cube, field, label_surface_fields=label_surface_fields)
field = _all_other_rules(cube, field)

return field
Expand Down
25 changes: 25 additions & 0 deletions lib/iris/tests/test_cube_to_pp.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,31 @@ def test_lbvc(self):
self.assertEqual(field.lblev, lblev)
self.assertEqual(field.blev, blev)

def test_surface_field(self):
def setup_cube(coord=None):
cube = stock.lat_lon_cube()
if coord:
cube.add_aux_coord(coord)
temp_pp_path = iris.util.create_temp_filename(".pp")
iris.fileformats.pp.save(
cube, target=temp_pp_path, label_surface_fields=True
)
cube = iris.fileformats.pp.load(temp_pp_path)
return cube

# check surface fields are correctly applied
cube = setup_cube()
for field in cube:
self.assertEqual(field.lbvc, 129)
self.assertEqual(field.lblev, 9999)

# check surface fields aren't incorrectly applied
v_coord = iris.coords.DimCoord(standard_name="depth", units="m", points=[-5])
cube = setup_cube(v_coord)
for field in cube:
self.assertNotEqual(field.lbvc, 129)
self.assertNotEqual(field.lblev, 9999)


def fields_from_cube(cubes):
"""Return an iterator of PP fields generated from saving the given cube(s)
Expand Down