Tamm–Dancoff approximation#
The Tamm–Dancoff approximation (TDA) corresponds to setting the off-diagonal \({B}\)-block in the electronic Hessian \({E}^{[2]}\) to zero and diagonalizing the \({A}\)-block. This leads to computational savings and gives a concrete configuration interaction singles (CIS) representation of the excited states. Moreover, it has typically a small impact on spectra and is therefore justifiable.
We will illustrate this approximation with a study of the lowest excited states in ethylene.
Show code cell source
import numpy as np
import py3Dmol as p3d
import veloxchem as vlx
Show code cell source
ethene_xyz = """6
C 0.67759997 0.00000000 0.00000000
C -0.67759997 0.00000000 0.00000000
H 1.21655197 0.92414474 0.00000000
H 1.21655197 -0.92414474 0.00000000
H -1.21655197 -0.92414474 0.00000000
H -1.21655197 0.92414474 0.00000000
"""
3Dmol.js failed to load for some reason. Please check your browser console for error messages.
First, we optimize the SCF reference state.
molecule = vlx.Molecule.from_xyz_string(ethene_xyz)
basis = vlx.MolecularBasis.read(molecule, "6-31g", ostream=None)
scf_drv = vlx.ScfRestrictedDriver()
scf_drv.ostream.mute()
scf_drv.xcfun = "b3lyp"
scf_results = scf_drv.compute(molecule, basis)
The matrix size in the TDA eigenvalue equation is given by the number of one-electron excitations.
norb = basis.get_dimension_of_basis(molecule)
nocc = molecule.number_of_alpha_electrons()
nvirt = norb - nocc
n = nocc * nvirt
print("Number of occupied orbitals:", nocc)
print("Number of unoccupied orbitals:", nvirt)
print("Number of excitations:", n)
Number of occupied orbitals: 8
Number of unoccupied orbitals: 18
Number of excitations: 144
Second, we retrieve the \(A\)-block of the \(E^{[2]}\)-matrix.
lres_drv = vlx.LinearResponseEigenSolver()
lres_drv.ostream.mute()
_ = lres_drv.compute(molecule, basis, scf_results)
E2 = lres_drv.get_e2(molecule, basis, scf_results)
A = E2[:n, :n]
Third, we perform a diagonalization of matrix \(A\) to obtain the excitation energies. The excitation energies of the five lowest states are printed out.
eigs, X = np.linalg.eigh(A)
print("Excitation energies (eV):\n", eigs[:5] * 27.2114)
Excitation energies (eV):
[8.57868869 9.00818369 9.22485575 9.60189688 9.76762482]
Reference calculation#
Spectra based on the TDA approach can also be obtained with the TDAExciDriver
class. We perform a reference calculation to confirm our results.
tda_drv = vlx.TDAExciDriver()
tda_drv.ostream.mute()
tda_drv.nstates = 5
tda_results = tda_drv.compute(molecule, basis, scf_results)
print("Excitation energies (eV):\n", tda_results["eigenvalues"] * 27.2114)
Excitation energies (eV):
[8.57868811 9.00818379 9.22485652 9.60189671 9.76762549]
We note that these results are in perfect agreement with those obtained above.