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.397835367120 -0.89 5.0 27.0ms
2 -8.400261736631 -2.62 -1.73 1.0 88.3ms
3 -8.400401250669 -3.86 -2.88 1.2 20.2ms
4 -8.400427754327 -4.58 -2.91 3.2 25.4ms
5 -8.400427885269 -6.88 -2.97 1.0 19.8ms
6 -8.400428144088 -6.59 -4.66 1.0 19.9ms
7 -8.400428151667 -8.12 -4.37 3.0 25.7ms
8 -8.400428152184 -9.29 -5.32 1.0 51.3ms
9 -8.400428152205 -10.68 -5.88 1.5 21.5ms
10 -8.400428152209 -11.45 -5.98 2.0 24.2ms
11 -8.400428152209 -12.46 -6.85 1.0 20.7ms
Potential-based SCF
scfres_scfv = DFTK.scf_potential_mixing(basis; tol);n Energy log10(ΔE) log10(Δρ) α Diag Δtime
--- --------------- --------- --------- ---- ---- ------
1 -8.397872626871 -0.90 5.2 1.85s
2 -8.400384914352 -2.60 -1.78 0.80 2.0 524ms
3 -8.400422691503 -4.42 -3.00 0.80 1.0 228ms
4 -8.400428104593 -5.27 -3.41 0.80 2.2 21.0ms
5 -8.400428148663 -7.36 -4.57 0.80 1.0 17.3ms
6 -8.400428152179 -8.45 -5.74 0.80 2.8 21.8ms
7 -8.400428152209 -10.53 -6.12 0.80 2.8 22.3ms
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 +1.018398868414 -1.05 3.73s
2 -1.628498611167 0.42 -0.69 161ms
3 -4.444848762327 0.45 -0.37 45.2ms
4 -6.117351721311 0.22 -0.50 45.6ms
5 -7.598689816813 0.17 -0.76 80.7ms
6 -8.059468101271 -0.34 -1.20 33.4ms
7 -8.249154659435 -0.72 -1.50 33.3ms
8 -8.322020221663 -1.14 -1.76 33.6ms
9 -8.366455367347 -1.35 -2.09 33.5ms
10 -8.380860911353 -1.84 -2.33 54.9ms
11 -8.391157430699 -1.99 -2.33 33.4ms
12 -8.395488231763 -2.36 -2.56 33.4ms
13 -8.398123700406 -2.58 -3.15 33.8ms
14 -8.399364218982 -2.91 -3.01 33.5ms
15 -8.399895445802 -3.27 -3.42 33.5ms
16 -8.400188276385 -3.53 -3.15 39.5ms
17 -8.400320927741 -3.88 -3.94 33.7ms
18 -8.400385963111 -4.19 -3.56 33.4ms
19 -8.400410183550 -4.62 -3.80 33.2ms
20 -8.400419558876 -5.03 -4.12 39.2ms
21 -8.400424550286 -5.30 -4.48 34.2ms
22 -8.400426073289 -5.82 -4.55 33.5ms
23 -8.400427410717 -5.87 -4.56 33.3ms
24 -8.400427751617 -6.47 -5.07 38.9ms
25 -8.400427987597 -6.63 -5.04 33.6ms
26 -8.400428068159 -7.09 -5.15 33.4ms
27 -8.400428116181 -7.32 -5.54 33.2ms
28 -8.400428136412 -7.69 -5.24 39.0ms
29 -8.400428144687 -8.08 -5.44 42.0ms
30 -8.400428148879 -8.38 -5.97 33.8ms
31 -8.400428150457 -8.80 -5.93 33.3ms
32 -8.400428151542 -8.96 -6.23 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.397810849394 -0.90 4.8 27.1ms
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.400427946144 -1.78 9.84s
2 -8.400428152209 -6.69 -3.98 2.63s
3 -8.400428152209 -14.27 -7.74 94.7ms
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| = 1.8642623093202785e-7
|ρ_newton - ρ_scfv| = 2.3506703116563346e-7
|ρ_newton - ρ_dm| = 6.269172317753076e-7