Grid Topology Overview#

Authors: Philip Chmielowiec, Orhan Eroglu

As discussed in our first notebook, UXarray uses the UGRID conventions as a foundation for represented Unstructured Grids. Here we’ll see how to access the underlying dimensions, coordinates, and connectivity variables that are available in UXarray.

Constructing the Grid Topology#

The grid topology is defined as a Grid object in UXarray. A Grid object can be created in different ways.

Constructing through uxarray.open_grid()#

If the intent is to explore only the grid topology instead of having any data sets with it, analyzing data variables, etc., a standalone Grid object can be instantiated as follows:

import uxarray
import uxarray as ux
# Base data path
base_path = "../../test/meshfiles/"

# Grid Path (MPAS Example)
grid_mpas_path = base_path + "/mpas/QU/mesh.QU.1920km.151026.nc"
grid_mpas = ux.open_grid(grid_mpas_path)
grid_mpas
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/latest/lib/python3.11/site-packages/uxarray/grid/grid.py:275: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
  for key, value in zip(self._ds.dims.keys(), self._ds.dims.values()):
<frozen _collections_abc>:880: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/latest/lib/python3.11/site-packages/uxarray/grid/grid.py:435: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
  return self._ds.dims["n_face"]
<uxarray.Grid>
Original Grid Type: MPAS
Grid Dimensions:
  * n_node: 320
  * n_face: 162
  * n_edge: 480
  * n_max_face_nodes: 6
  * n_max_faces_per_node: 3
  * two: 2
  * n_nodes_per_face: (162,)
Grid Coordinates (Spherical):
  * node_lon: (320,)
  * node_lat: (320,)
  * edge_lon: (480,)
  * edge_lat: (480,)
  * face_lon: (162,)
  * face_lat: (162,)
Grid Coordinates (Cartesian):
  * node_x: (320,)
  * node_y: (320,)
  * node_z: (320,)
  * edge_x: (480,)
  * edge_y: (480,)
  * edge_z: (480,)
  * face_x: (162,)
  * face_y: (162,)
  * face_z: (162,)
Grid Connectivity Variables:
  * face_node_connectivity: (162, 6)
  * edge_node_connectivity: (480, 2)
  * face_edge_connectivity: (162, 6)
  * edge_face_connectivity: (480, 2)
  * node_face_connectivity: (320, 3)

Constructing through UxDataset#

If the intent is to have an unstructured grid-aware dataset, i.e. UxDataset, and investigate the grid topology through it instead, the uxgrid property can be used. To be more precise, when a UxDataset object, or likewise UxDataArray object, is generated (e.g. through uxarray.open_dataset()), a Grid object via UxDataset.uxgrid, or UxDataArray.uxgrid, property is also created automatically and assigned to the dataset or variable.

The grid topology can be seen through this property as follows:

# Data File Path (UGRID Example)
grid_ne30_path = base_path + "/ugrid/outCSne30/outCSne30.ug"
data_ne30_path = base_path + "/ugrid/outCSne30/outCSne30_vortex.nc"
uxds = ux.open_dataset(grid_ne30_path, data_ne30_path)
uxds.uxgrid
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/latest/lib/python3.11/site-packages/uxarray/core/utils.py:22: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
  if ds.dims[dim] == grid.n_face:
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/latest/lib/python3.11/site-packages/uxarray/grid/grid.py:435: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
  return self._ds.dims["n_face"]
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/latest/lib/python3.11/site-packages/uxarray/grid/grid.py:275: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
  for key, value in zip(self._ds.dims.keys(), self._ds.dims.values()):
<frozen _collections_abc>:880: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
<uxarray.Grid>
Original Grid Type: UGRID
Grid Dimensions:
  * n_face: 5400
  * n_max_face_nodes: 4
  * n_node: 5402
  * n_nodes_per_face: (5400,)
Grid Coordinates (Spherical):
  * node_lon: (5402,)
  * node_lat: (5402,)
Grid Coordinates (Cartesian):
Grid Connectivity Variables:
  * face_node_connectivity: (5400, 4)

In addition, a previously-created Grid object can always be assigned to a new UxDataset as its uxgrid property as follows:

uxds_mpas = ux.UxDataset(uxgrid=grid_mpas)
uxds_mpas.uxgrid
<uxarray.Grid>
Original Grid Type: MPAS
Grid Dimensions:
  * n_node: 320
  * n_face: 162
  * n_edge: 480
  * n_max_face_nodes: 6
  * n_max_faces_per_node: 3
  * two: 2
  * n_nodes_per_face: (162,)
Grid Coordinates (Spherical):
  * node_lon: (320,)
  * node_lat: (320,)
  * edge_lon: (480,)
  * edge_lat: (480,)
  * face_lon: (162,)
  * face_lat: (162,)
