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 DFTK
using LinearAlgebra
a = 10.26 # Silicon lattice constant in Bohr
lattice = a / 2 * [[0 1 1.];
[1 0 1.];
[1 1 0.]]
Si = ElementPsp(:Si; psp=load_psp("hgh/lda/Si-q4"))
atoms = [Si, Si]
positions = [ones(3)/8, -ones(3)/8]
model = model_LDA(lattice, atoms, positions)
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 -7.847680505586 -0.70 4.2 27.9ms
2 -7.852388703731 -2.33 -1.53 1.0 16.8ms
3 -7.852608855028 -3.66 -2.54 1.2 17.5ms
4 -7.852645473561 -4.44 -2.78 2.5 21.8ms
5 -7.852646130446 -6.18 -2.89 1.2 17.3ms
6 -7.852646668229 -6.27 -3.87 1.0 16.8ms
7 -7.852646685960 -7.75 -4.60 2.0 20.0ms
8 -7.852646686689 -9.14 -5.15 1.5 18.2ms
9 -7.852646686726 -10.43 -6.28 1.2 18.1ms
Potential-based SCF
scfres_scfv = DFTK.scf_potential_mixing(basis; tol);
n Energy log10(ΔE) log10(Δρ) α Diag Δtime
--- --------------- --------- --------- ---- ---- ------
1 -7.847668809469 -0.70 4.8 29.0ms
2 -7.852559081664 -2.31 -1.62 0.80 2.2 19.9ms
3 -7.852641245248 -4.09 -2.69 0.80 1.0 15.4ms
4 -7.852646500486 -5.28 -3.39 0.80 1.8 18.2ms
5 -7.852646681385 -6.74 -4.38 0.80 2.0 18.7ms
6 -7.852646686645 -8.28 -4.89 0.80 2.5 21.2ms
7 -7.852646686726 -10.09 -5.58 0.80 1.0 15.6ms
8 -7.852646686730 -11.43 -6.64 0.80 1.8 17.9ms
Direct minimization
Note: Unlike the other algorithms, tolerance for this one is in the energy, thus we square the density tolerance value to be roughly equivalent.
scfres_dm = direct_minimization(basis; tol=tol^2);
n Energy log10(ΔE) log10(Δρ) Δtime
--- --------------- --------- --------- ------
1 +1.314028346966 -0.99 54.8ms
2 -1.726867674529 0.48 -0.67 24.9ms
3 -3.891611559688 0.34 -0.44 32.9ms
4 -5.223477079509 0.12 -0.59 32.6ms
5 -6.880634702019 0.22 -0.77 32.7ms
6 -7.459678585182 -0.24 -1.25 32.6ms
7 -7.680838395671 -0.66 -1.40 24.6ms
8 -7.764821658843 -1.08 -1.81 24.5ms
9 -7.801693397895 -1.43 -2.16 24.6ms
10 -7.822336088811 -1.69 -2.21 23.9ms
11 -7.834292578520 -1.92 -2.40 24.5ms
12 -7.842117181193 -2.11 -2.50 24.7ms
13 -7.845702512644 -2.45 -2.44 24.6ms
14 -7.848138041284 -2.61 -2.73 24.6ms
15 -7.850450452343 -2.64 -2.98 24.5ms
16 -7.851734560129 -2.89 -3.02 24.4ms
17 -7.852270840983 -3.27 -3.53 24.3ms
18 -7.852515722900 -3.61 -3.61 24.3ms
19 -7.852609278459 -4.03 -3.66 24.6ms
20 -7.852635461079 -4.58 -4.06 24.6ms
21 -7.852642940022 -5.13 -3.97 24.7ms
22 -7.852645447891 -5.60 -4.18 24.6ms
23 -7.852646173663 -6.14 -5.02 24.5ms
24 -7.852646456613 -6.55 -4.71 24.5ms
25 -7.852646583400 -6.90 -5.08 24.6ms
26 -7.852646646298 -7.20 -5.83 24.7ms
27 -7.852646675741 -7.53 -5.51 24.6ms
28 -7.852646683689 -8.10 -5.63 24.6ms
29 -7.852646685817 -8.67 -6.38 24.7ms
30 -7.852646686451 -9.20 -6.13 24.7ms
31 -7.852646686635 -9.73 -6.80 24.8ms
32 -7.852646686700 -10.19 -7.21 24.7ms
33 -7.852646686721 -10.67 -7.41 24.7ms
34 -7.852646686727 -11.24 -7.51 24.7ms
35 -7.852646686728 -11.74 -8.02 24.6ms
36 -7.852646686729 -12.12 -7.89 24.6ms
37 -7.852646686730 -12.39 -8.29 24.6ms
38 -7.852646686730 -12.73 -8.23 24.6ms
39 -7.852646686730 -13.30 -8.70 24.6ms
40 -7.852646686730 -13.80 -8.95 24.6ms
41 -7.852646686730 -14.27 -9.03 24.6ms
42 -7.852646686730 -14.27 -9.09 24.5ms
43 -7.852646686730 -14.27 -9.19 30.8ms
44 -7.852646686730 + -Inf -9.61 101ms
45 -7.852646686730 + -Inf -8.49 32.6ms
┌ Warning: DM not converged.
└ @ DFTK ~/work/DFTK.jl/DFTK.jl/src/scf/scf_callbacks.jl:60
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 -7.847779003699 -0.70 4.5 29.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 -7.852645852050 -1.63 404ms
2 -7.852646686730 -6.08 -3.69 287ms
3 -7.852646686730 -13.23 -7.20 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| = 2.677551900730093e-7
|ρ_newton - ρ_scfv| = 1.1440261958962013e-7
|ρ_newton - ρ_dm| = 3.957898427853214e-9