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.397608061713 -0.90 5.2 27.4ms
2 -8.400208110714 -2.59 -1.73 1.0 18.7ms
3 -8.400398251586 -3.72 -3.01 1.5 19.6ms
4 -8.400427756905 -4.53 -2.93 3.2 85.1ms
5 -8.400428028286 -6.57 -3.20 1.0 19.1ms
6 -8.400428149332 -6.92 -5.01 1.0 18.9ms
7 -8.400428151976 -8.58 -4.58 3.2 25.0ms
8 -8.400428152181 -9.69 -5.01 1.8 21.3ms
9 -8.400428152208 -10.57 -6.25 1.0 19.1ms
Potential-based SCF
scfres_scfv = DFTK.scf_potential_mixing(basis; tol);n Energy log10(ΔE) log10(Δρ) α Diag Δtime
--- --------------- --------- --------- ---- ---- ------
1 -8.397512401267 -0.90 5.2 1.74s
2 -8.400386677427 -2.54 -1.78 0.80 2.2 683ms
3 -8.400424136693 -4.43 -3.01 0.80 1.0 243ms
4 -8.400428113725 -5.40 -3.57 0.80 2.2 20.8ms
5 -8.400428148973 -7.45 -4.69 0.80 1.5 17.7ms
6 -8.400428152185 -8.49 -5.27 0.80 2.5 21.0ms
7 -8.400428152207 -10.64 -6.12 0.80 1.2 17.4ms
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.104023082474 -1.05 3.48s
2 -1.137950282150 0.35 -0.66 152ms
3 -4.212266229424 0.49 -0.36 44.5ms
4 -5.440145125008 0.09 -0.47 44.6ms
5 -7.312688061762 0.27 -0.53 88.3ms
6 -7.920009719918 -0.22 -1.15 44.7ms
7 -8.110428839432 -0.72 -1.31 32.9ms
8 -8.260577643586 -0.82 -1.76 32.6ms
9 -8.331566223218 -1.15 -1.88 32.7ms
10 -8.368706450728 -1.43 -2.03 57.1ms
11 -8.387136499057 -1.73 -2.32 32.9ms
12 -8.393037978036 -2.23 -2.44 32.8ms
13 -8.397591217131 -2.34 -2.76 32.8ms
14 -8.399081512512 -2.83 -2.97 32.7ms
15 -8.399879670975 -3.10 -3.14 39.4ms
16 -8.400157667920 -3.56 -3.52 32.6ms
17 -8.400318081015 -3.79 -3.70 32.7ms
18 -8.400372177441 -4.27 -4.02 32.7ms
19 -8.400407096196 -4.46 -3.95 32.7ms
20 -8.400417623987 -4.98 -4.28 38.5ms
21 -8.400423779408 -5.21 -4.16 33.0ms
22 -8.400425908056 -5.67 -4.62 32.9ms
23 -8.400427381343 -5.83 -4.47 32.6ms
24 -8.400427857298 -6.32 -5.27 32.6ms
25 -8.400428020962 -6.79 -4.97 38.9ms
26 -8.400428095637 -7.13 -5.64 32.7ms
27 -8.400428123764 -7.55 -5.20 33.0ms
28 -8.400428139845 -7.79 -5.55 33.5ms
29 -8.400428146165 -8.20 -5.82 33.0ms
30 -8.400428149850 -8.43 -5.89 39.3ms
31 -8.400428150940 -8.96 -5.96 32.7ms
32 -8.400428151738 -9.10 -6.19 33.1ms
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.397805832321 -0.90 4.8 30.3ms
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.400427943651 -1.77 12.6s
2 -8.400428152209 -6.68 -3.98 3.65s
3 -8.400428152209 -14.75 -7.73 86.9ms
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| = 6.74943963455187e-7
|ρ_newton - ρ_scfv| = 6.117630487432075e-7
|ρ_newton - ρ_dm| = 1.5887233912905272e-6