Coverage for /builds/kinetik161/ase/ase/calculators/acemolecule.py: 73.47%
98 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-12-10 11:04 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-12-10 11:04 +0000
1import os
2from copy import deepcopy
4from ase.calculators.calculator import FileIOCalculator, ReadError
5from ase.io import read
8class ACE(FileIOCalculator):
9 '''
10 ACE-Molecule logfile reader
11 It has default parameters of each input section
12 And parameters' type = list of dictionaries
13 '''
14 name = 'ace'
15 implemented_properties = ['energy', 'forces', 'excitation-energy']
16 basic_list = [{
17 'Type': 'Scaling', 'Scaling': '0.35', 'Basis': 'Sinc',
18 'Grid': 'Sphere',
19 'KineticMatrix': 'Finite_Difference', 'DerivativesOrder': '7',
20 'GeometryFilename': None, 'NumElectrons': None}
21 ]
22 scf_list = [{
23 'ExchangeCorrelation': {'XFunctional': 'GGA_X_PBE',
24 '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',
32 'CFunctional': 'GGA_C_PBE'},
33 }]
35 order_list = ['BasicInformation', 'Guess', 'Scf']
36 guess_list = [{}] # type: ignore[var-annotated]
37 default_parameters = {'BasicInformation': basic_list, 'Guess': guess_list,
38 'Scf': scf_list, 'Force': force_list,
39 'TDDFT': tddft_list, 'order': order_list}
41 def __init__(
42 self, restart=None,
43 ignore_bad_restart_file=FileIOCalculator._deprecated,
44 label='ace', atoms=None, command=None,
45 basisfile=None, **kwargs):
46 FileIOCalculator.__init__(self, restart, ignore_bad_restart_file,
47 label, atoms, command=command, **kwargs)
49 def set(self, **kwargs):
50 '''Update parameters self.parameter member variable.
51 1. Add default values for repeated parameter sections with
52 self.default_parameters using order.
53 2. Also add empty dictionary as an indicator for section existence
54 if no relevant default_parameters exist.
55 3. Update parameters from arguments.
57 Returns
58 =======
59 Updated parameter
60 '''
61 new_parameters = deepcopy(self.parameters)
63 changed_parameters = FileIOCalculator.set(self, **kwargs)
65 # Add default values for repeated parameter sections with
66 # self.default_parameters using order. Also add empty
67 # dictionary as an indicator for section existence if no
68 # relevant default_parameters exist.
69 if 'order' in kwargs:
70 new_parameters['order'] = kwargs['order']
71 section_sets = set(kwargs['order'])
72 for section_name in section_sets:
73 repeat = kwargs['order'].count(section_name)
74 if section_name in self.default_parameters.keys():
75 for i in range(repeat - 1):
76 new_parameters[section_name] += deepcopy(
77 self.default_parameters[section_name])
78 else:
79 new_parameters[section_name] = []
80 for i in range(repeat):
81 new_parameters[section_name].append({})
83 # Update parameters
84 for section in new_parameters['order']:
85 if section in kwargs:
86 if isinstance(kwargs[section], dict):
87 kwargs[section] = [kwargs[section]]
89 i = 0
90 for section_param in kwargs[section]:
91 new_parameters[section][i] = update_parameter(
92 new_parameters[section][i], section_param)
93 i += 1
94 self.parameters = new_parameters
95 return changed_parameters
97 def read(self, label):
98 FileIOCalculator.read(self, label)
99 filename = self.label + ".log"
101 with open(filename) as fd:
102 lines = fd.readlines()
103 if 'WARNING' in lines:
104 raise ReadError(
105 f"Not convergy energy in log file {filename}.")
106 if '! total energy' not in lines:
107 raise ReadError(f"Wrong ACE-Molecule log file {filename}.")
109 if not os.path.isfile(filename):
110 raise ReadError(
111 f"Wrong ACE-Molecule input file {filename}.")
113 self.read_results()
115 def write_input(self, atoms, properties=None, system_changes=None):
116 '''Initializes input parameters and xyz files. If force calculation is
117 requested, add Force section to parameters if not exists.
119 Parameters
120 ==========
121 atoms: ASE atoms object.
122 properties: List of properties to be calculated. Should be element
123 of self.implemented_properties.
124 system_chages: Ignored.
126 '''
127 FileIOCalculator.write_input(self, atoms, properties, system_changes)
128 with open(self.label + '.inp', 'w') as inputfile:
129 xyz_name = f"{self.label}.xyz"
130 atoms.write(xyz_name)
132 run_parameters = self.prepare_input(xyz_name, properties)
133 self.write_acemolecule_input(inputfile, run_parameters)
135 def prepare_input(self, geometry_filename, properties):
136 '''Initialize parameters dictionary based on geometry filename and
137 calculated properties.
139 Parameters
140 ==========
141 geometry_filename: Geometry (XYZ format) file path.
142 properties: Properties to be calculated.
144 Returns
145 =======
146 Updated version of self.parameters; geometry file and
147 optionally Force section are updated.
149 '''
150 copied_parameters = deepcopy(self.parameters)
151 if (properties is not None and "forces" in properties
152 and 'Force' not in copied_parameters['order']):
153 copied_parameters['order'].append('Force')
154 copied_parameters["BasicInformation"][0]["GeometryFilename"] = \
155 f"{self.label}.xyz"
156 copied_parameters["BasicInformation"][0]["GeometryFormat"] = "xyz"
157 return copied_parameters
159 def read_results(self):
160 '''Read calculation results, speficied by 'quantities' variable, from
161 the log file.
163 quantities
164 =======
165 energy : obtaing single point energy(eV) from log file
166 forces : obtaing force of each atom form log file
167 excitation-energy : it able to calculate TDDFT.
168 Return value is None. Result is not used.
169 atoms : ASE atoms object
171 '''
172 filename = self.label + '.log'
173 self.results = read(filename, format='acemolecule-out')
175 def write_acemolecule_section(self, fpt, section, depth=0):
176 '''Write parameters in each section of input
178 Parameters
179 ==========
180 fpt: ACE-Moleucle input file object. Should be write mode.
181 section: Dictionary of a parameter section.
182 depth: Nested input depth.
183 '''
184 for section, section_param in section.items():
185 if isinstance(section_param, (str, int, float)):
186 fpt.write(
187 ' ' *
188 depth +
189 str(section) +
190 " " +
191 str(section_param) +
192 "\n")
193 else:
194 if isinstance(section_param, dict):
195 fpt.write(' ' * depth + "%% " + str(section) + "\n")
196 self.write_acemolecule_section(
197 fpt, section_param, depth + 1)
198 fpt.write(' ' * depth + "%% End\n")
199 if isinstance(section_param, list):
200 for val in section_param:
201 fpt.write(
202 ' ' *
203 depth +
204 str(section) +
205 " " +
206 str(val) +
207 "\n")
209 def write_acemolecule_input(self, fpt, param, depth=0):
210 '''Write ACE-Molecule input
212 ACE-Molecule input examples (not minimal)
213 %% BasicInformation
214 Type Scaling
215 Scaling 0.4
216 Basis Sinc
217 Cell 10.0
218 Grid Sphere
219 GeometryFormat xyz
220 SpinMultiplicity 3.0
221 Polarize 1
222 Centered 0
223 %% Pseudopotential
224 Pseudopotential 1
225 UsingDoubleGrid 0
226 FilterType Sinc
227 Format upf
228 PSFilePath /PATH/TO/UPF
229 PSFileSuffix .pbe-theos.UPF
230 %% End
231 GeometryFilename xyz/C.xyz
232 %% End
233 %% Guess
234 InitialGuess 3
235 InitialFilenames 001.cube
236 InitialFilenames 002.cube
237 %% End
238 %% Scf
239 IterateMaxCycle 150
240 ConvergenceType Energy
241 ConvergenceTolerance 0.00001
242 EnergyDecomposition 1
243 ComputeInitialEnergy 1
244 %% Diagonalize
245 Tolerance 0.000001
246 %% End
247 %% ExchangeCorrelation
248 XFunctional GGA_X_PBE
249 CFunctional GGA_C_PBE
250 %% End
251 %% Mixing
252 MixingMethod 1
253 MixingType Density
254 MixingParameter 0.5
255 PulayMixingParameter 0.1
256 %% End
257 %% End
259 Parameters
260 ==========
261 fpt: File object, should be write mode.
262 param: Dictionary of parameters. Also should contain
263 special 'order' section_name for parameter section ordering.
264 depth: Nested input depth.
266 Notes
267 =====
268 - Order of parameter section
269 (denoted using %% -- %% BasicInformation, %% Guess, etc.)
270 is important, because it determines calculation order.
271 For example, if Guess section comes after Scf section,
272 calculation will not run because Scf will tries to run
273 without initial Hamiltonian.
274 - Order of each parameter section-section_name pair is
275 not important unless their keys are the same.
276 - Indentation unimportant and capital letters are important.
277 '''
278 prefix = " " * depth
280 for i in range(len(param['order'])):
281 fpt.write(prefix + "%% " + param['order'][i] + "\n")
282 section_list = param[param['order'][i]]
283 if len(section_list) > 0:
284 section = section_list.pop(0)
285 self.write_acemolecule_section(fpt, section, 1)
286 fpt.write("%% End\n")
287 return
290def update_parameter(oldpar, newpar):
291 '''Update each section of parameter (oldpar) using newpar keys and values.
292 If section of newpar exist in oldpar,
293 - Replace the section_name with newpar's section_name if
294 oldvar section_name type is not dict.
295 - Append the section_name with newpar's section_name
296 if oldvar section_name type is list.
297 - If oldpar section_name type is dict, it is subsection.
298 So call update_parameter again.
299 otherwise, add the parameter section and section_name from newpar.
301 Parameters
302 ==========
303 oldpar: dictionary of original parameters to be updated.
304 newpar: dictionary containing parameter section and values to update.
306 Return
307 ======
308 Updated parameter dictionary.
309 '''
310 for section, section_param in newpar.items():
311 if section in oldpar:
312 if isinstance(section_param, dict):
313 oldpar[section] = update_parameter(
314 oldpar[section], section_param)
315 else:
316 oldpar[section] = section_param
317 else:
318 oldpar[section] = section_param
319 return oldpar