upxo.viz.ebsdviz module

EBSD visualisation helpers for UPXO.

All functions accept an upxo.interfaces.defdap.ebsd_reader.EBSDReader instance (or plain NumPy arrays where noted) and produce Matplotlib figures.

Usage

import upxo.viz.ebsdviz as ebsdviz

upxo.viz.ebsdviz.plot_grain_labels(rdr, show_boundaries: bool = True, boundary_color=(0, 0, 0), cmap: str = 'nipy_spectral', figsize: tuple = (7, 6), dpi: int = 100, title: str = 'Grain label field', ax=None)[source]

Plot the grain label field (lfi_ebsd) with optional boundary overlay.

Parameters:
  • rdr (EBSDReader) – Loaded EBSD reader with lfi_ebsd populated.

  • show_boundaries (bool) – Whether to overlay grain boundaries in black.

  • boundary_color (tuple) – RGB tuple for boundary colour (values 0–1).

  • cmap (str) – Matplotlib colormap for grain IDs.

  • figsize (tuple, int) – Figure size and resolution.

  • dpi (tuple, int) – Figure size and resolution.

  • title (str) – Axes title.

  • ax (matplotlib.axes.Axes or None) – If provided, draw onto this axes; otherwise create a new figure.

Return type:

fig, ax

upxo.viz.ebsdviz.plot_euler_maps(rdr, show_boundaries: bool = True, boundary_color=(0, 0, 0), cmaps=('hsv', 'viridis', 'hsv'), figsize: tuple = (15, 5), dpi: int = 100, suptitle: str = 'Bunge Euler angle maps')[source]

Plot phi1, Phi, phi2 Euler maps side-by-side.

Parameters:
  • rdr (EBSDReader) – Loaded EBSD reader with euler_ebsd and lfi_ebsd populated.

  • show_boundaries (bool) – Overlay grain boundaries on every panel.

  • cmaps (tuple of str) – One colormap per channel (phi1, Phi, phi2).

  • figsize (tuple, int) – Figure size and resolution.

  • dpi (tuple, int) – Figure size and resolution.

Return type:

fig, axes

upxo.viz.ebsdviz.build_ipf_rgb(lfi: numpy.ndarray, quats_dict: dict, sample_direction=(0.0, 0.0, 1.0), grey: tuple = (0.75, 0.75, 0.75)) → numpy.ndarray[source]

Build a pixel-level IPF RGB image from an integer label field and a grain-quaternion mapping.

Parameters:
  • lfi (ndarray (ny, nx), int) – Pixel label field; each value is a grain ID.

  • quats_dict (dict {int gid -> ndarray (4,)}) – Quaternion (w, x, y, z) for each oriented grain.

  • sample_direction (array-like (3,)) – Sample reference direction for IPF colouring. Default [001] (ND).

  • grey (tuple of 3 floats) – RGB colour for pixels whose grain has no assigned orientation.

Returns:

rgb

Return type:

ndarray (ny, nx, 3), float32 in [0, 1]

upxo.viz.ebsdviz.plot_grain_size_histogram(rdr, figsize: tuple = (7, 4), dpi: int = 100, bins: int = 40, color: str = 'steelblue')[source]

Plot a grain area histogram in physical units (µm²).

Parameters:

rdr (EBSDReader) – Loaded EBSD reader with lfi_ebsd and step_size populated.

Return type:

fig, ax

upxo.viz.ebsdviz.plot_grain_structure_with_boundaries(rdr, show_euler_panel: bool = True, euler_channel: int = 0, boundary_color=(0, 0, 0), figsize: tuple = (16, 7), dpi: int = 100, suptitle: str = 'Grain structure with grain boundaries')[source]

Two-panel figure: grain label map + Euler background, both with boundaries.

Parameters:
  • rdr (EBSDReader) – Loaded EBSD reader.

  • show_euler_panel (bool) – If False, only the grain-label panel is shown (single axes).

  • euler_channel (int) – Which Euler channel (0=phi1, 1=Phi, 2=phi2) to use as orientation background in the right panel.

  • boundary_color (tuple) – RGB boundary overlay colour (values 0–1).

  • figsize (tuple, int) – Figure size and resolution.

  • dpi (tuple, int) – Figure size and resolution.

  • suptitle (str) – Overall figure title.

