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

1# type: ignore 

2import os 

3from copy import deepcopy 

4from ase.io import read 

5from ase.calculators.calculator import ReadError 

6from ase.calculators.calculator import FileIOCalculator 

7 

8 

9class ACE(FileIOCalculator): 

10 ''' 

11 ACE-Molecule logfile reader 

12 It has default parameters of each input section 

13 And parameters' type = list of dictionaries 

14 ''' 

15 name = 'ace' 

16 implemented_properties = ['energy', 'forces', 'excitation-energy'] 

17 basic_list = [{ 

18 'Type': 'Scaling', 'Scaling': '0.35', 'Basis': 'Sinc', 

19 'Grid': 'Sphere', 

20 'KineticMatrix': 'Finite_Difference', 'DerivativesOrder': '7', 

21 'GeometryFilename': None, 'NumElectrons': None} 

22 ] 

23 scf_list = [{ 

24 'ExchangeCorrelation': {'XFunctional': 'GGA_X_PBE', 'CFunctional': 'GGA_C_PBE'}, 

25 'NumberOfEigenvalues': None, 

26 }] 

27 

28 force_list = [{'ForceDerivative': 'Potential'}] 

29 tddft_list = [{ 

30 'SortOrbital': 'Order', 'MaximumOrder': '10', 

31 'ExchangeCorrelation': {'XFunctional': 'GGA_X_PBE', 'CFunctional': 'GGA_C_PBE'}, 

32 }] 

33 

34 order_list = ['BasicInformation', 'Guess', 'Scf'] 

35 guess_list = [{}] 

36 default_parameters = {'BasicInformation': basic_list, 'Guess': guess_list, 

37 'Scf': scf_list, 'Force': force_list, 'TDDFT': tddft_list, 'order': order_list} 

38 

39 def __init__( 

40 self, restart=None, 

41 ignore_bad_restart_file=FileIOCalculator._deprecated, 

42 label='ace', atoms=None, command=None, 

43 basisfile=None, **kwargs): 

44 FileIOCalculator.__init__(self, restart, ignore_bad_restart_file, 

45 label, atoms, command=command, **kwargs) 

46 

47 def set(self, **kwargs): 

48 '''Update parameters self.parameter member variable. 

49 1. Add default values for repeated parameter sections with self.default_parameters using order. 

50 2. Also add empty dictionary as an indicator for section existence if no relevant default_parameters exist. 

51 3. Update parameters from arguments. 

52 

53 Returns 

54 ======= 

55 Updated parameter 

56 ''' 

57 new_parameters = deepcopy(self.parameters) 

58 

59 changed_parameters = FileIOCalculator.set(self, **kwargs) 

60 

61 # Add default values for repeated parameter sections with self.default_parameters using order. 

62 # Also add empty dictionary as an indicator for section existence if no relevant default_parameters exist. 

63 if 'order' in kwargs: 

64 new_parameters['order'] = kwargs['order'] 

65 section_sets = set(kwargs['order']) 

66 for section_name in section_sets: 

67 repeat = kwargs['order'].count(section_name) 

68 if section_name in self.default_parameters.keys(): 

69 for i in range(repeat-1): 

70 new_parameters[section_name] += deepcopy(self.default_parameters[section_name]) 

71 else: 

72 new_parameters[section_name] = [] 

73 for i in range(repeat): 

74 new_parameters[section_name].append({}) 

75 

76 # Update parameters 

77 for section in new_parameters['order']: 

78 if section in kwargs.keys(): 

79 if isinstance(kwargs[section], dict): 

80 kwargs[section] = [kwargs[section]] 

81 

82 i = 0 

83 for section_param in kwargs[section]: 

84 new_parameters[section][i] = update_parameter(new_parameters[section][i], section_param) 

85 i += 1 

86 self.parameters = new_parameters 

87 return changed_parameters 

88 

89 def read(self, label): 

90 FileIOCalculator.read(self, label) 

91 filename = self.label + ".log" 

92 

93 with open(filename, 'r') as fd: 

94 lines = fd.readlines() 

95 if 'WARNING' in lines: 

96 raise ReadError("Not convergy energy in log file {}.".format(filename)) 

97 if '! total energy' not in lines: 

98 raise ReadError("Wrong ACE-Molecule log file {}.".format(filename)) 

99 

100 if not os.path.isfile(filename): 

101 raise ReadError("Wrong ACE-Molecule input file {}.".format(filename)) 

102 

103 self.read_results() 

104 

105 def write_input(self, atoms, properties=None, system_changes=None): 

106 '''Initializes input parameters and xyz files. If force calculation is requested, add Force section to parameters if not exists. 

107 

108 Parameters 

109 ========== 

110 atoms: ASE atoms object. 

111 properties: List of properties to be calculated. Should be element of self.implemented_properties. 

112 system_chages: Ignored. 

113 

114 ''' 

115 FileIOCalculator.write_input(self, atoms, properties, system_changes) 

116 with open(self.label + '.inp', 'w') as inputfile: 

117 xyz_name = "{}.xyz".format(self.label) 

118 atoms.write(xyz_name) 

119 

120 run_parameters = self.prepare_input(xyz_name, properties) 

121 self.write_acemolecule_input(inputfile, run_parameters) 

122 

123 def prepare_input(self, geometry_filename, properties): 

124 '''Initialize parameters dictionary based on geometry filename and calculated properties. 

125 

126 Parameters 

127 ========== 

128 geometry_filename: Geometry (XYZ format) file path. 

129 properties: Properties to be calculated. 

130 

131 Returns 

132 ======= 

133 Updated version of self.parameters; geometry file and optionally Force section are updated. 

134 ''' 

135 copied_parameters = deepcopy(self.parameters) 

