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.397869408670 -0.90 5.5 26.7ms
2 -8.400252184274 -2.62 -1.74 1.0 19.0ms
3 -8.400407569531 -3.81 -2.97 1.5 26.6ms
4 -8.400427888941 -4.69 -3.00 3.0 24.1ms
5 -8.400427947965 -7.23 -3.04 1.0 19.3ms
6 -8.400428145803 -6.70 -4.60 1.0 19.4ms
7 -8.400428151794 -8.22 -4.46 2.5 28.5ms
8 -8.400428152181 -9.41 -5.09 1.0 19.6ms
9 -8.400428152207 -10.57 -6.29 1.0 19.8ms
Potential-based SCF
scfres_scfv = DFTK.scf_potential_mixing(basis; tol);
n Energy log10(ΔE) log10(Δρ) α Diag Δtime
--- --------------- --------- --------- ---- ---- ------
1 -8.397846568465 -0.90 5.0 26.2ms
2 -8.400382305693 -2.60 -1.79 0.80 2.2 19.5ms
3 -8.400422496929 -4.40 -3.01 0.80 1.0 16.4ms
4 -8.400428101172 -5.25 -3.41 0.80 2.5 20.3ms
5 -8.400428147512 -7.33 -4.67 0.80 1.0 21.2ms
6 -8.400428152187 -8.33 -5.34 0.80 2.8 21.2ms
7 -8.400428152208 -10.68 -6.84 0.80 2.0 18.9ms
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.500756078601 -1.08 61.9ms
2 -1.802990078003 0.36 -0.64 32.9ms
3 -4.704024560266 0.46 -0.33 44.1ms
4 -6.194045821553 0.17 -0.44 47.8ms
5 -7.682048059060 0.17 -0.66 44.4ms
6 -7.975201065137 -0.53 -1.37 32.6ms
7 -8.226856529765 -0.60 -1.64 37.5ms
8 -8.295949022353 -1.16 -1.97 32.9ms
9 -8.345950122910 -1.30 -2.15 32.6ms
10 -8.372843766151 -1.57 -2.41 33.2ms
11 -8.388384810173 -1.81 -2.64 36.8ms
12 -8.393329010000 -2.31 -2.48 33.0ms
13 -8.396662502572 -2.48 -2.84 32.7ms
14 -8.398049741498 -2.86 -3.01 32.7ms
15 -8.399148424016 -2.96 -3.06 37.3ms
16 -8.399789308482 -3.19 -2.91 32.9ms
17 -8.400132025574 -3.47 -3.38 33.3ms
18 -8.400305685271 -3.76 -3.47 37.3ms
19 -8.400381097777 -4.12 -3.67 32.9ms
20 -8.400410901053 -4.53 -3.83 33.0ms
21 -8.400418343507 -5.13 -4.21 33.8ms
22 -8.400424156710 -5.24 -4.27 36.4ms
23 -8.400426365891 -5.66 -4.55 32.8ms
24 -8.400427208784 -6.07 -4.85 33.0ms
25 -8.400427691450 -6.32 -5.02 33.1ms
26 -8.400427933554 -6.62 -4.99 36.6ms
27 -8.400428061100 -6.89 -5.18 32.6ms
28 -8.400428112637 -7.29 -5.45 32.9ms
29 -8.400428133615 -7.68 -5.59 37.3ms
30 -8.400428143658 -8.00 -5.93 32.3ms
31 -8.400428148464 -8.32 -5.82 32.1ms
32 -8.400428150896 -8.61 -6.33 33.2ms
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.397845481102 -0.90 5.0 25.5ms
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.400427981114 -1.79 575ms
2 -8.400428152209 -6.77 -4.03 389ms
3 -8.400428152209 -14.45 -7.83 119ms
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.4857527455555035e-7
|ρ_newton - ρ_scfv| = 3.489946670649073e-7
|ρ_newton - ρ_dm| = 3.290745223565668e-6