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-6
1.0e-6
Density-based self-consistent field
scfres_scf = self_consistent_field(basis; tol);
n Energy log10(ΔE) log10(Δρ) Diag Δtime
--- --------------- --------- --------- ---- ------
1 -8.397797399189 -0.90 4.8 31.7ms
2 -8.400226111919 -2.61 -1.74 1.0 18.7ms
3 -8.400397474637 -3.77 -2.94 1.5 19.5ms
4 -8.400427739110 -4.52 -2.89 3.2 24.2ms
5 -8.400427751393 -7.91 -2.88 1.0 23.6ms
6 -8.400428140520 -6.41 -4.96 1.0 18.9ms
7 -8.400428151354 -7.97 -4.25 3.5 25.8ms
8 -8.400428152202 -9.07 -5.56 2.0 26.1ms
9 -8.400428152208 -11.22 -5.74 1.5 20.6ms
10 -8.400428152209 -12.06 -6.24 1.0 19.9ms
Potential-based SCF
scfres_scfv = DFTK.scf_potential_mixing(basis; tol);
n Energy log10(ΔE) log10(Δρ) α Diag Δtime
--- --------------- --------- --------- ---- ---- ------
1 -8.397786612086 -0.90 5.2 30.9ms
2 -8.400383130068 -2.59 -1.78 0.80 2.2 19.5ms
3 -8.400422583889 -4.40 -3.00 0.80 1.0 16.1ms
4 -8.400428099923 -5.26 -3.42 0.80 2.5 19.8ms
5 -8.400428148628 -7.31 -4.56 0.80 1.2 16.8ms
6 -8.400428152188 -8.45 -5.67 0.80 2.8 25.1ms
7 -8.400428152209 -10.67 -6.14 0.80 2.5 20.7ms
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/7krni/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/7krni/src/types.jl:120
n Energy log10(ΔE) log10(Δρ) Δtime
--- --------------- --------- --------- ------
1 +1.152313660900 -1.11 58.3ms
2 -1.538376776795 0.43 -0.65 37.1ms
3 -4.229463550170 0.43 -0.39 44.0ms
4 -5.842754278622 0.21 -0.47 43.7ms
5 -7.405089622628 0.19 -0.72 47.3ms
6 -7.773866451747 -0.43 -1.47 32.5ms
7 -8.034363701784 -0.58 -1.75 32.6ms
8 -8.128249719866 -1.03 -1.87 32.4ms
9 -8.226399601375 -1.01 -1.99 36.0ms
10 -8.280857042385 -1.26 -1.62 32.4ms
11 -8.339071407013 -1.23 -1.98 32.4ms
12 -8.375261719150 -1.44 -2.05 36.0ms
13 -8.388546571953 -1.88 -2.37 32.5ms
14 -8.395153561923 -2.18 -2.68 32.4ms
15 -8.397704624541 -2.59 -2.70 32.4ms
16 -8.399107922830 -2.85 -3.22 36.0ms
17 -8.399875283332 -3.12 -3.37 32.6ms
18 -8.400172688652 -3.53 -3.40 32.6ms
19 -8.400311637132 -3.86 -3.65 32.5ms
20 -8.400381514244 -4.16 -3.92 35.8ms
21 -8.400407027710 -4.59 -4.00 32.5ms
22 -8.400419155366 -4.92 -4.50 32.4ms
23 -8.400424555019 -5.27 -4.21 35.7ms
24 -8.400426892911 -5.63 -4.39 32.7ms
25 -8.400427708263 -6.09 -4.68 32.5ms
26 -8.400427984959 -6.56 -4.92 32.4ms
27 -8.400428096255 -6.95 -5.10 35.7ms
28 -8.400428126402 -7.52 -5.15 32.4ms
29 -8.400428142748 -7.79 -5.85 32.4ms
30 -8.400428147664 -8.31 -5.81 32.6ms
31 -8.400428150315 -8.58 -6.57 35.9ms
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.397818424611 -0.90 5.2 25.9ms
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.400427976288 -1.79 590ms
2 -8.400428152209 -6.75 -4.03 379ms
3 -8.400428152209 -14.45 -7.81 118ms
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| = 5.446089927868523e-7
|ρ_newton - ρ_scfv| = 2.736491459183635e-7
|ρ_newton - ρ_dm| = 2.6106906301697153e-6