Return type:

fig, axes (axes is a single Axes if show_euler_panel=False)

upxo.viz.ebsdviz.plot_mdf(mdf: dict, peaks: dict, figsize: tuple[float, float] = (9, 4), angle_max: float = 65.0) → tuple[source]

Plot a grain-boundary MDF histogram with KDE overlay, CSL reference lines, and auto-detected peak annotations.

Parameters:
  • mdf (dict) – Output of crystal_orientation.compute_mdf_from_quats().

  • peaks (dict) – Output of crystal_orientation.detect_mdf_peaks().

  • figsize ((float, float)) – Figure size. Default (9, 4).

  • angle_max (float) – Upper x-axis limit in degrees. Default 65.

Return type:

fig, ax

upxo.viz.ebsdviz.plot_mdf_selected(mdf: dict, peaks: dict, selected_peaks: dict, figsize: tuple[float, float] = (9, 4), angle_max: float = 65.0) → tuple[source]

Replot the MDF histogram + KDE with only the user-selected peaks highlighted in colour (unselected bins are greyed out).

Parameters:
  • mdf (dict) – Output of crystal_orientation.compute_mdf_from_quats().

  • peaks (dict) – Output of crystal_orientation.detect_mdf_peaks().

  • selected_peaks (dict) – Output of mdf_peak_selector() after user confirmation. Keys: 'angles' and 'indices'.

  • figsize ((float, float))

  • angle_max (float)

Return type:

fig, ax

upxo.viz.ebsdviz.plot_csl_grain_map(lfi: numpy.ndarray, csl_grains: dict, figsize: tuple[float, float] = (10, 5), dpi: int = 130, alpha_bg: float = 0.15, suptitle: str | None = None) → tuple[source]

Colour-code the grain label field by CSL boundary participation.

Each CSL type present in csl_grains gets its own colour. Grains that touch boundaries of more than one CSL type have blended colours. Non-CSL grains are shown in semi-transparent light grey.

Parameters:
  • lfi (ndarray, shape (ny, nx)) – Integer grain label field (positive = grain ID, ≤0 = unindexed).

  • csl_grains (dict) – Output of crystal_orientation.segregate_csl_pairs(). Keys are CSL labels; each value must have 'grains_all', 'n_pairs', and 'n_grains'.

  • figsize – Standard figure parameters.

  • dpi – Standard figure parameters.

  • alpha_bg – Standard figure parameters.

  • suptitle – Standard figure parameters.

Return type:

fig, ax

upxo.viz.ebsdviz.print_parent_grain_summary(parent_info: dict, csl_grains: dict) → None[source]

Print a formatted table of parent / twin / intermediate grain counts for each CSL type.

Parameters:
  • parent_info (dict) – Output of crystal_orientation.identify_parent_grains().

  • csl_grains (dict) – Output of crystal_orientation.segregate_csl_pairs() — used to retrieve the number of pairs per CSL label.

upxo.viz.ebsdviz.plot_parent_grain_summary(parent_info: dict, figsize: tuple[float, float] = (8, 4), dpi: int = 120, title: str = 'Parent / twin / intermediate grain counts per CSL type', bar_width: float = 0.25, colors: tuple[str, str, str] = ('steelblue', 'coral', 'mediumseagreen')) → tuple[source]

Grouped bar chart of pure-parent, pure-twin, and intermediate grain counts for each CSL type.

Parameters:
  • parent_info (dict) – Output of crystal_orientation.identify_parent_grains().

  • figsize – Standard figure parameters.

  • dpi – Standard figure parameters.

  • title – Standard figure parameters.

  • bar_width (float) – Width of each bar group.

  • colors (tuple of str) – Three colours for (pure parents, pure twins, intermediates).

Return type:

fig, ax

