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.397866551447 -0.90 5.2 27.9ms
2 -8.400234848488 -2.63 -1.74 1.0 91.9ms
3 -8.400406766425 -3.76 -2.99 1.5 20.6ms
4 -8.400427882565 -4.68 -2.99 3.2 25.5ms
5 -8.400427959397 -7.11 -3.06 1.0 19.1ms
6 -8.400428146929 -6.73 -4.67 1.0 19.1ms
7 -8.400428151847 -8.31 -4.49 2.5 23.2ms
8 -8.400428152194 -9.46 -5.40 1.0 20.0ms
9 -8.400428152208 -10.86 -6.33 1.5 20.8ms
Potential-based SCF
scfres_scfv = DFTK.scf_potential_mixing(basis; tol);n Energy log10(ΔE) log10(Δρ) α Diag Δtime
--- --------------- --------- --------- ---- ---- ------
1 -8.397819612817 -0.90 5.0 1.75s
2 -8.400386966863 -2.59 -1.79 0.80 2.2 543ms
3 -8.400423446565 -4.44 -3.00 0.80 1.0 239ms
4 -8.400428106556 -5.33 -3.47 0.80 2.2 21.3ms
5 -8.400428149157 -7.37 -4.67 0.80 1.2 18.0ms
6 -8.400428152186 -8.52 -5.76 0.80 2.8 22.3ms
7 -8.400428152209 -10.64 -6.08 0.80 2.5 115ms
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.097597716533 -1.06 3.42s
2 -1.707213938995 0.45 -0.67 151ms
3 -4.420403483846 0.43 -0.41 46.2ms
4 -5.998866724531 0.20 -0.48 117ms
5 -7.449420901549 0.16 -0.69 46.1ms
6 -7.813226714640 -0.44 -1.32 33.9ms
7 -8.046532874119 -0.63 -1.62 33.9ms
8 -8.167489053390 -0.92 -1.99 33.8ms
9 -8.268963574219 -0.99 -2.00 67.1ms
10 -8.299199620088 -1.52 -2.28 33.9ms
11 -8.320982476191 -1.66 -2.12 34.0ms
12 -8.328827142036 -2.11 -2.32 33.7ms
13 -8.338142183589 -2.03 -2.66 33.7ms
14 -8.346394871575 -2.08 -2.54 33.8ms
15 -8.358809908561 -1.91 -2.09 33.8ms
16 -8.369575955230 -1.97 -2.24 42.5ms
17 -8.387646271244 -1.74 -2.28 33.7ms
18 -8.393603751344 -2.22 -2.39 33.7ms
19 -8.398619555914 -2.30 -2.78 33.5ms
20 -8.399334823518 -3.15 -3.10 40.0ms
21 -8.400128847045 -3.10 -3.19 33.8ms
22 -8.400230110784 -3.99 -3.48 33.9ms
23 -8.400362972382 -3.88 -3.85 33.7ms
24 -8.400383886788 -4.68 -3.78 33.6ms
25 -8.400410173516 -4.58 -4.09 40.2ms
26 -8.400417449503 -5.14 -3.91 33.9ms
27 -8.400424675669 -5.14 -4.12 33.9ms
28 -8.400426027067 -5.87 -4.45 34.0ms
29 -8.400427562592 -5.81 -4.56 33.7ms
30 -8.400427715703 -6.81 -4.92 40.1ms
31 -8.400428001792 -6.54 -5.13 33.8ms
32 -8.400428057136 -7.26 -5.27 34.0ms
33 -8.400428115009 -7.24 -5.48 34.3ms
34 -8.400428132924 -7.75 -5.80 33.7ms
35 -8.400428146534 -7.87 -6.15 40.3ms
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.397836963643 -0.90 5.2 28.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.400427987240 -1.79 11.7s
2 -8.400428152209 -6.78 -4.03 3.82s
3 -8.400428152209 -14.75 -7.85 97.5ms
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| = 8.858849988741973e-7
|ρ_newton - ρ_scfv| = 1.187990723241967e-7
|ρ_newton - ρ_dm| = 4.662489102106395e-6