Coverage for eminus/xc/lda_xc_ksdt.py: 100.00%

291 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-21 12:19 +0000

1# SPDX-FileCopyrightText: 2024 The eminus developers 

2# SPDX-License-Identifier: Apache-2.0 

3"""KSDT LDA exchange-correlation. 

4 

5Reference: Phys. Rev. Lett. 112, 076403. 

6""" 

7 

8import dataclasses 

9import functools 

10import math 

11 

12import numpy as np 

13 

14from eminus import backend as xp 

15 

16# ### Temperature dependent coefficients ### 

17 

18 

19@dataclasses.dataclass 

20class Coefficients: 

21 """Coefficients class to calculate temperature/theta dependent coefficients. 

22 

23 Reference: Phys. Rev. Lett. 112, 076403. 

24 """ 

25 

26 theta: float #: Reduced temperature. 

27 omega: float 

28 e1: float 

29 e2: float 

30 e3: float 

31 e4: float 

32 e5: float 

33 d1: float 

34 d2: float 

35 d3: float 

36 d4: float 

37 d5: float 

38 c1: float 

39 c2: float 

40 c3: float 

41 b1: float 

42 b2: float 

43 b3: float 

44 b4: float 

45 a1: float = 0.75 

46 a2: float = 3.04363 

47 a3: float = -0.09227 

48 a4: float = 1.7035 

49 a5: float = 8.31051 

50 a6: float = 5.1105 

51 

52 @functools.cached_property 

53 def a0(self): 

54 """Calculate a0.""" 

55 return 1 / (math.pi * (4 / (9 * math.pi)) ** (1 / 3)) 

56 

57 @functools.cached_property 

58 def b5(self): 

59 """Calculate b5.""" 

60 return self.b3 * math.sqrt(3 / 2) * self.omega * (4 / (9 * math.pi)) ** (-1 / 3) 

61 

62 @functools.cached_property 

63 def a(self): 

64 """Calculate a.""" 

65 with np.errstate(divide="ignore"): 

66 u = self.a0 * xp.where(self.theta > 0, xp.tanh(1 / self.theta), 1) 

67 return u * _pade(self.theta, self.a1, self.a2, self.a3, self.a4, self.a5, self.a6) 

68 

69 @property 

70 def dadtheta(self): 

71 """Calculate da / dtheta.""" 

72 with np.errstate(divide="ignore", invalid="ignore"): 

73 u = self.a0 * xp.where(self.theta > 0, xp.tanh(1 / self.theta), 1) 

74 du = xp.where(self.theta > 0, (u**2 / self.a0 - self.a0) / self.theta**2, 0) 

75 v, dv = _dpade(self.theta, self.a1, self.a2, self.a3, self.a4, self.a5, self.a6) 

76 return du * v + u * dv 

77 

78 @functools.cached_property 

79 def b(self): 

80 """Calculate b.""" 

81 with np.errstate(divide="ignore"): 

82 u = xp.where(self.theta > 0, xp.tanh(1 / xp.sqrt(self.theta)), 1) 

83 return u * _pade(self.theta, self.b1, self.b2, 0, self.b3, self.b4, self.b5) 

84 

85 @property 

86 def dbdtheta(self): 

87 """Calculate db / dtheta.""" 

88 with np.errstate(divide="ignore", invalid="ignore"): 

89 u = xp.where(self.theta > 0, xp.tanh(1 / xp.sqrt(self.theta)), 1) 

90 du = xp.where(self.theta > 0, (u**2 - 1) / (2 * self.theta * xp.sqrt(self.theta)), 0) 

91 v, dv = _dpade(self.theta, self.b1, self.b2, 0, self.b3, self.b4, self.b5) 

92 return du * v + u * dv 

93 

94 @functools.cached_property 

95 def c(self): 

96 """Calculate c.""" 

97 with np.errstate(divide="ignore"): 

98 exp = xp.where(self.theta > 0, xp.exp(-self.c3 / self.theta), 0) 

99 return (self.c1 + self.c2 * exp) * self.e 

100 

101 @property 

102 def dcdtheta(self): 

103 """Calculate dc / dtheta.""" 

104 with np.errstate(divide="ignore"): 

105 exp = xp.where(self.theta > 0, xp.exp(-self.c3 / self.theta), 0) 

106 u = self.c1 + self.c2 * exp 

107 with np.errstate(invalid="ignore"): 

108 du = xp.where(self.theta > 0, self.c2 * self.c3 * exp / self.theta**2, 0) 

109 v, dv = self.e, self.dedtheta 

110 return du * v + u * dv 

111 

112 @functools.cached_property 

113 def d(self): 

114 """Calculate d.""" 

115 with np.errstate(divide="ignore"): 

116 u = xp.where(self.theta > 0, xp.tanh(1 / xp.sqrt(self.theta)), 1) 

117 return u * _pade(self.theta, self.d1, self.d2, 0, self.d3, self.d4, self.d5) 

118 

119 @property 

120 def dddtheta(self): 

121 """Calculate dd / dtheta.""" 

122 with np.errstate(divide="ignore", invalid="ignore"): 

123 u = xp.where(self.theta > 0, xp.tanh(1 / xp.sqrt(self.theta)), 1) 

124 du = xp.where(self.theta > 0, (u**2 - 1) / (2 * self.theta * xp.sqrt(self.theta)), 0) 

125 v, dv = _dpade(self.theta, self.d1, self.d2, 0, self.d3, self.d4, self.d5) 

126 return du * v + u * dv 

127 

128 @functools.cached_property 

129 def e(self): 

130 """Calculate e.""" 

131 with np.errstate(divide="ignore"): 

132 u = xp.where(self.theta > 0, xp.tanh(1 / self.theta), 1) 

133 return u * _pade(self.theta, self.e1, self.e2, 0, self.e3, self.e4, self.e5) 

134 

135 @functools.cached_property 

136 def dedtheta(self): 

137 """Calculate de / dtheta.""" 

138 with np.errstate(divide="ignore", invalid="ignore"): 

139 u = xp.where(self.theta > 0, xp.tanh(1 / self.theta), 1) 

140 du = xp.where(self.theta > 0, (u**2 - 1) / self.theta**2, 0) 

141 v, dv = _dpade(self.theta, self.e1, self.e2, 0, self.e3, self.e4, self.e5) 

142 return du * v + u * dv 

143 

144 

145# ### Parameters ### 

146 

147 

148@dataclasses.dataclass 

149class Zeta0Coeffs(Coefficients): 

150 """Coefficient class using the parameters for zeta=0. 

151 

152 Reference: Phys. Rev. Lett. 112, 076403. 

153 """ 

154 

155 omega: float = 1 

156 b1: float = 0.283997 

157 b2: float = 48.932154 

158 b3: float = 0.370919 

159 b4: float = 61.095357 

160 c1: float = 0.870089 

161 c2: float = 0.193077 

162 c3: float = 2.414644 

163 d1: float = 0.579824 

164 d2: float = 94.537454 

165 d3: float = 97.839603 

166 d4: float = 59.939999 

167 d5: float = 24.388037 

168 e1: float = 0.212036 

169 e2: float = 16.731249 

170 e3: float = 28.485792 

171 e4: float = 34.028876 

172 e5: float = 17.235515 

173 

174 

175@dataclasses.dataclass 

176class Zeta1Coeffs(Coefficients): 

177 """Coefficient class using the parameters for zeta=1. 

178 

179 Reference: Phys. Rev. Lett. 112, 076403. 

180 """ 

181 

182 omega: float = 2 ** (1 / 3) 

183 b1: float = 0.329001 

184 b2: float = 111.598308 

185 b3: float = 0.537053 

186 b4: float = 105.086663 

187 c1: float = 0.84893 

188 c2: float = 0.167952 

189 c3: float = 0.08882 

190 d1: float = 0.55133 

191 d2: float = 180.213159 

192 d3: float = 134.486231 

193 d4: float = 103.861695 

194 d5: float = 17.75071 

195 e1: float = 0.153124 

196 e2: float = 19.543945 

197 e3: float = 43.400337 

198 e4: float = 120.255145 

199 e5: float = 15.662836 

200 

201 

202@dataclasses.dataclass 

203class PhiParams: 

204 """Parameter class holding the spin-interpolation function parameters. 

205 

206 Reference: Phys. Rev. Lett. 112, 076403. 

207 """ 

208 

209 g1: float = 2 / 3 

210 g2: float = -0.0139261 

211 g3: float = 0.183208 

212 lambda1: float = 1.064009 

213 lambda2: float = 0.572565 

214 

215 

216# ### Functional implementation ### 

217 

218 

219def lda_xc_ksdt( 

220 n, T=0, zeta0_coeffs=Zeta0Coeffs, zeta1_coeffs=Zeta1Coeffs, phi_params=PhiParams, **kwargs 

221): 

222 """KSDT exchange-correlation functional (spin-polarized). 

223 

224 Similar to the functional with the label LDA_XC_KSDT and ID 259 in Libxc, but the 

225 implementations differ: https://gitlab.com/libxc/libxc/-/issues/525 

226 Exchange and correlation cannot be separated. 

227 

228 Reference: Phys. Rev. Lett. 112, 076403. 

229 

230 Args: 

231 n: Real-space electronic density. 

232 

233 Keyword Args: 

234 T: Temperature in Hartree. 

235 zeta0_coeffs: Coefficient class using the parameters for zeta=0. 

236 zeta1_coeffs: Coefficient class using the parameters for zeta=1. 

237 phi_params: Parameter class holding the spin-interpolation function parameters. 

238 **kwargs: Throwaway arguments. 

239 

240 Returns: 

241 KSDT exchange-correlation energy density and potential. 

242 """ 

243 kwargs["zeta"] = xp.zeros_like(n) 

244 exc, vxc, _ = lda_xc_ksdt_spin( 

245 n, 

246 T=T, 

247 zeta0_coeffs=zeta0_coeffs, 

248 zeta1_coeffs=zeta1_coeffs, 

249 phi_params=phi_params, 

250 **kwargs, 

251 ) 

252 return exc, xp.stack([vxc[0]]), None 

253 

254 

255def lda_xc_ksdt_spin( 

256 n, zeta, T=0, zeta0_coeffs=Zeta0Coeffs, zeta1_coeffs=Zeta1Coeffs, phi_params=PhiParams, **kwargs 

257): 

258 """KSDT exchange-correlation functional (spin-polarized). 

259 

260 Similar to the functional with the label LDA_XC_KSDT and ID 259 in Libxc, but the 

261 implementations differ: https://gitlab.com/libxc/libxc/-/issues/525 

262 Exchange and correlation cannot be separated. 

263 

264 Reference: Phys. Rev. Lett. 112, 076403. 

265 

266 Args: 

267 n: Real-space electronic density. 

268 zeta: Relative spin polarization. 

269 

270 Keyword Args: 

271 T: Temperature in Hartree. 

272 zeta0_coeffs: Coefficient class using the parameters for zeta=0. 

273 zeta1_coeffs: Coefficient class using the parameters for zeta=1. 

274 phi_params: Parameter class holding the spin-interpolation function parameters. 

275 **kwargs: Throwaway arguments. 

276 

277 Returns: 

278 KSDT exchange-correlation energy density and potential. 

279 """ 

280 # Calculate properties 

281 n_up = (1 + zeta) * n / 2 

282 rs = (3 / (4 * math.pi * n)) ** (1 / 3) 

283 theta = _get_theta(T, n, zeta) 

284 theta0 = _get_theta0(theta, zeta) 

285 theta1 = _get_theta1(theta, zeta) 

286 

287 # Initialize parameters 

288 # We need to calculate coefficients for specific theta 

289 # Create a coefficients object for each theta with its corresponding parameters 

290 phi_params = phi_params() 

291 zeta0theta = zeta0_coeffs(theta) 

292 zeta0theta0 = zeta0_coeffs(theta0) 

293 zeta1theta = zeta1_coeffs(theta) 

294 zeta1theta1 = zeta1_coeffs(theta1) 

295 

296 # Calculate fxc 

297 fxc0 = _get_fxc_zeta(rs, zeta0theta0) 

298 fxc1 = _get_fxc_zeta(rs, zeta1theta1) 

299 phi = _get_phi(rs, theta0, zeta, phi_params) 

300 fxc = fxc0 + (fxc1 - fxc0) * phi 

301 

302 # Generic derivatives 

303 drsdn = -(6 ** (1 / 3)) * (1 / n) ** (1 / 3) / (6 * math.pi ** (1 / 3) * n) 

304 dzetadn_up = -zeta / n**2 + 1 / n 

305 dzetadn_dw = -zeta / n**2 - 1 / n 

306 

307 # fxc derivatives 

308 dfxc0drs = _get_dfxc_zetadrs(rs, zeta0theta) 

309 dfxc1drs = _get_dfxc_zetadrs(rs, zeta1theta) 

310 dfxc0dtheta0 = _get_dfxc_zetadtheta(rs, zeta0theta0) 

311 dfxc1dtheta1 = _get_dfxc_zetadtheta(rs, zeta1theta1) 

312 

313 # phi derivatives 

314 dphidrs = _get_dphidrs(rs, theta0, zeta, phi_params) 

315 dphidtheta = _get_dphidtheta(rs, theta0, zeta, phi_params) 

316 dphidzeta = _get_dphidzeta(rs, theta0, zeta, phi_params) 

317 

318 # theta derivatives 

319 dthetadn_up = _get_dthetadn_up(T, n_up) 

320 dtheta0dtheta = _get_dtheta0dtheta(zeta) 

321 dtheta0dzeta = _get_dtheta0dzeta(theta0, zeta) 

322 dtheta1dtheta0 = _get_dtheta1dtheta0() 

323 

324 # Calculate vxc_up (using dndn_up=1) 

325 dfxc0a = dfxc0drs * drsdn # * dndn_up = 1 

326 dfxc1a = dfxc1drs * drsdn # * dndn_up = 1 

327 dfxc0b_up = dfxc0dtheta0 * (dtheta0dtheta * dthetadn_up + dtheta0dzeta * dzetadn_up) 

328 dfxc1b_up = ( 

329 dfxc1dtheta1 * dtheta1dtheta0 * (dtheta0dtheta * dthetadn_up + dtheta0dzeta * dzetadn_up) 

330 ) 

331 dphi_up = dphidtheta * dthetadn_up + dphidzeta * dzetadn_up + dphidrs * drsdn # * dndn_up = 1 

332 vxc_up = ( 

333 dfxc0a 

334 + dfxc0b_up 

335 - phi * (dfxc0a + dfxc0b_up - dfxc1a - dfxc1b_up) 

336 - (fxc0 - fxc1) * dphi_up 

337 ) 

338 

339 # Calculate vxc_dw (using dndn_dw=1 and dthetadn_dw=0) 

340 # dfxc0a and dfxc1a are identical for vxc_up and vxc_dw 

341 dfxc0b_dw = dfxc0dtheta0 * dtheta0dzeta * dzetadn_dw 

342 # dfxc0b += dfxc0dtheta0 * dtheta0dtheta * dthetadn_dw 

343 dfxc1b_dw = dfxc1dtheta1 * dtheta1dtheta0 * dtheta0dzeta * dzetadn_dw 

344 # dfxc1b += dfxc1dtheta1 * dtheta1dtheta0 * dtheta0dtheta * dthetadn_dw 

345 dphi_dw = dphidzeta * dzetadn_dw + dphidrs * drsdn # * dndn_dw = 1 

346 # dfxc1b += dphidtheta * dthetadn_dw 

347 vxc_dw = ( 

348 dfxc0a 

349 + dfxc0b_dw 

350 - phi * (dfxc0a + dfxc0b_dw - dfxc1a - dfxc1b_dw) 

351 - (fxc0 - fxc1) * dphi_dw 

352 ) 

353 

354 return fxc, fxc + xp.stack([vxc_up, vxc_dw]) * n, None 

355 

356 

357# ### Pade approximation and derivative ### 

358 

359 

360def _pade(x, n1, n2, n3, n4, d1, d2): 

361 """Pade approximation. 

362 

363 Not the general case but as needed for this functional. 

364 """ 

365 x2, x4 = x**2, x**4 

366 num = n1 + n2 * x2 + n3 * x**3 + n4 * x4 

367 denom = 1 + d1 * x2 + d2 * x4 

368 return num / denom 

369 

370 

371def _dpade(x, n1, n2, n3, n4, d1, d2): 

372 """Pade approximation and its derivative.""" 

373 x2, x3, x4 = x**2, x**3, x**4 

374 num = n1 + n2 * x2 + n3 * x3 + n4 * x4 

375 denom = 1 + d1 * x2 + d2 * x4 

376 dnum = 2 * n2 * x + 3 * n3 * x2 + 4 * n4 * x3 

377 ddenom = 2 * d1 * x + 4 * d2 * x3 

378 # df = (a'b - ab') / b^2 

379 return num / denom, (dnum * denom - num * ddenom) / denom**2 

380 

381 

382# ### theta and derivatives ### 

383 

384 

385def _get_theta(T, n, zeta): 

386 """Calculate the reduced temperature theta. 

387 

388 Reference: Phys. Rev. Lett. 119, 135001. 

389 Only mentioned in the arXiv version: https://arxiv.org/abs/1703.08074 

390 """ 

391 n_up = (1 + zeta) * n / 2 

392 T_fermi = (6 * math.pi**2 * n_up) ** (2 / 3) / 2 

393 return T / T_fermi 

394 

395 

396def _get_dthetadn_up(T, n_up): 

397 """Calculate dtheta / dn_up.""" 

398 return -4 / (3 * 6 ** (2 / 3)) * T / (math.pi ** (4 / 3) * n_up ** (5 / 3)) 

399 

400 

401def _get_theta0(theta, zeta): 

402 """Calculate theta0. 

403 

404 Reference: Phys. Rev. Lett. 119, 135001. 

405 """ 

406 return theta * (1 + zeta) ** (2 / 3) 

407 

408 

409def _get_dtheta0dtheta(zeta): 

410 """Calculate dtheta0 / dtheta.""" 

411 return (1 + zeta) ** (2 / 3) 

412 

413 

414def _get_dtheta0dzeta(theta, zeta): 

415 """Calculate dtheta0 / dzeta.""" 

416 return 2 * theta / (3 * (1 + zeta) ** (1 / 3)) 

417 

418 

419def _get_theta1(theta, zeta): 

420 """Calculate theta1. 

421 

422 Reference: Phys. Rev. Lett. 119, 135001. 

423 

424 It is not explicitly mentioned but introduced as used in Eq. (5). 

425 """ 

426 theta0 = _get_theta0(theta, zeta) 

427 return theta0 * 2 ** (-2 / 3) 

428 

429 

430def _get_dtheta1dtheta0(): 

431 """Calculate dtheta1 / dtheta0.""" 

432 return 2 ** (-2 / 3) 

433 

434 

435# ### fxc_zeta and derivatives ### 

436 

437 

438def _get_fxc_zeta(rs, p): 

439 """Calculate the Pade formula f_xc^zeta. 

440 

441 Reference: Phys. Rev. Lett. 112, 076403. 

442 """ 

443 p.theta = p.theta 

444 num = p.a * p.omega + p.b * xp.sqrt(rs) + p.c * rs 

445 denom = 1 + p.d * xp.sqrt(rs) + p.e * rs 

446 return -1 / rs * num / denom 

447 

448 

449def _get_dfxc_zetadrs(rs, p): 

450 """Calculate dfxc_zeta / drs.""" 

451 num = p.a * p.omega + p.b * xp.sqrt(rs) + p.c * rs 

452 denom = 1 + p.d * xp.sqrt(rs) + p.e * rs 

453 tmp1 = (p.d / (2 * xp.sqrt(rs)) + p.e) * num / denom**2 

454 tmp2 = (p.b / (2 * xp.sqrt(rs)) + p.c) / denom 

455 return (tmp1 - tmp2) / rs + num / denom / rs**2 

456 

457 

458def _get_dfxc_zetadtheta(rs, p): 

459 """Calculate dfxc_zeta / dzeta.""" 

460 num = p.a * p.omega + p.b * xp.sqrt(rs) + p.c * rs 

461 denom = 1 + p.d * xp.sqrt(rs) + p.e * rs 

462 tmp1 = (p.dddtheta * xp.sqrt(rs) + p.dedtheta * rs) * num / denom**2 

463 tmp2 = (p.dadtheta * p.omega + p.dbdtheta * xp.sqrt(rs) + p.dcdtheta * rs) / denom 

464 return (tmp1 - tmp2) / rs 

465 

466 

467# ### phi and derivatives ### 

468 

469 

470def _get_phi(rs, theta, zeta, phi_params): 

471 """Calculate the interpolation function phi. 

472 

473 Reference: Phys. Rev. Lett. 112, 076403. 

474 """ 

475 alpha = _get_alpha(rs, theta, phi_params) 

476 return ((1 + zeta) ** alpha + (1 - zeta) ** alpha - 2) / (2**alpha - 2) 

477 

478 

479def _get_dphidrs(rs, theta, zeta, phi_params): 

480 """Calculate dphi / drs.""" 

481 alpha = _get_alpha(rs, theta, phi_params) 

482 dalphadrs = _get_dalphadrs(rs, theta, phi_params) 

483 with np.errstate(divide="ignore"): 

484 tmp1 = (1 - zeta) ** alpha * xp.where(1 - zeta > 0, xp.log(1 - zeta), 0) 

485 tmp2 = (1 + zeta) ** alpha * xp.log(1 + zeta) 

486 duv = (tmp1 + tmp2) * (2**alpha - 2) 

487 udv = ((1 - zeta) ** alpha + (1 + zeta) ** alpha - 2) * 2**alpha * math.log(2) 

488 vv = (2**alpha - 2) ** 2 

489 return (duv - udv) * dalphadrs / vv 

490 

491 

492def _get_dphidtheta(rs, theta, zeta, phi_params): 

493 """Calculate dphi / dtheta.""" 

494 alpha = _get_alpha(rs, theta, phi_params) 

495 dalphadtheta = _get_dalphadtheta(rs, theta, phi_params) 

496 with np.errstate(divide="ignore"): 

497 tmp1 = (1 - zeta) ** alpha * xp.where(1 - zeta > 0, xp.log(1 - zeta), 0) 

498 tmp2 = (1 + zeta) ** alpha * xp.log(1 + zeta) 

499 duv = (tmp1 + tmp2) * (2**alpha - 2) 

500 udv = ((1 - zeta) ** alpha + (1 + zeta) ** alpha - 2) * 2**alpha * math.log(2) 

501 vv = (2**alpha - 2) ** 2 

502 return (duv - udv) * dalphadtheta / vv 

503 

504 

505def _get_dphidzeta(rs, theta, zeta, phi_params): 

506 """Calculate dphi / dzeta.""" 

507 alpha = _get_alpha(rs, theta, phi_params) 

508 tmp1 = alpha * (1 + zeta) ** alpha / (1 + zeta) 

509 with np.errstate(invalid="ignore"): 

510 tmp2 = xp.where(1 - zeta > 0, alpha * (1 - zeta) ** alpha / (1 - zeta), 0) 

511 return (tmp1 - tmp2) / (2**alpha - 2) 

512 

513 

514# ### alpha and derivatives ### 

515 

516 

517def _get_alpha(rs, theta, phi_params): 

518 """Calculate alpha. 

519 

520 Reference: Phys. Rev. Lett. 112, 076403. 

521 """ 

522 g = _get_g(rs, phi_params) 

523 lamda = _get_lambda(rs, theta, phi_params) 

524 return 2 - g * xp.exp(-theta * lamda) 

525 

526 

527def _get_dalphadrs(rs, theta, phi_params): 

528 """Calculate dalpha / drs.""" 

529 g = _get_g(rs, phi_params) 

530 lamda = _get_lambda(rs, theta, phi_params) 

531 dgdrs = _get_dgdrs(rs, phi_params) 

532 dlambdadrs = _get_dlambdadrs(rs, theta, phi_params) 

533 return -dgdrs * xp.exp(-theta * lamda) + dlambdadrs * theta * g * xp.exp(-theta * lamda) 

534 

535 

536def _get_dalphadtheta(rs, theta, phi_params): 

537 """Calculate dalpha / dtheta.""" 

538 g = _get_g(rs, phi_params) 

539 lamda = _get_lambda(rs, theta, phi_params) 

540 dlambdadtheta = _get_dlambdadtheta(rs, phi_params) 

541 return (dlambdadtheta * theta + lamda) * g * xp.exp(-theta * lamda) 

542 

543 

544# ### g and derivative ### 

545 

546 

547def _get_g(rs, phi_params): 

548 """Calculate g. 

549 

550 Reference: Phys. Rev. Lett. 112, 076403. 

551 """ 

552 return (phi_params.g1 + phi_params.g2 * rs) / (1 + phi_params.g3 * rs) 

553 

554 

555def _get_dgdrs(rs, phi_params): 

556 """Calculate dg / drs.""" 

557 return ( 

558 phi_params.g2 / (phi_params.g3 * rs + 1) 

559 - phi_params.g3 * (phi_params.g2 * rs + phi_params.g1) / (phi_params.g3 * rs + 1) ** 2 

560 ) 

561 

562 

563# ### lambda and derivatives ### 

564 

565 

566def _get_lambda(rs, theta, phi_params): 

567 """Calculate lambda. 

568 

569 Reference: Phys. Rev. Lett. 112, 076403. 

570 """ 

571 return phi_params.lambda1 + phi_params.lambda2 * theta * rs ** (1 / 2) 

572 

573 

574def _get_dlambdadrs(rs, theta, phi_params): 

575 """Calculate dlambda / drs.""" 

576 return phi_params.lambda2 * theta / (2 * xp.sqrt(rs)) 

577 

578 

579def _get_dlambdadtheta(rs, phi_params): 

580 """Calculate dlambda / dtheta.""" 

581 return phi_params.lambda2 * xp.sqrt(rs)