python wannier90 基于wannier90的*_hr.dat文件选取截断hopping绘制能带图

        我们知道wannier90可以根据选取TMDs的轨道信息生成详细的hopping energy *_hr.dat文件,选取所有的hopping绘制起来的时候比较简单,但是我们发现取几圈的近似hopping也可以将band表示出来,类似的思想有Pybinding的三带近似(DOI: 10.1103/PhysRevB.88.085433)和Fang的TMDs的紧束缚模型半经验参数近似(DOI: 10.1103/PhysRevB.92.205108)等等。

        我们首先要知道wannier90.dat文件里的数据格式,我们需要的是 类似于

-12   -6    0    1    1    0.000020   -0.000000
-12   -6    0    2    1    0.000000   -0.000000
-12   -6    0    3    1    0.000000    0.000000
-12   -6    0    4    1    0.000004    0.000000
-12   -6    0    5    1    0.000000    0.000000

这样的数据,这里前面三个代表directions[x,y,z] 中间两个代表[fromAtom, toAtom]也就是Rij,后面两个值代表了hopping,代表复数 a+bi 的[a, b]。

        这里的画圈有点类似,也就是根据选择当前原子和目标原子的距离来简要进行阶段,例如下图展示了四层原子(包括[0,0]),可能第三圈的hopping贡献远大于第四圈,所以截取到第三圈即可绘制band。

 这里给出MX_2部分示例,选取M的d_(z^2) d_(xy) d_(x^2-y^2)和总的S轨道计算的一个wannier90 *_hr.dat文件,任意选择截取1-10([0,0]就算了几乎是onsite energy)圈的一个代码,其实取到第5圈就已经比较近似于全部圈层的结果了:

#!/usr/bin python
# -*- encoding: utf-8 -*-
'''
@Author  :   Celeste Young
@File    :   基于fang2015提取MoS2轨道.py    
@Time    :   2023/4/3 14:47  
@E-mail  :   iamwxyoung@qq.com
@Tips    :   
'''

import time
import matplotlib.pyplot as plt
import pandas as pd
# import pybinding as pb
import numpy as np
import functools
import threading


