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.397787161399 -0.90 5.0 32.1ms
2 -8.400241368650 -2.61 -1.74 1.0 22.8ms
3 -8.400400355915 -3.80 -2.92 1.5 24.6ms
4 -8.400427743621 -4.56 -2.91 3.0 28.9ms
5 -8.400427869051 -6.90 -2.97 1.0 22.8ms
6 -8.400428143667 -6.56 -4.76 1.0 24.0ms
7 -8.400428151617 -8.10 -4.34 3.5 69.5ms
8 -8.400428152181 -9.25 -5.21 1.0 24.4ms
9 -8.400428152205 -10.62 -6.06 1.0 22.6ms
Potential-based SCF
scfres_scfv = DFTK.scf_potential_mixing(basis; tol);n Energy log10(ΔE) log10(Δρ) α Diag Δtime
--- --------------- --------- --------- ---- ---- ------
1 -8.397861436397 -0.90 5.2 1.39s
2 -8.400384526419 -2.60 -1.79 0.80 2.0 511ms
3 -8.400423037896 -4.41 -3.02 0.80 1.0 321ms
4 -8.400428106904 -5.30 -3.45 0.80 2.5 24.9ms
5 -8.400428148576 -7.38 -4.61 0.80 1.2 21.8ms
6 -8.400428152184 -8.44 -6.02 0.80 2.8 26.7ms
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.937800392760 -1.04 2.73s
2 -2.055427954915 0.48 -0.69 228ms
3 -4.666134892812 0.42 -0.40 49.6ms
4 -6.299557764255 0.21 -0.47 48.4ms
5 -7.681961336102 0.14 -0.72 48.5ms
6 -8.053222586822 -0.43 -1.25 38.1ms
7 -8.226020547837 -0.76 -1.63 36.7ms
8 -8.276482700629 -1.30 -2.00 72.8ms
9 -8.305318472471 -1.54 -2.37 37.1ms
10 -8.326123774754 -1.68 -2.05 35.7ms
11 -8.346879701161 -1.68 -2.26 37.8ms
12 -8.366848794681 -1.70 -2.31 35.3ms
13 -8.383620986798 -1.78 -2.56 37.4ms
14 -8.393623711048 -2.00 -2.51 44.5ms
15 -8.398027377503 -2.36 -2.80 36.4ms
16 -8.399197497578 -2.93 -2.94 37.4ms
17 -8.399958811797 -3.12 -3.12 35.4ms
18 -8.400243089919 -3.55 -3.89 37.2ms
19 -8.400349358486 -3.97 -3.81 42.5ms
20 -8.400406242717 -4.25 -4.16 37.7ms
21 -8.400415186693 -5.05 -3.95 39.8ms
22 -8.400423374588 -5.09 -4.89 36.1ms
23 -8.400425736259 -5.63 -4.21 42.1ms
24 -8.400426994418 -5.90 -4.60 36.4ms
25 -8.400427763343 -6.11 -4.79 35.6ms
26 -8.400428010610 -6.61 -4.98 36.1ms
27 -8.400428088930 -7.11 -5.00 36.6ms
28 -8.400428129324 -7.39 -5.31 43.1ms
29 -8.400428143652 -7.84 -5.21 36.5ms
30 -8.400428147461 -8.42 -5.58 36.1ms
31 -8.400428150595 -8.50 -5.86 35.7ms
32 -8.400428151487 -9.05 -5.78 36.5ms
33 -8.400428151867 -9.42 -6.46 42.9ms
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.397883873880 -0.90 5.2 33.7ms
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.400427988460 -1.79 10.4s
2 -8.400428152209 -6.79 -4.04 3.39s
3 -8.400428152209 -14.75 -7.85 98.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| = 2.5210269646082138e-6
|ρ_newton - ρ_scfv| = 4.922273956104773e-6
|ρ_newton - ρ_dm| = 1.931993493322587e-6