Grid Coordinates (Cartesian):
  * node_x: (320,)
  * node_y: (320,)
  * node_z: (320,)
  * edge_x: (480,)
  * edge_y: (480,)
  * edge_z: (480,)
  * face_x: (162,)
  * face_y: (162,)
  * face_z: (162,)
Grid Connectivity Variables:
  * face_node_connectivity: (162, 6)
  * edge_node_connectivity: (480, 2)
  * face_edge_connectivity: (162, 6)
  * edge_face_connectivity: (480, 2)
  * node_face_connectivity: (320, 3)

Grid Attributes#

If our input grid contained additional attributes that were not representable by the UGRID conventions, they would be stored here

uxds.uxgrid.parsed_attrs
{}
uxds_mpas.uxgrid.parsed_attrs
{'on_a_sphere': 'YES',
 'sphere_radius': 1.0,
 'is_periodic': 'NO',
 'history': 'MpasMeshConverter.x base_grids/x1.162.grid.nc base_meshes/x1.162.grid.nc',
 'mesh_spec': '1.0',
 'Conventions': 'MPAS',
 'source': 'MpasMeshConverter.x',
 'file_id': 'rku96q0z66'}

Grid Coordinates#

The coordinates by default are represented in terms of longitude and latitude.

uxds.uxgrid.node_lon
<xarray.DataArray 'node_lon' (n_node: 5402)> Size: 43kB
array([-45.        ,  45.        , 135.        , ..., 141.09968961,
       138.03317102, 135.        ])
Coordinates:
    node_lon  (n_node) float64 43kB -45.0 45.0 135.0 ... 141.1 138.0 135.0
    node_lat  (n_node) float64 43kB ...
Dimensions without coordinates: n_node
uxds.uxgrid.node_lat
<xarray.DataArray 'node_lat' (n_node: 5402)> Size: 43kB
[5402 values with dtype=float64]
Coordinates:
    node_lon  (n_node) float64 43kB -45.0 45.0 135.0 ... 141.1 138.0 135.0
    node_lat  (n_node) float64 43kB ...
Dimensions without coordinates: n_node
Attributes:
    standard_name:  latitude
    long_name:      latitude of 2D mesh nodes
    units:          degrees_north

If you wish to use the Cartesian coordinate system, you can access the following attributes, which will internally construct a set of Cartesian coordinates derived from the previous set.

uxds.uxgrid.node_x
<xarray.DataArray 'node_x' (n_node: 5402)> Size: 43kB
array([ 0.57735027,  0.57735027, -0.57735027, ..., -0.58878977,
       -0.57332232, -0.55611709])
Coordinates:
    node_lon  (n_node) float64 43kB -45.0 45.0 135.0 ... 141.1 138.0 135.0
    node_lat  (n_node) float64 43kB -35.26 -35.26 -35.26 ... 40.84 39.55 38.14
Dimensions without coordinates: n_node
Attributes:
    standard_name:  cartesian x
    units:          m
uxds.uxgrid.node_y
<xarray.DataArray 'node_y' (n_node: 5402)> Size: 43kB
array([-0.57735027,  0.57735027,  0.57735027, ...,  0.47509872,
        0.51562103,  0.55611709])
Coordinates:
    node_lon  (n_node) float64 43kB -45.0 45.0 135.0 ... 141.1 138.0 135.0
    node_lat  (n_node) float64 43kB -35.26 -35.26 -35.26 ... 40.84 39.55 38.14
Dimensions without coordinates: n_node
Attributes:
    standard_name:  cartesian y
    units:          m
uxds.uxgrid.node_z
<xarray.DataArray 'node_z' (n_node: 5402)> Size: 43kB
array([-0.57735027, -0.57735027, -0.57735027, ...,  0.65391729,
        0.63673894,  0.6176306 ])
Coordinates:
    node_lon  (n_node) float64 43kB -45.0 45.0 135.0 ... 141.1 138.0 135.0
    node_lat  (n_node) float64 43kB -35.26 -35.26 -35.26 ... 40.84 39.55 38.14
Dimensions without coordinates: n_node
Attributes:
    standard_name:  cartesian z
    units:          m

Grid Connectivity#

Connectivity variables are used to describe how various geometric elements (nodes, faces, edges) can be manipulated and interconnected to represent the topology of the unstructured grid.

As described in the UGRID conventions, these connectivity variables are stored as integer arrays and may contain a Fill Value. UXarray standardizes both of these at the data loading step, meaning that the data type and fill value can always be guaranteed to be the following:

ux.INT_DTYPE
numpy.int64
ux.INT_FILL_VALUE
-9223372036854775808

Below we can see how to access these connectivity variables.

