-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathnew_fileIO.py
212 lines (194 loc) · 7.48 KB
/
new_fileIO.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
"""
NEW_FILEIO IS UNUSED
please use fileIO instead
"""
import re
import cclib
from AaronTools.atoms import Atom
from AaronTools.const import ELEMENTS, PHYSICAL, UNIT
from AaronTools.utils.utils import is_alpha, is_int
float_num = re.compile("[-+]?\d+\.?\d*")
READTYPES = ["XYZ", "Gaussian"]
WRITETYPES = ["XYZ", "Gaussian"]
NORM_FINISH = "Normal termination"
ERRORS = {
"NtrErr Called from FileIO": "CHK", # delete
"Wrong number of Negative eigenvalues": "EIGEN", # opt=noeigen
"Convergence failure -- run terminated.": "CONV", # scf=xqc
# check quota and alert user; REMOVE error from end of file!
"Erroneous write": "QUOTA",
"Atoms too close": "CLASH", # flag as CLASH
# die and alert user to check catalyst structure or fix input file
"The combination of multiplicity": "CHARGEMULT",
"Bend failed for angle": "REDUND", # Using opt=cartesian
"Unknown message": "UNKNOWN",
}
class FileReader:
"""
:name: file name without extension
:file_ext: file extension
:file_type:
:comment:
:atoms: list of Atom()
:all_geom: list of [Atom()] if optimization steps requested
:other: dict() containing additional info
"""
def __init__(self, filename, get_all=False, just_geom=True):
"""
:filename: a file name or a tuple(file_name, file_extension, IOstream)
:get_all: if true, optimization steps are also saved in
self.other['all_geom']; otherwise only saves last geometry
:just_geom: if true, does not store other information, such as
frequencies, only what is needed to construct a Geometry() obj
"""
if isinstance(filename, str):
self.name, self.file_ext = filename.rsplit(".", 1)
else:
self.name, self.file_ext = filename[:2]
filename = filename[2]
self.file_type = ""
self.comment = ""
self.atoms = []
self.all_geom = None
self.other = {}
try:
parser = cclib.io.ccopen(filename)
data = parser.parse()
self.file_type = str(parser).split()[0].split(".")[-1]
self.other = data.__dict__
except AttributeError:
if self.file_ext == "com":
self.file_type = "Gaussian"
self.read_com(filename)
return
for key in self.other:
print(key)
print(self.other[key])
for i, (n, c) in enumerate(zip(data.atomnos, data.atomcoords[-1])):
self.atoms += [Atom(element=ELEMENTS[n], coords=c, name=i + 1)]
if len(data.atomcoords) == 1:
# if > 1, there are more geometries to handle
del self.other["atomnos"]
del self.other["atomcoords"]
elif get_all:
# only handle them if get_all is true
self.all_geom = []
for i, coords in enumerate(data.atomcoords[:-1]):
atoms = []
for j, (n, c) in enumerate(zip(data.atomnos, coords)):
self.atoms += [
Atom(element=ELEMENTS[n], coords=c, name=j + 1)
]
self.all_geom += [atoms]
# cclib doesn't store XYZ file comments
if self.file_type == "XYZ":
self.read_xyz(filename)
# Grab things cclib doesn't from log files
if self.file_type == "Gaussian" and self.file_ext == "log":
self.read_log(filename)
# fix naming conventions
self.fix_names()
return
def read_log(self, filename):
# Grab things cclib doesn't from log files
if not self.other["metadata"]["success"]:
if isinstance(filename, str):
f = open(filename)
else:
f = filename
for line in f:
if "Molecular mass" in line:
self.other["mass"] = float(float_num.search(line).group(0))
self.other["mass"] *= UNIT.AMU_TO_KG
if "Rotational constants (GHZ):" in line:
rot = float_num.findall(line)
rot = [
float(r) * PHYSICAL.PLANCK * (10 ** 9) / PHYSICAL.KB
for r in rot
]
self.other["rotational_temperature"] = rot
def read_xyz(self, filename):
if isinstance(filename, str):
f = open(filename)
else:
f = filename
f.readline()
self.comment = f.readline().strip()
f.close()
def read_com(self, filename):
if isinstance(filename, str):
f = open(filename)
else:
f = filename
atoms = []
other = {}
found_atoms = False
found_constraint = False
for line in f:
# header
if line.startswith("%"):
# checkfile spec
other["checkfile"] = line.strip().split("=")[1]
continue
if line.startswith("#"):
match = re.search("^#(\S+)", line).group(1)
other["method"] = match.split("/")[0]
other["basis"] = match.split("/")[1]
if "temperature=" in line:
other["temperature"] = re.search(
"temperature=(\d+\.?\d*)", line
).group(1)
if "solvent=" in line:
other["solvent"] = re.search(
"solvent=(\S+)\)", line
).group(1)
if "scrf=" in line:
other["solvent_model"] = re.search(
"scrf=\((\S+),", line
).group(1)
if "EmpiricalDispersion=" in line:
other["emp_dispersion"] = re.search(
"EmpiricalDispersion=(\s+)", line
).group(1)
if "int=(grid(" in line:
other["grid"] = re.search("int=\(grid(\S+)", line).group(1)
for _ in range(4):
line = f.readline()
line = line.split()
other["charge"] = line[0]
other["mult"] = line[1]
found_atoms = True
continue
# constraints
if found_atoms and line.startswith("B") and line.endswith("F"):
found_constraint = True
if "constraint" not in other:
other["constraint"] = []
other["constraint"] += [float_num.findall(line)]
continue
# footer
if found_constraint:
if "footer" not in other:
other["footer"] = ""
other["footer"] += line
continue
# atom coords
nums = float_num.findall(line)
line = line.split()
if len(line) == 5 and is_alpha(line[0]) and len(nums) == 4:
if not is_int(line[1]):
continue
a = Atom(element=line[0], coords=nums[1:], flag=nums[0])
atoms += [a]
elif len(line) == 4 and is_alpha(line[0]) and len(nums) == 3:
a = Atom(element=line[0], coords=nums)
atoms += [a]
else:
continue
self.atoms = atoms
self.other = other
return
def fix_names(self):
if "metadata" in self.other:
if "success" in self.other["metadata"]:
self.other["finished"] = self.other["metadata"]["success"]