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.397844207748 -0.90 5.2 27.8ms
2 -8.400236234605 -2.62 -1.74 1.0 18.8ms
3 -8.400406823827 -3.77 -2.97 1.5 19.9ms
4 -8.400427870063 -4.68 -3.00 3.0 23.3ms
5 -8.400427968691 -7.01 -3.08 1.0 18.6ms
6 -8.400428145869 -6.75 -4.51 1.0 18.4ms
7 -8.400428151814 -8.23 -4.48 2.2 72.5ms
8 -8.400428152172 -9.45 -5.04 1.0 18.9ms
9 -8.400428152207 -10.45 -6.42 1.5 19.7ms
Potential-based SCF
scfres_scfv = DFTK.scf_potential_mixing(basis; tol);n Energy log10(ΔE) log10(Δρ) α Diag Δtime
--- --------------- --------- --------- ---- ---- ------
1 -8.397843323003 -0.90 5.0 1.74s
2 -8.400382511707 -2.60 -1.80 0.80 2.5 592ms
3 -8.400422133296 -4.40 -3.01 0.80 1.0 219ms
4 -8.400428097608 -5.22 -3.39 0.80 2.5 21.2ms
5 -8.400428148457 -7.29 -4.43 0.80 1.5 18.3ms
6 -8.400428152159 -8.43 -5.13 0.80 2.5 20.7ms
7 -8.400428152207 -10.32 -6.54 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 +0.889846505268 -1.03 3.46s
2 -1.506281979837 0.38 -0.67 151ms
3 -4.364949466781 0.46 -0.39 44.1ms
4 -5.831104741969 0.17 -0.49 43.9ms
5 -7.439552899907 0.21 -0.69 44.0ms
6 -7.623919327222 -0.73 -1.41 63.2ms
7 -8.103466488537 -0.32 -1.58 32.4ms
8 -8.183383653682 -1.10 -1.68 32.4ms
9 -8.204395629238 -1.68 -1.74 32.4ms
10 -8.279037095057 -1.13 -1.67 43.3ms
11 -8.311593170501 -1.49 -1.83 65.5ms
12 -8.340953712539 -1.53 -2.29 32.5ms
13 -8.358990386328 -1.74 -2.63 32.5ms
14 -8.371774249563 -1.89 -2.45 32.9ms
15 -8.386978529973 -1.82 -2.65 32.4ms
16 -8.394863398386 -2.10 -2.65 32.9ms
17 -8.397546947171 -2.57 -2.85 39.3ms
18 -8.399316117527 -2.75 -3.16 32.3ms
19 -8.399885858717 -3.24 -3.69 32.9ms
20 -8.400207409301 -3.49 -3.63 33.1ms
21 -8.400346710612 -3.86 -3.75 38.0ms
22 -8.400393740813 -4.33 -3.90 32.8ms
23 -8.400412967768 -4.72 -3.83 32.3ms
24 -8.400421393005 -5.07 -4.40 32.5ms
25 -8.400424697451 -5.48 -4.11 32.4ms
26 -8.400426995990 -5.64 -4.39 39.6ms
27 -8.400427639626 -6.19 -4.71 32.7ms
28 -8.400427945708 -6.51 -5.04 32.4ms
29 -8.400428071063 -6.90 -4.97 32.4ms
30 -8.400428111831 -7.39 -5.93 32.4ms
31 -8.400428134929 -7.64 -5.35 38.3ms
32 -8.400428142954 -8.10 -5.65 32.8ms
33 -8.400428148719 -8.24 -5.55 32.6ms
34 -8.400428150578 -8.73 -6.06 33.1ms
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.397823692873 -0.90 5.0 47.2ms
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.400427974014 -1.78 11.5s
2 -8.400428152209 -6.75 -4.03 3.65s
3 -8.400428152209 -14.75 -7.80 89.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.268097354622436e-7
|ρ_newton - ρ_scfv| = 7.866198218055216e-7
|ρ_newton - ρ_dm| = 9.339721700357298e-7