upxo.pxtal.geometrification module

Geometrification module for converting raster grain structures to polygon representations.

class upxo.pxtal.geometrification.polygonised_grain_structure(lgi, gids, neigh_gid_pxtal)[source]

Bases: object

Raster-to-polygon geometrification of a 2D labelled grain image.

Converts an LFI / lgi field into Shapely polygons, grain-boundary segments, junction points, and neighbour topology for geometric analysis and conformal meshing. Intermediate stores include raw and smoothed GB multipolylines, centroids, and quality metrics.

lgi, gid, n

Source label image, grain IDs, and grain count.

polygons, GRAINS, POLYXTAL

Grain polygons and assembled geometric polycrystal views.

neigh_gid, gid_pair_ids*

Neighbour topology and unique neighbour pairs.

GBSEG*, GBP*, JNP*, gbsegments*

Grain-boundary segments, points, and junction points (raw/sorted).

centroids, quality, smoothed

Centroids, polygonisation quality flags, smoothing state.

EPS_coord_coincide = 1e-08
lgi
gid
neigh_gid_pxtal
n
GBSEG
polygons
polygons_raw_exteriors
polygons_raw_holes
gsmp
smoothed
geometrify(verbose=True)[source]

Geometrify.

polygonize(user_lgi=False, lgi=None, user_gids=False, gids=None, verbose=True)[source]

Polygonize grains in self.lgi.

setup_gsmp_datastructure()[source]

Setup gsmp datastructure.

make_gsmp(verbose=True)[source]

Build and return gsmp.

make_polygonal_grains_raw(verbose=True, perform_xtal_dict_check=True)[source]

Build and return polygonal grains raw.

property expol

Expol.

property hlpol

Hlpol.

property holes_exist

Holes exist.

property allpol

Allpol.

set_polygons(verbose=True)[source]

Set self.polygons to the list of external grain polygons.

find_neighbors(verbose=True)[source]

Calculate neighbouring polygon IDs for all polygons in the grain structure.

Parameters:

polygons (list) – A list of Shapely Polygon objects.

Returns:

Keys are polygon IDs (1-based) and values are lists of neighbouring polygon IDs.

Return type:

dict

val_neigh_gid(pixl_gs_neigh_gid)[source]
Parameters:

pixl_gs_neigh_gid (dict) – neigh_gid dict of the pixellated grain structure (i.e. mcgs).

Returns:

True if validation passes, else False.

Return type:

bool, bool

make_neighpols()[source]

Build and return neighpols.

extract_gbsegments_raw()[source]

Extract gbsegments raw.

extract_gbsegments_mls_raw()[source]

Extract gbsegments mls raw.

get_polygon_touch_lines(poly1, poly2)[source]

Calculate the lines or points where two Shapely polygons touch.

Parameters:
  • poly1 (shapely.geometry.Polygon) – The two polygon objects to test.

  • poly2 (shapely.geometry.Polygon) – The two polygon objects to test.

Returns:

List of Shapely LineString objects if the polygons share edges, or Shapely Point objects if they touch at a single point.

Return type:

list

get_multilinestring_touch_points(mls2)[source]

Calculate the point(s) where two Shapely MultiLineStrings touch.

Parameters:
  • mls1 (shapely.geometry.MultiLineString) – The two MultiLineString objects to test.

  • mls2 (shapely.geometry.MultiLineString) – The two MultiLineString objects to test.

Returns:

List of Shapely Point objects at touch points, or empty list if none.

Return type:

list

area_gid(gid, gsrepr='raw')[source]

Return area of gid grain. Valid values for gsrepr are self.gsmp.keys(). In any case, the returned area will be geometric and not pixellated.

Examples

self.area_gid(1, gsrepr='raw')
plot_linestrings(ax=None, color='blue', linewidth=1, **kwargs)[source]

Plot a list of Shapely LineStrings and return the axis.

Parameters:
  • linestrings (list) – A list of LineString or MultiLineString objects.

  • ax (matplotlib.axes.Axes, optional) – Axes object to plot on. If None, a new figure and axis are created.

  • color (str, optional) – Line colour. Default is 'blue'.

  • linewidth (float, optional) – Line width. Default is 1.

  • **kwargs – Additional keyword arguments passed to plt.plot().

Returns:

The Axes object on which the lines were plotted.

Return type:

matplotlib.axes.Axes

plot_gsmp(raw=True, overlay_on_lgi=False, xoffset=0.5, yoffset=0.5, ax=None)[source]