136 if properties is not None and "forces" in properties and 'Force' not in copied_parameters['order']: 

137 copied_parameters['order'].append('Force') 

138 copied_parameters["BasicInformation"][0]["GeometryFilename"] = "{}.xyz".format(self.label) 

139 copied_parameters["BasicInformation"][0]["GeometryFormat"] = "xyz" 

140 return copied_parameters 

141 

142 def read_results(self): 

143 '''Read calculation results, speficied by 'quantities' variable, from the log file. 

144 quantities 

145 ======= 

146 energy : obtaing single point energy(eV) from log file 

147 forces : obtaing force of each atom form log file 

148 excitation-energy : it able to calculate TDDFT. Return value is None. Result is not used. 

149 atoms : ASE atoms object 

150 ''' 

151 filename = self.label + '.log' 

152 #quantities = ['energy', 'forces', 'atoms', 'excitation-energy'] 

153 #for section_name in quantities: 

154 #self.results = read_acemolecule_out(filename) 

155 self.results = read(filename, format='acemolecule-out') 

156 

157 def write_acemolecule_section(self, fpt, section, depth=0): 

158 '''Write parameters in each section of input 

159 

160 Parameters 

161 ========== 

162 fpt: ACE-Moleucle input file object. Should be write mode. 

163 section: Dictionary of a parameter section. 

164 depth: Nested input depth. 

165 ''' 

166 for section, section_param in section.items(): 

167 if isinstance(section_param, str) or isinstance(section_param, int) or isinstance(section_param, float): 

168 fpt.write(' ' * depth + str(section) + " " + str(section_param) + "\n") 

169 else: 

170 if isinstance(section_param, dict): 

171 fpt.write(' ' * depth + "%% " + str(section) + "\n") 

172 self.write_acemolecule_section(fpt, section_param, depth + 1) 

173 fpt.write(' ' * depth + "%% End\n") 

174 if isinstance(section_param, list): 

175 for val in section_param: 

176 fpt.write(' ' * depth + str(section) + " " + str(val) + "\n") 

177 

178 def write_acemolecule_input(self, fpt, param, depth=0): 

179 '''Write ACE-Molecule input 

180 

181 ACE-Molecule input examples (not minimal) 

182 %% BasicInformation 

183 Type Scaling 

184 Scaling 0.4 

185 Basis Sinc 

186 Cell 10.0 

187 Grid Sphere 

188 GeometryFormat xyz 

189 SpinMultiplicity 3.0 

190 Polarize 1 

191 Centered 0 

192 %% Pseudopotential 

193 Pseudopotential 1 

194 UsingDoubleGrid 0 

195 FilterType Sinc 

196 Format upf 

197 PSFilePath /PATH/TO/UPF 

198 PSFileSuffix .pbe-theos.UPF 

199 %% End 

200 GeometryFilename xyz/C.xyz 

201 %% End 

202 %% Guess 

203 InitialGuess 3 

204 InitialFilenames 001.cube 

205 InitialFilenames 002.cube 

206 %% End 

207 %% Scf 

208 IterateMaxCycle 150 

209 ConvergenceType Energy 

210 ConvergenceTolerance 0.00001 

211 EnergyDecomposition 1 

212 ComputeInitialEnergy 1 

213 %% Diagonalize 

214 Tolerance 0.000001 

215 %% End 

216 %% ExchangeCorrelation 

217 XFunctional GGA_X_PBE 

218 CFunctional GGA_C_PBE 

219 %% End 

220 %% Mixing 

221 MixingMethod 1 

222 MixingType Density 

223 MixingParameter 0.5 

224 PulayMixingParameter 0.1 

225 %% End 

226 %% End 

227 

228 Parameters 

229 ========== 

230 fpt: File object, should be write mode. 

231 param: Dictionary of parameters. Also should contain special 'order' section_name for parameter section ordering. 

232 depth: Nested input depth. 

233 

234 Notes 

235 ===== 

236 - Order of parameter section (denoted using %% -- %% BasicInformation, %% Guess, etc.) is important, because it determines calculation order. 

237 For example, if Guess section comes after Scf section, calculation will not run because Scf will tries to run without initial Hamiltonian. 

238 - Order of each parameter section-section_name pair is not important unless their keys are the same. 

239 - Indentation unimportant and capital letters are important. 

240 ''' 

241 prefix = " " * depth 

242 

243 for i in range(len(param['order'])): 

244 fpt.write(prefix + "%% " + param['order'][i] + "\n") 

245 section_list = param[param['order'][i]] 

246 if len(section_list) > 0: 

247 section = section_list.pop(0) 

248 self.write_acemolecule_section(fpt, section, 1) 

249 fpt.write("%% End\n") 

250 return 

251 

252 

253def update_parameter(oldpar, newpar): 

254 '''Update each section of parameter (oldpar) using newpar keys and values. 

255 If section of newpar exist in oldpar, 

256 - Replace the section_name with newpar's section_name if oldvar section_name type is not dict. 

257 - Append the section_name with newpar's section_name if oldvar section_name type is list. 

258 - If oldpar section_name type is dict, it is subsection. So call update_parameter again. 

259 otherwise, add the parameter section and section_name from newpar. 

260 

261 Parameters 

262 ========== 

263 oldpar: dictionary of original parameters to be updated. 

264 newpar: dictionary containing parameter section and values to update. 

265 

266 Return 

267 ====== 

268 Updated parameter dictionary. 

269 ''' 

270 for section, section_param in newpar.items(): 

271 if section in oldpar: 

272 if isinstance(section_param, dict): 

273 oldpar[section] = update_parameter(oldpar[section], section_param) 

274 else: 

275 oldpar[section] = section_param 

276 else: 

277 oldpar[section] = section_param 

278 return oldpar