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.397533475602 -0.90 5.2 27.1ms
2 -8.400190098924 -2.58 -1.73 1.0 18.7ms
3 -8.400398784128 -3.68 -3.02 1.5 19.8ms
4 -8.400427805933 -4.54 -2.95 3.5 25.2ms
5 -8.400428056718 -6.60 -3.25 1.0 18.7ms
6 -8.400428149451 -7.03 -5.04 1.0 67.4ms
7 -8.400428152018 -8.59 -4.62 3.2 25.3ms
8 -8.400428152188 -9.77 -5.06 1.8 21.3ms
9 -8.400428152209 -10.69 -6.32 1.0 19.0ms
Potential-based SCF
scfres_scfv = DFTK.scf_potential_mixing(basis; tol);n Energy log10(ΔE) log10(Δρ) α Diag Δtime
--- --------------- --------- --------- ---- ---- ------
1 -8.397597610391 -0.90 5.5 1.82s
2 -8.400387568653 -2.55 -1.78 0.80 2.0 497ms
3 -8.400423547171 -4.44 -2.98 0.80 1.0 232ms
4 -8.400428107244 -5.34 -3.49 0.80 2.5 21.1ms
5 -8.400428148316 -7.39 -4.91 0.80 1.0 17.2ms
6 -8.400428152200 -8.41 -5.39 0.80 3.2 23.3ms
7 -8.400428152208 -11.11 -6.20 0.80 1.2 17.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/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 +0.825542008740 -1.06 3.39s
2 -1.443459399722 0.36 -0.65 145ms
3 -4.376234794314 0.47 -0.36 44.8ms
4 -5.692409576195 0.12 -0.42 44.8ms
5 -7.344055766914 0.22 -0.59 44.6ms
6 -7.562390618382 -0.66 -1.32 72.7ms
7 -7.954324430012 -0.41 -1.59 33.2ms
8 -8.088628887579 -0.87 -1.69 32.9ms
9 -8.257100741089 -0.77 -1.55 32.9ms
10 -8.302040659293 -1.35 -1.85 33.1ms
11 -8.350297898969 -1.32 -2.61 32.7ms
12 -8.369543166194 -1.72 -2.41 57.9ms
13 -8.382805613927 -1.88 -2.54 33.2ms
14 -8.389152712879 -2.20 -2.74 33.1ms
15 -8.393990214078 -2.32 -2.89 33.5ms
16 -8.396730556740 -2.56 -2.77 33.3ms
17 -8.397965307920 -2.91 -2.89 33.1ms
18 -8.399080362913 -2.95 -3.48 39.6ms
19 -8.399717658499 -3.20 -3.46 33.4ms
20 -8.400051546710 -3.48 -3.57 33.7ms
21 -8.400266049278 -3.67 -3.50 33.2ms
22 -8.400359851361 -4.03 -3.49 33.0ms
23 -8.400396655577 -4.43 -3.71 39.3ms
24 -8.400412279475 -4.81 -4.12 33.3ms
25 -8.400421256515 -5.05 -4.37 33.3ms
26 -8.400424549145 -5.48 -4.46 33.2ms
27 -8.400427145249 -5.59 -4.25 33.1ms
28 -8.400427572955 -6.37 -4.74 39.2ms
29 -8.400427963963 -6.41 -4.90 33.6ms
30 -8.400428029436 -7.18 -4.95 33.2ms
31 -8.400428102679 -7.14 -5.38 33.2ms
32 -8.400428124385 -7.66 -5.28 33.1ms
33 -8.400428143777 -7.71 -5.43 39.1ms
34 -8.400428148447 -8.33 -5.73 33.2ms
35 -8.400428150898 -8.61 -6.22 32.9ms
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.397493151231 -0.90 5.2 27.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 -8.400427980226 -1.80 11.3s
2 -8.400428152209 -6.76 -4.03 3.64s
3 -8.400428152209 -14.75 -7.84 93.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| = 5.514920433609086e-7
|ρ_newton - ρ_scfv| = 5.33750970933964e-7
|ρ_newton - ρ_dm| = 1.945874739613956e-6