xmsgrid

Introduction

The purpose of this tutorial is to provide explanation on how to use xmsgrid to create two and three dimensional unstructured grids, or UGrids. A UGrid has a set of points, and cells which are defined using those points.

In [1]:
from shapely.geometry import Polygon
import holoviews as hv
import pandas as pd

import numpy as np
import geoviews as gv
import xarray as xr
import cartopy.crs as ccrs

import hvplot.pandas
import hvplot.xarray

from xms import grid
import ugrid_tools

hv.extension("bokeh")

Defining Ugrid Cells

A ugrid cell is defined by a sequence of numbers, called a cell stream. A cell stream is composed of the following parts:

  • First, a numerical declaration of the shape
  • Second, the number of points
  • Third, the point indices

There are many different kinds of cells available, each specific to 2D or 3D shapes.

  • Supported 2D grid cells include: triangle (5), polygon (7), pixel (8), quad (9).
  • Supported 3D grid cells include: tetrahedron (10), voxel (11), hexahedron (12), wedge (13), pyramid (14), and polyhedron (42).

An example cell stream:

|- Shape declaration
|  |- # of points
|  |  |- Indicies
|  |  |-----------|
7, 5, 0, 1, 2, 3, 4

The cell definitions mirror VTK cell definitions which are available on page 9 of VTK File Formats for VTK version 4.2 at https://www.vtk.org/wp-content/uploads/2015/04/file-formats.pdf.

For convinience, the xmugrid_celltype_enum was created to put human-readable names to the numerical shape declarations.

Detailed Examples of Cell Streams

Shapes.png

2D

A triangle (5) has 3 points and the points are declared in a counter-clockwise direction. A cell stream example for a triangle is: 5, 3, 0, 1, 2.

A polygon (7) does not a have a defined number of points, but the points are still declared in a counter-clockwise direction. A cell stream example for a polygon is: 7, 5, 0, 1, 2, 3, 4.

A pixel (8) has 4 orthogonially defined points. A cell stream example for a pixel is: 8, 4, 0, 1, 2, 3.

A quad (9) has 4 points declared in a counter-clockwise direction. A cell stream example for a quad is: 9, 4, 0, 1, 2, 3.

3D

A tetrahedron (10) has 4 points. A cell stream example for a tetrahedron is: 10, 4, 0, 1, 2, 3.

A voxel (11) has 8 orthogonially defined points. A cell stream example for a voxel is: 11, 8, 0, 1, 2, 3, 4, 5, 6, 7.

A hexahedron (12) has 8 points. A cell stream example for a hexahedron is: 12, 8, 0, 1, 2, 3, 4, 5, 6, 7.

A wedge (13) has 6 points. A cell stream example for a wedge is: 13, 6, 0, 1, 2, 3, 4, 5.

A pyramid (14) has 5 points. A cell stream example for a pyramid is: 14, 5, 0, 1, 2, 3, 4.

A polyhedron (42) does not a have a defined number of points. A polyhedron cell stream has the following format: The cell type, number of faces, and then repeated for each face the number of points in the face followed by the points in the face declared in a counter-clockwise direction. A cell stream example for a polyhedron is: 42, 6, 4, 10, 13, 12, 11, 4, 10, 11, 15, 16, 4, 11, 12, 16, 15, 4, 12, 13, 17, 16, 4, 13, 10, 14, 17, 4, 14, 15, 16, 17.

In [2]:
pts = [
    (0, 0, 0), (2, 0, 0), (1, 2, 0), (4, 1, 0), (3, 3, 0), (-1, 3, 0), (-2, 1, 0),
    (0, -2, 0), (2, -2, 0)
]

cells = [
    grid.ugrid.UGrid.cell_type_enum.TRIANGLE, 3, 0, 1, 2,
    grid.ugrid.UGrid.cell_type_enum.QUAD, 4, 1, 3, 4, 2,
    grid.ugrid.UGrid.cell_type_enum.QUAD, 4, 2, 5, 6, 0,
    grid.ugrid.UGrid.cell_type_enum.QUAD, 4, 0, 7, 8, 1,
    grid.ugrid.UGrid.cell_type_enum.TRIANGLE, 3, 1, 8, 3,
    grid.ugrid.UGrid.cell_type_enum.TRIANGLE, 3, 2, 4, 5,
    grid.ugrid.UGrid.cell_type_enum.TRIANGLE, 3, 0, 6, 7,
]

