trimesh

trimesh

Triangulated-surface container (TriMesh) and its surface ops.

class nekmeshpy.trimesh.TriMesh[source]

Bases: object

A triangular surface mesh: points (P,3) and tris (T,3) connectivity.

__init__(points, tris)[source]
Parameters:
  • points (NDArray[float64])

  • tris (NDArray[int64])

Return type:

None

classmethod from_files(vtx_file, tri_file)[source]

Load a surface triangulation from point-list and 1-based triangle-index files.

Parameters:
  • vtx_file (str)

  • tri_file (str)

Return type:

TriMesh

classmethod from_faces(V, faces)[source]

Build a sub-mesh from V restricted to faces; returns (TriMesh, vids) where vids maps sub-index -> V-index.

Parameters:
  • V (NDArray[float64])

  • faces (NDArray[int64])

Return type:

tuple[TriMesh, NDArray[int64]]

EDGE_POINTS = array([[0, 1],        [1, 2],        [2, 0]])
property n_points: int

Number of points.

property n_tris: int

Number of triangles.

boundary_edges()[source]

(K,2) array of [triangle id, local edge (1-3)] for every open-boundary edge.

Return type:

NDArray[int64]

boundary_elements()[source]

Sorted unique triangle ids with at least one edge on the open boundary.

Return type:

NDArray[int64]

boundary_points()[source]

Sorted unique point ids lying on the open boundary.

Return type:

NDArray[int64]

boundary_loops()[source]

The open boundary grouped into loops – one vertex-id array per opening.

Return type:

list[NDArray[int64]]

topology_report()[source]

Manifold / connectivity report.

Return type:

dict[str, Any]

is_closed()[source]

True if the surface is a closed, single-component 2-manifold.

Return type:

bool

trimesh.ops

Surface algorithms on a TriMesh as free functions. All indices 0-based.

nekmeshpy.trimesh.ops.cotan_laplacian(surface)[source]

Linear (P1) cotangent-weight stiffness matrix (recomputed each call).

Parameters:

surface (TriMesh)

Return type:

csr_matrix

nekmeshpy.trimesh.ops.solve_dirichlet(surface, dpoints, dvals)[source]

Solve the Laplace system with Dirichlet values at dpoints; returns an (nv,) field.

Parameters:
  • surface (TriMesh)

  • dpoints (NDArray[int64])

  • dvals (NDArray[float64])

Return type:

NDArray[float64]

nekmeshpy.trimesh.ops.boundary_loops(surface)[source]

Group open-boundary edges into connected loops; returns a list of vertex-index arrays.

Parameters:

surface (TriMesh)

Return type:

list[NDArray[int64]]

nekmeshpy.trimesh.ops.order_boundary_loop(surface, lv)[source]

Return the vertices of one boundary loop lv in cyclic order.

Parameters:
  • surface (TriMesh)

  • lv (NDArray[int64])

Return type:

NDArray[int64]

nekmeshpy.trimesh.ops.conduction_field(surf, gloops, neumann, dvals)[source]

Laplace with Neumann on loop neumann, Dirichlet dvals on the other two, shifted to zero mean on the free loop.

Parameters:
Return type:

NDArray[float64]

nekmeshpy.trimesh.ops.seam_fields(surf, gloops)[source]

The three conduction seam fields U (nv, 3): column k is 0 on opening k, 1 on the next opening round, and Neumann (free) on the third – so its sign is what cut_surface_from_fields() reads to place seam k.

Parameters:
Return type:

NDArray[float64]

nekmeshpy.trimesh.ops.leg_label(F)[source]

Which of the three legs (1, 2 or 3) each row of seam-field values F (n, 3) falls in, by the sign pattern seam_fields() gives each leg.

Parameters:

F (NDArray[float64])

Return type:

NDArray[int64]

nekmeshpy.trimesh.ops.cut_surface_from_fields(surf, F)[source]

Cut surf into three legs defined by seam fields F (nv, 3), retriangulating triangles a seam crosses. Returns (V, faces): V is the surface’s own points plus every new seam-crossing and triple point, and faces is a 3-element list of (T,3) arrays – one leg’s triangles each – both indexing into V.

Parameters:
  • surf (TriMesh)

  • F (NDArray[float64])

Return type:

tuple[NDArray[float64], list[NDArray[int64]]]

nekmeshpy.trimesh.ops.leg_field(V, faces, leg, gloops)[source]

One leg as a sub-mesh with Laplace solved (0 on opening, 1 on seam). Returns (sub, us, opening, seam, vids).

Parameters:
  • V (NDArray[float64])

  • faces (list[NDArray[int64]])

  • leg (int)

  • gloops (Sequence[NDArray[int64]])

Return type:

tuple[TriMesh, NDArray[float64], NDArray[int64], NDArray[int64], NDArray[int64]]

nekmeshpy.trimesh.ops.arc_curve(V, arcverts, iA1, n, keep=0.5)[source]

Analytic A1 -> A2 seam arc: the scanned polyline’s deviation from its own chord, refit as a truncated sine series in the normalized arc-length parameter s, and returned as a callable p(s) on [0, 1].

