Coverage for gpaw/hybrids/wrapper.py: 96%
113 statements
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-20 00:19 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-20 00:19 +0000
1from typing import Tuple, Dict
3import numpy as np
5from gpaw.xc import XC
6from .coulomb import coulomb_interaction
7from .forces import calculate_forces
8from .paw import calculate_paw_stuff
9from .scf import apply1, apply2
10from .symmetry import Symmetry
13class HybridXC:
14 orbital_dependent = True
15 type = 'HYB'
17 def __init__(self,
18 xcname: str,
19 fraction: float = None,
20 omega: float = None,
21 yukawa=None):
22 from . import parse_name
23 if xcname in ['EXX', 'PBE0', 'HSE03', 'HSE06', 'B3LYP', 'YS-PBE0']:
24 if fraction is not None or omega is not None or yukawa is not None:
25 raise ValueError
26 self.name = xcname
27 xcname, fraction, omega, yukawa = parse_name(xcname)
28 else:
29 if fraction is None or omega is None:
30 raise ValueError
31 self.name = f'{xcname}-{fraction:.3f}-{omega:.3f}'
33 self.xc = XC(xcname)
34 self.exx_fraction = fraction
35 self.omega = omega
36 self.yukawa = yukawa
38 if xcname == 'null':
39 self.description = ''
40 else:
41 self.description = f'{xcname} + '
42 self.description += f'{fraction} * EXX(omega = {omega} bohr^-1)'
44 self.vlda_sR = None
45 self.v_sknG: Dict[Tuple[int, int], np.ndarray] = {}
47 self.ecc = np.nan
48 self.evc = np.nan
49 self.evv = np.nan
50 self.ekin = np.nan
52 self.sym = None
53 self.coulomb = None
55 def get_setup_name(self):
56 return 'PBE'
58 def initialize(self, dens, ham, wfs):
59 self.dens = dens
60 self.wfs = wfs
61 self.ecc = sum(setup.ExxC for setup in wfs.setups) * self.exx_fraction
62 assert wfs.world.size == wfs.gd.comm.size
64 def get_description(self):
65 return self.description
67 def set_positions(self, spos_ac):
68 self.spos_ac = spos_ac
70 def calculate(self, gd, nt_sr, vt_sr):
71 energy = self.ecc + self.evv + self.evc
72 energy += self.xc.calculate(gd, nt_sr, vt_sr)
73 return energy
75 def calculate_paw_correction(self, setup, D_sp, dH_sp=None, a=None):
76 return self.xc.calculate_paw_correction(setup, D_sp, dH_sp, a=a)
78 def get_kinetic_energy_correction(self):
79 return self.ekin
81 def apply_orbital_dependent_hamiltonian(self, kpt, psit_xG,
82 Htpsit_xG=None, dH_asp=None):
83 wfs = self.wfs
84 if self.coulomb is None:
85 self.coulomb = coulomb_interaction(
86 self.omega, wfs.gd, wfs.kd, yukawa=self.yukawa)
87 self.description += '\n' + self.coulomb.get_description()
88 self.sym = Symmetry(wfs.kd)
90 paw_s = calculate_paw_stuff(wfs, self.dens) # ???????
92 if kpt.f_n is None:
93 # Just use LDA_X for first step:
94 if self.vlda_sR is None:
95 # First time:
96 self.vlda_sR = self.calculate_lda_potential()
97 pd = kpt.psit.pd
98 for psit_G, Htpsit_G in zip(psit_xG, Htpsit_xG):
99 Htpsit_G += pd.fft(self.vlda_sR[kpt.s] *
100 pd.ifft(psit_G, kpt.k), kpt.q)
101 else:
102 self.vlda_sR = None
103 if kpt.psit.array.base is psit_xG.base:
104 if (kpt.s, kpt.k) not in self.v_sknG:
105 assert not any(s == kpt.s for s, k in self.v_sknG)
106 evc, evv, ekin, v_knG = apply1(
107 kpt, Htpsit_xG,
108 wfs,
109 self.coulomb, self.sym,
110 paw_s[kpt.s])
111 if kpt.s == 0:
112 self.evc = 0.0
113 self.evv = 0.0
114 self.ekin = 0.0
115 scale = 2 / wfs.nspins * self.exx_fraction
116 self.evc += evc * scale
117 self.evv += evv * scale
118 self.ekin += ekin * scale
119 self.v_sknG = {(kpt.s, k): v_nG
120 for k, v_nG in v_knG.items()}
121 v_nG = self.v_sknG.pop((kpt.s, kpt.k))
122 else:
123 v_nG = apply2(kpt, psit_xG, Htpsit_xG, wfs,
124 self.coulomb, self.sym,
125 paw_s[kpt.s])
126 Htpsit_xG += v_nG * self.exx_fraction
128 def calculate_lda_potential(self):
129 from gpaw.xc import XC
130 lda = XC('LDA_X')
131 nt_sr = self.dens.nt_sg
132 vt_sr = np.zeros_like(nt_sr)
133 vlda_sR = self.dens.gd.zeros(self.wfs.nspins)
134 lda.calculate(self.dens.finegd, nt_sr, vt_sr)
135 for vt_R, vt_r in zip(vlda_sR, vt_sr):
136 vt_R[:], _ = self.dens.pd3.restrict(vt_r, self.dens.pd2)
137 return vlda_sR * self.exx_fraction
139 def summary(self, log):
140 log(self.description)
142 def add_forces(self, F_av):
143 paw_s = calculate_paw_stuff(self.wfs, self.dens)
144 F_av += calculate_forces(self.wfs,
145 self.coulomb,
146 self.sym,
147 paw_s) * self.exx_fraction
149 def correct_hamiltonian_matrix(self, kpt, H_nn):
150 return
152 def rotate(self, kpt, U_nn):
153 pass # 1 / 0
155 def add_correction(self, kpt, psit_xG, Htpsit_xG, P_axi, c_axi, n_x,
156 calculate_change=False):
157 pass # 1 / 0
159 def read(self, reader):
160 pass
162 def write(self, writer):
163 pass
165 def set_grid_descriptor(self, gd):
166 pass