upxo.gbops.grainBoundOps3d module
Module: grainBoundOps3d
Grain-boundary operations for 3D labelled voxel structures.
Provides fast, Numba-accelerated routines for detecting grain-boundary voxels in 3-D labelled feature images (LFI) and for identifying topologically problematic voxels (articulation points) that violate manifold connectivity.
Functions
- compute_gb_boundary_mask_interiorVoxels(lfi)
Detect interior boundary voxels using 6-connectivity (Numba parallel).
- compute_gb_boundary_mask(lfi)
Extend interior boundary mask to include the full RVE outer shell.
- identify_articulation_voxels(lfi)
Flag voxels whose removal would disconnect their local grain neighbourhood.
Usage
import upxo.gbops.grainBoundOps3d as gbOps
@author: Dr. Sunil Anandatheertha
- upxo.gbops.grainBoundOps3d.compute_gb_boundary_mask_interiorVoxels(lfi)
Detect interior grain-boundary voxels using 6-connectivity.
A voxel is classified as a boundary voxel when at least one of its six face-adjacent neighbours (±x, ±y, ±z) carries a different grain ID. Voxels on the outermost shell of the array are excluded; use
compute_gb_boundary_mask()to include those.The loop over the x-axis is parallelised with
prange; inner loops remain serial so that Numba can apply vectorisation.- Parameters:
lfi (numpy.ndarray of int, shape (nx, ny, nz)) – 3-D array of integer grain (feature) IDs.
- Returns:
boundary – Boolean mask where
Truemarks a grain-boundary voxel. The outermost shell (index 0 and n-1 in each axis) is alwaysFalse— usecompute_gb_boundary_mask()to set it.- Return type:
Notes
Decorated with
@njit(parallel=True): the function is JIT-compiled by Numba on the first call and cached for subsequent calls. It is called internally bycompute_gb_boundary_mask(); direct use is only needed when the RVE outer faces should be excluded from the boundary mask.Usage
import upxo.gbops.grainBoundOps3d as gbOps
Examples
import numpy as np import upxo.gbops.grainBoundOps3d as gbOps lfi = np.zeros((10, 10, 10), dtype=np.int32) lfi[5:, :, :] = 1 # two-grain RVE split at x=5 mask = gbOps.compute_gb_boundary_mask_interiorVoxels(lfi) # voxels at x=4 and x=5 that face the split are True print(mask[4, 5, 5], mask[5, 5, 5]) # True True
- upxo.gbops.grainBoundOps3d.compute_gb_boundary_mask(lfi)[source]
Compute a grain-boundary mask that includes the RVE outer shell.
Calls
compute_gb_boundary_mask_interiorVoxels()to detect all interior boundary voxels (6-connectivity criterion), then sets every voxel on the six outer faces of the bounding box toTrue. This represents the physical convention that grains touching the RVE surface are bounded by that surface.- Parameters:
lfi (numpy.ndarray of int, shape (nx, ny, nz)) – 3-D array of integer grain (feature) IDs.
- Returns:
boundary – Boolean mask where
Truemarks either an interior grain-boundary voxel or a voxel on the outer RVE shell.- Return type:
Notes
The outer shell assignment is O(n²) per face and does not require Numba. The dominant cost is the parallel interior scan in
compute_gb_boundary_mask_interiorVoxels().Usage
import upxo.gbops.grainBoundOps3d as gbOps
Examples
import numpy as np import upxo.gbops.grainBoundOps3d as gbOps lfi = np.zeros((10, 10, 10), dtype=np.int32) lfi[5:, :, :] = 1 mask = gbOps.compute_gb_boundary_mask(lfi) # All six outer faces are True assert mask[0, :, :].all() assert mask[-1, :, :].all() # Interior boundary plane is also True assert mask[4, 5, 5] and mask[5, 5, 5]
- upxo.gbops.grainBoundOps3d.identify_articulation_voxels(lfi)[source]
Identify voxels whose removal disconnects their local grain neighbourhood.
For every boundary voxel, the function extracts the 3×3×3 neighbourhood and checks whether the same-grain voxels within that stencil remain 6-connected after the centre voxel is excluded. If they split into more than one connected component the centre voxel is an articulation point: removing it (e.g. during surface reconstruction or mesh smoothing) would create a topological hole in the grain.
- Parameters:
lfi (numpy.ndarray of int, shape (nx, ny, nz)) – 3-D array of integer grain (feature) IDs.
- Returns:
articulation_mask – Boolean mask where
Truemarks a voxel whose removal would disconnect the local 26-neighbourhood same-grain region under 6-connectivity.- Return type:
Notes
Complexity is O(N_boundary) × O(1) per voxel (the 3×3×3 stencil is constant size), so runtime scales with the total grain-boundary area rather than the full volume.
A simplified x-shift is used for the initial boundary pre-filter: only voxels where
lfi[i,j,k] != lfi[i-1,j,k]are considered. This may miss some boundary voxels detected by the full 6-neighbour check incompute_gb_boundary_mask_interiorVoxels(), but reduces unnecessary 6-connectivity tests on clearly interior voxels.The connected-component labelling is performed by
cc3d.connected_componentswithconnectivity=6.Usage
import upxo.gbops.grainBoundOps3d as gbOps
Examples
import numpy as np import upxo.gbops.grainBoundOps3d as gbOps lfi = np.zeros((20, 20, 20), dtype=np.int32) lfi[10:, :, :] = 1 art = gbOps.identify_articulation_voxels(lfi) # For a clean planar boundary no articulation voxels should be present print("Articulation voxels found:", art.sum())
See also
compute_gb_boundary_maskIdentify all grain-boundary voxels.