Skip to content

Allow global attributes on a Mesh #6085

Open
@jrackham-mo

Description

@jrackham-mo

✨ Feature Request

Following the introduction of split local/global attribute handling in Iris 3.8 (PR #5152), it would be useful to have the ability to set global attributes on a Mesh. Setting an attribute on a mesh currently sets a local attribute on the mesh topology variable. Global attributes that do exist on the file (for example added via the python netcdf4 library, or the Conventions attribute that iris always adds on save) are lost on load.

Motivation

The motivation is to be able to add attributes such as history, provenance, etc. which make sense at the file level (i.e. global) rather than the variable level. Especially in the case where multiple meshes exist in a single file.

Additional context

Example current behaviour (iris 3.9):

>>> from iris.experimental.ugrid import save_mesh
>>> from iris.tests.stock.mesh import sample_mesh
>>> mesh = sample_mesh()
>>> mesh.var_name = "dynamics"
>>> mesh.attributes["history"] = "timestamp"
>>> print(mesh)
Mesh : 'dynamics'
    topology_dimension: 2
    node
        node_dimension: 'Mesh2d_node'
        node coordinates
            <AuxCoord: longitude / (degrees_east)  [...]  shape(15,)>
            <AuxCoord: latitude / (unknown)  [...]  shape(15,)>
    edge
        edge_dimension: 'Mesh2d_edge'
        edge_node_connectivity: <Connectivity: unknown / (unknown)  [...]  shape(5, 2)>
        edge coordinates
            <AuxCoord: longitude / (unknown)  [...]  shape(5,)>
            <AuxCoord: latitude / (unknown)  [...]  shape(5,)>
    face
        face_dimension: 'Mesh2d_face'
        face_node_connectivity: <Connectivity: unknown / (unknown)  [...]  shape(3, 4)>
        face coordinates
            <AuxCoord: longitude / (unknown)  [...]  shape(3,)>
            <AuxCoord: latitude / (unknown)  [...]  shape(3,)>
    var_name: 'dynamics'
    attributes:
        history  'timestamp'
>>> save_mesh(mesh, "dynamics_mesh.nc")

Inspecting the saved netCDF file:

$ ncdump -h dynamics_mesh.nc
netcdf dynamics_mesh {
dimensions:
	Mesh2d_node = 15 ;
	Mesh2d_edge = 5 ;
	Mesh2d_face = 3 ;
	dynamics_face_N_nodes = 4 ;
	dynamics_edge_N_nodes = 2 ;
variables:
	int dynamics ;
		dynamics:cf_role = "mesh_topology" ;
		dynamics:topology_dimension = 2 ;
		dynamics:history = "timestamp" ;
		dynamics:node_coordinates = "var_name latitude" ;
		dynamics:edge_coordinates = "longitude latitude_0" ;
		dynamics:face_coordinates = "longitude_0 latitude_1" ;
		dynamics:face_node_connectivity = "mesh2d_face" ;
		dynamics:edge_node_connectivity = "mesh2d_edge" ;
	int64 var_name(Mesh2d_node) ;
		var_name:units = "degrees_east" ;
		var_name:standard_name = "longitude" ;
		var_name:long_name = "long-name" ;
		var_name:a = 1LL ;
		var_name:b = "c" ;
	int64 latitude(Mesh2d_node) ;
		latitude:standard_name = "latitude" ;
	int64 longitude(Mesh2d_edge) ;
		longitude:standard_name = "longitude" ;
	int64 latitude_0(Mesh2d_edge) ;
		latitude_0:standard_name = "latitude" ;
	int64 longitude_0(Mesh2d_face) ;
		longitude_0:standard_name = "longitude" ;
	int64 latitude_1(Mesh2d_face) ;
		latitude_1:standard_name = "latitude" ;
	int64 mesh2d_face(Mesh2d_face, dynamics_face_N_nodes) ;
		mesh2d_face:cf_role = "face_node_connectivity" ;
		mesh2d_face:start_index = 0LL ;
	int64 mesh2d_edge(Mesh2d_edge, dynamics_edge_N_nodes) ;
		mesh2d_edge:cf_role = "edge_node_connectivity" ;
		mesh2d_edge:start_index = 0LL ;

// global attributes:
		:Conventions = "CF-1.7" ;
}

Desired output:

$ ncdump -h dynamics_mesh.nc
netcdf dynamics_mesh {
dimensions:
	Mesh2d_node = 15 ;
	Mesh2d_edge = 5 ;
	Mesh2d_face = 3 ;
	dynamics_face_N_nodes = 4 ;
	dynamics_edge_N_nodes = 2 ;
variables:
	int dynamics ;
		dynamics:cf_role = "mesh_topology" ;
		dynamics:topology_dimension = 2 ;
		dynamics:node_coordinates = "var_name latitude" ;
		dynamics:edge_coordinates = "longitude latitude_0" ;
		dynamics:face_coordinates = "longitude_0 latitude_1" ;
		dynamics:face_node_connectivity = "mesh2d_face" ;
		dynamics:edge_node_connectivity = "mesh2d_edge" ;
	int64 var_name(Mesh2d_node) ;
		var_name:units = "degrees_east" ;
		var_name:standard_name = "longitude" ;
		var_name:long_name = "long-name" ;
		var_name:a = 1LL ;
		var_name:b = "c" ;
	int64 latitude(Mesh2d_node) ;
		latitude:standard_name = "latitude" ;
	int64 longitude(Mesh2d_edge) ;
		longitude:standard_name = "longitude" ;
	int64 latitude_0(Mesh2d_edge) ;
		latitude_0:standard_name = "latitude" ;
	int64 longitude_0(Mesh2d_face) ;
		longitude_0:standard_name = "longitude" ;
	int64 latitude_1(Mesh2d_face) ;
		latitude_1:standard_name = "latitude" ;
	int64 mesh2d_face(Mesh2d_face, dynamics_face_N_nodes) ;
		mesh2d_face:cf_role = "face_node_connectivity" ;
		mesh2d_face:start_index = 0LL ;
	int64 mesh2d_edge(Mesh2d_edge, dynamics_edge_N_nodes) ;
		mesh2d_edge:cf_role = "edge_node_connectivity" ;
		mesh2d_edge:start_index = 0LL ;

// global attributes:
		:Conventions = "CF-1.7" ;
                :history = "timestamp" ;
}

Interestingly, a global Conventions attribute is already added on save.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    No status

    Status

    📌 Prioritised

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions