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
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-08 00:17 +0000
1from __future__ import annotations
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.')
14 class QPoint(): # type: ignore
15 def __init__(self, *args, **kwargs):
16 raise ImportError('qeh not installed, \
17 or is too old.')
20class QEHChiCalc(ChiCalc):
21 qdim = {'x': 0, 'y': 1}
23 def __init__(self,
24 df: DielectricFunction,
25 qinf_rel: float = 1e-6,
26 direction: str = 'x'):
28 ''' GPAW superclass for interfacing with QEH
29 building block calculations.
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 '''
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
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")
56 qdir = self.qdim[self.direction]
57 kd = self.df.gs.kd
58 self.Nk = kd.N_c[qdir]
60 super().__init__()
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]
66 gd = self.df.gs.gd
67 icell_cv = gd.icell_cv
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)
74 # Avoid Gamma-point
75 q_qc[0] = q_qc[1] * self.qinf_rel
77 q_qv = q_qc @ icell_cv * 2 * pi
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]
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)]
90 return Q_q
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.
98 Parameters:
99 q_c (np.ndarray): The q-point in reciprocal space.
100 q_max (float | None): The maximum q-value.
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])]
115 def get_z_grid(self):
116 r = self.df.gs.gd.get_grid_point_coordinates()
117 return r[2, 0, 0, :]
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()
130 G_Gv = qpd.get_reciprocal_vectors(add_q=False)
132 return chi_wGG, G_Gv, wblocks
134 def get_atoms(self):
135 return self.df.gs.atoms