Plot multi-polygon form of the grain structure.

Parameters:
  • raw (bool, optional) – If True, plot the raw gsmp. Default is True.

  • overlay_on_lgi (bool, optional) – If True, overlay the polygons on the lgi image. Default is False.

  • xoffset (float, optional) – Coordinate offsets applied before plotting. Default is 0.5.

  • yoffset (float, optional) – Coordinate offsets applied before plotting. Default is 0.5.

  • ax (matplotlib.axes.Axes, optional) – Axes object to plot on. If None, a new figure and axis are created.

Return type:

matplotlib.axes.Axes

plot_grains_gids(gids, add_points=True, points=None, gclr='color', title='user grains', cmap_name='viridis', plot_centroids=True, add_gid_text=True, plot_gbseg=False, bjp_kwargs={'marker': 'o', 'mec': 'black', 'mfc': 'yellow', 'ms': 2.5}, addpoints_kwargs={'marker': 'x', 'mec': 'black', 'mfc': 'black', 'ms': 5})[source]
Parameters:
  • gids (int or list) – Either a single grain index number or a list of them.

  • title (str, optional) – Plot title. Default is "user grains".

  • gclr (str, optional) – Colour mode. Options: 'color', 'binary', 'grayscale'.

Return type:

matplotlib.axes.Axes

Examples

# After acquiring gids for aspect_ratio between ranks 80 and 100,
# visualize those grains.
gid, _, __ = PXGS.gs[8].get_gid_prop_range(PROP_NAME='aspect_ratio',
                                            range_type='rank',
                                            rank_range=[80, 100])
PXGS.gs[8].plot_grains_gids(gid, cmap_name='CMRmap_r')
set_minimum_nnodes_per_gbseg()[source]

Set or update minimum nnodes per gbseg.

subdivide_gbsegments(method=1)[source]

Subdivide grain boundary segments by inserting additional nodes.

smooth_moving_avg(n=3)[source]

Smooth moving avg.

smooth_polynomial()[source]

Smooth polynomial.

extract_gids_for_smoothing(verbose=True)[source]

Extract gids for smoothing.

heal_edges(verbose=True)[source]

Heal edges.

plot_gbseg(gid, segid=[])[source]

Visualise gbseg using Matplotlib or PyVista.

Link polygons.

heal_polygons()[source]

Heal polygons.

write_abq_script_data()[source]

Export or convert to the ABQ script data format.

set_mesh_properties()[source]

Set or update mesh properties.

mesh()[source]

Mesh.

assess_mesh_quality()[source]

Assess mesh quality.

export_mesh()[source]

Export the mesh data.

set_grain_centroids_raw(verbose=True)[source]

Set raw grain centroids computed from lgi pixel locations.

set_grain_centroids(verbose=True)[source]

Set grain centroids from polygon coordinates.

set_polygonization_xyoffset(xyoffset, verbose=True)[source]

Set or update polygonization xyoffset.

pix_to_geom(polygonisation_offset=0.5, verbose=True, perform_xtal_dict_check=True)[source]

Pix to geom.

Grain structures with island grains (a grain enclosed by another) are split into a hole-free structure plus one structure per island cluster; see _pix_to_geom_islands().

get_bounds_from_grain_boundary_points()[source]

Get x and y bounds from grain boundary points.

Notes

Works on grain boundary points obtained from polygonization of the raster image. self.xyoffset is subtracted from polygonization end coordinates to align with MCGS 2D results.

Examples

xmin, xmax, ymin, ymax = self.get_bounds_from_grain_boundary_points()
set_grain_loc_ids(verbose=True)[source]

Identify the location of grains and place their IDs in self.grain_loc_ids dictionary.

get_junction_points_from_grain_intersections(verbose=True)[source]

Return the junction points from grain intersections.

For every pair of intersecting grain polygons the end points of their shared boundary are junction points: both ends of a line, the boundary points of a multi-line, or a single touching point. Mixed intersections (a line plus an isolated point) contribute nothing, as before. Pairs come from an R-tree query and all intersections are computed in one vectorised call.

extract_GBP(verbose=True)[source]

Extract grain boundary points data from polygonization coordinate results.

set_up_quality_measures(verbose=True)[source]

Initiate the quality dictionary to hold results on operation quality.

get_bounds_from_GBP()[source]

Calculate the bounds of the polygonized grain structure.

