Basic notation#
Vectors#
Vectors are represented as a columns of numbers. In Wulfric vectors are stored and manipulated as (3,) NumPy arrays:
vector = np.array([vx, vy, vz])
Note
One-dimensional NumPy arrays do not distinguish between row and column vectors.
Cell#
Cell of the lattice is defined by the three lattice vectors \(\boldsymbol{a}_i = (a_i^x, a_i^y, a_i^z)^T\). In Wulfric those vectors are stored as a matrix with vectors as rows (hence the transposition symbol):
cell = [
[a1_x, a1_y, a1_z],
[a2_x, a2_y, a2_z],
[a3_x, a3_y, a3_z],
]
Note
That definition of the cell is the same as in the spglib's python interface and is a transpose of the definition of the C spglib.
Atom's positions#
Atoms positions are stored as vectors of the relative coordinates with respect to the cell vectors.
position = np.array([r1, r2, r3])
Cartesian (absolute) coordinates of the atoms can be calculated by the following formula:
x = r @ cell
# or
x = cell.T @ r
Note
Remember, that one-dimensional NumPy arrays do not distinguish between row and
column vectors and cell store transposed cell matrix in our notation:
cell \(\rightarrow (\boldsymbol{a}_1, \boldsymbol{a}_2, \boldsymbol{a}_3)^T\).
In the documentation analytical formulas are written for the
\((\boldsymbol{a}_1, \boldsymbol{a}_2, \boldsymbol{a}_3)\) matrix, while code
snippets are written for
\((\boldsymbol{a}_1, \boldsymbol{a}_2, \boldsymbol{a}_3)^T\) matrix.
Reciprocal cell#
Reciprocal cell is defined by the three reciprocal lattice vectors \(\boldsymbol{b}_i = (b_i^x, b_i^y, b_i^z)^T\). In Wulfric those vectors are stored as a matrix with vectors as rows (hence the transposition symbol):
reciprocal_cell = [
[b1_x, b1_y, b1_z],
[b2_x, b2_y, b2_z],
[b3_x, b3_y, b3_z],
]
Reciprocal cell is connected with the cell of the lattice as follows:
reciprocal_cell = 2 * np.pi * np.linalg.inv(cell.T)
K-points#
K-points are stored as vectors of the fractional coordinates with respect to the reciprocal cell vectors.
kpoint = np.array([g1, g2, g3])
Cartesian (absolute) coordinates of the k-points can be calculated by the following formula:
k = g @ reciprocal_cell
# or
k = reciprocal_cell.T @ g
Transformation of the cell#
The choice of the cell of the lattice is not unique. Transformation between two different cells of the same lattice might be expressed by the transformation matrix \(\boldsymbol{P}\):
tcell = np.linalg.inv(P.T) @ cell
Crystal is not affected by the change of the cell, i.e. the atom's Cartesian coordinates are not changed (\(\boldsymbol{x} = \boldsymbol{\tilde{x}}\)). Therefore, the atom's relative positions are transformed as
tr = P @ r
Hint
Reciprocal cell is changed by the transformation as follows:
reciprocal_tcell = np.linalg.inv(P) @ reciprocal_cell
Relative positions of the k-points are transformed as follows:
tg = np.linalg.inv(P).T @ g
Standardization of the cell#
When standardization of the cell is required, it can be expressed by the transformation matrix \(\boldsymbol{S}\) with \((\boldsymbol{a}_1^s, \boldsymbol{a}_2^s, \boldsymbol{a}_3^s)\) being the standardized primitive cell:
Note
Matrix \(\boldsymbol{S}\) is orthonormal for all Bravais lattices, except for the Base-centred monoclinic (MCLC). All matrices satisfy \(\det(\boldsymbol{S}) = 1\).
Details on how the standardization matrix is constructed are provided in the individual pages for each of the 14 Bravais lattices.
Conventional vs primitive cell (Setyawan and Curtarolo)#
In the reference paper [1] conventional (>=1 lattice point per cell) and primitive (1 lattice point per cell) standardized cells are defined. Transformation from primitive to conventional cell is expressed by the transformation matrix \(\boldsymbol{C}\):
Transformation matrices \(\boldsymbol{C}\) and its inverse are provided in the individual pages for each of the 14 Bravais lattices.
Important
Given cell is always interpreted as primitive.