Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migration of unmigrated content due to installation of a new plugin
scrollbar

Sep 25, 2013 - IMPORTANT NOTE: To make the proposal a more formal standard, the content of this page has been migrated to GitHub, please follow this link.
The documentation below is no longer updated.

--------- snapshot Sep 25, 2013 ---------
Latest update Feb 28, 2013: use topology_dimension attribute rather than dimension, as suggested by Jonathan Gregory here.
Update Sep 12, 2012: closed clockwise discussion, explained meaning of Optionally required attributes.
You can use the following shorthand http://bit.ly/ugrid_cf to refer to this page.

PLEASE NOTE: The title of this page suggests that this is only a proposal by Deltares. However, the support for this proposal is already much wider than that (see the UGRID Google Group) and is under active discussion with an important subset of the CF community. We keep the current title only such that it does not break any existing external links to this information.

The section of creating mosaics of meshes has been moved to this child page. Since experience with unstructured grid mosaics is still limited, and demand is low, the mosaics are not yet part of the current proposal.

Introduction.

This page describes a proposal for storing unstructured (or flexible mesh) model data in a netCDF file. Our focus is on data for environmental applications and hence we start from the CF-conventions which has been the standard in climate research for many years, and is increasingly adopted by others as the metadata standard for netCDF, see e.g. NASA Standards Process Group and OGC. The CF conventions allow you to define values at points, and the associated coordinates may have bounds attributes to indicate a spatial extent bigger than a single point. A two-dimensional array of points, encodes a structured topology but the CF conventions do not yet provide the means to define an unstructured topology. That's what this proposal adds.

In its most basic form unstructured data may be stored as data defined at a series of points, the CF-conventions are then sufficient. However, it is often useful or even necessary to also know the topology of the underlying unstructured mesh: is it a 1D network, a 2D triangular mesh or more flexible mixed triangle/quadrilateral mesh, a 2D mesh with vertical layers, or a fully unstructured 3D mesh. This document describes the attribute conventions for storing the mesh topology and for associating variables with (specific locations on) the mesh topology. The conventions have been designed to store the output data of a combined 1D-2D-3D flow model with staggered data, but the metadata for a simple 1D network or 2D triangular mesh doesn't suffer from the genericity needed for the most complex models.

Due to the wide variety in unstructured mesh models, some relevant concepts have not yet been worked out in detail. This includes the following concepts:

  • adaptive mesh topology (this could be supported by defining a time_concatenation attribute for a time-series of mesh topologies)
  • higher order element data; for an idea how such data could be stored see this other proposal.
  • subgrid data; the netCDF pages by the BAW contain some proposals on this topic (see their pages (in German)).
  • 3D fully unstructured meshes (included but still limited in scope).
    See also a related proposal for an unstructured mesh data model by Jeff Daily at PNNL.

More details can be found in the various sections below:

Table of Content Zone
locationtop
typelist

Topology.

Naming conventions for geometrical elements.

Inspired by Wikipedia's definition of network topology, we define the mesh topology here as the interconnection of various geometrical elements of the mesh. The pure interconnectivity is independent of georeferencing the individual geometrical elements, but for the practical applications for which we are defining this CF extension, we'll always add coordinate data. Within a mesh, one can distinguish 0-, 1-, 2- and 3-dimensional elements. We need some names to identify these four types of elements; after discussion we propose the following names:

Dimensionality

Proposed Name

Comments

0

node

A point, a coordinate pair or triplet: the most basic element of the topology.
The word node seems to be more commonly used than the alternative "vertex".

1

edge

A line or curve bounded by two nodes.

2

face

A plane or surface enclosed by a set of edges.
In a 2D horizontal application one may consider the word "polygon", but in the hierarchy of elements the word face is most common.

3

volume

A volume enclosed by a set of faces.

(warning) In favor of simpler code for interpreting compliant files, we have dropped to use of the locations attribute which allowed the user to specify his/her own names for nodes, edges, faces and volumes.

1D network topology.

The topology information is stored as attributes to a dummy variable (in the example below called "Mesh1") with cf_role mesh_topology.

Required topology attributes

Value

cf_role

mesh_topology

topology_dimension

1

node_coordinates

edge_node_connectivity

Optional attributes

edge_coordinates

The attribute topology_dimension indicates the highest dimensionality of the geometric elements; for a 1D network this should be 1. The attribute node_coordinates points to the auxiliary coordinate variables representing the node locations (latitude, longitude, and optional elevation or other coordinates). These auxiliary coordinate variables will have length nNodes. The attribute edge_node_connectivity points to an index variable identifying for every edge to the indices of its begin and end nodes. The connectivity array will thus be a matrix of size nEdges x 2. For the indexing one may use either 0- or 1-based indexing; the convention used should be specified using a start_index attribute to the index variable (i.e. Mesh1_edge_nodes in the example below). Consistent with the CF-conventions compression option, the connectivity indices are 0-based by default.

(warning) The option to support both 0- and 1-based indexing was introduced to be able to support existing files with 1-based index tables using ncML. See this section on 0-/1-based indexing for more details.

The mesh_topology may optionally include an edge_coordinates attribute which points to the auxiliary coordinate variables associated with the characteristic location of the edge (commonly the midpoint). These auxiliary coordinate variables will have length nEdges, and may have in turn a bounds attribute that specifies the bounding coordinates of the edge (thereby duplicating the data in the node_coordinates variables).

(warning) This use of the bounds attribute is consistent with the CF-convention on the use of bounds for multi-dimensional coordinate variables with p-sided cells, but it may not strictly be supported by the CF-convention right now.

Image Added

Example:

Code Block
dimensions:
        nMesh1_node = 5 ; // nNodes
        nMesh1_edge = 4 ; // nEdges

        Two = 2;

variables:

...

Introduction.

This page describes a proposal for storing unstructured model data in a netCDF file.
Our focus is on data for environmental applications and hence we have tried to be consistent with CF-conventions whenever possible.
In its most basic form unstructured data may be stored as data defined at a series of points, the CF-conventions are then sufficient.
However, it is often useful or even necessary to also know the topology of the underlying unstructured mesh: is it a 1D network, a 2D triangular mesh or more flexible mixed triangle/quadrilateral mesh, a 2D mesh with vertical layers, or a fully unstructured 3D mesh.
Therefore, the following description describes also attribute conventions for storing the mesh topology and for associating variables with the mesh topology.
The conventions have been designed to store the output data of a combined 1D-2D-3D flow model with staggered data, but the meta-data for a simple 1D network or 2D triangular mesh doesn't suffer from the genericity needed for the most complex models.

Due to the wide variety in unstructured mesh models, some relevant concepts have not yet been worked out in detail. This includes the following concepts:

  • adaptive mesh topology (this could be supported by defining a time_concatenation attribute for a time-series of mesh topologies)
  • 3D fully unstructured meshes (this can be defined analogous to the conventions for 2D mixed element topologies)
  • higher order element data locations (locations attribute allows for more locations; connectivity definition may require extension of the conventions)

1D network topology.

The topology information is stored as attributes to a dummy variable (in the example below called 'Mesh1'); the attributes are dimensionality and locations. The value of the dimensionality attribute should be integer 1 for a 1D network. The value of the location attribute is string with a space separated, extendable list of location names. Initially these locations have a purely topological meaning, but they may be used to define additional numerical stagger positions too. The names are not formally standardized, but the names 'node' and 'edge' are recommended for the 1D network geometry. For each location name X, there will be a further attribute composed of the concatenation of the name X and '_coordinates'. For example, the location name 'edge' points to an attribute 'edge_coordinates'. These coordinates attributes follow the CF-conventions, i.e. they point to the variables that contain the latitude and longitude (or other coordinate) variables that contain the coordinate data for that particular location. From these attributes it's still not clear what the exact network topology is. Therefore, we need one more attribute that specifies the connectivity of the locations. The attribute 'edge_node_connectivity' is composed of the location names 'edge' and 'node' and the word 'connectivity'. It tells us:

1. that the location 'edge' corresponds to the edges/links of the 1D network
2. that the location 'node' corresponds to the nodes/points of the 1D network
3. that "Mesh1_edge_nodes" (the value value of edge_node_connectivity) is the variable that contains the connectivity table

Code Block
dimensions: nMesh1_node = 3 ; nMesh1_edge = 2 ; Two = 2; variables: // Mesh coordinates double Mesh1_node_x(nMesh1_node) ; Mesh1_node_x:standard_name = "longitude" ; Mesh1_node_x:long_name = "Longitude of 1D network nodes." ; Mesh1_node_x:units = "degrees_east" ; double Mesh1_node_y(nMesh1_node) ; Mesh1_node_y:standard_name = "latitude" ; Mesh1_node_y:long_name = "Latitude of 1D network nodes." ; Mesh1_node_y:units = "degrees_north" ; double Mesh1_edge_x(nMesh1_edge) ; Mesh1_edge_x:standard_name = "longitude" ; Mesh1_edge_x:long_name = "Characteristic longitude of 1D network edge (e.g. center coordinate of the link)." ; Mesh1_edge_x:units = "degrees_east" ; double Mesh1_edge_y(nMesh1_edge) ; Mesh1_edge_y:standard_name = "latitude" ; Mesh1_edge_y:long_name = "Characteristic latitude of 1D network edge (e.g. center coordinate of the link)." ; Mesh1_edge_y:units = "degrees_north" ;

// Mesh topology
        integer Mesh1
_edge_nodes(nMesh1_edge,
 
Two)
;

                Mesh1
_edge_nodes
:
standard
cf_
name
role = "
YET TO BE DETERMINED
mesh_topology" ;

                Mesh1
_edge_nodes
:long_name = "
Maps
Topology 
every
data 
edge/link
of 
to
1D 
the
network" 
two
;
 
nodes
 
that
 
it
 
connects."
 
;
  
        
integer
 Mesh1:topology_dimension = 1 ;

                Mesh1:
standard
node_
name
coordinates = "
YET TO BE DETERMINED
Mesh1_node_x Mesh1_node_y" ;

                Mesh1:
long
edge_node_
name
connectivity = "
Topology data of 1D network
Mesh1_edge_nodes" ;
                Mesh1:
dimensionality
edge_coordinates = 
1
"Mesh1_edge_x Mesh1_edge_y" ;
 // optional attribute
        integer 
Mesh1:locations = "edge node"
Mesh1_edge_nodes(nMesh1_edge, Two) ;
                Mesh1_edge_nodes:
node
cf_
coordinates
role = "
Mesh1
edge_node_
x Mesh1_node_y
connectivity" ;
                Mesh1_edge_nodes:
edge
long_
coordinates
name = "
Mesh1_edge_x Mesh1_edge_y" ;
Maps every edge/link to the two nodes that it connects." ;
                Mesh1
:edge_node_connectivity = "Mesh1_edge_nodes" ;

2D triangular mesh topology.

2D flexible mesh (triangles, quadrilaterals, etc.) topology.

3D layered mesh topology.

Location maps.

Data on unstructured meshes.

Combined 1D-2D-3D meshes.

Example of a combination of connected 1D network, 2D mesh and 3D mesh.

The variable and dimension names used in this example differ slightly from those used in the examples on Unstructured grids. Please find a translation table below:

Unstructured grids

This example

NetNode

node

NetLink

edge/edge_nodes

NetElem/NetCell

face

NetElemNode/NetCellNode

