# RAD-tools - Sandbox (mainly condense matter plotting).
# Copyright (C) 2022-2024 Andrey Rybakov
#
# e-mail: anry@uv.es, web: rad-tools.org
#
# 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/>.
import numpy as np
import radtools.crystal.cell as Cell
from radtools.crystal.constants import (
ABS_TOL,
ABS_TOL_ANGLE,
REL_TOL,
TRANSFORM_TO_CONVENTIONAL,
)
from radtools.numerical import compare_numerically
__all__ = [
"standardize_cell",
"CUB_standardize_cell",
"FCC_standardize_cell",
"BCC_standardize_cell",
"TET_standardize_cell",
"BCT_standardize_cell",
"ORC_standardize_cell",
"ORCF_standardize_cell",
"ORCI_standardize_cell",
"ORCC_standardize_cell",
"HEX_standardize_cell",
"RHL_standardize_cell",
"MCL_standardize_cell",
"MCLC_standardize_cell",
"TRI_standardize_cell",
]
# Main routine, serves as interface to all of them
[docs]
def standardize_cell(cell, correct_lattice_type, rtol=REL_TOL, atol=ABS_TOL):
r"""
Analyse arbitrary cell and redefine it
if required to ensure the unique choice of lattice vectors.
See :ref:`docs for each Bravais lattice <library_bravais-lattices>` for the details.
Parameters
----------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
rtol : float, default ``REL_TOL``
Relative tolerance for numerical comparison.
atol : float, default ``ABS_TOL``
Absolute tolerance for numerical comparison.
correct_lattice_type : str
Correct lattice type.
Returns
-------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
"""
cell = np.array(cell)
functions = {
"CUB": CUB_standardize_cell,
"FCC": FCC_standardize_cell,
"BCC": BCC_standardize_cell,
"TET": TET_standardize_cell,
"BCT": BCT_standardize_cell,
"ORC": ORC_standardize_cell,
"ORCF": ORCF_standardize_cell,
"ORCI": ORCI_standardize_cell,
"ORCC": ORCC_standardize_cell,
"HEX": HEX_standardize_cell,
"RHL": RHL_standardize_cell,
"MCL": MCL_standardize_cell,
"MCLC": MCLC_standardize_cell,
"TRI": TRI_standardize_cell,
}
return functions[correct_lattice_type](cell, rtol=rtol, atol=atol)
[docs]
def CUB_standardize_cell(cell, rtol=REL_TOL, atol=ABS_TOL):
r"""
Analyse arbitrary cell and redefine vectors if required to satisfy the CUB lattice conditions.
See :ref:`guide_cub` for the details.
Parameters
----------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
rtol : float, default ``REL_TOL``
Relative tolerance for numerical comparison.
Ignored here, but preserved for the unification of input.
atol : float, default ``ABS_TOL``
Absolute tolerance for numerical comparison.
Ignored here, but preserved for the unification of input.
Returns
-------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
"""
return np.array(cell)
[docs]
def FCC_standardize_cell(cell, rtol=REL_TOL, atol=ABS_TOL):
r"""
Analyse arbitrary cell and redefine vectors if required to satisfy the FCC lattice conditions.
See :ref:`guide_fcc` for the details.
Parameters
----------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
rtol : float, default ``REL_TOL``
Relative tolerance for numerical comparison.
Ignored here, but preserved for the unification of input.
atol : float, default ``ABS_TOL``
Absolute tolerance for numerical comparison.
Ignored here, but preserved for the unification of input.
Returns
-------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
"""
return np.array(cell)
[docs]
def BCC_standardize_cell(cell, rtol=REL_TOL, atol=ABS_TOL):
r"""
Analyse arbitrary cell and redefine vectors if required to satisfy the BCC lattice conditions.
See :ref:`guide_bcc` for the details.
Parameters
----------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
rtol : float, default ``REL_TOL``
Relative tolerance for numerical comparison.
Ignored here, but preserved for the unification of input.
atol : float, default ``ABS_TOL``
Absolute tolerance for numerical comparison.
Ignored here, but preserved for the unification of input.
Returns
-------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
"""
return np.array(cell)
[docs]
def TET_standardize_cell(cell, rtol=REL_TOL, atol=ABS_TOL):
r"""
Analyse arbitrary cell and redefine vectors if required to satisfy the TET lattice conditions.
See :ref:`guide_tet` for the details.
Parameters
----------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
rtol : float, default ``REL_TOL``
Relative tolerance for numerical comparison.
atol : float, default ``ABS_TOL``
Absolute tolerance for numerical comparison.
Returns
-------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
"""
cell = np.array(cell)
a, b, c, alpha, beta, gamma = Cell.params(cell)
if compare_numerically(a, "==", c, rtol=rtol, atol=atol):
cell = [cell[2], cell[0], cell[1]]
elif compare_numerically(b, "==", c, rtol=rtol, atol=atol):
cell = [cell[1], cell[2], cell[0]]
return cell
[docs]
def BCT_standardize_cell(cell, rtol=REL_TOL, atol=ABS_TOL):
r"""
Analyse arbitrary cell and redefine vectors if required to satisfy the BCT lattice conditions.
See :ref:`guide_bct` for the details.
Parameters
----------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
rtol : float, default ``REL_TOL``
Relative tolerance for numerical comparison.
atol : float, default ``ABS_TOL``
Absolute tolerance for numerical comparison.
Returns
-------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
"""
cell = np.array(cell)
a, b, c, alpha, beta, gamma = Cell.params(TRANSFORM_TO_CONVENTIONAL["BCT"] @ cell)
if compare_numerically(a, "==", c, rtol=rtol, atol=atol):
cell = [cell[2], cell[0], cell[1]]
elif compare_numerically(b, "==", c, rtol=rtol, atol=atol):
cell = [cell[1], cell[2], cell[0]]
return cell
[docs]
def ORC_standardize_cell(cell, rtol=REL_TOL, atol=ABS_TOL):
r"""
Analyse arbitrary cell and redefine vectors if required to satisfy the ORC lattice conditions.
See :ref:`guide_orc` for the details.
Parameters
----------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
rtol : float, default ``REL_TOL``
Relative tolerance for numerical comparison.
atol : float, default ``ABS_TOL``
Absolute tolerance for numerical comparison.
Returns
-------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
"""
cell = np.array(cell)
a, b, c, alpha, beta, gamma = Cell.params(cell)
if compare_numerically(a, ">", b, rtol=rtol, atol=atol):
# minus preserves the handedness of the cell
cell = [cell[1], cell[0], -cell[2]]
a, b = b, a
if compare_numerically(a, ">", c, rtol=rtol, atol=atol):
cell = [cell[2], cell[0], cell[1]]
elif compare_numerically(b, ">", c, rtol=rtol, atol=atol):
# minus preserves the handedness of the cell
cell = [cell[0], -cell[2], cell[1]]
return cell
[docs]
def ORCF_standardize_cell(cell, rtol=REL_TOL, atol=ABS_TOL):
r"""
Analyse arbitrary cell and redefine vectors if required to satisfy the ORCF lattice conditions.
See :ref:`guide_orcf` for the details.
Parameters
----------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
rtol : float, default ``REL_TOL``
Relative tolerance for numerical comparison.
atol : float, default ``ABS_TOL``
Absolute tolerance for numerical comparison.
Returns
-------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
"""
cell = np.array(cell)
a, b, c, alpha, beta, gamma = Cell.params(cell)
if compare_numerically(a, "<", b, rtol=rtol, atol=atol):
# minus preserves the handedness of the cell
cell = [cell[1], cell[0], -cell[2]]
a, b = b, a
if compare_numerically(a, "<", c, rtol=rtol, atol=atol):
cell = [cell[2], cell[0], cell[1]]
elif compare_numerically(b, "<", c, rtol=rtol, atol=atol):
# minus preserves the handedness of the cell
cell = [cell[0], -cell[2], cell[1]]
return cell
[docs]
def ORCI_standardize_cell(cell, rtol=REL_TOL, atol=ABS_TOL):
r"""
Analyse arbitrary cell and redefine vectors if required to satisfy the ORCI lattice conditions.
See :ref:`guide_orci` for the details.
Parameters
----------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
rtol : float, default ``REL_TOL``
Relative tolerance for numerical comparison.
atol : float, default ``ABS_TOL``
Absolute tolerance for numerical comparison.
Returns
-------
cell : (3,3) :numpy:`ndarray`
Primitiv4e unit cell.
"""
cell = np.array(cell)
a, b, c, alpha, beta, gamma = Cell.params(TRANSFORM_TO_CONVENTIONAL["ORCI"] @ cell)
if compare_numerically(a, ">", b, rtol=rtol, atol=atol):
# minus preserves the handedness of the cell
cell = [cell[1], cell[0], -cell[2]]
a, b = b, a
if compare_numerically(a, ">", c, rtol=rtol, atol=atol):
cell = [cell[2], cell[0], cell[1]]
elif compare_numerically(b, ">", c, rtol=rtol, atol=atol):
# minus preserves the handedness of the cell
cell = [cell[0], -cell[2], cell[1]]
return cell
[docs]
def ORCC_standardize_cell(cell, rtol=REL_TOL, atol=ABS_TOL):
r"""
Analyse arbitrary cell and redefine vectors if required to satisfy the ORCC lattice conditions.
See :ref:`guide_orcc` for the details.
Parameters
----------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
rtol : float, default ``REL_TOL``
Relative tolerance for numerical comparison.
atol : float, default ``ABS_TOL``
Absolute tolerance for numerical comparison.
Returns
-------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
"""
a, b, c, alpha, beta, gamma = Cell.params(cell)
if (
compare_numerically(alpha, "==", 90.0, eps=ABS_TOL_ANGLE)
and compare_numerically(beta, "==", 90.0, eps=ABS_TOL_ANGLE)
and compare_numerically(gamma, "==", 90.0, eps=ABS_TOL_ANGLE)
):
return TET_standardize_cell(cell, rtol=rtol, atol=atol)
# next two check are based on the angle, because the length comparison is not enough
# a == c (beta != 90)
if compare_numerically(beta, "!=", 90.0, eps=ABS_TOL_ANGLE):
cell = [cell[2], cell[0], cell[1]]
# b = c (alpha != 90)
elif compare_numerically(alpha, "!=", 90.0, eps=ABS_TOL_ANGLE):
cell = [cell[1], cell[2], cell[0]]
a, b, c, alpha, beta, gamma = Cell.params(cell)
cell = np.array(cell)
a, b, c, alpha, beta, gamma = Cell.params(TRANSFORM_TO_CONVENTIONAL["ORCC"] @ cell)
if compare_numerically(a, ">", b, rtol=rtol, atol=atol):
# minus preserves the handedness of the cell
cell = [-cell[1], cell[0], cell[2]]
return cell
[docs]
def HEX_standardize_cell(cell, rtol=REL_TOL, atol=ABS_TOL):
r"""
Analyse arbitrary cell and redefine vectors if required to satisfy the HEX lattice conditions.
See :ref:`guide_hex` for the details.
Parameters
----------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
rtol : float, default ``REL_TOL``
Relative tolerance for numerical comparison.
Ignored here, but preserved for the unification of input.
atol : float, default ``ABS_TOL``
Absolute tolerance for numerical comparison.
Ignored here, but preserved for the unification of input.
Returns
-------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
"""
cell = np.array(cell)
a, b, c, alpha, beta, gamma = Cell.params(cell)
# a == c
if compare_numerically(beta, "==", 120.0, eps=ABS_TOL_ANGLE):
cell = [cell[2], cell[0], cell[1]]
# b = c
elif compare_numerically(alpha, "==", 120.0, eps=ABS_TOL_ANGLE):
cell = [cell[1], cell[2], cell[0]]
return cell
[docs]
def RHL_standardize_cell(cell, rtol=REL_TOL, atol=ABS_TOL):
r"""
Analyse arbitrary cell and redefine vectors if required to satisfy the RHL lattice conditions.
See :ref:`guide_rhl` for the details.
Parameters
----------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
rtol : float, default ``REL_TOL``
Relative tolerance for numerical comparison.
Ignored here, but preserved for the unification of input.
atol : float, default ``ABS_TOL``
Absolute tolerance for numerical comparison.
Ignored here, but preserved for the unification of input.
Returns
-------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
"""
return np.array(cell)
[docs]
def MCL_standardize_cell(cell, rtol=REL_TOL, atol=ABS_TOL):
r"""
Analyse arbitrary cell and redefine vectors if required to satisfy the MCL lattice conditions.
See :ref:`guide_mcl` for the details.
Parameters
----------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
rtol : float, default ``REL_TOL``
Relative tolerance for numerical comparison.
atol : float, default ``ABS_TOL``
Absolute tolerance for numerical comparison.
Returns
-------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
"""
cell = np.array(cell)
a, b, c, alpha, beta, gamma = Cell.params(cell)
# beta != 90
if compare_numerically(beta, "!=", 90.0, eps=ABS_TOL_ANGLE):
cell = [cell[1], cell[2], cell[0]]
# gamma != 90
elif compare_numerically(gamma, "!=", 90.0, eps=ABS_TOL_ANGLE):
cell = [cell[2], cell[0], cell[1]]
a, b, c, alpha, beta, gamma = Cell.params(cell)
# alpha > 90
if compare_numerically(alpha, ">", 90.0, eps=ABS_TOL_ANGLE):
cell = [cell[0], cell[2], -cell[1]]
a, b, c, alpha, beta, gamma = Cell.params(cell)
# b > c
if compare_numerically(b, ">", c, rtol=rtol, atol=atol):
cell = [-cell[0], cell[2], cell[1]]
return cell
[docs]
def MCLC_standardize_cell(cell, rtol=REL_TOL, atol=ABS_TOL):
r"""
Analyse arbitrary cell and redefine vectors if required to satisfy the MCLC lattice conditions.
See :ref:`guide_mclc` for the details.
Parameters
----------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
rtol : float, default ``REL_TOL``
Relative tolerance for numerical comparison.
atol : float, default ``ABS_TOL``
Absolute tolerance for numerical comparison.
Returns
-------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
"""
a, b, c, alpha, beta, gamma = Cell.params(cell)
# a == c
if compare_numerically(a, "==", c, rtol=rtol, atol=atol):
cell = [cell[2], cell[0], cell[1]]
# b == c
elif compare_numerically(b, "==", c, rtol=rtol, atol=atol):
cell = [cell[1], cell[2], cell[0]]
cell = np.array(cell)
a, b, c, alpha, beta, gamma = Cell.params(TRANSFORM_TO_CONVENTIONAL["MCLC"] @ cell)
# alpha > 90
if compare_numerically(alpha, ">", 90.0, eps=ABS_TOL_ANGLE):
cell = [cell[0], cell[2], -cell[1]]
cell = np.array(cell)
a, b, c, alpha, beta, gamma = Cell.params(TRANSFORM_TO_CONVENTIONAL["MCLC"] @ cell)
# b > c
if compare_numerically(b, ">", c, rtol=rtol, atol=atol):
cell = [-cell[0], cell[2], cell[1]]
return cell
[docs]
def TRI_standardize_cell(cell, rtol=REL_TOL, atol=ABS_TOL):
r"""
Analyse arbitrary cell and redefine vectors if required to satisfy the TRI lattice conditions.
See :ref:`guide_tri` for the details.
Parameters
----------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
rtol : float, default ``REL_TOL``
Relative tolerance for numerical comparison.
atol : float, default ``ABS_TOL``
Absolute tolerance for numerical comparison.
Returns
-------
cell : (3,3) :numpy:`ndarray`
Primitive unit cell.
"""
# Compute reciprocal cell
rcell = Cell.reciprocal(cell)
a, b, c, alpha, beta, gamma = Cell.params(rcell)
if (
compare_numerically(alpha, "==", 90.0)
or compare_numerically(beta, "==", 90.0)
or compare_numerically(gamma, "==", 90.0)
):
if compare_numerically(alpha, "==", 90.0):
rcell = [rcell[1], rcell[2], rcell[0]]
elif compare_numerically(beta, "==", 90.0):
rcell = [rcell[2], rcell[0], rcell[1]]
a, b, c, alpha, beta, gamma = Cell.params(rcell)
if (
compare_numerically(alpha, ">", 90.0)
and compare_numerically(beta, "<", 90.0)
or compare_numerically(alpha, "<", 90.0)
and compare_numerically(beta, ">", 90.0)
):
rcell = [rcell[1], -rcell[0], rcell[2]]
else:
if (
compare_numerically(alpha, ">", 90.0)
and compare_numerically(beta, ">", 90.0)
and compare_numerically(gamma, "<", 90.0)
):
rcell = [-rcell[0], -rcell[1], rcell[2]]
elif (
compare_numerically(alpha, ">", 90.0)
and compare_numerically(beta, "<", 90.0)
and compare_numerically(gamma, ">", 90.0)
):
rcell = [-rcell[0], rcell[1], -rcell[2]]
elif (
compare_numerically(alpha, ">", 90.0)
and compare_numerically(beta, "<", 90.0)
and compare_numerically(gamma, "<", 90.0)
):
rcell = [rcell[0], -rcell[1], -rcell[2]]
elif (
compare_numerically(alpha, "<", 90.0)
and compare_numerically(beta, ">", 90.0)
and compare_numerically(gamma, ">", 90.0)
):
rcell = [rcell[0], -rcell[1], -rcell[2]]
elif (
compare_numerically(alpha, "<", 90.0)
and compare_numerically(beta, ">", 90.0)
and compare_numerically(gamma, "<", 90.0)
):
rcell = [-rcell[0], rcell[1], -rcell[2]]
elif (
compare_numerically(alpha, "<", 90.0)
and compare_numerically(beta, "<", 90.0)
and compare_numerically(gamma, ">", 90.0)
):
rcell = [-rcell[0], -rcell[1], rcell[2]]
a, b, c, alpha, beta, gamma = Cell.params(rcell)
if compare_numerically(min(alpha, beta, gamma), ">", 90.0):
if compare_numerically(alpha, "<", beta) and compare_numerically(
alpha, "<", gamma
):
rcell = [rcell[1], rcell[2], rcell[0]]
elif compare_numerically(beta, "<", alpha) and compare_numerically(
beta, "<", gamma
):
rcell = [rcell[2], rcell[0], rcell[1]]
if compare_numerically(max(alpha, beta, gamma), "<", 90.0):
if compare_numerically(alpha, ">", beta) and compare_numerically(
alpha, ">", gamma
):
rcell = [rcell[1], rcell[2], rcell[0]]
elif compare_numerically(beta, ">", alpha) and compare_numerically(
beta, ">", gamma
):
rcell = [rcell[2], rcell[0], rcell[1]]
# Recompute back to the real-space cell
return Cell.reciprocal(rcell)
if __name__ == "__main__":
import numpy as np
cell = np.array(
[
[0.0, 0.0, 0.5],
[0.42284605, -0.26686416, 0.0],
[-0.41862494, 0.27343815, 0.0],
]
)
scell = ORCC_standardize_cell(cell, rtol=REL_TOL, atol=ABS_TOL)
print(scell)