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 

2 

3from ase.calculators.calculator import Calculator, all_properties 

4from ase.calculators.calculator import PropertyNotImplementedError 

5 

6 

7class SinglePointCalculator(Calculator): 

8 """Special calculator for a single configuration. 

9 

10 Used to remember the energy, force and stress for a given 

11 configuration. If the positions, atomic numbers, unit cell, or 

12 boundary conditions are changed, then asking for 

13 energy/forces/stress will raise an exception.""" 

14 

15 name = 'unknown' 

16 

17 def __init__(self, atoms, **results): 

18 """Save energy, forces, stress, ... for the current configuration.""" 

19 Calculator.__init__(self) 

20 self.results = {} 

21 for property, value in results.items(): 

22 assert property in all_properties 

23 if value is None: 

24 continue 

25 if property in ['energy', 'magmom', 'free_energy']: 

26 self.results[property] = value 

27 else: 

28 self.results[property] = np.array(value, float) 

29 self.atoms = atoms.copy() 

30 

31 def __str__(self): 

32 tokens = [] 

33 for key, val in sorted(self.results.items()): 

34 if np.isscalar(val): 

35 txt = '{}={}'.format(key, val) 

36 else: 

37 txt = '{}=...'.format(key) 

38 tokens.append(txt) 

39 return '{}({})'.format(self.__class__.__name__, ', '.join(tokens)) 

40 

41 def get_property(self, name, atoms=None, allow_calculation=True): 

42 if atoms is None: 

43 atoms = self.atoms 

44 if name not in self.results or self.check_state(atoms): 

45 if allow_calculation: 

46 raise PropertyNotImplementedError( 

47 'The property "{0}" is not available.'.format(name)) 

48 return None 

49 

50 result = self.results[name] 

51 if isinstance(result, np.ndarray): 

52 result = result.copy() 

53 return result 

54 

55 

56class SinglePointKPoint: 

57 def __init__(self, weight, s, k, eps_n=[], f_n=[]): 

58 self.weight = weight 

59 self.s = s # spin index 

60 self.k = k # k-point index 

61 self.eps_n = eps_n 

62 self.f_n = f_n 

63 

64 

65def arrays_to_kpoints(eigenvalues, occupations, weights): 

66 """Helper function for building SinglePointKPoints. 

67 

68 Convert eigenvalue, occupation, and weight arrays to list of 

69 SinglePointKPoint objects.""" 

70 nspins, nkpts, nbands = eigenvalues.shape 

71 assert eigenvalues.shape == occupations.shape 

72 assert len(weights) == nkpts 

73 kpts = [] 

74 for s in range(nspins): 

75 for k in range(nkpts): 

76 kpt = SinglePointKPoint( 

77 weight=weights[k], s=s, k=k, 

78 eps_n=eigenvalues[s, k], f_n=occupations[s, k]) 

79 kpts.append(kpt) 

80 return kpts 

81 

82 

83class SinglePointDFTCalculator(SinglePointCalculator): 

84 def __init__(self, atoms, 

85 efermi=None, bzkpts=None, ibzkpts=None, bz2ibz=None, 

86 **results): 

87 self.bz_kpts = bzkpts 

88 self.ibz_kpts = ibzkpts 

89 self.bz2ibz = bz2ibz 

90 self.eFermi = efermi 

91 

92 SinglePointCalculator.__init__(self, atoms, **results) 

93 self.kpts = None 

94 

95 def get_fermi_level(self): 

96 """Return the Fermi-level(s).""" 

97 return self.eFermi 

98 

99 def get_bz_to_ibz_map(self): 

100 return self.bz2ibz 

101 

102 def get_bz_k_points(self): 

103 """Return the k-points.""" 

104 return self.bz_kpts 

105 

106 def get_number_of_spins(self): 

107 """Return the number of spins in the calculation. 

108 

109 Spin-paired calculations: 1, spin-polarized calculation: 2.""" 

110 if self.kpts is not None: 

111 nspin = set() 

112 for kpt in self.kpts: 

113 nspin.add(kpt.s) 

114 return len(nspin) 

115 return None 

116 

117 def get_spin_polarized(self): 

118 """Is it a spin-polarized calculation?""" 

119 nos = self.get_number_of_spins() 

120 if nos is not None: 

121 return nos == 2 

122 return None 

123 

124 def get_ibz_k_points(self): 

125 """Return k-points in the irreducible part of the Brillouin zone.""" 

126 return self.ibz_kpts 

127 

128 def get_kpt(self, kpt=0, spin=0): 

129 if self.kpts is not None: 

130 counter = 0 

131 for kpoint in self.kpts: 

132 if kpoint.s == spin: 

133 if kpt == counter: 

134 return kpoint 

135 counter += 1 

136 return None 

137 

138 def get_k_point_weights(self): 

139 """ Retunrs the weights of the k points """ 

140 if self.kpts is not None: 

141 weights = [] 

142 for kpoint in self.kpts: 

143 if kpoint.s == 0: 

144 weights.append(kpoint.weight) 

145 return np.array(weights) 

146 return None 

147 

148 def get_occupation_numbers(self, kpt=0, spin=0): 

149 """Return occupation number array.""" 

150 kpoint = self.get_kpt(kpt, spin) 

151 if kpoint is not None: 

152 return kpoint.f_n 

153 return None 

154 

155 def get_eigenvalues(self, kpt=0, spin=0): 

156 """Return eigenvalue array.""" 

157 kpoint = self.get_kpt(kpt, spin) 

158 if kpoint is not None: 

159 return kpoint.eps_n 

160 return None 

161 

162 def get_homo_lumo(self): 

163 """Return HOMO and LUMO energies.""" 

164 if self.kpts is None: 

165 raise RuntimeError('No kpts') 

166 eHs = [] 

167 eLs = [] 

168 for kpt in self.kpts: 

169 eH, eL = self.get_homo_lumo_by_spin(kpt.s) 

170 eHs.append(eH) 

171 eLs.append(eL) 

172 return np.array(eHs).max(), np.array(eLs).min() 

173 

174 def get_homo_lumo_by_spin(self, spin=0): 

175 """Return HOMO and LUMO energies for a given spin.""" 

176 if self.kpts is None: 

177 raise RuntimeError('No kpts') 

178 for kpt in self.kpts: 

179 if kpt.s == spin: 

180 break 

181 else: 

182 raise RuntimeError('No k-point with spin {0}'.format(spin)) 

183 if self.eFermi is None: 

184 raise RuntimeError('Fermi level is not available') 

185 eH = -1.e32 

186 eL = 1.e32 

187 for kpt in self.kpts: 

188 if kpt.s == spin: 

189 for e in kpt.eps_n: 

190 if e <= self.eFermi: 

191 eH = max(eH, e) 

192 else: 

193 eL = min(eL, e) 

194 return eH, eL