Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
DFTK.jl/examples/custom_solvers.jl
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
76 lines (69 sloc)
2.67 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# # Custom solvers | |
# In this example, we show how to define custom solvers. Our system | |
# will again be silicon, because we are not very imaginative | |
using DFTK, LinearAlgebra | |
a = 10.26 | |
lattice = a / 2 * [[0 1 1.]; | |
[1 0 1.]; | |
[1 1 0.]] | |
Si = ElementPsp(:Si, psp=load_psp("hgh/lda/Si-q4")) | |
atoms = [Si, Si] | |
positions = [ones(3)/8, -ones(3)/8] | |
## We take very (very) crude parameters | |
model = model_LDA(lattice, atoms, positions) | |
basis = PlaneWaveBasis(model; Ecut=5, kgrid=[1, 1, 1]); | |
# We define our custom fix-point solver: simply a damped fixed-point | |
function my_fp_solver(f, x0, max_iter; tol) | |
mixing_factor = .7 | |
x = x0 | |
fx = f(x) | |
for n = 1:max_iter | |
inc = fx - x | |
if norm(inc) < tol | |
break | |
end | |
x = x + mixing_factor * inc | |
fx = f(x) | |
end | |
(fixpoint=x, converged=norm(fx-x) < tol) | |
end; | |
# Our eigenvalue solver just forms the dense matrix and diagonalizes | |
# it explicitly (this only works for very small systems) | |
function my_eig_solver(A, X0; maxiter, tol, kwargs...) | |
n = size(X0, 2) | |
A = Array(A) | |
E = eigen(A) | |
λ = E.values[1:n] | |
X = E.vectors[:, 1:n] | |
(; λ, X, residual_norms=[], iterations=0, converged=true, n_matvec=0) | |
end; | |
# Finally we also define our custom mixing scheme. It will be a mixture | |
# of simple mixing (for the first 2 steps) and than default to Kerker mixing. | |
# In the mixing interface `δF` is ``(ρ_\text{out} - ρ_\text{in})``, i.e. | |
# the difference in density between two subsequent SCF steps and the `mix` | |
# function returns ``δρ``, which is added to ``ρ_\text{in}`` to yield ``ρ_\text{next}``, | |
# the density for the next SCF step. | |
struct MyMixing | |
n_simple # Number of iterations for simple mixing | |
end | |
MyMixing() = MyMixing(2) | |
function DFTK.mix_density(mixing::MyMixing, basis, δF; n_iter, kwargs...) | |
if n_iter <= mixing.n_simple | |
return δF # Simple mixing -> Do not modify update at all | |
else | |
## Use the default KerkerMixing from DFTK | |
DFTK.mix_density(KerkerMixing(), basis, δF; kwargs...) | |
end | |
end | |
# That's it! Now we just run the SCF with these solvers | |
scfres = self_consistent_field(basis; | |
tol=1e-8, | |
solver=my_fp_solver, | |
eigensolver=my_eig_solver, | |
mixing=MyMixing()); | |
# Note that the default convergence criterion is the difference in | |
# density. When this gets below `tol`, the | |
# "driver" `self_consistent_field` artificially makes the fixed-point | |
# solver think it's converged by forcing `f(x) = x`. You can customize | |
# this with the `is_converged` keyword argument to | |
# `self_consistent_field`. |