upxo.viz.ebsdviz.plot_parent_twin_map(lfi: numpy.ndarray, parent_info: dict, figsize: tuple[float, float] = (10, 5), dpi: int = 130, alpha_bg: float = 0.12, colors: dict | None = None, suptitle: str | None = None) → tuple[source]

Colour-code the grain label field by parent / twin / intermediate role for each CSL type present in parent_info.

One subplot is produced per CSL type. Within each subplot:

  • Pure parents – solid blue (default)

  • Pure twins – solid coral / red

  • Intermediates – solid green

  • Non-role grains – semi-transparent light grey

Parameters:
  • lfi (ndarray, shape (ny, nx)) – Integer grain label field.

  • parent_info (dict) – Output of crystal_orientation.identify_parent_grains().

  • figsize – Figure dimensions per subplot (total width scaled by n_csl).

  • dpi – Figure dimensions per subplot (total width scaled by n_csl).

  • alpha_bg (float) – Opacity of non-role background grains.

  • colors (dict, optional) – Override role colours. Keys: 'pure_parent', 'pure_twin', 'intermediate'. Values: any Matplotlib colour spec.

  • suptitle (str, optional) – Figure-level title.

Return type:

fig, axes

upxo.viz.ebsdviz.plot_combined_parent_twin_map(lfi: numpy.ndarray, parent_info: dict, figsize: tuple[float, float] = (10, 5), dpi: int = 130, alpha_bg: float = 0.1, color_parent: str = '#4878CF', color_twin: str = '#D65F5F', color_intermediate: str = '#59A14F', color_non_participating: str = '#E8A33D', highlight: str = 'combined', suptitle: str | None = None) → tuple[source]

Single map showing pure-parent, pure-twin, intermediate, and/or non-participating grains aggregated across all CSL types in parent_info, depending on highlight.

A grain that is a pure-parent in any CSL type → parent role. A grain that is a pure-twin in any CSL type → twin role. A grain that is intermediate in any CSL type → intermediate role. Priority when a grain appears in multiple roles: intermediate > parent > twin. A grain that is none of the above in any CSL type → non-participating.

Parameters:
  • lfi (ndarray, shape (ny, nx)) – Integer grain label field.

  • parent_info (dict) – Output of crystal_orientation.identify_parent_grains().

  • figsize – Figure size and resolution.

  • dpi – Figure size and resolution.

  • alpha_bg (float) – Opacity of the dimmed (non-highlighted) grains.

  • color_parent (str) – Matplotlib colour specs for each role.

  • color_twin (str) – Matplotlib colour specs for each role.

  • color_intermediate (str) – Matplotlib colour specs for each role.

  • color_non_participating (str) – Matplotlib colour specs for each role.

  • highlight ({'combined', 'parent', 'non_participating'}) – 'combined' (default): parent/twin/intermediate all coloured, non-participating grains dimmed — the original behaviour. 'parent': only pure-parent grains coloured; twins, intermediates, and non-participating grains all dimmed alike. 'non_participating': only non-participating grains coloured; every role grain (parent/twin/intermediate) is dimmed instead.

  • suptitle (str or None) – Figure title. When None (default), an auto-generated title matching highlight is used.

Return type:

fig, ax

upxo.viz.ebsdviz.compute_pct_interior_grains(lfi: numpy.ndarray, grain_ids) → float | None[source]

Percentage of grain_ids that are completely interior to lfi – i.e. have no pixel on any of the map’s 4 edges (top/bottom row, left/right column). The EBSD-side equivalent of TwinnedSimple3DBase.rank_temporal_slices_by_n’s per-slice pct_ng_in_2d computation, for direct comparison against a synthetic candidate’s <%.NgIn.2D>.

Note this is inherently sensitive to the absolute size of the imaged/simulated region: a bigger map has relatively more grains that safely avoid the edge purely because there’s more area to be interior in, independent of anything about representativeness. Comparing this percentage between an EBSD map and a synthetic RVE of a different physical extent should be done cautiously.

Parameters:
  • lfi (ndarray, shape (ny, nx)) – Integer grain label field (e.g. rg.lfi_ebsd).

  • grain_ids (array-like of int) – The grain IDs to classify (e.g. parent_info['S3  (twin)']['pure_parents']).

