Coverage for gpaw/response/qeh.py: 30%

63 statements  

« prev     ^ index     » next       coverage.py v7.7.1, created at 2025-07-08 00:17 +0000

1from __future__ import annotations 

2 

3import numpy as np 

4from ase.units import pi 

5from gpaw.response.df import DielectricFunction 

6try: 

7 from qeh.bb_calculator.chicalc import ChiCalc, QPoint 

8except ImportError: 

9 class ChiCalc(): # type: ignore 

10 def __init__(self, *args, **kwargs): 

11 raise ImportError('qeh not installed, \ 

12 or is too old.') 

13 

14 class QPoint(): # type: ignore 

15 def __init__(self, *args, **kwargs): 

16 raise ImportError('qeh not installed, \ 

17 or is too old.') 

18 

19 

20class QEHChiCalc(ChiCalc): 

21 qdim = {'x': 0, 'y': 1} 

22 

23 def __init__(self, 

24 df: DielectricFunction, 

25 qinf_rel: float = 1e-6, 

26 direction: str = 'x'): 

27 

28 ''' GPAW superclass for interfacing with QEH 

29 building block calculations. 

30 

31 Parameters 

32 ---------- 

33 df : DielectricFunction 

34 the dielectric function calculator 

35 qinf_rel : float 

36 the position of the gamma q-point, 

37 relative to the first non-gamma q-point, 

38 necessary due to the undefined nature of 

39 chi_wGG in the gamma q-point. 

40 direction : str (either 'x' or 'y') 

41 the direction of the q-grid in terms of 

42 the reciprocal lattice vectors. 

43 ''' 

44 

45 self.df = df # DielectricFunctionCalculator 

46 self.L = df.gs.gd.cell_cv[2, 2] 

47 self.omega_w = self.df.chi0calc.wd.omega_w 

48 self.direction = direction 

49 self.context = self.df.context 

50 self.qinf_rel = qinf_rel 

51 

52 if not (self.df.gs.kd.ibzk_kc == [0, 0, 0]).any(): 

53 raise ValueError("Only Gamma-centered \ 

54 k-point grids are supported") 

55 

56 qdir = self.qdim[self.direction] 

57 kd = self.df.gs.kd 

58 self.Nk = kd.N_c[qdir] 

59 

60 super().__init__() 

61 

62 def get_q_grid(self, q_max: float | None = None): 

63 # First get q-points on the grid 

64 qdir = self.qdim[self.direction] 

65 

66 gd = self.df.gs.gd 

67 icell_cv = gd.icell_cv 

68 

69 # Make q-grid 

70 q_qc = np.zeros([self.Nk, 3], dtype=float) 

71 q_qc[:, qdir] = np.linspace(0, 1, self.Nk, 

72 endpoint=False) 

73 

74 # Avoid Gamma-point 

75 q_qc[0] = q_qc[1] * self.qinf_rel 

76 

77 q_qv = q_qc @ icell_cv * 2 * pi 

78 

79 # Filter the q-points with q_max 

80 if q_max is not None: 

81 q_mask = np.linalg.norm(q_qv, axis=1) <= q_max 

82 q_qc = q_qc[q_mask] 

83 q_qv = q_qv[q_mask] 

84 

85 # Make list of QPoints for calculation 

86 Q_q = [QPoint(q_c=q_c, q_v=q_v, 

87 P_rv=self.determine_P_rv(q_c, q_max)) 

88 for q_c, q_v in zip(q_qc, q_qv)] 

89 

90 return Q_q 

91 

92 def determine_P_rv(self, q_c: np.ndarray, q_max: float | None): 

93 """ 

94 Determine the reciprocal space vectors P_rv that correspond 

95 to unfold the given q-point out of the 1st BZ 

96 given a q-point and the maximum q-value. 

97 

98 Parameters: 

99 q_c (np.ndarray): The q-point in reciprocal space. 

100 q_max (float | None): The maximum q-value. 

101 

102 Returns: 

103 list: A list of reciprocal space vectors P_rv. 

104 """ 

105 if q_max is not None: 

106 icell_cv = self.df.gs.gd.icell_cv 

107 qdir = self.qdim[self.direction] 

108 qc_max = q_max / np.linalg.norm(icell_cv[qdir] * 2 * pi) 

109 P_rv = [icell_cv[qdir] * 2 * pi * i 

110 for i in range(0, int(qc_max - q_c[qdir]) + 1)] 

111 return P_rv 

112 else: 

113 return [np.array([0, 0, 0])] 

114 

115 def get_z_grid(self): 

116 r = self.df.gs.gd.get_grid_point_coordinates() 

117 return r[2, 0, 0, :] 

118 

119 def get_chi_wGG(self, qpoint: QPoint): 

120 if np.linalg.norm(qpoint.q_c) <= (2 * self.qinf_rel / self.Nk): 

121 chi0_dyson_eqs = self.df.get_chi0_dyson_eqs([0, 0, 0], 

122 truncation='2D') 

123 qpd, chi_wGG, wblocks = chi0_dyson_eqs.rpa_density_response( 

124 qinf_v=qpoint.q_v, direction=qpoint.q_v) 

125 else: 

126 chi0_dyson_eqs = self.df.get_chi0_dyson_eqs(qpoint.q_c, 

127 truncation='2D') 

128 qpd, chi_wGG, wblocks = chi0_dyson_eqs.rpa_density_response() 

129 

130 G_Gv = qpd.get_reciprocal_vectors(add_q=False) 

131 

132 return chi_wGG, G_Gv, wblocks 

133 

134 def get_atoms(self): 

135 return self.df.gs.atoms