from .grid import GridAbstract
from .Kpoint import KpointBZpath
import warnings
from collections.abc import Iterable
import numpy as np
[docs]
class Path(GridAbstract):
""" A class containing information about the k-path
Parameters
-----------
system : :class:`~wannierberri.system.System`
which the calculations will be made
length : float
(angstroms) -- in this case the grid is NK[i]=length*||B[i]||/2pi B- reciprocal lattice
dk : float
(inverse angstroms) -- in this case the grid is NK[i]=length*||B[i]||/2pi B- reciprocal lattice
nodes : list
cordinates of the nodes in the the reduced coordinates. Some entries may be None - which means that the segment should be skipped
| No labels or nk's should be assigned to None nodes
nk : int or list or numpy.array(3)
number of k-points along each directions
k_list : list or str
| if k_list is a list - Coordinatres of all k-points in the reduced coordinates
| if k_list = 'sphere' - Automatically generate k-points on a sphere (request r1 origin ntheta nphi)
| if k_list = 'spheroid' - Automatically generate k-points on a spheroid (request r1 r2 origin ntheta nphi)
labels : list of dict
| if k_list is set - it is a dict {i:lab} with i - index of k-point, lab - corresponding label (not all kpoints need to be labeled
| if nodes is set - it is a list of labels, one for every node
r1,r2 : float
radius.
sphere: x^2+y^2+z^2 = r1^2
spheroid: (x^2+y^2)/r1^2+z^2/r2^2 = 1
origin : array
origin of sphere or spheroid in k-space
nphi,ntheta: int
number of k-points along each angle in polar coordinates
Notes
-----
user needs to specify either `k_list` or (`nodes` + (`length` or `nk` or dk))
"""
def __init__(
self,
system,
k_list=None,
nodes=None,
length=None,
dk=None,
nk=None,
labels=None,
breaks=[],
r1=None,
r2=None,
ntheta=None,
nphi=None,
origin=None):
super().__init__(system=system, use_symmetry=False)
# self.findif = None
self.breaks = breaks
if k_list == 'sphere':
self.K_list = self.sphere(r1, r1, ntheta, nphi, origin)
self.labels = ['sphere']
elif k_list == 'spheroid':
self.K_list = self.sphere(r1, r2, ntheta, nphi, origin)
self.labels = ['spheroid']
elif k_list is None:
if nodes is None:
raise ValueError("need to specify either 'k_list' of 'nodes'")
if labels is None:
labels = [str(i + 1) for i, k in enumerate([k for k in nodes if k is not None])]
labels = (l for l in labels)
labels = [None if k is None else next(labels) for k in nodes]
if length is not None:
assert length > 0
if dk is not None:
raise ValueError("'length' and 'dk' cannot be set together")
dk = 2 * np.pi / length
if dk is not None:
if nk is not None:
raise ValueError("'nk' cannot be set together with 'length' or 'dk' ")
if isinstance(nk, Iterable):
nkgen = (x for x in nk)
else:
nkgen = (nk for x in nodes)
self.K_list = np.zeros((0, 3))
self.labels = {}
self.breaks = []
for start, end, l1, l2 in zip(nodes, nodes[1:], labels, labels[1:]):
if start is not None and end is not None:
self.labels[self.K_list.shape[0]] = l1
start = np.array(start)
end = np.array(end)
assert start.shape == end.shape == (3, )
if nk is not None:
_nk = next(nkgen)
else:
_nk = round(np.linalg.norm((start - end).dot(self.recip_lattice)) / dk) + 1
if _nk == 1:
_nk = 2
self.K_list = np.vstack(
(
self.K_list, start[None, :] + np.linspace(0, 1., _nk - 1, endpoint=False)[:, None] *
(end - start)[None, :]))
elif end is None:
self.breaks.append(self.K_list.shape[0] - 1)
self.K_list = np.vstack((self.K_list, nodes[-1]))
self.labels[self.K_list.shape[0] - 1] = labels[-1]
else:
self.K_list = np.array(k_list)
assert self.K_list.shape[1] == 3, "k_list should contain 3-vectors"
assert self.K_list.shape[0] > 0, "k_list should not be empty"
for var in 'nodes', 'length', 'nk', 'dk':
if locals()[var] is not None:
warnings.warn(f"k_list was entered manually, ignoring {var}")
self.labels = {} if labels is None else labels
self.breaks = [] if breaks is None else breaks
self.div = np.shape(self.K_list)[0]
self.breaks = np.array(self.breaks, dtype=int)
def sphere(self, r1, r2, ntheta, nphi, origin):
theta = np.linspace(0, np.pi, ntheta, endpoint=True)
phi = np.linspace(0, 2 * np.pi, nphi, endpoint=True)
theta_grid, phi_grid = np.meshgrid(theta, phi)
sphere = [
r1 * np.cos(phi_grid) * np.sin(theta_grid), r1 * np.sin(phi_grid) * np.sin(theta_grid),
r2 * np.cos(theta_grid)
]
cart_k_list = np.array(sphere).reshape(3, ntheta * nphi).transpose(1, 0)
list_k = cart_k_list.dot(np.linalg.inv(self.recip_lattice)) - origin[None, :]
with open("klist_cart.txt", "w") as f:
for i in range(ntheta * nphi):
f.write(f"{cart_k_list[i, 0]:12.6f}{cart_k_list[i, 1]:12.6f}{cart_k_list[i, 2]:12.6f}\n")
return list_k
@property
def str_short(self):
return f"Path() with {len(self.K_list)} points and labels {self.labels}"
@property
def recip_lattice(self):
return self.pointgroup.recip_lattice
def __str__(self):
return (
"\n" + "\n".join(
" ".join(f"{x:10.6f}"
for x in k) + ((" <--- " + self.labels[i]) if i in self.labels else "") + (
("\n" + "-" * 20) if i in self.breaks else "") for i, k in enumerate(self.K_list)))
def get_K_list(self, use_symmetry=False):
""" returns the list of K-points"""
if use_symmetry:
warnings.warn("symmetry is not used for a tabulation along path")
print("generating K_list")
K_list = [
KpointBZpath(K=K, pointgroup=self.pointgroup)
for K in self.K_list
]
print("Done ")
return K_list
def getKline(self, break_thresh=np.inf):
KPcart = self.K_list.dot(self.recip_lattice)
K = np.zeros(KPcart.shape[0])
k = np.linalg.norm(KPcart[1:, :] - KPcart[:-1, :], axis=1)
k[k > break_thresh] = 0.0
if len(self.breaks) > 0:
k[self.breaks] = 0.0
K[1:] = np.cumsum(k)
return K