distinguishing bands belonging to different orbitals

Hi all! I would like to plot bandstructure of a graphene lead with electron and hole structure (i.e. norbs=2), while distinguishing (e.g. with color) between electron and hole bands. The lattice is defined as lat = kwant.lattice.general([(sqrt(3)*1/2, 1/2), (0, 1)], [(0, 0), (1/(2*sqrt(3)),1/2)], norbs=2) the lead is created as lead0 = kwant.Builder(sym_lead) lead0[a.shape(lead0_shape,(0,0))] = -EF * tau_z lead0[b.shape(lead0_shape,(0,0))] = -EF * tau_z lead0[lat.neighbors()] = -t * tau_z Then the bandstructure is obtained as bands = kwant.physics.Bands(lead0.finalized()) momenta = numpy.linspace(-pi, pi, 201) energies = [bands(k) for k in momenta] Is there an easy way to get the bands just for electrons? Something like "bands = kwant.physics.Bands(lead0.finalized(), orbital = 0)" or for holes "bands = kwant.physics.Bands(lead0.finalized(), orbital = 1)" Thanks, Tibor Sekera

Hi Tibor,
Hi all!
I would like to plot bandstructure of a graphene lead with electron and hole structure (i.e. norbs=2), while distinguishing (e.g. with color) between electron and hole bands.
...
Is there an easy way to get the bands just for electrons? Something like
"bands = kwant.physics.Bands(lead0.finalized(), orbital = 0)"
or for holes
"bands = kwant.physics.Bands(lead0.finalized(), orbital = 1)"
Unfortunately Kwant does not do this for you out of the box. While in principle this would be cool, for this to be well defined the lead modes need to also be eigenstates of the projection operator(s) onto the states that you care about (if you had some coupling between your spin bands, what would you define to be "orbital 0" or "orbital 1" near the avoided crossings?) In the case (and basis) your described above the k-space Hamiltonian is already diagonal, so you don't even need to do a diagonalization! You just need to read off the diagonal elements of the k-space Hamiltonian. For example: from cmath import exp # complex exponential import numpy as np import matplotlib.pyplot as plt def H_k(lead): H0 = lead.cell_hamiltonian() # get inter-cell hopping and make it a square matrix _V = lead.inter_cell_hopping() V = np.empty(H0.shape, dtype=complex) V[:, :_V.shape[1]] = _V V[:, _V.shape[1]:] = 0 # return a function that, given 'k', calculates H(k) return lambda k: H0 + exp(-1j * k) * V + exp(1j * k) * V.conjugate().transpose() ... H = H_k(lead.finalized()) ks = np.linspace(-np.pi, np.pi) bands = np.array([H(k).diagonal() for k in ks]) bands = bands.transpose() plt.plot(ks, bands[0]) # mode 0 plt.plot(ks, bands[1]) # mode 1 For the more general case you would have to do the diagonalization, and would have to properly "identify" the modes that you care about (probably by looking at the eigenvectors). Anton recently wrote a blog post [1] about the related problem of properly colouring the bands (at the moment Kwant just colours the bands in order of ascending energy, which looks wrong when there are band crossings). Basically it just amounts to taking the inner product of the mode wavefunctions at a given k value with the basis states that you care about. Happy Kwanting, Joe [1]: https://quantumtinkerer.tudelft.nl/blog/connecting-the-dots/

