upxo.pxtal.fm_steel_3d.with_orientations_3d module
FMSteel3DWithOrientations: Final state class with full FM steel hierarchy and orientations.
This module contains the complete FM steel microstructure class with all hierarchical levels (grains → PAGs → packets → blocks) and crystal orientations assigned to blocks via Kurdjumov-Sachs relationship.
- Classes:
FMSteel3DWithOrientations: Final complete FM steel state class.
- class upxo.pxtal.fm_steel_3d.with_orientations_3d.FMSteel3DWithOrientations(parent, grain_orientations: Dict[int, Tuple[float, float, float]], block_orientations: Dict[str, Tuple[float, float, float]], pag_orientations: Dict[int, Tuple[float, float, float]] | None = None, block_to_variant_idx: Dict[str, int] | None = None, random_seed: int | None = None, verbosity: int | None = None, log_sink=None)[source]
Bases:
objectComplete 3D FM steel microstructure with full hierarchy and orientations.
This is the final state in the FM steel generation pipeline. It contains: - Base grain structure - PAG partitioning - Block hierarchy within each PAG - BCC crystal orientations assigned to blocks
This state is ready for mesh generation, visualization, or export.
- _parent
Reference to parent block structure (read-only complete structure).
- Type:
- grain_orientations
Maps grain ID → BCC Euler angles. Format: {grain_id: (phi1_deg, Phi_deg, phi2_deg)}.
- block_orientations
Maps block_id → BCC Euler angles. Format: {block_id: (phi1_deg, Phi_deg, phi2_deg)}.
- pag_orientations
Maps PAG ID → FCC (parent austenite) Euler angles. Format: {pag_id: (phi1_deg, Phi_deg, phi2_deg)}.
- grain_orientations
- block_orientations
- block_to_variant_idx
- pag_orientations
- property lgi: numpy.ndarray
Labeled grain image from base.
- property grain_locs: Dict[int, numpy.ndarray]
Grain voxel coordinates from base.
- property all_blocks: Dict[str, numpy.ndarray]
Block voxel data.
- property grain_to_blocks_map: Dict[int, List[str]]
grain_id -> [block_id, …] mapping (keys are grain_ids = packet ids).
- property grain_to_local_pkt_idx: Dict[int, int]
grain_id -> local_idx.
- Type:
1-based local packet ordinal within each PAG
- property grain_to_plane_idx: Dict[int, int]
grain_id -> {111}FCC habit-plane index (0-3) from parent block level.
- property block_slicing_normals: Dict[str, numpy.ndarray]
block_id -> unit normal of the {111}FCC habit plane used to slice it, from parent block level (see FMSteel3DWithBlocks docstring).
- property physical_dimensions
Physical domain size.
- property isolated_grains
Isolated grains from parent PAG level.
- property retained_austenite_pag_ids
Retained-austenite PAG IDs from parent PAG level (see FMSteel3DWithPAGs docstring).
- ensure_isolated_grain_orientations(random_seed: int | None = None) None[source]
See FMSteel3DWithPAGs.ensure_isolated_grain_orientations.
- get_isolated_grain_orientation(gid: int) Tuple[float, float, float] | None[source]
See FMSteel3DWithPAGs.get_isolated_grain_orientation.
- get_full_hierarchy_statistics() Dict[str, any][source]
Compute comprehensive statistics across all hierarchy levels.
- Returns:
Keys include: - n_grains, grain_voxel_stats - n_pags, pags_grains_stats - n_blocks, block_voxel_stats - n_isolated_grains - total_voxels_in_fm_structure
- Return type:
- compute_misorientation_distribution(n_sample_pairs: int = 5000, random_seed: int | None = None) Dict[str, numpy.ndarray][source]
Compute cubic-symmetry-reduced misorientation angles between block pairs.
Samples n_sample_pairs pairs in each category (within-PAG and across-PAG) and returns the disorientation angle (minimum over all 24 cubic symmetry operators).
Within-PAG distribution should show peaks at the KS-predicted angles (~10.5°, ~14.9°, ~20.6°, ~47.1°, ~60°). Across-PAG distribution should be roughly uniform (no preferred angle).
- Parameters:
- Returns:
‘within_pag’ : ndarray of disorientation angles in degrees ‘across_pag’ : ndarray of disorientation angles in degrees
- Return type:
- get_ks_variant_statistics() Dict[source]
How evenly the 6 KS variants within each packet were actually used.
For every (pag_id, plane_idx) packet with 2+ blocks, tallies how many blocks received each variant index that was actually assigned, then summarises that per-packet distribution with the same size_balance_metrics (cv, min_max_ratio, gini) used elsewhere for packet_size_balance – perfectly even usage across whichever variants were used gives cv=0, min_max_ratio=1, gini=0; a packet where every block ended up sharing one variant (the worst case the adjacency- aware graph-colouring in assign_orientations_to_all_blocks tries to avoid) sits at the opposite extreme.
Requires block_to_variant_idx, which is only populated when orientations were assigned via assign_orientations_to_all_blocks (i.e. FMSteel3DWithBlocks.assign_orientations()) – empty if custom block orientations were injected instead.
- available_phases() List[int][source]
Phase ids actually present in this structure, for populating a phase-selector dropdown (e.g. the Block-Level IPF Map panel).
PHASE_MARTENSITE is present whenever any blocks exist; PHASE_ RETAINED_AUSTENITE is present whenever isolated_grains is non-empty (the flattened view covering both PAG-covered and leftover-isolated retained-austenite grains – see FMSteel3DWithPAGs docstring).
- get_phase_voxels_and_orientations(phase_id: int) Tuple[Dict, Dict[int, Tuple[float, float, float]]][source]
Feature voxels + orientations for one phase, for phase-filtered IPF maps (see viz/orientation_viz_3d.py and gui/pages_viz.py).
PHASE_MARTENSITE -> block granularity: (all_blocks, block_orientations), exactly what the existing Block-Level IPF Map already renders (blocks only ever exist for transformed PAGs, so no filtering is needed).
PHASE_RETAINED_AUSTENITE -> grain granularity (retained austenite is never split into packets/blocks): every retained-austenite grain keyed by grain_id, orientation from either its retained PAG or its own isolated_grain_orientations entry.
- get_misorientation_statistics() Dict[str, any][source]
Compute grain-grain and block-block misorientation statistics.
Uses cubic_misorientation_old1 (from parent class) to compute misorientation angles between neighboring grains/blocks.
- Returns:
Keys: ‘grain_grain_misori’, ‘block_block_misori’, (each is dict of statistics: min, max, mean, etc.)
- Return type:
- build_euler_angle_3d_maps(level: str = 'block') Tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray][source]
Build dense 3D Euler angle maps (phi1, Phi, phi2) over the full RVE.
Each voxel receives the Bunge ZXZ Euler angles (degrees) of its parent block. Voxels not covered by any block (PAG boundaries, isolated grains) retain the zero initialisation.
- plot_gs_pvvox(alpha: float = 1.0, title: str = 'FM Steel 3D', **kwargs)[source]
Visualize grain structure (PyVista voxels).
- plot_distribution(data: numpy.ndarray, title: str, xlabel: str, **kwargs)[source]
Plot histogram.
- generate_subblocks(subblock_thickness_range_um: Tuple[float, float] = (0.5, 1.5), intrablock_ori_spread_deg: float = 2.0, thin_block_strategy: str = 'skip', random_seed: int | None = None, subblock_slab_connectivity: int = 26) FMSteel3DWithSubBlocks[source]
Subdivide every block into sub-blocks (laths) with per-sub-block orientations.
Sub-block slicing reuses each block’s inherited slicing normal, so laths are parallel to the parent block’s habit plane. Orientations are the parent block orientation plus a small random axis-angle perturbation within ±intrablock_ori_spread_deg.
- Parameters:
subblock_thickness_range_um (tuple of (float, float)) – (min, max) sub-block thickness in the same physical units as LX/LY/LZ (typically µm). Converted to voxels using the stored voxel_size.
intrablock_ori_spread_deg (float, optional) – Half-width of orientation spread around parent block (degrees). Default 2.0.
thin_block_strategy (str, optional) – Policy for blocks too thin to subdivide: ‘skip’ keeps the block whole. Default ‘skip’.
random_seed (int, optional) – Seed for reproducibility.
- Return type: