upxo.pxtal.gridops module

upxo.pxtal.gridops.find_label_voxel_locs(label_array, labels=None, dtype=numpy.int32)[source]

Return a mapping from label ID to voxel coordinates.

upxo.pxtal.gridops.compute_label_bounds(label_locs, array_shape)[source]

Return tight and one-voxel-extended bounds for label coordinates.

upxo.pxtal.gridops.extract_bounded_subarray(array, bounds, label_id)[source]

Return a bounded subarray using one-based label-id indexing.

upxo.pxtal.gridops.map_values_to_labels(label_array, labels, values_by_label, default_value=-1)[source]

Map per-label values onto a labelled array.

upxo.pxtal.gridops.slice_scalar_field_3d(array, normal='x', index=0)[source]

Return an axis-normal slice from a 3D scalar field.

upxo.pxtal.gridops.mask_labels(array, labels, mask_value=-10, valid_labels=None)[source]

Mask selected labels in an array.

upxo.pxtal.gridops.bresenham_line_3d(start, end)[source]

Generate integer locations on a 3D Bresenham line.

upxo.pxtal.gridops.values_along_line_3d(array, start, end)[source]

Return array values along a 3D Bresenham line.

upxo.pxtal.gridops.intercept_properties_from_values(values)[source]

Return intercept grain-size properties from sampled labels.

upxo.pxtal.gridops.intercept_summary_from_values(values, metric='mean', minimum=True, maximum=True, std=True, variance=True)[source]

Return summary intercept grain-size statistics from sampled labels.

upxo.pxtal.gridops.opposing_boundary_points(shape, plane='z', start_skip1=0, start_skip2=0, incr1=2, incr2=2, inclination='none', inclination_extent=0, shift_seperately=False, shift_starts=False, shift_ends=True, start_shift=0, end_shift=0)[source]

Return start/end points on opposing boundary planes of a 3D grid.

upxo.pxtal.gridops.axis_intercept_grain_size(lgi, voxel_size, axis, n_lines_target=300, phase_array=None, valid_phase_ids=None)[source]

Mean linear-intercept grain size along one axis of a 3D labeled grid, in physical units.

Reuses this module’s existing line-sampling/intercept primitives (opposing_boundary_points, values_along_line_3d, intercept_properties_from_values) rather than re-implementing the intercept-counting algorithm – see their docstrings for the underlying method (sample parallel lines spanning the full extent of the grid along the chosen axis; for each line, measure how many voxels belong to each grain it passes through; pool those counts across every sampled line). Label 0 (background/void) is excluded from the pooled statistics.

Axis-order note: opposing_boundary_points’ plane argument assumes a (z, y, x) axis order (its own historical convention: plane=’z’ sweeps array axis 0, plane=’x’ sweeps array axis 2), whereas lgi here follows UPXO’s fm_steel_3d convention of (x, y, z) axis order, i.e. shape (NX, NY, NZ) with axis 0 = X and axis 2 = Z. Concretely this means axis ‘x’ and axis ‘z’ are not the same as the like-named plane value – ‘y’ happens to coincide (axis 1 either way), but ‘x’/’z’ are swapped. This function takes axis in the natural (‘x’, ‘y’, ‘z’) sense and translates it to the correct plane value internally so callers never need to reason about the mismatch themselves.

Parameters:
  • lgi (np.ndarray) – 3D labeled grid, shape (NX, NY, NZ). 0 = background/void, excluded.

  • voxel_size (float) – Physical size of one (assumed isotropic) voxel edge. Every returned length statistic is voxel_count * voxel_size.

  • axis (str) – ‘x’, ‘y’, or ‘z’ – which axis to sample lines along.

  • n_lines_target (int, optional) – Approximate number of sample lines to use, spread evenly over the cross-section perpendicular to axis. Default 300 – enough for a stable estimate without scanning every possible line on large grids.

  • phase_array (np.ndarray, optional) – Per-voxel phase ID, same shape as lgi. When given together with valid_phase_ids, any voxel whose phase is not in valid_phase_ids is excluded from the pooled statistics – exactly like label 0 is – regardless of what label lgi happens to carry there. This lets a caller reject intercepts made with a non-target-phase grain (e.g. retained austenite domains in a lgi that otherwise labels blocks) by phase identity rather than relying on those domains happening to already be label 0 in lgi.

  • valid_phase_ids (sequence, optional) – Phase IDs considered valid/countable; only meaningful together with phase_array. Both must be given together to have any effect.

