Reading In Grid Data#

UXarray offers support for loading and representing unstructured grids by utilizing existing Xarray functionality paired with new routines that are specifically written for operating on unstructured grids.

Overview#

When working with unstructured grids, the grid definition and data variables are often stored as separate files (most of the time as netCDF). This means that there are multiple separate files that need to be read in at once.

For example, the NOAA Geoflow project consists of 4 files (1 grid file and 3 data files). This project follows the UGRID conventions. Special thanks to John Clyne, Shilpi Gupta, and the VAPOR team for providing these data!

geoflow-small
│   grid.nc
│   v1.nc
│   v2.nc
│   v3.nc

Opening The Grid and Data Files with Xarray#

First, read in the grid definition and data variable netCCDF files by using xarray.open_dataset. In addition, xr.open_mf_dataset can be used for combining multiple data variables into a single xarray.Dataset.

import xarray as xr
# Base data path
base_path = "../../test/meshfiles/ugrid/geoflow-small/"

# Path to Grid file
grid_path = base_path + "grid.nc"

# Paths to Data Variable files
var_names = ['v1.nc', 'v2.nc', 'v3.nc']
data_paths = [base_path + name for name in var_names]
# Open the Grid file
grid_ds = xr.open_dataset(grid_path)

# Open a single file or multiple files
v1_ds = xr.open_dataset(data_paths[0])
multi_data_ds = xr.open_mfdataset(data_paths)
grid_ds
<xarray.Dataset>
Dimensions:          (nMeshFaces: 3840, nFaceNodes: 4, nMeshNodes: 6000,
                      meshLayers: 20)
Coordinates:
    mesh_node_x      (nMeshNodes) float64 ...
    mesh_node_y      (nMeshNodes) float64 ...
Dimensions without coordinates: nMeshFaces, nFaceNodes, nMeshNodes, meshLayers
Data variables:
    mesh             int32 ...
    mesh_face_nodes  (nMeshFaces, nFaceNodes) float64 ...
    mesh_depth       (meshLayers, nMeshNodes) float64 ...
multi_data_ds
<xarray.Dataset>
Dimensions:  (time: 1, meshLayers: 20, nMeshNodes: 6000)
Coordinates:
  * time     (time) float64 13.0
Dimensions without coordinates: meshLayers, nMeshNodes
Data variables:
    v1       (time, meshLayers, nMeshNodes) float64 dask.array<chunksize=(1, 20, 6000), meta=np.ndarray>
    v2       (time, meshLayers, nMeshNodes) float64 dask.array<chunksize=(1, 20, 6000), meta=np.ndarray>
    v3       (time, meshLayers, nMeshNodes) float64 dask.array<chunksize=(1, 20, 6000), meta=np.ndarray>

Representing The Unstructured Grid with UXarray#

import uxarray as ux
# Construct a UXarray Grid object from `grid_ds`
grid = ux.Grid(grid_ds)
grid.ds
<xarray.Dataset>
Dimensions:          (nMeshFaces: 3840, nFaceNodes: 4, nMeshNodes: 6000,
                      meshLayers: 20)
Coordinates:
    mesh_node_x      (nMeshNodes) float64 ...
    mesh_node_y      (nMeshNodes) float64 ...
Dimensions without coordinates: nMeshFaces, nFaceNodes, nMeshNodes, meshLayers
Data variables:
    mesh             int32 ...
    mesh_face_nodes  (nMeshFaces, nFaceNodes) uint64 0 1 6 5 ... 5994 5999 5998
    mesh_depth       (meshLayers, nMeshNodes) float64 ...

As can be seen, the Grid object has its own grid.ds of type xarray.Dataset to define the grid topology. However, the Grid object has further attributes, variables, and functions to specify the unstructured grid and be executed on it, which can be explored in the next notebooks.