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 beta_oris() -> List[Quat]: """The 6 possible beta orientations for the `ori_single_valid` fixture.""" return [ Quat(0.11460994, 0.97057328, 0.10987647, -0.18105038), Quat(0.71894262, 0.52597293, -0.12611599, 0.43654181), Quat(0.4812518, 0.86403135, -0.00937589, 0.14750805), Quat(0.23608204, -0.12857975, 0.96217918, 0.04408791), Quat(0.39243229, -0.10727847, -0.59866151, -0.68999466), Quat(0.62851433, -0.23585823, 0.36351768, -0.64590675) ]
def test_calc(self): mock_map = Mock(spec=ebsd.Map) mock_map.quatArray = np.array([ Quat([0.31666724, -0.91588522, 0.13405465, -0.20713635]), Quat([0.17612928, -0.20214505, -0.21599713, -0.93886159]), Quat([0.24967011, -0.224283, -0.26045348, -0.90527673]), Quat([0.68070418, -0.53435491, 0.23619103, -0.44195072]), Quat([0.7293098, -0.34952765, -0.07857021, -0.58289309]), Quat([0.00169551, 0.14777021, 0.12010977, 0.98169992]), Quat([0.47365417, -0.20536756, -0.20468846, -0.83161201]), Quat([0.78776179, -0.27972705, 0.09209514, -0.54101999]), Quat([0.33727506, -0.13311405, -0.13924545, -0.92148624]), Quat([0.41137644, -0.85164404, 0.21420115, -0.24411007]), Quat([0.26092708, -0.27640969, -0.29049792, -0.87813763]), Quat([0.51237664, -0.62480323, -0.16679851, -0.56503925]) ]).reshape((3, 4)) mock_map.shape = mock_map.quatArray.shape variant_map = np.array([[0, 3, -1, 1], [2, 2, 4, 4], [5, -1, 1, 2]]) beta_quat_array = recon.construct_beta_quat_array( mock_map, variant_map=variant_map ) expected_comps = [ np.array([0.3586687, -0.42249045, -0.28570384, 0.78181321]), np.array([0.04396812, -0.20834367, -0.45309011, 0.86566105]), np.array([1., 0., 0., 0.]), np.array([0.13489744, -0.93982694, 0.30093181, -0.08926393]), np.array([0.66997125, -0.66745906, 0.24828395, 0.2097427]), np.array([0.46111146, -0.72030688, -0.25269131, -0.4524172]), np.array([0.31807292, 0.2294237, -0.791435, 0.46885503]), np.array([0.1535714, -0.26761921, -0.83850223, 0.44912114]), np.array([0.51219503, 0.6609771, -0.31826009, -0.44662741]), np.array([1., 0., 0., 0.]), np.array([0.26763438, -0.77098237, -0.48040495, -0.32119948]), np.array([0.56407948, -0.75208451, -0.06296879, 0.33498979]) ] assert all([np.allclose(quat.quatCoef, row) for quat, row in zip(beta_quat_array.flat, expected_comps)])
def ori_quat_list_valid() -> List[Quat]: """A list of sample quaternion representing the orientations of grains.""" return [ Quat(0.22484510, 0.45464871, -0.70807342, 0.49129550), Quat(0.36520321, 0.25903472, -0.40342268, 0.79798357) ]
def ori_single_valid_3() -> Quat: """A single sample quaternion representing the orientation of a grain.""" return Quat(0.8730071, -0.41360125, -0.02295757, -0.25742097)
def ori_single_valid_2() -> Quat: """A single sample quaternion representing the orientation of a grain.""" return Quat(0.11939881, -0.36445855, -0.67237386, -0.63310922)
def ori_single_valid() -> Quat: """A single sample quaternion representing the orientation of a grain.""" return Quat(0.22484510, 0.45464871, -0.70807342, 0.49129550)
def calcNye(self): """ Calculates Nye tensor and related GND density for the EBSD map. Stores result in self.Nye and self.GND. """ self.buildQuatArray() print("\rFinding boundaries...", end="") syms = Quat.symEqv(self.crystalSym) numSyms = len(syms) # array to store quat components of initial and symmetric equivalents quatComps = np.empty((numSyms, 4, self.yDim, self.xDim)) # populate with initial quat components for i, row in enumerate(self.quatArray): for j, quat in enumerate(row): quatComps[0, :, i, j] = quat.quatCoef # loop of over symmetries and apply to initial quat components # (excluding first symmetry as this is the identity transformation) for i, sym in enumerate(syms[1:], start=1): # sym[i] * quat for all points (* is quaternion product) quatComps[i, 0] = (quatComps[0, 0] * sym[0] - quatComps[0, 1] * sym[1] - quatComps[0, 2] * sym[2] - quatComps[0, 3] * sym[3]) quatComps[i, 1] = (quatComps[0, 0] * sym[1] + quatComps[0, 1] * sym[0] - quatComps[0, 2] * sym[3] + quatComps[0, 3] * sym[2]) quatComps[i, 2] = (quatComps[0, 0] * sym[2] + quatComps[0, 2] * sym[0] - quatComps[0, 3] * sym[1] + quatComps[0, 1] * sym[3]) quatComps[i, 3] = (quatComps[0, 0] * sym[3] + quatComps[0, 3] * sym[0] - quatComps[0, 1] * sym[2] + quatComps[0, 2] * sym[1]) # swap into positve hemisphere if required quatComps[i, :, quatComps[i, 0] < 0] *= -1 # Arrays to store neigbour misorientation in positive x and y direction misOrix = np.zeros((numSyms, self.yDim, self.xDim)) misOriy = np.zeros((numSyms, self.yDim, self.xDim)) # loop over symmetries calculating misorientation to initial for i in range(numSyms): for j in range(self.xDim - 1): misOrix[i, :, j] = abs(np.einsum("ij,ij->j", quatComps[0, :, :, j], quatComps[i, :, :, j + 1])) for j in range(self.yDim - 1): misOriy[i, j, :] = abs(np.einsum("ij,ij->j", quatComps[0, :, j, :], quatComps[i, :, j + 1, :])) misOrix[misOrix > 1] = 1 misOriy[misOriy > 1] = 1 # find min misorientation (max here as misorientaion is cos of this) argmisOrix = np.argmax(misOrix, axis=0) argmisOriy = np.argmax(misOriy, axis=0) misOrix = np.max(misOrix, axis=0) misOriy = np.max(misOriy, axis=0) # convert to misorientation in degrees misOrix = 360 * np.arccos(misOrix) / np.pi misOriy = 360 * np.arccos(misOriy) / np.pi # calculate relative elastic distortion tensors at each point in the two directions betaderx = np.zeros((3, 3, self.yDim, self.xDim)) betadery = betaderx for i in range(self.xDim - 1): for j in range(self.yDim - 1): q0x = Quat(quatComps[0, 0, j, i], quatComps[0, 1, j, i], quatComps[0, 2, j, i], quatComps[0, 3, j, i]) qix = Quat(quatComps[argmisOrix[j, i], 0, j, i + 1], quatComps[argmisOrix[j, i], 1, j, i + 1], quatComps[argmisOrix[j, i], 2, j, i + 1], quatComps[argmisOrix[j, i], 3, j, i + 1]) misoquatx = qix.conjugate * q0x # change stepsize to meters betaderx[:, :, j, i] = (Quat.rotMatrix(misoquatx) - np.eye(3)) / self.stepSize / 1e-6 q0y = Quat(quatComps[0, 0, j, i], quatComps[0, 1, j, i], quatComps[0, 2, j, i], quatComps[0, 3, j, i]) qiy = Quat(quatComps[argmisOriy[j, i], 0, j + 1, i], quatComps[argmisOriy[j, i], 1, j + 1, i], quatComps[argmisOriy[j, i], 2, j + 1, i], quatComps[argmisOriy[j, i], 3, j + 1, i]) misoquaty = qiy.conjugate * q0y # change stepsize to meters betadery[:, :, j, i] = (Quat.rotMatrix(misoquaty) - np.eye(3)) / self.stepSize / 1e-6 # Calculate the Nye Tensor alpha = np.empty((3, 3, self.yDim, self.xDim)) bavg = 1.4e-10 # Burgers vector alpha[0, 2] = (betadery[0, 0] - betaderx[0, 1]) / bavg alpha[1, 2] = (betadery[1, 0] - betaderx[1, 1]) / bavg alpha[2, 2] = (betadery[2, 0] - betaderx[2, 1]) / bavg alpha[:, 1] = betaderx[:, 2] / bavg alpha[:, 0] = -1 * betadery[:, 2] / bavg # Calculate 3 possible L1 norms of Nye tensor for total # disloction density alpha_total3 = np.empty((self.yDim, self.xDim)) alpha_total5 = np.empty((self.yDim, self.xDim)) alpha_total9 = np.empty((self.yDim, self.xDim)) alpha_total3 = 30 / 10. *( abs(alpha[0, 2]) + abs(alpha[1, 2]) + abs(alpha[2, 2]) ) alpha_total5 = 30 / 14. * ( abs(alpha[0, 2]) + abs(alpha[1, 2]) + abs(alpha[2, 2]) + abs(alpha[1, 0]) + abs(alpha[0, 1]) ) alpha_total9 = 30 / 20. * ( abs(alpha[0, 2]) + abs(alpha[1, 2]) + abs(alpha[2, 2]) + abs(alpha[0, 0]) + abs(alpha[1, 0]) + abs(alpha[2, 0]) + abs(alpha[0, 1]) + abs(alpha[1, 1]) + abs(alpha[2, 1]) ) alpha_total3[abs(alpha_total3) < 1] = 1e12 alpha_total5[abs(alpha_total3) < 1] = 1e12 alpha_total9[abs(alpha_total3) < 1] = 1e12 # choose from the different alpha_totals according to preference; # see Ruggles GND density paper self.GND = alpha_total9 self.Nye = alpha