cape.gruvoc.umeshbase: Base class for unstructured mesh

This module provides the base class for unstructured mesh handling in the GRU VOC tools.

cape.gruvoc.umeshbase.CoordBasis

alias of CoordSysBasis

cape.gruvoc.umeshbase.InterpWeightsTuple

alias of InterpWeights

class cape.gruvoc.umeshbase.SegmentedSlice(nodes, q)

Bases: tuple

nodes

Alias for field number 0

q

Alias for field number 1

class cape.gruvoc.umeshbase.SurfZone(nodes, jnode, tris, quads)

Bases: tuple

jnode

Alias for field number 1

nodes

Alias for field number 0

quads

Alias for field number 3

tris

Alias for field number 2

class cape.gruvoc.umeshbase.UmeshBase

Bases: ABC

add_cp(gam: float = 1.4)

Add pressure coefficient cp to state matrix

Call:
>>> mesh.add_cp(gam=1.4)
Inputs:
mesh: Umesh

Unstructured mesh instance

gam: {1.4} | float

Ratio of specific heats

add_mach(gam: float = 1.4)

Add Mach number as mach to state matrix

Call:
>>> mesh.add_mach(gam=1.4)
Inputs:
mesh: Umesh

Unstructured mesh instance

gam: {1.4} | float

Ratio of specific heats

basename
bldel
blds
build_voledges()

Build table of unique edges in the volume mesh

Call:
>>> mesh.build_voledges()
Inputs:
mesh: Umesh

Unstructured mesh instance

Attributes:
mesh.edges: np.ndarray[float]

1-based indices of node pairs defining each edge; lower index is always on the left

mesh.nedge: int

Number of edges

calc_ll_coeffs(Aref: float, nCut: int, comp: list | str | int | None = None, fweights: str | None = None)

Calculate sectional line load coefficients

Call:
>>> w8s = mesh.calc_ll_coeffs(Aref, nCut, comp)
Inputs:
Aref: float

Reference Area

nCut: int

