def csl_elem_div_thm_l2(t0, l_g2n_g2): """ The csl basis vectors are obtained from the diagonal matrix using the algorithm specified in doi:10.1107/S056773947601231X. There are two algorithms specified based on numerators or denominators of the T0 matrix. The denominators are used in this function. Parameters --------------- T0: numpy array The transformation matrix in G1n reference frame l_g2n_g2: numpy array The 'new' basis vectors of g2 lattice (g2n) in g2 reference frame Returns ------------ l_csl_g2: numpy array The CSL basis vectors in g2 reference frame """ t0 = np.array(t0) l2 = np.array(l_g2n_g2) if t0.shape[0] == 3: q1 = int_man.rat(np.array(t0[0, 0]), 1e-06)[1][0][0] q2 = int_man.rat(np.array(t0[1, 1]), 1e-06)[1][0][0] q3 = int_man.rat(np.array(t0[2, 2]), 1e-06)[1][0][0] l_csl_g2 = np.dot(l2, np.array([[q1, 0, 0], [0, q2, 0], [0, 0, q3]])) elif t0.shape[0] == 2: q1 = int_man.rat(np.array(t0[0, 0]), 1e-06)[1][0][0] q2 = int_man.rat(np.array(t0[1, 1]), 1e-06)[1][0][0] l_csl_g2 = np.dot(l2, np.array([[q1, 0, 0], [0, q2, 0]])) return l_csl_g2
def csl_elem_div_thm_l1(T0, l_g1n_g1): """ The csl basis vectors are obtained from the diagonal matrix using the algorithm specified in doi:10.1107/S056773947601231X. There are two algorithms specified based on numerators or denominators of the T0 matrix. The numerators are used in this function. Parameters --------------- T0: numpy array The transformation matrix in G1n reference frame l_g1n_g1: numpy array The 'new' basis vectors of g1 lattice (g1n) in g1 reference frame Returns ------------ l_csl_g1: numpy array The CSL basis vectors in g1 reference frame """ T0 = np.array(T0) L1 = np.array(l_g1n_g1) if T0.shape[0] == 3: p1 = int_man.rat(np.array(T0[0, 0]), 1e-06)[0][0][0] p2 = int_man.rat(np.array(T0[1, 1]), 1e-06)[0][0][0] p3 = int_man.rat(np.array(T0[2, 2]), 1e-06)[0][0][0] l_csl_g1 = np.dot(L1, np.array([[p1, 0, 0], [0, p2, 0], [0, 0, p3]])) elif T0.shape[0] == 2: p1 = int_man.rat(np.array(T0[0, 0]), 1e-06)[0][0][0] p2 = int_man.rat(np.array(T0[1, 1]), 1e-06)[0][0][0] l_csl_g1 = np.dot(L1, np.array([[p1, 0, 0], [0, p2, 0]])) return l_csl_g1
def compute_inp_params(lattice, sig_type): """ tau and kmax necessary for possible integer quadruple combinations are computed Parameters ---------------- lattice: Lattice class Attributes of the underlying lattice sig_type: {'common', 'specific'} Returns ----------- tau: float tau is a rational number :math:`= \\frac{\\nu}{\\mu}` kmax: float kmax is an integer that depends on :math:`\\mu \\ , \\nu` """ lat_params = lattice.lat_params cryst_ptgrp = proper_ptgrp(lattice.cryst_ptgrp) if cryst_ptgrp == 'D3': c_alpha = np.cos(lat_params['alpha']) tau = c_alpha / (1 + 2 * c_alpha) if sig_type == 'specific': [nu, mu] = int_man.rat(tau) rho = mu - 3 * nu kmax = 4 * mu * rho elif sig_type == 'common': kmax = [] if cryst_ptgrp == 'D4': tau = (lat_params['a'] ** 2) / (lat_params['c'] ** 2) if sig_type == 'specific': [nu, mu] = int_man.rat(tau) kmax = 4 * mu * nu if sig_type == 'common': kmax = [] if cryst_ptgrp == 'D6': tau = (lat_params['a'] ** 2) / (lat_params['c'] ** 2) if sig_type == 'specific': [nu, mu] = int_man.rat(tau) if np.remainder(nu, 2) == 0: if np.remainder(nu, 4) == 0: kmax = 3 * mu * nu else: kmax = 6 * mu * nu else: kmax = 12 * mu * nu if sig_type == 'common': kmax = [] if cryst_ptgrp == 'O': tau = 1 kmax = [] return tau, kmax
def compute_inp_params(lattice, sig_type): """ tau and kmax necessary for possible integer quadruple combinations are computed Parameters ---------------- lattice: Lattice class Attributes of the underlying lattice sig_type: {'common', 'specific'} Returns ----------- tau: float tau is a rational number :math:`= \\frac{\\nu}{\\mu}` kmax: float kmax is an integer that depends on :math:`\\mu \\ , \\nu` """ lat_params = lattice.lat_params cryst_ptgrp = proper_ptgrp(lattice.cryst_ptgrp) if cryst_ptgrp == 'D3': c_alpha = np.cos(lat_params['alpha']) tau = c_alpha / (1 + 2 * c_alpha) if sig_type == 'specific': [nu, mu] = int_man.rat(tau) rho = mu - 3 * nu kmax = 4 * mu * rho elif sig_type == 'common': kmax = [] if cryst_ptgrp == 'D4': tau = (lat_params['a']**2) / (lat_params['c']**2) if sig_type == 'specific': [nu, mu] = int_man.rat(tau) kmax = 4 * mu * nu if sig_type == 'common': kmax = [] if cryst_ptgrp == 'D6': tau = (lat_params['a']**2) / (lat_params['c']**2) if sig_type == 'specific': [nu, mu] = int_man.rat(tau) if np.remainder(nu, 2) == 0: if np.remainder(nu, 4) == 0: kmax = 3 * mu * nu else: kmax = 6 * mu * nu else: kmax = 12 * mu * nu if sig_type == 'common': kmax = [] if cryst_ptgrp == 'O': tau = 1 kmax = [] return tau, kmax
def sigma_calc(t_g1tog2_g1): """ Computes the sigma of the transformation matrix * if det(T) = det(T^{-1}) then sigma1 = sigma2 is returned * if det(T) \\neq det(T^{-1}) then max(sigma1, sigma2) is returned """ R = np.array(t_g1tog2_g1) R2 = np.linalg.det(R)*np.linalg.inv(R) n1, d1 = int_man.rat(R) n2, d2 = int_man.rat(R2) # ----------------------------- Sigma21 = int_man.lcm_array(d1[:]) # ----------------------------- Sigma22 = int_man.lcm_array(d2[:]) Sigma = np.array([Sigma21, Sigma22]).max() return Sigma
sig_rots = {} sig_rots[sig_type] = csl_rots save_csl_rots(sig_rots, sig_type, l1) elif test_case == 5: lat_tau = [] lat_tau.append(1.0/9.0) ca_rat = [] sig_type = 'specific' sig_rots = {} for tau_vals in lat_tau: ca_rat = np.sqrt(1/tau_vals) lat_type = 'tP_ca' l1 = lat.Lattice(lat_type, ca_rat) csl_rots = lit_csl_rots(l1, sig_type) [n1, d1] = int_man.rat(tau_vals) nu = n1[0][0] mu = d1[0][0] tau_str = str(nu) + '_' + str(mu) sig_rots[tau_str] = csl_rots save_csl_rots(sig_rots, sig_type, l1) elif test_case == 6: lat_tau = [] lat_tau.append(3.0/7.0) lat_tau.append(8.0/19.0) lat_tau.append(5.0/12.0) lat_tau.append(16.0/39.0) lat_tau.append(11.0/27.0) lat_tau.append(2.0/5.0) lat_tau.append(7.0/18.0)
def csl_rotations(sigma, sig_type, lat_type): """ The function computes the CSL rotation matrices r_g1tog2_g1 corresponding to a give sigma and lattice Parameters ---------- sigma : int Sigma corresponding to the transformation matrix sig_type: {'common', 'specific'} If the sigma generating function depends on the lattice type, then sig_type is 'specific', otherwise it is 'common' lat_type: Lattice class Attributes of the underlying lattice Returns ------- sig_rots: dictionary keys: 'N', 'D' sig_rots['N'], sig_rots['D']: Numerator and Integer matrices The transformation matrix is N/D in the g1 reference frame (i.e. r_g1tog2_g1) Notes ------- The following steps are considered to obtain the sigma rotation: * compute_inp_params: computes tau and kmax that fixes the range of integer qudruples sampled * mesh_muvw: Creates the integer quadruples that depend on sigma, tau, kmax, crystallographic point group * eliminate_idrots: Eliminates Identity rotations * If specific rotations are desired: - mesh_muvw_fz: Restricts quadruples to fundamental zone - check_fsig_int: Filters out quadruples that do not meet the condition specified in this function * sigtype_muvw: Filters out quadruple combinations depending on the type of sigma rotation * eliminate_mults: Eliminates integer quadruples that are same except for a scaling factor * check_sigma: Returns integer quadruples that result in the sigma rotation * compute_tmat: Computes the transformation matrix from the integer quadruple * disorient_sigmarots: Converts all the transformations to the fundamental zone of the corresponding crystallogrphic point group * check_sigma_rots: Checks that the transformation matrix is a sigma rotation and returns them as numerator and denominator matrices """ cryst_ptgrp = proper_ptgrp(lat_type.cryst_ptgrp) lat_type.cryst_ptgrp = cryst_ptgrp if cryst_ptgrp in ['T', 'Td', 'Th', 'O', 'Oh']: if np.remainder(sigma, 2) == 0: return {'N': np.empty(0), 'D': np.empty(0)} # Define Parameters [tau, kmax] = compute_inp_params(lat_type, sig_type) [nu, mu] = int_man.rat(tau) if sig_type == 'specific': lat_args = {} lat_args['mu'] = mu[0][0] lat_args['nu'] = nu[0][0] lat_args['kmax'] = kmax[0][0] # Create Integer Quadruples quad_int = mesh_muvw(cryst_ptgrp, sigma, sig_type, lat_args) # Restrict to Fundamental Zone quad_int = mesh_muvw_fz(quad_int, cryst_ptgrp, sig_type, lat_args) # Eliminate Identity Rotations quad_int = eliminate_idrots(quad_int) # Check $\frac{F}{\Sigma}$ is an iteger and a divisor of kmax quad_int = check_fsig_int(quad_int, cryst_ptgrp, sigma, lat_args) else: quad_int = mesh_muvw(cryst_ptgrp, sigma, sig_type) # Eliminate Identity Rotations quad_int = eliminate_idrots(quad_int) # Keep only 'specific' or 'common' rotations quad_int = sigtype_muvw(quad_int, cryst_ptgrp, sig_type) # Keep only those quadruples such that gcd(m, U, V, W) = 1 quad_int = eliminate_mults(quad_int) # Compute $\Sigma$ and check with input $\Sigma$ if sig_type == 'common': quad_int = check_sigma(quad_int, sigma, cryst_ptgrp, sig_type) if sig_type == 'specific': quad_int = check_sigma(quad_int, sigma, cryst_ptgrp, sig_type, lat_args) if np.size(quad_int) == 0: return {'N': np.empty(0), 'D': np.empty(0)} # Compute rotation matrices in G1 lattice r_g1tog2_g1 = compute_tmat(quad_int, tau, lat_type) l_p_po = lat_type.l_p_po # Convert to disorientations to keep the unique rotations r_g1tog2_g1 = disorient_sigmarots(r_g1tog2_g1, l_p_po, cryst_ptgrp) if np.size(r_g1tog2_g1) == 0: return {'N': np.empty(0), 'D': np.empty(0)} else: # Check that r_g1tog2_g1 are rational with lcm of denominator matrices # equal to $\Sigman$ sig_rots = check_sigma_rots(r_g1tog2_g1, sigma) return sig_rots