find_GBP_at_boundary(verbose=True)[source]

Identify grain boundary points lying on the grain structure boundary.

update_JNP_from_GBP_at_boundary(verbose=True)[source]

Update junction points with the grain boundary points which are on the boundaries of the poly-xtal.

build_jnp_objects(verbose=True)[source]

Build coordinates, UPXO Point2d objects and shapely point objects of all junction points.

build_all_gbp_objects(verbose=True)[source]

Build coordinates of all grain boundary points. Global.

get_gbp_grain_wise_coords(verbose=True)[source]

Build coordinates of all grain boundary points, grain-wise.

build_jnp_grain_wise_indices(verbose=True)[source]

Build indices of jnp for every grain. Indices will be from jnp_all_coords. These indices relate to:

  • jnp_all_coords, jnp_all_upxo, jnp_all_shapely

  • jnp_all_upxo_mp.points, jnp_all_upxo_mp.coords

Examples

# Data access:
jnp_all_coords[jnp_grain_wise_indices[gid]]
jnp_all_upxo[jnp_grain_wise_indices[gid]]
jnp_all_upxo_mp.points[jnp_grain_wise_indices[gid]]

# Verification:
gid = 10
plt.imshow(geom.lgi)
coord = jnp_all_coords[jnp_grain_wise_indices[gid]]
plt.plot(coord[:, 0], coord[:, 1], 'ko')

Notes

jnp_all_upxo and jnp_all_upxo_mp.points share the same objects: id(jnp_all_upxo[i]) == id(jnp_all_upxo_mp.points[i])

build_gbp_grain_wise_indices_geometric(verbose=True)[source]

This is to correct inconsistencies in build_jnp_grain_wise_indices method. There are cases which lead to mulline constityutent lines intersecting each other. This step is crucial to avoid it.

build_gbp_grain_wise_indices_coordbased(verbose=True)[source]

Build and return gbp grain wise indices coordbased.

build_gbp_grain_wise_indices_pointsbased(verbose=True)[source]

Build and return gbp grain wise indices pointsbased.

build_gbmullines_grain_wise(verbose=True)[source]

Build grain boundary multi-linestring objects from grain boundary point data.

arrange_junction_point_coords_new(gbcoords_thisgrain, junction_points_coord)[source]

Arrange junction point coords new.

build_sorted_jnp_objects(plot=False, verbose=True)[source]

Build and return sorted jnp objects.

align_gbmullines_start_to_jnp_start(plot_bf=False, plot_af=False, verbose=True)[source]

Align gbmullines start to jnp start.

splice_grain_boundary_segments_at_junction_points(plot=False, verbose=True)[source]

Splice the grain boundary into grain boundary segments using jnp point data

find_quality_of_grain_boundary_segmentation(verbose=True)[source]

Assess quality of grain boundary segmentation by comparing segment counts per grain.

create_neigh_gid_pair_ids(neigh_gid, verbose=True)[source]

Create a dictionary mapping unique grain pairs to integer IDs.

Parameters:

neigh_gid (dict) – Keys are grain IDs and values are lists of neighbouring grain IDs.

Returns:

Keys are integer pair IDs and values are lists of two grain IDs.

Return type:

dict

get_random_gbpoint_between_jnpoints(gbcoords, jnpcoords)[source]

Return a random grain boundary point located between two junction points.

get_random_gbpoints_between_jnpoints(gid)[source]

Return random grain boundary points between junction points for a given grain.

setup_neigh_connectivity_flags_DS(neigh_sense='lr', field_names=['gbseg', 'nnodes_eq', 'length_eq', 'n', 'areas_raw', 'uniquified'], verbose=True)[source]

Setup neigh connectivity flags ds.

extract_end_coordinates_of_grain_boundary_segments_grain_wise(gid)[source]

Extract end coordinates of grain boundary segments grain wise.

set_neigh_connectivity_flags_DS(centroid_eq_EPS=1e-08, verbose=True)[source]

Set or update neigh connectivity flags DS.

get_unique_object_indices(nnodes, lengths, centroids, tol=1e-08)[source]

Find indices of unique objects based on nnodes, lengths, and centroids.

Parameters:
  • nnodes (np.ndarray) – 1D array of nnode values.

  • lengths (np.ndarray) – 1D array of length values.

  • centroids (np.ndarray) – 2D array of centroid coordinates.

  • tol (float, optional) – Tolerance for centroid coordinate comparison. Default is 1e-8.

