Source code for pwtools.symmetry
import numpy as np
import spglib
from pwtools import atomic_data
from pwtools.crys import Structure
[docs]
def is_same_struct(st1, st2):
"""Test if two :class:`~pwtools.crys.Structure` instances are the same.
Use ``numpy.allclose`` for float-type properties.
"""
# maybe add early stopping (return if the first test fails), start with
# cheap tests, finally do spacegroup; only if we call this function very
# often and speed becomes an issue
ret = True
same = ['symbols', 'natoms']
close = ['volume', 'cryst_const', 'coords_frac']
for attr in same:
ret = ret and getattr(st1, attr) == getattr(st2, attr)
for attr in close:
ret = ret and np.allclose(getattr(st1, attr),
getattr(st2, attr))
ret = ret and spglib_get_spacegroup(st1) == spglib_get_spacegroup(st2)
return ret
[docs]
def spglib2struct(tup):
"""Transform returned tuple from various spglib functions to
:class:`~pwtools.crys.Structure`.
This applies to ``spglib.find_primitive()`` and probably some more. They
return a tuple `(cell,coords_frac,znucl)`. `znucl` is a list of integers
with atomic core charge (e.g. 1 for H), see
:data:`pwtools.atomic_data.numbers`.
Parameters
----------
tup : tuple (3,)
Return value from ``spglib.find_primitive()`` and maybe others.
Returns
-------
:class:`~pwtools.crys.Structure`
"""
assert isinstance(tup, tuple)
assert len(tup) == 3
symbols = [atomic_data.symbols[ii] for ii in tup[2]]
st = Structure(coords_frac=tup[1], cell=tup[0], symbols=symbols)
return st
[docs]
def struct2spglib(st):
"""Transform :class:`~pwtools.crys.Structure` to spglib input tuple (cell,
coords_frac, znucl).
"""
return st.get_spglib()
[docs]
def spglib_get_primitive(struct, **kwds):
"""Find primitive structure for given :class:`~pwtools.crys.Structure`.
If `struct` is irreducible (is already a primitive cell), we return None,
else a Structure.
Uses spglib.
Parameters
----------
struct : Structure
**kwds : keywords
passed to ``spglib.find_primitive()``, e.g. `symprec` and
`angle_tolerance` last time I checked
Returns
-------
Structure or None
Notes
-----
spglib used to return (None,None,None) if no primitive cell can be found,
i.e. the given input Structure cannot be reduced, which can occur if (a) a
given Structure is already a primitive cell or (b) any other reason like a
too small value of `symprec`. Now [py]spglib >= 1.8.x seems to always
return data instead. We use :func:`is_same_struct` to determine if the
struct is irreducible. In that case we return None in order to keep the API
unchanged.
Also note that a primitive cell (e.g. with 2 atoms) can have a number of
different realizations. Therefore, you may not always get the primitive
cell which you would expect or get from other tools like Wien2K's sgroup.
Only things like `natoms` and the spacegroup can be safely compared.
"""
candidate = spglib2struct(spglib.find_primitive(struct.get_spglib(),
**kwds))
if is_same_struct(candidate, struct):
return None
else:
return candidate
[docs]
def spglib_get_spacegroup(struct, **kwds):
"""Find spacegroup for given Structure.
Uses spglib.
Parameters
----------
struct : Structure
**kwds : keywords
passed to ``spglib.get_spacegroup()``, e.g. `symprec` and
`angle_tolerance` last time I checked
Returns
-------
spg_num, spg_sym
spg_num : int
space group number
spg_sym : str
space group symbol
Notes
-----
The used function ``spglib.get_spacegroup()`` returns a string, which we
split into `spg_num` and `spg_sym`.
"""
ret = spglib.get_spacegroup(struct.get_spglib(), **kwds)
spl = ret.split()
spg_sym = spl[0]
spg_num = spl[1]
spg_num = spg_num.replace('(','').replace(')','')
spg_num = int(spg_num)
return spg_num,spg_sym