This is fourier_ring()’s open-curve sibling, and the basis is chosen for the one property the seam needs: every sin(k*pi*s) vanishes at both ends, so A1 and A2 – the triple points where all three arcs meet – stay bit-exact however many modes are kept. That is what lets the three legs keep welding: each arc is fitted once, globally, and the same mesh is handed to both legs that share it, so no leg ever sees a privately-refitted seam (which is why the seam ring could not simply go through fourier_wall() – refitting per leg would mix the two arcs that leg happens to see).

Dropping the upper modes removes the STL facet noise for the same reason it does on a station ring, and keep is applied to the modes the mesh can resolve (n elements -> n-1 interior nodes -> modes 1..n-1), mirroring fourier_ring()’s keep * (M // 2 + 1).

Parameters:
  • V (NDArray[float64])

  • arcverts (NDArray[int64])

  • iA1 (int)

  • n (int)

  • keep (float)

Return type:

Callable[[NDArray[float64]], NDArray[float64]]

nekmeshpy.trimesh.ops.arc_mesh(p, n, order, element_tag)[source]

Mesh an arc_curve() into n high-order elements of the given order, evenly spaced by arc length. LineMesh.loft_fn evaluates p at every node – corners and the private GLL interiors – so no node lands on a chord.

Parameters:
  • p (Callable[[NDArray[float64]], NDArray[float64]])

  • n (int)

  • order (int)

  • element_tag (str)

Return type:

LineMesh

nekmeshpy.trimesh.ops.seam_rings(V, faces, gloops, n_half, *, order, keep=0.5, element_tag='wall')[source]

Build the three conformal seam rings + spine. Returns (rings, A1, A2, spine).

Parameters:
  • V (NDArray[float64])

  • faces (list[NDArray[int64]])

  • gloops (Sequence[NDArray[int64]])

  • n_half (int)

  • order (int)

  • keep (float)

  • element_tag (str)

Return type:

tuple[list[LineMesh], NDArray[float64], NDArray[float64], LineMesh]

nekmeshpy.trimesh.ops.fourier_ring(P, keep=0.5)[source]

Refit a closed scanned ring as a truncated Fourier series p(t), periodic on [0, 2*pi) with sample j at t = 2*pi*j/M.

x, y and z are transformed independently (np.fft.rfft against the uniform parameter) and only the lowest keep fraction of the modes is retained; the rest – facet-scale noise from the STL projection, which a high-order wall would otherwise resolve faithfully – is dropped. The result is a genuine closed-form curve, so LineMesh.loft_fn can place every GLL node on it instead of on the chords between the samples (see the straight-GLL-subdivision trap in CLAUDE.md).

Parameters:
  • P (NDArray[float64])

  • keep (float)

Return type:

Callable[[NDArray[float64]], NDArray[float64]]

nekmeshpy.trimesh.ops.fourier_wall(P, order, element_tag, keep=0.5)[source]

Closed high-order wall LineMesh through fourier_ring(), sampled at the same M parameters as P so index 0 / M//2 still land on the spine rails. LineMesh.loft_fn only ever makes an open chain, so weld the two ends into a loop the way the toolkit expects (linemesh.merge).

Parameters:
  • P (NDArray[float64])

  • order (int)

  • element_tag (str)

  • keep (float)

Return type:

LineMesh

nekmeshpy.trimesh.ops.extract_isocontour(surface, u, level)[source]

Extract {u == level} as the largest closed LineMesh loop, or None.

Parameters:
Return type:

LineMesh | None

nekmeshpy.trimesh.ops.extract_rings(surface, u, levels, min_loop_pts)[source]

Cross-section rings of u at each level; returns (rings, levels_kept).

Parameters:
  • surface (TriMesh)

  • u (NDArray[float64])

  • levels (NDArray[float64])

  • min_loop_pts (int)

Return type:

tuple[list[LineMesh], NDArray[float64]]

nekmeshpy.trimesh.ops.resample_polyline(points, fractions, *, closed=False)[source]

Arc-length resample of a polyline at normalized fractions in [0, 1]. With closed=True the polyline wraps (fraction 1 returns to the start). Returns (len(fractions), 3).

Parameters:
  • points (NDArray[float64])

  • fractions (NDArray[float64])

  • closed (bool)

Return type:

NDArray[float64]

nekmeshpy.trimesh.ops.conform_ring_stack(fine_rings, seam_ring, frlev, n_half)[source]

Conformalize a stack of scanned interior rings (arbitrary per-ring point counts) onto the seam ring’s topology.

Parameters:
  • fine_rings (list[LineMesh] | list[NDArray[float64]])

  • seam_ring (NDArray[float64])

  • frlev (NDArray[float64])

  • n_half (int)

Return type:

list[NDArray[float64]]

nekmeshpy.trimesh.ops.project_points(surface, P)[source]

Snap points onto the nearest vertex’s triangle fan (points assumed near the surface).

Parameters:
  • surface (TriMesh)

  • P (NDArray[float64])

Return type:

NDArray[float64]

nekmeshpy.trimesh.ops.project_to_surface(surface, P, faces=None)[source]

Closest-point projection over triangles; faces defaults to all triangles.

Parameters:
  • surface (TriMesh)

  • P (NDArray[float64])

  • faces (NDArray[int64] | None)

Return type:

NDArray[float64]