Gridding Ugrid1d Network Data to 2D Grids#

In this example, we demonstrate how to interpolate and grid data from a network of line elements (UGRID1D) to a 2D structured grid. We’ll cover the following xugrid techniques:

  • Creating Ugrid1d networks from node and edge arrays

  • Finding intersections between 1D and 2D grid edges

  • Refining networks by inserting intersection points

  • Topology-aware Laplace interpolation along networks

  • Converting node data to edge data

  • Regridding from 1D networks to 2D grids using NetworkGridder

  • Visualization of mixed 1D and 2D grids

We’ll start by setting up the structured grid first and converting it to a Ugrid2d grid.

import numpy as np
import xarray as xr

import xugrid as xu


def make_structured_grid(nrow, ncol, dx, dy):
    if dy >= 0:
        raise ValueError("dy must be negative.")

    shape = nrow, ncol

    xmin = 0.0
    xmax = dx * ncol
    ymin = 0.0
    ymax = abs(dy) * nrow
    dims = ("y", "x")

    y = np.arange(ymax, ymin, dy) + 0.5 * dy
    x = np.arange(xmin, xmax, dx) + 0.5 * dx
    coords = {"y": y, "x": x}

    return xr.DataArray(np.ones(shape, dtype=np.int32), coords=coords, dims=dims)


structured_grid = make_structured_grid(10, 10, 1.5, -1.5)
uda = xu.UgridDataArray.from_structured2d(structured_grid)
ugrid2d = uda.ugrid.grid

uda
<xarray.DataArray (mesh2d_nFaces: 100)> Size: 400B
array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=int32)
Coordinates:
  * mesh2d_nFaces  (mesh2d_nFaces) int64 800B 0 1 2 3 4 5 ... 94 95 96 97 98 99


Next, we create a 1D network. This network consists of 5 nodes and 4 edges. At node 2 the network forks to two branches. The data is located assigned to the nodes.

node_xy = np.array(
    [
        [0.0, 0.0],
        [5.0, 5.0],
        [10.0, 5.0],
        [15.0, 0.0],
        [15.0, 10.0],
    ]
)
edge_nodes = np.array(
    [
        [0, 1],
        [1, 2],
        [2, 3],
        [2, 4],
    ]
)
network = xu.Ugrid1d(*node_xy.T, -1, edge_nodes)
data = xr.DataArray(
    np.array([1, 1.5, 2, 4, -4], dtype=float), dims=(network.node_dimension,)
)
uda_1d = xu.UgridDataArray(data, grid=network)

uda_1d
<xarray.DataArray (network1d_nNodes: 5)> Size: 40B
array([ 1. ,  1.5,  2. ,  4. , -4. ])
Coordinates:
  * network1d_nNodes  (network1d_nNodes) int64 40B 0 1 2 3 4


Let’s plot the 1D network on top of the 2D grid. The 1D network is shown in light gray, the 2D grid in dark gray. The network’s nodes are colored by data values.

uda_1d.ugrid.plot(zorder=10)
uda_1d.ugrid.grid.plot(color="black", alpha=0.5)
ugrid2d.plot(color="gray", alpha=0.5)
network gridder
<matplotlib.collections.LineCollection object at 0x7fed5ac7de50>

Intersect edges#

First, we need to find the intersection points between the edges of the network and the 2D grid. We can do this by using the xugrid.Ugrid1D.intersect_edges() method.

edges_coords = ugrid2d.node_coordinates[ugrid2d.edge_node_connectivity]
_, _, intersections_xy = network.intersect_edges(edges_coords)

Let’s look at the intersection points.

import matplotlib.pyplot as plt

fig, ax = plt.subplots()

ugrid2d.plot(
    ax=ax,
    color="gray",
    alpha=0.5,
)
plt.scatter(*intersections_xy.T)
network gridder
<matplotlib.collections.PathCollection object at 0x7fed5adf0320>

Let’s take a look at the individual values. We can see that the intersection points are located at the vertices of the 2D grid. This introduces duplicate intersection points.

intersections_xy
array([[14. ,  9. ],
       [13.5,  8.5],
       [12.5,  7.5],
       [12. ,  7. ],
       [ 4.5,  4.5],
       [ 6. ,  5. ],
       [ 7.5,  5. ],
       [ 9. ,  5. ],
       [11. ,  6. ],
       [10.5,  4.5],
       [10.5,  5.5],
       [ 4.5,  4.5],
       [ 3. ,  3. ],
       [10.5,  4.5],
       [12. ,  3. ],
       [ 3. ,  3. ],
       [ 1.5,  1.5],
       [12. ,  3. ],
       [13.5,  1.5],
       [ 1.5,  1.5],
       [ 0. ,  0. ],
       [13.5,  1.5]])

Because the line interesects cell vertices, we have duplicate intersection points. Remove duplicates by finding the uniques, as duplicates are not allowed in the network refinement we’ll do in the next step.

_intersections_xy = np.unique(intersections_xy, axis=0)

