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
18 changes: 15 additions & 3 deletions docs/src/whatsnew/latest.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ This document explains the changes made to Iris for this release
#. `@bjlittle`_, `@pp-mo`_ and `@trexfeathers`_ added support for unstructured
meshes, as described by `UGRID`_. This involved adding a data model (:pull:`3968`,
:pull:`4014`, :pull:`4027`, :pull:`4036`, :pull:`4053`) and API (:pull:`4063`,
:pull:`4064`), and supporting representation (:pull:`4033`, :pull:`4054`) and
loading (:pull:`4058`) of data on meshes.
:pull:`4064`), and supporting representation (:pull:`4033`, :pull:`4054`) of
data on meshes.
Most of this new API can be found in :mod:`iris.experimental.ugrid`. The key
objects introduced are :class:`iris.experimental.ugrid.Mesh`,
:class:`iris.experimental.ugrid.MeshCoord` and
Expand All @@ -49,9 +49,21 @@ This document explains the changes made to Iris for this release
property :attr:`~iris.cube.Cube.mesh` which returns a
:class:`~iris.experimental.ugrid.Mesh` if one is attached to the
:class:`~iris.cube.Cube` via a :class:`~iris.experimental.ugrid.MeshCoord`.
Finally, the context manager :obj:`~iris.experimental.ugrid.PARSE_UGRID_ON_LOAD`

#. `@trexfeathers`_ added support for loading unstructured mesh data from netcdf data,
for files using the `UGRID`_ conventions.
The context manager :obj:`~iris.experimental.ugrid.PARSE_UGRID_ON_LOAD`
provides a way to load UGRID files so that :class:`~iris.cube.Cube`\ s can be
returned with a :class:`~iris.experimental.ugrid.Mesh` attached.
(:pull:`4058`).

#. `@pp-mo`_ added support to save cubes with meshes to netcdf files, using the
`UGRID`_ conventions.
The existing :meth:`iris.save` function now does this, when saving cubes with meshes.
A routine :meth:`iris.experimental.ugrid.save_mesh` allows saving
:class:`~iris.experimental.ugrid.Mesh` objects to netcdf *without* any associated data
(i.e. not attached to cubes).
(:pull:`4318` and :pull:`4339`).


🐛 Bugs Fixed
Expand Down
69 changes: 59 additions & 10 deletions lib/iris/experimental/ugrid/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@
from ...util import guess_coord_axis

__all__ = [
"CFUGridReader",
"Connectivity",
"ConnectivityMetadata",
"load_mesh",
"load_meshes",
"save_mesh",
"Mesh",
"Mesh1DConnectivities",
"Mesh1DCoords",
Expand Down Expand Up @@ -3729,9 +3729,8 @@ def _build_aux_coord(coord_var, file_path):
Construct a :class:`~iris.coords.AuxCoord` from a given
:class:`CFUGridAuxiliaryCoordinateVariable`, and guess its mesh axis.

todo: integrate with standard loading API post-pyke.

"""
# TODO: integrate with standard saving API when no longer 'experimental'.
assert isinstance(coord_var, CFUGridAuxiliaryCoordinateVariable)
attributes = {}
attr_units = get_attr_units(coord_var, attributes)
Expand All @@ -3745,7 +3744,8 @@ def _build_aux_coord(coord_var, file_path):
# Fetch climatological - not allowed for a Mesh, but loading it will
# mean an informative error gets raised.
climatological = False
# TODO: use CF_ATTR_CLIMATOLOGY once re-integrated post-pyke.
# TODO: use CF_ATTR_CLIMATOLOGY on re-integration, when no longer
# 'experimental'.
attr_climatology = getattr(coord_var, "climatology", None)
if attr_climatology is not None:
climatology_vars = coord_var.cf_group.climatology
Expand Down Expand Up @@ -3781,9 +3781,8 @@ def _build_connectivity(connectivity_var, file_path, location_dims):
:class:`CFUGridConnectivityVariable`, and identify the name of its first
dimension.

todo: integrate with standard loading API post-pyke.

"""
# TODO: integrate with standard saving API when no longer 'experimental'.
assert isinstance(connectivity_var, CFUGridConnectivityVariable)
attributes = {}
attr_units = get_attr_units(connectivity_var, attributes)
Expand Down Expand Up @@ -3823,9 +3822,8 @@ def _build_mesh(cf, mesh_var, file_path):
"""
Construct a :class:`Mesh` from a given :class:`CFUGridMeshVariable`.

todo: integrate with standard loading API post-pyke.

"""
# TODO: integrate with standard saving API when no longer 'experimental'.
assert isinstance(mesh_var, CFUGridMeshVariable)
attributes = {}
attr_units = get_attr_units(mesh_var, attributes)
Expand Down Expand Up @@ -3966,9 +3964,8 @@ def _build_mesh_coords(mesh, cf_var):
Construct a tuple of :class:`MeshCoord` using from a given :class:`Mesh`
and :class:`~iris.fileformats.cf.CFVariable`.

todo: integrate with standard loading API post-pyke.

"""
# TODO: integrate with standard saving API when no longer 'experimental'.
# Identify the cube's mesh dimension, for attaching MeshCoords.
locations_dimensions = {
"node": mesh.node_dimension,
Expand All @@ -3985,3 +3982,55 @@ def _build_mesh_coords(mesh, cf_var):

# END of loading section.
###############################################################################


###############################################################################
# SAVING


def save_mesh(mesh, filename, netcdf_format="NETCDF4"):
"""
Save mesh(es) to a netCDF file.

Args:

* mesh (:class:`iris.experimental.ugrid.Mesh` or iterable):
mesh(es) to save.

* filename (string):
Name of the netCDF file to create.

Kwargs:

* netcdf_format (string):
Underlying netCDF file format, one of 'NETCDF4', 'NETCDF4_CLASSIC',
'NETCDF3_CLASSIC' or 'NETCDF3_64BIT'. Default is 'NETCDF4' format.

"""
# TODO: integrate with standard saving API when no longer 'experimental'.

if isinstance(mesh, Iterable):
meshes = mesh
else:
meshes = [mesh]

# Initialise Manager for saving
with netcdf.Saver(filename, netcdf_format) as sman:
# Iterate through the list.
for mesh in meshes:
# Get suitable dimension names.
mesh_dimensions, _ = sman._get_dim_names(mesh)

# Create dimensions.
sman._create_cf_dimensions(
cube=None, dimension_names=mesh_dimensions
)

# Create the mesh components.
sman._add_mesh(mesh)

# Add a conventions attribute.
# TODO: add 'UGRID' to conventions, when this is agreed with CF ?
sman.update_global_attributes(
Conventions=netcdf.CF_CONVENTIONS_VERSION
)
Loading