def print_orbital_weights_dm_K(subdir, prefix): num_layers = 3 assert (num_layers == 3) # != 3 unimplemented work = _get_work(subdir, prefix) wannier_dir = os.path.join(work, "wannier") scf_path = os.path.join(wannier_dir, "scf.out") wout_path = os.path.join(wannier_dir, "{}.wout".format(prefix)) E_F = fermi_from_scf(scf_path) layer_orbitals = get_layer_orbitals(wout_path, num_layers) Pzs = get_layer_projections(wout_path, num_layers) Hr_path = os.path.join(wannier_dir, "{}_hr.dat".format(prefix)) Hr = extractHr(Hr_path) K_lat = np.array([1 / 3, 1 / 3, 0.0]) H_TB_K = Hk_recip(K_lat, Hr) Es, U = np.linalg.eigh(H_TB_K) top = top_valence_indices(E_F, 2 * num_layers, Es) layer_weights, layer_basis = get_layer_basis_from_dm_K(U, top, Pzs) dm_basis_weights = [ get_state_weights(layer_orbitals, s) for s in layer_basis ] print("dm basis weights") print(dm_basis_weights)
def _main(): parser = ArgumentParser(description="Update disentanglement window in W90 input") parser.add_argument('--subdir', type=str, default=None, help="Subdirectory under work_base for all job dirs") parser.add_argument('prefix', type=str, help="Prefix of system to update") parser.add_argument('outer_min', type=float, help="Distance below E_F to start outer window") parser.add_argument('outer_max', type=float, help="Distance above E_F to stop outer window") parser.add_argument('inner_min', type=float, help="Distance below E_F to start inner window") parser.add_argument('inner_max', type=float, help="Distance above E_F to stop inner window") args = parser.parse_args() gconf = global_config() base_path = os.path.expandvars(gconf["work_base"]) if args.subdir is not None: base_path = os.path.join(base_path, args.subdir) wandir = os.path.join(base_path, args.prefix, "wannier") scf_path = os.path.join(wandir, "scf.out") E_Fermi = fermi_from_scf(scf_path) outer = [args.outer_min, args.outer_max] inner = [args.inner_min, args.inner_max] win_path = os.path.join(wandir, "{}.win".format(args.prefix)) Update_Disentanglement(win_path, E_Fermi, outer, inner)
def print_orbital_weights_eigenstates_Gamma(subdir, prefix): num_layers = 3 assert (num_layers == 3) # != 3 unimplemented work = _get_work(subdir, prefix) wannier_dir = os.path.join(work, "wannier") scf_path = os.path.join(wannier_dir, "scf.out") wout_path = os.path.join(wannier_dir, "{}.wout".format(prefix)) E_F = fermi_from_scf(scf_path) layer_orbitals = get_layer_orbitals(wout_path, num_layers) Hr_path = os.path.join(wannier_dir, "{}_hr.dat".format(prefix)) Hr = extractHr(Hr_path) Gamma_lat = np.array([0.0, 0.0, 0.0]) H_TB_Gamma = Hk_recip(Gamma_lat, Hr) Es, U = np.linalg.eigh(H_TB_Gamma) top = top_valence_indices(E_F, 2 * num_layers, Es) eigenstate_weights = [ get_state_weights(layer_orbitals, U[:, [t]]) for t in top ] print("eigenstate weights") print(eigenstate_weights)
def plot_orbital_weights_K(subdir, prefix): num_layers = 3 assert (num_layers == 3) # != 3 unimplemented work = _get_work(subdir, prefix) wannier_dir = os.path.join(work, "wannier") scf_path = os.path.join(wannier_dir, "scf.out") wout_path = os.path.join(wannier_dir, "{}.wout".format(prefix)) E_F = fermi_from_scf(scf_path) layer_orbitals = get_layer_orbitals(wout_path, num_layers) Hr_path = os.path.join(wannier_dir, "{}_hr.dat".format(prefix)) Hr = extractHr(Hr_path) K_lat = np.array([1 / 3, 1 / 3, 0.0]) H_TB_K = Hk_recip(K_lat, Hr) Es, U = np.linalg.eigh(H_TB_K) top = top_valence_indices(E_F, 2 * num_layers, Es) eigenstate_weights = [ get_state_weights(layer_orbitals, U[:, [t]]) for t in top ] state_indices = [0, 0, 1, 1, 2, 2] labels = [ r"$s_1$; $(+, \uparrow)$", r"$s_1$; $(-, \downarrow)$", r"$s_2$; $(+, \uparrow)$", r"$s_2$; $(-, \downarrow)$", r"$s_3$; $(+, \uparrow)$", r"$s_3$; $(-, \downarrow)$" ] weight_keys = [ "(+, up)", "(-, down)", "(+, up)", "(-, down)", "(+, up)", "(-, down)" ] syms = ["D", "D", "o", "o", "s", "s"] colors = ["black", "red", "black", "red", "black", "red"] vals = [] for state_index, label, key, sym, color in zip(state_indices, labels, weight_keys, syms, colors): vals.append([]) for layer_index in range(num_layers): vals[-1].append(eigenstate_weights[state_index][key][layer_index]) plt.plot(range(num_layers), vals[-1], "k{}".format(sym), markerfacecolor=(1, 1, 1, 0.0), markeredgecolor=color, label=label, markersize=10) plt.xlim(0 - 0.1, num_layers - 1 + 0.1) plt.ylim(0, 1) plt.legend(loc=0) plt.savefig("{}_state_components.png".format(prefix), bbox_inches='tight', dpi=500)
def get_extrema(work, prefix, k, k_prefix, num_layers, num_valence_bands, num_conduction_bands): wannier_dir = os.path.join(work, prefix, "wannier") scf_path = os.path.join(wannier_dir, "scf.out") E_F = fermi_from_scf(scf_path) Hr = get_Hr(work, prefix) Hk = Hk_recip(k, Hr) Es, U = np.linalg.eigh(Hk) below_fermi, above_fermi = bracket_indices(Es, E_F) if num_layers != 2: raise ValueError( "get_layer_indices() unimplemented for num_layers != 2") layer_indices_up = get_layer_indices(work, prefix, 'up') layer_indices_down = get_layer_indices(work, prefix, 'down') layer_contribs_up = get_layer_contribs(layer_indices_up, U) layer_contribs_down = get_layer_contribs(layer_indices_down, U) extrema = {} def add_extrema_entries(i, band_index, vc_label): extrema["energy_{}_{}".format(vc_label, i)] = Es[band_index] for l in range(num_layers): layer_contrib = select_layer_contrib(layer_contribs_up, layer_contribs_down, None, l, band_index) extrema["contrib_layer_{}_{}_{}".format(l, vc_label, i)] = layer_contrib for spin in ['up', 'down']: spin_contrib = sum([ select_layer_contrib(layer_contribs_up, layer_contribs_down, spin, l, band_index) for l in range(num_layers) ]) extrema["contrib_spin_{}_{}_{}".format(spin, vc_label, i)] = spin_contrib for i, band_index in enumerate( range(below_fermi, below_fermi - num_valence_bands, -1)): add_extrema_entries(i, band_index, 'valence') for i, band_index in enumerate( range(above_fermi, above_fermi + num_conduction_bands)): add_extrema_entries(i, band_index, 'conduction') return extrema
def get_system_details(work, prefix): wannier_dir = os.path.join(work, prefix, "wannier") scf_path = os.path.join(wannier_dir, "scf.out") E_F = fermi_from_scf(scf_path) latVecs = _Angstrom_per_bohr * latVecs_from_scf(scf_path) D = latVecs.T R = 2.0 * np.pi * np.linalg.inv(D) K_lat = np.array([1 / 3, 1 / 3, 0.0]) K_Cart = np.dot(K_lat, R) Hr = get_Hr(work, prefix) Hk = Hk_Cart(K_Cart, Hr, latVecs) Es, U = np.linalg.eigh(Hk) return latVecs, K_Cart, Hr, Es, U, E_F
def make_effective_Hamiltonian_Gamma(subdir, prefix, top_two_only, verbose=False): num_layers = 3 work = _get_work(subdir, prefix) wannier_dir = os.path.join(work, "wannier") scf_path = os.path.join(wannier_dir, "scf.out") wout_path = os.path.join(wannier_dir, "{}.wout".format(prefix)) E_F = fermi_from_scf(scf_path) latVecs = latVecs_from_scf(scf_path) alat_Bohr = 1.0 R = 2 * np.pi * np.linalg.inv(latVecs.T) Gamma_cart = np.array([0.0, 0.0, 0.0]) K_lat = np.array([1 / 3, 1 / 3, 0.0]) K_cart = np.dot(K_lat, R) if verbose: print(K_cart) print(latVecs) upto_factor = 0.3 num_ks = 100 ks = vec_linspace(Gamma_cart, upto_factor * K_cart, num_ks) xs = np.linspace(0.0, upto_factor, num_ks) Hr_path = os.path.join(wannier_dir, "{}_hr.dat".format(prefix)) Hr = extractHr(Hr_path) if top_two_only: Pzs = [np.eye(get_total_orbitals(num_layers))] else: Pzs = get_layer_projections(wout_path, num_layers) H_TB_Gamma = Hk(Gamma_cart, Hr, latVecs) Es, U = np.linalg.eigh(H_TB_Gamma) if top_two_only: top = top_valence_indices(E_F, 2, Es) else: top = top_valence_indices(E_F, 2 * num_layers, Es) layer_weights, layer_basis = get_layer_basis_Gamma(U, top, Pzs, verbose) complement_basis_mat = nullspace(array_with_rows(layer_basis).conjugate()) complement_basis = [] for i in range(complement_basis_mat.shape[1]): v = complement_basis_mat[:, [i]] complement_basis.append(v / np.linalg.norm(v)) assert (len(layer_basis) + len(complement_basis) == 22 * num_layers) for vl in [v.conjugate().T for v in layer_basis]: for vc in complement_basis: assert (abs(np.dot(vl, vc)[0, 0]) < 1e-12) # 0th order effective Hamiltonian: H(Gamma) in layer basis. H_layer_Gamma = layer_Hamiltonian_0th_order(H_TB_Gamma, layer_basis) #E_repr = sum([Es[t] for t in top]) / len(top) E_repr = Es[top[0]] H_correction = correction_Hamiltonian_0th_order(Gamma_cart, Hr, latVecs, E_repr, complement_basis, layer_basis) H0_tot = H_layer_Gamma + H_correction H_PQ = correction_Hamiltonian_PQ(K_cart, Hr, latVecs, complement_basis, layer_basis) # Momentum expectation values <z_{lp}| dH/dk_{c}|_Gamma |z_l> ps = layer_Hamiltonian_ps(Gamma_cart, Hr, latVecs, layer_basis) ps_correction = correction_Hamiltonian_ps(Gamma_cart, Hr, latVecs, E_repr, complement_basis, layer_basis) ps_tot = deepcopy(ps) for i, v in enumerate(ps_correction): ps_tot[i] += v # Inverse effective masses <z_{lp}| d^2H/dk_{cp}dk_{c}|_Gamma |z_l> mstar_invs = layer_Hamiltonian_mstar_inverses(Gamma_cart, Hr, latVecs, layer_basis) mstar_invs_correction_base, mstar_invs_correction_other = correction_Hamiltonian_mstar_inverses( Gamma_cart, Hr, latVecs, E_repr, complement_basis, layer_basis) mstar_inv_tot = deepcopy(mstar_invs) for mstar_contrib in [ mstar_invs_correction_base, mstar_invs_correction_other ]: for k, v in mstar_contrib.items(): mstar_inv_tot[k] += v if verbose: print("H0") print(H_layer_Gamma) print("H_correction") print(H_correction) print("H_correction max") print(abs(H_correction).max()) print("H_PQ max") print(abs(H_PQ).max()) print("p") print(ps) print("ps max") print(max([abs(x).max() for x in ps])) print("ps correction") print(ps_correction) print("ps_correction max") print(max([abs(x).max() for x in ps_correction])) print("mstar_inv") print(mstar_invs) print("mstar_inv max") print(max([abs(v).max() for k, v in mstar_invs.items()])) print("mstar_inv_correction_base") print(mstar_invs_correction_base) print("mstar_inv_correction_base max") print( max([abs(v).max() for k, v in mstar_invs_correction_base.items()])) print("mstar_inv_correction_other") print(mstar_invs_correction_other) print("mstar_inv_correction_other max") print( max([abs(v).max() for k, v in mstar_invs_correction_other.items()])) # Fit quality plots. H_layers = [] for k in ks: q = k - Gamma_cart H_layers.append( H_kdotp(q, H_layer_Gamma, H_correction, ps, ps_correction, mstar_invs, mstar_invs_correction_base, mstar_invs_correction_other)) Emks, Umks = [], [] for band_index in range(len(layer_basis)): Emks.append([]) Umks.append([]) for k_index, Hk_layers in enumerate(H_layers): Es_layers, U_layers = np.linalg.eigh(Hk_layers) #print(k_index) #print("U", U) for band_index in range(len(layer_basis)): Emks[band_index].append(Es_layers) Umks[band_index].append(U_layers) for band_index in range(len(layer_basis)): plt.plot(xs, Emks[band_index]) TB_Emks = [] for m in range(len(top)): TB_Emks.append([]) for k in ks: this_H_TB_k = Hk(k, Hr, latVecs) this_Es, this_U = np.linalg.eigh(this_H_TB_k) for m, i in enumerate(top): TB_Emks[m].append(this_Es[i]) for TB_Em in TB_Emks: plt.plot(xs, TB_Em, 'k.') plt.show() # Effective masses. print("effective mass, top valence band, TB model: m^*_{xx; yy; xy}") print( effective_mass_band(lambda k: Hk(k, Hr, latVecs), Gamma_cart, top[0], alat_Bohr)) print( "effective mass, top valence band, k dot p model: m^*_{xx; yy; xy}" ) print( effective_mass_band( lambda k: H_kdotp(k - Gamma_cart, H_layer_Gamma, H_correction, ps, ps_correction, mstar_invs, mstar_invs_correction_base, mstar_invs_correction_other), Gamma_cart, len(layer_basis) - 1, alat_Bohr)) # Elements contributing to crossover scale. t_Gamma_a = H0_tot[0, 2] t_Gamma_b = H0_tot[0, 3] print("t_Gamma_a, t_Gamma_b") print(t_Gamma_a, t_Gamma_b) print("norm(t_Gamma_a)") print(abs(t_Gamma_a)) print("[t_Gamma]_{2, 4}, [t_Gamma]_{2, 5}") print(H0_tot[2, 4], H0_tot[2, 5]) t_Gamma_direct = H0_tot[0, 4] print("t_Gamma_direct, norm(t_Gamma_direct)") print(t_Gamma_direct, abs(t_Gamma_direct)) print("E_SL_Gamma") print([H0_tot[i, i] for i in range(6)]) print("H0_tot") print(H0_tot) Gamma_valence_max = Es[top[0]] print("H0") print_H0_LaTeX(H_layer_Gamma, Gamma_valence_max) print("H0_tot") print_H0_LaTeX(H0_tot, Gamma_valence_max) dump_model_np("{}_model_Gamma".format(prefix), H0_tot, ps_tot, mstar_inv_tot) return H0_tot, ps_tot, mstar_inv_tot
def _main(): parser = argparse.ArgumentParser( "Plot band structure", formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("prefix", type=str, help="Prefix for calculation") parser.add_argument( "--subdir", type=str, default=None, help="Subdirectory under work_base where calculation was run") parser.add_argument("--DFT_only", action='store_true', help="Use DFT bands only (no Wannier)") parser.add_argument("--fermi_shift", action='store_true', help="Shift plotted energies so that E_F = 0") parser.add_argument("--band_extrema", action='store_true', help="Add lines at VBM/CBM") parser.add_argument("--minE", type=float, default=None, help="Minimum energy to plot") parser.add_argument("--maxE", type=float, default=None, help="Maximum energy to plot") parser.add_argument("--plot_evecs", action='store_true', help="Plot eigenvector decomposition") parser.add_argument( "--num_layers", type=int, default=3, help="Number of layers (required if group_layer_* options given)") parser.add_argument( "--group_layer_evecs_soc", action='store_true', help="Group eigenvectors weights by layer, assuming SOC") parser.add_argument( "--group_layer_evecs_no_soc", action='store_true', help="Group eigenvectors weights by layer, assuming no SOC") parser.add_argument("--show", action='store_true', help="Show band plot instead of outputting file") args = parser.parse_args() work = _get_work(args.subdir, args.prefix) wannier_dir = os.path.join(work, "wannier") scf_path = os.path.join(wannier_dir, "scf.out") E_F = fermi_from_scf(scf_path) if args.minE is not None and args.maxE is not None: if args.fermi_shift: minE_plot = E_F + args.minE maxE_plot = E_F + args.maxE else: minE_plot = args.minE maxE_plot = args.maxE else: minE_plot, maxE_plot = None, None alat = alat_from_scf(scf_path) latVecs = latVecs_from_scf(scf_path) if args.DFT_only: Hr = None else: Hr_path = os.path.join(wannier_dir, "{}_hr.dat".format(args.prefix)) Hr = extractHr(Hr_path) bands_dir = os.path.join(work, "bands") bands_path = os.path.join(bands_dir, "{}_bands.dat".format(args.prefix)) num_bands, num_ks, qe_bands = extractQEBands(bands_path) outpath = args.prefix if args.group_layer_evecs_soc: orbitals_per_X = 6 orbitals_per_M = 10 comp_groups = make_comp_groups(orbitals_per_X, orbitals_per_M, args.num_layers) elif args.group_layer_evecs_no_soc: orbitals_per_X = 3 orbitals_per_M = 5 comp_groups = make_comp_groups(orbitals_per_X, orbitals_per_M, args.num_layers) else: comp_groups = None plotBands(qe_bands, Hr, alat, latVecs, minE_plot, maxE_plot, outpath, show=args.show, symList=band_path_labels(), fermi_energy=E_F, plot_evecs=args.plot_evecs, comp_groups=comp_groups, fermi_shift=args.fermi_shift, plot_band_extrema=args.band_extrema)
def _main(): parser = argparse.ArgumentParser( "Plot band structure for multiple electric field values", formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("prefix_E_0", type=str, help="Prefix for E = 0 calculation") parser.add_argument("prefix_E_mid", type=str, help="Prefix for E != 0 calculation, middle value") parser.add_argument("prefix_E_high", type=str, help="Prefix for E != 0 calculation, high value") parser.add_argument( "--subdir", type=str, default=None, help="Subdirectory under work_base where calculation was run") parser.add_argument( "--minE", type=float, default=1.85, help="Minimum energy to plot (using original energy zero)") parser.add_argument( "--maxE", type=float, default=5.75, help="Maximum energy to plot (using original energy zero)") parser.add_argument("--load", action='store_true', help="Load stored band data instead of recalculating") args = parser.parse_args() E_mid_val = args.prefix_E_mid.split('_')[-1] E_high_val = args.prefix_E_high.split('_')[-1] Gamma_lat = np.array([0.0, 0.0, 0.0]) M_lat = np.array([1 / 2, 0.0, 0.0]) K_lat = np.array([1 / 3, 1 / 3, 0.0]) band_path_lat = [Gamma_lat, M_lat, K_lat, Gamma_lat] band_path_labels = ["$\\Gamma$", "$M$", "$K$", "$\\Gamma$"] Nk_per_panel = 400 prefixes = [args.prefix_E_0, args.prefix_E_mid, args.prefix_E_high] labels = [ "$E = 0$", "$E = {}$ V/nm".format(E_mid_val), "$E = {}$ V/nm".format(E_high_val) ] styles = ['r-', 'b-.', 'k--'] xs, special_xs, Emks = [], [], [] if args.load: with open("Efield_bands.json", 'r') as fp: in_data = json.load(fp) xs, special_xs, Emks = list( map(lambda k: [d[k] for d in in_data], ["xs", "special_xs", "Emks"])) E_Gamma_Eperp_0 = in_data[0]["E_Gamma_Eperp_0"] else: out_data = [] for prefix_index, prefix in enumerate(prefixes): work = _get_work(args.subdir, prefix) wannier_dir = os.path.join(work, "wannier") scf_path = os.path.join(wannier_dir, "scf.out") latVecs = latVecs_from_scf(scf_path) Hr_path = os.path.join(wannier_dir, "{}_hr.dat".format(prefix)) Hr = extractHr(Hr_path) if prefix_index == 0: E_F = fermi_from_scf(scf_path) E_Gamma_Eperp_0 = get_E_Gamma(Hr, E_F) this_xs, this_special_xs, this_Emks = calculate_bands( Hr, latVecs, band_path_lat, Nk_per_panel) this_Emks = shift_Emks(this_Emks, E_Gamma_Eperp_0) xs.append(this_xs) special_xs.append(this_special_xs) Emks.append(this_Emks) out_data.append({ "prefix": prefix, "xs": this_xs, "special_xs": this_special_xs, "band_path_labels": band_path_labels, "Emks": this_Emks, "E_Gamma_Eperp_0": E_Gamma_Eperp_0 }) with open("Efield_bands.json", 'w') as fp: json.dump(out_data, fp) full_min_x, full_max_x = 0.0, 1.0 full_minE, full_maxE = args.minE - E_Gamma_Eperp_0, args.maxE - E_Gamma_Eperp_0 print("full_minE = ", full_minE) print("full_maxE = ", full_maxE) for this_xs, this_Emks, path_suffix in zip(xs, Emks, ["0.0", "0.5", "1.0"]): out_path = "Efield_bands_E_{}.csv".format(path_suffix) write_bands_csv(this_xs, this_Emks, out_path) write_bands_aux(special_xs[0], band_path_labels, "Efield_bands_aux.csv") plot_bands([xs[0], xs[-1]], special_xs[0], band_path_labels, [Emks[0], Emks[-1]], [labels[0], labels[-1]], [styles[0], styles[-1]], [full_min_x, full_max_x], [full_minE, full_maxE], "full") x_K = special_xs[0][2] zoom_xlim = [x_K - 0.05, x_K + 0.05] zoom_special_xs = [zoom_xlim[0], x_K, zoom_xlim[-1]] zoom_band_path_labels = [ "$\\leftarrow M$", "$K$", "$\\rightarrow \\Gamma$" ] zoom_ylim = [-0.3, 0.1] print("zoom_xlim = ", zoom_xlim) print("zoom_ylim = ", zoom_ylim) plot_bands(xs, zoom_special_xs, zoom_band_path_labels, Emks, labels, styles, zoom_xlim, zoom_ylim, "zoom")
def _main(): np.set_printoptions(threshold=np.inf) parser = argparse.ArgumentParser( "TMD multilayer response to electric field", formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("prefix", type=str, help="Prefix for calculation") parser.add_argument( "--subdir", type=str, default=None, help="Subdirectory under work_base where calculation was run") parser.add_argument("--num_layers", type=int, default=3, help="Number of layers") parser.add_argument("--holes", type=float, default=8e12, help="Hole concentration [cm^{-2}]") parser.add_argument("--screened", action='store_true', help="Include screening by holes") parser.add_argument( "--initial_mass", action='store_true', help= "Use effective mass at E_perp = 0, p = 0 instead of recalculating for each phi" ) parser.add_argument( "--plot_initial", action='store_true', help= "Plot initial band structure with max applied field, before charge convergence" ) args = parser.parse_args() if args.num_layers != 3: assert ("num_layers != 3 not implemented") work = _get_work(args.subdir, args.prefix) wannier_dir = os.path.join(work, "wannier") scf_path = os.path.join(wannier_dir, "scf.out") wout_path = os.path.join(wannier_dir, "{}.wout".format(args.prefix)) E_F_base = fermi_from_scf(scf_path) latVecs = latVecs_from_scf(scf_path) R = 2 * np.pi * np.linalg.inv(latVecs.T) Hr_path = os.path.join(wannier_dir, "{}_hr.dat".format(args.prefix)) Hr = extractHr(Hr_path) d_A = 6.488 # Angstrom d_bohr = _bohr_per_Angstrom * d_A hole_density_cm2 = args.holes hole_density_bohr2 = hole_density_cm2 / (10**8 * _bohr_per_Angstrom)**2 #print("hole_density_bohr2") #print(hole_density_bohr2) # Choose initial potential assuming holes are distributed uniformally. sigma_layer_initial = (1 / 3) * _e_C * hole_density_bohr2 sigmas_initial = sigma_layer_initial * np.array([1.0, 1.0, 1.0]) #print("sigmas_initial [C/bohr^2]") #print(sigmas_initial) # Dielectric constant of WSe2: # Kim et al., ACS Nano 9, 4527 (2015). # http://pubs.acs.org/doi/abs/10.1021/acsnano.5b01114 #epsilon_r = 7.2 # Effective dielectric constant from DFT (LDA). # avg(K^high_{top - bottom}, Gamma_{top - bottom}) epsilon_r = 7.87 Pzs = get_layer_projections(wout_path, args.num_layers) E_V_nms = np.linspace(0.0, 1.2, 84) if args.plot_initial: E_V_bohr = E_V_nms[-1] / (10 * _bohr_per_Angstrom) phis_initial_max = get_phis(sigmas_initial, d_bohr, E_V_bohr, epsilon_r, args.screened) plot_H_k0_phis(H_k0s, phis_initial, Pzs, band_indices, E_F_base) return if hole_density_cm2 > 0.0: tol_abs = 1e-6 * sum(sigmas_initial) else: tol_abs = 1e-12 * _e_C tol_rel = 1e-6 distr_args = [] for E_V_nm in E_V_nms: distr_args.append([ E_V_nm, R, Hr, latVecs, E_F_base, sigmas_initial, Pzs, hole_density_bohr2, d_bohr, epsilon_r, tol_abs, tol_rel, args.initial_mass, args.screened ]) with Pool() as p: nh_Es = p.starmap(hole_distribution, distr_args) plot_results(nh_Es, hole_density_bohr2, hole_density_cm2, epsilon_r, args.screened, E_V_nms)
def _main(): np.set_printoptions(threshold=np.inf) parser = argparse.ArgumentParser( "Plot band structure", formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("prefix", type=str, help="Prefix for calculation") parser.add_argument( "--subdir", type=str, default=None, help="Subdirectory under work_base where calculation was run") parser.add_argument("--num_layers", type=int, default=3, help="Number of layers") args = parser.parse_args() work = _get_work(args.subdir, args.prefix) wannier_dir = os.path.join(work, "wannier") scf_path = os.path.join(wannier_dir, "scf.out") wout_path = os.path.join(wannier_dir, "{}.wout".format(args.prefix)) E_F = fermi_from_scf(scf_path) Hr_path = os.path.join(wannier_dir, "{}_hr.dat".format(args.prefix)) Hr = extractHr(Hr_path) Gamma = np.array([0.0, 0.0, 0.0]) K = np.array([1 / 3, 1 / 3, 0.0]) upto_factor = 0.3 num_ks = 100 ks = vec_linspace(Gamma, upto_factor * K, num_ks) xs = np.linspace(0.0, upto_factor, num_ks) assert (Hr[(0, 0, 0)][0].shape[0] == get_total_orbitals(args.num_layers)) Pzs = get_layer_projections(wout_path, args.num_layers) num_top_bands = 2 * args.num_layers for k in ks: print("k = {}".format(k)) Hk = Hk_recip(k, Hr) Es, U = np.linalg.eigh(Hk) top = top_valence_indices(E_F, num_top_bands, Es) print("top orbitals = ", top, "energies = ", [Es[t] for t in top]) tb_states = [] for restricted_index_n, band_n in enumerate(top): state_n = U[:, [band_n]] print("orbital", band_n) for i, v in enumerate(state_n[:, 0]): print(i, v) tb_states.append(state_n) dm = density_matrix(tb_states, [1] * len(tb_states)) for z, Pz in enumerate(Pzs): print("z = {}".format(z)) proj_dm = np.dot(Pz, np.dot(dm, Pz)) proj_dm_evals, proj_dm_evecs = np.linalg.eigh(proj_dm) print("proj_dm_evals") print(proj_dm_evals) print("proj_dm_evecs") for i in range(proj_dm_evecs.shape[1]): for j in range(proj_dm_evecs.shape[0]): print(j, proj_dm_evecs[j, i])
def _main(): np.set_printoptions(threshold=np.inf) parser = argparse.ArgumentParser( "Plot band structure", formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("prefix", type=str, help="Prefix for calculation") parser.add_argument( "--subdir", type=str, default=None, help="Subdirectory under work_base where calculation was run") parser.add_argument("--num_layers", type=int, default=3, help="Number of layers") args = parser.parse_args() work = _get_work(args.subdir, args.prefix) wannier_dir = os.path.join(work, "wannier") scf_path = os.path.join(wannier_dir, "scf.out") wout_path = os.path.join(wannier_dir, "{}.wout".format(args.prefix)) E_F = fermi_from_scf(scf_path) Hr_path = os.path.join(wannier_dir, "{}_hr.dat".format(args.prefix)) Hr = extractHr(Hr_path) K = np.array([1 / 3, 1 / 3, 0.0]) reduction_factor = 0.7 num_ks = 100 ks = vec_linspace(K, reduction_factor * K, num_ks) xs = np.linspace(1, reduction_factor, num_ks) assert (Hr[(0, 0, 0)][0].shape[0] == get_total_orbitals(args.num_layers)) Pzs = get_layer_projections(wout_path, args.num_layers) spin_operators = Pauli_over_full_basis(get_total_orbitals(args.num_layers)) num_top_bands = 2 * args.num_layers deviations, proj_overlaps, spins = [], [], [] spins_non_layer_renormalized = [] for z in range(len(Pzs)): deviations.append([]) proj_overlaps.append([]) spins.append([]) spins_non_layer_renormalized.append([]) for k in ks: print("k = {}".format(k)) for z, Pz in enumerate(Pzs): Hk = Hk_recip(k, Hr) Es, U = np.linalg.eigh(Hk) top = top_valence_indices(E_F, num_top_bands, Es) print("top orbitals = ", top, "energies = ", [Es[t] for t in top]) proj_dms = [] # "dm" == density matrix kz_spins = [] kz_spins_non_layer_renormalized = [] for restricted_index_n, band_n in enumerate(top): state_n = U[:, [band_n]] print("orbital", band_n) for i, v in enumerate(state_n[:, 0]): print(i, v) dm_n = density_matrix([state_n], [1]) proj_dm = np.dot(Pz, np.dot(dm_n, Pz)) proj_dms.append(proj_dm) kz_spins.append(expectation_normalized(proj_dm, spin_operators)) kz_spins_non_layer_renormalized.append([ np.trace(np.dot(proj_dm, sigma)) for sigma in spin_operators ]) spins[z].append(kz_spins) spins_non_layer_renormalized[z].append( kz_spins_non_layer_renormalized) proj_overlap = np.zeros([num_top_bands, num_top_bands], dtype=np.complex128) for restricted_index_np in range(len(top)): for restricted_index_n in range(len(top)): dm_np, dm_n = proj_dms[restricted_index_np], proj_dms[ restricted_index_n] dm_np_norm = dm_np / np.trace(dm_np) dm_n_norm = dm_n / np.trace(dm_n) print( "np, n, Tr rho_np^z, Tr rho_n^z, Tr rho_np^z rho_n^z", restricted_index_np, restricted_index_n, np.trace(dm_np), np.trace(dm_n), np.trace(np.dot(dm_np, dm_n))) proj_overlap[restricted_index_np, restricted_index_n] = np.trace( np.dot(dm_np_norm, dm_n_norm)) print("z = {}".format(z)) print("overlap = ") print(proj_overlap) # proj_overlap should be real. eps_abs = 1e-12 assert (all([x.imag < eps_abs for x in np.nditer(proj_overlap)])) # Compute deviation from desired 'all ones' value of proj_overlap. deviation = sum([abs(1 - x) for x in np.nditer(proj_overlap)]) print("sum of abs(deviation) = {}".format(deviation)) proj_overlaps[z].append(proj_overlap) deviations[z].append(deviation) # Plot deviation from 1-band-per-layer representability. for z in range(len(Pzs)): plt.plot(xs, deviations[z], label="deviations for layer {}".format(z)) plt.legend(loc=0) plt.savefig("{}_deviation_separability_K.png".format(args.prefix), bbox_inches='tight', dpi=500) plt.clf() # Plot layer-projected band overlaps. for z in range(len(Pzs)): for ip, i in [(0, 1), (1, 2), (0, 2)]: ys = [v[ip, i].real for v in proj_overlaps[z]] plt.plot(xs, ys, label="({}, {}) overlap".format(ip, i)) plt.legend(loc=0) plt.savefig("{}_overlap_z_{}_separability_K.png".format( args.prefix, z), bbox_inches='tight', dpi=500) plt.clf() # Plot layer-projected spin expectation values. for z in range(len(Pzs)): colors = ['k', 'g', 'b'] styles = ['-', '--', '.'] for band_n, style in zip([0, 1, 2], styles): for (spin_index, spin_dir), color in zip(enumerate(['x', 'y', 'z']), colors): ys = [ spins[z][k][band_n][spin_index].real for k in range(num_ks) ] plt.plot(xs, ys, '{}{}'.format(color, style), label="Band {} spin {}".format(band_n, spin_dir)) plt.legend(loc=0) plt.savefig("{}_z_{}_spin_real.png".format(args.prefix, z), bbox_inches='tight', dpi=500) plt.clf() for band_n, style in zip([0, 1, 2], styles): for (spin_index, spin_dir), color in zip(enumerate(['x', 'y', 'z']), colors): ys = [ spins[z][k][band_n][spin_index].imag for k in range(num_ks) ] plt.plot(xs, ys, '{}{}'.format(color, style), label="Band {} spin {}".format(band_n, spin_dir)) plt.legend(loc=0) plt.savefig("{}_z_{}_spin_imag.png".format(args.prefix, z), bbox_inches='tight', dpi=500) plt.clf() # Plot sum of spin expectation values over layers. colors = ['k', 'g', 'b'] styles = ['-', '--', '.'] for (spin_index, spin_dir), color in zip(enumerate(['x', 'y', 'z']), colors): for band_n, style in zip([0, 1, 2], styles): ys = np.zeros([num_ks], dtype=np.float64) for z in range(len(Pzs)): ys += np.array([ spins_non_layer_renormalized[z][k][band_n][spin_index].real for k in range(num_ks) ]) plt.plot(xs, ys, '{}{}'.format(color, style), label="Band {} spin {}".format(band_n, spin_dir)) plt.legend(loc=0) plt.savefig("{}_z_total_spin_real.png".format(args.prefix), bbox_inches='tight', dpi=500) plt.clf()
def _main(): parser = ArgumentParser( description="Plot partial DOS from Wannier Hamiltonian.") parser.add_argument('prefix', type=str, help="Name of the system.") parser.add_argument( '--num', action='store_true', help= "Calculate number of states from tetrahedron method and DOS from n(E)." ) parser.add_argument( '--num_electrons', type=float, default=None, help= "Number of valence electrons in Wannier bands (must be specified if --num is used)" ) parser.add_argument( '--load', action='store_true', help="Load pre-calculated DOS/n data instead of calculating.") parser.add_argument('--spin_polarized', help="Use spin-polarized system.", action='store_true') parser.add_argument( '--groups', type=str, default=None, help= "Partial DOS values to be added together (ex: '0,1,2;3,4,5' reports pdos 0+1+2 and 3+4+5)" ) parser.add_argument('--group_labels', type=str, default=None, help="Labels for groups specified by --groups") parser.add_argument('--minE', type=float, help="Minimum energy to plot.", default=None) parser.add_argument('--maxE', type=float, help="Maximum energy to plot.", default=None) parser.add_argument( '--num_dos', type=int, default=3000, help="Number of energy values at which to calculate density of states") parser.add_argument('--n0', type=int, default=8, help="Number of k-points in each direction") parser.add_argument('--sigma', type=float, default=0.1, help="Gaussian delta-function broadening constant") parser.add_argument('--cwannier_path', type=str, default=None, help="Path to cwannier/ directory") args = parser.parse_args() base_dir = _base_dir() cwannier_path = os.path.join(base_dir, "cwannier") work_base = os.path.expandvars(_global_config()["work_base"]) wannier_dir = os.path.join(work_base, args.prefix, "wannier") outPath = "{}_{}".format(args.prefix, str(args.n0)) # Get Fermi energy from SCF output. scfPath = os.path.join(wannier_dir, "scf.out") fermi = fermi_from_scf(scfPath) alat = alat_from_scf(scfPath) D = alat * D_from_scf(scfPath) R = 2.0 * np.pi * np.linalg.inv(D) # Load Wannier Hamiltonian and calculate DOS. if not args.spin_polarized: spins = [""] else: spins = ["_up", "_dn"] # Calculate PDOS values. pdos, Es = [], [] pnum, n_fermi = [], [] fermi_from_num = 0.0 for sp in spins: Hr_path = os.path.join(wannier_dir, "{}{}_hr.dat".format(args.prefix, sp)) if args.num: pnum_outpath = outPath + "_pnum_data" if not args.load: this_pnum, this_Es, n_fermi, fermi_from_num = PartialNum( Hr_path, R, args.minE, args.maxE, args.num_dos, args.num_electrons, args.n0, cwannier_path, pnum_outpath) else: this_pnum, this_Es, n_fermi, fermi_from_num = _extract_pnum_vals( pnum_outpath) this_pdos = pdos_from_num(this_pnum, this_Es) pnum.append(this_pnum) pdos.append(this_pdos) Es.append(this_Es) else: pdos_outpath = outPath + "_pdos_data" if not args.load: this_pdos, this_Es = PartialDos(Hr_path, R, args.minE, args.maxE, args.num_dos, args.sigma, args.n0, cwannier_path, pdos_outpath) else: this_pdos, this_Es = _extract_pdos_vals(pdos_outpath) pdos.append(this_pdos) Es.append(this_Es) # Aggregate groups. group_pdos, group_pnum = [], [] if args.groups != None: group_vals = args.groups.split(";") num_groups = len(group_vals) for sp_index, sp_pdos in enumerate(pdos): sp_pnum = pnum[sp_index] group_pdos.append([]) if args.num: group_pnum.append([]) for group_index, group in enumerate(group_vals): group_pdos[sp_index].append([]) if args.num: group_pnum[sp_index].append([]) group_band_indices = list(map(int, group.split(","))) for E_index in range(len(Es[sp_index])): total_dos, total_num = 0.0, 0.0 for band_index in group_band_indices: total_dos += sp_pdos[band_index][E_index] if args.num: total_num += sp_pnum[band_index][E_index] group_pdos[sp_index][group_index].append(total_dos) if args.num: group_pnum[sp_index][group_index].append(total_num) else: group_pdos = pdos group_pnum = pnum if args.group_labels is not None: group_labels_split = args.group_labels.split(";") else: group_labels_split = None if args.num: # Echo n(E_Fermi) print("Got Fermi energy from n(E) = {}; scf Fermi energy = {}".format( str(fermi_from_num), str(fermi))) print("n(E) at the E_F from n(E) = {}".format(str(n_fermi))) # Make pnum plot. plot_pnum_outpath = outPath + "_pnum" plot_pvals(group_pnum, Es, fermi_from_num, plot_pnum_outpath, "$n(E)$", args.minE, args.maxE, group_labels_split) # Make PDOS plot. plot_pdos_outpath = outPath + "_pdos" plot_pvals(group_pdos, Es, fermi, plot_pdos_outpath, "PDOS [eV$^{-1}$]", args.minE, args.maxE, group_labels_split) # Export data to allow plotting with different styling. # NOTE - only supports case with non-spin-polarized or noncollinear spin cases. # TODO - support collinear spin-polarized case. Es_shifted = [E - fermi for E in Es[0]] export_pdos_data(outPath, Es_shifted, group_pdos[0], group_labels_split)
def get_occupations(prefix, subdir, screened, d_bohr, hole_density_bohr2, sigmas_initial, epsilon_r): work = _get_work(subdir, prefix) wannier_dir = os.path.join(work, "wannier") scf_path = os.path.join(wannier_dir, "scf.out") wout_path = os.path.join(wannier_dir, "{}.wout".format(prefix)) E_F = fermi_from_scf(scf_path) latVecs = latVecs_from_scf(scf_path) alat_Bohr = 1.0 R = 2 * np.pi * np.linalg.inv(latVecs.T) Gamma_cart = np.array([0.0, 0.0, 0.0]) K_lat = np.array([1 / 3, 1 / 3, 0.0]) K_cart = np.dot(K_lat, R) Hr_path = os.path.join(wannier_dir, "{}_hr.dat".format(prefix)) Hr = extractHr(Hr_path) H_TB_Gamma = Hk(Gamma_cart, Hr, latVecs) Es_Gamma, U_Gamma = np.linalg.eigh(H_TB_Gamma) H_TB_K = Hk(K_cart, Hr, latVecs) Es_K, U_K = np.linalg.eigh(H_TB_K) top_Gamma = top_valence_indices(E_F, 2, Es_Gamma) top_K = top_valence_indices(E_F, 3, Es_Gamma) assert (top_Gamma == [41, 40]) assert (top_K == [41, 40, 39]) E_top_Gamma, E_top_K = Es_Gamma[top_Gamma[0]], Es_K[top_K[0]] Ediff = E_top_Gamma - E_top_K def Hfn(k): return Hk(k, Hr, latVecs) mstar_top_Gamma = effective_mass_band(Hfn, Gamma_cart, top_Gamma[0], alat_Bohr) mstar_top_K = effective_mass_band(Hfn, K_cart, top_K[0], alat_Bohr) mstar_Gamma = mstar_top_Gamma[0] mstar_K = mstar_top_K[0] num_layers = 3 Pzs = get_layer_projections(wout_path, num_layers) if hole_density_bohr2 > 0.0: tol_abs = 1e-6 * sum(sigmas_initial) else: tol_abs = 1e-12 * _e_C tol_rel = 1e-6 nh_Gamma, nh_K, E_Gamma, E_K = hole_distribution(0.0, R, Hr, latVecs, E_F, sigmas_initial, Pzs, hole_density_bohr2, d_bohr, epsilon_r, tol_abs, tol_rel, screened=screened) if hole_density_bohr2 > 0.0: nh_Gamma_frac = nh_Gamma / hole_density_bohr2 nh_K_frac = nh_K / hole_density_bohr2 else: nh_Gamma_frac = 0.0 nh_K_frac = 0.0 return Ediff, mstar_Gamma, mstar_K, nh_Gamma_frac, nh_K_frac, E_Gamma, E_K
def _main(): parser = argparse.ArgumentParser( "Plot band structure", formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("prefix", type=str, help="Prefix for calculation") parser.add_argument( "--subdir", type=str, default=None, help="Subdirectory under work_base where calculation was run") parser.add_argument( "--num_layers", type=int, default=3, help="Number of layers (required if group_layer_* options given)") args = parser.parse_args() if args.num_layers != 3: raise ValueError("mirror check not implemented for num_layers != 3") work = _get_work(args.subdir, args.prefix) wannier_dir = os.path.join(work, "wannier") scf_path = os.path.join(wannier_dir, "scf.out") E_F = fermi_from_scf(scf_path) Hr_path = os.path.join(wannier_dir, "{}_hr.dat".format(args.prefix)) Hr = extractHr(Hr_path) K = np.array([1 / 3, 1 / 3, 0.0]) reduction_factor = 0.7 num_ks = 100 ks = vec_linspace(K, reduction_factor * K, num_ks) xs = np.linspace(1, reduction_factor, num_ks) basis = basis_state_labels(args.num_layers) M = mirror_op() # Assume SOC present and that model has 2*3 X(p) orbitals per layer # and 2*5 M(d) in canonical Wannier90 order. # Assumes atoms are ordered with all Xs first, then all Ms, and within # M/X groups the atoms are in layer order. orbitals_per_X = 6 orbitals_per_M = 10 # Base index for orbitals of each layer: base_orbitals = [ args.num_layers * 2 * orbitals_per_X + z * orbitals_per_M for z in range(args.num_layers) ] # Orbitals contributing to j = +5/2 state: x2y2_up = [base + 6 for base in base_orbitals] xy_up = [base + 8 for base in base_orbitals] x2y2_dn = [base + 7 for base in base_orbitals] xy_dn = [base + 9 for base in base_orbitals] num_top_bands = 2 * args.num_layers weights = [] for i in range(num_top_bands): weights.append([]) for k in ks: Hk = Hk_recip(k, Hr) Es, U = np.linalg.eigh(Hk) top = top_valence_indices(E_F, num_top_bands, Es) for i, band in enumerate(top): total = 0 state = U[:, band] mirror_eval = np.dot(state.conjugate().T, np.dot(M, state))[0, 0] print("band {}; mirror <v|M|v> = {}".format(band, mirror_eval)) print("mirror deviation elements") #Mdev = np.dot(M, state) - mirror_signs[i] * state #for n, v in enumerate(Mdev): # if abs(v)**2 > 1e-3: # print(n, basis[n], v, abs(v)**2) print("band, orb, orb_label, weight, evec comp") #for n, v in enumerate(state): # if abs(v)**2 > 1e-2: # print(band, n, basis[n], abs(v)**2, v) for n, v in enumerate(state): print(band, n, basis[n], abs(v)**2, v) for z, (n1_up, n2_up, n1_dn, n2_dn) in enumerate(zip(x2y2_up, xy_up, x2y2_dn, xy_dn)): # Appropriate arrangement for K. # TODO - swap for -K. # U[n, i] = <n|i> --> <i|n> = U[n, i].conjugate() if z % 2 == 0: evec_comp = ( 1 / np.sqrt(2)) * (U[n1_dn, band].conjugate() - 1j * U[n2_dn, band].conjugate()) else: evec_comp = ( 1 / np.sqrt(2)) * (U[n1_up, band].conjugate() + 1j * U[n2_up, band].conjugate()) total += abs(evec_comp)**2 weights[i].append(1 - total) for i, band_weights in enumerate(weights): plt.plot(xs, band_weights, label="Band {}".format(i)) plt.legend(loc=0) plt.xlabel("k / K", fontsize='large') plt.ylabel("Weight outside l_z = +/- 2, up/down group") plt.savefig("model_weights_K.png", bbox_inches='tight', dpi=500)
def _main(): parser = argparse.ArgumentParser( "Plot band structure", formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("prefix", type=str, help="Prefix for calculation") parser.add_argument( "--subdir", type=str, default=None, help="Subdirectory under work_base where calculation was run") parser.add_argument( "--num_layers", type=int, default=3, help="Number of layers (required if group_layer_* options given)") args = parser.parse_args() if args.num_layers != 3: raise ValueError("mirror check not implemented for num_layers != 3") work = _get_work(args.subdir, args.prefix) wannier_dir = os.path.join(work, "wannier") scf_path = os.path.join(wannier_dir, "scf.out") E_F = fermi_from_scf(scf_path) Hr_path = os.path.join(wannier_dir, "{}_hr.dat".format(args.prefix)) Hr = extractHr(Hr_path) Gamma = np.array([0.0, 0.0, 0.0]) K = np.array([1 / 3, 1 / 3, 0.0]) max_K_factor = 0.3 num_ks = 2 ks = vec_linspace(Gamma, max_K_factor * K, num_ks) xs = np.linspace(0.0, max_K_factor, num_ks) basis = basis_state_labels(args.num_layers) M = mirror_op() # Assume SOC present and that model has 2*3 X(p) orbitals per layer # and 2*5 M(d) in canonical Wannier90 order. # Assumes atoms are ordered with all Xs first, then all Ms, and within # M/X groups the atoms are in layer order. orbitals_per_X = 6 orbitals_per_M = 10 # Base index for orbitals of each layer: X_base_orbitals = [ z * orbitals_per_X for z in range(1, 2 * args.num_layers - 1) ] M_base_orbitals = [ args.num_layers * 2 * orbitals_per_X + z * orbitals_per_M for z in range(args.num_layers) ] pz_up = [n for n in X_base_orbitals] pz_dn = [n + 1 for n in X_base_orbitals] dz2_up = [n for n in M_base_orbitals] dz2_dn = [n + 1 for n in M_base_orbitals] #print(pz_up) #print(pz_dn) #print(dz2_up) #print(dz2_dn) orbital_group = list(itertools.chain(pz_up, pz_dn, dz2_up, dz2_dn)) num_top_bands = 2 * args.num_layers weights = [] for i in range(num_top_bands): weights.append([]) for k in ks: print("k = ", k) Hk = Hk_recip(k, Hr) Es, U = np.linalg.eigh(Hk) top = top_valence_indices(E_F, num_top_bands, Es) print("top valence", top, [Es[t] for t in top]) mirror_signs = [1, 1, -1, -1, 1, 1] for i, band in enumerate(top): state = U[:, band] mirror_eval = np.dot(state.conjugate().T, np.dot(M, state))[0, 0] print("band {}; mirror <v|M|v> = {}".format(band, mirror_eval)) print("mirror deviation elements") Mdev = np.dot(M, state) - mirror_signs[i] * state for n, v in enumerate(Mdev): if abs(v)**2 > 1e-3: print(n, basis[n], v, abs(v)**2) print("band, orb, orb_label, weight, evec comp") #for n, v in enumerate(state): # if abs(v)**2 > 1e-2: # print(band, n, basis[n], abs(v)**2, v) for n, v in enumerate(state): print(band, n, basis[n], abs(v)**2, v) total = 0 for n in orbital_group: evec_comp = U[n, band].conjugate() total += abs(evec_comp)**2 #print(total) weights[i].append(1 - total) #for i, band_weights in enumerate(weights): # plt.plot(xs, band_weights, label="Band {}".format(i)) plt.plot(xs, weights[0], 'b.', label="Band 0") plt.plot(xs, weights[1], 'g--', label="Band 1") plt.legend(loc=0) plt.xlabel("k / K", fontsize='large') plt.ylabel("Weight outside inner p_z, dz^2") plt.savefig("model_weights_Gamma.png", bbox_inches='tight', dpi=500)
def get_gaps(work, prefix, layer_threshold, k, spin_valence=None, spin_conduction=None, use_curvature=True): wannier_dir = os.path.join(work, prefix, "wannier") scf_path = os.path.join(wannier_dir, "scf.out") E_F = fermi_from_scf(scf_path) alat_Bohr = alat_from_scf(scf_path) D = D_from_scf(scf_path) R = 2*np.pi*np.linalg.inv(D) k_Cart = np.dot(np.array(k), R) layer_indices_up = get_layer_indices(work, prefix, 'up') layer_indices_down = get_layer_indices(work, prefix, 'down') Hr = get_Hr(work, prefix) # note: # rotated K 2pi/3: K_R2 = (-2/3, 1/3, 0.0) # rotated K 4pi/3: K_R4 = (1/3, -2/3, 0.0) Hk = Hk_recip(k, Hr) w, U = np.linalg.eigh(Hk) conduction, valence = layer_band_extrema(w, U, E_F, layer_indices_up, layer_indices_down, layer_threshold, spin_valence, spin_conduction) conduction_curvature = [None, None] valence_curvature = [None, None] for l in [0, 1]: valence_curvature[l] = get_curvature(D, Hr, k_Cart, valence[l]) conduction_curvature[l] = get_curvature(D, Hr, k_Cart, conduction[l]) gaps = {} layer_labels = ["0", "1"] # Generate gap for each layer pair. Keys have the form: "(0, 1)/(0, 1)". for cond_layer_index, cond_layer_label in enumerate(layer_labels): for valence_layer_index, valence_layer_label in enumerate(layer_labels): k = "{}/{}".format(cond_layer_label, valence_layer_label) gaps[k] = float(w[conduction[cond_layer_index]] - w[valence[valence_layer_index]]) # Generate band extremum energy for each choice of layer and valence or conduction. vc_labels = ["valence", "conduction"] all_vc_data = [valence, conduction] for vc_label, vc_data in zip(vc_labels, all_vc_data): for layer_index, layer_label in enumerate(layer_labels): k = "{}_{}".format(layer_label, vc_label) gaps[k] = float(w[vc_data[layer_index]]) # Minimum of the spin-orbit split partner of the lowest conduction band. # If layer gap is smaller than SO splitting, need to skip a band to find partner. conduction_min = min(conduction[0], conduction[1]) if abs(conduction[0] - conduction[1]) == 1: conduction_min_partner = conduction_min + 2 else: conduction_min_partner = conduction_min + 1 gaps["conduction_min_partner"] = float(w[conduction_min_partner]) if use_curvature: add_curvature(gaps, valence_curvature, conduction_curvature, alat_Bohr) return gaps
def _main(): np.set_printoptions(threshold=np.inf) parser = argparse.ArgumentParser( "TMD multilayer response to electric field", formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("prefix", type=str, help="Prefix for calculation") parser.add_argument( "--subdir", type=str, default=None, help="Subdirectory under work_base where calculation was run") parser.add_argument("--holes", type=float, default=8e12, help="Hole concentration [cm^{-2}]") parser.add_argument("--screened", action='store_true', help="Include screening by holes") parser.add_argument( "--diag_mass_only", action='store_true', help= "Include q variation only through qx^2, qy^2 mass terms on diagonal") args = parser.parse_args() work = _get_work(args.subdir, args.prefix) wannier_dir = os.path.join(work, "wannier") scf_path = os.path.join(wannier_dir, "scf.out") E_F_base = fermi_from_scf(scf_path) d_A = 6.488 # Angstrom d_bohr = _bohr_per_Angstrom * d_A hole_density_cm2 = args.holes hole_density_bohr2 = hole_density_cm2 / (10**8 * _bohr_per_Angstrom)**2 #print("hole_density_bohr2") #print(hole_density_bohr2) # Choose initial potential assuming holes are distributed uniformally. sigma_layer_initial = (1 / 3) * _e_C * hole_density_bohr2 sigmas_initial = sigma_layer_initial * np.array([1.0, 1.0, 1.0]) #print("sigmas_initial [C/bohr^2]") #print(sigmas_initial) # Dielectric constant of WSe2: # Kim et al., ACS Nano 9, 4527 (2015). # http://pubs.acs.org/doi/abs/10.1021/acsnano.5b01114 #epsilon_r = 7.2 # Effective dielectric constant from DFT (LDA). # avg(K^high_{top - bottom}, Gamma_{top - bottom}) epsilon_r = 7.87 H0_tot_Gamma, ps_tot_Gamma, mstar_inv_tot_Gamma = make_effective_Hamiltonian_Gamma( args.subdir, args.prefix, top_two_only=False, verbose=False) K_lat = np.array([1 / 3, 1 / 3, 0.0]) H0_tot_K, ps_tot_K, mstar_inv_tot_K = make_effective_Hamiltonian_K( K_lat, args.subdir, args.prefix, get_layer_basis_from_dm_K, verbose=False) Kp_lat = np.array([-1 / 3, -1 / 3, 0.0]) H0_tot_Kp, ps_tot_Kp, mstar_inv_tot_Kp = make_effective_Hamiltonian_K( Kp_lat, args.subdir, args.prefix, get_layer_basis_from_dm_K, verbose=False) H0s = [H0_tot_Gamma, H0_tot_K, H0_tot_Kp] if args.diag_mass_only: ps = [ np.zeros([6, 6], dtype=np.complex128), np.zeros([6, 6], dtype=np.complex128), np.zeros([6, 6], dtype=np.complex128) ] def extract_diag(mstar_inv): result = {} for cp in range(2): for c in range(2): result[(cp, c)] = np.zeros([6, 6], dtype=np.complex128) if c == cp: for i in range(6): result[(cp, c)][i, i] = mstar_inv[(cp, c)][i, i] return result mstar_invs = list( map(extract_diag, [mstar_inv_tot_Gamma, mstar_inv_tot_K, mstar_inv_tot_Kp])) else: ps = [ps_tot_Gamma, ps_tot_K, ps_tot_Kp] mstar_invs = [mstar_inv_tot_Gamma, mstar_inv_tot_K, mstar_inv_tot_Kp] Pzs = layer_projections() E_V_nms = np.linspace(0.0, 1.2, 84) if hole_density_cm2 > 0.0: tol_abs = 1e-6 * sum(sigmas_initial) else: tol_abs = 1e-12 * _e_C tol_rel = 1e-6 distr_args = [] for E_V_nm in E_V_nms: distr_args.append([ E_V_nm, H0s, ps, mstar_invs, E_F_base, sigmas_initial, Pzs, hole_density_bohr2, d_bohr, epsilon_r, tol_abs, tol_rel, args.screened ]) with Pool() as p: nh_Es = p.starmap(hole_distribution, distr_args) plot_results(nh_Es, hole_density_bohr2, hole_density_cm2, epsilon_r, args.screened, E_V_nms)
def make_effective_Hamiltonian_K(k0_lat, subdir, prefix, get_layer_basis, verbose=False): num_layers = 3 work = _get_work(subdir, prefix) wannier_dir = os.path.join(work, "wannier") scf_path = os.path.join(wannier_dir, "scf.out") wout_path = os.path.join(wannier_dir, "{}.wout".format(prefix)) E_F = fermi_from_scf(scf_path) latVecs = latVecs_from_scf(scf_path) alat_Bohr = 1.0 R = 2 * np.pi * np.linalg.inv(latVecs.T) K_cart = np.dot(k0_lat, R) if verbose: print(K_cart) print(latVecs) reduction_factor = 0.7 num_ks = 100 ks = vec_linspace(K_cart, reduction_factor * K_cart, num_ks) xs = np.linspace(1, reduction_factor, num_ks) Hr_path = os.path.join(wannier_dir, "{}_hr.dat".format(prefix)) Hr = extractHr(Hr_path) Pzs = get_layer_projections(wout_path, num_layers) H_TB_K = Hk(K_cart, Hr, latVecs) Es, U = np.linalg.eigh(H_TB_K) top = top_valence_indices(E_F, 2 * num_layers, Es) layer_weights, layer_basis = get_layer_basis(U, top, Pzs, verbose) complement_basis_mat = nullspace(array_with_rows(layer_basis).conjugate()) complement_basis = [] for i in range(complement_basis_mat.shape[1]): v = complement_basis_mat[:, [i]] complement_basis.append(v / np.linalg.norm(v)) assert (len(layer_basis) + len(complement_basis) == 22 * num_layers) for vl in [v.conjugate().T for v in layer_basis]: for vc in complement_basis: assert (abs(np.dot(vl, vc)[0, 0]) < 1e-12) # 0th order effective Hamiltonian: H(K) in layer basis. H_layer_K = layer_Hamiltonian_0th_order(H_TB_K, layer_basis) #E_repr = sum([Es[t] for t in top]) / len(top) E_repr = Es[top[0]] H_correction = correction_Hamiltonian_0th_order(K_cart, Hr, latVecs, E_repr, complement_basis, layer_basis) H0_tot = H_layer_K + H_correction H_PQ = correction_Hamiltonian_PQ(K_cart, Hr, latVecs, complement_basis, layer_basis) # Momentum expectation values <z_{lp}| dH/dk_{c}|_K |z_l> ps = layer_Hamiltonian_ps(K_cart, Hr, latVecs, layer_basis) ps_correction = correction_Hamiltonian_ps(K_cart, Hr, latVecs, E_repr, complement_basis, layer_basis) ps_tot = deepcopy(ps) for i, v in enumerate(ps_correction): ps_tot[i] += v # Inverse effective masses <z_{lp}| d^2H/dk_{cp}dk_{c}|_K |z_l> mstar_invs = layer_Hamiltonian_mstar_inverses(K_cart, Hr, latVecs, layer_basis) mstar_invs_correction_base, mstar_invs_correction_other = correction_Hamiltonian_mstar_inverses( K_cart, Hr, latVecs, E_repr, complement_basis, layer_basis) mstar_inv_tot = deepcopy(mstar_invs) for mstar_contrib in [ mstar_invs_correction_base, mstar_invs_correction_other ]: for k, v in mstar_contrib.items(): mstar_inv_tot[k] += v if verbose: print("H0") print(H_layer_K) print("H_correction") print(H_correction) print("H_correction max") print(abs(H_correction).max()) print("H_PQ max") print(abs(H_PQ).max()) print("ps") print(ps) print("ps max") print(max([abs(x).max() for x in ps])) print("ps correction") print(ps_correction) print("ps_correction max") print(max([abs(x).max() for x in ps_correction])) print("mstar_inv") print(mstar_invs) print("mstar_inv max") print(max([abs(v).max() for k, v in mstar_invs.items()])) print("mstar_inv_correction_base") print(mstar_invs_correction_base) print("mstar_inv_correction_base max") print( max([abs(v).max() for k, v in mstar_invs_correction_base.items()])) print("mstar_inv_correction_other") print(mstar_invs_correction_other) print("mstar_inv_correction_other max") print( max([abs(v).max() for k, v in mstar_invs_correction_other.items()])) # Fit quality plots. H_layers = [] for k in ks: q = k - K_cart H_layers.append( H_kdotp(q, H_layer_K, H_correction, ps, ps_correction, mstar_invs, mstar_invs_correction_base, mstar_invs_correction_other)) Emks, Umks = [], [] for band_index in range(len(layer_basis)): Emks.append([]) Umks.append([]) for k_index, Hk_layers in enumerate(H_layers): Es, U = np.linalg.eigh(Hk_layers) #print(k_index) #print("U", U) for band_index in range(len(layer_basis)): Emks[band_index].append(Es) Umks[band_index].append(U) for band_index in range(len(layer_basis)): plt.plot(xs, Emks[band_index]) TB_Emks = [] for m in range(len(top)): TB_Emks.append([]) for k in ks: this_H_TB_k = Hk(k, Hr, latVecs) this_Es, this_U = np.linalg.eigh(this_H_TB_k) for m, i in enumerate(top): TB_Emks[m].append(this_Es[i]) for TB_Em in TB_Emks: plt.plot(xs, TB_Em, 'k.') plt.show() plt.clf() # Effective masses. print( "effective mass, top valence band, TB model: m^*_{xx; yy; xy} / m_e" ) mstar_TB = effective_mass_band(lambda k: Hk(k, Hr, latVecs), K_cart, top[0], alat_Bohr) print(mstar_TB) print( "effective mass, top valence band, k dot p model: m^*_{xx; yy; xy} / m_e" ) mstar_kdotp = effective_mass_band( lambda k: H_kdotp(k - K_cart, H_layer_K, H_correction, ps, ps_correction, mstar_invs, mstar_invs_correction_base, mstar_invs_correction_other), K_cart, len(layer_basis) - 1, alat_Bohr) print(mstar_kdotp) # Elements contributing to crossover scale. t_K = H0_tot[1, 2] t_K_25 = H0_tot[2, 5] Delta_K = H0_tot[1, 1] - H0_tot[2, 2] lambda_SO = H0_tot[1, 1] - H0_tot[0, 0] t_K_direct = H0_tot[1, 5] print("t_K, Delta_K, lambda_SO") print(t_K, Delta_K, lambda_SO) print("t_K_25") print(t_K_25) print("norm(t_K), norm(t_K_25)") print(abs(t_K), abs(t_K_25)) print("t_K_direct, norm(t_K_direct)") print(t_K_direct, abs(t_K_direct)) print("E_SL_K top") print([H0_tot[i, i] for i in [1, 3, 5]]) print("E_SL_K bottom") print([H0_tot[i, i] for i in [0, 2, 4]]) # Elements contributing to effective dielectric constant. print("Layer on-site energy differences:") print( "Top group of bands: middle layer - bottom layer; top layer - middle layer" ) print(H0_tot[3, 3] - H0_tot[1, 1], H0_tot[5, 5] - H0_tot[3, 3]) print( "Bottom group of bands: middle layer - bottom layer; top layer - middle layer" ) print(H0_tot[2, 2] - H0_tot[0, 0], H0_tot[4, 4] - H0_tot[2, 2]) H_TB_Gamma = Hk_recip(np.array([0.0, 0.0, 0.0]), Hr) Es_Gamma, U_Gamma = np.linalg.eigh(H_TB_Gamma) Gamma_valence_max = Es_Gamma[top[0]] print("H0") print_H0_LaTeX(H_layer_K, Gamma_valence_max) print("H0_tot") print_H0_LaTeX(H0_tot, Gamma_valence_max) dump_model_np("{}_model_K".format(prefix), H0_tot, ps_tot, mstar_inv_tot) phases = {(i, j): cmath.phase(H0_tot[i, j]) for i, j in [(0, 3), (1, 2), (2, 5), (3, 4)]} phase_factors = list( map(lambda x: np.exp(-1j * x), [ -phases[(0, 3)], -phases[(1, 2)], 0.0, 0.0, phases[(3, 4)], phases[(2, 5)] ])) phase_U = np.diag(phase_factors) H0_tot_real_hop = np.dot(phase_U.conjugate().T, np.dot(H0_tot, phase_U)) print("H0_tot_real_hop") print_H0_LaTeX(H0_tot_real_hop, Gamma_valence_max) return H0_tot, ps_tot, mstar_inv_tot