_intersections_xy
array([[ 0. ,  0. ],
       [ 1.5,  1.5],
       [ 3. ,  3. ],
       [ 4.5,  4.5],
       [ 6. ,  5. ],
       [ 7.5,  5. ],
       [ 9. ,  5. ],
       [10.5,  4.5],
       [10.5,  5.5],
       [11. ,  6. ],
       [12. ,  3. ],
       [12. ,  7. ],
       [12.5,  7.5],
       [13.5,  1.5],
       [13.5,  8.5],
       [14. ,  9. ]])

Refining the network#

Let’s refine the network by the intersection points. This will create new nodes at the intersection points and add edges between the new nodes and the original

refined_network, refined_node_index = network.refine_by_vertices(
    _intersections_xy, return_index=True
)

refined_network
<xarray.Dataset> Size: 632B
Dimensions:               (network1d_nEdges: 19, two: 2, network1d_nNodes: 20)
Coordinates:
    network1d_node_x      (network1d_nNodes) float64 160B 0.0 5.0 ... 13.5 14.0
    network1d_node_y      (network1d_nNodes) float64 160B 0.0 5.0 ... 8.5 9.0
Dimensions without coordinates: network1d_nEdges, two, network1d_nNodes
Data variables:
    network1d             int64 8B 0
    network1d_edge_nodes  (network1d_nEdges, two) int64 304B 0 5 5 6 ... 19 19 4
Attributes:
    Conventions:  CF-1.9 UGRID-1.0

We’ll create a new UgridDataArray with the refined network. The data will be set to NaN at the refined nodes. The original data will be set to the original nodes.

refined_data = xr.DataArray(
    np.empty_like(refined_network.node_x), dims=(refined_network.node_dimension,)
)
uda_1d_refined = xu.UgridDataArray(refined_data, grid=refined_network)

# Set data
node_dim = uda_1d.ugrid.grid.node_dimension
uda_1d_refined.data[uda_1d[node_dim].data] = uda_1d.data
uda_1d_refined.data[refined_node_index] = np.nan

uda_1d_refined
<xarray.DataArray (network1d_nNodes: 20)> Size: 160B
array([ 1. ,  1.5,  2. ,  4. , -4. ,  nan,  nan,  nan,  nan,  nan,  nan,
        nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan,  nan])
Coordinates:
  * network1d_nNodes  (network1d_nNodes) int64 160B 0 1 2 3 4 ... 15 16 17 18 19


Interpolation#

Next, interpolate the data to the refined nodes, we can do this with a laplace interpolation. This nicely interpolates the data along the network: Notice that the two branches on the right-hand side are interpolated separately.

uda_1d_interpolated = uda_1d_refined.ugrid.laplace_interpolate()

fig, ax = plt.subplots()
uda_1d_interpolated.ugrid.plot(ax=ax, zorder=10)
uda_1d_interpolated.ugrid.grid.plot(ax=ax, color="black", alpha=0.5, zorder=2)
ugrid2d.plot(ax=ax, color="gray", alpha=0.5, zorder=3)
network gridder
<matplotlib.collections.LineCollection object at 0x7fed5dd9b3e0>

The final step before we can grid the network is setting the data to edge centroids. We do this by averaging the data at the nodes that are connected to the edges. This is done by using the edge_node_connectivity of the network.

edge_data = xr.DataArray(
    data=uda_1d_interpolated.data[refined_network.edge_node_connectivity].mean(axis=1),
    dims=(refined_network.edge_dimension,),
)
uda_1d_edge = xu.UgridDataArray(edge_data, grid=refined_network)

fig, ax = plt.subplots()
uda_1d_edge.ugrid.plot(ax=ax, zorder=10)
ugrid2d.plot(ax=ax, color="gray", alpha=0.5, zorder=3)
network gridder
<matplotlib.collections.LineCollection object at 0x7fed695a4cb0>

Gridding#

Finally, we can grid the data to the 2D grid. We can do this by using the xugrid.regrid.gridder.NetworkGridder class. This class takes the Ugrid1d grid as source and Ugrid2d grit as target, the method to use for gridding and the data to grid.

from xugrid.regrid.gridder import NetworkGridder

gridder = NetworkGridder(
    source=uda_1d_edge.ugrid.grid,
    target=ugrid2d,
    method="mean",
)

gridder
<xugrid.regrid.gridder.NetworkGridder object at 0x7fed5fa32a50>

Next, we can grid the data. Call the xugrid.regrid.gridder.NetworkGridder.regrid() method to grid the data.

network_gridded = gridder.regrid(uda_1d_edge)

fig, ax = plt.subplots()
network_gridded.ugrid.plot(ax=ax)
uda_1d_edge.ugrid.grid.plot(ax=ax, color="black", alpha=0.5, zorder=2)
ugrid2d.plot(ax=ax, color="gray", alpha=0.5, zorder=3)
network gridder
<matplotlib.collections.LineCollection object at 0x7fed695a79b0>

Total running time of the script: (0 minutes 0.334 seconds)

Gallery generated by Sphinx-Gallery