Returns:

{‘mean’, ‘median’, ‘std’, ‘min’, ‘max’} (all in physical units), plus ‘n_segments’ (grain crossings pooled) and ‘n_lines’ (lines sampled) as plain counts, plus ‘raw_segments’ – the full pooled per-crossing length array (physical units, shape (n_segments,)) the summary statistics above were computed from, for callers that need e.g. IQR/percentiles rather than just mean/median/min/max (matches directional_intercept_grain_size’s own ‘raw_segments’ key/purpose).

Return type:

dict

upxo.pxtal.gridops.directional_intercept_grain_size(lgi, voxel_size, direction, n_lines_target=300, phase_array=None, valid_phase_ids=None)[source]

Mean linear-intercept grain size along an arbitrary 3D direction, in physical units. Same statistics/output shape as axis_intercept_grain_size (and reuses its underlying primitives – values_along_line_3d, intercept_properties_from_values, the same optional phase_array/ valid_phase_ids masking), but not restricted to the X/Y/Z axes: direction can be any 3-vector (need not be unit – it is normalized internally).

This is a separate function from axis_intercept_grain_size (which is left completely untouched) specifically so a caller measuring along a block’s/packet’s own real slicing-plane normal – generally not aligned with any coordinate axis – gets a geometrically correct answer instead of one derived from the axis-aligned statistics.

Sampling method: opposing_boundary_points only supports axis-aligned sweeps, so sample lines here are generated by intersecting candidate lines (parallel to direction, spread over a regular grid in the plane perpendicular to it, centered on the grid’s centroid) against the array’s bounding box via the standard ray/box “slab” method. Candidates that miss the box entirely are dropped – this is what naturally limits sampling to the box’s true (generally non-rectangular, direction-dependent) footprint without needing to compute that polygon explicitly. n_lines_target is therefore an approximate target, same as in axis_intercept_grain_size.

Parameters:
  • lgi (np.ndarray) – 3D labeled grid, shape (NX, NY, NZ). 0 = background/void, excluded.

  • voxel_size (float) – Physical size of one (assumed isotropic) voxel edge.

  • direction (array-like, shape (3,)) – Direction to sample lines along, in the same (x, y, z) voxel-index axis order as lgi (not the (z, y, x) plane convention used elsewhere in this module for axis-aligned sweeps).

  • n_lines_target (int, optional) – Approximate number of sample lines. Default 300.

  • phase_array (optional) – See axis_intercept_grain_size – identical semantics.

  • valid_phase_ids (optional) – See axis_intercept_grain_size – identical semantics.

Returns:

Same shape as axis_intercept_grain_size’s return value.

Return type:

dict

upxo.pxtal.gridops.local_neighborhood(array, loc, radius=1)[source]

Return a bounded cubic neighborhood around a location.

upxo.pxtal.gridops.neighbor_labels_at_location(array, loc, radius=1)[source]

Return labels in a local neighborhood except the center label.

upxo.pxtal.gridops.plane_slice(array, plane='xy', index=0)[source]

Return a slice along one of the three fundamental planes.

upxo.pxtal.gridops.relabel_multistate_slice_2d(scalar_slice, connectivity=2)[source]

Relabel connected regions independently for each value in a 2D slice.

upxo.pxtal.gridops.grid_axis(vmin, vmax, vinc)[source]

Return one regularly-spaced grid axis.

upxo.pxtal.gridops.domain_volume_from_axes(xaxis, yaxis, zaxis)[source]

Return domain volume from grid axes.

upxo.pxtal.gridops.extract_random_subdomains(array, subdomain_shape, n=1, rng=None)[source]

Extract random subdomains from a 3D array.

upxo.pxtal.gridops.make_grid_pxtal(distribution_type, **kwargs)[source]

Build a grid-based polycrystal seed layout for the given distribution type.