Returns:

1D array of indices corresponding to unique objects.

Return type:

np.ndarray

gather_grain_boundary_segments_of_all_pairs(verbose=True)[source]

Gather grain boundary segments of all pairs.

consolidate_gbsegments(squeeze_segment_data_structure=False, verbose=True)[source]

Consolidate grain boundary segments by grain ID.

Parameters:

squeeze_segment_data_structure (bool, optional) – If True, flatten nested segment lists. Default is False.

Returns:

Keys are grain IDs and values are lists of grain boundary segments.

Return type:

dict

get_problematic_grains(plot=False)[source]

Def name must be changed.

find_segs_at_loc(gbsegs, axis='y', location=-0.5)[source]

Find segs at loc.

update_consolidated_segments_with_boundary_grain_gids(plot=False, verbose=True)[source]

Set or update te consolidated segments with boundary grain gids.

plot_consolidated_segments()[source]

Visualise consolidated segments using Matplotlib or PyVista.

check_if_all_gbsegs_can_form_closed_rings(GBSEGS, _print_individual_excoord_order_=True, _print_statement_=True)[source]

Check or validate check if all gbsegs can form closed rings.

flip_segments_to_reorder_GBS(plot_each_grain_details=False, verbose=True)[source]

Flip segments to reorder gbs.

update_segflip_requirements(verbose=True)[source]

Set or update te segflip requirements.

sort_subsets_by_original_order(CA, subsets)[source]

Sort subsets of coordinates based on their original order in the CA array.

Parameters:
  • CA (np.ndarray) – The original 2D coordinate array (N x 2).

  • subsets (list) – A list of np.ndarrays, each representing a subset of coordinates.

Returns:

  • list – Sorted np.ndarrays, each subset ordered according to its position in CA.

  • np.ndarray – 1D array of the original subset indices in sorted order.

sort_gbsegments_by_original_order(verbose=True)[source]

Sort gbsegments by original order.

calculate_grain_boundary_coordinates_after_gbseg_reordering(verbose=True)[source]

Calculate grain boundary coordinates after gbseg reordering.

are_grains_closed_usenodes()[source]

Are grains closed usenodes.

are_grains_closed_usecoords()[source]

Are grains closed usecoords.

set_pure_gbpoints(verbose=True)[source]

Set or update pure gbpoints.

plot_reordered_GBCoords(gid, force_close=True)[source]

Visualise reordered GBCoords using Matplotlib or PyVista.

plot_user_gbcoords(gbcoords, lw=1.5)[source]

Visualise user gbcoords using Matplotlib or PyVista.

plot_user_gbcoords1(gbcoords, lw=1.5)[source]

Visualise user gbcoords1 using Matplotlib or PyVista.

construct_geometric_xtals_from_gbcoords(coord_loop_dict, dtype='shapely', saa=True, throw=False, smoothed=None)[source]

self.construct_geometric_xtals_from_gbcoords(GBCoords).

dtype=’upxo’ wraps self.GB[gid] (already a ring2d) directly into a Polygon2d – zero-copy, ignores coord_loop_dict (which was only ever a coordinate array derived from that same ring). A gid with island hole(s) recorded in self.GB_holes (see _assemble_island_results) becomes a NestedPolygon2d instead. smoothed (dtype=’upxo’ only) is the name given to smooth_gbsegs: the smoothed rings and holes in self.smoothed[name] are wrapped instead of the raw ones.

construct_geometric_polyxtal_from_xtals(self.GRAINS.values(), dtype='shapely')[source]
construct_geometric_polyxtal_from_gbcoords(coord_loop_dict, dtype='shapely', saa=True, throw=False, plot_polyxtal=False, verbose=True)[source]

Construct geometric polyxtal from gbcoords.

AssembleGBSEGS(GB, saa=True, throw=False, verbose=True)[source]

Assemblegbsegs.

get_mids_gbsegs(gid)[source]

Return the mids gbsegs.

get_gbmid_indices_at_gid(gid, all_mids)[source]

Return the gbmid indices at gid.

smooth_gbsegs(GB, npasses=2, max_smooth_levels=[3, 3], plot=True, name='kali')[source]

Smooth gbsegs.

For a structure with islands, GB is ignored: the filled structure and each island cluster are smoothed from their own GB.

plotgs(gs_geometric, fig=None, ax=None, cmap='tab20', edgecolor='black', alpha=0.7, lw=1, figsize=(10, 10), dpi=100)[source]

