Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import numpy as np 

2import ase.units as un 

3 

4 

5class SiestaLRTDDFT: 

6 """Interface for linear response TDDFT for Siesta via `PyNAO`_ 

7 

8 When using PyNAO please cite the papers indicated in the 

9 `documentation <https://mbarbrywebsite.ddns.net/pynao/doc/html/references.html>`_ 

10 """ 

11 def __init__(self, initialize=False, **kw): 

12 """ 

13 Parameters 

14 ---------- 

15 initialize: bool 

16 To initialize the tddft calculations before calculating the polarizability 

17 Can be useful to calculate multiple frequency range without the need 

18 to recalculate the kernel 

19 kw: dictionary 

20 keywords for the tddft_iter function from PyNAO 

21 """ 

22 

23 try: 

24 from pynao import tddft_iter 

25 except ModuleNotFoundError as err: 

26 msg = "running lrtddft with Siesta calculator requires pynao package" 

27 raise ModuleNotFoundError(msg) from err 

28 

29 self.initialize = initialize 

30 self.lrtddft_params = kw 

31 self.tddft = None 

32 

33 # convert iter_broadening to Ha 

34 if "iter_broadening" in self.lrtddft_params: 

35 self.lrtddft_params["iter_broadening"] /= un.Ha 

36 

37 if self.initialize: 

38 self.tddft = tddft_iter(**self.lrtddft_params) 

39 

40 def get_ground_state(self, atoms, **kw): 

41 """ 

42 Run siesta calculations in order to get ground state properties. 

43 Makes sure that the proper parameters are unsed in order to be able 

44 to run pynao afterward, i.e., 

45 

46 COOP.Write = True 

47 WriteDenchar = True 

48 XML.Write = True 

49 """ 

50 from ase.calculators.siesta import Siesta 

51 

52 if "fdf_arguments" not in kw.keys(): 

53 kw["fdf_arguments"] = {"COOP.Write": True, 

54 "WriteDenchar": True, 

55 "XML.Write": True} 

56 else: 

57 for param in ["COOP.Write", "WriteDenchar", "XML.Write"]: 

58 kw["fdf_arguments"][param] = True 

59 

60 siesta = Siesta(**kw) 

61 atoms.calc = siesta 

62 atoms.get_potential_energy() 

63 

64 def get_polarizability(self, omega, Eext=np.array([1.0, 1.0, 1.0]), inter=True): 

65 """ 

66 Calculate the polarizability of a molecule via linear response TDDFT 

67 calculation. 

68 

69 Parameters 

70 ---------- 

71 omega: float or array like 

72 frequency range for which the polarizability should be computed, in eV 

73 

74 Returns 

75 ------- 

76 polarizability: array like (complex) 

77 array of dimension (3, 3, nff) with nff the number of frequency, 

78 the first and second dimension are the matrix elements of the 

79 polarizability in atomic units:: 

80 

81 P_xx, P_xy, P_xz, Pyx, ....... 

82 

83 Example 

84 ------- 

85 

86 from ase.calculators.siesta.siesta_lrtddft import siestaLRTDDFT 

87 from ase.build import molecule 

88 import numpy as np 

89 import matplotlib.pyplot as plt 

90 

91 # Define the systems 

92 CH4 = molecule('CH4') 

93 

94 lr = siestaLRTDDFT(label="siesta", jcutoff=7, iter_broadening=0.15, 

95 xc_code='LDA,PZ', tol_loc=1e-6, tol_biloc=1e-7) 

96 

97 # run DFT calculation with Siesta 

98 lr.get_ground_state(CH4) 

99 

100 # run TDDFT calculation with PyNAO 

101 freq=np.arange(0.0, 25.0, 0.05) 

102 pmat = lr.get_polarizability(freq)  

103 """ 

104 from pynao import tddft_iter 

105 

106 if not self.initialize: 

107 self.tddft = tddft_iter(**self.lrtddft_params) 

108 

109 if isinstance(omega, float): 

110 freq = np.array([omega]) 

111 elif isinstance(omega, list): 

112 freq = np.array([omega]) 

113 elif isinstance(omega, np.ndarray): 

114 freq = omega 

115 else: 

116 raise ValueError("omega soulf") 

117 

118 freq_cmplx = freq/un.Ha + 1j * self.tddft.eps 

119 if inter: 

120 pmat = -self.tddft.comp_polariz_inter_Edir(freq_cmplx, Eext=Eext) 

121 self.dn = self.tddft.dn 

122 else: 

123 pmat = -self.tddft.comp_polariz_nonin_Edir(freq_cmplx, Eext=Eext) 

124 self.dn = self.tddft.dn0 

125 

126 return pmat 

127 

128 

129class RamanCalculatorInterface(SiestaLRTDDFT): 

130 """Raman interface for Siesta calculator. 

131 When using the Raman calculator, please cite 

132 

133 M. Walter and M. Moseler, Ab Initio Wavelength-Dependent Raman Spectra: 

134 Placzek Approximation and Beyond, J. Chem. Theory Comput. 2020, 16, 1, 576–586 

135 """ 

136 def __init__(self, omega=0.0, **kw): 

137 """ 

138 Parameters 

139 ---------- 

140 omega: float 

141 frequency at which the Raman intensity should be computed, in eV 

142 

143 kw: dictionary 

144 The parameter for the siesta_lrtddft object 

145 """ 

146 

147 self.omega = omega 

148 super().__init__(**kw) 

149 

150 def __call__(self, *args, **kwargs): 

151 """Shorthand for calculate""" 

152 return self.calculate(*args, **kwargs) 

153 

154 def calculate(self, atoms): 

155 """ 

156 Calculate the polarizability for frequency omega 

157 

158 Parameters 

159 ---------- 

160 atoms: atoms class 

161 The atoms definition of the system. Not used but required by Raman 

162 calculator 

163 """ 

164 pmat = self.get_polarizability(self.omega, Eext=np.array([1.0, 1.0, 1.0])) 

165 

166 # Specific for raman calls, it expects just the tensor for a single 

167 # frequency and need only the real part 

168 

169 # For static raman, imaginary part is zero?? 

170 # Answer from Michael Walter: Yes, in the case of finite systems you may 

171 # choose the wavefunctions to be real valued. Then also the density 

172 # response function and hence the polarizability are real. 

173 

174 # Convert from atomic units to e**2 Ang**2/eV 

175 return pmat[:, :, 0].real * (un.Bohr**2) / un.Ha 

176 

177 

178def pol2cross_sec(p, omg): 

179 """ 

180 Convert the polarizability in au to cross section in nm**2 

181 

182 Input parameters: 

183 ----------------- 

184 p (np array): polarizability from mbpt_lcao calc 

185 omg (np.array): frequency range in eV 

186 

187 Output parameters: 

188 ------------------ 

189 sigma (np array): cross section in nm**2 

190 """ 

191 

192 c = 1 / un.alpha # speed of the light in au 

193 omg /= un.Ha # to convert from eV to Hartree 

194 sigma = 4 * np.pi * omg * p / (c) # bohr**2 

195 return sigma * (0.1 * un.Bohr)**2 # nm**2