Source code for multipie.symmetry_operation.symmetry_operation_g

"""
SymmetryOperationG manages point/space-group symmetry operations.
"""
import sympy as sp
from gcoreutils.nsarray import NSArray
from multipie.tag.tag_list import TagList
from multipie.tag.tag_group import TagGroup
from multipie.tag.tag_symmetry_operation import TagSymmetryOperation
from multipie.symmetry_operation.util.symmetry_operation_util import latticeT, to_cartesian, to_primitive, to_conventional
from multipie.symmetry_operation.symmetry_operation import SymmetryOperation
from multipie.data.data_symmetry_operation_pg import _data_symmetry_operation_pg
from multipie.data.data_symmetry_operation_sg import _data_symmetry_operation_sg


# ==================================================
[docs] class SymmetryOperationG( dict ): # dict of (symmetry operation tag, symmetry operation), {TagSymmetryOperation: SymmetryOperation}. """ point/space-group symmetry operations. Attributes: tag (TagGroup): point-group tag. gen ([TagSymmetryOperation]): generators. cc ([[TagSymmetryOperation]]): symmetry operations categorized by conjugacy class. cc1 ([TagSymmetryOperation]): representative symmetry operations in conjugacy class. full ([TagSymmetryOperation]): all symmetry operations. cc_num ([int]): the number of symmetry operations in conjugacy class. crystal (str): crystal type, (triclinic/monoclinic/orthorhombic/tetragonal/trigonal/hexagonal/cubic). lattice (str): lattice type, (A/B/C/P/I/F/R/null). translation (NSArray): partial translation vectors (reduced coordinate). """ # # __idx_cc1 ([int]): index of top of conjugacy class. # __product ({(TagSymmetryOperation,TagSymmetryOperation): TagSymmetryOperation}): product table (point-group only). # __inv ({TagSymmetryOperation:TagSymmetryOperation}): inverse operation. # # ================================================== def __init__(self, g_tag): """ initialize the class. Args: g_tag (TagGroup or str): tag of point/space group. """ g_tag = TagGroup(str(g_tag)) self.tag = g_tag """point-group tag.""" if self.tag.is_point_group(): gen, cc = _data_symmetry_operation_pg[str(g_tag)] else: gen, cc = _data_symmetry_operation_sg[str(g_tag)] self.gen = TagList.from_str(TagSymmetryOperation, gen) """generators.""" self.cc = [TagList.from_str(TagSymmetryOperation, i) for i in cc] """symmetry operations categorized by conjugacy class.""" self.cc1 = TagList.from_str(TagSymmetryOperation, [i[0] for i in cc]) """representative symmetry operations in conjugacy class.""" self.cc_num = [len(i) for i in self.cc] """the number of symmetry operations in conjugacy class.""" self.full = TagList.from_str(TagSymmetryOperation, sum(cc, [])) """all symmetry operations.""" self.__idx_cc1 = [self.full.index(i) for i in self.cc1] self.crystal = self.tag.crystal """crystal type, (triclinic/monoclinic/orthorhombic/tetragonal/trigonal/hexagonal/cubic).""" self.lattice = self.tag.lattice """lattice type, (A/B/C/P/I/F/R/null).""" if self.lattice != "": self.translation = latticeT[self.lattice] """partial translation vectors (reduced coordinate).""" else: self.translation = None for s in self.full: self[s] = SymmetryOperation(str(s), self.crystal) # product table. self.__product = {} self.__inv = {} if self.tag.is_point_group(): so = self.mat() so_inv = so.inverse() t = self.key_list() for i in range(len(so)): soi = so[i] self.__inv[t[i]] = t[so.index(so_inv[i])] for j in range(len(so)): soj = so[j] self.__product[(t[i], t[j])] = t[so.index(soi @ soj)] # ================================================== def __str__(self): return str(self.tag) # ================================================== def __repr__(self): return repr(self.tag) # ================================================== def latex(self): return self.tag.latex() # ================================================== @property def plus_set(self): """ additional partial translation vectors in space group. Returns: NSArray: translation vectors. """ assert not self.tag.is_point_group(), "plus_set is only for space group." return self.translation # ==================================================
[docs] def mat(self, axial=False, cc_only=False, primitive=False, cartesian=False): """ matrix for symmetry operatoins. Args: axial (bool, optional): matrix for axial vector ? cc_only (bool, optional): conjugacy-class operations only ? primitive (bool, optional): for primitive cell ? (space group only) cartesian (bool, optional): in cartesian coordinate ? Returns: list: list of operations (3x3: point group) or (4x4: space group), [NSArray]. """ if cc_only: idx = TagList([self.full[i] for i in self.__idx_cc1]) else: idx = self.full if axial: ops = NSArray([self[i].m_axial.tolist() for i in idx], "matrix") else: ops = NSArray([self[i].m_polar.tolist() for i in idx], "matrix") if primitive: ops = to_primitive(self.lattice, ops) if cartesian: ops = to_cartesian(self.crystal, ops, self.tag.is_point_group()) return ops
# ================================================== def __getitem__(self, item): if type(item) == str: return self.get(TagSymmetryOperation(item)) else: return self.get(item) # ================================================== def key_list(self): return TagList(self.keys()) # ==================================================
[docs] def inverse(self, i): """ inverse of symmetry operation (point group only). Args: i (TagSymmetryOperation): symmetry operation. Returns: TagSymmetryOperation: inverse of symmetry operation. """ assert self.tag.is_point_group(), "product table is available only for point group." if type(i) == str: i = TagSymmetryOperation(i) return self.__inv[i]
# ==================================================
[docs] def product(self, i, j): """ product of symmetry operations (point group only). Args: i (TagSymmetryOperation): symmetry operation (left). j (TagSymmetryOperation): symmetry operation (right). Returns: TagSymmetryOperation: product of symmetry operations. """ assert self.tag.is_point_group(), "product table is available only for point group." if type(i) == str: i = TagSymmetryOperation(i) if type(j) == str: j = TagSymmetryOperation(j) return self.__product[(i, j)]
# ================================================== def _transform_matrix(self, phi_p, phi, vs): """ transform matrix for polynomial basis (phi_i' = T_ij phi_j). Args: phi_p (NSArray): transformed polynomials. phi (NSArray): polynomials to be transformed. vs (NSArray): list of variables in polynomials. Returns: NSArray: transform matrix (phi' = M.phi). """ cv = NSArray(list(sp.symbols(f"coeffvar0:{phi.size}")), "vector") eqs = [phi_p[i].tolist() - phi @ cv for i in range(phi_p.size)] eqs = [i.expand() for i in eqs] tm = sp.conjugate(sp.Matrix([list(sp.linsolve(sp.Poly(eq, vs.tolist()).coeffs(), cv.tolist()))[0] for eq in eqs])) return NSArray(tm.tolist(), "matrix") # ================================================== def _transform_variable(self, phi, v_old, v_new): """ transform polynomials by substituting transformed variables. Args: phi (NSArray): polynomials to be transformed. v_old (NSArray): list of variables in polynomials. v_new (NSArray): list of transformed variables. Returns: NSArray: transformed polynomial or list of polynomials. """ return phi.subs({v_old[i]: v_new[i] for i in range(v_old.size)}) # ================================================== def _transform_matrix_orbital_basis(self, v, vp, phi, pdet=None): """ transform matrix for polynomial basis, (phi_i' = T_ij phi_j). Args: v (NSArray): polar vector variable (cartesian coordinate). vp (NSArray): list of transformed polar vectors, r' (cartesian coordinate). phi (NSArray): list of basis (cartesian coordinate). pdet (NSArray, optional): list of determinant. Returns: NSArray: list of transform matrices. """ tm = [] if pdet is None: for i in range(len(vp)): phi_p = self._transform_variable(phi, v, vp[i]) tm.append(self._transform_matrix(phi_p, phi, v).tolist()) else: for i in range(len(vp)): phi_p = self._transform_variable(phi, v, vp[i]) phi_p = [phi_p[j] * pdet[j] for j in range(len(phi_p))] tm.append(self._transform_matrix(phi_p, phi, v).tolist()) return NSArray(tm, "matrix") # ================================================== def _equivalent_vector( self, v, cc_only=False, axial=False, remove_duplicate=False, cartesian=False, primitive=False, plus_set=False, shift=False ): """ create symmetry-operation equivalent vectors. Args: v (str or NSArray): vector/site position, "[x,y,z]". cc_only (bool, optional): conjugacy-class operations only ? axial (bool, optional): axial vector ? remove_duplicate (bool, optional): remove the same sites ? cartesian (bool, optional): cartesian coordinate ? (point group only). primitive (bool, optional): primitive cell ? (space group only). plus_set (bool, optional): add partial translations ? (space group only). shift (bool, optional): move into unit cell ? (space group only). Returns: NSArray: equivalent vectors. Notes: - if remove_duplicate is True, return values are sorted. """ if not isinstance(v, (str, NSArray)): raise KeyError(f"{type(v)} is not accepted for vector/site.") if type(v) == str: v = NSArray(v) if self.tag.is_point_group(): ops = self.mat(axial=axial, cc_only=cc_only, cartesian=cartesian) ev = ops.apply(v) if remove_duplicate: ev = ev.remove_duplicate() else: ops = self.mat(axial=axial, cc_only=cc_only) ops = to_primitive(self.lattice, ops) v = to_primitive(self.lattice, v) ev = ops.apply(v) if shift: ev = ev.shift() if remove_duplicate: ev = ev.remove_duplicate().sort() if not primitive: ev = to_conventional(self.lattice, ev, plus_set=plus_set) if type(ev) == list: ev = NSArray.concat(ev) if shift: ev = ev.shift() if remove_duplicate: ev = ev.remove_duplicate().sort() return ev # ================================================== def _equivalent_bond( self, bond, cc_only=False, remove_duplicate=False, cartesian=False, primitive=False, plus_set=False, shift=False, nondirectional=False, ): """ create SO-equivalent bonds. Args: bond (str or NSArray): bond, "vector@center", "tail;head", "start:vector". cc_only (bool, optional): conjugacy-class operations only ? remove_duplicate (bool, optional): remove the same bonds ? cartesian (bool, optional): cartesian coordinate ? (point group only). primitive (bool, optional): primitive cell ? (space group only). plus_set (bool, optional): add partial translations ? (space group only). shift (bool, optional): move into unit cell ? (space group only). nondirectional (bool, optional): ignore directional property ? Returns: NSArray: equivalent bonds. Notes: - if bond is string, vector-center "[vx,vy,vz]@[cx,cy,cz]", tail-head "[tx,ty,tz];[hx,hy,hz], or start-vector "[sx,sy,sz]:[vx,vy,vz]" style is accepted. - if remove_duplicate is True, return values are sorted. """ if not isinstance(bond, (str, NSArray)): raise KeyError(f"{type(bond)} is not accepted for bond.") if type(bond) == str: bond = NSArray(bond) t, h = bond.convert_bond("bond_th") if self.tag.is_point_group(): t = self._equivalent_vector(t, cc_only=cc_only, cartesian=cartesian) h = self._equivalent_vector(h, cc_only=cc_only, cartesian=cartesian) b = NSArray.create_bond_from_pair(t, h, "bond_th") if nondirectional: b = b.regular_direction() if remove_duplicate: b = b.remove_duplicate().sort() else: t = self._equivalent_vector(t, cc_only=cc_only) h = self._equivalent_vector(h, cc_only=cc_only) t = to_primitive(self.lattice, t) h = to_primitive(self.lattice, h) b = NSArray.create_bond_from_pair(t, h, "bond_th") if shift: b = b.shift() if nondirectional: b = b.regular_direction() if remove_duplicate: b = b.remove_duplicate().sort() if not primitive: t, h = b.convert_bond("bond_th") t = to_conventional(self.lattice, t, plus_set=plus_set) h = to_conventional(self.lattice, h, plus_set=plus_set) b = NSArray.create_bond_from_pair(t, h, "bond_th") if shift: b = b.shift() if nondirectional: b = b.regular_direction() if remove_duplicate: b = b.remove_duplicate().sort() return b