graintrace.nf#

NF (near-field) HEDM voxel grid processing subpackage.

NF point-cloud to fixed-grid conversion and VTK export helpers.

graintrace.nf.convert.layer_number(path, token='layer')[source]#

Extract the layer index that follows token in a filename.

Raises:

ValueError if no layer number can be found. –

Parameters:
Return type:

int

graintrace.nf.convert.nf_to_pointcloud(folder, dz, layer_token='layer')[source]#

Convert nearfield data (.mic/.csv layers) to a stacked point cloud.

Parameters:
  • folder (str) – folder with nearfield data

  • dz (float) – spacing between layers in the z-direction

graintrace.nf.convert.process_layer_pointcloud(df_layer, plane_points, nx, ny)[source]#

Interpolate one point-cloud layer onto a fixed nx x ny grid.

Returns:

nx x ny x 7 array [phase, Eul1, Eul2, Eul3, X, Y, Z]

Return type:

np.ndarray

graintrace.nf.convert.pointcloud_to_fixed_grid(pointcloud, nx, ny)[source]#

Convert a point cloud to an nx x ny x nz x 7 fixed grid.

Parameters:
  • pointcloud (pd.DataFrame) – columns [phase, Eul1, Eul2, Eul3, X, Y, Z, layer]

  • nx (int) – in-plane grid resolution

  • ny (int) – in-plane grid resolution

Returns:

nx x ny x nz x 7 grid [phase, Eul1, Eul2, Eul3, X, Y, Z]

Return type:

np.ndarray

graintrace.nf.convert.fixed_grid_to_vtk(fixed_grid, filename, varlabels=['phase', 'Eul1', 'Eul2', 'Eul3', 'X', 'Y', 'Z'])[source]#

Save an nx x ny x nz x 7 fixed grid to a structured-grid VTK file.

Parameters:
  • fixed_grid (np.ndarray) – grid [phase, Eul1, Eul2, Eul3, X, Y, Z]

  • filename (str) – output VTK filename

  • varlabels (list) – labels for the 7 columns

Flood-fill grain segmentation and cleanup for NF voxel grids.

graintrace.nf.segment.flood(angles, phase, misorientation_tol, connectivity=26, batch_norm=10000000, grain_threshold=100, stop_count=100, **kwargs)[source]#

Flood-fill segmentation of connected components in a 3D voxel grid.

Parameters:
  • angles (nx,ny,nz,3) – Euler angles, interpreted per metrics.misorientation kwargs

  • phase (nx,ny,nz) – phase identifiers per voxel (>0 material, 0 void)

  • misorientation_tol (float) – misorientation tolerance for connectivity

  • connectivity (int) – 6 or 26

  • batch_norm (int) – batch size for distance calculation

  • grain_threshold (int) – minimum segment size to keep

  • stop_count (int) – stop after discarding this many small segments; must be >= 1

  • **kwargs – extra arguments for metrics.misorientation()

Returns:

(nx,ny,nz) array of segmented phases

Raises:

ValueError – if stop_count is less than 1

graintrace.nf.segment.infill_nearest_neighbor(grid, connectivity=6)[source]#

Infill unmarked voxels (phase == -1) from nearest filled neighbors.

Parameters:
  • grid (nx,ny,nz,7) – fixed grid, grid[…,0] is the phase index

  • connectivity (int) – 6 or 26

Returns:

(nx,ny,nz,7) tensor with all phase == -1 voxels filled

graintrace.nf.segment.remove_small_segments(grid, min_size, connectivity=6)[source]#

Merge segments smaller than min_size into their largest-contact neighbor.

Parameters:
  • grid (nx,ny,nz,7) – fixed grid, grid[…,0] is the phase index

  • min_size (int) – minimum segment size in voxels

  • connectivity (int) – 6 or 26

Returns:

(nx,ny,nz,7) tensor with small segments merged into neighbors

SCULPT hex meshing and orientation mapping for NF voxel grids.

graintrace.nf.mesh.write_spn(data, filename_spn, filename_orientations, angle_convention='bunge', angle_type='radians', symmetry='1')[source]#

Write an spn and per-grain orientation file from fixed grid data.

Parameters:
  • data (np.ndarray) – nx x ny x nz x 7 array [phase, Eul1, Eul2, Eul3, X, Y, Z]

  • filename_spn (str) – output spn filename

  • filename_orientations (str) – output orientations filename

  • angle_convention (str) – ‘kocks’, ‘bunge’, or ‘roe’

  • angle_type (str) – ‘degrees’ or ‘radians’

  • symmetry (str) – crystal symmetry for the per-grain orientation average (see graintrace.nf.metrics.average_rotations()). Pass the crystal point group (e.g. "432") so voxels of one grain reported in different symmetry variants average correctly.