face_nodes

NetElemLink/NetCellLink/FlowLink

face_links

Code Block
netcdf test_map { dimensions: // dimensions for mesh 1 nMesh1_node = 3
_edge_nodes:start_index = 1 ;

// Mesh node coordinates
        double Mesh1_node_x(nMesh1_node) ;
                Mesh1_node_x:standard_name = "longitude" ;
                Mesh1_node_x:long_name = "Longitude of 1D network nodes." ;
                Mesh1_node_x:units = "degrees_east" ;
        double Mesh1_node_y(nMesh1_node) ;
        
nMesh1_edge
 
= 2
 
;
      
nMaxMesh1_contour_pts
Mesh1_node_y:standard_name = 
99
"latitude" ;
        
nMesh1_cell
 
=
 
2
 
;
     
nMesh1_interface
Mesh1_node_y:long_name = 
2 ;
"Latitude of 1D network nodes." ;
    
//
 
dimensions
 
for
 
mesh
 
2
        
nMesh2
Mesh1_node_y:units = 
6
"degrees_north" ;

// Optional mesh 
nMesh2_
edge 
=
coordinate 
7 ;
variables
        
nMesh2_face = 2
double Mesh1_edge_x(nMesh1_edge) ;
        
nMesh2_edge_nodes
 
=
 
7;
      
nMesh2_face_nodes
Mesh1_edge_x:standard_name = 
4
"longitude" ;
        
nMesh2_face_links
 
=
 
1;
      
nMaxMesh2_face_nodes
Mesh1_edge_x:long_name = 
4;
"Characteristic longitude of 1D network edge (e.g. midpoint 
nMaxMesh2_contour_pts = 99 ;
of the edge)." ;
        
//
 
dimensions
 
for
 
mesh
 
3
    
nMesh3_node
Mesh1_edge_x:units = 
6
"degrees_east" ;
      
nMesh3_edge
 
=
 
7
 
;
       
nMesh3_face
Mesh1_edge_x:bounds = 
2
"Mesh1_edge_xbnds" ;
        double 
nMesh3
Mesh1_edge_
nodes = 7
y(nMesh1_edge) ;
        
nMesh3_face_nodes
 
=
 
4;
      
nMesh3_face_links
Mesh1_edge_y:standard_name = 
1
"latitude" ;
      
nMaxMesh3_face_nodes
 
=
 
4;
        
nMaxMesh3_contour_pts
Mesh1_edge_y:long_name = 
99
"Characteristic 
;
latitude of 1D network edge (e.g. midpoint of 
nMesh3_layer = 3
the edge)." ;
       
nMesh3_interface
 
=
 
4;
       
// common dimensions
Mesh1_edge_y:units = "degrees_north" ;
        
Two
 
=
 
2;
      Mesh1_edge_y:bounds 
time = UNLIMITED ; // (1 currently) variables: // Basic mesh data (coordinates independent of computational cells whatsoever)
= "Mesh1_edge_ybnds" ;
        double Mesh1_
node
edge_
x
xbnds(nMesh1_
node
edge,Two) ;

                Mesh1_
node
edge_
x
xbnds:standard_name = "
projection_x_coordinate
longitude" ;

                Mesh1_
node
edge_
x
xbnds:long_name = "
netnodal x-coordinate
Longitude bounds of 1D network edge (i.e. begin and end longitude)." ;

                Mesh1_
node
edge_
x
xbnds:units = "
m
degrees_east" ;
    
    double Mesh1_edge_ybnds(nMesh1_edge,Two) ;
          
Mesh1_node_x:bounds
 
=
 
"Mesh1_node_contour_x"
 
;
  
 
double
Mesh1_
node_y(nMesh1_node) ; Mesh1_node_y
edge_ybnds:standard_name = "
projection_y_coordinate
latitude" ;

                Mesh1_
node
edge_
y
ybnds:long_name = "
netnodal y-coordinate" ; Mesh1_node_y:units = "m" ;
Latitude bounds of 1D network edge (i.e. begin and end latitude)." ;
                Mesh1_
node
edge_
y
ybnds:
bounds
units = "
Mesh1_node_contour_y
degrees_north" ;
double Mesh1_edge_x(nMesh1_edge) ; Mesh1_edge_x:standard_name = "projection_x_coordinate" ; Mesh1_edge_x:long_name = "Center coordinate of net link (velocity point)." ; Mesh1_edge_x:units = "m" ; double Mesh1_edge_y(nMesh1_edge) ; Mesh1_edge_y:standard_name = "projection_y_coordinate" ; Mesh1_edge_y:long_name = "Center coordinate of net link (velocity point)." ; Mesh1_edge_y:units = "m" ; double Mesh1_node_contour_x(nMesh1_node, nMaxMesh1_contour_pts) ; Mesh1_node_contour_x:standard_name = "projection_x_coordinate" ; Mesh1_node_contour_x:long_name = "List of x-points that form outline of flow volume" ;

2D triangular mesh topology.

The topology information is stored as attributes to a dummy variable (in the example below called "Mesh2") with cf_role mesh_topology.

Required topology attributes

Value

cf_role

mesh_topology

topology_dimension

2

node_coordinates

face_node_connectivity

Optionally required attributes*

edge_node_connectivity

Optional attributes

face_edge_connectivity

face_face_connectivity

face_coordinates

edge_coordinates

*The "Optionally required" attribute edge_node_connectivity is required only if you want to store data on the edges (i.e. if you mind the numbering order of the edges).

The attribute topology_dimension indicates the highest dimensionality of the geometric elements; for a 2-dimensional (triangular) mesh this should be 2. The attribute node_coordinates points to the auxiliary coordinate variables representing the node locations (latitude, longitude, and optional elevation or other coordinates). These auxiliary coordinate variables will have length nNodes. The attribute face_node_connectivity points to an index variable identifying for every face (here consistently triangle) the indices of its three corner nodes. The corner nodes should be specified in anticlockwise (also referred to as counterclockwise) direction as viewed from above (consistent with the CF-convention for bounds of p-sided cells. The connectivity array will thus be a matrix of size nFaces x 3. For the indexing one may use either 0- or 1-based indexing; the convention used should be specified using a start_index attribute to the index variable (i.e. Mesh2_face_nodes in the example below). Consistent with the CF-conventions compression option, the connectivity indices are 0-based by default. See this section on 0-/1-based indexing for more details.

In case you want to define variables on the edges of the triangular mesh topology you need to specify the edge_node_connectivity attribute to map edges to nodes. Although the face to node mapping implicitly also defines the location of the edges, it does not specify the global numbering of the edges. Again the indexing convention of edge_node_connectivity should be specified using the start_index attribute to the index variable (i.e. Mesh2_edge_nodes in the example below) and 0-based indexing is the default.

Optionally the topology may have the following attributes:

  • face_edge_connectivity pointing to an index variable identifying for every face (here consistently triangle) the indices of its three edges. The edges should be specified in anticlockwise direction as viewed from above. This connectivity array will thus be a matrix of size nFaces x 3. Again the indexing convention of face_edge_connectivity should be specified using the start_index attribute to the index variable (i.e. Mesh2_face_edges in the example below) and 0-based indexing is the default.
  • face_face_connectivity pointing to an index variable identifying pairs of faces (here consistently triangle) that share an edge, i.e. are neighbors. TODO: CHECK DEFINITION This connectivity array will thus be a matrix of size nFacePairs x 2. Again the indexing convention of face_face_connectivity should be specified using the start_index attribute to the index variable (i.e. Mesh2_face_links in the example below) and 0-based indexing is the default.
  • face_coordinates and/or edge_coordinates pointing to the auxiliary coordinate variables associated with the characteristic location of the faces and edges. These auxiliary coordinate variables will have length nFaces and nEdges respectively, and may have in turn a bounds attribute that specifies the bounding coordinates of the face or edge (thereby duplicating the data in the node_coordinates variables).

Image Added

Example:

Code Block
dimensions:
        nMesh2_node = 4 ; // nNodes
     
Mesh1_node_contour_x:units
   nMesh2_edge = 
"m"
5 ; // nEdges
        
double Mesh1_node_contour_y(nMesh1_node, nMaxMesh1_contour_pts)
nMesh2_face = 2 ; // nFaces
        nMesh2_face_links = 1 ; // nFacePairs

        
Mesh1_node_contour_y:standard_name
Two = 
"projection_y_coordinate"
2 ;
  
      Three = 3 ;

variables:
// Mesh topology
     
Mesh1_node_contour_y:units
 
=
 
"m"
 
;
integer Mesh2 ;
                
Mesh1_node_contour_y:long_name
Mesh2:cf_role = "
List of y-points that form outline of flow volume" ; // Basic mesh topology integer Mesh1_edge_nodes(nMesh1_edge, Two) ;
mesh_topology" ;
                Mesh2:long_name = "Topology data of 2D unstructured mesh" ;
                
Mesh1_edge_nodes:long_name
Mesh2:topology_dimension = 
"links between two nodes"
2 ;

        
integer
 
Mesh1
 
;
 
     
Mesh1:long_name
Mesh2:node_coordinates = "
Topology data of Mesh1
Mesh2_node_x Mesh2_node_y" ;
                
Mesh1:dimensionality
Mesh2:face_node_connectivity = 
1
"Mesh2_face_nodes" ;
                
Mesh1:locations
Mesh2:edge_node_connectivity = "
link node
Mesh2_edge_nodes" ; // attribute required if variables will be defined on edges
                
Mesh1
Mesh2:
node
edge_coordinates = "
Mesh1
Mesh2_
node
edge_x 
Mesh1
Mesh2_
node
edge_y" ; // optional attribute (requires edge_node_connectivity)
                
Mesh1
Mesh2:
edge
face_coordinates = "
Mesh1
Mesh2_
edge
face_x 
Mesh1
Mesh2_
edge
face_y" ; // optional attribute
                
Mesh1
Mesh2:face_edge_
nodes
connectivity = "
Mesh1
Mesh2_
edge
face_
nodes
edges" ; // optional attribute (requires edge_node_connectivity)
                
Mesh1
Mesh2:
parent
face_face_
mesh
connectivity = "
CombinedMesh
Mesh2_face_links" ;
 // 
Similar
optional 
for Mesh2
attribute
        
double
integer Mesh2_
node
face_
x
nodes(nMesh2_
node
face, Three) ;

                Mesh2_
node
face_
x
nodes:
standard
cf_
name
role = "
projection
face_
x
node_
coordinate
connectivity" ;

                Mesh2_
node
face_
x
nodes:long_name = "
netnodal x-coordinate
Maps every triangular face to its three corner nodes." ;

                Mesh2_
node
face_
x
nodes:
units
start_index = 
"m"
1 ;

        
double
integer Mesh2_
node
edge_
y
nodes(nMesh2_
node
edge, Two) ;

                Mesh2_
node
edge_
y
nodes:
standard
cf_
name
role = "
projection
edge_
y
node_
coordinate
connectivity" ;

                Mesh2_
node
edge_
y
nodes:long_name = "
netnodal y-coordinate
Maps every edge to the two nodes that it connects." ;
                Mesh2_
node
edge_
y
nodes:
units
start_index = 
"m"
1 ;

// Optional mesh topology variables
        
double
integer Mesh2_face_
x
edges(nMesh2_face, Three) ;

                Mesh2_face_
x
edges:
standard
cf_
name
role = "
projection
face_
x
edge_
coordinate
connectivity" ;

                Mesh2_face_
x
edges:long_name = "
Flow
Maps 
element
every 
circumcenter
triangular 
x"
face 
;
to its 
Mesh2_face_x:units = "m
three edges." ;

                Mesh2_face_
x
edges:
bounds
start_index = 
"Mesh2_face_contour_x"
1 ;

        
double
integer Mesh2_face_
y
links(nMesh2_face_links, Two) ;

                Mesh2_face_
y
links:
standard
cf_
name
role = "
projection
face_
y
face_
coordinate
connectivity" ;

                Mesh2_face_
y
links:long_name = "
Flow element circumcenter y
Indicates pairs of (triangular) faces that share an edge." ;

                Mesh2_face_
y
nodes:
units
start_index = 
"m"
1 ;


// Mesh 
Mesh2_face_y:bounds = "Mesh2_face_contour_y" ;
node coordinates
        double Mesh2_
face
node_
contour_
x(nMesh2_
face, nMaxMesh2_contour_pts
node) ;

                Mesh2_
face
node_
contour_
x:standard_name = "
projection_x_coordinate
longitude" ;

                Mesh2_
face_contour
node_x:long_name = "
List
Longitude of 
x-points
2D 
that form outline of flow volume
mesh nodes." ;

                Mesh2_
face
node_
contour_
x:units = "
m
degrees_east" ;

        double Mesh2_
face_contour
node_y(nMesh2_
face, nMaxMesh2_contour_pts
node) ;

                Mesh2_
face_contour
node_y:standard_name = 
"projection_y_coordinate"
"latitude" ;

                Mesh2_
face_contour
node_y:
units
long_name = "
m
Latitude of 2D mesh nodes." ;

                Mesh2_
face_contour
node_y:
long_name
units = "
List of y-points that form outline of flow volume" ;
degrees_north" ;

// Optional mesh face and edge coordinate variables
        double Mesh2_
edge
face_x(nMesh2_
edge
face) ;

                Mesh2_
edge
face_x:standard_name = "
projection_x_coordinate
longitude" ;

                Mesh2_
edge
face_x:long_name = "
Center
Characteristics 
coordinate
longitude of 
net
2D mesh 
link
triangle (
velocity point
e.g. circumcenter coordinate)." ;

                Mesh2_
edge
face_x:units = "
m
degrees_east" ;

        double Mesh2_
edge
face_y(nMesh2_
edge
face) ;

                Mesh2_
edge
face_y:standard_name = "
projection_y_coordinate
latitude" ;

                Mesh2_
edge
face_y:long_name = "
Center
Characteristics 
coordinate
latitude of 
net
2D mesh 
link
triangle (
velocity point
e.g. circumcenter coordinate)." ;

                Mesh2_
edge
face_y:units = "
m
degrees_north" ;

        
integer
double Mesh2_edge_
nodes
x(nMesh2_edge
, Two
) ;

                Mesh2_edge_
nodes
x:
long
standard_name = "
link between two nodes" ;
longitude" ;
               
integer
 Mesh2_edge_
face_nodes(nMesh2_face, nMaxMesh2_face_nodes) ;
x:long_name = "Characteristic longitude of 2D mesh edge (e.g. midpoint of the edge)." ;
                Mesh2_
face
edge_
nodes
x:
long_name
units = "
Mapping from net face to net nodes.
degrees_east" ;

        
integer
double Mesh2_
face
edge_
links
y(nMesh2_
face_links, Two) ;
edge) ;
                
