Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added in a vertical rule for surface fields #5734

Merged
merged 14 commits into from
Oct 28, 2024
6 changes: 6 additions & 0 deletions docs/src/further_topics/um_files_loading.rst
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,12 @@ the derived ``altitude``.
it produces basic coordinates 'model_level_number', 'sigma' and
'level_pressure', and a manufactured 3D 'air_pressure' coordinate.

**Surface Fields**

In order for surface fields to be recognised when saving, you must include
`label_surface_fields=True` to :func:`iris.fileformats.pp.save` or
:func:`iris.save`. When surface fields are encountered with this flag set to True,
LBLEV will be set to 9999 and LBVC to 129.

.. _um_time_metadata:

Expand Down
9 changes: 9 additions & 0 deletions docs/src/whatsnew/latest.rst
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ This document explains the changes made to Iris for this release
references are split across multiple input fields, and :meth:`~iris.LOAD_POLICY` to
control it, as requested in :issue:`5369`, actioned in :pull:`6168`.

#. `@ESadek-MO`_ has updated :mod:`iris.fileformats.pp_save_rules` and
:mod:`iris.fileformats.pp` to include the `label_surface_fields` flag across
relevant functions, most notably :func:`iris.fileformats.pp.save`.
This allows the user to choose whether or not surface fields are recognised
and handled appropriately. (:issue:`3280`, :pull:`5734`)

🐛 Bugs Fixed
=============
Expand Down Expand Up @@ -116,6 +121,10 @@ This document explains the changes made to Iris for this release

#. `@bouweandela`_ added type hints for :class:`~iris.cube.Cube`. (:pull:`6037`)

#. `@ESadek-MO`_ has updated :ref:`um_files_loading` to include a short description
of the new `label_surface_fields` functionality. (:pull:`5734`)


💼 Internal
===========

Expand Down
27 changes: 21 additions & 6 deletions lib/iris/fileformats/pp.py
Original file line number Diff line number Diff line change
Expand Up @@ -2171,7 +2171,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):
pp-mo marked this conversation as resolved.
Show resolved Hide resolved
"""Use the PP saving rules (and any user rules) to save a cube to a PP file.

Parameters
Expand All @@ -2192,6 +2192,11 @@ def save(cube, target, append=False, field_coords=None):
coordinates of the resulting fields.
If None, the final two dimensions are chosen
for slicing.
label_surface_fields : bool, default=False
Whether you wish pp_save_rules to recognise surface fields or not.
When true, if surface fields are encountered, LBLEV will be set to 9999
and LBVC to 129.
Default is False.

Notes
-----
Expand All @@ -2200,11 +2205,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 @@ -2316,12 +2321,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):
pp-mo marked this conversation as resolved.
Show resolved Hide resolved
"""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 @@ -2335,9 +2340,19 @@ def as_fields(cube, field_coords=None):
reducing the given cube into 2d slices, which will ultimately
determine the x and y coordinates of the resulting fields.
If None, the final two dimensions are chosen for slicing.
label_surface_fields : bool, default=False
Whether you wish pp_save_rules to recognise surface fields or not.
When true, if surface fields are encountered, LBLEV will be set to 9999
and LBVC to 129.
Default is False.

"""
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-mo marked this conversation as resolved.
Show resolved Hide resolved
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
Loading