Returns:

Percentage (0-100) of grain_ids with no pixel on any of lfi’s 4 edges. None if grain_ids is empty.

Return type:

float or None

upxo.viz.ebsdviz.compute_grain_role_property_distributions(lfi: numpy.ndarray, parent_info: dict, prop: dict, neigh_gid: dict, selected_props: list | None = None, selected_groups: list | None = None, step_size: float = 1.0) → dict[source]

Extract per-grain-role-group property value arrays – the pure data step of plot_grain_role_property_stats, with no plotting. Split out so a caller can compute once and plot (or replot) separately, e.g. a GUI’s “Compute” vs “Plot” button pair.

Parameters:
  • lfi (ndarray, shape (ny, nx)) – Integer grain label field.

  • parent_info (dict) – Output of identify_parent_grains().

  • prop (dict) – Per-grain property dict (grain_id → dict) as from prop_ebsd, i.e. upxo.interfaces.defdap.ebsd_reader._char_lfi’s output – ALREADY in physical (µm) units, since EBSDReader.characterise() calls _char_lfi with the real step_size, not px_size=1. Expected keys: 'area', 'aspect_ratio', 'perimeter', 'solidity', 'eq_diameter', 'major_axis_length', 'minor_axis_length', 'eccentricity', 'euler_number' – used as-is, with NO further step_size scaling here.

  • neigh_gid (dict) – Neighbour-grain dict (grain_id → list of neighbour IDs) as from neigh_gid_ebsd. Used to derive n_neighbours and every Tier-4 neighbour-role-composition property.

  • selected_props (list of str, optional) – Subset of GRAIN_ROLE_ALL_PROPS (see that constant’s Tier 1-4 grouping for what each property needs). Defaults to all.

  • selected_groups (list of str, optional) – Subset of GRAIN_ROLE_ALL_GROUPS. Defaults to all four.

  • step_size (float) – EBSD step size (µm). Used ONLY for Tier 2 properties (circularity/gb_segment_length/n_tjp), which are recomputed fresh from lfi via compute_slice_metrics rather than read from prop – everything else already carries physical units.

Returns:

{prop_name: {group_name: ndarray of values}} – pass directly as the data argument of plot_grouped_distributions(), or to plot_grain_role_property_stats’s companion plot call.

Return type:

dict

upxo.viz.ebsdviz.plot_grain_role_property_distributions(data: dict, selected_groups: list | None = None, bins: int = 40, bw_method: str = 'scott', peak_prominence: float = 0.01, figsize_per: tuple[float, float] = (5, 4), dpi: int = 110, suptitle: str = 'Grain morphological & topological statistics by role', ncols: int | None = None, fontsize: float = 9.0) → tuple[source]

Plot overlaid histograms + KDE + detected peaks from a pre-computed data dict (output of compute_grain_role_property_distributions).

Parameters mirror plot_grain_role_property_stats’s plotting-related arguments; see that function’s docstring for details. selected_groups must match what was passed to compute_grain_role_property_distributions (defaults to all four groups, same as there).

Return type:

fig, axes

upxo.viz.ebsdviz.plot_grain_role_property_stats(lfi: numpy.ndarray, parent_info: dict, prop: dict, neigh_gid: dict, selected_props: list | None = None, selected_groups: list | None = None, step_size: float = 1.0, bins: int = 40, bw_method: str = 'scott', peak_prominence: float = 0.01, figsize_per: tuple[float, float] = (5, 4), dpi: int = 110, suptitle: str = 'Grain morphological & topological statistics by role', ncols: int | None = None, fontsize: float = 9.0) → tuple[source]

For each selected property, plot overlaid histograms + KDE + detected peaks for each selected grain-role group (pure parents, pure twins, intermediates, non-role). Min / max / mean / std are embedded in the legend labels.

One-shot compute + plot convenience wrapper around compute_grain_role_property_distributions + plot_grain_role_property_distributions – kept for existing callers (notebooks, repgen2d.see_grain_role_property_stats) that don’t need the compute/plot split.

