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.397558050258 -0.90 5.0 26.3ms
2 -8.400194544067 -2.58 -1.72 1.0 18.0ms
3 -8.400394663199 -3.70 -2.96 1.5 18.9ms
4 -8.400427711684 -4.48 -2.90 3.2 23.8ms
5 -8.400428084549 -6.43 -3.34 1.0 17.9ms
6 -8.400428148949 -7.19 -4.80 1.0 68.6ms
7 -8.400428152089 -8.50 -4.77 3.0 24.0ms
8 -8.400428152199 -9.96 -5.35 1.0 18.4ms
9 -8.400428152208 -11.03 -6.71 1.0 18.5ms
Potential-based SCF
scfres_scfv = DFTK.scf_potential_mixing(basis; tol);n Energy log10(ΔE) log10(Δρ) α Diag Δtime
--- --------------- --------- --------- ---- ---- ------
1 -8.397533914939 -0.90 5.2 1.86s
2 -8.400387700819 -2.54 -1.79 0.80 2.0 667ms
3 -8.400423547059 -4.45 -3.02 0.80 1.0 229ms
4 -8.400428125112 -5.34 -3.53 0.80 2.5 20.5ms
5 -8.400428149951 -7.60 -5.19 0.80 1.2 17.1ms
6 -8.400428152205 -8.65 -5.56 0.80 3.8 24.0ms
7 -8.400428152209 -11.41 -6.25 0.80 1.0 17.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 +1.286243257903 -1.08 3.31s
2 -1.728439362783 0.48 -0.63 143ms
3 -4.246626904820 0.40 -0.34 43.8ms
4 -5.950161462482 0.23 -0.43 43.8ms
5 -7.570249317767 0.21 -0.68 43.6ms
6 -7.864228531463 -0.53 -1.41 200ms
7 -8.187542297452 -0.49 -1.56 40.0ms
8 -8.264659310151 -1.11 -1.91 39.9ms
9 -8.328929000062 -1.19 -1.90 39.8ms
10 -8.354561067888 -1.59 -2.15 40.1ms
11 -8.371859193693 -1.76 -2.27 39.3ms
12 -8.385145931872 -1.88 -2.19 1.10s
13 -8.392350409612 -2.14 -2.79 33.3ms
14 -8.396300812007 -2.40 -3.04 32.9ms
15 -8.398694214127 -2.62 -2.97 32.8ms
16 -8.399464410377 -3.11 -3.17 32.8ms
17 -8.399989724449 -3.28 -3.44 32.8ms
18 -8.400234059400 -3.61 -3.68 32.9ms
19 -8.400339566754 -3.98 -3.95 34.1ms
20 -8.400389467186 -4.30 -4.11 36.1ms
21 -8.400411953276 -4.65 -4.34 34.8ms
22 -8.400419749581 -5.11 -4.23 36.2ms
23 -8.400424689147 -5.31 -4.55 36.0ms
24 -8.400426498166 -5.74 -4.84 34.4ms
25 -8.400427495334 -6.00 -4.77 34.5ms
26 -8.400427907321 -6.39 -5.00 36.6ms
27 -8.400428030920 -6.91 -5.20 40.3ms
28 -8.400428111860 -7.09 -5.51 38.5ms
29 -8.400428132306 -7.69 -5.29 33.7ms
30 -8.400428143313 -7.96 -5.90 34.3ms
31 -8.400428147242 -8.41 -5.72 33.3ms
32 -8.400428150275 -8.52 -6.15 35.2ms
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.397589109252 -0.90 5.2 27.4ms
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.400427981270 -1.79 11.2s
2 -8.400428152209 -6.77 -4.03 3.62s
3 -8.400428152209 -14.75 -7.85 90.1ms
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.131439750951786e-7
|ρ_newton - ρ_scfv| = 3.427322959759338e-7
|ρ_newton - ρ_dm| = 8.295665561022536e-7