-
Notifications
You must be signed in to change notification settings - Fork 94
Rewrite Wannier interface #854
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
e7c18f2
bce94e3
8670ba2
005085c
685c877
2baacd8
5a57f2c
92315bc
407d4d8
9ca7c9f
1bb42cd
423786c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
# # Wannierization using Wannier.jl | ||
# | ||
# DFTK features an interface with the program | ||
# [Wannier.jl](https://www.wannierjl.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 `run_wannier`. | ||
# | ||
# !!! warning "No guarantees on Wannier.jl 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 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 | ||
using Plots | ||
|
||
plot_bandstructure(scfres; kline_density=10) | ||
|
||
# Now we use the `run_wannier` routine to generate all files needed by | ||
# wannier90 and to perform the Wannierization procedure using Wannier.jl. | ||
# In Wannier90's convention, 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 "wannier" under the prefix "wannier" (i.e. "wannier/wannier.win", | ||
# "wannier/wannier.wout", etc.). A different file prefix can be given with the | ||
# keyword argument `fileprefix` as shown below. | ||
# | ||
# We now solve for 5 MLWF using Wannier.jl: | ||
|
||
using Wannier # Needed to make run_wannier available | ||
|
||
# The Wannier.jl interface is very similar to the Wannier90 example, | ||
# except that the function `run_wannier` is used instead of `run_wannier90`. | ||
# To further explore the functionalities of the MLWF interface, in this example | ||
# we use SCDM to generate a better initial guess for the MLWFs | ||
# (by default, `run_wannier` will use random initial guess which is not good). | ||
# We need to first unfold the `scfres` to a MP kgrid for Wannierization, | ||
# and remove the possibly unconverged bands (bands above `scfres.n_bands_converge`) | ||
exclude_bands = DFTK._default_exclude_bands(scfres) | ||
basis, ψ, eigenvalues = DFTK.unfold_scfres_wannier(scfres, exclude_bands) | ||
Comment on lines
+59
to
+60
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need to give the argument if it's the default parameter I would say. That's why we have these There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (BTW I'm reading this top to bottom, so some of my comments might better apply to the wannierio.jl files) |
||
|
||
# Then compute the SCDM initial guess with [`compute_amn_scdm`](@ref) | ||
# Since this is an entangled case, we need a weight factor, using `erfc` function. | ||
# Note that the unit of `μ` and `σ` are `Ha`, as in the DFTK convention. | ||
# Here we choose these numbers by inspecting the band structure, you can also | ||
# test with different values to see how it affects the result. | ||
μ, σ = 0.0, 0.01 | ||
f = DFTK.scdm_f_erfc(basis, eigenvalues, μ, σ) | ||
Comment on lines
+67
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you make There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should bikeshed these names ... |
||
# and construct 5 MLWFs | ||
n_wann = 5 | ||
A = DFTK.compute_amn_scdm(basis, ψ, n_wann, f) | ||
Comment on lines
+70
to
+71
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also better spell it out |
||
# we pass the `A` matrix to `run_wannier`, so it will skip the `compute_amn` step | ||
wann_model, = run_wannier( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does it need to be called There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also why |
||
scfres; | ||
fileprefix="wannier/graphene", | ||
n_wann, | ||
A, | ||
dis_froz_max=0.1, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess you use these cryptic keyword arguments for compatability with wannier90, but I'm really not a fan of this. Can we not avoid that somehow and give it more descriptive names? If not: at least add a comment here. |
||
); | ||
# Note we unwrap the returned objects since in `:collinear` case, the | ||
# `run_wannier` will return two `Wannier.Model` objects. | ||
# As can be observed standard optional arguments for the disentanglement | ||
# can be passed directly to `run_wannier` as keyword arguments. | ||
|
||
# The MLWF centers and spreads can be obtained from | ||
Wannier.omega(wann_model) | ||
|
||
# Please refer to the [Wannier.jl documentation](https://wannierjl.org/) | ||
# for more advanced usage of the Wannier function interface. | ||
|
||
# (Delete temporary files.) | ||
rm("wannier", recursive=true) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,3 +7,8 @@ | |
return x[:, 1:size(φk, 2)] | ||
end | ||
end | ||
|
||
function ortho_lowdin(A::AbstractMatrix) | ||
F = svd(A) | ||
return F.U * F.Vt | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1 @@ | ||
# Stubs for conditionally defined functions | ||
|
||
""" | ||
Wannerize the obtained bands using wannier90. By default all converged | ||
bands from the `scfres` are employed (change with `n_bands` kwargs) | ||
and `n_wannier = n_bands` wannier functions are computed using | ||
random Gaussians as guesses. All keyword arguments supported by | ||
Wannier90 for the disentanglement may be added as keyword arguments. | ||
The function returns the `fileprefix`. | ||
|
||
!!! warning "Experimental feature" | ||
Currently this is an experimental feature, which has not yet been tested | ||
to full depth. The interface is considered unstable and may change | ||
incompatibly in the future. Use at your own risk and please report bugs | ||
in case you encounter any. | ||
""" | ||
function run_wannier90 end | ||
Comment on lines
-2
to
-17
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why did you remove that? The idea is that the function is always available in DFTK, but the methods are only added once wannier90 and wannier are loaded (using the requires magic). I would keep that also for |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
""" | ||
Call `Wannier.jl` to Wannierize the results. | ||
|
||
# Arguments | ||
- `scfres`: the result of scf. | ||
|
||
`kwargs` will be passed to [`save_wannier`](@ref). | ||
""" | ||
function run_wannier( | ||
scfres::NamedTuple; | ||
fileprefix::AbstractString="wannier/wannier", | ||
exclude_bands::AbstractArray{<:Integer}=_default_exclude_bands(scfres), | ||
kwargs..., | ||
) | ||
basis, ψ, eigenvalues = unfold_scfres_wannier(scfres, exclude_bands) | ||
|
||
n_bands = length(eigenvalues[1]) | ||
@assert haskey(kwargs, :n_wann) "Must specify `n_wann` in `kwargs`" | ||
n_wann = kwargs[:n_wann] | ||
|
||
# Although with Wannier.jl we can pass matrices in-memory, | ||
# however I still save the files so that we can restart later. | ||
if basis.model.spin_polarization == :collinear | ||
prefixes = ["$(fileprefix)_up", "$(fileprefix)_dn"] | ||
else | ||
prefixes = [fileprefix] | ||
end | ||
|
||
@timing "Compute b-vectors" begin | ||
win = get_wannier90_win(basis; num_wann=n_wann, num_bands=n_bands) | ||
# Note I am not using directly `basis.model.recip_lattice` here since | ||
# that one is in Bohr unit, while `win.unit_cell_cart` is in Angstrom. | ||
recip_lattice = compute_recip_lattice(win.unit_cell_cart) | ||
bvectors = Wannier.generate_kspace_stencil(recip_lattice, win.mp_grid, win.kpoints) | ||
# TODO I am naming kpb_G as kpb_b in WannierIO.jl for the moment, | ||
# probably going to rename it in the future. | ||
kpb_k, kpb_G = bvectors.kpb_k, bvectors.kpb_G | ||
nnkp = (; kpb_k, kpb_G) | ||
end | ||
|
||
unk = get(kwargs, :wannier_plot, false) | ||
εF = auconvert(u"eV", scfres.εF).val | ||
|
||
models = [] | ||
for (spin, prefix) in enumerate(prefixes) | ||
@timing "Compute & save Wannier matrices" win, M, A, E = save_wannier( | ||
basis, ψ, eigenvalues; fileprefix=prefix, nnkp, spin, unk, | ||
fermi_energy=εF, kwargs...) | ||
|
||
@timing "Run Wannierization" begin | ||
# I simply call disentangle which works for both isolated and | ||
# entangled cases. Also if no frozen window provided, just use | ||
# 0.5 eV above Fermi. | ||
dis_froz_max = get(win, :dis_froz_max, εF + 0.5) | ||
dis_froz_min = get(win, :dis_froz_min, -Inf) | ||
frozen_bands = Wannier.get_frozen_bands(E, dis_froz_max, dis_froz_min) | ||
|
||
atom_labels = [first(i) for i in win.atoms_frac] | ||
atom_positions = [last(i) for i in win.atoms_frac] | ||
model = Wannier.Model( | ||
win.unit_cell_cart, atom_positions, atom_labels, | ||
bvectors, M, A, E, frozen_bands) | ||
model.gauges .= Wannier.disentangle(model) | ||
|
||
push!(models, model) | ||
end | ||
end | ||
models | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To avoid code duplication here, can't we just merge it with the wannier90 example? In fact I would even suggest to make this the main example and then just add a small section, that show how one could do the same thing with wannier90, but explaining the details a second time.