Topological Aggregations#
Data variables are typically mapped to either the nodes, edges, or faces of an unstructured grid. The data on each of these elements can be manipulated and aggregated to perform various operations, such as mean, min, max and many others. This section will introduce the concept of Topological Aggregations and how to perform them using UXarray.
import uxarray as ux
What are Aggregations?#
An aggregation is an operation that processes data and returns a summarized output. In the context of Numpy, this includes functions such as:
np.mean()
: Calculate the average value from an array of elementsnp.min()
: Calculate the minimum value from an array of elementsnp.std()
: Calculate the standard deviation from an array of elements
In the context of a one-dimensional array, the aggregation is performed over the entire array. Otherwise, it is performed across a specific axis.
What are Topological Aggregations?#
When working with unstructured grids, data variables are mapped to either nodes, edges, or faces and stored as one-dimensional slices in memory, with no spatial relationship between neighbors. This means that performing a regular aggregation as discussed above would not consider the topology of the grid elements.
A topological aggregation can be thought of as performing multiple aggregations on a per-element basis. For example, instead of computing the average across all values, we can compute the average of all the nodes that surround each face and store the result on each face.
By utilizing connectivity information, we can perform the following topological aggregations:
Node to Face: Applied to the nodes that surround each face
Node to Edge: Applied to the nodes that saddle each edge
Edge to Node: Applied to the edges that saddle each node
Edge to Face: Applied to the edges that surround each face
Face to Node: Applied to the faces that surround each node
Face to Edge: Applied to the faces that saddle each edge
UXarray supports the following topological aggregation functions:
UxDataArray.topological_mean()
UxDataArray.topological_max()
UxDataArray.topological_min()
UxDataArray.topological_prod()
UxDataArray.topological_sum()
UxDataArray.topological_std()
UxDataArray.topological_var()
UxDataArray.topological_median()
UxDataArray.topological_all()
UxDataArray.topological_any()
Each of these aggregations performs the same operation described in Numpy, but is applied on a per-element basis.
For the remainder of this guide, we will be using the topological_mean
aggregation, but can be swapped for any of the above methods if desired.
Data#
The data used in this section is the quad hexagon mesh, with three random data variables mapped to the nodes, edges, and faces.
grid_path = "../../test/meshfiles/ugrid/quad-hexagon/grid.nc"
data_paths = [
"../../test/meshfiles/ugrid/quad-hexagon/random-node-data.nc",
"../../test/meshfiles/ugrid/quad-hexagon/random-edge-data.nc",
"../../test/meshfiles/ugrid/quad-hexagon/random-face-data.nc",
]
uxds = ux.open_mfdataset(grid_path, data_paths)
uxds
<xarray.UxDataset> Size: 312B Dimensions: (n_node: 16, n_edge: 19, n_face: 4) Dimensions without coordinates: n_node, n_edge, n_face Data variables: random_data_node (n_node) float64 128B dask.array<chunksize=(16,), meta=np.ndarray> random_data_edge (n_edge) float64 152B dask.array<chunksize=(19,), meta=np.ndarray> random_data_face (n_face) float64 32B dask.array<chunksize=(4,), meta=np.ndarray>
We can visualize the data on each element by using different markers:
(
uxds.uxgrid.plot(line_color="black")
* uxds["random_data_node"]
.plot.points(
cmap="inferno", size=150, marker="circle", clabel=None, tools=["hover"]
)
.relabel("Node Data")
* uxds["random_data_edge"]
.plot.points(
cmap="inferno", size=150, marker="square", clabel=None, tools=["hover"]
)
.relabel("Edge Data")
* uxds["random_data_face"]
.plot.points(
cmap="inferno", size=150, marker="triangle", clabel=None, tools=["hover"]
)
.relabel("Face Data")
).opts(legend_position="top_right")
Node Aggregations#
The follow aggregations are for node-centered data.
Node to Face#
We can aggregate the data from the nodes that surround each face and store the result on each face.

uxda_node_face_agg = uxds["random_data_node"].topological_mean(destination="face")
(
uxds.uxgrid.plot(line_color="black")
* uxds["random_data_node"]
.plot.points(
cmap="inferno", size=150, marker="circle", clabel=None, tools=["hover"]
)
.relabel("Node Data")
* uxda_node_face_agg.plot.points(
cmap="inferno", size=150, marker="triangle", clabel=None, tools=["hover"]
).relabel("Node to Face Mean")
).opts(title="Node to Face Aggregation (Mean)", legend_position="top_right")
One use case for aggregating node-centered data to each face is that it allows for the result to be plotted as Polygons.
uxda_node_face_agg.plot.polygons(
cmap="inferno",
title="Polygon Plot of Node to Face Aggregation (Mean)",
tools=["hover"],
).opts(title="Node to Face Aggregation (Mean)")