FlowLink:long
Mesh2_edge_y:standard_name = "
link/interface between two flow elements (faces)
latitude" ;

        
integer
 
Mesh2
 
;
 
     
Mesh2
Mesh2_edge_y:long_name = "
Topology
Characteristic 
data
latitude of 
Mesh2"
2D 
;
mesh edge (e.g. midpoint of 
Mesh2:dimensionality = 2
the edge)." ;
                Mesh2_edge_y:
locations
units = "
face edge node" ; Mesh2:node_coordinates = "Mesh2_node_x Mesh2_node_y" ; Mesh2:edge_coordinates = "Mesh2_edge_x Mesh2_edge_y" ; Mesh2:edge_nodes = "Mesh2_edge_nodes" ; Mesh2:face_coordinates = "Mesh2_face_x Mesh2_face_y" ; Mesh2:face_nodes = "Mesh2_face_nodes" ; Mesh2:face_connectivity = "Mesh2_face_links" ; Mesh2:parent_mesh = "CombinedMesh" ; // Similar for Mesh3 double Mesh3_node_x(nMesh3_node) ; Mesh3_node_x:standard_name = "projection_x_coordinate" ; Mesh3_node_x:long_name = "netnodal x-coordinate" ; Mesh3_node_x:units = "m" ; double Mesh3_node_y(nMesh3_node) ; Mesh3_node_y:standard_name = "projection_y_coordinate" ; Mesh3_node_y:long_name = "netnodal y-coordinate" ;
degrees_north" ;

2D flexible mesh (mixed triangles, quadrilaterals, etc.) topology.

The case of a 2D mesh with mixed face sizes is identical to the 2D triangular mesh discussed above with the exception that not all faces have the same number of nodes. To support this variability we may use in the future a ragged array, but here we propose to use _FillValue to indicate faces with smaller number of nodes than the arrays allow.

The topology information is stored as attributes to a dummy variable (in the example below called "Mesh2") with cf_role mesh_topology.

Required topology attributes

Value

cf_role

mesh_topology

topology_dimension

2

node_coordinates

face_node_connectivity

Optionally required attributes*

edge_node_connectivity

Optional attributes

face_edge_connectivity

face_face_connectivity

face_coordinates

edge_coordinates

*The "Optionally required" attribute edge_node_connectivity is required only if you want to store data on the edges (i.e. if you mind the numbering order of the edges).

