Benchmark: density functionals and settings for band gaps

Jetsabel Figueroa, MSc. student in the OptoElectronics group at the TU Delft, benchmarked band gap calculations with BAND.

During her internship with SCM she made use of the python scripting tool PLAMS (see the script with recommended settings at the end of this article) determine the best exchange-correlation functionals for determining band gaps of insulators and semi-conductors. Testing a set of 59 materials, she furthermore studied convergence of band gaps with respect to basis sets and k-point sampling.

Summary: recommended settings for band gap calculations

For accurate band gaps, the TB-mBJ model potential (Physical Review Letters 102, 226401 (2009)) is recommended, with the computational more demanding HSE06 giving similar results. A TZP basis set with a small core and ‘good’ k-point sampling is recommended. For large band gap insulators a normal k-point sampling may also suffice. Spin-orbit coupling will affect band gaps of materials containing heavy elements. A sizable effect was reported for all systems containing Cu or heavier elements. For other systems, scalar ZORA is sufficient. Specifically systems with heavy main group elements (In, Sn, Ga, Sb, Te, Br, Pb, …) have strong spin-orbit coupling effects. See the report for more details. You may also consider the more recent KTB-mBJ parametrization.

Benchmark DFT band gaps

TB-mBJ does well for band gap predictions

Band structure GaN with fat bands and pDOS

Band structure GaN with fat bands and pDOS (TB-mBJ with spin-orbit coupling, TZP basis set and good k-point sampling)

Python script (PLAMS) for screening material band gaps with recommended settings

import os
import sys

coord_dir = '/path_to_xyz_files'

# Settings for the Band calculation:

sett = Settings()

sett.input.Units.length             = 'Angstrom'
sett.input.BasisDefaults.BasisType  = 'TZP'
sett.input.BasisDefaults.core       = 'None'
sett.input.KSpace.Quality           = 'Good'
sett.input.XC.Model                 = 'TB-mBJ'
sett.input.Relativistic             = 'Scalar ZORA'

for root, dirs, filenames in os.walk(coord_dir):
   for f in filenames:
      crystalName = os.path.basename(f).rsplit('.')[0]
      mol = Molecule(os.path.join(root, f))

      # Create and run the job:
      job = BANDJob(molecule=mol, settings=sett)
      job.run()

      if job.check():
         bandgap_au = job.results.readkf('BandStructure','BandGap')
         bandgap_ev = Units.convert(bandgap_au, 'Hartree', 'eV')
         print(crystalName, 'Bandgap: %f eV' % bandgap_ev) 
      else:
         print("job crashed ;", crystalName)
         print(job.results.grep_output('ERROR'))
      sys.stdout.flush()
Key concepts