Fitting tight-binding coefficients from Quantum Espresso.
ThreeBodyTB has a set of pre-fit coefficients that are sufficient for running elemental or binary systems without doing your own fitting. If you still want to do the fitting yourself, read on...
Currently, ThreeBodyTB is set up to fit coefficients from Quantum Espresso (QE) DFT calculations from pw.x
using the projwfc.x
code to get atomic-wavefunction-projected band structures.
It may be easiest to consider the fitting example in the examples/
folder while reading this.
A brief overview of the steps:
Tell
ThreeBody.jl
where you QE code is, and optionally pseudopotentials, etc.Run self-consistent-field (SCF) non-spin-polarized DFT total energy calculations.
Run
ThreeBodyTB.AtomicProj.projwfc_workf
to get k-space tight-binding models for each SCF calculation. This runs a non-SCF DFT calculation withpw.x
and atomic projections withprojwfc.x
, and then calculates the TB model.Run the fitting code
ThreeBodyTB.FitTB.do_fitting_recursive
0. Preliminaries
You need to install Quantum Espresso (make pw; make pp) and let the code know where the bin/ directory is. For example, set_bin_dirs(qe="/home/kfg/codes/q-e-qe-6.5/bin/")
. You also need to let it know about any mpi commands you may need to run in parallel unless you use mpirun. set_bin_dirs(qe="/home/kfg/codes/q-e-qe-6.5/bin/", mpi="mpirun -np ")
, where you must insert your appropriate command.
Using different pseudopotential or convergence settings.
By default, the code is set up for the slightly modified GBRV pseudopotential set available in this distribution under pseudo/gbrv_pbesol/
. The default template QE input files are in template_inputs/
. You can change those directories as well with set_bin_dirs(pseudodir="mypsp/", templatedir="mytemp/")
If you do change those, you will also have to edit ThreeBodyTB.Atomdata:atoms
. In particular, you need to perform some non-spin-polarized isolated atom calculations to get atomic data. From those, you need the total energy, the number of semicore states, the types of valence orbitals, the energies of valence orbitals, and the U values. You can get U from calculations with variable numbers of electrons.
1. Do DFT calculations.
You need SCF DFT data to fit to. Create crystal structures with makecrys
and then run dft calculations. For example:
c = makecrys([10 0 0; 0 10 0; 0 0 10], [0 0 0], ["H"]);
dft = ThreeBodyTB.DFT.runSCF(c, directory="dirname", tmpdir="dirname",nprocs=8 )
You can look in code_for_dataset_gen/Prototypes.jl
and reference_structures/
for examples of how to create a database of example structures.
You can load dft
variables from the "prefix.save/data-file-schema.xml" output file as [dft = ThreeBodyTB.QE.loadXML("prefix.save")
]. You will need the charge density in the next step to run the non-SCF calculation, but after that you don't need the wavefunctions or charge density from either the DFT or NSCF run.
2. Create the tight-binding Hamiltonian for each DFT calculation
The function ThreeBodyTB.AtomicProj.projwfc_workf
will perform all of the calculations to create a projected tight-binding Hamiltonian from a DFT calculation (workf stands for workflow). The basic steps it will perform are:
Run a non-SCF calculation with more bands, so that every atomic orbital can project onto bands.
Run projwfc.x to get the atomic wavefunction projected band structure (the file
prefix.save/atomic_proj.xml
).Get the projected Hamiltonian in k-space.
(Optional) Fourier transform to get the Hamiltonian in r-space
The optional Fourier transform only works if the k-grid from the NSCF is a regular gamma centered MP grid without symmetry. However, this step isn't necessary for the main fitting procedure. You can use the Hamiltonian in k-space directly, saving computing time.
A typical call is
tbc, tbck, projection_warning = ThreeBodyTB.AtomicProj.projwfc_workf(
dft,
directory="dirname",
nprocs=8,
only_kspace=true)
tbck
is the important return, which is the tight binding Hamiltonian in k-space tb_crys_kspace
. projection_warning
will warn you if the quality of the projection is detected to be low. This can be caused by atoms that are too close together or not including enough empty bands to guarantee that all of the atomic wavefunctions can project onto some states.
In order to fit a SCF model, it is necessary to subtract the self-consistent part from the TB Hamiltonian with ThreeBodyTB.SCF.remove_scf_from_tbc
:
tbck_scf = ThreeBodyTB.SCF.remove_scf_from_tbc(tbck);
You can read/write either tbck
or tbck_scf
with ThreeBodyTB.TB.read_tb_crys_kspace
or ThreeBodyTB.TB.write_tb_crys_kspace
If only_kspace=false
you will also get the real-space tbc
output, which is similar to the "prefix_hr.dat" from Wannier90, and you can also use remove_scf_from_tbc
on that struct and use it in the fitting. The read/write functions are ThreeBodyTB.read_tb_crys
and ThreeBodyTB.write_tb_crys
. These files can also be used to interpolate band structures or DOS to arbitrary k-points like a Wannier Hamiltonian, while the k-space versions are limited to fixed k-point grids.
3. Do actual fitting
An example command to actually do the fitting is
database = ThreeBodyTB.FitTB.do_fitting_recursive(
tbck_scf_list,
dft_list = dft_list,
weights_list=weights_list,
starting_dict=starting_dict,
NLIM=50)
Here, tbck_scf_list
is an array of tbck_scf
or tbc_scf
variables (type TB.tb_crys_kspace
), and dft_list
is an array of dft
variables (type DFToutMod.dftout
). weights_list
is an optional array of the real numbers with relative weights of the structures for the fitting. NLIM
is the maximum number of k-points to use for each structure. Higher values will require more time to do the fitting.
starting_dict
is a dictionary of previously fit coefficients that are not included in the fitting and are kept frozen. For example, when fitting binaries, typically the elemental fitting coefficients are kept frozen to their elemental values.
There are many other options to this function. ks_weight
and rs_weight
are the k-space and real-space weights for fitting of the tight-binding matrix elements. Default is to set both to zero and only fit eigenvalues/energies. energy_weight
is the weight of the total energy, relative to the eigenvalues. Default is 20.0
. niters
is the number of recursive iterations. lambda
is a regularization parameter, default is 0.0
(no regularization). RW_PARAM
is the weight of the fully empty eigenvalues relative to the filled ones. Default is 0.0
(no weight to high energy empty states, although there is some weight to states near the Fermi level).
4. Run calculations with new database
The dictionary returned by the fitting function has coefficient objects (type ThreeBodyTB.CalcTB.coefs
) in it, as well as a variable on whether the coefficients require self-consistency or not. For example, if we fit Al coefficients, there will be keys with (:Al, :Al)
, with the two-body interaction coefs, and (:Al, :Al, :Al)
with the three body interactions, and a bool
called "SCF"
.
You can save the coefficients in xml files for use later with ThreeBodyTB.CalcTB.write_coefs("Al_2bdy.xml", database[(:Al,:Al)])
and read them with ThreeBodyTB.CalcTB.read_coefs("Al_2bdy.xml")
You can use the coefficients to run a calculation by using scf_energy(c; database = database)
, which will override the default database and use your database. The force/stress and relaxations similarly take database
variables.
There is also a way to manage a directory with coefficients in it. You have to setup your directory structure the same way as dats/pbesol/v1.2/
. Then, you can also load coefficients as needed for crystal c
stored in a directory using ThreeBodyTB.ManageDatabase.prepare_database(c; directory="dirname")
into the internal database cache, instead of loading from the default directory with pre-fit coefficients. Note, this will only work if the coefficients follow my naming conventions.