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.397617985862 -0.90 5.2 24.4ms
2 -8.400224584391 -2.58 -1.72 1.0 18.2ms
3 -8.400395978336 -3.77 -2.97 1.2 18.5ms
4 -8.400427706393 -4.50 -2.91 3.2 22.7ms
5 -8.400428064297 -6.45 -3.28 1.0 18.1ms
6 -8.400428149447 -7.07 -4.76 1.0 18.1ms
7 -8.400428151931 -8.60 -4.58 2.2 206ms
8 -8.400428152168 -9.63 -5.00 1.0 19.0ms
9 -8.400428152207 -10.41 -6.28 1.0 18.7ms
Potential-based SCF
scfres_scfv = DFTK.scf_potential_mixing(basis; tol);n Energy log10(ΔE) log10(Δρ) α Diag Δtime
--- --------------- --------- --------- ---- ---- ------
1 -8.397614301244 -0.90 5.0 1.56s
2 -8.400388986182 -2.56 -1.78 0.80 2.2 1.06s
3 -8.400424306799 -4.45 -2.95 0.80 1.0 200ms
4 -8.400428114256 -5.42 -3.51 0.80 2.2 19.0ms
5 -8.400428149127 -7.46 -4.83 0.80 1.0 16.4ms
6 -8.400428152198 -8.51 -5.39 0.80 3.0 1.29s
7 -8.400428152208 -10.99 -6.25 0.80 1.5 18.5ms
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.292024224759 -1.02 2.87s
2 -2.011443612502 0.52 -0.64 148ms
3 -4.330354200346 0.37 -0.39 47.2ms
4 -6.006496484021 0.22 -0.44 47.1ms
5 -7.568458307716 0.19 -0.68 47.0ms
6 -7.805069211644 -0.63 -1.39 34.8ms
7 -8.162258318299 -0.45 -1.60 34.9ms
8 -8.232626233547 -1.15 -1.92 34.9ms
9 -8.295549657961 -1.20 -1.83 34.8ms
10 -8.297342190346 -2.75 -1.81 34.9ms
11 -8.357222412224 -1.22 -1.72 48.9ms
12 -8.376654866557 -1.71 -1.66 47.1ms
13 -8.385057117082 -2.08 -2.82 34.9ms
14 -8.394060982148 -2.05 -3.17 34.8ms
15 -8.396851526467 -2.55 -2.75 35.3ms
16 -8.397829842387 -3.01 -2.88 34.8ms
17 -8.399178718736 -2.87 -3.38 34.8ms
18 -8.399723670784 -3.26 -3.03 34.8ms
19 -8.400150678565 -3.37 -3.42 125ms
20 -8.400265859392 -3.94 -3.59 35.5ms
21 -8.400384605939 -3.93 -3.79 35.1ms
22 -8.400407181614 -4.65 -4.16 35.0ms
23 -8.400421080241 -4.86 -4.26 35.1ms
24 -8.400425627337 -5.34 -4.60 34.8ms
25 -8.400427178062 -5.81 -4.72 34.8ms
26 -8.400427660574 -6.32 -5.30 34.9ms
27 -8.400427948216 -6.54 -5.14 34.9ms
28 -8.400428047506 -7.00 -5.37 34.9ms
29 -8.400428112826 -7.18 -5.46 34.9ms
30 -8.400428136098 -7.63 -5.78 34.7ms
31 -8.400428146529 -7.98 -5.62 34.7ms
32 -8.400428149767 -8.49 -5.98 35.0ms
33 -8.400428151144 -8.86 -6.63 34.8ms
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.397630558151 -0.90 5.0 24.0ms
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.400427982787 -1.79 8.54s
2 -8.400428152209 -6.77 -4.03 3.29s
3 -8.400428152209 -14.45 -7.85 71.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| = 8.072359069419976e-7
|ρ_newton - ρ_scfv| = 4.4899376058431184e-7
|ρ_newton - ρ_dm| = 1.1385530124080065e-6