The attribute topology_dimension indicates the highest dimensionality of the geometric elements; for a 2-dimensional mesh this should be 2. The attribute node_coordinates points to the auxiliary coordinate variables representing the node locations (latitude, longitude, and optional elevation or other coordinates). These auxiliary coordinate variables will have length nNodes. The attribute face_node_connectivity points to an index variable identifying for every face the indices of its corner nodes. The corner nodes should be specified in anticlockwise direction as viewed from above (consistent with the CF-convention for bounds of p-sided cells. The connectivity array will be a matrix of size nFaces x MaxNumNodesPerFace; if a face has less corner nodes than MaxNumNodesPerFace then the last node indices shall be equal to _FillValue (which should obviously be larger than the number of nodes in the mesh). For the indexing one may use either 0- or 1-based indexing; the convention used should be specified using a start_index attribute to the index variable (i.e. Mesh2_face_nodes in the example below). Consistent with the CF-conventions compression option, the connectivity indices are 0-based by default. See this section on 0-/1-based indexing for more details.

In case you want to define variables on the edges of the 2D mesh topology you need to specify the edge_node_connectivity attribute to map edges to nodes. Although the face to node mapping implicitly also defines the location of the edges, it does not specify the global numbering of the edges. Again the indexing convention of edge_node_connectivity should be specified using the start_index attribute to the index variable (i.e. Mesh2_edge_nodes in the example below) and 0-based indexing is the default.

Optionally the topology may have the following attributes:

  • face_edge_connectivity pointing to an index variable identifying for every face the indices of its edges. The edges should be specified in anticlockwise direction as viewed from above. This connectivity array will be a matrix of size nFaces x MaxNumNodesPerFace. Again, if a face has less corners/edges than MaxNumNodesPerFace then the last edge indices shall be equal to _FillValue, and the indexing convention of face_edge_connectivity should be specified using the start_index attribute to the index variable (i.e. Mesh2_face_edges in the example below) and 0-based indexing is the default.
  • face_face_connectivity pointing to an index variable identifying pairs of faces that share an edge, i.e. are neighbors. TODO: CHECK DEFINITION This connectivity array will thus be a matrix of size nFacePairs x 2. Again the indexing convention of face_face_connectivity should be specified using the start_index attribute to the index variable (i.e. Mesh2_face_links in the example below) and 0-based indexing is the default.
  • face_coordinates and/or edge_coordinates pointing to the auxiliary coordinate variables associated with the characteristic location of the faces and edges. These auxiliary coordinate variables will have length nFaces and nEdges respectively, and may have in turn a bounds attribute that specifies the bounding coordinates of the face or edge (thereby duplicating the data in the node_coordinates variables).

(warning) The use of _FillValue to indicate faces with less nodes than MaxNumNodesPerFace extends to the coordinate bounds variables; this is an extension of the current convention.

Image Added

Example:

Code Block
dimensions:
        nMesh2_node = 5 ; // nNodes
        nMesh2_edge = 6 ; // nEdges
        nMesh2_face = 2 ; // nFaces
     
Mesh3_node_y:units
   nMesh2_face_links = 
"m"
1 ; // nFacePairs
        
double Mesh3_face_x(nMesh3_face) ;
nMaxMesh2_face_nodes = 4 ; // MaxNumNodesPerFace

        Two = 2 ;

variables:
// Mesh topology
        integer Mesh2 ;
                
Mesh3_face_x:standard_name
Mesh2:cf_role = "
projection_x_coordinate" ; Mesh3_face_x:long_name = "Flow element circumcenter x" ; Mesh3_face_x:units = "m" ; Mesh3_face_x:bounds = "Mesh3_face_contour_x" ; double Mesh3_face_y(nMesh3_face) ; Mesh3_face_y:standard_name = "projection_y_coordinate" ; Mesh3_face_y:long_name = "Flow element circumcenter y" ; Mesh3_face_y:units = "m" ; Mesh3_face_y:bounds = "Mesh3_face_contour_y" ; double Mesh3_face_contour_x(nMesh3_face, nMaxMesh3_contour_pts) ; Mesh3_face_contour_x:standard_name = "projection_x_coordinate" ; Mesh3_face_contour_x:long_name = "List of x-points that form outline of flow volume" ; Mesh3_face_contour_x:units = "m" ; double Mesh3_face_contour_y(nMesh3_face, nMaxMesh3_contour_pts) ; Mesh3_face_contour_y:standard_name = "projection_y_coordinate" ;
mesh_topology" ;
                Mesh2:long_name = "Topology data of 2D unstructured mesh" ;
                Mesh2:topology_dimension = 2 ;
                Mesh2:node_coordinates = "Mesh2_node_x Mesh2_node_y" ;
                Mesh2:face_node_connectivity = "Mesh2_face_nodes" ;
                Mesh2:edge_node_connectivity = "Mesh2_edge_nodes" ; // attribute required if variables will be defined on edges
                Mesh2:edge_coordinates = "Mesh2_edge_x Mesh2_edge_y" ; // optional attribute (requires edge_node_connectivity)
                Mesh2:face_coordinates = "Mesh2_face_x Mesh2_face_y" ; // optional attribute
                Mesh2:face_edge_connectivity = "Mesh2_face_edges" ; // optional attribute (requires edge_node_connectivity)
                Mesh2:face_face_connectivity = "Mesh2_face_links" ; // optional attribute
        integer Mesh2_face_nodes(nMesh2_face, nMaxMesh2_face_nodes) ;
                Mesh2_face_nodes:cf_role = "face_node_connectivity" ;
                Mesh2_face_nodes:long_name = "Maps every face to its corner nodes." ;
                Mesh2_face_nodes:_FillValue = 999999 ;
                Mesh2_face_nodes:start_index = 1 ;
        integer Mesh2_edge_nodes(nMesh2_edge, Two) ;
                Mesh2_edge_nodes:cf_role = "edge_node_connectivity" ;
                Mesh2_edge_nodes:long_name = "Maps every edge to the two nodes that it connects." ;
                Mesh2_edge_nodes:start_index = 1 ;

// Optional mesh topology variables
        integer Mesh2_face_edges(nMesh2_face, nMaxMesh2_face_nodes) ;
                Mesh2_face_edges:cf_role = "face_edge_connectivity" ;
                Mesh2_face_edges:long_name = "Maps every face to its edges." ;
                Mesh2_face_edges:_FillValue = 999999 ;
                Mesh2_face_edges:start_index = 1 ;
        integer Mesh2_face_links(nMesh2_face_links, Two) ;
                Mesh2_face_links:cf_role = "face_face_connectivity" ;
                Mesh2_face_links:long_name = "Indicates which faces are neighbors." ;
                Mesh2_face_links:start_index = 1 ;

// Mesh node coordinates
        double Mesh2_node_x(nMesh2_node) ;
                Mesh2_node_x:standard_name = "longitude" ;
                Mesh2_node_x:long_name = "Longitude of 2D mesh nodes." ;
                Mesh2_node_x:units = "degrees_east" ;
        double Mesh2_node_y(nMesh2_node) ;
                Mesh2_node_y:standard_name = "latitude" ;
                Mesh2_node_y:long_name = "Latitude of 2D mesh nodes." ;
                Mesh2_node_y:units = "degrees_north" ;

// Optional mesh face and edge coordinate variables
        double Mesh2_face_x(nMesh2_face) ;
                Mesh2_face_x:standard_name = "longitude" ;
                Mesh2_face_x:long_name = "Characteristics longitude of 2D mesh face." ;
                Mesh2_face_x:units = "degrees_east" ;
                Mesh2_face_x:bounds = "Mesh2_face_xbnds" ;
        double Mesh2_face_y(nMesh2_face) ;
                Mesh2_face_y:standard_name = "latitude" ;
                Mesh2_face_y:long_name = "Characteristics latitude of 2D mesh face." ;
                Mesh2_face_y:units = "degrees_north" ;
                Mesh2_face_y:bounds = "Mesh2_face_ybnds" ;
        double Mesh2_face_xbnds(nMesh2_face,nMaxMesh2_face_nodes) ;
                Mesh2_face_xbnds:standard_name = "longitude" ;
                Mesh2_face_xbnds:long_name = "Longitude bounds of 2D mesh face (i.e. corner coordinates)." ;
                Mesh2_face_xbnds:units = "degrees_east" ;
                Mesh2_face_xbnds:_FillValue = 9.9692099683868690E36;
        double Mesh2_face_ybnds(nMesh2_face,nMaxMesh2_face_nodes) ;
                Mesh2_face_ybnds:standard_name = "latitude" ;
                Mesh2_face_ybnds:long_name = "Latitude bounds of 2D mesh face (i.e. corner coordinates)." ;
                Mesh2_face_ybnds:units = "degrees_north" ;
                Mesh2_face_ybnds:_FillValue = 9.9692099683868690E36;
        double Mesh2_edge_x(nMesh2_edge) ;
                Mesh2_edge_x:standard_name = "longitude" ;
                Mesh2_edge_x:long_name = "Characteristic longitude of 2D mesh edge (e.g. midpoint of the edge)." ;
                Mesh2_edge_x:units = "degrees_east" ;
        double Mesh2_edge_y(nMesh2_edge) ;
                Mesh2_edge_y:standard_name = "latitude" ;
                Mesh2_edge_y:long_name = "Characteristic latitude of 2D mesh edge (e.g. midpoint of the edge)." ;
                Mesh2_edge_y:units = "degrees_north" ;
        // bounds variables for edges skipped

3D layered mesh topology.

For a 3D layered unstructured mesh topology this proposal follows the approach of the existing CF-conventions for structured meshes: horizontal and vertical dimensions are treated separately. For the horizontal plane a 2D unstructured mesh topology is defined, which is extruded in the vertical direction by means of a vertical coordinate. The example below matches the example in the previous section combined with a vertical coordinate according CF-conventions. This example introduces also the attributes mesh and location on the 2D variables "Mesh2_surface" and "Mesh2_depth". For more information about these attributes see the data definition section below.

Image Added

Example:

Code Block
dimensions:
        nMesh2_node = 6 ; // nNodes
        nMesh2_edge = 7 ; // nEdges
        nMesh2_face = 2 ; // nFaces
        nMesh2_face_links = 1 ; // nFacePairs
        nMaxMesh2_face_nodes = 4 ; // MaxNumNodesPerFace
        Mesh2_layers = 10 ;

        Two = 2 ;

variables:
// Mesh topology
        integer Mesh2 ;
                Mesh2:cf_role = "mesh_topology" ;
                Mesh2:long_name = "Topology data of 2D unstructured mesh" ;
                Mesh2:topology_dimension = 2 ;
                Mesh2:node_coordinates = "Mesh2_node_x Mesh2_node_y" ;
                Mesh2:face_node_connectivity = "Mesh2_face_nodes" ;
                Mesh2:edge_node_connectivity = "Mesh2_edge_nodes" ; // attribute required if variables will be defined on edges
                Mesh2:edge_coordinates = "Mesh2_edge_x Mesh2_edge_y" ; // optional attribute (requires edge_node_connectivity)
                Mesh2:face_coordinates = "Mesh2_face_x Mesh2_face_y" ; // optional attribute
                Mesh2:face_edge_connectivity = "Mesh2_face_edges" ; // optional attribute (requires edge_node_connectivity)
                Mesh2:face_face_connectivity = "Mesh2_face_links" ; // optional attribute
        integer Mesh2_face_nodes(nMesh2_face, nMaxMesh2_face_nodes) ;
                Mesh2_face_nodes:cf_role = "face_node_connectivity" ;
                Mesh2_face_nodes:long_name = "Maps every face to its corner nodes." ;
                Mesh2_face_nodes:_FillValue = 999999 ;
                Mesh2_face_nodes:start_index = 1 ;
        integer Mesh2_edge_nodes(nMesh2_edge, Two) ;
                Mesh2_edge_nodes:cf_role = "edge_node_connectivity" ;
                Mesh2_edge_nodes:long_name = "Maps every edge to the two nodes that it connects." ;
                Mesh2_edge_nodes:start_index = 1 ;

// Optional mesh topology variables
        integer Mesh2_face_edges(nMesh2_face, nMaxMesh2_face_nodes) ;
                Mesh2_face_edges:cf_role = "face_edge_connectivity" ;
                Mesh2_face_edges:long_name = "Maps every face to its edges." ;
                Mesh2_face_edges:_FillValue = 999999 ;
                Mesh2_face_edges:start_index = 1 ;
        integer Mesh2_face_links(nMesh2_face_links, Two) ;
                Mesh2_face_links:cf_role = "face_face_connectivity" ;
                Mesh2_face_links:long_name = "Indicates which faces are neighbors." ;
                Mesh2_face_links:start_index = 1 ;

// Mesh node coordinates
        double Mesh2_node_x(nMesh2_node) ;
                Mesh2_node_x:standard_name = "longitude" ;
                Mesh2_node_x:long_name = "Longitude of 2D mesh nodes." ;
                Mesh2_node_x:units = "degrees_east" ;
        double Mesh2_node_y(nMesh2_node) ;
                Mesh2_node_y:standard_name = "latitude" ;
                Mesh2_node_y:long_name = "Latitude of 2D mesh nodes." ;
                Mesh2_node_y:units = "degrees_north" ;

// Optional mesh face and edge coordinate variables
        double Mesh2_face_x(nMesh2_face) ;
                Mesh2_face_x:standard_name = "longitude" ;
                Mesh2_face_x:long_name = "Characteristics longitude of 2D mesh face." ;
                Mesh2_face_x:units = "degrees_east" ;
                Mesh2_face_x:bounds = "Mesh2_face_xbnds" ;
        double Mesh2_face_y(nMesh2_face) ;
                Mesh2_face_y:standard_name = "latitude" ;
                Mesh2_face_y:long_name = "Characteristics latitude of 2D mesh face." ;
                Mesh2_face_y:units = "degrees_north" ;
                Mesh2_face_y:bounds = "Mesh2_face_ybnds" ;
        double Mesh2_face_xbnds(nMesh2_face,nMaxMesh2_face_nodes) ;
                Mesh2_face_xbnds:standard_name = "longitude" ;
                Mesh2_face_xbnds:long_name = "Longitude bounds of 2D mesh face (i.e. corner coordinates)." ;
                Mesh2_face_xbnds:units = "degrees_east" ;
                Mesh2_face_xbnds:_FillValue = 9.9692099683868690E36;
        double Mesh2_face_ybnds(nMesh2_face,nMaxMesh2_face_nodes) ;
                Mesh2_face_ybnds:standard_name = "latitude" ;
                Mesh2_face_ybnds:long_name = "Latitude bounds of 2D mesh face (i.e. corner coordinates)." ;
                Mesh2_face_ybnds:units = "degrees_north" ;
                Mesh2_face_ybnds:_FillValue = 9.9692099683868690E36;
        double Mesh2_edge_x(nMesh2_edge) ;
                Mesh2_edge_x:standard_name = "longitude" ;
                Mesh2_edge_x:long_name = "Characteristic longitude of 2D mesh edge (e.g. midpoint of the edge)." ;
                Mesh2_edge_x:units = "degrees_east" ;
        double Mesh2_edge_y(nMesh2_edge) ;
                Mesh2_edge_y:standard_name = "latitude" ;
                Mesh2_edge_y:long_name = "Characteristic latitude of 2D mesh edge (e.g. midpoint of the edge)." ;
                Mesh2_edge_y:units = "degrees_north" ;
        // bounds variables for edges skipped

// Vertical coordinate
        double Mesh2_layers(Mesh2_layers) ;
                Mesh2_layers:standard_name = "ocean_sigma_coordinate" ;
                Mesh2_layers:long_name = "sigma at layer midpoints" ;
                Mesh2_layers:positive = "up" ;
                Mesh2_layers:formula_terms = "sigma: Mesh2_layers eta: Mesh2_surface depth: Mesh2_depth" ;
        double Mesh2_depth(nMesh2_node) ;
                Mesh2_depth:standard_name = "sea_floor_depth_below_geoid" ;
                Mesh2_depth:units = "m" ;
                Mesh2_depth:positive = "down" ;
                Mesh2_depth:mesh = "Mesh2"
                Mesh2_depth:location = "node" ;
                Mesh2_depth:coordinates = "Mesh2_node_x Mesh2_node_y" ;
        double Mesh2_surface(nMesh2_node) ;
                Mesh2_surface:standard_name = "sea_surface_height_above_geoid" ;
                Mesh2_surface:units = "m" ;
                Mesh2_surface:mesh = "Mesh2"
                Mesh2_surface:location = "face" ;
                Mesh2_surface:coordinates = "Mesh2_face_x Mesh2_face_y" ;

fully 3D unstructured (i.e. non-layered) mesh topology.

For a fully 3D unstructured mesh topology we extend the hierarchy of nodes, edges and faces to volumes. Contrary to layered case this type of mesh requires a fully 3D specification of the mesh; hence we not only need latitude and longitude coordinates but also some kind of elevation coordinate (this probably requires a new standard name).

The topology information is stored as attributes to a dummy variable (in the example below called "Mesh3D") with cf_role mesh_topology.

Required topology attributes

Value

cf_role

mesh_topology

topology_dimension

3

node_coordinates

volume_node_connectivity

volume_shape_type

Optionally required attributes*

face_node_connectivity

edge_node_connectivity

Optional attributes

volume_edge_connectivity

volume_face_connectivity

volume_volume_connectivity

face_edge_connectivity

volume_coordinates

face_coordinates

edge_coordinates

*The "Optionally required" attributes edge_node_connectivity and face_node_connectivity are required only if you want to store data on the edges or faces respectively (i.e. if you mind the numbering order of the edges/faces).

The attribute topology_dimension indicates the highest dimensionality of the geometric elements; for a fully 3-dimensional unstructured mesh this should be 3. The attribute node_coordinates points to the auxiliary coordinate variables representing the node locations (latitude, longitude, elevation and optional other coordinates). These auxiliary coordinate variables will have length nNodes. The attribute volume_node_connectivity points to an index variable identifying for every volume the indices of its corner nodes. For faces in the horizontal plane, it was possible to prescribe the order of the nodes, but this is not possible for the nodes of generic 3D volumes. For this reason we introduce an additional attribute volume_shape_type which points to a flag variable that specifies for every volume its shape:

flag_meaning name

description

tetrahedron

pyramid with triangular base, 4 nodes

pyramid

pyramid with square base, a pentahedron, 5 nodes

wedge

prism with triangular base, a pentahedron, 6 nodes

hexahedron

distorted cube, 8 nodes

These four volume shapes are the ones most commonly used. More shapes can be added; and if necessary, it's possible to add a generic shape type which allows for specification of the volume shape indirectly via volume_face_connectivity and face_node_connectivity variables. Such a generic shape is not included in this proposal since there is no practical need for such feature at this time.

(warning) If all volumes have the same shape type, then the shape typed could be determined based on the number of nodes per volume. Another option could be to allow the volume_shape_type to specify the shape type directly rather than pointing to a variable. However, for the time being we assume that the currently proposed volume shape type variable doesn't have too much impact on the performance.

The order in which the corner nodes of a volume are specified is fixed given its shape; this approach is common in 3D modeling, see e.g. this graph in the OpenFOAM documentation and PARAVIEW or VTK documentation. The volume_node_connectivity array will be a matrix of size nVolumes x MaxNumNodesPerVolume; if a volume has less corner nodes than MaxNumNodesPerVolume then the last node indices shall be equal to _FillValue (which should obviously be larger than the number of nodes in the mesh). For the indexing one may use either 0- or 1-based indexing; the convention used should be specified using a start_index attribute to the index variable (i.e. Mesh3D_vol_nodes in the example below). Consistent with the CF-conventions compression option, the connectivity indices are 0-based by default. See this section on 0-/1-based indexing for more details.

In case you want to define variables on the faces or edges of the 3D mesh topology you need to specify the face_node_connectivity or edge_node_connectivity attribute, respectively, to map faces or edges to nodes. Although the volume to node mapping implicitly also defines the location of the faces and edges, it does not specify their global numbering. Again the indexing convention of face_node_connectivity and edge_node_connectivity should be specified using the start_index attribute to the index variable and 0-based indexing is the default.

Optionally the topology may have the following attributes:

  • volume_face_connectivity pointing to an index variable identifying for every volume the indices of its faces. The order in which the face indices should be specified is determined by the volume geometry type. This connectivity array will be a matrix of size nVolumes x MaxNumFacesPerVolume. If a volume has less faces than MaxNumFacesPerVolume then the last face indices shall be equal to _FillValue, and the indexing convention of volume_edge_connectivity should be specified using the start_index attribute to the index variable and 0-based indexing is the default.
  • volume_edge_connectivity pointing to an index variable identifying for every volume the indices of its edges. The order in which the edge indices should be specified is determined by the volume geometry type. This connectivity array will be a matrix of size nVolumes x MaxNumEdgesPerVolume. Again, if a volume has less edges than MaxNumEdgesPerVolume then the last edge indices shall be equal to _FillValue, and the indexing convention of volume_edge_connectivity should be specified using the start_index attribute to the index variable and 0-based indexing is the default.
  • volume_volume_connectivity pointing to an index variable identifying pairs of volumes that share a face, i.e. are neighbors. This connectivity array will thus be a matrix of size nVolumePairs x 2. As usual the indexing convention of volume_volume_connectivity should be specified using the start_index attribute to the index variable (i.e. Mesh3D_vol_links in the example below) and 0-based indexing is the default.
  • face_edge_connectivity pointing to an index variable identifying for every face the indices of its edges. The edges should be specified in anticlockwise direction as viewed from above. This connectivity array will be a matrix of size nFaces x MaxNumNodesPerFace. As always, if a face has less corners/edges than MaxNumNodesPerFace then the last edge indices shall be equal to _FillValue, and the indexing convention of face_edge_connectivity should be specified using the start_index attribute to the index variable and 0-based indexing is the default.
  • face_node_connectivity pointing to an index variable identifying for every face the indices of its nodes. The nodes should be specified in either clockwise or anticlockwise order. This connectivity array will be a matrix of size nFaces x MaxNumNodesPerFace. Again, if a face has less corners/edges than MaxNumNodesPerFace then the last node indices shall be equal to _FillValue, and the indexing convention of face_node_connectivity should be specified using the start_index attribute to the index variable and 0-based indexing is the default.
  • volume_coordinates, face_coordinates and/or edge_coordinates pointing to the auxiliary coordinate variables associated with the characteristic location of the volumes, faces and edges. These auxiliary coordinate variables will have length nVolumes, nFaces and nEdges respectively, and may have in turn a bounds attribute that specifies the corner coordinates of the volume, face or edge (thereby duplicating the data in the node_coordinates variables). The order in which the corner coordinates of the volumes is given by the volume geometry type.

Image Added

Example:

Code Block
dimensions:
        nMesh3D_node = 12 ; // nNodes
        nMesh3D_edge = 23 ; // nEdges
        nMesh3D_face = 16 ; // nFaces
        nMesh3D_vol  = 4 ; // nVolumes
        nMesh3D_vol_links = 4 ; // nVolumePairs
        nMaxMesh3D_face_nodes = 4 ; // MaxNumNodesPerFace
        nMaxMesh3D_vol_nodes  = 8 ; // MaxNumNodesPerVolume
        nMaxMesh3D_vol_edges  = 12 ; // MaxNumEdgesPerVolume
        nMaxMesh3D_vol_faces  = 6 ; // MaxNumFacesPerVolume

        Two = 2 ;

variables:
// Mesh topology
        integer Mesh3D ;
                Mesh3D:cf_role = "mesh_topology" ;
                Mesh3D:long_name = "Topology data of 3D unstructured mesh" ;
                Mesh3D:topology_dimension = 3 ;
                Mesh3D:node_coordinates = "Mesh3D_node_x Mesh3D_node_y Mesh3D_node_z" ;
                Mesh3D:volume_shape_type = "Mesh3D_vol_types" ;
                Mesh3D:volume_node_connectivity = "Mesh3D_vol_nodes" ;
                Mesh3D:face_node_connectivity = "Mesh3D_face_nodes" ; // attribute required if variables will be defined on faces
                Mesh3D:edge_node_connectivity = "Mesh3D_edge_nodes" ; // attribute required if variables will be defined on edges
                Mesh3D:edge_coordinates = "Mesh3D_edge_x Mesh3D_edge_y Mesh3D_edge_z" ; // optional attribute (requires edge_node_connectivity)
                Mesh3D:face_coordinates = "Mesh3D_face_x Mesh3D_face_y Mesh3D_face_z" ; // optional attribute (requires face_node_connectivity)
                Mesh3D:volume_coordinates = "Mesh3D_vol_x Mesh3D_vol_y Mesh3D_vol_z" ; // optional attribute
                Mesh3D:volume_face_connectivity = "Mesh3D_vol_faces" ; // optional attribute (requires face_node_connectivity)
                Mesh3D:volume_edge_connectivity = "Mesh3D_vol_edges" ; // optional attribute (requires edge_node_connectivity)
                Mesh3D:face_edge_connectivity = "Mesh3D_face_edges" ; // optional attribute (requires face_node_connectivity and edge_node_connectivity)
                Mesh3D:volume_volume_connectivity = "Mesh3D_vol_links" ; // optional attribute
        integer Mesh3D_vol_types(nMesh3D_vol) ;
                Mesh3D_vol_types:cf_role = "volume_shape_type" ;
                Mesh3D_vol_types:long_name = "Specifies the shape of the individual volumes." ;
                Mesh3D_vol_types:flag_range = 0b, 2b ;
                Mesh3D_vol_types:flag_values = 0b, 1b, 2b ;
                Mesh3D_vol_types:flag_meanings = "tetrahedron wedge hexahedron" ;
        integer Mesh3D_vol_nodes(nMesh3D_vol, nMaxMesh3D_vol_nodes) ;
                Mesh3D_vol_nodes:cf_role = "volume_node_connectivity" ;
                Mesh3D_vol_nodes:long_name = "Maps every volume to its corner nodes." ;
                Mesh3D_vol_nodes:_FillValue = 999999 ;
                Mesh3D_vol_nodes:start_index = 1 ;

// Optional mesh topology variables
        integer Mesh3D_edge_nodes(nMesh3D_edge, Two) ;
                Mesh3D_edge_nodes:cf_role = "edge_node_connectivity" ;
                Mesh3D_edge_nodes:long_name = "Maps every edge to the two nodes that it connects." ;
                Mesh3D_edge_nodes:start_index = 1 ;
        integer Mesh3D_face_nodes(nMesh3D_face, nMaxMesh3D_face_nodes) ;
                Mesh3D_face_nodes:cf_role = "face_node_connectivity" ;
                Mesh3D_face_nodes:long_name = "Maps every face to its corner nodes." ;
                Mesh3D_face_nodes:_FillValue = 999999 ;
                Mesh3D_face_nodes:start_index = 1 ;
        integer Mesh3D_vol_faces(nMesh3D_vol, nMaxMesh3D_vol_faces) ;
                Mesh3D_vol_faces:cf_role = "volume_face_connectivity" ;
                Mesh3D_vol_faces:long_name = "Maps every volume to its faces." ;
                Mesh3D_vol_faces:_FillValue = 999999 ;
                Mesh3D_vol_faces:start_index = 1 ;
        integer Mesh3D_vol_edges(nMesh3D_vol, nMaxMesh3D_vol_edges) ;
                Mesh3D_vol_edges:cf_role = "volume_edge_connectivity" ;
                Mesh3D_vol_edges:long_name = "Maps every volume to its edges." ;
                Mesh3D_vol_edges:_FillValue = 999999 ;
                Mesh3D_vol_edges:start_index = 1 ;
        integer Mesh3D_face_edges(nMesh3D_face, nMaxMesh3D_face_nodes) ;
                Mesh3D_face_edges:cf_role = "face_edge_connectivity" ;
                Mesh3D_face_edges:long_name = "Maps every face to its edges." ;
                Mesh3D_face_edges:_FillValue = 999999 ;
                Mesh3D_face_edges:start_index = 1 ;
        integer Mesh3D_vol_links(nMesh3D_vol_links, Two) ;
                Mesh3D_vol_links:cf_role = "volume_volume_connectivity" ;
                Mesh3D_vol_links:long_name = "Indicates which volumes are neighbors." ;
                Mesh3D_vol_links:start_index = 1 ;

// Mesh node coordinates
        double Mesh3D_node_x(nMesh3D_node) ;
                Mesh3D_node_x:standard_name = "longitude" ;
                Mesh3D_node_x:long_name = "Longitude of 3D mesh nodes." ;
                Mesh3D_node_x:units = "degrees_east" ;
        double Mesh3D_node_y(nMesh3D_node) ;
                Mesh3D_node_y:standard_name = "latitude" ;
                Mesh3D_node_y:long_name = "Latitude of 3D mesh nodes." ;
                Mesh3D_node_y:units = "degrees_north" ;
        double Mesh3D_node_z(nMesh3D_node) ;
                Mesh3D_node_z:standard_name = "elevation" ;
                Mesh3D_node_z:long_name = "Elevation of 3D mesh nodes." ;
                Mesh3D_node_z:units = "m" ;

// Optional mesh volume, face and edge coordinate variables
        double Mesh3D_vol_x(nMesh3D_vol) ;
                Mesh3D_vol_x:standard_name = "longitude" ;
                Mesh3D_vol_x:long_name = "Characteristics longitude of mesh volumes." ;
                Mesh3D_vol_x:units = "degrees_east" ;
                Mesh3D_vol_x:bounds = "Mesh3D_vol_xbnds" ;
        double Mesh3D_vol_y(nMesh3D_vol) ;
                Mesh3D_vol_y:standard_name = "latitude" ;
                Mesh3D_vol_y:long_name = "Characteristics latitude of mesh volumes." ;
                Mesh3D_vol_y:units = "degrees_north" ;
                Mesh3D_vol_y:bounds = "Mesh3D_vol_ybnds" ;
        double Mesh3D_vol_z(nMesh3D_vol) ;
                Mesh3D_vol_z:standard_name = "elevation" ;
                Mesh3D_vol_z:long_name = "Characteristics elevation of mesh volumes." ;
                Mesh3D_vol_z:units = "m" ;
                Mesh3D_vol_z:bounds = "Mesh3D_vol_zbnds" ;
        double Mesh3D_vol_xbnds(nMesh3D_vol,nMaxMesh3D_vol_nodes) ;
                Mesh3D_vol_xbnds:standard_name = "longitude" ;
                Mesh3D_vol_xbnds:long_name = "Longitude bounds of mesh volumes (i.e. corner coordinates)." ;
                Mesh3D_vol_xbnds:units = "degrees_east" ;
                Mesh3D_vol_xbnds:_FillValue = 9.9692099683868690E36;
        double Mesh3D_vol_ybnds(nMesh3D_vol,nMaxMesh3D_vol_nodes) ;
                Mesh3D_vol_ybnds:standard_name = "latitude" ;
                Mesh3D_vol_ybnds:long_name = "Latitude bounds of mesh volumes (i.e. corner coordinates)." ;
                Mesh3D_vol_ybnds:units = "degrees_north" ;
                Mesh3D_vol_ybnds:_FillValue = 9.9692099683868690E36;
        double Mesh3D_vol_zbnds(nMesh3D_vol,nMaxMesh3D_vol_nodes) ;
                Mesh3D_vol_zbnds:standard_name = "elevation" ;
                Mesh3D_vol_zbnds:long_name = "Elevation bounds of mesh volumes (i.e. corner coordinates)." ;
                Mesh3D_vol_zbnds:units = "m" ;
                Mesh3D_vol_zbnds:_FillValue = 9.9692099683868690E36;
        double Mesh3D_face_x(nMesh3D_face) ;
                Mesh3D_face_x:standard_name = "longitude" ;
                Mesh3D_face_x:long_name = "Characteristics longitude of mesh faces." ;
                Mesh3D_face_x:units = "degrees_east" ;
        double Mesh3D_face_y(nMesh3D_face) ;
                Mesh3D_face_y:standard_name = "latitude" ;
                Mesh3D_face_y:long_name = "Characteristics latitude of mesh faces." ;
                Mesh3D_face_y:units = "degrees_north" ;
        double Mesh3D_face_z(nMesh3D_face) ;
                Mesh3D_face_z:standard_name = "elevation" ;
                Mesh3D_face_z:long_name = "Characteristics elevation of mesh faces." ;
                Mesh3D_face_z:units = "m" ;
        // bounds variables for faces skipped
        double Mesh3D_edge_x(nMesh3D_edge) ;
                Mesh3D_edge_x:standard_name = "longitude" ;
                Mesh3D_edge_x:long_name = "Characteristic longitude of 2D mesh edge (e.g. midpoint of the edge)." ;
                Mesh3D_edge_x:units = "degrees_east" ;
        double Mesh3D_edge_y(nMesh3D_edge) ;
                Mesh3D_edge_y:standard_name = "latitude" ;
                Mesh3D_edge_y:long_name = "Characteristic latitude of 2D mesh edge (e.g. midpoint of the edge)." ;
                Mesh3D_edge_y:units = "degrees_north" ;
        double Mesh3D_edge_z(nMesh3D_edge) ;
                Mesh3D_edge_z:standard_name = "latitude" ;
                Mesh3D_edge_z:long_name = "Characteristic latitude of 2D mesh edge (e.g. midpoint of the edge)." ;
                Mesh3D_edge_z:units = "degrees_north" ;
        // bounds variables for edges skipped

Data defined on unstructured meshes.

According to CF-conventions a variable defined on a structured mesh is specified as

Code Block
        double waterlevel(time,nmax,mmax) ;
                waterlevel:standard_name = "sea_surface_height_above_geoid" ;
                waterlevel:units = "m" ;
                waterlevel:coordinates = "lat lon" ;

The coordinates attribute refers to the variables that contain the latitude and longitude coordinates. For a curvilinear grid these variables will share two spatial dimensions, here nmax and mmax: lat(nmax,mmax) and lon(nmax,mmax). In numerical models the various quantities are often computed at different locations of the mesh: staggered data. The standard CF-conventions don't offer specific support for this functionality and thus for every stagger location coordinates need to be provided separately: cell center coordinates, corner point coordinates, u-flux point coordinates, and v-flux point coordinates. The underlying topology of the mesh, i.e. how these coordinates (variable definition locations) relate to each other isn't stored in the file. This shortcoming is to some degree solved by the gridspec proposal by Balaji. We introduce here attributes that link to the topological data defined above.

Data variables.

The use of the coordinates attribute is copied from the CF-conventions. It is used to map the values of variables defined on the unstructured meshes directly to their location: latitude, longitude and optional elevation. To map the variable onto the topology of the underlying mesh, two new attributes have been introduced. First, the attribute mesh points to the mesh_topology variable containing the meta-data attributes of the mesh on which the variable has been defined. Second, the attribute location points to the (stagger) location within the mesh at which the variable is defined. Note that in this example the coordinates attribute is redundant since the coordinates could also be obtained by using the face_coordinates attribute of the "Mesh2" variable.

Code Block
        double Mesh2_waterlevel(time,nMesh2_face) ;
                Mesh2_waterlevel:standard_name = "sea_surface_height_above_geoid" ;
                Mesh2_waterlevel:units = "m" ;
                Mesh2_waterlevel:mesh = "Mesh2"
                Mesh2_waterlevel:location = "face" ;
                Mesh2_waterlevel:coordinates = "Mesh2_face_x Mesh2_face_y" ;

Volume and flux variables.

The same mesh geometry can be used in different ways to schematize the hydrodynamic volumes and fluxes. Let's take a simple triangular mesh. From a finite volume point of view this mesh will generally be interpreted as consisting of two volumes with triangular base. However, others may use a continuous Galerkin finite element method that can be shown to be equivalent to a subdivision into four volumes. In the former case the two faces correspond to volumes and the fluxes cross the edges. In the latter case the volumes are defined surrounding the four nodes and the fluxes are directed along the edges. The two abbreviated ncdumps below show how the basic 2D triangular mesh definition can be extended to include this data. The coordinate variables for the volume data now include bounds attributes to define the surface area of the volumes. Note the subtle difference in the long names between the flux variables in the two cases; the standard_name attribute has to make a more formal distinction between the two cases.

Image Added Image Added

Variant 1: Volume at faces:

Code Block
dimensions:
        nMesh2_node = 4 ; // nNodes
        nMesh2_edge = 5 ; // nEdges
        nMesh2_face = 2 ; // nFaces
        nMesh2_face_links = 1 ; // nFacePairs

        Two = 2 ;
        Three = 3 ;

variables:
// Mesh topology
        integer Mesh2 ;
                // as in 2D triangular mesh example
        integer Mesh2_face_nodes(nMesh2_face, Three) ;
           
Mesh3_face_contour_y:units
 
=
 
"m"
 
;
  
// as in 2D triangular mesh example
        integer 
Mesh3
Mesh2_
face
edge_
contour_y:long_name = "List of y-points that form outline of flow volume" ;
nodes(nMesh2_edge, Two) ;
                // as in 
double Mesh3_edge_x(nMesh3_edge) ;
2D triangular mesh example

// Optional mesh topology variables
        integer Mesh2_face_edges(nMesh2_face, Three) ;
     
Mesh3_edge_x:standard_name
 
=
 
"projection_x_coordinate"
 
;
  
      // as in 2D triangular mesh example
    
Mesh3_edge_x:long_name
 
=
 
"Center
 
coordinate
 
of edges (velocity point)." ;
integer Mesh2_face_links(nMesh2_face_links, Two) ;
                // as 
Mesh3_edge_x:units = "m" ;
in 2D triangular mesh example

// Mesh node coordinates
        double 
Mesh3
Mesh2_
edge
node_
y
x(
nMesh3
nMesh2_
edge
node) ;
  
              // 
Mesh3_edge_y:standard_name = "projection_y_coordinate" ;
as in 2D triangular mesh example
        
Mesh3_edge_y:long_name = "Center coordinate of edges (velocity point)." ;
double Mesh2_node_y(nMesh2_node) ;
                // as in 2D triangular mesh example

// 
Mesh3_edge_y:units = "m" ;
Optional mesh face and edge coordinate variables
        
integer
double 
Mesh3
Mesh2_
edge
face_
nodes
x(
nMesh3_edge, Two
nMesh2_face) ;

                
Mesh3
Mesh2_
edge
face_
nodes
x:
long
standard_name = "
link between two nodes
longitude" ;

        
integer
 
Mesh3_face_nodes(nMesh3_face,
 
nMaxMesh3_face_nodes)
 
;
  
   Mesh2_face_x:long_name = "Characteristics longitude of 2D mesh triangle (e.g. circumcenter 
Mesh3_face_nodes:long_name = "Mapping from faces to nodes." ;
coordinate)." ;
               
