Source code for nekmeshpy.quadmesh.lower
"""Fixed-arity ``QuadMesh`` operations that **lower** a rung (delta -1)."""
from __future__ import annotations
import numpy as np
from .._typing import IntArray, PointArray
from ..core import conform
from ..linemesh import LineMesh
from ..tags import Tags
from .quadmesh import QuadMesh
from .query import boundary_edges
[docs]
def boundary_mesh(mesh: QuadMesh, tag: str | None = None) -> LineMesh:
"""A section's boundary as a ``LineMesh``, carrying the section's **own** nodes."""
# a tag names a shared *edge*, so the quads carrying it are looked up rather than
# stored: an edge on the boundary has one, an interior one has both of its own.
quads = np.asarray(mesh.quads, dtype=np.int64)
if tag is None:
sel: IntArray = boundary_edges(mesh)
else:
hit = np.argwhere(mesh.edge_tags.take_dense(quads).reshape(quads.shape) == tag)
if hit.shape[0] == 0:
raise ValueError(
"boundary_mesh: no edge carries the tag %r; this section has %s"
% (tag, sorted(mesh.edge_tags.unique()) or "no tagged edges"))
sel = np.column_stack([hit[:, 0], hit[:, 1] + 1]).astype(np.int64)
pairs: IntArray = mesh.corners[sel[:, 0][:, None],
QuadMesh.EDGE_POINTS[sel[:, 1] - 1, :]]
gids: IntArray = np.unique(pairs)
local: IntArray = np.searchsorted(gids, pairs)
e_idx = conform.locate_rows(mesh.edges, pairs, what="edge")
en: PointArray = np.asarray(mesh.edge_nodes, dtype=float)[e_idx].copy()
# the parent stores an edge's nodes min->max corner; flip those this loop traverses
# the other way, so they read along the extracted element's own direction
rev = mesh.edges[e_idx, 0] != pairs[:, 0]
if en.size:
en[rev] = en[rev][:, ::-1]
if tag is not None:
elem = Tags.full(pairs.shape[0], tag)
else:
elem = Tags.from_dense(
mesh.edge_tags.take_dense(quads[sel[:, 0], sel[:, 1] - 1]))
return LineMesh(mesh.points[gids], local, en, elem)
__all__ = ["boundary_mesh"]