Coverage for /builds/ase/ase/ase/calculators/gaussian.py : 35.87%

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 os
2import copy
3from collections.abc import Iterable
4from shutil import which
5from typing import Dict, Optional
7from ase.io import read, write
8from ase.calculators.calculator import FileIOCalculator, EnvironmentError
11class GaussianDynamics:
12 calctype = 'optimizer'
13 delete = ['force']
14 keyword: Optional[str] = None
15 special_keywords: Dict[str, str] = dict()
17 def __init__(self, atoms, calc=None):
18 self.atoms = atoms
19 if calc is not None:
20 self.calc = calc
21 else:
22 if self.atoms.calc is None:
23 raise ValueError("{} requires a valid Gaussian calculator "
24 "object!".format(self.__class__.__name__))
26 self.calc = self.atoms.calc
28 def todict(self):
29 return {'type': self.calctype,
30 'optimizer': self.__class__.__name__}
32 def delete_keywords(self, kwargs):
33 """removes list of keywords (delete) from kwargs"""
34 for d in self.delete:
35 kwargs.pop(d, None)
37 def set_keywords(self, kwargs):
38 args = kwargs.pop(self.keyword, [])
39 if isinstance(args, str):
40 args = [args]
41 elif isinstance(args, Iterable):
42 args = list(args)
44 for key, template in self.special_keywords.items():
45 if key in kwargs:
46 val = kwargs.pop(key)
47 args.append(template.format(val))
49 kwargs[self.keyword] = args
51 def run(self, **kwargs):
52 calc_old = self.atoms.calc
53 params_old = copy.deepcopy(self.calc.parameters)
55 self.delete_keywords(kwargs)
56 self.delete_keywords(self.calc.parameters)
57 self.set_keywords(kwargs)
59 self.calc.set(**kwargs)
60 self.atoms.calc = self.calc
62 try:
63 self.atoms.get_potential_energy()
64 except OSError:
65 converged = False
66 else:
67 converged = True
69 atoms = read(self.calc.label + '.log')
70 self.atoms.cell = atoms.cell
71 self.atoms.positions = atoms.positions
73 self.calc.parameters = params_old
74 self.calc.reset()
75 if calc_old is not None:
76 self.atoms.calc = calc_old
78 return converged
81class GaussianOptimizer(GaussianDynamics):
82 keyword = 'opt'
83 special_keywords = {
84 'fmax': '{}',
85 'steps': 'maxcycle={}',
86 }
89class GaussianIRC(GaussianDynamics):
90 keyword = 'irc'
91 special_keywords = {
92 'direction': '{}',
93 'steps': 'maxpoints={}',
94 }
97class Gaussian(FileIOCalculator):
98 implemented_properties = ['energy', 'forces', 'dipole']
99 command = 'GAUSSIAN < PREFIX.com > PREFIX.log'
100 discard_results_on_any_change = True
102 def __init__(self, *args, label='Gaussian', **kwargs):
103 FileIOCalculator.__init__(self, *args, label=label, **kwargs)
105 def calculate(self, *args, **kwargs):
106 gaussians = ('g16', 'g09', 'g03')
107 if 'GAUSSIAN' in self.command:
108 for gau in gaussians:
109 if which(gau):
110 self.command = self.command.replace('GAUSSIAN', gau)
111 break
112 else:
113 raise EnvironmentError('Missing Gaussian executable {}'
114 .format(gaussians))
116 FileIOCalculator.calculate(self, *args, **kwargs)
118 def write_input(self, atoms, properties=None, system_changes=None):
119 FileIOCalculator.write_input(self, atoms, properties, system_changes)
120 write(self.label + '.com', atoms, properties=properties,
121 format='gaussian-in', parallel=False, **self.parameters)
123 def read_results(self):
124 output = read(self.label + '.log', format='gaussian-out')
125 self.calc = output.calc
126 self.results = output.calc.results
128 # Method(s) defined in the old calculator, added here for
129 # backwards compatibility
130 def clean(self):
131 for suffix in ['.com', '.chk', '.log']:
132 try:
133 os.remove(os.path.join(self.directory, self.label + suffix))
134 except OSError:
135 pass
137 def get_version(self):
138 raise NotImplementedError # not sure how to do this yet