Source code for pydft_qmmm.common.utils.lattice_utils

"""A module containing helper functions accessed by multiple classes.
"""
from __future__ import annotations

from typing import TYPE_CHECKING

import numpy as np

if TYPE_CHECKING:
    from numpy.typing import NDArray


[docs] def compute_least_mirror( i_vector: NDArray[np.float64], j_vector: NDArray[np.float64], box: NDArray[np.float64], ) -> NDArray[np.float64]: r"""Calculate the least mirror vector. Args: i_vector: The position vector (:math:`\mathrm{\mathring{A}}`). j_vector: The reference vector (:math:`\mathrm{\mathring{A}}`). box: The lattice vectors (:math:`\mathrm{\mathring{A}}`) of an arbitrary triclinic box. Returns: Returns the least mirror coordinates of i_vector with respect to j_vector given a set of lattice vectors from a periodic triclinic system. """ r_vector = i_vector - j_vector r_vector -= box[2] * np.floor(r_vector[2]/box[2][2] + 0.5) r_vector -= box[1] * np.floor(r_vector[1]/box[1][1] + 0.5) r_vector -= box[0] * np.floor(r_vector[0]/box[0][0] + 0.5) return r_vector
[docs] def compute_lattice_constants( box: NDArray[np.float64], ) -> tuple[float, ...]: r"""Calculate the length and angle constants from lattice vectors. Returns the lattice constants a, b, c, alpha, beta, and gamma using a set of box vectors for a periodic triclinic system. Args: box: The lattice vectors (:math:`\mathrm{\mathring{A}}`) of an arbitrary triclinic box. Returns: The characteristic lengths (:math:`\mathrm{\mathring{A}}`) and angles (:math:`\mathrm{\degree}`) of an arbitrary triclinic box. """ vec_a = box[:, 0] vec_b = box[:, 1] vec_c = box[:, 2] len_a = np.linalg.norm(vec_a) len_b = np.linalg.norm(vec_b) len_c = np.linalg.norm(vec_c) alpha = 180*np.arccos(np.dot(vec_b, vec_c)/len_b/len_c)/np.pi beta = 180*np.arccos(np.dot(vec_a, vec_c)/len_a/len_c)/np.pi gamma = 180*np.arccos(np.dot(vec_a, vec_b)/len_a/len_b)/np.pi return tuple(float(x) for x in (len_a, len_b, len_c, alpha, beta, gamma))
[docs] def compute_lattice_vectors( a: float, b: float, c: float, alpha: float, beta: float, gamma: float, ) -> NDArray[np.float]: r"""Calculate the lattice vectors from length and angle constants. Args: a: The first characteristic length (:math:`\mathrm{\mathring{A}}`) of an arbitrary triclinic box. b: The second characteristic length (:math:`\mathrm{\mathring{A}}`) of an arbitrary triclinic box. b: The third characteristic length (:math:`\mathrm{\mathring{A}}`) of an arbitrary triclinic box. alpha: The first characteristic angle (:math:`\mathrm{\degree}`) of an arbitrary triclinic box. beta: The second characteristic angle (:math:`\mathrm{\degree}`) of an arbitrary triclinic box. gamma: The third characteristic angle (:math:`\mathrm{\degree}`) of an arbitrary triclinic box. Returns: The lattice vectors (:math:`\mathrm{\mathring{A}}`) of an arbitrary triclinic box. """ alpha *= np.pi/180 beta *= np.pi/180 gamma *= np.pi/180 vec_a = np.array([[a], [0.], [0.]]) vec_b = np.array( [ [b*np.cos(gamma)], [b*np.sin(gamma)], [0.], ], ) vec_c = np.array( [ [c*np.cos(beta)], [c*(np.cos(alpha) - np.cos(beta)*np.cos(gamma))/np.sin(gamma)], [np.sqrt( c**2 - (c*np.cos(beta))**2 - (c*(np.cos(alpha) - np.cos(beta)*np.cos(gamma))/np.sin(gamma))**2 )], ], ) box = np.concatenate((vec_a, vec_b, vec_c), axis=1) box[box < 1e-6] = 0. box[:, 2] -= box[:, 1]*np.round(box[1,2]/box[1,1]) box[:, 2] -= box[:, 0]*np.round(box[0,2]/box[0,0]) box[:, 1] -= box[:, 0]*np.round(box[0,1]/box[0,0]) return box