def construct_beta_quat_array( ebsd_map: ebsd.Map, alpha_phase_id: int = 0, variant_map: np.ndarray = None, ) -> np.ndarray: """Construct Parameters ---------- ebsd_map: EBSD map to assign the beta variants for. alpha_phase_id Index of the alpha phase in the EBSD map. """ if variant_map is None: variant_map = construct_variant_map(ebsd_map, alpha_phase_id) transformations = [] for sym in unq_hex_syms: transformations.append(burg_trans * sym.conjugate) trans_comps = Quat.extract_quat_comps(transformations) trans_comps = trans_comps[:, variant_map[variant_map >= 0]] quat_comps = Quat.extract_quat_comps(ebsd_map.quatArray[variant_map >= 0]) quat_comps_beta = np.empty_like(quat_comps) # transformations[variant] * quat quat_comps_beta[0, :] = (trans_comps[0, :] * quat_comps[0, :] - trans_comps[1, :] * quat_comps[1, :] - trans_comps[2, :] * quat_comps[2, :] - trans_comps[3, :] * quat_comps[3, :]) quat_comps_beta[1, :] = (trans_comps[1, :] * quat_comps[0, :] + trans_comps[0, :] * quat_comps[1, :] - trans_comps[3, :] * quat_comps[2, :] + trans_comps[2, :] * quat_comps[3, :]) quat_comps_beta[2, :] = (trans_comps[2, :] * quat_comps[0, :] + trans_comps[0, :] * quat_comps[2, :] - trans_comps[1, :] * quat_comps[3, :] + trans_comps[3, :] * quat_comps[1, :]) quat_comps_beta[3, :] = (trans_comps[3, :] * quat_comps[0, :] + trans_comps[0, :] * quat_comps[3, :] - trans_comps[2, :] * quat_comps[1, :] + trans_comps[1, :] * quat_comps[2, :]) # swap into positive hemisphere if required quat_comps_beta[:, quat_comps_beta[0, :] < 0] *= -1 beta_quat_array = np.empty_like(ebsd_map.quatArray) beta_quat_array[variant_map < 0] = Quat(1, 0, 0, 0) for i, idx in enumerate(zip(*np.where(variant_map >= 0))): beta_quat_array[idx] = Quat(quat_comps_beta[:, i]) return beta_quat_array
def calc_beta_oris_from_misori( alpha_ori: Quat, neighbour_oris: List[Quat], burg_tol: float = 5) -> Tuple[List[List[Quat]], List[float]]: """Calculate the possible beta orientations for a given alpha orientation using the misorientation relation to neighbour orientations. Parameters ---------- alpha_ori A quaternion representing the alpha orientation neighbour_oris Quaternions representing neighbour grain orientations burg_tol The threshold misorientation angle to determine neighbour relations Returns ------- list of lists of defdap.Quat.quat Possible beta orientations, grouped by each neighbour. Any neighbour with deviation greater than the tolerance is excluded. list of float Deviations from perfect Burgers transformation """ burg_tol *= np.pi / 180. # This needed to move further up calculation process unq_cub_sym_comps = Quat.extract_quat_comps(unq_cub_syms) alpha_ori_inv = alpha_ori.conjugate beta_oris = [] beta_devs = [] for neighbour_ori in neighbour_oris: min_misoris, min_cub_sym_idxs = calc_misori_of_variants( alpha_ori_inv, neighbour_ori, unq_cub_sym_comps) # find the hex symmetries (i, j) from give the minimum # deviation from the burgers relation for the minimum store: # the deviation, the hex symmetries (i, j) and the cubic # symmetry if the deviation is over a threshold then set # cubic symmetry to -1 min_misori_idx = np.unravel_index(np.argmin(min_misoris), min_misoris.shape) burg_dev = min_misoris[min_misori_idx] if burg_dev < burg_tol: beta_oris.append( beta_oris_from_cub_sym(alpha_ori, min_cub_sym_idxs[min_misori_idx], int(min_misori_idx[0]))) beta_devs.append(burg_dev) return beta_oris, beta_devs
def calc_misori_of_variants( alpha_ori_inv: Quat, neighbour_ori: Quat, unq_cub_sym_comps: np.ndarray) -> Tuple[np.ndarray, np.ndarray]: """Calculate possible symmetry variants between two orientations. Calculate all possible sym variants for misorientation between two orientations undergoing a Burgers type transformation. Then calculate the misorientation to the nearest cubic symmetry, this is the deviation to a perfect Burgers transformation. Parameters ---------- alpha_ori_inv Inverse of first orientation neighbour_ori Second orientation unq_cub_sym_comps Components of the unique cubic symmetries Returns ------- min_misoris : np.ndarray The minimum misorientation for each of the possible beta variants - shape (12, 12) min_cub_sym_idx : np.ndarray The minimum cubic symmetry index for each of the possible variants - shape (12, 12) """ # calculate all possible S^B_m (eqn 11. from [1]) from the # measured misorientation from 2 neighbour alpha grains # for each S^B_m calculate the 'closest' cubic symmetry # (from reduced subset) and the deviation from this symmetry # Vectorised calculation of: # hex_sym[j].inv * ((neighbour_ori * alpha_ori_inv) * hex_sym[i]) # labelled: d = h2.inv * (c * h1) hex_sym_comps = Quat.extract_quat_comps(hex_syms) c = (neighbour_ori * alpha_ori_inv).quatCoef h1 = np.repeat(hex_sym_comps, 12, axis=1) # outer loop h2 = np.tile(hex_sym_comps, (1, 12)) # inner loop d = np.zeros_like(h1) c_dot_h1 = c[1] * h1[1] + c[2] * h1[2] + c[3] * h1[3] c_dot_h2 = c[1] * h2[1] + c[2] * h2[2] + c[3] * h2[3] h1_dot_h2 = h1[1] * h2[1] + h1[2] * h2[2] + h1[3] * h2[3] d[0] = (c[0] * h1[0] * h2[0] - h2[0] * c_dot_h1 + c[0] * h1_dot_h2 + h1[0] * c_dot_h2 + h2[1] * (c[2] * h1[3] - c[3] * h1[2]) + h2[2] * (c[3] * h1[1] - c[1] * h1[3]) + h2[3] * (c[1] * h1[2] - c[2] * h1[1])) d[1] = (c[0] * h2[0] * h1[1] + h1[0] * h2[0] * c[1] - c[0] * h1[0] * h2[1] + c_dot_h1 * h2[1] + c_dot_h2 * h1[1] - h1_dot_h2 * c[1] + h2[0] * (c[2] * h1[3] - c[3] * h1[2]) + c[0] * (h1[2] * h2[3] - h1[3] * h2[2]) + h1[0] * (c[2] * h2[3] - c[3] * h2[2])) d[2] = (c[0] * h2[0] * h1[2] + h1[0] * h2[0] * c[2] - c[0] * h1[0] * h2[2] + c_dot_h1 * h2[2] + c_dot_h2 * h1[2] - h1_dot_h2 * c[2] + h2[0] * (c[3] * h1[1] - c[1] * h1[3]) + c[0] * (h1[3] * h2[1] - h1[1] * h2[3]) + h1[0] * (c[3] * h2[1] - c[1] * h2[3])) d[3] = (c[0] * h2[0] * h1[3] + h1[0] * h2[0] * c[3] - c[0] * h1[0] * h2[3] + c_dot_h1 * h2[3] + c_dot_h2 * h1[3] - h1_dot_h2 * c[3] + h2[0] * (c[1] * h1[2] - c[2] * h1[1]) + c[0] * (h1[1] * h2[2] - h1[2] * h2[1]) + h1[0] * (c[1] * h2[2] - c[2] * h2[1])) # Vectorised calculation of: # burg_trans * (d * burg_trans.inv) # labelled: beta_vars = b * (c * b.inv) b = burg_trans.quatCoef beta_vars = np.zeros_like(h1) b_dot_b = b[1] * b[1] + b[2] * b[2] + b[3] * b[3] b_dot_d = b[1] * d[1] + b[2] * d[2] + b[3] * d[3] beta_vars[0] = d[0] * (b[0] * b[0] + b_dot_b) beta_vars[1] = (d[1] * (b[0] * b[0] - b_dot_b) + 2 * b_dot_d * b[1] + 2 * b[0] * (b[2] * d[3] - b[3] * d[2])) beta_vars[2] = (d[2] * (b[0] * b[0] - b_dot_b) + 2 * b_dot_d * b[2] + 2 * b[0] * (b[3] * d[1] - b[1] * d[3])) beta_vars[3] = (d[3] * (b[0] * b[0] - b_dot_b) + 2 * b_dot_d * b[3] + 2 * b[0] * (b[1] * d[2] - b[2] * d[1])) # calculate misorientation to each of the cubic symmetries misoris = np.einsum("ij,ik->jk", beta_vars, unq_cub_sym_comps) misoris = np.abs(misoris) misoris[misoris > 1] = 1. misoris = 2 * np.arccos(misoris) # find the cubic symmetry with minimum misorientation for each of # the beta misorientation variants min_cub_sym_idx = np.argmin(misoris, axis=1) min_misoris = misoris[np.arange(144), min_cub_sym_idx] # reshape to 12 x 12 for each of the hex sym multiplications min_cub_sym_idx = min_cub_sym_idx.reshape((12, 12)) min_misoris = min_misoris.reshape((12, 12)) return min_misoris, min_cub_sym_idx
def calc_beta_oris_from_boundary_misori( grain: ebsd.Grain, neighbour_network: nx.Graph, quat_array: np.ndarray, alpha_phase_id: int, burg_tol: float = 5 ) -> Tuple[List[List[Quat]], List[float], List[Quat]]: """Calculate the possible beta orientations for pairs of alpha and neighbour orientations using the misorientation relation to neighbour orientations. Parameters ---------- grain The grain currently being reconstructed neighbour_network A neighbour network mapping grain boundary connectivity quat_array Array of quaternions, representing the orientations of the pixels of the EBSD map burg_tol : The threshold misorientation angle to determine neighbour relations Returns ------- list of lists of defdap.Quat.quat Possible beta orientations, grouped by each neighbour. Any neighbour with deviation greater than the tolerance is excluded. list of float Deviations from perfect Burgers transformation list of Quat Alpha orientations """ # This needed to move further up calculation process unq_cub_sym_comps = Quat.extract_quat_comps(unq_cub_syms) beta_oris = [] beta_devs = [] alpha_oris = [] neighbour_grains = neighbour_network.neighbors(grain) neighbour_grains = [ grain for grain in neighbour_grains if grain.phaseID == alpha_phase_id ] for neighbour_grain in neighbour_grains: bseg = neighbour_network[grain][neighbour_grain]['boundary'] # check sense of bseg if grain is bseg.grain1: ipoint = 0 else: ipoint = 1 for boundary_point_pair in bseg.boundaryPointPairsX: point = boundary_point_pair[ipoint] alpha_ori = quat_array[point[1], point[0]] point = boundary_point_pair[ipoint - 1] neighbour_ori = quat_array[point[1], point[0]] min_misoris, min_cub_sym_idxs = calc_misori_of_variants( alpha_ori.conjugate, neighbour_ori, unq_cub_sym_comps) # find the hex symmetries (i, j) from give the minimum # deviation from the burgers relation for the minimum store: # the deviation, the hex symmetries (i, j) and the cubic # symmetry if the deviation is over a threshold then set # cubic symmetry to -1 min_misori_idx = np.unravel_index(np.argmin(min_misoris), min_misoris.shape) burg_dev = min_misoris[min_misori_idx] if burg_dev < burg_tol / 180 * np.pi: beta_oris.append( beta_oris_from_cub_sym(alpha_ori, min_cub_sym_idxs[min_misori_idx], int(min_misori_idx[0]))) beta_devs.append(burg_dev) alpha_oris.append(alpha_ori) return beta_oris, beta_devs, alpha_oris