import numpy as np
import re
import logging
import matplotlib.pyplot as plt
import sys
[docs]class ProcarSymmetry:
def __init__(
self,
kpoints,
bands,
character=None,
sx=None,
sy=None,
sz=None,
loglevel=logging.WARNING,
):
self.log = logging.getLogger("ProcarSymmetry")
self.log.setLevel(loglevel)
self.ch = logging.StreamHandler()
self.ch.setFormatter(
logging.Formatter("%(name)s::%(levelname)s: " "%(message)s")
)
self.ch.setLevel(logging.DEBUG)
self.log.addHandler(self.ch)
self.log.debug("ProcarSymmetry.__init__: ...")
self.kpoints = kpoints
self.bands = bands
# optional arguments when not given will False, but they can still
# be treated like arrays
self.character = np.array([])
if character is not None:
self.character = character
self.sx = np.array([])
if sx is not None:
self.sx = sx
self.sy = np.array([])
if sy is not None:
self.sy = sy
self.sz = np.array([])
if sz is not None:
self.sz = sz
self.log.info("Kpoints : " + str(self.kpoints.shape))
self.log.info("bands : " + str(self.bands.shape))
self.log.info("character : " + str(self.character.shape))
self.log.info("sx : " + str(self.sx.shape))
self.log.info("sy : " + str(self.sy.shape))
self.log.info("sz : " + str(self.sz.shape))
self.log.debug("ProcarSymmetry.__init__: ...Done")
return
def _q_mult(self, q1, q2):
"""
Multiplication of quaternions, it doesn't fit in any other place
"""
w1, x1, y1, z1 = q1
w2, x2, y2, z2 = q2
w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2
x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2
y = w1 * y2 + y1 * w2 + z1 * x2 - x1 * z2
z = w1 * z2 + z1 * w2 + x1 * y2 - y1 * x2
return np.array((w, x, y, z))
[docs] def GeneralRotation(self, angle, rotAxis=[0, 0, 1], store=True):
"""Apply a rotation defined by an angle and an axis.
Returning value: (Kpoints, sx,sy,sz), the rotated Kpoints and spin
vectors (if not the case, they will be empty
arrays).
Arguments
angle: the rotation angle, must be in degrees!
rotAxis : a fixed Axis when applying the symmetry, usually it is
from Gamma to another point). It doesn't need to be normalized.
The RotAxis can be:
[x,y,z] : a cartesian vector in k-space.
'x': [1,0,0], a rotation in the yz plane.
'y': [0,1,0], a rotation in the zx plane.
'z': [0,0,1], a rotation in the xy plane
"""
if rotAxis == "x" or rotAxis == "X":
rotAxis = [1, 0, 0]
if rotAxis == "y" or rotAxis == "Y":
rotAxis = [0, 1, 0]
if rotAxis == "z" or rotAxis == "Z":
rotAxis = [0, 0, 1]
rotAxis = np.array(rotAxis, dtype=float)
self.log.debug("rotAxis : " + str(rotAxis))
rotAxis = rotAxis / np.linalg.norm(rotAxis)
self.log.debug("rotAxis Normalized : " + str(rotAxis))
self.log.debug("Angle : " + str(angle))
angle = angle * np.pi / 180
# defining a quaternion for rotatoin
angle = angle / 2
rotAxis = rotAxis * np.sin(angle)
qRot = np.array((np.cos(angle), rotAxis[0], rotAxis[1], rotAxis[2]))
qRotI = np.array((np.cos(angle), -rotAxis[0], -rotAxis[1], -rotAxis[2]))
self.log.debug("Rot. quaternion : " + str(qRot))
self.log.debug("Rot. quaternion conjugate : " + str(qRotI))
# converting self.kpoints into quaternions
w = np.zeros((len(self.kpoints), 1))
qvectors = np.column_stack((w, self.kpoints)).transpose()
self.log.debug(
"Kpoints-> quaternions (transposed):\n" + str(qvectors.transpose())
)
qvectors = self._q_mult(qRot, qvectors)
qvectors = self._q_mult(qvectors, qRotI).transpose()
kpoints = qvectors[:, 1:]
self.log.debug("Rotated kpoints :\n" + str(qvectors))
# rotating the spin vector (if exist)
sxShape, syShape, szShape = self.sx.shape, self.sy.shape, self.sz.shape
self.log.debug("Spin vector Shapes : " + str((sxShape, syShape, szShape)))
# The first entry has to be an array of 0s, w could do the work,
# but if len(self.sx)==0 qvectors will have a non-defined length
qvectors = (
0 * self.sx.flatten(),
self.sx.flatten(),
self.sy.flatten(),
self.sz.flatten(),
)
self.log.debug("Spin vector quaternions: \n" + str(qvectors))
qvectors = self._q_mult(qRot, qvectors)
qvectors = self._q_mult(qvectors, qRotI)
self.log.debug("Spin quaternions after rotation:\n" + str(qvectors))
sx, sy, sz = qvectors[1], qvectors[2], qvectors[3]
sx.shape, sy.shape, sz.shape = sxShape, syShape, szShape
if store is True:
self.kpoints, self.sx, self.sy, self.sz = kpoints, sx, sy, sz
self.log.debug("GeneralRotation: ...Done")
return (kpoints, sx, sy, sz)
[docs] def RotSymmetryZ(self, order):
"""Applies the given rotational crystal symmetry to the current
system. ie: to unfold the irreductible BZ to the full BZ.
Only rotations along z-axis are performed, you can use
self.GeneralRotation first.
The user is responsible of provide a useful input. The method
doesn't check the physics.
"""
self.log.debug("RotSymmetryZ:...")
rotations = [
self.GeneralRotation(360 * i / order, store=False) for i in range(order)
]
rotations = list(zip(*rotations))
self.log.debug(
"self.kpoints.shape (before concat.): " + str(self.kpoints.shape)
)
self.kpoints = np.concatenate(rotations[0], axis=0)
self.log.debug("self.kpoints.shape (after concat.): " + str(self.kpoints.shape))
self.sx = np.concatenate(rotations[1], axis=0)
self.sy = np.concatenate(rotations[2], axis=0)
self.sz = np.concatenate(rotations[3], axis=0)
# the bands and proj. character also need to be enlarged
bandsChar = [(self.bands, self.character) for i in range(order)]
bandsChar = list(zip(*bandsChar))
self.bands = np.concatenate(bandsChar[0], axis=0)
self.character = np.concatenate(bandsChar[1], axis=0)
self.log.debug("RotSymmZ:...Done")
return
[docs] def MirrorX(self):
"""Applies the given rotational crystal symmetry to the current
system. ie: to unfold the irreductible BZ to the full BZ.
"""
self.log.debug("Mirror:...")
newK = self.kpoints * np.array([1, -1, 1])
self.kpoints = np.concatenate((self.kpoints, newK), axis=0)
self.log.debug("self.kpoints.shape (after concat.): " + str(self.kpoints.shape))
newSx = -1 * self.sx
newSy = 1 * self.sy
newSz = 1 * self.sz
self.sx = np.concatenate((self.sx, newSx), axis=0)
self.sy = np.concatenate((self.sy, newSy), axis=0)
self.sz = np.concatenate((self.sz, newSz), axis=0)
print("self.sx", self.sx.shape)
print("self.sy", self.sy.shape)
print("self.sz", self.sz.shape)
# the bands and proj. character also need to be enlarged
self.bands = np.concatenate((self.bands, self.bands), axis=0)
self.character = np.concatenate((self.character, self.character), axis=0)
print("self.character", self.character.shape)
print("self.bands", self.bands.shape)
self.log.debug("Mirror:...Done")
return
[docs] def Translate(self, newOrigin):
"""Centers the Kpoints at newOrigin, newOrigin is either and index (of
some Kpoint) or the cartesian coordinates of one point in the
reciprocal space.
"""
self.log.debug("Translate(): ...")
if len(newOrigin) == 1:
newOrigin = int(newOrigin[0])
newOrigin = self.kpoints[newOrigin]
# Make sure newOrigin is a numpy array
newOrigin = np.array(newOrigin, dtype=float)
self.log.debug("newOrigin: " + str(newOrigin))
self.kpoints = self.kpoints - newOrigin
self.log.debug("new Kpoints:\n" + str(self.kpoints))
self.log.debug("Translate(): ...Done")
return