Quasi-eigenstates

The quasi-eigenstates are defined as

\[\begin{split}|\Psi(E) \rangle &=\frac{1}{2\pi} \int_{-\infty}^{\infty} \mathrm{e}^{\mathrm{i}Et} |\psi(t) \rangle \mathrm{d}t \nonumber \\ &=\frac{1}{2\pi}\sum_i a_i \int_{-\infty}^{\infty} \mathrm{e}^{\mathrm{i}(E-E_i)t} |\phi_i \rangle \mathrm{d}t \nonumber \\ &=\sum_i a_i \delta(E-E_i)|\phi_i \rangle\end{split}\]

If the energy \(E\) equals to an eigenvalue \(E_i\), then the quasi-eigenstate is the exact eigenstate corresonding to \(E_i\), or a superposition of degenerate eigenstates if \(E_i\) is degenerate. In this tutorial, we will show how to calculate the quasi-eigenstates and compare them with the exact eigenstates. The script can be found at examples/advanced/quasi_eigen.py. We begin with importing the necessary packages:

import numpy as np
import tbplas as tb

Exact eigenstates

We define the following function to calculate the exact eigenstates:

 1def wfc_diag(sample: tb.Sample) -> None:
 2    """
 3    Calculate wave function using exact diagonalization.
 4
 5    :param sample: graphene sample
 6    :return: None
 7    """
 8    k_points = np.array([[0.0, 0.0, 0.0]])
 9    solver = tb.DiagSolver(model=sample)
10    bands, states = solver.calc_states(k_points)
11
12    i_b = sample.num_orb // 2
13    wfc = np.abs(states[0, i_b])**2
14    wfc /= wfc.max()
15    vis = tb.Visualizer()
16    vis.plot_wfc(sample, wfc, scatter=True, site_size=wfc*100, site_color="r",
17                 with_model=True, model_style={"alpha": 0.25, "color": "gray"})
18    print(bands[0, i_b])

In line 8 we define the k-points on which the eigenstates will be calculated, where we consider the \(\Gamma\) point. In line 9 we define a DiagSolver object and call its calc_states method in line 10 to get the eigenvalues and eigenstates, as returned in bands and states. After that, we extract the eigenstate at the Fermi level and normalize it in line 12-14. To visualize the eigenstate, we create a visualizer from the Visualizer class and call its plot_wfc method. Note that we must use scatter plot by setting the scatter argument to true. The eigenstate should be passed as the site_size argument, i.e., sizes of scatters will indicate the projection of eigenstate on the sites. We also need to show the model alongside the eigenstate through the with_model argument and define its plotting style with the model_style argument. Finally, we print the eigenvalue corresonding to the eigenstate, which will be utilized to calcualte the quasi-eigenstate then.

The wfc_diag function should be called by:

 1def main():
 2    # Build a graphene sample with a single vacancy
 3    prim_cell = tb.make_graphene_diamond()
 4    super_cell = tb.SuperCell(prim_cell, dim=(17, 17, 1),
 5                            pbc=(True, True, False))
 6    super_cell.add_vacancies([(8, 8, 0, 0)])
 7    sample = tb.Sample(super_cell)
 8
 9    # Calcuate and Visualize the eigenstate
10    wfc_diag(sample)
11
12
13if __name__ == "__main__":
14    main()

where we create a \(17\times17\times1\) graphene sample with a vacancy in the center in line 2-7. The output is shown in Fig. 1(b), where the eigenstate is localized around the vacancy and shows a 3-fold rotational symmtery.

../../_images/quasi_eigen.png

Spatial distribution of (a) exact eigenstate and (b) quasi-eigenstate of graphene sample with a vacancy in the center. The X marks indicate the position of the vacancy.

Quasi-eigenstates

The quasi-eigenstate is evaluated and plotted in a similar approach:

 1def wfc_tbpm(sample: tb.Sample) -> None:
 2    """
 3    Calculate wave function using TBPM.
 4
 5    :param sample: graphene sample
 6    :return: None
 7    """
 8    sample.rescale_ham()
 9    config = tb.Config()
10    config.generic['nr_random_samples'] = 1
11    config.generic['nr_time_steps'] = 1024
12    config.quasi_eigenstates['energies'] = [0.0]
13
14    solver = tb.Solver(sample, config)
15    qs = solver.calc_quasi_eigenstates()
16    wfc = np.abs(qs[0])**2
17    wfc /= wfc.max()
18    vis = tb.Visualizer()
19    vis.plot_wfc(sample, wfc, scatter=True, site_size=wfc*100, site_color="b",
20                 with_model=True, model_style={"alpha": 0.25, "color": "gray"})

Evaluating quasi-eigenstates is a kind of TBPM calculation, so it follows the common procedure of TBPM. We firstly rescale the Hamiltonian in line 8, then create a config and a solver in line 9-14. Specially, the energy from the output of previous section should be specified in config.quasi_eigenstates['energies']. Then we call the calc_quasi_eigenstates method of solver to get the eigenstates, normalize it, and visualize it in line 15-20.

We call wfc_tbom by:

wfc_diag(sample)

The output is shown in Fig. 1(b), which is also localized around the vacancy and shows a 3-fold rotational symmtery, much similar to the exact eigenstate in Fig. 1(a).