Coverage for gpaw/atom/generator.py: 78%
720 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
1# Copyright (C) 2003 CAMP
2# Please see the accompanying LICENSE file for further information.
3from functools import cached_property
4from math import pi, sqrt
6import numpy as np
7from numpy.linalg import solve, inv
8from scipy.linalg import eigh
9from ase.units import Ha
11from gpaw.setup_data import SetupData
12from gpaw.atom.configurations import configurations
13from gpaw import __version__ as version
14from gpaw.atom.all_electron import AllElectron, shoot
15from gpaw.utilities import hartree
16from gpaw.xc.hybrid import constructX, atomic_exact_exchange
17from gpaw.atom.filter import Filter
20def enumerate_j_ln(anything_ln: list[list[object]]) -> list[list[int]]:
21 """Return j_ln to map between _j and _ln index."""
22 return [[0 for _ in anything_n] for anything_n in anything_ln]
25class Generator(AllElectron):
26 def __init__(self, symbol, xcname='LDA', scalarrel=True, corehole=None,
27 configuration=None,
28 nofiles=True, txt='-', gpernode=150,
29 orbital_free=False, tw_coeff=1.):
30 AllElectron.__init__(self, symbol, xcname, scalarrel, corehole,
31 configuration, nofiles, txt, gpernode,
32 orbital_free, tw_coeff)
34 def run(self, core='', rcut=1.0, extra=None,
35 logderiv=False, vbar=None, exx=False, name=None,
36 normconserving='', filter=(0.4, 1.75), rcutcomp=None,
37 write_xml=True,
38 empty_states='', yukawa_gamma=0.0):
40 self.name = name
42 self.core = core
43 if isinstance(rcut, float):
44 rcut_l = [rcut]
45 else:
46 rcut_l = rcut
47 rcutmax = max(rcut_l)
48 rcutmin = min(rcut_l)
49 self.rcut_l = rcut_l
51 if rcutcomp is None:
52 rcutcomp = rcutmin
53 self.rcutcomp = rcutcomp
55 hfilter, xfilter = filter
57 Z = self.Z
59 n_j = self.n_j
60 l_j = self.l_j
61 f_j = self.f_j
62 e_j = self.e_j
64 if vbar is None:
65 vbar = ('poly', rcutmin * 0.9)
66 vbar_type, rcutvbar = vbar
68 normconserving_l = [x in normconserving for x in 'spdf']
70 # Parse core string:
71 j = 0
72 if core.startswith('['):
73 a, core = core.split(']')
74 core_symbol = a[1:]
75 j = len(configurations[core_symbol][1])
77 while core != '':
78 assert n_j[j] == int(core[0])
79 assert l_j[j] == 'spdf'.find(core[1])
80 if j != self.jcorehole:
81 assert f_j[j] == 2 * (2 * l_j[j] + 1)
82 j += 1
83 core = core[2:]
85 njcore = j
86 self.njcore = njcore
88 while empty_states != '':
89 n = int(empty_states[0])
90 l = 'spdf'.find(empty_states[1])
91 assert n == 1 + l + l_j.count(l)
92 n_j.append(n)
93 l_j.append(l)
94 f_j.append(0.0)
95 e_j.append(-0.01)
96 empty_states = empty_states[2:]
98 if 2 in l_j[njcore:]:
99 # We have a bound valence d-state. Add bound s- and
100 # p-states if not already there:
101 for l in [0, 1]:
102 if l not in l_j[njcore:]:
103 n_j.append(1 + l + l_j.count(l))
104 l_j.append(l)
105 f_j.append(0.0)
106 e_j.append(-0.01)
108 if l_j[njcore:] == [0] and Z > 2:
109 # We have only a bound valence s-state and we are not
110 # hydrogen and not helium. Add bound p-state:
111 n_j.append(n_j[njcore])
112 l_j.append(1)
113 f_j.append(0.0)
114 e_j.append(-0.01)
116 nj = len(n_j)
118 self.Nv = sum(f_j[njcore:])
119 self.Nc = sum(f_j[:njcore])
121 lmaxocc = max(l_j[njcore:])
122 lmax = max(l_j[njcore:])
124 # Parameters for orbital_free
125 if self.orbital_free:
126 self.n_j = [1]
127 self.l_j = [0]
128 self.f_j = [self.Z]
129 self.e_j = [self.e_j[0]]
131 n_j = self.n_j
132 l_j = self.l_j
133 f_j = self.f_j
134 e_j = self.e_j
135 nj = len(n_j)
136 lmax = 0
137 lmaxocc = 0
139 # Do all-electron calculation:
140 AllElectron.run(self)
142 # Highest occupied atomic orbital:
143 self.emax = max(e_j)
145 N = self.N
146 r = self.r
147 dr = self.dr
148 d2gdr2 = self.d2gdr2
149 beta = self.beta
151 dv = r**2 * dr
153 t = self.text
154 t()
155 t('Generating PAW setup')
156 if core != '':
157 t('Frozen core:', core)
159 # So far - no ghost-states:
160 self.ghost = False
162 # Calculate the kinetic energy of the core states:
163 Ekincore = 0.0
164 j = 0
165 for f, e, u in zip(f_j[:njcore], e_j[:njcore], self.u_j[:njcore]):
166 u = np.where(abs(u) < 1e-160, 0, u) # XXX Numeric!
167 k = e - np.sum((u**2 * self.vr * dr)[1:] / r[1:])
168 Ekincore += f * k
169 if j == self.jcorehole:
170 self.Ekincorehole = k
171 j += 1
173 # Calculate core density:
174 if njcore == 0:
175 nc = np.zeros(N)
176 else:
177 uc_j = self.u_j[:njcore]
178 uc_j = np.where(abs(uc_j) < 1e-160, 0, uc_j) # XXX Numeric!
179 nc = np.dot(f_j[:njcore], uc_j**2) / (4 * pi)
180 nc[1:] /= r[1:]**2
181 nc[0] = nc[1]
183 self.nc = nc
185 # Calculate core kinetic energy density
186 if njcore == 0:
187 tauc = np.zeros(N)
188 else:
189 tauc = self.radial_kinetic_energy_density(f_j[:njcore],
190 l_j[:njcore],
191 self.u_j[:njcore])
192 t('Kinetic energy of the core from tauc =',
193 np.dot(tauc * r * r, dr) * 4 * pi)
195 # Order valence states with respect to angular momentum
196 # quantum number:
197 self.n_ln = n_ln = []
198 self.f_ln = f_ln = []
199 self.e_ln = e_ln = []
200 for l in range(lmax + 1):
201 n_n = []
202 f_n = []
203 e_n = []
204 for j in range(njcore, nj):
205 if l_j[j] == l:
206 n_n.append(n_j[j])
207 f_n.append(f_j[j])
208 e_n.append(e_j[j])
209 n_ln.append(n_n)
210 f_ln.append(f_n)
211 e_ln.append(e_n)
213 # Add extra projectors:
214 if extra is not None:
215 if len(extra) == 0:
216 lmaxextra = 0
217 else:
218 lmaxextra = max(extra.keys())
219 if lmaxextra > lmax:
220 for l in range(lmax, lmaxextra):
221 n_ln.append([])
222 f_ln.append([])
223 e_ln.append([])
224 lmax = lmaxextra
225 for l in extra:
226 nn = -1
227 for e in extra[l]:
228 n_ln[l].append(nn)
229 f_ln[l].append(0.0)
230 e_ln[l].append(e)
231 nn -= 1
232 else:
233 # Automatic:
235 # Make sure we have two projectors for each occupied channel:
236 for l in range(lmaxocc + 1):
237 if len(n_ln[l]) < 2 and not normconserving_l[l]:
238 # Only one - add one more:
239 assert len(n_ln[l]) == 1
240 n_ln[l].append(-1)
241 f_ln[l].append(0.0)
242 e_ln[l].append(1.0 + e_ln[l][0])
244 if lmaxocc < 2 and lmaxocc == lmax:
245 # Add extra projector for l = lmax + 1:
246 n_ln.append([-1])
247 f_ln.append([0.0])
248 e_ln.append([0.0])
249 lmax += 1
251 self.lmax = lmax
253 rcut_l.extend([rcutmin] * (lmax + 1 - len(rcut_l)))
255 t('Cutoffs:')
256 for rc, s in zip(rcut_l, 'spdf'):
257 t(f'rc({s})={rc:.3f}')
258 t('rc(vbar)=%.3f' % rcutvbar)
259 t('rc(comp)=%.3f' % rcutcomp)
260 t('rc(nct)=%.3f' % rcutmax)
261 t()
262 t('Kinetic energy of the core states: %.6f' % Ekincore)
264 # Allocate arrays:
265 self.u_ln = u_ln = [] # phi * r
266 self.s_ln = s_ln = [] # phi-tilde * r
267 self.q_ln = q_ln = [] # p-tilde * r
268 for l in range(lmax + 1):
269 nn = len(n_ln[l])
270 u_ln.append(np.zeros((nn, N)))
271 s_ln.append(np.zeros((nn, N)))
272 q_ln.append(np.zeros((nn, N)))
274 # Fill in all-electron wave functions:
275 for l in range(lmax + 1):
276 # Collect all-electron wave functions:
277 u_n = [self.u_j[j] for j in range(njcore, nj) if l_j[j] == l]
278 for n, u in enumerate(u_n):
279 u_ln[l][n] = u
281 # Grid-index corresponding to rcut:
282 gcut_l = [1 + int(rc * N / (rc + beta)) for rc in rcut_l]
284 rcutfilter = xfilter * rcutmax
285 self.rcutfilter = rcutfilter
286 gcutfilter = 1 + int(rcutfilter * N / (rcutfilter + beta))
287 gcutmax = 1 + int(rcutmax * N / (rcutmax + beta))
289 # Outward integration of unbound states stops at 3 * rcut:
290 gmax = int(3 * rcutmax * N / (3 * rcutmax + beta))
291 assert gmax > gcutfilter
293 # Calculate unbound extra states:
294 c2 = -(r / dr)**2
295 c10 = -d2gdr2 * r**2
296 for l, (n_n, e_n, u_n) in enumerate(zip(n_ln, e_ln, u_ln)):
297 for n, e, u in zip(n_n, e_n, u_n):
298 if n < 0:
299 u[:] = 0.0
300 shoot(u, l, self.vr, e, self.r2dvdr, r, dr, c10, c2,
301 self.scalarrel, gmax=gmax)
302 u *= 1.0 / u[gcut_l[l]]
304 charge = Z - self.Nv - self.Nc
305 t('Charge: %.1f' % charge)
306 t('Core electrons: %.1f' % self.Nc)
307 t('Valence electrons: %.1f' % self.Nv)
309 # Construct smooth wave functions:
310 coefs = []
311 for l, (u_n, s_n) in enumerate(zip(u_ln, s_ln)):
312 nodeless = True
313 gc = gcut_l[l]
314 for u, s in zip(u_n, s_n):
315 s[:] = u
316 if normconserving_l[l]:
317 A = np.zeros((5, 5))
318 A[:4, 0] = 1.0
319 A[:4, 1] = r[gc - 2:gc + 2]**2
320 A[:4, 2] = A[:4, 1]**2
321 A[:4, 3] = A[:4, 1] * A[:4, 2]
322 A[:4, 4] = A[:4, 2]**2
323 A[4, 4] = 1.0
324 a = u[gc - 2:gc + 3] / r[gc - 2:gc + 3]**(l + 1)
325 a = np.log(a)
327 def f(x):
328 a[4] = x
329 b = solve(A, a)
330 r1 = r[:gc]
331 r2 = r1**2
332 rl1 = r1**(l + 1)
333 y = b[0] + r2 * (b[1] + r2 * (b[2] + r2 *
334 (b[3] + r2 * b[4])))
335 y = np.exp(y)
336 s[:gc] = rl1 * y
337 return np.dot(s**2, dr) - 1
338 x1 = 0.0
339 x2 = 0.001
340 f1 = f(x1)
341 f2 = f(x2)
342 while abs(f1) > 1e-6:
343 x0 = (x1 / f1 - x2 / f2) / (1 / f1 - 1 / f2)
344 f0 = f(x0)
345 if abs(f1) < abs(f2):
346 x2, f2 = x1, f1
347 x1, f1 = x0, f0
349 else:
350 A = np.ones((4, 4))
351 A[:, 0] = 1.0
352 A[:, 1] = r[gc - 2:gc + 2]**2
353 A[:, 2] = A[:, 1]**2
354 A[:, 3] = A[:, 1] * A[:, 2]
355 a = u[gc - 2:gc + 2] / r[gc - 2:gc + 2]**(l + 1)
356 if 0: # l < 2 and nodeless:
357 a = np.log(a)
358 a = solve(A, a)
359 r1 = r[:gc]
360 r2 = r1**2
361 rl1 = r1**(l + 1)
362 y = a[0] + r2 * (a[1] + r2 * (a[2] + r2 * (a[3])))
363 if 0: # l < 2 and nodeless:
364 y = np.exp(y)
365 s[:gc] = rl1 * y
367 coefs.append(a)
368 if nodeless:
369 if not (s[1:gc] > 0.0).all():
370 raise RuntimeError(
371 'Error: The %d%s pseudo wave has a node!' %
372 (n_ln[l][0], 'spdf'[l]))
373 # Only the first state for each l must be nodeless:
374 nodeless = False
376 # Calculate pseudo core density:
377 gcutnc = 1 + int(rcutmax * N / (rcutmax + beta))
378 self.nct = nct = nc.copy()
379 A = np.ones((4, 4))
380 A[0] = 1.0
381 A[1] = r[gcutnc - 2:gcutnc + 2]**2
382 A[2] = A[1]**2
383 A[3] = A[1] * A[2]
384 a = nc[gcutnc - 2:gcutnc + 2]
385 a = solve(np.transpose(A), a)
386 r2 = r[:gcutnc]**2
387 nct[:gcutnc] = a[0] + r2 * (a[1] + r2 * (a[2] + r2 * a[3]))
388 t('Pseudo-core charge: %.6f' % (4 * pi * np.dot(nct, dv)))
390 # ... and the pseudo core kinetic energy density:
391 tauct = tauc.copy()
392 a = tauc[gcutnc - 2:gcutnc + 2]
393 a = solve(np.transpose(A), a)
394 tauct[:gcutnc] = a[0] + r2 * (a[1] + r2 * (a[2] + r2 * a[3]))
396 # ... and the soft valence density:
397 nt = np.zeros(N)
398 for f_n, s_n in zip(f_ln, s_ln):
399 nt += np.dot(f_n, s_n**2) / (4 * pi)
400 nt[1:] /= r[1:]**2
401 nt[0] = nt[1]
402 nt += nct
403 self.nt = nt
405 # Calculate the shape function:
406 x = r / rcutcomp
407 gaussian = np.zeros(N)
408 self.gamma = gamma = 10.0
409 gaussian[:gmax] = np.exp(-gamma * x[:gmax]**2)
410 gt = 4 * (gamma / rcutcomp**2)**1.5 / sqrt(pi) * gaussian
411 t('Shape function alpha=%.3f' % (gamma / rcutcomp**2))
412 norm = np.dot(gt, dv)
413 # print norm, norm-1
414 assert abs(norm - 1) < 1e-2
415 gt /= norm
417 # Calculate smooth charge density:
418 Nt = np.dot(nt, dv)
419 rhot = nt - (Nt + charge / (4 * pi)) * gt
420 t('Pseudo-electron charge', 4 * pi * Nt)
422 vHt = np.zeros(N)
423 hartree(0, rhot * r * dr, r, vHt)
424 vHt[1:] /= r[1:]
425 vHt[0] = vHt[1]
427 vXCt = np.zeros(N)
429 extra_xc_data = {}
431 if self.xc.type != 'GLLB':
432 self.xc.calculate_spherical(self.rgd,
433 nt.reshape((1, -1)),
434 vXCt.reshape((1, -1)))
435 else:
436 self.xc.get_smooth_xc_potential_and_energy_1d(vXCt)
438 # Calculate extra-stuff for non-local functionals
439 self.xc.get_extra_setup_data(extra_xc_data)
441 vt = vHt + vXCt
443 if self.orbital_free:
444 vt /= self.tw_coeff
446 # Construct zero potential:
447 gc = 1 + int(rcutvbar * N / (rcutvbar + beta))
448 if vbar_type == 'f':
449 assert lmax == 2
450 uf = np.zeros(N)
451 l = 3
453 # Solve for all-electron f-state:
454 eps = 0.0
455 shoot(uf, l, self.vr, eps, self.r2dvdr, r, dr, c10, c2,
456 self.scalarrel, gmax=gmax)
457 uf *= 1.0 / uf[gc]
459 # Fit smooth pseudo f-state polynomium:
460 A = np.ones((4, 4))
461 A[:, 0] = 1.0
462 A[:, 1] = r[gc - 2:gc + 2]**2
463 A[:, 2] = A[:, 1]**2
464 A[:, 3] = A[:, 1] * A[:, 2]
465 a = uf[gc - 2:gc + 2] / r[gc - 2:gc + 2]**(l + 1)
466 a0, a1, a2, a3 = solve(A, a)
467 r1 = r[:gc]
468 r2 = r1**2
469 rl1 = r1**(l + 1)
470 y = a0 + r2 * (a1 + r2 * (a2 + r2 * a3))
471 sf = uf.copy()
472 sf[:gc] = rl1 * y
474 # From 0 to gc, use analytic formula for kinetic energy operator:
475 r4 = r2**2
476 r6 = r4 * r2
477 enumerator = (a0 * l * (l + 1) +
478 a1 * (l + 2) * (l + 3) * r2 +
479 a2 * (l + 4) * (l + 5) * r4 +
480 a3 * (l + 6) * (l + 7) * r6)
481 denominator = a0 + a1 * r2 + a2 * r4 + a3 * r6
482 ekin_over_phit = - 0.5 * (enumerator / denominator - l * (l + 1))
483 ekin_over_phit[1:] /= r2[1:]
485 vbar = eps - vt
486 vbar[:gc] -= ekin_over_phit
487 vbar[0] = vbar[1] # Actually we can collect the terms into
488 # a single fraction without poles, so as to avoid doing this,
489 # but this is good enough
491 # From gc to gmax, use finite-difference formula for kinetic
492 # energy operator:
493 vbar[gc:gmax] -= self.kin(l, sf)[gc:gmax] / sf[gc:gmax]
494 vbar[gmax:] = 0.0
495 else:
496 assert vbar_type == 'poly'
497 A = np.ones((2, 2))
498 A[0] = 1.0
499 A[1] = r[gc - 1:gc + 1]**2
500 a = vt[gc - 1:gc + 1]
501 a = solve(np.transpose(A), a)
502 r2 = r**2
503 vbar = a[0] + r2 * a[1] - vt
504 vbar[gc:] = 0.0
506 vt += vbar
508 # Construct projector functions:
509 for l, (e_n, s_n, q_n) in enumerate(zip(e_ln, s_ln, q_ln)):
510 for e, s, q in zip(e_n, s_n, q_n):
511 q[:] = self.kin(l, s) + (vt - e) * s
512 q[gcutmax:] = 0.0
514 filter = Filter(r, dr, gcutfilter, hfilter).filter
515 if self.orbital_free:
516 vbar *= self.tw_coeff
517 vbar = filter(vbar * r)
519 # Calculate matrix elements:
520 self.dK_lnn = dK_lnn = []
521 self.dH_lnn = dH_lnn = []
522 self.dO_lnn = dO_lnn = []
524 for l, (e_n, u_n, s_n, q_n) in enumerate(zip(e_ln, u_ln,
525 s_ln, q_ln)):
526 A_nn = np.inner(s_n, q_n * dr)
527 # Do a LU decomposition of A:
528 nn = len(e_n)
529 L_nn = np.identity(nn, float)
530 U_nn = A_nn.copy()
532 # Keep all bound states normalized
533 if sum([n > 0 for n in n_ln[l]]) <= 1:
534 for i in range(nn):
535 for j in range(i + 1, nn):
536 L_nn[j, i] = 1.0 * U_nn[j, i] / U_nn[i, i]
537 U_nn[j, :] -= U_nn[i, :] * L_nn[j, i]
539 dO_nn = (np.inner(u_n, u_n * dr) -
540 np.inner(s_n, s_n * dr))
542 e_nn = np.zeros((nn, nn))
543 e_nn.ravel()[::nn + 1] = e_n
544 dH_nn = np.dot(dO_nn, e_nn) - A_nn
546 q_n[:] = np.dot(inv(np.transpose(U_nn)), q_n)
547 s_n[:] = np.dot(inv(L_nn), s_n)
548 u_n[:] = np.dot(inv(L_nn), u_n)
550 dO_nn = np.dot(np.dot(inv(L_nn), dO_nn),
551 inv(np.transpose(L_nn)))
552 dH_nn = np.dot(np.dot(inv(L_nn), dH_nn),
553 inv(np.transpose(L_nn)))
555 ku_n = [self.kin(l, u, e) for u, e in zip(u_n, e_n)]
556 ks_n = [self.kin(l, s) for s in s_n]
557 dK_nn = 0.5 * (np.inner(u_n, ku_n * dr) -
558 np.inner(s_n, ks_n * dr))
559 dK_nn += np.transpose(dK_nn).copy()
561 dK_lnn.append(dK_nn)
562 dO_lnn.append(dO_nn)
563 dH_lnn.append(dH_nn)
565 for n, q in enumerate(q_n):
566 q[:] = filter(q, l) * r**(l + 1)
568 A_nn = np.inner(s_n, q_n * dr)
569 q_n[:] = np.dot(inv(np.transpose(A_nn)), q_n)
571 self.vt = vt
572 self.vbar = vbar
574 t('state eigenvalue norm')
575 t('--------------------------------')
576 for l, (n_n, f_n, e_n) in enumerate(zip(n_ln, f_ln, e_ln)):
577 for n in range(len(e_n)):
578 if n_n[n] > 0:
579 f = '(%d)' % f_n[n]
580 t('%d%s%-4s: %12.6f %12.6f' % (
581 n_n[n], 'spdf'[l], f, e_n[n],
582 np.dot(s_ln[l][n]**2, dr)))
583 else:
584 t('*{} : {:12.6f}'.format('spdf'[l], e_n[n]))
585 t('--------------------------------')
587 self.logd = {}
588 if logderiv:
589 ni = 300
590 self.elog = np.linspace(-5.0, 1.0, ni)
591 delta = self.elog[1] - self.elog[0]
592 # Calculate logarithmic derivatives:
593 gld = gcutmax + 10
594 self.rlog = r[gld]
595 assert gld < gmax
596 t('Calculating logarithmic derivatives at r=%.3f' % r[gld])
597 t('(skip with [Ctrl-C])')
599 try:
600 u = np.zeros(N)
601 for l in range(4):
602 self.logd[l] = (np.empty(ni), np.empty(ni))
603 if l <= lmax:
604 dO_nn = dO_lnn[l]
605 dH_nn = dH_lnn[l]
606 q_n = q_ln[l]
608 ae = self.symbol + '.ae.ld.' + 'spdf'[l]
609 ps = self.symbol + '.ps.ld.' + 'spdf'[l]
610 with open(ae, 'w') as fae, open(ps, 'w') as fps:
611 ld_ae = 1000
612 ld_ps = 1000
613 nodes_ae = []
614 nodes_ps = []
615 for i, e in enumerate(self.elog):
616 # All-electron logarithmic derivative:
617 u[:] = 0.0
618 shoot(u, l, self.vr, e, self.r2dvdr, r, dr,
619 c10, c2, self.scalarrel, gmax=gld)
620 dudr = 0.5 * (u[gld + 1] - u[gld - 1]) / dr[gld]
621 ld = dudr / u[gld] - 1.0 / r[gld]
622 if ld > ld_ae:
623 nodes_ae.append(e)
624 ld_ae = ld
625 print(e, ld, file=fae)
626 self.logd[l][0][i] = ld
628 # PAW logarithmic derivative:
629 s = self.integrate(l, vt, e, gld)
630 if l <= lmax:
631 A_nn = dH_nn - e * dO_nn
632 s_n = [self.integrate(l, vt, e, gld, q)
633 for q in q_n]
634 B_nn = np.inner(q_n, s_n * dr)
635 a_n = np.dot(q_n, s * dr)
637 B_nn = np.dot(A_nn, B_nn)
638 B_nn.ravel()[::len(a_n) + 1] += 1.0
639 c_n = solve(B_nn, np.dot(A_nn, a_n))
640 s -= np.dot(c_n, s_n)
642 dsdr = 0.5 * (s[gld + 1] - s[gld - 1]) / dr[gld]
643 ld = dsdr / s[gld] - 1.0 / r[gld]
644 if ld > ld_ps:
645 nodes_ps.append(e)
646 ld_ps = ld
647 print(e, ld, file=fps)
648 self.logd[l][1][i] = ld
650 for e1, e2 in zip(nodes_ae[::-1], nodes_ps[::-1]):
651 if abs(e1 - e2) > 1.5 * delta:
652 print(f'{self.symbol}(l={l})-GHOST?',
653 f'AE={e1 * Ha:.3f} eV,',
654 f'PS={e2 * Ha:.3f} eV')
655 except KeyboardInterrupt:
656 pass
658 self.write(nc, 'nc')
659 self.write(nt, 'nt')
660 self.write(nct, 'nct')
661 self.write(vbar, 'vbar')
662 self.write(vt, 'vt')
663 self.write(tauc, 'tauc')
664 self.write(tauct, 'tauct')
666 for l, (n_n, f_n, u_n, s_n, q_n) in enumerate(zip(n_ln, f_ln,
667 u_ln, s_ln, q_ln)):
668 for n, f, u, s, q in zip(n_n, f_n, u_n, s_n, q_n):
669 if n < 0:
670 self.write(u, 'ae', n=n, l=l)
671 self.write(s, 'ps', n=n, l=l)
672 self.write(q, 'proj', n=n, l=l)
674 # Test for ghost states:
675 for h in [0.05]:
676 self.diagonalize(h)
678 self.vn_j = vn_j = []
679 self.vl_j = vl_j = []
680 self.vf_j = vf_j = []
681 self.ve_j = ve_j = []
682 self.vu_j = vu_j = []
683 self.vs_j = vs_j = []
684 self.vq_j = vq_j = []
685 gllb = 'w_ln' in extra_xc_data
686 self.vw_j = vw_j = []
688 j_ln = enumerate_j_ln(f_ln)
689 j = 0
690 for l, n_n in enumerate(n_ln):
691 for n, nn in enumerate(n_n):
692 if nn > 0:
693 vf_j.append(f_ln[l][n])
694 vn_j.append(nn)
695 vl_j.append(l)
696 ve_j.append(e_ln[l][n])
697 vu_j.append(u_ln[l][n])
698 vs_j.append(s_ln[l][n])
699 vq_j.append(q_ln[l][n])
700 if gllb:
701 vw_j.append(extra_xc_data['w_ln'][l][n])
702 j_ln[l][n] = j
703 j += 1
704 for l, n_n in enumerate(n_ln):
705 for n, nn in enumerate(n_n):
706 if nn < 0:
707 vf_j.append(0)
708 vn_j.append(nn)
709 vl_j.append(l)
710 ve_j.append(e_ln[l][n])
711 vu_j.append(u_ln[l][n])
712 vs_j.append(s_ln[l][n])
713 vq_j.append(q_ln[l][n])
714 if gllb:
715 vw_j.append(extra_xc_data['w_ln'][l][n])
716 j_ln[l][n] = j
717 j += 1
718 nj = j
719 if gllb:
720 extra_xc_data['w_j'] = vw_j
721 del extra_xc_data['w_ln']
722 self.dK_jj = np.zeros((nj, nj))
723 for l, j_n in enumerate(j_ln):
724 for n1, j1 in enumerate(j_n):
725 for n2, j2 in enumerate(j_n):
726 self.dK_jj[j1, j2] = self.dK_lnn[l][n1, n2]
727 if self.orbital_free:
728 self.dK_jj[j1, j2] *= self.tw_coeff
730 X_gamma = yukawa_gamma
731 if exx:
732 X_p = constructX(self)
733 if yukawa_gamma is not None and yukawa_gamma > 0:
734 X_pg = constructX(self, yukawa_gamma)
735 else:
736 X_pg = None
737 ExxC = atomic_exact_exchange(self, 'core-core')
738 else:
739 X_p = None
740 X_pg = None
741 ExxC = None
743 sqrt4pi = sqrt(4 * pi)
744 setup = SetupData(self.symbol, self.xc.name, self.name,
745 readxml=False,
746 generator_version=1)
748 def divide_by_r(x_g, l):
749 r = self.r
750 # for x_g, l in zip(x_jg, l_j):
751 p = x_g.copy()
752 p[1:] /= self.r[1:]
753 # XXXXX go to higher order!!!!!
754 if l == 0: # l_j[self.jcorehole] == 0:
755 p[0] = (p[2] +
756 (p[1] - p[2]) * (r[0] - r[2]) / (r[1] - r[2]))
757 return p
759 def divide_all_by_r(x_jg):
760 return [divide_by_r(x_g, l) for x_g, l in zip(x_jg, vl_j)]
762 setup.l_j = vl_j
763 setup.l_orb_J = vl_j
764 setup.n_j = vn_j
765 setup.f_j = vf_j
766 setup.eps_j = ve_j
767 setup.rcut_j = [rcut_l[l] for l in vl_j]
769 setup.nc_g = nc * sqrt4pi
770 setup.nct_g = nct * sqrt4pi
771 setup.nvt_g = (nt - nct) * sqrt4pi
772 setup.e_kinetic_core = Ekincore
773 setup.vbar_g = vbar * sqrt4pi
774 setup.tauc_g = tauc * sqrt4pi
775 setup.tauct_g = tauct * sqrt4pi
776 setup.extra_xc_data = extra_xc_data
777 setup.Z = Z
778 setup.Nc = self.Nc
779 setup.Nv = self.Nv
780 setup.e_kinetic = self.Ekin
781 setup.e_xc = self.Exc
782 setup.e_electrostatic = self.e_coulomb
783 setup.e_total = self.e_coulomb + self.Exc + self.Ekin
785 setup.rgd = self.rgd
787 setup.shape_function = {'type': 'gauss',
788 'rc': self.rcutcomp / sqrt(self.gamma)}
789 setup.e_kin_jj = self.dK_jj
790 setup.ExxC = ExxC
791 setup.phi_jg = divide_all_by_r(vu_j)
792 setup.phit_jg = divide_all_by_r(vs_j)
793 setup.pt_jg = divide_all_by_r(vq_j)
794 setup.X_p = X_p
795 setup.X_pg = X_pg
796 setup.X_gamma = X_gamma
798 if self.jcorehole is not None:
799 setup.has_corehole = True
800 setup.lcorehole = l_j[self.jcorehole] # l_j or vl_j ????? XXX
801 setup.ncorehole = n_j[self.jcorehole]
802 setup.phicorehole_g = divide_by_r(self.u_j[self.jcorehole],
803 setup.lcorehole)
804 setup.core_hole_e = self.e_j[self.jcorehole]
805 setup.core_hole_e_kin = self.Ekincorehole
806 setup.fcorehole = self.fcorehole
808 if self.ghost and not self.orbital_free:
809 # In orbital_free we are not interested in ghosts
810 raise RuntimeError('Ghost!')
812 if self.scalarrel:
813 reltype = 'scalar-relativistic'
814 else:
815 reltype = 'non-relativistic'
817 attrs = [('type', reltype), ('name', 'gpaw-%s' % version)]
818 data = 'Frozen core: ' + (self.core or 'none')
820 setup.type = reltype
821 setup.generatorattrs = attrs
822 setup.generatordata = data
823 setup.orbital_free = self.orbital_free
824 setup.version = '0.8'
826 self.id_j = []
827 for l, n in zip(vl_j, vn_j):
828 if n > 0:
829 id = '%s-%d%s' % (self.symbol, n, 'spdf'[l])
830 else:
831 id = '%s-%s%d' % (self.symbol, 'spdf'[l], -n)
832 self.id_j.append(id)
833 setup.id_j = self.id_j
835 if write_xml:
836 setup.write_xml()
837 self._return_setup = setup
838 return setup
840 @cached_property
841 def valence_data(self):
842 from gpaw.atom.all_electron import ValenceData
843 setup = self._return_setup
844 assert abs(self.rgd.beta - self.beta) < 1e-13
846 def multiply_r(array_jg):
847 return array_jg * self.rgd.r_g[None, :]
849 assert self.xcname == setup.setupname
851 valdata = ValenceData.from_setupdata_and_potentials(
852 setup, vr_g=self.vr,
853 r2dvdr_g=self.r2dvdr,
854 scalarrel=self.scalarrel)
856 # We can verify to what extent we can reproduce the potential:
857 # vr1_g, r2dvdr1_g, scalarrel1 = ValenceData.calculate_potential_data(
858 # setup)
860 # np.testing.assert_allclose(
861 # vr1_g, valdata.vr_g,rtol=1e-4, atol=0.005)
862 # np.testing.assert_allclose(
863 # r2dvdr1_g, valdata.r2dvdr_g, rtol=1e-4, atol=0.005)
864 return valdata
866 def diagonalize(self, h):
867 ng = 350
868 t = self.text
869 t()
870 t('Diagonalizing with gridspacing h=%.3f' % h)
871 R = h * np.arange(1, ng + 1)
872 G = (self.N * R / (self.beta + R) + 0.5).astype(int)
873 G = np.clip(G, 1, self.N - 2)
874 R1 = np.take(self.r, G - 1)
875 R2 = np.take(self.r, G)
876 R3 = np.take(self.r, G + 1)
877 x1 = (R - R2) * (R - R3) / (R1 - R2) / (R1 - R3)
878 x2 = (R - R1) * (R - R3) / (R2 - R1) / (R2 - R3)
879 x3 = (R - R1) * (R - R2) / (R3 - R1) / (R3 - R2)
881 def interpolate(f):
882 f1 = np.take(f, G - 1)
883 f2 = np.take(f, G)
884 f3 = np.take(f, G + 1)
885 return f1 * x1 + f2 * x2 + f3 * x3
886 vt = interpolate(self.vt)
887 t()
888 t('state all-electron PAW')
889 t('-------------------------------')
890 for l in range(4):
891 if l <= self.lmax:
892 q_n = np.array([interpolate(q) for q in self.q_ln[l]])
893 H = np.dot(np.transpose(q_n),
894 np.dot(self.dH_lnn[l], q_n)) * h
895 S = np.dot(np.transpose(q_n),
896 np.dot(self.dO_lnn[l], q_n)) * h
897 else:
898 H = np.zeros((ng, ng))
899 S = np.zeros((ng, ng))
900 H.ravel()[::ng + 1] += vt + 1.0 / h**2 + l * (l + 1) / 2.0 / R**2
901 H.ravel()[1::ng + 1] -= 0.5 / h**2
902 H.ravel()[ng::ng + 1] -= 0.5 / h**2
903 S.ravel()[::ng + 1] += 1.0
904 e_n, _ = eigh(H, S)
905 ePAW = e_n[0]
906 if l <= self.lmax and self.n_ln[l][0] > 0:
907 eAE = self.e_ln[l][0]
908 t('%d%s: %12.6f %12.6f' % (self.n_ln[l][0],
909 'spdf'[l], eAE, ePAW), end='')
910 if abs(eAE - ePAW) > 0.014:
911 t(' GHOST-STATE!')
912 self.ghost = True
913 else:
914 t()
915 else:
916 t('*{}: {:12.6f}'.format('spdf'[l], ePAW),
917 end='')
918 if ePAW < self.emax:
919 t(' GHOST-STATE!')
920 self.ghost = True
921 else:
922 t()
923 t('-------------------------------')
925 def integrate(self, l, vt, e, gld, q=None):
926 r = self.r[1:]
927 dr = self.dr[1:]
928 s = np.zeros(self.N)
930 c0 = 0.5 * l * (l + 1) / r**2
931 c1 = -0.5 * self.d2gdr2[1:]
932 c2 = -0.5 * dr**-2
934 fp = c2 + 0.5 * c1
935 fm = c2 - 0.5 * c1
936 f0 = c0 - 2 * c2
938 f0 += vt[1:] - e
939 if q is None:
940 s[1] = r[1]**(l + 1)
941 for g in range(gld):
942 s[g + 2] = (-fm[g] * s[g] - f0[g] * s[g + 1]) / fp[g]
943 return s
945 s[1] = q[1] / (vt[0] - e)
946 for g in range(gld):
947 s[g + 2] = (q[g + 1] - fm[g] * s[g] - f0[g] * s[g + 1]) / fp[g]
948 return s
951def construct_smooth_wavefunction(u, l, gc, r, s):
952 # Do a linear regression to a wave function
953 # s = a + br^2 + cr^4 + dr^6, such that
954 # the fitting is as good as possible in region gc-2:gc+2
955 A = np.ones((4, 4))
956 A[:, 0] = 1.0
957 A[:, 1] = r[gc - 2:gc + 2]**2
958 A[:, 2] = A[:, 1]**2
959 A[:, 3] = A[:, 1] * A[:, 2]
960 a = u[gc - 2:gc + 2] / r[gc - 2:gc + 2]**(l + 1)
961 a = solve(A, a)
962 r1 = r[:gc]
963 r2 = r1**2
964 rl1 = r1**(l + 1)
965 y = a[0] + r2 * (a[1] + r2 * (a[2] + r2 * (a[3])))
966 s[:gc] = rl1 * y
969if __name__ == '__main__':
970 import os
971 from gpaw.atom.basis import BasisMaker
972 from gpaw.atom.configurations import parameters
974 for xcname in ['LDA', 'PBE', 'RPBE', 'revPBE', 'GLLBSC']:
975 for symbol, par in parameters.items():
976 filename = symbol + '.' + xcname
977 if os.path.isfile(filename) or os.path.isfile(filename + '.gz'):
978 continue
979 generator = Generator(symbol, xcname, scalarrel=True, nofiles=True)
980 setup = generator.run(exx=True, logderiv=False, **par)
982 if xcname == 'PBE':
983 bm = BasisMaker.from_setup_and_generator(
984 setup, generator, name='dzp', run=False)
985 basis = bm.generate()
986 basis.write_xml()