Details
COHP is actually a Mulliken population analysis-like way decomposing DOS:
$E^{\mathrm{band}}=\sum_{n\mathbf{k}}{w\left( \mathbf{k}\right)f_{n\mathbf{k}}\epsilon_{n\mathbf{k}}}=\sum_{\mathbf{k}}{w\left( \mathbf{k}\right)}\int_{-\infty}^{\epsilon_F}{\mathrm{d}\epsilon\sum_n{f_{n\mathbf{k}}\epsilon_{n\mathbf{k}}\delta \left( \epsilon _{n\mathbf{k}}-\epsilon \right)}}$
rewritten the eigenenergy as:
$\epsilon_{n\mathbf{k}}=\sum_{Ii,Jj}{c_{Ii,n}^{*}\left(\mathbf{k} \right)H_{Ii,Ji}\left(\mathbf{k}\right) c_{Jj,n}\left(\mathbf{k}\right)}$
Band energy is then in the following form:
$E^{\mathrm{band}}=\sum_{n\mathbf{k}}{w\left( \mathbf{k} \right) f_{n\mathbf{k}}\sum_{Ii,Jj}{c_{Ii,n}^{*}\left( \mathbf{k} \right) H_{Ii,Ji}\left( \mathbf{k} \right) c_{Jj,n}\left( \mathbf{k} \right)}}$
Rearrange, then COHP term will emerge:
$\sum_{IJ}{\left( \sum_{n\mathbf{k}}{w\left( \mathbf{k} \right) f_{n\mathbf{k}}\sum_{ij}{c_{Ii,n}^{*}\left( \mathbf{k} \right) H_{Ii,Ji}\left( \mathbf{k} \right) c_{Jj,n}\left( \mathbf{k} \right)}} \right)}$
Define COHP between atom I and atom J is:
$COHP_{IJ}\left( \epsilon \right) =\sum_{ij}{COHP_{ij}\left( \epsilon \right)}$
indices i and j are those of orbitals belong to I and J, respectively,
$COHP_{ij}\left( \epsilon \right) =\sum_{n\mathbf{k}}{w\left( \mathbf{k} \right) f_{n\mathbf{k}}\delta \left( \epsilon_{n\mathbf{k}}-\epsilon \right) c_{Ii,n}^{*}\left( \mathbf{k} \right) H_{Ii,Ji}\left( \mathbf{k} \right) c_{Jj,n}\left( \mathbf{k} \right)}$
.
I have quickly implement one version of COHP directly based on ABACUS lcao:
To calculate $\Re \left[ c_{Ii,n}^{*}\left( \mathbf{k} \right) H_{Ii,Ji}\left( \mathbf{k} \right) c_{Jj,n}\left( \mathbf{k} \right) \right] $
def cal_COHPmatskIJ_e_ij(Hk, Sk, Ck, atomI_orbs, atomJ_orbs, mode="COHP"):
"""Calculate the energy-resolved COHP or COOP matrix for all bands, every selected orbital pair of the atom I and J, and a k-point.
Args:
Hk: Hamiltonian matrix for a k-point, H(k),
Sk: Overlap matrix for a k-point, S(k),
Ck: Wavefunction set of one k-point, arranged as C(k)[i, n], where i is the local index and n is the band index.
atomI_orbs: indices of the orbitals of the atom I,
atomJ_orbs: indices of the orbitals of the atom J.
mode: "COHP" or "COOP".
Returns:
np.ndarray: energy-resolved COHP or COOP matrix for a k-point.
"""
nrows, ncols = Hk.shape
nlocal, nbands = Ck.shape
assert nrows == ncols
assert nrows == nlocal
# allocate memory for value
value = [np.zeros((len(atomI_orbs), len(atomJ_orbs)), dtype=np.float64) for i in range(nbands)]
for ib in range(nbands):
for i_inval, i_inorb in enumerate(atomI_orbs):
for j_inval, j_inorb in enumerate(atomJ_orbs):
power = Sk[i_inorb, j_inorb] if mode == "COOP" else Hk[i_inorb, j_inorb]
value[ib][i_inval, j_inval] = (Ck[i_inorb, ib].conj()*power*Ck[j_inorb, ib]).real
return value
Then calculate $\sum_{ij}{\Re \left[ c_{Ii,n}^{*}\left( \mathbf{k} \right) H_{Ii,Ji}\left( \mathbf{k} \right) c_{Jj,n}\left( \mathbf{k} \right) \right]}$:
def cal_COHPvalskIJ_e(Hk, Sk, Ek, Ck, atomI_orbs, atomJ_orbs, mode="COHP"):
"""Compute the sum_{ij} over c*{Ii,n}(k)H{IiJj}(k)c{Jj,n}(k),
where I, J are atom indexes and i, j are orbitals indexes.
n is the band index and this quantity corresponds to an eigenenergy.
Args:
Hk (np.ndarray): Hamiltonian matrix for a k-point.
Ck (np.ndarray): Wavefunction set of one k-point.
atomI_orbs (list): indices of the orbitals of the atom I.
atomJ_orbs (list): indices of the orbitals of the atom J.
Returns:
tuple: eigenenergies and the sum_{ij} on c*{Ii,n}(k)H{IiJj}(k)c{Jj,n}(k) for each band.
"""
nlocal, nband = Ck.shape
matskIJ_ij_e = cal_COHPmatskIJ_e_ij(Hk, Sk, Ck, atomI_orbs, atomJ_orbs, mode=mode)
# dimension assertation
assert len(Ek) == nband
assert len(matskIJ_ij_e) == nband
for ib in range(nband):
assert matskIJ_ij_e[ib].shape == (len(atomI_orbs), len(atomJ_orbs))
# compute the sum_{ij} over c*{Ii,n}(k)H{IiJj}(k)c{Jj,n}(k), i.e., for atom-pair IJ
valskIJ_e = [np.sum(matskIJ_ij_e[i]) for i in range(nband)]
return Ek, valskIJ_e
Then $w\left( \mathbf{k} \right) \sum_{ij}{\Re \left[ c_{Ii,n}^{*}\left( \mathbf{k} \right) H_{Ii,Ji}\left( \mathbf{k} \right) c_{Jj,n}\left( \mathbf{k} \right) \right]}$, which means summation over kpoints should consider their weights due to symmetry.
def cal_COHPvalsIJ_e(Hks, Sks, Eks, Cks, wk = None,
atomI_orbs = None, atomJ_orbs = None,
mode: str = "COHP"):
nks = len(Hks)
nbands = len(Eks[0])
wk = [1/nks for i in range(nks)] if wk is None else wk
Evals = []
COHPvalsIJ_e = []
for ik in range(nks):
Evalsk, COHPvalskIJ_e = cal_COHPvalskIJ_e(Hk=Hks[ik], Sk=Sks[ik], Ek=Eks[ik], Ck=Cks[ik],
atomI_orbs=atomI_orbs, atomJ_orbs=atomJ_orbs, mode=mode)
# dimension assertation
assert len(Evalsk) == nbands
assert len(COHPvalskIJ_e) == nbands
# add k-point weight
COHPvalskIJ_e = [wk[ik]*COHPvalskIJ_e[i] for i in range(nbands)]
# add to the global list
Evals += Evalsk.tolist()
COHPvalsIJ_e += COHPvalskIJ_e
# dimension assertation
assert len(Evals) == nks*nbands
assert len(COHPvalsIJ_e) == nks*nbands
# sort COHPvalsIJ_e according to the Evals
Evals, COHPvalsIJ_e = zip(*sorted(zip(Evals, COHPvalsIJ_e)))
# energy unit conversion
Evals = np.array([rao.unit_conversion(e, "Ry", "eV") for e in Evals])
return dos_integral(Evals, COHPvalsIJ_e)
Then after zero padding, the COHP between atomI and atomJ is obtained.
However, this result is totally different with what LOBSTER calculated. LOBSTER uses STO or GTO->STO, and the COHP looks similar with Mulliken population analysis, therefore it is not basis set independent. I doubt about the whether COHP is directly useful for ABACUS LCAO with numerical orbitals.
Task list for Issue attackers (only for developers)
Details
COHP is actually a Mulliken population analysis-like way decomposing DOS:
$E^{\mathrm{band}}=\sum_{n\mathbf{k}}{w\left( \mathbf{k}\right)f_{n\mathbf{k}}\epsilon_{n\mathbf{k}}}=\sum_{\mathbf{k}}{w\left( \mathbf{k}\right)}\int_{-\infty}^{\epsilon_F}{\mathrm{d}\epsilon\sum_n{f_{n\mathbf{k}}\epsilon_{n\mathbf{k}}\delta \left( \epsilon _{n\mathbf{k}}-\epsilon \right)}}$
rewritten the eigenenergy as:
Band energy is then in the following form:
Rearrange, then COHP term will emerge:
Define COHP between atom I and atom J is:
indices i and j are those of orbitals belong to I and J, respectively,
.
I have quickly implement one version of COHP directly based on ABACUS lcao:
To calculate$\Re \left[ c_{Ii,n}^{*}\left( \mathbf{k} \right) H_{Ii,Ji}\left( \mathbf{k} \right) c_{Jj,n}\left( \mathbf{k} \right) \right] $
Then calculate$\sum_{ij}{\Re \left[ c_{Ii,n}^{*}\left( \mathbf{k} \right) H_{Ii,Ji}\left( \mathbf{k} \right) c_{Jj,n}\left( \mathbf{k} \right) \right]}$ :
Then$w\left( \mathbf{k} \right) \sum_{ij}{\Re \left[ c_{Ii,n}^{*}\left( \mathbf{k} \right) H_{Ii,Ji}\left( \mathbf{k} \right) c_{Jj,n}\left( \mathbf{k} \right) \right]}$ , which means summation over kpoints should consider their weights due to symmetry.
Then after zero padding, the COHP between
atomIandatomJis obtained.However, this result is totally different with what LOBSTER calculated. LOBSTER uses STO or GTO->STO, and the COHP looks similar with Mulliken population analysis, therefore it is not basis set independent. I doubt about the whether COHP is directly useful for ABACUS LCAO with numerical orbitals.
Task list for Issue attackers (only for developers)