Coverage for /builds/kinetik161/ase/ase/calculators/gaussian.py: 40.74%
81 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 copy
2import os
3from collections.abc import Iterable
4from typing import Dict, Optional
6from ase.calculators.calculator import FileIOCalculator
7from ase.io import read, write
10class GaussianDynamics:
11 calctype = 'optimizer'
12 delete = ['force']
13 keyword: Optional[str] = None
14 special_keywords: Dict[str, str] = {}
16 def __init__(self, atoms, calc=None):
17 self.atoms = atoms
18 if calc is not None:
19 self.calc = calc
20 else:
21 if self.atoms.calc is None:
22 raise ValueError("{} requires a valid Gaussian calculator "
23 "object!".format(self.__class__.__name__))
25 self.calc = self.atoms.calc
27 def todict(self):
28 return {'type': self.calctype,
29 'optimizer': self.__class__.__name__}
31 def delete_keywords(self, kwargs):
32 """removes list of keywords (delete) from kwargs"""
33 for d in self.delete:
34 kwargs.pop(d, None)
36 def set_keywords(self, kwargs):
37 args = kwargs.pop(self.keyword, [])
38 if isinstance(args, str):
39 args = [args]
40 elif isinstance(args, Iterable):
41 args = list(args)
43 for key, template in self.special_keywords.items():
44 if key in kwargs:
45 val = kwargs.pop(key)
46 args.append(template.format(val))
48 kwargs[self.keyword] = args
50 def run(self, **kwargs):
51 calc_old = self.atoms.calc
52 params_old = copy.deepcopy(self.calc.parameters)
54 self.delete_keywords(kwargs)
55 self.delete_keywords(self.calc.parameters)
56 self.set_keywords(kwargs)
58 self.calc.set(**kwargs)
59 self.atoms.calc = self.calc
61 try:
62 self.atoms.get_potential_energy()
63 except OSError:
64 converged = False
65 else:
66 converged = True
68 atoms = read(self.calc.label + '.log')
69 self.atoms.cell = atoms.cell
70 self.atoms.positions = atoms.positions
72 self.calc.parameters = params_old
73 self.calc.reset()
74 if calc_old is not None:
75 self.atoms.calc = calc_old
77 return converged
80class GaussianOptimizer(GaussianDynamics):
81 keyword = 'opt'
82 special_keywords = {
83 'fmax': '{}',
84 'steps': 'maxcycle={}',
85 }
88class GaussianIRC(GaussianDynamics):
89 keyword = 'irc'
90 special_keywords = {
91 'direction': '{}',
92 'steps': 'maxpoints={}',
93 }
96class Gaussian(FileIOCalculator):
97 implemented_properties = ['energy', 'forces', 'dipole']
98 discard_results_on_any_change = True
100 def __init__(self, *args, label='Gaussian', **kwargs):
101 super().__init__(*args, label=label, **kwargs)
103 def write_input(self, atoms, properties=None, system_changes=None):
104 super().write_input(atoms, properties, system_changes)
105 write(self.label + '.com', atoms, properties=properties,
106 format='gaussian-in', parallel=False, **self.parameters)
108 def read_results(self):
109 output = read(self.label + '.log', format='gaussian-out')
110 self.calc = output.calc
111 self.results = output.calc.results
113 # Method(s) defined in the old calculator, added here for
114 # backwards compatibility
115 def clean(self):
116 for suffix in ['.com', '.chk', '.log']:
117 try:
118 os.remove(os.path.join(self.directory, self.label + suffix))
119 except OSError:
120 pass
122 def get_version(self):
123 raise NotImplementedError # not sure how to do this yet