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.397538845565 -0.90 5.2 27.1ms
2 -8.400194756857 -2.58 -1.72 1.0 18.7ms
3 -8.400395266894 -3.70 -3.02 1.5 19.8ms
4 -8.400427836430 -4.49 -2.98 3.2 93.3ms
5 -8.400428087441 -6.60 -3.36 1.0 18.8ms
6 -8.400428149986 -7.20 -5.07 1.0 18.7ms
7 -8.400428152073 -8.68 -4.72 3.2 24.6ms
8 -8.400428152187 -9.94 -5.08 1.5 20.2ms
9 -8.400428152208 -10.67 -6.79 1.0 19.0ms
Potential-based SCF
scfres_scfv = DFTK.scf_potential_mixing(basis; tol);n Energy log10(ΔE) log10(Δρ) α Diag Δtime
--- --------------- --------- --------- ---- ---- ------
1 -8.397533237549 -0.90 5.2 1.82s
2 -8.400385704566 -2.54 -1.80 0.80 2.0 703ms
3 -8.400423160694 -4.43 -3.02 0.80 1.0 258ms
4 -8.400428114330 -5.31 -3.52 0.80 2.2 20.8ms
5 -8.400428148638 -7.46 -5.06 0.80 1.2 17.6ms
6 -8.400428152206 -8.45 -5.35 0.80 3.5 24.0ms
7 -8.400428152209 -11.55 -6.56 0.80 1.0 17.4ms
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.832296113287 -1.05 3.63s
2 -1.459921759952 0.36 -0.65 156ms
3 -4.557706194883 0.49 -0.36 44.0ms
4 -6.082620736756 0.18 -0.47 43.8ms
5 -7.650391623259 0.20 -0.67 91.5ms
6 -8.093207847246 -0.35 -1.25 32.6ms
7 -8.256505027431 -0.79 -1.56 32.7ms
8 -8.305920782550 -1.31 -1.85 32.5ms
9 -8.344917725077 -1.41 -2.16 32.6ms
10 -8.365991629860 -1.68 -2.37 56.9ms
11 -8.383229413726 -1.76 -2.36 32.9ms
12 -8.390696160542 -2.13 -2.38 32.7ms
13 -8.394922017078 -2.37 -2.93 32.8ms
14 -8.397524494449 -2.58 -2.92 32.8ms
15 -8.398900778159 -2.86 -3.02 32.7ms
16 -8.399680206844 -3.11 -3.01 40.0ms
17 -8.400098726912 -3.38 -3.65 32.8ms
18 -8.400270074798 -3.77 -3.54 33.0ms
19 -8.400360291649 -4.04 -3.85 32.4ms
20 -8.400398139441 -4.42 -4.27 32.6ms
21 -8.400411617883 -4.87 -4.16 39.4ms
22 -8.400421671378 -5.00 -4.35 32.9ms
23 -8.400424839369 -5.50 -4.37 33.0ms
24 -8.400426681772 -5.73 -4.66 33.2ms
25 -8.400427422021 -6.13 -5.00 32.7ms
26 -8.400427881622 -6.34 -5.14 38.7ms
27 -8.400428018266 -6.86 -5.51 33.2ms
28 -8.400428088998 -7.15 -5.38 32.8ms
29 -8.400428125927 -7.43 -5.93 32.4ms
30 -8.400428141621 -7.80 -5.52 32.8ms
31 -8.400428148013 -8.19 -6.00 38.5ms
32 -8.400428150256 -8.65 -6.09 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.397541562927 -0.90 5.2 26.6ms
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.400427982316 -1.79 11.7s
2 -8.400428152209 -6.77 -4.04 3.76s
3 -8.400428152209 -14.45 -7.84 91.1ms
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.379856343796421e-7
|ρ_newton - ρ_scfv| = 2.1745553699176832e-7
|ρ_newton - ρ_dm| = 1.8865964914831804e-6