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.397813632007 -0.90 5.0 24.2ms
2 -8.400241189226 -2.61 -1.73 1.0 25.8ms
3 -8.400399136119 -3.80 -2.95 1.5 18.4ms
4 -8.400427696881 -4.54 -2.89 3.2 22.8ms
5 -8.400427910116 -6.67 -3.02 1.0 17.5ms
6 -8.400428148559 -6.62 -5.06 1.0 17.6ms
7 -8.400428155759 -8.14 -4.38 3.5 24.6ms
8 -8.400428156273 -9.29 -5.44 2.0 20.5ms
9 -8.400428156277 -11.45 -6.43 1.0 17.8ms
Potential-based SCF
scfres_scfv = DFTK.scf_potential_mixing(basis; tol);
n Energy log10(ΔE) log10(Δρ) α Diag Δtime
--- --------------- --------- --------- ---- ---- ------
1 -8.397823224325 -0.90 5.5 25.2ms
2 -8.400375967824 -2.59 -1.79 0.80 2.0 23.2ms
3 -8.400422330500 -4.33 -3.07 0.80 1.0 14.9ms
4 -8.400428115139 -5.24 -3.42 0.80 2.5 19.0ms
5 -8.400428153389 -7.42 -4.56 0.80 1.0 14.9ms
6 -8.400428156238 -8.55 -5.67 0.80 2.2 18.4ms
7 -8.400428156277 -10.42 -5.89 0.80 3.2 20.8ms
8 -8.400428156277 -12.69 -7.05 0.80 1.0 15.1ms
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/8dE7C/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/8dE7C/src/types.jl:120
n Energy log10(ΔE) log10(Δρ) Δtime
--- --------------- --------- --------- ------
1 +0.666741851717 -1.06 49.5ms
2 -2.039540939097 0.43 -0.65 27.8ms
3 -4.635288465619 0.41 -0.38 43.5ms
4 -6.418051145558 0.25 -0.48 37.5ms
5 -7.702360377454 0.11 -0.82 37.4ms
6 -8.054020480095 -0.45 -1.29 27.8ms
7 -8.195212691035 -0.85 -1.74 27.7ms
8 -8.268323700675 -1.14 -1.96 32.3ms
9 -8.311121803617 -1.37 -2.33 27.8ms
10 -8.341829687537 -1.51 -2.03 27.7ms
11 -8.367458501220 -1.59 -2.09 27.7ms
12 -8.385686452155 -1.74 -2.21 27.7ms
13 -8.392813165442 -2.15 -2.56 32.4ms
14 -8.397225566598 -2.36 -2.88 27.9ms
15 -8.398695129057 -2.83 -2.61 27.8ms
16 -8.399722873904 -2.99 -3.06 27.7ms
17 -8.400105946813 -3.42 -2.82 31.5ms
18 -8.400301814282 -3.71 -3.11 27.8ms
19 -8.400363513504 -4.21 -3.59 27.7ms
20 -8.400401685577 -4.42 -3.76 27.7ms
21 -8.400416364354 -4.83 -3.98 31.3ms
22 -8.400423217367 -5.16 -4.45 27.9ms
23 -8.400426075587 -5.54 -4.21 27.8ms
24 -8.400427323182 -5.90 -4.94 30.9ms
25 -8.400427781015 -6.34 -4.82 28.4ms
26 -8.400428019346 -6.62 -5.23 27.8ms
27 -8.400428114018 -7.02 -5.01 27.8ms
28 -8.400428135929 -7.66 -5.63 31.3ms
29 -8.400428148960 -7.89 -5.70 27.7ms
30 -8.400428151839 -8.54 -5.75 27.8ms
31 -8.400428154351 -8.60 -6.03 27.8ms
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.397864719616 -0.90 4.8 23.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.400427981609 -1.79 524ms
2 -8.400428156277 -6.76 -4.02 346ms
3 -8.400428156277 + -Inf -7.82 111ms
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| = 3.0632737107740273e-7
|ρ_newton - ρ_scfv| = 6.199899358817966e-8
|ρ_newton - ρ_dm| = 3.662890961207304e-6