Source code for gpaw.lcao.dipoletransition

import numpy as np

from gpaw.mpi import world
from gpaw.typing import ArrayND


# NOTE: This routine is not specific to Raman per se. Maybe it should go
#       somewhere else?
def get_dipole_transitions(wfs) -> ArrayND:
    r"""
    Finds dipole transition matrix elements based on the velocity form.

    Dipole and momentum matrix elements are related by the expression:
    <nk|r|mk> = -i hbar/m <nk|p|mk> / (E_nk-E_mk)

    For n=m this is ill defined, and element will be set to zero.

    Parameters
    ----------
    wfs
        LCAO WaveFunctions object
    """
    p_skvnm = get_momentum_transitions(wfs, False)
    r_skvnm = np.zeros_like(p_skvnm)
    for kpt in wfs.kpt_u:
        # NOTE: Check whether it's this way or other way around.
        deltaE = kpt.eps_n[:, None] - kpt.eps_n[None, :]
        # The treatment of the energy difference in the denominator is
        # from https://doi.org/10.1103/PhysRevB.52.14636 eq 7 and note 18
        # therein
        r_skvnm[kpt.s, kpt.k, :] = -1j * p_skvnm[kpt.s, kpt.k, :] * \
            np.reciprocal(deltaE, where=~np.isclose(deltaE, 0.0))
    wfs.kd.comm.sum(r_skvnm)
    return r_skvnm


[docs]def get_momentum_transitions(wfs, savetofile: bool = True) -> ArrayND: r""" Finds the momentum matrix elements: <nk|p|mk> = k \delta_nm - i <nk|\nabla|mk> Parameters ---------- wfs LCAO WaveFunctions object savetofile: bool Determines whether matrix is written to the file mom_skvnm.npy (default=True) """ assert wfs.bd.comm.size == 1 assert wfs.mode == 'lcao' nbands = wfs.bd.nbands nspins = wfs.nspins nk = wfs.kd.nibzkpts gd = wfs.gd dtype = wfs.dtype ksl = wfs.ksl # print(wfs.kd.comm.size, wfs.gd.comm.size, wfs.bd.comm.size) mom_skvnm = np.zeros((nspins, nk, 3, nbands, nbands), dtype=complex) dThetadR_qvMM, _ = wfs.manytci.O_qMM_T_qMM(gd.comm, ksl.Mstart, ksl.Mstop, False, derivative=True) mome_skvnm = np.zeros((nspins, nk, 3, nbands, nbands), dtype=dtype) momd_skv = np.zeros((nspins, nk, 3), dtype=dtype) moma_skvnm = np.zeros((nspins, nk, 3, nbands, nbands), dtype=dtype) B_cv = 2.0 * np.pi * gd.icell_cv for kpt in wfs.kpt_u: C_nM = kpt.C_nM for v in range(3): dThetadRv_MM = dThetadR_qvMM[kpt.q, v] nabla_nn = -(C_nM.conj() @ dThetadRv_MM.conj() @ C_nM.T) gd.comm.sum(nabla_nn) mome_skvnm[kpt.s, kpt.k, v] = nabla_nn # augmentation part moma_vnm = np.zeros((3, nbands, nbands), dtype=dtype) for a, P_ni in kpt.P_ani.items(): nabla_iiv = wfs.setups[a].nabla_iiv moma_vnm += np.einsum('ni,ijv,mj->vnm', P_ni.conj(), nabla_iiv, P_ni, optimize=True) gd.comm.sum(moma_vnm) moma_skvnm[kpt.s, kpt.k] = moma_vnm # diagonal term k_v = np.dot(wfs.kd.ibzk_kc[kpt.k], B_cv) momd_skv[kpt.s, kpt.k, :] = k_v mom_skvnm = - 1j * (mome_skvnm + moma_skvnm) # Extract a view of the diagonal elements out # Einsum with no optimization since that is empirically faster here mom_diag = np.einsum('skvnn->skvn', mom_skvnm) # Add the diagonal term. Since mom_diag is a view, this changes mom_skvnm mom_diag += momd_skv[..., None] wfs.kd.comm.sum(mom_skvnm) if world.rank == 0 and savetofile: np.save('mom_skvnm.npy', mom_skvnm) return mom_skvnm