Plotgs.

pxtal
neigh_gid
gbsegments_raw
gbsegments_mls
gbseg_smoothed
GBPOINTS
raster_img_polygonisation_results
neigh_pols
centroids_raw
centroids
xyoffset
grain_loc_ids
JNP
GBP
GBP_pure
GBP_at_boundary
jnp_all_coords
quality
sorted_segs
jnp_all_upxo
jnp_all_shapely
gbp_all_coords
gbp_all_upxo
gbp_all_shapely
jnp_grain_wise_indices
gbp_grain_wise_coords
gbp_grain_wise_indices
gbp_grain_wise_points
gbmullines_grain_wise
junction_points_coord
jnp_all_sorted_coords
jnp_all_sorted_upxo
gbsegments
gid_pair_ids
gid_pair_ids_unique_lr
gid_pair_ids_unique_rl
nconn
GBSEGMENTS
consolidated_segments
GB
GBCoords
GB_holes
GRAINS
POLYXTAL
mids_all_gbsegs
sgseg_obj_list
class upxo.pxtal.geometrification.VoronoiMasking(lfi)[source]

Bases: ABC

Abstract base: Voronoi cells masked by sampling a label field (LFI).

Seeds define a tessellation; each seed samples lfi to obtain a grain/feature ID, then dimension-specific subclasses merge cells sharing an ID into manifolds (Shapely 2D / PyVista 3D).

Factory methods (abstract): by_tessellation, load_tessellation.

map_seeds_to_lfi(channel=0)[source]

Samples the LFI at seed coordinates. Correctly handles the conversion from Cartesian (x, y) seeds to array indices (row, col) for sampling.

abstractmethod classmethod by_tessellation(lfi, seeds)[source]

Factory method to create instance via new Voronoi computation.

abstractmethod classmethod load_tessellation(lfi, filepath)[source]

Factory method to create instance from a saved geometry file.

abstractmethod assemble_cells()[source]

Dimension-specific: Shapely UnaryUnion vs PyVista Merge.

class upxo.pxtal.geometrification.GrainManifold2D(lfi)[source]

Bases: VoronoiMasking

2D grain manifolds from Voronoi tessellation + LFI sampling.

Each seed maps to a label in the LFI; Voronoi polygons with the same label are unioned (Shapely) into one manifold per grain/feature ID. Construct via by_tessellation(lfi, seeds).

classmethod by_tessellation(lfi, seeds, channel=0)[source]

By tessellation.

assemble_cells(polygons)[source]

Steps 5 & 6: Groups by ID and performs manifold union.

classmethod load_tessellation(lfi, filepath)[source]

Placeholder implementation to satisfy abstract requirement.

smooth_interfaces(iterations=10, lmbda=0.5, mu=-0.53, method='taubin', ma_window=3, corner_angle_deg=30.0, thin_grain_px=1.5)[source]

Smooth interfaces.

Parameters:

thin_grain_px (float, optional) – Vertices of every grain whose minimum bounding-rectangle width is below this (pixel units) are held fixed. Smoothing shrinks any small closed loop, since its Laplacian modes all lie outside the Taubin passband. A four-vertex single-pixel grain retains about 0.77 of its size per iteration, so an island (whose vertices are not junctions and so are not pinned otherwise) collapsed to a sliver. Default 1.5 keeps single-pixel grains and one-pixel-wide grains exact. Use 0 to disable.

gb_smooth(ma_window=3, taubin_iter=10, lmbda=0.5, mu=-0.5, ma_smoother_version=2)[source]

Gb smooth.

apply_taubin_smoothing(iterations=10, lmbda=0.5, mu=-0.53)[source]

Step 8: Smooths interfaces while keeping triple points pinned.

trim_to_rve(bounds=(0, 0, 200, 200))[source]

Trims all grain geometries to the exact RVE bounding box. This ensures perfectly straight, rectangular external faces.

class upxo.pxtal.geometrification.GrainManifold3D(lfi)[source]

Bases: VoronoiMasking

3D grain manifolds from Voronoi tessellation + LFI sampling.

3D counterpart of GrainManifold2D: seeds sample a 3D label volume; clipped Voronoi solids (PyVista) are grouped by feature ID. assemble_cells is currently a stub for full boolean union merge.

classmethod by_tessellation(lfi, seeds, channel=0)[source]

By tessellation.

assemble_cells(solids)[source]

Assemble cells.