integer
 
Mesh3
Mesh2_face_
links(nMesh3_face_links, Two) ;
x:units = "degrees_east" ;
                
FlowLink:long_name
Mesh2_face_x:bounds = "
link/interface between two flow elements (faces)
Mesh2_face_xbnds" ;

        double 
Mesh3
Mesh2_face_
layers
y(
Mesh3
nMesh2_
layers
face) ;
                
Mesh3
Mesh2_face_
layers
y:standard_name = "
ocean_sigma_coordinate
latitude" ;
                
Mesh3
Mesh2_face_
layers
y:long_name = "
sigma at layer midpoints
Characteristics latitude of 2D mesh triangle (e.g. circumcenter coordinate)." ;
                
Mesh3
Mesh2_face_
layers
y:
positive
units = "
up
degrees_north" ;
           
Mesh3_layers:formula_terms
 
=
 
"sigma:
 
Mesh3_layers
 
eta:
 
Mesh3_zwl depth: Mesh3_depth
Mesh2_face_y:bounds = "Mesh2_face_ybnds" ;
        double 
Mesh3
Mesh2_face_
interfaces
xbnds(
Mesh3
nMesh2_
interfaces
face,Three) ;
                
Mesh3
Mesh2_face_
interfaces
xbnds:standard_name = "
ocean_sigma_coordinate
longitude" ;
                
