Source code for spglib._internal

"""These APIs are for the internal usage and development of Spglib itself."""
# Copyright (C) 2015 Atsushi Togo
# This file is part of spglib.
# SPDX-License-Identifier: BSD-3-Clause

from __future__ import annotations

import numpy as np
from numpy._typing import ArrayLike

from . import _spglib
from .error import _set_no_error, _set_or_throw_error
from .spg import SpgCell, SpglibDataset
from .utils import _expand_cell

__all__ = [
    "get_pointgroup",
    "get_symmetry_layerdataset",
    "get_layergroup",
    "get_grid_point_from_address",
    "get_stabilized_reciprocal_mesh",
    "get_grid_points_by_rotations",
    "get_BZ_grid_points_by_rotations",
    "relocate_BZ_grid_address",
]


[docs] def get_pointgroup(rotations: ArrayLike[np.intc]) -> tuple[str, int, np.ndarray] | None: """Return point group in international table symbol and number. The symbols are mapped to the numbers as follows: 1 "1 " 2 "-1 " 3 "2 " 4 "m " 5 "2/m " 6 "222 " 7 "mm2 " 8 "mmm " 9 "4 " 10 "-4 " 11 "4/m " 12 "422 " 13 "4mm " 14 "-42m " 15 "4/mmm" 16 "3 " 17 "-3 " 18 "32 " 19 "3m " 20 "-3m " 21 "6 " 22 "-6 " 23 "6/m " 24 "622 " 25 "6mm " 26 "-62m " 27 "6/mmm" 28 "23 " 29 "m-3 " 30 "432 " 31 "-43m " 32 "m-3m " """ _set_no_error() # (symbol, pointgroup_number, transformation_matrix) try: return _spglib.pointgroup(np.array(rotations, dtype="intc", order="C")) except Exception as exc: _set_or_throw_error(exc) return None
[docs] def get_symmetry_layerdataset( cell: SpgCell, aperiodic_dir: int = 2, symprec: float = 1e-5, _throw: bool = False, ) -> SpglibDataset | None: """TODO: Add comments.""" _set_no_error(_throw) lattice, positions, numbers, _ = _expand_cell(cell) try: spg_ds = _spglib.layer_dataset( lattice, positions, numbers, int(aperiodic_dir), float(symprec), ) except Exception as exc: _set_or_throw_error(exc, _throw) return None return SpglibDataset(**spg_ds)
[docs] def get_layergroup( cell: SpgCell, aperiodic_dir: int = 2, symprec: float = 1e-5, ) -> SpglibDataset | None: """Return layer group in .... If it fails, None is returned. """ _set_no_error() try: return get_symmetry_layerdataset( cell, aperiodic_dir=aperiodic_dir, symprec=symprec, _throw=True, ) except Exception as exc: _set_or_throw_error(exc) return None
[docs] def get_grid_point_from_address( grid_address: ArrayLike[np.intc], mesh: ArrayLike[np.intc], ) -> int | None: """Return grid point index by translating grid address.""" _set_no_error() try: return _spglib.grid_point_from_address( np.array(grid_address, dtype="intc"), np.array(mesh, dtype="intc"), ) except Exception as exc: _set_or_throw_error(exc) return None
[docs] def get_stabilized_reciprocal_mesh( mesh: ArrayLike[np.intc], rotations: ArrayLike[np.intc], is_shift: ArrayLike[np.intc] | None = None, is_time_reversal: bool = True, qpoints: ArrayLike[np.double] | None = None, is_dense: bool = False, ) -> tuple[np.ndarray, np.ndarray] | None: """Return k-point map to the irreducible k-points and k-point grid points. The symmetry is searched from the input rotation matrices in real space. Parameters ---------- mesh : array_like Uniform sampling mesh numbers. dtype='intc', shape=(3,) rotations : array_like Rotation matrices with respect to real space basis vectors. dtype='intc', shape=(rotations, 3) is_shift : array_like [0, 0, 0] gives Gamma center mesh and value 1 gives half mesh shift. dtype='intc', shape=(3,) is_time_reversal : bool Time reversal symmetry is included or not. qpoints : array_like q-points used as stabilizer(s) given in reciprocal space with respect to reciprocal basis vectors. dtype='double', shape=(qpoints ,3) or (3,) is_dense : bool, optional grid_mapping_table is returned with dtype='uintp' if True. Otherwise its dtype='intc'. Default is False. Returns ------- grid_mapping_table : ndarray Grid point mapping table to ir-gird-points. dtype='intc', shape=(prod(mesh),) grid_address : ndarray Address of all grid points. Each address is given by three unsigned integers. dtype='intc', shape=(prod(mesh), 3) """ _set_no_error() if is_dense: dtype = "uintp" else: dtype = "intc" mapping_table = np.zeros(np.prod(mesh), dtype=dtype) grid_address = np.zeros((np.prod(mesh), 3), dtype="intc") if is_shift is None: is_shift = [0, 0, 0] if qpoints is None: qpoints = np.array([[0, 0, 0]], dtype="double", order="C") else: qpoints = np.array(qpoints, dtype="double", order="C") if qpoints.shape == (3,): qpoints = np.array([qpoints], dtype="double", order="C") try: _spglib.stabilized_reciprocal_mesh( grid_address, mapping_table, np.array(mesh, dtype="intc"), np.array(is_shift, dtype="intc"), int(is_time_reversal * 1), np.array(rotations, dtype="intc", order="C"), qpoints, ) except Exception as exc: _set_or_throw_error(exc) return None return mapping_table, grid_address
[docs] def get_grid_points_by_rotations( address_orig: ArrayLike[np.intc], reciprocal_rotations: ArrayLike[np.intc], mesh: ArrayLike[np.intc], is_shift: ArrayLike[np.intc] | None = None, is_dense: bool = False, ) -> np.ndarray | None: """Return grid points obtained after rotating input grid address. Parameters ---------- address_orig : array_like Grid point address to be rotated. dtype='intc', shape=(3,) reciprocal_rotations : array_like Rotation matrices {R} with respect to reciprocal basis vectors. Defined by q'=Rq. dtype='intc', shape=(rotations, 3, 3) mesh : array_like dtype='intc', shape=(3,) is_shift : array_like, optional With (1) or without (0) half grid shifts with respect to grid intervals sampled along reciprocal basis vectors. Default is None, which gives [0, 0, 0]. is_dense : bool, optional rot_grid_points is returned with dtype='uintp' if True. Otherwise its dtype='intc'. Default is False. Returns ------- rot_grid_points : ndarray Grid points obtained after rotating input grid address dtype='intc' or 'uintp', shape=(rotations,) """ _set_no_error() if is_shift is None: _is_shift = np.zeros(3, dtype="intc") else: _is_shift = np.array(is_shift, dtype="intc") rot_grid_points = np.zeros(len(reciprocal_rotations), dtype="uintp") try: _spglib.grid_points_by_rotations( rot_grid_points, np.array(address_orig, dtype="intc"), np.array(reciprocal_rotations, dtype="intc", order="C"), np.array(mesh, dtype="intc"), _is_shift, ) except Exception as exc: _set_or_throw_error(exc) return None if is_dense: return rot_grid_points else: return np.array(rot_grid_points, dtype="intc")
[docs] def get_BZ_grid_points_by_rotations( address_orig: ArrayLike[np.intc], reciprocal_rotations: ArrayLike[np.intc], mesh: ArrayLike[np.intc], bz_map: ArrayLike[np.uintp], is_shift: ArrayLike[np.intc] | None = None, is_dense: bool = False, ) -> np.ndarray | None: """Return grid points obtained after rotating input grid address. Parameters ---------- address_orig : array_like Grid point address to be rotated. dtype='intc', shape=(3,) reciprocal_rotations : array_like Rotation matrices {R} with respect to reciprocal basis vectors. Defined by q'=Rq. dtype='intc', shape=(rotations, 3, 3) mesh : array_like dtype='intc', shape=(3,) bz_map : array_like TODO is_shift : array_like, optional With (1) or without (0) half grid shifts with respect to grid intervals sampled along reciprocal basis vectors. Default is None, which gives [0, 0, 0]. is_dense : bool, optional rot_grid_points is returned with dtype='uintp' if True. Otherwise its dtype='intc'. Default is False. Returns ------- rot_grid_points : ndarray Grid points obtained after rotating input grid address dtype='intc' or 'uintp', shape=(rotations,) """ _set_no_error() if is_shift is None: _is_shift = np.zeros(3, dtype="intc") else: _is_shift = np.array(is_shift, dtype="intc") if bz_map.dtype == "uintp" and bz_map.flags.c_contiguous: _bz_map = bz_map else: _bz_map = np.array(bz_map, dtype="uintp") rot_grid_points = np.zeros(len(reciprocal_rotations), dtype="uintp") try: _spglib.BZ_grid_points_by_rotations( rot_grid_points, np.array(address_orig, dtype="intc"), np.array(reciprocal_rotations, dtype="intc", order="C"), np.array(mesh, dtype="intc"), _is_shift, _bz_map, ) except Exception as exc: _set_or_throw_error(exc) return None if is_dense: return rot_grid_points else: return np.array(rot_grid_points, dtype="intc")
[docs] def relocate_BZ_grid_address( grid_address: ArrayLike[np.intc], mesh: ArrayLike[np.intc], reciprocal_lattice: ArrayLike[np.double], # column vectors is_shift: ArrayLike[np.intc] | None = None, is_dense: bool = False, ) -> tuple[np.ndarray, np.ndarray] | None: """Grid addresses are relocated to be inside first Brillouin zone. Number of ir-grid-points inside Brillouin zone is returned. It is assumed that the following arrays have the shapes of Note that the shape of grid_address is (prod(mesh), 3) and the addresses in grid_address are arranged to be in parallelepiped made of reciprocal basis vectors. The addresses in bz_grid_address are inside the first Brillouin zone or on its surface. Each address in grid_address is mapped to one of those in bz_grid_address by a reciprocal lattice vector (including zero vector) with keeping element order. For those inside first Brillouin zone, the mapping is one-to-one. For those on the first Brillouin zone surface, more than one addresses in bz_grid_address that are equivalent by the reciprocal lattice translations are mapped to one address in grid_address. In this case, those grid points except for one of them are appended to the tail of this array, for which bz_grid_address has the following data storing: .. code-block:: |------------------array size of bz_grid_address-------------------------| |--those equivalent to grid_address--|--those on surface except for one--| |-----array size of grid_address-----| Number of grid points stored in bz_grid_address is returned. bz_map is used to recover grid point index expanded to include BZ surface from grid address. The grid point indices are mapped to (mesh[0] * 2) x (mesh[1] * 2) x (mesh[2] * 2) space (bz_map). """ _set_no_error() if is_shift is None: _is_shift = np.zeros(3, dtype="intc") else: _is_shift = np.array(is_shift, dtype="intc") bz_grid_address = np.zeros((np.prod(np.add(mesh, 1)), 3), dtype="intc") bz_map = np.zeros(np.prod(np.multiply(mesh, 2)), dtype="uintp") try: num_bz_ir = _spglib.BZ_grid_address( bz_grid_address, bz_map, grid_address, np.array(mesh, dtype="intc"), np.array(reciprocal_lattice, dtype="double", order="C"), _is_shift, ) except Exception as exc: _set_or_throw_error(exc) return None if is_dense: return bz_grid_address[:num_bz_ir], bz_map else: return bz_grid_address[:num_bz_ir], np.array(bz_map, dtype="intc")