Creating UGrid Objects

Here is an example UGrid with 2D cells. This grid will have 3 quadrilateral cells and 4 triangle cells.

In [3]:
ugrid= grid.ugrid.UGrid(pts, cells)     
polygons, lines, points = ugrid_tools.create_hv_objects_for_image(ugrid)
polygons.redim.range(x=(-3, 5), y=(-3, 4)) * points.options(size=5)
Out[3]:

Point Functions

The following section demonstrates the xmsgrid api for getting information about the UGrid points.

In [4]:
print(f"UGrid Point Count: {ugrid.point_count}")  # Should be 9 
UGrid Point Count: 9
In [5]:
# get_locations()

locations = ugrid.locations  # Returns a list of point locations

print(f"Number of Point Locations: {len(locations)}")
print(f"Location 1: {locations[0]}")
print(f"Location 2: {locations[1]}")
print(f"...")
print(f"Location n: {locations[-1]}")
Number of Point Locations: 9
Location 1: [0. 0. 0.]
Location 2: [2. 0. 0.]
...
Location n: [ 2. -2.  0.]
In [6]:
# get_point_location()

location = ugrid.get_point_location(1)
print(f"Location: {location}")
Location: (2.0, 0.0, 0.0)
In [7]:
# set_point_location()

location = ugrid.get_point_location(1)
print(f"Location Before: {location}")
location = ugrid.set_point_location(1, (3, 1, 0))
location = ugrid.get_point_location(1)
print(f"Location After: {location}")
location = ugrid.set_point_location(1, (2, 0, 0))
location = ugrid.get_point_location(1)
print(f"Location Reset: {location}")
Location Before: (2.0, 0.0, 0.0)
Location After: (3.0, 1.0, 0.0)
Location Reset: (2.0, 0.0, 0.0)
In [8]:
# get_points_locations()

indices = [2, 4, 6]
locations = ugrid.get_points_locations(indices)

for i, value in enumerate(locations):
    print(f"location {indices[i]}: {value}")
location 2: [1. 2. 0.]
location 4: [3. 3. 0.]
location 6: [-2.  1.  0.]
In [9]:
# get_extents()

extents_min, extents_max = ugrid.extents

print(f"Extents Minimum: {extents_min}")
print(f"Extents Maximum: {extents_max}")
Extents Minimum: (-2.0, -2.0, 0.0)
Extents Maximum: (4.0, 3.0, 0.0)
In [10]:
# get_point_adjacent_cells()

cell_indices = ugrid.get_point_adjacent_cells(2)

print(f"Adjacent Cells: {cell_indices}")
Adjacent Cells: (0, 1, 2, 5)
In [11]:
# get_points_adjacent_cells()

point_indices = [0, 1]
cell_indices = ugrid.get_points_adjacent_cells(point_indices)

print(f"Adjacent Cells: {cell_indices}")
Adjacent Cells: (0, 3)

Working with UGrid Cell Functions

The following section demonstrates the xmsgrid api for getting information about the UGrid cells.

In [12]:
# get_cell_type()

cell_type_0 = ugrid.get_cell_type(0)
cell_type_1 = ugrid.get_cell_type(2)

print(f"Cell 0 Type: {cell_type_0}")
print(f"Cell 1 Type: {cell_type_1}")
Cell 0 Type: ugrid_celltype_enum.TRIANGLE
Cell 1 Type: ugrid_celltype_enum.QUAD
In [13]:
# get_dimension_counts()

_0d, _1d, _2d, _3d = ugrid.dimension_counts

print(f"Number of 0d dimensions: {_0d}")
print(f"Number of 1d dimensions: {_1d}")
print(f"Number of 2d dimensions: {_2d}")
print(f"Number of 3d dimensions: {_3d}")
Number of 0d dimensions: 0
Number of 1d dimensions: 0
Number of 2d dimensions: 7
Number of 3d dimensions: 0
In [14]:
# get_dimension_count()

dimension = ugrid.get_cell_dimension(2)
bad_dimension = ugrid.get_cell_dimension(99)

print(f"Dimension of cell 02: {dimension}")
print(f"Dimension of cell 99: {bad_dimension}")
Dimension of cell 02: 2
Dimension of cell 99: -1

Working with UGrid Cell Stream functions

The following section demonstrates the xmsgrid api for getting information about the UGrid cell streams.

In [15]:
# get_cell_cellstream()