Mesh3
Mesh2_face_
interfaces
xbnds:long_name = "
sigma at layer interfaces
Longitude bounds of 2D mesh triangle (i.e. corner coordinates)." ;
                
Mesh3
Mesh2_face_
interfaces
xbnds:
positive
units = "
up
degrees_east" ;
        
Mesh3_interfaces:formula_terms = "sigma: Mesh3_interfaces eta: Mesh3_zwl depth: Mesh3_depth" ;
double Mesh2_face_ybnds(nMesh2_face,Three) ;
                
integer Mesh3
Mesh2_face_ybnds:standard_name = "latitude" ;

                
Mesh3
Mesh2_face_ybnds:long_name = "
Topology
Latitude 
data
bounds of
Mesh3
 2D mesh triangle (i.e. corner coordinates)." ;
                
Mesh3:dimensionality
Mesh2_face_ybnds:units = 
2
"degrees_north" ;
        double Mesh2_edge_x(nMesh2_edge) ;
       
Mesh3:locations
 
=
 
"face
 
edge
 
node"
 
;
    // as in 2D triangular mesh example
      
Mesh3:node_coordinates
 
= "Mesh3_node_x Mesh3_node_y" ;
 double Mesh2_edge_y(nMesh2_edge) ;
                // as in 2D triangular mesh example

