Comparison of DFT solvers
We compare four different approaches for solving the DFT minimisation problem, namely a density-based SCF, a potential-based SCF, direct minimisation and Newton.
First we setup our problem
using DFTK
using LinearAlgebra
a = 10.26 # Silicon lattice constant in Bohr
lattice = a / 2 * [[0 1 1.];
[1 0 1.];
[1 1 0.]]
Si = ElementPsp(:Si; psp=load_psp("hgh/lda/Si-q4"))
atoms = [Si, Si]
positions = [ones(3)/8, -ones(3)/8]
model = model_LDA(lattice, atoms, positions)
basis = PlaneWaveBasis(model; Ecut=5, kgrid=[3, 3, 3])
# Convergence we desire in the density
tol = 1e-6
1.0e-6
Density-based self-consistent field
scfres_scf = self_consistent_field(basis; tol);
n Energy log10(ΔE) log10(Δρ) Diag Δtime
--- --------------- --------- --------- ---- ------
1 -7.847784922373 -0.70 4.8 35.9ms
2 -7.852412836877 -2.33 -1.53 1.0 18.5ms
3 -7.852611196162 -3.70 -2.54 1.0 18.2ms
4 -7.852645346415 -4.47 -2.78 2.0 23.4ms
5 -7.852646081798 -6.13 -2.87 1.0 18.4ms
6 -7.852646668236 -6.23 -3.95 1.0 18.3ms
7 -7.852646686048 -7.75 -4.43 1.8 22.3ms
8 -7.852646686694 -9.19 -5.43 1.2 19.8ms
9 -7.852646686727 -10.48 -5.56 2.5 25.6ms
10 -7.852646686729 -11.58 -5.95 1.0 19.3ms
11 -7.852646686730 -12.34 -6.59 1.0 18.8ms
Potential-based SCF
scfres_scfv = DFTK.scf_potential_mixing(basis; tol);
n Energy log10(ΔE) log10(Δρ) α Diag Δtime
--- --------------- --------- --------- ---- ---- ------
1 -7.847781266583 -0.70 4.8 36.1ms
2 -7.852561457636 -2.32 -1.62 0.80 2.0 22.3ms
3 -7.852641075024 -4.10 -2.71 0.80 1.0 17.1ms
4 -7.852646501951 -5.27 -3.38 0.80 1.8 21.0ms
5 -7.852646681880 -6.74 -4.42 0.80 1.8 21.0ms
6 -7.852646686619 -8.32 -4.87 0.80 2.2 24.0ms
7 -7.852646686726 -9.97 -5.51 0.80 1.2 18.5ms
8 -7.852646686729 -11.47 -6.62 0.80 1.5 19.6ms
Direct minimization
Note: Unlike the other algorithms, tolerance for this one is in the energy, thus we square the density tolerance value to be roughly equivalent.
scfres_dm = direct_minimization(basis; tol=tol^2);
n Energy log10(ΔE) log10(Δρ) Δtime
--- --------------- --------- --------- ------
1 +1.286355930195 -1.01 57.0ms
2 -1.299846290199 0.41 -0.64 25.8ms
3 -3.573127666365 0.36 -0.43 33.5ms
4 -4.536687730223 -0.02 -0.57 33.5ms
5 -6.378414191374 0.27 -0.67 33.4ms
6 -7.112116295715 -0.13 -1.07 33.5ms
7 -7.435383518221 -0.49 -1.49 25.3ms
8 -7.634760662205 -0.70 -1.92 25.5ms
9 -7.709576554902 -1.13 -2.17 25.5ms
10 -7.738598312409 -1.54 -2.18 25.3ms
11 -7.759474704592 -1.68 -2.10 26.2ms
12 -7.781226858798 -1.66 -2.08 25.3ms
13 -7.809504486942 -1.55 -2.09 25.3ms
14 -7.827450132110 -1.75 -2.28 25.2ms
15 -7.838075909678 -1.97 -2.50 30.5ms
16 -7.846000975481 -2.10 -2.86 34.3ms
17 -7.850571298907 -2.34 -2.92 27.1ms
18 -7.852072079404 -2.82 -2.97 25.5ms
19 -7.852467760838 -3.40 -3.15 25.4ms
20 -7.852599963932 -3.88 -3.50 25.4ms
21 -7.852636332571 -4.44 -4.06 25.3ms
22 -7.852643497006 -5.14 -4.13 25.2ms
23 -7.852645747267 -5.65 -4.41 25.3ms
24 -7.852646331541 -6.23 -5.17 25.3ms
25 -7.852646549487 -6.66 -5.07 25.3ms
26 -7.852646639926 -7.04 -5.68 25.4ms
27 -7.852646667546 -7.56 -5.86 25.5ms
28 -7.852646680096 -7.90 -5.85 25.5ms
29 -7.852646684011 -8.41 -6.49 25.9ms
30 -7.852646685608 -8.80 -6.12 25.8ms
31 -7.852646686297 -9.16 -6.61 25.6ms
32 -7.852646686553 -9.59 -6.68 25.6ms
33 -7.852646686670 -9.93 -6.66 25.7ms
34 -7.852646686712 -10.38 -7.25 25.5ms
35 -7.852646686724 -10.92 -7.43 25.6ms
36 -7.852646686728 -11.42 -7.30 25.4ms
37 -7.852646686729 -11.83 -7.56 25.6ms
38 -7.852646686730 -12.27 -8.25 25.7ms
39 -7.852646686730 -12.76 -7.93 25.6ms
40 -7.852646686730 -13.30 -8.22 25.5ms
41 -7.852646686730 -14.05 -9.08 25.6ms
42 -7.852646686730 -14.35 -9.13 25.7ms
43 -7.852646686730 -14.75 -9.21 33.7ms
44 -7.852646686730 -14.75 -9.17 25.4ms
45 -7.852646686730 + -Inf -9.96 33.4ms
46 -7.852646686730 + -Inf -9.78 33.5ms
┌ Warning: DM not converged.
└ @ DFTK ~/work/DFTK.jl/DFTK.jl/src/scf/scf_callbacks.jl:60
Newton algorithm
Start not too far from the solution to ensure convergence: We run first a very crude SCF to get close and then switch to Newton.
scfres_start = self_consistent_field(basis; tol=0.5);
n Energy log10(ΔE) log10(Δρ) Diag Δtime
--- --------------- --------- --------- ---- ------
1 -7.847775126146 -0.70 4.8 35.8ms
Remove the virtual orbitals (which Newton cannot treat yet)
ψ = DFTK.select_occupied_orbitals(basis, scfres_start.ψ, scfres_start.occupation).ψ
scfres_newton = newton(basis, ψ; tol);
n Energy log10(ΔE) log10(Δρ) Δtime
--- --------------- --------- --------- ------
1 -7.852645843584 -1.63 390ms
2 -7.852646686730 -6.07 -3.69 289ms
3 -7.852646686730 -13.23 -7.20 112ms
Comparison of results
println("|ρ_newton - ρ_scf| = ", norm(scfres_newton.ρ - scfres_scf.ρ))
println("|ρ_newton - ρ_scfv| = ", norm(scfres_newton.ρ - scfres_scfv.ρ))
println("|ρ_newton - ρ_dm| = ", norm(scfres_newton.ρ - scfres_dm.ρ))
|ρ_newton - ρ_scf| = 2.4563028441990545e-7
|ρ_newton - ρ_scfv| = 5.821154799600917e-7
|ρ_newton - ρ_dm| = 5.998030954344661e-10