Raises:

ValueError – if any voxel still carries a negative (“unsegmented”) phase. Such a voxel is not a grain, and mapping it to void here would silently delete material from the mesh, so the caller decides.

graintrace.nf.mesh.mesh_sculpt(sculpt_config, sculpt_options, input_spn, data, output_mesh_filename, output_angle_filename, split_output_prefix='output_mesh', angle_convention='bunge', angle_type='radians', symmetry='1')[source]#

Call sculpt (+epu) to generate an Exodus mesh from an spn file.

Parameters:
  • sculpt_config (dict) – configuration for the sculpt call

  • sculpt_options (list) – extra command line options for sculpt

  • input_spn (str) – path to input spn file

  • data (np.ndarray) – nx x ny x nz x 7 array [phase, Eul1, Eul2, Eul3, X, Y, Z]

  • output_mesh_filename (str) – path to output exodus mesh file

  • output_angle_filename (str) – path to output orientations file

  • split_output_prefix (str) – prefix for temporary output files

  • angle_convention (str) – ‘kocks’, ‘bunge’, or ‘roe’

  • angle_type (str) – ‘degrees’ or ‘radians’

  • symmetry (str) – crystal symmetry for the per-block orientation average (see graintrace.nf.metrics.average_rotations()).

graintrace.nf.mesh.rescale_exodus_mesh(exo_file, data)[source]#

Rescale Exodus mesh coordinates to match those in a fixed grid.

Parameters:
  • exo_file (str) – path to Exodus mesh file

  • data (np.ndarray) – nx x ny x nz x 7 array [phase, Eul1, Eul2, Eul3, X, Y, Z]

graintrace.nf.mesh.map_orientations(exo_file, data, output_angle_filename, angle_convention='bunge', angle_type='radians', symmetry='1', background_id=0)[source]#

Map orientations from fixed grid data onto an Exodus mesh’s blocks.

Each Exodus element block is one grain, so a block’s elements are looked up against that grain’s own voxels only: void voxels (background_id, which carry a meaningless Euler (0, 0, 0)) are excluded from the search, and the per-block KD-tree is restricted to voxels of the block’s grain. A single tree over every voxel lets a surface or void-adjacent element take the identity from a void voxel, or a neighbouring grain’s orientation across the boundary.

Block-to-grain association uses the Exodus block ids (eb_prop1) when they are all present in the grid – the SCULPT path, where write_spn() relabels the grid ids to the spn material ids that become the block ids. Otherwise, when the block count matches the grain count, blocks are associated positionally with the sorted non-void grain ids.

Parameters:
  • exo_file (str) – path to Exodus mesh file

  • data (np.ndarray) – nx x ny x nz x 7 array [phase, Eul1, Eul2, Eul3, X, Y, Z]

  • output_angle_filename (str) – path to output orientations file

  • angle_convention (str) – ‘kocks’, ‘bunge’, or ‘roe’

  • angle_type (str) – ‘degrees’ or ‘radians’

  • symmetry (str) – crystal symmetry for the per-block orientation average (see graintrace.nf.metrics.average_rotations()).

  • background_id (int) – grid id treated as void and excluded from the lookup. Default 0.

Raises:

ValueError – if the grid has no non-void voxels, or the Exodus blocks cannot be associated with the grid’s grains by id or by position.

graintrace.nf.mesh.write_voxel_exodus(data, output_mesh_filename, output_angle_filename, angle_convention='bunge', angle_type='radians', background_id=0, symmetry='1')[source]#

Dump a segmented voxel grid directly to an Exodus hex mesh (no SCULPT).

Emits one axis-aligned cube HEX8 per non-background voxel, with nodes shared between adjacent voxels (conformal, including across grain boundaries). Because every element is a perfect cube its scaled Jacobian is exactly 1, so the mesh has no inverted/sliver elements – at the cost of stair-stepped grain boundaries and one hex per filled voxel. This is the robust alternative to mesh_sculpt when conformal smoothing produces bad elements.

