PageRenderTime 39ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/molmod/io/gaussian03/file_parsers.py

https://github.com/woutersmet/Molmodsummer
Python | 375 lines | 258 code | 97 blank | 20 comment | 38 complexity | 49d238c8e2343b99682301ef6f894e20 MD5 | raw file
Possible License(s): GPL-3.0
  1. # MolMod is a collection of molecular modelling tools for python.
  2. # Copyright (C) 2007 - 2008 Toon Verstraelen <Toon.Verstraelen@UGent.be>
  3. #
  4. # This file is part of MolMod.
  5. #
  6. # MolMod is free software; you can redistribute it and/or
  7. # modify it under the terms of the GNU General Public License
  8. # as published by the Free Software Foundation; either version 3
  9. # of the License, or (at your option) any later version.
  10. #
  11. # MolMod is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program; if not, see <http://www.gnu.org/licenses/>
  18. #
  19. # --
  20. from molmod.io.output_parsers import FileParser, MultiLineParser
  21. from molmod.molecules import Molecule
  22. from molmod.units import angstrom, unified
  23. import numpy
  24. import re
  25. __all__ = [
  26. "LinkParser", "ThermoChemParser", "HessianParser", "FrequenciesParser",
  27. "LowFrequenciesParser", "SelectedFrequenciesParser", "MassParser",
  28. "GradientParser", "InputOrientationGradientParser",
  29. "StandardOrientationGradientParser", "ConfigurationParser",
  30. "CoordinatesParser", "StandardOrientationCoordinatesParser",
  31. "InputOrientationCoordinatesParser", "OptimizedParser", "IsOptimizedParser",
  32. "OptimizedCoordinatesParser", "SCFParser", "EnergyParser"
  33. ]
  34. class LinkParser(MultiLineParser):
  35. filename = ".log"
  36. extension = True
  37. def __init__(self, link, label, activator=None, deactivator=None, condition=None, depends_on=[]):
  38. MultiLineParser.__init__(self, label, activator, deactivator, condition, depends_on)
  39. self.link = str(link)
  40. def reset(self):
  41. MultiLineParser.reset(self)
  42. self.in_link = False
  43. def parse(self, line):
  44. if line[:11] == " Leave Link" and line[13:13+len(self.link)] == self.link:
  45. self.in_link = False
  46. if self.in_link:
  47. MultiLineParser.parse(self, line)
  48. if line[:8] == " (Enter " and line[-6-len(self.link):-6] == self.link:
  49. self.in_link = True
  50. class ThermoChemParser(LinkParser):
  51. def __init__(self, label, activator=None, deactivator=None, condition=None, depends_on=[]):
  52. LinkParser.__init__(self, "716", label, activator, deactivator, condition, depends_on)
  53. class HessianParser(ThermoChemParser):
  54. def __init__(self, label="hessian", condition=None):
  55. ThermoChemParser.__init__(self, label,
  56. activator=re.compile("Force constants in Cartesian coordinates:"),
  57. deactivator=re.compile(r"^\s*\b[^0-9+\-]"),
  58. condition=condition
  59. )
  60. def reset(self):
  61. ThermoChemParser.reset(self)
  62. self.hessian = []
  63. def start_collecting(self):
  64. self.hessian = []
  65. def collect(self, line):
  66. words = line.split()
  67. if (len(words) > 1) and (words[1].find("D") >= 0):
  68. row_number = int(words[0])
  69. if row_number > len(self.hessian):
  70. row = []
  71. self.hessian.append(row)
  72. else:
  73. row = self.hessian[row_number-1]
  74. for word in words[1:]:
  75. row.append(float(word.replace("D", "e")))
  76. def stop_collecting(self):
  77. hessian = numpy.zeros((len(self.hessian), len(self.hessian)), float)
  78. for row_index, row in enumerate(self.hessian):
  79. for col_index, value in enumerate(row):
  80. hessian[row_index, col_index] = value
  81. if row_index != col_index:
  82. hessian[col_index, row_index] = value
  83. self.hessian = hessian
  84. def result(self):
  85. return self.hessian
  86. class FrequenciesParser(ThermoChemParser):
  87. def __init__(self, label, pattern, condition):
  88. # returns the frequencies in cm-1
  89. ThermoChemParser.__init__(self, label, None, None, condition)
  90. self.pattern = pattern
  91. def reset(self):
  92. ThermoChemParser.reset(self)
  93. self.frequencies = []
  94. def collect(self, line):
  95. if line[:len(self.pattern)] == self.pattern:
  96. words = line[len(self.pattern):].split()
  97. self.frequencies.extend(float(word) for word in words)
  98. def result(self):
  99. return numpy.array(self.frequencies)
  100. class LowFrequenciesParser(FrequenciesParser):
  101. def __init__(self, label="low_frequencies", condition=None):
  102. FrequenciesParser.__init__(self, label, " Low frequencies ---", condition)
  103. class SelectedFrequenciesParser(FrequenciesParser):
  104. def __init__(self, label="selected_frequencies", condition=None):
  105. FrequenciesParser.__init__(self, label, " Frequencies --", condition)
  106. class MassParser(ThermoChemParser):
  107. def __init__(self, label="masses", condition=None):
  108. ThermoChemParser.__init__(self, label,
  109. activator=re.compile("Temperature\s+\S+\s+Kelvin.\s+Pressure\s+\S+\s+Atm."),
  110. deactivator=re.compile("Molecular mass:\s+\S+\s+amu."),
  111. condition=condition
  112. )
  113. self.re = re.compile("Atom\s*\d+\s+has atomic number\s+\d+\s+and mass\s+(?P<mass>\S+)")
  114. def reset(self):
  115. ThermoChemParser.reset(self)
  116. self.masses = []
  117. def start_collecting(self):
  118. self.masses = []
  119. def collect(self, line):
  120. match = self.re.search(line)
  121. if match is not None:
  122. self.masses.append(float(match.group("mass"))*unified)
  123. def stop_collecting(self):
  124. self.masses = numpy.array(self.masses, float)
  125. def result(self):
  126. return self.masses
  127. class GradientParser(ThermoChemParser):
  128. def __init__(self, label, activator, deactivator, condition=None):
  129. ThermoChemParser.__init__(self, label, activator, deactivator, condition)
  130. self.re = re.compile("\d+\s+\d+\s+(?P<fx>\S+)\s+(?P<fy>\S+)\s+(?P<fz>\S+)")
  131. def reset(self):
  132. ThermoChemParser.reset(self)
  133. self.gradient_list = []
  134. def start_collecting(self):
  135. self.gradient = []
  136. def collect(self, line):
  137. match = self.re.search(line)
  138. if match is not None:
  139. self.gradient.append([
  140. -float(match.group("fx")),
  141. -float(match.group("fy")),
  142. -float(match.group("fz"))
  143. ])
  144. def stop_collecting(self):
  145. self.gradient_list.append(numpy.array(self.gradient, float))
  146. def result(self):
  147. return self.gradient_list
  148. class InputOrientationGradientParser(GradientParser):
  149. def __init__(self, label="io_gradient_list", condition=None):
  150. GradientParser.__init__(self, label,
  151. activator=re.compile("\*\*\*\*\* Axes restored to original set \*\*\*\*\*"),
  152. deactivator=re.compile("Cartesian Forces:"),
  153. condition=condition
  154. )
  155. class StandardOrientationGradientParser(GradientParser):
  156. def __init__(self, label="so_gradient_list", condition=None):
  157. GradientParser.__init__(self, label,
  158. activator=re.compile("Forces in standard orientation"),
  159. deactivator=re.compile("\*\*\*\*\* Axes restored to original set \*\*\*\*\*"),
  160. condition=condition
  161. )
  162. class ConfigurationParser(LinkParser):
  163. def __init__(self, label, activator=None, deactivator=None, condition=None, depends_on=[]):
  164. LinkParser.__init__(self, "202", label, activator, deactivator, condition, depends_on)
  165. class CoordinatesParser(ConfigurationParser):
  166. def __init__(self, label, activator, deactivator, condition=None):
  167. ConfigurationParser.__init__(self, label, activator, deactivator, condition)
  168. self.re = re.compile("\d+\s+\d+\s+\d+\s+(?P<x>\S+)\s+(?P<y>\S+)\s+(?P<z>\S+)")
  169. def reset(self):
  170. ConfigurationParser.reset(self)
  171. self.coordinates = []
  172. def start_collecting(self):
  173. self.current_coordinates = []
  174. def collect(self, line):
  175. match = self.re.search(line)
  176. if match is not None:
  177. self.current_coordinates.append([
  178. float(match.group("x")) * angstrom,
  179. float(match.group("y")) * angstrom,
  180. float(match.group("z")) * angstrom,
  181. ])
  182. def stop_collecting(self):
  183. self.coordinates.append(numpy.array(self.current_coordinates, float))
  184. def result(self):
  185. return self.coordinates
  186. class StandardOrientationCoordinatesParser(CoordinatesParser):
  187. def __init__(self, label="so_coordinates_list", condition=None):
  188. CoordinatesParser.__init__(self, label,
  189. re.compile("Standard orientation"),
  190. re.compile("Rotational constants"),
  191. condition
  192. )
  193. class InputOrientationCoordinatesParser(CoordinatesParser):
  194. def __init__(self, label="io_coordinates_list", condition=None):
  195. CoordinatesParser.__init__(self, label,
  196. re.compile("Input orientation"),
  197. re.compile("Standard orientation"),
  198. condition
  199. )
  200. class OptimizedParser(LinkParser):
  201. def __init__(self, label, activator=None, deactivator=None, condition=None, depends_on=[]):
  202. LinkParser.__init__(self, "103", label, activator, deactivator, condition, depends_on)
  203. class IsOptimizedParser(OptimizedParser):
  204. def __init__(self, label="optimized", condition=None):
  205. OptimizedParser.__init__(self, label, None, None, condition)
  206. self.re = re.compile("-- Stationary point found\.")
  207. def reset(self):
  208. OptimizedParser.reset(self)
  209. self.optimized = False
  210. def collect(self, line):
  211. if not self.optimized and self.re.search(line) is not None:
  212. self.optimized = True
  213. def result(self):
  214. return self.optimized
  215. class OptimizedCoordinatesParser(OptimizedParser):
  216. def __init__(self, label="optimized_coordinates", condition=None):
  217. OptimizedParser.__init__(self, label,
  218. re.compile("Optimized Parameters"),
  219. re.compile("GradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGradGrad"),
  220. condition
  221. )
  222. self.re = re.compile("\S+\s+R\(\d+,-\d\)\s+(?P<coordinate>\S+)\s+-DE/DX")
  223. def reset(self):
  224. OptimizedParser.reset(self)
  225. self.completed = False
  226. def start_collecting(self):
  227. if not self.completed:
  228. self.optimized_coordinates = []
  229. def collect(self, line):
  230. if not self.completed:
  231. match = self.re.search(line)
  232. if match is not None:
  233. self.optimized_coordinates.append(float(match.group("coordinate"))*angstrom)
  234. def stop_collecting(self):
  235. if not self.completed:
  236. self.optimized_coordinates = numpy.array(self.optimized_coordinates, float)
  237. self.optimized_coordinates.shape = (-1, 3)
  238. self.completed = True
  239. def result(self):
  240. return self.optimized_coordinates
  241. class SCFParser(LinkParser):
  242. def __init__(self, label, activator=None, deactivator=None, condition=None, depends_on=[]):
  243. LinkParser.__init__(self, "502", label, activator, deactivator, condition, depends_on)
  244. class EnergyParser(SCFParser):
  245. def __init__(self, label="energies", condition=None):
  246. SCFParser.__init__(self, label, None, None, condition)
  247. self.re = re.compile("SCF Done:\s+E\S+\s+=\s+(?P<energy>\S+)\s+A.U.")
  248. def reset(self):
  249. SCFParser.reset(self)
  250. self.energies = []
  251. def collect(self, line):
  252. match = self.re.search(line)
  253. if match is not None:
  254. self.energies.append(float(match.group("energy")))
  255. def result(self):
  256. return numpy.array(self.energies)
  257. class PopulationParser(LinkParser):
  258. def __init__(self, label, activator=None, deactivator=None, condition=None, depends_on=[]):
  259. LinkParser.__init__(self, "602", label, activator, deactivator, condition, depends_on)
  260. class ESPFitParser(PopulationParser):
  261. def __init__(self, label="espfit", condition=None):
  262. PopulationParser.__init__(self, label,
  263. re.compile("Charges from ESP fit"),
  264. re.compile("-----------------------------------------------------------------"),
  265. condition
  266. )
  267. def start_collecting(self):
  268. self.espfit = []
  269. self.started = False
  270. def collect(self, line):
  271. if self.started:
  272. self.espfit.append([float(word) for word in line.split()[2:]])
  273. if line.startswith(" 1"):
  274. self.started = True
  275. def stop_collecting(self):
  276. pass
  277. def result(self):
  278. return numpy.array(self.espfit)