upxo.meshing.confMesh3d.surface_nets module

surface_nets.py

vtkSurfaceNets3D wrapper for multi-label grain surface extraction.

Pipeline

  1. Pad LGI with wall_label on all 6 faces (prevents RVE-face shrinkage).

  2. Run vtkSurfaceNets3D → PyVista PolyData with BoundaryLabels cell array.

  3. Project wall-adjacent mesh points back to exact RVE face planes (residual ±0.016 vox overshoot from smoothing).

  4. Separate output into interior faces (grain-grain) and cap faces (grain-wall → the RVE face closing patches).

  5. Optional PyVista visualisation at each step.

class upxo.meshing.confMesh3d.surface_nets.SurfaceNetsResult(full_mesh: pyvista.PolyData, interior_cells: numpy.ndarray, cap_cells: numpy.ndarray, wall_label: int, voxel_size: float, rve_shape: Tuple[int, int, int], grain_ids: numpy.ndarray, grain_cell_masks: Dict[int, numpy.ndarray] = None)[source]

Bases: object

Output of the vtkSurfaceNets3D multi-label surface extraction stage.

Separates grain–grain interior faces from grain–wall cap faces (RVE boundary patches). Produced by run_surface_nets().

full_mesh: pyvista.PolyData
interior_cells: numpy.ndarray
cap_cells: numpy.ndarray
wall_label: int
voxel_size: float
rve_shape: Tuple[int, int, int]
grain_ids: numpy.ndarray
grain_cell_masks: Dict[int, numpy.ndarray] = None
upxo.meshing.confMesh3d.surface_nets.run_surface_nets(lgi: numpy.ndarray, voxel_size: float, config: SurfaceNetsConfig | None = None, verbose: bool = True) → SurfaceNetsResult[source]

Extract the multi-label grain boundary surface from a 3-D label array.

Parameters:
  • lgi (ndarray (nx, ny, nz), integer) – Cleaned labelled grain image. Label 0 = background (not used here).

  • voxel_size (float) – Physical voxel edge length (microns). Used for RVE face projection.

  • config (SurfaceNetsConfig or None)

  • verbose (bool)

Return type:

SurfaceNetsResult

upxo.meshing.confMesh3d.surface_nets.apply_volume_correction(result: SurfaceNetsResult, lgi: numpy.ndarray, config: SurfaceNetsConfig | None = None, verbose: bool = True) → SurfaceNetsResult[source]

Iterative volume-conservative normal displacement for grains whose SurfaceNets3D surface mesh volume differs from the voxel volume.

For each grain G: - Compute V_mesh (divergence theorem on triangle mesh) - Compute V_voxel = voxel_count × voxel_size³ - If |V_mesh - V_voxel| / V_voxel > volume_tol:

  • Iteratively displace vertices along outward normal by dV/A

  • Guard: vertex pinned if it would come within min_clearance of a neighbouring grain surface

upxo.meshing.confMesh3d.surface_nets.visualize_surface_nets(result: SurfaceNetsResult, mode: str = 'full', grain_id: int | None = None, show: bool = True) → pyvista.Plotter[source]

PyVista visualisation of the SurfaceNets3D output.

Parameters:
  • result (SurfaceNetsResult)

  • mode ('full' — colour by first BoundaryLabel (grain label)) – ‘interior’ — show only grain-grain faces ‘caps’ — show only grain-wall faces ‘grain’ — show one specific grain’s surface (requires grain_id)

  • grain_id (int, required when mode='grain')

  • show (bool — call pvp.show() immediately)