// Volume and flux data
      
Mesh3:edge_coordinates
 
= "Mesh3_edge_x Mesh3_edge_y"
 double Mesh2_volumes(nMesh2_face) ;
                
Mesh3
Mesh2_volumes:
edge
long_
nodes
name = "
Mesh3_edge_nodes
volumes" ;
                
Mesh3:face_coordinates
Mesh2_volumes:units = "
Mesh3_face_x Mesh3_face_y
m3" ;
                
Mesh3:face_nodes
Mesh2_volumes:mesh = "
Mesh3_face_nodes
Mesh2" ;
                
Mesh3:face_connectivity
Mesh2_volumes:location = "
Mesh3_
face
_links
" ;
                
Mesh3:parent_mesh
Mesh2_volumes:coordinates = "
CombinedMesh
Mesh2_face_x Mesh2_face_y" ;

        
integer
double 
CombiMesh
Mesh2_
edge_mesh
fluxes(
nCombiMesh_contacts, Two
nMesh2_edge) ;

                
CombiMesh_edge
Mesh2_
mesh
fluxes:long_name = "
Mesh
flux 
number
across 
of contact
edge" ;

                
CombiMesh
Mesh2_
edge_mesh:valid_range
fluxes:units = 
0, 2
"m3 s-1" ;
                
CombiMesh
Mesh2_
edge_mesh:valid_values
fluxes:mesh = 
0, 1, 2;
"Mesh2"
                
CombiMesh
Mesh2_
edge_mesh:flag_meanings
fluxes:location = "
Mesh1 Mesh2 Mesh3
edge" ;
        
integer
 
CombiMesh_edge(nCombiMesh_contacts,
 
Two)
 
;
 
    Mesh2_fluxes:coordinates = "Mesh2_edge_x Mesh2_edge_y" ;

Variant 2: Volume at nodes:

Code Block
dimensions:
        
CombiMesh_edge:long_name
nMesh2_node = 
"Edge
4 
number of contact" ;
; // nNodes
        
integer CombinedMesh(nCombinedMesh_contacts, Four)
nMesh2_edge = 5 ; // nEdges
        nMesh2_face = 2 ; // nFaces
   
CombinedMesh:long_name
 
=
 
"Topology
 
data
 
of CombinedMesh" ;
 nMesh2_face_links = 1 ; // nFacePairs
        
CombinedMesh:sub_meshes
nMaxMesh2_bnds =
"Mesh1 Mesh2 Mesh3" ;
 6 ;

        Two = 2 ;
        
CombinedMesh:contact
Three = 
"CombiMesh_edge_mesh CombiMesh_edge" ;
3 ;

variables:
// Mesh topology
      
double
 
time(time)
 
;
integer Mesh2 ;
                
time:standard_name = "time" ;
// as in 2D triangular mesh example
        integer Mesh2_face_nodes(nMesh2_face, Three) ;
   
time:units
 
=
 
"seconds
 
since
 
1992-08-31
 
00:00:00"
 
;
  
//
 
1D
 
mesh
 
nodes
 
may
 
be
// 
merged
as 
with
in 2D triangular mesh example
 
faces.
 
Or
 
2D
 
mesh
 
faces
 
may
 
be
 
masked and omitted in // solution output. As a result, allow for a map between flow node numbers and basic mesh entities/numbers). // // Relation between basic mesh (nodes/edges/faces) and computational mesh (corners/interfaces/cells) // (For cell center data, nMesh1_cell <= nMesh1_node, nMesh2_cell <= nMesh2_face) // (In case of identity maps, leave out the location_map, and specify coordinates, grid and location // directly in attributes of solution variable.)
integer Mesh2_edge_nodes(nMesh2_edge, Two) ;
                // as in 2D triangular mesh example

// Optional mesh topology variables
        integer Mesh2_face_edges(nMesh2_face, Three) ;
                // as in 2D triangular mesh example
        
double
integer 
Mesh1
Mesh2_
cell
face_
x
links(
nMesh1_cell
nMesh2_face_links, Two) ;
                // as in 2D triangular mesh example

// Mesh node coordinates
       
Mesh1_cell_x:standard_name = "projection_x_coordinate" ;
 double Mesh2_node_x(nMesh2_node) ;
                
Mesh1
Mesh2_
cell
node_x:
long
standard_name = "
flow cell circumcenter x-coordinate
longitude" ;

                
Mesh1
Mesh2_
cell
node_x:
units
long_name = "
m"
Longitude 
;
of 2D 
double Mesh1_cell_y(nMesh1_cell)
mesh nodes." ;
                
Mesh1
Mesh2_
cell
node_
y
x:
standard_name
units = "
projection
degrees_
y_coordinate
east" ;

                
Mesh1
Mesh2_
cell
node_
y
x:
long_name
bounds = "
flow cell circumcenter y-coordinate
Mesh2_node_xbnds" ;

        
Mesh1_cell_y:units = "m" ;
double Mesh2_node_y(nMesh2_node) ;
               
double
 
Mesh1_flownode(nMesh1_cell)
Mesh2_node_y:standard_name = "latitude" ;
                
Mesh1
Mesh2_
cell
node_
node
y:long_name = "
map
Latitude 
from
of 
flowcell
2D 
to 1D
mesh 
node
nodes." ;

                
Mesh1
Mesh2_
cell
node_
node
y:
grid
units = "
Mesh1
degrees_north" ;
                
Mesh1
Mesh2_
cell
node_
node
y:
location
bounds = "Mesh2_node_ybnds" ;
 
//
   
location
 
of
 
flow
 
cell
 
on topological mesh entity.
double Mesh2_node_xbnds(nMesh2_node, nMaxMesh2_bnds) ;
             
double Mesh1_flowlink(nMesh1_interface)
   Mesh2_node_xbnds:standard_name = "longitude" ;
                
Mesh1
Mesh2_
cell
node_
node
xbnds:long_name =
"map from flowlink to 1D mesh edge" ;
 "List of x-points that form outline of flow volume" ;
                
Mesh1
Mesh2_
cell
node_
node
xbnds:
grid
units = "
Mesh1
degrees_east" ;
                
Mesh1
Mesh2_
cell
node_
node
xbnds:
location
_FillValue = 
"edge" ; // location of flow link on topological mesh entity.
9.9692099683868690E36;
        double 
Mesh1
Mesh2_node_
zwl
ybnds(
time
nMesh2_node, 
nMesh1
nMaxMesh2_
cell
bnds) ;

                
Mesh1
Mesh2_node_
zwl
ybnds:standard_name = "
sea_surface_height_above_geoid
latitude" ;
                
Mesh1
Mesh2_node_
zwl
ybnds:units = "
m
degrees_north" ;
                
Mesh1
Mesh2_node_
zwl:location_map = "Mesh1_flownode
ybnds:long_name = "List of y-points that form outline of flow volume" ;
                
Mesh1
Mesh2_node_
zwl
xbnds:
coordinates
_FillValue = 
"Mesh1_cell_x Mesh1_cell_y" ;
9.9692099683868690E36;

// Optional mesh face and edge coordinate variables
        double 
