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-6
1.0e-6
Density-based self-consistent field
scfres_scf = self_consistent_field(basis; tol);
n Energy log10(ΔE) log10(Δρ) Diag Δtime
--- --------------- --------- --------- ---- ------
1 -8.397870395058 -0.90 5.2 39.0ms
2 -8.400248242784 -2.62 -1.74 1.0 19.7ms
3 -8.400405279453 -3.80 -2.96 1.5 20.8ms
4 -8.400427836862 -4.65 -2.97 3.0 25.0ms
5 -8.400427970370 -6.87 -3.08 1.0 19.3ms
6 -8.400428147250 -6.75 -4.78 1.0 19.6ms
7 -8.400428151852 -8.34 -4.49 2.5 23.8ms
8 -8.400428152191 -9.47 -5.27 1.0 30.1ms
9 -8.400428152208 -10.77 -6.30 1.0 19.8ms
Potential-based SCF
scfres_scfv = DFTK.scf_potential_mixing(basis; tol);
n Energy log10(ΔE) log10(Δρ) α Diag Δtime
--- --------------- --------- --------- ---- ---- ------
1 -8.397804622323 -0.90 5.0 27.4ms
2 -8.400383114103 -2.59 -1.79 0.80 2.0 19.7ms
3 -8.400422382548 -4.41 -3.02 0.80 1.0 16.8ms
4 -8.400428111447 -5.24 -3.44 0.80 2.5 20.7ms
5 -8.400428149690 -7.42 -4.60 0.80 1.0 25.9ms
6 -8.400428152187 -8.60 -5.76 0.80 2.5 21.3ms
7 -8.400428152209 -10.66 -6.17 0.80 2.8 21.5ms
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/7krni/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/7krni/src/types.jl:120
n Energy log10(ΔE) log10(Δρ) Δtime
--- --------------- --------- --------- ------
1 +0.798586406526 -1.05 58.3ms
2 -1.567143741012 0.37 -0.63 33.9ms
3 -4.485441310758 0.47 -0.38 52.9ms
4 -6.057696227312 0.20 -0.46 44.2ms
5 -7.577622778738 0.18 -0.74 44.2ms
6 -7.997350920034 -0.38 -1.42 32.4ms
7 -8.200447472971 -0.69 -1.64 32.6ms
8 -8.271715967352 -1.15 -1.92 40.6ms
9 -8.326518448416 -1.26 -2.10 33.3ms
10 -8.355414863002 -1.54 -2.08 32.8ms
11 -8.381111968009 -1.59 -2.21 33.0ms
12 -8.390387067738 -2.03 -2.26 32.8ms
13 -8.395406596581 -2.30 -2.79 32.7ms
14 -8.397799615367 -2.62 -3.28 32.7ms
15 -8.399243782916 -2.84 -3.46 40.7ms
16 -8.399832940692 -3.23 -3.51 32.8ms
17 -8.400146742923 -3.50 -3.75 32.9ms
18 -8.400281836888 -3.87 -3.72 32.7ms
19 -8.400366921395 -4.07 -3.75 32.7ms
20 -8.400402814670 -4.44 -3.73 32.7ms
21 -8.400416439117 -4.87 -4.72 41.8ms
22 -8.400424036416 -5.12 -4.07 33.0ms
23 -8.400426489422 -5.61 -4.36 33.4ms
24 -8.400427471914 -6.01 -4.93 33.0ms
25 -8.400427821779 -6.46 -5.32 32.8ms
26 -8.400428010392 -6.72 -5.28 32.9ms
27 -8.400428074518 -7.19 -5.23 40.8ms
28 -8.400428124209 -7.30 -5.32 32.8ms
29 -8.400428134242 -8.00 -5.85 33.1ms
30 -8.400428146138 -7.92 -5.53 32.9ms
31 -8.400428148911 -8.56 -5.86 32.8ms
32 -8.400428150974 -8.69 -6.24 32.5ms
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.397847327823 -0.90 5.2 27.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.400427981782 -1.79 595ms
2 -8.400428152209 -6.77 -4.03 390ms
3 -8.400428152209 -14.45 -7.83 108ms
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| = 5.277287891702114e-7
|ρ_newton - ρ_scfv| = 2.0495469659677585e-7
|ρ_newton - ρ_dm| = 1.821428428830081e-6