Source code for symmetr.symmetry

from __future__ import print_function
from __future__ import division
from builtins import str
from builtins import range
from past.utils import old_div
from builtins import object
import sympy
import re

__all__ = ['Symmetry', 'findsym2sym', 'matsym2sym', 'sym2R', 'sym2Rs', 'sym2trans', 'create_T', 'create_I', 'create_P',
           'convert_op', 'optype_to_basic']

[docs] class Symmetry(object):
[docs] def __init__(self,R,has_T,Rs=None,permutations=None): self.R = R self.has_T = has_T if Rs is None: factor = 1 if has_T: factor *= -1 if self.R.det() == -1: factor *= -1 self.Rs = factor * self.R else: if abs(abs(Rs.det())-1) > 1e-10: raise Warning('Rs must have determinant +1 or -1') self.Rs = Rs self.permutations = permutations self.custom_Rs = {}
[docs] def get_R(self,op=None): R_op = None op = optype_to_basic(op) if op is None or op == 'x': R_op = self.R elif op == 's': R_op = self.Rs elif op == 'v': if not self.has_T: R_op = self.R else: R_op = -self.R elif op == 't': R_op = self.R.det() * self.R elif op == 'L': if not self.has_T: R_op = self.R.det() * self.R else: R_op = -self.R.det() * self.R elif op in self.custom_Rs: R_op = self.custom_Rs[op] if R_op is None: print(self.custom_Rs) print(op) raise Exception('wrong op') else: return R_op
[docs] def convert(self,T,in_place=False): """ Converts the symmetry operation to a different coordinate system. Args: T (matrix): Coordinate transformation matrix. If it is set, the symmetry operation will be transformed by this matrix. Symmetry operations are given in basis A. T transforms from A to B, ie Tx_A = x_B. Doesn't return anything, the symmetry itself is modified. """ R_T = T * self.R * T.inv() Rs_T = T * self.Rs * T.inv() custom_Rs_T = {} for name in self.custom_Rs: custom_Rs_T[name] = T * self.custom_Rs[name] * T.inv() if in_place: self.R = R_T self.Rs = Rs_T for name in self.custom_Rs: self.custom_Rs[name] = self.custom_Rs_T[name] else: out = Symmetry(R=R_T,has_T=self.has_T,Rs=Rs_T,permutations=self.permutations) out.custom_Rs = custom_Rs_T return out
[docs] def def_custom_R(self,name,R_c): self.custom_Rs[name] = R_c
[docs] def copy(self): return Symmetry(self.R,self.has_T,self.Rs,self.permutations)
[docs] def __str__(self): out = 'R: ' out += self.R.__str__() + '\n' out += 'Rs: ' out += self.Rs.__str__() + '\n' out += 'has_T: ' + str(self.has_T) + '\n' out += 'permutations: ' + str(self.permutations) return out
[docs] def __eq__(self,other): if self.R != other.R: return False if self.has_T != other.has_T: return False if self.Rs != other.Rs: return False if self.permutations is None or other.permutations is None: if not (self.permutations is None and other.permutations is None): return False if self.permutations != other.permutations: return False if self.custom_Rs != other.custom_Rs: return False return True
[docs] def eq_numeric(self,other,prec=1e-4): if sympy.N(self.R - other.R).norm() > prec: return False if self.has_T != other.has_T: return False if sympy.N(self.Rs - other.Rs).norm() > prec: return False if self.permutations is None or other.permutations is None: if not (self.permutations is None and other.permutations is None): return False if self.permutations != other.permutations: return False for op in self.custom_Rs: if op not in other.custom_Rs: return False else: if sympy.N(self.custom_Rs[op] - other.custom_Rs[op]).norm() > prec: return False return True
[docs] def __mul__(self,other): R = self.R * other.R Rs = self.Rs * other.Rs if not self.has_T and not other.has_T: has_T = False elif self.has_T and other.has_T: has_T = False else: has_T = True if self.permutations is None and other.permutation is None: permutations = None else: if self.permutations is None or other.permutations is None: raise Exception('Cannot multiply symmetry operations when only one permutation is defined') permutations = {} for i in other.permutations: permutations[i] = self.permutations[other.permutations[i]] if self.custom_Rs.keys() != other.custom_Rs.keys(): raise Exception('For multiplication the custom_Rs must be the same') custom_Rs = {} for name in custom_Rs: custom_Rs[name] = self.custom_Rs[name]*other.custom_Rs[name] out = Symmetry(R,has_T,Rs,permutations) out.custom_Rs = custom_Rs return out
[docs] def pprint(self): sympy.pprint(self.R) sympy.pprint(self.Rs) for custom_R in self.custom_Rs: print(custom_R) sympy.pprint(self.custom_Rs[custom_R]) print('has T:', self.has_T) if self.permutations is not None: print('permutations: ', self.permutations)
[docs] def inv(self): Ri = self.R.inv() Rsi = self.Rs.inv() has_Ti = self.has_T if self.permutations is not None: permuationsi = {} for p in self.permutations: permuationsi[self.permutations[p]] = p else: permutationsi = None out = Symmetry(Ri,has_Ti,Rsi,permuationsi) for Rc in self.custom_Rs: out.custom_Rs[Rc] = self.custom_Rs[Rc].inv() return out
[docs] def findsym2sym(sym_findsym): R = sym2R(sym_findsym) Rs = sym2Rs(sym_findsym) if sym_findsym[3] == '-1': has_T = True elif sym_findsym[3] == '+1': has_T = False else: print(sym_findsym[3]) raise Exception('Wrong findsym format') if len(sym_findsym) > 4: permutations = {} for (i,j) in sym_findsym[4]: permutations[i] = j else: permutations = None return Symmetry(R=R,Rs=Rs,has_T=has_T,permutations=permutations)
[docs] def matsym2sym(sym): R = sym[0] Rs = sym[2] if sym[3] == '-1': has_T = True elif sym[3] == '+1': has_T = False else: print(sym[3]) raise Exception('Wrong findsym format') permutations = {} for (i,j) in sym[4]: permutations[i] = j return Symmetry(R=R,Rs=Rs,has_T=has_T,permutations=permutations)
[docs] def sym2R(sym): R = sympy.zeros(3) for i in range(3): trans = convert_op(sym,['x',i]) for t in trans: for l in range(3): if t[0] == l: R[i,l] = t[1] return R
[docs] def sym2Rs(sym): sym_s = sympy.zeros(3) for i in range(3): trans = convert_op(sym,['s',i]) for t in trans: for l in range(3): if t[0] == l: sym_s[i,l] = t[1] return sym_s
[docs] def sym2trans(sym): out = [] for i in range(3): out.append(sym[1][i]) for s in ['x','y','z']: out[i] = out[i].replace(s,'0') out[i] = sympy.sympify(out[i]) return sympy.Matrix(out)
[docs] def create_T(): R = sympy.diag(1,1,1) return Symmetry(R=R,has_T=True)
[docs] def create_I(): R = sympy.diag(1,1,1) return Symmetry(R=R,has_T=False)
[docs] def create_P(): """ Note this doesn't contain any permutations, so it's not always correct! """ R = -sympy.diag(1,1,1) return Symmetry(R=R,has_T=False)
[docs] def convert_op(sym,op_type): """ Transforms operator component by a symmetry operation. Args: sym: The symmetry operation. op_type: Determines the operator type and operator component to be transformed. [operator type,component index(0,1 or 2)] Returns: out: A list of tuples. For example [(0,1),(1,-1)]. First component means operator index. Second component means sign. The tuples are to be summed up. The example means: op_x-op_y """ #velocity operator if op_type[0] == 'v': s = sym[1][op_type[1]] op = re.sub('-','+-',s) op = re.split('\+',op) op = [_f for _f in op if _f] #remove empty strings from the list out = [] for j in range(len(op)): match = False if re.match('^x',op[j]): t = (0,1) match = True if re.match('^-x',op[j]): t = (0,-1) match = True if re.match('^y',op[j]): t = (1,1) match = True if re.match('^-y',op[j]): t = (1,-1) match = True if re.match('^z',op[j]): t = (2,1) match = True if re.match('^-z',op[j]): t = (2,-1) match = True #if there is a time-reversal, v has a minus compared to space transformation if match: if sym[3] == '-1': t = (t[0],-1*t[1]) out.append(t) return out #spin operator if op_type[0] == 's': s = sym[2][op_type[1]] op = re.sub('-','+-',s) op = re.split('\+',op) op = [_f for _f in op if _f] #remove empty strings from the list out = [] for j in range(len(op)): if re.match('^mx',op[j]): t = (0,1) out.append(t) if re.match('^-mx',op[j]): t = (0,-1) out.append(t) if re.match('^my',op[j]): t = (1,1) out.append(t) if re.match('^-my',op[j]): t = (1,-1) out.append(t) if re.match('^mz',op[j]): t = (2,1) out.append(t) if re.match('^-mz',op[j]): t = (2,-1) out.append(t) return out #torque operator if op_type[0] == 't': s = sym[2][op_type[1]] op = re.sub('-','+-',s) op = re.split('\+',op) op = [_f for _f in op if _f] #remove empty strings from the list out = [] for j in range(len(op)): match = False if re.match('^mx',op[j]): t = (0,1) out.append(t) match = True if re.match('^-mx',op[j]): t = (0,-1) out.append(t) match = True if re.match('^my',op[j]): t = (1,1) out.append(t) match = True if re.match('^-my',op[j]): t = (1,-1) out.append(t) match = True if re.match('^mz',op[j]): t = (2,1) out.append(t) match = True if re.match('^-mz',op[j]): t = (2,-1) out.append(t) match = True if match: if sym[3] == '-1': t = (t[0],-1*t[1]) out.append(t) return out #transformation of a position operator if op_type[0] == 'x': s = sym[1][op_type[1]] op = re.sub('-','+-',s) op = re.split('\+',op) op = [_f for _f in op if _f] #remove empty strings from the list out = [] for j in range(len(op)): match = False if re.match('^x',op[j]): t = (0,1) match = True if re.match('^-x',op[j]): t = (0,-1) match = True if re.match('^y',op[j]): t = (1,1) match = True if re.match('^-y',op[j]): t = (1,-1) match = True if re.match('^z',op[j]): t = (2,1) match = True if re.match('^-z',op[j]): t = (2,-1) match = True if match: out.append(t) return out #finds a translational component of a transformation if op_type[0] == 'translation': s = sym[1][op_type[1]] op = re.sub('-','+-',s) op = re.split('\+',op) op = [_f for _f in op if _f] #remove empty strings from the list out = [] for j in range(len(op)): match = False if re.match('-?[0-9]*[./]?[0-9]+',op[j]): if re.match('-?[0-9.]+/[0-9.]+',op[j]): op_s = op[j].split('/') op[j] = old_div(float(op_s[0]), float(op_s[1])) match = True if match: out.append(op[j]) return out
[docs] def optype_to_basic(op): if op in ['t','T']: return 't' elif op in ['B','L','l']: return 'L' elif op in ['v','j','jq']: return 'v' elif op in ['x','E','gT','r','V']: return 'x' elif op in ['s']: return 's' else: return op