Interface to Spglib#

Wulfric relies on spglib in all its functions that need any information about crystal's (cell + atoms) symmetry.

>>> import numpy as np
>>> cell = np.array([
...     [0.000000, 4.744935, 0.000000],
...     [3.553350, 0.000000, 0.000000],
...     [0.000000, 0.000000, 8.760497],
... ])
>>> atoms = {
...     "names": ["Cr1", "Br1", "S1", "Cr2", "Br2", "S2"],
...     "positions": np.array([
...         [0.000000, -0.500000,  0.882382],
...         [0.000000, 0.000000,  0.677322],
...         [-0.500000, -0.500000,  0.935321],
...         [0.500000, 0.000000,  0.117618],
...         [0.500000, 0.500000,  0.322678],
...         [0.000000, 0.000000,  0.064679],
...     ]),
... }

As wulfric is primarily designed as a set of well-defined functions, we face the problem of multiple calls to spglib, when more than one property is desired by user.

For example, if the user want to know both conventional and primitive cells, they can be computed as

>>> import wulfric
>>> conv_cell, conv_atoms = wulfric.crystal.get_conventional(cell, atoms)
>>> prim_cell, prim_atoms = wulfric.crystal.get_primitive(cell, atoms)

However, the conventional cell is required to compute the primitive cell, therefore function wulfric.crystal.get_conventional() is called twice - one time by the user and one time within the wulfric.crystal.get_primitive().

To solve this problem of executing the code twice (or at least of calling spglib twice, which might be expensive), we isolate the interaction of wulfric with spglib into a single function wulfric.get_spglib_data(). Then, the same code as above can be rewritten as

>>> import wulfric
>>> spglib_data = wulfric.get_spglib_data(cell, atoms)
>>> conv_cell, conv_atoms = wulfric.crystal.get_conventional(cell, atoms, spglib_data=spglib_data)
>>> prim_cell, prim_atoms = wulfric.crystal.get_primitive(cell, atoms, spglib_data=spglib_data)

In that way spglib is called only once and the data are re-used.

All functions that accept cell and atoms and rely on spglib have optional argument spglib_data that can be passed to them.

Moreover, spglib_data object by itself (which is an inventory of data stored as an instance of the dataclass wulfric.SpglibData) can be used by the user to access additional information derived from spglib:

# Fancy dot syntax
>>> spglib_data.space_group_number
59
>>> spglib_data.crystal_family
'o'
>>> spglib_data.centring_type
'P'

All properties of spglib_data are documented in wulfric.get_spglib_data().