Coverage for eminus/xc/lda_xc_gdsmfb.py: 100.00%
264 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-18 08:43 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-18 08:43 +0000
1# SPDX-FileCopyrightText: 2021 The eminus developers
2# SPDX-License-Identifier: Apache-2.0
3"""GDSMFB LDA exchange-correlation.
5Reference: Phys. Rev. Lett. 119, 135001.
6"""
8import dataclasses
10import numpy as np
13def lda_xc_gdsmfb(n, T=0, **kwargs):
14 """GDSMFB exchange-correlation functional (spin-paired).
16 Similar to the functional with the label LDA_XC_GDSMFB and ID 577 in Libxc, but the
17 implementations differ: https://gitlab.com/libxc/libxc/-/issues/525
18 Exchange and correlation cannot be separated.
20 Reference: Phys. Rev. Lett. 119, 135001.
22 Args:
23 n: Real-space electronic density.
25 Keyword Args:
26 T: Temperature in Hartree.
27 **kwargs: Throwaway arguments.
29 Returns:
30 GDSMFB exchange-correlation energy density and potential.
31 """
32 kwargs["zeta"] = np.zeros_like(n)
33 exc, vxc, _ = lda_xc_gdsmfb_spin(n, T=T, **kwargs)
34 return exc, np.array([vxc[0]]), None
37def lda_xc_gdsmfb_spin(n, zeta, T=0, **kwargs):
38 """GDSMFB exchange-correlation functional (spin-polarized).
40 Similar to the functional with the label LDA_XC_GDSMFB and ID 577 in Libxc, but the
41 implementations differ: https://gitlab.com/libxc/libxc/-/issues/525
42 Exchange and correlation cannot be separated.
44 Reference: Phys. Rev. Lett. 119, 135001.
46 Args:
47 n: Real-space electronic density.
48 zeta: Relative spin polarization.
50 Keyword Args:
51 T: Temperature in Hartree.
52 **kwargs: Throwaway arguments.
54 Returns:
55 GDSMFB exchange-correlation energy density and potential.
56 """
57 # Calculate properties
58 n_up = (1 + zeta) * n / 2
59 rs = (3 / (4 * np.pi * n)) ** (1 / 3)
60 theta = _get_theta(T, n, zeta)
61 theta0 = _get_theta0(theta, zeta)
62 theta1 = _get_theta1(theta, zeta)
64 # Initialize parameters
65 # We need to calculate coefficients for specific theta
66 # Create a coefficients object for each theta with its corresponding parameters
67 phi_params = PhiParams()
68 zeta0theta = Zeta0Coeffs(theta)
69 zeta0theta0 = Zeta0Coeffs(theta0)
70 zeta1theta = Zeta1Coeffs(theta)
71 zeta1theta1 = Zeta1Coeffs(theta1)
73 # Calculate fxc
74 fxc0 = _get_fxc_zeta(rs, zeta0theta0)
75 fxc1 = _get_fxc_zeta(rs, zeta1theta1)
76 phi = _get_phi(rs, theta0, zeta, phi_params)
77 fxc = fxc0 + (fxc1 - fxc0) * phi
79 # Generic derivatives
80 drsdn = -(6 ** (1 / 3)) * (1 / n) ** (1 / 3) / (6 * np.pi ** (1 / 3) * n)
81 dzetadn_up = -zeta / n**2 + 1 / n
82 dzetadn_dw = -zeta / n**2 - 1 / n
84 # fxc derivatives
85 dfxc0drs = _get_dfxc_zetadrs(rs, zeta0theta)
86 dfxc1drs = _get_dfxc_zetadrs(rs, zeta1theta)
87 dfxc0dtheta0 = _get_dfxc_zetadtheta(rs, zeta0theta0)
88 dfxc1dtheta1 = _get_dfxc_zetadtheta(rs, zeta1theta1)
90 # phi derivatives
91 dphidrs = _get_dphidrs(rs, theta0, zeta, phi_params)
92 dphidtheta = _get_dphidtheta(rs, theta0, zeta, phi_params)
93 dphidzeta = _get_dphidzeta(rs, theta0, zeta, phi_params)
95 # theta derivatives
96 dthetadn_up = _get_dthetadn_up(T, n_up)
97 dtheta0dtheta = _get_dtheta0dtheta(zeta)
98 dtheta0dzeta = _get_dtheta0dzeta(theta0, zeta)
99 dtheta1dtheta0 = _get_dtheta1dtheta0()
101 # Calculate vxc_up (using dndn_up=1)
102 dfxc0a = dfxc0drs * drsdn # * dndn_up = 1
103 dfxc1a = dfxc1drs * drsdn # * dndn_up = 1
104 dfxc0b_up = dfxc0dtheta0 * (dtheta0dtheta * dthetadn_up + dtheta0dzeta * dzetadn_up)
105 dfxc1b_up = (
106 dfxc1dtheta1 * dtheta1dtheta0 * (dtheta0dtheta * dthetadn_up + dtheta0dzeta * dzetadn_up)
107 )
108 dphi_up = dphidtheta * dthetadn_up + dphidzeta * dzetadn_up + dphidrs * drsdn # * dndn_up = 1
109 vxc_up = (
110 dfxc0a
111 + dfxc0b_up
112 - phi * (dfxc0a + dfxc0b_up - dfxc1a - dfxc1b_up)
113 - (fxc0 - fxc1) * dphi_up
114 )
116 # Calculate vxc_dw (using dndn_dw=1 and dthetadn_dw=0)
117 # dfxc0a and dfxc1a are identical for vxc_up and vxc_dw
118 dfxc0b_dw = dfxc0dtheta0 * dtheta0dzeta * dzetadn_dw
119 # dfxc0b += dfxc0dtheta0 * dtheta0dtheta * dthetadn_dw
120 dfxc1b_dw = dfxc1dtheta1 * dtheta1dtheta0 * dtheta0dzeta * dzetadn_dw
121 # dfxc1b += dfxc1dtheta1 * dtheta1dtheta0 * dtheta0dtheta * dthetadn_dw
122 dphi_dw = dphidzeta * dzetadn_dw + dphidrs * drsdn # * dndn_dw = 1
123 # dfxc1b += dphidtheta * dthetadn_dw
124 vxc_dw = (
125 dfxc0a
126 + dfxc0b_dw
127 - phi * (dfxc0a + dfxc0b_dw - dfxc1a - dfxc1b_dw)
128 - (fxc0 - fxc1) * dphi_dw
129 )
131 return fxc, fxc + np.array([vxc_up, vxc_dw]) * n, None
134# ### Temperature dependent coefficients ###
137@dataclasses.dataclass
138class Coefficients:
139 """Coefficients class to calculate temperature/theta dependent coefficients.
141 Reference: Phys. Rev. Lett. 119, 135001.
142 """
144 theta: float #: Reduced temperature.
145 a0: float = 0.610887
146 a1: float = 0.75
147 a2: float = 3.04363
148 a3: float = -0.09227
149 a4: float = 1.7035
150 a5: float = 8.31051
151 a6: float = 5.1105
153 @property
154 def b5(self):
155 """Calculate b5."""
156 return self.b3 * np.sqrt(3 / 2) * self.omega * (4 / (9 * np.pi)) ** (-1 / 3)
158 @property
159 def a(self):
160 """Calculate a."""
161 with np.errstate(divide="ignore"):
162 u = self.a0 * np.tanh(
163 1 / self.theta, out=np.ones_like(self.theta), where=self.theta > 0
164 )
165 return u * _pade(self.theta, self.a1, self.a2, self.a3, self.a4, self.a5, self.a6)
167 @property
168 def dadtheta(self):
169 """Calculate da / dtheta."""
170 with np.errstate(divide="ignore"):
171 u = self.a0 * np.tanh(
172 1 / self.theta, out=np.ones_like(self.theta), where=self.theta > 0
173 )
174 du = np.divide(
175 u**2 / self.a0 - self.a0,
176 self.theta**2,
177 out=np.zeros_like(self.theta),
178 where=self.theta > 0,
179 )
180 v = _pade(self.theta, self.a1, self.a2, self.a3, self.a4, self.a5, self.a6)
181 dv = _dpade(self.theta, self.a1, self.a2, self.a3, self.a4, self.a5, self.a6)
182 return du * v + u * dv
184 @property
185 def b(self):
186 """Calculate b."""
187 with np.errstate(divide="ignore"):
188 u = np.tanh(1 / np.sqrt(self.theta), out=np.ones_like(self.theta), where=self.theta > 0)
189 return u * _pade(self.theta, self.b1, self.b2, 0, self.b3, self.b4, self.b5)
191 @property
192 def dbdtheta(self):
193 """Calculate db / dtheta."""
194 with np.errstate(divide="ignore"):
195 u = np.tanh(1 / np.sqrt(self.theta), out=np.ones_like(self.theta), where=self.theta > 0)
196 du = np.divide(
197 u**2 - 1,
198 2 * self.theta * np.sqrt(self.theta),
199 out=np.zeros_like(self.theta),
200 where=self.theta > 0,
201 )
202 v = _pade(self.theta, self.b1, self.b2, 0, self.b3, self.b4, self.b5)
203 dv = _dpade(self.theta, self.b1, self.b2, 0, self.b3, self.b4, self.b5)
204 return du * v + u * dv
206 @property
207 def c(self):
208 """Calculate c."""
209 with np.errstate(divide="ignore"):
210 exp = np.exp(-1 / self.theta, out=np.zeros_like(self.theta), where=self.theta > 0)
211 return (self.c1 + self.c2 * exp) * self.e
213 @property
214 def dcdtheta(self):
215 """Calculate dc / dtheta."""
216 with np.errstate(divide="ignore"):
217 exp = np.exp(-1 / self.theta, out=np.zeros_like(self.theta), where=self.theta > 0)
218 u = self.c1 + self.c2 * exp
219 du = np.divide(
220 self.c2 * exp, self.theta**2, out=np.zeros_like(self.theta), where=self.theta > 0
221 )
222 v = self.e
223 dv = self.dedtheta
224 return du * v + u * dv
226 @property
227 def d(self):
228 """Calculate d."""
229 with np.errstate(divide="ignore"):
230 u = np.tanh(1 / np.sqrt(self.theta), out=np.ones_like(self.theta), where=self.theta > 0)
231 return u * _pade(self.theta, self.d1, self.d2, 0, self.d3, self.d4, self.d5)
233 @property
234 def dddtheta(self):
235 """Calculate dd / dtheta."""
236 with np.errstate(divide="ignore"):
237 u = np.tanh(1 / np.sqrt(self.theta), out=np.ones_like(self.theta), where=self.theta > 0)
238 du = np.divide(
239 u**2 - 1,
240 2 * self.theta * np.sqrt(self.theta),
241 out=np.zeros_like(self.theta),
242 where=self.theta > 0,
243 )
244 v = _pade(self.theta, self.d1, self.d2, 0, self.d3, self.d4, self.d5)
245 dv = _dpade(self.theta, self.d1, self.d2, 0, self.d3, self.d4, self.d5)
246 return du * v + u * dv
248 @property
249 def e(self):
250 """Calculate e."""
251 with np.errstate(divide="ignore"):
252 u = np.tanh(1 / self.theta, out=np.ones_like(self.theta), where=self.theta > 0)
253 return u * _pade(self.theta, self.e1, self.e2, 0, self.e3, self.e4, self.e5)
255 @property
256 def dedtheta(self):
257 """Calculate de / dtheta."""
258 with np.errstate(divide="ignore"):
259 u = np.tanh(1 / self.theta, out=np.ones_like(self.theta), where=self.theta > 0)
260 du = np.divide(u**2 - 1, self.theta**2, out=np.zeros_like(self.theta), where=self.theta > 0)
261 v = _pade(self.theta, self.e1, self.e2, 0, self.e3, self.e4, self.e5)
262 dv = _dpade(self.theta, self.e1, self.e2, 0, self.e3, self.e4, self.e5)
263 return du * v + u * dv
266# ### Parameters ###
269@dataclasses.dataclass
270class Zeta0Coeffs(Coefficients):
271 """Coefficient class using the parameters for zeta=0.
273 Reference: Phys. Rev. Lett. 119, 135001.
274 """
276 omega: float = 1
277 b1: float = 0.3436902
278 b2: float = 7.82159531356
279 b3: float = 0.300483986662
280 b4: float = 15.8443467125
281 c1: float = 0.8759442
282 c2: float = -0.230130843551
283 d1: float = 0.72700876
284 d2: float = 2.38264734144
285 d3: float = 0.30221237251
286 d4: float = 4.39347718395
287 d5: float = 0.729951339845
288 e1: float = 0.25388214
289 e2: float = 0.815795138599
290 e3: float = 0.0646844410481
291 e4: float = 15.0984620477
292 e5: float = 0.230761357474
295@dataclasses.dataclass
296class Zeta1Coeffs(Coefficients):
297 """Coefficient class using the parameters for zeta=1.
299 Reference: Phys. Rev. Lett. 119, 135001.
300 """
302 omega: float = 2 ** (1 / 3)
303 b1: float = 0.84987704
304 b2: float = 3.04033012073
305 b3: float = 0.0775730131248
306 b4: float = 7.57703592489
307 c1: float = 0.91126873
308 c2: float = -0.0307957123308
309 d1: float = 1.48658718
310 d2: float = 4.92684905511
311 d3: float = 0.0849387225179
312 d4: float = 8.3269821188
313 d5: float = 0.218864952126
314 e1: float = 0.27454097
315 e2: float = 0.400994856555
316 e3: float = 2.88773194962
317 e4: float = 6.33499237092
318 e5: float = 24.823008753
321@dataclasses.dataclass
322class PhiParams:
323 """Parameter class holding the spin-interpolation function parameters.
325 Reference: Phys. Rev. Lett. 119, 135001.
326 """
328 # Sign of parameters is different from the supplemental material
329 h1: float = 3.18747258
330 h2: float = 7.74662802
331 lambda1: float = 1.85909536
332 lambda2: float = 0
335# ### Pade approximation and derivative ###
338def _pade(x, n1, n2, n3, n4, d1, d2):
339 """Pade approximation.
341 Not the general case but as needed for this functional.
342 """
343 num = n1 + n2 * x**2 + n3 * x**3 + n4 * x**4
344 denom = 1 + d1 * x**2 + d2 * x**4
345 return num / denom
348def _dpade(x, n1, n2, n3, n4, d1, d2):
349 """Pade approximation derivative."""
350 num = n1 + n2 * x**2 + n3 * x**3 + n4 * x**4
351 denom = 1 + d1 * x**2 + d2 * x**4
352 dnum = 2 * n2 * x + 3 * n3 * x**2 + 4 * n4 * x**3
353 ddenom = 2 * d1 * x + 4 * d2 * x**3
354 # df = (a'b - ab') / b^2
355 return (dnum * denom - num * ddenom) / denom**2
358# ### theta and derivatives ###
361def _get_theta(T, n, zeta):
362 """Calculate the reduced temperature theta.
364 Reference: Phys. Rev. Lett. 119, 135001.
365 Only mentioned in the arXiv version: https://arxiv.org/abs/1703.08074
366 """
367 n_up = (1 + zeta) * n / 2
368 T_fermi = (6 * np.pi**2 * n_up) ** (2 / 3) / 2
369 return T / T_fermi
372def _get_dthetadn_up(T, n_up):
373 """Calculate dtheta / dn_up."""
374 return -4 / (3 * 6 ** (2 / 3)) * T / (np.pi ** (4 / 3) * n_up ** (5 / 3))
377def _get_theta0(theta, zeta):
378 """Calculate theta0.
380 Reference: Phys. Rev. Lett. 119, 135001.
381 """
382 return theta * (1 + zeta) ** (2 / 3)
385def _get_dtheta0dtheta(zeta):
386 """Calculate dtheta0 / dtheta."""
387 return (1 + zeta) ** (2 / 3)
390def _get_dtheta0dzeta(theta, zeta):
391 """Calculate dtheta0 / dzeta."""
392 return 2 * theta / (3 * (1 + zeta) ** (1 / 3))
395def _get_theta1(theta, zeta):
396 """Calculate theta1.
398 Reference: Phys. Rev. Lett. 119, 135001.
400 It is not explicitly mentioned but introduced as used in Eq. (5).
401 """
402 theta0 = _get_theta0(theta, zeta)
403 return theta0 * 2 ** (-2 / 3)
406def _get_dtheta1dtheta0():
407 """Calculate dtheta1 / dtheta0."""
408 return 2 ** (-2 / 3)
411# ### fxc_zeta and derivatives ###
414def _get_fxc_zeta(rs, p):
415 """Calculate the Pade formula f_xc^zeta.
417 Reference: Phys. Rev. Lett. 119, 135001.
418 """
419 num = p.omega * p.a + np.sqrt(rs) * p.b + rs * p.c
420 denom = 1 + np.sqrt(rs) * p.d + rs * p.e
421 return -1 / rs * num / denom
424def _get_dfxc_zetadrs(rs, p):
425 """Calculate dfxc_zeta / drs."""
426 tmp1 = (-p.b / (2 * np.sqrt(rs)) - p.c) / (rs * (p.d * np.sqrt(rs) + p.e * rs + 1))
427 tmp2 = (-p.d / (2 * np.sqrt(rs)) - p.e) * (-p.a * p.omega - p.b * np.sqrt(rs) - p.c * rs)
428 tmp3 = rs * (p.d * np.sqrt(rs) + p.e * rs + 1) ** 2
429 tmp4 = (-p.a * p.omega - p.b * np.sqrt(rs) - p.c * rs) / (
430 rs**2 * (p.d * np.sqrt(rs) + p.e * rs + 1)
431 )
432 return tmp1 + tmp2 / tmp3 - tmp4
435def _get_dfxc_zetadtheta(rs, p):
436 """Calculate dfxc_zeta / dzeta."""
437 tmp1 = (-np.sqrt(rs) * p.dddtheta - rs * p.dedtheta) * (
438 -p.omega * p.a - p.b * np.sqrt(rs) - p.c * rs
439 )
440 tmp2 = (p.d * np.sqrt(rs) + p.e * rs + 1) ** 2 * rs
441 tmp3 = -p.omega * p.dadtheta - np.sqrt(rs) * p.dbdtheta - rs * p.dcdtheta
442 tmp4 = (p.d * np.sqrt(rs) + p.e * rs + 1) * rs
443 return tmp1 / tmp2 + tmp3 / tmp4
446# ### phi and derivatives ###
449def _get_phi(rs, theta, zeta, phi_params):
450 """Calculate the interpolation function phi.
452 Reference: Phys. Rev. Lett. 119, 135001.
453 """
454 alpha = _get_alpha(rs, theta, phi_params)
455 return ((1 + zeta) ** alpha + (1 - zeta) ** alpha - 2) / (2**alpha - 2)
458def _get_dphidrs(rs, theta, zeta, phi_params):
459 """Calculate dphi / drs."""
460 alpha = _get_alpha(rs, theta, phi_params)
461 tmp1 = (1 - zeta) ** alpha * np.log(1 - zeta, out=np.zeros_like(zeta), where=1 - zeta > 0)
462 tmp2 = (1 + zeta) ** alpha * np.log(1 + zeta)
463 duv = (tmp1 + tmp2) * (2**alpha - 2)
464 udv = ((1 - zeta) ** alpha + (1 + zeta) ** alpha - 2) * 2**alpha * np.log(2)
465 vv = (2**alpha - 2) ** 2
466 dalphadrs = _get_dalphadrs(rs, theta, phi_params)
467 return (duv - udv) * dalphadrs / vv
470def _get_dphidtheta(rs, theta, zeta, phi_params):
471 """Calculate dphi / dtheta."""
472 alpha = _get_alpha(rs, theta, phi_params)
473 dalphadtheta = _get_dalphadtheta(rs, theta, phi_params)
474 tmp1 = (1 - zeta) ** alpha * np.log(1 - zeta, out=np.zeros_like(zeta), where=1 - zeta > 0)
475 tmp2 = (1 + zeta) ** alpha * np.log(1 + zeta)
476 duv = (tmp1 + tmp2) * (2**alpha - 2)
477 udv = ((1 - zeta) ** alpha + (1 + zeta) ** alpha - 2) * 2**alpha * np.log(2)
478 vv = (2**alpha - 2) ** 2
479 dalphadtheta = _get_dalphadtheta(rs, theta, phi_params)
480 return (duv - udv) * dalphadtheta / vv
483def _get_dphidzeta(rs, theta, zeta, phi_params):
484 """Calculate dphi / dzeta."""
485 alpha = _get_alpha(rs, theta, phi_params)
486 tmp1 = alpha * (1 + zeta) ** alpha / (1 + zeta)
487 tmp2 = np.divide(
488 alpha * (1 - zeta) ** alpha, 1 - zeta, out=np.zeros_like(zeta), where=1 - zeta > 0
489 )
490 return (tmp1 - tmp2) / (2**alpha - 2)
493# ### alpha and derivatives ###
496def _get_alpha(rs, theta, phi_params):
497 """Calculate alpha.
499 Reference: Phys. Rev. Lett. 119, 135001.
500 """
501 h = _get_h(rs, phi_params)
502 lamda = _get_lambda(rs, theta, phi_params)
503 return 2 - h * np.exp(-theta * lamda)
506def _get_dalphadrs(rs, theta, phi_params):
507 """Calculate dalpha / drs."""
508 h = _get_h(rs, phi_params)
509 lamda = _get_lambda(rs, theta, phi_params)
510 dhdrs = _get_dhdrs(rs, phi_params)
511 dlambdadrs = _get_dlambdadrs(rs, theta, phi_params)
512 return -dhdrs * np.exp(-theta * lamda) + dlambdadrs * theta * h * np.exp(-theta * lamda)
515def _get_dalphadtheta(rs, theta, phi_params):
516 """Calculate dalpha / dtheta."""
517 h = _get_h(rs, phi_params)
518 lamda = _get_lambda(rs, theta, phi_params)
519 dlambdadtheta = _get_dlambdadtheta(rs, phi_params)
520 return (dlambdadtheta * theta + lamda) * h * np.exp(-theta * lamda)
523# ### h and derivative ###
526def _get_h(rs, phi_params):
527 """Calculate h.
529 Reference: Phys. Rev. Lett. 119, 135001.
530 """
531 return (2 / 3 + phi_params.h1 * rs) / (1 + phi_params.h2 * rs)
534def _get_dhdrs(rs, phi_params):
535 """Calculate dh / drs."""
536 return (
537 phi_params.h1 / (phi_params.h2 * rs + 1)
538 - phi_params.h2 * (phi_params.h1 * rs + 2 / 3) / (phi_params.h2 * rs + 1) ** 2
539 )
542# ### lambda and derivatives ###
545def _get_lambda(rs, theta, phi_params):
546 """Calculate lambda.
548 Reference: Phys. Rev. Lett. 119, 135001.
549 """
550 return phi_params.lambda1 + phi_params.lambda2 * theta * rs ** (1 / 2)
553def _get_dlambdadrs(rs, theta, phi_params):
554 """Calculate dlambda / drs."""
555 return phi_params.lambda2 * theta / (2 * np.sqrt(rs))
558def _get_dlambdadtheta(rs, phi_params):
559 """Calculate dlambda / dtheta."""
560 return phi_params.lambda2 * np.sqrt(rs)