-
Notifications
You must be signed in to change notification settings - Fork 89
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add basic Wannier.jl integration (#899)
Co-authored-by: Junfeng Qiao <[email protected]> Co-authored-by: Michael F. Herbst <[email protected]>
- Loading branch information
1 parent
b93b849
commit d48e5ef
Showing
15 changed files
with
458 additions
and
154 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
# # Wannierization using Wannier.jl or Wannier90 | ||
# | ||
# DFTK features an interface with the programs | ||
# [Wannier.jl](https://wannierjl.org) and [Wannier90](http://www.wannier.org/), | ||
# in order to compute maximally-localized Wannier functions (MLWFs) | ||
# from an initial self consistent field calculation. | ||
# All processes are handled by calling the routine `wannier_model` (for Wannier.jl) or `run_wannier90` (for Wannier90). | ||
# | ||
# !!! warning "No guarantees on Wannier interface" | ||
# This code is at an early stage and has so far not been fully tested. | ||
# Bugs are likely and we welcome issues in case you find any! | ||
# | ||
# This example shows how to obtain the MLWFs corresponding | ||
# to the first five bands of graphene. Since the bands 2 to 11 are entangled, | ||
# 15 bands are first computed to obtain 5 MLWFs by a disantanglement procedure. | ||
|
||
using DFTK | ||
using Plots | ||
using Unitful | ||
using UnitfulAtomic | ||
|
||
d = 10u"Å" | ||
a = 2.641u"Å" # Graphene Lattice constant | ||
lattice = [a -a/2 0; | ||
0 √3*a/2 0; | ||
0 0 d] | ||
|
||
C = ElementPsp(:C; psp=load_psp("hgh/pbe/c-q4")) | ||
atoms = [C, C] | ||
positions = [[0.0, 0.0, 0.0], [1//3, 2//3, 0.0]] | ||
model = model_PBE(lattice, atoms, positions) | ||
basis = PlaneWaveBasis(model; Ecut=15, kgrid=[5, 5, 1]) | ||
nbandsalg = AdaptiveBands(basis.model; n_bands_converge=15) | ||
scfres = self_consistent_field(basis; nbandsalg, tol=1e-5); | ||
|
||
# Plot bandstructure of the system | ||
|
||
bands = compute_bands(scfres; kline_density=10) | ||
plot_bandstructure(bands) | ||
|
||
# ## Wannierization with Wannier.jl | ||
# | ||
# Now we use the `wannier_model` routine to generate a Wannier.jl model | ||
# that can be used to perform the wannierization procedure. | ||
# For now, this model generation produces file in the Wannier90 convention, | ||
# where all files are named with the same prefix and only differ by | ||
# their extensions. By default all generated input and output files are stored | ||
# in the subfolder "wannierjl" under the prefix "wannier" (i.e. "wannierjl/wannier.win", | ||
# "wannierjl/wannier.wout", etc.). A different file prefix can be given with the | ||
# keyword argument `fileprefix` as shown below. | ||
# | ||
# We now produce a simple Wannier model for 5 MLFWs. | ||
# | ||
# For a good MLWF, we need to provide initial projections that resemble the expected shape | ||
# of the Wannier functions. | ||
# Here we will use: | ||
# - 3 bond-centered 2s hydrogenic orbitals for the expected σ bonds | ||
# - 2 atom-centered 2pz hydrogenic orbitals for the expected π bands | ||
|
||
using Wannier # Needed to make Wannier.Model available | ||
|
||
# From chemical intuition, we know that the bonds with the lowest energy are: | ||
# - the 3 σ bonds, | ||
# - the π and π* bonds. | ||
# We provide relevant initial projections to help Wannierization | ||
# converge to functions with a similar shape. | ||
s_guess(center) = DFTK.HydrogenicWannierProjection(center, 2, 0, 0, C.Z) | ||
pz_guess(center) = DFTK.HydrogenicWannierProjection(center, 2, 1, 0, C.Z) | ||
projections = [ | ||
## Note: fractional coordinates for the centers! | ||
## 3 bond-centered 2s hydrogenic orbitals to imitate σ bonds | ||
s_guess((positions[1] + positions[2]) / 2), | ||
s_guess((positions[1] + positions[2] + [0, -1, 0]) / 2), | ||
s_guess((positions[1] + positions[2] + [-1, -1, 0]) / 2), | ||
## 2 atom-centered 2pz hydrogenic orbitals | ||
pz_guess(positions[1]), | ||
pz_guess(positions[2]), | ||
] | ||
|
||
# Wannierize: | ||
wannier_model = Wannier.Model(scfres; | ||
fileprefix="wannier/graphene", | ||
n_bands=scfres.n_bands_converge, | ||
n_wannier=5, | ||
projections, | ||
dis_froz_max=ustrip(auconvert(u"eV", scfres.εF))+1) # maximum frozen window, for example 1 eV above Fermi level | ||
|
||
# Once we have the `wannier_model`, we can use the functions in the Wannier.jl package: | ||
# | ||
# Compute MLWF: | ||
U = disentangle(wannier_model, max_iter=200); | ||
|
||
# Inspect localization before and after Wannierization: | ||
omega(wannier_model) | ||
omega(wannier_model, U) | ||
|
||
# Build a Wannier interpolation model: | ||
kpath = irrfbz_path(model) | ||
interp_model = Wannier.InterpModel(wannier_model; kpath=kpath) | ||
|
||
# And so on... | ||
# Refer to the Wannier.jl documentation for further examples. | ||
|
||
# (Delete temporary files when done.) | ||
rm("wannier", recursive=true) | ||
|
||
# ### Custom initial guesses | ||
# | ||
# We can also provide custom initial guesses for Wannierization, | ||
# by passing a callable function in the `projections` array. | ||
# The function receives the basis and a list of points (fractional coordinates in reciprocal space), | ||
# and returns the Fourier transform of the initial guess function evaluated at each point. | ||
# | ||
# For example, we could use Gaussians for the σ and pz guesses with the following code: | ||
s_guess(center) = DFTK.GaussianWannierProjection(center) | ||
function pz_guess(center) | ||
## Approximate with two Gaussians offset by 0.5 Å from the center of the atom | ||
offset = model.inv_lattice * [0, 0, austrip(0.5u"Å")] | ||
center1 = center + offset | ||
center2 = center - offset | ||
## Build the custom projector | ||
(basis, qs) -> DFTK.GaussianWannierProjection(center1)(basis, qs) - DFTK.GaussianWannierProjection(center2)(basis, qs) | ||
end | ||
## Feed to Wannier via the `projections` as before... | ||
|
||
# This example assumes that Wannier.jl version 0.3.2 is used, | ||
# and may need to be updated to accomodate for changes in Wannier.jl. | ||
# | ||
# Note: Some parameters supported by Wannier90 may have to be passed to Wannier.jl differently, | ||
# for example the max number of iterations is passed to `disentangle` in Wannier.jl, | ||
# but as `num_iter` to `run_wannier90`. | ||
|
||
# ## Wannierization with Wannier90 | ||
# | ||
# We can use the `run_wannier90` routine to generate all required files and perform the wannierization procedure: | ||
|
||
using wannier90_jll # Needed to make run_wannier90 available | ||
run_wannier90(scfres; | ||
fileprefix="wannier/graphene", | ||
n_wannier=5, | ||
projections, | ||
num_print_cycles=25, | ||
num_iter=200, | ||
## | ||
dis_win_max=19.0, | ||
dis_froz_max=ustrip(auconvert(u"eV", scfres.εF))+1, # 1 eV above Fermi level | ||
dis_num_iter=300, | ||
dis_mix_ratio=1.0, | ||
## | ||
wannier_plot=true, | ||
wannier_plot_format="cube", | ||
wannier_plot_supercell=5, | ||
write_xyz=true, | ||
translate_home_cell=true, | ||
); | ||
|
||
# As can be observed standard optional arguments for the disentanglement | ||
# can be passed directly to `run_wannier90` as keyword arguments. | ||
|
||
# (Delete temporary files.) | ||
rm("wannier", recursive=true) |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
module DFTKWannier90Ext | ||
|
||
using DFTK | ||
import wannier90_jll | ||
|
||
""" | ||
Run the Wannierization procedure with Wannier90. | ||
""" | ||
@DFTK.timing function DFTK.run_wannier90(scfres; | ||
n_bands=scfres.n_bands_converge, | ||
n_wannier=n_bands, | ||
projections=DFTK.default_wannier_centers(n_wannier), | ||
fileprefix=joinpath("wannier90", "wannier"), | ||
wannier_plot=false, | ||
kwargs...) | ||
|
||
prefix, dir = basename(fileprefix), dirname(fileprefix) | ||
|
||
# Prepare files | ||
DFTK.write_wannier90_files(scfres; n_bands, n_wannier, projections, fileprefix, wannier_plot, kwargs...) do | ||
run(Cmd(`$(wannier90_jll.wannier90()) -pp $prefix`; dir)) | ||
DFTK.read_w90_nnkp(fileprefix) | ||
end | ||
|
||
# Run Wannierisation procedure | ||
@DFTK.timing "Wannierization" begin | ||
run(Cmd(`$(wannier90_jll.wannier90()) $prefix`; dir)) | ||
end | ||
fileprefix | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
module DFTKWannierExt | ||
|
||
using DFTK | ||
import Wannier | ||
|
||
""" | ||
Use Wannier.jl to read a .win file and produce the nnkp data (i.e. the b-vectors used in the Mmn matrix). | ||
""" | ||
function get_nnkpt_from_wannier(fileprefix) | ||
model = Wannier.read_w90(fileprefix; amn=false, mmn=false, eig=false) | ||
bvectors = model.bvectors | ||
|
||
nnkpts = reduce(vcat, | ||
map(1:model.n_kpts) do ik | ||
zp = zip(bvectors.kpb_k[:, ik], eachcol(bvectors.kpb_b[:, :, ik])) | ||
map(zp) do (ikpb, G_shift) | ||
(ik, ikpb, G_shift=copy(G_shift)) | ||
end | ||
end) | ||
|
||
(nntot=model.n_bvecs, nnkpts) | ||
end | ||
|
||
""" | ||
Build a Wannier.jl model that can be used for Wannierization. | ||
""" | ||
@DFTK.timing function Wannier.Model(scfres; | ||
n_bands=scfres.n_bands_converge, | ||
n_wannier=n_bands, | ||
projections=DFTK.default_wannier_centers(n_wannier), | ||
fileprefix=joinpath("wannierjl", "wannier"), | ||
wannier_plot=false, | ||
kwargs...) | ||
# Write the files | ||
DFTK.write_wannier90_files(scfres; n_bands, n_wannier, projections, fileprefix, wannier_plot, kwargs...) do | ||
get_nnkpt_from_wannier(fileprefix) | ||
end | ||
|
||
# Read Wannier.jl model | ||
Wannier.read_w90(fileprefix) | ||
end | ||
|
||
end |
Oops, something went wrong.