Hi Joe, thank you for the answer! If I use your example code, in the step where H = H_k(lead.finalized()), the matrix H is diagonal in k-space, and would be block-diagonal in (electron,hole)-space, where each block is N-by-N (generally not diagonal), where N is the number of sites in the unit cell of a lead. However, KWANT seems to return H which is not in basis sorted as (electron,hole)-space, hence H is not block-diagonal. I would need to rearrange the basis that H is written in. Is there an easy way to do this? Thanks, Tibor Sekera ________________________________ From: Joseph Weston [joseph.weston08@gmail.com] Sent: Monday, June 26, 2017 5:48 PM To: Tibor Sekera; kwant-discuss@kwant-project.org Subject: Re: [Kwant] distinguishing bands belonging to different orbitals Hi Tibor, Hi all! I would like to plot bandstructure of a graphene lead with electron and hole structure (i.e. norbs=2), while distinguishing (e.g. with color) between electron and hole bands. ... Is there an easy way to get the bands just for electrons? Something like "bands = kwant.physics.Bands(lead0.finalized(), orbital = 0)" or for holes "bands = kwant.physics.Bands(lead0.finalized(), orbital = 1)" Unfortunately Kwant does not do this for you out of the box. While in principle this would be cool, for this to be well defined the lead modes need to also be eigenstates of the projection operator(s) onto the states that you care about (if you had some coupling between your spin bands, what would you define to be "orbital 0" or "orbital 1" near the avoided crossings?) In the case (and basis) your described above the k-space Hamiltonian is already diagonal, so you don't even need to do a diagonalization! You just need to read off the diagonal elements of the k-space Hamiltonian. For example: from cmath import exp # complex exponential import numpy as np import matplotlib.pyplot as plt def H_k(lead): H0 = lead.cell_hamiltonian() # get inter-cell hopping and make it a square matrix _V = lead.inter_cell_hopping() V = np.empty(H0.shape, dtype=complex) V[:, :_V.shape[1]] = _V V[:, _V.shape[1]:] = 0 # return a function that, given 'k', calculates H(k) return lambda k: H0 + exp(-1j * k) * V + exp(1j * k) * V.conjugate().transpose() ... H = H_k(lead.finalized()) ks = np.linspace(-np.pi, np.pi) bands = np.array([H(k).diagonal() for k in ks]) bands = bands.transpose() plt.plot(ks, bands[0]) # mode 0 plt.plot(ks, bands[1]) # mode 1 For the more general case you would have to do the diagonalization, and would have to properly "identify" the modes that you care about (probably by looking at the eigenvectors). Anton recently wrote a blog post [1] about the related problem of properly colouring the bands (at the moment Kwant just colours the bands in order of ascending energy, which looks wrong when there are band crossings). Basically it just amounts to taking the inner product of the mode wavefunctions at a given k value with the basis states that you care about. Happy Kwanting, Joe [1]: https://quantumtinkerer.tudelft.nl/blog/connecting-the-dots/

Hi again Tibor,
If I use your example code, in the step where
H = H_k(lead.finalized()),
the matrix H is diagonal in k-space, and would be block-diagonal in (electron,hole)-space, where each block is N-by-N (generally not diagonal), where N is the number of sites in the unit cell of a lead.
However, KWANT seems to return H which is not in basis sorted as (electron,hole)-space, hence H is not block-diagonal.
Indeed, Kwant orders the Hilbert space first by site, and then by any internal degrees of freedom (electron-hole in your case), so the block-diagonal structure will not be obvious in this ordering.
I would need to rearrange the basis that H is written in.
Is there an easy way to do this?
If you want to get out just the electron or just the hole part of the Hamiltonian you could construct a projector over the appropriate orbitals. Assuming everything is ordered (electron, hole) then: P_electron = np.kron(np.eye(n_sites), np.array([[1, 0], [0, 0]])) P_hole = np.kron(np.eye(n_sites), np.array([[0, 0], [0, 1]])) You can also (since Kwant 1.3) declare any discrete symmetry that your model has by passing a 'conservation_law' when constructing your Builder. In your case you would do something like: TS = kwant.TranslationalSymmetry((-1, 0)) DS = np.diag([-1, 1]) # discrete symmetry operator with integer eigenvalues syst = Builder(TS, conservation_law=DS) ... fsyst = syst.finalized() After finalization you can get the discrete symmetry over the whole Hilbert space (of a single unit cell for systems with a translational symmetry) by using the 'discrete_symmetry()' method of finalized systems: sym = fsyst.discrete_symmetry() P_electron, P_hole = sym.projectors You can apply these projectors to the hamiltonian (they are just sparse matrices) to get the corresponding blocks: H_electron = P_electron @ H @ P_electron.conjugate().transpose() H_hole = P_hole @ H @ P_hole.conjugate().transpose() Specifying 'conservation_law' and letting Kwant build the projectors for you is simpler when your symmetry operator is larger, and is not diagonal in the basis in which you write the Hamiltonian when constructing the Builder. Hope that helps, Joe