Mesh1
Mesh2_face_
u(time, nMesh1_interface
x(nMesh2_face) ;

                
Mesh1_zwl:standard_name = "sea_water_speed" ;
// as in 2D triangular mesh example
        double 
Mesh1_u:long_name = "Velocity (along the edge)" ;
Mesh2_face_y(nMesh2_face) ;
           
Mesh1_u:units = "m
 
s-1"
 
;
  
 // as in 2D triangular mesh example
        double 
Mesh1
Mesh2_
u:location_map = "Mesh1_flowlink"
edge_x(nMesh2_edge) ;
                
Mesh1_u:coordinates = "Mesh1_interface_x Mesh1_interface_y" ;
// as in 2D triangular mesh example
        double Mesh2_edge_
depth
y(nMesh2_
node
edge) ;
                // 
as in 2D triangular mesh example

// Volume and flux data
       
Mesh2_depth:standard_name = "sea_floor_depth_below_geoid"
 double Mesh2_volumes(nMesh2_node) ;
                Mesh2_
depth
volumes:
units
long_name = "
m
volumes" ;
                Mesh2_
depth
volumes:
positive
units = "
down
m3" ;
                Mesh2_
depth
volumes:
grid
mesh = "Mesh2" ;
                Mesh2_
depth
volumes:location = "node" ;
                Mesh2_
depth
volumes:coordinates = "Mesh2_node_x Mesh2_node_y" ;
        double Mesh2_
zwl
fluxes(
time,
nMesh2_
face
edge) ;

                Mesh2_
zwl
fluxes:
standard
long_name = "
sea_surface_height_above_geoid" ; Mesh2_zwl:units = "m
flux along edge" ;
                Mesh2_
zwl
fluxes:
grid
units = "
Mesh2" Mesh2_zwl:location = "face
m3 s-1" ;
   
Mesh2_zwl:coordinates
 
=
 
"Mesh2_face_x
 
Mesh2_face_y"
 
;
        
double
 Mesh2_
ucx(time, nMesh2_face) ;
fluxes:mesh = "Mesh2"
                Mesh2_
ucx
fluxes:
standard_name
location = "
eastward_sea_water_velocity
edge" ;
                Mesh2_
ucx:units = "m s-1" ; Mesh2_ucx:grid = "Mesh2" Mesh2_ucx:location = "face" ; Mesh2_ucx:coordinates = "Mesh2_face_x Mesh2_face_y" ; double Mesh2_ucy(time, nMesh2_face) ; Mesh2_ucy:standard_name = "northward_sea_water_velocity" ; Mesh2_ucy:units = "m s-1" ; Mesh2_ucy:grid = "Mesh2" Mesh2_ucy:location = "face" ; Mesh2_ucy:coordinates = "Mesh2_face_x Mesh2_face_y" ; double Mesh2_unorm(time, nMesh2_edge) ;
fluxes:coordinates = "Mesh2_edge_x Mesh2_edge_y" ;

Location index set.

Some variables may only be defined at specific locations within the mesh, e.g. only at boundary points or at special locations like weirs and gates. To save space and to improve readability, the concept of a location_index_set is introduced. It is basically identical to the compression option in the the CF-conventions except for the fact that the compression works on a (set of) orthogonal coordinate dimension(s) and the location_index_set works on a topology location.

The location index set is an integer variable that contains the indices of the locations at which a specific quantity is defined. The example below defines a location index set "Mesh1_set" as a subset of the "node"s of Mesh1 (red points). The attribute location_index_set of the variable "Mesh_waterlevel" points to this index set and the coordinates attribute points to the corresponding (subset) of latitude and longitude coordinates. The mesh and location attributes of the location_index_set variable are required; the coordinates attribute is optional. Note that the coordinates attributes on both "Mesh1_cell" and "Mesh1_waterlevel" are again redundant since the coordinates could also be obtained by using the index set "Mesh1_set" and the node_coordinates attribute of the "Mesh1" variable. Consistent with all other index variables defined here, the indexing convention of the location index set should be specified using the start_index attribute to the index variable and 0-based indexing is the default. See this section on 0-/1-based indexing for more details.

Contrary to a coordinate variable, the index set doesn't have to be monotonic. So, it can be used for creating subsets of the original locations as well as for renumbering the locations. If the location_index_set attribute is used on a variable, then the mesh and location attributes should not also be used on that variable.

Image Added

Code Block
dimensions:
        
Mesh2_unorm:long_name
nMesh1_set = 
"Normal component of velocity at the interface" ; Mesh2_unorm:units = "m s-1" ;
4 ;

variables:
        
Mesh2_unorm:grid = "Mesh2"
integer Mesh1_set(nMesh1_set) ;
                
Mesh2
Mesh1_
unorm
set:
location
cf_role = "
edge
location_index_set" ; 

                
Mesh2
Mesh1_
unorm
set:
coordinates
long_name = "
Mesh2_edge_x Mesh2_edge_y" ; todo: meaning? integer Mesh2_edgetype(nMesh2_edge) ;
Defines Mesh1_set as subset of the nodes of Mesh1." ;
                
Mesh2
Mesh1_
edgetype
set:
long_name
mesh = "
Type of edge
Mesh1" ;

                
Mesh2
Mesh1_
edgetype
set:
valid_range
location = 
0,
"node" 
2
;
                
Mesh2
Mesh1_
edgetype
set:
valid
start_
values
index =
0,
 1
,
 
2
;
                Mesh1_set:coordinates = "Mesh1_set_x Mesh1_set_y" ;
       
Mesh2_edgetype:flag_meanings = "closed_edge open_internal_edge open_boundary_edge"
 double Mesh1_set_x(nMesh1_set) ;
                
Mesh2
Mesh1_set_
edgetype
x:
grid
standard_name = "
Mesh2
longitude" ;
                
Mesh2
Mesh1_set_
edgetype
x:
location
long_name = "
edge
Characteristic longitude of set (e.g. longitude of node)." ;

                
Mesh2
Mesh1_set_
edgetype
x:
coordinates
units = "
Mesh2_edge_x Mesh2_edge_y
degrees_east" ;

        double 
Mesh3
Mesh1_set_
depth
y(
nMesh3
nMesh1_
node
set) ;

                
Mesh3
Mesh1_set_
depth
y:standard_name = "
sea_floor_depth_below_geoid
latitude" ;
                
Mesh3
Mesh1_set_
depth
y:
units
long_name = "
m
Characteristic latitude of set (e.g. latitude of node)" ;
                
Mesh3
Mesh1_set_
depth
y:
positive
units = "
down"
degrees_north" ;

        double Mesh1_waterlevel(time, nMesh1_set) ;
                
Mesh3
Mesh1_
depth
waterlevel:
grid
standard_name = "
Mesh3"
sea_surface_height_above_geoid" ;
                
Mesh3
Mesh1_
depth
waterlevel:
location
units = "
node
m" ;
                
Mesh3
Mesh1_
depth:coordinates
waterlevel:location_index_set = "
Mesh3_node_x Mesh3_node_y
Mesh1_set" ;
       
double
 
Mesh3_zwl(time,
 
nMesh3_face)
 
;
  
    Mesh1_waterlevel:coordinates = "Mesh1_set_x 
Mesh3_zwl:standard_name = "sea_surface_height_above_geoid" ; Mesh3_zwl:units = "m" ; Mesh3_zwl:grid = "Mesh3"
Mesh1_set_y" ;

0-/1-based indexing

The indexing using by the CF compression option is 0-based. Therefore, it is most consistent for this CF extension for unstructured data to also use 0-based indexing, which means that points, edges, faces and volumes will be numbered starting with 0. This convention is consistent with languages like C and Java, but unlike FORTRAN and MATLAB. Since many of the unstructured models have been programed in FORTRAN and legacy netCDF files exist that use 1-based indexing (which could be upgraded to be consistent with this new proposal using ncML if 1-based indexing were allowed), we propose to support both 0- and 1-based indexing by means of the start_index attribute. You will find below two examples of the same network geometry using either 0- or 1-based indexing. Switching between 0- and 1-based indexing is as easy as adding 1 to or subtracting 1 from the indices upon reading or writing depending on the setting of start_index; allowing both options should only have a minor impact on the reading routines and no effect at all on the rest of your code.

Example of 0-based indexing:

Image Added

Code Block
dimensions:
        
Mesh3_zwl:location
nMesh1_node = 
"face"
5 ;
 // nNodes
        
Mesh3_zwl:coordinates
nMesh1_edge = 
"Mesh3_face_x Mesh3_face_y"
4 ;
 
double Mesh3_ucx(time, nMesh3_face, nMesh3_layer) ;
// nEdges

        Two = 2;

variables:
// Mesh topology
       
Mesh3_ucx:standard_name = "eastward_sea_water_velocity"
 integer Mesh1 ;
                
Mesh3_ucx:units
Mesh1:cf_role = "
m s-1
mesh_topology" ;
                
Mesh3_ucx:grid
Mesh1:long_name = "
Mesh3"
Topology data of 1D network" ;
                
Mesh3_ucx:location
Mesh1:topology_dimension = 
"face"
1 ;
                
Mesh3
Mesh1:node_
ucx:
coordinates = "
Mesh3
Mesh1_
face
node_x 
Mesh3
Mesh1_
face
node_y
Mesh3_layers
" ;
   
            
double
 
Mesh3_ucy(time, nMesh3_face, nMesh3_layer) ;
Mesh1:edge_node_connectivity = "Mesh1_edge_nodes" ;
                
Mesh3_ucy
Mesh1:
standard
edge_
name
coordinates = "
northward_sea_water_velocity
Mesh1_edge_x Mesh1_edge_y" ;
 // optional attribute
        
Mesh3_ucy:units = "m s-1"
integer Mesh1_edge_nodes(nMesh1_edge, Two) ;
                
Mesh3
Mesh1_edge_
ucy
nodes:
grid
cf_role = "
Mesh3"
edge_node_connectivity" ;
                
Mesh3
Mesh1_edge_
ucy
nodes:
location
long_name = "
face" ;
Maps every edge/link to the two nodes that it connects." ;
         
Mesh3_ucy:coordinates
 
=
 
"Mesh3_face_x
 
Mesh3_face_y
 
Mesh3_layers"
 
;
  
double Mesh3_unorm(time, nMesh3_edge, nMesh3_layer) ;
Mesh1_edge_nodes:start_index = 0 ; // default setting, attribute could have been skipped.

// Coordinate variables skipped
        
data:

    Mesh1 = 0 
Mesh3_unorm:long_name = "Normal component of velocity at the interface" ;
; // dummy
    
    Mesh1_edge_nodes =
         0,  2,
      
Mesh3_unorm:units
 
=
 
"m
 
s-
1
"
, 
;
 
2,
         2,  
3,
    
Mesh3_unorm:grid
 
=
 
"Mesh3"
   3,  4 ;

Example of 1-based indexing:

Image Added

Code Block
dimensions:
        
Mesh3_unorm:location
nMesh1_node = 
"edge"
5 ; // nNodes
        nMesh1_edge = 4 ; // nEdges

   
Mesh3_unorm:coordinates
 
=
 
"Mesh3_edge_x
 
Mesh3_edge_y
 
Mesh3_layers"
 
;
Two = 2;

TODO
variables:
meaning

// Mesh topology
        integer 
Mesh3_edgetype(nMesh3_edge)
Mesh1 ;

                
Mesh3_edgetype
Mesh1:
long
cf_
name
role = "
Type of edge
mesh_topology" ;

                
Mesh3_edgetype
Mesh1:
valid
long_
range
name =
0, 2
 "Topology data of 1D network" ;
                
Mesh3_edgetype
Mesh1:
valid
topology_
values
dimension =
0,
 1
,
 
2
;
                
Mesh3_edgetype
Mesh1:
flag
node_
meanings
coordinates = "
closed_edge open_internal_edge open_boundary_edge
Mesh1_node_x Mesh1_node_y" ;
                
Mesh3_edgetype:grid
Mesh1:edge_node_connectivity = "
Mesh3"
Mesh1_edge_nodes" ;
                
Mesh3_edgetype:location
Mesh1:edge_coordinates = "Mesh1_edge_x Mesh1_edge_y" ; // optional attribute
        integer Mesh1_edge_nodes(nMesh1_edge, Two) ;
         
Mesh3_edgetype:coordinates
       Mesh1_edge_nodes:cf_role = "
Mesh3_
edge_
x Mesh3_edge_y
node_connectivity" ;
     
//
 
global
 
attributes:
  
       Mesh1_edge_nodes:long_name = "Maps every edge/link to the two nodes 
:institution
that 
=
it 
"Deltares
connects." ;

                
:references
Mesh1_edge_nodes:start_index = 
"http:
1 ;

//
www.deltares.nl" ;
 Coordinate variables skipped
        
data:

    Mesh1 
:source
= 
"UNSTRUC"
0 ; // dummy
    
    Mesh1_edge_nodes =
       
:history
 
= "Created on 2010-04-12
 1, 
Bert
 
Jagers\n"
3,
    
"2010-06-01, distinguish
 
basic
 
mesh/net
 
and
 
flow
 
mesh
2, 
include mapping between the two, Arthur van Dam" ;
 3,
         3,  4,
         4,  
:Conventions = "CF-1.4:Deltares-0.1" ;
5 ;