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.397871422849 -0.90 5.2 29.1ms
2 -8.400256360100 -2.62 -1.74 1.0 20.2ms
3 -8.400406731886 -3.82 -2.96 1.5 84.1ms
4 -8.400427867511 -4.67 -2.99 3.2 25.7ms
5 -8.400427973109 -6.98 -3.08 1.0 20.3ms
6 -8.400428147126 -6.76 -4.64 1.0 20.8ms
7 -8.400428151843 -8.33 -4.49 2.2 23.4ms
8 -8.400428152197 -9.45 -5.94 1.0 20.3ms
9 -8.400428152209 -10.92 -6.01 2.5 24.2ms
Potential-based SCF
scfres_scfv = DFTK.scf_potential_mixing(basis; tol);n Energy log10(ΔE) log10(Δρ) α Diag Δtime
--- --------------- --------- --------- ---- ---- ------
1 -8.397873371344 -0.90 5.2 1.33s
2 -8.400384592063 -2.60 -1.79 0.80 2.2 489ms
3 -8.400423044826 -4.42 -2.98 0.80 1.0 215ms
4 -8.400428109568 -5.30 -3.46 0.80 2.5 22.7ms
5 -8.400428149287 -7.40 -4.74 0.80 1.2 19.3ms
6 -8.400428152190 -8.54 -5.66 0.80 2.8 23.0ms
7 -8.400428152209 -10.73 -6.14 0.80 2.0 20.8ms
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.851540917567 -1.07 2.76s
2 -1.693367225484 0.41 -0.68 146ms
3 -4.371156220351 0.43 -0.41 46.9ms
4 -5.987513648481 0.21 -0.50 46.8ms
5 -7.560530922402 0.20 -0.71 47.2ms
6 -7.915992780144 -0.45 -1.33 34.1ms
7 -8.240117397411 -0.49 -1.57 96.1ms
8 -8.301792812880 -1.21 -1.83 34.7ms
9 -8.359339659398 -1.24 -2.28 35.3ms
10 -8.373267170605 -1.86 -2.21 34.1ms
11 -8.388053035236 -1.83 -2.87 35.0ms
12 -8.391888896641 -2.42 -2.86 34.1ms
13 -8.394357196809 -2.61 -2.91 34.0ms
14 -8.395550409041 -2.92 -3.18 34.5ms
15 -8.396736118751 -2.93 -3.15 63.5ms
16 -8.398337276630 -2.80 -2.83 34.9ms
17 -8.399151523072 -3.09 -3.09 35.0ms
18 -8.400015173084 -3.06 -3.92 33.8ms
19 -8.400224684349 -3.68 -3.07 34.5ms
20 -8.400359054470 -3.87 -3.27 34.5ms
21 -8.400388161030 -4.54 -4.00 44.0ms
22 -8.400414513730 -4.58 -4.00 34.5ms
23 -8.400421219811 -5.17 -4.36 34.5ms
24 -8.400425888180 -5.33 -4.48 34.5ms
25 -8.400426874589 -6.01 -4.71 34.7ms
26 -8.400427779034 -6.04 -5.04 41.1ms
27 -8.400427909159 -6.89 -5.24 34.1ms
28 -8.400428066151 -6.80 -5.61 35.6ms
29 -8.400428101593 -7.45 -5.41 34.5ms
30 -8.400428134952 -7.48 -5.94 34.2ms
31 -8.400428142422 -8.13 -5.78 42.0ms
32 -8.400428149596 -8.14 -6.00 34.4ms
33 -8.400428150611 -8.99 -6.14 34.8ms
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.397844512611 -0.90 5.2 29.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.400427983814 -1.78 8.59s
2 -8.400428152209 -6.77 -4.03 3.26s
3 -8.400428152209 -14.45 -7.84 81.0ms
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| = 1.218622089202394e-6
|ρ_newton - ρ_scfv| = 1.445305486135598e-7
|ρ_newton - ρ_dm| = 7.632944843918924e-7