upxo.connops.neighbour_ops module
Module: neighbour_ops
Neighbour (connectivity) operations for 2D grain structures.
Identifies neighbouring grain IDs and grain-boundary pixel segments for individual grains or across an entire grain structure. Provides first- through nth-order neighbourhood queries with optional Numba acceleration for large datasets.
Usage
import upxo.connops.neighbour_ops as neighOps
- upxo.connops.neighbour_ops.NMB_get_neighbor_mask(neigh_mask, gid)
Mark the boundary pixels of a single grain in a 2D label sub-array.
JIT-compiled with Numba (parallel outer loop via
prange). For every pixel that belongs togid, the four 4-connected neighbours that do not belong togidare marked-1. All remaining pixels are set to0.- Parameters:
neigh_mask (numpy.ndarray of int, shape (R, C)) – Sub-array of the full labelled image covering the extended bounding box of the grain. Modified in-place and returned.
gid (int) – Label value of the grain whose boundary-adjacent pixels are to be identified.
- Returns:
neigh_mask – Same array, now containing
-1at pixels that are direct 4-connected neighbours ofgidbut belong to a different grain, and0everywhere else.- Return type:
numpy.ndarray of int, shape (R, C)
Notes
The outer row loop is parallelised with
prange; the inner column loop is sequential. Bounds checks are explicit — no zero-padding of the input array is required.
- upxo.connops.neighbour_ops.find_neigh_fid(gdict, lfi, fid, nfeatures, include_central_grain=False, update_grain_object=True, user_defined_bbox_ex_bounds=False, bbox_ex_bounds_fid=None, use_numba=False, _char_fx_version_=2, get_gbsegs=True, save_gbsegs=False, dtype_gbseg=numpy.int32, throw=False, throw_gbsegs=False)[source]
Find the neighbour IDs of a single grain and optionally its boundary segments.
Extracts the extended bounding-box sub-array for
fid, constructs a neighbour mask (via Numba or pure Python depending onnfeatures), and returns the unique IDs of all directly touching grains. Grain-boundary segment maps can be generated and stored in the grain object’s sparse format.- Parameters:
gdict (dict) – Grain-object dictionary keyed by grain ID. Each value is either a grain object directly (
_char_fx_version_=2) or a dict with a'grain'key (_char_fx_version_=1).lfi (numpy.ndarray of int, shape (R, C)) – Full labelled feature image for the grain structure.
fid (int) – Grain ID for which neighbours are to be found.
nfeatures (int) – Total number of grains in the structure. Governs the Numba/pure-Python branch threshold (Numba is used when
nfeatures > 2000oruse_numbais True).include_central_grain (bool, default False) – Include
fiditself in the returned neighbour array.update_grain_object (bool, default True) – Write the neighbour ID array into the grain object as
grain.neigh.user_defined_bbox_ex_bounds (bool, default False) – Use
bbox_ex_bounds_fidinstead of the bounding-box stored in the grain object.bbox_ex_bounds_fid (tuple of int or None, default None) –
(rmin, rmax, cmin, cmax)bounding-box extents to use whenuser_defined_bbox_ex_boundsis True.use_numba (bool, default False) – Force use of
NMB_get_neighbor_mask()regardless ofnfeatures._char_fx_version_ ({1, 2}, default 2) – Selects how grain objects are accessed from
gdict. Version 2 accesses grain attributes directly; version 1 assumes a nested dict with a'grain'key.get_gbsegs (bool, default True) – Compute the grain-boundary segment map (a 2D array where each non-zero pixel contains the neighbour grain’s ID at that boundary location).
save_gbsegs (bool, default False) – Serialise the boundary segment map into the grain object in sparse format (
shape,NZI,NZV,dtypekeys).dtype_gbseg (numpy dtype, default numpy.int32) – Data type recorded in the sparse
dtypefield when saving segments.throw (bool, default False) – Return
neighbour_ids(or a tuple) instead ofNone.throw_gbsegs (bool, default False) – When both
get_gbsegsandthrow_gbsegsare True, return(neighbour_ids, gbsegs)instead ofneighbour_idsalone.
- Returns:
None – When
throwis False.neighbour_ids (numpy.ndarray of int) – Unique IDs of all grains directly touching
fid. Returned whenthrowis True andthrow_gbsegsis False.(neighbour_ids, gbsegs) (tuple) – Tuple of the neighbour ID array and the 2D boundary segment map. Returned when both
throwandthrow_gbsegsare True.
Notes
Grain-boundary segments are always stored in the grain object in sparse format (non-zero indices and values). To reconstruct the dense array use the
lfi_gbsegsproperty of the grain object, or rebuild manually from theshape,NZI, andNZVfields.Examples
Typical usage through the grain-structure object (which calls this function internally):
from upxo.ggrowth.mcgs import mcgs pxtal = mcgs(input_dashboard='input_dashboard.xls') pxtal.simulate() pxtal.detect_grains() tslice = 10 pxtal.gs[tslice].char_morph_2d(char_gb=True) pxtal.gs[tslice].find_neigh(include_central_grain=False) print(pxtal.gs[tslice].neigh_gid[10])
For direct use at module level:
import upxo.connops.neighbour_ops as neighOps neigh_ids = neighOps.find_neigh_fid( gdict, lfi, fid=5, nfeatures=200, throw=True, throw_gbsegs=False)
- upxo.connops.neighbour_ops.find_neigh(lfi, fids, gdict, nfeatures, include_central_grain=False, char_fx_version=2, print_msg=True, user_defined_bbox_ex_bounds=False, bbox_ex_bounds=None, update_grain_object=True, use_numba=False)[source]
Find the neighbour IDs for every grain in the structure.
Iterates over all grain IDs in
fids, callsfind_neigh_fid()for each, and collects the results into a single dictionary.- Parameters:
lfi (numpy.ndarray of int, shape (R, C)) – Full labelled feature image for the grain structure.
fids (iterable of int) – Grain IDs to process. Typically
range(1, nfeatures + 1).gdict (dict) – Grain-object dictionary keyed by grain ID (see
find_neigh_fid()).nfeatures (int) – Total number of grains; governs the Numba/pure-Python branch in
find_neigh_fid().include_central_grain (bool, default False) – Include each grain’s own ID in its neighbour list.
char_fx_version ({1, 2}, default 2) – Grain-object access version forwarded to
find_neigh_fid().print_msg (bool, default True) – Print a progress message before the loop begins.
user_defined_bbox_ex_bounds (bool, default False) – Use bounding-box extents from
bbox_ex_boundsinstead of those stored in the grain objects.bbox_ex_bounds (dict or None, default None) – Mapping of grain ID →
(rmin, rmax, cmin, cmax)whenuser_defined_bbox_ex_boundsis True.update_grain_object (bool, default True) – Write each grain’s neighbour array into its grain object.
use_numba (bool, default False) – Force Numba acceleration regardless of
nfeatures.
- Returns:
neigh_gid – Mapping of grain ID → list of neighbour grain IDs.
- Return type:
Examples
from upxo.ggrowth.mcgs import mcgs pxtal = mcgs(input_dashboard='input_dashboard.xls') pxtal.simulate() pxtal.detect_grains() tslice = 10 pxtal.gs[tslice].char_morph_2d(char_gb=True) # With central grain included pxtal.gs[tslice].find_neigh(include_central_grain=True) print(pxtal.gs[tslice].neigh_gid[10]) # Without central grain pxtal.gs[tslice].find_neigh(include_central_grain=False) print(pxtal.gs[tslice].neigh_gid[10])
- upxo.connops.neighbour_ops.get_upto_nth_order_neighbors(neigh_gid, grain_id, neigh_order, include_parent=True, output_type='list')[source]
Return all grains reachable within
neigh_orderhops fromgrain_id.Starting from the 1st-order neighbours of
grain_id, the function iteratively expands the set by one hop per order untilneigh_orderhops are exhausted.- Parameters:
neigh_gid (dict[int, list of int]) – Pre-computed neighbour map — typically
gs.neigh_gidpopulated byfind_neigh().grain_id (int) – ID of the central grain.
neigh_order (int) – Maximum hop count.
1returns direct neighbours;2returns neighbours-of-neighbours, and so on.0returns[grain_id].include_parent (bool, default True) – Include
grain_idin the returned set.output_type ({'list', 'nparray', 'set'}, default 'list') – Return type.
'nparray'also accepts'np','np.array','numpy'.
- Returns:
All grain IDs within
neigh_orderhops ofgrain_id.- Return type:
list, numpy.ndarray, or set
Examples
import upxo.connops.neighbour_ops as neighOps # neigh_gid populated by find_neigh or find_neigh_v2 result = neighOps.get_upto_nth_order_neighbors( gs.neigh_gid, grain_id=6, neigh_order=3, include_parent=True, output_type='list')
See also
get_nth_order_neighborsReturns only the grains at exactly order n.
- upxo.connops.neighbour_ops.get_nth_order_neighbors(neigh_gid, grain_id, neigh_order, include_parent=True)[source]
Return the grains reachable in exactly
neigh_orderhops.Computes the set difference between the up-to-n and up-to-(n-1) neighbourhood sets to isolate grains that first become reachable at exactly the requested order.
- Parameters:
neigh_gid (dict[int, list of int]) – Pre-computed neighbour map — typically
gs.neigh_gid.grain_id (int) – ID of the central grain.
neigh_order (int) – Exact hop count. Grains reachable in fewer hops are excluded.
include_parent (bool, default True) – Include
grain_idin the candidate set before differencing.
- Returns:
Grain IDs reachable in exactly
neigh_orderhops fromgrain_id.- Return type:
Examples
import upxo.connops.neighbour_ops as neighOps result = neighOps.get_nth_order_neighbors( gs.neigh_gid, grain_id=10, neigh_order=2, include_parent=True)
- upxo.connops.neighbour_ops.get_upto_nth_order_neighbors_all_grains(neigh_gid, gids, neigh_order, include_parent=True, output_type='list')[source]
Return up-to-nth-order neighbours for every grain.
Calls
get_upto_nth_order_neighbors()for each grain ID ingidsand collects the results into a dictionary.- Parameters:
neigh_gid (dict[int, list of int]) – Pre-computed neighbour map — typically
gs.neigh_gid.gids (array-like of int) – Grain IDs to process — typically
gs.gid.neigh_order (int) – Maximum hop count.
include_parent (bool, default True) – Include each grain’s own ID in its neighbour list.
output_type ({'list', 'nparray', 'set'}, default 'list') – Return type for each grain’s neighbour collection.
- Returns:
Mapping of grain ID → neighbours up to order
neigh_order.- Return type:
dict[int, list or numpy.ndarray or set]
Examples
import upxo.connops.neighbour_ops as neighOps result = neighOps.get_upto_nth_order_neighbors_all_grains( gs.neigh_gid, gs.gid, neigh_order=2, include_parent=True, output_type='list') print(result[5]) # neighbours of grain 5 up to order 2
- upxo.connops.neighbour_ops.get_nth_order_neighbors_all_grains(neigh_gid, gids, neigh_order, include_parent=True)[source]
Return the exactly-nth-order neighbours for every grain.
Calls
get_nth_order_neighbors()for each grain ID ingidsand collects results into a dictionary. Only grains first reachable at exactlyneigh_orderhops are included.- Parameters:
neigh_gid (dict[int, list of int]) – Pre-computed neighbour map — typically
gs.neigh_gid.gids (array-like of int) – Grain IDs to process — typically
gs.gid.neigh_order (int) – Exact hop count.
include_parent (bool, default True) – Include each grain’s own ID in the candidate set before differencing.
- Returns:
Mapping of grain ID → list of grains reachable in exactly
neigh_orderhops.- Return type:
Examples
import upxo.connops.neighbour_ops as neighOps result = neighOps.get_nth_order_neighbors_all_grains( gs.neigh_gid, gs.gid, neigh_order=2, include_parent=True) print(result[10]) # grains at exactly order 2 from grain 10
- upxo.connops.neighbour_ops.get_upto_nth_order_neighbors_all_grains_prob(neigh_gid, gids, neigh_order, include_parent=False, print_msg=False, _int_approx_=0.05)[source]
Return up-to-nth-order neighbours for all grains with probabilistic fractional orders.
Extends
get_upto_nth_order_neighbors_all_grains()to accept non-integerneigh_ordervalues. A fractional order such as1.5returns all grains up to order 1 plus a random subset of order-2 grains, where the fraction included equals the decimal part ofneigh_order.- Parameters:
Neighbourhood order.
Integer (or float within
_int_approx_of an integer): delegates directly toget_upto_nth_order_neighbors_all_grains().Non-integer float (e.g.
1.5): returns all up-to-floor neighbours plusceil(decimal_part × |order-n neighbours|)randomly chosen grains from the next order shell.
recalculate (bool, default False) – Force recomputation of the base neighbour map.
include_parent (bool, default False) – Include each grain’s own ID in its neighbour set.
print_msg (bool, default False) – Print a diagnostic message indicating which branch was taken.
_int_approx_ (float, default 0.05) – Maximum distance from the nearest integer for
neigh_orderto be treated as an integer.
- Returns:
When
neigh_orderis (effectively) an integer, a mapping of grain ID → neighbours up to that integer order.When
neigh_orderis a true fraction, a mapping of grain ID → blended neighbour list (floor-order neighbours + probabilistic sample of the next order shell).- Return type:
- Raises:
ValueError – If
neigh_orderis neither an int nor a float.
Examples
from upxo.ggrowth.mcgs import mcgs pxtal = mcgs(input_dashboard='input_dashboard.xls') pxtal.simulate() pxtal.detect_grains() tslice = 10 fn = pxtal.gs[tslice].get_upto_nth_order_neighbors_all_grains_prob neigh_int = fn(1, recalculate=False, include_parent=True) neigh_near = fn(1.06, recalculate=False, include_parent=True) neigh_half = fn(1.5, recalculate=False, include_parent=True) print(neigh_int[22]) # integer-order result for grain 22 print(neigh_near[22]) # near-integer result (treated as order 1) print(neigh_half[22]) # probabilistic blend for grain 22