Coverage for gpaw/xc/gllb/nonlocalfunctional.py: 78%
119 statements
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-09 00:21 +0000
« prev ^ index » next coverage.py v7.7.1, created at 2025-07-09 00:21 +0000
1import numpy as np
2from ase.units import Ha
3from gpaw.xc.functional import XCFunctional
6class NonLocalFunctional(XCFunctional):
7 def __init__(self, xcname, setup_name=None):
8 self.contributions = []
9 # TODO: remove self.xcs once deprecated calculate_delta_xc()
10 # in c_response.py is removed
11 self.xcs = {}
12 XCFunctional.__init__(self, xcname, 'GLLB')
13 if setup_name is None:
14 self.setup_name = self.name
15 else:
16 self.setup_name = setup_name
17 self.mix = None
18 self.mix_vt_sg = None
19 self.old_vt_sg = None
20 self.old_H_asp = {}
21 self.response = None
23 def get_setup_name(self):
24 return self.setup_name
26 def set_mix(self, mix):
27 self.mix = mix
29 def initialize(self, density, hamiltonian, wfs):
30 for contribution in self.contributions:
31 contribution.initialize(density, hamiltonian, wfs)
33 def initialize_1d(self, ae):
34 for contribution in self.contributions:
35 contribution.initialize_1d(ae)
37 def calculate_impl(self, gd, n_sg, v_sg, e_g):
38 e_g[:] = 0.0
39 if self.mix is None:
40 for contribution in self.contributions:
41 contribution.calculate(e_g, n_sg, v_sg)
42 else:
43 cmix = self.mix
44 if self.mix_vt_sg is None:
45 self.mix_vt_sg = np.zeros_like(v_sg)
46 self.old_vt_sg = np.zeros_like(v_sg)
47 cmix = 1.0
48 self.mix_vt_sg[:] = 0.0
49 for contribution in self.contributions:
50 contribution.calculate(e_g, n_sg, self.mix_vt_sg)
51 self.mix_vt_sg = (cmix * self.mix_vt_sg
52 + (1.0 - cmix) * self.old_vt_sg)
53 v_sg += self.mix_vt_sg
54 self.old_vt_sg[:] = self.mix_vt_sg
56 def calculate_paw_correction(self, setup, D_sp, dEdD_sp, a=None,
57 addcoredensity=True):
58 return self.calculate_energy_and_derivatives(setup, D_sp, dEdD_sp, a,
59 addcoredensity)
61 def calculate_energy_and_derivatives(self, setup, D_sp, H_sp, a,
62 addcoredensity=True):
63 if setup.xc_correction is None:
64 return 0.0
65 Exc = 0.0
66 # We are supposed to add to H_sp, not write directly
67 H0_sp = H_sp
68 H_sp = H0_sp.copy()
69 H_sp[:] = 0.0
71 if self.mix is None:
72 for contribution in self.contributions:
73 Exc += contribution.calculate_energy_and_derivatives(
74 setup, D_sp, H_sp, a, addcoredensity)
75 else:
76 cmix = self.mix
77 if a not in self.old_H_asp:
78 self.old_H_asp[a] = H_sp.copy()
79 cmix = 1.0
81 for contribution in self.contributions:
82 Exc += contribution.calculate_energy_and_derivatives(
83 setup, D_sp, H_sp, a, addcoredensity)
84 H_sp *= cmix
85 H_sp += (1 - cmix) * self.old_H_asp[a]
86 self.old_H_asp[a][:] = H_sp.copy()
88 H0_sp += H_sp
89 Exc -= setup.xc_correction.e_xc0
90 return Exc
92 def get_xc_potential_and_energy_1d(self, v_g):
93 Exc = 0.0
94 for contribution in self.contributions:
95 Exc += contribution.add_xc_potential_and_energy_1d(v_g)
96 return Exc
98 def get_smooth_xc_potential_and_energy_1d(self, vt_g):
99 Exc = 0.0
100 for contribution in self.contributions:
101 Exc += contribution.add_smooth_xc_potential_and_energy_1d(vt_g)
102 return Exc
104 def initialize_from_atomic_orbitals(self, basis_functions):
105 for contribution in self.contributions:
106 contribution.initialize_from_atomic_orbitals(basis_functions)
108 def get_extra_setup_data(self, extra_data):
109 for contribution in self.contributions:
110 contribution.get_extra_setup_data(extra_data)
112 def add_contribution(self, contribution):
113 self.contributions.append(contribution)
114 self.xcs[contribution.get_name()] = contribution
115 if contribution.get_name() == 'RESPONSE':
116 assert self.response is None
117 self.response = contribution
119 def get_description(self):
120 fmt = '| %-6s | %-10s | %-45s |'
121 header = fmt % ('Weight', 'Module', 'Description')
122 dashes = '-' * len(header)
123 s = ['{} functional being used consists of'.format(self.name)]
124 s += [dashes]
125 s += [header]
126 s += [dashes]
127 for c in self.contributions:
128 s += [fmt % ('%6.3f' % c.weight, c.get_name(), c.get_desc())]
129 s += [dashes]
130 return '\n'.join(s)
132 def summary(self, log):
133 if self.response is not None:
134 eref_s = self.response.eref_s
135 source_s = self.response.eref_source_s
136 if eref_s is not None and source_s is not None:
137 text_s = [f'{eref * Ha:.5f} eV (from {source})'
138 for eref, source in zip(eref_s, source_s)]
139 log('{} response reference energy: {}\n'
140 .format(self.name, ', '.join(text_s)))
142 def read(self, reader):
143 for contribution in self.contributions:
144 contribution.read(reader)
146 def write(self, writer):
147 for contribution in self.contributions:
148 contribution.write(writer)
150 def heeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeelp(self, olddens):
151 # XXX This function should be removed once the deprecated
152 # `fixdensity=True` option is removed.
153 for contribution in self.contributions:
154 try:
155 contribution.heeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeelp(olddens)
156 except AttributeError:
157 pass