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.397521039997 -0.90 5.2 27.1ms
2 -8.400194917171 -2.57 -1.72 1.0 18.5ms
3 -8.400398667365 -3.69 -3.04 1.5 19.8ms
4 -8.400427818253 -4.54 -2.97 3.2 24.3ms
5 -8.400428073292 -6.59 -3.31 1.0 18.6ms
6 -8.400428149701 -7.12 -5.13 1.0 18.7ms
7 -8.400428152071 -8.63 -4.70 3.5 58.6ms
8 -8.400428152194 -9.91 -5.14 1.8 21.1ms
9 -8.400428152209 -10.82 -6.54 1.0 19.2ms
Potential-based SCF
scfres_scfv = DFTK.scf_potential_mixing(basis; tol);n Energy log10(ΔE) log10(Δρ) α Diag Δtime
--- --------------- --------- --------- ---- ---- ------
1 -8.397553352719 -0.90 5.2 1.82s
2 -8.400387981834 -2.55 -1.79 0.80 2.0 679ms
3 -8.400423767489 -4.45 -2.98 0.80 1.0 326ms
4 -8.400428113999 -5.36 -3.49 0.80 2.2 20.9ms
5 -8.400428149412 -7.45 -4.86 0.80 1.2 17.5ms
6 -8.400428152198 -8.55 -5.55 0.80 3.2 22.7ms
7 -8.400428152209 -10.98 -6.06 0.80 1.5 18.3ms
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 +1.152334597589 -1.10 3.38s
2 -1.366769378124 0.40 -0.71 217ms
3 -4.345440095705 0.47 -0.43 44.2ms
4 -5.958875528274 0.21 -0.48 44.7ms
5 -7.442598449178 0.17 -0.81 44.2ms
6 -7.881444917747 -0.36 -1.26 33.0ms
7 -8.134070278544 -0.60 -1.47 64.9ms
8 -8.228074323837 -1.03 -1.75 33.0ms
9 -8.269616702955 -1.38 -1.82 33.2ms
10 -8.274254451957 -2.33 -1.85 32.7ms
11 -8.339154336788 -1.19 -1.61 44.0ms
12 -8.361822461346 -1.64 -1.80 51.9ms
13 -8.387053734960 -1.60 -2.68 32.8ms
14 -8.392887180505 -2.23 -2.85 32.7ms
15 -8.397090026219 -2.38 -2.91 33.1ms
16 -8.399118759992 -2.69 -2.88 32.7ms
17 -8.399550320453 -3.36 -3.10 39.9ms
18 -8.400135635829 -3.23 -3.46 32.9ms
19 -8.400278504207 -3.85 -3.57 32.8ms
20 -8.400367589997 -4.05 -4.02 32.8ms
21 -8.400402284424 -4.46 -3.98 32.8ms
22 -8.400416826575 -4.84 -3.98 40.7ms
23 -8.400423047035 -5.21 -4.24 33.0ms
24 -8.400425808341 -5.56 -5.25 33.4ms
25 -8.400427227823 -5.85 -4.64 32.9ms
26 -8.400427746547 -6.29 -4.72 32.8ms
27 -8.400427991879 -6.61 -5.04 40.7ms
28 -8.400428094242 -6.99 -5.42 33.1ms
29 -8.400428126159 -7.50 -5.22 33.1ms
30 -8.400428140362 -7.85 -5.46 33.2ms
31 -8.400428146891 -8.19 -5.70 33.0ms
32 -8.400428149809 -8.53 -5.79 32.8ms
33 -8.400428151005 -8.92 -6.20 40.6ms
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.397535836762 -0.90 5.2 27.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.400427980936 -1.79 12.5s
2 -8.400428152209 -6.77 -4.03 3.72s
3 -8.400428152209 -14.45 -7.85 70.4ms
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| = 4.536886537453675e-7
|ρ_newton - ρ_scfv| = 3.6220752953770615e-7
|ρ_newton - ρ_dm| = 2.526715210847308e-6