def make_path(k0, k1, *ks, step=0.1):  # kpath 
    k_points = [np.atleast_1d(k) for k in (k0, k1) + ks]
    if not all(k.shape == k_points[0].shape for k in k_points[:1]):
        raise RuntimeError("All k-points must have the same shape")

    k_paths = []
    point_indices = [0]
    for k_start, k_end in zip(k_points[:-1], k_points[1:]):
        num_steps = int(np.linalg.norm(k_end - k_start) // step)
        # k_path.shape == num_steps, k_space_dimensions
        k_path = np.array([np.linspace(s, e, num_steps, endpoint=False)
                           for s, e in zip(k_start, k_end)]).T
        k_paths.append(k_path)
        point_indices.append(point_indices[-1] + num_steps)
    k_paths.append(k_points[-1])

    return np.vstack(k_paths)  # , point_indices)


def isinDirect(v_, hvts):  # 判断是否在所选的directions里,上图的n层
    for vs in hvts:
        if ''.join(v_) == ''.join(list(map(str, vs))):
            return True
    return False


def read_dat(*args, **kwargs):
    with open("data/wannier90_hr_MoS2.dat", "r") as f:
        lines = f.readlines()
    rij = [[[] for col in range(nb)] for row in range(nb)]
    tij = [[[] for col in range(nb)] for row in range(nb)]
    for line in lines:
        ll = line.split()
        if isinDirect(ll[:3], kwargs['hvts']):
            x, y, z, frAt, toAt = list(map(int, ll[:5]))
            t_real, t_image = list(map(float, ll[5:]))
            rij[frAt - 1][toAt - 1].append([x, y, z])
            tij[frAt - 1][toAt - 1].append([t_real + 1j * t_image])
 
    return rij, tij


def plot_rec(*args, **kwargs):  # 绘制布里渊区的积分路径
    fig = plt.figure(figsize=(5, 5))
    ax = plt.axes()
    ax.arrow(0, 0, a1[0], a1[1], length_includes_head=False, head_width=0.05, fc='b', ec='k')
    ax.arrow(0, 0, a2[0], a2[1], length_includes_head=False, head_width=0.05, fc='b', ec='k')
    ax.arrow(0, 0, b1[0], b1[1], length_includes_head=False, head_width=0.05, fc='r', ec='red')
    ax.arrow(0, 0, b2[0], b2[1], length_includes_head=False, head_width=0.05, fc='r', ec='red')
    ax.plot([0, Middle[0], K1[0], 0], [0, Middle[1], K1[1], 0])
    ax.scatter([0, Middle[0], K1[0], 0], [0, Middle[1], K1[1], 0])
    ax.set_xlim(-1, 4)
    ax.set_ylim(-2, 4)
    ax.grid()
    plt.show()


def phase(*args, **kwargs):
    rij = kwargs['hr']
    wk = kwargs['wk']
    R_vec = 0
    for ii in range(3):
        R_vec += rij[ii] * basis_vector[ii]
    inner_product = np.dot(R_vec, wk)
    return np.exp(1j * inner_product)


def Hamham(wak, tij, rij):
    h = np.zeros((nb, nb), dtype=complex)
    for ii in range(nb):
        for jj in range(nb):
            for vij in range(len(rij[ii][jj])):
                hr = rij[ii][jj][vij]
                h[ii, jj] = h[ii, jj] + tij[ii][jj][vij][0] * phase(hr=hr, wk=wak)
    return h


def get_sphere(*args, **kwargs):  #获取需要的层的directions,传入参数n=[1,11)
    sp_ = args[0]
    spDict = {}
    # sp = 0
    sp0 = [[0, 0, 0]]
    spDict['sp0'] = sp0
    # sp = 1 >> 3*2
    sp1 = [[1, 0, 0], [1, 1, 0], [0, 1, 0]]
    sp1 = sp1 + [list(-np.array(sp1[i])) for i in range(len(sp1))]
    spDict['sp1'] = sp1
    # sp = 2 >> 3*2
    sp2 = [[2, 1, 0], [1, 2, 0], [-1, 1, 0]]
    sp2 = sp2 + [list(-np.array(sp2[i])) for i in range(len(sp2))]
    spDict['sp2'] = sp2
    # sp = 3 >> 3*2
    sp3 = [[2, 0, 0], [2, 2, 0], [0, 2, 0]]
    sp3 = sp3 + [list(-np.array(sp3[i])) for i in range(len(sp3))]
    spDict['sp3'] = sp3
    # sp = 4 >> 6*2
    sp4 = [[3, 1, 0], [3, 2, 0], [2, 3, 0], [1, 3, 0], [-1, 2, 0], [-2, 1, 0]]
    sp4 = sp4 + [list(-np.array(sp4[i])) for i in range(len(sp4))]
    spDict['sp4'] = sp4
    # sp = 5 >> 3*2
    sp5 = [[3, 0, 0], [3, 3, 0], [0, 3, 0]]
    sp5 = sp5 + [list(-np.array(sp5[i])) for i in range(len(sp5))]
    spDict['sp5'] = sp5
    # sp = 6 >> 3*2
    sp6 = [[4, 2, 0], [2, 4, 0], [-2, 2, 0]]
    sp6 = sp6 + [list(-np.array(sp6[i])) for i in range(len(sp6))]
    spDict['sp6'] = sp6
    # sp = 7 >> 3*2
    sp7 = [[4, 1, 0], [4, 3, 0], [3, 4, 0], [1, 4, 0], [-1, 3, 0], [-3, 1, 0]]
    sp7 = sp7 + [list(-np.array(sp7[i])) for i in range(len(sp7))]
    spDict['sp7'] = sp7
    # sp = 8 >> 3*2
    sp8 = [[4, 0, 0], [4, 4, 0], [0, 4, 0]]
    sp8 = sp8 + [list(-np.array(sp8[i])) for i in range(len(sp8))]
    spDict['sp8'] = sp8
    sp9 = [[5, 2, 0], [5, 3, 0], [2, 5, 0], [3, 5, 0], [-2, 3, 0], [-3, 2, 0]]
    sp9 = sp9 + [list(-np.array(sp9[i])) for i in range(len(sp9))]
    spDict['sp9'] = sp9
    sp10 = [[5, 1, 0], [5, 4, 0], [1, 5, 0], [4, 5, 0], [-1, 4, 0], [-4, 1, 0]]
    sp10 = sp10 + [list(-np.array(sp10[i])) for i in range(len(sp10))]
    spDict['sp10'] = sp10
    if sp_ == 0:
        return sp0

    tmpl = []
    for i in range(sp_):
        tmpl += [j for j in spDict['sp%d' % i]]
    return tmpl


def selectAllOrbital():
    with open("data/wannier90_hr_MoS2.dat", "r") as f:
        lines = f.readlines()
    rij = [[[] for col in range(nb)] for row in range(nb)]
    tij = [[[] for col in range(nb)] for row in range(nb)]
    for line in lines:
        ll = line.split()
        if len(ll) == 7:
            x, y, z, frAt, toAt = list(map(int, ll[:5]))
            t_real, t_image = list(map(float, ll[5:]))
            rij[frAt - 1][toAt - 1].append([x, y, z])
            tij[frAt - 1][toAt - 1].append([t_real + 1j * t_image])
    hamilton = functools.partial(Hamham, tij=tij, rij=rij)
    idx = 0
    result = np.zeros([len(path), 11])
    for kxy in range(len(path)):
        k = np.r_[path[kxy], [0]]
        w, t = np.linalg.eig(hamilton(k))
        w = list(w)
        w.sort()
        result[idx, :] = np.real(w)  # 将本征值进行保存
        idx += 1
    xk = [0, rt3, rt3 + 1, rt3 + 3]
    kk = np.linspace(0, 4.7, num=len(path))  # 作为x轴,使其和本征值矩阵每一列的y的值个数相同
    plt.figure(figsize=(4, 5))
    plt.plot(kk, result, c="r")
    plt.xticks(xk, ["Γ", "M", "K", "Γ"])
    plt.ylabel("Energy(eV)", fontsize=14)
    plt.axvline(rt3, color='gray', linestyle='--')
    plt.axvline(rt3 + 1, color='gray', linestyle='--')
    plt.title('all orbital')
    plt.savefig('all orbital.png')
    print('Saved all orbital figure!')


def selectOrbital(orbi):
    print('started %d/%d' % (orbi, 10))
    hvts = get_sphere(orbi)
    result = np.zeros([len(path), 11])  # 解的矩阵
    Rij, Tij = read_dat(hvts=hvts)
    hamilton = functools.partial(Hamham, tij=Tij, rij=Rij)
    idx = 0
    for kxy in range(len(path)):
        k = np.r_[path[kxy], [0]]
        w, t = np.linalg.eig(hamilton(k))
        w = list(w)
        w.sort()
        result[idx, :] = np.real(w)  # 将本征值进行保存
        idx += 1
    xk = [0, rt3, rt3 + 1, rt3 + 3]
    kk = np.linspace(0, 4.7, num=len(path))  # 作为x轴,使其和本征值矩阵每一列的y的值个数相同
    plt.figure(figsize=(4, 5))
    plt.plot(kk, result, c="r")
    plt.xticks(xk, ["Γ", "M", "K", "Γ"])
    plt.ylabel("Energy(eV)", fontsize=14)
    plt.axvline(rt3, color='gray', linestyle='--')
    plt.axvline(rt3 + 1, color='gray', linestyle='--')
    plt.title('%d orbital' % orbi)
    plt.savefig('%d orbital.png' % orbi)
    print('finish %d/%d' % (orbi, 10))


if __name__ == '__main__':
    # start here
    time1 = time.time()
    nb = 11  # number of bands
    a = 3.160  # 3.18
    c = 12.29  # distance of layers
    dxx = 3.13  # distance of orbital X-X
    dxm = 2.41  # distance of orbital X-M
    # constant checked
    rt3 = 3 ** 0.5
    pi = np.pi
    a1 = a * np.array([rt3 / 2, -1 / 2, 0])
    a2 = a * np.array([0, 1, 0])
    a3 = a * np.array([0, 0, 5])
    basis_vector = np.array([a1, a2, a3])

    b1 = 2 * pi / a * np.array([1 / rt3, 1])
    b2 = 4 * pi / a / rt3 * np.array([1, 0])

    Gamma = np.array([0, 0])
    Middle = 1 / 2 * b1
    K1 = 1 / 3 * (2 * b1 - b2)
    K2 = -1 / 3 * (2 * b1 - b2)
    # plot_rec()
    path = make_path(Gamma, Middle, K1, Gamma, step=0.01)

    # selectAllOrbital()  # 这里取消注释会绘制总的圈层的band

    for orb in range(1, 11):
        selectOrbital(orbi=orb)


    time2 = time.time()
    print('>> Finished, use time %d s' % (time2 - time1))

附上我的hr.dat的结果图:

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
平面波展开法是计算周期性结构(如晶体)能带结构的一种常用方法。对于声子晶体,可以将其看作具有周期性结构的介质,因此也可以使用平面波展开法来计算其带隙特性。 在二维声子晶体中,声波的传播方向被限制在平面内,因此只需要考虑平面波在二维平面内的展开。假设晶体的基元周期为$a$,则可以将平面波表示为: $$ e^{i\vec{k}\cdot\vec{r}}=e^{ik_xx}e^{ik_yy} $$ 其中$\vec{k}$为波矢,$x$和$y$为晶体平面内的坐标。将平面波代入声子晶体的动力学方程中,可以得到一个本征值问题,其解给出了声子晶体的能带结构。 在实际计算中,需要对波矢$\vec{k}$进行离散化,即将其分解为$k_x=2\pi n_x/L_x$和$k_y=2\pi n_y/L_y$,其中$L_x$和$L_y$为晶体的尺寸,$n_x$和$n_y$为整数。然后,可以将平面波展开为: $$ e^{i\vec{k}\cdot\vec{r}}=\sum_{n_x,n_y}c_{n_x,n_y}e^{i\frac{2\pi}{L_x}n_xx}e^{i\frac{2\pi}{L_y}n_yy} $$ 其中$c_{n_x,n_y}$为系数,需要通过求解本征值问题来确定。将展开后的平面波代入动力学方程中,可以得到一个矩阵本征值问题,其解给出了声子晶体的能带结构和带隙特性。 需要注意的是,由于离散化导致的误差和计算量的增加,平面波展开法在计算大尺寸的声子晶体时可能会遇到困难。因此,一些改进方法,如Wannier函数和投影算子方法,已经被提出来用于加速计算。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值