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 AtomsBuilder
using DFTK
using LinearAlgebra
using PseudoPotentialData
pseudopotentials = PseudoFamily("dojo.nc.sr.pbesol.v0_4_1.standard.upf")
model = model_DFT(bulk(:Si); functionals=PBEsol(), pseudopotentials)
basis = PlaneWaveBasis(model; Ecut=5, kgrid=[3, 3, 3])
# Convergence we desire in the density
tol = 1e-61.0e-6Density-based self-consistent field
scfres_scf = self_consistent_field(basis; tol);n Energy log10(ΔE) log10(Δρ) Diag Δtime
--- --------------- --------- --------- ---- ------
1 -8.397893623604 -0.90 5.2 27.0ms
2 -8.400241063616 -2.63 -1.74 1.0 19.4ms
3 -8.400403411189 -3.79 -2.97 1.5 20.5ms
4 -8.400427813025 -4.61 -2.95 3.2 25.2ms
5 -8.400427945534 -6.88 -3.06 1.0 68.6ms
6 -8.400428145969 -6.70 -4.81 1.0 19.7ms
7 -8.400428151760 -8.24 -4.42 3.2 25.5ms
8 -8.400428152189 -9.37 -5.33 1.0 19.7ms
9 -8.400428152207 -10.76 -6.16 1.0 19.6ms
Potential-based SCF
scfres_scfv = DFTK.scf_potential_mixing(basis; tol);n Energy log10(ΔE) log10(Δρ) α Diag Δtime
--- --------------- --------- --------- ---- ---- ------
1 -8.397830086846 -0.90 5.2 1.71s
2 -8.400378716699 -2.59 -1.78 0.80 2.2 504ms
3 -8.400422820048 -4.36 -3.01 0.80 1.0 216ms
4 -8.400428098144 -5.28 -3.45 0.80 2.5 21.0ms
5 -8.400428148663 -7.30 -4.61 0.80 1.5 18.2ms
6 -8.400428152178 -8.45 -5.71 0.80 2.0 19.9ms
7 -8.400428152209 -10.52 -6.31 0.80 3.0 22.0ms
Direct minimization
scfres_dm = direct_minimization(basis; tol);┌ Warning: x_tol is deprecated. Use x_abstol or x_reltol instead. The provided value (-1) will be used as x_abstol.
└ @ Optim ~/.julia/packages/Optim/gmigl/src/types.jl:110
┌ Warning: f_tol is deprecated. Use f_abstol or f_reltol instead. The provided value (-1) will be used as f_reltol.
└ @ Optim ~/.julia/packages/Optim/gmigl/src/types.jl:120
n Energy log10(ΔE) log10(Δρ) Δtime
--- --------------- --------- --------- ------
1 +0.676711003861 -1.14 3.42s
2 -1.674584149586 0.37 -0.64 149ms
3 -4.398523426463 0.44 -0.39 43.9ms
4 -5.779794607133 0.14 -0.43 44.1ms
5 -7.382924313603 0.20 -0.62 78.8ms
6 -7.908752217167 -0.28 -1.02 47.3ms
7 -8.088043251853 -0.75 -1.38 32.6ms
8 -8.193777142342 -0.98 -1.78 32.6ms
9 -8.264169493573 -1.15 -1.81 33.1ms
10 -8.322153408959 -1.24 -1.80 56.0ms
11 -8.359285921040 -1.43 -2.13 33.6ms
12 -8.381528551771 -1.65 -2.06 33.8ms
13 -8.392393201711 -1.96 -2.24 33.0ms
14 -8.397729601180 -2.27 -2.50 33.4ms
15 -8.399587507666 -2.73 -2.60 33.1ms
16 -8.400162781466 -3.24 -2.84 41.0ms
17 -8.400343668241 -3.74 -2.93 33.3ms
18 -8.400405513536 -4.21 -3.22 33.1ms
19 -8.400415601890 -5.00 -3.41 33.0ms
20 -8.400423960312 -5.08 -3.74 38.6ms
21 -8.400425663148 -5.77 -3.86 32.9ms
22 -8.400427291341 -5.79 -4.25 33.2ms
23 -8.400427692196 -6.40 -4.23 33.3ms
24 -8.400427980587 -6.54 -4.86 33.0ms
25 -8.400428056878 -7.12 -4.61 40.6ms
26 -8.400428120768 -7.19 -5.31 33.2ms
27 -8.400428134585 -7.86 -4.93 33.1ms
28 -8.400428146810 -7.91 -5.72 32.9ms
29 -8.400428149313 -8.60 -5.28 39.0ms
30 -8.400428151308 -8.70 -5.96 33.1ms
31 -8.400428151764 -9.34 -5.82 33.3ms
32 -8.400428152060 -9.53 -6.54 33.4ms
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 -8.397851628949 -0.90 5.2 49.7ms
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 -8.400427980482 -1.78 11.7s
2 -8.400428152209 -6.77 -4.02 3.69s
3 -8.400428152209 -14.75 -7.83 91.5ms
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.0152472189209338e-6
|ρ_newton - ρ_scfv| = 1.4784777299239654e-7
|ρ_newton - ρ_dm| = 1.1344652303167526e-6