In this short tutorial, we will describe the mathematical notation used in the symmetry-representation library, and show how the symmetry operations for InAs can be constructed.


In general, the symmetries of a crystal can be described as a space group \(G\), with symmetry operations \(g \in G\). The real-space effect of a symmetry \(\{S_g | \boldsymbol{\alpha}_g \}\) (using the so-called Seitz notation) is given by

\[g~\mathbf{r} = S_g \mathbf{r} + \boldsymbol{\alpha}_g,\]

where \(S_g\) is a rotation matrix, and \(\boldsymbol{\alpha}_g\) is a translation vector.

For a Hamiltonian \(\mathcal{H}(\mathbf{k})\), the effect of these symmetries can be captured in the symmetry constraint

\[\mathcal{H}(\mathbf{k}) = D^\mathbf{k}(g) \mathcal{H}(g^{-1}\mathbf{k}) D^\mathbf{k}(g^{-1}),\]

where \(D^\mathbf{k}(g)\) are the symmetry representations. The \(\mathbf{k}\) - dependence of these representations comes from the translational part of the symmetry operation, and can be expressed as

\[D^\mathbf{k}(g) = e^{i \boldsymbol{\alpha}_{g}.\mathbf{k}} D(g),\]

where \(D(g)\) is a \(\mathbf{k}\) - independent representation matrix. In the symmetry constraint, these \(\mathbf{k}\) - dependent phase factors cancel out:

\[\mathcal{H}(\mathbf{k}) = D^\mathbf{k}(g) \mathcal{H}(g^{-1}\mathbf{k}) D^\mathbf{k}(g^{-1}).\]

For this reason, we consider only the \(\mathbf{k}\) - independent representation matrices \(D(g)\) in the symmetry-representation code.

Depending on the kind of symmetry operation, these representation matrices are either unitary or anti-unitary. That is, we can write them as either \(D(g) = U_g\) (unitary) or \(D(g) = U_g \hat{K}\) (anti-unitary), where \(U_g\) is a unitary matrix, and \(\hat{K}\) is the complex conjugation operator.

The SymmetryOperation and SymmetryGroup classes

The main purpose of the symmetry-representation code is to provide classes which describe these symmetry operations and their representations. These can then be used by other codes, such as TBmodels or kdotp-symmetry. It also provides helper functions which allow for more easily constructing such symmetry operations.

Within symmetry-representation, symmetry operations are described using the SymmetryOperation class. This class has the following parameters:

  • rotation_matrix: The rotation matrix \(S_g\), in reduced coordinates.

  • translation_vector: The translation vector \(\boldsymbol{\alpha}_{g}\), in reduced coordinates.

  • repr_matrix: The matrix \(U_g\) of the representation matrix.

  • has_cc: A Boolean, which describes if the representation \(D(g)\) contains the complex conjugation operator \(\hat{K}\).

These symmetry operations can be combined into a group, using the SymmetryGroup class. In addition to the list of symmetry operations, it has a Boolean parameter full_group. This parameter describes if the symmetries given represent the full group (True), or only a generating subset (False).

Automatic construction from orbitals

A common obstacle to creating such symmetry operations is that their representation matrices are not known. Commonly, only the shape of the orbitals which span the basis of a given model is known. For this case, symmetry-representation provides helper functions to create the symmetry operations directly from the orbitals, and real-space symmetry operations only.

Here, we show the use of these helper functions for the case of a model for InAs, with \(s\) and \(p\) orbitals located on the In atom, and \(p\) orbitals located on As. The first task is to create a list of orbitals in the model. Each orbital requires three inputs:

  • The position, in reduced coordinates.

  • The shape of the orbital, as a string which expresses the shape in terms of cartesian coordinates x, y and z.

  • The spin of the orbital, as a Spin instance.

For the case of InAs, the following code constructs the correct orbitals:

In [1]: import symmetry_representation as sr

In [2]: pos_In = (0, 0, 0)

In [3]: pos_As = (0.25, 0.25, 0.25)

In [4]: orbitals = []

In [5]: for spin in (sr.SPIN_UP, sr.SPIN_DOWN):
   ...:     orbitals.extend([
   ...:         sr.Orbital(position=pos_In, function_string=fct, spin=spin)
   ...:         for fct in sr.WANNIER_ORBITALS['s'] + sr.WANNIER_ORBITALS['p']
   ...:     ])
   ...:     orbitals.extend([
   ...:         sr.Orbital(position=pos_As, function_string=fct, spin=spin)
   ...:         for fct in sr.WANNIER_ORBITALS['p']
   ...:     ])

Here we used some pre-defined constants of the symmetry-representation code, namely the spins

In [6]: sr.SPIN_UP
Out[6]: Spin(total=Fraction(1, 2), z_component=Fraction(1, 2))

In [7]: sr.SPIN_DOWN
Out[7]: Spin(total=Fraction(1, 2), z_component=Fraction(-1, 2))

and the orbitals as created by the Wanier90 code

In [8]: sr.WANNIER_ORBITALS['s']
Out[8]: ['1']

In [9]: sr.WANNIER_ORBITALS['p']
Out[9]: ['z', 'x', 'y']

Having defined the orbitals, we also need to obtain the real-space symmetry operations of InAs. Since it has a symmorphic symmetry group, we only need rotation matrices, in both cartesian and reduced coordinates. We can use the pymatgen code to simplify this:

In [10]: import pymatgen as mg

In [11]: structure = mg.Structure(
   ....:     lattice=[[0., 3.029, 3.029], [3.029, 0., 3.029], [3.029, 3.029, 0.]],
   ....:     species=['In', 'As'],
   ....:     coords=np.array([pos_In, pos_As])
   ....: )

In [12]: analyzer = mg.symmetry.analyzer.SpacegroupAnalyzer(structure)

In [13]: sym_ops = analyzer.get_symmetry_operations(cartesian=False)

In [14]: sym_ops_cart = analyzer.get_symmetry_operations(cartesian=True)

And finally, we can use the symmetry group using the from_orbitals() method of the SymmetryOperation class:

In [15]: symmetry_group = sr.SymmetryGroup(
   ....:     symmetries=[
   ....:         sr.SymmetryOperation.from_orbitals(
   ....:             orbitals=orbitals,
   ....:             real_space_operator=sr.RealSpaceOperator.
   ....:             from_pymatgen(sym_reduced),
   ....:             rotation_matrix_cartesian=sym_cart.rotation_matrix,
   ....:             numeric=True
   ....:         ) for sym_reduced, sym_cart in zip(sym_ops, sym_ops_cart)
   ....:     ],
   ....:     full_group=True
   ....: )

The numeric flag determines whether numpy arrays (True) or sympy matrices (False) are created. The former are suited for use with the TBmodels code, while the latter should be used for kdotp-symmetry. Note that in order to create correct sympy matrices, the real space matrices should also be sympy matrices.

Additionally, there is a special helper function to create a representation of the time-reversal symmetry operation:

In [16]: time_reversal = sr.get_time_reversal(orbitals=orbitals, numeric=True)