Parameters:
  • lfi (ndarray, shape (ny, nx)) – Integer grain label field.

  • parent_info (dict) – Output of identify_parent_grains().

  • prop (dict) – Per-grain property dict (grain_id → dict) as from prop_ebsd. Expected keys: 'area', 'aspect_ratio', 'perimeter', 'solidity'.

  • neigh_gid (dict) – Neighbour-grain dict (grain_id → list of neighbour IDs) as from neigh_gid_ebsd. Used to derive n_neighbours.

  • selected_props (list of str, optional) – Subset of ['area', 'aspect_ratio', 'perimeter', 'solidity', 'n_neighbours']. Defaults to all five.

  • selected_groups (list of str, optional) – Subset of ['pure_parents', 'pure_twins', 'intermediates', 'non_role']. Defaults to all four.

  • step_size (float) – EBSD step size (µm). Multiplied into area (px² → µm²) and perimeter (px → µm) values.

  • bins (int) – Number of histogram bins.

  • bw_method (str) – Bandwidth selector passed to scipy.stats.gaussian_kde.

  • peak_prominence (float) – Fraction of KDE max used as minimum prominence for peak annotation.

  • figsize_per (tuple) – (width, height) in inches for each subplot.

  • dpi (int) – Figure resolution.

  • suptitle (str) – Figure-level title.

  • ncols (int or None) – Number of columns in the subplot grid. None (default) places all subplots in a single row. E.g. ncols=2 gives a 2-column grid; ncols=1 gives a single column.

  • fontsize (float) – Base font size in points. Axis labels and titles use this size; tick labels use fontsize - 2; legend text uses fontsize - 2; peak annotations use fontsize - 3; the figure suptitle uses fontsize + 1. Default 9.0.

Return type:

fig, axes

upxo.viz.ebsdviz.print_grain_role_ratios(ratios: dict) → None[source]

Print a formatted table of grain-role counts and ratios produced by crystal_orientation.compute_grain_role_ratios().

Parameters:

ratios (dict) – Output of compute_grain_role_ratios().

upxo.viz.ebsdviz.plot_grain_role_map(lfi: numpy.ndarray, role_sets: dict, figsize: tuple[float, float] = (10, 5), dpi: int = 130, alpha_bg: float = 0.1, color_parent: str = '#4878CF', color_twin: str = '#D65F5F', color_intermediate: str = '#59A14F', suptitle: str | None = None) → tuple[source]

Plot a grain role map from pre-computed role sets (output of crystal_orientation.assign_grain_roles_by_ratio() or crystal_orientation.compute_grain_role_ratios()).

Parameters:
  • lfi (ndarray, shape (ny, nx)) – Integer grain label field.

  • role_sets (dict) – Dict with keys 'pure_parents', 'pure_twins', 'intermediates', 'non_role' — each value either a set of grain IDs or a sub-dict with a 'grain_ids' key (as returned by assign_grain_roles_by_ratio()).

  • figsize – Figure parameters.

  • dpi – Figure parameters.

  • alpha_bg – Figure parameters.

  • color_parent (str) – Matplotlib colour specs for each role.

  • color_twin (str) – Matplotlib colour specs for each role.

  • color_intermediate (str) – Matplotlib colour specs for each role.

  • suptitle (str or None) – Figure title.

Return type:

fig, ax

upxo.viz.ebsdviz.plot_twin_introduction_map(lfi_before: numpy.ndarray, twin_result: dict, figsize: tuple = (14, 5), dpi: int = 130, color_parent: str = '#4878CF', color_twin: str = '#D65F5F', color_other: str = '#CCCCCC', alpha_other: float = 0.3, show_cut_lines: bool = True, suptitle=None) → tuple[source]

Side-by-side comparison of the grain label field before and after twin introduction, with cut-lines overlaid on the “after” panel.