successful_2, cellstream_2 = ugrid.get_cell_cellstream(2)
successful_5, cellstream_5 = ugrid.get_cell_cellstream(5)
fail_99, _ = ugrid.get_cell_cellstream(99)

if successful_2:
    print(f"Cellstream for cell 2: {cellstream_2}")
if successful_5:
    print(f"Cellstream for cell 5: {cellstream_5}")
if not fail_99:
    print(f"Cellstream 99 failed as expected...")
Cellstream for cell 2: (9, 4, 2, 5, 6, 0)
Cellstream for cell 5: (5, 3, 2, 4, 5)
Cellstream 99 failed as expected...
In [16]:
# get_cell_adjacent_cells()

adjacent_cells = ugrid.get_cell_adjacent_cells(3)

print(f"Adjacent cells to cell 3: {adjacent_cells}")
Adjacent cells to cell 3: (0, 2, 6, 4, 1)
In [17]:
# get_cell_plan_view_polygon()

success, plan_view_polygon_3 = ugrid.get_cell_plan_view_polygon(3)
fail, _ = ugrid.get_cell_plan_view_polygon(99)

if success:
    print(f"Plan view polygon indices for cell 3: {plan_view_polygon_3}")
if not fail:
    print(f"Plan view polygon for cell 99 failed as expected...")
Plan view polygon indices for cell 3: [[ 0.  0.  0.]
 [ 0. -2.  0.]
 [ 2. -2.  0.]
 [ 2.  0.  0.]]
Plan view polygon for cell 99 failed as expected...
In [18]:
# get_cell_edge_count()

cell_edge_count_0 = ugrid.get_cell_edge_count(0)
cell_edge_count_1 = ugrid.get_cell_edge_count(1)

print(f"Cell edge count for cell 0: {cell_edge_count_0}")
print(f"Cell edge count for cell 1: {cell_edge_count_1}")
Cell edge count for cell 0: 3
Cell edge count for cell 1: 4
In [19]:
#### get_cell_edge()

edge_start, edge_end = ugrid.get_cell_edge(4, 2)

print(f"Edge Start Index: {edge_start}")
print(f"Edge End Index: {edge_end}")
Edge Start Index: 3
Edge End Index: 1
In [20]:
#get_cell_edge_adjacent_cells()

adjacent_cell = ugrid.get_cell_edge_adjacent_cells(0, 2)

print(f"Cells adjacent to cell 0 on edge 2: {adjacent_cell}")
Cells adjacent to cell 0 on edge 2: (2,)
In [21]:
#get_cell2d_edge_adjacent_cell()

adjacent_cell = ugrid.get_cell_2d_edge_adjacent_cell(0, 2)

print(f"Cells adjacent to cell 0 on edge 2: {adjacent_cell}")
Cells adjacent to cell 0 on edge 2: 2
In [22]:
# get_edge_adjacent_cells()

adjacent_cells_2_to_0 = ugrid.get_edge_adjacent_cells((2, 0))
adjacent_cells_1_to_2 = ugrid.get_edge_adjacent_cells((1, 3))

print(f"cells adjacent to edge (2, 0): {adjacent_cells_2_to_0}")
print(f"cells adjacent to edge (1, 2): {adjacent_cells_1_to_2}")
cells adjacent to edge (2, 0): (0, 2)
cells adjacent to edge (1, 2): (1, 4)
In [23]:
# get_point_adjacent_locations

adjacent_locations = ugrid.get_point_adjacent_locations(2)
print(f"Points connected by edges from point 2:\n {adjacent_locations}")
Points connected by edges from point 2:
 [[ 0.  0.  0.]
 [ 2.  0.  0.]
 [ 3.  3.  0.]
 [-1.  3.  0.]]
In [24]:
# get_cell_edges()

cell_edges = ugrid.get_cell_edges(4)

print(f"Cell edges for cell 4:\n {cell_edges}")
Cell edges for cell 4:
 ((1, 8), (8, 3), (3, 1))

Reading a UGrid from a file

In [25]:
my_ugrid = grid.ugrid.ugrid_utils.read_ugrid_from_ascii_file("ug3.xmsgrid")

Display UGrid

In [26]:
polygons, lines, points = ugrid_tools.create_hv_objects_for_image(my_ugrid, ccrs.LambertConformal())
%opts Polygons Points [width=700, height=700]
polygons * lines * points
Out[26]:
In [ ]: