-
Notifications
You must be signed in to change notification settings - Fork 94
Fix compute_fermi_level for FermiZeroTemperature and collinear spins #928
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 7 commits
7f142d2
e6c9f2a
16d2818
862eaa0
3fccd53
f6deab0
20824f2
4b158db
6fe210a
2ea59a8
0dc770b
004eadd
d34d51a
2cbb7d0
921ce2f
af5f835
fa6629d
a454b50
88326c1
7e7d1cd
7c9b6c9
d34be28
728b4d8
b49c889
a4fb489
6e8d9de
4877610
551c83c
609aa13
5749fce
66e1b70
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 |
---|---|---|
|
@@ -137,34 +137,39 @@ function compute_fermi_level(basis::PlaneWaveBasis{T}, eigenvalues, ::FermiZeroT | |
temperature, smearing, tol_n_elec) where {T} | ||
filled_occ = filled_occupation(basis.model) | ||
n_electrons = basis.model.n_electrons | ||
n_spin = basis.model.n_spin_components | ||
@assert iszero(temperature) | ||
|
||
# Sanity check that we can indeed fill the appropriate number of states | ||
if n_electrons % (n_spin * filled_occ) != 0 | ||
if n_electrons % (filled_occ) != 0 | ||
error("$n_electrons electrons cannot be attained by filling states with " * | ||
"occupation $filled_occ. Typically this indicates that you need to put " * | ||
"a temperature or switch to a calculation with collinear spin polarization.") | ||
end | ||
n_fill = div(n_electrons, n_spin * filled_occ, RoundUp) | ||
|
||
# For zero temperature, two cases arise: either there are as many bands | ||
# as electrons, in which case we set εF to the highest energy level | ||
# reached, or there are unoccupied conduction bands and we take | ||
# εF as the midpoint between valence and conduction bands. | ||
if n_fill == length(eigenvalues[1]) | ||
εF = maximum(maximum, eigenvalues) + 1 | ||
εF = mpi_max(εF, basis.comm_kpts) | ||
|
||
all_eigenvalues = sort(vcat(eigenvalues...)) | ||
|
||
# Bissection method to find the index of the eigenvalue such that excess_n_electrons = 0 | ||
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. One s at bisection, and there's too much space between find and the |
||
i_min = 1 | ||
i_max = length(all_eigenvalues) | ||
|
||
if excess_n_electrons(basis, eigenvalues, all_eigenvalues[i_max]; temperature, smearing) == 0 | ||
εF = all_eigenvalues[i_max] | ||
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 used +1 before but I'm not sure why, this is fine. |
||
else | ||
# highest occupied energy level | ||
HOMO = maximum([εk[n_fill] for εk in eigenvalues]) | ||
HOMO = mpi_max(HOMO, basis.comm_kpts) | ||
# lowest unoccupied energy level, be careful that not all k-points | ||
# might have at least n_fill+1 energy levels so we have to take care | ||
# of that by specifying init to minimum | ||
LUMO = minimum(minimum.([εk[n_fill+1:end] for εk in eigenvalues]; init=T(Inf))) | ||
LUMO = mpi_min(LUMO, basis.comm_kpts) | ||
εF = (HOMO + LUMO) / 2 | ||
while i_max - i_min > 1 | ||
i = div(i_min+i_max, 2) | ||
εF = all_eigenvalues[i] | ||
if excess_n_electrons(basis, eigenvalues, εF; temperature, smearing) <= 0 | ||
i_min = i | ||
else | ||
i_max = i | ||
end | ||
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. searchsortedfirst 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. searchsorted qui renvoie tous les indices |
||
end | ||
εF = 1/2*(all_eigenvalues[i_min]+all_eigenvalues[i_max]) | ||
end | ||
|
||
if !allequal(compute_occupation(basis, eigenvalues, εF; temperature, smearing).occupation) | ||
@warn("Not all kpoints have the same number of occupied states, which could mean "* | ||
"that a metallic system is treated at zero temperature.") | ||
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 want this to be fine for an insulating spin-polarized system (ie where occupations are different between different spins, but not different kpoints) |
||
end | ||
|
||
excess(εF) = excess_n_electrons(basis, eigenvalues, εF; temperature, smearing) | ||
|
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.
Careful that this will not work with MPI (hopefully the tests should catch this)
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.
You need to wrap and call MPI.Allgatherv here, so that each process does a bisection on the full array. (You cannot do a Gatherv because excess_n_electrons calls mpi_sum and will hang if not all processes participate.)