Source code for radtools.crystal.kpoints

r"""
General 3D lattice.
"""

from typing import Iterable
import numpy as np

__all__ = ["Kpoints"]


[docs] class Kpoints: r""" K-point path. Parameters ---------- points : dict Dictionary of the high symmetry points. Coordinates are given in absolute coordinates in reciprocal space. labels : dict Dictionary of the high symmetry points labels. Has to have the same keys as ``points``. path : str, optional K points path. n : int Number of points between each pair of the high symmetry points (high symmetry points excluded). """ def __init__(self, points, labels, path=None, n=100): self._path = None self._hs_points = [point for point in points] self._hs_coordinates = dict( [(point, np.array(points[point])) for point in points] ) self._labels = labels self._n = n if path is None: path = self._hs_points[0] for i in self._hs_points[1:]: path += "-" + i self.path = path @property def path(self): r""" K points path. Returns ------- path : list of list of str K points path. Each subpath is a list of the high symmetry points. """ return self._path @path.setter def path(self, new_path): if isinstance(new_path, str): tmp_path = new_path.split("|") new_path = [] for i in range(len(tmp_path)): subpath = tmp_path[i].split("-") # Each subpath has to contain at least two points. if len(subpath) != 1: new_path.append(subpath) elif isinstance(new_path, Iterable): tmp_path = new_path new_path = [] for subpath in tmp_path: if isinstance(subpath, str) and "-" in subpath: subpath = subpath.split("-") # Each subpath has to contain at least two points. if len(subpath) != 1: new_path.append(subpath) elif ( not isinstance(subpath, str) and isinstance(subpath, Iterable) and len(subpath) != 1 ): new_path.append(subpath) else: new_path = [tmp_path] break # Check if all points are defined. for subpath in new_path: for point in subpath: if point not in self._hs_points: message = f"Point '{point}' is not defined. Defined points are:" for defined_point in self._hs_points: message += f"\n {defined_point} : {self._hs_coordinates[defined_point]}" raise ValueError(message) self._path = new_path @property def path_string(self): r""" K points path as a string. Returns ------- path : str """ result = "" for s_i, subpath in enumerate(self.path): for i, name in enumerate(subpath): if i != 0: result += "-" result += name if s_i != len(self.path) - 1: result += "|" return result @property def n(self): r""" Amount of points between each pair of the high symmetry points (high symmetry points excluded). Returns ------- n : int """ return self._n @n.setter def n(self, new_n): if not isinstance(new_n, int): raise ValueError(f"n has to be integer. Given: {new_n}") self._n = new_n @property def labels(self): r""" Labels of high symmetry points, ready to be plotted. For example for point "Gamma" it returns r"$\Gamma$". If there are two high symmetry points following one another in the path, it returns "X|Y" where X and Y are the labels of the two high symmetry points. Returns ------- labels : list of str Labels, ready to be plotted. Same length as :py:attr:`.coordinates`. """ labels = [] for s_i, subpath in enumerate(self.path): if s_i != 0: labels[-1] += "|" + self._labels[subpath[0]] else: labels.append(self._labels[subpath[0]]) for name in subpath[1:]: labels.append(self._labels[name]) return labels @property def coordinates(self): r""" Flatten coordinates of the high symmetry points, ready to be plotted. Returns ------- coordinates : :numpy:`ndarray` Coordinates, ready to be plotted. Same length as :py:attr:`.labels`. """ coordinates = [] for s_i, subpath in enumerate(self.path): if s_i == 0: coordinates.append(0) for i, name in enumerate(subpath[1:]): coordinates.append( np.linalg.norm( self._hs_coordinates[name] - self._hs_coordinates[subpath[i]] ) + coordinates[-1] ) return np.array(coordinates) @property def points(self): r""" Coordinates of all points with n points between each pair of the high symmetry points (high symmetry points excluded). Returns ------- points : (N, 3) :numpy:`ndarray` Coordinates of all points. """ points = None for subpath in self.path: for i in range(len(subpath) - 1): name = subpath[i] next_name = subpath[i + 1] new_points = np.linspace( self._hs_coordinates[name], self._hs_coordinates[next_name], self._n + 2, ) if points is None: points = new_points else: points = np.concatenate((points, new_points)) return points # It can not just call for points and flatten them, because it has to treat "|" as a special case. @property def flatten_points(self): r""" Flatten coordinates of all points with n points between each pair of the high symmetry points (high symmetry points excluded). Used to plot band structure, dispersion, etc. Returns ------- flatten_points : (N, 3) :numpy:`ndarray` Flatten coordinates of all points. """ flatten_points = None for s_i, subpath in enumerate(self.path): for i in range(len(subpath) - 1): name = subpath[i] next_name = subpath[i + 1] points = ( np.linspace( self._hs_coordinates[name], self._hs_coordinates[next_name], self._n + 2, ) - self._hs_coordinates[name] ) delta = np.linalg.norm(points, axis=1) if s_i == 0 and i == 0: flatten_points = delta else: delta += flatten_points[-1] flatten_points = np.concatenate((flatten_points, delta)) return flatten_points