AtomsBase integration
AtomsBase.jl is a common interface for representing atomic structures in Julia. DFTK directly supports using such structures to run a calculation as is demonstrated here.
using DFTK
using AtomsBuilder
Feeding an AtomsBase AbstractSystem to DFTK
In this example we construct a bulk silicon system using the bulk
function from AtomsBuilder. This function uses tabulated data to set up a reasonable starting geometry and lattice for bulk silicon.
system = bulk(:Si)
FlexibleSystem(Si₂, periodicity = TTT):
cell_vectors : [ 0 2.715 2.715;
2.715 0 2.715;
2.715 2.715 0]u"Å"
Atom(Si, [ 0, 0, 0]u"Å")
Atom(Si, [ 1.3575, 1.3575, 1.3575]u"Å")
By default the atoms of an AbstractSystem
employ the bare Coulomb potential. To employ pseudpotential models (which is almost always advisable for plane-wave DFT) one employs the pseudopotential
keyword argument in model constructors such as model_DFT
. For example we can employ a PseudoFamily
object from the PseudoPotentialData package. See its documentation for more information on the available pseudopotential families and how to select them.
using PseudoPotentialData # defines PseudoFamily
pd_lda_family = PseudoFamily("dojo.nc.sr.lda.v0_4_1.oncvpsp3.standard.upf")
model = model_DFT(system;
functionals=LDA(),
temperature=1e-3,
pseudopotentials=pd_lda_family)
Model(lda_x+lda_c_pw, 3D):
lattice (in Bohr) : [0 , 5.13061 , 5.13061 ]
[5.13061 , 0 , 5.13061 ]
[5.13061 , 5.13061 , 0 ]
unit cell volume : 270.11 Bohr³
atoms : Si₂
atom potentials : ElementPsp(Si, "/home/runner/.julia/artifacts/1ea71a84cf375286564538a9cab789991f4bf1f4/Si.upf")
ElementPsp(Si, "/home/runner/.julia/artifacts/1ea71a84cf375286564538a9cab789991f4bf1f4/Si.upf")
num. electrons : 8
spin polarization : none
temperature : 0.001 Ha
smearing : DFTK.Smearing.FermiDirac()
terms : Kinetic()
AtomicLocal()
AtomicNonlocal()
Ewald(nothing)
PspCorrection()
Hartree()
Xc(lda_x, lda_c_pw)
Entropy()
Alternatively the pseudopotentials
object also accepts a Dict{Symbol,String}
, which provides for each element symbol the filename or identifier of the pseudopotential to be employed, e.g.
model = model_DFT(system;
functionals=LDA(),
temperature=1e-3,
pseudopotentials=Dict(:Si => "hgh/lda/si-q4"))
Model(lda_x+lda_c_pw, 3D):
lattice (in Bohr) : [0 , 5.13061 , 5.13061 ]
[5.13061 , 0 , 5.13061 ]
[5.13061 , 5.13061 , 0 ]
unit cell volume : 270.11 Bohr³
atoms : Si₂
atom potentials : ElementPsp(Si, "hgh/lda/si-q4")
ElementPsp(Si, "hgh/lda/si-q4")
num. electrons : 8
spin polarization : none
temperature : 0.001 Ha
smearing : DFTK.Smearing.FermiDirac()
terms : Kinetic()
AtomicLocal()
AtomicNonlocal()
Ewald(nothing)
PspCorrection()
Hartree()
Xc(lda_x, lda_c_pw)
Entropy()
We can then discretise such a model and solve:
basis = PlaneWaveBasis(model; Ecut=15, kgrid=[4, 4, 4])
scfres = self_consistent_field(basis, tol=1e-8);
n Energy log10(ΔE) log10(Δρ) Diag Δtime
--- --------------- --------- --------- ---- ------
1 -7.921718134493 -0.69 6.2 262ms
2 -7.926160277393 -2.35 -1.22 1.0 165ms
3 -7.926838399200 -3.17 -2.37 1.9 238ms
4 -7.926861174510 -4.64 -3.03 2.1 783ms
5 -7.926861646000 -6.33 -3.38 2.0 179ms
6 -7.926861668110 -7.66 -3.75 1.5 142ms
7 -7.926861678709 -7.97 -4.08 1.2 136ms
8 -7.926861681792 -8.51 -5.06 1.4 138ms
9 -7.926861681859 -10.18 -5.18 3.2 202ms
10 -7.926861681872 -10.88 -6.40 1.0 133ms
11 -7.926861681873 -12.14 -6.53 3.0 187ms
12 -7.926861681873 -14.21 -7.42 1.0 158ms
┌ Warning: Eigensolver not converged
│ n_iter =
│ 8-element Vector{Int64}:
│ 2
│ 2
│ 2
│ 3
│ 3
│ 2
│ 2
│ 3
└ @ DFTK ~/work/DFTK.jl/DFTK.jl/src/scf/self_consistent_field.jl:76
13 -7.926861681873 -15.05 -7.66 2.4 164ms
14 -7.926861681873 + -14.57 -7.89 1.1 141ms
15 -7.926861681873 -14.35 -9.58 1.5 350ms
If we did not want to use AtomsBuilder we could of course use any other package which yields an AbstractSystem object. This includes:
Reading a system using AtomsIO
Read a file using AtomsIO, which directly yields an AbstractSystem.
using AtomsIO
system = load_system("Si.extxyz");
Run the LDA calculation:
pseudopotentials = Dict(:Si => "hgh/lda/si-q4")
model = model_DFT(system; pseudopotentials, functionals=LDA(), temperature=1e-3)
basis = PlaneWaveBasis(model; Ecut=15, kgrid=[4, 4, 4])
scfres = self_consistent_field(basis, tol=1e-8);
n Energy log10(ΔE) log10(Δρ) Diag Δtime
--- --------------- --------- --------- ---- ------
1 -7.921721310671 -0.69 5.9 268ms
2 -7.926168572746 -2.35 -1.22 1.0 141ms
3 -7.926838043091 -3.17 -2.37 1.9 165ms
4 -7.926861195075 -4.64 -3.03 2.1 177ms
5 -7.926861647017 -6.34 -3.39 1.9 157ms
6 -7.926861669314 -7.65 -3.78 1.8 149ms
7 -7.926861678938 -8.02 -4.11 1.4 143ms
8 -7.926861681795 -8.54 -5.07 1.4 141ms
9 -7.926861681859 -10.19 -5.20 2.8 176ms
10 -7.926861681872 -10.89 -6.42 1.0 137ms
11 -7.926861681873 -12.14 -6.54 3.1 194ms
12 -7.926861681873 -14.15 -7.25 1.0 137ms
13 -7.926861681873 + -14.75 -7.67 1.9 154ms
14 -7.926861681873 + -Inf -8.46 1.8 173ms
The same could be achieved using ExtXYZ by system = Atoms(read_frame("Si.extxyz"))
, since the ExtXYZ.Atoms
object is directly AtomsBase-compatible.
Directly setting up a system in AtomsBase
using AtomsBase
using Unitful
using UnitfulAtomic
# Construct a system in the AtomsBase world
a = 10.26u"bohr" # Silicon lattice constant
lattice = a / 2 * [[0, 1, 1.], # Lattice as vector of vectors
[1, 0, 1.],
[1, 1, 0.]]
atoms = [:Si => ones(3)/8, :Si => -ones(3)/8]
system = periodic_system(atoms, lattice; fractional=true)
# Now run the LDA calculation:
pseudopotentials = Dict(:Si => "hgh/lda/si-q4")
model = model_DFT(system; pseudopotentials, functionals=LDA(), temperature=1e-3)
basis = PlaneWaveBasis(model; Ecut=15, kgrid=[4, 4, 4])
scfres = self_consistent_field(basis, tol=1e-4);
n Energy log10(ΔE) log10(Δρ) Diag Δtime
--- --------------- --------- --------- ---- ------
1 -7.921720433959 -0.69 5.9 291ms
2 -7.926167646636 -2.35 -1.22 1.0 148ms
3 -7.926842141940 -3.17 -2.37 1.9 182ms
4 -7.926864595578 -4.65 -3.02 2.2 215ms
5 -7.926865046688 -6.35 -3.34 2.0 182ms
6 -7.926865074887 -7.55 -3.68 1.4 146ms
7 -7.926865090665 -7.80 -4.17 1.0 131ms
Obtaining an AbstractSystem from DFTK data
At any point we can also get back the DFTK model as an AtomsBase-compatible AbstractSystem
:
second_system = atomic_system(model)
FlexibleSystem(Si₂, periodicity = TTT):
cell_vectors : [ 0 5.13 5.13;
5.13 0 5.13;
5.13 5.13 0]u"a₀"
Atom(Si, [ 1.2825, 1.2825, 1.2825]u"a₀")
Atom(Si, [ -1.2825, -1.2825, -1.2825]u"a₀")
Similarly DFTK offers a method to the atomic_system
and periodic_system
functions (from AtomsBase), which enable a seamless conversion of the usual data structures for setting up DFTK calculations into an AbstractSystem
:
lattice = 5.431u"Å" / 2 * [[0 1 1.];
[1 0 1.];
[1 1 0.]];
Si = ElementPsp(:Si, load_psp("hgh/lda/Si-q4"))
atoms = [Si, Si]
positions = [ones(3)/8, -ones(3)/8]
third_system = atomic_system(lattice, atoms, positions)
FlexibleSystem(Si₂, periodicity = TTT):
cell_vectors : [ 0 5.13155 5.13155;
5.13155 0 5.13155;
5.13155 5.13155 0]u"a₀"
Atom(Si, [ 1.28289, 1.28289, 1.28289]u"a₀")
Atom(Si, [-1.28289, -1.28289, -1.28289]u"a₀")