uxds.uxgrid.face_node_connectivity
<xarray.DataArray 'face_node_connectivity' (n_face: 5400, n_max_face_nodes: 4)> Size: 173kB
array([[   0,    8,  356,  124],
       [   8,    9,  357,  356],
       [   9,   10,  358,  357],
       ...,
       [5399, 5400,  299,  300],
       [5400, 5401,  298,  299],
       [5401,  297,    6,  298]])
Dimensions without coordinates: n_face, n_max_face_nodes
Attributes:
    cf_role:      face_node_connectivity
    start_index:  0
    _FillValue:   -9223372036854775808
uxds.uxgrid.n_nodes_per_face
<xarray.DataArray 'n_nodes_per_face' (n_face: 5400)> Size: 43kB
array([4, 4, 4, ..., 4, 4, 4])
Dimensions without coordinates: n_face
Attributes:
    long_name:  number of non-fill value nodes for each face
uxds.uxgrid
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/latest/lib/python3.11/site-packages/uxarray/grid/grid.py:275: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
  for key, value in zip(self._ds.dims.keys(), self._ds.dims.values()):
<frozen _collections_abc>:880: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
<uxarray.Grid>
Original Grid Type: UGRID
Grid Dimensions:
  * n_face: 5400
  * n_max_face_nodes: 4
  * n_node: 5402
  * n_nodes_per_face: (5400,)
Grid Coordinates (Spherical):
  * node_lon: (5402,)
  * node_lat: (5402,)
Grid Coordinates (Cartesian):
  * node_x: (5402,)
  * node_y: (5402,)
  * node_z: (5402,)
Grid Connectivity Variables:
  * face_node_connectivity: (5400, 4)

As we can see above, these are the only two connectivity variables listed. In addition to these, UXarray provides support for constructing additional connectivity variables.

uxds.uxgrid.edge_node_connectivity
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/latest/lib/python3.11/site-packages/uxarray/grid/grid.py:435: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
  return self._ds.dims["n_face"]
<xarray.DataArray 'edge_node_connectivity' (n_edge: 10800, Two: 2)> Size: 173kB
array([[   0,    8],
       [   0,  123],
       [   0,  124],
       ...,
       [5398, 5399],
       [5399, 5400],
       [5400, 5401]])
Dimensions without coordinates: n_edge, Two
Attributes:
    cf_role:          edge_node_connectivity
    _FillValue:       -9223372036854775808
    long_name:        Maps every edge to the two nodes that it connects
    start_index:      0
    inverse_indices:  [  0  25 369 ...  19  20 886]
    fill_value_mask:  [False False False ... False False False]
uxds.uxgrid.face_edge_connectivity
<xarray.DataArray 'face_edge_connectivity' (n_face: 5400, n_max_face_edges: 4)> Size: 173kB
array([[    0,    25,   369,     2],
       [   24,    28,  1056,    25],
       [   27,    31,  1058,    28],
       ...,
       [10798,   889,   887,   892],
       [10799,   886,   884,   889],
       [  883,    19,    20,   886]])
Dimensions without coordinates: n_face, n_max_face_edges
Attributes:
    cf_role:      face_edges_connectivity
    start_index:  0
    long_name:    Maps every edge to the two nodes that it connects
uxds.uxgrid
<uxarray.Grid>
Original Grid Type: UGRID
Grid Dimensions:
  * n_face: 5400
  * n_max_face_nodes: 4
  * n_node: 5402
  * n_edge: 10800
  * Two: 2
  * n_max_face_edges: 4
  * n_nodes_per_face: (5400,)
Grid Coordinates (Spherical):
  * node_lon: (5402,)
  * node_lat: (5402,)
Grid Coordinates (Cartesian):
  * node_x: (5402,)
  * node_y: (5402,)
  * node_z: (5402,)
Grid Connectivity Variables:
  * face_node_connectivity: (5400, 4)
  * edge_node_connectivity: (10800, 2)
  * face_edge_connectivity: (5400, 4)

These additional variables are constructed upon calling their respective attributes and are now stored under the uxgrid property. Additionally, the Mesh2_node_cart_x, Mesh2_node_cart_y, and Mesh2_node_cart_z that we constructed earlier are now also shown here.

Grid Dimensions#

uxds.uxgrid.n_node
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/latest/lib/python3.11/site-packages/uxarray/grid/grid.py:429: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
  return self._ds.dims["n_node"]
5402
uxds.uxgrid.n_edge
/home/docs/checkouts/readthedocs.org/user_builds/uxarray/conda/latest/lib/python3.11/site-packages/uxarray/grid/grid.py:444: FutureWarning: The return type of `Dataset.dims` will be changed to return a set of dimension names in future, in order to be more consistent with `DataArray.dims`. To access a mapping from dimension names to lengths, please use `Dataset.sizes`.
  return self._ds.dims["n_edge"]
10800
uxds.uxgrid.n_face
5400
uxds.uxgrid.n_max_face_nodes
4
uxds.uxgrid.n_max_face_edges
4