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.397556467063 -0.90 5.2 24.2ms
2 -8.400200706131 -2.58 -1.72 1.0 18.4ms
3 -8.400396329311 -3.71 -3.03 1.5 18.9ms
4 -8.400427727662 -4.50 -2.93 3.2 22.6ms
5 -8.400428044313 -6.50 -3.24 1.0 18.3ms
6 -8.400428149262 -6.98 -4.76 1.0 18.4ms
7 -8.400428151908 -8.58 -4.56 2.2 21.3ms
8 -8.400428152173 -9.58 -5.05 1.0 18.5ms
9 -8.400428152207 -10.46 -6.60 1.0 18.5ms
Potential-based SCF
scfres_scfv = DFTK.scf_potential_mixing(basis; tol);n Energy log10(ΔE) log10(Δρ) α Diag Δtime
--- --------------- --------- --------- ---- ---- ------
1 -8.397586539266 -0.90 5.2 1.71s
2 -8.400388203825 -2.55 -1.79 0.80 2.0 1.06s
3 -8.400423805353 -4.45 -2.98 0.80 1.0 201ms
4 -8.400428106049 -5.37 -3.46 0.80 2.2 19.2ms
5 -8.400428150175 -7.36 -4.67 0.80 1.5 17.5ms
6 -8.400428152174 -8.70 -5.70 0.80 2.5 19.8ms
7 -8.400428152209 -10.46 -6.22 0.80 2.8 21.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 +0.902157582141 -1.02 2.97s
2 -1.979271000623 0.46 -0.64 152ms
3 -4.746725829762 0.44 -0.40 132ms
4 -6.540959124718 0.25 -0.42 48.3ms
5 -7.712741556779 0.07 -0.90 47.2ms
6 -8.049859317199 -0.47 -1.40 35.1ms
7 -8.177389304149 -0.89 -1.84 35.1ms
8 -8.257974473013 -1.09 -1.96 35.3ms
9 -8.310384408654 -1.28 -2.07 35.2ms
10 -8.350858515499 -1.39 -2.02 35.0ms
11 -8.373838585096 -1.64 -2.01 77.1ms
12 -8.387281844540 -1.87 -1.99 35.1ms
13 -8.393501283430 -2.21 -2.53 35.0ms
14 -8.395725727141 -2.65 -2.45 35.2ms
15 -8.397144890372 -2.85 -3.12 35.2ms
16 -8.398208646323 -2.97 -2.60 35.3ms
17 -8.399170388075 -3.02 -3.14 35.2ms
18 -8.399954374142 -3.11 -3.04 35.0ms
19 -8.400230572955 -3.56 -3.35 44.1ms
20 -8.400354549822 -3.91 -3.43 35.3ms
21 -8.400390669773 -4.44 -3.27 35.2ms
22 -8.400415345940 -4.61 -3.96 35.0ms
23 -8.400421795599 -5.19 -3.68 36.1ms
24 -8.400425785706 -5.40 -4.34 35.7ms
25 -8.400427076567 -5.89 -4.09 35.1ms
26 -8.400427687655 -6.21 -4.95 43.6ms
27 -8.400427935837 -6.61 -4.53 35.1ms
28 -8.400428050678 -6.94 -5.17 35.2ms
29 -8.400428105936 -7.26 -4.92 35.1ms
30 -8.400428130546 -7.61 -5.54 35.5ms
31 -8.400428143463 -7.89 -5.26 35.6ms
32 -8.400428148968 -8.26 -5.54 35.3ms
33 -8.400428151010 -8.69 -5.83 44.0ms
34 -8.400428151666 -9.18 -5.87 35.2ms
35 -8.400428152002 -9.47 -6.37 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.397496305861 -0.90 4.8 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.400427934186 -1.77 8.75s
2 -8.400428152209 -6.66 -3.97 3.32s
3 -8.400428152209 -14.27 -7.73 88.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.238354116595835e-7
|ρ_newton - ρ_scfv| = 1.8370271985249607e-7
|ρ_newton - ρ_dm| = 1.752237202186734e-6