Translation Group
When performing calculations, we need to use various properties of both the infinite translation group (Lattice) and the finite translation group (LatticeFTG). The infinite translation group contains the lattice vectors, and computes various information from them (e.g. vector lengths and angles, volume, reciprocal lattice, etc). The FTG is naturally characterized by a supercell \(\hat{S}_{BZ}\), and LatticeFTG derives from Lattice. Finding all translation points within a given supercell is a key task. Similarly, another key task is finding all the k-points commensurate with a given FTG, but this is currently execute in the separate kpoints class.
Additionally, we will need to find the smallest supercell that will accomodate a given set of \(\bm q\)-points.
Lattice
Here introduce the basic functionality of the Lattice
class using the
face center cubic as a demonstration:
>>> import numpy as np
>>> from principia_materia.translation_group import Lattice
>>> fcc_vec = 0.5*(np.ones((3,3))-np.identity(3))
>>> lattice = Lattice(vec=fcc_vec)
>>> lattice.vec
array([[0. , 0.5, 0.5],
[0.5, 0. , 0.5],
[0.5, 0.5, 0. ]])
With the above Lattice class object, we can compute various properties of the lattice.
>>> print("volume:", lattice.vol)
volume: 0.25
>>> print("lengths of the lattice vectors:", lattice.abc)
lengths of the lattice vectors: [0.70710678 0.70710678 0.70710678]
>>> print("angles between the lattice vectors:", lattice.abg)
angles between the lattice vectors: [60. 60. 60.]
There is a reciprocal lattice associated with the lattice, which is also constructed in this class, and we follow the convention \(\bm a_i\cdot \bm b_j =2\pi \delta_{ij} \), where \(\bm a_i\) is a real space vector and \(\bm b_j\) is a reciprocal space vector.
>>> print(lattice.rvec)
[[-6.28318531 6.28318531 6.28318531]
[ 6.28318531 -6.28318531 6.28318531]
[ 6.28318531 6.28318531 -6.28318531]]
>>> print("volume of reciprocal lattice:", lattice.rvol)
volume of reciprocal lattice: 992.200854
>>> print("lengths of the reciprocal lattice vectors:", lattice.rabc)
lengths of the reciprocal lattice vectors: [10.88279619 10.88279619 10.88279619]
>>> print("angles between the reciprocal lattice vectors:", lattice.rabg)
angles between the reciprocal lattice vectors: [109.47122063 109.47122063 109.47122063]
Additionally, the Lattice class allows us to perform various operation to a lattice, for example applying an axial strain:
>>> strain = [0.02, 0, 0] # axial strain along x
>>> example_lattice = lattice.copy()
>>> example_lattice.axial_strain(strain)
>>> print(example_lattice.vec)
[[0. 0.5 0.5 ]
[0.51 0. 0.5 ]
[0.51 0.5 0. ]]
LatticeFTG
LatticeFTG is the class which handles finite translation groups, and the key method of the class finds all tranlsation points (i.e. t-points) contained within the supercell. While this result can be obtained from a simple Cartesian product in the case of a diagonal supercell, non-diagonal supercells are more subtle. Non-diagonal supercells are important in the context of lattice dynamic, because it is always preferrable to work with the smallest number of irreducible derivatives which yield a smooth interpolation, and non-diagonal supercells yield multiplicities which cannot be obtained solely using diagonal supercells. Therefore, it is important to have a robust algorithm to find all lattice points within an arbitrary supercell, and our algorithm is detailed in Appendix D in PRB 100 014303. The same algorithm can be reversed to compute the indices of the lattice points at \(\mathcal{O}(1)\) time complexity, removing the need to create a hash function.
Below is a simple example which is easily recognizable: a fcc lattice with a supercell that yields the conventional cubic cell.
>>> import numpy as np
>>> from principia_materia.translation_group import LatticeFTG
>>> fcc_vec = 0.5*(np.ones((3,3))-np.identity(3))
>>> S_bz = np.ones((3, 3)) - 2 * np.identity(3)
>>> lattice_ftg = LatticeFTG(fcc_vec,S_bz)
>>> lattice_ftg.vec
array([[0. , 0.5, 0.5],
[0.5, 0. , 0.5],
[0.5, 0.5, 0. ]])
>>> lattice_ftg.supa
array([[-1, 1, 1],
[ 1, -1, 1],
[ 1, 1, -1]])
>>> lattice_ftg.tpoints
array([[0, 0, 0],
[0, 0, 1],
[0, 1, 0],
[1, 0, 0]])
>>> lattice_ftg.get_index(lattice_ftg.tpoints)
array([0, 1, 2, 3])
>>> lattice_ftg.supa_vec
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
>>> lattice_ftg.supa_tpoints
array([[ 0 , 0 , 0 ],
[1/2, 1/2, 0 ],
[1/2, 0 , 1/2],
[ 0 , 1/2, 1/2]], dtype=object)
KpointsFTG
We have a dedicated class for construcint k-points, though this is basically just a trivial wrapper around LatticeFTG.
>>> import numpy as np
>>> from principia_materia.translation_group import KpointsFTG
>>> fcc_vec = 0.5*(np.ones((3,3))-np.identity(3))
>>> S_bz = np.ones((3, 3)) - 2 * np.identity(3)
>>> qpts = KpointsFTG(vec=fcc_vec, supa=S_bz)
>>> qpts.ppoints
array([[0, 0, 0],
[0, 0, 1],
[0, 1, 0],
[1, 0, 0]])
>>> qpts.kpoints
array([[ 0 , 0 , 0 ],
[1/2, 1/2, 0 ],
[1/2, 0 , 1/2],
[ 0 , 1/2, 1/2]], dtype=object)
>>> qpts.ppoints_to_kpoints(
... np.array([[0, 0, 1],
... [1, 0, 0]]) )
array([[1/2, 1/2, 0 ],
[ 0 , 1/2, 1/2]], dtype=object)
>>> qpts.kpoints_to_ppoints(
... np.array([[0.5, 0.5, 0 ],
... [0 , 0.5, 0.5]]) )
array([[0, 0, 1],
[1, 0, 0]])
KpointsFSG
Similar to KpointsFTG, the class KpointsFSG is basically a trivial wrapper around LatticeFSG, which handles the point symmetry of the lattice. Given that the most common use cases of point symmetry are in reciprocal space, only KpointsFSG is outlined here. KpointsFSG derives from KpointsFTG, so it shares all the same attributes outlined above.
>>> import numpy as np
>>> from principia_materia.translation_group import KpointsFSG
>>> fcc_vec = 0.5*(np.ones((3,3))-np.identity(3))
>>> S_bz = 2*np.identity(3)
>>> qpts = KpointsFSG(vec=fcc_vec, supa=S_bz, pg='Oh')
>>> qpts.kpoints
array([[ 0 , 0 , 0 ],
[1/2, 0 , 0 ],
[ 0 , 1/2, 0 ],
[1/2, 1/2, 0 ],
[ 0 , 0 , 1/2],
[1/2, 0 , 1/2],
[ 0 , 1/2, 1/2],
[1/2, 1/2, 1/2]], dtype=object)
>>> qpts.irr_kpoints
array([[ 0 , 0 , 0 ],
[1/2, 0 , 0 ],
[1/2, 1/2, 0 ]], dtype=object)
>>> qpts.irr_ppoints
array([[0, 0, 0],
[1, 0, 0],
[1, 1, 0]])
>>> qpts.N_irr
3
>>> qpts.irr_indexes
array([0, 1, 3])
>>> qpts.indexes_map_irr_index
array([0, 1, 1, 3, 1, 3, 3, 1])
>>> qpts.indexes_map_irr_order
array([0, 1, 1, 2, 1, 2, 2, 1])
>>> qpts.stars
[[0], [1, 2, 4, 7], [3, 5, 6]]
>>> qpts.little_groups
['Oh', 'D3d', 'D4h']
>>> qpts.kpoint_type
['real', 'real', 'real']
>>> # operations that yield 4th point from irr counterpart
>>> qpts.indexes_map_irr_ops[4]
['c3be', 'ci3de', 'c2y', 'c4x', 'c4z', 'c2d', 'Ic3be', 'Ici3de', 'Ic2y', 'Ic4x', 'Ic4z', 'Ic2d']
QpointsN
Next, we can use the above information to find irreducible \(Q\)-points.
>>> qpointsn = QpointsN(vec=lattice_vectors, supa=supa, order=3, pg=pg)
>>> qpointsn.find_irreducible_Qpoints()
array([[[Fraction(0, 1), Fraction(0, 1), Fraction(0, 1)],
[Fraction(0, 1), Fraction(0, 1), Fraction(0, 1)],
[Fraction(0, 1), Fraction(0, 1), Fraction(0, 1)]],
...,
[[Fraction(3, 4), Fraction(1, 4), Fraction(1, 2)],
[Fraction(3, 4), Fraction(1, 4), Fraction(1, 2)],
[Fraction(1, 2), Fraction(1, 2), Fraction(0, 1)]]], dtype=object)
get_minimum_supercell
Algorithm to find the minimum supercell for a given list of \(\textbf{q}\)-points
The minimum supercell multiplicity is derived in the paper. Here using the implemented function, we find that for the \(\textbf{q}\)-point \(\textbf{q}=\left(\frac{1}{4}, \frac{3}{4}, 0\right)\) the supercell of the minimum multiplicity is:
$$ \hat{S}_{\textbf{q}} = \begin{bmatrix} 1 & 1 & 0 \\ 0 & 4 & 0 \\ 0 & 0 & 1 \end{bmatrix} $$
, and for the \(Q\)-point \(Q=\left[\left(\frac{1}{4}, \frac{3}{4}, 0\right), \left(\frac{1}{4}, \frac{1}{2}, 0\right), \left(\frac{3}{4}, \frac{1}{4}, \frac{1}{2}\right)\right]\) the supercell of the minimum multiplicity is:
$$ \hat{S}_{Q} = \begin{bmatrix} 4 & 0 & 0 \\ 0 & 4 & 0 \\ 0 & 0 & 2 \end{bmatrix} $$
>>> import numpy as np
>>> from principia_materia.translation_group import get_minimum_supercell
>>> from principia_materia.io_interface import parse_array
>>> from principia_materia.mathematics import Fraction
>>> Qpoint = parse_array("""\
... 1/4 3/4 0
... """, dtype=Fraction)
>>> Qpoint
array([1/4, 3/4, 0 ], dtype=object)
>>> get_minimum_supercell(Qpoint)
array([[1, 1, 0],
[0, 4, 0],
[0, 0, 1]])
>>> Qpoint = parse_array("""\
... 1/4 3/4 0
... 1/4 1/2 0
... 3/4 1/4 1/2
... """, dtype=Fraction)
>>> Qpoint
array([[1/4, 3/4, 0 ],
[1/4, 1/2, 0 ],
[3/4, 1/4, 1/2]], dtype=object)
>>> get_minimum_supercell(Qpoint)
array([[4, 0, 0],
[0, 4, 0],
[0, 0, 2]])
Feedback
Was this page helpful?
Glad to hear it! Please tell us how we can improve.
Sorry to hear that. Please tell us how we can improve.