Coverage for gpaw/response/qpd.py: 79%
68 statements
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-12 00:18 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-12 00:18 +0000
1from math import pi
2import numpy as np
4from gpaw.kpt_descriptor import KPointDescriptor
5from gpaw.pw.descriptor import PWDescriptor
6import gpaw.fftw as fftw
9class SingleQPWDescriptor(PWDescriptor):
10 @staticmethod
11 def from_q(q_c, ecut, gd, gammacentered=False):
12 """Construct a plane wave descriptor for q_c with a given cutoff."""
13 qd = KPointDescriptor([q_c])
14 if not isinstance(ecut, dict):
15 return SingleQPWDescriptor(ecut, gd, complex, qd,
16 gammacentered=gammacentered)
17 elif ecut['class'] is SingleCylQPWDescriptor:
18 return SingleCylQPWDescriptor(gd=gd,
19 kd=qd,
20 gammacentered=gammacentered,
21 dtype=complex,
22 **ecut['kwargs'])
23 else:
24 raise NotImplementedError(
25 f'Unrecognized QPW class: {ecut["class"]}')
27 @property
28 def q_c(self):
29 return self.kd.bzk_kc[0]
31 @property
32 def optical_limit(self):
33 return np.allclose(self.q_c, 0.0)
35 def copy(self):
36 return self.copy_with()
38 def copy_with(self, ecut=None, gd=None, gammacentered=None):
39 if ecut is None:
40 ecut = self.ecut
41 if gd is None:
42 gd = self.gd
43 if gammacentered is None:
44 gammacentered = self.gammacentered
46 return SingleQPWDescriptor.from_q(
47 self.q_c, ecut, gd, gammacentered=gammacentered)
50class SingleCylQPWDescriptor(SingleQPWDescriptor):
51 def __init__(self, ecut_xy, ecut_z, gd, dtype=None, kd=None,
52 fftwflags=fftw.MEASURE, gammacentered=False):
54 ecut0 = 0.5 * pi**2 / (gd.h_cv**2).sum(1).max()
55 if ecut_xy is None:
56 ecut_xy = 0.9999 * ecut0
57 else:
58 assert ecut_xy <= ecut0
60 if ecut_z is None:
61 ecut_z = 0.9999 * ecut0
62 else:
63 assert ecut_z <= ecut0
65 self.ecut_z = ecut_z
66 self.ecut_xy = ecut_xy
68 super().__init__(ecut_xy, gd, dtype, kd,
69 fftwflags, gammacentered)
71 def setup_pw_grid(self, i_Qc, Q_Q):
72 ng_q = []
73 Q_qG = []
74 G2_qG = []
75 for q, K_v in enumerate(self.K_qv):
76 G2_Q = ((self.G_Qv + K_v)**2).sum(axis=1)
77 if self.gammacentered:
78 mask_Q = ((self.G_Qv[:, 0:2]**2).sum(axis=1)
79 <= 2 * self.ecut_xy) \
80 & ((self.G_Qv[:, 2]**2) <= 2 * self.ecut_z)
81 else:
82 G3_Q = ((self.G_Qv[:, 0:2] + K_v[0:2])**2).sum(axis=1)
83 mask_Q = (G3_Q <= (2 * self.ecut_xy)) \
84 & ((self.G_Qv[:, 2]**2) <= (2 * self.ecut_z))
86 if self.dtype == float:
87 mask_Q &= ((i_Qc[:, 2] > 0) |
88 (i_Qc[:, 1] > 0) |
89 ((i_Qc[:, 0] >= 0) & (i_Qc[:, 1] == 0)))
90 Q_G = Q_Q[mask_Q]
91 Q_qG.append(Q_G)
92 G2_qG.append(G2_Q[Q_G])
93 ng = len(Q_G)
94 ng_q.append(ng)
96 return ng_q, Q_qG, G2_qG
98 def copy_with(self, ecut=None, gd=None, gammacentered=None):
99 if ecut is None:
100 ecut = self.ecut_xy
101 if gd is None:
102 gd = self.gd
103 if gammacentered is None:
104 gammacentered = self.gammacentered
106 return SingleCylQPWDescriptor.from_q(
107 self.q_c, ecut_xy=ecut, ecut_z=self.ecut_z,
108 gd=gd, gammacentered=gammacentered)