Parameters:
  • lfi_before (ndarray, shape (ny, nx)) – Grain label field before twin introduction.

  • twin_result (dict) – Output of crystal_orientation.introduce_twins_by_csl().

  • figsize (tuple) – Total figure size (width, height).

  • dpi (int) – Figure resolution.

  • color_parent (str) – Colour for parent grains.

  • color_twin (str) – Colour for newly introduced twin grains.

  • color_other (str) – Colour for all other grains.

  • alpha_other (float) – Opacity for other grains.

  • show_cut_lines (bool) – If True, overlay the Sline2d cut-lines on the “after” panel.

  • suptitle (str or None) – Figure-level title.

Return type:

fig, axes

upxo.viz.ebsdviz.default_bin_width(data) → float[source]

Freedman-Diaconis default histogram bin width for data, with the same fallback chain the EBSD Twin Thickness Statistics page’s inline plot uses: Freedman-Diaconis (2*IQR*n^-1/3) when the IQR is usable, Scott’s rule (3.49*std*n^-1/3) when the IQR is degenerate (e.g. very few points), and range/10 as a last resort when even std is 0 (constant data). Never returns 0 – callers divide by this.

upxo.viz.ebsdviz.plot_twin_thickness_stats(stats: dict, bins: int | None = None, bin_width: float | None = None, figsize: tuple = (7, 4), dpi: int = 120, color: str = 'steelblue', title: str | None = None, show_kde: bool = False, kde_width: float | None = None, show_iqr: bool = False, extra_curves: dict | None = None, ax=None) → tuple[source]

Plot a histogram of twin lamella thickness from the dict returned by compute_twin_thickness_stats (i.e. compute_mc_twin_thickness’s result).

Parameters:
  • stats (dict) – Output of compute_twin_thickness_stats.

  • bins (int or None) – Explicit histogram bin count. Mutually exclusive with bin_width – if both are None, default_bin_width picks a Freedman-Diaconis-based width automatically.

  • bin_width (float or None) – Histogram bin width in the same units as thick_um (µm). Converted to a bin count via the data’s own range.

  • figsize ((width, height))

  • dpi (int)

  • color (str) – Bar fill colour for the primary (thickness) histogram.

  • title (str or None) – Custom figure title; auto-generated when None.

  • show_kde (bool) – Overlay a Gaussian KDE curve on top of the histogram, for the primary curve and every curve in extra_curves.

  • kde_width (float or None) – KDE bandwidth in µm (shared by every plotted curve). None: each curve uses scipy’s own default (Scott’s rule) bandwidth.

  • show_iqr (bool) – Draw vertical dashed lines at the primary thickness distribution’s Q1/Q3 (stats['thick_q1']/['thick_q3']).

  • extra_curves (dict {label: ndarray} or None) – Additional distributions (already in µm) to overlay as their own histogram + optional KDE curve, alongside the primary thickness curve – e.g. per-axis linear-intercept lengths. Each gets its own colour from a fixed cycle and its own legend entry.

  • ax (matplotlib.axes.Axes or None) – Draw into this existing Axes instead of creating a new Figure/Axes pair (for embedding into a GUI’s own canvas). When given, the caller owns the Figure and must NOT call fig.tight_layout()/redraw itself – this function does not call plt.show() either way.

Return type:

fig, ax

upxo.viz.ebsdviz.plot_grain_area_comparison(scale_result: dict, bins: int = 50, pct_clip: float = 97.0, figsize: tuple = (8, 4), dpi: int = 120, color_ebsd: str = 'steelblue', color_sim: str = 'tomato') → tuple[source]

Plot overlapping grain-area histograms for the EBSD target and the scaled synthetic structure.

Parameters:
  • scale_result (dict) – Output of compute_scale_factor_grain_size. Must contain 'areas_ebsd_um2', 'areas_sim_um2', 'mean_area_ebsd_um2', 'scale_factor', and 'mean_area_sim_um2'.

  • bins (int) – Number of histogram bins.

  • pct_clip (float) – Upper percentile used to clip the x-axis range (avoids giant outlier grains dominating the axis).

  • figsize (figure size and resolution.)

  • dpi (figure size and resolution.)

  • color_ebsd (bar fill colours.)

  • color_sim (bar fill colours.)

Return type:

fig, ax