Number of cuts in line load (# bins = nCut - 1)

comp: list

List of components to include in cuts

Outputs:
result: np.ndarray

Sectional line load coefficients

comment1
comment2
comment3
comment4
config
coord_system
copy() → UmeshBase

Return a copy with new data arrays

Call:
>>> meshc = mesh.copy()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
meshc: Umesh

Copy of mesh

edge_ids
edges
farfield_type
fdir
find_duplicate_nodes(tol: float = 1e-08) → ndarray

Find duplicated nodes

Find identical copies of nodes, which are often generated along boundaries between neighboring zones.

Call:
>>> dup_pairs mesh.find_duplicate_nodes(tol=1e-8)
Inputs:
mesh: Umesh

Unstructured mesh instance

tol: {1e-8} | float

Spatial distance tolerance for repeat nodes

Outputs:
dup_pairs: np.ndarray[int]

An Nx2 array of 1-based node numbers where N is the number of duplicated nodes, the first column is the number of the duplicated node, and second column is the matching node. Within the same row, column 1 always has a lower value than column 0.

fname
follow_edges() → SegmentedSlice

Follow edges created by a surface slice

The output is two simple arrays of spatial coordinates and state variables long the edges. See also slicesurf() as this method is meant to be called on the output of slicesurf().

Call:
>>> seg = mesh.follow_edges()
>>> x, q = mesh.follow_edges()
Inputs:
x: np.ndarray[float]

Coordinates of the cut plane center

n: np.ndarray[float]

Vector normal to desired cut plane

comp: {None} | str | list

Optional name of component(s) to intersect

fname: {None} | str

Name of output file

closed: {False} | True

Attempt to close returned edges

Outputs:
seg: SegmentedSlice

Coordinates and state variables of connected slice

x: np.ndarray[float]

Nodal coordinates of connected slice; a row of np.nan reporesents a gap in the connected slices

q: np.ndarray[float]

State variables; each row corresponds to row of x

Versions:
  • 2025-07-01 @ddalle: v1.0

classmethod from_datakit(db: DataKit) → UmeshBase

Infer a Umesh from a DataKit

Call:
>>> mesh = Umesh.from_datakit(db)
Inputs:
db: DataKit

DataKit with cols matching Umesh slots

Outputs:
mesh: Umesh

Unstructured mesh instance

classmethod from_pvmesh(pvmesh) → UmeshBase

Interpret Umesh from a PyVista mesh object

The mesh.pvmesh attribute must be a PyVista unstructured grid instance.

Call:
>>> mesh = Umesh.from_pvmesh(pvmesh)
Inputs:
pvmesh: pv.UnstructuredGrid

PyVista unstructured grid instance

Outputs:
mesh: Umesh

Unstructured mesh instance

classmethod from_tri(tri) → UmeshBase

Interpret Umesh from a cape.trifile.Tri

Call:
>>> mesh = Umesh.from_tri(tri)
Inputs:
tri: cape.trifile.Tri

Alternate CAPE tri-file instance

Outputs:
mesh: Umesh

Unstructured mesh instance

genr8_datakit() → DataKit

Create a DataKit from an unstructured mesh

Call:
>>> db = mesh.genr8_datakit()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
db: DataKit

DataKit with cols matching Umesh slots

genr8_interp_weights(y: ndarray) → InterpWeights

Get interpolation weights for nodes y

Call:
>>> W = mesh.genr8_interp_weights(y)
Inputs:
mesh: Umesh

Unstructured mesh instance

y: np.ndarray

Points to interpolate to

Outputs:
W: InterpWeightsTuple

Interpolation weights tuple

W.w: np.ndarray[float]

3 weights for each row of y

W.i: np.ndarray[float]

Indices of 3 nodes to use with W.w

Versions:
  • 2026-04-10 @ddalle: v1.0

genr8_ll_w8s(nCut: int, comp: list | str | int | None = None)

Generate line load sectional weighting on each triangle

Call:
>>> w8s = mesh.genr8_ll_w8s(comp, nCut)
Inputs:
comp: list

List of components to include in cuts

nCut: int

Number of cuts in line load (# bins = nCut - 1)

Outputs:
wbins: np.ndarray

Fractional area weights for each triangle and each bin

xbnds: np.ndarray

Bounds of each cut bin

genr8_surf_zone(surf_id: int) → SurfZone

Get nodes, tris, and quads for a given surface ID

This compresses the node indices to only those that are nodes of tris or quads that have the correct ID.

Call:
>>> zone = mesh.genr8_surf_zone(surf_id)
Inputs:
mesh: Umesh

Unstructured mesh instance

surf_id: int

Surface ID number

Outputs:
zone: SurfZone

Surface zone information

zone.nodes: np.ndarray[float]

Coordinates of nodes of relevant tris/quads

zone.jnode: np.ndarray[int]

Indices of nodes included (0-based)

zone.tris: np.ndarray[int]

Compressed node indices of matching tris (1-based)

zone.quads: np.ndarray[int]

Compressed node indices of matching quads (1-based)

genr8_tri_areas() → ndarray

Get the areas of each surface tri

Call:
>>> areas = mesh.genr8_tri_areas()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
areas: ndarray, shape=(mesh,ntri,)

Area of each triangle

genr8_tri_bases() → CoordSysBasis

Create local coord system for each tri

Call:
>>> basis = mesh.genr8_tri_bases()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
basis: CoordBasis

Tuple of unit vectors for each tri

basis.e1: np.ndarray[float]

Unit vector pointing from node 0 to node 1 for each tri

basis.e2: np.ndarray[float]

Unit vector in plane of each tri normal to 0->1 dir

basis.e3: np.ndarray[float]

Unit normal for each tri

Versions:
  • 2026-04-09 @ddalle: v1.0

genr8_vol_zone(vol_id: int) → VolumeZone

Get nodes, tets, pyrs, pris, and hexs for a given volume ID

This compresses the node indices to only those that are nodes of tris or quads that have the correct ID. In most cases, there is only a single volume, so it returns the entire volume.

Call:
>>> zone = mesh.genr8_vol_zone(vol_id)
Inputs:
mesh: Umesh

Unstructured mesh instance

vol_id: int

volume ID number

Outputs:
zone: SurfZone

Surface zone information

zone.nodes: np.ndarray[float]

Coordinates of nodes of requested volume

zone.jnode: np.ndarray[int]

Indices of nodes included (0-based)

zone.tets: np.ndarray[int]

Compressed node indices of matching tets (1-based)

zone.pyrs: np.ndarray[int]

Compressed node indices of matching pyramids (1-based)

zone.pris: np.ndarray[int]

Compressed node indices of matching tri prisms (1-based)

zone.hexs: np.ndarray[int]

Compressed node indices of matching hexs (1-based)

genr8_w8s_segment(xa: float, xb: float, ktris: ndarray | None = None, comp: list | str | int | None = None) → ndarray

Calculate triangle area weights between two x-cut planes

Call:
>>> w8s = mesh.genr8_w8s_segment(xa, xb, ktris, comp)
Inputs:
xa: float

x-coord for left cut plane

xb: float

x-coord for right cut plane

ktris: np.ndarray

Indexes of tris in comp

comp: list

List of components to include in cuts

Outputs:
w8s: np.ndarray

Fractional area weights for each triangle in ktris

genr8_yplus(p, T, M, L=1.0, bound0=None, R=None, gam=None, mu0=None, T0=None, C=None, mks=True)

Calculate wall normal distances

Call:
>>> yplus = genr8_yplus(p, T, M)
Inputs:
p: float

Static pressure [Pa|psf]

T: float

Static temperature [K|R]

M: float

Mach number

mks: {True} | False

Flag for unit system

R: float

Gas constant [m^2/s^2*R|ft^2/s^2*R]

gam: {1.4} | float

Ratio of specific heats

mu0: float

Reference viscosity [kg/m*s|slug/ft*s]

T0: float

Reference temperature [K|R]

C: float

Reference temperature [K|R]

L: {1.0} | int

Reference length [m|ft]

bound0: int

First non-farfield comp id (for make_pv_skin_friction)

Outputs:
yplus: np.ndarray

Wall normal distances

Versions:
  • 2025-07-17 @aburkhea: v1.0

get_bbox(comp=None, **kwargs)

Find bounding box of specified component

Find a bounding box based on the coordinates of a specified component or list of components, with an optional buffer or buffers in each direction

Call:
>>> xlim = mesh.GetCompBBox(compID, **kwargs)
Inputs:
mesh: Umesh

Unstructured mesh instance

compID: {None} | int | str

Component or list of components to use for bounding box; if None; return bounding box for entire surface

pad: float

Buffer to add in each dimension to min and max coords

xpad: float

Buffer to minimum and maximum x-coordinates

ypad: float

Buffer to minimum and maximum y-coordinates

zpad: float

Buffer to minimum and maximum z-coordinates

xp: float

Buffer for the maximum x-coordinate

xm: float

Buffer for the minimum x-coordinate

yp: float

Buffer for the maximum y-coordinate

ym: float

Buffer for the minimum y-coordinate

zp: float

Buffer for the maximum z-coordinate

zm: float

Buffer for the minimum z-coordinate

Outputs:
xlim: numpy.ndarray (float), shape=(6,)

List of xmin, xmax, ymin, ymax, zmin, zmax

Versions:
  • 2014-06-16 @ddalle: v1.0

  • 2014-08-03 @ddalle: v1.1; “buff” –> “pad”

  • 2017-02-08 @ddalle: v1.2; CompID=None behavior

get_bbox_uncached(comp=None, **kwargs)
get_hex_ids() → ndarray

Get ID numbers for each hexahedral cell

Call:
>>> gex_ids = mesh.get_hex_ids()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
gex_ids: np.ndarray[int]

ID numbers for each hex cell

get_hexs_by_id(vol_id: int) → ndarray

Get indices (0-based) of hexahedra matching volume ID

Call:
>>> mask = mesh.get_hexs_by_id(vol_id)
Inputs:
mesh: Umesh

Unstructured mesh instance

vol_id: int

Volume ID number

Outputs:
mask: np.ndarray[int]

Indices of elements whose ID matches vol_id

get_nearest_tri_small(p: ndarray, mask: ndarray | None = None) → int

Get tri nearest to point p

This function is intended for cases where mask has only a few candidate triangles in it.

Call:
>>> k = mesh.get_nearest_tri(p, mask=None)
Inputs:
mesh: Umesh

Unstructured mesh instance

p: np.ndarray[float]

x, y, and z coordinates of test point

mask: {None} | np.ndarray

Optional subset of tris to use as candidates

Outputs:
k: int

0-based index of nearest tri

Versions:
  • 2026-04-09 @ddalle: v1.0

get_nface() → int

Get number of unique surface IDs

Call:
>>> nface = mesh.get_nface()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
nface: int

Number of unique entries in tri_ids and quad_ids

get_nodes_by_comp(comp: int | list | str, tri_ids: ndarray | None = None, quad_ids: ndarray | None = None) → ndarray

Get indices of all nodes contained on a given component

Call:
>>> mask = mesh.get_nodes_by_comp(comp)
Inputs:
mesh: Umesh

Unstructured mesh instance

comp: int | str | list

Surface ID name, number, or list thereof

Outputs:
mask: np.ndarray[int]

Indices of nodes in at least one tri/quad w/ matching ID

get_nodes_by_id(surf_id: int, tri_ids: ndarray | None = None, quad_ids: ndarray | None = None) → ndarray

Get indices of all nodes contained on a given surface ID

Call:
>>> mask = mesh.get_nodes_by_id(surf_id)
Inputs:
mesh: Umesh

Unstructured mesh instance

surf_id: int

Surface ID number

Outputs:
mask: np.ndarray[int]

Indices of nodes in at least one tri/quad w/ matching ID

get_nodes_by_vol_id(vol_id: int) → ndarray

Get indices of all nodes contained on a given volume ID

Call:
>>> mask = mesh.get_nodes_by_vol_id(vol_id)
Inputs:
mesh: Umesh

Unstructured mesh instance

vol_id: int

Volume ID number

Outputs:
mask: np.ndarray[int]

Indices of nodes part of >0 matching volume elements

get_pri_ids() → ndarray

Get ID numbers for each triangular-prism cell

Call:
>>> pri_ids = mesh.get_pri_ids()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
pri_ids: np.ndarray[int]

ID numbers for each pri cell

get_pris_by_id(vol_id: int) → ndarray

Get indices (0-based) of prism cells matching volume ID

Call:
>>> mask = mesh.get_pris_by_id(vol_id)
Inputs:
mesh: Umesh

Unstructured mesh instance

vol_id: int

Volume ID number

Outputs:
mask: np.ndarray[int]

Indices of elements whose ID matches vol_id

get_pyr_ids() → ndarray

Get ID numbers for each pyramid (penta_5) cell

Call:
>>> tet_ids = mesh.get_pyr_ids()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
pyr_ids: np.ndarray[int]

ID numbers for each pyr cell

get_pyrs_by_id(vol_id: int) → ndarray

Get indices (0-based) of pyramids matching volume ID

Call:
>>> mask = mesh.get_pyrs_by_id(vol_id)
Inputs:
mesh: Umesh

Unstructured mesh instance

vol_id: int

Volume ID number

Outputs:
mask: np.ndarray[int]

Indices of elements whose ID matches vol_id

get_quad_ids() → ndarray

Get ID numbers for each quad face

Call:
>>> quad_ids = mesh.get_quad_ids()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
quad_ids: np.ndarray[int]

ID numbers for each quad face

get_quads_by_comp(comp: list | int | None = None) → ndarray

Get indices of quads in one or more component

Call:
>>> kquads = mesh.get_quads_by_comp(comp)
>>> kquads = mesh.get_quads_by_comp(comps)
Inputs:
mesh: Umesh

Unstructured mesh instance

comp: int

Single target for mesh.tri_ids to match

comps: list[int]

List of target tri_ids

Outputs:
kquads: np.ndarray[int]

Indices of matching quads (0-based indexing)

get_quads_by_id(surf_id: int) → ndarray

Get indices (0-based) of quads with specified ID

Call:
>>> mask = mesh.get_quads_by_id(surf_id)
Inputs:
mesh: Umesh

Unstructured mesh instance

surf_id: int

Surface ID number

Outputs:
mask: np.ndarray[int]

Indices of quads whose ID matches surf_id

get_qvar(qvar, index=-1) → ndarray

Get state variable in the mesh object

Call:
>>> var = mesh.get_qvar()
Inputs:
mesh: Umesh

Unstructured mesh instance

qvar: str

Name of q variable

index: {-1} | int

Dimension of mesh.q that corresponds to qvar indexing

Outputs:
var: np.ndarray

Returns qvar array from mesh.q

Versions:
  • 2025-07-16 @aburkhea: v1.0

get_qvars() → list

Get list of state variables in the mesh object

Call:
>>> varlist = mesh.get_qvars()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
varlist: list[str]

List of names of variables, default ["x", "y", "z"]

get_strand_id(j: int) → int
get_summary(h=False) → str

Create summary of mesh

Call:
>>> txt = mesh.get_summary(h=False)
Inputs:
mesh: Umesh

Unstructured mesh instance

h: True | {False}

Human-readable, e.g. “12345678” -> “12.3M”

Outputs:
txt: str

Summary of mesh properties and counts

get_surf_hexs(j: ndarray | None = None) → ndarray

Get indicator of hexs with at least one surf node

Using j, this function can also be used to find hexs having at least one node in any arbitrary list of node indices.

Call:
>>> inds = mesh.get_surf_hexs(j=None)
Inputs:
mesh: Umesh

Unstructured mesh instance

j: {None} | np.ndarray[int]

Optional node list (default is all nodes on surf)

Outputs:
inds: np.ndarray[int]

Indicator for each cell 0/1 if it has a node in j

get_surf_ids(comp=None) → ndarray

Expand integer, str, or list to list of surface ID ints

Call:
>>> surf_ids = mesh.get_surf_ids(comp=None)
Inputs:
mesh: Umesh

Unstructured mesh instance

comp: {None} | str | int

Name or index of a component, or list thereof

get_surf_nearest(j: ndarray | None = None) → ndarray
get_surf_nodes() → ndarray

Get indices (1-based) of all nodes on a surface tri/quad

Call:
>>> inds = mesh.get_surf_nodes()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
inds: np.ndarray[int]

1D array of node indices on a tri or quad

get_surf_pris(j: ndarray | None = None) → ndarray

Get indicator of prisms with at least one surf node

Using j, this function can also be used to find pris having at least one node in any arbitrary list of node indices.

Call:
>>> inds = mesh.get_surf_pris(j=None)
Inputs:
mesh: Umesh

Unstructured mesh instance

j: {None} | np.ndarray[int]

Optional node list (default is all nodes on surf)

Outputs:
inds: np.ndarray[int]

Indicator for each cell 0/1 if it has a node in j

get_surf_pyrs(j: ndarray | None = None) → ndarray

Get indicator of pyrs with at least one surf node

Using j, this function can also be used to find pyrs having at least one node in any arbitrary list of node indices.

Call:
>>> inds = mesh.get_surf_pyrs(j=None)
Inputs:
mesh: Umesh

Unstructured mesh instance

j: {None} | np.ndarray[int]

Optional node list (default is all nodes on surf)

Outputs:
inds: np.ndarray[int]

Indicator for each cell 0/1 if it has a node in j

get_surf_tets(j: ndarray | None = None) → ndarray

Get indicator of tets with at least one surf node

Using j, this function can also be used to find tets having at least one node in any arbitrary list of node indices.

Call:
>>> inds = mesh.get_surf_tets(j=None)
Inputs:
mesh: Umesh

Unstructured mesh instance

j: {None} | np.ndarray[int]

Optional node list (default is all nodes on surf)

Outputs:
inds: np.ndarray[int]

Indicator for each cell 0/1 if it has a node in j

get_surf_zones() → list

Get list of zones

Call:
>>> zones = mesh.get_zones()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
zones: list[str]

List of zone names, defaults to "boundary 1", "boundary 2", …, ["tets", "pyramids", "prisms", "hexs"]

get_surfzone_ids() → ndarray

Get list of ID numbers for surface zones

Call:
>>> surf_ids = mesh.get_surfzone_ids()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
surf_ids: np.ndarray[int]

List of unique surface zone IDs

get_tet_ids() → ndarray

Get ID numbers for each tetrahedral cell

Call:
>>> tet_ids = mesh.get_tet_ids()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
tet_ids: np.ndarray[int]

ID numbers for each tet cell

get_tets_by_id(vol_id: int) → ndarray

Get indices (0-based) of tetrahedra matching volume ID

Call:
>>> mask = mesh.get_tets_by_id(vol_id)
Inputs:
mesh: Umesh

Unstructured mesh instance

vol_id: int

Volume ID number

Outputs:
mask: np.ndarray[int]

Indices of elements whose ID matches vol_id

get_time(j: int) → float64
get_title() → str

Get “title” of mesh object

Tries mesh.title, then mesh.fname

Call:
>>> title = mesh.get_title()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
title: str

Title of mesh

get_tri_ids() → ndarray

Get ID numbers for each tri face

Call:
>>> tri_ids = mesh.get_tri_ids()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
tri_ids: np.ndarray[int]

ID numbers for each tri face

get_tris_by_comp(comp: list | int | None = None) → ndarray

Get indices of tris in one or more component

Call:
>>> ktris = mesh.get_tris_by_comp(comp)
>>> ktris = mesh.get_tris_by_comp(comps)
Inputs:
mesh: Umesh

Unstructured mesh instance

comp: int

Single target for mesh.tri_ids to match

comps: list[int]

List of target tri_ids

Outputs:
ktris: np.ndarray[int]

Indices of matching tris (0-based indexing)

get_tris_by_id(surf_id: int) → ndarray

Get indices (0-based) of tris with specified ID

Call:
>>> mask = mesh.get_tris_by_id(surf_id)
Inputs:
mesh: Umesh

Unstructured mesh instance

surf_id: int

Surface ID number

Outputs:
mask: np.ndarray[int]

Indices of tris whose ID matches surf_id

get_varlist() → list

Get list of variables in the mesh object

Call:
>>> varlist = mesh.get_varlist()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
varlist: list[str]

List of names of variables, default ["x", "y", "z"]

get_vol_zones() → list

Get list of volume zone names

Call:
>>> zones = mesh.get_volzones()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
zones: list[str]

List of volume zone names, defaults to "volume 1", ["volume 2", […]]

get_volzone_ids() → ndarray

Get list of ID numbers for each volume cell

Call:
>>> vol_ids = mesh.get_volzone_ids()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
zone_ids: np.ndarray[int]

List of unique surface zone IDs

get_zones() → list

Get list of zones

Call:
>>> zones = mesh.get_zones()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
zones: list[str]

List of zone names, defaults to "boundary 1", "boundary 2", …, ["volume 1"]

hex_ids
hexs
make_pv_skin_friction(p, T, M, R=None, gam=1.4, mu0=None, T0=None, C=None, mks=True)

Compute skin friction using pyvista methods

Call:

>>>surf = mesh.make_pv_skin_friction(p, T, M)

Inputs:
mesh: Umesh

Unstructured mesh instance

p: float

Static pressure [Pa|psf]

T: float

Static temperature [K|R]

M: float

Mach number

mks: {True} | False

Flag for unit system

R: float

Gas constant [m^2/s^2*R|ft^2/s^2*R]

gam: {1.4} | float

Ratio of specific heats

mu0: float

Reference viscosity [kg/m*s|slug/ft*s]

T0: float

Reference temperature [K|R]

C: float

Reference temperature [K|R]

Outputs:
surf: Umesh

Surface points with computed skin friction quantities

make_pvmesh_surf()

Make a PyVista unstructed mesh w/ solutions vars if present

Call:
>>> mesh.make_pvmesh_surf()
Inputs:
mesh: Umesh

Unstructured mesh instance

make_pvmesh_vol()

Make a PyVista unstructed mesh w/ solutions vars if present

Call:
>>> mesh.make_pvmesh_vol()
Inputs:
mesh: Umesh

Unstructured mesh instance

make_quad_areas()

Get the quad areas and unit normals

Call:
>>> a, nhat = mesh.make_quad_areas()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
a: ndarray, shape=(mesh.nquad,)

Area of each quad

nhat: np.ndarray, shape=(mesh.nquad, 4)

Unit normal of each quad

make_skin_friction(p, T, M, R=None, gam=1.4, mu0=None, T0=None, C=None, mks=True)

Compute skin friction

Call:

>>>surf = mesh.make_skin_friction(p, T, M)

Inputs:
mesh: Umesh

Unstructured mesh instance

p: float

Static pressure [Pa|psf]

T: float

Static temperature [K|R]

M: float

Mach number

mks: {True} | False

Flag for unit system

R: float

Gas constant [m^2/s^2*R|ft^2/s^2*R]

gam: {1.4} | float

Ratio of specific heats

mu0: float

Reference viscosity [kg/m*s|slug/ft*s]

T0: float

Reference temperature [K|R]

C: float

Reference temperature [K|R]

Outputs:
surf: Umesh

Surface points with computed skin friction quantities

make_tri_areas()

Get the tri areas and unit normals

Call:
>>> a, nhat = mesh.make_tri_areas()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
a: ndarray, shape=(mesh.ntri,)

Area of each triangle

nhat: np.ndarray, shape=(mesh.ntri, 3)

Unit normal of each triangle

make_tri_bases() → CoordSysBasis

Create local coord system for each tri

Call:
>>> basis = mesh.make_tri_bases()
Inputs:
mesh: Umesh

Unstructured mesh instance

Outputs:
basis: CoordBasis

Tuple of unit vectors for each tri

basis.e1: np.ndarray[float]

Unit vector pointing from node 0 to node 1 for each tri

basis.e2: np.ndarray[float]

Unit vector in plane of each tri normal to 0->1 dir

basis.e3: np.ndarray[float]

Unit normal for each tri

Versions:
  • 2026-04-09 @ddalle: v1.0

merge(mesh: UmeshBase) → UmeshBase

Copy data from one mesh into another

Call:
>>> mesh.merge(meshc)
Inputs:
mesh: Umesh

Unstructured mesh instance

meshc: Umesh

Copy of mesh

mesh_type
name
ndim
nedge
nface
nhex
niter
nnode
nodes
npri
npyr
nq
nq_matrix
nq_metric
nq_scalar
nq_vector
nquad
ntet
ntet_bl
ntri
nzone
parentzone
path
pri_ids
print_summary(h=False)

Print summary of mesh

Call:
>>> txt = mesh.get_summary(h=False)
Inputs:
mesh: Umesh

Unstructured mesh instance

h: True | {False}

Human-readable, e.g. “12345678” -> “12.3M”

pris
project_to_plane(n: ndarray, o: ndarray) → ndarray

Get coordinates of each node projected into a plane

Call:
>>> y = mesh.project_to_plane(n, o)
Inputs:
mesh: Umesh

Unstructured mesh instance

n: np.ndarray[float]

Normal vector defining plane

o: np.ndarray[float]

A point in the plane

Outputs:
y: np.ndarray[float]

Projection of mesh.nodes into plane

pvmesh
pyr_ids
pyrs
q
q_matrix
q_metric
q_scalar
q_type
q_vector
qinf
qinfvars
quad_areas
quad_bcs
quad_flags
quad_ids
quad_normals
quads
qvars
qvars_matrix
qvars_scalar
qvars_vector
read_vtk(fname: str)
remove_offbody(j: ndarray | None = None)

Remove all volume cells not touching a surface node

Call:
>>> mesh.remove_offbody(j=None)
Inputs:
mesh: Umesh

Unstructured mesh instance

j: {None} | np.ndarray[int]

Optional node list (default is all nodes on surf)

remove_unused_nodes()

Remove any nodes that are not used in any triangles

Call:
>>> mesh.remove_unused_nodes()
Inputs:
mesh: Umesh

Unstructured mesh instance

Versions:
  • 2017-02-10 @ddalle: cape.trifile v1.0

  • 2019-06-18 @ddalle: cape.trifile v2.0; NO LOOPS

  • 2026-03-26 @ddalle: v1.0 (tris only)

remove_volume()

Remove all volume cells and nodes not used on surface

Call:
>>> mesh.remove_volume()
Inputs:
mesh: Umesh

Unstructured mesh instance

report_small_cells(smallvol: float | None = None, nrows: int = 25)
report_small_hexs(smallvol: float, nrows: int = 25)
report_small_pris(smallvol: float, nrows: int = 25)
report_small_pyrs(smallvol: float, nrows: int = 25)
report_small_tets(smallvol: float, nrows: int = 25)
report_small_tris(smallvol: float, nrows: int = 25)
rotate(v1: ndarray, v2: ndarray, theta: float, comp: str | None = None)

Rotate subset of points about a vector defined by two points

Call:
>>> mesh.rotate(v1, v2, theta, comp=None)
Inputs:
mesh: Umesh

Unstructured mesh instance

v1: np.ndarray[float]

Coordinates of start point of rotation vector; shape (3,)

v2: np.ndarray[float]

Coordinates of end point of rotation vector; shape (3,)

theta: float

Angle by which to rotate X, in degrees

comp: {None} | str | int

Optional component name/ID (else rotate all points)

select_comp(comp: list | tuple | int) → UmeshBase
slicesurf(x: array, n: array, comp: list | str | int | None = None, closed: bool | None = False, **kw)

Create a slice through surface tris and quads

Call:
>>> crv = mesh.slicesurf(x, n, comp=None, **kw)
Inputs:
x: np.ndarray[float]

Coordinates of the cut plane center

n: np.ndarray[float]

Vector normal to desired cut plane

comp: {None} | str | list

Optional name of component(s) to intersect

fname: {None} | str

Name of output file

closed: {False} | True

Attempt to close returned edges

Outputs:
crv: Umesh

Unstructured mesh instance for intersect

crv.nodes: np.ndarray[float]

Nx3 array of node coordinates

crv.edges: np.ndarray[int]

Nx2 array of edge nodes, sorted into contiguous sections

crv.edge_ids: np.ndarray[int]

The tri_id or quad_id from which this edge came

crv.q: np.ndarray[float]

Values of the mesh.q at the slice.nodes

Versions:
  • 2025-05-12 @aburkhea: v1.0

slicevol_pvmesh(origin: tuple = (0.0, 0.0, 0.0), normal: list = (0.0, 0.0, 1.0)) → UmeshBase

Use PyVista to create a volume slice

Call:
>>> vslice = mesh.slicevol_pvmesh(origin, normal)
Inputs:
mesh: Umesh

Unstructured mesh instance

origin: tuple[float]

Point in specified cut plane

normal: tuple[float]

Normal to define cut plane

Outputs:
vslice: Umesh

Unstructured mesh of just the slice

strand_ids
surfzone_ids
surfzones
t
target_config
tet_ids
tets
title
translate(v: ndarray, comp: str | None = None)

Translate subset of points

Call:
>>> mesh.translate(v, comp=None)
Inputs:
mesh: Umesh

Unstructured mesh instance

v: np.ndarray[float]

Vector by which to translate each point; shape (3,)

comp: {None} | str

Component to translate

tri_areas
tri_bcs
tri_e1
tri_e2
tri_e3
tri_flags
tri_ids
tri_normals
tris
units
volzone_ids
volzones
write_vtk(fname: str, force: bool = True)

Write Umesh to vtk file

Call:
>>> mesh.write_vtk(fname)
Inputs:
mesh: Umesh

Unstructured mesh instance

fname: str

Name of file to write

force: {True} | False

Overwrite any mesh.pvmesh

zip_duplicate_edges(tol: float = 1e-08)

Find identical edges and zip them into shared edges

This fixes issues in meshes where two zones are defined independently, for example in a Tecplot surface file. It zips the duplicated edges by finding a common set of nodes along each duplicated edge and then removing the duplicate nodes.

Call:
>>> mesh.zip_duplicate_edges(tol=1e-8)
Inputs:
mesh: Umesh

Unstructured mesh instance

tol: {1e-8} | float

Spatial distance tolerance for repeat nodes

zone_types
zones
class cape.gruvoc.umeshbase.VolumeZone(nodes, jnode, tets, pyrs, pris, hexs)

Bases: tuple

hexs

Alias for field number 5

jnode

Alias for field number 1

nodes

Alias for field number 0

pris

Alias for field number 4

pyrs

Alias for field number 3

tets

Alias for field number 2

cape.gruvoc.umeshbase.compress_indices(node_indices: ndarray, keep_indices: ndarray | None = None) → ndarray

Renumber node indices array, e.g. array of tris or tets

Call:
>>> new_inds = compress_indices(node_indices, keep_indices=None)
Inputs:
node_indices: :class;`np.ndarray`[int]

Original array of node indices, e.g. Nx3 array of tri nodes

keep_indices: {None} | np.ndarray[int]

Indices to keep (default is all inds in node_indices)

Outputs:
new_inds: np.ndarray[int]

Renumbered array with same shape as node_indices

cape.gruvoc.umeshbase.get_intersect_elems(eles, ptsz)

Get mask of elems that are intersected by the z plane

cape.gruvoc.umeshbase.interpolate_q(smesh: UmeshBase, rmesh: UmeshBase, k=5)

Interpolate mesh.q to another mesh.nodes w/ NN-inverse distance

Call:
>>> interpolate_q(smesh, rmesh)
Inputs:
smesh: Umesh

Source mesh

rmesh: Umesh

Receptor mesh

k: {5} | int

Number of nearest neighbors to use

Versions:
  • 2025-07-22 @aburkhea: Version 1.0

cape.gruvoc.umeshbase.pprint_n(x: float | int, nchar: int = 3) → str

Format a string as number thousands, millions, etc.

Call:
>>> txt = pprint_n(x, nchar=3)
Inputs:
x: int | float

Number to format

nchar: {3} | int

Number of digits to print

Outputs:
txt: str

Formatted text

cape.gruvoc.umeshbase.print_smallvol_table(v, j, x, y, z)
cape.gruvoc.umeshbase.rotate_points_to_plane(points, plane_center, plane_normal)

Rotates a numpy array of 3D Cartesian nodes such that the plane defined by the given normal vector becomes parallel to the xy-plane

Inputs:
points: class:np.array

Array representing the node coordinates (x, y, z)

plane_center: class:np.array

Array of the center point of the cut plane (a, b, c)

plane_normal: class:np.array

Array of the normal vector to the cut plane (a, b, c)

Outputs:
rotated_points: class:np.array

A numpy array of the rotated coordinates.