Parameters:
  • data – (nx, ny, nz, 7) array [grain_id, Eul1, Eul2, Eul3, X, Y, Z] (torch or numpy); coordinates are voxel centers.

  • output_mesh_filename (str) – output Exodus (.e) path. One element block per grain, block ids relabeled to a contiguous 1..N (matching MOOSE/PUMA).

  • output_angle_filename (str) – orientation output stem; the per-block MRP file is written to output_angle_filename + ".csv" (one row per block, same order/convention as map_orientations).

  • angle_convention (str) – ‘kocks’, ‘bunge’, or ‘roe’.

  • angle_type (str) – ‘degrees’ or ‘radians’.

  • background_id (int) – voxel id treated as void (skipped). Default 0. Voxels with a negative (“unsegmented”) id are skipped as well, so an orphan left by remove_small_segments does not become a block of its own and shift every grain id.

  • symmetry (str) – crystal symmetry for the per-block orientation average (see graintrace.nf.metrics.average_rotations()).

Returns:

{‘nodes’, ‘elements’, ‘blocks’} counts.

Return type:

dict

Orientation-field smoothing for NF voxel grids.

graintrace.nf.smooth.smooth(data, connectivity=6, symmetry='1', angle_convention='bunge', angle_type='radians')[source]#

Smooth voxel orientations via a neighbor quaternion mean.

Parameters:
  • data (nx,ny,nz,7) – fixed grid [phase, Eul1, Eul2, Eul3, X, Y, Z]

  • connectivity (int) – 6 or 26

  • symmetry (str) – crystal symmetry in orbifold notation

  • angle_convention (str) – ‘kocks’, ‘bunge’, or ‘roe’

  • angle_type (str) – ‘degrees’ or ‘radians’

Voxel connectivity helpers for NF grid images.

graintrace.nf.image.get_neighbor_indices(offsets, shape)[source]#

Compute neighbor indices for every voxel given a set of offsets.

Parameters:
  • offsets – (n_offsets, 3) tensor of (dx, dy, dz)

  • shape – (nx, ny, nz) tuple

Returns:

(n_offsets, nx, ny, nz) clamped neighbor indices valid: (n_offsets, nx, ny, nz) mask of in-bounds neighbors

Return type:

Xk, Yk, Zk

Orientation averaging and misorientation metrics for NF grids.

graintrace.nf.metrics.fold_to_reference_variant(R, symmetry)[source]#

Fold rotations onto the symmetry variant closest to the first of the set.

A crystal orientation has one rotation matrix per symmetry operator (24 for cubic 432), and a reconstruction may report neighbouring voxels of one grain in different variants. Averaging across variants is meaningless, so every member is first replaced by the symmetry-equivalent that is closest (largest Frobenius inner product) to the reference member R[..., 0, :, :].

Parameters:
  • R – (..., N, 3, 3) rotation matrices; the set is averaged over axis -3.

  • symmetry (str) – crystal symmetry in orbifold notation; "1" disables folding and returns R unchanged.

Returns:

(..., N, 3, 3) rotation matrices, all in a common symmetry variant.

graintrace.nf.metrics.average_rotations(e, angle_convention='kocks', angle_type='radians', symmetry='1')[source]#

Average a set of Euler angles via quaternion (eigenvector) mean.

Parameters:
  • e – Nx…x3 array of Euler angles

  • angle_convention (str) – ‘kocks’, ‘bunge’, or ‘roe’

  • angle_type (str) – ‘degrees’ or ‘radians’

  • symmetry (str) – crystal symmetry in orbifold notation (e.g. "432"). Members are folded into a common symmetry variant before averaging, so a grain whose voxels are reported in different variants still yields the correct mean. "1" (the default) disables folding and gives the plain quaternion eigenvector (Markley) mean.

Returns:

averaged orientation as a neml2 v3 MRP (…, 3) and as Euler angles (…, 3) in the given convention.

Return type:

tuple (mrp, euler)

graintrace.nf.metrics.batched_norm(v1, v2, norm, chunk_size, **kwargs)[source]#

Compute norm(v1, v2) in chunks of chunk_size to save memory.

graintrace.nf.metrics.misorientation(e1, e2, angle_convention='kocks', angle_type='degrees', symmetry='1')[source]#

Compute misorientation angles between two sets of Euler angles.

Parameters:
  • e1 – Nx3 arrays of Euler angles

  • e2 – Nx3 arrays of Euler angles

  • angle_convention (str) – ‘kocks’, ‘bunge’, or ‘roe’

  • angle_type (str) – ‘degrees’ or ‘radians’

  • symmetry (str) – crystal symmetry in orbifold notation

Returns:

Nx1 array of misorientation angles in degrees