# Wulfric - Cell, Atoms, K-path.
# Copyright (C) 2023-2025 Andrey Rybakov
#
# e-mail: anry@uv.es, web: adrybakov.com
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
from math import cos, sin, tan
import numpy as np
from wulfric.cell._basic_manipulation import get_params, get_reciprocal
from wulfric.cell._lepage import lepage
from wulfric.cell._sc_standardize import get_C_matrix, get_conventional, get_S_matrix
from wulfric.cell._sc_variation import get_variation
from wulfric.constants._numerical import TORADIANS
from wulfric.constants._sc_notation import DEFAULT_K_PATHS, HS_PLOT_NAMES
# Save local scope at this moment
old_dir = set(dir())
old_dir.add("old_dir")
def _CUB_hs_points():
r"""
Get high-symmetry points for the CUB lattice.
See :ref:`guide_cub` for the details.
Returns
-------
kpoints : dict
High-symmetry points.
"""
return {
"G": np.array([0, 0, 0]),
"M": np.array([1 / 2, 1 / 2, 0]),
"R": np.array([1 / 2, 1 / 2, 1 / 2]),
"X": np.array([0, 1 / 2, 0]),
}
def _FCC_hs_points():
r"""
Get high-symmetry points for the FCC lattice.
See :ref:`guide_fcc` for the details.
Returns
-------
kpoints : dict
High-symmetry points.
"""
return {
"G": np.array([0, 0, 0]),
"K": np.array([3 / 8, 3 / 8, 3 / 4]),
"L": np.array([1 / 2, 1 / 2, 1 / 2]),
"U": np.array([5 / 8, 1 / 4, 5 / 8]),
"W": np.array([1 / 2, 1 / 4, 3 / 4]),
"X": np.array([1 / 2, 0, 1 / 2]),
}
def _BCC_hs_points():
r"""
Get high-symmetry points for the CUB lattice.
See :ref:`guide_bcc` for the details.
Returns
-------
kpoints : dict
High-symmetry points.
"""
return {
"G": np.array([0, 0, 0]),
"H": np.array([1 / 2, -1 / 2, 1 / 2]),
"P": np.array([1 / 4, 1 / 4, 1 / 4]),
"N": np.array([0, 0, 1 / 2]),
}
def _TET_hs_points():
r"""
Get high-symmetry points for the TET lattice.
See :ref:`guide_tet` for the details.
Returns
-------
kpoints : dict
High-symmetry points.
"""
return {
"G": np.array([0, 0, 0]),
"A": np.array([1 / 2, 1 / 2, 1 / 2]),
"M": np.array([1 / 2, 1 / 2, 0]),
"R": np.array([0, 1 / 2, 1 / 2]),
"X": np.array([0, 1 / 2, 0]),
"Z": np.array([0, 0, 1 / 2]),
}
def _BCT_hs_points(variation, conv_a, conv_c):
r"""
Get high-symmetry points for the BCT lattice.
See :ref:`guide_bct` for the details.
Parameters
----------
variation : str
BCT variation. Case-insensitive.
conv_a : float
Length of the first two lattice vectors of the conventional cell.
conv_c : float
Length of the third lattice vector of the conventional cell.
Returns
-------
kpoints : dict
High-symmetry points.
"""
variation = variation.upper()
if variation == "BCT1":
eta = (1 + conv_c**2 / conv_a**2) / 4
kpoints = {
"G": np.array([0, 0, 0]),
"M": np.array([-1 / 2, 1 / 2, 1 / 2]),
"N": np.array([0, 1 / 2, 0]),
"P": np.array([1 / 4, 1 / 4, 1 / 4]),
"X": np.array([0, 0, 1 / 2]),
"Z": np.array([eta, eta, -eta]),
"Z1": np.array([-eta, 1 - eta, eta]),
}
elif variation == "BCT2":
eta = (1 + conv_a**2 / conv_c**2) / 4
zeta = conv_a**2 / (2 * conv_c**2)
kpoints = {
"G": np.array([0, 0, 0]),
"N": np.array([0, 1 / 2, 0]),
"P": np.array([1 / 4, 1 / 4, 1 / 4]),
"S": np.array([-eta, eta, eta]),
"S1": np.array([eta, 1 - eta, -eta]),
"X": np.array([0, 0, 1 / 2]),
"Y": np.array([-zeta, zeta, 1 / 2]),
"Y1": np.array([1 / 2, 1 / 2, -zeta]),
"Z": np.array([1 / 2, 1 / 2, -1 / 2]),
}
return kpoints
def _ORC_hs_points():
r"""
Get high-symmetry points for the ORC lattice.
See :ref:`guide_orc` for the details.
Returns
-------
kpoints : dict
High-symmetry points.
"""
return {
"G": np.array([0, 0, 0]),
"R": np.array([1 / 2, 1 / 2, 1 / 2]),
"S": np.array([1 / 2, 1 / 2, 0]),
"T": np.array([0, 1 / 2, 1 / 2]),
"U": np.array([1 / 2, 0, 1 / 2]),
"X": np.array([1 / 2, 0, 0]),
"Y": np.array([0, 1 / 2, 0]),
"Z": np.array([0, 0, 1 / 2]),
}
def _ORCF_hs_points(variation, conv_a, conv_b, conv_c):
r"""
Get high-symmetry points for the ORCF lattice.
See :ref:`guide_orcf` for the details.
Parameters
----------
variation : str
ORCF variation. Case-insensitive.
conv_a : float
Length of the first lattice vector of the conventional cell.
conv_b : float
Length of the second lattice vector of the conventional cell.
conv_c : float
Length of the third lattice vector of the conventional cell.
Returns
-------
kpoints : dict
High-symmetry points.
"""
variation = variation.upper()
if variation == "ORCF1":
eta = (1 + conv_a**2 / conv_b**2 + conv_a**2 / conv_c**2) / 4
zeta = (1 + conv_a**2 / conv_b**2 - conv_a**2 / conv_c**2) / 4
kpoints = {
"G": np.array([0, 0, 0]),
"A": np.array([1 / 2, 1 / 2 + zeta, zeta]),
"A1": np.array([1 / 2, 1 / 2 - zeta, 1 - zeta]),
"L": np.array([1 / 2, 1 / 2, 1 / 2]),
"T": np.array([1, 1 / 2, 1 / 2]),
"X": np.array([0, eta, eta]),
"X1": np.array([1, 1 - eta, 1 - eta]),
"Y": np.array([1 / 2, 0, 1 / 2]),
"Z": np.array([1 / 2, 1 / 2, 0]),
}
elif variation == "ORCF2":
eta = (1 + conv_a**2 / conv_b**2 - conv_a**2 / conv_c**2) / 4
delta = (1 + conv_b**2 / conv_a**2 - conv_b**2 / conv_c**2) / 4
phi = (1 + conv_c**2 / conv_b**2 - conv_c**2 / conv_a**2) / 4
kpoints = {
"G": np.array([0, 0, 0]),
"C": np.array([1 / 2, 1 / 2 - eta, 1 - eta]),
"C1": np.array([1 / 2, 1 / 2 + eta, eta]),
"D": np.array([1 / 2 - delta, 1 / 2, 1 - delta]),
"D1": np.array([1 / 2 + delta, 1 / 2, delta]),
"L": np.array([1 / 2, 1 / 2, 1 / 2]),
"H": np.array([1 - phi, 1 / 2 - phi, 1 / 2]),
"H1": np.array([phi, 1 / 2 + phi, 1 / 2]),
"X": np.array([0, 1 / 2, 1 / 2]),
"Y": np.array([1 / 2, 0, 1 / 2]),
"Z": np.array([1 / 2, 1 / 2, 0]),
}
elif variation == "ORCF3":
eta = (1 + conv_a**2 / conv_b**2 + conv_a**2 / conv_c**2) / 4
zeta = (1 + conv_a**2 / conv_b**2 - conv_a**2 / conv_c**2) / 4
kpoints = {
"G": np.array([0, 0, 0]),
"A": np.array([1 / 2, 1 / 2 + zeta, zeta]),
"A1": np.array([1 / 2, 1 / 2 - zeta, 1 - zeta]),
"L": np.array([1 / 2, 1 / 2, 1 / 2]),
"T": np.array([1, 1 / 2, 1 / 2]),
"X": np.array([0, eta, eta]),
"Y": np.array([1 / 2, 0, 1 / 2]),
"Z": np.array([1 / 2, 1 / 2, 0]),
}
return kpoints
def _ORCI_hs_points(conv_a, conv_b, conv_c):
r"""
Get high-symmetry points for the ORCI lattice.
See :ref:`guide_orci` for the details.
Parameters
----------
conv_a : float
Length of the first lattice vector of the conventional cell.
conv_b : float
Length of the second lattice vector of the conventional cell.
conv_c : float
Length of the third lattice vector of the conventional cell.
Returns
-------
kpoints : dict
High-symmetry points.
"""
zeta = (1 + conv_a**2 / conv_c**2) / 4
eta = (1 + conv_b**2 / conv_c**2) / 4
delta = (conv_b**2 - conv_a**2) / (4 * conv_c**2)
mu = (conv_a**2 + conv_b**2) / (4 * conv_c**2)
return {
"G": np.array([0, 0, 0]),
"L": np.array([-mu, mu, 1 / 2 - delta]),
"L1": np.array([mu, -mu, 1 / 2 + delta]),
"L2": np.array([1 / 2 - delta, 1 / 2 + delta, -mu]),
"R": np.array([0, 1 / 2, 0]),
"S": np.array([1 / 2, 0, 0]),
"T": np.array([0, 0, 1 / 2]),
"W": np.array([1 / 4, 1 / 4, 1 / 4]),
"X": np.array([-zeta, zeta, zeta]),
"X1": np.array([zeta, 1 - zeta, -zeta]),
"Y": np.array([eta, -eta, eta]),
"Y1": np.array([1 - eta, eta, -eta]),
"Z": np.array([1 / 2, 1 / 2, -1 / 2]),
}
def _ORCC_hs_points(conv_a, conv_b):
r"""
Get high-symmetry points for the ORCC lattice.
See :ref:`guide_orcc` for the details.
Parameters
----------
conv_a : float
Length of the first lattice vector of the conventional cell.
conv_b : float
Length of the second lattice vector of the conventional cell.
Returns
-------
kpoints : dict
High-symmetry points.
"""
zeta = (1 + conv_a**2 / conv_b**2) / 4
return {
"G": np.array([0, 0, 0]),
"A": np.array([zeta, zeta, 1 / 2]),
"A1": np.array([-zeta, 1 - zeta, 1 / 2]),
"R": np.array([0, 1 / 2, 1 / 2]),
"S": np.array([0, 1 / 2, 0]),
"T": np.array([-1 / 2, 1 / 2, 1 / 2]),
"X": np.array([zeta, zeta, 0]),
"X1": np.array([-zeta, 1 - zeta, 0]),
"Y": np.array([-1 / 2, 1 / 2, 0]),
"Z": np.array([0, 0, 1 / 2]),
}
def _HEX_hs_points():
r"""
Get high-symmetry points for the HEX lattice.
See :ref:`guide_hex` for the details.
Returns
-------
kpoints : dict
High-symmetry points.
"""
return {
"G": np.array([0, 0, 0]),
"A": np.array([0, 0, 1 / 2]),
"H": np.array([1 / 3, 1 / 3, 1 / 2]),
"K": np.array([1 / 3, 1 / 3, 0]),
"L": np.array([1 / 2, 0, 1 / 2]),
"M": np.array([1 / 2, 0, 0]),
}
def _RHL_hs_points(variation, conv_alpha):
r"""
Get high-symmetry points for the RHL lattice.
See :ref:`guide_rhl` for the details.
Parameters
----------
variation : str
RHL variation. Case-insensitive.
alpha : float
Angle between the lattice vectors.
Returns
-------
kpoints : dict
High-symmetry points.
"""
variation = variation.upper()
conv_alpha *= TORADIANS
if variation == "RHL1":
eta = (1 + 4 * cos(conv_alpha)) / (2 + 4 * cos(conv_alpha))
nu = 3 / 4 - eta / 2
return {
"G": np.array([0, 0, 0]),
"B": np.array([eta, 1 / 2, 1 - eta]),
"B1": np.array([1 / 2, 1 - eta, eta - 1]),
"F": np.array([1 / 2, 1 / 2, 0]),
"L": np.array([1 / 2, 0, 0]),
"L1": np.array([0, 0, -1 / 2]),
"P": np.array([eta, nu, nu]),
"P1": np.array([1 - nu, 1 - nu, 1 - eta]),
"P2": np.array([nu, nu, eta - 1]),
"Q": np.array([1 - nu, nu, 0]),
"X": np.array([nu, 0, -nu]),
"Z": np.array([1 / 2, 1 / 2, 1 / 2]),
}
elif variation == "RHL2":
eta = 1 / (2 * tan(conv_alpha / 2) ** 2)
nu = 3 / 4 - eta / 2
return {
"G": np.array([0, 0, 0]),
"F": np.array([1 / 2, -1 / 2, 0]),
"L": np.array([1 / 2, 0, 0]),
"P": np.array([1 - nu, -nu, 1 - nu]),
"P1": np.array([nu, nu - 1, nu - 1]),
"Q": np.array([eta, eta, eta]),
"Q1": np.array([1 - eta, -eta, -eta]),
"Z": np.array([1 / 2, -1 / 2, 1 / 2]),
}
def _MCL_hs_points(conv_b, conv_c, conv_alpha):
r"""
Get high-symmetry points for the MCL lattice.
See :ref:`guide_mcl` for the details.
Parameters
----------
conv_b : float
Length of the second lattice vector of the conventional cell.
conv_c : float
Length of the third lattice vector of the conventional cell.
conv_alpha : float
Angle between the lattice vectors.
Returns
-------
kpoints : dict
High-symmetry points.
"""
conv_alpha *= TORADIANS
eta = (1 - conv_b * cos(conv_alpha) / conv_c) / (2 * sin(conv_alpha) ** 2)
nu = 1 / 2 - eta * conv_c * cos(conv_alpha) / conv_b
return {
"G": np.array([0, 0, 0]),
"A": np.array([1 / 2, 1 / 2, 0]),
"C": np.array([0, 1 / 2, 1 / 2]),
"D": np.array([1 / 2, 0, 1 / 2]),
"D1": np.array([1 / 2, 0, -1 / 2]),
"E": np.array([1 / 2, 1 / 2, 1 / 2]),
"H": np.array([0, eta, 1 - nu]),
"H1": np.array([0, 1 - eta, nu]),
"H2": np.array([0, eta, -nu]),
"M": np.array([1 / 2, eta, 1 - nu]),
"M1": np.array([1 / 2, 1 - eta, nu]),
"M2": np.array([1 / 2, eta, -nu]),
"X": np.array([0, 1 / 2, 0]),
"Y": np.array([0, 0, 1 / 2]),
"Y1": np.array([0, 0, -1 / 2]),
"Z": np.array([1 / 2, 0, 0]),
}
def _MCLC_hs_points(variation, conv_a, conv_b, conv_c, conv_alpha):
r"""
Get high-symmetry points for the MCLC lattice.
See :ref:`guide_mclc` for the details.
Parameters
----------
variation : str
MCLC variation. Case-insensitive.
conv_a : float
Length of the first lattice vector of the conventional cell.
conv_b : float
Length of the second lattice vector of the conventional cell.
conv_c : float
Length of the third lattice vector of the conventional cell.
conv_alpha : float
Angle between the lattice vectors.
Returns
-------
kpoints : dict
High-symmetry points.
"""
variation = variation.upper()
conv_alpha *= TORADIANS
# Parameters
if variation in ["MCLC1", "MCLC2"]:
zeta = (2 - conv_b * cos(conv_alpha) / conv_c) / (4 * sin(conv_alpha) ** 2)
eta = 1 / 2 + 2 * zeta * conv_c * cos(conv_alpha) / conv_b
psi = 3 / 4 - conv_a**2 / (4 * conv_b**2 * sin(conv_alpha) ** 2)
phi = psi + (3 / 4 - psi) * conv_b * cos(conv_alpha) / conv_c
elif variation in ["MCLC3", "MCLC4"]:
mu = (1 + conv_b**2 / conv_a**2) / 4
delta = conv_b * conv_c * cos(conv_alpha) / (2 * conv_a**2)
zeta = (
mu
- 1 / 4
+ (1 - conv_b * cos(conv_alpha) / conv_c) / (4 * sin(conv_alpha) ** 2)
)
eta = 1 / 2 + 2 * zeta * conv_c * cos(conv_alpha) / conv_b
phi = 1 + zeta - 2 * mu
psi = eta - 2 * delta
elif variation == "MCLC5":
zeta = (
conv_b**2 / conv_a**2
+ (1 - conv_b * cos(conv_alpha) / conv_c) / sin(conv_alpha) ** 2
) / 4
eta = 1 / 2 + 2 * zeta * conv_c * cos(conv_alpha) / conv_b
mu = (
eta / 2
+ conv_b**2 / (4 * conv_a**2)
- conv_b * conv_c * cos(conv_alpha) / (2 * conv_a**2)
)
nu = 2 * mu - zeta
rho = 1 - zeta * conv_a**2 / conv_b**2
omega = (
(4 * nu - 1 - conv_b**2 * sin(conv_alpha) ** 2 / conv_a**2)
* conv_c
/ (2 * conv_b * cos(conv_alpha))
)
delta = zeta * conv_c * cos(conv_alpha) / conv_b + omega / 2 - 1 / 4
# Path
if variation == "MCLC1":
return {
"G": np.array([0, 0, 0]),
"N": np.array([1 / 2, 0, 0]),
"N1": np.array([0, -1 / 2, 0]),
"F": np.array([1 - zeta, 1 - zeta, 1 - eta]),
"F1": np.array([zeta, zeta, eta]),
"F2": np.array([-zeta, -zeta, 1 - eta]),
"I": np.array([phi, 1 - phi, 1 / 2]),
"I1": np.array([1 - phi, phi - 1, 1 / 2]),
"L": np.array([1 / 2, 1 / 2, 1 / 2]),
"M": np.array([1 / 2, 0, 1 / 2]),
"X": np.array([1 - psi, psi - 1, 0]),
"X1": np.array([psi, 1 - psi, 0]),
"X2": np.array([psi - 1, -psi, 0]),
"Y": np.array([1 / 2, 1 / 2, 0]),
"Y1": np.array([-1 / 2, -1 / 2, 0]),
"Z": np.array([0, 0, 1 / 2]),
}
elif variation == "MCLC2":
return {
"G": np.array([0, 0, 0]),
"N": np.array([1 / 2, 0, 0]),
"N1": np.array([0, -1 / 2, 0]),
"F": np.array([1 - zeta, 1 - zeta, 1 - eta]),
"F1": np.array([zeta, zeta, eta]),
"F2": np.array([-zeta, -zeta, 1 - eta]),
"F3": np.array([1 - zeta, -zeta, 1 - eta]),
"I": np.array([phi, 1 - phi, 1 / 2]),
"I1": np.array([1 - phi, phi - 1, 1 / 2]),
"L": np.array([1 / 2, 1 / 2, 1 / 2]),
"M": np.array([1 / 2, 0, 1 / 2]),
"X": np.array([1 - psi, psi - 1, 0]),
"Y": np.array([1 / 2, 1 / 2, 0]),
"Y1": np.array([-1 / 2, -1 / 2, 0]),
"Z": np.array([0, 0, 1 / 2]),
}
elif variation == "MCLC3":
return {
"G": np.array([0, 0, 0]),
"F": np.array([1 - phi, 1 - phi, 1 - psi]),
"F1": np.array([phi, phi - 1, psi]),
"F2": np.array([1 - phi, -phi, 1 - psi]),
"H": np.array([zeta, zeta, eta]),
"H1": np.array([1 - zeta, -zeta, 1 - eta]),
"H2": np.array([-zeta, -zeta, 1 - eta]),
"I": np.array([1 / 2, -1 / 2, 1 / 2]),
"M": np.array([1 / 2, 0, 1 / 2]),
"N": np.array([1 / 2, 0, 0]),
"N1": np.array([0, -1 / 2, 0]),
"X": np.array([1 / 2, -1 / 2, 0]),
"Y": np.array([mu, mu, delta]),
"Y1": np.array([1 - mu, -mu, -delta]),
"Y2": np.array([-mu, -mu, -delta]),
"Y3": np.array([mu, mu - 1, delta]),
"Z": np.array([0, 0, 1 / 2]),
}
elif variation == "MCLC4":
return {
"G": np.array([0, 0, 0]),
"F": np.array([1 - phi, 1 - phi, 1 - psi]),
"H": np.array([zeta, zeta, eta]),
"H1": np.array([1 - zeta, -zeta, 1 - eta]),
"H2": np.array([-zeta, -zeta, 1 - eta]),
"I": np.array([1 / 2, -1 / 2, 1 / 2]),
"M": np.array([1 / 2, 0, 1 / 2]),
"N": np.array([1 / 2, 0, 0]),
"N1": np.array([0, -1 / 2, 0]),
"X": np.array([1 / 2, -1 / 2, 0]),
"Y": np.array([mu, mu, delta]),
"Y1": np.array([1 - mu, -mu, -delta]),
"Y2": np.array([-mu, -mu, -delta]),
"Y3": np.array([mu, mu - 1, delta]),
"Z": np.array([0, 0, 1 / 2]),
}
elif variation == "MCLC5":
return {
"G": np.array([0, 0, 0]),
"F": np.array([nu, nu, omega]),
"F1": np.array([1 - nu, 1 - nu, 1 - omega]),
"F2": np.array([nu, nu - 1, omega]),
"H": np.array([zeta, zeta, eta]),
"H1": np.array([1 - zeta, -zeta, 1 - eta]),
"H2": np.array([-zeta, -zeta, 1 - eta]),
"I": np.array([rho, 1 - rho, 1 / 2]),
"I1": np.array([1 - rho, rho - 1, 1 / 2]),
"L": np.array([1 / 2, 1 / 2, 1 / 2]),
"M": np.array([1 / 2, 0, 1 / 2]),
"N": np.array([1 / 2, 0, 0]),
"N1": np.array([0, -1 / 2, 0]),
"X": np.array([1 / 2, -1 / 2, 0]),
"Y": np.array([mu, mu, delta]),
"Y1": np.array([1 - mu, -mu, -delta]),
"Y2": np.array([-mu, -mu, -delta]),
"Y3": np.array([mu, mu - 1, delta]),
"Z": np.array([0, 0, 1 / 2]),
}
def _TRI_hs_points(variation):
r"""
Get high-symmetry points for the TRI lattice.
See :ref:`guide_tri` for the details.
Parameters
----------
variation : str
TRI variation. Case-insensitive.
Returns
-------
kpoints : dict
High-symmetry points.
"""
variation = variation.upper()
if variation in ["TRI1A", "TRI2A"]:
return {
"G": np.array([0, 0, 0]),
"L": np.array([1 / 2, 1 / 2, 0]),
"M": np.array([0, 1 / 2, 1 / 2]),
"N": np.array([1 / 2, 0, 1 / 2]),
"R": np.array([1 / 2, 1 / 2, 1 / 2]),
"X": np.array([1 / 2, 0, 0]),
"Y": np.array([0, 1 / 2, 0]),
"Z": np.array([0, 0, 1 / 2]),
}
elif variation in ["TRI1B", "TRI2B"]:
return {
"G": np.array([0, 0, 0]),
"L": np.array([1 / 2, -1 / 2, 0]),
"M": np.array([0, 0, 1 / 2]),
"N": np.array([-1 / 2, -1 / 2, 1 / 2]),
"R": np.array([0, -1 / 2, 1 / 2]),
"X": np.array([0, -1 / 2, 0]),
"Y": np.array([1 / 2, 0, 0]),
"Z": np.array([-1 / 2, 0, 1 / 2]),
}
[docs]
def get_hs_data(
cell,
return_relative=True,
lattice_type=None,
lattice_variation=None,
S_matrix=None,
C_matrix=None,
length_tolerance=1e-8,
angle_tolerance=1e-4,
):
r"""
Return information about high symmetry points and path as defined in the paper by
Setyawan and Curtarolo [1]_.
Parameters
----------
cell : (3, 3) |array-like|_
Matrix of a cell, rows are interpreted as vectors.
return_relative : bool, default True
Whether to return coordinates as relative to the reciprocal cell or in absolute
coordinates in the reciprocal Cartesian space.
lattice_type : str, optional
One of the 14 lattice types that correspond to the provided ``cell``,
case-insensitive. If not provided, then computed automatically from ``cell``. If
provided, then it user's responsibility to ensure that ``lattice_type`` is
correct.
lattice_variation : str, optional
One of the lattice variations that correspond to the provided ``cell`` and
``lattice_type``. If not provided, then computed automatically. Case-insensitive.
S_matrix : (3, 3) |array-like|_, optional
Transformation matrix S. If not provided, then computed automatically from
``cell``. If provided, then it is user's responsibility to ensure that the matrix
is the correct one for the given ``cell``.
C_matrix : (3, 3) |array-like|_, optional
Transformation matrix C. If not provided, then computed automatically from
``cell``. If provided, then it is user's responsibility to ensure that the matrix
is the correct one for the given ``cell``.
length_tolerance : float, default :math:`10^{-8}`
Tolerance for length variables (lengths of the lattice vectors). Default value is
chosen in the contexts of condense matter physics, assuming that length is given
in Angstroms. Please choose appropriate tolerance for your problem.
angle_tolerance : float, default :math:`10^{-4}`
Tolerance for angle variables (angles of the lattice). Default value is chosen in
the contexts of condense matter physics, assuming that angles are in degrees.
Please choose appropriate tolerance for your problem.
Returns
-------
coordinates : list of (3, 3) :numpy:`ndarray`
Coordinates of the high symmetry points in reciprocal space. Relative to the
reciprocal cell.
names: list of str
Names of the high symmetry points. Used for programming, not for plotting. Have
the same length as ``coordinates``.
labels : list of str
List of the high symmetry points labels for plotting. Have the same length as
``coordinates``. Labels are not necessary equal to the names.
path : str
K path. High symmetry points are referenced by elements of ``names``.
References
----------
.. [1] Setyawan, W. and Curtarolo, S., 2010.
High-throughput electronic band structure calculations: Challenges and tools.
Computational materials science, 49(2), pp. 299-312.
See Also
--------
wulfric.Kpoints : Class with a convenient interface for the same information.
Examples
--------
.. doctest::
>>> import wulfric as wulf
>>> cell = wulf.cell.get_cell_example("hex")
>>> coordinates, names, labels, kpath = wulf.cell.get_hs_data(cell)
>>> kpath
'G-M-K-G-A-L-H-A|L-M|K-H'
>>> labels
['$\\Gamma$', 'A', 'H', 'K', 'L', 'M']
>>> names
['G', 'A', 'H', 'K', 'L', 'M']
>>> coordinates
[array([0., 0., 0.]), array([0. , 0. , 0.5]), array([0.33333333, 0.33333333, 0.5 ]), array([0.33333333, 0.33333333, 0. ]), array([0.5, 0. , 0.5]), array([0.5, 0. , 0. ])]
"""
cell = np.array(cell, dtype=float)
if lattice_type is None:
lattice_type = lepage(cell, angle_tolerance=angle_tolerance)
lattice_type = lattice_type.upper()
if lattice_variation is None:
lattice_variation = get_variation(
cell=cell, lattice_type=lattice_type, angle_tolerance=angle_tolerance
)
lattice_variation = lattice_variation.upper()
if C_matrix is None:
C_matrix = get_C_matrix(lattice_type)
else:
C_matrix = np.array(C_matrix, dtype=float)
if S_matrix is None:
S_matrix = get_S_matrix(
cell,
lattice_type,
length_tolerance=length_tolerance,
angle_tolerance=angle_tolerance,
)
else:
S_matrix = np.array(S_matrix, dtype=float)
if lattice_type in ["BCT", "ORCF", "ORCI", "ORCC", "RHL", "MCL", "MCLC"]:
conv_a, conv_b, conv_c, conv_alpha, conv_beta, conv_gamma = get_params(
get_conventional(cell, S_matrix=S_matrix, C_matrix=C_matrix)
)
if lattice_type == "CUB":
hs_points = _CUB_hs_points()
elif lattice_type == "FCC":
hs_points = _FCC_hs_points()
elif lattice_type == "BCC":
hs_points = _BCC_hs_points()
elif lattice_type == "TET":
hs_points = _TET_hs_points()
elif lattice_type == "BCT":
hs_points = _BCT_hs_points(lattice_variation, conv_a, conv_c)
elif lattice_type == "ORC":
hs_points = _ORC_hs_points()
elif lattice_type == "ORCF":
hs_points = _ORCF_hs_points(lattice_variation, conv_a, conv_b, conv_c)
elif lattice_type == "ORCI":
hs_points = _ORCI_hs_points(conv_a, conv_b, conv_c)
elif lattice_type == "ORCC":
hs_points = _ORCC_hs_points(conv_a, conv_b)
elif lattice_type == "HEX":
hs_points = _HEX_hs_points()
elif lattice_type == "RHL":
hs_points = _RHL_hs_points(lattice_variation, conv_alpha)
elif lattice_type == "MCL":
hs_points = _MCL_hs_points(conv_b, conv_c, conv_alpha)
elif lattice_type == "MCLC":
hs_points = _MCLC_hs_points(
lattice_variation, conv_a, conv_b, conv_c, conv_alpha
)
elif lattice_type == "TRI":
hs_points = _TRI_hs_points(lattice_variation)
names = []
labels = []
coordinates = []
for point in hs_points:
names.append(point)
# Compute relative coordinates with respect to the
# non-standardized primitive cell
# here hs_points[point] <- \tilde{g} and coordinates <- g
coordinates.append(np.linalg.inv(S_matrix).T @ hs_points[point])
# Post-process two edge cases
if point == "S" and lattice_type == "BCT":
labels.append("$\\Sigma$")
elif point == "S1" and lattice_type == "BCT":
labels.append("$\\Sigma_1$")
# General assignment
else:
labels.append(HS_PLOT_NAMES[point])
if not return_relative:
rcell = get_reciprocal(cell)
for i in range(len(coordinates)):
coordinates[i] = coordinates[i] @ rcell
return coordinates, names, labels, DEFAULT_K_PATHS[lattice_variation]
# Populate __all__ with objects defined in this file
__all__ = list(set(dir()) - old_dir)
# Remove all semi-private objects
__all__ = [i for i in __all__ if not i.startswith("_")]
del old_dir