Coverage for gpaw/lcao/dipoletransition.py: 100%
48 statements
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-14 00:18 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-14 00:18 +0000
1import numpy as np
3from gpaw.mpi import world
4from gpaw.typing import ArrayND
7# NOTE: This routine is not specific to Raman per se. Maybe it should go
8# somewhere else?
9def get_dipole_transitions(wfs) -> ArrayND:
10 r"""
11 Finds dipole transition matrix elements based on the velocity form.
13 Dipole and momentum matrix elements are related by the expression:
14 <nk|r|mk> = -i hbar/m <nk|p|mk> / (E_nk-E_mk)
16 For n=m this is ill defined, and element will be set to zero.
18 Parameters
19 ----------
20 wfs
21 LCAO WaveFunctions object
22 """
23 p_skvnm = get_momentum_transitions(wfs, False)
24 r_skvnm = np.zeros_like(p_skvnm)
25 for kpt in wfs.kpt_u:
26 # NOTE: Check whether it's this way or other way around.
27 deltaE = kpt.eps_n[:, None] - kpt.eps_n[None, :]
28 # The treatment of the energy difference in the denominator is
29 # from https://doi.org/10.1103/PhysRevB.52.14636 eq 7 and note 18
30 # therein
31 r_skvnm[kpt.s, kpt.k, :] = -1j * p_skvnm[kpt.s, kpt.k, :] * \
32 np.reciprocal(deltaE, where=~np.isclose(deltaE, 0.0))
33 wfs.kd.comm.sum(r_skvnm)
34 return r_skvnm
37def get_momentum_transitions(wfs, savetofile: bool = True) -> ArrayND:
38 r"""
39 Finds the momentum matrix elements:
40 <nk|p|mk> = k \delta_nm - i <nk|\nabla|mk>
42 Parameters
43 ----------
44 wfs
45 LCAO WaveFunctions object
46 savetofile: bool
47 Determines whether matrix is written to the
48 file mom_skvnm.npy (default=True)
49 """
50 assert wfs.bd.comm.size == 1
51 assert wfs.mode == 'lcao'
52 nbands = wfs.bd.nbands
53 nspins = wfs.nspins
54 nk = wfs.kd.nibzkpts
55 gd = wfs.gd
56 dtype = wfs.dtype
57 ksl = wfs.ksl
59 # print(wfs.kd.comm.size, wfs.gd.comm.size, wfs.bd.comm.size)
61 mom_skvnm = np.zeros((nspins, nk, 3, nbands, nbands), dtype=complex)
63 dThetadR_qvMM, _ = wfs.manytci.O_qMM_T_qMM(gd.comm, ksl.Mstart,
64 ksl.Mstop, False,
65 derivative=True)
67 mome_skvnm = np.zeros((nspins, nk, 3, nbands, nbands), dtype=dtype)
68 momd_skv = np.zeros((nspins, nk, 3), dtype=dtype)
69 moma_skvnm = np.zeros((nspins, nk, 3, nbands, nbands), dtype=dtype)
71 B_cv = 2.0 * np.pi * gd.icell_cv
72 for kpt in wfs.kpt_u:
73 C_nM = kpt.C_nM
74 for v in range(3):
75 dThetadRv_MM = dThetadR_qvMM[kpt.q, v]
76 nabla_nn = -(C_nM.conj() @ dThetadRv_MM.conj() @ C_nM.T)
77 gd.comm.sum(nabla_nn)
78 mome_skvnm[kpt.s, kpt.k, v] = nabla_nn
80 # augmentation part
81 moma_vnm = np.zeros((3, nbands, nbands), dtype=dtype)
82 for a, P_ni in kpt.P_ani.items():
83 nabla_iiv = wfs.setups[a].nabla_iiv
84 moma_vnm += np.einsum('ni,ijv,mj->vnm',
85 P_ni.conj(), nabla_iiv, P_ni, optimize=True)
86 gd.comm.sum(moma_vnm)
87 moma_skvnm[kpt.s, kpt.k] = moma_vnm
89 # diagonal term
90 k_v = np.dot(wfs.kd.ibzk_kc[kpt.k], B_cv)
91 momd_skv[kpt.s, kpt.k, :] = k_v
93 mom_skvnm = - 1j * (mome_skvnm + moma_skvnm)
94 # Extract a view of the diagonal elements out
95 # Einsum with no optimization since that is empirically faster here
96 mom_diag = np.einsum('skvnn->skvn', mom_skvnm)
97 # Add the diagonal term. Since mom_diag is a view, this changes mom_skvnm
98 mom_diag += momd_skv[..., None]
99 wfs.kd.comm.sum(mom_skvnm)
101 if world.rank == 0 and savetofile:
102 np.save('mom_skvnm.npy', mom_skvnm)
103 return mom_skvnm