/ase/calculators/gaussian.py

https://gitlab.com/asod/ase
Python | 138 lines | 105 code | 30 blank | 3 comment | 19 complexity | 0cc1ba2634572ba4d2dace18c8287ee7 MD5 | raw file
  1. import os
  2. import copy
  3. from collections.abc import Iterable
  4. from shutil import which
  5. from typing import Dict, Optional
  6. from ase.io import read, write
  7. from ase.calculators.calculator import FileIOCalculator, EnvironmentError
  8. class GaussianDynamics:
  9. calctype = 'optimizer'
  10. delete = ['force']
  11. keyword: Optional[str] = None
  12. special_keywords: Dict[str, str] = dict()
  13. def __init__(self, atoms, calc=None):
  14. self.atoms = atoms
  15. if calc is not None:
  16. self.calc = calc
  17. else:
  18. if self.atoms.calc is None:
  19. raise ValueError("{} requires a valid Gaussian calculator "
  20. "object!".format(self.__class__.__name__))
  21. self.calc = self.atoms.calc
  22. def todict(self):
  23. return {'type': self.calctype,
  24. 'optimizer': self.__class__.__name__}
  25. def delete_keywords(self, kwargs):
  26. """removes list of keywords (delete) from kwargs"""
  27. for d in self.delete:
  28. kwargs.pop(d, None)
  29. def set_keywords(self, kwargs):
  30. args = kwargs.pop(self.keyword, [])
  31. if isinstance(args, str):
  32. args = [args]
  33. elif isinstance(args, Iterable):
  34. args = list(args)
  35. for key, template in self.special_keywords.items():
  36. if key in kwargs:
  37. val = kwargs.pop(key)
  38. args.append(template.format(val))
  39. kwargs[self.keyword] = args
  40. def run(self, **kwargs):
  41. calc_old = self.atoms.calc
  42. params_old = copy.deepcopy(self.calc.parameters)
  43. self.delete_keywords(kwargs)
  44. self.delete_keywords(self.calc.parameters)
  45. self.set_keywords(kwargs)
  46. self.calc.set(**kwargs)
  47. self.atoms.calc = self.calc
  48. try:
  49. self.atoms.get_potential_energy()
  50. except OSError:
  51. converged = False
  52. else:
  53. converged = True
  54. atoms = read(self.calc.label + '.log')
  55. self.atoms.cell = atoms.cell
  56. self.atoms.positions = atoms.positions
  57. self.calc.parameters = params_old
  58. self.calc.reset()
  59. if calc_old is not None:
  60. self.atoms.calc = calc_old
  61. return converged
  62. class GaussianOptimizer(GaussianDynamics):
  63. keyword = 'opt'
  64. special_keywords = {
  65. 'fmax': '{}',
  66. 'steps': 'maxcycle={}',
  67. }
  68. class GaussianIRC(GaussianDynamics):
  69. keyword = 'irc'
  70. special_keywords = {
  71. 'direction': '{}',
  72. 'steps': 'maxpoints={}',
  73. }
  74. class Gaussian(FileIOCalculator):
  75. implemented_properties = ['energy', 'forces', 'dipole']
  76. command = 'GAUSSIAN < PREFIX.com > PREFIX.log'
  77. discard_results_on_any_change = True
  78. def __init__(self, *args, label='Gaussian', **kwargs):
  79. FileIOCalculator.__init__(self, *args, label=label, **kwargs)
  80. def calculate(self, *args, **kwargs):
  81. gaussians = ('g16', 'g09', 'g03')
  82. if 'GAUSSIAN' in self.command:
  83. for gau in gaussians:
  84. if which(gau):
  85. self.command = self.command.replace('GAUSSIAN', gau)
  86. break
  87. else:
  88. raise EnvironmentError('Missing Gaussian executable {}'
  89. .format(gaussians))
  90. FileIOCalculator.calculate(self, *args, **kwargs)
  91. def write_input(self, atoms, properties=None, system_changes=None):
  92. FileIOCalculator.write_input(self, atoms, properties, system_changes)
  93. write(self.label + '.com', atoms, properties=properties,
  94. format='gaussian-in', parallel=False, **self.parameters)
  95. def read_results(self):
  96. output = read(self.label + '.log', format='gaussian-out')
  97. self.calc = output.calc
  98. self.results = output.calc.results
  99. # Method(s) defined in the old calculator, added here for
  100. # backwards compatibility
  101. def clean(self):
  102. for suffix in ['.com', '.chk', '.log']:
  103. try:
  104. os.remove(os.path.join(self.directory, self.label + suffix))
  105. except OSError:
  106. pass
  107. def get_version(self):
  108. raise NotImplementedError # not sure how to do this yet