Hi Joe, thank you very much, it works! If someone finds it helpful, here's a small snippet to plot electron and hole bands with different colors: --------------------------------------------------------------- lead0 = make_system_NS() lead0f = lead0.finalized() sym = lead0f.discrete_symmetry() projector_e, projector_h = sym.projectors def H_k(lead): HL = lead.cell_hamiltonian(params=dict(phi=phi)) # get inter-cell hopping and make it a square matrix _V = lead.inter_cell_hopping(params=dict(phi=phi)) V = numpy.empty(HL.shape, dtype=complex) #create HL.shape matrix of uninitialized (arbitrary) data V[:, :_V.shape[1]] = _V V[:, _V.shape[1]:] = 0 # return a function that, given 'k', calculates H(k) return lambda k: HL + exp(-1j * k) * V + exp(1j * k) * V.conjugate().transpose() def eig_e(k): H_e = projector_e.conjugate().transpose() @ H(k) @ projector_e eig_e = LA.eigh(H_e) return eig_e[0] def eig_h(k): H_h = projector_h.conjugate().transpose() @ H(k) @ projector_h eig_h = LA.eigh(H_h) return eig_h[0] H = H_k(lead0f) ks = numpy.linspace(-numpy.pi, numpy.pi, num = 50) bands_e = numpy.array([eig_e(k) for k in ks]) bands_h = numpy.array([eig_h(k) for k in ks]) plt.plot(ks, bands_e, color = 'blue') plt.plot(ks, bands_h, color = 'grey'); --------------------------------------------------------------- Best regards, Tibor ________________________________ From: Joseph Weston [joseph.weston08@gmail.com] Sent: Thursday, June 29, 2017 10:43 AM To: Tibor Sekera; kwant-discuss@kwant-project.org Subject: Re: [Kwant] distinguishing bands belonging to different orbitals Hi again Tibor, If I use your example code, in the step where H = H_k(lead.finalized()), the matrix H is diagonal in k-space, and would be block-diagonal in (electron,hole)-space, where each block is N-by-N (generally not diagonal), where N is the number of sites in the unit cell of a lead. However, KWANT seems to return H which is not in basis sorted as (electron,hole)-space, hence H is not block-diagonal. Indeed, Kwant orders the Hilbert space first by site, and then by any internal degrees of freedom (electron-hole in your case), so the block-diagonal structure will not be obvious in this ordering. I would need to rearrange the basis that H is written in. Is there an easy way to do this? If you want to get out just the electron or just the hole part of the Hamiltonian you could construct a projector over the appropriate orbitals. Assuming everything is ordered (electron, hole) then: P_electron = np.kron(np.eye(n_sites), np.array([[1, 0], [0, 0]])) P_hole = np.kron(np.eye(n_sites), np.array([[0, 0], [0, 1]])) You can also (since Kwant 1.3) declare any discrete symmetry that your model has by passing a 'conservation_law' when constructing your Builder. In your case you would do something like: TS = kwant.TranslationalSymmetry((-1, 0)) DS = np.diag([-1, 1]) # discrete symmetry operator with integer eigenvalues syst = Builder(TS, conservation_law=DS) ... fsyst = syst.finalized() After finalization you can get the discrete symmetry over the whole Hilbert space (of a single unit cell for systems with a translational symmetry) by using the 'discrete_symmetry()' method of finalized systems: sym = fsyst.discrete_symmetry() P_electron, P_hole = sym.projectors You can apply these projectors to the hamiltonian (they are just sparse matrices) to get the corresponding blocks: H_electron = P_electron @ H @ P_electron.conjugate().transpose() H_hole = P_hole @ H @ P_hole.conjugate().transpose() Specifying 'conservation_law' and letting Kwant build the projectors for you is simpler when your symmetry operator is larger, and is not diagonal in the basis in which you write the Hamiltonian when constructing the Builder. Hope that helps, Joe
participants (2)
-
Joseph Weston
-
Tibor Sekera