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.397871684260 -0.89 5.0 27.2ms
2 -8.400258931030 -2.62 -1.73 1.0 19.6ms
3 -8.400400904333 -3.85 -2.90 1.2 31.0ms
4 -8.400427707761 -4.57 -2.89 3.2 25.1ms
5 -8.400427903515 -6.71 -2.99 1.0 19.7ms
6 -8.400428143778 -6.62 -4.78 1.0 19.7ms
7 -8.400428151576 -8.11 -4.33 3.2 25.7ms
8 -8.400428152186 -9.21 -5.85 1.0 20.5ms
9 -8.400428152209 -10.64 -5.91 2.2 32.9ms
10 -8.400428152209 + -13.50 -5.92 1.0 20.0ms
11 -8.400428152209 -12.60 -6.32 1.0 20.5msPotential-based SCF
scfres_scfv = DFTK.scf_potential_mixing(basis; tol);n Energy log10(ΔE) log10(Δρ) α Diag Δtime
--- --------------- --------- --------- ---- ---- ------
1 -8.397731805557 -0.90 5.0 26.9ms
2 -8.400381210642 -2.58 -1.80 0.80 2.2 20.0ms
3 -8.400422528823 -4.38 -3.03 0.80 1.0 24.0ms
4 -8.400428095043 -5.25 -3.44 0.80 2.5 21.1ms
5 -8.400428148384 -7.27 -4.57 0.80 1.2 17.5ms
6 -8.400428152184 -8.42 -5.64 0.80 2.5 20.7ms
7 -8.400428152209 -10.61 -6.12 0.80 2.5 21.0msDirect 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.899934478728 -1.03 58.0ms
2 -1.691384719218 0.41 -0.68 32.4ms
3 -4.468274557807 0.44 -0.38 50.9ms
4 -6.081411528329 0.21 -0.47 43.8ms
5 -7.615560542601 0.19 -0.69 43.7ms
6 -8.030811593196 -0.38 -1.41 32.2ms
7 -8.210587557168 -0.75 -1.68 39.2ms
8 -8.243422912498 -1.48 -2.00 32.8ms
9 -8.292211535855 -1.31 -2.06 32.9ms
10 -8.310926579201 -1.73 -2.23 32.5ms
11 -8.344017106425 -1.48 -2.04 32.4ms
12 -8.373056081001 -1.54 -1.85 32.0ms
13 -8.387466044340 -1.84 -2.56 38.3ms
14 -8.395368217210 -2.10 -2.72 32.4ms
15 -8.398149198979 -2.56 -2.74 32.5ms
16 -8.399526321548 -2.86 -2.99 32.3ms
17 -8.400032316983 -3.30 -2.99 32.0ms
18 -8.400277846626 -3.61 -3.23 31.7ms
19 -8.400360387826 -4.08 -3.27 38.7ms
20 -8.400402653517 -4.37 -3.59 33.0ms
21 -8.400413874892 -4.95 -3.71 32.6ms
22 -8.400422787880 -5.05 -3.98 32.4ms
23 -8.400425148563 -5.63 -4.02 32.3ms
24 -8.400427036235 -5.72 -4.53 38.0ms
25 -8.400427725488 -6.16 -4.28 32.3ms
26 -8.400427990509 -6.58 -5.13 32.4ms
27 -8.400428076523 -7.07 -4.71 32.4ms
28 -8.400428124223 -7.32 -5.22 32.5ms
29 -8.400428139541 -7.81 -5.10 32.7ms
30 -8.400428147065 -8.12 -5.74 38.7ms
31 -8.400428149770 -8.57 -5.44 32.5ms
32 -8.400428151276 -8.82 -6.23 32.7msNewton 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.397864708749 -0.90 5.8 27.9msRemove 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.400427987447 -1.79 582ms
2 -8.400428152209 -6.78 -4.04 406ms
3 -8.400428152209 -14.45 -7.85 106msComparison 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.068046991347762e-7
|ρ_newton - ρ_scfv| = 1.8503596117007803e-7
|ρ_newton - ρ_dm| = 2.4855398025396908e-6