Skip to content

Note: theoretical framework of COHP may have strong basis set dependence #3718

@kirk0830

Description

@kirk0830

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)

  • Reproduce the performance issue on a similar system or environment.
  • Identify the specific section of the code causing the performance issue.
  • Investigate the issue and determine the root cause.
  • Research best practices and potential solutions for the identified performance issue.
  • Implement the chosen solution to address the performance issue.
  • Test the implemented solution to ensure it improves performance without introducing new issues.
  • Optimize the solution if necessary, considering trade-offs between performance and other factors (e.g., code complexity, readability, maintainability).
  • Review and incorporate any relevant feedback from users or developers.
  • Merge the improved solution into the main codebase and notify the issue reporter.

Metadata

Metadata

Assignees

Labels

Feature DiscussedThe features will be discussed first but will not be implemented soon

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions