Coverage for gpaw/xc/libxc.py: 92%

65 statements  

« prev     ^ index     » next       coverage.py v7.7.1, created at 2025-07-08 00:17 +0000

1import warnings 

2 

3import gpaw.cgpaw as cgpaw 

4from gpaw import debug 

5from gpaw.xc.kernel import XCKernel 

6 

7 

8class FunctionalNeedsLaplacianError(Exception): 

9 """(MGGA) Functional needs laplacian. 

10 

11 """ 

12 

13 

14short_names = { 

15 'LDA': 'LDA_X+LDA_C_PW', 

16 'PW91': 'GGA_X_PW91+GGA_C_PW91', 

17 'PBE': 'GGA_X_PBE+GGA_C_PBE', 

18 'PBEsol': 'GGA_X_PBE_SOL+GGA_C_PBE_SOL', 

19 'revPBE': 'GGA_X_PBE_R+GGA_C_PBE', 

20 'RPBE': 'GGA_X_RPBE+GGA_C_PBE', 

21 'BLYP': 'GGA_X_B88+GGA_C_LYP', 

22 'HCTH407': 'GGA_XC_HCTH_407', 

23 'WC': 'GGA_X_WC+GGA_C_PBE', 

24 'AM05': 'GGA_X_AM05+GGA_C_AM05', 

25 'M06-L': 'MGGA_X_M06_L+MGGA_C_M06_L', 

26 'TPSS': 'MGGA_X_TPSS+MGGA_C_TPSS', 

27 'revTPSS': 'MGGA_X_REVTPSS+MGGA_C_REVTPSS', 

28 'mBEEF': 'MGGA_X_MBEEF+GGA_C_PBE_SOL', 

29 'SCAN': 'MGGA_X_SCAN+MGGA_C_SCAN'} 

30 

31 

32class LibXC(XCKernel): 

33 """Functionals from libxc.""" 

34 def __init__(self, name, provides_laplacian=False, disable_fhc=True): 

35 if not hasattr(cgpaw, 'lxcXCFuncNum'): 

36 raise NameError( 

37 f'Unable to use {name}: GPAW not compiled with LibXC!') 

38 self.name = name 

39 self.omega = None 

40 self.disable_fhc = disable_fhc 

41 self.initialize(nspins=1, provides_laplacian=provides_laplacian) 

42 

43 def initialize(self, 

44 nspins, 

45 provides_laplacian=False): 

46 self.nspins = nspins 

47 name = short_names.get(self.name, self.name) 

48 number = cgpaw.lxcXCFuncNum(name) 

49 if number is not None: 

50 f = number 

51 xc = -1 

52 x = -1 

53 c = -1 

54 if '_XC_' in name: 

55 xc = f 

56 elif '_C_' in name: 

57 c = f 

58 else: 

59 x = f 

60 else: 

61 try: 

62 x, c = name.split('+') 

63 except ValueError: 

64 raise NameError(f'Unknown functional: {name}.') 

65 xc = -1 

66 x = cgpaw.lxcXCFuncNum(x) 

67 c = cgpaw.lxcXCFuncNum(c) 

68 if x is None or c is None: 

69 raise NameError(f'Unknown functional: {name}.') 

70 

71 self.xc = cgpaw.lxcXCFunctional(xc, x, c, nspins) 

72 self.set_omega() 

73 

74 if self.xc.is_mgga(): 

75 self.type = 'MGGA' 

76 if self.disable_fhc: 

77 ok = self.xc.disable_fhc() 

78 if not ok: 

79 warnings.warn( 

80 'libxc should be compiled with --disable-fhc' + 

81 ' otherwise SCF calculations might not converge.') 

82 if self.xc.needs_laplacian() and not provides_laplacian: 

83 msg = f'Functional "{name}" needs laplacian' 

84 msg += ' (unsupported)' 

85 raise FunctionalNeedsLaplacianError(msg, self.xc) 

86 elif self.xc.is_gga(): 

87 self.type = 'GGA' 

88 else: 

89 self.type = 'LDA' 

90 

91 def calculate(self, e_g, n_sg, dedn_sg, 

92 sigma_xg=None, dedsigma_xg=None, 

93 tau_sg=None, dedtau_sg=None): 

94 if debug: 

95 self.check_arguments(e_g, n_sg, dedn_sg, sigma_xg, dedsigma_xg, 

96 tau_sg, dedtau_sg) 

97 nspins = len(n_sg) 

98 if self.nspins != nspins: 

99 self.initialize(nspins) 

100 

101 self.xc.calculate(e_g.ravel(), n_sg, dedn_sg, 

102 sigma_xg, dedsigma_xg, 

103 tau_sg, dedtau_sg) 

104 

105 def set_omega(self, omega=None): 

106 """Set the value of gamma/omega in RSF.""" 

107 if omega is not None: 

108 self.omega = omega 

109 if self.omega is not None: 

110 if not self.xc.set_omega(self.omega): 

111 raise ValueError('Tried setting omega on a non RSF hybrid.')