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
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 }]
28 force_list = [{'ForceDerivative': 'Potential'}]
29 tddft_list = [{
30 'SortOrbital': 'Order', 'MaximumOrder': '10',
31 'ExchangeCorrelation': {'XFunctional': 'GGA_X_PBE', 'CFunctional': 'GGA_C_PBE'},
32 }]
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}
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)
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.
53 Returns
54 =======
55 Updated parameter
56 '''
57 new_parameters = deepcopy(self.parameters)
59 changed_parameters = FileIOCalculator.set(self, **kwargs)
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({})
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]]
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
89 def read(self, label):
90 FileIOCalculator.read(self, label)
91 filename = self.label + ".log"
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))
100 if not os.path.isfile(filename):
101 raise ReadError("Wrong ACE-Molecule input file {}.".format(filename))
103 self.read_results()
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.
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.
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)
120 run_parameters = self.prepare_input(xyz_name, properties)
121 self.write_acemolecule_input(inputfile, run_parameters)
123 def prepare_input(self, geometry_filename, properties):
124 '''Initialize parameters dictionary based on geometry filename and calculated properties.
126 Parameters
127 ==========
128 geometry_filename: Geometry (XYZ format) file path.
129 properties: Properties to be calculated.
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
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')
157 def write_acemolecule_section(self, fpt, section, depth=0):
158 '''Write parameters in each section of input
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")
178 def write_acemolecule_input(self, fpt, param, depth=0):
179 '''Write ACE-Molecule input
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
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.
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
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
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.
261 Parameters
262 ==========
263 oldpar: dictionary of original parameters to be updated.
264 newpar: dictionary containing parameter section and values to update.
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